summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/SCsub7
-rw-r--r--modules/arkit/SCsub15
-rw-r--r--modules/arkit/arkit_interface.h134
-rw-r--r--modules/arkit/arkit_interface.mm791
-rw-r--r--modules/arkit/config.py6
-rw-r--r--modules/assimp/SCsub94
-rw-r--r--modules/assimp/editor_scene_importer_assimp.cpp1486
-rw-r--r--modules/assimp/editor_scene_importer_assimp.h149
-rwxr-xr-xmodules/assimp/godot_update_assimp.sh262
-rw-r--r--modules/assimp/import_state.h132
-rw-r--r--modules/assimp/import_utils.h463
-rw-r--r--modules/basis_universal/SCsub38
-rw-r--r--modules/basis_universal/register_types.cpp16
-rw-r--r--modules/basis_universal/register_types.h4
-rw-r--r--modules/basis_universal/texture_basisu.cpp23
-rw-r--r--modules/basis_universal/texture_basisu.h10
-rw-r--r--modules/bmp/image_loader_bmp.cpp60
-rw-r--r--modules/bmp/image_loader_bmp.h34
-rw-r--r--modules/bmp/register_types.cpp4
-rw-r--r--modules/bmp/register_types.h4
-rw-r--r--modules/bullet/SCsub27
-rw-r--r--modules/bullet/area_bullet.cpp21
-rw-r--r--modules/bullet/area_bullet.h22
-rw-r--r--modules/bullet/btRayShape.cpp8
-rw-r--r--modules/bullet/btRayShape.h10
-rw-r--r--modules/bullet/bullet_physics_server.cpp592
-rw-r--r--modules/bullet/bullet_physics_server.h117
-rw-r--r--modules/bullet/bullet_types_converter.cpp10
-rw-r--r--modules/bullet/bullet_types_converter.h10
-rw-r--r--modules/bullet/bullet_utilities.h4
-rw-r--r--modules/bullet/collision_object_bullet.cpp28
-rw-r--r--modules/bullet/collision_object_bullet.h26
-rw-r--r--modules/bullet/cone_twist_joint_bullet.cpp10
-rw-r--r--modules/bullet/cone_twist_joint_bullet.h6
-rw-r--r--modules/bullet/config.py4
-rw-r--r--modules/bullet/constraint_bullet.cpp4
-rw-r--r--modules/bullet/constraint_bullet.h4
-rw-r--r--modules/bullet/generic_6dof_joint_bullet.cpp34
-rw-r--r--modules/bullet/generic_6dof_joint_bullet.h17
-rw-r--r--modules/bullet/godot_collision_configuration.cpp4
-rw-r--r--modules/bullet/godot_collision_configuration.h4
-rw-r--r--modules/bullet/godot_collision_dispatcher.cpp8
-rw-r--r--modules/bullet/godot_collision_dispatcher.h6
-rw-r--r--modules/bullet/godot_motion_state.h6
-rw-r--r--modules/bullet/godot_ray_world_algorithm.cpp4
-rw-r--r--modules/bullet/godot_ray_world_algorithm.h6
-rw-r--r--modules/bullet/godot_result_callbacks.cpp39
-rw-r--r--modules/bullet/godot_result_callbacks.h48
-rw-r--r--modules/bullet/hinge_joint_bullet.cpp10
-rw-r--r--modules/bullet/hinge_joint_bullet.h6
-rw-r--r--modules/bullet/joint_bullet.cpp4
-rw-r--r--modules/bullet/joint_bullet.h4
-rw-r--r--modules/bullet/pin_joint_bullet.cpp4
-rw-r--r--modules/bullet/pin_joint_bullet.h4
-rw-r--r--modules/bullet/register_types.cpp8
-rw-r--r--modules/bullet/register_types.h4
-rw-r--r--modules/bullet/rid_bullet.h8
-rw-r--r--modules/bullet/rigid_body_bullet.cpp85
-rw-r--r--modules/bullet/rigid_body_bullet.h51
-rw-r--r--modules/bullet/shape_bullet.cpp57
-rw-r--r--modules/bullet/shape_bullet.h28
-rw-r--r--modules/bullet/shape_owner_bullet.cpp4
-rw-r--r--modules/bullet/shape_owner_bullet.h4
-rw-r--r--modules/bullet/slider_joint_bullet.cpp34
-rw-r--r--modules/bullet/slider_joint_bullet.h18
-rw-r--r--modules/bullet/soft_body_bullet.cpp141
-rw-r--r--modules/bullet/soft_body_bullet.h40
-rw-r--r--modules/bullet/space_bullet.cpp137
-rw-r--r--modules/bullet/space_bullet.h35
-rw-r--r--modules/camera/SCsub12
-rw-r--r--modules/camera/camera_ios.mm445
-rw-r--r--modules/camera/camera_osx.h6
-rw-r--r--modules/camera/camera_osx.mm60
-rw-r--r--modules/camera/camera_win.cpp4
-rw-r--r--modules/camera/camera_win.h4
-rw-r--r--modules/camera/config.py2
-rw-r--r--modules/camera/register_types.cpp10
-rw-r--r--modules/camera/register_types.h4
-rw-r--r--modules/csg/config.py2
-rw-r--r--modules/csg/csg.cpp90
-rw-r--r--modules/csg/csg.h56
-rw-r--r--modules/csg/csg_gizmos.cpp170
-rw-r--r--modules/csg/csg_gizmos.h26
-rw-r--r--modules/csg/csg_shape.cpp1029
-rw-r--r--modules/csg/csg_shape.h83
-rw-r--r--modules/csg/doc_classes/CSGBox3D.xml14
-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.xml7
-rw-r--r--modules/csg/doc_classes/CSGPolygon3D.xml61
-rw-r--r--modules/csg/doc_classes/CSGPrimitive3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGShape3D.xml58
-rw-r--r--modules/csg/doc_classes/CSGSphere3D.xml4
-rw-r--r--modules/csg/doc_classes/CSGTorus3D.xml4
-rw-r--r--modules/csg/icons/CSGBox3D.svg7
-rw-r--r--modules/csg/icons/CSGCapsule3D.svg7
-rw-r--r--modules/csg/icons/CSGCombiner3D.svg9
-rw-r--r--modules/csg/icons/CSGCylinder3D.svg7
-rw-r--r--modules/csg/icons/CSGMesh3D.svg7
-rw-r--r--modules/csg/icons/CSGPolygon3D.svg7
-rw-r--r--modules/csg/icons/CSGSphere3D.svg7
-rw-r--r--modules/csg/icons/CSGTorus3D.svg7
-rw-r--r--modules/csg/register_types.cpp22
-rw-r--r--modules/csg/register_types.h4
-rw-r--r--modules/cvtt/SCsub15
-rw-r--r--modules/cvtt/image_compress_cvtt.cpp37
-rw-r--r--modules/cvtt/image_compress_cvtt.h6
-rw-r--r--modules/cvtt/register_types.cpp4
-rw-r--r--modules/cvtt/register_types.h4
-rw-r--r--modules/dds/register_types.cpp6
-rw-r--r--modules/dds/register_types.h4
-rw-r--r--modules/dds/texture_loader_dds.cpp20
-rw-r--r--modules/dds/texture_loader_dds.h6
-rw-r--r--modules/denoise/SCsub17
-rw-r--r--modules/denoise/config.py11
-rw-r--r--modules/denoise/denoise_wrapper.cpp4
-rw-r--r--modules/denoise/denoise_wrapper.h4
-rw-r--r--modules/denoise/lightmap_denoiser.cpp6
-rw-r--r--modules/denoise/lightmap_denoiser.h6
-rw-r--r--modules/denoise/register_types.cpp6
-rw-r--r--modules/denoise/register_types.h4
-rw-r--r--modules/enet/SCsub16
-rw-r--r--modules/enet/config.py4
-rw-r--r--modules/enet/doc_classes/ENetConnection.xml195
-rw-r--r--modules/enet/doc_classes/ENetMultiplayerPeer.xml84
-rw-r--r--modules/enet/doc_classes/ENetPacketPeer.xml184
-rw-r--r--modules/enet/doc_classes/NetworkedMultiplayerENet.xml169
-rw-r--r--modules/enet/enet_connection.cpp470
-rw-r--r--modules/enet/enet_connection.h138
-rw-r--r--modules/enet/enet_multiplayer_peer.cpp670
-rw-r--r--modules/enet/enet_multiplayer_peer.h (renamed from modules/enet/networked_multiplayer_enet.h)153
-rw-r--r--modules/enet/enet_packet_peer.cpp263
-rw-r--r--modules/enet/enet_packet_peer.h131
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp933
-rw-r--r--modules/enet/register_types.cpp14
-rw-r--r--modules/enet/register_types.h4
-rw-r--r--modules/etc/SCsub37
-rw-r--r--modules/etc/config.py6
-rw-r--r--modules/etc/image_etc.cpp226
-rw-r--r--modules/etc/texture_loader_pkm.cpp114
-rw-r--r--modules/etcpak/SCsub36
-rw-r--r--modules/etcpak/config.py (renamed from modules/assimp/config.py)0
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp184
-rw-r--r--modules/etcpak/image_compress_etcpak.h52
-rw-r--r--modules/etcpak/register_types.cpp (renamed from modules/gdnative/xr/register_types.cpp)16
-rw-r--r--modules/etcpak/register_types.h (renamed from modules/assimp/register_types.h)14
-rw-r--r--modules/fbx/README.md196
-rw-r--r--modules/fbx/SCsub18
-rw-r--r--modules/fbx/config.py16
-rw-r--r--modules/fbx/data/fbx_anim_container.h46
-rw-r--r--modules/fbx/data/fbx_bone.cpp (renamed from modules/arkit/arkit_session_delegate.mm)42
-rw-r--r--modules/fbx/data/fbx_bone.h90
-rw-r--r--modules/fbx/data/fbx_material.cpp468
-rw-r--r--modules/fbx/data/fbx_material.h285
-rw-r--r--modules/fbx/data/fbx_mesh_data.cpp1435
-rw-r--r--modules/fbx/data/fbx_mesh_data.h200
-rw-r--r--modules/fbx/data/fbx_node.h63
-rw-r--r--modules/fbx/data/fbx_skeleton.cpp130
-rw-r--r--modules/fbx/data/fbx_skeleton.h53
-rw-r--r--modules/fbx/data/import_state.h112
-rw-r--r--modules/fbx/data/model_abstraction.h52
-rw-r--r--modules/fbx/data/pivot_transform.cpp307
-rw-r--r--modules/fbx/data/pivot_transform.h115
-rw-r--r--modules/fbx/editor_scene_importer_fbx.cpp1470
-rw-r--r--modules/fbx/editor_scene_importer_fbx.h134
-rw-r--r--modules/fbx/fbx_parser/ByteSwapper.h283
-rw-r--r--modules/fbx/fbx_parser/CREDITS183
-rw-r--r--modules/fbx/fbx_parser/FBXAnimation.cpp273
-rw-r--r--modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp442
-rw-r--r--modules/fbx/fbx_parser/FBXCommon.h110
-rw-r--r--modules/fbx/fbx_parser/FBXDeformer.cpp271
-rw-r--r--modules/fbx/fbx_parser/FBXDocument.cpp636
-rw-r--r--modules/fbx/fbx_parser/FBXDocument.h1252
-rw-r--r--modules/fbx/fbx_parser/FBXDocumentUtil.cpp141
-rw-r--r--modules/fbx/fbx_parser/FBXDocumentUtil.h134
-rw-r--r--modules/fbx/fbx_parser/FBXImportSettings.h162
-rw-r--r--modules/fbx/fbx_parser/FBXMaterial.cpp388
-rw-r--r--modules/fbx/fbx_parser/FBXMeshGeometry.cpp485
-rw-r--r--modules/fbx/fbx_parser/FBXMeshGeometry.h263
-rw-r--r--modules/fbx/fbx_parser/FBXModel.cpp171
-rw-r--r--modules/fbx/fbx_parser/FBXNodeAttribute.cpp174
-rw-r--r--modules/fbx/fbx_parser/FBXParseTools.h111
-rw-r--r--modules/fbx/fbx_parser/FBXParser.cpp1322
-rw-r--r--modules/fbx/fbx_parser/FBXParser.h270
-rw-r--r--modules/fbx/fbx_parser/FBXPose.cpp104
-rw-r--r--modules/fbx/fbx_parser/FBXProperties.cpp246
-rw-r--r--modules/fbx/fbx_parser/FBXProperties.h212
-rw-r--r--modules/fbx/fbx_parser/FBXTokenizer.cpp253
-rw-r--r--modules/fbx/fbx_parser/FBXTokenizer.h203
-rw-r--r--modules/fbx/fbx_parser/FBXUtil.cpp222
-rw-r--r--modules/fbx/fbx_parser/FBXUtil.h122
-rw-r--r--modules/fbx/fbx_parser/LICENSE39
-rw-r--r--modules/fbx/register_types.cpp (renamed from modules/assimp/register_types.cpp)18
-rw-r--r--modules/fbx/register_types.h (renamed from modules/gdnative/net/register_types.h)14
-rw-r--r--modules/fbx/tools/import_utils.cpp151
-rw-r--r--modules/fbx/tools/import_utils.h400
-rw-r--r--modules/fbx/tools/validation_tools.cpp48
-rw-r--r--modules/fbx/tools/validation_tools.h92
-rw-r--r--modules/freetype/SCsub15
-rw-r--r--modules/freetype/register_types.cpp4
-rw-r--r--modules/freetype/register_types.h4
-rw-r--r--modules/freetype/uwpdef.h4
-rw-r--r--modules/gdnative/SCsub4
-rw-r--r--modules/gdnative/android/android_gdn.cpp6
-rw-r--r--modules/gdnative/config.py8
-rw-r--r--modules/gdnative/doc_classes/GDNative.xml22
-rw-r--r--modules/gdnative/doc_classes/GDNativeLibrary.xml14
-rw-r--r--modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml13
-rw-r--r--modules/gdnative/doc_classes/NativeScript.xml28
-rw-r--r--modules/gdnative/doc_classes/PluginScript.xml5
-rw-r--r--modules/gdnative/doc_classes/VideoStreamGDNative.xml11
-rw-r--r--modules/gdnative/doc_classes/WebRTCPeerConnectionGDNative.xml13
-rw-r--r--modules/gdnative/doc_classes/XRInterfaceGDNative.xml15
-rw-r--r--modules/gdnative/gdnative.cpp60
-rw-r--r--modules/gdnative/gdnative.h14
-rw-r--r--modules/gdnative/gdnative/aabb.cpp192
-rw-r--r--modules/gdnative/gdnative/array.cpp347
-rw-r--r--modules/gdnative/gdnative/basis.cpp261
-rw-r--r--modules/gdnative/gdnative/callable.cpp213
-rw-r--r--modules/gdnative/gdnative/color.cpp182
-rw-r--r--modules/gdnative/gdnative/dictionary.cpp146
-rw-r--r--modules/gdnative/gdnative/gdnative.cpp33
-rw-r--r--modules/gdnative/gdnative/node_path.cpp86
-rw-r--r--modules/gdnative/gdnative/packed_arrays.cpp990
-rw-r--r--modules/gdnative/gdnative/plane.cpp136
-rw-r--r--modules/gdnative/gdnative/quat.cpp237
-rw-r--r--modules/gdnative/gdnative/quaternion.cpp61
-rw-r--r--modules/gdnative/gdnative/rect2.cpp299
-rw-r--r--modules/gdnative/gdnative/rid.cpp44
-rw-r--r--modules/gdnative/gdnative/signal.cpp57
-rw-r--r--modules/gdnative/gdnative/string.cpp1255
-rw-r--r--modules/gdnative/gdnative/string_name.cpp51
-rw-r--r--modules/gdnative/gdnative/transform.cpp230
-rw-r--r--modules/gdnative/gdnative/transform2d.cpp176
-rw-r--r--modules/gdnative/gdnative/transform_3d.cpp51
-rw-r--r--modules/gdnative/gdnative/variant.cpp902
-rw-r--r--modules/gdnative/gdnative/vector2.cpp424
-rw-r--r--modules/gdnative/gdnative/vector3.cpp428
-rw-r--r--modules/gdnative/gdnative_api.json12731
-rw-r--r--modules/gdnative/gdnative_builders.py2
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.cpp85
-rw-r--r--modules/gdnative/gdnative_library_editor_plugin.h10
-rw-r--r--modules/gdnative/gdnative_library_singleton_editor.cpp4
-rw-r--r--modules/gdnative/gdnative_library_singleton_editor.h4
-rw-r--r--modules/gdnative/icons/GDNativeLibrary.svg6
-rw-r--r--modules/gdnative/icons/NativeScript.svg6
-rw-r--r--modules/gdnative/include/android/godot_android.h4
-rw-r--r--modules/gdnative/include/gdnative/aabb.h74
-rw-r--r--modules/gdnative/include/gdnative/array.h99
-rw-r--r--modules/gdnative/include/gdnative/basis.h92
-rw-r--r--modules/gdnative/include/gdnative/callable.h74
-rw-r--r--modules/gdnative/include/gdnative/color.h75
-rw-r--r--modules/gdnative/include/gdnative/dictionary.h55
-rw-r--r--modules/gdnative/include/gdnative/gdnative.h43
-rw-r--r--modules/gdnative/include/gdnative/math_defs.h66
-rw-r--r--modules/gdnative/include/gdnative/node_path.h36
-rw-r--r--modules/gdnative/include/gdnative/packed_arrays.h378
-rw-r--r--modules/gdnative/include/gdnative/plane.h55
-rw-r--r--modules/gdnative/include/gdnative/quat.h118
-rw-r--r--modules/gdnative/include/gdnative/quaternion.h60
-rw-r--r--modules/gdnative/include/gdnative/rect2.h113
-rw-r--r--modules/gdnative/include/gdnative/rid.h24
-rw-r--r--modules/gdnative/include/gdnative/signal.h60
-rw-r--r--modules/gdnative/include/gdnative/string.h246
-rw-r--r--modules/gdnative/include/gdnative/string_name.h28
-rw-r--r--modules/gdnative/include/gdnative/transform.h111
-rw-r--r--modules/gdnative/include/gdnative/transform2d.h65
-rw-r--r--modules/gdnative/include/gdnative/transform_3d.h60
-rw-r--r--modules/gdnative/include/gdnative/variant.h256
-rw-r--r--modules/gdnative/include/gdnative/variant_struct.h (renamed from modules/etc/register_types.cpp)35
-rw-r--r--modules/gdnative/include/gdnative/vector2.h150
-rw-r--r--modules/gdnative/include/gdnative/vector3.h155
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h17
-rw-r--r--modules/gdnative/include/net/godot_net.h120
-rw-r--r--modules/gdnative/include/net/godot_webrtc.h122
-rw-r--r--modules/gdnative/include/pluginscript/godot_pluginscript.h19
-rw-r--r--modules/gdnative/include/videodecoder/godot_videodecoder.h12
-rw-r--r--modules/gdnative/include/xr/godot_xr.h91
-rw-r--r--modules/gdnative/nativescript/api_generator.cpp526
-rw-r--r--modules/gdnative/nativescript/api_generator.h7
-rw-r--r--modules/gdnative/nativescript/godot_nativescript.cpp40
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp534
-rw-r--r--modules/gdnative/nativescript/nativescript.h96
-rw-r--r--modules/gdnative/nativescript/register_types.cpp10
-rw-r--r--modules/gdnative/nativescript/register_types.h4
-rw-r--r--modules/gdnative/net/SCsub12
-rw-r--r--modules/gdnative/net/multiplayer_peer_gdnative.cpp125
-rw-r--r--modules/gdnative/net/stream_peer_gdnative.cpp77
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.cpp49
-rw-r--r--modules/gdnative/pluginscript/pluginscript_instance.h27
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.cpp61
-rw-r--r--modules/gdnative/pluginscript/pluginscript_language.h20
-rw-r--r--modules/gdnative/pluginscript/pluginscript_loader.cpp8
-rw-r--r--modules/gdnative/pluginscript/pluginscript_loader.h8
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.cpp150
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.h41
-rw-r--r--modules/gdnative/pluginscript/register_types.cpp10
-rw-r--r--modules/gdnative/pluginscript/register_types.h4
-rw-r--r--modules/gdnative/register_types.cpp42
-rw-r--r--modules/gdnative/register_types.h4
-rw-r--r--modules/gdnative/tests/test_string.h1980
-rw-r--r--modules/gdnative/tests/test_variant.h205
-rw-r--r--modules/gdnative/videodecoder/register_types.cpp10
-rw-r--r--modules/gdnative/videodecoder/register_types.h4
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.cpp48
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.h10
-rw-r--r--modules/gdnative/xr/SCsub6
-rw-r--r--modules/gdnative/xr/xr_interface_gdnative.cpp420
-rw-r--r--modules/gdnative/xr/xr_interface_gdnative.h91
-rw-r--r--modules/gdnavigation/config.py6
-rw-r--r--modules/gdscript/config.py1
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml1257
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml10
-rw-r--r--modules/gdscript/doc_classes/GDScriptFunctionState.xml45
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp131
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.h14
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.cpp6
-rw-r--r--modules/gdscript/editor/gdscript_translation_parser_plugin.h11
-rw-r--r--modules/gdscript/gdscript.cpp762
-rw-r--r--modules/gdscript/gdscript.h113
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp1411
-rw-r--r--modules/gdscript/gdscript_analyzer.h35
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp1303
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h319
-rw-r--r--modules/gdscript/gdscript_cache.cpp38
-rw-r--r--modules/gdscript/gdscript_cache.h13
-rw-r--r--modules/gdscript/gdscript_codegen.h51
-rw-r--r--modules/gdscript/gdscript_compiler.cpp759
-rw-r--r--modules/gdscript/gdscript_compiler.h30
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp1019
-rw-r--r--modules/gdscript/gdscript_editor.cpp571
-rw-r--r--modules/gdscript/gdscript_function.cpp2121
-rw-r--r--modules/gdscript/gdscript_function.h385
-rw-r--r--modules/gdscript/gdscript_functions.cpp1964
-rw-r--r--modules/gdscript/gdscript_functions.h137
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp95
-rw-r--r--modules/gdscript/gdscript_lambda_callable.h65
-rw-r--r--modules/gdscript/gdscript_parser.cpp1281
-rw-r--r--modules/gdscript/gdscript_parser.h214
-rw-r--r--modules/gdscript/gdscript_tokenizer.cpp71
-rw-r--r--modules/gdscript/gdscript_tokenizer.h39
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp731
-rw-r--r--modules/gdscript/gdscript_utility_functions.h58
-rw-r--r--modules/gdscript/gdscript_vm.cpp3358
-rw-r--r--modules/gdscript/gdscript_warning.cpp10
-rw-r--r--modules/gdscript/gdscript_warning.h9
-rw-r--r--modules/gdscript/icons/GDScript.svg6
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.cpp153
-rw-r--r--modules/gdscript/language_server/gdscript_extend_parser.h8
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.cpp60
-rw-r--r--modules/gdscript/language_server/gdscript_language_protocol.h25
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.cpp34
-rw-r--r--modules/gdscript/language_server/gdscript_language_server.h17
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.cpp81
-rw-r--r--modules/gdscript/language_server/gdscript_text_document.h15
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.cpp280
-rw-r--r--modules/gdscript/language_server/gdscript_workspace.h18
-rw-r--r--modules/gdscript/language_server/lsp.hpp369
-rw-r--r--modules/gdscript/register_types.cpp38
-rw-r--r--modules/gdscript/register_types.h4
-rw-r--r--modules/gdscript/tests/README.md8
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.cpp588
-rw-r--r--modules/gdscript/tests/gdscript_test_runner.h126
-rw-r--r--modules/gdscript/tests/gdscript_test_runner_suite.h74
-rw-r--r--modules/gdscript/tests/scripts/.gitignore2
-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.out2
-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/call_self_get_name.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.gd3
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.out3
-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/gdscript_to_preload.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out1
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.gd11
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.out2
-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/analyzer/features/use_preload_script_as_type.gd5
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.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_empty_assignee.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.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_closing_expr_paren.gd2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_colon.gd3
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/missing_colon.out2
-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/missing_paren_after_args.out2
-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.gd3
-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.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out2
-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.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out2
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd5
-rw-r--r--modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out2
-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.out5
-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/str_preserves_case.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/features/str_preserves_case.out4
-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/trailing_comma_in_function_args.gd7
-rw-r--r--modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.out2
-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.gd20
-rw-r--r--modules/gdscript/tests/scripts/parser/features/variable_declaration.out3
-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_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/unused_variable.out5
-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/project.godot10
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd5
-rw-r--r--modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out6
-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.cpp62
-rw-r--r--modules/gdscript/tests/test_gdscript.h11
-rw-r--r--modules/glslang/SCsub77
-rw-r--r--modules/glslang/register_types.cpp193
-rw-r--r--modules/glslang/register_types.h4
-rw-r--r--modules/gltf/SCsub10
-rw-r--r--modules/gltf/config.py31
-rw-r--r--modules/gltf/doc_classes/GLTFAccessor.xml39
-rw-r--r--modules/gltf/doc_classes/GLTFAnimation.xml13
-rw-r--r--modules/gltf/doc_classes/GLTFBufferView.xml21
-rw-r--r--modules/gltf/doc_classes/GLTFCamera.xml19
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml37
-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.xml31
-rw-r--r--modules/gltf/doc_classes/GLTFMesh.xml17
-rw-r--r--modules/gltf/doc_classes/GLTFNode.xml37
-rw-r--r--modules/gltf/doc_classes/GLTFSkeleton.xml55
-rw-r--r--modules/gltf/doc_classes/GLTFSkin.xml60
-rw-r--r--modules/gltf/doc_classes/GLTFSpecGloss.xml21
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml212
-rw-r--r--modules/gltf/doc_classes/GLTFTexture.xml13
-rw-r--r--modules/gltf/editor_scene_exporter_gltf_plugin.cpp98
-rw-r--r--modules/gltf/editor_scene_exporter_gltf_plugin.h (renamed from modules/etc/texture_loader_pkm.h)38
-rw-r--r--modules/gltf/editor_scene_importer_gltf.cpp (renamed from modules/gdnative/net/webrtc_gdnative.cpp)53
-rw-r--r--modules/gltf/editor_scene_importer_gltf.h55
-rw-r--r--modules/gltf/gltf_accessor.cpp191
-rw-r--r--modules/gltf/gltf_accessor.h105
-rw-r--r--modules/gltf/gltf_animation.cpp53
-rw-r--r--modules/gltf/gltf_animation.h74
-rw-r--r--modules/gltf/gltf_buffer_view.cpp92
-rw-r--r--modules/gltf/gltf_buffer_view.h68
-rw-r--r--modules/gltf/gltf_camera.cpp47
-rw-r--r--modules/gltf/gltf_camera.h58
-rw-r--r--modules/gltf/gltf_document.cpp6976
-rw-r--r--modules/gltf/gltf_document.h454
-rw-r--r--modules/gltf/gltf_document_extension.cpp88
-rw-r--r--modules/gltf/gltf_document_extension.h63
-rw-r--r--modules/gltf/gltf_document_extension_convert_importer_mesh.cpp (renamed from modules/mono/editor/script_class_parser.h)136
-rw-r--r--modules/gltf/gltf_document_extension_convert_importer_mesh.h (renamed from modules/arkit/arkit_session_delegate.h)41
-rw-r--r--modules/gltf/gltf_light.cpp101
-rw-r--r--modules/gltf/gltf_light.h (renamed from modules/gdnative/net/packet_peer_gdnative.h)55
-rw-r--r--modules/gltf/gltf_mesh.cpp (renamed from modules/gdnative/net/packet_peer_gdnative.cpp)57
-rw-r--r--modules/gltf/gltf_mesh.h59
-rw-r--r--modules/gltf/gltf_node.cpp178
-rw-r--r--modules/gltf/gltf_node.h (renamed from modules/gdnative/net/multiplayer_peer_gdnative.h)89
-rw-r--r--modules/gltf/gltf_skeleton.cpp95
-rw-r--r--modules/gltf/gltf_skeleton.h101
-rw-r--r--modules/gltf/gltf_skin.cpp155
-rw-r--r--modules/gltf/gltf_skin.h109
-rw-r--r--modules/gltf/gltf_spec_gloss.cpp90
-rw-r--r--modules/gltf/gltf_spec_gloss.h (renamed from modules/gdnative/net/stream_peer_gdnative.h)57
-rw-r--r--modules/gltf/gltf_state.cpp307
-rw-r--r--modules/gltf/gltf_state.h190
-rw-r--r--modules/gltf/gltf_texture.cpp46
-rw-r--r--modules/gltf/gltf_texture.h (renamed from modules/camera/camera_ios.h)30
-rw-r--r--modules/gltf/register_types.cpp91
-rw-r--r--modules/gltf/register_types.h (renamed from modules/gdnative/xr/register_types.h)13
-rw-r--r--modules/gridmap/config.py2
-rw-r--r--modules/gridmap/doc_classes/GridMap.xml152
-rw-r--r--modules/gridmap/grid_map.cpp278
-rw-r--r--modules/gridmap/grid_map.h80
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp253
-rw-r--r--modules/gridmap/grid_map_editor_plugin.h53
-rw-r--r--modules/gridmap/icons/GridMap.svg6
-rw-r--r--modules/gridmap/register_types.cpp8
-rw-r--r--modules/gridmap/register_types.h4
-rw-r--r--modules/hdr/image_loader_hdr.cpp10
-rw-r--r--modules/hdr/image_loader_hdr.h4
-rw-r--r--modules/hdr/register_types.cpp4
-rw-r--r--modules/hdr/register_types.h4
-rw-r--r--modules/jpg/SCsub17
-rw-r--r--modules/jpg/image_loader_jpegd.cpp10
-rw-r--r--modules/jpg/image_loader_jpegd.h4
-rw-r--r--modules/jpg/register_types.cpp4
-rw-r--r--modules/jpg/register_types.h4
-rw-r--r--modules/jsonrpc/jsonrpc.cpp19
-rw-r--r--modules/jsonrpc/jsonrpc.h8
-rw-r--r--modules/jsonrpc/register_types.cpp8
-rw-r--r--modules/jsonrpc/register_types.h4
-rw-r--r--modules/lightmapper_rd/SCsub3
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.cpp409
-rw-r--r--modules/lightmapper_rd/lightmapper_rd.h130
-rw-r--r--modules/lightmapper_rd/lm_blendseams.glsl4
-rw-r--r--modules/lightmapper_rd/lm_common_inc.glsl39
-rw-r--r--modules/lightmapper_rd/lm_compute.glsl150
-rw-r--r--modules/lightmapper_rd/lm_raster.glsl16
-rw-r--r--modules/lightmapper_rd/register_types.cpp30
-rw-r--r--modules/lightmapper_rd/register_types.h4
-rw-r--r--[-rwxr-xr-x]modules/mbedtls/SCsub25
-rw-r--r--[-rwxr-xr-x]modules/mbedtls/config.py0
-rw-r--r--modules/mbedtls/crypto_mbedtls.cpp102
-rw-r--r--modules/mbedtls/crypto_mbedtls.h29
-rw-r--r--modules/mbedtls/dtls_server_mbedtls.cpp8
-rw-r--r--modules/mbedtls/dtls_server_mbedtls.h4
-rw-r--r--modules/mbedtls/packet_peer_mbed_dtls.cpp17
-rw-r--r--[-rwxr-xr-x]modules/mbedtls/packet_peer_mbed_dtls.h8
-rw-r--r--modules/mbedtls/register_types.cpp8
-rw-r--r--[-rwxr-xr-x]modules/mbedtls/register_types.h4
-rw-r--r--modules/mbedtls/ssl_context_mbedtls.cpp6
-rw-r--r--modules/mbedtls/ssl_context_mbedtls.h18
-rw-r--r--modules/mbedtls/stream_peer_mbedtls.cpp11
-rw-r--r--[-rwxr-xr-x]modules/mbedtls/stream_peer_mbedtls.h8
-rw-r--r--modules/mbedtls/tests/test_crypto_mbedtls.cpp62
-rw-r--r--modules/mbedtls/tests/test_crypto_mbedtls.h61
-rw-r--r--modules/meshoptimizer/SCsub45
-rw-r--r--modules/meshoptimizer/config.py7
-rw-r--r--modules/meshoptimizer/register_types.cpp48
-rw-r--r--modules/meshoptimizer/register_types.h37
-rw-r--r--modules/minimp3/SCsub17
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp241
-rw-r--r--modules/minimp3/audio_stream_mp3.h112
-rw-r--r--modules/minimp3/config.py (renamed from modules/stb_vorbis/config.py)2
-rw-r--r--modules/minimp3/doc_classes/AudioStreamMP3.xml (renamed from modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml)12
-rw-r--r--modules/minimp3/register_types.cpp (renamed from modules/stb_vorbis/register_types.cpp)22
-rw-r--r--modules/minimp3/register_types.h37
-rw-r--r--modules/minimp3/resource_importer_mp3.cpp (renamed from modules/stb_vorbis/resource_importer_ogg_vorbis.cpp)60
-rw-r--r--modules/minimp3/resource_importer_mp3.h58
-rw-r--r--modules/mobile_vr/config.py2
-rw-r--r--modules/mobile_vr/doc_classes/MobileVRInterface.xml5
-rw-r--r--modules/mobile_vr/mobile_vr_interface.cpp227
-rw-r--r--modules/mobile_vr/mobile_vr_interface.h94
-rw-r--r--modules/mobile_vr/register_types.cpp25
-rw-r--r--modules/mobile_vr/register_types.h4
-rw-r--r--modules/mono/.editorconfig14
-rw-r--r--modules/mono/Directory.Build.props3
-rw-r--r--modules/mono/SCsub8
-rw-r--r--modules/mono/SdkPackageVersions.props6
-rw-r--r--modules/mono/build_scripts/godot_net_sdk_build.py55
-rw-r--r--modules/mono/build_scripts/make_android_mono_config.py9
-rw-r--r--modules/mono/build_scripts/mono_configure.py16
-rw-r--r--modules/mono/build_scripts/mono_reg_utils.py1
-rw-r--r--modules/mono/class_db_api_json.cpp61
-rw-r--r--modules/mono/class_db_api_json.h12
-rw-r--r--modules/mono/config.py11
-rw-r--r--modules/mono/csharp_script.cpp1026
-rw-r--r--modules/mono/csharp_script.h124
-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.sln18
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj38
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec22
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props3
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.targets5
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Bar.cs15
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Foo.cs11
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj31
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs33
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs89
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj41
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props7
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs9
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs182
-rw-r--r--modules/mono/editor/GodotTools/.gitignore1
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs93
-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.CLI/Program.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs14
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs82
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj1
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs10
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs5
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs47
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets36
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools.sln6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs339
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs (renamed from modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs)19
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs (renamed from modules/mono/editor/GodotTools/GodotTools/BuildManager.cs)107
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs417
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs42
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs197
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs20
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs297
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildTab.cs267
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs86
-rw-r--r--[-rwxr-xr-x]modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs47
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs91
-rw-r--r--[-rwxr-xr-x]modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs2
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs213
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs14
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs38
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs85
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs32
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs37
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs15
-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/Internals/Internal.cs8
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs61
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/FsPathUtils.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs58
-rw-r--r--modules/mono/editor/bindings_generator.cpp642
-rw-r--r--modules/mono/editor/bindings_generator.h64
-rw-r--r--modules/mono/editor/code_completion.cpp51
-rw-r--r--modules/mono/editor/code_completion.h10
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp195
-rw-r--r--modules/mono/editor/editor_internal_calls.h4
-rw-r--r--modules/mono/editor/godotsharp_export.cpp17
-rw-r--r--modules/mono/editor/godotsharp_export.h11
-rw-r--r--modules/mono/editor/script_class_parser.cpp753
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs344
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs349
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs22
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs7
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs15
-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.cs234
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Callable.cs72
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Color.cs490
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs589
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs24
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs376
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs41
-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.cs36
-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.cs461
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotSynchronizationContext.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs17
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs127
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs265
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs36
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs162
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs53
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs153
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs541
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs673
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/RID.cs39
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs205
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2i.cs203
-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.cs969
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/StringName.cs33
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs256
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs (renamed from modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs)204
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs20
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs420
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs308
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs433
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs323
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj10
-rw-r--r--modules/mono/glue/arguments_vector.h4
-rw-r--r--modules/mono/glue/base_object_glue.cpp79
-rw-r--r--modules/mono/glue/callable_glue.cpp79
-rw-r--r--modules/mono/glue/collections_glue.cpp130
-rw-r--r--modules/mono/glue/gd_glue.cpp83
-rw-r--r--modules/mono/glue/glue_header.h23
-rw-r--r--modules/mono/glue/nodepath_glue.cpp30
-rw-r--r--modules/mono/glue/rid_glue.cpp16
-rw-r--r--modules/mono/glue/scene_tree_glue.cpp14
-rw-r--r--modules/mono/glue/string_glue.cpp28
-rw-r--r--modules/mono/glue/string_name_glue.cpp16
-rw-r--r--modules/mono/godotsharp_defs.h4
-rw-r--r--modules/mono/godotsharp_dirs.cpp27
-rw-r--r--modules/mono/godotsharp_dirs.h7
-rw-r--r--modules/mono/icons/CSharpScript.svg6
-rw-r--r--modules/mono/managed_callable.cpp4
-rw-r--r--modules/mono/managed_callable.h8
-rw-r--r--modules/mono/mono_gc_handle.cpp4
-rw-r--r--modules/mono/mono_gc_handle.h11
-rw-r--r--modules/mono/mono_gd/android_mono_config.h6
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp150
-rw-r--r--modules/mono/mono_gd/gd_mono.h25
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.cpp131
-rw-r--r--modules/mono/mono_gd/gd_mono_assembly.h23
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp39
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h22
-rw-r--r--modules/mono/mono_gd/gd_mono_class.cpp34
-rw-r--r--modules/mono/mono_gd/gd_mono_class.h19
-rw-r--r--modules/mono/mono_gd/gd_mono_field.cpp337
-rw-r--r--modules/mono/mono_gd/gd_mono_field.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_header.h6
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.cpp42
-rw-r--r--modules/mono/mono_gd/gd_mono_internals.h8
-rw-r--r--modules/mono/mono_gd/gd_mono_log.cpp18
-rw-r--r--modules/mono/mono_gd/gd_mono_log.h10
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.cpp1107
-rw-r--r--modules/mono/mono_gd/gd_mono_marshal.h137
-rw-r--r--modules/mono/mono_gd/gd_mono_method.cpp29
-rw-r--r--modules/mono/mono_gd/gd_mono_method.h15
-rw-r--r--modules/mono/mono_gd/gd_mono_method_thunk.h4
-rw-r--r--modules/mono/mono_gd/gd_mono_property.cpp37
-rw-r--r--modules/mono/mono_gd/gd_mono_property.h9
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp48
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.h28
-rw-r--r--modules/mono/mono_gd/gd_mono_wasm_m2n.cpp79
-rw-r--r--modules/mono/mono_gd/gd_mono_wasm_m2n.h263
-rw-r--r--modules/mono/mono_gd/i_mono_class_member.h4
-rw-r--r--modules/mono/mono_gd/managed_type.cpp4
-rw-r--r--modules/mono/mono_gd/managed_type.h4
-rw-r--r--modules/mono/mono_gd/support/android_support.cpp35
-rw-r--r--[-rwxr-xr-x]modules/mono/mono_gd/support/android_support.h7
-rw-r--r--[-rwxr-xr-x]modules/mono/mono_gd/support/ios_support.h7
-rw-r--r--modules/mono/mono_gd/support/ios_support.mm13
-rw-r--r--modules/mono/register_types.cpp20
-rw-r--r--modules/mono/register_types.h4
-rw-r--r--modules/mono/signal_awaiter_utils.cpp28
-rw-r--r--modules/mono/signal_awaiter_utils.h6
-rw-r--r--modules/mono/utils/macros.h5
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp13
-rw-r--r--modules/mono/utils/mono_reg_utils.h6
-rw-r--r--modules/mono/utils/osx_utils.cpp6
-rw-r--r--modules/mono/utils/osx_utils.h6
-rw-r--r--modules/mono/utils/path_utils.cpp18
-rw-r--r--modules/mono/utils/path_utils.h9
-rw-r--r--modules/mono/utils/string_utils.cpp61
-rw-r--r--modules/mono/utils/string_utils.h8
-rw-r--r--modules/msdfgen/SCsub66
-rw-r--r--modules/msdfgen/config.py6
-rw-r--r--modules/msdfgen/register_types.cpp (renamed from modules/opus/register_types.cpp)10
-rw-r--r--modules/msdfgen/register_types.h37
-rw-r--r--modules/navigation/SCsub (renamed from modules/gdnavigation/SCsub)31
-rw-r--r--modules/navigation/config.py (renamed from modules/gdnative/xr/config.py)0
-rw-r--r--modules/navigation/godot_navigation_server.cpp (renamed from modules/gdnavigation/gd_navigation_server.cpp)335
-rw-r--r--modules/navigation/godot_navigation_server.h (renamed from modules/gdnavigation/gd_navigation_server.h)45
-rw-r--r--modules/navigation/nav_map.cpp (renamed from modules/gdnavigation/nav_map.cpp)536
-rw-r--r--modules/navigation/nav_map.h (renamed from modules/gdnavigation/nav_map.h)7
-rw-r--r--modules/navigation/nav_region.cpp (renamed from modules/gdnavigation/nav_region.cpp)36
-rw-r--r--modules/navigation/nav_region.h (renamed from modules/gdnavigation/nav_region.h)26
-rw-r--r--modules/navigation/nav_rid.h (renamed from modules/gdnavigation/nav_rid.h)6
-rw-r--r--modules/navigation/nav_utils.h (renamed from modules/gdnavigation/nav_utils.h)49
-rw-r--r--modules/navigation/navigation_mesh_editor_plugin.cpp (renamed from modules/gdnavigation/navigation_mesh_editor_plugin.cpp)14
-rw-r--r--modules/navigation/navigation_mesh_editor_plugin.h (renamed from modules/gdnavigation/navigation_mesh_editor_plugin.h)4
-rw-r--r--modules/navigation/navigation_mesh_generator.cpp (renamed from modules/gdnavigation/navigation_mesh_generator.cpp)53
-rw-r--r--modules/navigation/navigation_mesh_generator.h (renamed from modules/gdnavigation/navigation_mesh_generator.h)10
-rw-r--r--modules/navigation/register_types.cpp (renamed from modules/gdnavigation/register_types.cpp)26
-rw-r--r--modules/navigation/register_types.h37
-rw-r--r--modules/navigation/rvo_agent.cpp (renamed from modules/gdnavigation/rvo_agent.cpp)4
-rw-r--r--modules/navigation/rvo_agent.h (renamed from modules/gdnavigation/rvo_agent.h)9
-rw-r--r--modules/ogg/SCsub19
-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.cpp11
-rw-r--r--modules/ogg/register_types.h4
-rw-r--r--modules/opensimplex/SCsub17
-rw-r--r--modules/opensimplex/doc_classes/NoiseTexture.xml22
-rw-r--r--modules/opensimplex/doc_classes/OpenSimplexNoise.xml93
-rw-r--r--modules/opensimplex/icons/NoiseTexture.svg4
-rw-r--r--modules/opensimplex/noise_texture.cpp95
-rw-r--r--modules/opensimplex/noise_texture.h43
-rw-r--r--modules/opensimplex/open_simplex_noise.cpp54
-rw-r--r--modules/opensimplex/open_simplex_noise.h52
-rw-r--r--modules/opensimplex/register_types.cpp8
-rw-r--r--modules/opensimplex/register_types.h4
-rw-r--r--modules/opus/SCsub239
-rw-r--r--modules/opus/config.py6
-rw-r--r--modules/opus/register_types.h37
-rw-r--r--modules/pvr/SCsub15
-rw-r--r--modules/pvr/image_compress_pvrtc.cpp88
-rw-r--r--modules/pvr/image_compress_pvrtc.h (renamed from modules/etc/image_etc.h)14
-rw-r--r--modules/pvr/register_types.cpp9
-rw-r--r--modules/pvr/register_types.h4
-rw-r--r--modules/pvr/texture_loader_pvr.cpp78
-rw-r--r--modules/pvr/texture_loader_pvr.h6
-rw-r--r--modules/raycast/SCsub115
-rw-r--r--modules/raycast/config.py19
-rw-r--r--modules/raycast/godot_update_embree.py261
-rw-r--r--modules/raycast/lightmap_raycaster.cpp196
-rw-r--r--modules/raycast/lightmap_raycaster.h77
-rw-r--r--modules/raycast/raycast_occlusion_cull.cpp618
-rw-r--r--modules/raycast/raycast_occlusion_cull.h193
-rw-r--r--modules/raycast/register_types.cpp (renamed from modules/webm/register_types.cpp)31
-rw-r--r--modules/raycast/register_types.h (renamed from modules/etc/register_types.h)13
-rw-r--r--modules/raycast/static_raycaster.cpp137
-rw-r--r--modules/raycast/static_raycaster.h64
-rw-r--r--modules/regex/SCsub18
-rw-r--r--modules/regex/config.py2
-rw-r--r--modules/regex/doc_classes/RegEx.xml71
-rw-r--r--modules/regex/doc_classes/RegExMatch.xml27
-rw-r--r--modules/regex/regex.cpp6
-rw-r--r--modules/regex/regex.h31
-rw-r--r--modules/regex/register_types.cpp10
-rw-r--r--modules/regex/register_types.h4
-rw-r--r--modules/regex/tests/test_regex.h164
-rw-r--r--modules/register_module_types.h4
-rw-r--r--modules/squish/SCsub16
-rw-r--r--modules/squish/image_compress_squish.h39
-rw-r--r--modules/squish/image_decompress_squish.cpp (renamed from modules/squish/image_compress_squish.cpp)88
-rw-r--r--modules/squish/image_decompress_squish.h38
-rw-r--r--modules/squish/register_types.cpp8
-rw-r--r--modules/squish/register_types.h4
-rw-r--r--modules/stb_vorbis/SCsub16
-rw-r--r--modules/stb_vorbis/audio_stream_ogg_vorbis.cpp276
-rw-r--r--modules/stb_vorbis/register_types.h37
-rw-r--r--modules/svg/SCsub17
-rw-r--r--modules/svg/image_loader_svg.cpp6
-rw-r--r--modules/svg/image_loader_svg.h6
-rw-r--r--modules/svg/register_types.cpp4
-rw-r--r--modules/svg/register_types.h4
-rw-r--r--modules/text_server_adv/SCsub533
-rw-r--r--modules/text_server_adv/config.py16
-rw-r--r--modules/text_server_adv/doc_classes/TextServerAdvanced.xml10
-rw-r--r--modules/text_server_adv/icu_data/icudata_stub.cpp63
-rw-r--r--modules/text_server_adv/register_types.cpp (renamed from modules/gdnative/net/register_types.cpp)25
-rw-r--r--modules/text_server_adv/register_types.h40
-rw-r--r--modules/text_server_adv/script_iterator.cpp128
-rw-r--r--modules/text_server_adv/script_iterator.h63
-rw-r--r--modules/text_server_adv/text_server_adv.cpp5091
-rw-r--r--modules/text_server_adv/text_server_adv.h527
-rw-r--r--modules/text_server_fb/SCsub25
-rw-r--r--modules/text_server_fb/config.py21
-rw-r--r--modules/text_server_fb/doc_classes/TextServerFallback.xml10
-rw-r--r--modules/text_server_fb/register_types.cpp (renamed from modules/arkit/register_types.cpp)23
-rw-r--r--modules/text_server_fb/register_types.h (renamed from modules/gdnavigation/register_types.h)19
-rw-r--r--modules/text_server_fb/text_server_fb.cpp3275
-rw-r--r--modules/text_server_fb/text_server_fb.h432
-rw-r--r--modules/tga/image_loader_tga.cpp53
-rw-r--r--modules/tga/image_loader_tga.h30
-rw-r--r--modules/tga/register_types.cpp4
-rw-r--r--modules/tga/register_types.h4
-rw-r--r--modules/theora/SCsub16
-rw-r--r--modules/theora/config.py2
-rw-r--r--modules/theora/doc_classes/VideoStreamTheora.xml13
-rw-r--r--modules/theora/register_types.cpp8
-rw-r--r--modules/theora/register_types.h4
-rw-r--r--modules/theora/video_stream_theora.cpp61
-rw-r--r--modules/theora/video_stream_theora.h61
-rw-r--r--modules/tinyexr/SCsub17
-rw-r--r--modules/tinyexr/image_loader_tinyexr.cpp8
-rw-r--r--modules/tinyexr/image_loader_tinyexr.h4
-rw-r--r--modules/tinyexr/image_saver_tinyexr.cpp6
-rw-r--r--modules/tinyexr/image_saver_tinyexr.h4
-rw-r--r--modules/tinyexr/register_types.cpp4
-rw-r--r--modules/tinyexr/register_types.h4
-rw-r--r--modules/upnp/SCsub30
-rw-r--r--modules/upnp/doc_classes/UPNP.xml112
-rw-r--r--modules/upnp/doc_classes/UPNPDevice.xml35
-rw-r--r--modules/upnp/register_types.cpp10
-rw-r--r--modules/upnp/register_types.h4
-rw-r--r--modules/upnp/upnp.cpp27
-rw-r--r--modules/upnp/upnp.h17
-rw-r--r--modules/upnp/upnp_device.cpp6
-rw-r--r--modules/upnp/upnp_device.h11
-rw-r--r--modules/vhacd/SCsub17
-rw-r--r--modules/vhacd/config.py2
-rw-r--r--modules/vhacd/register_types.cpp71
-rw-r--r--modules/vhacd/register_types.h4
-rw-r--r--modules/visual_script/SCsub3
-rw-r--r--modules/visual_script/config.py1
-rw-r--r--modules/visual_script/doc_classes/VisualScript.xml433
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBasicTypeConstant.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml79
-rw-r--r--modules/visual_script/doc_classes/VisualScriptClassConstant.xml8
-rw-r--r--modules/visual_script/doc_classes/VisualScriptComment.xml6
-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.xml20
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCustomNode.xml131
-rw-r--r--modules/visual_script/doc_classes/VisualScriptCustomNodes.xml (renamed from modules/visual_script/doc_classes/VisualScriptEditor.xml)27
-rw-r--r--modules/visual_script/doc_classes/VisualScriptDeconstruct.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptEmitSignal.xml6
-rw-r--r--modules/visual_script/doc_classes/VisualScriptEngineSingleton.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptExpression.xml6
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunction.xml6
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunctionCall.xml28
-rw-r--r--modules/visual_script/doc_classes/VisualScriptFunctionState.xml30
-rw-r--r--modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml7
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIndexGet.xml6
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIndexSet.xml6
-rw-r--r--modules/visual_script/doc_classes/VisualScriptInputAction.xml12
-rw-r--r--modules/visual_script/doc_classes/VisualScriptIterator.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLists.xml84
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLocalVar.xml6
-rw-r--r--modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml6
-rw-r--r--modules/visual_script/doc_classes/VisualScriptMathConstant.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptNode.xml23
-rw-r--r--modules/visual_script/doc_classes/VisualScriptOperator.xml7
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPreload.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPropertyGet.xml21
-rw-r--r--modules/visual_script/doc_classes/VisualScriptPropertySet.xml31
-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.xml6
-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.xml14
-rw-r--r--modules/visual_script/doc_classes/VisualScriptSwitch.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptTypeCast.xml10
-rw-r--r--modules/visual_script/doc_classes/VisualScriptVariableGet.xml6
-rw-r--r--modules/visual_script/doc_classes/VisualScriptVariableSet.xml6
-rw-r--r--modules/visual_script/doc_classes/VisualScriptWhile.xml4
-rw-r--r--modules/visual_script/doc_classes/VisualScriptYield.xml9
-rw-r--r--modules/visual_script/doc_classes/VisualScriptYieldSignal.xml15
-rw-r--r--modules/visual_script/editor/visual_script_editor.cpp (renamed from modules/visual_script/visual_script_editor.cpp)2981
-rw-r--r--modules/visual_script/editor/visual_script_editor.h (renamed from modules/visual_script/visual_script_editor.h)67
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.cpp (renamed from modules/visual_script/visual_script_property_selector.cpp)160
-rw-r--r--modules/visual_script/editor/visual_script_property_selector.h (renamed from modules/visual_script/visual_script_property_selector.h)4
-rw-r--r--modules/visual_script/icons/VisualScript.svg7
-rw-r--r--modules/visual_script/register_types.cpp117
-rw-r--r--modules/visual_script/register_types.h4
-rw-r--r--modules/visual_script/visual_script.cpp1307
-rw-r--r--modules/visual_script/visual_script.h263
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp240
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.h21
-rw-r--r--modules/visual_script/visual_script_expression.cpp41
-rw-r--r--modules/visual_script/visual_script_expression.h53
-rw-r--r--modules/visual_script/visual_script_flow_control.cpp40
-rw-r--r--modules/visual_script/visual_script_flow_control.h20
-rw-r--r--modules/visual_script/visual_script_func_nodes.cpp387
-rw-r--r--modules/visual_script/visual_script_func_nodes.h14
-rw-r--r--modules/visual_script/visual_script_nodes.cpp609
-rw-r--r--modules/visual_script/visual_script_nodes.h102
-rw-r--r--modules/visual_script/visual_script_yield_nodes.cpp54
-rw-r--r--modules/visual_script/visual_script_yield_nodes.h14
-rw-r--r--modules/vorbis/SCsub23
-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)74
-rw-r--r--modules/vorbis/config.py11
-rw-r--r--modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml20
-rw-r--r--modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml (renamed from modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml)6
-rw-r--r--modules/vorbis/register_types.cpp19
-rw-r--r--modules/vorbis/register_types.h4
-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)28
-rw-r--r--modules/webm/SCsub37
-rw-r--r--modules/webm/config.py19
-rw-r--r--modules/webm/doc_classes/VideoStreamWebm.xml32
-rw-r--r--modules/webm/libvpx/SCsub387
-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/SCsub16
-rw-r--r--modules/webp/image_loader_webp.cpp89
-rw-r--r--modules/webp/image_loader_webp.h4
-rw-r--r--modules/webp/register_types.cpp4
-rw-r--r--modules/webp/register_types.h4
-rw-r--r--modules/webrtc/SCsub9
-rw-r--r--modules/webrtc/config.py4
-rw-r--r--modules/webrtc/doc_classes/WebRTCDataChannel.xml39
-rw-r--r--modules/webrtc/doc_classes/WebRTCDataChannelExtension.xml106
-rw-r--r--modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml (renamed from modules/webrtc/doc_classes/WebRTCMultiplayer.xml)66
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnection.xml84
-rw-r--r--modules/webrtc/doc_classes/WebRTCPeerConnectionExtension.xml71
-rw-r--r--modules/webrtc/library_godot_webrtc.js438
-rw-r--r--modules/webrtc/register_types.cpp36
-rw-r--r--modules/webrtc/register_types.h4
-rw-r--r--modules/webrtc/webrtc_data_channel.cpp7
-rw-r--r--modules/webrtc/webrtc_data_channel.h6
-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)58
-rw-r--r--modules/webrtc/webrtc_data_channel_gdnative.cpp137
-rw-r--r--modules/webrtc/webrtc_data_channel_js.cpp261
-rw-r--r--modules/webrtc/webrtc_data_channel_js.h23
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.cpp (renamed from modules/webrtc/webrtc_multiplayer.cpp)241
-rw-r--r--modules/webrtc/webrtc_multiplayer_peer.h (renamed from modules/webrtc/webrtc_multiplayer.h)42
-rw-r--r--modules/webrtc/webrtc_peer_connection.cpp28
-rw-r--r--modules/webrtc/webrtc_peer_connection.h15
-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)51
-rw-r--r--modules/webrtc/webrtc_peer_connection_gdnative.cpp121
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.cpp253
-rw-r--r--modules/webrtc/webrtc_peer_connection_js.h30
-rw-r--r--modules/websocket/SCsub40
-rw-r--r--modules/websocket/doc_classes/WebSocketClient.xml52
-rw-r--r--modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml35
-rw-r--r--modules/websocket/doc_classes/WebSocketPeer.xml44
-rw-r--r--modules/websocket/doc_classes/WebSocketServer.xml86
-rw-r--r--modules/websocket/editor_debugger_server_websocket.cpp24
-rw-r--r--modules/websocket/editor_debugger_server_websocket.h15
-rw-r--r--modules/websocket/emws_client.cpp161
-rw-r--r--modules/websocket/emws_client.h23
-rw-r--r--modules/websocket/emws_peer.cpp78
-rw-r--r--modules/websocket/emws_peer.h35
-rw-r--r--modules/websocket/emws_server.cpp8
-rw-r--r--modules/websocket/emws_server.h8
-rw-r--r--modules/websocket/library_godot_websocket.js202
-rw-r--r--modules/websocket/packet_buffer.h11
-rw-r--r--modules/websocket/register_types.cpp10
-rw-r--r--modules/websocket/register_types.h4
-rw-r--r--modules/websocket/remote_debugger_peer_websocket.cpp6
-rw-r--r--modules/websocket/remote_debugger_peer_websocket.h10
-rw-r--r--modules/websocket/websocket_client.cpp62
-rw-r--r--modules/websocket/websocket_client.h10
-rw-r--r--modules/websocket/websocket_macros.h10
-rw-r--r--modules/websocket/websocket_multiplayer_peer.cpp110
-rw-r--r--modules/websocket/websocket_multiplayer_peer.h36
-rw-r--r--modules/websocket/websocket_peer.cpp5
-rw-r--r--modules/websocket/websocket_peer.h9
-rw-r--r--modules/websocket/websocket_server.cpp55
-rw-r--r--modules/websocket/websocket_server.h20
-rw-r--r--modules/websocket/wsl_client.cpp50
-rw-r--r--modules/websocket/wsl_client.h24
-rw-r--r--modules/websocket/wsl_peer.cpp30
-rw-r--r--modules/websocket/wsl_peer.h52
-rw-r--r--modules/websocket/wsl_server.cpp78
-rw-r--r--modules/websocket/wsl_server.h38
-rw-r--r--modules/webxr/SCsub11
-rw-r--r--modules/webxr/config.py14
-rw-r--r--modules/webxr/doc_classes/WebXRInterface.xml242
-rw-r--r--modules/webxr/godot_webxr.h85
-rw-r--r--modules/webxr/native/library_godot_webxr.js670
-rw-r--r--modules/webxr/native/webxr.externs.js499
-rw-r--r--modules/webxr/register_types.cpp66
-rw-r--r--modules/webxr/register_types.h (renamed from modules/arkit/register_types.h)14
-rw-r--r--modules/webxr/webxr_interface.cpp71
-rw-r--r--modules/webxr/webxr_interface.h65
-rw-r--r--modules/webxr/webxr_interface_js.cpp520
-rw-r--r--modules/webxr/webxr_interface_js.h105
-rw-r--r--modules/xatlas_unwrap/SCsub16
-rw-r--r--modules/xatlas_unwrap/register_types.cpp212
-rw-r--r--modules/xatlas_unwrap/register_types.h4
1333 files changed, 90991 insertions, 52712 deletions
diff --git a/modules/SCsub b/modules/SCsub
index edfc4ed9c6..5ff4623743 100644
--- a/modules/SCsub
+++ b/modules/SCsub
@@ -10,6 +10,7 @@ env_modules = env.Clone()
Export("env_modules")
# Header with MODULE_*_ENABLED defines.
+env.Depends("modules_enabled.gen.h", Value(env.module_list))
env.CommandNoCache(
"modules_enabled.gen.h",
Value(env.module_list),
@@ -23,6 +24,7 @@ env.CommandNoCache(
# Header to be included in `tests/test_main.cpp` to run module-specific tests.
if env["tests"]:
+ env.Depends("modules_tests.gen.h", Value(env.module_list))
env.CommandNoCache(
"modules_tests.gen.h",
Value(env.module_list),
@@ -45,11 +47,6 @@ for name, path in env.module_list.items():
else:
SConscript(path + "/SCsub") # Custom.
- # Some modules are not linked automatically but can be enabled optionally
- # on iOS, so we handle those specially.
- if env["platform"] == "iphone" and name in ["arkit", "camera"]:
- continue
-
lib = env_modules.add_library("module_%s" % name, env.modules_sources)
env.Prepend(LIBS=[lib])
if env["vsproj"]:
diff --git a/modules/arkit/SCsub b/modules/arkit/SCsub
deleted file mode 100644
index 7e103d6565..0000000000
--- a/modules/arkit/SCsub
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-env_arkit = env_modules.Clone()
-
-# (iOS) Enable module support
-env_arkit.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
-
-# (iOS) Build as separate static library
-modules_sources = []
-env_arkit.add_source_files(modules_sources, "*.cpp")
-env_arkit.add_source_files(modules_sources, "*.mm")
-mod_lib = env_modules.add_library("#bin/libgodot_arkit_module" + env["LIBSUFFIX"], modules_sources)
diff --git a/modules/arkit/arkit_interface.h b/modules/arkit/arkit_interface.h
deleted file mode 100644
index 29e09411ff..0000000000
--- a/modules/arkit/arkit_interface.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*************************************************************************/
-/* arkit_interface.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 ARKIT_INTERFACE_H
-#define ARKIT_INTERFACE_H
-
-#include "servers/camera/camera_feed.h"
-#include "servers/xr/xr_interface.h"
-#include "servers/xr/xr_positional_tracker.h"
-
-/**
- @author Bastiaan Olij <mux213@gmail.com>
-
- ARKit interface between iPhone and Godot
-*/
-
-// forward declaration for some needed objects
-class ARKitShader;
-
-#ifdef __OBJC__
-
-typedef ARAnchor GodotARAnchor;
-
-#else
-
-typedef void GodotARAnchor;
-#endif
-
-class ARKitInterface : public XRInterface {
- GDCLASS(ARKitInterface, XRInterface);
-
-private:
- bool initialized;
- bool session_was_started;
- bool plane_detection_is_enabled;
- bool light_estimation_is_enabled;
- real_t ambient_intensity;
- real_t ambient_color_temperature;
-
- Transform transform;
- CameraMatrix projection;
- float eye_height, z_near, z_far;
-
- Ref<CameraFeed> feed;
- size_t image_width[2];
- size_t image_height[2];
- Vector<uint8_t> img_data[2];
-
- struct anchor_map {
- XRPositionalTracker *tracker;
- unsigned char uuid[16];
- };
-
- ///@TODO should use memory map object from Godot?
- unsigned int num_anchors;
- unsigned int max_anchors;
- anchor_map *anchors;
- XRPositionalTracker *get_anchor_for_uuid(const unsigned char *p_uuid);
- void remove_anchor_for_uuid(const unsigned char *p_uuid);
- void remove_all_anchors();
-
-protected:
- static void _bind_methods();
-
-public:
- void start_session();
- void stop_session();
-
- bool get_anchor_detection_is_enabled() const override;
- void set_anchor_detection_is_enabled(bool p_enable) override;
- virtual int get_camera_feed_id() override;
-
- bool get_light_estimation_is_enabled() const;
- void set_light_estimation_is_enabled(bool p_enable);
-
- real_t get_ambient_intensity() const;
- real_t get_ambient_color_temperature() const;
-
- /* while Godot has its own raycast logic this takes ARKits camera into account and hits on any ARAnchor */
- Array raycast(Vector2 p_screen_coord);
-
- virtual void notification(int p_what) override;
-
- virtual StringName get_name() const override;
- virtual int get_capabilities() const override;
-
- virtual bool is_initialized() const override;
- virtual bool initialize() override;
- virtual void uninitialize() override;
-
- virtual Size2 get_render_targetsize() override;
- virtual bool is_stereo() override;
- virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) override;
- virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
- virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override;
-
- virtual void process() override;
-
- // called by delegate (void * because C++ and Obj-C don't always mix, should really change all platform/iphone/*.cpp files to .mm)
- void _add_or_update_anchor(GodotARAnchor *p_anchor);
- void _remove_anchor(GodotARAnchor *p_anchor);
-
- ARKitInterface();
- ~ARKitInterface();
-};
-
-#endif /* !ARKIT_INTERFACE_H */
diff --git a/modules/arkit/arkit_interface.mm b/modules/arkit/arkit_interface.mm
deleted file mode 100644
index e8fa023ac7..0000000000
--- a/modules/arkit/arkit_interface.mm
+++ /dev/null
@@ -1,791 +0,0 @@
-/*************************************************************************/
-/* arkit_interface.mm */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "core/input/input.h"
-#include "core/os/os.h"
-#include "scene/resources/surface_tool.h"
-#include "servers/rendering/rendering_server_globals.h"
-
-#import <ARKit/ARKit.h>
-#import <UIKit/UIKit.h>
-
-#include <dlfcn.h>
-
-#include "arkit_interface.h"
-#include "arkit_session_delegate.h"
-
-// just a dirty workaround for now, declare these as globals. I'll probably encapsulate ARSession and associated logic into an mm object and change ARKitInterface to a normal cpp object that consumes it.
-API_AVAILABLE(ios(11.0))
-ARSession *ar_session;
-
-ARKitSessionDelegate *ar_delegate;
-NSTimeInterval last_timestamp;
-
-/* this is called when we initialize or when we come back from having our app pushed to the background, just (re)start our session */
-void ARKitInterface::start_session() {
- // We're active...
- session_was_started = true;
-
- // Ignore this if we're not initialized...
- if (initialized) {
- print_line("Starting ARKit session");
-
- if (@available(iOS 11, *)) {
- Class ARWorldTrackingConfigurationClass = NSClassFromString(@"ARWorldTrackingConfiguration");
- ARWorldTrackingConfiguration *configuration = [ARWorldTrackingConfigurationClass new];
-
- configuration.lightEstimationEnabled = light_estimation_is_enabled;
- if (plane_detection_is_enabled) {
- if (@available(iOS 11.3, *)) {
- configuration.planeDetection = ARPlaneDetectionVertical | ARPlaneDetectionHorizontal;
- } else {
- configuration.planeDetection = ARPlaneDetectionHorizontal;
- }
- } else {
- configuration.planeDetection = 0;
- }
-
- // make sure our camera is on
- if (feed.is_valid()) {
- feed->set_active(true);
- }
-
- [ar_session runWithConfiguration:configuration];
- }
- }
-}
-
-void ARKitInterface::stop_session() {
- session_was_started = false;
-
- // Ignore this if we're not initialized...
- if (initialized) {
- // make sure our camera is off
- if (feed.is_valid()) {
- feed->set_active(false);
- }
-
- if (@available(iOS 11.0, *)) {
- [ar_session pause];
- }
- }
-}
-
-void ARKitInterface::notification(int p_what) {
- // TODO, this is not being called, need to find out why, possibly because this is not a node.
- // in that case we need to find a way to get these notifications!
- switch (p_what) {
- case DisplayServer::WINDOW_EVENT_FOCUS_IN: {
- print_line("Focus in");
-
- start_session();
- }; break;
- case DisplayServer::WINDOW_EVENT_FOCUS_OUT: {
- print_line("Focus out");
-
- stop_session();
- }; break;
- default:
- break;
- }
-}
-
-bool ARKitInterface::get_anchor_detection_is_enabled() const {
- return plane_detection_is_enabled;
-}
-
-void ARKitInterface::set_anchor_detection_is_enabled(bool p_enable) {
- if (plane_detection_is_enabled != p_enable) {
- plane_detection_is_enabled = p_enable;
-
- // Restart our session (this will be ignore if we're not initialised)
- if (session_was_started) {
- start_session();
- }
- }
-}
-
-int ARKitInterface::get_camera_feed_id() {
- if (feed.is_null()) {
- return 0;
- } else {
- return feed->get_id();
- }
-}
-
-bool ARKitInterface::get_light_estimation_is_enabled() const {
- return light_estimation_is_enabled;
-}
-
-void ARKitInterface::set_light_estimation_is_enabled(bool p_enable) {
- if (light_estimation_is_enabled != p_enable) {
- light_estimation_is_enabled = p_enable;
-
- // Restart our session (this will be ignore if we're not initialised)
- if (session_was_started) {
- start_session();
- }
- }
-}
-
-real_t ARKitInterface::get_ambient_intensity() const {
- return ambient_intensity;
-}
-
-real_t ARKitInterface::get_ambient_color_temperature() const {
- return ambient_color_temperature;
-}
-
-StringName ARKitInterface::get_name() const {
- return "ARKit";
-}
-
-int ARKitInterface::get_capabilities() const {
- return ARKitInterface::XR_MONO + ARKitInterface::XR_AR;
-}
-
-Array ARKitInterface::raycast(Vector2 p_screen_coord) {
- if (@available(iOS 11, *)) {
- Array arr;
- Size2 screen_size = DisplayServer::get_singleton()->screen_get_size();
- CGPoint point;
- point.x = p_screen_coord.x / screen_size.x;
- point.y = p_screen_coord.y / screen_size.y;
-
- ///@TODO maybe give more options here, for now we're taking just ARAchors into account that were found during plane detection keeping their size into account
-
- NSArray<ARHitTestResult *> *results = [ar_session.currentFrame hitTest:point types:ARHitTestResultTypeExistingPlaneUsingExtent];
-
- for (ARHitTestResult *result in results) {
- Transform transform;
-
- matrix_float4x4 m44 = result.worldTransform;
- transform.basis.elements[0].x = m44.columns[0][0];
- transform.basis.elements[1].x = m44.columns[0][1];
- transform.basis.elements[2].x = m44.columns[0][2];
- transform.basis.elements[0].y = m44.columns[1][0];
- transform.basis.elements[1].y = m44.columns[1][1];
- transform.basis.elements[2].y = m44.columns[1][2];
- transform.basis.elements[0].z = m44.columns[2][0];
- transform.basis.elements[1].z = m44.columns[2][1];
- transform.basis.elements[2].z = m44.columns[2][2];
- transform.origin.x = m44.columns[3][0];
- transform.origin.y = m44.columns[3][1];
- transform.origin.z = m44.columns[3][2];
-
- /* important, NOT scaled to world_scale !! */
- arr.push_back(transform);
- }
-
- return arr;
- } else {
- return Array();
- }
-}
-
-void ARKitInterface::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_notification", "what"), &ARKitInterface::_notification);
-
- ClassDB::bind_method(D_METHOD("set_light_estimation_is_enabled", "enable"), &ARKitInterface::set_light_estimation_is_enabled);
- ClassDB::bind_method(D_METHOD("get_light_estimation_is_enabled"), &ARKitInterface::get_light_estimation_is_enabled);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_estimation"), "set_light_estimation_is_enabled", "get_light_estimation_is_enabled");
-
- ClassDB::bind_method(D_METHOD("get_ambient_intensity"), &ARKitInterface::get_ambient_intensity);
- ClassDB::bind_method(D_METHOD("get_ambient_color_temperature"), &ARKitInterface::get_ambient_color_temperature);
-
- ClassDB::bind_method(D_METHOD("raycast", "screen_coord"), &ARKitInterface::raycast);
-}
-
-bool ARKitInterface::is_stereo() {
- // this is a mono device...
- return false;
-}
-
-bool ARKitInterface::is_initialized() const {
- return initialized;
-}
-
-bool ARKitInterface::initialize() {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, false);
-
- if (@available(iOS 11, *)) {
- if (!initialized) {
- print_line("initializing ARKit");
-
- // create our ar session and delegate
- Class ARSessionClass = NSClassFromString(@"ARSession");
- if (ARSessionClass == Nil) {
- void *arkit_handle = dlopen("/System/Library/Frameworks/ARKit.framework/ARKit", RTLD_NOW);
- if (arkit_handle) {
- ARSessionClass = NSClassFromString(@"ARSession");
- } else {
- print_line("ARKit init failed");
- return false;
- }
- }
- ar_session = [ARSessionClass new];
- ar_delegate = [ARKitSessionDelegate new];
- ar_delegate.arkit_interface = this;
- ar_session.delegate = ar_delegate;
-
- // reset our transform
- transform = Transform();
-
- // make this our primary interface
- xr_server->set_primary_interface(this);
-
- // make sure we have our feed setup
- if (feed.is_null()) {
- feed.instance();
- feed->set_name("ARKit");
-
- CameraServer *cs = CameraServer::get_singleton();
- if (cs != NULL) {
- cs->add_feed(feed);
- }
- }
- feed->set_active(true);
-
- // yeah!
- initialized = true;
-
- // Start our session...
- start_session();
- }
-
- return true;
- } else {
- return false;
- }
-}
-
-void ARKitInterface::uninitialize() {
- if (initialized) {
- XRServer *xr_server = XRServer::get_singleton();
- if (xr_server != NULL) {
- // no longer our primary interface
- xr_server->clear_primary_interface_if(this);
- }
-
- if (feed.is_valid()) {
- CameraServer *cs = CameraServer::get_singleton();
- if ((cs != NULL)) {
- cs->remove_feed(feed);
- }
- feed.unref();
- }
-
- remove_all_anchors();
-
- if (@available(iOS 11.0, *)) {
- ar_session = nil;
- }
-
- ar_delegate = nil;
- initialized = false;
- session_was_started = false;
- }
-}
-
-Size2 ARKitInterface::get_render_targetsize() {
- // _THREAD_SAFE_METHOD_
-
- Size2 target_size = DisplayServer::get_singleton()->screen_get_size();
-
- return target_size;
-}
-
-Transform ARKitInterface::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) {
- // _THREAD_SAFE_METHOD_
-
- Transform transform_for_eye;
-
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, transform_for_eye);
-
- if (initialized) {
- float world_scale = xr_server->get_world_scale();
-
- // just scale our origin point of our transform, note that we really shouldn't be using world_scale in ARKit but....
- transform_for_eye = transform;
- transform_for_eye.origin *= world_scale;
-
- transform_for_eye = p_cam_transform * xr_server->get_reference_frame() * transform_for_eye;
- } else {
- // huh? well just return what we got....
- transform_for_eye = p_cam_transform;
- }
-
- return transform_for_eye;
-}
-
-CameraMatrix ARKitInterface::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) {
- // Remember our near and far, it will be used in process when we obtain our projection from our ARKit session.
- z_near = p_z_near;
- z_far = p_z_far;
-
- return projection;
-}
-
-void ARKitInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) {
- // _THREAD_SAFE_METHOD_
-
- // We must have a valid render target
- ERR_FAIL_COND(!p_render_target.is_valid());
-
- // Because we are rendering to our device we must use our main viewport!
- ERR_FAIL_COND(p_screen_rect == Rect2());
-
- // get the size of our screen
- // Rect2 screen_rect = p_screen_rect;
-
- // screen_rect.position.x += screen_rect.size.x;
- // screen_rect.size.x = -screen_rect.size.x;
- // screen_rect.position.y += screen_rect.size.y;
- // screen_rect.size.y = -screen_rect.size.y;
-
- // VSG::rasterizer->set_current_render_target(RID());
- // VSG::rasterizer->blit_render_target_to_screen(p_render_target, screen_rect, 0);
-}
-
-XRPositionalTracker *ARKitInterface::get_anchor_for_uuid(const unsigned char *p_uuid) {
- if (anchors == NULL) {
- num_anchors = 0;
- max_anchors = 10;
- anchors = (anchor_map *)malloc(sizeof(anchor_map) * max_anchors);
- }
-
- ERR_FAIL_NULL_V(anchors, NULL);
-
- for (unsigned int i = 0; i < num_anchors; i++) {
- if (memcmp(anchors[i].uuid, p_uuid, 16) == 0) {
- return anchors[i].tracker;
- }
- }
-
- if (num_anchors + 1 == max_anchors) {
- max_anchors += 10;
- anchors = (anchor_map *)realloc(anchors, sizeof(anchor_map) * max_anchors);
- ERR_FAIL_NULL_V(anchors, NULL);
- }
-
- XRPositionalTracker *new_tracker = memnew(XRPositionalTracker);
- new_tracker->set_type(XRServer::TRACKER_ANCHOR);
-
- char tracker_name[256];
- sprintf(tracker_name, "Anchor %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", p_uuid[0], p_uuid[1], p_uuid[2], p_uuid[3], p_uuid[4], p_uuid[5], p_uuid[6], p_uuid[7], p_uuid[8], p_uuid[9], p_uuid[10], p_uuid[11], p_uuid[12], p_uuid[13], p_uuid[14], p_uuid[15]);
-
- String name = tracker_name;
- print_line("Adding tracker " + name);
- new_tracker->set_name(name);
-
- // add our tracker
- XRServer::get_singleton()->add_tracker(new_tracker);
- anchors[num_anchors].tracker = new_tracker;
- memcpy(anchors[num_anchors].uuid, p_uuid, 16);
- num_anchors++;
-
- return new_tracker;
-}
-
-void ARKitInterface::remove_anchor_for_uuid(const unsigned char *p_uuid) {
- if (anchors != NULL) {
- for (unsigned int i = 0; i < num_anchors; i++) {
- if (memcmp(anchors[i].uuid, p_uuid, 16) == 0) {
- // remove our tracker
- XRServer::get_singleton()->remove_tracker(anchors[i].tracker);
- memdelete(anchors[i].tracker);
-
- // bring remaining forward
- for (unsigned int j = i + 1; j < num_anchors; j++) {
- anchors[j - 1] = anchors[j];
- };
-
- // decrease count
- num_anchors--;
- return;
- }
- }
- }
-}
-
-void ARKitInterface::remove_all_anchors() {
- if (anchors != NULL) {
- for (unsigned int i = 0; i < num_anchors; i++) {
- // remove our tracker
- XRServer::get_singleton()->remove_tracker(anchors[i].tracker);
- memdelete(anchors[i].tracker);
- };
-
- free(anchors);
- anchors = NULL;
- num_anchors = 0;
- }
-}
-
-void ARKitInterface::process() {
- // _THREAD_SAFE_METHOD_
-
- if (@available(iOS 11.0, *)) {
- if (initialized) {
- // get our next ARFrame
- ARFrame *current_frame = ar_session.currentFrame;
- if (last_timestamp != current_frame.timestamp) {
- // only process if we have a new frame
- last_timestamp = current_frame.timestamp;
-
- // get some info about our screen and orientation
- Size2 screen_size = DisplayServer::get_singleton()->screen_get_size();
- UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
-
- if (@available(iOS 13, *)) {
- orientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
-#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
- } else {
- orientation = [[UIApplication sharedApplication] statusBarOrientation];
-#endif
- }
-
- // Grab our camera image for our backbuffer
- CVPixelBufferRef pixelBuffer = current_frame.capturedImage;
- if ((CVPixelBufferGetPlaneCount(pixelBuffer) == 2) && (feed != NULL)) {
- // Plane 0 is our Y and Plane 1 is our CbCr buffer
-
- // ignored, we check each plane separately
- // image_width = CVPixelBufferGetWidth(pixelBuffer);
- // image_height = CVPixelBufferGetHeight(pixelBuffer);
-
- // printf("Pixel buffer %i - %i\n", image_width, image_height);
-
- CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
-
- // get our buffers
- unsigned char *dataY = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
- unsigned char *dataCbCr = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
-
- if (dataY == NULL) {
- print_line("Couldn't access Y pixel buffer data");
- } else if (dataCbCr == NULL) {
- print_line("Couldn't access CbCr pixel buffer data");
- } else {
- Ref<Image> img[2];
- size_t extraLeft, extraRight, extraTop, extraBottom;
-
- CVPixelBufferGetExtendedPixels(pixelBuffer, &extraLeft, &extraRight, &extraTop, &extraBottom);
-
- {
- // do Y
- size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
- size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
- size_t bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
-
- if ((image_width[0] != new_width) || (image_height[0] != new_height)) {
- printf("- Camera padding l:%lu r:%lu t:%lu b:%lu\n", extraLeft, extraRight, extraTop, extraBottom);
- printf("- Camera Y plane size: %lu, %lu - %lu\n", new_width, new_height, bytes_per_row);
-
- image_width[0] = new_width;
- image_height[0] = new_height;
- img_data[0].resize(new_width * new_height);
- }
-
- uint8_t *w = img_data[0].ptrw();
- if (new_width == bytes_per_row) {
- memcpy(w, dataY, new_width * new_height);
- } else {
- size_t offset_a = 0;
- size_t offset_b = extraLeft + (extraTop * bytes_per_row);
- for (size_t r = 0; r < new_height; r++) {
- memcpy(w + offset_a, dataY + offset_b, new_width);
- offset_a += new_width;
- offset_b += bytes_per_row;
- }
- }
-
- img[0].instance();
- img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]);
- }
-
- {
- // do CbCr
- size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
- size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
- size_t bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
-
- if ((image_width[1] != new_width) || (image_height[1] != new_height)) {
- printf("- Camera CbCr plane size: %lu, %lu - %lu\n", new_width, new_height, bytes_per_row);
-
- image_width[1] = new_width;
- image_height[1] = new_height;
- img_data[1].resize(2 * new_width * new_height);
- }
-
- uint8_t *w = img_data[1].ptrw();
- if ((2 * new_width) == bytes_per_row) {
- memcpy(w, dataCbCr, 2 * new_width * new_height);
- } else {
- size_t offset_a = 0;
- size_t offset_b = extraLeft + (extraTop * bytes_per_row);
- for (size_t r = 0; r < new_height; r++) {
- memcpy(w + offset_a, dataCbCr + offset_b, 2 * new_width);
- offset_a += 2 * new_width;
- offset_b += bytes_per_row;
- }
- }
-
- img[1].instance();
- img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]);
- }
-
- // set our texture...
- feed->set_YCbCr_imgs(img[0], img[1]);
-
- // now build our transform to display this as a background image that matches our camera
- CGAffineTransform affine_transform = [current_frame displayTransformForOrientation:orientation viewportSize:CGSizeMake(screen_size.width, screen_size.height)];
-
- // we need to invert this, probably row v.s. column notation
- affine_transform = CGAffineTransformInvert(affine_transform);
-
- if (orientation != UIInterfaceOrientationPortrait) {
- affine_transform.b = -affine_transform.b;
- affine_transform.d = -affine_transform.d;
- affine_transform.ty = 1.0 - affine_transform.ty;
- } else {
- affine_transform.c = -affine_transform.c;
- affine_transform.a = -affine_transform.a;
- affine_transform.tx = 1.0 - affine_transform.tx;
- }
-
- Transform2D display_transform = Transform2D(
- affine_transform.a, affine_transform.b,
- affine_transform.c, affine_transform.d,
- affine_transform.tx, affine_transform.ty);
-
- feed->set_transform(display_transform);
- }
-
- // and unlock
- CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
- }
-
- // Record light estimation to apply to our scene
- if (light_estimation_is_enabled) {
- ambient_intensity = current_frame.lightEstimate.ambientIntensity;
-
- ///@TODO it's there, but not there.. what to do with this...
- // https://developer.apple.com/documentation/arkit/arlightestimate?language=objc
- // ambient_color_temperature = current_frame.lightEstimate.ambientColorTemperature;
- }
-
- // Process our camera
- ARCamera *camera = current_frame.camera;
-
- // strangely enough we have to states, rolling them up into one
- if (camera.trackingState == ARTrackingStateNotAvailable) {
- // no tracking, would be good if we black out the screen or something...
- tracking_state = XRInterface::XR_NOT_TRACKING;
- } else {
- if (camera.trackingState == ARTrackingStateNormal) {
- tracking_state = XRInterface::XR_NORMAL_TRACKING;
- } else if (camera.trackingStateReason == ARTrackingStateReasonExcessiveMotion) {
- tracking_state = XRInterface::XR_EXCESSIVE_MOTION;
- } else if (camera.trackingStateReason == ARTrackingStateReasonInsufficientFeatures) {
- tracking_state = XRInterface::XR_INSUFFICIENT_FEATURES;
- } else {
- tracking_state = XRInterface::XR_UNKNOWN_TRACKING;
- }
-
- // copy our current frame transform
- matrix_float4x4 m44 = camera.transform;
- if (orientation == UIInterfaceOrientationLandscapeLeft) {
- transform.basis.elements[0].x = m44.columns[0][0];
- transform.basis.elements[1].x = m44.columns[0][1];
- transform.basis.elements[2].x = m44.columns[0][2];
- transform.basis.elements[0].y = m44.columns[1][0];
- transform.basis.elements[1].y = m44.columns[1][1];
- transform.basis.elements[2].y = m44.columns[1][2];
- } else if (orientation == UIInterfaceOrientationPortrait) {
- transform.basis.elements[0].x = m44.columns[1][0];
- transform.basis.elements[1].x = m44.columns[1][1];
- transform.basis.elements[2].x = m44.columns[1][2];
- transform.basis.elements[0].y = -m44.columns[0][0];
- transform.basis.elements[1].y = -m44.columns[0][1];
- transform.basis.elements[2].y = -m44.columns[0][2];
- } else if (orientation == UIInterfaceOrientationLandscapeRight) {
- transform.basis.elements[0].x = -m44.columns[0][0];
- transform.basis.elements[1].x = -m44.columns[0][1];
- transform.basis.elements[2].x = -m44.columns[0][2];
- transform.basis.elements[0].y = -m44.columns[1][0];
- transform.basis.elements[1].y = -m44.columns[1][1];
- transform.basis.elements[2].y = -m44.columns[1][2];
- } else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
- // this may not be correct
- transform.basis.elements[0].x = m44.columns[1][0];
- transform.basis.elements[1].x = m44.columns[1][1];
- transform.basis.elements[2].x = m44.columns[1][2];
- transform.basis.elements[0].y = m44.columns[0][0];
- transform.basis.elements[1].y = m44.columns[0][1];
- transform.basis.elements[2].y = m44.columns[0][2];
- }
- transform.basis.elements[0].z = m44.columns[2][0];
- transform.basis.elements[1].z = m44.columns[2][1];
- transform.basis.elements[2].z = m44.columns[2][2];
- transform.origin.x = m44.columns[3][0];
- transform.origin.y = m44.columns[3][1];
- transform.origin.z = m44.columns[3][2];
-
- // copy our current frame projection, investigate using projectionMatrixWithViewportSize:orientation:zNear:zFar: so we can set our own near and far
- m44 = [camera projectionMatrixForOrientation:orientation viewportSize:CGSizeMake(screen_size.width, screen_size.height) zNear:z_near zFar:z_far];
- projection.matrix[0][0] = m44.columns[0][0];
- projection.matrix[1][0] = m44.columns[1][0];
- projection.matrix[2][0] = m44.columns[2][0];
- projection.matrix[3][0] = m44.columns[3][0];
- projection.matrix[0][1] = m44.columns[0][1];
- projection.matrix[1][1] = m44.columns[1][1];
- projection.matrix[2][1] = m44.columns[2][1];
- projection.matrix[3][1] = m44.columns[3][1];
- projection.matrix[0][2] = m44.columns[0][2];
- projection.matrix[1][2] = m44.columns[1][2];
- projection.matrix[2][2] = m44.columns[2][2];
- projection.matrix[3][2] = m44.columns[3][2];
- projection.matrix[0][3] = m44.columns[0][3];
- projection.matrix[1][3] = m44.columns[1][3];
- projection.matrix[2][3] = m44.columns[2][3];
- projection.matrix[3][3] = m44.columns[3][3];
- }
- }
- }
- }
-}
-
-void ARKitInterface::_add_or_update_anchor(GodotARAnchor *p_anchor) {
- // _THREAD_SAFE_METHOD_
-
- if (@available(iOS 11.0, *)) {
- ARAnchor *anchor = (ARAnchor *)p_anchor;
-
- unsigned char uuid[16];
- [anchor.identifier getUUIDBytes:uuid];
-
- XRPositionalTracker *tracker = get_anchor_for_uuid(uuid);
- if (tracker != NULL) {
- // lets update our mesh! (using Arjens code as is for now)
- // we should also probably limit how often we do this...
-
- // can we safely cast this?
- ARPlaneAnchor *planeAnchor = (ARPlaneAnchor *)anchor;
-
- if (@available(iOS 11.3, *)) {
- if (planeAnchor.geometry.triangleCount > 0) {
- Ref<SurfaceTool> surftool;
- surftool.instance();
- surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
-
- for (int j = planeAnchor.geometry.triangleCount * 3 - 1; j >= 0; j--) {
- int16_t index = planeAnchor.geometry.triangleIndices[j];
- simd_float3 vrtx = planeAnchor.geometry.vertices[index];
- simd_float2 textcoord = planeAnchor.geometry.textureCoordinates[index];
- surftool->add_uv(Vector2(textcoord[0], textcoord[1]));
- surftool->add_color(Color(0.8, 0.8, 0.8));
- surftool->add_vertex(Vector3(vrtx[0], vrtx[1], vrtx[2]));
- }
-
- surftool->generate_normals();
- tracker->set_mesh(surftool->commit());
- } else {
- Ref<Mesh> nomesh;
- tracker->set_mesh(nomesh);
- }
- } else {
- Ref<Mesh> nomesh;
- tracker->set_mesh(nomesh);
- }
-
- // Note, this also contains a scale factor which gives us an idea of the size of the anchor
- // We may extract that in our XRAnchor class
- Basis b;
- matrix_float4x4 m44 = anchor.transform;
- b.elements[0].x = m44.columns[0][0];
- b.elements[1].x = m44.columns[0][1];
- b.elements[2].x = m44.columns[0][2];
- b.elements[0].y = m44.columns[1][0];
- b.elements[1].y = m44.columns[1][1];
- b.elements[2].y = m44.columns[1][2];
- b.elements[0].z = m44.columns[2][0];
- b.elements[1].z = m44.columns[2][1];
- b.elements[2].z = m44.columns[2][2];
- tracker->set_orientation(b);
- tracker->set_rw_position(Vector3(m44.columns[3][0], m44.columns[3][1], m44.columns[3][2]));
- }
- }
-}
-
-void ARKitInterface::_remove_anchor(GodotARAnchor *p_anchor) {
- // _THREAD_SAFE_METHOD_
-
- if (@available(iOS 11.0, *)) {
- ARAnchor *anchor = (ARAnchor *)p_anchor;
-
- unsigned char uuid[16];
- [anchor.identifier getUUIDBytes:uuid];
-
- remove_anchor_for_uuid(uuid);
- }
-}
-
-ARKitInterface::ARKitInterface() {
- initialized = false;
- session_was_started = false;
- plane_detection_is_enabled = false;
- light_estimation_is_enabled = false;
- if (@available(iOS 11.0, *)) {
- ar_session = nil;
- }
- z_near = 0.01;
- z_far = 1000.0;
- projection.set_perspective(60.0, 1.0, z_near, z_far, false);
- anchors = NULL;
- num_anchors = 0;
- ambient_intensity = 1.0;
- ambient_color_temperature = 1.0;
- image_width[0] = 0;
- image_width[1] = 0;
- image_height[0] = 0;
- image_height[1] = 0;
-}
-
-ARKitInterface::~ARKitInterface() {
- remove_all_anchors();
-
- // and make sure we cleanup if we haven't already
- if (is_initialized()) {
- uninitialize();
- }
-}
diff --git a/modules/arkit/config.py b/modules/arkit/config.py
deleted file mode 100644
index e68603fc93..0000000000
--- a/modules/arkit/config.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def can_build(env, platform):
- return platform == "iphone"
-
-
-def configure(env):
- pass
diff --git a/modules/assimp/SCsub b/modules/assimp/SCsub
deleted file mode 100644
index f1d0c742b4..0000000000
--- a/modules/assimp/SCsub
+++ /dev/null
@@ -1,94 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-env_assimp = env_modules.Clone()
-
-# Force bundled version for now, there's no released version of Assimp with
-# support for ArmaturePopulate which we use from their master branch.
-if True: # env['builtin_assimp']:
- thirdparty_dir = "#thirdparty/assimp"
-
- env_assimp.Prepend(CPPPATH=["#thirdparty/assimp"])
- env_assimp.Prepend(CPPPATH=["#thirdparty/assimp/code"])
- env_assimp.Prepend(CPPPATH=["#thirdparty/assimp/include"])
-
- # env_assimp.Append(CPPDEFINES=['ASSIMP_DOUBLE_PRECISION']) # TODO default to what godot is compiled with for future double support
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_SINGLETHREADED"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_BOOST_WORKAROUND"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OWN_ZLIB"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_EXPORT"])
-
- # Importers we don't need
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3D_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3DS_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_3MF_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_AC_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_AMF_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_ASE_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_ASSBIN_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_B3D_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_BLEND_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_BVH_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_C4D_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_COB_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_COLLADA_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_CSM_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_DXF_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_GLTF2_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_GLTF_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_HMP_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IFC_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IRR_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_IRRMESH_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_LWO_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_LWS_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_M3D_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD2_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD3_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD5_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MD5_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MDC_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MDL_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MMD_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_MS3D_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_NDO_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_NFF_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OBJ_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OFF_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OGRE_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_OPENGEX_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_PLY_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_Q3BSP_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_Q3D_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_RAW_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_SIB_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_SMD_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_STEP_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_STL_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_TERRAGEN_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_X3D_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_XGL_IMPORTER"])
- env_assimp.Append(CPPDEFINES=["ASSIMP_BUILD_NO_X_IMPORTER"])
-
- if env["platform"] == "windows":
- env_assimp.Append(CPPDEFINES=["PLATFORM_WINDOWS"])
- env_assimp.Append(CPPDEFINES=[("PLATFORM", "WINDOWS")])
- elif env["platform"] == "linuxbsd":
- env_assimp.Append(CPPDEFINES=["PLATFORM_LINUX"])
- env_assimp.Append(CPPDEFINES=[("PLATFORM", "LINUX")])
- elif env["platform"] == "osx":
- env_assimp.Append(CPPDEFINES=["PLATFORM_DARWIN"])
- env_assimp.Append(CPPDEFINES=[("PLATFORM", "DARWIN")])
-
- env_thirdparty = env_assimp.Clone()
- env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/CApi/*.cpp"))
- env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/Common/*.cpp"))
- env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/PostProcessing/*.cpp"))
- env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/Material/*.cpp"))
- env_thirdparty.add_source_files(env.modules_sources, Glob("#thirdparty/assimp/code/FBX/*.cpp"))
-
-# Godot's own source files
-env_assimp.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp
deleted file mode 100644
index e5becfd559..0000000000
--- a/modules/assimp/editor_scene_importer_assimp.cpp
+++ /dev/null
@@ -1,1486 +0,0 @@
-/*************************************************************************/
-/* editor_scene_importer_assimp.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "editor_scene_importer_assimp.h"
-#include "core/io/image_loader.h"
-#include "editor/import/resource_importer_scene.h"
-#include "import_utils.h"
-#include "scene/3d/camera_3d.h"
-#include "scene/3d/light_3d.h"
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/main/node.h"
-#include "scene/resources/material.h"
-#include "scene/resources/surface_tool.h"
-
-#include <assimp/matrix4x4.h>
-#include <assimp/postprocess.h>
-#include <assimp/scene.h>
-#include <assimp/Importer.hpp>
-#include <assimp/LogStream.hpp>
-
-// move into assimp
-aiBone *get_bone_by_name(const aiScene *scene, aiString bone_name) {
- for (unsigned int mesh_id = 0; mesh_id < scene->mNumMeshes; ++mesh_id) {
- aiMesh *mesh = scene->mMeshes[mesh_id];
-
- // iterate over all the bones on the mesh for this node only!
- for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
- aiBone *bone = mesh->mBones[boneIndex];
- if (bone->mName == bone_name) {
- printf("matched bone by name: %s\n", bone->mName.C_Str());
- return bone;
- }
- }
- }
-
- return nullptr;
-}
-
-void EditorSceneImporterAssimp::get_extensions(List<String> *r_extensions) const {
- const String import_setting_string = "filesystem/import/open_asset_import/";
-
- Map<String, ImportFormat> import_format;
- {
- Vector<String> exts;
- exts.push_back("fbx");
- ImportFormat import = { exts, true };
- import_format.insert("fbx", import);
- }
- for (Map<String, ImportFormat>::Element *E = import_format.front(); E; E = E->next()) {
- _register_project_setting_import(E->key(), import_setting_string, E->get().extensions, r_extensions,
- E->get().is_default);
- }
-}
-
-void EditorSceneImporterAssimp::_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 {
- const String use_generic = "use_" + generic;
- _GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true);
- if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) {
- for (int32_t i = 0; i < exts.size(); i++) {
- r_extensions->push_back(exts[i]);
- }
- }
-}
-
-uint32_t EditorSceneImporterAssimp::get_import_flags() const {
- return IMPORT_SCENE;
-}
-
-void EditorSceneImporterAssimp::_bind_methods() {
-}
-
-Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps,
- List<String> *r_missing_deps, Error *r_err) {
- Assimp::Importer importer;
- importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true);
- // Cannot remove pivot points because the static mesh will be in the wrong place
- importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
- int32_t max_bone_weights = 4;
- //if (p_flags & IMPORT_ANIMATION_EIGHT_WEIGHTS) {
- // const int eight_bones = 8;
- // importer.SetPropertyBool(AI_CONFIG_PP_LBW_MAX_WEIGHTS, eight_bones);
- // max_bone_weights = eight_bones;
- //}
-
- importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT);
-
- //importer.SetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD, 1.0f);
- int32_t post_process_Steps = aiProcess_CalcTangentSpace |
- aiProcess_GlobalScale |
- // imports models and listens to their file scale for CM to M conversions
- //aiProcess_FlipUVs |
- aiProcess_FlipWindingOrder |
- // very important for culling so that it is done in the correct order.
- //aiProcess_DropNormals |
- //aiProcess_GenSmoothNormals |
- //aiProcess_JoinIdenticalVertices |
- aiProcess_ImproveCacheLocality |
- //aiProcess_RemoveRedundantMaterials | // Causes a crash
- //aiProcess_SplitLargeMeshes |
- aiProcess_Triangulate |
- aiProcess_GenUVCoords |
- //aiProcess_FindDegenerates |
- //aiProcess_SortByPType |
- // aiProcess_FindInvalidData |
- aiProcess_TransformUVCoords |
- aiProcess_FindInstances |
- //aiProcess_FixInfacingNormals |
- //aiProcess_ValidateDataStructure |
- aiProcess_OptimizeMeshes |
- aiProcess_PopulateArmatureData |
- //aiProcess_OptimizeGraph |
- //aiProcess_Debone |
- // aiProcess_EmbedTextures |
- //aiProcess_SplitByBoneCount |
- 0;
- String g_path = ProjectSettings::get_singleton()->globalize_path(p_path);
- aiScene *scene = (aiScene *)importer.ReadFile(g_path.utf8().ptr(), post_process_Steps);
-
- ERR_FAIL_COND_V_MSG(scene == nullptr, nullptr, String("Open Asset Import failed to open: ") + String(importer.GetErrorString()));
-
- return _generate_scene(p_path, scene, p_flags, p_bake_fps, max_bone_weights);
-}
-
-template <class T>
-struct EditorSceneImporterAssetImportInterpolate {
- T lerp(const T &a, const T &b, float c) const {
- return a + (b - a) * c;
- }
-
- T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) {
- float t2 = t * t;
- float t3 = t2 * t;
-
- return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
- }
-
- T bezier(T start, T control_1, T control_2, T end, float t) {
- /* Formula from Wikipedia article on Bezier curves. */
- real_t omt = (1.0 - t);
- real_t omt2 = omt * omt;
- real_t omt3 = omt2 * omt;
- real_t t2 = t * t;
- real_t t3 = t2 * t;
-
- return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
- }
-};
-
-//thank you for existing, partial specialization
-template <>
-struct EditorSceneImporterAssetImportInterpolate<Quat> {
- Quat lerp(const Quat &a, const Quat &b, float c) const {
- ERR_FAIL_COND_V_MSG(!a.is_normalized(), Quat(), "The quaternion \"a\" must be normalized.");
- ERR_FAIL_COND_V_MSG(!b.is_normalized(), Quat(), "The quaternion \"b\" must be normalized.");
-
- return a.slerp(b, c).normalized();
- }
-
- Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, float c) {
- ERR_FAIL_COND_V_MSG(!p1.is_normalized(), Quat(), "The quaternion \"p1\" must be normalized.");
- ERR_FAIL_COND_V_MSG(!p2.is_normalized(), Quat(), "The quaternion \"p2\" must be normalized.");
-
- return p1.slerp(p2, c).normalized();
- }
-
- Quat bezier(Quat start, Quat control_1, Quat control_2, Quat end, float t) {
- ERR_FAIL_COND_V_MSG(!start.is_normalized(), Quat(), "The start quaternion must be normalized.");
- ERR_FAIL_COND_V_MSG(!end.is_normalized(), Quat(), "The end quaternion must be normalized.");
-
- return start.slerp(end, t).normalized();
- }
-};
-
-template <class T>
-T EditorSceneImporterAssimp::_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;
- for (int i = 0; i < p_times.size(); i++) {
- if (p_times[i] > p_time) {
- break;
- }
- idx++;
- }
-
- EditorSceneImporterAssetImportInterpolate<T> interp;
-
- switch (p_interp) {
- case AssetImportAnimation::INTERP_LINEAR: {
- if (idx == -1) {
- return p_values[0];
- } else if (idx >= p_times.size() - 1) {
- return p_values[p_times.size() - 1];
- }
-
- float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
-
- return interp.lerp(p_values[idx], p_values[idx + 1], c);
-
- } break;
- case AssetImportAnimation::INTERP_STEP: {
- if (idx == -1) {
- return p_values[0];
- } else if (idx >= p_times.size() - 1) {
- return p_values[p_times.size() - 1];
- }
-
- return p_values[idx];
-
- } break;
- case AssetImportAnimation::INTERP_CATMULLROMSPLINE: {
- if (idx == -1) {
- return p_values[1];
- } else if (idx >= p_times.size() - 1) {
- return p_values[1 + p_times.size() - 1];
- }
-
- float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
-
- return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c);
-
- } break;
- case AssetImportAnimation::INTERP_CUBIC_SPLINE: {
- if (idx == -1) {
- return p_values[1];
- } else if (idx >= p_times.size() - 1) {
- return p_values[(p_times.size() - 1) * 3 + 1];
- }
-
- float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
-
- T from = p_values[idx * 3 + 1];
- T c1 = from + p_values[idx * 3 + 2];
- T to = p_values[idx * 3 + 4];
- T c2 = to + p_values[idx * 3 + 3];
-
- return interp.bezier(from, c1, c2, to, c);
-
- } break;
- }
-
- ERR_FAIL_V(p_values[0]);
-}
-
-aiBone *EditorSceneImporterAssimp::get_bone_from_stack(ImportState &state, aiString name) {
- List<aiBone *>::Element *iter;
- aiBone *bone = nullptr;
- for (iter = state.bone_stack.front(); iter; iter = iter->next()) {
- bone = (aiBone *)iter->get();
-
- if (bone && bone->mName == name) {
- state.bone_stack.erase(bone);
- return bone;
- }
- }
-
- return nullptr;
-}
-
-Node3D *
-EditorSceneImporterAssimp::_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps,
- const int32_t p_max_bone_weights) {
- ERR_FAIL_COND_V(scene == nullptr, nullptr);
-
- ImportState state;
- state.path = p_path;
- state.assimp_scene = scene;
- state.max_bone_weights = p_max_bone_weights;
- state.animation_player = nullptr;
- state.import_flags = p_flags;
-
- // populate light map
- for (unsigned int l = 0; l < scene->mNumLights; l++) {
- aiLight *ai_light = scene->mLights[l];
- ERR_CONTINUE(ai_light == nullptr);
- state.light_cache[AssimpUtils::get_assimp_string(ai_light->mName)] = l;
- }
-
- // fill camera cache
- for (unsigned int c = 0; c < scene->mNumCameras; c++) {
- aiCamera *ai_camera = scene->mCameras[c];
- ERR_CONTINUE(ai_camera == nullptr);
- state.camera_cache[AssimpUtils::get_assimp_string(ai_camera->mName)] = c;
- }
-
- if (scene->mRootNode) {
- state.nodes.push_back(scene->mRootNode);
-
- // make flat node tree - in order to make processing deterministic
- for (unsigned int i = 0; i < scene->mRootNode->mNumChildren; i++) {
- _generate_node(state, scene->mRootNode->mChildren[i]);
- }
-
- RegenerateBoneStack(state);
-
- Node *last_valid_parent = nullptr;
-
- List<const aiNode *>::Element *iter;
- for (iter = state.nodes.front(); iter; iter = iter->next()) {
- const aiNode *element_assimp_node = iter->get();
- const aiNode *parent_assimp_node = element_assimp_node->mParent;
-
- String node_name = AssimpUtils::get_assimp_string(element_assimp_node->mName);
- //print_verbose("node: " + node_name);
-
- Node3D *spatial = nullptr;
- Transform transform = AssimpUtils::assimp_matrix_transform(element_assimp_node->mTransformation);
-
- // retrieve this node bone
- aiBone *bone = get_bone_from_stack(state, element_assimp_node->mName);
-
- if (state.light_cache.has(node_name)) {
- spatial = create_light(state, node_name, transform);
- } else if (state.camera_cache.has(node_name)) {
- spatial = create_camera(state, node_name, transform);
- } else if (state.armature_nodes.find(element_assimp_node)) {
- // create skeleton
- print_verbose("Making skeleton: " + node_name);
- Skeleton3D *skeleton = memnew(Skeleton3D);
- spatial = skeleton;
- if (!state.armature_skeletons.has(element_assimp_node)) {
- state.armature_skeletons.insert(element_assimp_node, skeleton);
- }
- } else if (bone != nullptr) {
- continue;
- } else {
- spatial = memnew(Node3D);
- }
-
- ERR_CONTINUE_MSG(spatial == nullptr, "FBX Import - are we out of ram?");
- // we on purpose set the transform and name after creating the node.
-
- spatial->set_name(node_name);
- spatial->set_global_transform(transform);
-
- // first element is root
- if (iter == state.nodes.front()) {
- state.root = spatial;
- }
-
- // flat node map parent lookup tool
- state.flat_node_map.insert(element_assimp_node, spatial);
-
- Map<const aiNode *, Node3D *>::Element *parent_lookup = state.flat_node_map.find(parent_assimp_node);
-
- // note: this always fails on the root node :) keep that in mind this is by design
- if (parent_lookup) {
- Node3D *parent_node = parent_lookup->value();
-
- ERR_FAIL_COND_V_MSG(parent_node == nullptr, state.root,
- "Parent node invalid even though lookup successful, out of ram?");
-
- if (spatial != state.root) {
- parent_node->add_child(spatial);
- spatial->set_owner(state.root);
- } else {
- // required - think about it root never has a parent yet is valid, anything else without a parent is not valid.
- }
- } else if (spatial != state.root) {
- // if the ainode is not in the tree
- // parent it to the last good parent found
- if (last_valid_parent) {
- last_valid_parent->add_child(spatial);
- spatial->set_owner(state.root);
- } else {
- // this is a serious error?
- memdelete(spatial);
- }
- }
-
- // update last valid parent
- last_valid_parent = spatial;
- }
- print_verbose("node counts: " + itos(state.nodes.size()));
-
- // make clean bone stack
- RegenerateBoneStack(state);
-
- print_verbose("generating godot bone data");
-
- print_verbose("Godot bone stack count: " + itos(state.bone_stack.size()));
-
- // This is a list of bones, duplicates are from other meshes and must be dealt with properly
- for (List<aiBone *>::Element *element = state.bone_stack.front(); element; element = element->next()) {
- aiBone *bone = element->get();
-
- ERR_CONTINUE_MSG(!bone, "invalid bone read from assimp?");
-
- // Utilities for armature lookup - for now only FBX makes these
- aiNode *armature_for_bone = bone->mArmature;
-
- // Utilities for bone node lookup - for now only FBX makes these
- aiNode *bone_node = bone->mNode;
- aiNode *parent_node = bone_node->mParent;
-
- String bone_name = AssimpUtils::get_anim_string_from_assimp(bone->mName);
- ERR_CONTINUE_MSG(armature_for_bone == nullptr, "Armature for bone invalid: " + bone_name);
- Skeleton3D *skeleton = state.armature_skeletons[armature_for_bone];
-
- state.skeleton_bone_map[bone] = skeleton;
-
- if (bone_name.empty()) {
- bone_name = "untitled_bone_name";
- WARN_PRINT("Untitled bone name detected... report with file please");
- }
-
- // todo: this is where skin support goes
- if (skeleton && skeleton->find_bone(bone_name) == -1) {
- print_verbose("[Godot Glue] Imported bone" + bone_name);
- int boneIdx = skeleton->get_bone_count();
-
- Transform pform = AssimpUtils::assimp_matrix_transform(bone->mNode->mTransformation);
- skeleton->add_bone(bone_name);
- skeleton->set_bone_rest(boneIdx, pform);
-
- if (parent_node != nullptr) {
- int parent_bone_id = skeleton->find_bone(AssimpUtils::get_anim_string_from_assimp(parent_node->mName));
- int current_bone_id = boneIdx;
- skeleton->set_bone_parent(current_bone_id, parent_bone_id);
- }
- }
- }
-
- print_verbose("generating mesh phase from skeletal mesh");
-
- List<Node3D *> cleanup_template_nodes;
-
- for (Map<const aiNode *, Node3D *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
- const aiNode *assimp_node = key_value_pair->key();
- Node3D *mesh_template = key_value_pair->value();
-
- ERR_CONTINUE(assimp_node == nullptr);
- ERR_CONTINUE(mesh_template == nullptr);
-
- Node *parent_node = mesh_template->get_parent();
-
- if (mesh_template == state.root) {
- continue;
- }
-
- if (parent_node == nullptr) {
- print_error("Found invalid parent node!");
- continue; // root node
- }
-
- String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
- Transform node_transform = AssimpUtils::assimp_matrix_transform(assimp_node->mTransformation);
-
- if (assimp_node->mNumMeshes > 0) {
- MeshInstance3D *mesh = create_mesh(state, assimp_node, node_name, parent_node, node_transform);
- if (mesh) {
- parent_node->remove_child(mesh_template);
-
- // re-parent children
- List<Node *> children;
- // re-parent all children to new node
- // note: since get_child_count will change during execution we must build a list first to be safe.
- for (int childId = 0; childId < mesh_template->get_child_count(); childId++) {
- // get child
- Node *child = mesh_template->get_child(childId);
- children.push_back(child);
- }
-
- for (List<Node *>::Element *element = children.front(); element; element = element->next()) {
- // reparent the children to the real mesh node.
- mesh_template->remove_child(element->get());
- mesh->add_child(element->get());
- element->get()->set_owner(state.root);
- }
-
- // update mesh in list so that each mesh node is available
- // this makes the template unavailable which is the desired behaviour
- state.flat_node_map[assimp_node] = mesh;
-
- cleanup_template_nodes.push_back(mesh_template);
-
- // clean up this list we don't need it
- children.clear();
- }
- }
- }
-
- for (List<Node3D *>::Element *element = cleanup_template_nodes.front(); element; element = element->next()) {
- if (element->get()) {
- memdelete(element->get());
- }
- }
- }
-
- if (p_flags & IMPORT_ANIMATION && scene->mNumAnimations) {
- state.animation_player = memnew(AnimationPlayer);
- state.root->add_child(state.animation_player);
- state.animation_player->set_owner(state.root);
-
- for (uint32_t i = 0; i < scene->mNumAnimations; i++) {
- _import_animation(state, i, p_bake_fps);
- }
- }
-
- //
- // Cleanup operations
- //
-
- state.mesh_cache.clear();
- state.material_cache.clear();
- state.light_cache.clear();
- state.camera_cache.clear();
- state.assimp_node_map.clear();
- state.path_to_image_cache.clear();
- state.nodes.clear();
- state.flat_node_map.clear();
- state.armature_skeletons.clear();
- state.bone_stack.clear();
- return state.root;
-}
-
-void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int track_id,
- int anim_fps, Ref<Animation> animation, float ticks_per_second,
- Skeleton3D *skeleton, const NodePath &node_path,
- const String &node_name, aiBone *track_bone) {
- const aiNodeAnim *assimp_track = assimp_anim->mChannels[track_id];
- //make transform track
- int track_idx = animation->get_track_count();
- animation->add_track(Animation::TYPE_TRANSFORM);
- animation->track_set_path(track_idx, node_path);
- //first determine animation length
-
- float increment = 1.0 / float(anim_fps);
- float time = 0.0;
-
- bool last = false;
-
- Vector<Vector3> pos_values;
- Vector<float> pos_times;
- Vector<Vector3> scale_values;
- Vector<float> scale_times;
- Vector<Quat> rot_values;
- Vector<float> rot_times;
-
- for (size_t p = 0; p < assimp_track->mNumPositionKeys; p++) {
- aiVector3D pos = assimp_track->mPositionKeys[p].mValue;
- pos_values.push_back(Vector3(pos.x, pos.y, pos.z));
- pos_times.push_back(assimp_track->mPositionKeys[p].mTime / ticks_per_second);
- }
-
- for (size_t r = 0; r < assimp_track->mNumRotationKeys; r++) {
- aiQuaternion quat = assimp_track->mRotationKeys[r].mValue;
- rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized());
- rot_times.push_back(assimp_track->mRotationKeys[r].mTime / ticks_per_second);
- }
-
- for (size_t sc = 0; sc < assimp_track->mNumScalingKeys; sc++) {
- aiVector3D scale = assimp_track->mScalingKeys[sc].mValue;
- scale_values.push_back(Vector3(scale.x, scale.y, scale.z));
- scale_times.push_back(assimp_track->mScalingKeys[sc].mTime / ticks_per_second);
- }
-
- while (true) {
- Vector3 pos;
- Quat rot;
- Vector3 scale(1, 1, 1);
-
- if (pos_values.size()) {
- pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR);
- }
-
- if (rot_values.size()) {
- rot = _interpolate_track<Quat>(rot_times, rot_values, time,
- AssetImportAnimation::INTERP_LINEAR)
- .normalized();
- }
-
- if (scale_values.size()) {
- scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR);
- }
-
- if (skeleton) {
- int skeleton_bone = skeleton->find_bone(node_name);
-
- if (skeleton_bone >= 0 && track_bone) {
- Transform xform;
- xform.basis.set_quat_scale(rot, scale);
- xform.origin = pos;
-
- xform = skeleton->get_bone_rest(skeleton_bone).inverse() * xform;
-
- rot = xform.basis.get_rotation_quat();
- rot.normalize();
- scale = xform.basis.get_scale();
- pos = xform.origin;
- } else {
- ERR_FAIL_MSG("Skeleton bone lookup failed for skeleton: " + skeleton->get_name());
- }
- }
-
- animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR);
- animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
-
- if (last) { //done this way so a key is always inserted past the end (for proper interpolation)
- break;
- }
- time += increment;
- if (time >= animation->get_length()) {
- last = true;
- }
- }
-}
-
-// I really do not like this but need to figure out a better way of removing it later.
-Node *EditorSceneImporterAssimp::get_node_by_name(ImportState &state, String name) {
- for (Map<const aiNode *, Node3D *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
- const aiNode *assimp_node = key_value_pair->key();
- Node3D *node = key_value_pair->value();
-
- String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
- if (name == node_name && node) {
- return node;
- }
- }
- return nullptr;
-}
-
-/* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */
-void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state) {
- state.bone_stack.clear();
- // build bone stack list
- for (unsigned int mesh_id = 0; mesh_id < state.assimp_scene->mNumMeshes; ++mesh_id) {
- aiMesh *mesh = state.assimp_scene->mMeshes[mesh_id];
-
- // iterate over all the bones on the mesh for this node only!
- for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
- aiBone *bone = mesh->mBones[boneIndex];
-
- // doubtful this is required right now but best to check
- if (!state.bone_stack.find(bone)) {
- //print_verbose("[assimp] bone stack added: " + String(bone->mName.C_Str()) );
- state.bone_stack.push_back(bone);
- }
- }
- }
-}
-
-/* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */
-void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state, aiMesh *mesh) {
- state.bone_stack.clear();
- // iterate over all the bones on the mesh for this node only!
- for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
- aiBone *bone = mesh->mBones[boneIndex];
- if (state.bone_stack.find(bone) == nullptr) {
- state.bone_stack.push_back(bone);
- }
- }
-}
-
-// animation tracks are per bone
-
-void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_animation_index, int p_bake_fps) {
- ERR_FAIL_INDEX(p_animation_index, (int)state.assimp_scene->mNumAnimations);
-
- const aiAnimation *anim = state.assimp_scene->mAnimations[p_animation_index];
- String name = AssimpUtils::get_anim_string_from_assimp(anim->mName);
- if (name == String()) {
- name = "Animation " + itos(p_animation_index + 1);
- }
- print_verbose("import animation: " + name);
- float ticks_per_second = anim->mTicksPerSecond;
-
- if (state.assimp_scene->mMetaData != nullptr && Math::is_equal_approx(ticks_per_second, 0.0f)) {
- int32_t time_mode = 0;
- state.assimp_scene->mMetaData->Get("TimeMode", time_mode);
- ticks_per_second = AssimpUtils::get_fbx_fps(time_mode, state.assimp_scene);
- }
-
- //?
- //if ((p_path.get_file().get_extension().to_lower() == "glb" || p_path.get_file().get_extension().to_lower() == "gltf") && Math::is_equal_approx(ticks_per_second, 0.0f)) {
- // ticks_per_second = 1000.0f;
- //}
-
- if (Math::is_equal_approx(ticks_per_second, 0.0f)) {
- ticks_per_second = 25.0f;
- }
-
- Ref<Animation> animation;
- animation.instance();
- animation->set_name(name);
- animation->set_length(anim->mDuration / ticks_per_second);
-
- if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
- animation->set_loop(true);
- }
-
- // generate bone stack for animation import
- RegenerateBoneStack(state);
-
- //regular tracks
- for (size_t i = 0; i < anim->mNumChannels; i++) {
- const aiNodeAnim *track = anim->mChannels[i];
- String node_name = AssimpUtils::get_assimp_string(track->mNodeName);
- print_verbose("track name import: " + node_name);
- if (track->mNumRotationKeys == 0 && track->mNumPositionKeys == 0 && track->mNumScalingKeys == 0) {
- continue; //do not bother
- }
-
- Skeleton3D *skeleton = nullptr;
- NodePath node_path;
- aiBone *bone = nullptr;
-
- // Import skeleton bone animation for this track
- // Any bone will do, no point in processing more than just what is in the skeleton
- {
- bone = get_bone_from_stack(state, track->mNodeName);
-
- if (bone) {
- // get skeleton by bone
- skeleton = state.armature_skeletons[bone->mArmature];
-
- if (skeleton) {
- String path = state.root->get_path_to(skeleton);
- path += ":" + node_name;
- node_path = path;
-
- if (node_path != NodePath()) {
- _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton,
- node_path, node_name, bone);
- } else {
- print_error("Failed to find valid node path for animation");
- }
- }
- }
- }
-
- // not a bone
- // note this is flaky it uses node names which is unreliable
- Node *allocated_node = get_node_by_name(state, node_name);
- // todo: implement skeleton grabbing for node based animations too :)
- // check if node exists, if it does then also apply animation track for node and bones above are all handled.
- // this is now inclusive animation handling so that
- // we import all the data and do not miss anything.
- if (allocated_node) {
- node_path = state.root->get_path_to(allocated_node);
-
- if (node_path != NodePath()) {
- _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton,
- node_path, node_name, nullptr);
- }
- }
- }
-
- //blend shape tracks
-
- for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) {
- const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i];
-
- const String prop_name = AssimpUtils::get_assimp_string(anim_mesh->mName);
- const String mesh_name = prop_name.split("*")[0];
-
- ERR_CONTINUE(prop_name.split("*").size() != 2);
-
- Node *item = get_node_by_name(state, mesh_name);
- ERR_CONTINUE_MSG(!item, "failed to look up node by name");
- const MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(item);
- ERR_CONTINUE(mesh_instance == nullptr);
-
- String base_path = state.root->get_path_to(mesh_instance);
-
- Ref<Mesh> mesh = mesh_instance->get_mesh();
- ERR_CONTINUE(mesh.is_null());
-
- //add the tracks for this mesh
- int base_track = animation->get_track_count();
- for (int j = 0; j < mesh->get_blend_shape_count(); j++) {
- animation->add_track(Animation::TYPE_VALUE);
- animation->track_set_path(base_track + j, base_path + ":blend_shapes/" + mesh->get_blend_shape_name(j));
- }
-
- for (size_t k = 0; k < anim_mesh->mNumKeys; k++) {
- for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) {
- float t = anim_mesh->mKeys[k].mTime / ticks_per_second;
- float w = anim_mesh->mKeys[k].mWeights[j];
-
- animation->track_insert_key(base_track + j, t, w);
- }
- }
- }
-
- if (animation->get_track_count()) {
- state.animation_player->add_animation(name, animation);
- }
-}
-
-//
-// Mesh Generation from indices ? why do we need so much mesh code
-// [debt needs looked into]
-Ref<Mesh>
-EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices,
- const aiNode *assimp_node, Ref<Skin> &skin,
- Skeleton3D *&skeleton_assigned) {
- Ref<ArrayMesh> mesh;
- mesh.instance();
- bool has_uvs = false;
- bool compress_vert_data = state.import_flags & IMPORT_USE_COMPRESSION;
- uint32_t mesh_flags = compress_vert_data ? Mesh::ARRAY_COMPRESS_DEFAULT : 0;
-
- Map<String, uint32_t> morph_mesh_string_lookup;
-
- for (int i = 0; i < p_surface_indices.size(); i++) {
- const unsigned int mesh_idx = p_surface_indices[0];
- const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx];
- for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) {
- String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName);
- if (!morph_mesh_string_lookup.has(ai_anim_mesh_name)) {
- morph_mesh_string_lookup.insert(ai_anim_mesh_name, j);
- mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
- if (ai_anim_mesh_name.empty()) {
- ai_anim_mesh_name = String("morph_") + itos(j);
- }
- mesh->add_blend_shape(ai_anim_mesh_name);
- }
- }
- }
- //
- // Process Vertex Weights
- //
- for (int i = 0; i < p_surface_indices.size(); i++) {
- const unsigned int mesh_idx = p_surface_indices[i];
- const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx];
-
- Map<uint32_t, Vector<BoneInfo>> vertex_weights;
-
- if (ai_mesh->mNumBones > 0) {
- for (size_t b = 0; b < ai_mesh->mNumBones; b++) {
- aiBone *bone = ai_mesh->mBones[b];
-
- if (!skeleton_assigned) {
- print_verbose("Assigned mesh skeleton during mesh creation");
- skeleton_assigned = state.skeleton_bone_map[bone];
-
- if (!skin.is_valid()) {
- print_verbose("Configured new skin");
- skin.instance();
- } else {
- print_verbose("Reusing existing skin!");
- }
- }
- // skeleton_assigned =
- String bone_name = AssimpUtils::get_assimp_string(bone->mName);
- int bone_index = skeleton_assigned->find_bone(bone_name);
- ERR_CONTINUE(bone_index == -1);
- for (size_t w = 0; w < bone->mNumWeights; w++) {
- aiVertexWeight ai_weights = bone->mWeights[w];
-
- BoneInfo bi;
- uint32_t vertex_index = ai_weights.mVertexId;
- bi.bone = bone_index;
- bi.weight = ai_weights.mWeight;
-
- if (!vertex_weights.has(vertex_index)) {
- vertex_weights[vertex_index] = Vector<BoneInfo>();
- }
-
- vertex_weights[vertex_index].push_back(bi);
- }
- }
- }
-
- //
- // Create mesh from data from assimp
- //
-
- Ref<SurfaceTool> st;
- st.instance();
- st->begin(Mesh::PRIMITIVE_TRIANGLES);
-
- for (size_t j = 0; j < ai_mesh->mNumVertices; j++) {
- // Get the texture coordinates if they exist
- if (ai_mesh->HasTextureCoords(0)) {
- has_uvs = true;
- st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y));
- }
-
- if (ai_mesh->HasTextureCoords(1)) {
- has_uvs = true;
- st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y));
- }
-
- // Assign vertex colors
- if (ai_mesh->HasVertexColors(0)) {
- Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b,
- ai_mesh->mColors[0]->a);
- st->add_color(color);
- }
-
- // Work out normal calculations? - this needs work it doesn't work properly on huestos
- if (ai_mesh->mNormals != nullptr) {
- const aiVector3D normals = ai_mesh->mNormals[j];
- const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
- st->add_normal(godot_normal);
- if (ai_mesh->HasTangentsAndBitangents()) {
- const aiVector3D tangents = ai_mesh->mTangents[j];
- const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z);
- const aiVector3D bitangent = ai_mesh->mBitangents[j];
- const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
- float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
- st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d));
- }
- }
-
- // We have vertex weights right?
- if (vertex_weights.has(j)) {
- Vector<BoneInfo> bone_info = vertex_weights[j];
- Vector<int> bones;
- bones.resize(bone_info.size());
- Vector<float> weights;
- weights.resize(bone_info.size());
-
- // todo? do we really need to loop over all bones? - assimp may have helper to find all influences on this vertex.
- for (int k = 0; k < bone_info.size(); k++) {
- bones.write[k] = bone_info[k].bone;
- weights.write[k] = bone_info[k].weight;
- }
-
- st->add_bones(bones);
- st->add_weights(weights);
- }
-
- // Assign vertex
- const aiVector3D pos = ai_mesh->mVertices[j];
-
- // note we must include node offset transform as this is relative to world space not local space.
- Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z);
- st->add_vertex(godot_pos);
- }
-
- // fire replacement for face handling
- for (size_t j = 0; j < ai_mesh->mNumFaces; j++) {
- const aiFace face = ai_mesh->mFaces[j];
- for (unsigned int k = 0; k < face.mNumIndices; k++) {
- st->add_index(face.mIndices[k]);
- }
- }
-
- if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) {
- st->generate_tangents();
- }
-
- aiMaterial *ai_material = state.assimp_scene->mMaterials[ai_mesh->mMaterialIndex];
- Ref<StandardMaterial3D> mat;
- mat.instance();
-
- int32_t mat_two_sided = 0;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) {
- if (mat_two_sided > 0) {
- mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
- } else {
- mat->set_cull_mode(StandardMaterial3D::CULL_BACK);
- }
- }
-
- aiString mat_name;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) {
- mat->set_name(AssimpUtils::get_assimp_string(mat_name));
- }
-
- // Culling handling for meshes
-
- // cull all back faces
- mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
-
- // Now process materials
- aiTextureType base_color = aiTextureType_BASE_COLOR;
- {
- String filename, path;
- AssimpImageData image_data;
-
- if (AssimpUtils::GetAssimpTexture(state, ai_material, base_color, filename, path, image_data)) {
- AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-
- // anything transparent must be culled
- if (image_data.raw_image->detect_alpha() != Image::ALPHA_NONE) {
- mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS);
- mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); // since you can see both sides in transparent mode
- }
-
- mat->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, image_data.texture);
- }
- }
-
- aiTextureType tex_diffuse = aiTextureType_DIFFUSE;
- {
- String filename, path;
- AssimpImageData image_data;
-
- if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_diffuse, filename, path, image_data)) {
- AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
-
- // anything transparent must be culled
- if (image_data.raw_image->detect_alpha() != Image::ALPHA_NONE) {
- mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS);
- mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); // since you can see both sides in transparent mode
- }
-
- mat->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, image_data.texture);
- }
-
- aiColor4D clr_diffuse;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) {
- if (Math::is_equal_approx(clr_diffuse.a, 1.0f) == false) {
- mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS);
- mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); // since you can see both sides in transparent mode
- }
- mat->set_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a));
- }
- }
-
- aiTextureType tex_normal = aiTextureType_NORMALS;
- {
- String filename, path;
- Ref<ImageTexture> texture;
- AssimpImageData image_data;
-
- // Process texture normal map
- if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_normal, filename, path, image_data)) {
- AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
- mat->set_feature(StandardMaterial3D::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(StandardMaterial3D::TEXTURE_NORMAL, image_data.texture);
- } else {
- aiString texture_path;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, AI_PROPERTIES, texture_path)) {
- if (AssimpUtils::CreateAssimpTexture(state, texture_path, filename, path, image_data)) {
- mat->set_feature(StandardMaterial3D::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(StandardMaterial3D::TEXTURE_NORMAL, image_data.texture);
- }
- }
- }
- }
-
- aiTextureType tex_normal_camera = aiTextureType_NORMAL_CAMERA;
- {
- String filename, path;
- Ref<ImageTexture> texture;
- AssimpImageData image_data;
-
- // Process texture normal map
- if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_normal_camera, filename, path, image_data)) {
- AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
- mat->set_feature(StandardMaterial3D::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(StandardMaterial3D::TEXTURE_NORMAL, image_data.texture);
- }
- }
-
- aiTextureType tex_emission_color = aiTextureType_EMISSION_COLOR;
- {
- String filename, path;
- Ref<ImageTexture> texture;
- AssimpImageData image_data;
-
- // Process texture normal map
- if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_emission_color, filename, path, image_data)) {
- AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
- mat->set_feature(StandardMaterial3D::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(StandardMaterial3D::TEXTURE_NORMAL, image_data.texture);
- }
- }
-
- aiTextureType tex_metalness = aiTextureType_METALNESS;
- {
- String filename, path;
- Ref<ImageTexture> texture;
- AssimpImageData image_data;
-
- // Process texture normal map
- if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_metalness, filename, path, image_data)) {
- AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
- mat->set_texture(StandardMaterial3D::TEXTURE_METALLIC, image_data.texture);
- }
- }
-
- aiTextureType tex_roughness = aiTextureType_DIFFUSE_ROUGHNESS;
- {
- String filename, path;
- Ref<ImageTexture> texture;
- AssimpImageData image_data;
-
- // Process texture normal map
- if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_roughness, filename, path, image_data)) {
- AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
- mat->set_texture(StandardMaterial3D::TEXTURE_ROUGHNESS, image_data.texture);
- }
- }
-
- aiTextureType tex_emissive = aiTextureType_EMISSIVE;
- {
- String filename = "";
- String path = "";
- Ref<Image> texture;
- AssimpImageData image_data;
-
- if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_emissive, filename, path, image_data)) {
- AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
- mat->set_feature(StandardMaterial3D::FEATURE_EMISSION, true);
- mat->set_texture(StandardMaterial3D::TEXTURE_EMISSION, image_data.texture);
- } else {
- // Process emission textures
- aiString texture_emissive_path;
- if (AI_SUCCESS ==
- ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE, AI_PROPERTIES, texture_emissive_path)) {
- if (AssimpUtils::CreateAssimpTexture(state, texture_emissive_path, filename, path, image_data)) {
- mat->set_feature(StandardMaterial3D::FEATURE_EMISSION, true);
- mat->set_texture(StandardMaterial3D::TEXTURE_EMISSION, image_data.texture);
- }
- } else {
- float pbr_emission = 0.0f;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSIVE_FACTOR, AI_NULL, pbr_emission)) {
- mat->set_emission(Color(pbr_emission, pbr_emission, pbr_emission, 1.0f));
- }
- }
- }
- }
-
- aiTextureType tex_specular = aiTextureType_SPECULAR;
- {
- String filename, path;
- Ref<ImageTexture> texture;
- AssimpImageData image_data;
-
- // Process texture normal map
- if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_specular, filename, path, image_data)) {
- AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
- mat->set_texture(StandardMaterial3D::TEXTURE_METALLIC, image_data.texture);
- }
- }
-
- aiTextureType tex_ao_map = aiTextureType_AMBIENT_OCCLUSION;
- {
- String filename, path;
- Ref<ImageTexture> texture;
- AssimpImageData image_data;
-
- // Process texture normal map
- if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_ao_map, filename, path, image_data)) {
- AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
- mat->set_feature(StandardMaterial3D::FEATURE_AMBIENT_OCCLUSION, true);
- mat->set_texture(StandardMaterial3D::TEXTURE_AMBIENT_OCCLUSION, image_data.texture);
- }
- }
-
- Array array_mesh = st->commit_to_arrays();
- Array morphs;
- morphs.resize(ai_mesh->mNumAnimMeshes);
- Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES;
-
- for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) {
- String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName);
-
- if (ai_anim_mesh_name.empty()) {
- ai_anim_mesh_name = String("morph_") + itos(j);
- }
-
- Array array_copy;
- array_copy.resize(RenderingServer::ARRAY_MAX);
-
- for (int l = 0; l < RenderingServer::ARRAY_MAX; l++) {
- array_copy[l] = array_mesh[l].duplicate(true);
- }
-
- const size_t num_vertices = ai_mesh->mAnimMeshes[j]->mNumVertices;
- array_copy[Mesh::ARRAY_INDEX] = Variant();
- if (ai_mesh->mAnimMeshes[j]->HasPositions()) {
- PackedVector3Array vertices;
- vertices.resize(num_vertices);
- for (size_t l = 0; l < num_vertices; l++) {
- const aiVector3D ai_pos = ai_mesh->mAnimMeshes[j]->mVertices[l];
- Vector3 position = Vector3(ai_pos.x, ai_pos.y, ai_pos.z);
- vertices.ptrw()[l] = position;
- }
- PackedVector3Array new_vertices = array_copy[RenderingServer::ARRAY_VERTEX].duplicate(true);
- ERR_CONTINUE(vertices.size() != new_vertices.size());
- for (int32_t l = 0; l < new_vertices.size(); l++) {
- Vector3 *w = new_vertices.ptrw();
- w[l] = vertices[l];
- }
- array_copy[RenderingServer::ARRAY_VERTEX] = new_vertices;
- }
-
- int32_t color_set = 0;
- if (ai_mesh->mAnimMeshes[j]->HasVertexColors(color_set)) {
- PackedColorArray colors;
- colors.resize(num_vertices);
- for (size_t l = 0; l < num_vertices; l++) {
- const aiColor4D ai_color = ai_mesh->mAnimMeshes[j]->mColors[color_set][l];
- Color color = Color(ai_color.r, ai_color.g, ai_color.b, ai_color.a);
- colors.ptrw()[l] = color;
- }
- PackedColorArray new_colors = array_copy[RenderingServer::ARRAY_COLOR].duplicate(true);
- ERR_CONTINUE(colors.size() != new_colors.size());
- for (int32_t l = 0; l < colors.size(); l++) {
- Color *w = new_colors.ptrw();
- w[l] = colors[l];
- }
- array_copy[RenderingServer::ARRAY_COLOR] = new_colors;
- }
-
- if (ai_mesh->mAnimMeshes[j]->HasNormals()) {
- PackedVector3Array normals;
- normals.resize(num_vertices);
- for (size_t l = 0; l < num_vertices; l++) {
- const aiVector3D ai_normal = ai_mesh->mAnimMeshes[j]->mNormals[l];
- Vector3 normal = Vector3(ai_normal.x, ai_normal.y, ai_normal.z);
- normals.ptrw()[l] = normal;
- }
- PackedVector3Array new_normals = array_copy[RenderingServer::ARRAY_NORMAL].duplicate(true);
- ERR_CONTINUE(normals.size() != new_normals.size());
- for (int l = 0; l < normals.size(); l++) {
- Vector3 *w = new_normals.ptrw();
- w[l] = normals[l];
- }
- array_copy[RenderingServer::ARRAY_NORMAL] = new_normals;
- }
-
- if (ai_mesh->mAnimMeshes[j]->HasTangentsAndBitangents()) {
- PackedColorArray tangents;
- tangents.resize(num_vertices);
- Color *w = tangents.ptrw();
- for (size_t l = 0; l < num_vertices; l++) {
- AssimpUtils::calc_tangent_from_mesh(ai_mesh, j, l, l, w);
- }
- PackedFloat32Array new_tangents = array_copy[RenderingServer::ARRAY_TANGENT].duplicate(true);
- ERR_CONTINUE(new_tangents.size() != tangents.size() * 4);
- for (int32_t l = 0; l < tangents.size(); l++) {
- new_tangents.ptrw()[l + 0] = tangents[l].r;
- new_tangents.ptrw()[l + 1] = tangents[l].g;
- new_tangents.ptrw()[l + 2] = tangents[l].b;
- new_tangents.ptrw()[l + 3] = tangents[l].a;
- }
- array_copy[RenderingServer::ARRAY_TANGENT] = new_tangents;
- }
-
- morphs[j] = array_copy;
- }
- mesh->add_surface_from_arrays(primitive, array_mesh, morphs, Dictionary(), mesh_flags);
- mesh->surface_set_material(i, mat);
- mesh->surface_set_name(i, AssimpUtils::get_assimp_string(ai_mesh->mName));
- }
-
- return mesh;
-}
-
-/**
- * Create a new mesh for the node supplied
- */
-MeshInstance3D *
-EditorSceneImporterAssimp::create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *active_node, Transform node_transform) {
- /* MESH NODE */
- Ref<Mesh> mesh;
- Ref<Skin> skin;
- // see if we have mesh cache for this.
- Vector<int> surface_indices;
-
- RegenerateBoneStack(state);
-
- // Configure indices
- for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) {
- int mesh_index = assimp_node->mMeshes[i];
- // create list of mesh indexes
- surface_indices.push_back(mesh_index);
- }
-
- //surface_indices.sort();
- String mesh_key;
- for (int i = 0; i < surface_indices.size(); i++) {
- if (i > 0) {
- mesh_key += ":";
- }
- mesh_key += itos(surface_indices[i]);
- }
-
- Skeleton3D *skeleton = nullptr;
- aiNode *armature = nullptr;
-
- if (!state.mesh_cache.has(mesh_key)) {
- mesh = _generate_mesh_from_surface_indices(state, surface_indices, assimp_node, skin, skeleton);
- state.mesh_cache[mesh_key] = mesh;
- }
-
- MeshInstance3D *mesh_node = memnew(MeshInstance3D);
- mesh = state.mesh_cache[mesh_key];
- mesh_node->set_mesh(mesh);
-
- // if we have a valid skeleton set it up
- if (skin.is_valid()) {
- for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) {
- unsigned int mesh_index = assimp_node->mMeshes[i];
- const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_index];
-
- // please remember bone id relative to the skin is NOT the mesh relative index.
- // it is the index relative to the skeleton that is why
- // we have state.bone_id_map, it allows for duplicate bone id's too :)
- // hope this makes sense
-
- int bind_count = 0;
- for (unsigned int boneId = 0; boneId < ai_mesh->mNumBones; ++boneId) {
- aiBone *iterBone = ai_mesh->mBones[boneId];
-
- // used to reparent mesh to the correct armature later on if assigned.
- if (!armature) {
- print_verbose("Configured mesh armature, will reparent later to armature");
- armature = iterBone->mArmature;
- }
-
- if (skeleton) {
- int id = skeleton->find_bone(AssimpUtils::get_assimp_string(iterBone->mName));
- if (id != -1) {
- print_verbose("Set bind bone: mesh: " + itos(mesh_index) + " bone index: " + itos(id));
- Transform t = AssimpUtils::assimp_matrix_transform(iterBone->mOffsetMatrix);
-
- skin->add_bind(bind_count, t);
- skin->set_bind_bone(bind_count, id);
- bind_count++;
- }
- }
- }
- }
-
- print_verbose("Finished configuring bind pose for skin mesh");
- }
-
- // this code parents all meshes with bones to the armature they are for
- // GLTF2 specification relies on this and we are enforcing it for FBX.
- if (armature && state.flat_node_map[armature]) {
- Node *armature_parent = state.flat_node_map[armature];
- print_verbose("Parented mesh " + node_name + " to armature " + armature_parent->get_name());
- // static mesh handling
- armature_parent->add_child(mesh_node);
- // transform must be identity
- mesh_node->set_global_transform(Transform());
- mesh_node->set_name(node_name);
- mesh_node->set_owner(state.root);
- } else {
- // static mesh handling
- active_node->add_child(mesh_node);
- mesh_node->set_global_transform(node_transform);
- mesh_node->set_name(node_name);
- mesh_node->set_owner(state.root);
- }
-
- if (skeleton) {
- print_verbose("Attempted to set skeleton path!");
- mesh_node->set_skeleton_path(mesh_node->get_path_to(skeleton));
- mesh_node->set_skin(skin);
- }
-
- return mesh_node;
-}
-
-/**
- * Create a light for the scene
- * Automatically caches lights for lookup later
- */
-Node3D *EditorSceneImporterAssimp::create_light(
- ImportState &state,
- const String &node_name,
- Transform &look_at_transform) {
- Light3D *light = nullptr;
- aiLight *assimp_light = state.assimp_scene->mLights[state.light_cache[node_name]];
- ERR_FAIL_COND_V(!assimp_light, nullptr);
-
- if (assimp_light->mType == aiLightSource_DIRECTIONAL) {
- light = memnew(DirectionalLight3D);
- } else if (assimp_light->mType == aiLightSource_POINT) {
- light = memnew(OmniLight3D);
- } else if (assimp_light->mType == aiLightSource_SPOT) {
- light = memnew(SpotLight3D);
- }
- ERR_FAIL_COND_V(light == nullptr, nullptr);
-
- if (assimp_light->mType != aiLightSource_POINT) {
- Vector3 pos = Vector3(
- assimp_light->mPosition.x,
- assimp_light->mPosition.y,
- assimp_light->mPosition.z);
- Vector3 look_at = Vector3(
- assimp_light->mDirection.y,
- assimp_light->mDirection.x,
- assimp_light->mDirection.z)
- .normalized();
- Vector3 up = Vector3(
- assimp_light->mUp.x,
- assimp_light->mUp.y,
- assimp_light->mUp.z);
-
- look_at_transform.set_look_at(pos, look_at, up);
- }
- // properties for light variables should be put here.
- // not really hugely important yet but we will need them in the future
-
- light->set_color(
- Color(assimp_light->mColorDiffuse.r, assimp_light->mColorDiffuse.g, assimp_light->mColorDiffuse.b));
-
- return light;
-}
-
-/**
- * Create camera for the scene
- */
-Node3D *EditorSceneImporterAssimp::create_camera(
- ImportState &state,
- const String &node_name,
- Transform &look_at_transform) {
- aiCamera *camera = state.assimp_scene->mCameras[state.camera_cache[node_name]];
- ERR_FAIL_COND_V(!camera, nullptr);
-
- Camera3D *camera_node = memnew(Camera3D);
- ERR_FAIL_COND_V(!camera_node, nullptr);
- float near = camera->mClipPlaneNear;
- if (Math::is_equal_approx(near, 0.0f)) {
- near = 0.1f;
- }
- camera_node->set_perspective(Math::rad2deg(camera->mHorizontalFOV) * 2.0f, near, camera->mClipPlaneFar);
- Vector3 pos = Vector3(camera->mPosition.x, camera->mPosition.y, camera->mPosition.z);
- Vector3 look_at = Vector3(camera->mLookAt.y, camera->mLookAt.x, camera->mLookAt.z).normalized();
- Vector3 up = Vector3(camera->mUp.x, camera->mUp.y, camera->mUp.z);
-
- look_at_transform.set_look_at(pos + look_at_transform.origin, look_at, up);
- return camera_node;
-}
-
-/**
- * Generate node
- * Recursive call to iterate over all nodes
- */
-void EditorSceneImporterAssimp::_generate_node(
- ImportState &state,
- const aiNode *assimp_node) {
- ERR_FAIL_COND(assimp_node == nullptr);
- state.nodes.push_back(assimp_node);
- String parent_name = AssimpUtils::get_assimp_string(assimp_node->mParent->mName);
-
- // please note
- // duplicate bone names exist
- // this is why we only check if the bone exists
- // so everything else is useless but the name
- // please do not copy any other values from get_bone_by_name.
- aiBone *parent_bone = get_bone_by_name(state.assimp_scene, assimp_node->mParent->mName);
- aiBone *current_bone = get_bone_by_name(state.assimp_scene, assimp_node->mName);
-
- // is this an armature
- // parent null
- // and this is the first bone :)
- if (parent_bone == nullptr && current_bone) {
- state.armature_nodes.push_back(assimp_node->mParent);
- print_verbose("found valid armature: " + parent_name);
- }
-
- for (size_t i = 0; i < assimp_node->mNumChildren; i++) {
- _generate_node(state, assimp_node->mChildren[i]);
- }
-}
diff --git a/modules/assimp/editor_scene_importer_assimp.h b/modules/assimp/editor_scene_importer_assimp.h
deleted file mode 100644
index 7be80c4ad0..0000000000
--- a/modules/assimp/editor_scene_importer_assimp.h
+++ /dev/null
@@ -1,149 +0,0 @@
-/*************************************************************************/
-/* editor_scene_importer_assimp.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 EDITOR_SCENE_IMPORTER_ASSIMP_H
-#define EDITOR_SCENE_IMPORTER_ASSIMP_H
-
-#ifdef TOOLS_ENABLED
-#include "core/bind/core_bind.h"
-#include "core/io/resource_importer.h"
-#include "core/vector.h"
-#include "editor/import/resource_importer_scene.h"
-#include "editor/project_settings_editor.h"
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/3d/node_3d.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/animation/animation_player.h"
-#include "scene/resources/animation.h"
-#include "scene/resources/surface_tool.h"
-
-#include <assimp/matrix4x4.h>
-#include <assimp/scene.h>
-#include <assimp/types.h>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/LogStream.hpp>
-#include <assimp/Logger.hpp>
-#include <map>
-
-#include "import_state.h"
-#include "import_utils.h"
-
-using namespace AssimpImporter;
-
-class AssimpStream : public Assimp::LogStream {
-public:
- // Constructor
- AssimpStream() {}
-
- // Destructor
- ~AssimpStream() {}
- // Write something using your own functionality
- void write(const char *message) {
- print_verbose(String("Open Asset Import: ") + String(message).strip_edges());
- }
-};
-
-class EditorSceneImporterAssimp : public EditorSceneImporter {
-private:
- GDCLASS(EditorSceneImporterAssimp, EditorSceneImporter);
-
- struct AssetImportAnimation {
- enum Interpolation {
- INTERP_LINEAR,
- INTERP_STEP,
- INTERP_CATMULLROMSPLINE,
- INTERP_CUBIC_SPLINE
- };
- };
-
- struct BoneInfo {
- uint32_t bone;
- float weight;
- };
-
- Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices,
- const aiNode *assimp_node, Ref<Skin> &skin,
- Skeleton3D *&skeleton_assigned);
-
- // simple object creation functions
- Node3D *create_light(ImportState &state,
- const String &node_name,
- Transform &look_at_transform);
- Node3D *create_camera(
- ImportState &state,
- const String &node_name,
- Transform &look_at_transform);
- // non recursive - linear so must not use recursive arguments
- MeshInstance3D *create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *active_node, Transform node_transform);
- // recursive node generator
- void _generate_node(ImportState &state, const aiNode *assimp_node);
- void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int track_id,
- int anim_fps, Ref<Animation> animation, float ticks_per_second,
- Skeleton3D *skeleton, const NodePath &node_path,
- const String &node_name, aiBone *track_bone);
-
- void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps);
- Node *get_node_by_name(ImportState &state, String name);
- aiBone *get_bone_from_stack(ImportState &state, aiString name);
- Node3D *_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights);
-
- template <class T>
- T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp);
- 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;
-
- struct ImportFormat {
- Vector<String> extensions;
- bool is_default;
- };
-
-protected:
- static void _bind_methods();
-
-public:
- EditorSceneImporterAssimp() {
- Assimp::DefaultLogger::create("", Assimp::Logger::VERBOSE);
- unsigned int severity = Assimp::Logger::Info | Assimp::Logger::Err | Assimp::Logger::Warn;
- Assimp::DefaultLogger::get()->attachStream(new AssimpStream(), severity);
- }
- ~EditorSceneImporterAssimp() {
- Assimp::DefaultLogger::kill();
- }
-
- virtual void get_extensions(List<String> *r_extensions) const override;
- virtual uint32_t get_import_flags() const 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;
- Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path);
-
- static void RegenerateBoneStack(ImportState &state);
-
- void RegenerateBoneStack(ImportState &state, aiMesh *mesh);
-};
-#endif
-#endif
diff --git a/modules/assimp/godot_update_assimp.sh b/modules/assimp/godot_update_assimp.sh
deleted file mode 100755
index ff8ff59e97..0000000000
--- a/modules/assimp/godot_update_assimp.sh
+++ /dev/null
@@ -1,262 +0,0 @@
-rm -rf ../../thirdparty/assimp
-cd ../../thirdparty/
-git clone https://github.com/assimp/assimp.git
-cd assimp
-rm -rf code/3DSExporter.h
-rm -rf code/3DSLoader.h
-rm -rf code/3MFXmlTags.h
-rm -rf code/ABCImporter.h
-rm -rf code/ACLoader.h
-rm -rf code/AMFImporter_Macro.hpp
-rm -rf code/ASELoader.h
-rm -rf code/assbin_chunks.h
-rm -rf code/AssbinExporter.h
-rm -rf code/AssbinLoader.h
-rm -rf code/AssimpCExport.cpp
-rm -rf code/AssxmlExporter.h
-rm -rf code/B3DImporter.h
-# rm -rf code/BaseProcess.cpp
-# rm -rf code/BaseProcess.h
-# rm -rf code/Bitmap.cpp
-rm -rf code/BlenderBMesh.cpp
-rm -rf code/BlenderBMesh.h
-rm -rf code/BlenderCustomData.cpp
-rm -rf code/BlenderCustomData.h
-rm -rf code/BlenderIntermediate.h
-rm -rf code/BlenderLoader.h
-rm -rf code/BlenderModifier.h
-rm -rf code/BlenderSceneGen.h
-rm -rf code/BlenderTessellator.h
-rm -rf code/BVHLoader.h
-rm -rf code/C4DImporter.h
-# rm -rf code/CalcTangentsProcess.h
-# rm -rf code/CInterfaceIOWrapper.cpp
-# rm -rf code/CInterfaceIOWrapper.h
-rm -rf code/COBLoader.h
-rm -rf code/COBScene.h
-rm -rf code/ColladaExporter.h
-rm -rf code/ColladaLoader.h
-# rm -rf code/ComputeUVMappingProcess.h
-# rm -rf code/ConvertToLHProcess.h
-# rm -rf code/CreateAnimMesh.cpp
-rm -rf code/CSMLoader.h
-rm -rf code/D3MFExporter.h
-rm -rf code/D3MFImporter.h
-rm -rf code/D3MFOpcPackage.h
-# rm -rf code/DeboneProcess.h
-# rm -rf code/DefaultIOStream.cpp
-# rm -rf code/DefaultIOSystem.cpp
-# rm -rf code/DefaultProgressHandler.h
-# rm -rf code/DropFaceNormalsProcess.cpp
-# rm -rf code/DropFaceNormalsProcess.h
-rm -rf code/DXFHelper.h
-rm -rf code/DXFLoader.h
-# rm -rf code/EmbedTexturesProcess.cpp
-# rm -rf code/EmbedTexturesProcess.h
-# rm -rf code/FBXCommon.h
-# rm -rf code/FBXCompileConfig.h
-# rm -rf code/FBXDeformer.cpp
-# rm -rf code/FBXDocumentUtil.cpp
-# rm -rf code/FBXDocumentUtil.h
-# rm -rf code/FBXExporter.h
-# rm -rf code/FBXExportNode.h
-# rm -rf code/FBXExportProperty.h
-# rm -rf code/FBXImporter.cpp
-# rm -rf code/FBXImporter.h
-# rm -rf code/FBXImportSettings.h
-# rm -rf code/FBXMeshGeometry.h
-# rm -rf code/FBXModel.cpp
-# rm -rf code/FBXNodeAttribute.cpp
-# rm -rf code/FBXParser.h
-# rm -rf code/FBXProperties.cpp
-# rm -rf code/FBXProperties.h
-# rm -rf code/FBXTokenizer.cpp
-# rm -rf code/FBXTokenizer.h
-# rm -rf code/FBXUtil.cpp
-# rm -rf code/FBXUtil.h
-# rm -rf code/FileLogStream.h
-# rm -rf code/FindDegenerates.h
-# rm -rf code/FindInstancesProcess.h
-# rm -rf code/FindInvalidDataProcess.h
-rm -rf code/FIReader.hpp
-# rm -rf code/FixNormalsStep.cpp
-# rm -rf code/FixNormalsStep.h
-# rm -rf code/GenFaceNormalsProcess.cpp
-# rm -rf code/GenFaceNormalsProcess.h
-# rm -rf code/GenVertexNormalsProcess.cpp
-# rm -rf code/GenVertexNormalsProcess.h
-rm -rf code/glTF2Asset.h
-rm -rf code/glTF2Asset.inl
-rm -rf code/glTF2AssetWriter.inl
-rm -rf code/glTF2Exporter.cpp
-rm -rf code/glTF2Importer.cpp
-rm -rf code/glTF2AssetWriter.h
-rm -rf code/glTFAsset.h
-rm -rf code/glTFAsset.inl
-rm -rf code/glTFAssetWriter.inl
-rm -rf code/glTFExporter.cpp
-rm -rf code/glTFImporter.cpp
-rm -rf code/glTF2Exporter.h
-rm -rf code/glTF2Importer.h
-rm -rf code/glTFAssetWriter.h
-rm -rf code/glTFExporter.h
-rm -rf code/glTFImporter.h
-rm -rf code/HalfLifeFileData.h
-rm -rf code/HMPFileData.h
-rm -rf code/HMPLoader.h
-rm -rf code/HMPLoader.cpp
-rm -rf code/IFF.h
-# rm -rf code/Importer.h
-# rm -rf code/ImproveCacheLocality.h
-rm -rf code/IRRLoader.h
-rm -rf code/IRRMeshLoader.h
-rm -rf code/IRRShared.h
-# rm -rf code/JoinVerticesProcess.h
-# rm -rf code/LimitBoneWeightsProcess.cpp
-# rm -rf code/LimitBoneWeightsProcess.h
-rm -rf code/LWSLoader.h
-rm -rf code/makefile.mingw
-# rm -rf code/MakeVerboseFormat.cpp
-# rm -rf code/MakeVerboseFormat.h
-# rm -rf code/MaterialSystem.h
-rm -rf code/MD2FileData.h
-rm -rf code/MD2Loader.h
-rm -rf code/MD2NormalTable.h
-rm -rf code/MD3FileData.h
-rm -rf code/MD3Loader.h
-rm -rf code/MD4FileData.h
-rm -rf code/MD5Loader.h
-rm -rf code/MD5Parser.cpp
-rm -rf code/MDCFileData.h
-rm -rf code/MDCLoader.h
-rm -rf code/MDLDefaultColorMap.h
-# rm -rf code/MMDCpp14.h
-# rm -rf code/MMDImporter.h
-rm -rf code/MS3DLoader.h
-rm -rf code/NDOLoader.h
-rm -rf code/NFFLoader.h
-rm -rf code/ObjExporter.h
-rm -rf code/ObjFileImporter.h
-rm -rf code/ObjFileMtlImporter.h
-rm -rf code/ObjFileParser.h
-rm -rf code/ObjTools.h
-rm -rf code/ObjExporter.cpp
-rm -rf code/ObjFileImporter.cpp
-rm -rf code/ObjFileMtlImporter.cpp
-rm -rf code/ObjFileParser.cpp
-rm -rf code/OFFLoader.h
-rm -rf code/OFFLoader.cpp
-rm -rf code/OgreImporter.cpp
-rm -rf code/OgreImporter.h
-rm -rf code/OgreParsingUtils.h
-rm -rf code/OgreXmlSerializer.h
-rm -rf code/OgreXmlSerializer.cpp
-rm -rf code/OgreBinarySerializer.cpp
-rm -rf code/OpenGEXExporter.cpp
-rm -rf code/OpenGEXExporter.h
-rm -rf code/OpenGEXImporter.h
-rm -rf code/OpenGEXStructs.h
-rm -rf code/OpenGEXImporter.cpp
-# rm -rf code/OptimizeGraph.h
-# rm -rf code/OptimizeMeshes.cpp
-# rm -rf code/OptimizeMeshes.h
-rm -rf code/PlyExporter.h
-rm -rf code/PlyLoader.h
-# rm -rf code/PolyTools.h
-# rm -rf code/PostStepRegistry.cpp
-# rm -rf code/PretransformVertices.h
-rm -rf code/Q3BSPFileData.h
-rm -rf code/Q3BSPFileImporter.h
-rm -rf code/Q3BSPFileParser.cpp
-rm -rf code/Q3BSPFileParser.h
-rm -rf code/Q3BSPZipArchive.cpp
-rm -rf code/Q3BSPZipArchive.h
-rm -rf code/Q3DLoader.h
-rm -rf code/Q3DLoader.cpp
-rm -rf code/Q3BSPFileImporter.cpp
-rm -rf code/RawLoader.h
-# rm -rf code/RemoveComments.cpp
-# rm -rf code/RemoveRedundantMaterials.cpp
-# rm -rf code/RemoveRedundantMaterials.h
-# rm -rf code/RemoveVCProcess.h
-# rm -rf code/ScaleProcess.cpp
-# rm -rf code/ScaleProcess.h
-# rm -rf code/scene.cpp
-# rm -rf code/ScenePreprocessor.cpp
-# rm -rf code/ScenePreprocessor.h
-# rm -rf code/ScenePrivate.h
-# rm -rf code/SGSpatialSort.cpp
-rm -rf code/SIBImporter.h
-rm -rf code/SMDLoader.cpp
-# rm -rf code/simd.cpp
-# rm -rf code/simd.h
-# rm -rf code/SortByPTypeProcess.h
-# rm -rf code/SplitByBoneCountProcess.h
-# rm -rf code/SplitLargeMeshes.h
-# rm -rf code/StdOStreamLogStream.h
-rm -rf code/StepExporter.h
-rm -rf code/StepExporter.cpp
-rm -rf code/STLExporter.cpp
-rm -rf code/STLExporter.h
-rm -rf code/STLLoader.h
-rm -rf code/STLLoader.cpp
-# rm -rf code/TargetAnimation.cpp
-# rm -rf code/TargetAnimation.h
-rm -rf code/TerragenLoader.h
-rm -rf code/TerragenLoader.cpp
-# rm -rf code/TextureTransform.h
-# rm -rf code/TriangulateProcess.h
-rm -rf code/UnrealLoader.h
-# rm -rf code/ValidateDataStructure.h
-# rm -rf code/Version.cpp
-# rm -rf code/VertexTriangleAdjacency.cpp
-# rm -rf code/VertexTriangleAdjacency.h
-# rm -rf code/Win32DebugLogStream.h
-rm -rf code/X3DImporter_Macro.hpp
-rm -rf code/X3DImporter_Metadata.cpp
-rm -rf code/X3DImporter_Networking.cpp
-rm -rf code/X3DImporter_Texturing.cpp
-rm -rf code/X3DImporter_Shape.cpp
-rm -rf code/X3DImporter_Rendering.cpp
-rm -rf code/X3DImporter_Postprocess.cpp
-rm -rf code/X3DImporter_Light.cpp
-rm -rf code/X3DImporter_Group.cpp
-rm -rf code/X3DImporter_Geometry3D.cpp
-rm -rf code/X3DImporter_Geometry2D.cpp
-rm -rf code/X3DImporter.cpp
-rm -rf code/X3DExporter.cpp
-rm -rf code/X3DVocabulary.cpp
-rm -rf code/XFileExporter.h
-rm -rf code/XFileExporter.cpp
-rm -rf code/XFileHelper.h
-rm -rf code/XFileHelper.cpp
-rm -rf code/XFileImporter.h
-rm -rf code/XFileImporter.cpp
-rm -rf code/XFileParser.h
-rm -rf code/XFileParser.cpp
-rm -rf code/XGLLoader.h
-rm -rf code/XGLLoader.cpp
-rm -rf code/Importer
-rm -rf .git
-rm -rf cmake-modules
-rm -rf doc
-rm -rf packaging
-rm -rf port
-rm -rf samples
-rm -rf scripts
-rm -rf test
-rm -rf tools
-rm -rf contrib/zlib
-rm -rf contrib/android-cmake
-rm -rf contrib/gtest
-rm -rf contrib/clipper
-rm -rf contrib/irrXML
-rm -rf contrib/Open3DGC
-rm -rf contrib/openddlparser
-rm -rf contrib/poly2tri
-#rm -rf contrib/rapidjson
-rm -rf contrib/unzip
-rm -rf contrib/zip
-rm -rf contrib/stb_image
-rm .travis*
-
diff --git a/modules/assimp/import_state.h b/modules/assimp/import_state.h
deleted file mode 100644
index ee22800ac4..0000000000
--- a/modules/assimp/import_state.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*************************************************************************/
-/* import_state.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 EDITOR_SCENE_IMPORT_STATE_H
-#define EDITOR_SCENE_IMPORT_STATE_H
-
-#include "core/bind/core_bind.h"
-#include "core/io/resource_importer.h"
-#include "core/vector.h"
-#include "editor/import/resource_importer_scene.h"
-#include "editor/project_settings_editor.h"
-#include "scene/3d/mesh_instance_3d.h"
-#include "scene/3d/node_3d.h"
-#include "scene/3d/skeleton_3d.h"
-#include "scene/animation/animation_player.h"
-#include "scene/resources/animation.h"
-#include "scene/resources/surface_tool.h"
-
-#include <assimp/matrix4x4.h>
-#include <assimp/scene.h>
-#include <assimp/types.h>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/LogStream.hpp>
-#include <assimp/Logger.hpp>
-
-namespace AssimpImporter {
-/** Import state is for global scene import data
- * This makes the code simpler and contains useful lookups.
- */
-struct ImportState {
- String path;
- Node3D *root;
- const aiScene *assimp_scene;
- uint32_t max_bone_weights;
-
- Map<String, Ref<Mesh>> mesh_cache;
- Map<int, Ref<Material>> material_cache;
- Map<String, int> light_cache;
- Map<String, int> camera_cache;
-
- // very useful for when you need to ask assimp for the bone mesh
-
- Map<const aiNode *, Node *> assimp_node_map;
- Map<String, Ref<Image>> path_to_image_cache;
-
- // Generation 3 - determinisitic iteration
- // to lower potential recursion errors
- List<const aiNode *> nodes;
- Map<const aiNode *, Node3D *> flat_node_map;
- AnimationPlayer *animation_player;
-
- // Generation 3 - deterministic armatures
- // list of armature nodes - flat and simple to parse
- // assimp node, node in godot
- List<aiNode *> armature_nodes;
- Map<const aiNode *, Skeleton3D *> armature_skeletons;
- Map<aiBone *, Skeleton3D *> skeleton_bone_map;
- // Generation 3 - deterministic bone handling
- // bones from the stack are popped when found
- // this means we can detect
- // what bones are for other armatures
- List<aiBone *> bone_stack;
-
- // EditorSceneImporter::ImportFlags
- uint32_t import_flags;
-};
-
-struct AssimpImageData {
- Ref<Image> raw_image;
- Ref<ImageTexture> texture;
- aiTextureMapMode map_mode[2];
-};
-
-/** Recursive state is used to push state into functions instead of specifying them
- * This makes the code easier to handle too and add extra arguments without breaking things
- */
-struct RecursiveState {
- RecursiveState() {} // do not construct :)
- RecursiveState(
- Transform &_node_transform,
- Skeleton3D *_skeleton,
- Node3D *_new_node,
- String &_node_name,
- aiNode *_assimp_node,
- Node *_parent_node,
- aiBone *_bone) :
- node_transform(_node_transform),
- skeleton(_skeleton),
- new_node(_new_node),
- node_name(_node_name),
- assimp_node(_assimp_node),
- parent_node(_parent_node),
- bone(_bone) {}
-
- Transform node_transform;
- Skeleton3D *skeleton = nullptr;
- Node3D *new_node = nullptr;
- String node_name;
- aiNode *assimp_node = nullptr;
- Node *parent_node = nullptr;
- aiBone *bone = nullptr;
-};
-} // namespace AssimpImporter
-
-#endif // EDITOR_SCENE_IMPORT_STATE_H
diff --git a/modules/assimp/import_utils.h b/modules/assimp/import_utils.h
deleted file mode 100644
index dc85d06fed..0000000000
--- a/modules/assimp/import_utils.h
+++ /dev/null
@@ -1,463 +0,0 @@
-/*************************************************************************/
-/* import_utils.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 IMPORT_UTILS_IMPORTER_ASSIMP_H
-#define IMPORT_UTILS_IMPORTER_ASSIMP_H
-
-#include "core/io/image_loader.h"
-#include "import_state.h"
-
-#include <assimp/SceneCombiner.h>
-#include <assimp/cexport.h>
-#include <assimp/cimport.h>
-#include <assimp/matrix4x4.h>
-#include <assimp/pbrmaterial.h>
-#include <assimp/postprocess.h>
-#include <assimp/scene.h>
-#include <assimp/DefaultLogger.hpp>
-#include <assimp/Importer.hpp>
-#include <assimp/LogStream.hpp>
-#include <assimp/Logger.hpp>
-#include <string>
-
-using namespace AssimpImporter;
-
-#define AI_PROPERTIES aiTextureType_UNKNOWN, 0
-#define AI_NULL 0, 0
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR "$raw.Maya|baseColor"
-#define AI_MATKEY_FBX_MAYA_METALNESS_FACTOR "$raw.Maya|metalness"
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR "$raw.Maya|diffuseRoughness"
-
-#define AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE "$raw.Maya|emissionColor|file"
-#define AI_MATKEY_FBX_MAYA_EMISSIVE_FACTOR "$raw.Maya|emission"
-#define AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE "$raw.Maya|metalness|file"
-#define AI_MATKEY_FBX_MAYA_METALNESS_UV_XFORM "$raw.Maya|metalness|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE "$raw.Maya|diffuseRoughness|file"
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_UV_XFORM "$raw.Maya|diffuseRoughness|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE "$raw.Maya|baseColor|file"
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM "$raw.Maya|baseColor|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE "$raw.Maya|normalCamera|file"
-#define AI_MATKEY_FBX_MAYA_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo"
-
-#define AI_MATKEY_FBX_NORMAL_TEXTURE "$raw.Maya|normalCamera|file"
-#define AI_MATKEY_FBX_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo"
-
-#define AI_MATKEY_FBX_MAYA_STINGRAY_DISPLACEMENT_SCALING_FACTOR "$raw.Maya|displacementscaling"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR "$raw.Maya|base_color"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR "$raw.Maya|emissive"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR "$raw.Maya|metallic"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR "$raw.Maya|roughness"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR "$raw.Maya|emissive_intensity"
-
-#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE "$raw.Maya|TEX_normal_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_UV_XFORM "$raw.Maya|TEX_normal_map|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE "$raw.Maya|TEX_color_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM "$raw.Maya|TEX_color_map|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE "$raw.Maya|TEX_metallic_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_UV_XFORM "$raw.Maya|TEX_metallic_map|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE "$raw.Maya|TEX_roughness_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_UV_XFORM "$raw.Maya|TEX_roughness_map|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE "$raw.Maya|TEX_emissive_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_UV_XFORM "$raw.Maya|TEX_emissive_map|uvtrafo"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_TEXTURE "$raw.Maya|TEX_ao_map|file"
-#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_UV_XFORM "$raw.Maya|TEX_ao_map|uvtrafo"
-
-/**
- * Assimp Utils
- * Conversion tools / glue code to convert from assimp to godot
-*/
-class AssimpUtils {
-public:
- /**
- * calculate tangents for mesh data from assimp data
- */
- static void calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, Color *w) {
- const aiVector3D normals = ai_mesh->mAnimMeshes[i]->mNormals[tri_index];
- const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
- const aiVector3D tangent = ai_mesh->mAnimMeshes[i]->mTangents[tri_index];
- const Vector3 godot_tangent = Vector3(tangent.x, tangent.y, tangent.z);
- const aiVector3D bitangent = ai_mesh->mAnimMeshes[i]->mBitangents[tri_index];
- const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
- float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
- Color plane_tangent = Color(tangent.x, tangent.y, tangent.z, d);
- w[index] = plane_tangent;
- }
-
- struct AssetImportFbx {
- enum ETimeMode {
- TIME_MODE_DEFAULT = 0,
- TIME_MODE_120 = 1,
- TIME_MODE_100 = 2,
- TIME_MODE_60 = 3,
- TIME_MODE_50 = 4,
- TIME_MODE_48 = 5,
- TIME_MODE_30 = 6,
- TIME_MODE_30_DROP = 7,
- TIME_MODE_NTSC_DROP_FRAME = 8,
- TIME_MODE_NTSC_FULL_FRAME = 9,
- TIME_MODE_PAL = 10,
- TIME_MODE_CINEMA = 11,
- TIME_MODE_1000 = 12,
- TIME_MODE_CINEMA_ND = 13,
- TIME_MODE_CUSTOM = 14,
- TIME_MODE_TIME_MODE_COUNT = 15
- };
- enum UpAxis {
- UP_VECTOR_AXIS_X = 1,
- UP_VECTOR_AXIS_Y = 2,
- UP_VECTOR_AXIS_Z = 3
- };
- enum FrontAxis {
- FRONT_PARITY_EVEN = 1,
- FRONT_PARITY_ODD = 2,
- };
-
- enum CoordAxis {
- COORD_RIGHT = 0,
- COORD_LEFT = 1
- };
- };
-
- /** Get assimp string
- * automatically filters the string data
- */
- static String get_assimp_string(const aiString &p_string) {
- //convert an assimp String to a Godot String
- String name;
- name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
- if (name.find(":") != -1) {
- String replaced_name = name.split(":")[1];
- print_verbose("Replacing " + name + " containing : with " + replaced_name);
- name = replaced_name;
- }
-
- return name;
- }
-
- static String get_anim_string_from_assimp(const aiString &p_string) {
- String name;
- name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
- if (name.find(":") != -1) {
- String replaced_name = name.split(":")[1];
- print_verbose("Replacing " + name + " containing : with " + replaced_name);
- name = replaced_name;
- }
- return name;
- }
-
- /**
- * No filter logic get_raw_string_from_assimp
- * This just convers the aiString to a parsed utf8 string
- * Without removing special chars etc
- */
- static String get_raw_string_from_assimp(const aiString &p_string) {
- String name;
- name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
- return name;
- }
-
- static Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
- return Ref<Animation>();
- }
-
- /**
- * Converts aiMatrix4x4 to godot Transform
- */
- static const Transform assimp_matrix_transform(const aiMatrix4x4 p_matrix) {
- aiMatrix4x4 matrix = p_matrix;
- Transform xform;
- xform.set(matrix.a1, matrix.a2, matrix.a3, matrix.b1, matrix.b2, matrix.b3, matrix.c1, matrix.c2, matrix.c3, matrix.a4, matrix.b4, matrix.c4);
- return xform;
- }
-
- /** Get fbx fps for time mode meta data
- */
- static float get_fbx_fps(int32_t time_mode, const aiScene *p_scene) {
- switch (time_mode) {
- case AssetImportFbx::TIME_MODE_DEFAULT:
- return 24; //hack
- case AssetImportFbx::TIME_MODE_120:
- return 120;
- case AssetImportFbx::TIME_MODE_100:
- return 100;
- case AssetImportFbx::TIME_MODE_60:
- return 60;
- case AssetImportFbx::TIME_MODE_50:
- return 50;
- case AssetImportFbx::TIME_MODE_48:
- return 48;
- case AssetImportFbx::TIME_MODE_30:
- return 30;
- case AssetImportFbx::TIME_MODE_30_DROP:
- return 30;
- case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME:
- return 29.9700262f;
- case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME:
- return 29.9700262f;
- case AssetImportFbx::TIME_MODE_PAL:
- return 25;
- case AssetImportFbx::TIME_MODE_CINEMA:
- return 24;
- case AssetImportFbx::TIME_MODE_1000:
- return 1000;
- case AssetImportFbx::TIME_MODE_CINEMA_ND:
- return 23.976f;
- case AssetImportFbx::TIME_MODE_CUSTOM:
- int32_t frame_rate = -1;
- p_scene->mMetaData->Get("FrameRate", frame_rate);
- return frame_rate;
- }
- return 0;
- }
-
- /**
- * Get global transform for the current node - so we can use world space rather than
- * local space coordinates
- * useful if you need global - although recommend using local wherever possible over global
- * as you could break fbx scaling :)
- */
- static Transform _get_global_assimp_node_transform(const aiNode *p_current_node) {
- aiNode const *current_node = p_current_node;
- Transform xform;
- while (current_node != nullptr) {
- xform = assimp_matrix_transform(current_node->mTransformation) * xform;
- current_node = current_node->mParent;
- }
- return xform;
- }
-
- /**
- * Find hardcoded textures from assimp which could be in many different directories
- */
- static void find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension) {
- Vector<String> paths;
- paths.push_back(path.get_basename() + extension);
- paths.push_back(path + extension);
- paths.push_back(path);
- paths.push_back(p_path.get_base_dir().plus_file(path.get_file().get_basename() + extension));
- paths.push_back(p_path.get_base_dir().plus_file(path.get_file() + extension));
- paths.push_back(p_path.get_base_dir().plus_file(path.get_file()));
- paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file().get_basename() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file()));
- paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file().get_basename() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file()));
- paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file().get_basename() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file()));
- paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file().get_basename() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file()));
- paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file().get_basename() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file()));
- paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file().get_basename() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file()));
- paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file().get_basename() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file()));
- paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file().get_basename() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file() + extension));
- paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file()));
- for (int i = 0; i < paths.size(); i++) {
- if (dir.file_exists(paths[i])) {
- found = true;
- path = paths[i];
- return;
- }
- }
- }
-
- /** find the texture path for the supplied fbx path inside godot
- * very simple lookup for subfolders etc for a texture which may or may not be in a directory
- */
- static void find_texture_path(const String &r_p_path, String &r_path, bool &r_found) {
- _Directory dir;
-
- List<String> exts;
- ImageLoader::get_recognized_extensions(&exts);
-
- Vector<String> split_path = r_path.get_basename().split("*");
- if (split_path.size() == 2) {
- r_found = true;
- return;
- }
-
- if (dir.file_exists(r_p_path.get_base_dir() + r_path.get_file())) {
- r_path = r_p_path.get_base_dir() + r_path.get_file();
- r_found = true;
- return;
- }
-
- for (int32_t i = 0; i < exts.size(); i++) {
- if (r_found) {
- return;
- }
- find_texture_path(r_p_path, dir, r_path, r_found, "." + exts[i]);
- }
- }
-
- /**
- * 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);
- // FIXME: Commented out during Vulkan port.
- /*
- aiTextureMapMode tex_mode = map_mode[0];
-
- int32_t flags = Texture2D::FLAGS_DEFAULT;
- if (tex_mode == aiTextureMapMode_Wrap) {
- //Default
- } else if (tex_mode == aiTextureMapMode_Clamp) {
- flags = flags & ~Texture2D::FLAG_REPEAT;
- } else if (tex_mode == aiTextureMapMode_Mirror) {
- flags = flags | Texture2D::FLAG_MIRRORED_REPEAT;
- }
- texture->set_flags(flags);
- */
- }
-
- /**
- * Load or load from cache image :)
- */
- 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);
-
- // if our cache contains this image then don't bother
- if (match) {
- return match->get();
- }
-
- Vector<String> split_path = p_path.get_basename().split("*");
- if (split_path.size() == 2) {
- size_t texture_idx = split_path[1].to_int();
- ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Image>());
- aiTexture *tex = p_scene->mTextures[texture_idx];
- String filename = AssimpUtils::get_raw_string_from_assimp(tex->mFilename);
- filename = filename.get_file();
- print_verbose("Open Asset Import: Loading embedded texture " + filename);
- if (tex->mHeight == 0) {
- if (tex->CheckFormat("png")) {
- ERR_FAIL_COND_V(Image::_png_mem_loader_func == nullptr, Ref<Image>());
- Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
- ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
- state.path_to_image_cache.insert(p_path, img);
- return img;
- } else if (tex->CheckFormat("jpg")) {
- ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == nullptr, Ref<Image>());
- Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
- ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
- state.path_to_image_cache.insert(p_path, img);
- return img;
- } else if (tex->CheckFormat("dds")) {
- ERR_FAIL_COND_V_MSG(true, Ref<Image>(), "Open Asset Import: Embedded dds not implemented");
- }
- } else {
- Ref<Image> img;
- img.instance();
- PackedByteArray arr;
- uint32_t size = tex->mWidth * tex->mHeight;
- arr.resize(size);
- memcpy(arr.ptrw(), tex->pcData, size);
- ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Image>());
- //ARGB8888 to RGBA8888
- for (int32_t i = 0; i < arr.size() / 4; i++) {
- arr.ptrw()[(4 * i) + 3] = arr[(4 * i) + 0];
- arr.ptrw()[(4 * i) + 0] = arr[(4 * i) + 1];
- arr.ptrw()[(4 * i) + 1] = arr[(4 * i) + 2];
- arr.ptrw()[(4 * i) + 2] = arr[(4 * i) + 3];
- }
- img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr);
- ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
- state.path_to_image_cache.insert(p_path, img);
- return img;
- }
- return Ref<Image>();
- } else {
- Ref<Texture2D> texture = ResourceLoader::load(p_path);
- ERR_FAIL_COND_V(texture.is_null(), Ref<Image>());
- Ref<Image> image = texture->get_data();
- ERR_FAIL_COND_V(image.is_null(), Ref<Image>());
- state.path_to_image_cache.insert(p_path, image);
- return image;
- }
-
- return Ref<Image>();
- }
-
- /* create texture from assimp data, if found in path */
- static bool CreateAssimpTexture(
- AssimpImporter::ImportState &state,
- aiString texture_path,
- String &filename,
- String &path,
- AssimpImageData &image_state) {
- filename = get_raw_string_from_assimp(texture_path);
- path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- find_texture_path(state.path, path, found);
- if (found) {
- image_state.raw_image = AssimpUtils::load_image(state, state.assimp_scene, path);
- if (image_state.raw_image.is_valid()) {
- image_state.texture.instance();
- image_state.texture->create_from_image(image_state.raw_image);
- // FIXME: Commented out during Vulkan port.
- //image_state.texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
- return true;
- }
- }
-
- return false;
- }
- /** GetAssimpTexture
- * Designed to retrieve textures for you
- */
- static bool GetAssimpTexture(
- AssimpImporter::ImportState &state,
- aiMaterial *ai_material,
- aiTextureType texture_type,
- String &filename,
- String &path,
- AssimpImageData &image_state) {
- aiString ai_filename = aiString();
- if (AI_SUCCESS == ai_material->GetTexture(texture_type, 0, &ai_filename, nullptr, nullptr, nullptr, nullptr, image_state.map_mode)) {
- return CreateAssimpTexture(state, ai_filename, filename, path, image_state);
- }
-
- return false;
- }
-};
-
-#endif // IMPORT_UTILS_IMPORTER_ASSIMP_H
diff --git a/modules/basis_universal/SCsub b/modules/basis_universal/SCsub
index dc7b176d24..1f9fde966d 100644
--- a/modules/basis_universal/SCsub
+++ b/modules/basis_universal/SCsub
@@ -6,43 +6,59 @@ Import("env_modules")
env_basisu = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
# Not unbundled so far since not widespread as shared library
thirdparty_dir = "#thirdparty/basis_universal/"
-tool_sources = [
+# Sync list with upstream CMakeLists.txt
+encoder_sources = [
+ "apg_bmp.c",
"basisu_astc_decomp.cpp",
"basisu_backend.cpp",
"basisu_basis_file.cpp",
+ "basisu_bc7enc.cpp",
"basisu_comp.cpp",
"basisu_enc.cpp",
"basisu_etc.cpp",
"basisu_frontend.cpp",
"basisu_global_selector_palette_helpers.cpp",
"basisu_gpu_texture.cpp",
+ "basisu_kernels_sse.cpp",
"basisu_pvrtc1_4.cpp",
- "basisu_resample_filters.cpp",
"basisu_resampler.cpp",
+ "basisu_resample_filters.cpp",
"basisu_ssim.cpp",
+ "basisu_uastc_enc.cpp",
+ "jpgd.cpp",
"lodepng.cpp",
]
-tool_sources = [thirdparty_dir + file for file in tool_sources]
+encoder_sources = [thirdparty_dir + "encoder/" + file for file in encoder_sources]
transcoder_sources = [thirdparty_dir + "transcoder/basisu_transcoder.cpp"]
# Treat Basis headers as system headers to avoid raising warnings. Not supported on MSVC.
if not env.msvc:
- env_basisu.Append(
- CPPFLAGS=["-isystem", Dir(thirdparty_dir).path, "-isystem", Dir(thirdparty_dir + "transcoder").path]
- )
+ env_basisu.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path])
else:
- env_basisu.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "transcoder"])
+ env_basisu.Prepend(CPPPATH=[thirdparty_dir])
if env["target"] == "debug":
- env_basisu.Append(CPPFLAGS=["-DBASISU_DEVEL_MESSAGES=1", "-DBASISD_ENABLE_DEBUG_FLAGS=1"])
+ env_basisu.Append(CPPDEFINES=[("BASISU_DEVEL_MESSAGES", 1), ("BASISD_ENABLE_DEBUG_FLAGS", 1)])
env_thirdparty = env_basisu.Clone()
env_thirdparty.disable_warnings()
if env["tools"]:
- env_thirdparty.add_source_files(env.modules_sources, tool_sources)
-env_thirdparty.add_source_files(env.modules_sources, transcoder_sources)
+ env_thirdparty.Append(CPPDEFINES=["BASISU_NO_IMG_LOADERS"])
+ env_thirdparty.add_source_files(thirdparty_obj, encoder_sources)
+env_thirdparty.add_source_files(thirdparty_obj, transcoder_sources)
+env.modules_sources += thirdparty_obj
# Godot source files
-env_basisu.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_basisu.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/basis_universal/register_types.cpp b/modules/basis_universal/register_types.cpp
index 27b299a65d..d2105d7c5d 100644
--- a/modules/basis_universal/register_types.cpp
+++ b/modules/basis_universal/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,7 +35,7 @@
#include "texture_basisu.h"
#ifdef TOOLS_ENABLED
-#include <basisu_comp.h>
+#include <encoder/basisu_comp.h>
#endif
#include <transcoder/basisu_transcoder.h>
@@ -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 normalmaps, 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;
}
@@ -233,7 +233,7 @@ static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
basist::basisu_image_info info;
tr.get_image_info(ptr, size, info, 0);
- int block_size = basist::basis_get_bytes_per_block(format);
+ int block_size = basist::basis_get_bytes_per_block_or_pixel(format);
Vector<uint8_t> gpudata;
gpudata.resize(info.m_total_blocks * block_size);
@@ -260,7 +260,7 @@ static Ref<Image> basis_universal_unpacker(const Vector<uint8_t> &p_buffer) {
};
};
- image.instance();
+ image.instantiate();
image->create(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata);
return image;
@@ -272,7 +272,7 @@ void register_basis_universal_types() {
Image::basis_universal_packer = basis_universal_packer;
#endif
Image::basis_universal_unpacker = basis_universal_unpacker;
- //ClassDB::register_class<TextureBasisU>();
+ //GDREGISTER_CLASS(TextureBasisU);
}
void unregister_basis_universal_types() {
diff --git a/modules/basis_universal/register_types.h b/modules/basis_universal/register_types.h
index 5053dc27ce..30b465e344 100644
--- a/modules/basis_universal/register_types.h
+++ b/modules/basis_universal/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/basis_universal/texture_basisu.cpp b/modules/basis_universal/texture_basisu.cpp
index 2ed0340927..9e917420ce 100644
--- a/modules/basis_universal/texture_basisu.cpp
+++ b/modules/basis_universal/texture_basisu.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,50 +33,42 @@
#include "core/os/os.h"
#ifdef TOOLS_ENABLED
-#include <basisu_comp.h>
+#include <encoder/basisu_comp.h>
#endif
#include <transcoder/basisu_transcoder.h>
void TextureBasisU::_bind_methods() {
-
ClassDB::bind_method(D_METHOD("set_basisu_data", "data"), &TextureBasisU::set_basisu_data);
ClassDB::bind_method(D_METHOD("get_basisu_data"), &TextureBasisU::get_data);
ClassDB::bind_method(D_METHOD("import"), &TextureBasisU::import);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "basisu_data"), "set_basisu_data", "get_basisu_data");
-
};
int TextureBasisU::get_width() const {
-
return tex_size.x;
};
int TextureBasisU::get_height() const {
-
return tex_size.y;
};
RID TextureBasisU::get_rid() const {
-
return texture;
};
bool TextureBasisU::has_alpha() const {
-
return false;
};
void TextureBasisU::set_flags(uint32_t p_flags) {
-
flags = p_flags;
RenderingServer::get_singleton()->texture_set_flags(texture, p_flags);
};
uint32_t TextureBasisU::get_flags() const {
-
return flags;
};
@@ -95,12 +87,10 @@ void TextureBasisU::set_basisu_data(const Vector<uint8_t>& p_data) {
Image::Format imgfmt;
if (OS::get_singleton()->has_feature("s3tc")) {
-
format = basist::cTFBC3; // get this from renderer
imgfmt = Image::FORMAT_DXT5;
} else if (OS::get_singleton()->has_feature("etc2")) {
-
format = basist::cTFETC2;
imgfmt = Image::FORMAT_ETC2_RGBA8;
};
@@ -126,7 +116,6 @@ void TextureBasisU::set_basisu_data(const Vector<uint8_t>& p_data) {
int ofs = 0;
tr.start_transcoding(ptr, size);
for (int i=0; i<info.m_total_levels; i++) {
-
basist::basisu_image_level_info level;
tr.get_image_level_info(ptr, size, level, 0, i);
@@ -141,7 +130,7 @@ void TextureBasisU::set_basisu_data(const Vector<uint8_t>& p_data) {
};
Ref<Image> img;
- img.instance();
+ img.instantiate();
img->create(info.m_width, info.m_height, info.m_total_levels > 1, imgfmt, gpudata);
RenderingServer::get_singleton()->texture_allocate(texture, tex_size.x, tex_size.y, 0, img->get_format(), RS::TEXTURE_TYPE_2D, flags);
@@ -214,19 +203,15 @@ Error TextureBasisU::import(const Ref<Image>& p_img) {
Vector<uint8_t> TextureBasisU::get_basisu_data() const {
-
return data;
};
TextureBasisU::TextureBasisU() {
-
- flags = FLAGS_DEFAULT;
texture = RenderingServer::get_singleton()->texture_create();
};
TextureBasisU::~TextureBasisU() {
-
RenderingServer::get_singleton()->free(texture);
};
diff --git a/modules/basis_universal/texture_basisu.h b/modules/basis_universal/texture_basisu.h
index 20ecf15a59..3316035404 100644
--- a/modules/basis_universal/texture_basisu.h
+++ b/modules/basis_universal/texture_basisu.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,21 +34,20 @@
#include "scene/resources/texture.h"
#ifdef TOOLS_ENABLED
-#include <basisu_comp.h>
+#include <encoder/basisu_comp.h>
#endif
#include <transcoder/basisu_transcoder.h>
#if 0
class TextureBasisU : public Texture {
-
GDCLASS(TextureBasisU, Texture);
RES_BASE_EXTENSION("butex");
RID texture;
Size2 tex_size;
- uint32_t flags;
+ uint32_t flags = FLAGS_DEFAULT;
Vector<uint8_t> data;
@@ -74,7 +73,6 @@ public:
TextureBasisU();
~TextureBasisU();
-
};
#endif
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp
index 757afeb9e3..3d47055247 100644
--- a/modules/bmp/image_loader_bmp.cpp
+++ b/modules/bmp/image_loader_bmp.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,6 +30,8 @@
#include "image_loader_bmp.h"
+#include "core/io/file_access_memory.h"
+
Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
const uint8_t *p_buffer,
const uint8_t *p_color_buffer,
@@ -89,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;
@@ -128,23 +132,19 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
line_ptr += 1;
} break;
case 24: {
- uint32_t color = *((uint32_t *)line_ptr);
-
- write_buffer[index + 2] = color & 0xff;
- write_buffer[index + 1] = (color >> 8) & 0xff;
- write_buffer[index + 0] = (color >> 16) & 0xff;
+ write_buffer[index + 2] = line_ptr[0];
+ write_buffer[index + 1] = line_ptr[1];
+ write_buffer[index + 0] = line_ptr[2];
write_buffer[index + 3] = 0xff;
index += 4;
line_ptr += 3;
} break;
case 32: {
- uint32_t color = *((uint32_t *)line_ptr);
-
- write_buffer[index + 2] = color & 0xff;
- write_buffer[index + 1] = (color >> 8) & 0xff;
- write_buffer[index + 0] = (color >> 16) & 0xff;
- write_buffer[index + 3] = color >> 24;
+ write_buffer[index + 2] = line_ptr[0];
+ write_buffer[index + 1] = line_ptr[1];
+ write_buffer[index + 0] = line_ptr[2];
+ write_buffer[index + 3] = line_ptr[3];
index += 4;
line_ptr += 4;
@@ -170,11 +170,9 @@ Error ImageLoaderBMP::convert_to_image(Ref<Image> p_image,
const uint8_t *cb = p_color_buffer;
for (unsigned int i = 0; i < color_table_size; ++i) {
- uint32_t color = *((uint32_t *)cb);
-
- pal[i * 4 + 0] = (color >> 16) & 0xff;
- pal[i * 4 + 1] = (color >> 8) & 0xff;
- pal[i * 4 + 2] = (color)&0xff;
+ pal[i * 4 + 0] = cb[2];
+ pal[i * 4 + 1] = cb[1];
+ pal[i * 4 + 2] = cb[0];
pal[i * 4 + 3] = 0xff;
cb += 4;
@@ -209,7 +207,7 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
// A valid bmp file should always at least have a
// file header and a minimal info header
- if (f->get_len() > BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_MIN_SIZE) {
+ if (f->get_length() > BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_MIN_SIZE) {
// File Header
bmp_header.bmp_file_header.bmp_signature = f->get_16();
if (bmp_header.bmp_file_header.bmp_signature == BITMAP_SIGNATURE) {
@@ -250,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;
@@ -273,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);
@@ -293,9 +289,21 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
return err;
}
-void ImageLoaderBMP::get_recognized_extensions(
- List<String> *p_extensions) const {
+void ImageLoaderBMP::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("bmp");
}
-ImageLoaderBMP::ImageLoaderBMP() {}
+static Ref<Image> _bmp_mem_loader_func(const uint8_t *p_bmp, int p_size) {
+ FileAccessMemory memfile;
+ Error open_memfile_error = memfile.open_custom(p_bmp, p_size);
+ ERR_FAIL_COND_V_MSG(open_memfile_error, Ref<Image>(), "Could not create memfile for BMP image buffer.");
+ Ref<Image> img;
+ img.instantiate();
+ Error load_error = ImageLoaderBMP().load_image(img, &memfile, false, 1.0f);
+ ERR_FAIL_COND_V_MSG(load_error, Ref<Image>(), "Failed to load BMP image.");
+ return img;
+}
+
+ImageLoaderBMP::ImageLoaderBMP() {
+ Image::_bmp_mem_loader_func = _bmp_mem_loader_func;
+}
diff --git a/modules/bmp/image_loader_bmp.h b/modules/bmp/image_loader_bmp.h
index 3f10a1c598..379e971458 100644
--- a/modules/bmp/image_loader_bmp.h
+++ b/modules/bmp/image_loader_bmp.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -55,24 +55,24 @@ protected:
struct bmp_header_s {
struct bmp_file_header_s {
- uint16_t bmp_signature;
- uint32_t bmp_file_size;
- uint32_t bmp_file_padding;
- uint32_t bmp_file_offset;
+ uint16_t bmp_signature = 0;
+ uint32_t bmp_file_size = 0;
+ uint32_t bmp_file_padding = 0;
+ uint32_t bmp_file_offset = 0;
} bmp_file_header;
struct bmp_info_header_s {
- uint32_t bmp_header_size;
- uint32_t bmp_width;
- uint32_t bmp_height;
- uint16_t bmp_planes;
- uint16_t bmp_bit_count;
- uint32_t bmp_compression;
- uint32_t bmp_size_image;
- uint32_t bmp_pixels_per_meter_x;
- uint32_t bmp_pixels_per_meter_y;
- uint32_t bmp_colors_used;
- uint32_t bmp_important_colors;
+ uint32_t bmp_header_size = 0;
+ uint32_t bmp_width = 0;
+ uint32_t bmp_height = 0;
+ uint16_t bmp_planes = 0;
+ uint16_t bmp_bit_count = 0;
+ uint32_t bmp_compression = 0;
+ uint32_t bmp_size_image = 0;
+ uint32_t bmp_pixels_per_meter_x = 0;
+ uint32_t bmp_pixels_per_meter_y = 0;
+ uint32_t bmp_colors_used = 0;
+ uint32_t bmp_important_colors = 0;
} bmp_info_header;
};
diff --git a/modules/bmp/register_types.cpp b/modules/bmp/register_types.cpp
index 6220e956d6..d36ce9cdaf 100644
--- a/modules/bmp/register_types.cpp
+++ b/modules/bmp/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bmp/register_types.h b/modules/bmp/register_types.h
index e7561dc32d..3ce81eba1b 100644
--- a/modules/bmp/register_types.h
+++ b/modules/bmp/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/SCsub b/modules/bullet/SCsub
index 21bdcca18e..09509abc44 100644
--- a/modules/bullet/SCsub
+++ b/modules/bullet/SCsub
@@ -7,9 +7,13 @@ env_bullet = env_modules.Clone()
# Thirdparty source files
+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"])
thirdparty_dir = "#thirdparty/bullet/"
bullet2_src = [
@@ -185,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",
@@ -196,20 +201,24 @@ 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()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
# Godot source files
-env_bullet.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_bullet.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/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
index 7bf1a1b2b6..8b45507198 100644
--- a/modules/bullet/area_bullet.cpp
+++ b/modules/bullet/area_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -94,10 +94,9 @@ void AreaBullet::dispatch_callbacks() {
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);
- if (!areaGodoObject) {
- event.event_callback_id = ObjectID();
+ if (!event.event_callback.is_valid()) {
+ event.event_callback = Callable();
return;
}
@@ -108,7 +107,8 @@ void AreaBullet::call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3
call_event_res[4] = 0; // self_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() {
@@ -270,13 +270,12 @@ 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));
@@ -285,7 +284,7 @@ void AreaBullet::set_event_callback(Type p_callbackObjectType, ObjectID p_id, co
}
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 c927cd9eef..4d9227c31a 100644
--- a/modules/bullet/area_bullet.h
+++ b/modules/bullet/area_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define AREABULLET_H
#include "collision_object_bullet.h"
-#include "core/vector.h"
+#include "core/templates/vector.h"
#include "servers/physics_server_3d.h"
#include "space_bullet.h"
@@ -47,8 +47,7 @@ class AreaBullet : public RigidCollisionObjectBullet {
public:
struct InOutEventCallback {
- ObjectID event_callback_id;
- StringName event_callback_method;
+ Callable event_callback;
InOutEventCallback() {}
};
@@ -80,18 +79,18 @@ public:
private:
// These are used by function callEvent. Instead to create this each call I create if one time.
Variant call_event_res[5];
- Variant *call_event_res_ptr[5];
+ Variant *call_event_res_ptr[5] = {};
- btGhostObject *btGhost;
+ btGhostObject *btGhost = nullptr;
Vector<OverlappingObjectData> overlappingObjects;
bool monitorable = true;
PhysicsServer3D::AreaSpaceOverrideMode spOv_mode = PhysicsServer3D::AREA_SPACE_OVERRIDE_DISABLED;
bool spOv_gravityPoint = false;
- real_t spOv_gravityPointDistanceScale = 0;
- real_t spOv_gravityPointAttenuation = 1;
+ real_t spOv_gravityPointDistanceScale = 0.0;
+ real_t spOv_gravityPointAttenuation = 1.0;
Vector3 spOv_gravityVec = Vector3(0, -1, 0);
- real_t spOv_gravityMag = 10;
+ real_t spOv_gravityMag = 10.0;
real_t spOv_linearDump = 0.1;
real_t spOv_angularDump = 0.1;
int spOv_priority = 0;
@@ -145,7 +144,6 @@ public:
virtual void dispatch_callbacks();
void call_event(CollisionObjectBullet *p_otherObject, PhysicsServer3D::AreaBodyStatus p_status);
- void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
void scratch();
void clear_overlaps(bool p_notify);
@@ -163,7 +161,7 @@ public:
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/btRayShape.cpp b/modules/bullet/btRayShape.cpp
index a754ca6a89..109854c9dd 100644
--- a/modules/bullet/btRayShape.cpp
+++ b/modules/bullet/btRayShape.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,11 +39,9 @@
*/
btRayShape::btRayShape(btScalar length) :
- btConvexInternalShape(),
- m_shapeAxis(0, 0, 1) {
+ btConvexInternalShape() {
m_shapeType = CUSTOM_CONVEX_SHAPE_TYPE;
setLength(length);
- slipsOnSlope = false;
}
btRayShape::~btRayShape() {
diff --git a/modules/bullet/btRayShape.h b/modules/bullet/btRayShape.h
index d9ecde81e6..330755aa31 100644
--- a/modules/bullet/btRayShape.h
+++ b/modules/bullet/btRayShape.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,10 +42,10 @@
/// Ray shape around z axis
ATTRIBUTE_ALIGNED16(class)
btRayShape : public btConvexInternalShape {
- btScalar m_length;
- bool slipsOnSlope;
+ btScalar m_length = 0;
+ bool slipsOnSlope = false;
/// The default axis is the z
- btVector3 m_shapeAxis;
+ btVector3 m_shapeAxis = btVector3(0, 0, 1);
btTransform m_cacheSupportPoint;
btScalar m_cacheScaledLength;
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 8f64c11867..684a20cf4d 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,9 +32,9 @@
#include "bullet_utilities.h"
#include "cone_twist_joint_bullet.h"
-#include "core/class_db.h"
-#include "core/error_macros.h"
-#include "core/ustring.h"
+#include "core/error/error_macros.h"
+#include "core/object/class_db.h"
+#include "core/string/ustring.h"
#include "generic_6dof_joint_bullet.h"
#include "hinge_joint_bullet.h"
#include "pin_joint_bullet.h"
@@ -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 Transform &p_transform, bool p_disabled) {
- AreaBullet *area = area_owner.getornull(p_area);
+void BulletPhysicsServer3D::area_add_shape(RID p_area, RID p_shape, const Transform3D &p_transform, bool p_disabled) {
+ 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 Transform &p_transform) {
- AreaBullet *area = area_owner.getornull(p_area);
+void BulletPhysicsServer3D::area_set_shape_transform(RID p_area, int p_shape_idx, const Transform3D &p_transform) {
+ 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();
}
-Transform BulletPhysicsServer3D::area_get_shape_transform(RID p_area, int p_shape_idx) const {
- AreaBullet *area = area_owner.getornull(p_area);
- ERR_FAIL_COND_V(!area, Transform());
+Transform3D BulletPhysicsServer3D::area_get_shape_transform(RID p_area, int p_shape_idx) const {
+ 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,73 +372,67 @@ 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);
}
}
-void BulletPhysicsServer3D::area_set_transform(RID p_area, const Transform &p_transform) {
- AreaBullet *area = area_owner.getornull(p_area);
+void BulletPhysicsServer3D::area_set_transform(RID p_area, const Transform3D &p_transform) {
+ AreaBullet *area = area_owner.get_or_null(p_area);
ERR_FAIL_COND(!area);
area->set_transform(p_transform);
}
-Transform BulletPhysicsServer3D::area_get_transform(RID p_area) const {
- AreaBullet *area = area_owner.getornull(p_area);
- ERR_FAIL_COND_V(!area, Transform());
+Transform3D BulletPhysicsServer3D::area_get_transform(RID p_area) const {
+ 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);
}
-bool BulletPhysicsServer3D::area_is_ray_pickable(RID p_area) const {
- AreaBullet *area = area_owner.getornull(p_area);
- ERR_FAIL_COND_V(!area, false);
- return area->is_ray_pickable();
-}
-
RID BulletPhysicsServer3D::body_create(BodyMode p_mode, bool p_init_sleeping) {
RigidBodyBullet *body = bulletnew(RigidBodyBullet);
body->set_mode(p_mode);
@@ -451,24 +445,24 @@ 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);
}
if (body->get_space() == space) {
- return; //pointles
+ return; //pointless
}
body->set_space(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();
@@ -479,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 Transform &p_transform, bool p_disabled) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+void BulletPhysicsServer3D::body_add_shape(RID p_body, RID p_shape, const Transform3D &p_transform, bool p_disabled) {
+ 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 Transform &p_transform) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+void BulletPhysicsServer3D::body_set_shape_transform(RID p_body, int p_shape_idx, const Transform3D &p_transform) {
+ 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);
@@ -533,28 +527,28 @@ RID BulletPhysicsServer3D::body_get_shape(RID p_body, int p_shape_idx) const {
return shape->get_self();
}
-Transform BulletPhysicsServer3D::body_get_shape_transform(RID p_body, int p_shape_idx) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
- ERR_FAIL_COND_V(!body, Transform());
+Transform3D BulletPhysicsServer3D::body_get_shape_transform(RID p_body, int p_shape_idx) const {
+ 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();
@@ -575,72 +569,72 @@ 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();
}
void BulletPhysicsServer3D::body_set_user_flags(RID p_body, uint32_t p_flags) {
- // This function si not currently supported
+ // This function is not currently supported
}
uint32_t BulletPhysicsServer3D::body_get_user_flags(RID p_body) const {
- // This function si not currently supported
+ // This function is not currently supported
return 0;
}
-void BulletPhysicsServer3D::body_set_param(RID p_body, BodyParameter p_param, float p_value) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+void BulletPhysicsServer3D::body_set_param(RID p_body, BodyParameter p_param, real_t p_value) {
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_param(p_param, p_value);
}
-float BulletPhysicsServer3D::body_get_param(RID p_body, BodyParameter p_param) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+real_t BulletPhysicsServer3D::body_get_param(RID p_body, BodyParameter p_param) const {
+ 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()) {
@@ -649,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()) {
@@ -660,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();
@@ -754,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]);
@@ -794,76 +788,79 @@ 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();
}
-void BulletPhysicsServer3D::body_set_contacts_reported_depth_threshold(RID p_body, float p_threshold) {
+void BulletPhysicsServer3D::body_set_contacts_reported_depth_threshold(RID p_body, real_t p_threshold) {
// Not supported by bullet and even Godot
}
-float BulletPhysicsServer3D::body_get_contacts_reported_depth_threshold(RID p_body) const {
+real_t BulletPhysicsServer3D::body_get_contacts_reported_depth_threshold(RID p_body) const {
// Not supported by bullet and even Godot
return 0.;
}
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, Object *p_receiver, const StringName &p_method, const Variant &p_udata) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+void BulletPhysicsServer3D::body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata) {
+ RigidBodyBullet *body = rigid_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
- body->set_force_integration_callback(p_receiver ? p_receiver->get_instance_id() : ObjectID(), p_method, p_udata);
+ 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);
}
-bool BulletPhysicsServer3D::body_is_ray_pickable(RID p_body) const {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
- ERR_FAIL_COND_V(!body, false);
- return body->is_ray_pickable();
-}
-
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 Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_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.get_or_null(p_body);
ERR_FAIL_COND_V(!body, false);
ERR_FAIL_COND_V(!body->get_space(), false);
- return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result, p_exclude_raycast_shapes);
+ return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result, p_exclude_raycast_shapes, p_exclude);
}
-int BulletPhysicsServer3D::body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin) {
- RigidBodyBullet *body = rigid_body_owner.getornull(p_body);
+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.get_or_null(p_body);
ERR_FAIL_COND_V(!body, 0);
ERR_FAIL_COND_V(!body->get_space(), 0);
@@ -880,32 +877,32 @@ RID BulletPhysicsServer3D::soft_body_create(bool p_init_sleeping) {
CreateThenReturnRID(soft_body_owner, body);
}
-void BulletPhysicsServer3D::soft_body_update_rendering_server(RID p_body, class SoftBodyRenderingServerHandler *p_rendering_server_handler) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+void BulletPhysicsServer3D::soft_body_update_rendering_server(RID p_body, RenderingServerHandler *p_rendering_server_handler) {
+ 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);
}
if (body->get_space() == space) {
- return; //pointles
+ return; //pointless
}
body->set_space(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();
@@ -915,48 +912,55 @@ 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);
}
+AABB BulletPhysicsServer::soft_body_get_bounds(RID p_body) const {
+ SoftBodyBullet *body = soft_body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, AABB());
+
+ return body->get_bounds();
+}
+
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);
@@ -964,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);
@@ -977,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]);
@@ -995,184 +999,125 @@ Variant BulletPhysicsServer3D::soft_body_get_state(RID p_body, BodyState p_state
return Variant();
}
-void BulletPhysicsServer3D::soft_body_set_transform(RID p_body, const Transform &p_transform) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+void BulletPhysicsServer3D::soft_body_set_transform(RID p_body, const Transform3D &p_transform) {
+ SoftBodyBullet *body = soft_body_owner.get_or_null(p_body);
ERR_FAIL_COND(!body);
body->set_soft_transform(p_transform);
}
-Vector3 BulletPhysicsServer3D::soft_body_get_vertex_position(RID p_body, int vertex_index) const {
- const SoftBodyBullet *body = soft_body_owner.getornull(p_body);
- Vector3 pos;
- ERR_FAIL_COND_V(!body, pos);
-
- body->get_node_position(vertex_index, pos);
- return pos;
-}
-
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);
}
-bool BulletPhysicsServer3D::soft_body_is_ray_pickable(RID p_body) const {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
- ERR_FAIL_COND_V(!body, false);
- return body->is_ray_pickable();
-}
-
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) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+int BulletPhysicsServer3D::soft_body_get_simulation_precision(RID p_body) const {
+ 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) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+real_t BulletPhysicsServer3D::soft_body_get_total_mass(RID p_body) const {
+ 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) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+void BulletPhysicsServer3D::soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) const {
+ 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_areaAngular_stiffness(RID p_body, real_t p_stiffness) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
- ERR_FAIL_COND(!body);
- body->set_areaAngular_stiffness(p_stiffness);
-}
-
-real_t BulletPhysicsServer3D::soft_body_get_areaAngular_stiffness(RID p_body) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
- ERR_FAIL_COND_V(!body, 0.f);
- return body->get_areaAngular_stiffness();
-}
-
-void BulletPhysicsServer3D::soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
- ERR_FAIL_COND(!body);
- body->set_volume_stiffness(p_stiffness);
-}
-
-real_t BulletPhysicsServer3D::soft_body_get_volume_stiffness(RID p_body) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
- ERR_FAIL_COND_V(!body, 0.f);
- return body->get_volume_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) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+real_t BulletPhysicsServer3D::soft_body_get_pressure_coefficient(RID p_body) const {
+ 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_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
- ERR_FAIL_COND(!body);
- return body->set_pose_matching_coefficient(p_pose_matching_coefficient);
-}
-
-real_t BulletPhysicsServer3D::soft_body_get_pose_matching_coefficient(RID p_body) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
- ERR_FAIL_COND_V(!body, 0.f);
- return body->get_pose_matching_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) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+real_t BulletPhysicsServer3D::soft_body_get_damping_coefficient(RID p_body) const {
+ 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) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+real_t BulletPhysicsServer3D::soft_body_get_drag_coefficient(RID p_body) const {
+ 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) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+Vector3 BulletPhysicsServer3D::soft_body_get_point_global_position(RID p_body, int p_point_index) const {
+ 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);
return pos;
}
-Vector3 BulletPhysicsServer3D::soft_body_get_point_offset(RID p_body, int p_point_index) const {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
- ERR_FAIL_COND_V(!body, Vector3());
- Vector3 res;
- body->get_node_offset(p_point_index, res);
- return res;
-}
-
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) {
- SoftBodyBullet *body = soft_body_owner.getornull(p_body);
+bool BulletPhysicsServer3D::soft_body_is_point_pinned(RID p_body, int p_point_index) const {
+ 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();
}
@@ -1187,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());
}
@@ -1221,16 +1166,16 @@ RID BulletPhysicsServer3D::joint_create_pin(RID p_body_A, const Vector3 &p_local
CreateThenReturnRID(joint_owner, joint);
}
-void BulletPhysicsServer3D::pin_joint_set_param(RID p_joint, PinJointParam p_param, float p_value) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+void BulletPhysicsServer3D::pin_joint_set_param(RID p_joint, PinJointParam p_param, real_t p_value) {
+ 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);
pin_joint->set_param(p_param, p_value);
}
-float BulletPhysicsServer3D::pin_joint_get_param(RID p_joint, PinJointParam p_param) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+real_t BulletPhysicsServer3D::pin_joint_get_param(RID p_joint, PinJointParam p_param) const {
+ 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);
@@ -1238,7 +1183,7 @@ float BulletPhysicsServer3D::pin_joint_get_param(RID p_joint, PinJointParam p_pa
}
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);
@@ -1246,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);
@@ -1254,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);
@@ -1262,21 +1207,21 @@ 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);
return pin_joint->getPivotInB();
}
-RID BulletPhysicsServer3D::joint_create_hinge(RID p_body_A, const Transform &p_hinge_A, RID p_body_B, const Transform &p_hinge_B) {
- RigidBodyBullet *body_A = rigid_body_owner.getornull(p_body_A);
+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.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());
}
@@ -1290,13 +1235,13 @@ RID BulletPhysicsServer3D::joint_create_hinge(RID p_body_A, const Transform &p_h
}
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());
}
@@ -1309,16 +1254,16 @@ RID BulletPhysicsServer3D::joint_create_hinge_simple(RID p_body_A, const Vector3
CreateThenReturnRID(joint_owner, joint);
}
-void BulletPhysicsServer3D::hinge_joint_set_param(RID p_joint, HingeJointParam p_param, float p_value) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+void BulletPhysicsServer3D::hinge_joint_set_param(RID p_joint, HingeJointParam p_param, real_t p_value) {
+ 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);
hinge_joint->set_param(p_param, p_value);
}
-float BulletPhysicsServer3D::hinge_joint_get_param(RID p_joint, HingeJointParam p_param) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+real_t BulletPhysicsServer3D::hinge_joint_get_param(RID p_joint, HingeJointParam p_param) const {
+ 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);
@@ -1326,7 +1271,7 @@ float 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);
@@ -1334,21 +1279,21 @@ 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);
return hinge_joint->get_flag(p_flag);
}
-RID BulletPhysicsServer3D::joint_create_slider(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) {
- RigidBodyBullet *body_A = rigid_body_owner.getornull(p_body_A);
+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.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());
}
@@ -1361,30 +1306,30 @@ RID BulletPhysicsServer3D::joint_create_slider(RID p_body_A, const Transform &p_
CreateThenReturnRID(joint_owner, joint);
}
-void BulletPhysicsServer3D::slider_joint_set_param(RID p_joint, SliderJointParam p_param, float p_value) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+void BulletPhysicsServer3D::slider_joint_set_param(RID p_joint, SliderJointParam p_param, real_t p_value) {
+ 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);
slider_joint->set_param(p_param, p_value);
}
-float BulletPhysicsServer3D::slider_joint_get_param(RID p_joint, SliderJointParam p_param) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+real_t BulletPhysicsServer3D::slider_joint_get_param(RID p_joint, SliderJointParam p_param) const {
+ 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);
return slider_joint->get_param(p_param);
}
-RID BulletPhysicsServer3D::joint_create_cone_twist(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) {
- RigidBodyBullet *body_A = rigid_body_owner.getornull(p_body_A);
+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.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());
}
@@ -1395,30 +1340,30 @@ RID BulletPhysicsServer3D::joint_create_cone_twist(RID p_body_A, const Transform
CreateThenReturnRID(joint_owner, joint);
}
-void BulletPhysicsServer3D::cone_twist_joint_set_param(RID p_joint, ConeTwistJointParam p_param, float p_value) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+void BulletPhysicsServer3D::cone_twist_joint_set_param(RID p_joint, ConeTwistJointParam p_param, real_t p_value) {
+ 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);
coneTwist_joint->set_param(p_param, p_value);
}
-float BulletPhysicsServer3D::cone_twist_joint_get_param(RID p_joint, ConeTwistJointParam p_param) const {
- JointBullet *joint = joint_owner.getornull(p_joint);
+real_t BulletPhysicsServer3D::cone_twist_joint_get_param(RID p_joint, ConeTwistJointParam p_param) const {
+ 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);
return coneTwist_joint->get_param(p_param);
}
-RID BulletPhysicsServer3D::joint_create_generic_6dof(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) {
- RigidBodyBullet *body_A = rigid_body_owner.getornull(p_body_A);
+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.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());
}
@@ -1431,16 +1376,16 @@ RID BulletPhysicsServer3D::joint_create_generic_6dof(RID p_body_A, const Transfo
CreateThenReturnRID(joint_owner, joint);
}
-void BulletPhysicsServer3D::generic_6dof_joint_set_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param, float p_value) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+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.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);
generic_6dof_joint->set_param(p_axis, p_param, p_value);
}
-float BulletPhysicsServer3D::generic_6dof_joint_get_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param) {
- JointBullet *joint = joint_owner.getornull(p_joint);
+real_t BulletPhysicsServer3D::generic_6dof_joint_get_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param) {
+ 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);
@@ -1448,7 +1393,7 @@ float 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);
@@ -1456,42 +1401,26 @@ 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);
return generic_6dof_joint->get_flag(p_axis, p_flag);
}
-void BulletPhysicsServer3D::generic_6dof_joint_set_precision(RID p_joint, int p_precision) {
- JointBullet *joint = joint_owner.getornull(p_joint);
- ERR_FAIL_COND(!joint);
- ERR_FAIL_COND(joint->get_type() != JOINT_6DOF);
- Generic6DOFJointBullet *generic_6dof_joint = static_cast<Generic6DOFJointBullet *>(joint);
- generic_6dof_joint->set_precision(p_precision);
-}
-
-int BulletPhysicsServer3D::generic_6dof_joint_get_precision(RID p_joint) {
- JointBullet *joint = joint_owner.getornull(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);
- return generic_6dof_joint->get_precision();
-}
-
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);
@@ -1501,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);
@@ -1509,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);
@@ -1519,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();
@@ -1541,7 +1470,7 @@ void BulletPhysicsServer3D::init() {
BulletPhysicsDirectBodyState3D::initSingleton();
}
-void BulletPhysicsServer3D::step(float p_deltaTime) {
+void BulletPhysicsServer3D::step(real_t p_deltaTime) {
if (!active) {
return;
}
@@ -1553,9 +1482,6 @@ void BulletPhysicsServer3D::step(float p_deltaTime) {
}
}
-void BulletPhysicsServer3D::sync() {
-}
-
void BulletPhysicsServer3D::flush_queries() {
if (!active) {
return;
@@ -1576,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 6078babaf8..94635b5bfc 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,8 +32,8 @@
#define BULLET_PHYSICS_SERVER_H
#include "area_bullet.h"
-#include "core/rid.h"
-#include "core/rid_owner.h"
+#include "core/templates/rid.h"
+#include "core/templates/rid_owner.h"
#include "joint_bullet.h"
#include "rigid_body_bullet.h"
#include "servers/physics_server_3d.h"
@@ -134,12 +134,12 @@ public:
virtual void area_set_space_override_mode(RID p_area, AreaSpaceOverrideMode p_mode) override;
virtual AreaSpaceOverrideMode area_get_space_override_mode(RID p_area) const override;
- virtual void area_add_shape(RID p_area, RID p_shape, const Transform &p_transform = Transform(), bool p_disabled = false) override;
+ virtual void area_add_shape(RID p_area, RID p_shape, const Transform3D &p_transform = Transform3D(), bool p_disabled = false) override;
virtual void area_set_shape(RID p_area, int p_shape_idx, RID p_shape) override;
- virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform &p_transform) override;
+ virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform3D &p_transform) override;
virtual int area_get_shape_count(RID p_area) const override;
virtual RID area_get_shape(RID p_area, int p_shape_idx) const override;
- virtual Transform area_get_shape_transform(RID p_area, int p_shape_idx) const override;
+ virtual Transform3D area_get_shape_transform(RID p_area, int p_shape_idx) const override;
virtual void area_remove_shape(RID p_area, int p_shape_idx) override;
virtual void area_clear_shapes(RID p_area) override;
virtual void area_set_shape_disabled(RID p_area, int p_shape_idx, bool p_disabled) override;
@@ -153,21 +153,20 @@ public:
virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value) override;
virtual Variant area_get_param(RID p_area, AreaParameter p_param) const override;
- virtual void area_set_transform(RID p_area, const Transform &p_transform) override;
- virtual Transform area_get_transform(RID p_area) const override;
+ virtual void area_set_transform(RID p_area, const Transform3D &p_transform) override;
+ virtual Transform3D area_get_transform(RID p_area) const override;
virtual void area_set_collision_mask(RID p_area, uint32_t p_mask) override;
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;
- virtual bool area_is_ray_pickable(RID p_area) const override;
/* RIGID BODY API */
- virtual RID body_create(BodyMode p_mode = BODY_MODE_RIGID, bool p_init_sleeping = false) override;
+ virtual RID body_create(BodyMode p_mode = BODY_MODE_DYNAMIC, bool p_init_sleeping = false) override;
virtual void body_set_space(RID p_body, RID p_space) override;
virtual RID body_get_space(RID p_body) const override;
@@ -175,14 +174,14 @@ public:
virtual void body_set_mode(RID p_body, BodyMode p_mode) override;
virtual BodyMode body_get_mode(RID p_body) const override;
- virtual void body_add_shape(RID p_body, RID p_shape, const Transform &p_transform = Transform(), bool p_disabled = false) override;
+ virtual void body_add_shape(RID p_body, RID p_shape, const Transform3D &p_transform = Transform3D(), bool p_disabled = false) override;
// Not supported, Please remove and add new shape
virtual void body_set_shape(RID p_body, int p_shape_idx, RID p_shape) override;
- virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform &p_transform) override;
+ virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform3D &p_transform) override;
virtual int body_get_shape_count(RID p_body) const override;
virtual RID body_get_shape(RID p_body, int p_shape_idx) const override;
- virtual Transform body_get_shape_transform(RID p_body, int p_shape_idx) const override;
+ virtual Transform3D body_get_shape_transform(RID p_body, int p_shape_idx) const override;
virtual void body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) override;
@@ -207,8 +206,8 @@ public:
/// This is not supported by physics server
virtual uint32_t body_get_user_flags(RID p_body) const override;
- virtual void body_set_param(RID p_body, BodyParameter p_param, float p_value) override;
- virtual float body_get_param(RID p_body, BodyParameter p_param) const override;
+ virtual void body_set_param(RID p_body, BodyParameter p_param, real_t p_value) override;
+ virtual real_t body_get_param(RID p_body, BodyParameter p_param) const override;
virtual void body_set_kinematic_safe_margin(RID p_body, real_t p_margin) override;
virtual real_t body_get_kinematic_safe_margin(RID p_body) const override;
@@ -241,33 +240,34 @@ public:
virtual void body_set_max_contacts_reported(RID p_body, int p_contacts) override;
virtual int body_get_max_contacts_reported(RID p_body) const override;
- virtual void body_set_contacts_reported_depth_threshold(RID p_body, float p_threshold) override;
- virtual float body_get_contacts_reported_depth_threshold(RID p_body) const override;
+ virtual void body_set_contacts_reported_depth_threshold(RID p_body, real_t p_threshold) override;
+ virtual real_t body_get_contacts_reported_depth_threshold(RID p_body) const override;
virtual void body_set_omit_force_integration(RID p_body, bool p_omit) override;
virtual bool body_is_omitting_force_integration(RID p_body) const override;
- virtual void body_set_force_integration_callback(RID p_body, Object *p_receiver, const StringName &p_method, const Variant &p_udata = Variant()) override;
+ virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) override;
virtual void body_set_ray_pickable(RID p_body, bool p_enable) override;
- virtual bool body_is_ray_pickable(RID p_body) const override;
// this function only works on physics process, errors and returns null otherwise
virtual PhysicsDirectBodyState3D *body_get_direct_state(RID p_body) override;
- virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true) override;
- virtual int body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001) override;
+ virtual bool body_test_motion(RID p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = nullptr, bool p_exclude_raycast_shapes = true, const Set<RID> &p_exclude = Set<RID>()) override;
+ virtual int 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 = 0.001) override;
/* SOFT BODY API */
virtual RID soft_body_create(bool p_init_sleeping = false) override;
- virtual void soft_body_update_rendering_server(RID p_body, class SoftBodyRenderingServerHandler *p_rendering_server_handler) override;
+ virtual void soft_body_update_rendering_server(RID p_body, RenderingServerHandler *p_rendering_server_handler) override;
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;
virtual void soft_body_set_collision_layer(RID p_body, uint32_t p_layer) override;
virtual uint32_t soft_body_get_collision_layer(RID p_body) const override;
@@ -283,47 +283,34 @@ public:
virtual Variant soft_body_get_state(RID p_body, BodyState p_state) const override;
/// Special function. This function has bad performance
- virtual void soft_body_set_transform(RID p_body, const Transform &p_transform) override;
- virtual Vector3 soft_body_get_vertex_position(RID p_body, int vertex_index) const override;
+ virtual void soft_body_set_transform(RID p_body, const Transform3D &p_transform) override;
virtual void soft_body_set_ray_pickable(RID p_body, bool p_enable) override;
- virtual bool soft_body_is_ray_pickable(RID p_body) const override;
virtual void soft_body_set_simulation_precision(RID p_body, int p_simulation_precision) override;
- virtual int soft_body_get_simulation_precision(RID p_body) override;
+ virtual int soft_body_get_simulation_precision(RID p_body) const override;
virtual void soft_body_set_total_mass(RID p_body, real_t p_total_mass) override;
- virtual real_t soft_body_get_total_mass(RID p_body) override;
+ virtual real_t soft_body_get_total_mass(RID p_body) const override;
virtual void soft_body_set_linear_stiffness(RID p_body, real_t p_stiffness) override;
- virtual real_t soft_body_get_linear_stiffness(RID p_body) override;
-
- virtual void soft_body_set_areaAngular_stiffness(RID p_body, real_t p_stiffness) override;
- virtual real_t soft_body_get_areaAngular_stiffness(RID p_body) override;
-
- virtual void soft_body_set_volume_stiffness(RID p_body, real_t p_stiffness) override;
- virtual real_t soft_body_get_volume_stiffness(RID p_body) override;
+ virtual real_t soft_body_get_linear_stiffness(RID p_body) const override;
virtual void soft_body_set_pressure_coefficient(RID p_body, real_t p_pressure_coefficient) override;
- virtual real_t soft_body_get_pressure_coefficient(RID p_body) override;
-
- virtual void soft_body_set_pose_matching_coefficient(RID p_body, real_t p_pose_matching_coefficient) override;
- virtual real_t soft_body_get_pose_matching_coefficient(RID p_body) override;
+ virtual real_t soft_body_get_pressure_coefficient(RID p_body) const override;
virtual void soft_body_set_damping_coefficient(RID p_body, real_t p_damping_coefficient) override;
- virtual real_t soft_body_get_damping_coefficient(RID p_body) override;
+ virtual real_t soft_body_get_damping_coefficient(RID p_body) const override;
virtual void soft_body_set_drag_coefficient(RID p_body, real_t p_drag_coefficient) override;
- virtual real_t soft_body_get_drag_coefficient(RID p_body) override;
+ virtual real_t soft_body_get_drag_coefficient(RID p_body) const override;
virtual void soft_body_move_point(RID p_body, int p_point_index, const Vector3 &p_global_position) override;
- virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) override;
-
- virtual Vector3 soft_body_get_point_offset(RID p_body, int p_point_index) const override;
+ virtual Vector3 soft_body_get_point_global_position(RID p_body, int p_point_index) const override;
virtual void soft_body_remove_all_pinned_points(RID p_body) override;
virtual void soft_body_pin_point(RID p_body, int p_point_index, bool p_pin) override;
- virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) override;
+ virtual bool soft_body_is_point_pinned(RID p_body, int p_point_index) const override;
/* JOINT API */
@@ -337,8 +324,8 @@ public:
virtual RID joint_create_pin(RID p_body_A, const Vector3 &p_local_A, RID p_body_B, const Vector3 &p_local_B) override;
- virtual void pin_joint_set_param(RID p_joint, PinJointParam p_param, float p_value) override;
- virtual float pin_joint_get_param(RID p_joint, PinJointParam p_param) const override;
+ virtual void pin_joint_set_param(RID p_joint, PinJointParam p_param, real_t p_value) override;
+ virtual real_t pin_joint_get_param(RID p_joint, PinJointParam p_param) const override;
virtual void pin_joint_set_local_a(RID p_joint, const Vector3 &p_A) override;
virtual Vector3 pin_joint_get_local_a(RID p_joint) const override;
@@ -346,39 +333,36 @@ public:
virtual void pin_joint_set_local_b(RID p_joint, const Vector3 &p_B) override;
virtual Vector3 pin_joint_get_local_b(RID p_joint) const override;
- virtual RID joint_create_hinge(RID p_body_A, const Transform &p_hinge_A, RID p_body_B, const Transform &p_hinge_B) override;
+ virtual RID joint_create_hinge(RID p_body_A, const Transform3D &p_hinge_A, RID p_body_B, const Transform3D &p_hinge_B) override;
virtual RID 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) override;
- virtual void hinge_joint_set_param(RID p_joint, HingeJointParam p_param, float p_value) override;
- virtual float hinge_joint_get_param(RID p_joint, HingeJointParam p_param) const override;
+ virtual void hinge_joint_set_param(RID p_joint, HingeJointParam p_param, real_t p_value) override;
+ virtual real_t hinge_joint_get_param(RID p_joint, HingeJointParam p_param) const override;
virtual void hinge_joint_set_flag(RID p_joint, HingeJointFlag p_flag, bool p_value) override;
virtual bool hinge_joint_get_flag(RID p_joint, HingeJointFlag p_flag) const override;
/// Reference frame is A
- virtual RID joint_create_slider(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) override;
+ virtual RID joint_create_slider(RID p_body_A, const Transform3D &p_local_frame_A, RID p_body_B, const Transform3D &p_local_frame_B) override;
- virtual void slider_joint_set_param(RID p_joint, SliderJointParam p_param, float p_value) override;
- virtual float slider_joint_get_param(RID p_joint, SliderJointParam p_param) const override;
+ virtual void slider_joint_set_param(RID p_joint, SliderJointParam p_param, real_t p_value) override;
+ virtual real_t slider_joint_get_param(RID p_joint, SliderJointParam p_param) const override;
/// Reference frame is A
- virtual RID joint_create_cone_twist(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) override;
+ virtual RID joint_create_cone_twist(RID p_body_A, const Transform3D &p_local_frame_A, RID p_body_B, const Transform3D &p_local_frame_B) override;
- virtual void cone_twist_joint_set_param(RID p_joint, ConeTwistJointParam p_param, float p_value) override;
- virtual float cone_twist_joint_get_param(RID p_joint, ConeTwistJointParam p_param) const override;
+ virtual void cone_twist_joint_set_param(RID p_joint, ConeTwistJointParam p_param, real_t p_value) override;
+ virtual real_t cone_twist_joint_get_param(RID p_joint, ConeTwistJointParam p_param) const override;
/// Reference frame is A
- virtual RID joint_create_generic_6dof(RID p_body_A, const Transform &p_local_frame_A, RID p_body_B, const Transform &p_local_frame_B) override;
+ virtual RID joint_create_generic_6dof(RID p_body_A, const Transform3D &p_local_frame_A, RID p_body_B, const Transform3D &p_local_frame_B) override;
- virtual void generic_6dof_joint_set_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param, float p_value) override;
- virtual float generic_6dof_joint_get_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param) override;
+ virtual void generic_6dof_joint_set_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param, real_t p_value) override;
+ virtual real_t generic_6dof_joint_get_param(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisParam p_param) override;
virtual void generic_6dof_joint_set_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag, bool p_enable) override;
virtual bool generic_6dof_joint_get_flag(RID p_joint, Vector3::Axis p_axis, G6DOFJointAxisFlag p_flag) override;
- virtual void generic_6dof_joint_set_precision(RID p_joint, int precision) override;
- virtual int generic_6dof_joint_get_precision(RID p_joint) override;
-
/* MISC */
virtual void free(RID p_rid) override;
@@ -396,8 +380,7 @@ public:
}
virtual void init() override;
- virtual void step(float p_deltaTime) override;
- virtual void sync() override;
+ virtual void step(real_t p_deltaTime) override;
virtual void flush_queries() override;
virtual void finish() override;
diff --git a/modules/bullet/bullet_types_converter.cpp b/modules/bullet/bullet_types_converter.cpp
index 09b90fe09e..01461767bd 100644
--- a/modules/bullet/bullet_types_converter.cpp
+++ b/modules/bullet/bullet_types_converter.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -59,7 +59,7 @@ void INVERT_B_TO_G(btMatrix3x3 const &inVal, Basis &outVal) {
INVERT_B_TO_G(inVal[2], outVal[2]);
}
-void B_TO_G(btTransform const &inVal, Transform &outVal) {
+void B_TO_G(btTransform const &inVal, Transform3D &outVal) {
B_TO_G(inVal.getBasis(), outVal.basis);
B_TO_G(inVal.getOrigin(), outVal.origin);
}
@@ -89,7 +89,7 @@ void INVERT_G_TO_B(Basis const &inVal, btMatrix3x3 &outVal) {
INVERT_G_TO_B(inVal[2], outVal[2]);
}
-void G_TO_B(Transform const &inVal, btTransform &outVal) {
+void G_TO_B(Transform3D const &inVal, btTransform &outVal) {
G_TO_B(inVal.basis, outVal.getBasis());
G_TO_B(inVal.origin, outVal.getOrigin());
}
@@ -116,7 +116,7 @@ void UNSCALE_BT_BASIS(btTransform &scaledBasis) {
}
} else { // Column 1 scale not fuzzy zero.
if (column2.fuzzyZero()) {
- // Create two vectors othogonal to column 1.
+ // Create two vectors orthogonal to column 1.
// Ensure that a default basis is created if column 1 = <0, 1, 0>
column0 = btVector3(column1[1], -column1[0], 0);
column2 = column0.cross(column1);
diff --git a/modules/bullet/bullet_types_converter.h b/modules/bullet/bullet_types_converter.h
index fef07c55b7..e184fe1769 100644
--- a/modules/bullet/bullet_types_converter.h
+++ b/modules/bullet/bullet_types_converter.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define BULLET_TYPES_CONVERTER_H
#include "core/math/basis.h"
-#include "core/math/transform.h"
+#include "core/math/transform_3d.h"
#include "core/math/vector3.h"
#include "core/typedefs.h"
@@ -49,14 +49,14 @@ extern void B_TO_G(btVector3 const &inVal, Vector3 &outVal);
extern void INVERT_B_TO_G(btVector3 const &inVal, Vector3 &outVal);
extern void B_TO_G(btMatrix3x3 const &inVal, Basis &outVal);
extern void INVERT_B_TO_G(btMatrix3x3 const &inVal, Basis &outVal);
-extern void B_TO_G(btTransform const &inVal, Transform &outVal);
+extern void B_TO_G(btTransform const &inVal, Transform3D &outVal);
// Godot TO Bullet
extern void G_TO_B(Vector3 const &inVal, btVector3 &outVal);
extern void INVERT_G_TO_B(Vector3 const &inVal, btVector3 &outVal);
extern void G_TO_B(Basis const &inVal, btMatrix3x3 &outVal);
extern void INVERT_G_TO_B(Basis const &inVal, btMatrix3x3 &outVal);
-extern void G_TO_B(Transform const &inVal, btTransform &outVal);
+extern void G_TO_B(Transform3D const &inVal, btTransform &outVal);
extern void UNSCALE_BT_BASIS(btTransform &scaledBasis);
#endif
diff --git a/modules/bullet/bullet_utilities.h b/modules/bullet/bullet_utilities.h
index a5e33d9829..a7c0fafbea 100644
--- a/modules/bullet/bullet_utilities.h
+++ b/modules/bullet/bullet_utilities.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index 9ae688d7df..afd8cf4bf4 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -49,7 +49,7 @@
CollisionObjectBullet::ShapeWrapper::~ShapeWrapper() {}
-void CollisionObjectBullet::ShapeWrapper::set_transform(const Transform &p_transform) {
+void CollisionObjectBullet::ShapeWrapper::set_transform(const Transform3D &p_transform) {
G_TO_B(p_transform.get_basis().get_scale_abs(), scale);
G_TO_B(p_transform, transform);
UNSCALE_BT_BASIS(transform);
@@ -148,6 +148,9 @@ void CollisionObjectBullet::add_collision_exception(const CollisionObjectBullet
void CollisionObjectBullet::remove_collision_exception(const CollisionObjectBullet *p_ignoreCollisionObject) {
exceptions.erase(p_ignoreCollisionObject->get_self());
+ if (!bt_collision_object) {
+ return;
+ }
bt_collision_object->setIgnoreCollisionCheck(p_ignoreCollisionObject->bt_collision_object, false);
if (space) {
space->get_broadphase()->getOverlappingPairCache()->cleanProxyFromPairs(bt_collision_object->getBroadphaseHandle(), space->get_dispatcher());
@@ -155,11 +158,14 @@ void CollisionObjectBullet::remove_collision_exception(const CollisionObjectBull
}
bool CollisionObjectBullet::has_collision_exception(const CollisionObjectBullet *p_otherCollisionObject) const {
- return !bt_collision_object->checkCollideWith(p_otherCollisionObject->bt_collision_object);
+ return exceptions.has(p_otherCollisionObject->get_self());
}
void CollisionObjectBullet::set_collision_enabled(bool p_enabled) {
collisionsEnabled = p_enabled;
+ if (!bt_collision_object) {
+ return;
+ }
if (collisionsEnabled) {
bt_collision_object->setCollisionFlags(bt_collision_object->getCollisionFlags() & (~btCollisionObject::CF_NO_CONTACT_RESPONSE));
} else {
@@ -188,7 +194,7 @@ int CollisionObjectBullet::get_godot_object_flags() const {
return bt_collision_object->getUserIndex2();
}
-void CollisionObjectBullet::set_transform(const Transform &p_global_transform) {
+void CollisionObjectBullet::set_transform(const Transform3D &p_global_transform) {
set_body_scale(p_global_transform.basis.get_scale_abs());
btTransform bt_transform;
@@ -198,8 +204,8 @@ void CollisionObjectBullet::set_transform(const Transform &p_global_transform) {
set_transform__bullet(bt_transform);
}
-Transform CollisionObjectBullet::get_transform() const {
- Transform t;
+Transform3D CollisionObjectBullet::get_transform() const {
+ Transform3D t;
B_TO_G(get_transform__bullet(), t);
t.basis.scale(body_scale);
return t;
@@ -225,7 +231,7 @@ RigidCollisionObjectBullet::~RigidCollisionObjectBullet() {
}
}
-void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform &p_transform, bool p_disabled) {
+void RigidCollisionObjectBullet::add_shape(ShapeBullet *p_shape, const Transform3D &p_transform, bool p_disabled) {
shapes.push_back(ShapeWrapper(p_shape, p_transform, !p_disabled));
p_shape->add_owner(this);
reload_shapes();
@@ -291,7 +297,7 @@ void RigidCollisionObjectBullet::remove_all_shapes(bool p_permanentlyFromThisBod
}
}
-void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transform &p_transform) {
+void RigidCollisionObjectBullet::set_shape_transform(int p_index, const Transform3D &p_transform) {
ERR_FAIL_INDEX(p_index, get_shape_count());
shapes.write[p_index].set_transform(p_transform);
@@ -302,8 +308,8 @@ const btTransform &RigidCollisionObjectBullet::get_bt_shape_transform(int p_inde
return shapes[p_index].transform;
}
-Transform RigidCollisionObjectBullet::get_shape_transform(int p_index) const {
- Transform trs;
+Transform3D RigidCollisionObjectBullet::get_shape_transform(int p_index) const {
+ Transform3D trs;
B_TO_G(shapes[p_index].transform, trs);
return trs;
}
diff --git a/modules/bullet/collision_object_bullet.h b/modules/bullet/collision_object_bullet.h
index c23a21c519..6d2c564e44 100644
--- a/modules/bullet/collision_object_bullet.h
+++ b/modules/bullet/collision_object_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,10 +31,10 @@
#ifndef COLLISION_OBJECT_BULLET_H
#define COLLISION_OBJECT_BULLET_H
-#include "core/math/transform.h"
+#include "core/math/transform_3d.h"
#include "core/math/vector3.h"
-#include "core/object.h"
-#include "core/vset.h"
+#include "core/object/class_db.h"
+#include "core/templates/vset.h"
#include "shape_owner_bullet.h"
#include <LinearMath/btTransform.h>
@@ -83,7 +83,7 @@ public:
set_transform(p_transform);
}
- ShapeWrapper(ShapeBullet *p_shape, const Transform &p_transform, bool p_active) :
+ ShapeWrapper(ShapeBullet *p_shape, const Transform3D &p_transform, bool p_active) :
shape(p_shape),
active(p_active) {
set_transform(p_transform);
@@ -102,7 +102,7 @@ public:
active = otherShape.active;
}
- void set_transform(const Transform &p_transform);
+ void set_transform(const Transform3D &p_transform);
void set_transform(const btTransform &p_transform);
btTransform get_adjusted_transform() const;
@@ -110,7 +110,7 @@ public:
};
protected:
- Type type;
+ Type type = TYPE_AREA;
ObjectID instance_id;
uint32_t collisionLayer = 0;
uint32_t collisionMask = 0;
@@ -202,8 +202,8 @@ public:
void set_godot_object_flags(int flags);
int get_godot_object_flags() const;
- void set_transform(const Transform &p_global_transform);
- Transform get_transform() const;
+ void set_transform(const Transform3D &p_global_transform);
+ Transform3D get_transform() const;
virtual void set_transform__bullet(const btTransform &p_global_transform);
virtual const btTransform &get_transform__bullet() const;
virtual void notify_transform_changed();
@@ -225,7 +225,7 @@ public:
_FORCE_INLINE_ btCollisionShape *get_main_shape() const { return mainShape; }
- void add_shape(ShapeBullet *p_shape, const Transform &p_transform = Transform(), bool p_disabled = false);
+ void add_shape(ShapeBullet *p_shape, const Transform3D &p_transform = Transform3D(), bool p_disabled = false);
void set_shape(int p_index, ShapeBullet *p_shape);
int get_shape_count() const;
@@ -238,10 +238,10 @@ public:
void remove_shape_full(int p_index);
void remove_all_shapes(bool p_permanentlyFromThisBody = false, bool p_force_not_reload = false);
- void set_shape_transform(int p_index, const Transform &p_transform);
+ void set_shape_transform(int p_index, const Transform3D &p_transform);
const btTransform &get_bt_shape_transform(int p_index) const;
- Transform get_shape_transform(int p_index) const;
+ Transform3D get_shape_transform(int p_index) const;
void set_shape_disabled(int p_index, bool p_disabled);
bool is_shape_disabled(int p_index);
diff --git a/modules/bullet/cone_twist_joint_bullet.cpp b/modules/bullet/cone_twist_joint_bullet.cpp
index b4735fa9e9..34516d8b3b 100644
--- a/modules/bullet/cone_twist_joint_bullet.cpp
+++ b/modules/bullet/cone_twist_joint_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,16 +40,16 @@
@author AndreaCatania
*/
-ConeTwistJointBullet::ConeTwistJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &rbAFrame, const Transform &rbBFrame) :
+ConeTwistJointBullet::ConeTwistJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform3D &rbAFrame, const Transform3D &rbBFrame) :
JointBullet() {
- Transform scaled_AFrame(rbAFrame.scaled(rbA->get_body_scale()));
+ Transform3D scaled_AFrame(rbAFrame.scaled(rbA->get_body_scale()));
scaled_AFrame.basis.rotref_posscale_decomposition(scaled_AFrame.basis);
btTransform btFrameA;
G_TO_B(scaled_AFrame, btFrameA);
if (rbB) {
- Transform scaled_BFrame(rbBFrame.scaled(rbB->get_body_scale()));
+ Transform3D scaled_BFrame(rbBFrame.scaled(rbB->get_body_scale()));
scaled_BFrame.basis.rotref_posscale_decomposition(scaled_BFrame.basis);
btTransform btFrameB;
diff --git a/modules/bullet/cone_twist_joint_bullet.h b/modules/bullet/cone_twist_joint_bullet.h
index ed4baa9d1b..7e51f7d644 100644
--- a/modules/bullet/cone_twist_joint_bullet.h
+++ b/modules/bullet/cone_twist_joint_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,7 +43,7 @@ class ConeTwistJointBullet : public JointBullet {
class btConeTwistConstraint *coneConstraint;
public:
- ConeTwistJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &rbAFrame, const Transform &rbBFrame);
+ ConeTwistJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform3D &rbAFrame, const Transform3D &rbBFrame);
virtual PhysicsServer3D::JointType get_type() const { return PhysicsServer3D::JOINT_CONE_TWIST; }
diff --git a/modules/bullet/config.py b/modules/bullet/config.py
index d22f9454ed..83605f1f9b 100644
--- a/modules/bullet/config.py
+++ b/modules/bullet/config.py
@@ -1,5 +1,7 @@
def can_build(env, platform):
- return True
+ # API Changed and bullet is disabled at the moment
+ return False
+ # Later change to return not env["disable_3d"]
def configure(env):
diff --git a/modules/bullet/constraint_bullet.cpp b/modules/bullet/constraint_bullet.cpp
index c47a23e75f..e610727685 100644
--- a/modules/bullet/constraint_bullet.cpp
+++ b/modules/bullet/constraint_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/constraint_bullet.h b/modules/bullet/constraint_bullet.h
index 538808be51..6afd8c9b52 100644
--- a/modules/bullet/constraint_bullet.h
+++ b/modules/bullet/constraint_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/generic_6dof_joint_bullet.cpp b/modules/bullet/generic_6dof_joint_bullet.cpp
index 56a66dba45..7e04d57b9d 100644
--- a/modules/bullet/generic_6dof_joint_bullet.cpp
+++ b/modules/bullet/generic_6dof_joint_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,7 +40,7 @@
@author AndreaCatania
*/
-Generic6DOFJointBullet::Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB) :
+Generic6DOFJointBullet::Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform3D &frameInA, const Transform3D &frameInB) :
JointBullet() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < PhysicsServer3D::G6DOF_JOINT_FLAG_MAX; j++) {
@@ -48,7 +48,7 @@ Generic6DOFJointBullet::Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBu
}
}
- Transform scaled_AFrame(frameInA.scaled(rbA->get_body_scale()));
+ Transform3D scaled_AFrame(frameInA.scaled(rbA->get_body_scale()));
scaled_AFrame.basis.rotref_posscale_decomposition(scaled_AFrame.basis);
@@ -56,7 +56,7 @@ Generic6DOFJointBullet::Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBu
G_TO_B(scaled_AFrame, btFrameA);
if (rbB) {
- Transform scaled_BFrame(frameInB.scaled(rbB->get_body_scale()));
+ Transform3D scaled_BFrame(frameInB.scaled(rbB->get_body_scale()));
scaled_BFrame.basis.rotref_posscale_decomposition(scaled_BFrame.basis);
@@ -71,30 +71,30 @@ Generic6DOFJointBullet::Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBu
setup(sixDOFConstraint);
}
-Transform Generic6DOFJointBullet::getFrameOffsetA() const {
+Transform3D Generic6DOFJointBullet::getFrameOffsetA() const {
btTransform btTrs = sixDOFConstraint->getFrameOffsetA();
- Transform gTrs;
+ Transform3D gTrs;
B_TO_G(btTrs, gTrs);
return gTrs;
}
-Transform Generic6DOFJointBullet::getFrameOffsetB() const {
+Transform3D Generic6DOFJointBullet::getFrameOffsetB() const {
btTransform btTrs = sixDOFConstraint->getFrameOffsetB();
- Transform gTrs;
+ Transform3D gTrs;
B_TO_G(btTrs, gTrs);
return gTrs;
}
-Transform Generic6DOFJointBullet::getFrameOffsetA() {
+Transform3D Generic6DOFJointBullet::getFrameOffsetA() {
btTransform btTrs = sixDOFConstraint->getFrameOffsetA();
- Transform gTrs;
+ Transform3D gTrs;
B_TO_G(btTrs, gTrs);
return gTrs;
}
-Transform Generic6DOFJointBullet::getFrameOffsetB() {
+Transform3D Generic6DOFJointBullet::getFrameOffsetB() {
btTransform btTrs = sixDOFConstraint->getFrameOffsetB();
- Transform gTrs;
+ Transform3D gTrs;
B_TO_G(btTrs, gTrs);
return gTrs;
}
@@ -273,11 +273,3 @@ bool Generic6DOFJointBullet::get_flag(Vector3::Axis p_axis, PhysicsServer3D::G6D
ERR_FAIL_INDEX_V(p_axis, 3, false);
return flags[p_axis][p_flag];
}
-
-void Generic6DOFJointBullet::set_precision(int p_precision) {
- sixDOFConstraint->setOverrideNumSolverIterations(MAX(1, p_precision));
-}
-
-int Generic6DOFJointBullet::get_precision() const {
- return sixDOFConstraint->getOverrideNumSolverIterations();
-}
diff --git a/modules/bullet/generic_6dof_joint_bullet.h b/modules/bullet/generic_6dof_joint_bullet.h
index 316708bb11..00567e3085 100644
--- a/modules/bullet/generic_6dof_joint_bullet.h
+++ b/modules/bullet/generic_6dof_joint_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -48,14 +48,14 @@ class Generic6DOFJointBullet : public JointBullet {
bool flags[3][PhysicsServer3D::G6DOF_JOINT_FLAG_MAX];
public:
- Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB);
+ Generic6DOFJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform3D &frameInA, const Transform3D &frameInB);
virtual PhysicsServer3D::JointType get_type() const { return PhysicsServer3D::JOINT_6DOF; }
- Transform getFrameOffsetA() const;
- Transform getFrameOffsetB() const;
- Transform getFrameOffsetA();
- Transform getFrameOffsetB();
+ Transform3D getFrameOffsetA() const;
+ Transform3D getFrameOffsetB() const;
+ Transform3D getFrameOffsetA();
+ Transform3D getFrameOffsetB();
void set_linear_lower_limit(const Vector3 &linearLower);
void set_linear_upper_limit(const Vector3 &linearUpper);
@@ -68,9 +68,6 @@ public:
void set_flag(Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag, bool p_value);
bool get_flag(Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag) const;
-
- void set_precision(int p_precision);
- int get_precision() const;
};
#endif
diff --git a/modules/bullet/godot_collision_configuration.cpp b/modules/bullet/godot_collision_configuration.cpp
index ec7a1dbd9a..94f150b712 100644
--- a/modules/bullet/godot_collision_configuration.cpp
+++ b/modules/bullet/godot_collision_configuration.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/godot_collision_configuration.h b/modules/bullet/godot_collision_configuration.h
index ffad1b1bda..8ed55cb1da 100644
--- a/modules/bullet/godot_collision_configuration.h
+++ b/modules/bullet/godot_collision_configuration.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/godot_collision_dispatcher.cpp b/modules/bullet/godot_collision_dispatcher.cpp
index d919c85469..423166c408 100644
--- a/modules/bullet/godot_collision_dispatcher.cpp
+++ b/modules/bullet/godot_collision_dispatcher.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,7 +43,7 @@ GodotCollisionDispatcher::GodotCollisionDispatcher(btCollisionConfiguration *col
bool GodotCollisionDispatcher::needsCollision(const btCollisionObject *body0, const btCollisionObject *body1) {
if (body0->getUserIndex() == CASTED_TYPE_AREA || body1->getUserIndex() == CASTED_TYPE_AREA) {
- // Avoide area narrow phase
+ // Avoid area narrow phase
return false;
}
return btCollisionDispatcher::needsCollision(body0, body1);
@@ -51,7 +51,7 @@ bool GodotCollisionDispatcher::needsCollision(const btCollisionObject *body0, co
bool GodotCollisionDispatcher::needsResponse(const btCollisionObject *body0, const btCollisionObject *body1) {
if (body0->getUserIndex() == CASTED_TYPE_AREA || body1->getUserIndex() == CASTED_TYPE_AREA) {
- // Avoide area narrow phase
+ // Avoid area narrow phase
return false;
}
return btCollisionDispatcher::needsResponse(body0, body1);
diff --git a/modules/bullet/godot_collision_dispatcher.h b/modules/bullet/godot_collision_dispatcher.h
index 5a96268ee9..77472a9432 100644
--- a/modules/bullet/godot_collision_dispatcher.h
+++ b/modules/bullet/godot_collision_dispatcher.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef GODOT_COLLISION_DISPATCHER_H
#define GODOT_COLLISION_DISPATCHER_H
-#include "core/int_types.h"
+#include <cstdint>
#include <btBulletDynamicsCommon.h>
diff --git a/modules/bullet/godot_motion_state.h b/modules/bullet/godot_motion_state.h
index 90d1614a77..ca17349130 100644
--- a/modules/bullet/godot_motion_state.h
+++ b/modules/bullet/godot_motion_state.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -51,7 +51,7 @@ class GodotMotionState : public btMotionState {
/// This data is used to store last world position
btTransform bodyCurrentWorldTransform;
- RigidBodyBullet *owner;
+ RigidBodyBullet *owner = nullptr;
public:
GodotMotionState(RigidBodyBullet *p_owner) :
diff --git a/modules/bullet/godot_ray_world_algorithm.cpp b/modules/bullet/godot_ray_world_algorithm.cpp
index a84f3511ba..a8291d4ab4 100644
--- a/modules/bullet/godot_ray_world_algorithm.cpp
+++ b/modules/bullet/godot_ray_world_algorithm.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/godot_ray_world_algorithm.h b/modules/bullet/godot_ray_world_algorithm.h
index 9786732d40..25798aecb4 100644
--- a/modules/bullet/godot_ray_world_algorithm.h
+++ b/modules/bullet/godot_ray_world_algorithm.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -45,7 +45,7 @@ class GodotRayWorldAlgorithm : public btActivatingCollisionAlgorithm {
const btDiscreteDynamicsWorld *m_world;
btPersistentManifold *m_manifoldPtr;
bool m_ownManifold = false;
- bool m_isSwapped;
+ bool m_isSwapped = false;
public:
GodotRayWorldAlgorithm(const btDiscreteDynamicsWorld *world, btPersistentManifold *mf, const btCollisionAlgorithmConstructionInfo &ci, const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap, bool isSwapped);
diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp
index f82648d6ff..1f962772e7 100644
--- a/modules/bullet/godot_result_callbacks.cpp
+++ b/modules/bullet/godot_result_callbacks.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,17 +47,12 @@ bool godotContactAddedCallback(btManifoldPoint &cp, const btCollisionObjectWrapp
return true;
}
-bool GodotFilterCallback::test_collision_filters(uint32_t body0_collision_layer, uint32_t body0_collision_mask, uint32_t body1_collision_layer, uint32_t body1_collision_mask) {
- return body0_collision_layer & body1_collision_mask || body1_collision_layer & body0_collision_mask;
-}
-
bool GodotFilterCallback::needBroadphaseCollision(btBroadphaseProxy *proxy0, btBroadphaseProxy *proxy1) const {
- return GodotFilterCallback::test_collision_filters(proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask, proxy1->m_collisionFilterGroup, proxy1->m_collisionFilterMask);
+ return (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) || (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask);
}
bool GodotClosestRayResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
- const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
- if (needs) {
+ if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
@@ -90,8 +85,7 @@ bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) con
return false;
}
- const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
- if (needs) {
+ if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
if (m_exclude->has(gObj->get_self())) {
@@ -113,7 +107,7 @@ btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalCo
PhysicsDirectSpaceState3D::ShapeResult &result = m_results[count];
- result.shape = convexResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is a odd name but contains the compound shape ID
+ result.shape = convexResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is an odd name but contains the compound shape ID
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);
@@ -123,8 +117,7 @@ btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalCo
}
bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
- const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
- if (needs) {
+ if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
if (gObj == m_self_object) {
@@ -142,6 +135,10 @@ bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *prox
if (m_self_object->has_collision_exception(gObj) || gObj->has_collision_exception(m_self_object)) {
return false;
}
+
+ if (m_exclude->has(gObj->get_self())) {
+ return false;
+ }
}
return true;
} else {
@@ -150,8 +147,7 @@ bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *prox
}
bool GodotClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
- const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
- if (needs) {
+ if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
@@ -176,7 +172,7 @@ 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 a odd name but contains the compound shape ID
+ m_shapeId = convexResult.m_localShapeInfo->m_triangleIndex; // "m_triangleIndex" Is an odd name but contains the compound shape ID
} else {
m_shapeId = 0;
}
@@ -188,8 +184,7 @@ bool GodotAllContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) co
return false;
}
- const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
- if (needs) {
+ if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
@@ -244,8 +239,7 @@ bool GodotContactPairContactResultCallback::needsCollision(btBroadphaseProxy *pr
return false;
}
- const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
- if (needs) {
+ if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
@@ -287,8 +281,7 @@ btScalar GodotContactPairContactResultCallback::addSingleResult(btManifoldPoint
}
bool GodotRestInfoContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
- const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
- if (needs) {
+ if (proxy0->m_collisionFilterGroup & m_collisionFilterMask) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h
index 1325542973..96a649d77a 100644
--- a/modules/bullet/godot_result_callbacks.h
+++ b/modules/bullet/godot_result_callbacks.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,8 +47,6 @@ bool godotContactAddedCallback(btManifoldPoint &cp, const btCollisionObjectWrapp
/// This class is required to implement custom collision behaviour in the broadphase
struct GodotFilterCallback : public btOverlapFilterCallback {
- static bool test_collision_filters(uint32_t body0_collision_layer, uint32_t body0_collision_mask, uint32_t body1_collision_layer, uint32_t body1_collision_mask);
-
// return true when pairs need collision
virtual bool needBroadphaseCollision(btBroadphaseProxy *proxy0, btBroadphaseProxy *proxy1) const;
};
@@ -59,8 +57,8 @@ struct GodotClosestRayResultCallback : public btCollisionWorld::ClosestRayResult
bool m_pickRay = false;
int m_shapeId = 0;
- bool collide_with_bodies;
- bool collide_with_areas;
+ bool collide_with_bodies = false;
+ bool collide_with_areas = false;
public:
GodotClosestRayResultCallback(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
@@ -84,8 +82,8 @@ public:
// store all colliding object
struct GodotAllConvexResultCallback : public btCollisionWorld::ConvexResultCallback {
public:
- PhysicsDirectSpaceState3D::ShapeResult *m_results;
- int m_resultMax;
+ PhysicsDirectSpaceState3D::ShapeResult *m_results = nullptr;
+ int m_resultMax = 0;
const Set<RID> *m_exclude;
int count = 0;
@@ -102,11 +100,13 @@ public:
struct GodotKinClosestConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback {
public:
const RigidBodyBullet *m_self_object;
+ const Set<RID> *m_exclude;
const bool m_infinite_inertia;
- GodotKinClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const RigidBodyBullet *p_self_object, bool p_infinite_inertia) :
+ GodotKinClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const RigidBodyBullet *p_self_object, bool p_infinite_inertia, const Set<RID> *p_exclude) :
btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld),
m_self_object(p_self_object),
+ m_exclude(p_exclude),
m_infinite_inertia(p_infinite_inertia) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
@@ -117,8 +117,8 @@ public:
const Set<RID> *m_exclude;
int m_shapeId = 0;
- bool collide_with_bodies;
- bool collide_with_areas;
+ bool collide_with_bodies = false;
+ bool collide_with_areas = false;
GodotClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld),
@@ -134,13 +134,13 @@ public:
struct GodotAllContactResultCallback : public btCollisionWorld::ContactResultCallback {
public:
const btCollisionObject *m_self_object;
- PhysicsDirectSpaceState3D::ShapeResult *m_results;
- int m_resultMax;
+ PhysicsDirectSpaceState3D::ShapeResult *m_results = nullptr;
+ int m_resultMax = 0;
const Set<RID> *m_exclude;
int m_count = 0;
- bool collide_with_bodies;
- bool collide_with_areas;
+ bool collide_with_bodies = false;
+ bool collide_with_areas = false;
GodotAllContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState3D::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
m_self_object(p_self_object),
@@ -159,13 +159,13 @@ public:
struct GodotContactPairContactResultCallback : public btCollisionWorld::ContactResultCallback {
public:
const btCollisionObject *m_self_object;
- Vector3 *m_results;
- int m_resultMax;
+ Vector3 *m_results = nullptr;
+ int m_resultMax = 0;
const Set<RID> *m_exclude;
int m_count = 0;
- bool collide_with_bodies;
- bool collide_with_areas;
+ bool collide_with_bodies = false;
+ bool collide_with_areas = false;
GodotContactPairContactResultCallback(btCollisionObject *p_self_object, Vector3 *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
m_self_object(p_self_object),
@@ -183,14 +183,14 @@ public:
struct GodotRestInfoContactResultCallback : public btCollisionWorld::ContactResultCallback {
public:
const btCollisionObject *m_self_object;
- PhysicsDirectSpaceState3D::ShapeRestInfo *m_result;
+ PhysicsDirectSpaceState3D::ShapeRestInfo *m_result = nullptr;
const Set<RID> *m_exclude;
bool m_collided = false;
- real_t m_min_distance = 0;
- const btCollisionObject *m_rest_info_collision_object;
+ real_t m_min_distance = 0.0;
+ const btCollisionObject *m_rest_info_collision_object = nullptr;
btVector3 m_rest_info_bt_point;
- bool collide_with_bodies;
- bool collide_with_areas;
+ bool collide_with_bodies = false;
+ bool collide_with_areas = false;
GodotRestInfoContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState3D::ShapeRestInfo *p_result, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
m_self_object(p_self_object),
diff --git a/modules/bullet/hinge_joint_bullet.cpp b/modules/bullet/hinge_joint_bullet.cpp
index 2338277565..b5fe50cf5f 100644
--- a/modules/bullet/hinge_joint_bullet.cpp
+++ b/modules/bullet/hinge_joint_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,16 +40,16 @@
@author AndreaCatania
*/
-HingeJointBullet::HingeJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameA, const Transform &frameB) :
+HingeJointBullet::HingeJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform3D &frameA, const Transform3D &frameB) :
JointBullet() {
- Transform scaled_AFrame(frameA.scaled(rbA->get_body_scale()));
+ Transform3D scaled_AFrame(frameA.scaled(rbA->get_body_scale()));
scaled_AFrame.basis.rotref_posscale_decomposition(scaled_AFrame.basis);
btTransform btFrameA;
G_TO_B(scaled_AFrame, btFrameA);
if (rbB) {
- Transform scaled_BFrame(frameB.scaled(rbB->get_body_scale()));
+ Transform3D scaled_BFrame(frameB.scaled(rbB->get_body_scale()));
scaled_BFrame.basis.rotref_posscale_decomposition(scaled_BFrame.basis);
btTransform btFrameB;
diff --git a/modules/bullet/hinge_joint_bullet.h b/modules/bullet/hinge_joint_bullet.h
index 120c40e5c0..dd0f69ba68 100644
--- a/modules/bullet/hinge_joint_bullet.h
+++ b/modules/bullet/hinge_joint_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -41,7 +41,7 @@ class HingeJointBullet : public JointBullet {
class btHingeConstraint *hingeConstraint;
public:
- HingeJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameA, const Transform &frameB);
+ HingeJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform3D &frameA, const Transform3D &frameB);
HingeJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Vector3 &pivotInA, const Vector3 &pivotInB, const Vector3 &axisInA, const Vector3 &axisInB);
virtual PhysicsServer3D::JointType get_type() const { return PhysicsServer3D::JOINT_HINGE; }
diff --git a/modules/bullet/joint_bullet.cpp b/modules/bullet/joint_bullet.cpp
index 6257ff0058..ac371658f5 100644
--- a/modules/bullet/joint_bullet.cpp
+++ b/modules/bullet/joint_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/joint_bullet.h b/modules/bullet/joint_bullet.h
index c70cea817e..5bb8b50961 100644
--- a/modules/bullet/joint_bullet.h
+++ b/modules/bullet/joint_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/pin_joint_bullet.cpp b/modules/bullet/pin_joint_bullet.cpp
index 1cfbc65c78..8e8ff57f11 100644
--- a/modules/bullet/pin_joint_bullet.cpp
+++ b/modules/bullet/pin_joint_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/pin_joint_bullet.h b/modules/bullet/pin_joint_bullet.h
index e7d05f34d4..6fbb6f7e02 100644
--- a/modules/bullet/pin_joint_bullet.h
+++ b/modules/bullet/pin_joint_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/register_types.cpp b/modules/bullet/register_types.cpp
index 009d0dff63..b5ad5749a6 100644
--- a/modules/bullet/register_types.cpp
+++ b/modules/bullet/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#include "register_types.h"
#include "bullet_physics_server.h"
-#include "core/class_db.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
+#include "core/object/class_db.h"
/**
@author AndreaCatania
diff --git a/modules/bullet/register_types.h b/modules/bullet/register_types.h
index 5a01a1422e..e405996705 100644
--- a/modules/bullet/register_types.h
+++ b/modules/bullet/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/rid_bullet.h b/modules/bullet/rid_bullet.h
index 3551ca05f9..face6b4861 100644
--- a/modules/bullet/rid_bullet.h
+++ b/modules/bullet/rid_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef RID_BULLET_H
#define RID_BULLET_H
-#include "core/rid.h"
+#include "core/templates/rid.h"
/**
@author AndreaCatania
@@ -41,7 +41,7 @@ class BulletPhysicsServer3D;
class RIDBullet {
RID self;
- BulletPhysicsServer3D *physicsServer;
+ BulletPhysicsServer3D *physicsServer = nullptr;
public:
_FORCE_INLINE_ void set_self(const RID &p_self) { self = p_self; }
diff --git a/modules/bullet/rigid_body_bullet.cpp b/modules/bullet/rigid_body_bullet.cpp
index 13e6982d60..4faab19539 100644
--- a/modules/bullet/rigid_body_bullet.cpp
+++ b/modules/bullet/rigid_body_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -56,11 +56,11 @@ Vector3 BulletPhysicsDirectBodyState3D::get_total_gravity() const {
return gVec;
}
-float BulletPhysicsDirectBodyState3D::get_total_angular_damp() const {
+real_t BulletPhysicsDirectBodyState3D::get_total_angular_damp() const {
return body->btBody->getAngularDamping();
}
-float BulletPhysicsDirectBodyState3D::get_total_linear_damp() const {
+real_t BulletPhysicsDirectBodyState3D::get_total_linear_damp() const {
return body->btBody->getLinearDamping();
}
@@ -74,7 +74,7 @@ Basis BulletPhysicsDirectBodyState3D::get_principal_inertia_axes() const {
return Basis();
}
-float BulletPhysicsDirectBodyState3D::get_inverse_mass() const {
+real_t BulletPhysicsDirectBodyState3D::get_inverse_mass() const {
return body->btBody->getInvMass();
}
@@ -106,14 +106,24 @@ Vector3 BulletPhysicsDirectBodyState3D::get_angular_velocity() const {
return body->get_angular_velocity();
}
-void BulletPhysicsDirectBodyState3D::set_transform(const Transform &p_transform) {
+void BulletPhysicsDirectBodyState3D::set_transform(const Transform3D &p_transform) {
body->set_transform(p_transform);
}
-Transform BulletPhysicsDirectBodyState3D::get_transform() const {
+Transform3D BulletPhysicsDirectBodyState3D::get_transform() const {
return body->get_transform();
}
+Vector3 BulletPhysicsDirectBodyState3D::get_velocity_at_local_position(const Vector3 &p_position) const {
+ btVector3 local_position;
+ G_TO_B(p_position, local_position);
+
+ Vector3 velocity;
+ B_TO_G(body->btBody->getVelocityInLocalPoint(local_position), velocity);
+
+ return velocity;
+}
+
void BulletPhysicsDirectBodyState3D::add_central_force(const Vector3 &p_force) {
body->apply_central_force(p_force);
}
@@ -158,7 +168,7 @@ Vector3 BulletPhysicsDirectBodyState3D::get_contact_local_normal(int p_contact_i
return body->collisions[p_contact_idx].hitNormal;
}
-float BulletPhysicsDirectBodyState3D::get_contact_impulse(int p_contact_idx) const {
+real_t BulletPhysicsDirectBodyState3D::get_contact_impulse(int p_contact_idx) const {
return body->collisions[p_contact_idx].appliedImpulse;
}
@@ -264,10 +274,11 @@ RigidBodyBullet::RigidBodyBullet() :
btRigidBody::btRigidBodyConstructionInfo cInfo(mass, godotMotionState, nullptr, localInertia);
btBody = bulletnew(btRigidBody(cInfo));
+ btBody->setFriction(1.0);
reload_shapes();
setupBulletCollisionObject(btBody);
- set_mode(PhysicsServer3D::BODY_MODE_RIGID);
+ set_mode(PhysicsServer3D::BODY_MODE_DYNAMIC);
reload_axis_lock();
areasWhereIam.resize(maxAreasWhereIam);
@@ -292,6 +303,7 @@ RigidBodyBullet::~RigidBodyBullet() {
void RigidBodyBullet::init_kinematic_utilities() {
kinematic_utilities = memnew(KinematicUtilities(this));
+ reload_kinematic_shapes();
}
void RigidBodyBullet::destroy_kinematic_utilities() {
@@ -321,10 +333,8 @@ void RigidBodyBullet::set_space(SpaceBullet *p_space) {
if (space) {
can_integrate_forces = false;
isScratchedSpaceOverrideModificator = false;
-
- // Remove all eventual constraints
- assert_no_constraints();
-
+ // Remove any constraints
+ space->remove_rigid_body_constraints(this);
// Remove this object form the physics world
space->remove_rigid_body(this);
}
@@ -347,16 +357,17 @@ void RigidBodyBullet::dispatch_callbacks() {
Variant variantBodyDirect = bodyDirect;
- Object *obj = ObjectDB::get_instance(force_integration_callback->id);
+ Object *obj = force_integration_callback->callable.get_object();
if (!obj) {
// Remove integration callback
- set_force_integration_callback(ObjectID(), StringName());
+ set_force_integration_callback(Callable());
} else {
const Variant *vp[2] = { &variantBodyDirect, &force_integration_callback->udata };
Callable::CallError responseCallError;
int argc = (force_integration_callback->udata.get_type() == Variant::NIL) ? 1 : 2;
- obj->call(force_integration_callback->method, vp, argc, responseCallError);
+ Variant rv;
+ force_integration_callback->callable.call(vp, argc, rv, responseCallError);
}
}
@@ -372,16 +383,15 @@ void RigidBodyBullet::dispatch_callbacks() {
previousActiveState = btBody->isActive();
}
-void RigidBodyBullet::set_force_integration_callback(ObjectID p_id, const StringName &p_method, const Variant &p_udata) {
+void RigidBodyBullet::set_force_integration_callback(const Callable &p_callable, const Variant &p_udata) {
if (force_integration_callback) {
memdelete(force_integration_callback);
force_integration_callback = nullptr;
}
- if (p_id.is_valid()) {
+ if (p_callable.get_object()) {
force_integration_callback = memnew(ForceIntegrationCallback);
- force_integration_callback->id = p_id;
- force_integration_callback->method = p_method;
+ force_integration_callback->callable = p_callable;
force_integration_callback->udata = p_udata;
}
}
@@ -413,7 +423,7 @@ void RigidBodyBullet::on_collision_checker_end() {
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 float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index) {
+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) {
if (collisionsCount >= maxCollisionsDetection) {
return false;
}
@@ -442,12 +452,6 @@ bool RigidBodyBullet::was_colliding(RigidBodyBullet *p_other_object) {
return false;
}
-void RigidBodyBullet::assert_no_constraints() {
- if (btBody->getNumConstraintRefs()) {
- WARN_PRINT("A body with a joints is destroyed. Please check the implementation in order to destroy the joint before the body.");
- }
-}
-
void RigidBodyBullet::set_activation_state(bool p_active) {
if (p_active) {
btBody->activate();
@@ -522,30 +526,27 @@ real_t RigidBodyBullet::get_param(PhysicsServer3D::BodyParameter p_param) const
}
void RigidBodyBullet::set_mode(PhysicsServer3D::BodyMode p_mode) {
- // This is necessary to block force_integration untile next move
+ // This is necessary to block force_integration until next move
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_RIGID:
- mode = PhysicsServer3D::BODY_MODE_RIGID;
+ case PhysicsServer3D::BODY_MODE_DYNAMIC:
reload_axis_lock();
_internal_set_mass(0 == mass ? 1 : mass);
scratch_space_override_modificator();
break;
- case PhysicsServer3D::BODY_MODE_CHARACTER:
- mode = PhysicsServer3D::BODY_MODE_CHARACTER;
+ case PhysicsServer3D::MODE_DYNAMIC_LINEAR:
reload_axis_lock();
_internal_set_mass(0 == mass ? 1 : mass);
scratch_space_override_modificator();
@@ -717,12 +718,12 @@ bool RigidBodyBullet::is_axis_locked(PhysicsServer3D::BodyAxis p_axis) const {
}
void RigidBodyBullet::reload_axis_lock() {
- btBody->setLinearFactor(btVector3(float(!is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_X)), float(!is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_Y)), float(!is_axis_locked(PhysicsServer3D::BODY_AXIS_LINEAR_Z))));
- if (PhysicsServer3D::BODY_MODE_CHARACTER == mode) {
+ 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_LINEAR == mode) {
/// When character angular is always locked
btBody->setAngularFactor(btVector3(0., 0., 0.));
} else {
- btBody->setAngularFactor(btVector3(float(!is_axis_locked(PhysicsServer3D::BODY_AXIS_ANGULAR_X)), float(!is_axis_locked(PhysicsServer3D::BODY_AXIS_ANGULAR_Y)), float(!is_axis_locked(PhysicsServer3D::BODY_AXIS_ANGULAR_Z))));
+ btBody->setAngularFactor(btVector3(btScalar(!is_axis_locked(PhysicsServer3D::BODY_AXIS_ANGULAR_X)), btScalar(!is_axis_locked(PhysicsServer3D::BODY_AXIS_ANGULAR_Y)), btScalar(!is_axis_locked(PhysicsServer3D::BODY_AXIS_ANGULAR_Z))));
}
}
@@ -732,7 +733,7 @@ void RigidBodyBullet::set_continuous_collision_detection(bool p_enable) {
// 1 meter in one simulation frame
btBody->setCcdMotionThreshold(1e-7);
- /// Calculate using the rule writte below the CCD swept sphere radius
+ /// Calculate using the rule write below the CCD swept sphere radius
/// CCD works on an embedded sphere of radius, make sure this radius
/// is embedded inside the convex objects, preferably smaller:
/// for an object of dimensions 1 meter, try 0.2
@@ -743,7 +744,7 @@ void RigidBodyBullet::set_continuous_collision_detection(bool p_enable) {
}
btBody->setCcdSweptSphereRadius(radius * 0.2);
} else {
- btBody->setCcdMotionThreshold(10000.0);
+ btBody->setCcdMotionThreshold(0.);
btBody->setCcdSweptSphereRadius(0.);
}
}
@@ -823,7 +824,7 @@ void RigidBodyBullet::reload_shapes() {
btBody->updateInertiaTensor();
reload_kinematic_shapes();
- set_continuous_collision_detection(btBody->getCcdMotionThreshold() < 9998.0);
+ set_continuous_collision_detection(is_continuous_collision_detection_enabled());
reload_body();
}
@@ -1013,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_RIGID != mode && PhysicsServer3D::BODY_MODE_CHARACTER != mode) {
+ if (PhysicsServer3D::BODY_MODE_DYNAMIC != mode && PhysicsServer3D::MODE_DYNAMIC_LINEAR != mode) {
return;
}
@@ -1022,7 +1023,7 @@ void RigidBodyBullet::_internal_set_mass(real_t p_mass) {
mainShape->calculateLocalInertia(p_mass, localInertia);
}
- if (PhysicsServer3D::BODY_MODE_RIGID == mode) {
+ if (PhysicsServer3D::BODY_MODE_DYNAMIC == mode) {
btBody->setCollisionFlags(clearedCurrentFlags); // Just set the flags without Kin and Static
} else {
btBody->setCollisionFlags(clearedCurrentFlags | btCollisionObject::CF_CHARACTER_OBJECT);
diff --git a/modules/bullet/rigid_body_bullet.h b/modules/bullet/rigid_body_bullet.h
index c643611397..01ac1e4836 100644
--- a/modules/bullet/rigid_body_bullet.h
+++ b/modules/bullet/rigid_body_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -81,21 +81,21 @@ public:
}
public:
- RigidBodyBullet *body;
- real_t deltaTime;
+ RigidBodyBullet *body = nullptr;
+ real_t deltaTime = 0.0;
private:
BulletPhysicsDirectBodyState3D() {}
public:
virtual Vector3 get_total_gravity() const override;
- virtual float get_total_angular_damp() const override;
- virtual float get_total_linear_damp() const override;
+ virtual real_t get_total_angular_damp() const override;
+ virtual real_t get_total_linear_damp() const override;
virtual Vector3 get_center_of_mass() const override;
virtual Basis get_principal_inertia_axes() const override;
// get the mass
- virtual float get_inverse_mass() const override;
+ virtual real_t get_inverse_mass() const override;
// get density of this body space
virtual Vector3 get_inverse_inertia() const override;
// get density of this body space
@@ -107,8 +107,10 @@ public:
virtual void set_angular_velocity(const Vector3 &p_velocity) override;
virtual Vector3 get_angular_velocity() const override;
- virtual void set_transform(const Transform &p_transform) override;
- virtual Transform get_transform() const override;
+ virtual void set_transform(const Transform3D &p_transform) override;
+ virtual Transform3D get_transform() const override;
+
+ virtual Vector3 get_velocity_at_local_position(const Vector3 &p_position) const override;
virtual void add_central_force(const Vector3 &p_force) override;
virtual void add_force(const Vector3 &p_force, const Vector3 &p_position = Vector3()) override;
@@ -124,7 +126,7 @@ public:
virtual Vector3 get_contact_local_position(int p_contact_idx) const override;
virtual Vector3 get_contact_local_normal(int p_contact_idx) const override;
- virtual float get_contact_impulse(int p_contact_idx) const override;
+ virtual real_t get_contact_impulse(int p_contact_idx) const override;
virtual int get_contact_local_shape(int p_contact_idx) const override;
virtual RID get_contact_collider(int p_contact_idx) const override;
@@ -144,18 +146,17 @@ public:
class RigidBodyBullet : public RigidCollisionObjectBullet {
public:
struct CollisionData {
- RigidBodyBullet *otherObject;
- int other_object_shape;
- int local_shape;
+ RigidBodyBullet *otherObject = nullptr;
+ int other_object_shape = 0;
+ int local_shape = 0;
Vector3 hitLocalLocation;
Vector3 hitWorldLocation;
Vector3 hitNormal;
- float appliedImpulse;
+ real_t appliedImpulse = 0.0;
};
struct ForceIntegrationCallback {
- ObjectID id;
- StringName method;
+ Callable callable;
Variant udata;
};
@@ -169,7 +170,7 @@ public:
};
struct KinematicUtilities {
- RigidBodyBullet *owner;
+ RigidBodyBullet *owner = nullptr;
btScalar safe_margin;
Vector<KinematicShape> shapes;
@@ -194,10 +195,10 @@ private:
GodotMotionState *godotMotionState;
btRigidBody *btBody;
uint16_t locked_axis = 0;
- real_t mass = 1;
- real_t gravity_scale = 1;
- real_t linearDamp = 0;
- real_t angularDamp = 0;
+ real_t mass = 1.0;
+ real_t gravity_scale = 1.0;
+ real_t linearDamp = 0.0;
+ real_t angularDamp = 0.0;
bool can_sleep = true;
bool omit_forces_integration = false;
bool can_integrate_forces = false;
@@ -240,7 +241,7 @@ public:
virtual void set_space(SpaceBullet *p_space);
virtual void dispatch_callbacks();
- void set_force_integration_callback(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
+ void set_force_integration_callback(const Callable &p_callable, const Variant &p_udata = Variant());
void scratch_space_override_modificator();
virtual void on_collision_filters_change();
@@ -264,11 +265,9 @@ public:
}
bool can_add_collision() { return collisionsCount < maxCollisionsDetection; }
- bool add_collision_object(RigidBodyBullet *p_otherObject, const Vector3 &p_hitWorldLocation, const Vector3 &p_hitLocalLocation, const Vector3 &p_hitNormal, const float &p_appliedImpulse, int p_other_shape_index, int p_local_shape_index);
+ bool 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);
bool was_colliding(RigidBodyBullet *p_other_object);
- void assert_no_constraints();
-
void set_activation_state(bool p_active);
bool is_active() const;
@@ -302,7 +301,7 @@ public:
void reload_axis_lock();
/// Doc:
- /// https://web.archive.org/web/20180404091446/http://www.bulletphysics.org/mediawiki-1.5.8/index.php/Anti_tunneling_by_Motion_Clamping
+ /// https://web.archive.org/web/20180404091446/https://www.bulletphysics.org/mediawiki-1.5.8/index.php/Anti_tunneling_by_Motion_Clamping
void set_continuous_collision_detection(bool p_enable);
bool is_continuous_collision_detection_enabled() const;
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index 340680c8d9..ec039ba842 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,7 +34,7 @@
#include "bullet_physics_server.h"
#include "bullet_types_converter.h"
#include "bullet_utilities.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "shape_owner_bullet.h"
#include <BulletCollision/CollisionDispatch/btInternalEdgeUtility.h>
@@ -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));
}
@@ -142,7 +142,7 @@ btScaledBvhTriangleMeshShape *ShapeBullet::create_shape_concave(btBvhTriangleMes
}
}
-btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
+btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(Vector<float> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
const btScalar ignoredHeightScale(1);
const int YAxis = 1; // 0=X, 1=Y, 2=Z
const bool flipQuadEdges = false;
@@ -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 */
@@ -275,7 +275,7 @@ void CapsuleShapeBullet::setup(real_t p_height, real_t p_radius) {
}
btCollisionShape *CapsuleShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
- return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_extra_edge, height * p_implicit_scale[1] + p_extra_edge));
+ return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_extra_edge, height * p_implicit_scale[1]));
}
/* Cylinder */
@@ -375,11 +375,17 @@ ConcavePolygonShapeBullet::~ConcavePolygonShapeBullet() {
}
void ConcavePolygonShapeBullet::set_data(const Variant &p_data) {
- setup(p_data);
+ Dictionary d = p_data;
+ ERR_FAIL_COND(!d.has("faces"));
+
+ setup(d["faces"]);
}
Variant ConcavePolygonShapeBullet::get_data() const {
- return faces;
+ Dictionary d;
+ d["faces"] = faces;
+
+ return d;
}
PhysicsServer3D::ShapeType ConcavePolygonShapeBullet::get_type() const {
@@ -474,10 +480,7 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) {
ERR_FAIL_COND_MSG(l_width < 2, "Map width must be at least 2.");
ERR_FAIL_COND_MSG(l_depth < 2, "Map depth must be at least 2.");
- // TODO This code will need adjustments if real_t is set to `double`,
- // because that precision is unnecessary for a heightmap and Bullet doesn't support it...
-
- Vector<real_t> l_heights;
+ Vector<float> l_heights;
Variant l_heights_v = d["heights"];
if (l_heights_v.get_type() == Variant::PACKED_FLOAT32_ARRAY) {
@@ -501,7 +504,7 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) {
l_heights.resize(l_image->get_width() * l_image->get_height());
- real_t *w = l_heights.ptrw();
+ float *w = l_heights.ptrw();
const uint8_t *r = im_data.ptr();
float *rp = (float *)r;
// At this point, `rp` could be used directly for Bullet, but I don't know how safe it would be.
@@ -520,11 +523,11 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) {
// Compute min and max heights if not specified.
if (!d.has("min_height") && !d.has("max_height")) {
- const real_t *r = l_heights.ptr();
+ const float *r = l_heights.ptr();
int heights_size = l_heights.size();
for (int i = 0; i < heights_size; ++i) {
- real_t h = r[i];
+ float h = r[i];
if (h < l_min_height) {
l_min_height = h;
@@ -545,7 +548,7 @@ PhysicsServer3D::ShapeType HeightMapShapeBullet::get_type() const {
return PhysicsServer3D::SHAPE_HEIGHTMAP;
}
-void HeightMapShapeBullet::setup(Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
+void HeightMapShapeBullet::setup(Vector<float> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
// TODO cell size must be tweaked using localScaling, which is a shared property for all Bullet shapes
// If this array is resized outside of here, it should be preserved due to CoW
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index a35a1d8a18..0822399b5e 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define SHAPE_BULLET_H
#include "core/math/geometry_3d.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "rid_bullet.h"
#include "servers/physics_server_3d.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);
@@ -89,15 +89,15 @@ public:
/// IMPORTANT: Remember to delete the shape interface by calling: delete my_shape->getMeshInterface();
static class btConvexPointCloudShape *create_shape_convex(btAlignedObjectArray<btVector3> &p_vertices, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
static class btScaledBvhTriangleMeshShape *create_shape_concave(btBvhTriangleMeshShape *p_mesh_shape, const btVector3 &p_local_scaling = btVector3(1, 1, 1));
- static class btHeightfieldTerrainShape *create_shape_height_field(Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
+ static class btHeightfieldTerrainShape *create_shape_height_field(Vector<float> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
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;
@@ -212,11 +212,11 @@ private:
class HeightMapShapeBullet : public ShapeBullet {
public:
- Vector<real_t> heights;
- int width;
- int depth;
- real_t min_height;
- real_t max_height;
+ Vector<float> heights;
+ int width = 0;
+ int depth = 0;
+ real_t min_height = 0.0;
+ real_t max_height = 0.0;
HeightMapShapeBullet();
@@ -226,12 +226,12 @@ public:
virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
- void setup(Vector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
+ void setup(Vector<float> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
};
class RayShapeBullet : public ShapeBullet {
public:
- real_t length = 1;
+ real_t length = 1.0;
bool slips_on_slope = false;
RayShapeBullet();
diff --git a/modules/bullet/shape_owner_bullet.cpp b/modules/bullet/shape_owner_bullet.cpp
index d63096d9a3..ea8821eaec 100644
--- a/modules/bullet/shape_owner_bullet.cpp
+++ b/modules/bullet/shape_owner_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/shape_owner_bullet.h b/modules/bullet/shape_owner_bullet.h
index f909632c99..4bd583e096 100644
--- a/modules/bullet/shape_owner_bullet.h
+++ b/modules/bullet/shape_owner_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/bullet/slider_joint_bullet.cpp b/modules/bullet/slider_joint_bullet.cpp
index 6d5d95d07a..1d83118468 100644
--- a/modules/bullet/slider_joint_bullet.cpp
+++ b/modules/bullet/slider_joint_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,16 +40,16 @@
@author AndreaCatania
*/
-SliderJointBullet::SliderJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB) :
+SliderJointBullet::SliderJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform3D &frameInA, const Transform3D &frameInB) :
JointBullet() {
- Transform scaled_AFrame(frameInA.scaled(rbA->get_body_scale()));
+ Transform3D scaled_AFrame(frameInA.scaled(rbA->get_body_scale()));
scaled_AFrame.basis.rotref_posscale_decomposition(scaled_AFrame.basis);
btTransform btFrameA;
G_TO_B(scaled_AFrame, btFrameA);
if (rbB) {
- Transform scaled_BFrame(frameInB.scaled(rbB->get_body_scale()));
+ Transform3D scaled_BFrame(frameInB.scaled(rbB->get_body_scale()));
scaled_BFrame.basis.rotref_posscale_decomposition(scaled_BFrame.basis);
btTransform btFrameB;
@@ -70,44 +70,44 @@ const RigidBodyBullet *SliderJointBullet::getRigidBodyB() const {
return static_cast<RigidBodyBullet *>(sliderConstraint->getRigidBodyB().getUserPointer());
}
-const Transform SliderJointBullet::getCalculatedTransformA() const {
+const Transform3D SliderJointBullet::getCalculatedTransformA() const {
btTransform btTransform = sliderConstraint->getCalculatedTransformA();
- Transform gTrans;
+ Transform3D gTrans;
B_TO_G(btTransform, gTrans);
return gTrans;
}
-const Transform SliderJointBullet::getCalculatedTransformB() const {
+const Transform3D SliderJointBullet::getCalculatedTransformB() const {
btTransform btTransform = sliderConstraint->getCalculatedTransformB();
- Transform gTrans;
+ Transform3D gTrans;
B_TO_G(btTransform, gTrans);
return gTrans;
}
-const Transform SliderJointBullet::getFrameOffsetA() const {
+const Transform3D SliderJointBullet::getFrameOffsetA() const {
btTransform btTransform = sliderConstraint->getFrameOffsetA();
- Transform gTrans;
+ Transform3D gTrans;
B_TO_G(btTransform, gTrans);
return gTrans;
}
-const Transform SliderJointBullet::getFrameOffsetB() const {
+const Transform3D SliderJointBullet::getFrameOffsetB() const {
btTransform btTransform = sliderConstraint->getFrameOffsetB();
- Transform gTrans;
+ Transform3D gTrans;
B_TO_G(btTransform, gTrans);
return gTrans;
}
-Transform SliderJointBullet::getFrameOffsetA() {
+Transform3D SliderJointBullet::getFrameOffsetA() {
btTransform btTransform = sliderConstraint->getFrameOffsetA();
- Transform gTrans;
+ Transform3D gTrans;
B_TO_G(btTransform, gTrans);
return gTrans;
}
-Transform SliderJointBullet::getFrameOffsetB() {
+Transform3D SliderJointBullet::getFrameOffsetB() {
btTransform btTransform = sliderConstraint->getFrameOffsetB();
- Transform gTrans;
+ Transform3D gTrans;
B_TO_G(btTransform, gTrans);
return gTrans;
}
diff --git a/modules/bullet/slider_joint_bullet.h b/modules/bullet/slider_joint_bullet.h
index 6410b952ed..0c93558449 100644
--- a/modules/bullet/slider_joint_bullet.h
+++ b/modules/bullet/slider_joint_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,18 +44,18 @@ class SliderJointBullet : public JointBullet {
public:
/// Reference frame is A
- SliderJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform &frameInA, const Transform &frameInB);
+ SliderJointBullet(RigidBodyBullet *rbA, RigidBodyBullet *rbB, const Transform3D &frameInA, const Transform3D &frameInB);
virtual PhysicsServer3D::JointType get_type() const { return PhysicsServer3D::JOINT_SLIDER; }
const RigidBodyBullet *getRigidBodyA() const;
const RigidBodyBullet *getRigidBodyB() const;
- const Transform getCalculatedTransformA() const;
- const Transform getCalculatedTransformB() const;
- const Transform getFrameOffsetA() const;
- const Transform getFrameOffsetB() const;
- Transform getFrameOffsetA();
- Transform getFrameOffsetB();
+ const Transform3D getCalculatedTransformA() const;
+ const Transform3D getCalculatedTransformB() const;
+ const Transform3D getFrameOffsetA() const;
+ const Transform3D getFrameOffsetB() const;
+ Transform3D getFrameOffsetA();
+ Transform3D getFrameOffsetB();
real_t getLowerLinLimit() const;
void setLowerLinLimit(real_t lowerLimit);
real_t getUpperLinLimit() const;
diff --git a/modules/bullet/soft_body_bullet.cpp b/modules/bullet/soft_body_bullet.cpp
index 6794d6c313..3a2370ff31 100644
--- a/modules/bullet/soft_body_bullet.cpp
+++ b/modules/bullet/soft_body_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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) {}
@@ -65,12 +66,12 @@ void SoftBodyBullet::on_enter_area(AreaBullet *p_area) {}
void SoftBodyBullet::on_exit_area(AreaBullet *p_area) {}
-void SoftBodyBullet::update_rendering_server(SoftBodyRenderingServerHandler *p_rendering_server_handler) {
+void SoftBodyBullet::update_rendering_server(RenderingServerHandler *p_rendering_server_handler) {
if (!bt_soft_body) {
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(SoftBodyRenderingServerHandler *p_r
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;
}
@@ -136,12 +140,30 @@ void SoftBodyBullet::destroy_soft_body() {
bt_soft_body = nullptr;
}
-void SoftBodyBullet::set_soft_transform(const Transform &p_transform) {
+void SoftBodyBullet::set_soft_transform(const Transform3D &p_transform) {
reset_all_node_positions();
move_all_nodes(p_transform);
}
-void SoftBodyBullet::move_all_nodes(const Transform &p_transform) {
+AABB SoftBodyBullet::get_bounds() const {
+ if (!bt_soft_body) {
+ return AABB();
+ }
+
+ btVector3 aabb_min;
+ btVector3 aabb_max;
+ bt_soft_body->getAabb(aabb_min, aabb_max);
+
+ btVector3 size(aabb_max - aabb_min);
+
+ AABB aabb;
+ B_TO_G(aabb_min, aabb.position);
+ B_TO_G(size, aabb.size);
+
+ return aabb;
+}
+
+void SoftBodyBullet::move_all_nodes(const Transform3D &p_transform) {
if (!bt_soft_body) {
return;
}
@@ -169,41 +191,24 @@ void SoftBodyBullet::get_node_position(int p_node_index, Vector3 &r_position) co
}
}
-void SoftBodyBullet::get_node_offset(int p_node_index, Vector3 &r_offset) const {
- if (soft_mesh.is_null()) {
- return;
- }
-
- Array arrays = soft_mesh->surface_get_arrays(0);
- Vector<Vector3> vertices(arrays[RS::ARRAY_VERTEX]);
-
- if (0 <= p_node_index && vertices.size() > p_node_index) {
- r_offset = vertices[p_node_index];
- }
-}
-
-void SoftBodyBullet::get_node_offset(int p_node_index, btVector3 &r_offset) const {
- Vector3 off;
- get_node_offset(p_node_index, off);
- G_TO_B(off, r_offset);
-}
-
-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;
}
}
@@ -259,20 +264,6 @@ void SoftBodyBullet::set_linear_stiffness(real_t p_val) {
}
}
-void SoftBodyBullet::set_areaAngular_stiffness(real_t p_val) {
- areaAngular_stiffness = p_val;
- if (bt_soft_body) {
- mat0->m_kAST = areaAngular_stiffness;
- }
-}
-
-void SoftBodyBullet::set_volume_stiffness(real_t p_val) {
- volume_stiffness = p_val;
- if (bt_soft_body) {
- mat0->m_kVST = volume_stiffness;
- }
-}
-
void SoftBodyBullet::set_simulation_precision(int p_val) {
simulation_precision = p_val;
if (bt_soft_body) {
@@ -290,13 +281,6 @@ void SoftBodyBullet::set_pressure_coefficient(real_t p_val) {
}
}
-void SoftBodyBullet::set_pose_matching_coefficient(real_t p_val) {
- pose_matching_coefficient = p_val;
- if (bt_soft_body) {
- bt_soft_body->m_cfg.kMT = pose_matching_coefficient;
- }
-}
-
void SoftBodyBullet::set_damping_coefficient(real_t p_val) {
damping_coefficient = p_val;
if (bt_soft_body) {
@@ -311,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
@@ -336,7 +320,7 @@ void SoftBodyBullet::set_trimesh_body_shape(Vector<int> p_indices, Vector<Vector
Map<Vector3, int>::Element *e = unique_vertices.find(p_vertices_read[vs_vertex_index]);
int vertex_id;
if (e) {
- // Already rxisting
+ // Already existing
vertex_id = e->value();
} else {
// Create new one
@@ -385,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() {
@@ -409,8 +395,6 @@ void SoftBodyBullet::setup_soft_body() {
bt_soft_body->generateBendingConstraints(2, mat0);
mat0->m_kLST = linear_stiffness;
- mat0->m_kAST = areaAngular_stiffness;
- mat0->m_kVST = volume_stiffness;
// Clusters allow to have Soft vs Soft collision but doesn't work well right now
@@ -430,7 +414,6 @@ void SoftBodyBullet::setup_soft_body() {
bt_soft_body->m_cfg.kDP = damping_coefficient;
bt_soft_body->m_cfg.kDG = drag_coefficient;
bt_soft_body->m_cfg.kPR = pressure_coefficient;
- bt_soft_body->m_cfg.kMT = pose_matching_coefficient;
bt_soft_body->setTotalMass(total_mass);
btSoftBodyHelpers::ReoptimizeLinkOrder(bt_soft_body);
@@ -438,17 +421,25 @@ 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);
diff --git a/modules/bullet/soft_body_bullet.h b/modules/bullet/soft_body_bullet.h
index da8a2412ed..84da56ae69 100644
--- a/modules/bullet/soft_body_bullet.h
+++ b/modules/bullet/soft_body_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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
@@ -55,22 +53,21 @@
@author AndreaCatania
*/
+class RenderingServerHandler;
+
class SoftBodyBullet : public CollisionObjectBullet {
private:
btSoftBody *bt_soft_body = nullptr;
Vector<Vector<int>> indices_table;
- btSoftBody::Material *mat0; // This is just a copy of pointer managed by btSoftBody
+ 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.;
real_t linear_stiffness = 0.5; // [0,1]
- real_t areaAngular_stiffness = 0.5; // [0,1]
- real_t volume_stiffness = 0.5; // [0,1]
real_t pressure_coefficient = 0.; // [-inf,+inf]
- real_t pose_matching_coefficient = 0.; // [0,1]
real_t damping_coefficient = 0.01; // [0,1]
real_t drag_coefficient = 0.; // [0,1]
Vector<int> pinned_nodes;
@@ -99,22 +96,20 @@ public:
_FORCE_INLINE_ btSoftBody *get_bt_soft_body() const { return bt_soft_body; }
- void update_rendering_server(class SoftBodyRenderingServerHandler *p_rendering_server_handler);
+ 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
- void set_soft_transform(const Transform &p_transform);
+ void set_soft_transform(const Transform3D &p_transform);
+
+ AABB get_bounds() const;
- void move_all_nodes(const Transform &p_transform);
+ void move_all_nodes(const Transform3D &p_transform);
void set_node_position(int node_index, const Vector3 &p_global_position);
void set_node_position(int node_index, const btVector3 &p_global_position);
void get_node_position(int node_index, Vector3 &r_position) const;
- // Heavy function, Please cache this info
- void get_node_offset(int node_index, Vector3 &r_offset) const;
- // Heavy function, Please cache this info
- void get_node_offset(int node_index, btVector3 &r_offset) const;
void set_node_mass(int node_index, btScalar p_mass);
btScalar get_node_mass(int node_index) const;
@@ -129,21 +124,12 @@ public:
void set_linear_stiffness(real_t p_val);
_FORCE_INLINE_ real_t get_linear_stiffness() const { return linear_stiffness; }
- void set_areaAngular_stiffness(real_t p_val);
- _FORCE_INLINE_ real_t get_areaAngular_stiffness() const { return areaAngular_stiffness; }
-
- void set_volume_stiffness(real_t p_val);
- _FORCE_INLINE_ real_t get_volume_stiffness() const { return volume_stiffness; }
-
void set_simulation_precision(int p_val);
_FORCE_INLINE_ int get_simulation_precision() const { return simulation_precision; }
void set_pressure_coefficient(real_t p_val);
_FORCE_INLINE_ real_t get_pressure_coefficient() const { return pressure_coefficient; }
- void set_pose_matching_coefficient(real_t p_val);
- _FORCE_INLINE_ real_t get_pose_matching_coefficient() const { return pose_matching_coefficient; }
-
void set_damping_coefficient(real_t p_val);
_FORCE_INLINE_ real_t get_damping_coefficient() const { return damping_coefficient; }
@@ -151,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 628e6ebd3c..bf47127877 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,8 +34,8 @@
#include "bullet_types_converter.h"
#include "bullet_utilities.h"
#include "constraint_bullet.h"
-#include "core/project_settings.h"
-#include "core/ustring.h"
+#include "core/config/project_settings.h"
+#include "core/string/ustring.h"
#include "godot_collision_configuration.h"
#include "godot_collision_dispatcher.h"
#include "rigid_body_bullet.h"
@@ -81,7 +81,7 @@ int BulletPhysicsDirectSpaceState::intersect_point(const Vector3 &p_point, Shape
btResult.m_collisionFilterMask = p_collision_mask;
space->dynamicsWorld->contactTest(&collision_object_point, btResult);
- // The results is already populated by GodotAllConvexResultCallback
+ // The results are already populated by GodotAllConvexResultCallback
return btResult.m_count;
}
@@ -117,12 +117,12 @@ bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const V
}
}
-int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
+int BulletPhysicsDirectSpaceState::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, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0) {
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);
@@ -152,13 +152,13 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
return btQuery.m_count;
}
-bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &r_closest_safe, float &r_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, ShapeRestInfo *r_info) {
+bool BulletPhysicsDirectSpaceState::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, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, ShapeRestInfo *r_info) {
r_closest_safe = 0.0f;
r_closest_unsafe = 0.0f;
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);
@@ -177,8 +177,10 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
bt_xform_to.getOrigin() += bt_motion;
if ((bt_xform_to.getOrigin() - bt_xform_from.getOrigin()).fuzzyZero()) {
+ r_closest_safe = 1.0f;
+ r_closest_unsafe = 1.0f;
bulletdelete(btShape);
- return false;
+ return true;
}
GodotClosestConvexResultCallback btResult(bt_xform_from.getOrigin(), bt_xform_to.getOrigin(), &p_exclude, p_collide_with_bodies, p_collide_with_areas);
@@ -212,12 +214,12 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
}
/// Returns the list of contacts pairs in this order: Local contact, other body contact
-bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
+bool BulletPhysicsDirectSpaceState::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, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0) {
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);
@@ -248,8 +250,8 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
return btQuery.m_count;
}
-bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_shape_xform, float 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);
+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()->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);
@@ -443,7 +445,7 @@ real_t SpaceBullet::get_param(PhysicsServer3D::SpaceParameter p_param) {
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.");
+ WARN_PRINT("The SpaceBullet doesn't support this get parameter (" + itos(p_param) + "), 0 is returned.");
return 0.f;
}
}
@@ -477,11 +479,25 @@ void SpaceBullet::add_rigid_body(RigidBodyBullet *p_body) {
}
}
+void SpaceBullet::remove_rigid_body_constraints(RigidBodyBullet *p_body) {
+ btRigidBody *btBody = p_body->get_bt_rigid_body();
+
+ int constraints = btBody->getNumConstraintRefs();
+ if (constraints > 0) {
+ ERR_PRINT("A body connected to joints was removed.");
+ for (int i = 0; i < constraints; i++) {
+ dynamicsWorld->removeConstraint(btBody->getConstraintRef(i));
+ }
+ }
+}
+
void SpaceBullet::remove_rigid_body(RigidBodyBullet *p_body) {
+ btRigidBody *btBody = p_body->get_bt_rigid_body();
+
if (p_body->is_static()) {
- dynamicsWorld->removeCollisionObject(p_body->get_bt_rigid_body());
+ dynamicsWorld->removeCollisionObject(btBody);
} else {
- dynamicsWorld->removeRigidBody(p_body->get_bt_rigid_body());
+ dynamicsWorld->removeRigidBody(btBody);
}
}
@@ -819,26 +835,38 @@ 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;
Vector3 collisionLocalPosition;
Vector3 normalOnB;
- float appliedImpulse = pt.m_appliedImpulse;
+ real_t appliedImpulse = pt.m_appliedImpulse;
B_TO_G(pt.m_normalWorldOnB, normalOnB);
+ // The pt.m_index only contains the shape index when more than one collision shape is used
+ // and only if the collision shape is not a concave collision shape.
+ // A value of -1 in pt.m_partId indicates the pt.m_index is a shape index.
+ int shape_index_a = 0;
+ if (bodyA->get_shape_count() > 1 && pt.m_partId0 == -1) {
+ shape_index_a = pt.m_index0;
+ }
+ int shape_index_b = 0;
+ if (bodyB->get_shape_count() > 1 && pt.m_partId1 == -1) {
+ shape_index_b = pt.m_index1;
+ }
+
if (bodyA->can_add_collision()) {
B_TO_G(pt.getPositionWorldOnB(), collisionWorldPosition);
/// pt.m_localPointB Doesn't report the exact point in local space
B_TO_G(pt.getPositionWorldOnB() - contactManifold->getBody1()->getWorldTransform().getOrigin(), collisionLocalPosition);
- bodyA->add_collision_object(bodyB, collisionWorldPosition, collisionLocalPosition, normalOnB, appliedImpulse, pt.m_index1, pt.m_index0);
+ bodyA->add_collision_object(bodyB, collisionWorldPosition, collisionLocalPosition, normalOnB, appliedImpulse, shape_index_b, shape_index_a);
}
if (bodyB->can_add_collision()) {
B_TO_G(pt.getPositionWorldOnA(), collisionWorldPosition);
/// pt.m_localPointA Doesn't report the exact point in local space
B_TO_G(pt.getPositionWorldOnA() - contactManifold->getBody0()->getWorldTransform().getOrigin(), collisionLocalPosition);
- bodyB->add_collision_object(bodyA, collisionWorldPosition, collisionLocalPosition, normalOnB * -1, appliedImpulse * -1, pt.m_index0, pt.m_index1);
+ bodyB->add_collision_object(bodyA, collisionWorldPosition, collisionLocalPosition, normalOnB * -1, appliedImpulse * -1, shape_index_a, shape_index_b);
}
#ifdef DEBUG_ENABLED
@@ -880,7 +908,7 @@ static Ref<StandardMaterial3D> red_mat;
static Ref<StandardMaterial3D> blue_mat;
#endif
-bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes) {
+bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes, const Set<RID> &p_exclude) {
#if debug_test_motion
/// Yes I know this is not good, but I've used it as fast debugging hack.
/// I'm leaving it here just for speedup the other eventual debugs
@@ -917,10 +945,14 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
G_TO_B(p_from, body_transform);
UNSCALE_BT_BASIS(body_transform);
+ if (!p_body->get_kinematic_utilities()) {
+ p_body->init_kinematic_utilities();
+ }
+
btVector3 initial_recover_motion(0, 0, 0);
{ /// Phase one - multi shapes depenetration using margin
for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) {
- if (!recover_from_penetration(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, initial_recover_motion)) {
+ if (!recover_from_penetration(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, initial_recover_motion, nullptr, p_exclude)) {
break;
}
}
@@ -930,6 +962,9 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
btVector3 motion;
G_TO_B(p_motion, motion);
+ real_t total_length = motion.length();
+ real_t unsafe_fraction = 1.0;
+ real_t safe_fraction = 1.0;
{
// Phase two - sweep test, from a secure position without margin
@@ -972,13 +1007,22 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
break;
}
- GodotKinClosestConvexResultCallback btResult(shape_world_from.getOrigin(), shape_world_to.getOrigin(), p_body, p_infinite_inertia);
+ GodotKinClosestConvexResultCallback btResult(shape_world_from.getOrigin(), shape_world_to.getOrigin(), p_body, p_infinite_inertia, &p_exclude);
btResult.m_collisionFilterGroup = p_body->get_collision_layer();
btResult.m_collisionFilterMask = p_body->get_collision_mask();
dynamicsWorld->convexSweepTest(convex_shape_test, shape_world_from, shape_world_to, btResult, dynamicsWorld->getDispatchInfo().m_allowedCcdPenetration);
if (btResult.hasHit()) {
+ if (total_length > CMP_EPSILON) {
+ real_t hit_fraction = btResult.m_closestHitFraction * motion.length() / total_length;
+ if (hit_fraction < unsafe_fraction) {
+ unsafe_fraction = hit_fraction;
+ real_t margin = p_body->get_kinematic_utilities()->safe_margin;
+ safe_fraction = MAX(hit_fraction - (1 - ((total_length - margin) / total_length)), 0);
+ }
+ }
+
/// Since for each sweep test I fix the motion of new shapes in base the recover result,
/// if another shape will hit something it means that has a deepest penetration respect the previous shape
motion *= btResult.m_closestHitFraction;
@@ -995,7 +1039,7 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
btVector3 __rec(0, 0, 0);
RecoverResult r_recover_result;
- has_penetration = recover_from_penetration(p_body, body_transform, 1, p_infinite_inertia, __rec, &r_recover_result);
+ has_penetration = recover_from_penetration(p_body, body_transform, 1, p_infinite_inertia, __rec, &r_recover_result, p_exclude);
// Parse results
if (r_result) {
@@ -1015,6 +1059,9 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
r_result->collider_id = collisionObject->get_instance_id();
r_result->collider_shape = r_recover_result.other_compound_shape_index;
r_result->collision_local_shape = r_recover_result.local_shape_most_recovered;
+ r_result->collision_depth = Math::abs(r_recover_result.penetration_distance);
+ r_result->collision_safe_fraction = safe_fraction;
+ r_result->collision_unsafe_fraction = unsafe_fraction;
#if debug_test_motion
Vector3 sup_line2;
@@ -1034,11 +1081,15 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
return has_penetration;
}
-int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer3D::SeparationResult *r_results, int p_result_max, float p_margin) {
+int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer3D::SeparationResult *r_results, int p_result_max, real_t p_margin) {
btTransform body_transform;
G_TO_B(p_transform, body_transform);
UNSCALE_BT_BASIS(body_transform);
+ if (!p_body->get_kinematic_utilities()) {
+ p_body->init_kinematic_utilities();
+ }
+
btVector3 recover_motion(0, 0, 0);
int rays_found = 0;
@@ -1064,13 +1115,12 @@ private:
btDbvtVolume bounds;
const btCollisionObject *self_collision_object;
- uint32_t collision_layer;
- uint32_t collision_mask;
+ uint32_t collision_layer = 0;
struct CompoundLeafCallback : btDbvt::ICollide {
private:
- RecoverPenetrationBroadPhaseCallback *parent_callback;
- btCollisionObject *collision_object;
+ RecoverPenetrationBroadPhaseCallback *parent_callback = nullptr;
+ btCollisionObject *collision_object = nullptr;
public:
CompoundLeafCallback(RecoverPenetrationBroadPhaseCallback *p_parent_callback, btCollisionObject *p_collision_object) :
@@ -1088,17 +1138,16 @@ private:
public:
struct BroadphaseResult {
- btCollisionObject *collision_object;
- int compound_child_index;
+ btCollisionObject *collision_object = nullptr;
+ int compound_child_index = 0;
};
Vector<BroadphaseResult> results;
public:
- RecoverPenetrationBroadPhaseCallback(const btCollisionObject *p_self_collision_object, uint32_t p_collision_layer, uint32_t p_collision_mask, btVector3 p_aabb_min, btVector3 p_aabb_max) :
+ RecoverPenetrationBroadPhaseCallback(const btCollisionObject *p_self_collision_object, uint32_t p_collision_layer, btVector3 p_aabb_min, btVector3 p_aabb_max) :
self_collision_object(p_self_collision_object),
- collision_layer(p_collision_layer),
- collision_mask(p_collision_mask) {
+ collision_layer(p_collision_layer) {
bounds = btDbvtVolume::FromMM(p_aabb_min, p_aabb_max);
}
@@ -1107,7 +1156,7 @@ public:
virtual bool process(const btBroadphaseProxy *proxy) {
btCollisionObject *co = static_cast<btCollisionObject *>(proxy->m_clientObject);
if (co->getInternalType() <= btCollisionObject::CO_RIGID_BODY) {
- if (self_collision_object != proxy->m_clientObject && GodotFilterCallback::test_collision_filters(collision_layer, collision_mask, proxy->m_collisionFilterGroup, proxy->m_collisionFilterMask)) {
+ if (self_collision_object != proxy->m_clientObject && (proxy->collision_layer & m_collisionFilterMask)) {
if (co->getCollisionShape()->isCompound()) {
const btCompoundShape *cs = static_cast<btCompoundShape *>(co->getCollisionShape());
@@ -1147,7 +1196,7 @@ public:
}
};
-bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result) {
+bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result, const Set<RID> &p_exclude) {
// Calculate the cumulative AABB of all shapes of the kinematic body
btVector3 aabb_min, aabb_max;
bool shapes_found = false;
@@ -1190,7 +1239,7 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran
}
// Perform broadphase test
- RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask(), aabb_min, aabb_max);
+ RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), aabb_min, aabb_max);
dynamicsWorld->getBroadphase()->aabbTest(aabb_min, aabb_max, recover_broad_result);
bool penetration = false;
@@ -1207,11 +1256,21 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran
continue;
}
+ if (kin_shape.shape->getShapeType() == EMPTY_SHAPE_PROXYTYPE) {
+ continue;
+ }
+
btTransform shape_transform = p_body_position * kin_shape.transform;
shape_transform.getOrigin() += r_delta_recover_movement;
for (int i = recover_broad_result.results.size() - 1; 0 <= i; --i) {
btCollisionObject *otherObject = recover_broad_result.results[i].collision_object;
+
+ CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(otherObject->getUserPointer());
+ if (p_exclude.has(gObj->get_self())) {
+ continue;
+ }
+
if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) {
otherObject->activate(); // Force activation of hitten rigid, soft body
continue;
@@ -1377,7 +1436,7 @@ int SpaceBullet::recover_from_penetration_ray(RigidBodyBullet *p_body, const btT
}
// Perform broadphase test
- RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask(), aabb_min, aabb_max);
+ RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), aabb_min, aabb_max);
dynamicsWorld->getBroadphase()->aabbTest(aabb_min, aabb_max, recover_broad_result);
int ray_count = 0;
diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h
index de281064af..cf8549030d 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#ifndef SPACE_BULLET_H
#define SPACE_BULLET_H
-#include "core/variant.h"
-#include "core/vector.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
#include "godot_result_callbacks.h"
#include "rid_bullet.h"
#include "servers/physics_server_3d.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 Transform &p_xform, float 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 Transform &p_xform, const Vector3 &p_motion, float p_margin, float &r_closest_safe, float &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 Transform &p_shape_xform, float 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 Transform &p_shape_xform, float 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;
};
@@ -100,12 +100,12 @@ class SpaceBullet : public RIDBullet {
btGhostPairCallback *ghostPairCallback = nullptr;
GodotFilterCallback *godotFilterCallback = nullptr;
- btGjkEpaPenetrationDepthSolver *gjk_epa_pen_solver;
- btVoronoiSimplexSolver *gjk_simplex_solver;
+ btGjkEpaPenetrationDepthSolver *gjk_epa_pen_solver = nullptr;
+ btVoronoiSimplexSolver *gjk_simplex_solver = nullptr;
BulletPhysicsDirectSpaceState *direct_access;
Vector3 gravityDirection = Vector3(0, -1, 0);
- real_t gravityMagnitude = 10;
+ real_t gravityMagnitude = 10.0;
real_t linear_damp = 0.0;
real_t angular_damp = 0.0;
@@ -151,6 +151,7 @@ public:
void reload_collision_filters(AreaBullet *p_area);
void add_rigid_body(RigidBodyBullet *p_body);
+ void remove_rigid_body_constraints(RigidBodyBullet *p_body);
void remove_rigid_body(RigidBodyBullet *p_body);
void reload_collision_filters(RigidBodyBullet *p_body);
@@ -167,7 +168,7 @@ public:
BulletPhysicsDirectSpaceState *get_direct_state();
void set_debug_contacts(int p_amount) { contactDebug.resize(p_amount); }
- _FORCE_INLINE_ bool is_debugging_contacts() const { return !contactDebug.empty(); }
+ _FORCE_INLINE_ bool is_debugging_contacts() const { return !contactDebug.is_empty(); }
_FORCE_INLINE_ void reset_debug_contact_count() {
contactDebugCount = 0;
}
@@ -187,8 +188,8 @@ public:
real_t get_linear_damp() const { return linear_damp; }
real_t get_angular_damp() const { return angular_damp; }
- bool test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes);
- int test_ray_separation(RigidBodyBullet *p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer3D::SeparationResult *r_results, int p_result_max, float p_margin);
+ bool test_body_motion(RigidBodyBullet *p_body, const Transform3D &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer3D::MotionResult *r_result, bool p_exclude_raycast_shapes, const Set<RID> &p_exclude = Set<RID>());
+ int test_ray_separation(RigidBodyBullet *p_body, const Transform3D &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer3D::SeparationResult *r_results, int p_result_max, real_t p_margin);
private:
void create_empty_world(bool p_create_soft_world);
@@ -208,7 +209,7 @@ private:
RecoverResult() {}
};
- bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = nullptr);
+ bool recover_from_penetration(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = nullptr, const Set<RID> &p_exclude = Set<RID>());
/// This is an API that recover a kinematic object from penetration
/// This allow only Convex Convex test and it always use GJK algorithm, With this API we don't benefit of Bullet special accelerated functions
bool RFP_convex_convex_test(const btConvexShape *p_shapeA, const btConvexShape *p_shapeB, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = nullptr);
diff --git a/modules/camera/SCsub b/modules/camera/SCsub
index 631a65bde2..de97724d09 100644
--- a/modules/camera/SCsub
+++ b/modules/camera/SCsub
@@ -5,17 +5,7 @@ Import("env_modules")
env_camera = env_modules.Clone()
-if env["platform"] == "iphone":
- # (iOS) Enable module support
- env_camera.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
-
- # (iOS) Build as separate static library
- modules_sources = []
- env_camera.add_source_files(modules_sources, "register_types.cpp")
- env_camera.add_source_files(modules_sources, "camera_ios.mm")
- mod_lib = env_modules.add_library("#bin/libgodot_camera_module" + env["LIBSUFFIX"], modules_sources)
-
-elif env["platform"] == "windows":
+if env["platform"] == "windows":
env_camera.add_source_files(env.modules_sources, "register_types.cpp")
env_camera.add_source_files(env.modules_sources, "camera_win.cpp")
diff --git a/modules/camera/camera_ios.mm b/modules/camera/camera_ios.mm
deleted file mode 100644
index e4cb928805..0000000000
--- a/modules/camera/camera_ios.mm
+++ /dev/null
@@ -1,445 +0,0 @@
-/*************************************************************************/
-/* camera_ios.mm */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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. */
-/*************************************************************************/
-
-///@TODO this is a near duplicate of CameraOSX, we should find a way to combine those to minimize code duplication!!!!
-// If you fix something here, make sure you fix it there as wel!
-
-#include "camera_ios.h"
-#include "servers/camera/camera_feed.h"
-
-#import <AVFoundation/AVFoundation.h>
-#import <UIKit/UIKit.h>
-
-//////////////////////////////////////////////////////////////////////////
-// MyCaptureSession - This is a little helper class so we can capture our frames
-
-@interface MyCaptureSession : AVCaptureSession <AVCaptureVideoDataOutputSampleBufferDelegate> {
- Ref<CameraFeed> feed;
- size_t width[2];
- size_t height[2];
- Vector<uint8_t> img_data[2];
-
- AVCaptureDeviceInput *input;
- AVCaptureVideoDataOutput *output;
-}
-
-@end
-
-@implementation MyCaptureSession
-
-- (id)initForFeed:(Ref<CameraFeed>)p_feed andDevice:(AVCaptureDevice *)p_device {
- if (self = [super init]) {
- NSError *error;
- feed = p_feed;
- width[0] = 0;
- height[0] = 0;
- width[1] = 0;
- height[1] = 0;
-
- // prepare our device
- [p_device lockForConfiguration:&error];
-
- [p_device setFocusMode:AVCaptureFocusModeLocked];
- [p_device setExposureMode:AVCaptureExposureModeLocked];
- [p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeLocked];
-
- [p_device unlockForConfiguration];
-
- [self beginConfiguration];
-
- // setup our capture
- self.sessionPreset = AVCaptureSessionPreset1280x720;
-
- input = [AVCaptureDeviceInput deviceInputWithDevice:p_device error:&error];
- if (!input) {
- print_line("Couldn't get input device for camera");
- } else {
- [self addInput:input];
- }
-
- output = [AVCaptureVideoDataOutput new];
- if (!output) {
- print_line("Couldn't get output device for camera");
- } else {
- NSDictionary *settings = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) };
- output.videoSettings = settings;
-
- // discard if the data output queue is blocked (as we process the still image)
- [output setAlwaysDiscardsLateVideoFrames:YES];
-
- // now set ourselves as the delegate to receive new frames. Note that we're doing this on the main thread at the moment, we may need to change this..
- [output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
-
- [self addOutput:output];
- }
-
- [self commitConfiguration];
-
- // kick off our session..
- [self startRunning];
- };
- return self;
-}
-
-- (void)cleanup {
- // stop running
- [self stopRunning];
-
- // cleanup
- [self beginConfiguration];
-
- if (input) {
- [self removeInput:input];
- // don't release this
- input = nil;
- }
-
- if (output) {
- [self removeOutput:output];
- [output setSampleBufferDelegate:nil queue:NULL];
- output = nil;
- }
-
- [self commitConfiguration];
-}
-
-- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
- // This gets called every time our camera has a new image for us to process.
- // May need to investigate in a way to throttle this if we get more images then we're rendering frames..
-
- // For now, version 1, we're just doing the bare minimum to make this work...
-
- CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
- // int width = CVPixelBufferGetWidth(pixelBuffer);
- // int height = CVPixelBufferGetHeight(pixelBuffer);
-
- // It says that we need to lock this on the documentation pages but it's not in the samples
- // need to lock our base address so we can access our pixel buffers, better safe then sorry?
- CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
-
- // get our buffers
- unsigned char *dataY = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
- unsigned char *dataCbCr = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
- if (dataY == NULL) {
- print_line("Couldn't access Y pixel buffer data");
- } else if (dataCbCr == NULL) {
- print_line("Couldn't access CbCr pixel buffer data");
- } else {
- UIInterfaceOrientation orientation = UIInterfaceOrientationUnknown;
-
- if (@available(iOS 13, *)) {
- orientation = [UIApplication sharedApplication].delegate.window.windowScene.interfaceOrientation;
-#if !defined(TARGET_OS_SIMULATOR) || !TARGET_OS_SIMULATOR
- } else {
- orientation = [[UIApplication sharedApplication] statusBarOrientation];
-#endif
- }
-
- Ref<Image> img[2];
-
- {
- // do Y
- size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
- size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
-
- if ((width[0] != new_width) || (height[0] != new_height)) {
- width[0] = new_width;
- height[0] = new_height;
- img_data[0].resize(new_width * new_height);
- }
-
- uint8_t *w = img_data[0].ptrw();
- memcpy(w, dataY, new_width * new_height);
-
- img[0].instance();
- img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]);
- }
-
- {
- // do CbCr
- size_t new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
- size_t new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
-
- if ((width[1] != new_width) || (height[1] != new_height)) {
- width[1] = new_width;
- height[1] = new_height;
- img_data[1].resize(2 * new_width * new_height);
- }
-
- 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
- img[1].instance();
- img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]);
- }
-
- // set our texture...
- feed->set_YCbCr_imgs(img[0], img[1]);
-
- // update our matrix to match the orientation, note, before changing anything
- // here, be aware that the project orientation settings must match your xcode
- // settings or this will go wrong!
- Transform2D display_transform;
- switch (orientation) {
- case UIInterfaceOrientationPortrait: {
- display_transform = Transform2D(0.0, -1.0, -1.0, 0.0, 1.0, 1.0);
- } break;
- case UIInterfaceOrientationLandscapeRight: {
- display_transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
- } break;
- case UIInterfaceOrientationLandscapeLeft: {
- display_transform = Transform2D(-1.0, 0.0, 0.0, 1.0, 1.0, 0.0);
- } break;
- default: {
- display_transform = Transform2D(0.0, 1.0, 1.0, 0.0, 0.0, 0.0);
- } break;
- }
-
- //TODO: this is correct for the camera on the back, I have a feeling this needs to be inversed for the camera on the front!
- feed->set_transform(display_transform);
- }
-
- // and unlock
- CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
-}
-
-@end
-
-//////////////////////////////////////////////////////////////////////////
-// CameraFeedIOS - Subclass for camera feeds in iOS
-
-class CameraFeedIOS : public CameraFeed {
-private:
- AVCaptureDevice *device;
- MyCaptureSession *capture_session;
-
-public:
- bool get_is_arkit() const;
- AVCaptureDevice *get_device() const;
-
- CameraFeedIOS();
- ~CameraFeedIOS();
-
- void set_device(AVCaptureDevice *p_device);
-
- bool activate_feed();
- void deactivate_feed();
-};
-
-AVCaptureDevice *CameraFeedIOS::get_device() const {
- return device;
-};
-
-CameraFeedIOS::CameraFeedIOS() {
- capture_session = NULL;
- device = NULL;
- transform = Transform2D(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); /* should re-orientate this based on device orientation */
-};
-
-void CameraFeedIOS::set_device(AVCaptureDevice *p_device) {
- device = p_device;
-
- // get some info
- NSString *device_name = p_device.localizedName;
- name = device_name.UTF8String;
- position = CameraFeed::FEED_UNSPECIFIED;
- if ([p_device position] == AVCaptureDevicePositionBack) {
- position = CameraFeed::FEED_BACK;
- } else if ([p_device position] == AVCaptureDevicePositionFront) {
- position = CameraFeed::FEED_FRONT;
- };
-};
-
-CameraFeedIOS::~CameraFeedIOS() {
- if (capture_session) {
- capture_session = nil;
- };
-
- if (device) {
- device = nil;
- };
-};
-
-bool CameraFeedIOS::activate_feed() {
- if (capture_session) {
- // already recording!
- } else {
- // start camera capture
- capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
- };
-
- return true;
-};
-
-void CameraFeedIOS::deactivate_feed() {
- // end camera capture if we have one
- if (capture_session) {
- [capture_session cleanup];
- capture_session = nil;
- };
-};
-
-//////////////////////////////////////////////////////////////////////////
-// MyDeviceNotifications - This is a little helper class gets notifications
-// when devices are connected/disconnected
-
-@interface MyDeviceNotifications : NSObject {
- CameraIOS *camera_server;
-}
-
-@end
-
-@implementation MyDeviceNotifications
-
-- (void)devices_changed:(NSNotification *)notification {
- camera_server->update_feeds();
-}
-
-- (id)initForServer:(CameraIOS *)p_server {
- if (self = [super init]) {
- camera_server = p_server;
-
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(devices_changed:) name:AVCaptureDeviceWasConnectedNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(devices_changed:) name:AVCaptureDeviceWasDisconnectedNotification object:nil];
- };
- return self;
-}
-
-- (void)dealloc {
- // remove notifications
- [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasConnectedNotification object:nil];
- [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasDisconnectedNotification object:nil];
-}
-
-@end
-
-MyDeviceNotifications *device_notifications = nil;
-
-//////////////////////////////////////////////////////////////////////////
-// CameraIOS - Subclass for our camera server on iPhone
-
-void CameraIOS::update_feeds() {
- // this way of doing things is deprecated but still works,
- // rewrite to using AVCaptureDeviceDiscoverySession
-
- NSMutableArray *deviceTypes = [NSMutableArray array];
-
- if (@available(iOS 10, *)) {
- [deviceTypes addObject:AVCaptureDeviceTypeBuiltInWideAngleCamera];
- [deviceTypes addObject:AVCaptureDeviceTypeBuiltInTelephotoCamera];
-
- if (@available(iOS 10.2, *)) {
- [deviceTypes addObject:AVCaptureDeviceTypeBuiltInDualCamera];
- }
-
- if (@available(iOS 11.1, *)) {
- [deviceTypes addObject:AVCaptureDeviceTypeBuiltInTrueDepthCamera];
- }
-
- AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession
- discoverySessionWithDeviceTypes:deviceTypes
- mediaType:AVMediaTypeVideo
- position:AVCaptureDevicePositionUnspecified];
-
- // remove devices that are gone..
- for (int i = feeds.size() - 1; i >= 0; i--) {
- Ref<CameraFeedIOS> feed(feeds[i]);
-
- if (feed.is_null()) {
- // feed not managed by us
- } else if (![session.devices containsObject:feed->get_device()]) {
- // remove it from our array, this will also destroy it ;)
- remove_feed(feed);
- };
- };
-
- // add new devices..
- for (AVCaptureDevice *device in session.devices) {
- bool found = false;
-
- for (int i = 0; i < feeds.size() && !found; i++) {
- Ref<CameraFeedIOS> feed(feeds[i]);
-
- if (feed.is_null()) {
- // feed not managed by us
- } else if (feed->get_device() == device) {
- found = true;
- };
- };
-
- if (!found) {
- Ref<CameraFeedIOS> newfeed;
- newfeed.instance();
- newfeed->set_device(device);
- add_feed(newfeed);
- };
- };
- }
-};
-
-CameraIOS::CameraIOS() {
- // check if we have our usage description
- NSString *usage_desc = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSCameraUsageDescription"];
- if (usage_desc == NULL) {
- // don't initialise if we don't get anything
- print_line("No NSCameraUsageDescription key in pList, no access to cameras.");
- return;
- } else if (usage_desc.length == 0) {
- // don't initialise if we don't get anything
- print_line("Empty NSCameraUsageDescription key in pList, no access to cameras.");
- return;
- }
-
- // now we'll request access.
- // If this is the first time the user will be prompted with the string (iOS will read it).
- // Once a decision is made it is returned. If the user wants to change it later on they
- // need to go into setting.
- print_line("Requesting Camera permissions");
-
- [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo
- completionHandler:^(BOOL granted) {
- if (granted) {
- print_line("Access to cameras granted!");
-
- // Find available cameras we have at this time
- update_feeds();
-
- // should only have one of these....
- device_notifications = [[MyDeviceNotifications alloc] initForServer:this];
- } else {
- print_line("No access to cameras!");
- }
- }];
-};
-
-CameraIOS::~CameraIOS() {
- device_notifications = nil;
-};
diff --git a/modules/camera/camera_osx.h b/modules/camera/camera_osx.h
index a07b83c6af..84274f0bf6 100644
--- a/modules/camera/camera_osx.h
+++ b/modules/camera/camera_osx.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define CAMERAOSX_H
///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!!
-// If you fix something here, make sure you fix it there as wel!
+// If you fix something here, make sure you fix it there as well!
#include "servers/camera_server.h"
diff --git a/modules/camera/camera_osx.mm b/modules/camera/camera_osx.mm
index 306632a016..6def813e5c 100644
--- a/modules/camera/camera_osx.mm
+++ b/modules/camera/camera_osx.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,10 +29,11 @@
/*************************************************************************/
///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimize code duplication!!!!
-// If you fix something here, make sure you fix it there as wel!
+// If you fix something here, make sure you fix it there as well!
#include "camera_osx.h"
#include "servers/camera/camera_feed.h"
+
#import <AVFoundation/AVFoundation.h>
//////////////////////////////////////////////////////////////////////////
@@ -106,15 +107,15 @@
if (input) {
[self removeInput:input];
// don't release this
- input = NULL;
+ input = nullptr;
}
// free up our output
if (output) {
[self removeOutput:output];
- [output setSampleBufferDelegate:nil queue:NULL];
+ [output setSampleBufferDelegate:nil queue:nullptr];
[output release];
- output = NULL;
+ output = nullptr;
}
[self commitConfiguration];
@@ -141,9 +142,9 @@
// get our buffers
unsigned char *dataY = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
unsigned char *dataCbCr = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
- if (dataY == NULL) {
+ if (dataY == nullptr) {
print_line("Couldn't access Y pixel buffer data");
- } else if (dataCbCr == NULL) {
+ } else if (dataCbCr == nullptr) {
print_line("Couldn't access CbCr pixel buffer data");
} else {
Ref<Image> img[2];
@@ -162,7 +163,7 @@
uint8_t *w = img_data[0].ptrw();
memcpy(w, dataY, new_width * new_height);
- img[0].instance();
+ img[0].instantiate();
img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]);
}
@@ -180,8 +181,8 @@
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
- img[1].instance();
+ ///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]);
}
@@ -220,8 +221,8 @@ AVCaptureDevice *CameraFeedOSX::get_device() const {
};
CameraFeedOSX::CameraFeedOSX() {
- device = NULL;
- capture_session = NULL;
+ device = nullptr;
+ capture_session = nullptr;
};
void CameraFeedOSX::set_device(AVCaptureDevice *p_device) {
@@ -240,23 +241,38 @@ void CameraFeedOSX::set_device(AVCaptureDevice *p_device) {
};
CameraFeedOSX::~CameraFeedOSX() {
- if (capture_session != NULL) {
+ if (capture_session != nullptr) {
[capture_session release];
- capture_session = NULL;
+ capture_session = nullptr;
};
- if (device != NULL) {
+ if (device != nullptr) {
[device release];
- device = NULL;
+ device = nullptr;
};
};
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;
@@ -267,7 +283,7 @@ void CameraFeedOSX::deactivate_feed() {
if (capture_session) {
[capture_session cleanup];
[capture_session release];
- capture_session = NULL;
+ capture_session = nullptr;
};
};
@@ -341,7 +357,7 @@ void CameraOSX::update_feeds() {
if (!found) {
Ref<CameraFeedOSX> newfeed;
- newfeed.instance();
+ newfeed.instantiate();
newfeed->set_device(device);
// assume display camera so inverse
diff --git a/modules/camera/camera_win.cpp b/modules/camera/camera_win.cpp
index 1646644be3..226a642dcf 100644
--- a/modules/camera/camera_win.cpp
+++ b/modules/camera/camera_win.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/camera/camera_win.h b/modules/camera/camera_win.h
index bbc8880c12..671e7d5beb 100644
--- a/modules/camera/camera_win.h
+++ b/modules/camera/camera_win.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/camera/config.py b/modules/camera/config.py
index 87d7542741..8a22751aa7 100644
--- a/modules/camera/config.py
+++ b/modules/camera/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return platform == "iphone" or platform == "osx" or platform == "windows"
+ return platform == "osx" or platform == "windows"
def configure(env):
diff --git a/modules/camera/register_types.cpp b/modules/camera/register_types.cpp
index 3b6c916914..0d33ff9ddc 100644
--- a/modules/camera/register_types.cpp
+++ b/modules/camera/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,9 +33,6 @@
#if defined(WINDOWS_ENABLED)
#include "camera_win.h"
#endif
-#if defined(IPHONE_ENABLED)
-#include "camera_ios.h"
-#endif
#if defined(OSX_ENABLED)
#include "camera_osx.h"
#endif
@@ -44,9 +41,6 @@ void register_camera_types() {
#if defined(WINDOWS_ENABLED)
CameraServer::make_default<CameraWindows>();
#endif
-#if defined(IPHONE_ENABLED)
- CameraServer::make_default<CameraIOS>();
-#endif
#if defined(OSX_ENABLED)
CameraServer::make_default<CameraOSX>();
#endif
diff --git a/modules/camera/register_types.h b/modules/camera/register_types.h
index e34f84bf2c..0ae9aa2c0b 100644
--- a/modules/camera/register_types.h
+++ b/modules/camera/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/csg/config.py b/modules/csg/config.py
index 9106cbceca..3991b846f9 100644
--- a/modules/csg/config.py
+++ b/modules/csg/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return True
+ return not env["disable_3d"]
def configure(env):
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
index 47982d519a..a7742ef415 100644
--- a/modules/csg/csg.cpp
+++ b/modules/csg/csg.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,34 +32,34 @@
#include "core/math/geometry_2d.h"
#include "core/math/math_funcs.h"
-#include "core/sort_array.h"
+#include "core/templates/sort_array.h"
// 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 (segment_length < CMP_EPSILON) {
+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);
}
inline static Vector2 interpolate_triangle_uv(const Vector2 p_vertices[3], const Vector2 p_uvs[3], const Vector2 &p_interpolation_point) {
- if (p_interpolation_point.distance_squared_to(p_vertices[0]) < CMP_EPSILON2) {
+ if (p_interpolation_point.is_equal_approx(p_vertices[0])) {
return p_uvs[0];
}
- if (p_interpolation_point.distance_squared_to(p_vertices[1]) < CMP_EPSILON2) {
+ if (p_interpolation_point.is_equal_approx(p_vertices[1])) {
return p_uvs[1];
}
- if (p_interpolation_point.distance_squared_to(p_vertices[2]) < CMP_EPSILON2) {
+ if (p_interpolation_point.is_equal_approx(p_vertices[2])) {
return p_uvs[2];
}
@@ -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,14 +258,14 @@ 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();
}
-void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform &p_xform) {
+void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform3D &p_xform) {
faces = p_brush.faces;
materials = p_brush.materials;
@@ -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;
@@ -530,8 +530,8 @@ void CSGBrushOperation::MeshMerge::_add_distance(List<real_t> &r_intersectionsA,
List<real_t> &intersections = p_from_B ? r_intersectionsB : r_intersectionsA;
// Check if distance exists.
- for (const List<real_t>::Element *E = intersections.front(); E; E = E->next()) {
- if (Math::is_equal_approx(**E, p_distance)) {
+ for (const real_t E : intersections) {
+ if (Math::is_equal_approx(E, p_distance)) {
return;
}
}
@@ -589,14 +589,14 @@ bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_de
Vector3 intersection_point;
// Check if faces are co-planar.
- if ((current_normal - face_normal).length_squared() < CMP_EPSILON2 &&
+ if (current_normal.is_equal_approx(face_normal) &&
is_point_in_triangle(face_center, current_points)) {
// Only add an intersection if not a B face.
if (!face.from_b) {
_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]);
}
@@ -931,7 +931,7 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_
// Delete the old faces in reverse index order.
merge_faces_idx.sort();
- merge_faces_idx.invert();
+ merge_faces_idx.reverse();
for (int i = 0; i < merge_faces_idx.size(); ++i) {
faces.remove(merge_faces_idx[i]);
}
@@ -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;
}
@@ -970,7 +970,7 @@ void CSGBrushOperation::Build2DFaces::_merge_faces(const Vector<int> &p_segment_
continue;
}
- // Check if point is on an each edge.
+ // Check if point is on each edge.
for (int face_edge_idx = 0; face_edge_idx < 3; ++face_edge_idx) {
Vector2 edge_points[2] = {
face_points[face_edge_idx],
@@ -978,7 +978,7 @@ 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.
@@ -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;
}
@@ -1076,9 +1076,9 @@ void CSGBrushOperation::Build2DFaces::_find_edge_intersections(const Vector2 p_s
break;
}
- // If opposite point is on the segemnt, add its index to segment indices too.
+ // 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);
}
@@ -1132,12 +1132,12 @@ 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];
}
}
- // Check if point is on an each edge.
+ // Check if point is on each edge.
bool on_edge = false;
for (int face_edge_idx = 0; face_edge_idx < 3; ++face_edge_idx) {
Vector2 edge_points[2] = {
@@ -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.
@@ -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;
}
@@ -1400,7 +1400,7 @@ void CSGBrushOperation::update_faces(const CSGBrush &p_brush_a, const int p_face
under_count++;
}
}
- // If all points under or over the plane, there is no intesection.
+ // If all points under or over the plane, there is no intersection.
if (over_count == 3 || under_count == 3) {
return;
}
@@ -1421,7 +1421,7 @@ void CSGBrushOperation::update_faces(const CSGBrush &p_brush_a, const int p_face
under_count++;
}
}
- // If all points under or over the plane, there is no intesection.
+ // If all points under or over the plane, there is no intersection.
if (over_count == 3 || under_count == 3) {
return;
}
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
index 34ee239c17..b1fe933268 100644
--- a/modules/csg/csg.h
+++ b/modules/csg/csg.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,16 +31,16 @@
#ifndef CSG_H
#define CSG_H
-#include "core/list.h"
-#include "core/map.h"
#include "core/math/aabb.h"
#include "core/math/plane.h"
-#include "core/math/transform.h"
+#include "core/math/transform_3d.h"
#include "core/math/vector2.h"
#include "core/math/vector3.h"
-#include "core/oa_hash_map.h"
-#include "core/reference.h"
-#include "core/vector.h"
+#include "core/object/ref_counted.h"
+#include "core/templates/list.h"
+#include "core/templates/map.h"
+#include "core/templates/oa_hash_map.h"
+#include "core/templates/vector.h"
#include "scene/resources/material.h"
struct CSGBrush {
@@ -48,9 +48,9 @@ struct CSGBrush {
Vector3 vertices[3];
Vector2 uvs[3];
AABB aabb;
- bool smooth;
- bool invert;
- int material;
+ bool smooth = false;
+ bool invert = false;
+ int material = 0;
};
Vector<Face> faces;
@@ -60,34 +60,34 @@ struct CSGBrush {
// Create a brush from faces.
void build_from_faces(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uvs, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials, const Vector<bool> &p_invert_faces);
- void copy_from(const CSGBrush &p_brush, const Transform &p_xform);
+ void copy_from(const CSGBrush &p_brush, const Transform3D &p_xform);
};
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);
struct MeshMerge {
struct Face {
- bool from_b;
- bool inside;
- int points[3];
+ bool from_b = false;
+ bool inside = false;
+ int points[3] = {};
Vector2 uvs[3];
- bool smooth;
- bool invert;
- int material_idx;
+ bool smooth = false;
+ bool invert = false;
+ int material_idx = 0;
};
struct FaceBVH {
- int face;
- int left;
- int right;
- int next;
+ int face = 0;
+ int left = 0;
+ int right = 0;
+ int next = 0;
Vector3 center;
AABB aabb;
};
@@ -142,7 +142,7 @@ struct CSGBrushOperation {
Map<Ref<Material>, int> materials;
Map<Vector3, int> vertex_map;
OAHashMap<VertexKey, int, VertexKeyHash> snap_cache;
- float vertex_snap;
+ float vertex_snap = 0.0;
inline void _add_distance(List<real_t> &r_intersectionsA, List<real_t> &r_intersectionsB, bool p_from_B, real_t p_distance) const;
inline bool _bvh_inside(FaceBVH *facebvhptr, int p_max_depth, int p_bvh_first, int p_face_idx) const;
@@ -159,15 +159,15 @@ struct CSGBrushOperation {
};
struct Face2D {
- int vertex_idx[3];
+ int vertex_idx[3] = {};
};
Vector<Vertex2D> vertices;
Vector<Face2D> faces;
Plane plane;
- Transform to_2D;
- Transform to_3D;
- float vertex_snap2;
+ Transform3D to_2D;
+ Transform3D to_3D;
+ float vertex_snap2 = 0.0;
inline int _get_point_idx(const Vector2 &p_point);
inline int _add_vertex(const Vertex2D &p_vertex);
diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp
index cce72770f5..2f8b354bb7 100644
--- a/modules/csg/csg_gizmos.cpp
+++ b/modules/csg/csg_gizmos.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,8 @@
/*************************************************************************/
#include "csg_gizmos.h"
+#include "editor/plugins/node_3d_editor_plugin.h"
+#include "scene/3d/camera_3d.h"
///////////
@@ -48,7 +50,7 @@ CSGShape3DGizmoPlugin::CSGShape3DGizmoPlugin() {
create_handle_material("handles");
}
-String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const {
+String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {
CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());
if (Object::cast_to<CSGSphere3D>(cs)) {
@@ -56,22 +58,21 @@ String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo,
}
if (Object::cast_to<CSGBox3D>(cs)) {
- static const char *hname[3] = { "Width", "Height", "Depth" };
- return hname[p_idx];
+ return "Size";
}
if (Object::cast_to<CSGCylinder3D>(cs)) {
- return p_idx == 0 ? "Radius" : "Height";
+ return p_id == 0 ? "Radius" : "Height";
}
if (Object::cast_to<CSGTorus3D>(cs)) {
- return p_idx == 0 ? "InnerRadius" : "OuterRadius";
+ return p_id == 0 ? "InnerRadius" : "OuterRadius";
}
return "";
}
-Variant CSGShape3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const {
+Variant CSGShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {
CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());
if (Object::cast_to<CSGSphere3D>(cs)) {
@@ -81,35 +82,28 @@ Variant CSGShape3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int
if (Object::cast_to<CSGBox3D>(cs)) {
CSGBox3D *s = Object::cast_to<CSGBox3D>(cs);
- switch (p_idx) {
- case 0:
- return s->get_width();
- case 1:
- return s->get_height();
- case 2:
- return s->get_depth();
- }
+ return s->get_size();
}
if (Object::cast_to<CSGCylinder3D>(cs)) {
CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);
- return p_idx == 0 ? s->get_radius() : s->get_height();
+ return p_id == 0 ? s->get_radius() : s->get_height();
}
if (Object::cast_to<CSGTorus3D>(cs)) {
CSGTorus3D *s = Object::cast_to<CSGTorus3D>(cs);
- return p_idx == 0 ? s->get_inner_radius() : s->get_outer_radius();
+ return p_id == 0 ? s->get_inner_radius() : s->get_outer_radius();
}
return Variant();
}
-void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) {
+void CSGShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) {
CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());
- Transform gt = cs->get_global_transform();
+ Transform3D gt = cs->get_global_transform();
//gt.orthonormalize();
- Transform gi = gt.affine_inverse();
+ Transform3D gi = gt.affine_inverse();
Vector3 ray_from = p_camera->project_ray_origin(p_point);
Vector3 ray_dir = p_camera->project_ray_normal(p_point);
@@ -123,7 +117,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca
Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
float d = ra.x;
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
- d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
+ d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
if (d < 0.001) {
@@ -137,50 +131,48 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca
CSGBox3D *s = Object::cast_to<CSGBox3D>(cs);
Vector3 axis;
- axis[p_idx] = 1.0;
+ axis[p_id] = 1.0;
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
- float d = ra[p_idx];
+ float d = ra[p_id];
+
+ if (Math::is_nan(d)) {
+ // The handle is perpendicular to the camera.
+ return;
+ }
+
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
- d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
+ d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
if (d < 0.001) {
d = 0.001;
}
- switch (p_idx) {
- case 0:
- s->set_width(d * 2);
- break;
- case 1:
- s->set_height(d * 2);
- break;
- case 2:
- s->set_depth(d * 2);
- break;
- }
+ Vector3 h = s->get_size();
+ h[p_id] = d * 2;
+ s->set_size(h);
}
if (Object::cast_to<CSGCylinder3D>(cs)) {
CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);
Vector3 axis;
- axis[p_idx == 0 ? 0 : 1] = 1.0;
+ axis[p_id == 0 ? 0 : 1] = 1.0;
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
float d = axis.dot(ra);
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
- d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
+ d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
if (d < 0.001) {
d = 0.001;
}
- if (p_idx == 0) {
+ if (p_id == 0) {
s->set_radius(d);
- } else if (p_idx == 1) {
+ } else if (p_id == 1) {
s->set_height(d * 2.0);
}
}
@@ -194,22 +186,22 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca
Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
float d = axis.dot(ra);
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
- d = Math::stepify(d, Node3DEditor::get_singleton()->get_translate_snap());
+ d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
if (d < 0.001) {
d = 0.001;
}
- if (p_idx == 0) {
+ if (p_id == 0) {
s->set_inner_radius(d);
- } else if (p_idx == 1) {
+ } else if (p_id == 1) {
s->set_outer_radius(d);
}
}
}
-void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) {
+void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) {
CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());
if (Object::cast_to<CSGSphere3D>(cs)) {
@@ -229,45 +221,21 @@ void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx,
if (Object::cast_to<CSGBox3D>(cs)) {
CSGBox3D *s = Object::cast_to<CSGBox3D>(cs);
if (p_cancel) {
- switch (p_idx) {
- case 0:
- s->set_width(p_restore);
- break;
- case 1:
- s->set_height(p_restore);
- break;
- case 2:
- s->set_depth(p_restore);
- break;
- }
+ s->set_size(p_restore);
return;
}
UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
- ur->create_action(TTR("Change Box Shape Extents"));
- static const char *method[3] = { "set_width", "set_height", "set_depth" };
- float current = 0;
- switch (p_idx) {
- case 0:
- current = s->get_width();
- break;
- case 1:
- current = s->get_height();
- break;
- case 2:
- current = s->get_depth();
- break;
- }
-
- ur->add_do_method(s, method[p_idx], current);
- ur->add_undo_method(s, method[p_idx], p_restore);
+ ur->create_action(TTR("Change Box Shape Size"));
+ ur->add_do_method(s, "set_size", s->get_size());
+ ur->add_undo_method(s, "set_size", p_restore);
ur->commit_action();
}
if (Object::cast_to<CSGCylinder3D>(cs)) {
CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);
if (p_cancel) {
- if (p_idx == 0) {
+ if (p_id == 0) {
s->set_radius(p_restore);
} else {
s->set_height(p_restore);
@@ -276,7 +244,7 @@ void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx,
}
UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
- if (p_idx == 0) {
+ if (p_id == 0) {
ur->create_action(TTR("Change Cylinder Radius"));
ur->add_do_method(s, "set_radius", s->get_radius());
ur->add_undo_method(s, "set_radius", p_restore);
@@ -292,7 +260,7 @@ void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx,
if (Object::cast_to<CSGTorus3D>(cs)) {
CSGTorus3D *s = Object::cast_to<CSGTorus3D>(cs);
if (p_cancel) {
- if (p_idx == 0) {
+ if (p_id == 0) {
s->set_inner_radius(p_restore);
} else {
s->set_outer_radius(p_restore);
@@ -301,7 +269,7 @@ void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx,
}
UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
- if (p_idx == 0) {
+ if (p_id == 0) {
ur->create_action(TTR("Change Torus Inner Radius"));
ur->add_do_method(s, "set_inner_radius", s->get_inner_radius());
ur->add_undo_method(s, "set_inner_radius", p_restore);
@@ -319,7 +287,7 @@ bool CSGShape3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<CSGSphere3D>(p_spatial) || Object::cast_to<CSGBox3D>(p_spatial) || Object::cast_to<CSGCylinder3D>(p_spatial) || Object::cast_to<CSGTorus3D>(p_spatial) || Object::cast_to<CSGMesh3D>(p_spatial) || Object::cast_to<CSGPolygon3D>(p_spatial);
}
-String CSGShape3DGizmoPlugin::get_name() const {
+String CSGShape3DGizmoPlugin::get_gizmo_name() const {
return "CSGShape3D";
}
@@ -332,27 +300,16 @@ bool CSGShape3DGizmoPlugin::is_selectable_when_hidden() const {
}
void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
- CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());
-
p_gizmo->clear();
- Ref<Material> material;
- switch (cs->get_operation()) {
- case CSGShape3D::OPERATION_UNION:
- material = get_material("shape_union_material", p_gizmo);
- break;
- case CSGShape3D::OPERATION_INTERSECTION:
- material = get_material("shape_intersection_material", p_gizmo);
- break;
- case CSGShape3D::OPERATION_SUBTRACTION:
- material = get_material("shape_subtraction_material", p_gizmo);
- break;
- }
-
- Ref<Material> handles_material = get_material("handles");
+ CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());
Vector<Vector3> faces = cs->get_brush_faces();
+ if (faces.size() == 0) {
+ return;
+ }
+
Vector<Vector3> lines;
lines.resize(faces.size() * 2);
{
@@ -368,6 +325,21 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
}
+ Ref<Material> material;
+ switch (cs->get_operation()) {
+ case CSGShape3D::OPERATION_UNION:
+ material = get_material("shape_union_material", p_gizmo);
+ break;
+ case CSGShape3D::OPERATION_INTERSECTION:
+ material = get_material("shape_intersection_material", p_gizmo);
+ break;
+ case CSGShape3D::OPERATION_SUBTRACTION:
+ material = get_material("shape_subtraction_material", p_gizmo);
+ break;
+ }
+
+ Ref<Material> handles_material = get_material("handles");
+
p_gizmo->add_lines(lines, material);
p_gizmo->add_collision_segments(lines);
@@ -392,7 +364,7 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
break;
}
- p_gizmo->add_mesh(mesh, false, Ref<SkinReference>(), solid_material);
+ p_gizmo->add_mesh(mesh, solid_material);
}
if (Object::cast_to<CSGSphere3D>(cs)) {
@@ -408,9 +380,13 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
CSGBox3D *s = Object::cast_to<CSGBox3D>(cs);
Vector<Vector3> handles;
- handles.push_back(Vector3(s->get_width() * 0.5, 0, 0));
- handles.push_back(Vector3(0, s->get_height() * 0.5, 0));
- handles.push_back(Vector3(0, 0, s->get_depth() * 0.5));
+
+ for (int i = 0; i < 3; i++) {
+ Vector3 h;
+ h[i] = s->get_size()[i] / 2;
+ handles.push_back(h);
+ }
+
p_gizmo->add_handles(handles, handles_material);
}
diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h
index 83ee847caf..2a6ab91102 100644
--- a/modules/csg/csg_gizmos.h
+++ b/modules/csg/csg_gizmos.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,22 +33,22 @@
#include "csg_shape.h"
#include "editor/editor_plugin.h"
-#include "editor/node_3d_editor_gizmos.h"
+#include "editor/plugins/node_3d_editor_gizmos.h"
class CSGShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(CSGShape3DGizmoPlugin, EditorNode3DGizmoPlugin);
public:
- bool has_gizmo(Node3D *p_spatial) override;
- String get_name() const override;
- int get_priority() const override;
- bool is_selectable_when_hidden() const override;
- void redraw(EditorNode3DGizmo *p_gizmo) override;
-
- String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override;
- Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override;
- void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override;
- void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) override;
+ virtual bool has_gizmo(Node3D *p_spatial) override;
+ virtual String get_gizmo_name() const override;
+ virtual int get_priority() const override;
+ virtual bool is_selectable_when_hidden() const override;
+ virtual void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override;
+ virtual Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override;
+ virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override;
+ virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) override;
CSGShape3DGizmoPlugin();
};
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index 8f2ebc7232..b9be7535dc 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,8 +29,8 @@
/*************************************************************************/
#include "csg_shape.h"
+
#include "core/math/geometry_2d.h"
-#include "scene/3d/path_3d.h"
void CSGShape3D::set_use_collision(bool p_enable) {
if (use_collision == p_enable) {
@@ -44,8 +44,9 @@ void CSGShape3D::set_use_collision(bool p_enable) {
}
if (use_collision) {
- root_collision_shape.instance();
- root_collision_instance = PhysicsServer3D::get_singleton()->body_create(PhysicsServer3D::BODY_MODE_STATIC);
+ root_collision_shape.instantiate();
+ root_collision_instance = PhysicsServer3D::get_singleton()->body_create();
+ PhysicsServer3D::get_singleton()->body_set_mode(root_collision_instance, PhysicsServer3D::BODY_MODE_STATIC);
PhysicsServer3D::get_singleton()->body_set_state(root_collision_instance, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform());
PhysicsServer3D::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
PhysicsServer3D::get_singleton()->body_set_space(root_collision_instance, get_world_3d()->get_space());
@@ -58,7 +59,7 @@ void CSGShape3D::set_use_collision(bool p_enable) {
root_collision_instance = RID();
root_collision_shape.unref();
}
- _change_notify();
+ notify_property_list_changed();
}
bool CSGShape3D::is_using_collision() const {
@@ -87,32 +88,40 @@ uint32_t CSGShape3D::get_collision_mask() const {
return collision_mask;
}
-void CSGShape3D::set_collision_mask_bit(int p_bit, bool p_value) {
- uint32_t mask = get_collision_mask();
+void CSGShape3D::set_collision_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
+ uint32_t collision_layer = get_collision_layer();
if (p_value) {
- mask |= 1 << p_bit;
+ collision_layer |= 1 << (p_layer_number - 1);
} else {
- mask &= ~(1 << p_bit);
+ collision_layer &= ~(1 << (p_layer_number - 1));
}
- set_collision_mask(mask);
+ set_collision_layer(collision_layer);
}
-bool CSGShape3D::get_collision_mask_bit(int p_bit) const {
- return get_collision_mask() & (1 << p_bit);
+bool CSGShape3D::get_collision_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
+ return get_collision_layer() & (1 << (p_layer_number - 1));
}
-void CSGShape3D::set_collision_layer_bit(int p_bit, bool p_value) {
- uint32_t mask = get_collision_layer();
+void CSGShape3D::set_collision_mask_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
+ uint32_t mask = get_collision_mask();
if (p_value) {
- mask |= 1 << p_bit;
+ mask |= 1 << (p_layer_number - 1);
} else {
- mask &= ~(1 << p_bit);
+ mask &= ~(1 << (p_layer_number - 1));
}
- set_collision_layer(mask);
+ set_collision_mask(mask);
}
-bool CSGShape3D::get_collision_layer_bit(int p_bit) const {
- return get_collision_layer() & (1 << p_bit);
+bool CSGShape3D::get_collision_mask_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
+ return get_collision_mask() & (1 << (p_layer_number - 1));
}
bool CSGShape3D::is_root_shape() const {
@@ -135,7 +144,7 @@ void CSGShape3D::_make_dirty() {
if (parent) {
parent->_make_dirty();
} else if (!dirty) {
- call_deferred("_update_shape");
+ call_deferred(SNAME("_update_shape"));
}
dirty = true;
@@ -183,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);
@@ -271,7 +280,7 @@ void CSGShape3D::mikktSetTSpaceDefault(const SMikkTSpaceContext *pContext, const
}
void CSGShape3D::_update_shape() {
- if (parent) {
+ if (parent || !is_inside_tree()) {
return;
}
@@ -406,7 +415,7 @@ void CSGShape3D::_update_shape() {
}
}
- root_mesh.instance();
+ root_mesh.instantiate();
//create surfaces
for (int i = 0; i < surfaces.size(); i++) {
@@ -493,8 +502,9 @@ void CSGShape3D::_notification(int p_what) {
}
if (use_collision && is_root_shape()) {
- root_collision_shape.instance();
- root_collision_instance = PhysicsServer3D::get_singleton()->body_create(PhysicsServer3D::BODY_MODE_STATIC);
+ root_collision_shape.instantiate();
+ root_collision_instance = PhysicsServer3D::get_singleton()->body_create();
+ PhysicsServer3D::get_singleton()->body_set_mode(root_collision_instance, PhysicsServer3D::BODY_MODE_STATIC);
PhysicsServer3D::get_singleton()->body_set_state(root_collision_instance, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform());
PhysicsServer3D::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
PhysicsServer3D::get_singleton()->body_set_space(root_collision_instance, get_world_3d()->get_space());
@@ -542,7 +552,7 @@ void CSGShape3D::_notification(int p_what) {
void CSGShape3D::set_operation(Operation p_operation) {
operation = p_operation;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
CSGShape3D::Operation CSGShape3D::get_operation() const {
@@ -562,17 +572,18 @@ 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 {
if (root_mesh.is_valid()) {
Array arr;
arr.resize(2);
- arr[0] = Transform();
+ arr[0] = Transform3D();
arr[1] = root_mesh;
return arr;
}
@@ -599,11 +610,11 @@ void CSGShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CSGShape3D::set_collision_mask);
ClassDB::bind_method(D_METHOD("get_collision_mask"), &CSGShape3D::get_collision_mask);
- ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CSGShape3D::set_collision_mask_bit);
- ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CSGShape3D::get_collision_mask_bit);
+ ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CSGShape3D::set_collision_mask_value);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CSGShape3D::get_collision_mask_value);
- ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &CSGShape3D::set_collision_layer_bit);
- ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CSGShape3D::get_collision_layer_bit);
+ ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CSGShape3D::set_collision_layer_value);
+ ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CSGShape3D::get_collision_layer_value);
ClassDB::bind_method(D_METHOD("set_calculate_tangents", "enabled"), &CSGShape3D::set_calculate_tangents);
ClassDB::bind_method(D_METHOD("is_calculating_tangents"), &CSGShape3D::is_calculating_tangents);
@@ -625,15 +636,6 @@ void CSGShape3D::_bind_methods() {
}
CSGShape3D::CSGShape3D() {
- operation = OPERATION_UNION;
- parent = nullptr;
- brush = nullptr;
- dirty = false;
- snap = 0.001;
- use_collision = false;
- collision_layer = 1;
- collision_mask = 1;
- calculate_tangents = true;
set_notify_local_transform(true);
}
@@ -701,7 +703,7 @@ CSGPrimitive3D::CSGPrimitive3D() {
CSGBrush *CSGMesh3D::_build_brush() {
if (!mesh.is_valid()) {
- return nullptr;
+ return memnew(CSGBrush);
}
Vector<Vector3> vertices;
@@ -719,7 +721,7 @@ CSGBrush *CSGMesh3D::_build_brush() {
if (arrays.size() == 0) {
_make_dirty();
- ERR_FAIL_COND_V(arrays.size() == 0, nullptr);
+ ERR_FAIL_COND_V(arrays.size() == 0, memnew(CSGBrush));
}
Vector<Vector3> avertices = arrays[Mesh::ARRAY_VERTEX];
@@ -781,7 +783,7 @@ CSGBrush *CSGMesh3D::_build_brush() {
}
}
- bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON;
+ bool flat = normal[0].is_equal_approx(normal[1]) && normal[0].is_equal_approx(normal[2]);
vw[as + j + 0] = vertex[0];
vw[as + j + 1] = vertex[1];
@@ -823,7 +825,7 @@ CSGBrush *CSGMesh3D::_build_brush() {
}
}
- bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON;
+ bool flat = normal[0].is_equal_approx(normal[1]) && normal[0].is_equal_approx(normal[2]);
vw[as + j + 0] = vertex[0];
vw[as + j + 1] = vertex[1];
@@ -840,7 +842,7 @@ CSGBrush *CSGMesh3D::_build_brush() {
}
if (vertices.size() == 0) {
- return nullptr;
+ return memnew(CSGBrush);
}
return _create_brush_from_arrays(vertices, uvs, smooth, materials);
@@ -848,7 +850,7 @@ CSGBrush *CSGMesh3D::_build_brush() {
void CSGMesh3D::_mesh_changed() {
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
void CSGMesh3D::set_material(const Ref<Material> &p_material) {
@@ -871,7 +873,7 @@ void CSGMesh3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_material"), &CSGMesh3D::get_material);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "StandardMaterial3D,ShaderMaterial"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
void CSGMesh3D::set_mesh(const Ref<Mesh> &p_mesh) {
@@ -887,7 +889,7 @@ void CSGMesh3D::set_mesh(const Ref<Mesh> &p_mesh) {
mesh->connect("changed", callable_mp(this, &CSGMesh3D::_mesh_changed));
}
- _make_dirty();
+ _mesh_changed();
}
Ref<Mesh> CSGMesh3D::get_mesh() {
@@ -926,47 +928,51 @@ CSGBrush *CSGSphere3D::_build_brush() {
Ref<Material> *materialsw = materials.ptrw();
bool *invertw = invert.ptrw();
+ // We want to follow an order that's convenient for UVs.
+ // For latitude step we start at the top and move down like in an image.
+ const double latitude_step = -Math_PI / rings;
+ const double longitude_step = Math_TAU / radial_segments;
int face = 0;
-
- for (int i = 1; i <= rings; i++) {
- double lat0 = Math_PI * (-0.5 + (double)(i - 1) / rings);
- double z0 = Math::sin(lat0);
- double zr0 = Math::cos(lat0);
- double u0 = double(i - 1) / rings;
-
- double lat1 = Math_PI * (-0.5 + (double)i / rings);
- double z1 = Math::sin(lat1);
- double zr1 = Math::cos(lat1);
- double u1 = double(i) / rings;
-
- for (int j = radial_segments; j >= 1; j--) {
- double lng0 = 2 * Math_PI * (double)(j - 1) / radial_segments;
- double x0 = Math::cos(lng0);
- double y0 = Math::sin(lng0);
- double v0 = double(i - 1) / radial_segments;
-
- double lng1 = 2 * Math_PI * (double)(j) / radial_segments;
- double x1 = Math::cos(lng1);
- double y1 = Math::sin(lng1);
- double v1 = double(i) / radial_segments;
+ for (int i = 0; i < rings; i++) {
+ double latitude0 = latitude_step * i + Math_TAU / 4;
+ double cos0 = Math::cos(latitude0);
+ double sin0 = Math::sin(latitude0);
+ double v0 = double(i) / rings;
+
+ double latitude1 = latitude_step * (i + 1) + Math_TAU / 4;
+ double cos1 = Math::cos(latitude1);
+ double sin1 = Math::sin(latitude1);
+ double v1 = double(i + 1) / rings;
+
+ for (int j = 0; j < radial_segments; j++) {
+ double longitude0 = longitude_step * j;
+ // We give sin to X and cos to Z on purpose.
+ // This allows UVs to be CCW on +X so it maps to images well.
+ double x0 = Math::sin(longitude0);
+ double z0 = Math::cos(longitude0);
+ double u0 = double(j) / radial_segments;
+
+ double longitude1 = longitude_step * (j + 1);
+ double x1 = Math::sin(longitude1);
+ double z1 = Math::cos(longitude1);
+ double u1 = double(j + 1) / radial_segments;
Vector3 v[4] = {
- Vector3(x1 * zr0, z0, y1 * zr0) * radius,
- Vector3(x1 * zr1, z1, y1 * zr1) * radius,
- Vector3(x0 * zr1, z1, y0 * zr1) * radius,
- Vector3(x0 * zr0, z0, y0 * zr0) * radius
+ Vector3(x0 * cos0, sin0, z0 * cos0) * radius,
+ Vector3(x1 * cos0, sin0, z1 * cos0) * radius,
+ Vector3(x1 * cos1, sin1, z1 * cos1) * radius,
+ Vector3(x0 * cos1, sin1, z0 * cos1) * radius,
};
Vector2 u[4] = {
- Vector2(v1, u0),
- Vector2(v1, u1),
- Vector2(v0, u1),
- Vector2(v0, u0),
-
+ Vector2(u0, v0),
+ Vector2(u1, v0),
+ Vector2(u1, v1),
+ Vector2(u0, v1),
};
- if (i < rings) {
- //face 1
+ // Draw the first face, but skip this at the north pole (i == 0).
+ if (i > 0) {
facesw[face * 3 + 0] = v[0];
facesw[face * 3 + 1] = v[1];
facesw[face * 3 + 2] = v[2];
@@ -982,8 +988,8 @@ CSGBrush *CSGSphere3D::_build_brush() {
face++;
}
- if (i > 1) {
- //face 2
+ // Draw the second face, but skip this at the south pole (i == rings - 1).
+ if (i < rings - 1) {
facesw[face * 3 + 0] = v[2];
facesw[face * 3 + 1] = v[3];
facesw[face * 3 + 2] = v[0];
@@ -1030,15 +1036,14 @@ void CSGSphere3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments");
ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "StandardMaterial3D,ShaderMaterial"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
void CSGSphere3D::set_radius(const float p_radius) {
ERR_FAIL_COND(p_radius <= 0);
radius = p_radius;
_make_dirty();
- update_gizmo();
- _change_notify("radius");
+ update_gizmos();
}
float CSGSphere3D::get_radius() const {
@@ -1048,7 +1053,7 @@ float CSGSphere3D::get_radius() const {
void CSGSphere3D::set_radial_segments(const int p_radial_segments) {
radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
int CSGSphere3D::get_radial_segments() const {
@@ -1058,7 +1063,7 @@ int CSGSphere3D::get_radial_segments() const {
void CSGSphere3D::set_rings(const int p_rings) {
rings = p_rings > 1 ? p_rings : 1;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
int CSGSphere3D::get_rings() const {
@@ -1125,7 +1130,7 @@ CSGBrush *CSGBox3D::_build_brush() {
int face = 0;
- Vector3 vertex_mul(width * 0.5, height * 0.5, depth * 0.5);
+ Vector3 vertex_mul = size / 2;
{
for (int i = 0; i < 6; i++) {
@@ -1166,7 +1171,7 @@ CSGBrush *CSGBox3D::_build_brush() {
materialsw[face] = material;
face++;
- //face 1
+ //face 2
facesw[face * 3 + 0] = face_points[2] * vertex_mul;
facesw[face * 3 + 1] = face_points[3] * vertex_mul;
facesw[face * 3 + 2] = face_points[0] * vertex_mul;
@@ -1194,74 +1199,36 @@ CSGBrush *CSGBox3D::_build_brush() {
}
void CSGBox3D::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_width", "width"), &CSGBox3D::set_width);
- ClassDB::bind_method(D_METHOD("get_width"), &CSGBox3D::get_width);
-
- ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGBox3D::set_height);
- ClassDB::bind_method(D_METHOD("get_height"), &CSGBox3D::get_height);
-
- ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGBox3D::set_depth);
- ClassDB::bind_method(D_METHOD("get_depth"), &CSGBox3D::get_depth);
+ ClassDB::bind_method(D_METHOD("set_size", "size"), &CSGBox3D::set_size);
+ ClassDB::bind_method(D_METHOD("get_size"), &CSGBox3D::get_size);
ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox3D::set_material);
ClassDB::bind_method(D_METHOD("get_material"), &CSGBox3D::get_material);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_width", "get_width");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_height", "get_height");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_depth", "get_depth");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "StandardMaterial3D,ShaderMaterial"), "set_material", "get_material");
-}
-
-void CSGBox3D::set_width(const float p_width) {
- width = p_width;
- _make_dirty();
- update_gizmo();
- _change_notify("width");
-}
-
-float CSGBox3D::get_width() const {
- return width;
-}
-
-void CSGBox3D::set_height(const float p_height) {
- height = p_height;
- _make_dirty();
- update_gizmo();
- _change_notify("height");
-}
-
-float CSGBox3D::get_height() const {
- return height;
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "size"), "set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
-void CSGBox3D::set_depth(const float p_depth) {
- depth = p_depth;
+void CSGBox3D::set_size(const Vector3 &p_size) {
+ size = p_size;
_make_dirty();
- update_gizmo();
- _change_notify("depth");
+ update_gizmos();
}
-float CSGBox3D::get_depth() const {
- return depth;
+Vector3 CSGBox3D::get_size() const {
+ return size;
}
void CSGBox3D::set_material(const Ref<Material> &p_material) {
material = p_material;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
Ref<Material> CSGBox3D::get_material() const {
return material;
}
-CSGBox3D::CSGBox3D() {
- // defaults
- width = 2.0;
- height = 2.0;
- depth = 2.0;
-}
-
///////////////
CSGBrush *CSGCylinder3D::_build_brush() {
@@ -1303,8 +1270,8 @@ CSGBrush *CSGCylinder3D::_build_brush() {
float inc = float(i) / sides;
float inc_n = float((i + 1)) / sides;
- float ang = inc * Math_PI * 2.0;
- float ang_n = inc_n * Math_PI * 2.0;
+ float ang = inc * Math_TAU;
+ float ang_n = inc_n * Math_TAU;
Vector3 base(Math::cos(ang), 0, Math::sin(ang));
Vector3 base_n(Math::cos(ang_n), 0, Math::sin(ang_n));
@@ -1415,19 +1382,18 @@ void CSGCylinder3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder3D::set_smooth_faces);
ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder3D::get_smooth_faces);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_radius", "get_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "cone"), "set_cone", "is_cone");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "StandardMaterial3D,ShaderMaterial"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
void CSGCylinder3D::set_radius(const float p_radius) {
radius = p_radius;
_make_dirty();
- update_gizmo();
- _change_notify("radius");
+ update_gizmos();
}
float CSGCylinder3D::get_radius() const {
@@ -1437,8 +1403,7 @@ float CSGCylinder3D::get_radius() const {
void CSGCylinder3D::set_height(const float p_height) {
height = p_height;
_make_dirty();
- update_gizmo();
- _change_notify("height");
+ update_gizmos();
}
float CSGCylinder3D::get_height() const {
@@ -1449,7 +1414,7 @@ void CSGCylinder3D::set_sides(const int p_sides) {
ERR_FAIL_COND(p_sides < 3);
sides = p_sides;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
int CSGCylinder3D::get_sides() const {
@@ -1459,7 +1424,7 @@ int CSGCylinder3D::get_sides() const {
void CSGCylinder3D::set_cone(const bool p_cone) {
cone = p_cone;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
bool CSGCylinder3D::is_cone() const {
@@ -1502,7 +1467,7 @@ CSGBrush *CSGTorus3D::_build_brush() {
float max_radius = outer_radius;
if (min_radius == max_radius) {
- return nullptr; //sorry, can't
+ return memnew(CSGBrush); //sorry, can't
}
if (min_radius > max_radius) {
@@ -1545,8 +1510,8 @@ CSGBrush *CSGTorus3D::_build_brush() {
float inci = float(i) / sides;
float inci_n = float((i + 1)) / sides;
- float angi = inci * Math_PI * 2.0;
- float angi_n = inci_n * Math_PI * 2.0;
+ float angi = inci * Math_TAU;
+ float angi_n = inci_n * Math_TAU;
Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi));
Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n));
@@ -1555,8 +1520,8 @@ CSGBrush *CSGTorus3D::_build_brush() {
float incj = float(j) / ring_sides;
float incj_n = float((j + 1)) / ring_sides;
- float angj = incj * Math_PI * 2.0;
- float angj_n = incj_n * Math_PI * 2.0;
+ float angj = incj * Math_TAU;
+ float angj_n = incj_n * Math_TAU;
Vector2 normalj = Vector2(Math::cos(angj), Math::sin(angj)) * radius + Vector2(min_radius + radius, 0);
Vector2 normalj_n = Vector2(Math::cos(angj_n), Math::sin(angj_n)) * radius + Vector2(min_radius + radius, 0);
@@ -1636,19 +1601,18 @@ void CSGTorus3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus3D::set_smooth_faces);
ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus3D::get_smooth_faces);
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_inner_radius", "get_inner_radius");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_outer_radius", "get_outer_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_inner_radius", "get_inner_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001,or_greater,exp"), "set_outer_radius", "get_outer_radius");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_sides", "get_sides");
ADD_PROPERTY(PropertyInfo(Variant::INT, "ring_sides", PROPERTY_HINT_RANGE, "3,64,1"), "set_ring_sides", "get_ring_sides");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "StandardMaterial3D,ShaderMaterial"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
}
void CSGTorus3D::set_inner_radius(const float p_inner_radius) {
inner_radius = p_inner_radius;
_make_dirty();
- update_gizmo();
- _change_notify("inner_radius");
+ update_gizmos();
}
float CSGTorus3D::get_inner_radius() const {
@@ -1658,8 +1622,7 @@ float CSGTorus3D::get_inner_radius() const {
void CSGTorus3D::set_outer_radius(const float p_outer_radius) {
outer_radius = p_outer_radius;
_make_dirty();
- update_gizmo();
- _change_notify("outer_radius");
+ update_gizmos();
}
float CSGTorus3D::get_outer_radius() const {
@@ -1670,7 +1633,7 @@ void CSGTorus3D::set_sides(const int p_sides) {
ERR_FAIL_COND(p_sides < 3);
sides = p_sides;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
int CSGTorus3D::get_sides() const {
@@ -1681,7 +1644,7 @@ void CSGTorus3D::set_ring_sides(const int p_ring_sides) {
ERR_FAIL_COND(p_ring_sides < 3);
ring_sides = p_ring_sides;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
int CSGTorus3D::get_ring_sides() const {
@@ -1718,110 +1681,86 @@ CSGTorus3D::CSGTorus3D() {
///////////////
CSGBrush *CSGPolygon3D::_build_brush() {
- // set our bounding box
+ CSGBrush *brush = memnew(CSGBrush);
if (polygon.size() < 3) {
- return nullptr;
+ return brush;
}
- Vector<Point2> final_polygon = polygon;
-
- if (Triangulate::get_area(final_polygon) > 0) {
- final_polygon.invert();
+ // Triangulate polygon shape.
+ Vector<Point2> shape_polygon = polygon;
+ if (Triangulate::get_area(shape_polygon) > 0) {
+ shape_polygon.reverse();
}
-
- Vector<int> triangles = Geometry2D::triangulate_polygon(final_polygon);
-
- if (triangles.size() < 3) {
- return nullptr;
+ int shape_sides = shape_polygon.size();
+ Vector<int> shape_faces = Geometry2D::triangulate_polygon(shape_polygon);
+ ERR_FAIL_COND_V_MSG(shape_faces.size() < 3, brush, "Failed to triangulate CSGPolygon");
+
+ // Get polygon enclosing Rect2.
+ Rect2 shape_rect(shape_polygon[0], Vector2());
+ for (int i = 1; i < shape_sides; i++) {
+ shape_rect.expand_to(shape_polygon[i]);
}
- Path3D *path = nullptr;
+ // If MODE_PATH, check if curve has changed.
Ref<Curve3D> curve;
-
- // get bounds for our polygon
- Vector2 final_polygon_min;
- Vector2 final_polygon_max;
- for (int i = 0; i < final_polygon.size(); i++) {
- Vector2 p = final_polygon[i];
- if (i == 0) {
- final_polygon_min = p;
- final_polygon_max = final_polygon_min;
- } else {
- if (p.x < final_polygon_min.x) {
- final_polygon_min.x = p.x;
- }
- if (p.y < final_polygon_min.y) {
- final_polygon_min.y = p.y;
- }
-
- if (p.x > final_polygon_max.x) {
- final_polygon_max.x = p.x;
+ if (mode == MODE_PATH) {
+ Path3D *current_path = Object::cast_to<Path3D>(get_node_or_null(path_node));
+ if (path != current_path) {
+ if (path) {
+ path->disconnect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited));
+ path->disconnect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed));
}
- if (p.y > final_polygon_max.y) {
- final_polygon_max.y = p.y;
+ path = current_path;
+ if (path) {
+ path->connect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited));
+ path->connect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed));
}
}
- }
- Vector2 final_polygon_size = final_polygon_max - final_polygon_min;
- if (mode == MODE_PATH) {
- if (!has_node(path_node)) {
- return nullptr;
- }
- Node *n = get_node(path_node);
- if (!n) {
- return nullptr;
- }
- path = Object::cast_to<Path3D>(n);
if (!path) {
- return nullptr;
+ return brush;
}
- if (path != path_cache) {
- if (path_cache) {
- path_cache->disconnect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited));
- path_cache->disconnect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed));
- path_cache = nullptr;
- }
-
- path_cache = path;
-
- path_cache->connect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited));
- path_cache->connect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed));
- path_cache = nullptr;
- }
curve = path->get_curve();
- if (curve.is_null()) {
- return nullptr;
- }
- if (curve->get_baked_length() <= 0) {
- return nullptr;
+ if (curve.is_null() || curve->get_point_count() < 2) {
+ return brush;
}
}
- CSGBrush *brush = memnew(CSGBrush);
-
- int face_count = 0;
+ // Calculate the number extrusions, ends and faces.
+ int extrusions = 0;
+ 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:
- face_count = triangles.size() * 2 / 3 + (final_polygon.size()) * 2;
+ extrusions = 1;
+ end_count = 2;
break;
case MODE_SPIN:
- face_count = (spin_degrees < 360 ? triangles.size() * 2 / 3 : 0) + (final_polygon.size()) * 2 * spin_sides;
+ extrusions = spin_sides;
+ if (spin_degrees < 360) {
+ end_count = 2;
+ }
break;
case MODE_PATH: {
- float bl = curve->get_baked_length();
- int splits = MAX(2, Math::ceil(bl / path_interval));
- if (path_joined) {
- face_count = splits * final_polygon.size() * 2;
+ curve_length = curve->get_baked_length();
+ if (path_interval_type == PATH_INTERVAL_DISTANCE) {
+ extrusions = MAX(1, Math::ceil(curve_length / path_interval)) + 1;
} else {
- face_count = triangles.size() * 2 / 3 + splits * final_polygon.size() * 2;
+ extrusions = Math::ceil(1.0 * curve->get_point_count() / path_interval);
+ }
+ if (!path_joined) {
+ end_count = 2;
+ extrusions -= 1;
}
} break;
}
+ int face_count = extrusions * extrusion_face_count + end_count * shape_face_count;
- bool invert_val = is_inverting_faces();
+ // Intialize variables used to create the mesh.
Ref<Material> material = get_material();
Vector<Vector3> faces;
@@ -1832,12 +1771,11 @@ CSGBrush *CSGPolygon3D::_build_brush() {
faces.resize(face_count * 3);
uvs.resize(face_count * 3);
-
smooth.resize(face_count);
materials.resize(face_count);
invert.resize(face_count);
+ int faces_removed = 0;
- AABB aabb; //must be computed
{
Vector3 *facesw = faces.ptrw();
Vector2 *uvsw = uvs.ptrw();
@@ -1846,346 +1784,234 @@ CSGBrush *CSGPolygon3D::_build_brush() {
bool *invertw = invert.ptrw();
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;
+ }
- switch (mode) {
- case MODE_DEPTH: {
- //add triangles, front and back
- for (int i = 0; i < 2; i++) {
- for (int j = 0; j < triangles.size(); j += 3) {
- for (int k = 0; k < 3; k++) {
- int src[3] = { 0, i == 0 ? 1 : 2, i == 0 ? 2 : 1 };
- Vector2 p = final_polygon[triangles[j + src[k]]];
- Vector3 v = Vector3(p.x, p.y, 0);
- if (i == 0) {
- v.z -= depth;
- }
- facesw[face * 3 + k] = v;
- uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size;
- if (i == 0) {
- uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */
- }
- }
-
- smoothw[face] = false;
- materialsw[face] = material;
- invertw[face] = invert_val;
- face++;
- }
- }
-
- //add triangles for depth
- for (int i = 0; i < final_polygon.size(); i++) {
- int i_n = (i + 1) % final_polygon.size();
-
- Vector3 v[4] = {
- Vector3(final_polygon[i].x, final_polygon[i].y, -depth),
- Vector3(final_polygon[i_n].x, final_polygon[i_n].y, -depth),
- Vector3(final_polygon[i_n].x, final_polygon[i_n].y, 0),
- Vector3(final_polygon[i].x, final_polygon[i].y, 0),
- };
-
- Vector2 u[4] = {
- Vector2(0, 0),
- Vector2(0, 1),
- Vector2(1, 1),
- Vector2(1, 0)
- };
-
- // 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_val;
- materialsw[face] = material;
+ if (mode == MODE_PATH) {
+ if (!path_local) {
+ base_xform = path->get_global_transform();
+ }
- face++;
+ 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;
- // face 2
- facesw[face * 3 + 0] = v[2];
- facesw[face * 3 + 1] = v[3];
- facesw[face * 3 + 2] = v[0];
+ if (path_joined) {
+ Vector3 last_point = curve->interpolate_baked(curve->get_baked_length());
+ direction = next_point - last_point;
+ }
- uvsw[face * 3 + 0] = u[2];
- uvsw[face * 3 + 1] = u[3];
- uvsw[face * 3 + 2] = u[0];
+ 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;
+ }
- smoothw[face] = smooth_faces;
- invertw[face] = invert_val;
- materialsw[face] = material;
+ Transform3D facing = Transform3D().looking_at(direction, current_up);
+ current_xform = base_xform.translated(current_point) * facing;
+ }
- face++;
+ // 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;
}
- } break;
- case MODE_SPIN: {
- for (int i = 0; i < spin_sides; i++) {
- float inci = float(i) / spin_sides;
- float inci_n = float((i + 1)) / spin_sides;
-
- float angi = -(inci * spin_degrees / 360.0) * Math_PI * 2.0;
- float angi_n = -(inci_n * spin_degrees / 360.0) * Math_PI * 2.0;
-
- Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi));
- Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n));
-
- //add triangles for depth
- for (int j = 0; j < final_polygon.size(); j++) {
- int j_n = (j + 1) % final_polygon.size();
-
- Vector3 v[4] = {
- Vector3(normali.x * final_polygon[j].x, final_polygon[j].y, normali.z * final_polygon[j].x),
- Vector3(normali.x * final_polygon[j_n].x, final_polygon[j_n].y, normali.z * final_polygon[j_n].x),
- Vector3(normali_n.x * final_polygon[j_n].x, final_polygon[j_n].y, normali_n.z * final_polygon[j_n].x),
- Vector3(normali_n.x * final_polygon[j].x, final_polygon[j].y, normali_n.z * final_polygon[j].x),
- };
-
- Vector2 u[4] = {
- Vector2(0, 0),
- Vector2(0, 1),
- Vector2(1, 1),
- Vector2(1, 0)
- };
-
- // face 1
- facesw[face * 3 + 0] = v[0];
- facesw[face * 3 + 1] = v[2];
- facesw[face * 3 + 2] = v[1];
-
- uvsw[face * 3 + 0] = u[0];
- uvsw[face * 3 + 1] = u[2];
- uvsw[face * 3 + 2] = u[1];
-
- smoothw[face] = smooth_faces;
- invertw[face] = invert_val;
- materialsw[face] = material;
-
- face++;
-
- // face 2
- facesw[face * 3 + 0] = v[2];
- facesw[face * 3 + 1] = v[0];
- facesw[face * 3 + 2] = v[3];
-
- uvsw[face * 3 + 0] = u[2];
- uvsw[face * 3 + 1] = u[0];
- uvsw[face * 3 + 2] = u[3];
-
- smoothw[face] = smooth_faces;
- invertw[face] = invert_val;
- materialsw[face] = material;
-
- face++;
- }
-
- if (i == 0 && spin_degrees < 360) {
- for (int j = 0; j < triangles.size(); j += 3) {
- for (int k = 0; k < 3; k++) {
- int src[3] = { 0, 2, 1 };
- Vector2 p = final_polygon[triangles[j + src[k]]];
- Vector3 v = Vector3(p.x, p.y, 0);
- facesw[face * 3 + k] = v;
- uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size;
- }
-
- smoothw[face] = false;
- materialsw[face] = material;
- invertw[face] = invert_val;
- face++;
- }
- }
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_faces;
+ face++;
+ }
+ }
- if (i == spin_sides - 1 && spin_degrees < 360) {
- for (int j = 0; j < triangles.size(); j += 3) {
- for (int k = 0; k < 3; k++) {
- int src[3] = { 0, 1, 2 };
- Vector2 p = final_polygon[triangles[j + src[k]]];
- Vector3 v = Vector3(normali_n.x * p.x, p.y, normali_n.z * p.x);
- facesw[face * 3 + k] = v;
- uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size;
- uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */
- }
-
- smoothw[face] = false;
- materialsw[face] = material;
- invertw[face] = invert_val;
- face++;
+ 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;
}
}
- }
- } break;
- case MODE_PATH: {
- float bl = curve->get_baked_length();
- int splits = MAX(2, Math::ceil(bl / path_interval));
- float u1 = 0.0;
- float u2 = path_continuous_u ? 0.0 : 1.0;
-
- Transform path_to_this;
- if (!path_local) {
- // center on paths origin
- path_to_this = get_global_transform().affine_inverse() * path->get_global_transform();
- }
-
- Transform prev_xf;
-
- Vector3 lookat_dir;
-
- if (path_rotation == PATH_ROTATION_POLYGON) {
- lookat_dir = (path->get_global_transform().affine_inverse() * get_global_transform()).xform(Vector3(0, 0, -1));
- } else {
- Vector3 p1, p2;
- p1 = curve->interpolate_baked(0);
- p2 = curve->interpolate_baked(0.1);
- lookat_dir = (p2 - p1).normalized();
- }
-
- for (int i = 0; i <= splits; i++) {
- float ofs = i * path_interval;
- if (ofs > bl) {
- ofs = bl;
- }
- if (i == splits && path_joined) {
- ofs = 0.0;
- }
-
- Transform xf;
- xf.origin = curve->interpolate_baked(ofs);
-
- Vector3 local_dir;
-
- if (path_rotation == PATH_ROTATION_PATH_FOLLOW && ofs > 0) {
- //before end
- Vector3 p1 = curve->interpolate_baked(ofs - 0.1);
- Vector3 p2 = curve->interpolate_baked(ofs);
- local_dir = (p2 - p1).normalized();
+ 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 {
- local_dir = lookat_dir;
+ faces_combined = 0;
+ previous_simplify_dir = current_dir;
}
- xf = xf.looking_at(xf.origin + local_dir, Vector3(0, 1, 0));
- Basis rot(Vector3(0, 0, 1), curve->interpolate_baked_tilt(ofs));
-
- xf = xf * rot; //post mult
+ 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;
+ }
- xf = path_to_this * xf;
+ Transform3D facing = Transform3D().looking_at(direction, current_up);
+ current_xform = base_xform.translated(current_point) * facing;
+ } break;
+ }
- if (i > 0) {
- if (path_continuous_u) {
- u1 = u2;
- u2 += (prev_xf.origin - xf.origin).length();
- };
+ 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;
+ }
- //put triangles where they belong
- //add triangles for depth
- for (int j = 0; j < final_polygon.size(); j++) {
- int j_n = (j + 1) % final_polygon.size();
+ 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] = {
- prev_xf.xform(Vector3(final_polygon[j].x, final_polygon[j].y, 0)),
- prev_xf.xform(Vector3(final_polygon[j_n].x, final_polygon[j_n].y, 0)),
- xf.xform(Vector3(final_polygon[j_n].x, final_polygon[j_n].y, 0)),
- xf.xform(Vector3(final_polygon[j].x, final_polygon[j].y, 0)),
- };
+ 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(u1, 1),
- Vector2(u1, 0),
- Vector2(u2, 0),
- Vector2(u2, 1)
- };
+ 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];
+ // 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];
+ 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_val;
- materialsw[face] = material;
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_faces;
+ materialsw[face] = material;
- face++;
+ face++;
- // face 2
- facesw[face * 3 + 0] = v[2];
- facesw[face * 3 + 1] = v[3];
- facesw[face * 3 + 2] = v[0];
+ // 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];
+ 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_val;
- materialsw[face] = material;
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_faces;
+ materialsw[face] = material;
- face++;
- }
- }
+ face++;
+ }
+ }
- if (i == 0 && !path_joined) {
- for (int j = 0; j < triangles.size(); j += 3) {
- for (int k = 0; k < 3; k++) {
- int src[3] = { 0, 1, 2 };
- Vector2 p = final_polygon[triangles[j + src[k]]];
- Vector3 v = Vector3(p.x, p.y, 0);
- facesw[face * 3 + k] = xf.xform(v);
- uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size;
- }
-
- smoothw[face] = false;
- materialsw[face] = material;
- invertw[face] = invert_val;
- 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;
- if (i == splits && !path_joined) {
- for (int j = 0; j < triangles.size(); j += 3) {
- for (int k = 0; k < 3; k++) {
- int src[3] = { 0, 2, 1 };
- Vector2 p = final_polygon[triangles[j + src[k]]];
- Vector3 v = Vector3(p.x, p.y, 0);
- facesw[face * 3 + k] = xf.xform(v);
- uvsw[face * 3 + k] = (p - final_polygon_min) / final_polygon_size;
- uvsw[face * 3 + k].x = 1.0 - uvsw[face * 3 + k].x; /* flip x */
- }
-
- smoothw[face] = false;
- materialsw[face] = material;
- invertw[face] = invert_val;
- face++;
- }
- }
+ // 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);
- prev_xf = xf;
+ facesw[face * 3 + face_vertex_idx] = current_xform.xform(Vector3(p.x, p.y, 0));
+ uvsw[face * 3 + face_vertex_idx] = uv;
}
- } break;
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_faces;
+ face++;
+ }
}
- if (face != face_count) {
- ERR_PRINT("Face mismatch bug! fix code");
- }
- for (int i = 0; i < face_count * 3; i++) {
- if (i == 0) {
- aabb.position = facesw[i];
- } else {
- aabb.expand_to(facesw[i]);
- }
+ face_count -= faces_removed;
+ ERR_FAIL_COND_V_MSG(face != face_count, brush, "Bug: Failed to create the CSGPolygon mesh correctly.");
+ }
- // invert UVs on the Y-axis OpenGL = upside down
- uvsw[i].y = 1.0 - uvsw[i].y;
- }
+ 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);
@@ -2195,23 +2021,23 @@ CSGBrush *CSGPolygon3D::_build_brush() {
void CSGPolygon3D::_notification(int p_what) {
if (p_what == NOTIFICATION_EXIT_TREE) {
- if (path_cache) {
- path_cache->disconnect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited));
- path_cache->disconnect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed));
- path_cache = nullptr;
+ if (path) {
+ path->disconnect("tree_exited", callable_mp(this, &CSGPolygon3D::_path_exited));
+ path->disconnect("curve_changed", callable_mp(this, &CSGPolygon3D::_path_changed));
+ path = nullptr;
}
}
}
void CSGPolygon3D::_validate_property(PropertyInfo &property) const {
if (property.name.begins_with("spin") && mode != MODE_SPIN) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name.begins_with("path") && mode != MODE_PATH) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
if (property.name == "depth" && mode != MODE_DEPTH) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
CSGShape3D::_validate_property(property);
@@ -2219,11 +2045,11 @@ void CSGPolygon3D::_validate_property(PropertyInfo &property) const {
void CSGPolygon3D::_path_changed() {
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
void CSGPolygon3D::_path_exited() {
- path_cache = nullptr;
+ path = nullptr;
}
void CSGPolygon3D::_bind_methods() {
@@ -2245,10 +2071,16 @@ 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", "distance"), &CSGPolygon3D::set_path_interval);
+ 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_rotation", "mode"), &CSGPolygon3D::set_path_rotation);
+ 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);
ClassDB::bind_method(D_METHOD("set_path_local", "enable"), &CSGPolygon3D::set_path_local);
@@ -2257,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);
@@ -2271,17 +2106,20 @@ void CSGPolygon3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Depth,Spin,Path"), "set_mode", "get_mode");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_depth", "get_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth", PROPERTY_HINT_RANGE, "0.01,100.0,0.01,or_greater,exp"), "set_depth", "get_depth");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "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, "Path"), "set_path_node", "get_path_node");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_interval", PROPERTY_HINT_EXP_RANGE, "0.001,1000.0,0.001,or_greater"), "set_path_interval", "get_path_interval");
+ 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::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, "StandardMaterial3D,ShaderMaterial"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial"), "set_material", "get_material");
BIND_ENUM_CONSTANT(MODE_DEPTH);
BIND_ENUM_CONSTANT(MODE_SPIN);
@@ -2290,12 +2128,15 @@ 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) {
polygon = p_polygon;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
Vector<Vector2> CSGPolygon3D::get_polygon() const {
@@ -2305,8 +2146,8 @@ Vector<Vector2> CSGPolygon3D::get_polygon() const {
void CSGPolygon3D::set_mode(Mode p_mode) {
mode = p_mode;
_make_dirty();
- update_gizmo();
- _change_notify();
+ update_gizmos();
+ notify_property_list_changed();
}
CSGPolygon3D::Mode CSGPolygon3D::get_mode() const {
@@ -2317,7 +2158,7 @@ void CSGPolygon3D::set_depth(const float p_depth) {
ERR_FAIL_COND(p_depth < 0.001);
depth = p_depth;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
float CSGPolygon3D::get_depth() const {
@@ -2333,22 +2174,32 @@ 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;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
float CSGPolygon3D::get_spin_degrees() const {
return spin_degrees;
}
-void CSGPolygon3D::set_spin_sides(const int p_spin_sides) {
+void CSGPolygon3D::set_spin_sides(int p_spin_sides) {
ERR_FAIL_COND(p_spin_sides < 3);
spin_sides = p_spin_sides;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
int CSGPolygon3D::get_spin_sides() const {
@@ -2358,28 +2209,47 @@ int CSGPolygon3D::get_spin_sides() const {
void CSGPolygon3D::set_path_node(const NodePath &p_path) {
path_node = p_path;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
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.001, "Path interval cannot be smaller than 0.001.");
path_interval = p_interval;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
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();
- update_gizmo();
+ update_gizmos();
}
CSGPolygon3D::PathRotation CSGPolygon3D::get_path_rotation() const {
@@ -2389,7 +2259,7 @@ CSGPolygon3D::PathRotation CSGPolygon3D::get_path_rotation() const {
void CSGPolygon3D::set_path_local(bool p_enable) {
path_local = p_enable;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
bool CSGPolygon3D::is_path_local() const {
@@ -2399,7 +2269,7 @@ bool CSGPolygon3D::is_path_local() const {
void CSGPolygon3D::set_path_joined(bool p_enable) {
path_joined = p_enable;
_make_dirty();
- update_gizmo();
+ update_gizmos();
}
bool CSGPolygon3D::is_path_joined() const {
@@ -2443,10 +2313,13 @@ CSGPolygon3D::CSGPolygon3D() {
spin_degrees = 360;
spin_sides = 8;
smooth_faces = false;
- path_interval = 1;
- path_rotation = PATH_ROTATION_PATH;
+ 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 = false;
+ path_continuous_u = true;
+ path_u_distance = 1.0;
path_joined = false;
- path_cache = nullptr;
+ path = nullptr;
}
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index d93693f145..c85cce776b 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,6 +34,7 @@
#define CSGJS_HEADER_ONLY
#include "csg.h"
+#include "scene/3d/path_3d.h"
#include "scene/3d/visual_instance_3d.h"
#include "scene/resources/concave_polygon_shape_3d.h"
#include "thirdparty/misc/mikktspace.h"
@@ -50,23 +51,23 @@ public:
};
private:
- Operation operation;
- CSGShape3D *parent;
+ Operation operation = OPERATION_UNION;
+ CSGShape3D *parent = nullptr;
- CSGBrush *brush;
+ CSGBrush *brush = nullptr;
AABB node_aabb;
- bool dirty;
- float snap;
+ bool dirty = false;
+ float snap = 0.001;
- bool use_collision;
- uint32_t collision_layer;
- uint32_t collision_mask;
+ bool use_collision = false;
+ uint32_t collision_layer = 1;
+ uint32_t collision_mask = 1;
Ref<ConcavePolygonShape3D> root_collision_shape;
RID root_collision_instance;
- bool calculate_tangents;
+ bool calculate_tangents = true;
Ref<ArrayMesh> root_mesh;
@@ -83,14 +84,14 @@ private:
Vector<Vector3> vertices;
Vector<Vector3> normals;
Vector<Vector2> uvs;
- Vector<float> tans;
+ Vector<real_t> tans;
Ref<Material> material;
- int last_added;
+ int last_added = 0;
- Vector3 *verticesw;
- Vector3 *normalsw;
- Vector2 *uvsw;
- float *tansw;
+ Vector3 *verticesw = nullptr;
+ Vector3 *normalsw = nullptr;
+ Vector2 *uvsw = nullptr;
+ real_t *tansw = nullptr;
};
//mikktspace callbacks
@@ -136,11 +137,11 @@ public:
void set_collision_mask(uint32_t p_mask);
uint32_t get_collision_mask() const;
- void set_collision_layer_bit(int p_bit, bool p_value);
- bool get_collision_layer_bit(int p_bit) const;
+ void set_collision_layer_value(int p_layer_number, bool p_value);
+ bool get_collision_layer_value(int p_layer_number) const;
- void set_collision_mask_bit(int p_bit, bool p_value);
- bool get_collision_mask_bit(int p_bit) const;
+ void set_collision_mask_value(int p_layer_number, bool p_value);
+ bool get_collision_mask_value(int p_layer_number) const;
void set_snap(float p_snap);
float get_snap() const;
@@ -168,10 +169,8 @@ public:
class CSGPrimitive3D : public CSGShape3D {
GDCLASS(CSGPrimitive3D, CSGShape3D);
-private:
- bool invert_faces;
-
protected:
+ bool invert_faces;
CSGBrush *_create_brush_from_arrays(const Vector<Vector3> &p_vertices, const Vector<Vector2> &p_uv, const Vector<bool> &p_smooth, const Vector<Ref<Material>> &p_materials);
static void _bind_methods();
@@ -240,27 +239,19 @@ class CSGBox3D : public CSGPrimitive3D {
virtual CSGBrush *_build_brush() override;
Ref<Material> material;
- float width;
- float height;
- float depth;
+ Vector3 size = Vector3(2, 2, 2);
protected:
static void _bind_methods();
public:
- void set_width(const float p_width);
- float get_width() const;
-
- void set_height(const float p_height);
- float get_height() const;
-
- void set_depth(const float p_depth);
- float get_depth() const;
+ void set_size(const Vector3 &p_size);
+ Vector3 get_size() const;
void set_material(const Ref<Material> &p_material);
Ref<Material> get_material() const;
- CSGBox3D();
+ CSGBox3D() {}
};
class CSGCylinder3D : public CSGPrimitive3D {
@@ -345,6 +336,11 @@ public:
MODE_PATH
};
+ enum PathIntervalType {
+ PATH_INTERVAL_DISTANCE,
+ PATH_INTERVAL_SUBDIVIDE
+ };
+
enum PathRotation {
PATH_ROTATION_POLYGON,
PATH_ROTATION_PATH,
@@ -365,14 +361,17 @@ private:
int spin_sides;
NodePath path_node;
+ PathIntervalType path_interval_type;
float path_interval;
+ float path_simplify_angle;
PathRotation path_rotation;
bool path_local;
- Node *path_cache;
+ Path3D *path;
bool smooth_faces;
bool path_continuous_u;
+ real_t path_u_distance;
bool path_joined;
bool _is_editable_3d_polygon() const;
@@ -405,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;
@@ -417,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;
@@ -431,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 492bf68c44..d64e58ae4d 100644
--- a/modules/csg/doc_classes/CSGBox3D.xml
+++ b/modules/csg/doc_classes/CSGBox3D.xml
@@ -8,22 +8,12 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
- <member name="depth" type="float" setter="set_depth" getter="get_depth" default="2.0">
- Depth of the box measured from the center of the box.
- </member>
- <member name="height" type="float" setter="set_height" getter="get_height" default="2.0">
- Height of the box measured from the center of the box.
- </member>
<member name="material" type="Material" setter="set_material" getter="get_material">
The material used to render the box.
</member>
- <member name="width" type="float" setter="set_width" getter="get_width" default="2.0">
- Width of the box measured from the center of the box.
+ <member name="size" type="Vector3" setter="set_size" getter="get_size" default="Vector3(2, 2, 2)">
+ 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 1bab8f4ee9..2810343139 100644
--- a/modules/csg/doc_classes/CSGMesh3D.xml
+++ b/modules/csg/doc_classes/CSGMesh3D.xml
@@ -4,20 +4,17 @@
A CSG Mesh shape that uses a mesh resource.
</brief_description>
<description>
- This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more then two faces.
+ This CSG node allows you to use any mesh resource as a CSG shape, provided it is closed, does not self-intersect, does not contain internal faces and has no edges that connect to more than two faces.
</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.
</member>
<member name="mesh" type="Mesh" setter="set_mesh" getter="get_mesh">
The [Mesh] resource to use as a CSG shape.
+ [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 c55fa0983e..ecbb7962d1 100644
--- a/modules/csg/doc_classes/CSGPolygon3D.xml
+++ b/modules/csg/doc_classes/CSGPolygon3D.xml
@@ -4,71 +4,86 @@
Extrudes a 2D polygon shape to create a 3D mesh.
</brief_description>
<description>
- This node takes a 2D polygon shape and extrudes it to create a 3D mesh.
+ An array of 2D points is extruded to quickly and easily create a variety of 3D meshes.
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
<member name="depth" type="float" setter="set_depth" getter="get_depth" default="1.0">
- Extrusion depth when [member mode] is [constant MODE_DEPTH].
+ When [member mode] is [constant MODE_DEPTH], the depth of the extrusion.
</member>
<member name="material" type="Material" setter="set_material" getter="get_material">
- Material to use for the resulting mesh.
+ Material to use for the resulting mesh. The UV maps the top half of the material to the extruded shape (U along the length of the extrusions and V around the outline of the [member polygon]), the bottom-left quarter to the front end face, and the bottom-right quarter to the back end face.
</member>
<member name="mode" type="int" setter="set_mode" getter="get_mode" enum="CSGPolygon3D.Mode" default="0">
- Extrusion mode.
+ The [member mode] used to extrude the [member polygon].
</member>
<member name="path_continuous_u" type="bool" setter="set_path_continuous_u" getter="is_path_continuous_u">
- If [code]true[/code] the u component of our uv will continuously increase in unison with the distance traveled along our path when [member mode] is [constant MODE_PATH].
+ When [member mode] is [constant MODE_PATH], by default, the top half of the [member material] is stretched along the entire length of the extruded shape. If [code]false[/code] the top half of the material is repeated every step of the extrusion.
</member>
<member name="path_interval" type="float" setter="set_path_interval" getter="get_path_interval">
- Interval at which a new extrusion slice is added along the path when [member mode] is [constant MODE_PATH].
+ 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">
- If [code]true[/code] the start and end of our path are joined together ensuring there is no seam when [member mode] is [constant MODE_PATH].
+ 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>
<member name="path_local" type="bool" setter="set_path_local" getter="is_path_local">
- If [code]false[/code] we extrude centered on our path, if [code]true[/code] we extrude in relation to the position of our CSGPolygon3D when [member mode] is [constant MODE_PATH].
+ When [member mode] is [constant MODE_PATH], if [code]true[/code] the [Transform3D] of the [CSGPolygon3D] is used as the starting point for the extrusions, not the [Transform3D] of the [member path_node].
</member>
<member name="path_node" type="NodePath" setter="set_path_node" getter="get_path_node">
- The [Shape3D] object containing the path along which we extrude when [member mode] is [constant MODE_PATH].
+ When [member mode] is [constant MODE_PATH], the location of the [Path3D] object used to extrude the [member polygon].
</member>
<member name="path_rotation" type="int" setter="set_path_rotation" getter="get_path_rotation" enum="CSGPolygon3D.PathRotation">
- The method by which each slice is rotated along the path when [member mode] is [constant MODE_PATH].
+ 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 )">
- Point array that defines the shape that we'll extrude.
+ <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>
<member name="smooth_faces" type="bool" setter="set_smooth_faces" getter="get_smooth_faces" default="false">
- Generates smooth normals so smooth shading is applied to our mesh.
+ If [code]true[/code], applies smooth shading to the extrusions.
</member>
<member name="spin_degrees" type="float" setter="set_spin_degrees" getter="get_spin_degrees">
- Degrees to rotate our extrusion for each slice when [member mode] is [constant MODE_SPIN].
+ When [member mode] is [constant MODE_SPIN], the total number of degrees the [member polygon] is rotated when extruding.
</member>
<member name="spin_sides" type="int" setter="set_spin_sides" getter="get_spin_sides">
- Number of extrusion when [member mode] is [constant MODE_SPIN].
+ When [member mode] is [constant MODE_SPIN], the number of extrusions made.
</member>
</members>
<constants>
<constant name="MODE_DEPTH" value="0" enum="Mode">
- Shape3D is extruded to [member depth].
+ The [member polygon] shape is extruded along the negative Z axis.
</constant>
<constant name="MODE_SPIN" value="1" enum="Mode">
- Shape3D is extruded by rotating it around an axis.
+ The [member polygon] shape is extruded by rotating it around the Y axis.
</constant>
<constant name="MODE_PATH" value="2" enum="Mode">
- Shape3D is extruded along a path set by a [Shape3D] set in [member path_node].
+ The [member polygon] shape is extruded along the [Path3D] specified in [member path_node].
</constant>
<constant name="PATH_ROTATION_POLYGON" value="0" enum="PathRotation">
- Slice is not rotated.
+ The [member polygon] shape is not rotated.
+ [b]Note:[/b] Requires the path Z coordinates to continually decrease to ensure viable shapes.
</constant>
<constant name="PATH_ROTATION_PATH" value="1" enum="PathRotation">
- Slice is rotated around the up vector of the path.
+ The [member polygon] shape is rotated along the path, but it is not rotated around the path axis.
+ [b]Note:[/b] Requires the path Z coordinates to continually decrease to ensure viable shapes.
</constant>
<constant name="PATH_ROTATION_PATH_FOLLOW" value="2" enum="PathRotation">
- Slice is rotate to match the path exactly.
+ 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 dac556c7f1..f5031064d6 100644
--- a/modules/csg/doc_classes/CSGShape3D.xml
+++ b/modules/csg/doc_classes/CSGShape3D.xml
@@ -9,58 +9,46 @@
<tutorials>
</tutorials>
<methods>
- <method name="get_collision_layer_bit" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="bit" type="int">
- </argument>
+ <method name="get_collision_layer_value" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="layer_number" type="int" />
<description>
- Returns an individual bit on the collision mask.
+ Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
- <method name="get_collision_mask_bit" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="bit" type="int">
- </argument>
+ <method name="get_collision_mask_value" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="layer_number" type="int" />
<description>
- Returns an individual bit on the collision mask.
+ Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
<method name="get_meshes" qualifiers="const">
- <return type="Array">
- </return>
+ <return type="Array" />
<description>
- Returns an [Array] with two elements, the first is the [Transform] of this node and the second is the root [Mesh] of this node. Only works when this node is the root shape.
+ Returns an [Array] with two elements, the first is the [Transform3D] of this node and the second is the root [Mesh] of this node. Only works when this node is the root shape.
</description>
</method>
<method name="is_root_shape" qualifiers="const">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if this is a root shape and is thus the object that is rendered.
</description>
</method>
- <method name="set_collision_layer_bit">
- <return type="void">
- </return>
- <argument index="0" name="bit" type="int">
- </argument>
- <argument index="1" name="value" type="bool">
- </argument>
+ <method name="set_collision_layer_value">
+ <return type="void" />
+ <argument index="0" name="layer_number" type="int" />
+ <argument index="1" name="value" type="bool" />
<description>
- Sets individual bits on the layer mask. Use this if you only need to change one layer's value.
+ Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
- <method name="set_collision_mask_bit">
- <return type="void">
- </return>
- <argument index="0" name="bit" type="int">
- </argument>
- <argument index="1" name="value" type="bool">
- </argument>
+ <method name="set_collision_mask_value">
+ <return type="void" />
+ <argument index="0" name="layer_number" type="int" />
+ <argument index="1" name="value" type="bool" />
<description>
- Sets individual bits on the collision mask. Use this if you only need to change one layer's value.
+ Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
</methods>
@@ -71,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/csg/icons/CSGBox3D.svg b/modules/csg/icons/CSGBox3D.svg
index 67e34df444..2740cc2f8c 100644
--- a/modules/csg/icons/CSGBox3D.svg
+++ b/modules/csg/icons/CSGBox3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/>
-<path transform="translate(0 1036.4)" d="m8 0.94531-7 3.5v7.2227l7 3.5 0.29492-0.14844c-0.18282-0.30101-0.29492-0.64737-0.29492-1.0195v-2c0-0.72651 0.40824-1.3664 1-1.7168v-1.6699l4-2v1.3867h1c0.36419 0 0.70336 0.10754 1 0.2832v-3.8379zm0 2.1152 3.9395 1.9707-3.9395 1.9688-3.9395-1.9688zm-5 3.5527 4 2v3.9414l-4-2.002z" fill="#fc9c9c" stroke-width="1.0667"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#5fb2ff"/><path d="m8 .94531-7 3.5v7.2227l7 3.5.29492-.14844c-.18282-.30101-.29492-.64737-.29492-1.0195v-2c0-.72651.40824-1.3664 1-1.7168v-1.6699l4-2v1.3867h1c.36419 0 .70336.10754 1 .2832v-3.8379zm0 2.1152 3.9395 1.9707-3.9395 1.9688-3.9395-1.9688zm-5 3.5527 4 2v3.9414l-4-2.002z" fill="#fc7f7f" stroke-width="1.0667"/></svg>
diff --git a/modules/csg/icons/CSGCapsule3D.svg b/modules/csg/icons/CSGCapsule3D.svg
index 92a7b5a870..db4f71864b 100644
--- a/modules/csg/icons/CSGCapsule3D.svg
+++ b/modules/csg/icons/CSGCapsule3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g>
-<path d="m8 1c-2.7527 0-5 2.2418-5 4.9902v4.0176c0 2.7484 2.2473 4.9922 5 4.9922 0.092943 0 0.18367-0.008623 0.27539-0.013672-0.17055-0.29341-0.27539-0.62792-0.27539-0.98633v-2c0-0.72887 0.41095-1.3691 1.0059-1.7188v-0.28125c0.34771-0.034464 0.68259-0.10691 1.0156-0.19922 0.10394-0.99856 0.95603-1.8008 1.9785-1.8008h1v-2.0098c0-2.7484-2.2473-4.9902-5-4.9902zm-1.0059 2.127v4.8574c-0.66556-0.1047-1.2974-0.37231-1.9941-0.66211v-1.3223c0-1.3474 0.79841-2.4642 1.9941-2.873zm2.0117 0c1.1957 0.4088 1.9941 1.5256 1.9941 2.873v1.3457c-0.68406 0.3054-1.3142 0.57292-1.9941 0.66602v-4.8848zm-4.0059 6.334c0.67836 0.2231 1.3126 0.44599 1.9941 0.52539v2.8848c-1.1957-0.4092-1.9941-1.5237-1.9941-2.8711v-0.53906z" fill="#fc9c9c"/>
-<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-2.7527 0-5 2.2418-5 4.9902v4.0176c0 2.7484 2.2473 4.9922 5 4.9922.092943 0 .18367-.008623.27539-.013672-.17055-.29341-.27539-.62792-.27539-.98633v-2c0-.72887.41095-1.3691 1.0059-1.7188v-.28125c.34771-.034464.68259-.10691 1.0156-.19922.10394-.99856.95603-1.8008 1.9785-1.8008h1v-2.0098c0-2.7484-2.2473-4.9902-5-4.9902zm-1.0059 2.127v4.8574c-.66556-.1047-1.2974-.37231-1.9941-.66211v-1.3223c0-1.3474.79841-2.4642 1.9941-2.873zm2.0117 0c1.1957.4088 1.9941 1.5256 1.9941 2.873v1.3457c-.68406.3054-1.3142.57292-1.9941.66602v-4.8848zm-4.0059 6.334c.67836.2231 1.3126.44599 1.9941.52539v2.8848c-1.1957-.4092-1.9941-1.5237-1.9941-2.8711v-.53906z" fill="#fc7f7f"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#5fb2ff"/></svg>
diff --git a/modules/csg/icons/CSGCombiner3D.svg b/modules/csg/icons/CSGCombiner3D.svg
index cce2902e24..692ba54cb8 100644
--- a/modules/csg/icons/CSGCombiner3D.svg
+++ b/modules/csg/icons/CSGCombiner3D.svg
@@ -1,8 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/>
-<g fill="#fc9c9c">
-<path transform="translate(0 1036.4)" d="m3 1c-1.1046 0-2 0.89543-2 2h2zm2 0v2h2v-2zm4 0v2h2v-2zm4 0v2h2c0-1.1046-0.89543-2-2-2zm-12 4v2h2v-2zm12 0v2h2v-2zm-12 4v2h2v-2zm0 4c0 1.1046 0.89543 2 2 2v-2zm4 0v2h2v-2z" fill="#fc9c9c"/>
-</g>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#5fb2ff"/><path d="m3 1c-1.1046 0-2 .89543-2 2h2zm2 0v2h2v-2zm4 0v2h2v-2zm4 0v2h2c0-1.1046-.89543-2-2-2zm-12 4v2h2v-2zm12 0v2h2v-2zm-12 4v2h2v-2zm0 4c0 1.1046.89543 2 2 2v-2zm4 0v2h2v-2z" fill="#fc7f7f"/></svg>
diff --git a/modules/csg/icons/CSGCylinder3D.svg b/modules/csg/icons/CSGCylinder3D.svg
index 645a74c79b..4bc2427887 100644
--- a/modules/csg/icons/CSGCylinder3D.svg
+++ b/modules/csg/icons/CSGCylinder3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 14.999999 14.999999" xmlns="http://www.w3.org/2000/svg">
-<g>
-<path transform="scale(.9375)" d="m8 1c-1.7469 0-3.328 0.22648-4.5586 0.63672-0.61528 0.20512-1.1471 0.45187-1.5898 0.80078-0.44272 0.34891-0.85156 0.88101-0.85156 1.5625v8c0 0.68149 0.40884 1.2155 0.85156 1.5645 0.44272 0.34891 0.97457 0.59577 1.5898 0.80078 1.2306 0.41024 2.8117 0.63477 4.5586 0.63477 0.095648 0 0.18467-0.008426 0.2793-0.009766-0.1722-0.29446-0.2793-0.62995-0.2793-0.99023v-1c-1.5668 0-2.9867-0.2195-3.9277-0.5332-0.46329-0.15435-0.90474-0.33752-1.0723-0.4668v-5.8125c0.1468 0.058667 0.2835 0.12515 0.44141 0.17773 1.2306 0.41024 2.8117 0.63477 4.5586 0.63477s3.328-0.22453 4.5586-0.63477c0.15791-0.052267 0.29461-0.11864 0.44141-0.17773v1.8125h1c0.36396 0 0.70348 0.10774 1 0.2832v-4.2832c0-0.68149-0.40884-1.2136-0.85156-1.5625-0.44272-0.34891-0.97457-0.59566-1.5898-0.80078-1.2306-0.41024-2.8117-0.63672-4.5586-0.63672zm0 2c1.5668 0 2.9867 0.22145 3.9277 0.53516 0.46368 0.15456 0.80138 0.33741 0.96875 0.4668-0.16752 0.12928-0.50546 0.3105-0.96875 0.46484-0.94102 0.31371-2.361 0.5332-3.9277 0.5332s-2.9867-0.2195-3.9277-0.5332c-0.46329-0.15435-0.80123-0.33556-0.96875-0.46484 0.16737-0.12939 0.50507-0.31224 0.96875-0.4668 0.94102-0.31371 2.361-0.53516 3.9277-0.53516z" fill="#fc9c9c" stroke-width="1.0667"/>
-<path d="m11.25 8.4375c-0.51938 0-0.9375 0.41812-0.9375 0.9375v0.9375h1.875v1.875h0.9375c0.51938 0 0.9375-0.41812 0.9375-0.9375v-1.875c0-0.51938-0.41812-0.9375-0.9375-0.9375zm0.9375 3.75h-1.875v-1.875h-0.9375c-0.51938 0-0.9375 0.41812-0.9375 0.9375v1.875c0 0.51938 0.41812 0.9375 0.9375 0.9375h1.875c0.51938 0 0.9375-0.41812 0.9375-0.9375z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 14.999999 14.999999" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-1.7469 0-3.328.22648-4.5586.63672-.61528.20512-1.1471.45187-1.5898.80078-.44272.34891-.85156.88101-.85156 1.5625v8c0 .68149.40884 1.2155.85156 1.5645.44272.34891.97457.59577 1.5898.80078 1.2306.41024 2.8117.63477 4.5586.63477.095648 0 .18467-.008426.2793-.009766-.1722-.29446-.2793-.62995-.2793-.99023v-1c-1.5668 0-2.9867-.2195-3.9277-.5332-.46329-.15435-.90474-.33752-1.0723-.4668v-5.8125c.1468.058667.2835.12515.44141.17773 1.2306.41024 2.8117.63477 4.5586.63477s3.328-.22453 4.5586-.63477c.15791-.052267.29461-.11864.44141-.17773v1.8125h1c.36396 0 .70348.10774 1 .2832v-4.2832c0-.68149-.40884-1.2136-.85156-1.5625-.44272-.34891-.97457-.59566-1.5898-.80078-1.2306-.41024-2.8117-.63672-4.5586-.63672zm0 2c1.5668 0 2.9867.22145 3.9277.53516.46368.15456.80138.33741.96875.4668-.16752.12928-.50546.3105-.96875.46484-.94102.31371-2.361.5332-3.9277.5332s-2.9867-.2195-3.9277-.5332c-.46329-.15435-.80123-.33556-.96875-.46484.16737-.12939.50507-.31224.96875-.4668.94102-.31371 2.361-.53516 3.9277-.53516z" fill="#fc7f7f" stroke-width="1.0667" transform="scale(.9375)"/><path d="m11.25 8.4375c-.51938 0-.9375.41812-.9375.9375v.9375h1.875v1.875h.9375c.51938 0 .9375-.41812.9375-.9375v-1.875c0-.51938-.41812-.9375-.9375-.9375zm.9375 3.75h-1.875v-1.875h-.9375c-.51938 0-.9375.41812-.9375.9375v1.875c0 .51938.41812.9375.9375.9375h1.875c.51938 0 .9375-.41812.9375-.9375z" fill="#5fb2ff"/></svg>
diff --git a/modules/csg/icons/CSGMesh3D.svg b/modules/csg/icons/CSGMesh3D.svg
index 6e940a4aa5..8f4a1736fb 100644
--- a/modules/csg/icons/CSGMesh3D.svg
+++ b/modules/csg/icons/CSGMesh3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g>
-<path d="m3 1c-1.1046 0-2 0.89543-2 2 5.649e-4 0.71397 0.38169 1.3735 1 1.7305v6.541c-0.61771 0.35663-0.99874 1.0152-1 1.7285 0 1.1046 0.89543 2 2 2 0.71397-5.65e-4 1.3735-0.38169 1.7305-1h3.2695v-2h-3.2715c-0.17478-0.30301-0.42598-0.55488-0.72852-0.73047v-5.8555l4.916 4.916c0.31428-0.20669 0.68609-0.33008 1.084-0.33008 0-0.3979 0.12338-0.76971 0.33008-1.084l-4.916-4.916h5.8574c0.17478 0.30301 0.42598 0.55488 0.72852 0.73047v3.2695h2v-3.2715c0.61771-0.35663 0.99874-1.0152 1-1.7285 0-1.1046-0.89543-2-2-2-0.71397 5.648e-4 -1.3735 0.38169-1.7305 1h-6.541c-0.35663-0.61771-1.0152-0.99874-1.7285-1z" fill="#fc9c9c"/>
-<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3 1c-1.1046 0-2 .89543-2 2 .0005649.71397.38169 1.3735 1 1.7305v6.541c-.61771.35663-.99874 1.0152-1 1.7285 0 1.1046.89543 2 2 2 .71397-.000565 1.3735-.38169 1.7305-1h3.2695v-2h-3.2715c-.17478-.30301-.42598-.55488-.72852-.73047v-5.8555l4.916 4.916c.31428-.20669.68609-.33008 1.084-.33008 0-.3979.12338-.76971.33008-1.084l-4.916-4.916h5.8574c.17478.30301.42598.55488.72852.73047v3.2695h2v-3.2715c.61771-.35663.99874-1.0152 1-1.7285 0-1.1046-.89543-2-2-2-.71397.0005648-1.3735.38169-1.7305 1h-6.541c-.35663-.61771-1.0152-.99874-1.7285-1z" fill="#fc7f7f"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#5fb2ff"/></svg>
diff --git a/modules/csg/icons/CSGPolygon3D.svg b/modules/csg/icons/CSGPolygon3D.svg
index 71b03cb8e6..971f3577bb 100644
--- a/modules/csg/icons/CSGPolygon3D.svg
+++ b/modules/csg/icons/CSGPolygon3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m7.9629 1.002c-0.14254 0.00487-0.28238 0.04016-0.41016 0.10352l-6 3c-0.33878 0.16944-0.55276 0.51574-0.55273 0.89453v5.832c-0.105 0.61631 0.37487 1.1768 1 1.168h5v2c2.16e-5 0.67546 0.64487 1.1297 1.2617 0.95898-0.16118-0.28721-0.26172-0.61135-0.26172-0.95898v-2c0-0.72673 0.40794-1.3664 1-1.7168v-1.666l4-2v1.3828h1c0.36397 0 0.70348 0.10774 1 0.2832v-3.2773c6e-6 -0.00195 6e-6 -0.0039094 0-0.0058594 2.6e-5 -0.37879-0.21395-0.72509-0.55273-0.89453l-6-3c-0.15022-0.074574-0.31679-0.11017-0.48438-0.10352zm0.037109 2.1172l3.7637 1.8809-2.7637 1.3809v-1.3809c-5.52e-5 -0.55226-0.44774-0.99994-1-1h-1.7617l1.7617-0.88086zm-5 2.8809h4v4h-4v-4z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fc9c9c" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
-<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7.9629 1.002c-.14254.00487-.28238.04016-.41016.10352l-6 3c-.33878.16944-.55276.51574-.55273.89453v5.832c-.105.61631.37487 1.1768 1 1.168h5v2c.0000216.67546.64487 1.1297 1.2617.95898-.16118-.28721-.26172-.61135-.26172-.95898v-2c0-.72673.40794-1.3664 1-1.7168v-1.666l4-2v1.3828h1c.36397 0 .70348.10774 1 .2832v-3.2773c.000006-.00195.000006-.0039094 0-.0058594.000026-.37879-.21395-.72509-.55273-.89453l-6-3c-.15022-.074574-.31679-.11017-.48438-.10352zm.037109 2.1172 3.7637 1.8809-2.7637 1.3809v-1.3809c-.0000552-.55226-.44774-.99994-1-1h-1.7617l1.7617-.88086zm-5 2.8809h4v4h-4z" fill="#fc7f7f"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#5fb2ff"/></svg>
diff --git a/modules/csg/icons/CSGSphere3D.svg b/modules/csg/icons/CSGSphere3D.svg
index f81b566993..770af80632 100644
--- a/modules/csg/icons/CSGSphere3D.svg
+++ b/modules/csg/icons/CSGSphere3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g>
-<path d="m8 1c-3.8541 0-7 3.1459-7 7 0 3.8542 3.1459 7 7 7 0.093042 0 0.18321-0.01004 0.27539-0.013672-0.17055-0.29341-0.27539-0.62792-0.27539-0.98633v-2c0-0.72673 0.40794-1.3664 1-1.7168v-0.33398c0.34074-0.019259 0.67728-0.069097 1.0156-0.10547 0.083091-1.0187 0.94713-1.8438 1.9844-1.8438h2c0.35841 0 0.69292 0.10484 0.98633 0.27539 0.003633-0.092184 0.013672-0.18235 0.013672-0.27539 0-3.8541-3.1459-7-7-7zm-1 2.0977v4.8711c-1.2931-0.071342-2.6061-0.29819-3.9434-0.69141 0.30081-2.0978 1.8852-3.7665 3.9434-4.1797zm2 0c2.0549 0.41253 3.637 2.0767 3.9414 4.1699-1.3046 0.36677-2.6158 0.60259-3.9414 0.6875v-4.8574zm-5.7793 6.2988c1.2733 0.31892 2.5337 0.50215 3.7793 0.5625v2.9414c-1.8291-0.36719-3.266-1.7339-3.7793-3.5039z" fill="#fc9c9c"/>
-<path d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1c-3.8541 0-7 3.1459-7 7 0 3.8542 3.1459 7 7 7 .093042 0 .18321-.01004.27539-.013672-.17055-.29341-.27539-.62792-.27539-.98633v-2c0-.72673.40794-1.3664 1-1.7168v-.33398c.34074-.019259.67728-.069097 1.0156-.10547.083091-1.0187.94713-1.8438 1.9844-1.8438h2c.35841 0 .69292.10484.98633.27539.003633-.092184.013672-.18235.013672-.27539 0-3.8541-3.1459-7-7-7zm-1 2.0977v4.8711c-1.2931-.071342-2.6061-.29819-3.9434-.69141.30081-2.0978 1.8852-3.7665 3.9434-4.1797zm2 0c2.0549.41253 3.637 2.0767 3.9414 4.1699-1.3046.36677-2.6158.60259-3.9414.6875zm-5.7793 6.2988c1.2733.31892 2.5337.50215 3.7793.5625v2.9414c-1.8291-.36719-3.266-1.7339-3.7793-3.5039z" fill="#fc7f7f"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#5fb2ff"/></svg>
diff --git a/modules/csg/icons/CSGTorus3D.svg b/modules/csg/icons/CSGTorus3D.svg
index 3d30aa47b2..ece9c68d28 100644
--- a/modules/csg/icons/CSGTorus3D.svg
+++ b/modules/csg/icons/CSGTorus3D.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m8 3c-1.8145 0-3.4691 0.41721-4.7461 1.1621-1.277 0.745-2.2539 1.9082-2.2539 3.3379 0 1.4298 0.9769 2.5949 2.2539 3.3398 1.277 0.7449 2.9316 1.1602 4.7461 1.1602 0-1.0907 0.90931-2 2-2 0-0.080836 0.013744-0.15778 0.023438-0.23633-0.61769 0.14673-1.3008 0.23633-2.0234 0.23633-1.4992 0-2.8437-0.36687-3.7383-0.88867-0.89456-0.5219-1.2617-1.108-1.2617-1.6113 0-0.5032 0.36716-1.0876 1.2617-1.6094 0.89456-0.5219 2.2391-0.89062 3.7383-0.89062s2.8437 0.36872 3.7383 0.89062c0.89456 0.5218 1.2617 1.1062 1.2617 1.6094 0 0.15978-0.053679 0.32822-0.13281 0.5h1.1328c0.32481 0 0.62893 0.088408 0.90234 0.23047 0.057552-0.23582 0.097656-0.47718 0.097656-0.73047 0-1.4297-0.9769-2.5929-2.2539-3.3379-1.277-0.7449-2.9316-1.1621-4.7461-1.1621z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#fc9c9c" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
-<path transform="translate(0 1036.4)" d="m12 9c-0.55401 0-1 0.44599-1 1v1h2v2h1c0.55401 0 1-0.44599 1-1v-2c0-0.55401-0.44599-1-1-1h-2zm1 4h-2v-2h-1c-0.55401 0-1 0.44599-1 1v2c0 0.55401 0.44599 1 1 1h2c0.55401 0 1-0.44599 1-1v-1z" fill="#84c2ff"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 3c-1.8145 0-3.4691.41721-4.7461 1.1621-1.277.745-2.2539 1.9082-2.2539 3.3379 0 1.4298.9769 2.5949 2.2539 3.3398s2.9316 1.1602 4.7461 1.1602c0-1.0907.90931-2 2-2 0-.080836.013744-.15778.023438-.23633-.61769.14673-1.3008.23633-2.0234.23633-1.4992 0-2.8437-.36687-3.7383-.88867-.89456-.5219-1.2617-1.108-1.2617-1.6113 0-.5032.36716-1.0876 1.2617-1.6094.89456-.5219 2.2391-.89062 3.7383-.89062s2.8437.36872 3.7383.89062c.89456.5218 1.2617 1.1062 1.2617 1.6094 0 .15978-.053679.32822-.13281.5h1.1328c.32481 0 .62893.088408.90234.23047.057552-.23582.097656-.47718.097656-.73047 0-1.4297-.9769-2.5929-2.2539-3.3379-1.277-.7449-2.9316-1.1621-4.7461-1.1621z" fill="#fc7f7f"/><path d="m12 9c-.55401 0-1 .44599-1 1v1h2v2h1c.55401 0 1-.44599 1-1v-2c0-.55401-.44599-1-1-1zm1 4h-2v-2h-1c-.55401 0-1 .44599-1 1v2c0 .55401.44599 1 1 1h2c.55401 0 1-.44599 1-1z" fill="#5fb2ff"/></svg>
diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp
index a8bcc2fed1..a47390c2b2 100644
--- a/modules/csg/register_types.cpp
+++ b/modules/csg/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,15 +36,15 @@
void register_csg_types() {
#ifndef _3D_DISABLED
- ClassDB::register_virtual_class<CSGShape3D>();
- ClassDB::register_virtual_class<CSGPrimitive3D>();
- ClassDB::register_class<CSGMesh3D>();
- ClassDB::register_class<CSGSphere3D>();
- ClassDB::register_class<CSGBox3D>();
- ClassDB::register_class<CSGCylinder3D>();
- ClassDB::register_class<CSGTorus3D>();
- ClassDB::register_class<CSGPolygon3D>();
- ClassDB::register_class<CSGCombiner3D>();
+ GDREGISTER_VIRTUAL_CLASS(CSGShape3D);
+ GDREGISTER_VIRTUAL_CLASS(CSGPrimitive3D);
+ GDREGISTER_CLASS(CSGMesh3D);
+ GDREGISTER_CLASS(CSGSphere3D);
+ GDREGISTER_CLASS(CSGBox3D);
+ GDREGISTER_CLASS(CSGCylinder3D);
+ GDREGISTER_CLASS(CSGTorus3D);
+ GDREGISTER_CLASS(CSGPolygon3D);
+ GDREGISTER_CLASS(CSGCombiner3D);
#ifdef TOOLS_ENABLED
EditorPlugins::add_by_type<EditorPluginCSG>();
diff --git a/modules/csg/register_types.h b/modules/csg/register_types.h
index 926e598561..8747b3fade 100644
--- a/modules/csg/register_types.h
+++ b/modules/csg/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/cvtt/SCsub b/modules/cvtt/SCsub
index 5438f7ebac..e56177d6e9 100644
--- a/modules/cvtt/SCsub
+++ b/modules/cvtt/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_cvtt = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
thirdparty_dir = "#thirdparty/cvtt/"
thirdparty_sources = [
"ConvectionKernels.cpp",
@@ -17,7 +20,15 @@ env_cvtt.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_cvtt.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
# Godot source files
-env_cvtt.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_cvtt.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/cvtt/image_compress_cvtt.cpp b/modules/cvtt/image_compress_cvtt.cpp
index 2a4f836478..3beca3d12a 100644
--- a/modules/cvtt/image_compress_cvtt.cpp
+++ b/modules/cvtt/image_compress_cvtt.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,31 +32,32 @@
#include "core/os/os.h"
#include "core/os/thread.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
+#include "core/templates/safe_refcount.h"
#include <ConvectionKernels.h>
struct CVTTCompressionJobParams {
- bool is_hdr;
- bool is_signed;
- int bytes_per_pixel;
+ bool is_hdr = false;
+ bool is_signed = false;
+ int bytes_per_pixel = 0;
cvtt::Options options;
};
struct CVTTCompressionRowTask {
const uint8_t *in_mm_bytes;
- uint8_t *out_mm_bytes;
- int y_start;
- int width;
- int height;
+ uint8_t *out_mm_bytes = nullptr;
+ int y_start = 0;
+ int width = 0;
+ int height = 0;
};
struct CVTTCompressionJobQueue {
CVTTCompressionJobParams job_params;
const CVTTCompressionRowTask *job_tasks;
- uint32_t num_tasks;
- uint32_t current_task;
+ uint32_t num_tasks = 0;
+ SafeNumeric<uint32_t> current_task;
};
static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const CVTTCompressionRowTask &p_row_task) {
@@ -131,7 +132,7 @@ static void _digest_row_task(const CVTTCompressionJobParams &p_job_params, const
static void _digest_job_queue(void *p_job_queue) {
CVTTCompressionJobQueue *job_queue = static_cast<CVTTCompressionJobQueue *>(p_job_queue);
- for (uint32_t next_task = atomic_increment(&job_queue->current_task); next_task <= job_queue->num_tasks; next_task = atomic_increment(&job_queue->current_task)) {
+ for (uint32_t next_task = job_queue->current_task.increment(); next_task <= job_queue->num_tasks; next_task = job_queue->current_task.increment()) {
_digest_row_task(job_queue->job_params, job_queue->job_tasks[next_task - 1]);
}
}
@@ -168,9 +169,10 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
flags |= cvtt::Flags::BC7_RespectPunchThrough;
- if (p_channels == Image::USED_CHANNELS_RG) { //guessing this is a normalmap
+ if (p_channels == Image::USED_CHANNELS_RG) { //guessing this is a normal map
flags |= cvtt::Flags::Uniform;
}
+ options.flags = flags;
Image::Format target_format = Image::FORMAT_BPTC_RGBA;
@@ -262,16 +264,17 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
const CVTTCompressionRowTask *tasks_rb = tasks.ptr();
job_queue.job_tasks = &tasks_rb[0];
- job_queue.current_task = 0;
+ job_queue.current_task.set(0);
job_queue.num_tasks = static_cast<uint32_t>(tasks.size());
for (int i = 0; i < num_job_threads; i++) {
- threads_wb[i] = Thread::create(_digest_job_queue, &job_queue);
+ threads_wb[i] = memnew(Thread);
+ threads_wb[i]->start(_digest_job_queue, &job_queue);
}
_digest_job_queue(&job_queue);
for (int i = 0; i < num_job_threads; i++) {
- Thread::wait_to_finish(threads_wb[i]);
+ threads_wb[i]->wait_to_finish();
memdelete(threads_wb[i]);
}
}
diff --git a/modules/cvtt/image_compress_cvtt.h b/modules/cvtt/image_compress_cvtt.h
index c1772199af..bef5653fa9 100644
--- a/modules/cvtt/image_compress_cvtt.h
+++ b/modules/cvtt/image_compress_cvtt.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef IMAGE_COMPRESS_CVTT_H
#define IMAGE_COMPRESS_CVTT_H
-#include "core/image.h"
+#include "core/io/image.h"
void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels);
void image_decompress_cvtt(Image *p_image);
diff --git a/modules/cvtt/register_types.cpp b/modules/cvtt/register_types.cpp
index e4a01cc787..055b5dc6e3 100644
--- a/modules/cvtt/register_types.cpp
+++ b/modules/cvtt/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/cvtt/register_types.h b/modules/cvtt/register_types.h
index 36b5e332d6..e62e8c0e9a 100644
--- a/modules/cvtt/register_types.h
+++ b/modules/cvtt/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/dds/register_types.cpp b/modules/dds/register_types.cpp
index 3991964a28..60282c3f36 100644
--- a/modules/dds/register_types.cpp
+++ b/modules/dds/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,7 +35,7 @@
static Ref<ResourceFormatDDS> resource_loader_dds;
void register_dds_types() {
- resource_loader_dds.instance();
+ resource_loader_dds.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_dds);
}
diff --git a/modules/dds/register_types.h b/modules/dds/register_types.h
index 3cb7b5c2a6..b84bcd06c8 100644
--- a/modules/dds/register_types.h
+++ b/modules/dds/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index 2f4f7d7a4c..fced61a600 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "texture_loader_dds.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#define PF_FOURCC(s) ((uint32_t)(((s)[3] << 24U) | ((s)[2] << 16U) | ((s)[1] << 8U) | ((s)[0])))
@@ -69,11 +69,11 @@ enum DDSFormat {
struct DDSFormatInfo {
const char *name;
- bool compressed;
- bool palette;
- uint32_t divisor;
- uint32_t block_size;
- Image::Format format;
+ bool compressed = false;
+ bool palette = false;
+ uint32_t divisor = 0;
+ uint32_t block_size = 0;
+ Image::Format format = Image::Format::FORMAT_BPTC_RGBA;
};
static const DDSFormatInfo dds_format_info[DDS_MAX] = {
@@ -94,7 +94,7 @@ static const DDSFormatInfo dds_format_info[DDS_MAX] = {
{ "GRAYSCALE_ALPHA", false, false, 1, 2, Image::FORMAT_LA8 }
};
-RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatDDS::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) {
if (r_error) {
*r_error = ERR_CANT_OPEN;
}
@@ -373,7 +373,6 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
int colcount = size/4;
for(int i=0;i<colcount;i++) {
-
uint8_t r = wb[i*4+1];
uint8_t g = wb[i*4+2];
uint8_t b = wb[i*4+3];
@@ -392,7 +391,6 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
int colcount = size/3;
for(int i=0;i<colcount;i++) {
-
SWAP( wb[i*3+0],wb[i*3+2] );
}*/
} break;
diff --git a/modules/dds/texture_loader_dds.h b/modules/dds/texture_loader_dds.h
index ef08967df7..cf93156423 100644
--- a/modules/dds/texture_loader_dds.h
+++ b/modules/dds/texture_loader_dds.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,7 +36,7 @@
class ResourceFormatDDS : 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, bool p_no_cache = false);
+ 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;
diff --git a/modules/denoise/SCsub b/modules/denoise/SCsub
index bf3bd7d073..97feea2b44 100644
--- a/modules/denoise/SCsub
+++ b/modules/denoise/SCsub
@@ -8,6 +8,9 @@ Import("env_modules")
env_oidn = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
thirdparty_dir = "#thirdparty/oidn/"
thirdparty_sources = [
"core/api.cpp",
@@ -106,7 +109,8 @@ env_oidn.Append(
env_thirdparty = env_oidn.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
weights_in_path = thirdparty_dir + "weights/rtlightmap_hdr.tza"
weights_out_path = thirdparty_dir + "weights/rtlightmap_hdr.gen.cpp"
@@ -114,5 +118,12 @@ weights_out_path = thirdparty_dir + "weights/rtlightmap_hdr.gen.cpp"
env_thirdparty.Depends(weights_out_path, weights_in_path)
env_thirdparty.CommandNoCache(weights_out_path, weights_in_path, resource_to_cpp.tza_to_cpp)
-env_oidn.add_source_files(env.modules_sources, "denoise_wrapper.cpp")
-env_modules.add_source_files(env.modules_sources, ["register_types.cpp", "lightmap_denoiser.cpp"])
+# Godot source files
+
+module_obj = []
+
+env_oidn.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/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/denoise/denoise_wrapper.cpp b/modules/denoise/denoise_wrapper.cpp
index c12c6d9c31..e71fce5958 100644
--- a/modules/denoise/denoise_wrapper.cpp
+++ b/modules/denoise/denoise_wrapper.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/denoise/denoise_wrapper.h b/modules/denoise/denoise_wrapper.h
index 2107df09c1..25e342bc93 100644
--- a/modules/denoise/denoise_wrapper.h
+++ b/modules/denoise/denoise_wrapper.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/denoise/lightmap_denoiser.cpp b/modules/denoise/lightmap_denoiser.cpp
index 29d02e8ee2..71dcc1d75f 100644
--- a/modules/denoise/lightmap_denoiser.cpp
+++ b/modules/denoise/lightmap_denoiser.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,6 +31,8 @@
#include "lightmap_denoiser.h"
#include "denoise_wrapper.h"
+#include "core/io/image.h"
+
LightmapDenoiser *LightmapDenoiserOIDN::create_oidn_denoiser() {
return memnew(LightmapDenoiserOIDN);
}
diff --git a/modules/denoise/lightmap_denoiser.h b/modules/denoise/lightmap_denoiser.h
index d01bbd10a5..f1992a1733 100644
--- a/modules/denoise/lightmap_denoiser.h
+++ b/modules/denoise/lightmap_denoiser.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef LIGHTMAP_DENOISER_H
#define LIGHTMAP_DENOISER_H
-#include "core/object.h"
+#include "core/object/class_db.h"
#include "scene/3d/lightmapper.h"
struct OIDNDeviceImpl;
diff --git a/modules/denoise/register_types.cpp b/modules/denoise/register_types.cpp
index b78734a531..936e5f604d 100644
--- a/modules/denoise/register_types.cpp
+++ b/modules/denoise/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "lightmap_denoiser.h"
void register_denoise_types() {
diff --git a/modules/denoise/register_types.h b/modules/denoise/register_types.h
index f0f1f44bfe..516a91b134 100644
--- a/modules/denoise/register_types.h
+++ b/modules/denoise/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/enet/SCsub b/modules/enet/SCsub
index c8f4b3885e..580e5a3eb0 100644
--- a/modules/enet/SCsub
+++ b/modules/enet/SCsub
@@ -7,6 +7,8 @@ env_enet = env_modules.Clone()
# Thirdparty source files
+thirdparty_obj = []
+
if env["builtin_enet"]:
thirdparty_dir = "#thirdparty/enet/"
thirdparty_sources = [
@@ -26,6 +28,16 @@ if env["builtin_enet"]:
env_thirdparty = env_enet.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
+
+module_obj = []
+
+env_enet.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-env_enet.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/enet/config.py b/modules/enet/config.py
index 5fd343c75d..9102c74579 100644
--- a/modules/enet/config.py
+++ b/modules/enet/config.py
@@ -8,7 +8,9 @@ def configure(env):
def get_doc_classes():
return [
- "NetworkedMultiplayerENet",
+ "ENetMultiplayerPeer",
+ "ENetConnection",
+ "ENetPacketPeer",
]
diff --git a/modules/enet/doc_classes/ENetConnection.xml b/modules/enet/doc_classes/ENetConnection.xml
new file mode 100644
index 0000000000..fcdf282a7d
--- /dev/null
+++ b/modules/enet/doc_classes/ENetConnection.xml
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ENetConnection" inherits="RefCounted" version="4.0">
+ <brief_description>
+ A wrapper class for an [url=http://enet.bespin.org/group__host.html]ENetHost[/url].
+ </brief_description>
+ <description>
+ ENet's purpose is to provide a relatively thin, simple and robust network communication layer on top of UDP (User Datagram Protocol).
+ </description>
+ <tutorials>
+ <link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link>
+ </tutorials>
+ <methods>
+ <method name="bandwidth_limit">
+ <return type="void" />
+ <argument index="0" name="in_bandwidth" type="int" default="0" />
+ <argument index="1" name="out_bandwidth" type="int" default="0" />
+ <description>
+ Adjusts the bandwidth limits of a host.
+ </description>
+ </method>
+ <method name="broadcast">
+ <return type="void" />
+ <argument index="0" name="channel" type="int" />
+ <argument index="1" name="packet" type="PackedByteArray" />
+ <argument index="2" name="flags" type="int" />
+ <description>
+ Queues a [code]packet[/code] to be sent to all peers associated with the host over the specified [code]channel[/code]. See [ENetPacketPeer] [code]FLAG_*[/code] constants for available packet flags.
+ </description>
+ </method>
+ <method name="channel_limit">
+ <return type="void" />
+ <argument index="0" name="limit" type="int" />
+ <description>
+ Limits the maximum allowed channels of future incoming connections.
+ </description>
+ </method>
+ <method name="compress">
+ <return type="void" />
+ <argument index="0" name="mode" type="int" enum="ENetConnection.CompressionMode" />
+ <description>
+ Sets the compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all.
+ [b]Note:[/b] Most games' network design involve sending many small packets frequently (smaller than 4 KB each). If in doubt, it is recommended to keep the default compression algorithm as it works best on these small packets.
+ [b]Note:[/b] The compression mode must be set to the same value on both the server and all its clients. Clients will fail to connect if the compression mode set on the client differs from the one set on the server.
+ </description>
+ </method>
+ <method name="connect_to_host">
+ <return type="ENetPacketPeer" />
+ <argument index="0" name="address" type="String" />
+ <argument index="1" name="port" type="int" />
+ <argument index="2" name="channels" type="int" default="0" />
+ <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.
+ [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">
+ <return type="int" enum="Error" />
+ <argument index="0" name="max_peers" type="int" default="32" />
+ <argument index="1" name="max_channels" type="int" default="0" />
+ <argument index="2" name="in_bandwidth" type="int" default="0" />
+ <argument index="3" name="out_bandwidth" type="int" default="0" />
+ <description>
+ Create an ENetHost that will allow up to [code]max_peers[/code] connected peers, each allocating up to [code]max_channels[/code] channels, optionally limiting bandwith to [code]in_bandwidth[/code] and [code]out_bandwidth[/code].
+ </description>
+ </method>
+ <method name="create_host_bound">
+ <return type="int" enum="Error" />
+ <argument index="0" name="bind_address" type="String" />
+ <argument index="1" name="bind_port" type="int" />
+ <argument index="2" name="max_peers" type="int" default="32" />
+ <argument index="3" name="max_channels" type="int" default="0" />
+ <argument index="4" name="in_bandwidth" type="int" default="0" />
+ <argument index="5" name="out_bandwidth" type="int" default="0" />
+ <description>
+ Create an ENetHost like [method create_host] which is also bound to the given [code]bind_address[/code] and [code]bind_port[/code].
+ </description>
+ </method>
+ <method name="destroy">
+ <return type="void" />
+ <description>
+ Destroys the host and all resources associated with it.
+ </description>
+ </method>
+ <method name="dtls_client_setup">
+ <return type="int" enum="Error" />
+ <argument index="0" name="certificate" type="X509Certificate" />
+ <argument index="1" name="hostname" type="String" />
+ <argument index="2" name="verify" type="bool" default="true" />
+ <description>
+ Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet clients. Call this before [method connect_to_host] to have ENet connect using DTLS with [code]certificate[/code] and [code]hostname[/code] verification. Verification can be optionally turned off via the [code]verify[/code] parameter.
+ </description>
+ </method>
+ <method name="dtls_server_setup">
+ <return type="int" enum="Error" />
+ <argument index="0" name="key" type="CryptoKey" />
+ <argument index="1" name="certificate" type="X509Certificate" />
+ <description>
+ Configure this ENetHost to use the custom Godot extension allowing DTLS encryption for ENet servers. Call this right after [method create_host_bound] to have ENet expect peers to connect using DTLS.
+ </description>
+ </method>
+ <method name="flush">
+ <return type="void" />
+ <description>
+ Sends any queued packets on the host specified to its designated peers.
+ </description>
+ </method>
+ <method name="get_local_port" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the local port to which this peer is bound.
+ </description>
+ </method>
+ <method name="get_max_channels" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the maximum number of channels allowed for connected peers.
+ </description>
+ </method>
+ <method name="get_peers">
+ <return type="Array" />
+ <description>
+ Returns the list of peers associated with this host.
+ [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">
+ <return type="float" />
+ <argument index="0" name="statistic" type="int" enum="ENetConnection.HostStatistic" />
+ <description>
+ Returns and resets host statistics. See [enum HostStatistic] for more info.
+ </description>
+ </method>
+ <method name="refuse_new_connections">
+ <return type="void" />
+ <argument index="0" name="refuse" type="bool" />
+ <description>
+ Configures the DTLS server to automatically drop new connections.
+ [b]Note:[/b] This method is only relevant after calling [method dtls_server_setup].
+ </description>
+ </method>
+ <method name="service">
+ <return type="Array" />
+ <argument index="0" name="timeout" type="int" default="0" />
+ <description>
+ Waits for events on the host specified and shuttles packets between the host and its peers. The returned [Array] will have 4 elements. An [enum EventType], the [ENetPacketPeer] which generated the event, the event associated data (if any), the event associated channel (if any). If the generated event is [constant EVENT_RECEIVE], the received packet will be queued to the associated [ENetPacketPeer].
+ Call this function regularly to handle connections, disconnections, and to receive new packets.
+ </description>
+ </method>
+ </methods>
+ <constants>
+ <constant name="COMPRESS_NONE" value="0" enum="CompressionMode">
+ No compression. This uses the most bandwidth, but has the upside of requiring the fewest CPU resources. This option may also be used to make network debugging using tools like Wireshark easier.
+ </constant>
+ <constant name="COMPRESS_RANGE_CODER" value="1" enum="CompressionMode">
+ ENet's built-in range encoding. Works well on small packets, but is not the most efficient algorithm on packets larger than 4 KB.
+ </constant>
+ <constant name="COMPRESS_FASTLZ" value="2" enum="CompressionMode">
+ [url=https://fastlz.org/]FastLZ[/url] compression. This option uses less CPU resources compared to [constant COMPRESS_ZLIB], at the expense of using more bandwidth.
+ </constant>
+ <constant name="COMPRESS_ZLIB" value="3" enum="CompressionMode">
+ [url=https://www.zlib.net/]Zlib[/url] compression. This option uses less bandwidth compared to [constant COMPRESS_FASTLZ], at the expense of using more CPU resources.
+ </constant>
+ <constant name="COMPRESS_ZSTD" value="4" enum="CompressionMode">
+ [url=https://facebook.github.io/zstd/]Zstandard[/url] compression. Note that this algorithm is not very efficient on packets smaller than 4 KB. Therefore, it's recommended to use other compression algorithms in most cases.
+ </constant>
+ <constant name="EVENT_ERROR" value="-1" enum="EventType">
+ An error occurred during [method service]. You will likely need to [method destroy] the host and recreate it.
+ </constant>
+ <constant name="EVENT_NONE" value="0" enum="EventType">
+ No event occurred within the specified time limit.
+ </constant>
+ <constant name="EVENT_CONNECT" value="1" enum="EventType">
+ A connection request initiated by enet_host_connect has completed. The array will contain the peer which successfully connected.
+ </constant>
+ <constant name="EVENT_DISCONNECT" value="2" enum="EventType">
+ A peer has disconnected. This event is generated on a successful completion of a disconnect initiated by [method ENetPacketPeer.peer_disconnect], if a peer has timed out, or if a connection request intialized by [method connect_to_host] has timed out. The array will contain the peer which disconnected. The data field contains user supplied data describing the disconnection, or 0, if none is available.
+ </constant>
+ <constant name="EVENT_RECEIVE" value="3" enum="EventType">
+ A packet has been received from a peer. The array will contain the peer which sent the packet, the channel number upon which the packet was received, and the received packet.
+ </constant>
+ <constant name="HOST_TOTAL_SENT_DATA" value="0" enum="HostStatistic">
+ Total data sent.
+ </constant>
+ <constant name="HOST_TOTAL_SENT_PACKETS" value="1" enum="HostStatistic">
+ Total UDP packets sent.
+ </constant>
+ <constant name="HOST_TOTAL_RECEIVED_DATA" value="2" enum="HostStatistic">
+ Total data received.
+ </constant>
+ <constant name="HOST_TOTAL_RECEIVED_PACKETS" value="3" enum="HostStatistic">
+ Total UDP packets received.
+ </constant>
+ </constants>
+</class>
diff --git a/modules/enet/doc_classes/ENetMultiplayerPeer.xml b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
new file mode 100644
index 0000000000..d2456d3360
--- /dev/null
+++ b/modules/enet/doc_classes/ENetMultiplayerPeer.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ENetMultiplayerPeer" inherits="MultiplayerPeer" version="4.0">
+ <brief_description>
+ 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.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">$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>
+ <method name="add_mesh_peer">
+ <return type="int" enum="Error" />
+ <argument index="0" name="peer_id" type="int" />
+ <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].
+ [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">
+ <return type="void" />
+ <argument index="0" name="wait_usec" type="int" default="100" />
+ <description>
+ Closes the connection. Ignored if no connection is currently established. If this is a server it tries to notify all clients before forcibly disconnecting them. If this is a client it simply closes the connection to the server.
+ </description>
+ </method>
+ <method name="create_client">
+ <return type="int" enum="Error" />
+ <argument index="0" name="address" type="String" />
+ <argument index="1" name="port" type="int" />
+ <argument index="2" name="channel_count" type="int" default="0" />
+ <argument index="3" name="in_bandwidth" type="int" default="0" />
+ <argument index="4" name="out_bandwidth" type="int" default="0" />
+ <argument index="5" name="local_port" type="int" default="0" />
+ <description>
+ Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain name (e.g. [code]"www.example.com"[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]"192.168.1.1"[/code]). The [code]port[/code] is the port the server is listening on. The [code]channel_count[/code] parameter can be used to specify the number of ENet channels allocated for the connection. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [constant OK] if a client was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [code]local_port[/code] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques.
+ </description>
+ </method>
+ <method name="create_mesh">
+ <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.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">
+ <return type="int" enum="Error" />
+ <argument index="0" name="port" type="int" />
+ <argument index="1" name="max_clients" type="int" default="32" />
+ <argument index="2" name="max_channels" type="int" default="0" />
+ <argument index="3" name="in_bandwidth" type="int" default="0" />
+ <argument index="4" name="out_bandwidth" type="int" default="0" />
+ <description>
+ Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]"*"[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4095 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [constant OK] if a server was created, [constant ERR_ALREADY_IN_USE] if this ENetMultiplayerPeer instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the server could not be created.
+ </description>
+ </method>
+ <method name="get_peer" qualifiers="const">
+ <return type="ENetPacketPeer" />
+ <argument index="0" name="id" type="int" />
+ <description>
+ Return the [ENetPacketPeer] associated to the given [code]id[/code].
+ </description>
+ </method>
+ <method name="set_bind_ip">
+ <return type="void" />
+ <argument index="0" name="ip" type="String" />
+ <description>
+ The IP used when creating a server. This is set to the wildcard [code]"*"[/code] by default, which binds to all available interfaces. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]"192.168.1.1"[/code].
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="host" type="ENetConnection" setter="" getter="get_host">
+ The underlying [ENetConnection] created after [method create_client] and [method create_server].
+ </member>
+ <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>
+ </members>
+</class>
diff --git a/modules/enet/doc_classes/ENetPacketPeer.xml b/modules/enet/doc_classes/ENetPacketPeer.xml
new file mode 100644
index 0000000000..4116ba17f2
--- /dev/null
+++ b/modules/enet/doc_classes/ENetPacketPeer.xml
@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="ENetPacketPeer" inherits="PacketPeer" version="4.0">
+ <brief_description>
+ A wrapper class for an [url=http://enet.bespin.org/group__peer.html]ENetPeer[/url].
+ </brief_description>
+ <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>
+ </tutorials>
+ <methods>
+ <method name="get_channels" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the number of channels allocated for communication with peer.
+ </description>
+ </method>
+ <method name="get_state" qualifiers="const">
+ <return type="int" enum="ENetPacketPeer.PeerState" />
+ <description>
+ Returns the current peer state. See [enum PeerState].
+ </description>
+ </method>
+ <method name="get_statistic">
+ <return type="float" />
+ <argument index="0" name="statistic" type="int" enum="ENetPacketPeer.PeerStatistic" />
+ <description>
+ Returns the requested [code]statistic[/code] for this peer. See [enum PeerStatistic].
+ </description>
+ </method>
+ <method name="is_active" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if the peer is currently active (i.e. the associated [ENetConnection] is still valid).
+ </description>
+ </method>
+ <method name="peer_disconnect">
+ <return type="void" />
+ <argument index="0" name="data" type="int" default="0" />
+ <description>
+ Request a disconnection from a peer. An [constant ENetConnection.EVENT_DISCONNECT] will be generated during [method ENetConnection.service] once the disconnection is complete.
+ </description>
+ </method>
+ <method name="peer_disconnect_later">
+ <return type="void" />
+ <argument index="0" name="data" type="int" default="0" />
+ <description>
+ Request a disconnection from a peer, but only after all queued outgoing packets are sent. An [constant ENetConnection.EVENT_DISCONNECT] will be generated during [method ENetConnection.service] once the disconnection is complete.
+ </description>
+ </method>
+ <method name="peer_disconnect_now">
+ <return type="void" />
+ <argument index="0" name="data" type="int" default="0" />
+ <description>
+ Force an immediate disconnection from a peer. No [constant ENetConnection.EVENT_DISCONNECT] will be generated. The foreign peer is not guaranteed to receive the disconnect notification, and is reset immediately upon return from this function.
+ </description>
+ </method>
+ <method name="ping">
+ <return type="void" />
+ <description>
+ Sends a ping request to a peer. ENet automatically pings all connected peers at regular intervals, however, this function may be called to ensure more frequent ping requests.
+ </description>
+ </method>
+ <method name="ping_interval">
+ <return type="void" />
+ <argument index="0" name="ping_interval" type="int" />
+ <description>
+ Sets the [code]ping_interval[/code] in milliseconds at which pings will be sent to a peer. Pings are used both to monitor the liveness of the connection and also to dynamically adjust the throttle during periods of low traffic so that the throttle has reasonable responsiveness during traffic spikes.
+ </description>
+ </method>
+ <method name="reset">
+ <return type="void" />
+ <description>
+ Forcefully disconnects a peer. The foreign host represented by the peer is not notified of the disconnection and will timeout on its connection to the local host.
+ </description>
+ </method>
+ <method name="send">
+ <return type="int" enum="Error" />
+ <argument index="0" name="channel" type="int" />
+ <argument index="1" name="packet" type="PackedByteArray" />
+ <argument index="2" name="flags" type="int" />
+ <description>
+ Queues a [code]packet[/code] to be sent over the specified [code]channel[/code]. See [code]FLAG_*[/code] constants for available packet flags.
+ </description>
+ </method>
+ <method name="set_timeout">
+ <return type="void" />
+ <argument index="0" name="timeout" type="int" />
+ <argument index="1" name="timeout_min" type="int" />
+ <argument index="2" name="timeout_max" type="int" />
+ <description>
+ Sets the timeout parameters for a peer. The timeout parameters control how and when a peer will timeout from a failure to acknowledge reliable traffic. Timeout values are expressed in milliseconds.
+ The [code]timeout_limit[/code] is a factor that, multiplied by a value based on the average round trip time, will determine the timeout limit for a reliable packet. When that limit is reached, the timeout will be doubled, and the peer will be disconnected if that limit has reached [code]timeout_min[/code]. The [code]timeout_max[/code] parameter, on the other hand, defines a fixed timeout for which any packet must be acknowledged or the peer will be dropped.
+ </description>
+ </method>
+ <method name="throttle_configure">
+ <return type="void" />
+ <argument index="0" name="interval" type="int" />
+ <argument index="1" name="acceleration" type="int" />
+ <argument index="2" name="deceleration" type="int" />
+ <description>
+ Configures throttle parameter for a peer.
+ Unreliable packets are dropped by ENet in response to the varying conditions of the Internet connection to the peer. The throttle represents a probability that an unreliable packet should not be dropped and thus sent by ENet to the peer. By measuring fluctuations in round trip times of reliable packets over the specified [code]interval[/code], ENet will either increase the probably by the amount specified in the [code]acceleration[/code] parameter, or decrease it by the amount specified in the [code]deceleration[/code] parameter (both are ratios to [constant PACKET_THROTTLE_SCALE]).
+ When the throttle has a value of [constant PACKET_THROTTLE_SCALE], no unreliable packets are dropped by ENet, and so 100% of all unreliable packets will be sent.
+ When the throttle has a value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable packets will be sent.
+ Intermediate values for the throttle represent intermediate probabilities between 0% and 100% of unreliable packets being sent. The bandwidth limits of the local and foreign hosts are taken into account to determine a sensible limit for the throttle probability above which it should not raise even in the best of conditions.
+ </description>
+ </method>
+ </methods>
+ <constants>
+ <constant name="STATE_DISCONNECTED" value="0" enum="PeerState">
+ </constant>
+ <constant name="STATE_CONNECTING" value="1" enum="PeerState">
+ </constant>
+ <constant name="STATE_ACKNOWLEDGING_CONNECT" value="2" enum="PeerState">
+ </constant>
+ <constant name="STATE_CONNECTION_PENDING" value="3" enum="PeerState">
+ </constant>
+ <constant name="STATE_CONNECTION_SUCCEEDED" value="4" enum="PeerState">
+ </constant>
+ <constant name="STATE_CONNECTED" value="5" enum="PeerState">
+ </constant>
+ <constant name="STATE_DISCONNECT_LATER" value="6" enum="PeerState">
+ </constant>
+ <constant name="STATE_DISCONNECTING" value="7" enum="PeerState">
+ </constant>
+ <constant name="STATE_ACKNOWLEDGING_DISCONNECT" value="8" enum="PeerState">
+ </constant>
+ <constant name="STATE_ZOMBIE" value="9" enum="PeerState">
+ </constant>
+ <constant name="PEER_PACKET_LOSS" value="0" enum="PeerStatistic">
+ Mean packet loss of reliable packets as a ratio with respect to the [constant PACKET_LOSS_SCALE].
+ </constant>
+ <constant name="PEER_PACKET_LOSS_VARIANCE" value="1" enum="PeerStatistic">
+ Packet loss variance.
+ </constant>
+ <constant name="PEER_PACKET_LOSS_EPOCH" value="2" enum="PeerStatistic">
+ </constant>
+ <constant name="PEER_ROUND_TRIP_TIME" value="3" enum="PeerStatistic">
+ Mean packet round trip time for reliable packets.
+ </constant>
+ <constant name="PEER_ROUND_TRIP_TIME_VARIANCE" value="4" enum="PeerStatistic">
+ Variance of the mean round trip time.
+ </constant>
+ <constant name="PEER_LAST_ROUND_TRIP_TIME" value="5" enum="PeerStatistic">
+ Last recorded round trip time for a reliable packet.
+ </constant>
+ <constant name="PEER_LAST_ROUND_TRIP_TIME_VARIANCE" value="6" enum="PeerStatistic">
+ Variance of the last trip time recorded.
+ </constant>
+ <constant name="PEER_PACKET_THROTTLE" value="7" enum="PeerStatistic">
+ </constant>
+ <constant name="PEER_PACKET_THROTTLE_LIMIT" value="8" enum="PeerStatistic">
+ </constant>
+ <constant name="PEER_PACKET_THROTTLE_COUNTER" value="9" enum="PeerStatistic">
+ </constant>
+ <constant name="PEER_PACKET_THROTTLE_EPOCH" value="10" enum="PeerStatistic">
+ </constant>
+ <constant name="PEER_PACKET_THROTTLE_ACCELERATION" value="11" enum="PeerStatistic">
+ </constant>
+ <constant name="PEER_PACKET_THROTTLE_DECELERATION" value="12" enum="PeerStatistic">
+ </constant>
+ <constant name="PEER_PACKET_THROTTLE_INTERVAL" value="13" enum="PeerStatistic">
+ </constant>
+ <constant name="PACKET_LOSS_SCALE" value="65536">
+ The reference scale for packet loss. See [method get_statistic] and [constant PEER_PACKET_LOSS].
+ </constant>
+ <constant name="PACKET_THROTTLE_SCALE" value="32">
+ The reference value for throttle configuration. See [method throttle_configure].
+ </constant>
+ <constant name="FLAG_RELIABLE" value="1">
+ Mark the packet to be sent as reliable.
+ </constant>
+ <constant name="FLAG_UNSEQUENCED" value="2">
+ Mark the packet to be sent unsequenced (unreliable).
+ </constant>
+ <constant name="FLAG_UNRELIABLE_FRAGMENT" value="8">
+ Mark the packet to be sent unreliable even if the packet is too big and needs fragmentation (increasing the chance of it being dropped).
+ </constant>
+ </constants>
+</class>
diff --git a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml b/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
deleted file mode 100644
index f46ef2d812..0000000000
--- a/modules/enet/doc_classes/NetworkedMultiplayerENet.xml
+++ /dev/null
@@ -1,169 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="NetworkedMultiplayerENet" inherits="NetworkedMultiplayerPeer" version="4.0">
- <brief_description>
- PacketPeer implementation using the [url=http://enet.bespin.org/index.html]ENet[/url] library.
- </brief_description>
- <description>
- A PacketPeer implementation that should be passed to [member SceneTree.network_peer] after being initialized as either a client or server. Events can then be handled by connecting to [SceneTree] signals.
- </description>
- <tutorials>
- <link title="High-level multiplayer">https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html</link>
- <link title="API documentation on the ENet website">http://enet.bespin.org/usergroup0.html</link>
- </tutorials>
- <methods>
- <method name="close_connection">
- <return type="void">
- </return>
- <argument index="0" name="wait_usec" type="int" default="100">
- </argument>
- <description>
- Closes the connection. Ignored if no connection is currently established. If this is a server it tries to notify all clients before forcibly disconnecting them. If this is a client it simply closes the connection to the server.
- </description>
- </method>
- <method name="create_client">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="address" type="String">
- </argument>
- <argument index="1" name="port" type="int">
- </argument>
- <argument index="2" name="in_bandwidth" type="int" default="0">
- </argument>
- <argument index="3" name="out_bandwidth" type="int" default="0">
- </argument>
- <argument index="4" name="client_port" type="int" default="0">
- </argument>
- <description>
- Create client that connects to a server at [code]address[/code] using specified [code]port[/code]. The given address needs to be either a fully qualified domain name (e.g. [code]"www.example.com"[/code]) or an IP address in IPv4 or IPv6 format (e.g. [code]"192.168.1.1"[/code]). The [code]port[/code] is the port the server is listening on. The [code]in_bandwidth[/code] and [code]out_bandwidth[/code] parameters can be used to limit the incoming and outgoing bandwidth to the given number of bytes per second. The default of 0 means unlimited bandwidth. Note that ENet will strategically drop packets on specific sides of a connection between peers to ensure the peer's bandwidth is not overwhelmed. The bandwidth parameters also determine the window size of a connection which limits the amount of reliable packets that may be in transit at any given time. Returns [constant OK] if a client was created, [constant ERR_ALREADY_IN_USE] if this NetworkedMultiplayerENet instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the client could not be created. If [code]client_port[/code] is specified, the client will also listen to the given port; this is useful for some NAT traversal techniques.
- </description>
- </method>
- <method name="create_server">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="port" type="int">
- </argument>
- <argument index="1" name="max_clients" type="int" default="32">
- </argument>
- <argument index="2" name="in_bandwidth" type="int" default="0">
- </argument>
- <argument index="3" name="out_bandwidth" type="int" default="0">
- </argument>
- <description>
- Create server that listens to connections via [code]port[/code]. The port needs to be an available, unused port between 0 and 65535. Note that ports below 1024 are privileged and may require elevated permissions depending on the platform. To change the interface the server listens on, use [method set_bind_ip]. The default IP is the wildcard [code]"*"[/code], which listens on all available interfaces. [code]max_clients[/code] is the maximum number of clients that are allowed at once, any number up to 4095 may be used, although the achievable number of simultaneous clients may be far lower and depends on the application. For additional details on the bandwidth parameters, see [method create_client]. Returns [constant OK] if a server was created, [constant ERR_ALREADY_IN_USE] if this NetworkedMultiplayerENet instance already has an open connection (in which case you need to call [method close_connection] first) or [constant ERR_CANT_CREATE] if the server could not be created.
- </description>
- </method>
- <method name="disconnect_peer">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="now" type="bool" default="false">
- </argument>
- <description>
- Disconnect the given peer. If "now" is set to [code]true[/code], the connection will be closed immediately without flushing queued messages.
- </description>
- </method>
- <method name="get_last_packet_channel" qualifiers="const">
- <return type="int">
- </return>
- <description>
- Returns the channel of the last packet fetched via [method PacketPeer.get_packet].
- </description>
- </method>
- <method name="get_packet_channel" qualifiers="const">
- <return type="int">
- </return>
- <description>
- Returns the channel of the next packet that will be retrieved via [method PacketPeer.get_packet].
- </description>
- </method>
- <method name="get_peer_address" qualifiers="const">
- <return type="String">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the IP address of the given peer.
- </description>
- </method>
- <method name="get_peer_port" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <description>
- Returns the remote port of the given peer.
- </description>
- </method>
- <method name="set_bind_ip">
- <return type="void">
- </return>
- <argument index="0" name="ip" type="String">
- </argument>
- <description>
- The IP used when creating a server. This is set to the wildcard [code]"*"[/code] by default, which binds to all available interfaces. The given IP needs to be in IPv4 or IPv6 address format, for example: [code]"192.168.1.1"[/code].
- </description>
- </method>
- <method name="set_dtls_certificate">
- <return type="void">
- </return>
- <argument index="0" name="certificate" type="X509Certificate">
- </argument>
- <description>
- Configure the [X509Certificate] to use when [member use_dtls] is [code]true[/code]. For servers, you must also setup the [CryptoKey] via [method set_dtls_key].
- </description>
- </method>
- <method name="set_dtls_key">
- <return type="void">
- </return>
- <argument index="0" name="key" type="CryptoKey">
- </argument>
- <description>
- Configure the [CryptoKey] to use when [member use_dtls] is [code]true[/code]. Remember to also call [method set_dtls_certificate] to setup your [X509Certificate].
- </description>
- </method>
- </methods>
- <members>
- <member name="always_ordered" type="bool" setter="set_always_ordered" getter="is_always_ordered" default="false">
- Enforce ordered packets when using [constant NetworkedMultiplayerPeer.TRANSFER_MODE_UNRELIABLE] (thus behaving similarly to [constant NetworkedMultiplayerPeer.TRANSFER_MODE_UNRELIABLE_ORDERED]). This is the only way to use ordering with the RPC system.
- </member>
- <member name="channel_count" type="int" setter="set_channel_count" getter="get_channel_count" default="3">
- The number of channels to be used by ENet. Channels are used to separate different kinds of data. In reliable or ordered mode, for example, the packet delivery order is ensured on a per-channel basis. This is done to combat latency and reduces ordering restrictions on packets. The delivery status of a packet in one channel won't stall the delivery of other packets in another channel.
- </member>
- <member name="compression_mode" type="int" setter="set_compression_mode" getter="get_compression_mode" enum="NetworkedMultiplayerENet.CompressionMode" default="0">
- The compression method used for network packets. These have different tradeoffs of compression speed versus bandwidth, you may need to test which one works best for your use case if you use compression at all.
- </member>
- <member name="dtls_verify" type="bool" setter="set_dtls_verify_enabled" getter="is_dtls_verify_enabled" default="true">
- Enable or disable certificate verification when [member use_dtls] [code]true[/code].
- </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_channel" type="int" setter="set_transfer_channel" getter="get_transfer_channel" default="-1">
- Set the default channel to be used to transfer data. By default, this value is [code]-1[/code] which means that ENet will only use 2 channels: one for reliable packets, and one for unreliable packets. The channel [code]0[/code] is reserved and cannot be used. Setting this member to any value between [code]0[/code] and [member channel_count] (excluded) will force ENet to use that channel for sending data. See [member channel_count] for more information about ENet channels.
- </member>
- <member name="transfer_mode" type="int" setter="set_transfer_mode" getter="get_transfer_mode" override="true" enum="NetworkedMultiplayerPeer.TransferMode" default="2" />
- <member name="use_dtls" type="bool" setter="set_dtls_enabled" getter="is_dtls_enabled" default="false">
- When enabled, the client or server created by this peer, will use [PacketPeerDTLS] instead of raw UDP sockets for communicating with the remote peer. This will make the communication encrypted with DTLS at the cost of higher resource usage and potentially larger packet size.
- Note: When creating a DTLS server, make sure you setup the key/certificate pair via [method set_dtls_key] and [method set_dtls_certificate]. For DTLS clients, have a look at the [member dtls_verify] option, and configure the certificate accordingly via [method set_dtls_certificate].
- </member>
- </members>
- <constants>
- <constant name="COMPRESS_NONE" value="0" enum="CompressionMode">
- No compression. This uses the most bandwidth, but has the upside of requiring the fewest CPU resources.
- </constant>
- <constant name="COMPRESS_RANGE_CODER" value="1" enum="CompressionMode">
- ENet's built-in range encoding.
- </constant>
- <constant name="COMPRESS_FASTLZ" value="2" enum="CompressionMode">
- [url=http://fastlz.org/]FastLZ[/url] compression. This option uses less CPU resources compared to [constant COMPRESS_ZLIB], at the expense of using more bandwidth.
- </constant>
- <constant name="COMPRESS_ZLIB" value="3" enum="CompressionMode">
- [url=https://www.zlib.net/]Zlib[/url] compression. This option uses less bandwidth compared to [constant COMPRESS_FASTLZ], at the expense of using more CPU resources.
- </constant>
- <constant name="COMPRESS_ZSTD" value="4" enum="CompressionMode">
- [url=https://facebook.github.io/zstd/]Zstandard[/url] compression.
- </constant>
- </constants>
-</class>
diff --git a/modules/enet/enet_connection.cpp b/modules/enet/enet_connection.cpp
new file mode 100644
index 0000000000..e833264d6a
--- /dev/null
+++ b/modules/enet/enet_connection.cpp
@@ -0,0 +1,470 @@
+/*************************************************************************/
+/* enet_connection.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 "enet_connection.h"
+
+#include "enet_packet_peer.h"
+
+#include "core/io/compression.h"
+#include "core/io/ip.h"
+
+void ENetConnection::broadcast(enet_uint8 p_channel, ENetPacket *p_packet) {
+ ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active.");
+ ERR_FAIL_COND_MSG(p_channel >= host->channelLimit, vformat("Unable to send packet on channel %d, max channels: %d", p_channel, (int)host->channelLimit));
+ enet_host_broadcast(host, p_channel, p_packet);
+}
+
+Error ENetConnection::create_host_bound(const IPAddress &p_bind_address, int p_port, int p_max_peers, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) {
+ ERR_FAIL_COND_V_MSG(!p_bind_address.is_valid() && !p_bind_address.is_wildcard(), ERR_INVALID_PARAMETER, "Invalid bind IP.");
+ ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The local port number must be between 0 and 65535 (inclusive).");
+
+ ENetAddress address;
+ memset(&address, 0, sizeof(address));
+ address.port = p_port;
+#ifdef GODOT_ENET
+ if (p_bind_address.is_wildcard()) {
+ address.wildcard = 1;
+ } else {
+ enet_address_set_ip(&address, p_bind_address.get_ipv6(), 16);
+ }
+#else
+ if (p_bind_address.is_wildcard()) {
+ address.host = 0;
+ } else {
+ ERR_FAIL_COND_V(!p_bind_address.is_ipv4(), ERR_INVALID_PARAMETER);
+ address.host = *(uint32_t *)p_bind_address.get_ipv4();
+ }
+#endif
+ return _create(&address, p_max_peers, p_max_channels, p_in_bandwidth, p_out_bandwidth);
+}
+
+Error ENetConnection::create_host(int p_max_peers, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) {
+ return _create(nullptr, p_max_peers, p_max_channels, p_in_bandwidth, p_out_bandwidth);
+}
+
+void ENetConnection::destroy() {
+ ERR_FAIL_COND_MSG(!host, "Host already destroyed");
+ for (List<Ref<ENetPacketPeer>>::Element *E = peers.front(); E; E = E->next()) {
+ E->get()->_on_disconnect();
+ }
+ peers.clear();
+ enet_host_destroy(host);
+ host = nullptr;
+}
+
+Ref<ENetPacketPeer> ENetConnection::connect_to_host(const String &p_address, int p_port, int p_channels, int p_data) {
+ Ref<ENetPacketPeer> out;
+ ERR_FAIL_COND_V_MSG(!host, out, "The ENetConnection instance isn't currently active.");
+ ERR_FAIL_COND_V_MSG(peers.size(), out, "The ENetConnection is already connected to a peer.");
+ ERR_FAIL_COND_V_MSG(p_port < 1 || p_port > 65535, out, "The remote port number must be between 1 and 65535 (inclusive).");
+
+ IPAddress ip;
+ if (p_address.is_valid_ip_address()) {
+ ip = p_address;
+ } else {
+#ifdef GODOT_ENET
+ ip = IP::get_singleton()->resolve_hostname(p_address);
+#else
+ ip = IP::get_singleton()->resolve_hostname(p_address, IP::TYPE_IPV4);
+#endif
+ ERR_FAIL_COND_V_MSG(!ip.is_valid(), out, "Couldn't resolve the server IP address or domain name.");
+ }
+
+ ENetAddress address;
+#ifdef GODOT_ENET
+ enet_address_set_ip(&address, ip.get_ipv6(), 16);
+#else
+ ERR_FAIL_COND_V_MSG(!ip.is_ipv4(), out, "Connecting to an IPv6 server isn't supported when using vanilla ENet. Recompile Godot with the bundled ENet library.");
+ address.host = *(uint32_t *)ip.get_ipv4();
+#endif
+ address.port = p_port;
+
+ // Initiate connection, allocating enough channels
+ ENetPeer *peer = enet_host_connect(host, &address, p_channels > 0 ? p_channels : ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT, p_data);
+
+ if (peer == nullptr) {
+ return nullptr;
+ }
+ out = Ref<ENetPacketPeer>(memnew(ENetPacketPeer(peer)));
+ peers.push_back(out);
+ 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) {
+ case ENET_EVENT_TYPE_CONNECT: {
+ if (event.peer->data == nullptr) {
+ Ref<ENetPacketPeer> pp = memnew(ENetPacketPeer(event.peer));
+ peers.push_back(pp);
+ }
+ r_event.peer = Ref<ENetPacketPeer>((ENetPacketPeer *)event.peer->data);
+ r_event.data = 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);
+ pp->_on_disconnect();
+ peers.erase(pp);
+ r_event.peer = pp;
+ r_event.data = 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;
+ return EVENT_RECEIVE;
+ }
+ return EVENT_ERROR;
+ } break;
+ case ENET_EVENT_TYPE_NONE:
+ return EVENT_NONE;
+ default:
+ return EVENT_NONE;
+ }
+}
+
+void ENetConnection::flush() {
+ ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active.");
+ enet_host_flush(host);
+}
+
+void ENetConnection::bandwidth_limit(int p_in_bandwidth, int p_out_bandwidth) {
+ ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active.");
+ enet_host_bandwidth_limit(host, p_in_bandwidth, p_out_bandwidth);
+}
+
+void ENetConnection::channel_limit(int p_max_channels) {
+ ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active.");
+ enet_host_channel_limit(host, p_max_channels);
+}
+
+void ENetConnection::bandwidth_throttle() {
+ ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active.");
+ enet_host_bandwidth_throttle(host);
+}
+
+void ENetConnection::compress(CompressionMode p_mode) {
+ ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active.");
+ Compressor::setup(host, p_mode);
+}
+
+double ENetConnection::pop_statistic(HostStatistic p_stat) {
+ ERR_FAIL_COND_V_MSG(!host, 0, "The ENetConnection instance isn't currently active.");
+ uint32_t *ptr = nullptr;
+ switch (p_stat) {
+ case HOST_TOTAL_SENT_DATA:
+ ptr = &(host->totalSentData);
+ break;
+ case HOST_TOTAL_SENT_PACKETS:
+ ptr = &(host->totalSentPackets);
+ break;
+ case HOST_TOTAL_RECEIVED_DATA:
+ ptr = &(host->totalReceivedData);
+ break;
+ case HOST_TOTAL_RECEIVED_PACKETS:
+ ptr = &(host->totalReceivedPackets);
+ break;
+ }
+ ERR_FAIL_COND_V_MSG(ptr == nullptr, 0, "Invalid statistic: " + itos(p_stat));
+ uint32_t ret = *ptr;
+ *ptr = 0;
+ return ret;
+}
+
+int ENetConnection::get_max_channels() const {
+ ERR_FAIL_COND_V_MSG(!host, 0, "The ENetConnection instance isn't currently active.");
+ return host->channelLimit;
+}
+
+int ENetConnection::get_local_port() const {
+ ERR_FAIL_COND_V_MSG(!host, 0, "The ENetConnection instance isn't currently active.");
+ ERR_FAIL_COND_V_MSG(!(host->socket), 0, "The ENetConnection instance isn't currently bound");
+ ENetAddress address;
+ ERR_FAIL_COND_V_MSG(enet_socket_get_address(host->socket, &address), 0, "Unable to get socket address");
+ return address.port;
+}
+
+void ENetConnection::get_peers(List<Ref<ENetPacketPeer>> &r_peers) {
+ for (const Ref<ENetPacketPeer> &I : peers) {
+ r_peers.push_back(I);
+ }
+}
+
+Array ENetConnection::_get_peers() {
+ ERR_FAIL_COND_V_MSG(!host, Array(), "The ENetConnection instance isn't currently active.");
+ Array out;
+ for (const Ref<ENetPacketPeer> &I : peers) {
+ out.push_back(I);
+ }
+ return out;
+}
+
+Error ENetConnection::dtls_server_setup(Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert) {
+#ifdef GODOT_ENET
+ ERR_FAIL_COND_V_MSG(!host, ERR_UNCONFIGURED, "The ENetConnection instance isn't currently active.");
+ return enet_host_dtls_server_setup(host, p_key.ptr(), p_cert.ptr()) ? FAILED : OK;
+#else
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "ENet DTLS support not available in this build.");
+#endif
+}
+
+void ENetConnection::refuse_new_connections(bool p_refuse) {
+#ifdef GODOT_ENET
+ ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active.");
+ enet_host_refuse_new_connections(host, p_refuse);
+#else
+ ERR_FAIL_MSG("ENet DTLS support not available in this build.");
+#endif
+}
+
+Error ENetConnection::dtls_client_setup(Ref<X509Certificate> p_cert, const String &p_hostname, bool p_verify) {
+#ifdef GODOT_ENET
+ ERR_FAIL_COND_V_MSG(!host, ERR_UNCONFIGURED, "The ENetConnection instance isn't currently active.");
+ return enet_host_dtls_client_setup(host, p_cert.ptr(), p_verify, p_hostname.utf8().get_data()) ? FAILED : OK;
+#else
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "ENet DTLS support not available in this build.");
+#endif
+}
+
+Error ENetConnection::_create(ENetAddress *p_address, int p_max_peers, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth) {
+ ERR_FAIL_COND_V_MSG(host != nullptr, ERR_ALREADY_IN_USE, "The ENetConnection instance is already active.");
+ ERR_FAIL_COND_V_MSG(p_max_peers < 1 || p_max_peers > 4095, ERR_INVALID_PARAMETER, "The number of clients must be set between 1 and 4095 (inclusive).");
+ ERR_FAIL_COND_V_MSG(p_max_channels < 0 || p_max_channels > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT, ERR_INVALID_PARAMETER, "Invalid channel count. Must be between 0 and 255 (0 means maximum, i.e. 255)");
+ ERR_FAIL_COND_V_MSG(p_in_bandwidth < 0, ERR_INVALID_PARAMETER, "The incoming bandwidth limit must be greater than or equal to 0 (0 disables the limit).");
+ ERR_FAIL_COND_V_MSG(p_out_bandwidth < 0, ERR_INVALID_PARAMETER, "The outgoing bandwidth limit must be greater than or equal to 0 (0 disables the limit).");
+
+ host = enet_host_create(p_address /* the address to bind the server host to */,
+ p_max_peers /* allow up to p_max_peers connections */,
+ p_max_channels /* allow up to p_max_channel to be used */,
+ p_in_bandwidth /* limit incoming bandwidth if > 0 */,
+ p_out_bandwidth /* limit outgoing bandwidth if > 0 */);
+
+ ERR_FAIL_COND_V_MSG(!host, ERR_CANT_CREATE, "Couldn't create an ENet host.");
+ return OK;
+}
+
+Array ENetConnection::_service(int p_timeout) {
+ Array out;
+ Event event;
+ Ref<ENetPacketPeer> peer;
+ EventType ret = service(p_timeout, event);
+ out.push_back(ret);
+ out.push_back(event.peer);
+ out.push_back(event.data);
+ out.push_back(event.channel_id);
+ if (event.packet && event.peer.is_valid()) {
+ event.peer->_queue_packet(event.packet);
+ }
+ return out;
+}
+
+void ENetConnection::_broadcast(int p_channel, PackedByteArray p_packet, int p_flags) {
+ ERR_FAIL_COND_MSG(!host, "The ENetConnection instance isn't currently active.");
+ ERR_FAIL_COND_MSG(p_channel < 0 || p_channel > (int)host->channelLimit, "Invalid channel");
+ ERR_FAIL_COND_MSG(p_flags & ~ENetPacketPeer::FLAG_ALLOWED, "Invalid flags");
+ ENetPacket *pkt = enet_packet_create(p_packet.ptr(), p_packet.size(), p_flags);
+ broadcast(p_channel, pkt);
+}
+
+void ENetConnection::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create_host_bound", "bind_address", "bind_port", "max_peers", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetConnection::create_host_bound, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("create_host", "max_peers", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetConnection::create_host, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("destroy"), &ENetConnection::destroy);
+ ClassDB::bind_method(D_METHOD("connect_to_host", "address", "port", "channels", "data"), &ENetConnection::connect_to_host, DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("service", "timeout"), &ENetConnection::_service, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("flush"), &ENetConnection::flush);
+ ClassDB::bind_method(D_METHOD("bandwidth_limit", "in_bandwidth", "out_bandwidth"), &ENetConnection::bandwidth_limit, DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("channel_limit", "limit"), &ENetConnection::channel_limit);
+ ClassDB::bind_method(D_METHOD("broadcast", "channel", "packet", "flags"), &ENetConnection::_broadcast);
+ ClassDB::bind_method(D_METHOD("compress", "mode"), &ENetConnection::compress);
+ ClassDB::bind_method(D_METHOD("dtls_server_setup", "key", "certificate"), &ENetConnection::dtls_server_setup);
+ ClassDB::bind_method(D_METHOD("dtls_client_setup", "certificate", "hostname", "verify"), &ENetConnection::dtls_client_setup, DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("refuse_new_connections", "refuse"), &ENetConnection::refuse_new_connections);
+ ClassDB::bind_method(D_METHOD("pop_statistic", "statistic"), &ENetConnection::pop_statistic);
+ ClassDB::bind_method(D_METHOD("get_max_channels"), &ENetConnection::get_max_channels);
+ ClassDB::bind_method(D_METHOD("get_local_port"), &ENetConnection::get_local_port);
+ ClassDB::bind_method(D_METHOD("get_peers"), &ENetConnection::_get_peers);
+
+ BIND_ENUM_CONSTANT(COMPRESS_NONE);
+ BIND_ENUM_CONSTANT(COMPRESS_RANGE_CODER);
+ BIND_ENUM_CONSTANT(COMPRESS_FASTLZ);
+ BIND_ENUM_CONSTANT(COMPRESS_ZLIB);
+ BIND_ENUM_CONSTANT(COMPRESS_ZSTD);
+
+ BIND_ENUM_CONSTANT(EVENT_ERROR);
+ BIND_ENUM_CONSTANT(EVENT_NONE);
+ BIND_ENUM_CONSTANT(EVENT_CONNECT);
+ BIND_ENUM_CONSTANT(EVENT_DISCONNECT);
+ BIND_ENUM_CONSTANT(EVENT_RECEIVE);
+
+ BIND_ENUM_CONSTANT(HOST_TOTAL_SENT_DATA);
+ BIND_ENUM_CONSTANT(HOST_TOTAL_SENT_PACKETS);
+ BIND_ENUM_CONSTANT(HOST_TOTAL_RECEIVED_DATA);
+ BIND_ENUM_CONSTANT(HOST_TOTAL_RECEIVED_PACKETS);
+}
+
+ENetConnection::~ENetConnection() {
+ if (host) {
+ destroy();
+ }
+}
+
+size_t ENetConnection::Compressor::enet_compress(void *context, const ENetBuffer *inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 *outData, size_t outLimit) {
+ Compressor *compressor = (Compressor *)(context);
+
+ if (size_t(compressor->src_mem.size()) < inLimit) {
+ compressor->src_mem.resize(inLimit);
+ }
+
+ int total = inLimit;
+ int ofs = 0;
+ while (total) {
+ for (size_t i = 0; i < inBufferCount; i++) {
+ int to_copy = MIN(total, int(inBuffers[i].dataLength));
+ memcpy(&compressor->src_mem.write[ofs], inBuffers[i].data, to_copy);
+ ofs += to_copy;
+ total -= to_copy;
+ }
+ }
+
+ Compression::Mode mode;
+
+ switch (compressor->mode) {
+ case COMPRESS_FASTLZ: {
+ mode = Compression::MODE_FASTLZ;
+ } break;
+ case COMPRESS_ZLIB: {
+ mode = Compression::MODE_DEFLATE;
+ } break;
+ case COMPRESS_ZSTD: {
+ mode = Compression::MODE_ZSTD;
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(0, vformat("Invalid ENet compression mode: %d", compressor->mode));
+ }
+ }
+
+ int req_size = Compression::get_max_compressed_buffer_size(ofs, mode);
+ if (compressor->dst_mem.size() < req_size) {
+ compressor->dst_mem.resize(req_size);
+ }
+ int ret = Compression::compress(compressor->dst_mem.ptrw(), compressor->src_mem.ptr(), ofs, mode);
+
+ if (ret < 0) {
+ return 0;
+ }
+
+ if (ret > int(outLimit)) {
+ return 0; // Do not bother
+ }
+
+ memcpy(outData, compressor->dst_mem.ptr(), ret);
+
+ return ret;
+}
+
+size_t ENetConnection::Compressor::enet_decompress(void *context, const enet_uint8 *inData, size_t inLimit, enet_uint8 *outData, size_t outLimit) {
+ Compressor *compressor = (Compressor *)(context);
+ int ret = -1;
+ switch (compressor->mode) {
+ case COMPRESS_FASTLZ: {
+ ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_FASTLZ);
+ } break;
+ case COMPRESS_ZLIB: {
+ ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_DEFLATE);
+ } break;
+ case COMPRESS_ZSTD: {
+ ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_ZSTD);
+ } break;
+ default: {
+ }
+ }
+ if (ret < 0) {
+ return 0;
+ } else {
+ return ret;
+ }
+}
+
+void ENetConnection::Compressor::setup(ENetHost *p_host, CompressionMode p_mode) {
+ ERR_FAIL_COND(!p_host);
+ switch (p_mode) {
+ case COMPRESS_NONE: {
+ enet_host_compress(p_host, nullptr);
+ } break;
+ case COMPRESS_RANGE_CODER: {
+ enet_host_compress_with_range_coder(p_host);
+ } break;
+ case COMPRESS_FASTLZ:
+ case COMPRESS_ZLIB:
+ case COMPRESS_ZSTD: {
+ Compressor *compressor = memnew(Compressor(p_mode));
+ enet_host_compress(p_host, &(compressor->enet_compressor));
+ } break;
+ }
+}
+
+ENetConnection::Compressor::Compressor(CompressionMode p_mode) {
+ mode = p_mode;
+ enet_compressor.context = this;
+ enet_compressor.compress = enet_compress;
+ enet_compressor.decompress = enet_decompress;
+ enet_compressor.destroy = enet_compressor_destroy;
+}
diff --git a/modules/enet/enet_connection.h b/modules/enet/enet_connection.h
new file mode 100644
index 0000000000..0f7744953e
--- /dev/null
+++ b/modules/enet/enet_connection.h
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* enet_connection.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 ENET_CONNECTION_H
+#define ENET_CONNECTION_H
+
+#include "core/object/ref_counted.h"
+
+#include "core/crypto/crypto.h"
+#include "enet_packet_peer.h"
+
+#include <enet/enet.h>
+
+class ENetConnection : public RefCounted {
+ GDCLASS(ENetConnection, RefCounted);
+
+public:
+ enum CompressionMode {
+ COMPRESS_NONE = 0,
+ COMPRESS_RANGE_CODER,
+ COMPRESS_FASTLZ,
+ COMPRESS_ZLIB,
+ COMPRESS_ZSTD,
+ };
+
+ enum HostStatistic {
+ HOST_TOTAL_SENT_DATA,
+ HOST_TOTAL_SENT_PACKETS,
+ HOST_TOTAL_RECEIVED_DATA,
+ HOST_TOTAL_RECEIVED_PACKETS,
+ };
+
+ enum EventType {
+ EVENT_ERROR = -1,
+ EVENT_NONE = 0,
+ EVENT_CONNECT,
+ EVENT_DISCONNECT,
+ EVENT_RECEIVE,
+ };
+
+ struct Event {
+ Ref<ENetPacketPeer> peer;
+ enet_uint8 channel_id = 0;
+ enet_uint32 data = 0;
+ ENetPacket *packet = nullptr;
+ };
+
+protected:
+ static void _bind_methods();
+
+private:
+ ENetHost *host = nullptr;
+ List<Ref<ENetPacketPeer>> peers;
+
+ 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);
+ Array _get_peers();
+
+ class Compressor {
+ private:
+ CompressionMode mode = COMPRESS_NONE;
+ Vector<uint8_t> src_mem;
+ Vector<uint8_t> dst_mem;
+ ENetCompressor enet_compressor;
+
+ Compressor(CompressionMode mode);
+
+ static size_t enet_compress(void *context, const ENetBuffer *inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 *outData, size_t outLimit);
+ static size_t enet_decompress(void *context, const enet_uint8 *inData, size_t inLimit, enet_uint8 *outData, size_t outLimit);
+ static void enet_compressor_destroy(void *context) {
+ memdelete((Compressor *)context);
+ }
+
+ public:
+ static void setup(ENetHost *p_host, CompressionMode p_mode);
+ };
+
+public:
+ void broadcast(enet_uint8 p_channel, ENetPacket *p_packet);
+ Error create_host_bound(const IPAddress &p_bind_address = IPAddress("*"), int p_port = 0, int p_max_peers = 32, int p_max_channels = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
+ Error create_host(int p_max_peers = 32, int p_max_channels = 0, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
+ 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);
+ void flush();
+ void bandwidth_limit(int p_in_bandwidth = 0, int p_out_bandwidth = 0);
+ void channel_limit(int p_max_channels);
+ void bandwidth_throttle();
+ void compress(CompressionMode p_mode);
+ double pop_statistic(HostStatistic p_stat);
+ int get_max_channels() const;
+
+ // Extras
+ void get_peers(List<Ref<ENetPacketPeer>> &r_peers);
+ int get_local_port() const;
+
+ // Godot additions
+ Error dtls_server_setup(Ref<CryptoKey> p_key, Ref<X509Certificate> p_cert);
+ Error dtls_client_setup(Ref<X509Certificate> p_cert, const String &p_hostname, bool p_verify = true);
+ void refuse_new_connections(bool p_refuse);
+
+ ENetConnection() {}
+ ~ENetConnection();
+};
+
+VARIANT_ENUM_CAST(ENetConnection::CompressionMode);
+VARIANT_ENUM_CAST(ENetConnection::EventType);
+VARIANT_ENUM_CAST(ENetConnection::HostStatistic);
+
+#endif // ENET_CONNECTION_H
diff --git a/modules/enet/enet_multiplayer_peer.cpp b/modules/enet/enet_multiplayer_peer.cpp
new file mode 100644
index 0000000000..2cfae60ad2
--- /dev/null
+++ b/modules/enet/enet_multiplayer_peer.cpp
@@ -0,0 +1,670 @@
+/*************************************************************************/
+/* enet_multiplayer_peer.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 "enet_multiplayer_peer.h"
+#include "core/io/ip.h"
+#include "core/io/marshalls.h"
+#include "core/os/os.h"
+
+void ENetMultiplayerPeer::set_target_peer(int p_peer) {
+ target_peer = p_peer;
+}
+
+int ENetMultiplayerPeer::get_packet_peer() const {
+ ERR_FAIL_COND_V_MSG(!_is_active(), 1, "The multiplayer instance isn't currently active.");
+ ERR_FAIL_COND_V(incoming_packets.size() == 0, 1);
+
+ return incoming_packets.front()->get().from;
+}
+
+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);
+ if (err != OK) {
+ return err;
+ }
+
+ active_mode = MODE_SERVER;
+ unique_id = 1;
+ connection_status = CONNECTION_CONNECTED;
+ hosts[0] = host;
+ return OK;
+}
+
+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;
+ if (p_local_port) {
+ err = host->create_host_bound(bind_ip, p_local_port, 1, 0, p_in_bandwidth, p_out_bandwidth);
+ } else {
+ err = host->create_host(1, 0, p_in_bandwidth, p_out_bandwidth);
+ }
+ if (err != OK) {
+ return err;
+ }
+
+ unique_id = generate_unique_id();
+
+ Ref<ENetPacketPeer> peer = host->connect_to_host(p_address, p_port, p_channel_count > 0 ? p_channel_count + SYSCH_MAX : 0, unique_id);
+ if (peer.is_null()) {
+ host->destroy();
+ ERR_FAIL_V_MSG(ERR_CANT_CREATE, "Couldn't connect to the ENet multiplayer server.");
+ }
+
+ // Need to wait for CONNECT event.
+ connection_status = CONNECTION_CONNECTING;
+ active_mode = MODE_CLIENT;
+ peers[1] = peer;
+ hosts[0] = host;
+
+ return OK;
+}
+
+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;
+ unique_id = p_id;
+ connection_status = CONNECTION_CONNECTED;
+ return OK;
+}
+
+Error ENetMultiplayerPeer::add_mesh_peer(int p_id, Ref<ENetConnection> p_host) {
+ ERR_FAIL_COND_V(p_host.is_null(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V_MSG(active_mode != MODE_MESH, ERR_UNCONFIGURED, "The multiplayer instance is not configured as a mesh. Call 'create_mesh' first.");
+ List<Ref<ENetPacketPeer>> host_peers;
+ p_host->get_peers(host_peers);
+ ERR_FAIL_COND_V_MSG(host_peers.size() != 1 || host_peers[0]->get_state() != ENetPacketPeer::STATE_CONNECTED, ERR_INVALID_PARAMETER, "The provided host must have excatly one peer in the connected state.");
+ hosts[p_id] = p_host;
+ peers[p_id] = host_peers[0];
+ emit_signal(SNAME("peer_connected"), p_id);
+ 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) {
+ case ENetConnection::EVENT_CONNECT: {
+ if (is_refusing_new_connections()) {
+ 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();
+ return false;
+ }
+ int id = event.data;
+ event.peer->set_meta(SNAME("_net_id"), id);
+ peers[id] = event.peer;
+
+ emit_signal(SNAME("peer_connected"), id);
+ if (server_relay) {
+ _notify_peers(id, true);
+ }
+ return false;
+ }
+ case ENetConnection::EVENT_DISCONNECT: {
+ int id = event.peer->get_meta(SNAME("_net_id"));
+ if (!peers.has(id)) {
+ // Never fully connected.
+ return false;
+ }
+
+ emit_signal(SNAME("peer_disconnected"), id);
+ peers.erase(id);
+ if (server_relay) {
+ _notify_peers(id, false);
+ }
+ return false;
+ }
+ case ENetConnection::EVENT_RECEIVE: {
+ if (event.channel_id == SYSCH_CONFIG) {
+ _destroy_unused(event.packet);
+ ERR_FAIL_V_MSG(false, "Only server can send config messages");
+ } else {
+ if (event.packet->dataLength < 8) {
+ _destroy_unused(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 id = event.peer->get_meta(SNAME("_net_id"));
+ // Someone is cheating and trying to fake the source!
+ if (source != id) {
+ _destroy_unused(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.from = id;
+
+ // Even if relaying is disabled, these targets are valid as incoming packets.
+ if (target == 1 || target == 0 || target < -1) {
+ packet.packet->referenceCount++;
+ incoming_packets.push_back(packet);
+ }
+
+ if (server_relay && target != 1) {
+ packet.packet->referenceCount++;
+ _relay(source, target, event.channel_id, event.packet);
+ packet.packet->referenceCount--;
+ _destroy_unused(event.packet);
+ }
+ // Destroy packet later
+ }
+ return false;
+ }
+ default:
+ return true;
+ }
+}
+
+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) {
+ case ENetConnection::EVENT_CONNECT: {
+ connection_status = CONNECTION_CONNECTED;
+ emit_signal(SNAME("peer_connected"), 1);
+ emit_signal(SNAME("connection_succeeded"));
+ return false;
+ }
+ case ENetConnection::EVENT_DISCONNECT: {
+ 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;
+ }
+ case ENetConnection::EVENT_RECEIVE: {
+ if (event.channel_id == SYSCH_CONFIG) {
+ // Config message
+ if (event.packet->dataLength != 8) {
+ _destroy_unused(event.packet);
+ ERR_FAIL_V(false);
+ }
+
+ int msg = decode_uint32(&event.packet->data[0]);
+ int id = decode_uint32(&event.packet->data[4]);
+
+ switch (msg) {
+ case SYSMSG_ADD_PEER: {
+ peers[id] = Ref<ENetPacketPeer>();
+ emit_signal(SNAME("peer_connected"), id);
+
+ } break;
+ case SYSMSG_REMOVE_PEER: {
+ peers.erase(id);
+ emit_signal(SNAME("peer_disconnected"), id);
+ } break;
+ }
+ _destroy_unused(event.packet);
+ } else {
+ if (event.packet->dataLength < 8) {
+ _destroy_unused(event.packet);
+ ERR_FAIL_V_MSG(false, "Invalid packet size");
+ }
+
+ uint32_t source = decode_uint32(&event.packet->data[0]);
+ Packet packet;
+ packet.packet = event.packet;
+ packet.from = source;
+ packet.channel = event.channel_id;
+
+ packet.packet->referenceCount++;
+ incoming_packets.push_back(packet);
+ // Destroy packet later
+ }
+ return false;
+ }
+ default:
+ return true;
+ }
+}
+
+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 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(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->referenceCount++;
+ incoming_packets.push_back(packet);
+ } break;
+ default:
+ break; // Nothing to do
+ }
+ }
+ return should_stop;
+}
+
+void ENetMultiplayerPeer::poll() {
+ ERR_FAIL_COND_MSG(!_is_active(), "The multiplayer instance isn't currently active.");
+
+ _pop_current_packet();
+
+ while (true) {
+ switch (active_mode) {
+ case MODE_CLIENT:
+ if (_poll_client()) {
+ return;
+ }
+ break;
+ case MODE_SERVER:
+ if (_poll_server()) {
+ return;
+ }
+ break;
+ case MODE_MESH:
+ if (_poll_mesh()) {
+ return;
+ }
+ break;
+ default:
+ return;
+ }
+ }
+}
+
+bool ENetMultiplayerPeer::is_server() const {
+ return active_mode == MODE_SERVER;
+}
+
+void ENetMultiplayerPeer::close_connection(uint32_t wait_usec) {
+ if (!_is_active()) {
+ return;
+ }
+
+ _pop_current_packet();
+
+ bool peers_disconnected = false;
+ for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
+ if (E.value.is_valid() && E.value->get_state() == ENetPacketPeer::STATE_CONNECTED) {
+ E.value->peer_disconnect_now(unique_id);
+ peers_disconnected = true;
+ }
+ }
+
+ if (peers_disconnected) {
+ for (KeyValue<int, Ref<ENetConnection>> &E : hosts) {
+ E.value->flush();
+ }
+
+ if (wait_usec > 0) {
+ OS::get_singleton()->delay_usec(wait_usec); // Wait for disconnection packets to send
+ }
+ }
+
+ active_mode = MODE_NONE;
+ incoming_packets.clear();
+ peers.clear();
+ hosts.clear();
+ unique_id = 0;
+ connection_status = CONNECTION_DISCONNECTED;
+ set_refuse_new_connections(false);
+}
+
+int ENetMultiplayerPeer::get_available_packet_count() const {
+ return incoming_packets.size();
+}
+
+Error ENetMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ ERR_FAIL_COND_V_MSG(incoming_packets.size() == 0, ERR_UNAVAILABLE, "No incoming packets available.");
+
+ _pop_current_packet();
+
+ current_packet = incoming_packets.front()->get();
+ incoming_packets.pop_front();
+
+ *r_buffer = (const uint8_t *)(&current_packet.packet->data[8]);
+ r_buffer_size = current_packet.packet->dataLength - 8;
+
+ return OK;
+}
+
+Error ENetMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ ERR_FAIL_COND_V_MSG(!_is_active(), ERR_UNCONFIGURED, "The multiplayer instance isn't currently active.");
+ ERR_FAIL_COND_V_MSG(connection_status != CONNECTION_CONNECTED, ERR_UNCONFIGURED, "The multiplayer instance isn't currently connected to any server or client.");
+ ERR_FAIL_COND_V_MSG(target_peer != 0 && !peers.has(ABS(target_peer)), ERR_INVALID_PARAMETER, vformat("Invalid target peer: %d", target_peer));
+ ERR_FAIL_COND_V(active_mode == MODE_CLIENT && !peers.has(1), ERR_BUG);
+
+ 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 (get_transfer_mode()) {
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE: {
+ packet_flags = ENET_PACKET_FLAG_UNSEQUENCED;
+ channel = SYSCH_UNRELIABLE;
+ } break;
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED: {
+ packet_flags = 0;
+ channel = SYSCH_UNRELIABLE;
+ } break;
+ case Multiplayer::TRANSFER_MODE_RELIABLE: {
+ packet_flags = ENET_PACKET_FLAG_RELIABLE;
+ channel = SYSCH_RELIABLE;
+ } break;
+ }
+ }
+
+ 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
+ memcpy(&packet->data[8], p_buffer, p_buffer_size);
+
+ if (is_server()) {
+ if (target_peer == 0) {
+ hosts[0]->broadcast(channel, packet);
+
+ } else if (target_peer < 0) {
+ // Send to all but one and make copies for sending.
+ int exclude = -target_peer;
+ for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
+ if (E.key == exclude) {
+ continue;
+ }
+ E.value->send(channel, packet);
+ }
+ _destroy_unused(packet);
+ } else {
+ peers[target_peer]->send(channel, packet);
+ }
+ ERR_FAIL_COND_V(!hosts.has(0), ERR_BUG);
+ hosts[0]->flush();
+
+ } else if (active_mode == MODE_CLIENT) {
+ peers[1]->send(channel, packet); // Send to server for broadcast.
+ ERR_FAIL_COND_V(!hosts.has(0), ERR_BUG);
+ hosts[0]->flush();
+
+ } else {
+ if (target_peer <= 0) {
+ int exclude = ABS(target_peer);
+ for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
+ if (E.key == exclude) {
+ continue;
+ }
+ E.value->send(channel, packet);
+ ERR_CONTINUE(!hosts.has(E.key));
+ hosts[E.key]->flush();
+ }
+ _destroy_unused(packet);
+ } else {
+ peers[target_peer]->send(channel, packet);
+ ERR_FAIL_COND_V(!hosts.has(target_peer), ERR_BUG);
+ hosts[target_peer]->flush();
+ }
+ }
+
+ return OK;
+}
+
+int ENetMultiplayerPeer::get_max_packet_size() const {
+ return 1 << 24; // Anything is good
+}
+
+void ENetMultiplayerPeer::_pop_current_packet() {
+ if (current_packet.packet) {
+ current_packet.packet->referenceCount--;
+ _destroy_unused(current_packet.packet);
+ current_packet.packet = nullptr;
+ current_packet.from = 0;
+ current_packet.channel = -1;
+ }
+}
+
+MultiplayerPeer::ConnectionStatus ENetMultiplayerPeer::get_connection_status() const {
+ return connection_status;
+}
+
+int ENetMultiplayerPeer::get_unique_id() const {
+ ERR_FAIL_COND_V_MSG(!_is_active(), 0, "The multiplayer instance isn't currently active.");
+ return unique_id;
+}
+
+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_enabled);
+ }
+ }
+#endif
+ MultiplayerPeer::set_refuse_new_connections(p_enabled);
+}
+
+void ENetMultiplayerPeer::set_server_relay_enabled(bool p_enabled) {
+ ERR_FAIL_COND_MSG(_is_active(), "Server relaying can't be toggled while the multiplayer instance is active.");
+
+ server_relay = p_enabled;
+}
+
+bool ENetMultiplayerPeer::is_server_relay_enabled() const {
+ return server_relay;
+}
+
+Ref<ENetConnection> ENetMultiplayerPeer::get_host() const {
+ ERR_FAIL_COND_V(!_is_active(), nullptr);
+ ERR_FAIL_COND_V(active_mode == MODE_MESH, nullptr);
+ return hosts[0];
+}
+
+Ref<ENetPacketPeer> ENetMultiplayerPeer::get_peer(int p_id) const {
+ ERR_FAIL_COND_V(!_is_active(), nullptr);
+ ERR_FAIL_COND_V(!peers.has(p_id), nullptr);
+ ERR_FAIL_COND_V(active_mode == MODE_CLIENT && p_id != 1, nullptr);
+ return peers[p_id];
+}
+
+void ENetMultiplayerPeer::_destroy_unused(ENetPacket *p_packet) {
+ if (p_packet->referenceCount == 0) {
+ enet_packet_destroy(p_packet);
+ }
+}
+
+void ENetMultiplayerPeer::_relay(int p_from, int p_to, enet_uint8 p_channel, ENetPacket *p_packet) {
+ if (p_to == 0) {
+ // Re-send to everyone but sender :|
+ for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
+ if (E.key == p_from) {
+ continue;
+ }
+
+ E.value->send(p_channel, p_packet);
+ }
+ } else if (p_to < 0) {
+ // Re-send to everyone but excluded and sender.
+ for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
+ if (E.key == p_from || E.key == -p_to) { // Do not resend to self, also do not send to excluded
+ continue;
+ }
+
+ E.value->send(p_channel, p_packet);
+ }
+ } else {
+ // To someone else, specifically
+ ERR_FAIL_COND(!peers.has(p_to));
+ ENetPacket *packet = enet_packet_create(p_packet->data, p_packet->dataLength, p_packet->flags);
+ peers[p_to]->send(p_channel, packet);
+ }
+}
+
+void ENetMultiplayerPeer::_notify_peers(int p_id, bool p_connected) {
+ if (p_connected) {
+ ERR_FAIL_COND(!peers.has(p_id));
+ // Someone connected, notify all the peers available.
+ Ref<ENetPacketPeer> peer = peers[p_id];
+ ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
+ encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
+ encode_uint32(p_id, &packet->data[4]);
+ for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
+ if (E.key == p_id) {
+ continue;
+ }
+ // Send new peer to existing peer.
+ E.value->send(SYSCH_CONFIG, packet);
+ // Send existing peer to new peer.
+ // This packet will be automatically destroyed by ENet after send.
+ ENetPacket *packet2 = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
+ encode_uint32(SYSMSG_ADD_PEER, &packet2->data[0]);
+ encode_uint32(E.key, &packet2->data[4]);
+ peer->send(SYSCH_CONFIG, packet2);
+ }
+ _destroy_unused(packet);
+ } else {
+ // Server just received a client disconnect and is in relay mode, notify everyone else.
+ ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
+ encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
+ encode_uint32(p_id, &packet->data[4]);
+ for (KeyValue<int, Ref<ENetPacketPeer>> &E : peers) {
+ if (E.key == p_id) {
+ continue;
+ }
+ E.value->send(SYSCH_CONFIG, packet);
+ }
+ _destroy_unused(packet);
+ }
+}
+
+void ENetMultiplayerPeer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "max_channels", "in_bandwidth", "out_bandwidth"), &ENetMultiplayerPeer::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("create_client", "address", "port", "channel_count", "in_bandwidth", "out_bandwidth", "local_port"), &ENetMultiplayerPeer::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("create_mesh", "unique_id"), &ENetMultiplayerPeer::create_mesh);
+ ClassDB::bind_method(D_METHOD("add_mesh_peer", "peer_id", "host"), &ENetMultiplayerPeer::add_mesh_peer);
+ ClassDB::bind_method(D_METHOD("close_connection", "wait_usec"), &ENetMultiplayerPeer::close_connection, DEFVAL(100));
+ ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &ENetMultiplayerPeer::set_bind_ip);
+
+ ClassDB::bind_method(D_METHOD("set_server_relay_enabled", "enabled"), &ENetMultiplayerPeer::set_server_relay_enabled);
+ ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &ENetMultiplayerPeer::is_server_relay_enabled);
+ ClassDB::bind_method(D_METHOD("get_host"), &ENetMultiplayerPeer::get_host);
+ ClassDB::bind_method(D_METHOD("get_peer", "id"), &ENetMultiplayerPeer::get_peer);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "host", PROPERTY_HINT_RESOURCE_TYPE, "ENetConnection", PROPERTY_USAGE_NONE), "", "get_host");
+}
+
+ENetMultiplayerPeer::ENetMultiplayerPeer() {
+ bind_ip = IPAddress("*");
+}
+
+ENetMultiplayerPeer::~ENetMultiplayerPeer() {
+ if (_is_active()) {
+ close_connection();
+ }
+}
+
+// Sets IP for ENet to bind when using create_server or create_client
+// if no IP is set, then ENet bind to ENET_HOST_ANY
+void ENetMultiplayerPeer::set_bind_ip(const IPAddress &p_ip) {
+ ERR_FAIL_COND_MSG(!p_ip.is_valid() && !p_ip.is_wildcard(), vformat("Invalid bind IP address: %s", String(p_ip)));
+
+ bind_ip = p_ip;
+}
diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/enet_multiplayer_peer.h
index 722c7001fd..7a60e2359c 100644
--- a/modules/enet/networked_multiplayer_enet.h
+++ b/modules/enet/enet_multiplayer_peer.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* networked_multiplayer_enet.h */
+/* enet_multiplayer_peer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,22 +32,13 @@
#define NETWORKED_MULTIPLAYER_ENET_H
#include "core/crypto/crypto.h"
-#include "core/io/compression.h"
-#include "core/io/networked_multiplayer_peer.h"
+#include "core/multiplayer/multiplayer_peer.h"
+#include "enet_connection.h"
#include <enet/enet.h>
-class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer {
- GDCLASS(NetworkedMultiplayerENet, NetworkedMultiplayerPeer);
-
-public:
- enum CompressionMode {
- COMPRESS_NONE,
- COMPRESS_RANGE_CODER,
- COMPRESS_FASTLZ,
- COMPRESS_ZLIB,
- COMPRESS_ZSTD
- };
+class ENetMultiplayerPeer : public MultiplayerPeer {
+ GDCLASS(ENetMultiplayerPeer, MultiplayerPeer);
private:
enum {
@@ -56,128 +47,92 @@ private:
};
enum {
- SYSCH_CONFIG,
- SYSCH_RELIABLE,
- SYSCH_UNRELIABLE,
- SYSCH_MAX
+ SYSCH_CONFIG = 0,
+ SYSCH_RELIABLE = 1,
+ SYSCH_UNRELIABLE = 2,
+ SYSCH_MAX = 3
};
- bool active;
- bool server;
+ enum Mode {
+ MODE_NONE,
+ MODE_SERVER,
+ MODE_CLIENT,
+ MODE_MESH,
+ };
- uint32_t unique_id;
+ Mode active_mode = MODE_NONE;
- int target_peer;
- TransferMode transfer_mode;
- int transfer_channel;
- int channel_count;
- bool always_ordered;
+ uint32_t unique_id = 0;
- ENetEvent event;
- ENetPeer *peer;
- ENetHost *host;
+ int target_peer = 0;
- bool refuse_connections;
- bool server_relay;
+ bool server_relay = true;
- ConnectionStatus connection_status;
+ ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
- Map<int, ENetPeer *> peer_map;
+ Map<int, Ref<ENetConnection>> hosts;
+ Map<int, Ref<ENetPacketPeer>> peers;
struct Packet {
- ENetPacket *packet;
- int from;
- int channel;
+ ENetPacket *packet = nullptr;
+ int from = 0;
+ int channel = 0;
};
- CompressionMode compression_mode;
-
List<Packet> incoming_packets;
Packet current_packet;
- uint32_t _gen_unique_id() const;
void _pop_current_packet();
+ bool _poll_server();
+ bool _poll_client();
+ bool _poll_mesh();
+ 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);
+ _FORCE_INLINE_ bool _is_active() const { return active_mode != MODE_NONE; }
- Vector<uint8_t> src_compressor_mem;
- Vector<uint8_t> dst_compressor_mem;
-
- ENetCompressor enet_compressor;
- static size_t enet_compress(void *context, const ENetBuffer *inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 *outData, size_t outLimit);
- static size_t enet_decompress(void *context, const enet_uint8 *inData, size_t inLimit, enet_uint8 *outData, size_t outLimit);
- static void enet_compressor_destroy(void *context);
- void _setup_compressor();
-
- IP_Address bind_ip;
-
- bool dtls_enabled;
- Ref<CryptoKey> dtls_key;
- Ref<X509Certificate> dtls_cert;
- bool dtls_verify;
+ IPAddress bind_ip;
protected:
static void _bind_methods();
public:
- 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;
- virtual IP_Address get_peer_address(int p_peer_id) const;
- virtual int get_peer_port(int p_peer_id) const;
-
- Error create_server(int p_port, int p_max_clients = 32, int p_in_bandwidth = 0, int p_out_bandwidth = 0);
- Error create_client(const String &p_address, int p_port, int p_in_bandwidth = 0, int p_out_bandwidth = 0, int p_client_port = 0);
-
- 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_compression_mode(CompressionMode p_mode);
- CompressionMode get_compression_mode() const;
-
- int get_packet_channel() const;
- int get_last_packet_channel() const;
- void set_transfer_channel(int p_channel);
- int get_transfer_channel() const;
- void set_channel_count(int p_channel);
- int get_channel_count() const;
- void set_always_ordered(bool p_ordered);
- bool is_always_ordered() const;
+ void set_bind_ip(const IPAddress &p_ip);
void set_server_relay_enabled(bool p_enabled);
bool is_server_relay_enabled() const;
- NetworkedMultiplayerENet();
- ~NetworkedMultiplayerENet();
+ Ref<ENetConnection> get_host() const;
+ Ref<ENetPacketPeer> get_peer(int p_id) const;
- void set_bind_ip(const IP_Address &p_ip);
- void set_dtls_enabled(bool p_enabled);
- bool is_dtls_enabled() const;
- void set_dtls_verify_enabled(bool p_enabled);
- bool is_dtls_verify_enabled() const;
- void set_dtls_key(Ref<CryptoKey> p_key);
- void set_dtls_certificate(Ref<X509Certificate> p_cert);
+ ENetMultiplayerPeer();
+ ~ENetMultiplayerPeer();
};
-VARIANT_ENUM_CAST(NetworkedMultiplayerENet::CompressionMode);
-
#endif // NETWORKED_MULTIPLAYER_ENET_H
diff --git a/modules/enet/enet_packet_peer.cpp b/modules/enet/enet_packet_peer.cpp
new file mode 100644
index 0000000000..d7d2ec9ebe
--- /dev/null
+++ b/modules/enet/enet_packet_peer.cpp
@@ -0,0 +1,263 @@
+/*************************************************************************/
+/* enet_packet_peer.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 "enet_packet_peer.h"
+
+void ENetPacketPeer::peer_disconnect(int p_data) {
+ ERR_FAIL_COND(!peer);
+ enet_peer_disconnect(peer, p_data);
+}
+
+void ENetPacketPeer::peer_disconnect_later(int p_data) {
+ ERR_FAIL_COND(!peer);
+ enet_peer_disconnect_later(peer, p_data);
+}
+
+void ENetPacketPeer::peer_disconnect_now(int p_data) {
+ ERR_FAIL_COND(!peer);
+ enet_peer_disconnect_now(peer, p_data);
+ _on_disconnect();
+}
+
+void ENetPacketPeer::ping() {
+ ERR_FAIL_COND(!peer);
+ enet_peer_ping(peer);
+}
+
+void ENetPacketPeer::ping_interval(int p_interval) {
+ ERR_FAIL_COND(!peer);
+ enet_peer_ping_interval(peer, p_interval);
+}
+
+int ENetPacketPeer::send(uint8_t p_channel, ENetPacket *p_packet) {
+ ERR_FAIL_COND_V(peer == nullptr, -1);
+ ERR_FAIL_COND_V(p_packet == nullptr, -1);
+ ERR_FAIL_COND_V_MSG(p_channel >= peer->channelCount, -1, vformat("Unable to send packet on channel %d, max channels: %d", p_channel, (int)peer->channelCount));
+ return enet_peer_send(peer, p_channel, p_packet);
+}
+
+void ENetPacketPeer::reset() {
+ ERR_FAIL_COND_MSG(peer == nullptr, "Peer not connected");
+ enet_peer_reset(peer);
+ _on_disconnect();
+}
+
+void ENetPacketPeer::throttle_configure(int p_interval, int p_acceleration, int p_deceleration) {
+ ERR_FAIL_COND_MSG(peer == nullptr, "Peer not connected");
+ enet_peer_throttle_configure(peer, p_interval, p_acceleration, p_deceleration);
+}
+
+void ENetPacketPeer::set_timeout(int p_timeout, int p_timeout_min, int p_timeout_max) {
+ ERR_FAIL_COND_MSG(peer == nullptr, "Peer not connected");
+ ERR_FAIL_COND_MSG(p_timeout > p_timeout_min || p_timeout_min > p_timeout_max, "Timeout limit must be less than minimum timeout, which itself must be less then maximum timeout");
+ enet_peer_timeout(peer, p_timeout, p_timeout_min, p_timeout_max);
+}
+
+int ENetPacketPeer::get_max_packet_size() const {
+ return 1 << 24;
+}
+
+int ENetPacketPeer::get_available_packet_count() const {
+ return packet_queue.size();
+}
+
+Error ENetPacketPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+ ERR_FAIL_COND_V(!peer, ERR_UNCONFIGURED);
+ ERR_FAIL_COND_V(!packet_queue.size(), ERR_UNAVAILABLE);
+ if (last_packet) {
+ enet_packet_destroy(last_packet);
+ last_packet = nullptr;
+ }
+ last_packet = packet_queue.front()->get();
+ packet_queue.pop_front();
+ *r_buffer = (const uint8_t *)(last_packet->data);
+ r_buffer_size = last_packet->dataLength;
+ return OK;
+}
+
+Error ENetPacketPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+ ERR_FAIL_COND_V(!peer, ERR_UNCONFIGURED);
+ ENetPacket *packet = enet_packet_create(p_buffer, p_buffer_size, ENET_PACKET_FLAG_RELIABLE);
+ return send(0, packet) < 0 ? FAILED : OK;
+}
+
+IPAddress ENetPacketPeer::get_remote_address() const {
+ ERR_FAIL_COND_V(!peer, IPAddress());
+ IPAddress out;
+#ifdef GODOT_ENET
+ out.set_ipv6((uint8_t *)&(peer->address.host));
+#else
+ out.set_ipv4((uint8_t *)&(peer->address.host));
+#endif
+ return out;
+}
+
+int ENetPacketPeer::get_remote_port() const {
+ ERR_FAIL_COND_V(!peer, 0);
+ return peer->address.port;
+}
+
+bool ENetPacketPeer::is_active() const {
+ return peer != nullptr;
+}
+
+double ENetPacketPeer::get_statistic(PeerStatistic p_stat) {
+ ERR_FAIL_COND_V(!peer, 0);
+ switch (p_stat) {
+ case PEER_PACKET_LOSS:
+ return peer->packetLoss;
+ case PEER_PACKET_LOSS_VARIANCE:
+ return peer->packetLossVariance;
+ case PEER_PACKET_LOSS_EPOCH:
+ return peer->packetLossEpoch;
+ case PEER_ROUND_TRIP_TIME:
+ return peer->roundTripTime;
+ case PEER_ROUND_TRIP_TIME_VARIANCE:
+ return peer->roundTripTimeVariance;
+ case PEER_LAST_ROUND_TRIP_TIME:
+ return peer->lastRoundTripTime;
+ case PEER_LAST_ROUND_TRIP_TIME_VARIANCE:
+ return peer->lastRoundTripTimeVariance;
+ case PEER_PACKET_THROTTLE:
+ return peer->packetThrottle;
+ case PEER_PACKET_THROTTLE_LIMIT:
+ return peer->packetThrottleLimit;
+ case PEER_PACKET_THROTTLE_COUNTER:
+ return peer->packetThrottleCounter;
+ case PEER_PACKET_THROTTLE_EPOCH:
+ return peer->packetThrottleEpoch;
+ case PEER_PACKET_THROTTLE_ACCELERATION:
+ return peer->packetThrottleAcceleration;
+ case PEER_PACKET_THROTTLE_DECELERATION:
+ return peer->packetThrottleDeceleration;
+ case PEER_PACKET_THROTTLE_INTERVAL:
+ return peer->packetThrottleInterval;
+ }
+ ERR_FAIL_V(0);
+}
+
+ENetPacketPeer::PeerState ENetPacketPeer::get_state() const {
+ if (!is_active()) {
+ return STATE_DISCONNECTED;
+ }
+ return (PeerState)peer->state;
+}
+
+int ENetPacketPeer::get_channels() const {
+ ERR_FAIL_COND_V_MSG(!peer, 0, "The ENetConnection instance isn't currently active.");
+ return peer->channelCount;
+}
+
+void ENetPacketPeer::_on_disconnect() {
+ if (peer) {
+ peer->data = nullptr;
+ }
+ peer = nullptr;
+}
+
+void ENetPacketPeer::_queue_packet(ENetPacket *p_packet) {
+ ERR_FAIL_COND(!peer);
+ packet_queue.push_back(p_packet);
+}
+
+Error ENetPacketPeer::_send(int p_channel, PackedByteArray p_packet, int p_flags) {
+ ERR_FAIL_COND_V_MSG(peer == nullptr, ERR_UNCONFIGURED, "Peer not connected");
+ ERR_FAIL_COND_V_MSG(p_channel < 0 || p_channel > (int)peer->channelCount, ERR_INVALID_PARAMETER, "Invalid channel");
+ ERR_FAIL_COND_V_MSG(p_flags & ~FLAG_ALLOWED, ERR_INVALID_PARAMETER, "Invalid flags");
+ ENetPacket *packet = enet_packet_create(p_packet.ptr(), p_packet.size(), p_flags);
+ return send(p_channel, packet) == 0 ? OK : FAILED;
+}
+
+void ENetPacketPeer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("peer_disconnect", "data"), &ENetPacketPeer::peer_disconnect, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("peer_disconnect_later", "data"), &ENetPacketPeer::peer_disconnect_later, DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("peer_disconnect_now", "data"), &ENetPacketPeer::peer_disconnect_now, DEFVAL(0));
+
+ ClassDB::bind_method(D_METHOD("ping"), &ENetPacketPeer::ping);
+ ClassDB::bind_method(D_METHOD("ping_interval", "ping_interval"), &ENetPacketPeer::ping_interval);
+ ClassDB::bind_method(D_METHOD("reset"), &ENetPacketPeer::reset);
+ ClassDB::bind_method(D_METHOD("send", "channel", "packet", "flags"), &ENetPacketPeer::_send);
+ ClassDB::bind_method(D_METHOD("throttle_configure", "interval", "acceleration", "deceleration"), &ENetPacketPeer::throttle_configure);
+ ClassDB::bind_method(D_METHOD("set_timeout", "timeout", "timeout_min", "timeout_max"), &ENetPacketPeer::set_timeout);
+ ClassDB::bind_method(D_METHOD("get_statistic", "statistic"), &ENetPacketPeer::get_statistic);
+ ClassDB::bind_method(D_METHOD("get_state"), &ENetPacketPeer::get_state);
+ ClassDB::bind_method(D_METHOD("get_channels"), &ENetPacketPeer::get_channels);
+ ClassDB::bind_method(D_METHOD("is_active"), &ENetPacketPeer::is_active);
+
+ BIND_ENUM_CONSTANT(STATE_DISCONNECTED);
+ BIND_ENUM_CONSTANT(STATE_CONNECTING);
+ BIND_ENUM_CONSTANT(STATE_ACKNOWLEDGING_CONNECT);
+ BIND_ENUM_CONSTANT(STATE_CONNECTION_PENDING);
+ BIND_ENUM_CONSTANT(STATE_CONNECTION_SUCCEEDED);
+ BIND_ENUM_CONSTANT(STATE_CONNECTED);
+ BIND_ENUM_CONSTANT(STATE_DISCONNECT_LATER);
+ BIND_ENUM_CONSTANT(STATE_DISCONNECTING);
+ BIND_ENUM_CONSTANT(STATE_ACKNOWLEDGING_DISCONNECT);
+ BIND_ENUM_CONSTANT(STATE_ZOMBIE);
+
+ BIND_ENUM_CONSTANT(PEER_PACKET_LOSS);
+ BIND_ENUM_CONSTANT(PEER_PACKET_LOSS_VARIANCE);
+ BIND_ENUM_CONSTANT(PEER_PACKET_LOSS_EPOCH);
+ BIND_ENUM_CONSTANT(PEER_ROUND_TRIP_TIME);
+ BIND_ENUM_CONSTANT(PEER_ROUND_TRIP_TIME_VARIANCE);
+ BIND_ENUM_CONSTANT(PEER_LAST_ROUND_TRIP_TIME);
+ BIND_ENUM_CONSTANT(PEER_LAST_ROUND_TRIP_TIME_VARIANCE);
+ BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE);
+ BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_LIMIT);
+ BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_COUNTER);
+ BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_EPOCH);
+ BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_ACCELERATION);
+ BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_DECELERATION);
+ BIND_ENUM_CONSTANT(PEER_PACKET_THROTTLE_INTERVAL);
+
+ BIND_CONSTANT(PACKET_LOSS_SCALE);
+ BIND_CONSTANT(PACKET_THROTTLE_SCALE);
+
+ BIND_CONSTANT(FLAG_RELIABLE);
+ BIND_CONSTANT(FLAG_UNSEQUENCED);
+ BIND_CONSTANT(FLAG_UNRELIABLE_FRAGMENT);
+}
+
+ENetPacketPeer::ENetPacketPeer(ENetPeer *p_peer) {
+ peer = p_peer;
+ peer->data = this;
+}
+
+ENetPacketPeer::~ENetPacketPeer() {
+ _on_disconnect();
+ if (last_packet) {
+ enet_packet_destroy(last_packet);
+ last_packet = nullptr;
+ }
+ for (List<ENetPacket *>::Element *E = packet_queue.front(); E; E = E->next()) {
+ enet_packet_destroy(E->get());
+ }
+ packet_queue.clear();
+}
diff --git a/modules/enet/enet_packet_peer.h b/modules/enet/enet_packet_peer.h
new file mode 100644
index 0000000000..9af004de2f
--- /dev/null
+++ b/modules/enet/enet_packet_peer.h
@@ -0,0 +1,131 @@
+/*************************************************************************/
+/* enet_packet_peer.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 ENET_PACKET_PEER_H
+#define ENET_PACKET_PEER_H
+
+#include "core/io/packet_peer.h"
+
+#include <enet/enet.h>
+
+class ENetPacketPeer : public PacketPeer {
+ GDCLASS(ENetPacketPeer, PacketPeer);
+
+private:
+ ENetPeer *peer = nullptr;
+ List<ENetPacket *> packet_queue;
+ ENetPacket *last_packet = nullptr;
+
+ static void _bind_methods();
+ Error _send(int p_channel, PackedByteArray p_packet, int p_flags);
+
+protected:
+ friend class ENetConnection;
+ // Internally used by ENetConnection during service, destroy, etc.
+ void _on_disconnect();
+ void _queue_packet(ENetPacket *p_packet);
+
+public:
+ enum {
+ PACKET_THROTTLE_SCALE = ENET_PEER_PACKET_THROTTLE_SCALE,
+ PACKET_LOSS_SCALE = ENET_PEER_PACKET_LOSS_SCALE,
+ };
+
+ enum {
+ FLAG_RELIABLE = ENET_PACKET_FLAG_RELIABLE,
+ FLAG_UNSEQUENCED = ENET_PACKET_FLAG_UNSEQUENCED,
+ FLAG_UNRELIABLE_FRAGMENT = ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT,
+ FLAG_ALLOWED = ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT,
+ };
+
+ enum PeerState {
+ STATE_DISCONNECTED = ENET_PEER_STATE_DISCONNECTED,
+ STATE_CONNECTING = ENET_PEER_STATE_CONNECTING,
+ STATE_ACKNOWLEDGING_CONNECT = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT,
+ STATE_CONNECTION_PENDING = ENET_PEER_STATE_CONNECTION_PENDING,
+ STATE_CONNECTION_SUCCEEDED = ENET_PEER_STATE_CONNECTION_SUCCEEDED,
+ STATE_CONNECTED = ENET_PEER_STATE_CONNECTED,
+ STATE_DISCONNECT_LATER = ENET_PEER_STATE_DISCONNECT_LATER,
+ STATE_DISCONNECTING = ENET_PEER_STATE_DISCONNECTING,
+ STATE_ACKNOWLEDGING_DISCONNECT = ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT,
+ STATE_ZOMBIE = ENET_PEER_STATE_ZOMBIE,
+ };
+
+ enum PeerStatistic {
+ PEER_PACKET_LOSS,
+ PEER_PACKET_LOSS_VARIANCE,
+ PEER_PACKET_LOSS_EPOCH,
+ PEER_ROUND_TRIP_TIME,
+ PEER_ROUND_TRIP_TIME_VARIANCE,
+ PEER_LAST_ROUND_TRIP_TIME,
+ PEER_LAST_ROUND_TRIP_TIME_VARIANCE,
+ PEER_PACKET_THROTTLE,
+ PEER_PACKET_THROTTLE_LIMIT,
+ PEER_PACKET_THROTTLE_COUNTER,
+ PEER_PACKET_THROTTLE_EPOCH,
+ PEER_PACKET_THROTTLE_ACCELERATION,
+ PEER_PACKET_THROTTLE_DECELERATION,
+ PEER_PACKET_THROTTLE_INTERVAL,
+ };
+
+ int get_max_packet_size() const override;
+ int get_available_packet_count() const override;
+ Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
+ Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
+
+ void peer_disconnect(int p_data = 0);
+ void peer_disconnect_later(int p_data = 0);
+ void peer_disconnect_now(int p_data = 0);
+
+ void ping();
+ void ping_interval(int p_interval);
+ void reset();
+ int send(uint8_t p_channel, ENetPacket *p_packet);
+ void throttle_configure(int interval, int acceleration, int deceleration);
+ void set_timeout(int p_timeout, int p_timeout_min, int p_timeout_max);
+ double get_statistic(PeerStatistic p_stat);
+ PeerState get_state() const;
+ int get_channels() const;
+
+ // Extras
+ IPAddress get_remote_address() const;
+ int get_remote_port() const;
+
+ // Used by ENetMultiplayer (TODO use meta? If only they where StringNames)
+ bool is_active() const;
+
+ ENetPacketPeer(ENetPeer *p_peer);
+ ~ENetPacketPeer();
+};
+
+VARIANT_ENUM_CAST(ENetPacketPeer::PeerState);
+VARIANT_ENUM_CAST(ENetPacketPeer::PeerStatistic);
+
+#endif // ENET_PACKET_PEER_H
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
deleted file mode 100644
index 64977ad237..0000000000
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ /dev/null
@@ -1,933 +0,0 @@
-/*************************************************************************/
-/* networked_multiplayer_enet.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "networked_multiplayer_enet.h"
-#include "core/io/ip.h"
-#include "core/io/marshalls.h"
-#include "core/os/os.h"
-
-void NetworkedMultiplayerENet::set_transfer_mode(TransferMode p_mode) {
- transfer_mode = p_mode;
-}
-
-NetworkedMultiplayerPeer::TransferMode NetworkedMultiplayerENet::get_transfer_mode() const {
- return transfer_mode;
-}
-
-void NetworkedMultiplayerENet::set_target_peer(int p_peer) {
- target_peer = p_peer;
-}
-
-int NetworkedMultiplayerENet::get_packet_peer() const {
- ERR_FAIL_COND_V_MSG(!active, 1, "The multiplayer instance isn't currently active.");
- ERR_FAIL_COND_V(incoming_packets.size() == 0, 1);
-
- return incoming_packets.front()->get().from;
-}
-
-int NetworkedMultiplayerENet::get_packet_channel() const {
- ERR_FAIL_COND_V_MSG(!active, -1, "The multiplayer instance isn't currently active.");
- ERR_FAIL_COND_V(incoming_packets.size() == 0, -1);
-
- return incoming_packets.front()->get().channel;
-}
-
-int NetworkedMultiplayerENet::get_last_packet_channel() const {
- ERR_FAIL_COND_V_MSG(!active, -1, "The multiplayer instance isn't currently active.");
- ERR_FAIL_COND_V(!current_packet.packet, -1);
-
- return current_packet.channel;
-}
-
-Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int p_in_bandwidth, int p_out_bandwidth) {
- ERR_FAIL_COND_V_MSG(active, ERR_ALREADY_IN_USE, "The multiplayer instance is already active.");
- ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The port number must be set between 0 and 65535 (inclusive).");
- ERR_FAIL_COND_V_MSG(p_max_clients < 1 || p_max_clients > 4095, ERR_INVALID_PARAMETER, "The number of clients must be set between 1 and 4095 (inclusive).");
- ERR_FAIL_COND_V_MSG(p_in_bandwidth < 0, ERR_INVALID_PARAMETER, "The incoming bandwidth limit must be greater than or equal to 0 (0 disables the limit).");
- ERR_FAIL_COND_V_MSG(p_out_bandwidth < 0, ERR_INVALID_PARAMETER, "The outgoing bandwidth limit must be greater than or equal to 0 (0 disables the limit).");
- ERR_FAIL_COND_V(dtls_enabled && (dtls_key.is_null() || dtls_cert.is_null()), ERR_INVALID_PARAMETER);
-
- ENetAddress address;
- memset(&address, 0, sizeof(address));
-
-#ifdef GODOT_ENET
- if (bind_ip.is_wildcard()) {
- address.wildcard = 1;
- } else {
- enet_address_set_ip(&address, bind_ip.get_ipv6(), 16);
- }
-#else
- if (bind_ip.is_wildcard()) {
- address.host = 0;
- } else {
- ERR_FAIL_COND_V(!bind_ip.is_ipv4(), ERR_INVALID_PARAMETER);
- address.host = *(uint32_t *)bind_ip.get_ipv4();
- }
-#endif
- address.port = p_port;
-
- host = enet_host_create(&address /* the address to bind the server host to */,
- p_max_clients /* allow up to 32 clients and/or outgoing connections */,
- channel_count /* allow up to channel_count to be used */,
- p_in_bandwidth /* limit incoming bandwidth if > 0 */,
- p_out_bandwidth /* limit outgoing bandwidth if > 0 */);
-
- ERR_FAIL_COND_V_MSG(!host, ERR_CANT_CREATE, "Couldn't create an ENet multiplayer server.");
-#ifdef GODOT_ENET
- if (dtls_enabled) {
- enet_host_dtls_server_setup(host, dtls_key.ptr(), dtls_cert.ptr());
- }
- enet_host_refuse_new_connections(host, refuse_connections);
-#endif
-
- _setup_compressor();
- active = true;
- server = true;
- refuse_connections = false;
- unique_id = 1;
- connection_status = CONNECTION_CONNECTED;
- return OK;
-}
-
-Error NetworkedMultiplayerENet::create_client(const String &p_address, int p_port, int p_in_bandwidth, int p_out_bandwidth, int p_client_port) {
- ERR_FAIL_COND_V_MSG(active, ERR_ALREADY_IN_USE, "The multiplayer instance is already active.");
- ERR_FAIL_COND_V_MSG(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER, "The server port number must be set between 0 and 65535 (inclusive).");
- ERR_FAIL_COND_V_MSG(p_client_port < 0 || p_client_port > 65535, ERR_INVALID_PARAMETER, "The client port number must be set between 0 and 65535 (inclusive).");
- ERR_FAIL_COND_V_MSG(p_in_bandwidth < 0, ERR_INVALID_PARAMETER, "The incoming bandwidth limit must be greater than or equal to 0 (0 disables the limit).");
- ERR_FAIL_COND_V_MSG(p_out_bandwidth < 0, ERR_INVALID_PARAMETER, "The outgoing bandwidth limit must be greater than or equal to 0 (0 disables the limit).");
-
- if (p_client_port != 0) {
- ENetAddress c_client;
-
-#ifdef GODOT_ENET
- if (bind_ip.is_wildcard()) {
- c_client.wildcard = 1;
- } else {
- enet_address_set_ip(&c_client, bind_ip.get_ipv6(), 16);
- }
-#else
- if (bind_ip.is_wildcard()) {
- c_client.host = 0;
- } else {
- ERR_FAIL_COND_V_MSG(!bind_ip.is_ipv4(), ERR_INVALID_PARAMETER, "Wildcard IP addresses are only permitted in IPv4, not IPv6.");
- c_client.host = *(uint32_t *)bind_ip.get_ipv4();
- }
-#endif
-
- c_client.port = p_client_port;
-
- host = enet_host_create(&c_client /* create a client host */,
- 1 /* only allow 1 outgoing connection */,
- channel_count /* allow up to channel_count to be used */,
- p_in_bandwidth /* limit incoming bandwidth if > 0 */,
- p_out_bandwidth /* limit outgoing bandwidth if > 0 */);
- } else {
- host = enet_host_create(nullptr /* create a client host */,
- 1 /* only allow 1 outgoing connection */,
- channel_count /* allow up to channel_count to be used */,
- p_in_bandwidth /* limit incoming bandwidth if > 0 */,
- p_out_bandwidth /* limit outgoing bandwidth if > 0 */);
- }
-
- ERR_FAIL_COND_V_MSG(!host, ERR_CANT_CREATE, "Couldn't create the ENet client host.");
-#ifdef GODOT_ENET
- if (dtls_enabled) {
- enet_host_dtls_client_setup(host, dtls_cert.ptr(), dtls_verify, p_address.utf8().get_data());
- }
- enet_host_refuse_new_connections(host, refuse_connections);
-#endif
-
- _setup_compressor();
-
- IP_Address ip;
- if (p_address.is_valid_ip_address()) {
- ip = p_address;
- } else {
-#ifdef GODOT_ENET
- ip = IP::get_singleton()->resolve_hostname(p_address);
-#else
- ip = IP::get_singleton()->resolve_hostname(p_address, IP::TYPE_IPV4);
-#endif
-
- ERR_FAIL_COND_V_MSG(!ip.is_valid(), ERR_CANT_RESOLVE, "Couldn't resolve the server IP address or domain name.");
- }
-
- ENetAddress address;
-#ifdef GODOT_ENET
- enet_address_set_ip(&address, ip.get_ipv6(), 16);
-#else
- ERR_FAIL_COND_V_MSG(!ip.is_ipv4(), ERR_INVALID_PARAMETER, "Connecting to an IPv6 server isn't supported when using vanilla ENet. Recompile Godot with the bundled ENet library.");
- address.host = *(uint32_t *)ip.get_ipv4();
-#endif
- address.port = p_port;
-
- unique_id = _gen_unique_id();
-
- // Initiate connection, allocating enough channels
- ENetPeer *peer = enet_host_connect(host, &address, channel_count, unique_id);
-
- if (peer == nullptr) {
- enet_host_destroy(host);
- ERR_FAIL_COND_V_MSG(!peer, ERR_CANT_CREATE, "Couldn't connect to the ENet multiplayer server.");
- }
-
- // Technically safe to ignore the peer or anything else.
-
- connection_status = CONNECTION_CONNECTING;
- active = true;
- server = false;
- refuse_connections = false;
-
- return OK;
-}
-
-void NetworkedMultiplayerENet::poll() {
- ERR_FAIL_COND_MSG(!active, "The multiplayer instance isn't currently active.");
-
- _pop_current_packet();
-
- ENetEvent event;
- /* Keep servicing until there are no available events left in queue. */
- while (true) {
- if (!host || !active) { // Might have been disconnected while emitting a notification
- return;
- }
-
- int ret = enet_host_service(host, &event, 0);
-
- if (ret < 0) {
- // Error, do something?
- break;
- } else if (ret == 0) {
- break;
- }
-
- switch (event.type) {
- case ENET_EVENT_TYPE_CONNECT: {
- // Store any relevant client information here.
-
- if (server && refuse_connections) {
- enet_peer_reset(event.peer);
- break;
- }
-
- // A client joined with an invalid ID (negative values, 0, and 1 are reserved).
- // Probably trying to exploit us.
- if (server && ((int)event.data < 2 || peer_map.has((int)event.data))) {
- enet_peer_reset(event.peer);
- ERR_CONTINUE(true);
- }
-
- int *new_id = memnew(int);
- *new_id = event.data;
-
- if (*new_id == 0) { // Data zero is sent by server (enet won't let you configure this). Server is always 1.
- *new_id = 1;
- }
-
- event.peer->data = new_id;
-
- peer_map[*new_id] = event.peer;
-
- connection_status = CONNECTION_CONNECTED; // If connecting, this means it connected to something!
-
- emit_signal("peer_connected", *new_id);
-
- if (server) {
- // Do not notify other peers when server_relay is disabled.
- if (!server_relay) {
- break;
- }
-
- // Someone connected, notify all the peers available
- for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (E->key() == *new_id) {
- continue;
- }
- // Send existing peers to new peer
- ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
- encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
- encode_uint32(E->key(), &packet->data[4]);
- enet_peer_send(event.peer, SYSCH_CONFIG, packet);
- // Send the new peer to existing peers
- packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
- encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
- encode_uint32(*new_id, &packet->data[4]);
- enet_peer_send(E->get(), SYSCH_CONFIG, packet);
- }
- } else {
- emit_signal("connection_succeeded");
- }
-
- } break;
- case ENET_EVENT_TYPE_DISCONNECT: {
- // Reset the peer's client information.
-
- int *id = (int *)event.peer->data;
-
- if (!id) {
- if (!server) {
- emit_signal("connection_failed");
- }
- // Never fully connected.
- break;
- }
-
- if (!server) {
- // Client just disconnected from server.
- emit_signal("server_disconnected");
- close_connection();
- return;
- } else if (server_relay) {
- // Server just received a client disconnect and is in relay mode, notify everyone else.
- for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (E->key() == *id) {
- continue;
- }
-
- ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
- encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
- encode_uint32(*id, &packet->data[4]);
- enet_peer_send(E->get(), SYSCH_CONFIG, packet);
- }
- }
-
- emit_signal("peer_disconnected", *id);
- peer_map.erase(*id);
- memdelete(id);
- } break;
- case ENET_EVENT_TYPE_RECEIVE: {
- if (event.channelID == SYSCH_CONFIG) {
- // Some config message
- ERR_CONTINUE(event.packet->dataLength < 8);
-
- // Only server can send config messages
- ERR_CONTINUE(server);
-
- int msg = decode_uint32(&event.packet->data[0]);
- int id = decode_uint32(&event.packet->data[4]);
-
- switch (msg) {
- case SYSMSG_ADD_PEER: {
- peer_map[id] = nullptr;
- emit_signal("peer_connected", id);
-
- } break;
- case SYSMSG_REMOVE_PEER: {
- peer_map.erase(id);
- emit_signal("peer_disconnected", id);
- } break;
- }
-
- enet_packet_destroy(event.packet);
- } else if (event.channelID < channel_count) {
- Packet packet;
- packet.packet = event.packet;
-
- uint32_t *id = (uint32_t *)event.peer->data;
-
- ERR_CONTINUE(event.packet->dataLength < 8);
-
- uint32_t source = decode_uint32(&event.packet->data[0]);
- int target = decode_uint32(&event.packet->data[4]);
-
- packet.from = source;
- packet.channel = event.channelID;
-
- if (server) {
- // Someone is cheating and trying to fake the source!
- ERR_CONTINUE(source != *id);
-
- packet.from = *id;
-
- if (target == 1) {
- // To myself and only myself
- incoming_packets.push_back(packet);
- } else if (!server_relay) {
- // No other destination is allowed when server is not relaying
- continue;
- } else if (target == 0) {
- // Re-send to everyone but sender :|
-
- incoming_packets.push_back(packet);
- // And make copies for sending
- for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (uint32_t(E->key()) == source) { // Do not resend to self
- continue;
- }
-
- ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, packet.packet->flags);
-
- enet_peer_send(E->get(), event.channelID, packet2);
- }
-
- } else if (target < 0) {
- // To all but one
-
- // And make copies for sending
- for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (uint32_t(E->key()) == source || E->key() == -target) { // Do not resend to self, also do not send to excluded
- continue;
- }
-
- ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, packet.packet->flags);
-
- enet_peer_send(E->get(), event.channelID, packet2);
- }
-
- if (-target != 1) {
- // Server is not excluded
- incoming_packets.push_back(packet);
- } else {
- // Server is excluded, erase packet
- enet_packet_destroy(packet.packet);
- }
-
- } else {
- // To someone else, specifically
- ERR_CONTINUE(!peer_map.has(target));
- enet_peer_send(peer_map[target], event.channelID, packet.packet);
- }
- } else {
- incoming_packets.push_back(packet);
- }
-
- // Destroy packet later
- } else {
- ERR_CONTINUE(true);
- }
-
- } break;
- case ENET_EVENT_TYPE_NONE: {
- // Do nothing
- } break;
- }
- }
-}
-
-bool NetworkedMultiplayerENet::is_server() const {
- ERR_FAIL_COND_V_MSG(!active, false, "The multiplayer instance isn't currently active.");
-
- return server;
-}
-
-void NetworkedMultiplayerENet::close_connection(uint32_t wait_usec) {
- ERR_FAIL_COND_MSG(!active, "The multiplayer instance isn't currently active.");
-
- _pop_current_packet();
-
- bool peers_disconnected = false;
- for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (E->get()) {
- enet_peer_disconnect_now(E->get(), unique_id);
- int *id = (int *)(E->get()->data);
- memdelete(id);
- peers_disconnected = true;
- }
- }
-
- if (peers_disconnected) {
- enet_host_flush(host);
-
- if (wait_usec > 0) {
- OS::get_singleton()->delay_usec(wait_usec); // Wait for disconnection packets to send
- }
- }
-
- enet_host_destroy(host);
- active = false;
- incoming_packets.clear();
- peer_map.clear();
- unique_id = 1; // Server is 1
- connection_status = CONNECTION_DISCONNECTED;
-}
-
-void NetworkedMultiplayerENet::disconnect_peer(int p_peer, bool now) {
- ERR_FAIL_COND_MSG(!active, "The multiplayer instance isn't currently active.");
- ERR_FAIL_COND_MSG(!is_server(), "Can't disconnect a peer when not acting as a server.");
- ERR_FAIL_COND_MSG(!peer_map.has(p_peer), vformat("Peer ID %d not found in the list of peers.", p_peer));
-
- if (now) {
- int *id = (int *)peer_map[p_peer]->data;
- enet_peer_disconnect_now(peer_map[p_peer], 0);
-
- // enet_peer_disconnect_now doesn't generate ENET_EVENT_TYPE_DISCONNECT,
- // notify everyone else, send disconnect signal & remove from peer_map like in poll()
- if (server_relay) {
- for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
- if (E->key() == p_peer) {
- continue;
- }
-
- ENetPacket *packet = enet_packet_create(nullptr, 8, ENET_PACKET_FLAG_RELIABLE);
- encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
- encode_uint32(p_peer, &packet->data[4]);
- enet_peer_send(E->get(), SYSCH_CONFIG, packet);
- }
- }
-
- if (id) {
- memdelete(id);
- }
-
- emit_signal("peer_disconnected", p_peer);
- peer_map.erase(p_peer);
- } else {
- enet_peer_disconnect_later(peer_map[p_peer], 0);
- }
-}
-
-int NetworkedMultiplayerENet::get_available_packet_count() const {
- return incoming_packets.size();
-}
-
-Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
- ERR_FAIL_COND_V_MSG(incoming_packets.size() == 0, ERR_UNAVAILABLE, "No incoming packets available.");
-
- _pop_current_packet();
-
- current_packet = incoming_packets.front()->get();
- incoming_packets.pop_front();
-
- *r_buffer = (const uint8_t *)(&current_packet.packet->data[8]);
- r_buffer_size = current_packet.packet->dataLength - 8;
-
- return OK;
-}
-
-Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
- ERR_FAIL_COND_V_MSG(!active, ERR_UNCONFIGURED, "The multiplayer instance isn't currently active.");
- ERR_FAIL_COND_V_MSG(connection_status != CONNECTION_CONNECTED, ERR_UNCONFIGURED, "The multiplayer instance isn't currently connected to any server or client.");
-
- int packet_flags = 0;
- int channel = SYSCH_RELIABLE;
-
- switch (transfer_mode) {
- case TRANSFER_MODE_UNRELIABLE: {
- if (always_ordered) {
- packet_flags = 0;
- } else {
- packet_flags = ENET_PACKET_FLAG_UNSEQUENCED;
- }
- channel = SYSCH_UNRELIABLE;
- } break;
- case TRANSFER_MODE_UNRELIABLE_ORDERED: {
- packet_flags = 0;
- channel = SYSCH_UNRELIABLE;
- } break;
- case TRANSFER_MODE_RELIABLE: {
- packet_flags = ENET_PACKET_FLAG_RELIABLE;
- channel = SYSCH_RELIABLE;
- } break;
- }
-
- if (transfer_channel > SYSCH_CONFIG) {
- channel = transfer_channel;
- }
-
- Map<int, ENetPeer *>::Element *E = nullptr;
-
- if (target_peer != 0) {
- E = peer_map.find(ABS(target_peer));
- ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, vformat("Invalid target peer: %d", target_peer));
- }
-
- 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
- copymem(&packet->data[8], p_buffer, p_buffer_size);
-
- if (server) {
- if (target_peer == 0) {
- enet_host_broadcast(host, channel, packet);
- } else if (target_peer < 0) {
- // Send to all but one
- // and make copies for sending
-
- int exclude = -target_peer;
-
- for (Map<int, ENetPeer *>::Element *F = peer_map.front(); F; F = F->next()) {
- if (F->key() == exclude) { // Exclude packet
- continue;
- }
-
- ENetPacket *packet2 = enet_packet_create(packet->data, packet->dataLength, packet_flags);
-
- enet_peer_send(F->get(), channel, packet2);
- }
-
- enet_packet_destroy(packet); // Original packet no longer needed
- } else {
- enet_peer_send(E->get(), channel, packet);
- }
- } else {
- ERR_FAIL_COND_V(!peer_map.has(1), ERR_BUG);
- enet_peer_send(peer_map[1], channel, packet); // Send to server for broadcast
- }
-
- enet_host_flush(host);
-
- return OK;
-}
-
-int NetworkedMultiplayerENet::get_max_packet_size() const {
- return 1 << 24; // Anything is good
-}
-
-void NetworkedMultiplayerENet::_pop_current_packet() {
- if (current_packet.packet) {
- enet_packet_destroy(current_packet.packet);
- current_packet.packet = nullptr;
- current_packet.from = 0;
- current_packet.channel = -1;
- }
-}
-
-NetworkedMultiplayerPeer::ConnectionStatus NetworkedMultiplayerENet::get_connection_status() const {
- return connection_status;
-}
-
-uint32_t NetworkedMultiplayerENet::_gen_unique_id() const {
- uint32_t hash = 0;
-
- while (hash == 0 || hash == 1) {
- hash = hash_djb2_one_32(
- (uint32_t)OS::get_singleton()->get_ticks_usec());
- hash = hash_djb2_one_32(
- (uint32_t)OS::get_singleton()->get_unix_time(), hash);
- hash = hash_djb2_one_32(
- (uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash);
- hash = hash_djb2_one_32(
- (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap
- hash = hash_djb2_one_32(
- (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack
-
- hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
- }
-
- return hash;
-}
-
-int NetworkedMultiplayerENet::get_unique_id() const {
- ERR_FAIL_COND_V_MSG(!active, 0, "The multiplayer instance isn't currently active.");
- return unique_id;
-}
-
-void NetworkedMultiplayerENet::set_refuse_new_connections(bool p_enable) {
- refuse_connections = p_enable;
-#ifdef GODOT_ENET
- if (active) {
- enet_host_refuse_new_connections(host, p_enable);
- }
-#endif
-}
-
-bool NetworkedMultiplayerENet::is_refusing_new_connections() const {
- return refuse_connections;
-}
-
-void NetworkedMultiplayerENet::set_compression_mode(CompressionMode p_mode) {
- compression_mode = p_mode;
-}
-
-NetworkedMultiplayerENet::CompressionMode NetworkedMultiplayerENet::get_compression_mode() const {
- return compression_mode;
-}
-
-size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer *inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 *outData, size_t outLimit) {
- NetworkedMultiplayerENet *enet = (NetworkedMultiplayerENet *)(context);
-
- if (size_t(enet->src_compressor_mem.size()) < inLimit) {
- enet->src_compressor_mem.resize(inLimit);
- }
-
- int total = inLimit;
- int ofs = 0;
- while (total) {
- for (size_t i = 0; i < inBufferCount; i++) {
- int to_copy = MIN(total, int(inBuffers[i].dataLength));
- copymem(&enet->src_compressor_mem.write[ofs], inBuffers[i].data, to_copy);
- ofs += to_copy;
- total -= to_copy;
- }
- }
-
- Compression::Mode mode;
-
- switch (enet->compression_mode) {
- case COMPRESS_FASTLZ: {
- mode = Compression::MODE_FASTLZ;
- } break;
- case COMPRESS_ZLIB: {
- mode = Compression::MODE_DEFLATE;
- } break;
- case COMPRESS_ZSTD: {
- mode = Compression::MODE_ZSTD;
- } break;
- default: {
- ERR_FAIL_V_MSG(0, vformat("Invalid ENet compression mode: %d", enet->compression_mode));
- }
- }
-
- int req_size = Compression::get_max_compressed_buffer_size(ofs, mode);
- if (enet->dst_compressor_mem.size() < req_size) {
- enet->dst_compressor_mem.resize(req_size);
- }
- int ret = Compression::compress(enet->dst_compressor_mem.ptrw(), enet->src_compressor_mem.ptr(), ofs, mode);
-
- if (ret < 0) {
- return 0;
- }
-
- if (ret > int(outLimit)) {
- return 0; // Do not bother
- }
-
- copymem(outData, enet->dst_compressor_mem.ptr(), ret);
-
- return ret;
-}
-
-size_t NetworkedMultiplayerENet::enet_decompress(void *context, const enet_uint8 *inData, size_t inLimit, enet_uint8 *outData, size_t outLimit) {
- NetworkedMultiplayerENet *enet = (NetworkedMultiplayerENet *)(context);
- int ret = -1;
- switch (enet->compression_mode) {
- case COMPRESS_FASTLZ: {
- ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_FASTLZ);
- } break;
- case COMPRESS_ZLIB: {
- ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_DEFLATE);
- } break;
- case COMPRESS_ZSTD: {
- ret = Compression::decompress(outData, outLimit, inData, inLimit, Compression::MODE_ZSTD);
- } break;
- default: {
- }
- }
- if (ret < 0) {
- return 0;
- } else {
- return ret;
- }
-}
-
-void NetworkedMultiplayerENet::_setup_compressor() {
- switch (compression_mode) {
- case COMPRESS_NONE: {
- enet_host_compress(host, nullptr);
- } break;
- case COMPRESS_RANGE_CODER: {
- enet_host_compress_with_range_coder(host);
- } break;
- case COMPRESS_FASTLZ:
- case COMPRESS_ZLIB:
- case COMPRESS_ZSTD: {
- enet_host_compress(host, &enet_compressor);
- } break;
- }
-}
-
-void NetworkedMultiplayerENet::enet_compressor_destroy(void *context) {
- // Nothing to do
-}
-
-IP_Address NetworkedMultiplayerENet::get_peer_address(int p_peer_id) const {
- ERR_FAIL_COND_V_MSG(!peer_map.has(p_peer_id), IP_Address(), vformat("Peer ID %d not found in the list of peers.", p_peer_id));
- ERR_FAIL_COND_V_MSG(!is_server() && p_peer_id != 1, IP_Address(), "Can't get the address of peers other than the server (ID -1) when acting as a client.");
- ERR_FAIL_COND_V_MSG(peer_map[p_peer_id] == nullptr, IP_Address(), vformat("Peer ID %d found in the list of peers, but is null.", p_peer_id));
-
- IP_Address out;
-#ifdef GODOT_ENET
- out.set_ipv6((uint8_t *)&(peer_map[p_peer_id]->address.host));
-#else
- out.set_ipv4((uint8_t *)&(peer_map[p_peer_id]->address.host));
-#endif
-
- return out;
-}
-
-int NetworkedMultiplayerENet::get_peer_port(int p_peer_id) const {
- ERR_FAIL_COND_V_MSG(!peer_map.has(p_peer_id), 0, vformat("Peer ID %d not found in the list of peers.", p_peer_id));
- ERR_FAIL_COND_V_MSG(!is_server() && p_peer_id != 1, 0, "Can't get the address of peers other than the server (ID -1) when acting as a client.");
- ERR_FAIL_COND_V_MSG(peer_map[p_peer_id] == nullptr, 0, vformat("Peer ID %d found in the list of peers, but is null.", p_peer_id));
-#ifdef GODOT_ENET
- return peer_map[p_peer_id]->address.port;
-#else
- return peer_map[p_peer_id]->address.port;
-#endif
-}
-
-void NetworkedMultiplayerENet::set_transfer_channel(int p_channel) {
- ERR_FAIL_COND_MSG(p_channel < -1 || p_channel >= channel_count, vformat("The transfer channel must be set between 0 and %d, inclusive (got %d).", channel_count - 1, p_channel));
- ERR_FAIL_COND_MSG(p_channel == SYSCH_CONFIG, vformat("The channel %d is reserved.", SYSCH_CONFIG));
- transfer_channel = p_channel;
-}
-
-int NetworkedMultiplayerENet::get_transfer_channel() const {
- return transfer_channel;
-}
-
-void NetworkedMultiplayerENet::set_channel_count(int p_channel) {
- ERR_FAIL_COND_MSG(active, "The channel count can't be set while the multiplayer instance is active.");
- ERR_FAIL_COND_MSG(p_channel < SYSCH_MAX, vformat("The channel count must be greater than or equal to %d to account for reserved channels (got %d).", SYSCH_MAX, p_channel));
- channel_count = p_channel;
-}
-
-int NetworkedMultiplayerENet::get_channel_count() const {
- return channel_count;
-}
-
-void NetworkedMultiplayerENet::set_always_ordered(bool p_ordered) {
- always_ordered = p_ordered;
-}
-
-bool NetworkedMultiplayerENet::is_always_ordered() const {
- return always_ordered;
-}
-
-void NetworkedMultiplayerENet::set_server_relay_enabled(bool p_enabled) {
- ERR_FAIL_COND_MSG(active, "Server relaying can't be toggled while the multiplayer instance is active.");
-
- server_relay = p_enabled;
-}
-
-bool NetworkedMultiplayerENet::is_server_relay_enabled() const {
- return server_relay;
-}
-
-void NetworkedMultiplayerENet::_bind_methods() {
- ClassDB::bind_method(D_METHOD("create_server", "port", "max_clients", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_server, DEFVAL(32), DEFVAL(0), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("create_client", "address", "port", "in_bandwidth", "out_bandwidth", "client_port"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("close_connection", "wait_usec"), &NetworkedMultiplayerENet::close_connection, DEFVAL(100));
- ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "now"), &NetworkedMultiplayerENet::disconnect_peer, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("set_compression_mode", "mode"), &NetworkedMultiplayerENet::set_compression_mode);
- ClassDB::bind_method(D_METHOD("get_compression_mode"), &NetworkedMultiplayerENet::get_compression_mode);
- ClassDB::bind_method(D_METHOD("set_bind_ip", "ip"), &NetworkedMultiplayerENet::set_bind_ip);
- ClassDB::bind_method(D_METHOD("set_dtls_enabled", "enabled"), &NetworkedMultiplayerENet::set_dtls_enabled);
- ClassDB::bind_method(D_METHOD("is_dtls_enabled"), &NetworkedMultiplayerENet::is_dtls_enabled);
- ClassDB::bind_method(D_METHOD("set_dtls_key", "key"), &NetworkedMultiplayerENet::set_dtls_key);
- ClassDB::bind_method(D_METHOD("set_dtls_certificate", "certificate"), &NetworkedMultiplayerENet::set_dtls_certificate);
- ClassDB::bind_method(D_METHOD("set_dtls_verify_enabled", "enabled"), &NetworkedMultiplayerENet::set_dtls_verify_enabled);
- ClassDB::bind_method(D_METHOD("is_dtls_verify_enabled"), &NetworkedMultiplayerENet::is_dtls_verify_enabled);
- ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &NetworkedMultiplayerENet::get_peer_address);
- ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &NetworkedMultiplayerENet::get_peer_port);
-
- ClassDB::bind_method(D_METHOD("get_packet_channel"), &NetworkedMultiplayerENet::get_packet_channel);
- ClassDB::bind_method(D_METHOD("get_last_packet_channel"), &NetworkedMultiplayerENet::get_last_packet_channel);
- ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &NetworkedMultiplayerENet::set_transfer_channel);
- ClassDB::bind_method(D_METHOD("get_transfer_channel"), &NetworkedMultiplayerENet::get_transfer_channel);
- ClassDB::bind_method(D_METHOD("set_channel_count", "channels"), &NetworkedMultiplayerENet::set_channel_count);
- ClassDB::bind_method(D_METHOD("get_channel_count"), &NetworkedMultiplayerENet::get_channel_count);
- ClassDB::bind_method(D_METHOD("set_always_ordered", "ordered"), &NetworkedMultiplayerENet::set_always_ordered);
- ClassDB::bind_method(D_METHOD("is_always_ordered"), &NetworkedMultiplayerENet::is_always_ordered);
- ClassDB::bind_method(D_METHOD("set_server_relay_enabled", "enabled"), &NetworkedMultiplayerENet::set_server_relay_enabled);
- ClassDB::bind_method(D_METHOD("is_server_relay_enabled"), &NetworkedMultiplayerENet::is_server_relay_enabled);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "compression_mode", PROPERTY_HINT_ENUM, "None,Range Coder,FastLZ,ZLib,ZStd"), "set_compression_mode", "get_compression_mode");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel"), "set_transfer_channel", "get_transfer_channel");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "channel_count"), "set_channel_count", "get_channel_count");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "always_ordered"), "set_always_ordered", "is_always_ordered");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "server_relay"), "set_server_relay_enabled", "is_server_relay_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "dtls_verify"), "set_dtls_verify_enabled", "is_dtls_verify_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_dtls"), "set_dtls_enabled", "is_dtls_enabled");
-
- BIND_ENUM_CONSTANT(COMPRESS_NONE);
- BIND_ENUM_CONSTANT(COMPRESS_RANGE_CODER);
- BIND_ENUM_CONSTANT(COMPRESS_FASTLZ);
- BIND_ENUM_CONSTANT(COMPRESS_ZLIB);
- BIND_ENUM_CONSTANT(COMPRESS_ZSTD);
-}
-
-NetworkedMultiplayerENet::NetworkedMultiplayerENet() {
- active = false;
- server = false;
- refuse_connections = false;
- server_relay = true;
- unique_id = 0;
- target_peer = 0;
- current_packet.packet = nullptr;
- transfer_mode = TRANSFER_MODE_RELIABLE;
- channel_count = SYSCH_MAX;
- transfer_channel = -1;
- always_ordered = false;
- connection_status = CONNECTION_DISCONNECTED;
- compression_mode = COMPRESS_NONE;
- enet_compressor.context = this;
- enet_compressor.compress = enet_compress;
- enet_compressor.decompress = enet_decompress;
- enet_compressor.destroy = enet_compressor_destroy;
-
- bind_ip = IP_Address("*");
-
- dtls_enabled = false;
- dtls_verify = true;
-}
-
-NetworkedMultiplayerENet::~NetworkedMultiplayerENet() {
- if (active) {
- close_connection();
- }
-}
-
-// Sets IP for ENet to bind when using create_server or create_client
-// if no IP is set, then ENet bind to ENET_HOST_ANY
-void NetworkedMultiplayerENet::set_bind_ip(const IP_Address &p_ip) {
- ERR_FAIL_COND_MSG(!p_ip.is_valid() && !p_ip.is_wildcard(), vformat("Invalid bind IP address: %s", String(p_ip)));
-
- bind_ip = p_ip;
-}
-
-void NetworkedMultiplayerENet::set_dtls_enabled(bool p_enabled) {
- ERR_FAIL_COND(active);
- dtls_enabled = p_enabled;
-}
-
-bool NetworkedMultiplayerENet::is_dtls_enabled() const {
- return dtls_enabled;
-}
-
-void NetworkedMultiplayerENet::set_dtls_verify_enabled(bool p_enabled) {
- ERR_FAIL_COND(active);
- dtls_verify = p_enabled;
-}
-
-bool NetworkedMultiplayerENet::is_dtls_verify_enabled() const {
- return dtls_verify;
-}
-
-void NetworkedMultiplayerENet::set_dtls_key(Ref<CryptoKey> p_key) {
- ERR_FAIL_COND(active);
- dtls_key = p_key;
-}
-
-void NetworkedMultiplayerENet::set_dtls_certificate(Ref<X509Certificate> p_cert) {
- ERR_FAIL_COND(active);
- dtls_cert = p_cert;
-}
diff --git a/modules/enet/register_types.cpp b/modules/enet/register_types.cpp
index 18051f756a..7570f5b643 100644
--- a/modules/enet/register_types.cpp
+++ b/modules/enet/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,8 +29,10 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/error_macros.h"
-#include "networked_multiplayer_enet.h"
+#include "core/error/error_macros.h"
+#include "enet_connection.h"
+#include "enet_multiplayer_peer.h"
+#include "enet_packet_peer.h"
static bool enet_ok = false;
@@ -41,7 +43,9 @@ void register_enet_types() {
enet_ok = true;
}
- ClassDB::register_class<NetworkedMultiplayerENet>();
+ GDREGISTER_CLASS(ENetMultiplayerPeer);
+ GDREGISTER_VIRTUAL_CLASS(ENetPacketPeer);
+ GDREGISTER_CLASS(ENetConnection);
}
void unregister_enet_types() {
diff --git a/modules/enet/register_types.h b/modules/enet/register_types.h
index cac0a4f7ee..75f4cba61b 100644
--- a/modules/enet/register_types.h
+++ b/modules/enet/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/etc/SCsub b/modules/etc/SCsub
deleted file mode 100644
index 383bbf83c3..0000000000
--- a/modules/etc/SCsub
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-env_etc = env_modules.Clone()
-
-# Thirdparty source files
-# Not unbundled so far since not widespread as shared library
-thirdparty_dir = "#thirdparty/etc2comp/"
-thirdparty_sources = [
- "EtcBlock4x4.cpp",
- "EtcBlock4x4Encoding.cpp",
- "EtcBlock4x4Encoding_ETC1.cpp",
- "EtcBlock4x4Encoding_R11.cpp",
- "EtcBlock4x4Encoding_RG11.cpp",
- "EtcBlock4x4Encoding_RGB8A1.cpp",
- "EtcBlock4x4Encoding_RGB8.cpp",
- "EtcBlock4x4Encoding_RGBA8.cpp",
- "Etc.cpp",
- "EtcDifferentialTrys.cpp",
- "EtcFilter.cpp",
- "EtcImage.cpp",
- "EtcIndividualTrys.cpp",
- "EtcMath.cpp",
- "EtcSortedBlockList.cpp",
-]
-thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-
-env_etc.Prepend(CPPPATH=[thirdparty_dir])
-
-env_thirdparty = env_etc.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
-
-# Godot source files
-env_etc.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/etc/config.py b/modules/etc/config.py
deleted file mode 100644
index 53b8f2f2e3..0000000000
--- a/modules/etc/config.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def can_build(env, platform):
- return env["tools"]
-
-
-def configure(env):
- pass
diff --git a/modules/etc/image_etc.cpp b/modules/etc/image_etc.cpp
deleted file mode 100644
index d1ba3dc355..0000000000
--- a/modules/etc/image_etc.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-/*************************************************************************/
-/* image_etc.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "image_etc.h"
-#include "Etc.h"
-#include "EtcFilter.h"
-#include "core/image.h"
-#include "core/os/copymem.h"
-#include "core/os/os.h"
-#include "core/print_string.h"
-
-static Image::Format _get_etc2_mode(Image::UsedChannels format) {
- switch (format) {
- case Image::USED_CHANNELS_R:
- return Image::FORMAT_ETC2_R11;
-
- case Image::USED_CHANNELS_RG:
- return Image::FORMAT_ETC2_RG11;
-
- case Image::USED_CHANNELS_RGB:
- return Image::FORMAT_ETC2_RGB8;
-
- case Image::USED_CHANNELS_RGBA:
- return Image::FORMAT_ETC2_RGBA8;
-
- // TODO: would be nice if we could use FORMAT_ETC2_RGB8A1 for FORMAT_RGBA5551
- default:
- // TODO: Kept for compatibility, but should be investigated whether it's correct or if it should error out
- return Image::FORMAT_ETC2_RGBA8;
- }
-}
-
-static Etc::Image::Format _image_format_to_etc2comp_format(Image::Format format) {
- switch (format) {
- case Image::FORMAT_ETC:
- return Etc::Image::Format::ETC1;
-
- case Image::FORMAT_ETC2_R11:
- return Etc::Image::Format::R11;
-
- case Image::FORMAT_ETC2_R11S:
- return Etc::Image::Format::SIGNED_R11;
-
- case Image::FORMAT_ETC2_RG11:
- return Etc::Image::Format::RG11;
-
- case Image::FORMAT_ETC2_RG11S:
- return Etc::Image::Format::SIGNED_RG11;
-
- case Image::FORMAT_ETC2_RGB8:
- return Etc::Image::Format::RGB8;
-
- case Image::FORMAT_ETC2_RGBA8:
- return Etc::Image::Format::RGBA8;
-
- case Image::FORMAT_ETC2_RGB8A1:
- return Etc::Image::Format::RGB8A1;
-
- default:
- ERR_FAIL_V(Etc::Image::Format::UNKNOWN);
- }
-}
-
-static void _compress_etc(Image *p_img, float p_lossy_quality, bool force_etc1_format, Image::UsedChannels p_channels) {
- Image::Format img_format = p_img->get_format();
-
- if (img_format >= Image::FORMAT_DXT1) {
- return; //do not compress, already compressed
- }
-
- if (img_format > Image::FORMAT_RGBA8) {
- // TODO: we should be able to handle FORMAT_RGBA4444 and FORMAT_RGBA5551 eventually
- return;
- }
-
- // FIXME: Commented out during Vulkan rebase.
- /*
- if (force_etc1_format) {
- // If VRAM compression is using ETC, but image has alpha, convert to RGBA4444 or LA8
- // This saves space while maintaining the alpha channel
- if (detected_channels == Image::USED_CHANNELS_RGBA) {
-
- if (p_img->has_mipmaps()) {
- // Image doesn't support mipmaps with RGBA4444 textures
- p_img->clear_mipmaps();
- }
- p_img->convert(Image::FORMAT_RGBA4444);
- return;
- } else if (detected_channels == Image::USE_CHANNELS_LA) {
-
- p_img->convert(Image::FORMAT_LA8);
- return;
- }
- }
- */
-
- uint32_t imgw = p_img->get_width(), imgh = p_img->get_height();
-
- Image::Format etc_format = force_etc1_format ? Image::FORMAT_ETC : _get_etc2_mode(p_channels);
-
- Ref<Image> img = p_img->duplicate();
-
- if (img->get_format() != Image::FORMAT_RGBA8) {
- img->convert(Image::FORMAT_RGBA8); //still uses RGBA to convert
- }
-
- if (img->has_mipmaps()) {
- if (next_power_of_2(imgw) != imgw || next_power_of_2(imgh) != imgh) {
- img->resize_to_po2();
- imgw = img->get_width();
- imgh = img->get_height();
- }
- } else {
- if (imgw % 4 != 0 || imgh % 4 != 0) {
- if (imgw % 4) {
- imgw += 4 - imgw % 4;
- }
- if (imgh % 4) {
- imgh += 4 - imgh % 4;
- }
-
- img->resize(imgw, imgh);
- }
- }
-
- const uint8_t *r = img->get_data().ptr();
- ERR_FAIL_COND(!r);
-
- unsigned int target_size = Image::get_image_data_size(imgw, imgh, etc_format, p_img->has_mipmaps());
- int mmc = 1 + (p_img->has_mipmaps() ? Image::get_image_required_mipmaps(imgw, imgh, etc_format) : 0);
-
- Vector<uint8_t> dst_data;
- dst_data.resize(target_size);
-
- uint8_t *w = dst_data.ptrw();
-
- // prepare parameters to be passed to etc2comp
- int num_cpus = OS::get_singleton()->get_processor_count();
- int encoding_time = 0;
- float effort = 0.0; //default, reasonable time
-
- if (p_lossy_quality > 0.75) {
- effort = 0.4;
- } else if (p_lossy_quality > 0.85) {
- effort = 0.6;
- } else if (p_lossy_quality > 0.95) {
- effort = 0.8;
- }
-
- Etc::ErrorMetric error_metric = Etc::ErrorMetric::RGBX; // NOTE: we can experiment with other error metrics
- Etc::Image::Format etc2comp_etc_format = _image_format_to_etc2comp_format(etc_format);
-
- int wofs = 0;
-
- print_verbose("ETC: Begin encoding, format: " + Image::get_format_name(etc_format));
- uint64_t t = OS::get_singleton()->get_ticks_msec();
- for (int i = 0; i < mmc; i++) {
- // convert source image to internal etc2comp format (which is equivalent to Image::FORMAT_RGBAF)
- // NOTE: We can alternatively add a case to Image::convert to handle Image::FORMAT_RGBAF conversion.
- int mipmap_ofs = 0, mipmap_size = 0, mipmap_w = 0, mipmap_h = 0;
- img->get_mipmap_offset_size_and_dimensions(i, mipmap_ofs, mipmap_size, mipmap_w, mipmap_h);
- const uint8_t *src = &r[mipmap_ofs];
-
- Etc::ColorFloatRGBA *src_rgba_f = new Etc::ColorFloatRGBA[mipmap_w * mipmap_h];
- for (int j = 0; j < mipmap_w * mipmap_h; j++) {
- int si = j * 4; // RGBA8
- src_rgba_f[j] = Etc::ColorFloatRGBA::ConvertFromRGBA8(src[si], src[si + 1], src[si + 2], src[si + 3]);
- }
-
- unsigned char *etc_data = nullptr;
- unsigned int etc_data_len = 0;
- unsigned int extended_width = 0, extended_height = 0;
- Etc::Encode((float *)src_rgba_f, mipmap_w, mipmap_h, etc2comp_etc_format, error_metric, effort, num_cpus, num_cpus, &etc_data, &etc_data_len, &extended_width, &extended_height, &encoding_time);
-
- CRASH_COND(wofs + etc_data_len > target_size);
- memcpy(&w[wofs], etc_data, etc_data_len);
- wofs += etc_data_len;
-
- delete[] etc_data;
- delete[] src_rgba_f;
- }
-
- print_verbose("ETC: Time encoding: " + rtos(OS::get_singleton()->get_ticks_msec() - t));
-
- p_img->create(imgw, imgh, p_img->has_mipmaps(), etc_format, dst_data);
-}
-
-static void _compress_etc1(Image *p_img, float p_lossy_quality) {
- _compress_etc(p_img, p_lossy_quality, true, Image::USED_CHANNELS_RGB);
-}
-
-static void _compress_etc2(Image *p_img, float p_lossy_quality, Image::UsedChannels p_channels) {
- _compress_etc(p_img, p_lossy_quality, false, p_channels);
-}
-
-void _register_etc_compress_func() {
- Image::_image_compress_etc1_func = _compress_etc1;
- Image::_image_compress_etc2_func = _compress_etc2;
-}
diff --git a/modules/etc/texture_loader_pkm.cpp b/modules/etc/texture_loader_pkm.cpp
deleted file mode 100644
index c40e9612a8..0000000000
--- a/modules/etc/texture_loader_pkm.cpp
+++ /dev/null
@@ -1,114 +0,0 @@
-/*************************************************************************/
-/* texture_loader_pkm.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "texture_loader_pkm.h"
-
-#include "core/os/file_access.h"
-#include <string.h>
-
-struct ETC1Header {
- char tag[6]; // "PKM 10"
- uint16_t format; // Format == number of mips (== zero)
- uint16_t texWidth; // Texture dimensions, multiple of 4 (big-endian)
- uint16_t texHeight;
- uint16_t origWidth; // Original dimensions (big-endian)
- uint16_t origHeight;
-};
-
-RES ResourceFormatPKM::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
- if (r_error) {
- *r_error = ERR_CANT_OPEN;
- }
-
- Error err;
- FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
- if (!f) {
- return RES();
- }
-
- FileAccessRef fref(f);
- if (r_error) {
- *r_error = ERR_FILE_CORRUPT;
- }
-
- ERR_FAIL_COND_V_MSG(err != OK, RES(), "Unable to open PKM texture file '" + p_path + "'.");
-
- // big endian
- f->set_endian_swap(true);
-
- ETC1Header h;
- f->get_buffer((uint8_t *)&h.tag, sizeof(h.tag));
- ERR_FAIL_COND_V_MSG(strncmp(h.tag, "PKM 10", sizeof(h.tag)), RES(), "Invalid or unsupported PKM texture file '" + p_path + "'.");
-
- h.format = f->get_16();
- h.texWidth = f->get_16();
- h.texHeight = f->get_16();
- h.origWidth = f->get_16();
- h.origHeight = f->get_16();
-
- Vector<uint8_t> src_data;
-
- uint32_t size = h.texWidth * h.texHeight / 2;
- src_data.resize(size);
- uint8_t *wb = src_data.ptrw();
- f->get_buffer(wb, size);
-
- int mipmaps = h.format;
- int width = h.origWidth;
- int height = h.origHeight;
-
- Ref<Image> img = memnew(Image(width, height, mipmaps, Image::FORMAT_ETC, src_data));
-
- Ref<ImageTexture> texture = memnew(ImageTexture);
- texture->create_from_image(img);
-
- if (r_error) {
- *r_error = OK;
- }
-
- f->close();
- memdelete(f);
- return texture;
-}
-
-void ResourceFormatPKM::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("pkm");
-}
-
-bool ResourceFormatPKM::handles_type(const String &p_type) const {
- return ClassDB::is_parent_class(p_type, "Texture2D");
-}
-
-String ResourceFormatPKM::get_resource_type(const String &p_path) const {
- if (p_path.get_extension().to_lower() == "pkm") {
- return "ImageTexture";
- }
- return "";
-}
diff --git a/modules/etcpak/SCsub b/modules/etcpak/SCsub
new file mode 100644
index 0000000000..2d3b69be75
--- /dev/null
+++ b/modules/etcpak/SCsub
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_etcpak = env_modules.Clone()
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+thirdparty_dir = "#thirdparty/etcpak/"
+thirdparty_sources = [
+ "Dither.cpp",
+ "ProcessDxtc.cpp",
+ "ProcessRGB.cpp",
+ "Tables.cpp",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_etcpak.Prepend(CPPPATH=[thirdparty_dir])
+
+env_thirdparty = env_etcpak.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_etcpak.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/assimp/config.py b/modules/etcpak/config.py
index 53b8f2f2e3..53b8f2f2e3 100644
--- a/modules/assimp/config.py
+++ b/modules/etcpak/config.py
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
new file mode 100644
index 0000000000..aff7538fb6
--- /dev/null
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -0,0 +1,184 @@
+/*************************************************************************/
+/* image_compress_etcpak.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 "image_compress_etcpak.h"
+
+#include "core/os/os.h"
+#include "core/string/print_string.h"
+
+#include "thirdparty/etcpak/ProcessDxtc.hpp"
+#include "thirdparty/etcpak/ProcessRGB.hpp"
+
+EtcpakType _determine_etc_type(Image::UsedChannels p_channels) {
+ switch (p_channels) {
+ case Image::USED_CHANNELS_L:
+ return EtcpakType::ETCPAK_TYPE_ETC1;
+ case Image::USED_CHANNELS_LA:
+ return EtcpakType::ETCPAK_TYPE_ETC2_ALPHA;
+ case Image::USED_CHANNELS_R:
+ return EtcpakType::ETCPAK_TYPE_ETC2;
+ case Image::USED_CHANNELS_RG:
+ return EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG;
+ case Image::USED_CHANNELS_RGB:
+ return EtcpakType::ETCPAK_TYPE_ETC2;
+ case Image::USED_CHANNELS_RGBA:
+ return EtcpakType::ETCPAK_TYPE_ETC2_ALPHA;
+ default:
+ return EtcpakType::ETCPAK_TYPE_ETC2_ALPHA;
+ }
+}
+
+EtcpakType _determine_dxt_type(Image::UsedChannels p_channels) {
+ switch (p_channels) {
+ case Image::USED_CHANNELS_L:
+ return EtcpakType::ETCPAK_TYPE_DXT1;
+ case Image::USED_CHANNELS_LA:
+ return EtcpakType::ETCPAK_TYPE_DXT5;
+ case Image::USED_CHANNELS_R:
+ return EtcpakType::ETCPAK_TYPE_DXT5;
+ case Image::USED_CHANNELS_RG:
+ return EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG;
+ case Image::USED_CHANNELS_RGB:
+ return EtcpakType::ETCPAK_TYPE_DXT5;
+ case Image::USED_CHANNELS_RGBA:
+ return EtcpakType::ETCPAK_TYPE_DXT5;
+ default:
+ return EtcpakType::ETCPAK_TYPE_DXT5;
+ }
+}
+
+void _compress_etc1(Image *r_img, float p_lossy_quality) {
+ _compress_etcpak(EtcpakType::ETCPAK_TYPE_ETC1, r_img, p_lossy_quality);
+}
+
+void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) {
+ EtcpakType type = _determine_etc_type(p_channels);
+ _compress_etcpak(type, r_img, p_lossy_quality);
+}
+
+void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels) {
+ EtcpakType type = _determine_dxt_type(p_channels);
+ _compress_etcpak(type, r_img, p_lossy_quality);
+}
+
+void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality) {
+ uint64_t start_time = OS::get_singleton()->get_ticks_msec();
+
+ // TODO: See how to handle lossy quality.
+
+ Image::Format img_format = r_img->get_format();
+ if (img_format >= Image::FORMAT_DXT1) {
+ return; // Do not compress, already compressed.
+ }
+ if (img_format > Image::FORMAT_RGBA8) {
+ // TODO: we should be able to handle FORMAT_RGBA4444 and FORMAT_RGBA5551 eventually
+ return;
+ }
+
+ // Use RGBA8 to convert.
+ if (img_format != Image::FORMAT_RGBA8) {
+ r_img->convert(Image::FORMAT_RGBA8);
+ }
+
+ // Determine output format based on Etcpak type.
+ Image::Format target_format = Image::FORMAT_RGBA8;
+ if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
+ target_format = Image::FORMAT_ETC;
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) {
+ target_format = Image::FORMAT_ETC2_RGB8;
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
+ target_format = Image::FORMAT_ETC2_RA_AS_RG;
+ r_img->convert_rg_to_ra_rgba8();
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
+ target_format = Image::FORMAT_ETC2_RGBA8;
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
+ target_format = Image::FORMAT_DXT1;
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) {
+ target_format = Image::FORMAT_DXT5_RA_AS_RG;
+ r_img->convert_rg_to_ra_rgba8();
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5) {
+ target_format = Image::FORMAT_DXT5;
+ } else {
+ ERR_FAIL_MSG("Invalid or unsupported Etcpak compression format.");
+ }
+
+ // Compress image data and (if required) mipmaps.
+
+ const bool mipmaps = r_img->has_mipmaps();
+ const int width = r_img->get_width();
+ const int height = r_img->get_height();
+ const uint8_t *src_read = r_img->get_data().ptr();
+
+ print_verbose(vformat("ETCPAK: Encoding image size %dx%d to format %s.", width, height, Image::get_format_name(target_format)));
+
+ int dest_size = Image::get_image_data_size(width, height, target_format, mipmaps);
+ Vector<uint8_t> dest_data;
+ dest_data.resize(dest_size);
+ uint8_t *dest_write = dest_data.ptrw();
+
+ int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
+
+ for (int i = 0; i < mip_count + 1; i++) {
+ // Get write mip metrics for target image.
+ int mip_w, mip_h;
+ int mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, mip_w, mip_h);
+ // Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer).
+ ERR_FAIL_COND(mip_ofs % 8 != 0);
+ uint64_t *dest_mip_write = (uint64_t *)&dest_write[mip_ofs];
+
+ // Block size. Align stride to multiple of 4 (RGBA8).
+ mip_w = (mip_w + 3) & ~3;
+ mip_h = (mip_h + 3) & ~3;
+ const uint32_t blocks = mip_w * mip_h / 16;
+
+ // Get mip data from source image for reading.
+ int src_mip_ofs = r_img->get_mipmap_offset(i);
+ const uint32_t *src_mip_read = (const uint32_t *)&src_read[src_mip_ofs];
+
+ 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, true);
+ } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
+ 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) {
+ CompressDxt5(src_mip_read, dest_mip_write, blocks, mip_w);
+ } else {
+ ERR_FAIL_MSG("Invalid or unsupported Etcpak compression format.");
+ }
+ }
+
+ // Replace original image with compressed one.
+ r_img->create(width, height, mipmaps, target_format, dest_data);
+
+ print_verbose(vformat("ETCPAK encode took %s ms.", rtos(OS::get_singleton()->get_ticks_msec() - start_time)));
+}
diff --git a/modules/etcpak/image_compress_etcpak.h b/modules/etcpak/image_compress_etcpak.h
new file mode 100644
index 0000000000..ccf157fada
--- /dev/null
+++ b/modules/etcpak/image_compress_etcpak.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* image_compress_etcpak.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 IMAGE_COMPRESS_ETCPAK_H
+#define IMAGE_COMPRESS_ETCPAK_H
+
+#include "core/io/image.h"
+
+enum class EtcpakType {
+ ETCPAK_TYPE_ETC1,
+ ETCPAK_TYPE_ETC2,
+ ETCPAK_TYPE_ETC2_ALPHA,
+ ETCPAK_TYPE_ETC2_RA_AS_RG,
+ ETCPAK_TYPE_DXT1,
+ ETCPAK_TYPE_DXT5,
+ ETCPAK_TYPE_DXT5_RA_AS_RG,
+};
+
+void _compress_etc1(Image *r_img, float p_lossy_quality);
+void _compress_etc2(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels);
+void _compress_bc(Image *r_img, float p_lossy_quality, Image::UsedChannels p_channels);
+
+void _compress_etcpak(EtcpakType p_compresstype, Image *r_img, float p_lossy_quality);
+
+#endif // IMAGE_COMPRESS_ETCPAK_H
diff --git a/modules/gdnative/xr/register_types.cpp b/modules/etcpak/register_types.cpp
index da3a7dc4b8..d57d2f747a 100644
--- a/modules/gdnative/xr/register_types.cpp
+++ b/modules/etcpak/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,12 +29,14 @@
/*************************************************************************/
#include "register_types.h"
-#include "xr_interface_gdnative.h"
-void register_xr_types() {
- ClassDB::register_class<XRInterfaceGDNative>();
- ClassDB::add_compatibility_class("ARVRInterfaceGDNative", "XRInterfaceGDNative");
+#include "image_compress_etcpak.h"
+
+void register_etcpak_types() {
+ Image::_image_compress_etc1_func = _compress_etc1;
+ Image::_image_compress_etc2_func = _compress_etc2;
+ Image::_image_compress_bc_func = _compress_bc;
}
-void unregister_xr_types() {
+void unregister_etcpak_types() {
}
diff --git a/modules/assimp/register_types.h b/modules/etcpak/register_types.h
index f399a7acc6..a9e10a4aae 100644
--- a/modules/assimp/register_types.h
+++ b/modules/etcpak/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,10 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef ASSIMP_REGISTER_TYPES_H
-#define ASSIMP_REGISTER_TYPES_H
+#ifndef ETCPAK_REGISTER_TYPES_H
+#define ETCPAK_REGISTER_TYPES_H
-void register_assimp_types();
-void unregister_assimp_types();
+void register_etcpak_types();
+void unregister_etcpak_types();
-#endif // ASSIMP_REGISTER_TYPES_H
+#endif // ETCPAK_REGISTER_TYPES_H
diff --git a/modules/fbx/README.md b/modules/fbx/README.md
new file mode 100644
index 0000000000..8eca4bd3c9
--- /dev/null
+++ b/modules/fbx/README.md
@@ -0,0 +1,196 @@
+# Open Source FBX Specification for the Importer
+
+The goal of this document is to make everything in FBX clearly stated, any errors will be corrected over time this
+is a first draft.
+
+## fbx parser - originally from assimp
+
+- Folder: /modules/fbx/fbx_parser
+- Upstream: assimp
+- Original Version: git (308db73d0b3c2d1870cd3e465eaa283692a4cf23, 2019)
+- License: BSD-3-Clause
+
+This can never be updated from upstream, we have heavily modified the parser to provide memory safety and add some
+functionality. If anything we should give this parser back to assimp at some point as it has a lot of new features.
+
+# Updating assimp fbx parser
+
+Don't. It's not possible the code is rewritten in many areas to remove thirdparty deps and various bugs are fixed.
+
+Many days were put into rewriting the parser to use safe code and safe memory accessors.
+
+# File Headers
+
+FBX Binaries start with the header "Kaydara FBX Binary"
+
+FBX ASCII documents contain a larger header, sometimes with copyright information for a file.
+
+Detecting these is pretty simple.
+
+# What is an OP link?
+It's an object to property link. It lists the properties for that object in some cases. Source and destination based by
+ID.
+
+# What is a OO link?
+Its an object to object link, it contains the ID source and destination ID.
+
+# FBX Node connections
+
+Nodes in FBX are connected using OO links, This means Object to Object.
+
+FBX has a single other kind of link which is Object Property, this is used for Object to Property Links, this can be
+ extra attributes, defaults, or even some simple settings.
+
+# Bones / Joints / Locators
+
+Bones in FBX are nodes, they initially have the Model:: Type, then have links to SubDeformer the sub deformer
+is part of the skin there is also an explicit Skin link, which then links to the geometry using OO links in the
+document.
+
+# Rotation Order in FBX compared to Godot
+
+**Godot uses the rotation order:** YXZ
+
+**FBX has dynamic rotation order to prevent gimbal lock with complex animations**
+
+```cpp
+enum RotOrder {
+ RotOrder_EulerXYZ = 0
+ RotOrder_EulerXZY,
+ RotOrder_EulerYZX,
+ RotOrder_EulerYXZ,
+ RotOrder_EulerZXY,
+ RotOrder_EulerZYX,
+ RotOrder_SphericXYZ // nobody uses this - as far as we can tell
+};
+```
+
+
+# Pivot transforms
+
+### Pivot description:
+- Maya and 3DS max consider everything to be in node space (bones joints, skins, lights, cameras, etc)
+- Everything is a node, this means essentially nodes are auto or variants
+- They are local to the node in the tree.
+- They are used to calculate where a node is in space
+```c++
+// For a better reference you can check editor_scene_importer_fbx.h
+// references: GenFBXTransform / read the data in
+// references: ComputePivotTransform / run the calculation
+// This is the local pivot transform for the node, not the global transforms
+Transform ComputePivotTransform(
+ Transform3D chain[TransformationComp_MAXIMUM],
+ Transform3D &geometric_transform) {
+ // Maya pivots
+ Transform3D T = chain[TransformationComp_Translation];
+ Transform3D Roff = chain[TransformationComp_RotationOffset];
+ Transform3D Rp = chain[TransformationComp_RotationPivot];
+ Transform3D Rpre = chain[TransformationComp_PreRotation];
+ Transform3D R = chain[TransformationComp_Rotation];
+ Transform3D Rpost = chain[TransformationComp_PostRotation];
+ Transform3D Soff = chain[TransformationComp_ScalingOffset];
+ Transform3D Sp = chain[TransformationComp_ScalingPivot];
+ Transform3D S = chain[TransformationComp_Scaling];
+
+ // 3DS Max Pivots
+ Transform3D OT = chain[TransformationComp_GeometricTranslation];
+ Transform3D OR = chain[TransformationComp_GeometricRotation];
+ Transform3D OS = chain[TransformationComp_GeometricScaling];
+
+ // Calculate 3DS max pivot transform - use geometric space (e.g doesn't effect children nodes only the current node)
+ geometric_transform = OT * OR * OS;
+ // Calculate standard maya pivots
+ return T * Roff * Rp * Rpre * R * Rpost.inverse() * Rp.inverse() * Soff * Sp * S * Sp.inverse();
+}
+```
+
+# Transform inheritance for FBX Nodes
+
+The goal of below is to explain why they implement this in the first place.
+
+The use case is to make nodes have an option to override their local scaling or to make scaling influenced by orientation, which i would imagine would be useful for when you need to rotate a node and the child to scale based on the orientation rather than setting on the rotation matrix planes.
+```cpp
+// not modified the formatting here since this code must remain clear
+enum TransformInheritance {
+ Transform_RrSs = 0,
+ // Parent Rotation * Local Rotation * Parent Scale * Local Scale -- Parent Rotation Offset * Parent ScalingOffset (Local scaling is offset by rotation of parent node)
+ Transform_RSrs = 1, // Parent Rotation * Parent Scale * Local Rotation * Local Scale -- Parent * Local (normal mode)
+ Transform_Rrs = 2, // Parent Rotation * Local Rotation * Local Scale -- Node transform scale is the only relevant component
+ TransformInheritance_MAX // end-of-enum sentinel
+};
+
+enum TransformInheritance {
+ Transform_RrSs = 0,
+ // Local scaling is offset by rotation of parent node
+ Transform_RSrs = 1,
+ // Parent * Local (normal mode)
+ Transform_Rrs = 2,
+ // Node transform scale is the only relevant component
+ TransformInheritance_MAX // end-of-enum sentinel
+};
+```
+
+# Axis in FBX
+
+Godot has one format for the declared axis
+
+AXIS X, AXIS Y, -AXIS Z
+
+FBX supports any format you can think of. As it has to support Maya and 3DS Max.
+
+#### FBX File Header
+```json
+GlobalSettings: {
+ Version: 1000
+ Properties70: {
+ P: "UpAxis", "int", "Integer", "",1
+ P: "UpAxisSign", "int", "Integer", "",1
+ P: "FrontAxis", "int", "Integer", "",2
+ P: "FrontAxisSign", "int", "Integer", "",1
+ P: "CoordAxis", "int", "Integer", "",0
+ P: "CoordAxisSign", "int", "Integer", "",1
+ P: "OriginalUpAxis", "int", "Integer", "",1
+ P: "OriginalUpAxisSign", "int", "Integer", "",1
+ P: "UnitScaleFactor", "double", "Number", "",1
+ P: "OriginalUnitScaleFactor", "double", "Number", "",1
+ P: "AmbientColor", "ColorRGB", "Color", "",0,0,0
+ P: "DefaultCamera", "KString", "", "", "Producer Perspective"
+ P: "TimeMode", "enum", "", "",6
+ P: "TimeProtocol", "enum", "", "",2
+ P: "SnapOnFrameMode", "enum", "", "",0
+ P: "TimeSpanStart", "KTime", "Time", "",0
+ P: "TimeSpanStop", "KTime", "Time", "",92372316000
+ P: "CustomFrameRate", "double", "Number", "",-1
+ P: "TimeMarker", "Compound", "", ""
+ P: "CurrentTimeMarker", "int", "Integer", "",-1
+ }
+}
+```
+
+#### FBX FILE declares axis dynamically using FBX header
+Coord is X
+Up is Y
+Front is Z
+
+#### GODOT - constant reference point
+Coord is X positive,
+Y is up positive,
+Front is -Z negative
+
+### Explaining MeshGeometry indexing
+
+Reference type declared:
+- Direct (directly related to the mapping information type)
+- IndexToDirect (Map with key value, meaning depends on the MappingInformationType)
+
+ControlPoint is a vertex
+* None The mapping is undetermined.
+* ByVertex There will be one mapping coordinate for each surface control point/vertex.
+ * If you have direct reference type vertices [x]
+ * If you have IndexToDirect reference type the UV
+* ByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part. (Sorted by polygon, referencing vertex)
+* ByPolygon There can be only one mapping coordinate for the whole polygon.
+ * One mapping per polygon polygon x has this normal x
+ * For each vertex of the polygon then set the normal to x
+* ByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements. (Mapping is referencing the edge id)
+* AllSame There can be only one mapping coordinate for the whole surface.
diff --git a/modules/fbx/SCsub b/modules/fbx/SCsub
new file mode 100644
index 0000000000..0311fddfee
--- /dev/null
+++ b/modules/fbx/SCsub
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_fbx = env_modules.Clone()
+
+# Make includes relative to the folder path specified here so our includes are clean
+env_fbx.Prepend(CPPPATH=["#modules/fbx/"])
+
+if env["builtin_zlib"]:
+ env_fbx.Prepend(CPPPATH=["#thirdparty/zlib/"])
+
+# Godot's own source files
+env_fbx.add_source_files(env.modules_sources, "tools/*.cpp")
+env_fbx.add_source_files(env.modules_sources, "data/*.cpp")
+env_fbx.add_source_files(env.modules_sources, "fbx_parser/*.cpp")
+env_fbx.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/fbx/config.py b/modules/fbx/config.py
new file mode 100644
index 0000000000..78929800b3
--- /dev/null
+++ b/modules/fbx/config.py
@@ -0,0 +1,16 @@
+def can_build(env, platform):
+ return env["tools"]
+
+
+def configure(env):
+ pass
+
+
+def get_doc_classes():
+ return [
+ "EditorSceneImporterFBX",
+ ]
+
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/fbx/data/fbx_anim_container.h b/modules/fbx/data/fbx_anim_container.h
new file mode 100644
index 0000000000..8c25d65871
--- /dev/null
+++ b/modules/fbx/data/fbx_anim_container.h
@@ -0,0 +1,46 @@
+/*************************************************************************/
+/* fbx_anim_container.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 FBX_ANIM_CONTAINER_H
+#define FBX_ANIM_CONTAINER_H
+
+#include "core/math/vector3.h"
+
+// Generic keyframes 99.99 percent of files will be vector3, except if quat interp is used, or visibility tracks
+// FBXTrack is used in a map in the implementation in fbx/editor_scene_importer_fbx.cpp
+// to avoid having to rewrite the entire logic I refactored this into the code instead.
+// once it works I can rewrite so we can add the fun misc features / small features
+struct FBXTrack {
+ bool has_default = false;
+ Vector3 default_value;
+ std::map<int64_t, Vector3> keyframes;
+};
+
+#endif //MODEL_ABSTRACTION_ANIM_CONTAINER_H
diff --git a/modules/arkit/arkit_session_delegate.mm b/modules/fbx/data/fbx_bone.cpp
index f44f46b7b7..38dada33af 100644
--- a/modules/arkit/arkit_session_delegate.mm
+++ b/modules/fbx/data/fbx_bone.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* arkit_session_delegate.mm */
+/* fbx_bone.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,29 +28,29 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "arkit_session_delegate.h"
-#include "arkit_interface.h"
+#include "fbx_bone.h"
-@implementation ARKitSessionDelegate
+#include "fbx_node.h"
+#include "import_state.h"
-@synthesize arkit_interface;
+Ref<FBXNode> FBXSkinDeformer::get_link(const ImportState &state) const {
+ print_verbose("bone name: " + bone->bone_name);
-- (void)session:(ARSession *)session didAddAnchors:(NSArray<ARAnchor *> *)anchors {
- for (ARAnchor *anchor in anchors) {
- arkit_interface->_add_or_update_anchor(anchor);
+ // safe for when deformers must be polyfilled when skin has different count of binds to bones in the scene ;)
+ if (!cluster) {
+ return nullptr;
}
-}
-- (void)session:(ARSession *)session didRemoveAnchors:(NSArray<ARAnchor *> *)anchors {
- for (ARAnchor *anchor in anchors) {
- arkit_interface->_remove_anchor(anchor);
- }
-}
+ ERR_FAIL_COND_V_MSG(cluster->TargetNode() == nullptr, nullptr, "bone has invalid target node");
-- (void)session:(ARSession *)session didUpdateAnchors:(NSArray<ARAnchor *> *)anchors {
- for (ARAnchor *anchor in anchors) {
- arkit_interface->_add_or_update_anchor(anchor);
+ Ref<FBXNode> link_node;
+ uint64_t id = cluster->TargetNode()->ID();
+ if (state.fbx_target_map.has(id)) {
+ link_node = state.fbx_target_map[id];
+ } else {
+ print_error("link node not found for " + itos(id));
}
-}
-@end
+ // the node in space this is for, like if it's FOR a target.
+ return link_node;
+}
diff --git a/modules/fbx/data/fbx_bone.h b/modules/fbx/data/fbx_bone.h
new file mode 100644
index 0000000000..83918ad1e2
--- /dev/null
+++ b/modules/fbx/data/fbx_bone.h
@@ -0,0 +1,90 @@
+/*************************************************************************/
+/* fbx_bone.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 FBX_BONE_H
+#define FBX_BONE_H
+
+#include "fbx_node.h"
+#include "import_state.h"
+
+#include "fbx_parser/FBXDocument.h"
+
+struct PivotTransform;
+
+struct FBXBone : public RefCounted {
+ uint64_t parent_bone_id = 0;
+ uint64_t bone_id = 0;
+
+ bool valid_parent = false; // if the parent bone id is set up.
+ String bone_name = String(); // bone name
+
+ bool is_root_bone() const {
+ return !valid_parent;
+ }
+
+ // Godot specific data
+ int godot_bone_id = -2; // godot internal bone id assigned after import
+
+ // if a bone / armature is the root then FBX skeleton will contain the bone not any other skeleton.
+ // this is to support joints by themselves in scenes
+ bool valid_armature_id = false;
+ uint64_t armature_id = 0;
+
+ /* link node is the parent bone */
+ mutable const FBXDocParser::Geometry *geometry = nullptr;
+ mutable const FBXDocParser::ModelLimbNode *limb_node = nullptr;
+
+ void set_node(Ref<FBXNode> p_node) {
+ node = p_node;
+ }
+
+ // Stores the pivot xform for this bone
+
+ Ref<FBXNode> node = nullptr;
+ Ref<FBXBone> parent_bone = nullptr;
+ Ref<FBXSkeleton> fbx_skeleton = nullptr;
+};
+
+struct FBXSkinDeformer {
+ FBXSkinDeformer(Ref<FBXBone> p_bone, const FBXDocParser::Cluster *p_cluster) :
+ cluster(p_cluster), bone(p_bone) {}
+ ~FBXSkinDeformer() {}
+ const FBXDocParser::Cluster *cluster;
+ Ref<FBXBone> bone;
+
+ /* get associate model - the model can be invalid sometimes */
+ Ref<FBXBone> get_associate_model() const {
+ return bone->parent_bone;
+ }
+
+ Ref<FBXNode> get_link(const ImportState &state) const;
+};
+
+#endif // FBX_BONE_H
diff --git a/modules/fbx/data/fbx_material.cpp b/modules/fbx/data/fbx_material.cpp
new file mode 100644
index 0000000000..86baec4244
--- /dev/null
+++ b/modules/fbx/data/fbx_material.cpp
@@ -0,0 +1,468 @@
+/*************************************************************************/
+/* fbx_material.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 "fbx_material.h"
+
+// FIXME: Shouldn't depend on core_bind.h! Use DirAccessRef like the rest of
+// the engine instead of core_bind::Directory.
+#include "core/core_bind.h"
+#include "scene/resources/material.h"
+#include "scene/resources/texture.h"
+#include "tools/validation_tools.h"
+
+String FBXMaterial::get_material_name() const {
+ return material_name;
+}
+
+void FBXMaterial::set_imported_material(FBXDocParser::Material *p_material) {
+ material = p_material;
+}
+
+void FBXMaterial::add_search_string(String p_filename, String p_current_directory, String search_directory, Vector<String> &texture_search_paths) {
+ if (search_directory.is_empty()) {
+ texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file(p_filename));
+ } else {
+ texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file(search_directory + "/" + p_filename));
+ texture_search_paths.push_back(p_current_directory.get_base_dir().plus_file("../" + search_directory + "/" + p_filename));
+ }
+}
+
+String find_file(const String &p_base, const String &p_file_to_find) {
+ core_bind::Directory dir;
+ dir.open(p_base);
+
+ dir.list_dir_begin();
+ String n = dir.get_next();
+ while (n != String()) {
+ if (n == "." || n == "..") {
+ n = dir.get_next();
+ continue;
+ }
+ 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 != "") {
+ return f;
+ }
+ } else if (n == p_file_to_find) {
+ return p_base + "/" + n;
+ }
+ n = dir.get_next();
+ }
+ dir.list_dir_end();
+
+ return String();
+}
+
+// fbx will not give us good path information and let's not regex them to fix them
+// no relative paths are in fbx generally they have a rel field but it's populated incorrectly by the SDK.
+String FBXMaterial::find_texture_path_by_filename(const String p_filename, const String p_current_directory) {
+ core_bind::Directory dir;
+ Vector<String> paths;
+ add_search_string(p_filename, p_current_directory, "", paths);
+ add_search_string(p_filename, p_current_directory, "texture", paths);
+ add_search_string(p_filename, p_current_directory, "textures", paths);
+ add_search_string(p_filename, p_current_directory, "Textures", paths);
+ add_search_string(p_filename, p_current_directory, "materials", paths);
+ add_search_string(p_filename, p_current_directory, "mats", paths);
+ add_search_string(p_filename, p_current_directory, "pictures", paths);
+ add_search_string(p_filename, p_current_directory, "images", paths);
+
+ for (int i = 0; i < paths.size(); i++) {
+ if (dir.file_exists(paths[i])) {
+ return paths[i];
+ }
+ }
+
+ // We were not able to find the texture in the common locations,
+ // try to find it into the project globally.
+ // The common textures can be stored into one of those folders:
+ // res://asset
+ // res://texture
+ // res://material
+ // res://mat
+ // res://image
+ // res://picture
+ //
+ // Note the folders can also be called with custom names, like:
+ // res://my_assets
+ // since the keyword `asset` is into the directory name the textures will be
+ // searched there too.
+
+ dir.open("res://");
+ dir.list_dir_begin();
+ String n = dir.get_next();
+ while (n != String()) {
+ if (n == "." || n == "..") {
+ n = dir.get_next();
+ continue;
+ }
+ if (dir.current_is_dir()) {
+ const String lower_n = n.to_lower();
+ if (
+ // Don't need to use plural.
+ lower_n.find("asset") >= 0 ||
+ lower_n.find("texture") >= 0 ||
+ lower_n.find("material") >= 0 ||
+ lower_n.find("mat") >= 0 ||
+ lower_n.find("image") >= 0 ||
+ 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 != "") {
+ return f;
+ }
+ }
+ }
+ n = dir.get_next();
+ }
+ dir.list_dir_end();
+
+ return "";
+}
+
+template <class T>
+T extract_from_prop(FBXDocParser::PropertyPtr prop, const T &p_default, const std::string &p_name, const String &p_type) {
+ ERR_FAIL_COND_V_MSG(prop == nullptr, p_default, "invalid property passed to extractor");
+ const FBXDocParser::TypedProperty<T> *val = dynamic_cast<const FBXDocParser::TypedProperty<T> *>(prop);
+
+ ERR_FAIL_COND_V_MSG(val == nullptr, p_default, "The FBX is corrupted, the property `" + String(p_name.c_str()) + "` is a `" + String(typeid(*prop).name()) + "` but should be a " + p_type);
+ // Make sure to not lost any eventual opacity.
+ return val->Value();
+}
+
+Ref<StandardMaterial3D> FBXMaterial::import_material(ImportState &state) {
+ ERR_FAIL_COND_V(material == nullptr, nullptr);
+
+ const String p_fbx_current_directory = state.path;
+
+ Ref<StandardMaterial3D> spatial_material;
+ spatial_material.instantiate();
+
+ // read the material file
+ // is material two sided
+ // read material name
+ print_verbose("[material] material name: " + ImportUtils::FBXNodeToName(material->Name()));
+
+ material_name = ImportUtils::FBXNodeToName(material->Name());
+
+ for (const std::pair<std::string, const FBXDocParser::Texture *> iter : material->Textures()) {
+ const uint64_t texture_id = iter.second->ID();
+ const std::string &fbx_mapping_name = iter.first;
+ const FBXDocParser::Texture *fbx_texture_data = iter.second;
+ const String absolute_texture_path = iter.second->FileName().c_str();
+ const String texture_name = absolute_texture_path.get_file();
+ const String file_extension = absolute_texture_path.get_extension().to_upper();
+
+ const String debug_string = "texture id: " + itos(texture_id) + " texture name: " + String(iter.second->Name().c_str()) + " mapping name: " + String(fbx_mapping_name.c_str());
+ // remember errors STILL need this string at the end for when you aren't in verbose debug mode :) they need context for when you're not verbose-ing.
+ print_verbose(debug_string);
+
+ const String file_extension_uppercase = file_extension.to_upper();
+
+ if (fbx_transparency_flags.count(fbx_mapping_name) > 0) {
+ // just enable it later let's make this fine-tuned.
+ spatial_material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA);
+ }
+
+ ERR_CONTINUE_MSG(file_extension.is_empty(), "your texture has no file extension so we had to ignore it, let us know if you think this is wrong file an issue on github! " + debug_string);
+ ERR_CONTINUE_MSG(fbx_texture_map.count(fbx_mapping_name) <= 0, "This material has a texture with mapping name: " + String(fbx_mapping_name.c_str()) + " which is not yet supported by this importer. Consider opening an issue so we can support it.");
+ ERR_CONTINUE_MSG(
+ file_extension_uppercase != "PNG" &&
+ file_extension_uppercase != "JPEG" &&
+ file_extension_uppercase != "JPG" &&
+ file_extension_uppercase != "TGA" &&
+ file_extension_uppercase != "WEBP" &&
+ file_extension_uppercase != "DDS",
+ "The FBX file contains a texture with an unrecognized extension: " + file_extension_uppercase);
+
+ print_verbose("Getting FBX mapping mode for " + String(fbx_mapping_name.c_str()));
+ // get the texture map type
+ const StandardMaterial3D::TextureParam mapping_mode = fbx_texture_map.at(fbx_mapping_name);
+ print_verbose("Set FBX mapping mode to " + get_texture_param_name(mapping_mode));
+
+ Ref<Texture> texture;
+ print_verbose("texture mapping name: " + texture_name);
+
+ if (state.cached_image_searches.has(texture_name)) {
+ texture = state.cached_image_searches[texture_name];
+ } else {
+ String path = find_texture_path_by_filename(texture_name, p_fbx_current_directory);
+ if (!path.is_empty()) {
+ Ref<Texture2D> image_texture = ResourceLoader::load(path);
+
+ ERR_CONTINUE(image_texture.is_null());
+
+ texture = image_texture;
+ state.cached_image_searches.insert(texture_name, texture);
+ print_verbose("Created texture from loaded image file.");
+
+ } else if (fbx_texture_data != nullptr && fbx_texture_data->Media() != nullptr && fbx_texture_data->Media()->IsEmbedded()) {
+ // This is an embedded texture. Extract it.
+ Ref<Image> image;
+ //image.instantiate(); // oooo double instance bug? why make Image::_png_blah call
+
+ const String extension = texture_name.get_extension().to_upper();
+ if (extension == "PNG") {
+ // The stored file is a PNG.
+ image = Image::_png_mem_loader_func(fbx_texture_data->Media()->Content(), fbx_texture_data->Media()->ContentLength());
+ ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded PNG image load fail.");
+
+ } else if (
+ extension == "JPEG" ||
+ extension == "JPG") {
+ // The stored file is a JPEG.
+ image = Image::_jpg_mem_loader_func(fbx_texture_data->Media()->Content(), fbx_texture_data->Media()->ContentLength());
+ ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded JPEG image load fail.");
+
+ } else if (extension == "TGA") {
+ // The stored file is a TGA.
+ image = Image::_tga_mem_loader_func(fbx_texture_data->Media()->Content(), fbx_texture_data->Media()->ContentLength());
+ ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded TGA image load fail.");
+
+ } else if (extension == "WEBP") {
+ // The stored file is a WEBP.
+ image = Image::_webp_mem_loader_func(fbx_texture_data->Media()->Content(), fbx_texture_data->Media()->ContentLength());
+ ERR_CONTINUE_MSG(image.is_valid() == false, "FBX Embedded WEBP image load fail.");
+
+ // } else if (extension == "DDS") {
+ // // In this moment is not possible to extract a DDS from a buffer, TODO consider add it to godot. See `textureloader_dds.cpp::load().
+ // // The stored file is a DDS.
+ } else {
+ ERR_CONTINUE_MSG(true, "The embedded image with extension: " + extension + " is not yet supported. Open an issue please.");
+ }
+
+ Ref<ImageTexture> image_texture;
+ image_texture.instantiate();
+ image_texture->create_from_image(image);
+
+ texture = image_texture;
+
+ // TODO: this is potentially making something with the same name have a match incorrectly USE FBX ID as Hash. #fuck it later.
+ state.cached_image_searches[texture_name] = texture;
+ print_verbose("Created texture from embedded image.");
+ } else {
+ ERR_CONTINUE_MSG(true, "The FBX texture, with name: `" + texture_name + "`, is not found into the project nor is stored as embedded file. Make sure to insert the texture as embedded file or into the project, then reimport.");
+ }
+ }
+
+ spatial_material->set_texture(mapping_mode, texture);
+ }
+
+ if (spatial_material.is_valid()) {
+ spatial_material->set_name(material_name);
+ }
+
+ /// ALL below is related to properties
+ for (FBXDocParser::LazyPropertyMap::value_type iter : material->GetLazyProperties()) {
+ const std::string name = iter.first;
+
+ if (name.empty()) {
+ continue;
+ }
+
+ PropertyDesc desc = PROPERTY_DESC_NOT_FOUND;
+ if (fbx_properties_desc.count(name) > 0) {
+ desc = fbx_properties_desc.at(name);
+ }
+
+ // check if we can ignore this it will be done at the next phase
+ if (desc == PROPERTY_DESC_NOT_FOUND || desc == PROPERTY_DESC_IGNORE) {
+ // count the texture mapping references. Skip this one if it's found and we can't look up a property value.
+ if (fbx_texture_map.count(name) > 0) {
+ continue; // safe to ignore it's a texture mapping.
+ }
+ }
+
+ if (desc == PROPERTY_DESC_IGNORE) {
+ //WARN_PRINT("[Ignored] The FBX material parameter: `" + String(name.c_str()) + "` is ignored.");
+ continue;
+ } else {
+ print_verbose("FBX Material parameter: " + String(name.c_str()));
+
+ // Check for Diffuse material system / lambert materials / legacy basically
+ if (name == "Diffuse" && !warning_non_pbr_material) {
+ ValidationTracker::get_singleton()->add_validation_error(state.path, "Invalid material settings change to Ai Standard Surface shader, mat name: " + material_name.c_escape());
+ warning_non_pbr_material = true;
+ }
+ }
+
+ // DISABLE when adding support for all weird and wonderful material formats
+ if (desc == PROPERTY_DESC_NOT_FOUND) {
+ continue;
+ }
+
+ ERR_CONTINUE_MSG(desc == PROPERTY_DESC_NOT_FOUND, "The FBX material parameter: `" + String(name.c_str()) + "` was not recognized. Please open an issue so we can add the support to it.");
+
+ const FBXDocParser::PropertyTable *tbl = material;
+ FBXDocParser::PropertyPtr prop = tbl->Get(name);
+
+ ERR_CONTINUE_MSG(prop == nullptr, "This file may be corrupted because is not possible to extract the material parameter: " + String(name.c_str()));
+
+ if (spatial_material.is_null()) {
+ // Done here so if no data no material is created.
+ spatial_material.instantiate();
+ }
+
+ const FBXDocParser::TypedProperty<real_t> *real_value = dynamic_cast<const FBXDocParser::TypedProperty<real_t> *>(prop);
+ const FBXDocParser::TypedProperty<Vector3> *vector_value = dynamic_cast<const FBXDocParser::TypedProperty<Vector3> *>(prop);
+
+ if (!real_value && !vector_value) {
+ //WARN_PRINT("unsupported datatype in property: " + String(name.c_str()));
+ continue;
+ }
+
+ if (vector_value && !real_value) {
+ if (vector_value->Value() == Vector3(0, 0, 0) && !real_value) {
+ continue;
+ }
+ }
+
+ switch (desc) {
+ case PROPERTY_DESC_ALBEDO_COLOR: {
+ if (vector_value) {
+ const Vector3 &color = vector_value->Value();
+ // Make sure to not lost any eventual opacity.
+ if (color != Vector3(0, 0, 0)) {
+ Color c = Color();
+ c[0] = color[0];
+ c[1] = color[1];
+ c[2] = color[2];
+ spatial_material->set_albedo(c);
+ }
+
+ } else if (real_value) {
+ print_error("albedo is unsupported format?");
+ }
+ } break;
+ case PROPERTY_DESC_TRANSPARENT: {
+ if (real_value) {
+ const real_t opacity = real_value->Value();
+ if (opacity < (1.0 - CMP_EPSILON)) {
+ Color c = spatial_material->get_albedo();
+ c.a = opacity;
+ spatial_material->set_albedo(c);
+
+ spatial_material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA);
+ spatial_material->set_depth_draw_mode(BaseMaterial3D::DEPTH_DRAW_OPAQUE_ONLY);
+ }
+ } else if (vector_value) {
+ print_error("unsupported transparent desc type vector!");
+ }
+ } break;
+ case PROPERTY_DESC_SPECULAR: {
+ if (real_value) {
+ print_verbose("specular real value: " + rtos(real_value->Value()));
+ spatial_material->set_specular(MIN(1.0, real_value->Value()));
+ }
+
+ if (vector_value) {
+ print_error("unsupported specular vector value: " + vector_value->Value());
+ }
+ } break;
+
+ case PROPERTY_DESC_SPECULAR_COLOR: {
+ if (vector_value) {
+ print_error("unsupported specular color: " + vector_value->Value());
+ }
+ } break;
+ case PROPERTY_DESC_SHINYNESS: {
+ if (real_value) {
+ print_error("unsupported shinyness:" + rtos(real_value->Value()));
+ }
+ } break;
+ case PROPERTY_DESC_METALLIC: {
+ if (real_value) {
+ print_verbose("metallic real value: " + rtos(real_value->Value()));
+ spatial_material->set_metallic(MIN(1.0f, real_value->Value()));
+ } else {
+ print_error("unsupported value type for metallic");
+ }
+ } break;
+ case PROPERTY_DESC_ROUGHNESS: {
+ if (real_value) {
+ print_verbose("roughness real value: " + rtos(real_value->Value()));
+ spatial_material->set_roughness(MIN(1.0f, real_value->Value()));
+ } else {
+ print_error("unsupported value type for roughness");
+ }
+ } break;
+ case PROPERTY_DESC_COAT: {
+ if (real_value) {
+ print_verbose("clearcoat real value: " + rtos(real_value->Value()));
+ spatial_material->set_clearcoat(MIN(1.0f, real_value->Value()));
+ } else {
+ print_error("unsupported value type for clearcoat");
+ }
+ } break;
+ case PROPERTY_DESC_COAT_ROUGHNESS: {
+ // meaning is that approx equal to zero is disabled not actually zero. ;)
+ if (real_value && Math::is_zero_approx(real_value->Value())) {
+ print_verbose("clearcoat real value: " + rtos(real_value->Value()));
+ spatial_material->set_clearcoat_gloss(1.0 - real_value->Value());
+ } else {
+ print_error("unsupported value type for clearcoat gloss");
+ }
+ } break;
+ case PROPERTY_DESC_EMISSIVE: {
+ if (real_value && Math::is_zero_approx(real_value->Value())) {
+ print_verbose("Emissive real value: " + rtos(real_value->Value()));
+ spatial_material->set_emission_energy(real_value->Value());
+ } else if (vector_value && !vector_value->Value().is_equal_approx(Vector3(0, 0, 0))) {
+ const Vector3 &color = vector_value->Value();
+ Color c;
+ c[0] = color[0];
+ c[1] = color[1];
+ c[2] = color[2];
+ spatial_material->set_emission(c);
+ }
+ } break;
+ case PROPERTY_DESC_EMISSIVE_COLOR: {
+ if (vector_value && !vector_value->Value().is_equal_approx(Vector3(0, 0, 0))) {
+ const Vector3 &color = vector_value->Value();
+ Color c;
+ c[0] = color[0];
+ c[1] = color[1];
+ c[2] = color[2];
+ spatial_material->set_emission(c);
+ } else {
+ print_error("unsupported value type for emissive color");
+ }
+ } break;
+ case PROPERTY_DESC_NOT_FOUND:
+ case PROPERTY_DESC_IGNORE:
+ break;
+ default:
+ break;
+ }
+ }
+
+ return spatial_material;
+}
diff --git a/modules/fbx/data/fbx_material.h b/modules/fbx/data/fbx_material.h
new file mode 100644
index 0000000000..5fd4d9212b
--- /dev/null
+++ b/modules/fbx/data/fbx_material.h
@@ -0,0 +1,285 @@
+/*************************************************************************/
+/* fbx_material.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 FBX_MATERIAL_H
+#define FBX_MATERIAL_H
+
+#include "tools/import_utils.h"
+
+#include "core/object/ref_counted.h"
+#include "core/string/ustring.h"
+
+struct FBXMaterial : public RefCounted {
+ String material_name = String();
+ bool warning_non_pbr_material = false;
+ FBXDocParser::Material *material = nullptr;
+
+ /* Godot materials
+ *** Texture Maps:
+ * Albedo - color, texture
+ * Metallic - specular, metallic, texture
+ * Roughness - roughness, texture
+ * Emission - color, texture
+ * Normal Map - scale, texture
+ * Ambient Occlusion - texture
+ * Refraction - scale, texture
+ *** Has Settings for:
+ * UV1 - SCALE, OFFSET
+ * UV2 - SCALE, OFFSET
+ *** Flags for
+ * Transparent
+ * Cull Mode
+ */
+
+ enum class MapMode {
+ AlbedoM = 0,
+ MetallicM,
+ SpecularM,
+ EmissionM,
+ RoughnessM,
+ NormalM,
+ AmbientOcclusionM,
+ RefractionM,
+ ReflectionM,
+ };
+
+ /* Returns the string representation of the TextureParam enum */
+ static String get_texture_param_name(StandardMaterial3D::TextureParam param) {
+ switch (param) {
+ case StandardMaterial3D::TEXTURE_ALBEDO:
+ return "TEXTURE_ALBEDO";
+ case StandardMaterial3D::TEXTURE_METALLIC:
+ return "TEXTURE_METALLIC";
+ case StandardMaterial3D::TEXTURE_ROUGHNESS:
+ return "TEXTURE_ROUGHNESS";
+ case StandardMaterial3D::TEXTURE_EMISSION:
+ return "TEXTURE_EMISSION";
+ case StandardMaterial3D::TEXTURE_NORMAL:
+ return "TEXTURE_NORMAL";
+ case StandardMaterial3D::TEXTURE_RIM:
+ return "TEXTURE_RIM";
+ case StandardMaterial3D::TEXTURE_CLEARCOAT:
+ return "TEXTURE_CLEARCOAT";
+ case StandardMaterial3D::TEXTURE_FLOWMAP:
+ return "TEXTURE_FLOWMAP";
+ case StandardMaterial3D::TEXTURE_AMBIENT_OCCLUSION:
+ return "TEXTURE_AMBIENT_OCCLUSION";
+ // case StandardMaterial3D::TEXTURE_DEPTH: // TODO: work out how to make this function again!
+ // return "TEXTURE_DEPTH";
+ case StandardMaterial3D::TEXTURE_SUBSURFACE_SCATTERING:
+ return "TEXTURE_SUBSURFACE_SCATTERING";
+ // case StandardMaterial3D::TEXTURE_TRANSMISSION: // TODO: work out how to make this function again!
+ // return "TEXTURE_TRANSMISSION";
+ case StandardMaterial3D::TEXTURE_REFRACTION:
+ return "TEXTURE_REFRACTION";
+ case StandardMaterial3D::TEXTURE_DETAIL_MASK:
+ return "TEXTURE_DETAIL_MASK";
+ case StandardMaterial3D::TEXTURE_DETAIL_ALBEDO:
+ return "TEXTURE_DETAIL_ALBEDO";
+ case StandardMaterial3D::TEXTURE_DETAIL_NORMAL:
+ return "TEXTURE_DETAIL_NORMAL";
+ case StandardMaterial3D::TEXTURE_MAX:
+ return "TEXTURE_MAX";
+ default:
+ return "broken horribly";
+ }
+ };
+
+ // TODO make this static?
+ const std::map<std::string, bool> fbx_transparency_flags = {
+ /* Transparent */
+ { "TransparentColor", true },
+ { "Maya|opacity", true }
+ };
+
+ // TODO make this static?
+ const std::map<std::string, StandardMaterial3D::TextureParam> fbx_texture_map = {
+ /* Diffuse */
+ { "Maya|base", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO },
+ { "DiffuseColor", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO },
+ { "Maya|DiffuseTexture", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO },
+ { "Maya|baseColor", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO },
+ { "Maya|baseColor|file", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO },
+ { "3dsMax|Parameters|base_color_map", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO },
+ { "Maya|TEX_color_map|file", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO },
+ { "Maya|TEX_color_map", StandardMaterial3D::TextureParam::TEXTURE_ALBEDO },
+ /* Emission */
+ { "EmissiveColor", StandardMaterial3D::TextureParam::TEXTURE_EMISSION },
+ { "EmissiveFactor", StandardMaterial3D::TextureParam::TEXTURE_EMISSION },
+ { "Maya|emissionColor", StandardMaterial3D::TextureParam::TEXTURE_EMISSION },
+ { "Maya|emissionColor|file", StandardMaterial3D::TextureParam::TEXTURE_EMISSION },
+ { "3dsMax|Parameters|emission_map", StandardMaterial3D::TextureParam::TEXTURE_EMISSION },
+ { "Maya|TEX_emissive_map", StandardMaterial3D::TextureParam::TEXTURE_EMISSION },
+ { "Maya|TEX_emissive_map|file", StandardMaterial3D::TextureParam::TEXTURE_EMISSION },
+ /* Metallic */
+ { "Maya|metalness", StandardMaterial3D::TextureParam::TEXTURE_METALLIC },
+ { "Maya|metalness|file", StandardMaterial3D::TextureParam::TEXTURE_METALLIC },
+ { "3dsMax|Parameters|metalness_map", StandardMaterial3D::TextureParam::TEXTURE_METALLIC },
+ { "Maya|TEX_metallic_map", StandardMaterial3D::TextureParam::TEXTURE_METALLIC },
+ { "Maya|TEX_metallic_map|file", StandardMaterial3D::TextureParam::TEXTURE_METALLIC },
+
+ /* Roughness */
+ // Arnold Roughness Map
+ { "Maya|specularRoughness", StandardMaterial3D::TextureParam::TEXTURE_ROUGHNESS },
+
+ { "3dsMax|Parameters|roughness_map", StandardMaterial3D::TextureParam::TEXTURE_ROUGHNESS },
+ { "Maya|TEX_roughness_map", StandardMaterial3D::TextureParam::TEXTURE_ROUGHNESS },
+ { "Maya|TEX_roughness_map|file", StandardMaterial3D::TextureParam::TEXTURE_ROUGHNESS },
+
+ /* Normal */
+ { "NormalMap", StandardMaterial3D::TextureParam::TEXTURE_NORMAL },
+ //{ "Bump", Material::TextureParam::TEXTURE_NORMAL },
+ //{ "3dsMax|Parameters|bump_map", Material::TextureParam::TEXTURE_NORMAL },
+ { "Maya|NormalTexture", StandardMaterial3D::TextureParam::TEXTURE_NORMAL },
+ //{ "Maya|normalCamera", Material::TextureParam::TEXTURE_NORMAL },
+ //{ "Maya|normalCamera|file", Material::TextureParam::TEXTURE_NORMAL },
+ { "Maya|TEX_normal_map", StandardMaterial3D::TextureParam::TEXTURE_NORMAL },
+ { "Maya|TEX_normal_map|file", StandardMaterial3D::TextureParam::TEXTURE_NORMAL },
+ /* AO */
+ { "Maya|TEX_ao_map", StandardMaterial3D::TextureParam::TEXTURE_AMBIENT_OCCLUSION },
+ { "Maya|TEX_ao_map|file", StandardMaterial3D::TextureParam::TEXTURE_AMBIENT_OCCLUSION },
+
+ // TODO: specular workflow conversion
+ // { "SpecularColor", StandardMaterial3D::TextureParam::TEXTURE_METALLIC },
+ // { "Maya|specularColor", StandardMaterial3D::TextureParam::TEXTURE_METALLIC },
+ // { "Maya|SpecularTexture", StandardMaterial3D::TextureParam::TEXTURE_METALLIC },
+ // { "Maya|SpecularTexture|file", StandardMaterial3D::TextureParam::TEXTURE_METALLIC },
+ // { "ShininessExponent", SpatialMaterial::TextureParam::UNSUPPORTED },
+ // { "ReflectionFactor", SpatialMaterial::TextureParam::UNSUPPORTED },
+
+ //{ "TransparentColor",SpatialMaterial::TextureParam::TEXTURE_CHANNEL_ALPHA },
+ //{ "TransparencyFactor",SpatialMaterial::TextureParam::TEXTURE_CHANNEL_ALPHA }
+
+ // TODO: diffuse roughness
+ //{ "Maya|diffuseRoughness", SpatialMaterial::TextureParam::UNSUPPORTED },
+ //{ "Maya|diffuseRoughness|file", SpatialMaterial::TextureParam::UNSUPPORTED },
+
+ };
+
+ // TODO make this static?
+ enum PropertyDesc {
+ PROPERTY_DESC_NOT_FOUND,
+ PROPERTY_DESC_ALBEDO_COLOR,
+ PROPERTY_DESC_TRANSPARENT,
+ PROPERTY_DESC_METALLIC,
+ PROPERTY_DESC_ROUGHNESS,
+ PROPERTY_DESC_SPECULAR,
+ PROPERTY_DESC_SPECULAR_COLOR,
+ PROPERTY_DESC_SHINYNESS,
+ PROPERTY_DESC_COAT,
+ PROPERTY_DESC_COAT_ROUGHNESS,
+ PROPERTY_DESC_EMISSIVE,
+ PROPERTY_DESC_EMISSIVE_COLOR,
+ PROPERTY_DESC_IGNORE
+ };
+
+ const std::map<std::string, PropertyDesc> fbx_properties_desc = {
+ /* Albedo */
+ { "DiffuseColor", PROPERTY_DESC_ALBEDO_COLOR },
+ { "Maya|baseColor", PROPERTY_DESC_ALBEDO_COLOR },
+
+ /* Specular */
+ { "Maya|specular", PROPERTY_DESC_SPECULAR },
+ { "Maya|specularColor", PROPERTY_DESC_SPECULAR_COLOR },
+
+ /* Specular roughness - arnold roughness map */
+ { "Maya|specularRoughness", PROPERTY_DESC_ROUGHNESS },
+
+ /* Transparent */
+ { "Opacity", PROPERTY_DESC_TRANSPARENT },
+ { "TransparencyFactor", PROPERTY_DESC_TRANSPARENT },
+ { "Maya|opacity", PROPERTY_DESC_TRANSPARENT },
+
+ { "Maya|metalness", PROPERTY_DESC_METALLIC },
+ { "Maya|metallic", PROPERTY_DESC_METALLIC },
+
+ /* Roughness */
+ { "Maya|roughness", PROPERTY_DESC_ROUGHNESS },
+
+ /* Coat */
+ //{ "Maya|coat", PROPERTY_DESC_COAT },
+
+ /* Coat roughness */
+ //{ "Maya|coatRoughness", PROPERTY_DESC_COAT_ROUGHNESS },
+
+ /* Emissive */
+ { "Maya|emission", PROPERTY_DESC_EMISSIVE },
+ { "Maya|emissive", PROPERTY_DESC_EMISSIVE },
+
+ /* Emissive color */
+ { "EmissiveColor", PROPERTY_DESC_EMISSIVE_COLOR },
+ { "Maya|emissionColor", PROPERTY_DESC_EMISSIVE_COLOR },
+
+ /* Ignore */
+ { "Shininess", PROPERTY_DESC_IGNORE },
+ { "Reflectivity", PROPERTY_DESC_IGNORE },
+ { "Maya|diffuseRoughness", PROPERTY_DESC_IGNORE },
+ { "Maya", PROPERTY_DESC_IGNORE },
+ { "Diffuse", PROPERTY_DESC_ALBEDO_COLOR },
+ { "Maya|TypeId", PROPERTY_DESC_IGNORE },
+ { "Ambient", PROPERTY_DESC_IGNORE },
+ { "AmbientColor", PROPERTY_DESC_IGNORE },
+ { "ShininessExponent", PROPERTY_DESC_IGNORE },
+ { "Specular", PROPERTY_DESC_IGNORE },
+ { "SpecularColor", PROPERTY_DESC_IGNORE },
+ { "SpecularFactor", PROPERTY_DESC_IGNORE },
+ //{ "BumpFactor", PROPERTY_DESC_IGNORE },
+ { "Maya|exitToBackground", PROPERTY_DESC_IGNORE },
+ { "Maya|indirectDiffuse", PROPERTY_DESC_IGNORE },
+ { "Maya|indirectSpecular", PROPERTY_DESC_IGNORE },
+ { "Maya|internalReflections", PROPERTY_DESC_IGNORE },
+ { "DiffuseFactor", PROPERTY_DESC_IGNORE },
+ { "AmbientFactor", PROPERTY_DESC_IGNORE },
+ { "ReflectionColor", PROPERTY_DESC_IGNORE },
+ { "Emissive", PROPERTY_DESC_IGNORE },
+ { "Maya|coatColor", PROPERTY_DESC_IGNORE },
+ { "Maya|coatNormal", PROPERTY_DESC_IGNORE },
+ { "Maya|coatIOR", PROPERTY_DESC_IGNORE },
+ };
+
+ /* storing the texture properties like color */
+ template <class T>
+ struct TexturePropertyMapping : RefCounted {
+ StandardMaterial3D::TextureParam map_mode = StandardMaterial3D::TextureParam::TEXTURE_ALBEDO;
+ const T property = T();
+ };
+
+ static void add_search_string(String p_filename, String p_current_directory, String search_directory, Vector<String> &texture_search_paths);
+
+ static String find_texture_path_by_filename(const String p_filename, const String p_current_directory);
+
+ String get_material_name() const;
+
+ void set_imported_material(FBXDocParser::Material *p_material);
+
+ Ref<StandardMaterial3D> import_material(ImportState &state);
+};
+
+#endif // FBX_MATERIAL_H
diff --git a/modules/fbx/data/fbx_mesh_data.cpp b/modules/fbx/data/fbx_mesh_data.cpp
new file mode 100644
index 0000000000..e1eacc68b3
--- /dev/null
+++ b/modules/fbx/data/fbx_mesh_data.cpp
@@ -0,0 +1,1435 @@
+/*************************************************************************/
+/* fbx_mesh_data.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 "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"
+
+#include "thirdparty/misc/polypartition.h"
+
+template <class T>
+T collect_first(const Vector<VertexData<T>> *p_data, T p_fall_back) {
+ if (p_data->is_empty()) {
+ return p_fall_back;
+ }
+
+ return (*p_data)[0].data;
+}
+
+template <class T>
+HashMap<int, T> collect_all(const Vector<VertexData<T>> *p_data, HashMap<int, T> p_fall_back) {
+ if (p_data->is_empty()) {
+ return p_fall_back;
+ }
+
+ HashMap<int, T> collection;
+ for (int i = 0; i < p_data->size(); i += 1) {
+ const VertexData<T> &vd = (*p_data)[i];
+ collection[vd.polygon_index] = vd.data;
+ }
+ return collection;
+}
+
+template <class T>
+T collect_average(const Vector<VertexData<T>> *p_data, T p_fall_back) {
+ if (p_data->is_empty()) {
+ return p_fall_back;
+ }
+
+ T combined = (*p_data)[0].data; // Make sure the data is always correctly initialized.
+ print_verbose("size of data: " + itos(p_data->size()));
+ for (int i = 1; i < p_data->size(); i += 1) {
+ combined += (*p_data)[i].data;
+ }
+ combined = combined / real_t(p_data->size());
+
+ return combined.normalized();
+}
+
+HashMap<int, Vector3> collect_normal(const Vector<VertexData<Vector3>> *p_data, HashMap<int, Vector3> p_fall_back) {
+ if (p_data->is_empty()) {
+ return p_fall_back;
+ }
+
+ HashMap<int, Vector3> collection;
+ for (int i = 0; i < p_data->size(); i += 1) {
+ const VertexData<Vector3> &vd = (*p_data)[i];
+ collection[vd.polygon_index] = vd.data;
+ }
+ return collection;
+}
+
+HashMap<int, Vector2> collect_uv(const Vector<VertexData<Vector2>> *p_data, HashMap<int, Vector2> p_fall_back) {
+ if (p_data->is_empty()) {
+ return p_fall_back;
+ }
+
+ HashMap<int, Vector2> collection;
+ for (int i = 0; i < p_data->size(); i += 1) {
+ const VertexData<Vector2> &vd = (*p_data)[i];
+ collection[vd.polygon_index] = vd.data;
+ }
+ return collection;
+}
+
+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();
+
+ // TODO: perf hotspot on large files
+ // this can be a very large copy
+ std::vector<int> polygon_indices = mesh_geometry->get_polygon_indices();
+ std::vector<Vector3> vertices = mesh_geometry->get_vertices();
+
+ // Phase 1. Parse all FBX data.
+ HashMap<int, Vector3> normals;
+ HashMap<int, HashMap<int, Vector3>> normals_raw = extract_per_vertex_data(
+ vertices.size(),
+ mesh_geometry->get_edge_map(),
+ polygon_indices,
+ mesh_geometry->get_normals(),
+ &collect_all,
+ HashMap<int, Vector3>());
+
+ HashMap<int, Vector2> uvs_0;
+ HashMap<int, HashMap<int, Vector2>> uvs_0_raw = extract_per_vertex_data(
+ vertices.size(),
+ mesh_geometry->get_edge_map(),
+ polygon_indices,
+ mesh_geometry->get_uv_0(),
+ &collect_all,
+ HashMap<int, Vector2>());
+
+ HashMap<int, Vector2> uvs_1;
+ HashMap<int, HashMap<int, Vector2>> uvs_1_raw = extract_per_vertex_data(
+ vertices.size(),
+ mesh_geometry->get_edge_map(),
+ polygon_indices,
+ mesh_geometry->get_uv_1(),
+ &collect_all,
+ HashMap<int, Vector2>());
+
+ HashMap<int, Color> colors;
+ HashMap<int, HashMap<int, Color>> colors_raw = extract_per_vertex_data(
+ vertices.size(),
+ mesh_geometry->get_edge_map(),
+ polygon_indices,
+ mesh_geometry->get_colors(),
+ &collect_all,
+ HashMap<int, Color>());
+
+ // TODO what about tangents?
+ // TODO what about bi-nomials?
+ // TODO there is other?
+
+ HashMap<int, SurfaceId> polygon_surfaces = extract_per_polygon(
+ vertices.size(),
+ polygon_indices,
+ mesh_geometry->get_material_allocation_id(),
+ -1);
+
+ HashMap<String, MorphVertexData> morphs;
+ extract_morphs(mesh_geometry, morphs);
+
+ // TODO please add skinning.
+ //mesh_id = mesh_geometry->ID();
+
+ sanitize_vertex_weights(state);
+
+ // Re organize polygon vertices to to correctly take into account strange
+ // UVs.
+ reorganize_vertices(
+ polygon_indices,
+ vertices,
+ normals,
+ uvs_0,
+ uvs_1,
+ colors,
+ morphs,
+ normals_raw,
+ colors_raw,
+ uvs_0_raw,
+ uvs_1_raw);
+
+ const int color_count = colors.size();
+ print_verbose("Vertex color count: " + itos(color_count));
+
+ // Make sure that from this moment on the mesh_geometry is no used anymore.
+ // This is a safety step, because the mesh_geometry data are no more valid
+ // at this point.
+
+ const int vertex_count = vertices.size();
+
+ print_verbose("Vertex count: " + itos(vertex_count));
+
+ // The map key is the material allocator id that is also used as surface id.
+ HashMap<SurfaceId, SurfaceData> surfaces;
+
+ // Phase 2. For each material create a surface tool (So a different mesh).
+ {
+ if (polygon_surfaces.is_empty()) {
+ // No material, just use the default one with index -1.
+ // Set -1 to all polygons.
+ const int polygon_count = count_polygons(polygon_indices);
+ for (int p = 0; p < polygon_count; p += 1) {
+ polygon_surfaces[p] = -1;
+ }
+ }
+
+ // Create the surface now.
+ for (const int *polygon_id = polygon_surfaces.next(nullptr); polygon_id != nullptr; polygon_id = polygon_surfaces.next(polygon_id)) {
+ const int surface_id = polygon_surfaces[*polygon_id];
+ if (surfaces.has(surface_id) == false) {
+ SurfaceData sd;
+ sd.surface_tool.instantiate();
+ sd.surface_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
+
+ if (surface_id < 0) {
+ // nothing to do
+ } else if (surface_id < (int)material_lookup.size()) {
+ const FBXDocParser::Material *mat_mapping = material_lookup.at(surface_id);
+ const uint64_t mapping_id = mat_mapping->ID();
+ if (state.cached_materials.has(mapping_id)) {
+ sd.material = state.cached_materials[mapping_id];
+ }
+ } else {
+ WARN_PRINT("out of bounds surface detected, FBX file has corrupt material data");
+ }
+
+ surfaces.set(surface_id, sd);
+ }
+ }
+ }
+
+ // Phase 3. Map the vertices relative to each surface, in this way we can
+ // just insert the vertices that we need per each surface.
+ {
+ PolygonId polygon_index = -1;
+ SurfaceId surface_id = -1;
+ SurfaceData *surface_data = nullptr;
+
+ for (size_t polygon_vertex = 0; polygon_vertex < polygon_indices.size(); polygon_vertex += 1) {
+ if (is_start_of_polygon(polygon_indices, polygon_vertex)) {
+ polygon_index += 1;
+ ERR_FAIL_COND_V_MSG(polygon_surfaces.has(polygon_index) == false, nullptr, "The FBX file is corrupted, This surface_index is not expected.");
+ surface_id = polygon_surfaces[polygon_index];
+ surface_data = surfaces.getptr(surface_id);
+ CRASH_COND(surface_data == nullptr); // Can't be null.
+ }
+
+ const int vertex = get_vertex_from_polygon_vertex(polygon_indices, polygon_vertex);
+
+ // The vertex position in the surface
+ // Uses a lookup table for speed with large scenes
+ bool has_polygon_vertex_index = surface_data->lookup_table.has(vertex);
+ int surface_polygon_vertex_index = -1;
+
+ if (has_polygon_vertex_index) {
+ surface_polygon_vertex_index = surface_data->lookup_table[vertex];
+ } else {
+ surface_polygon_vertex_index = surface_data->vertices_map.size();
+ surface_data->lookup_table[vertex] = surface_polygon_vertex_index;
+ surface_data->vertices_map.push_back(vertex);
+ }
+
+ surface_data->surface_polygon_vertex[polygon_index].push_back(surface_polygon_vertex_index);
+ }
+ }
+
+ //print_verbose("[debug UV 1] UV1: " + itos(uvs_0.size()));
+ //print_verbose("[debug UV 2] UV2: " + itos(uvs_1.size()));
+
+ // Phase 4. Per each surface just insert the vertices and add the indices.
+ for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) {
+ SurfaceData *surface = surfaces.getptr(*surface_id);
+
+ // Just add the vertices data.
+ for (unsigned int i = 0; i < surface->vertices_map.size(); i += 1) {
+ const Vertex vertex = surface->vertices_map[i];
+
+ // This must be done before add_vertex because the surface tool is
+ // expecting this before the st->add_vertex() call
+ add_vertex(state,
+ surface->surface_tool,
+ state.scale,
+ vertex,
+ vertices,
+ normals,
+ uvs_0,
+ uvs_1,
+ colors);
+ }
+
+ // Triangulate the various polygons and add the indices.
+ for (const PolygonId *polygon_id = surface->surface_polygon_vertex.next(nullptr); polygon_id != nullptr; polygon_id = surface->surface_polygon_vertex.next(polygon_id)) {
+ const Vector<DataIndex> *indices = surface->surface_polygon_vertex.getptr(*polygon_id);
+ triangulate_polygon(
+ surface,
+ *indices,
+ vertices);
+ }
+ }
+
+ // Phase 5. Compose the morphs if any.
+ for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) {
+ SurfaceData *surface = surfaces.getptr(*surface_id);
+
+ for (const String *morph_name = morphs.next(nullptr); morph_name != nullptr; morph_name = morphs.next(morph_name)) {
+ MorphVertexData *morph_data = morphs.getptr(*morph_name);
+
+ // As said by the docs, this is not supposed to be different than
+ // vertex_count.
+ CRASH_COND(morph_data->vertices.size() != vertex_count);
+ CRASH_COND(morph_data->normals.size() != vertex_count);
+
+ Vector3 *vertices_ptr = morph_data->vertices.ptrw();
+ Vector3 *normals_ptr = morph_data->normals.ptrw();
+
+ Ref<SurfaceTool> morph_st;
+ morph_st.instantiate();
+ morph_st->begin(Mesh::PRIMITIVE_TRIANGLES);
+
+ for (unsigned int vi = 0; vi < surface->vertices_map.size(); vi += 1) {
+ const Vertex &vertex = surface->vertices_map[vi];
+ add_vertex(
+ state,
+ morph_st,
+ state.scale,
+ vertex,
+ vertices,
+ normals,
+ uvs_0,
+ uvs_1,
+ colors,
+ vertices_ptr[vertex],
+ normals_ptr[vertex]);
+ }
+
+ if (state.is_blender_fbx) {
+ morph_st->generate_normals();
+ }
+ morph_st->generate_tangents();
+ surface->morphs.push_back(morph_st->commit_to_arrays());
+ }
+ }
+
+ // Phase 6. Compose the mesh and return it.
+ Ref<ImporterMesh> mesh;
+ mesh.instantiate();
+
+ // Add blend shape info.
+ for (const String *morph_name = morphs.next(nullptr); morph_name != nullptr; morph_name = morphs.next(morph_name)) {
+ mesh->add_blend_shape(*morph_name);
+ }
+
+ // TODO always normalized, Why?
+ mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
+
+ // Add surfaces.
+ for (const SurfaceId *surface_id = surfaces.next(nullptr); surface_id != nullptr; surface_id = surfaces.next(surface_id)) {
+ SurfaceData *surface = surfaces.getptr(*surface_id);
+
+ if (state.is_blender_fbx) {
+ surface->surface_tool->generate_normals();
+ }
+ // you can't generate them without a valid uv map.
+ if (uvs_0_raw.size() > 0) {
+ surface->surface_tool->generate_tangents();
+ }
+
+ Array mesh_array = surface->surface_tool->commit_to_arrays();
+ Array blend_shapes = surface->morphs;
+
+ if (surface->material.is_valid()) {
+ mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, mesh_array, blend_shapes, Dictionary(), surface->material, surface->material->get_name());
+ } else {
+ mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, mesh_array, blend_shapes);
+ }
+ }
+
+ 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 >.<
+ mesh->set_name("mesh3d-" + name);
+ return godot_mesh;
+}
+
+void FBXMeshData::sanitize_vertex_weights(const ImportState &state) {
+ const int max_vertex_influence_count = RS::ARRAY_WEIGHTS_SIZE;
+ Map<int, int> skeleton_to_skin_bind_id;
+ // TODO: error's need added
+ const FBXDocParser::Skin *fbx_skin = mesh_geometry->DeformerSkin();
+
+ if (fbx_skin == nullptr || fbx_skin->Clusters().size() == 0) {
+ return; // do nothing
+ }
+
+ //
+ // Precalculate the skin cluster mapping
+ //
+
+ int bind_id = 0;
+ for (const FBXDocParser::Cluster *cluster : fbx_skin->Clusters()) {
+ ERR_CONTINUE_MSG(!state.fbx_bone_map.has(cluster->TargetNode()->ID()), "Missing bone map for cluster target node with id " + uitos(cluster->TargetNode()->ID()) + ".");
+ Ref<FBXBone> bone = state.fbx_bone_map[cluster->TargetNode()->ID()];
+ skeleton_to_skin_bind_id.insert(bone->godot_bone_id, bind_id);
+ bind_id++;
+ }
+
+ for (const Vertex *v = vertex_weights.next(nullptr); v != nullptr; v = vertex_weights.next(v)) {
+ VertexWeightMapping *vm = vertex_weights.getptr(*v);
+ ERR_CONTINUE(vm->bones.size() != vm->weights.size()); // No message, already checked.
+ ERR_CONTINUE(vm->bones_ref.size() != vm->weights.size()); // No message, already checked.
+
+ const int initial_size = vm->weights.size();
+ {
+ // Init bone id
+ int *bones_ptr = vm->bones.ptrw();
+ Ref<FBXBone> *bones_ref_ptr = vm->bones_ref.ptrw();
+
+ for (int i = 0; i < vm->weights.size(); i += 1) {
+ // At this point this is not possible because the skeleton is already initialized.
+ CRASH_COND(bones_ref_ptr[i]->godot_bone_id == -2);
+ bones_ptr[i] = skeleton_to_skin_bind_id[bones_ref_ptr[i]->godot_bone_id];
+ }
+
+ // From this point on the data is no more valid.
+ vm->bones_ref.clear();
+ }
+
+ {
+ // Sort
+ float *weights_ptr = vm->weights.ptrw();
+ int *bones_ptr = vm->bones.ptrw();
+ for (int i = 0; i < vm->weights.size(); i += 1) {
+ for (int x = i + 1; x < vm->weights.size(); x += 1) {
+ if (weights_ptr[i] < weights_ptr[x]) {
+ SWAP(weights_ptr[i], weights_ptr[x]);
+ SWAP(bones_ptr[i], bones_ptr[x]);
+ }
+ }
+ }
+ }
+
+ {
+ // Resize
+ vm->weights.resize(max_vertex_influence_count);
+ vm->bones.resize(max_vertex_influence_count);
+ float *weights_ptr = vm->weights.ptrw();
+ int *bones_ptr = vm->bones.ptrw();
+ for (int i = initial_size; i < max_vertex_influence_count; i += 1) {
+ weights_ptr[i] = 0.0;
+ bones_ptr[i] = 0;
+ }
+
+ // Normalize
+ real_t sum = 0.0;
+ for (int i = 0; i < max_vertex_influence_count; i += 1) {
+ sum += weights_ptr[i];
+ }
+ if (sum > 0.0) {
+ for (int i = 0; i < vm->weights.size(); i += 1) {
+ weights_ptr[i] = weights_ptr[i] / sum;
+ }
+ }
+ }
+ }
+}
+
+void FBXMeshData::reorganize_vertices(
+ // TODO: perf hotspot on insane files
+ std::vector<int> &r_polygon_indices,
+ std::vector<Vector3> &r_vertices,
+ HashMap<int, Vector3> &r_normals,
+ HashMap<int, Vector2> &r_uv_1,
+ HashMap<int, Vector2> &r_uv_2,
+ HashMap<int, Color> &r_color,
+ HashMap<String, MorphVertexData> &r_morphs,
+ HashMap<int, HashMap<int, Vector3>> &r_normals_raw,
+ HashMap<int, HashMap<int, Color>> &r_colors_raw,
+ HashMap<int, HashMap<int, Vector2>> &r_uv_1_raw,
+ HashMap<int, HashMap<int, Vector2>> &r_uv_2_raw) {
+ // Key: OldVertex; Value: [New vertices];
+ HashMap<int, Vector<int>> duplicated_vertices;
+
+ PolygonId polygon_index = -1;
+ for (int pv = 0; pv < (int)r_polygon_indices.size(); pv += 1) {
+ if (is_start_of_polygon(r_polygon_indices, pv)) {
+ polygon_index += 1;
+ }
+ const Vertex index = get_vertex_from_polygon_vertex(r_polygon_indices, pv);
+
+ bool need_duplication = false;
+ Vector2 this_vert_poly_uv1 = Vector2();
+ Vector2 this_vert_poly_uv2 = Vector2();
+ Vector3 this_vert_poly_normal = Vector3();
+ Color this_vert_poly_color = Color();
+
+ // Take the normal and see if we need to duplicate this polygon.
+ if (r_normals_raw.has(index)) {
+ const HashMap<PolygonId, Vector3> *nrml_arr = r_normals_raw.getptr(index);
+
+ if (nrml_arr->has(polygon_index)) {
+ this_vert_poly_normal = nrml_arr->get(polygon_index);
+ } else if (nrml_arr->has(-1)) {
+ this_vert_poly_normal = nrml_arr->get(-1);
+ } else {
+ print_error("invalid normal detected: " + itos(index) + " polygon index: " + itos(polygon_index));
+ for (const PolygonId *pid = nrml_arr->next(nullptr); pid != nullptr; pid = nrml_arr->next(pid)) {
+ print_verbose("debug contents key: " + itos(*pid));
+
+ if (nrml_arr->has(*pid)) {
+ print_verbose("contents valid: " + nrml_arr->get(*pid));
+ }
+ }
+ }
+
+ // Now, check if we need to duplicate it.
+ for (const PolygonId *pid = nrml_arr->next(nullptr); pid != nullptr; pid = nrml_arr->next(pid)) {
+ if (*pid == polygon_index) {
+ continue;
+ }
+
+ const Vector3 vert_poly_normal = *nrml_arr->getptr(*pid);
+ if (!vert_poly_normal.is_equal_approx(this_vert_poly_normal)) {
+ // Yes this polygon need duplication.
+ need_duplication = true;
+ break;
+ }
+ }
+ }
+
+ // TODO: make me vertex color
+ // Take the normal and see if we need to duplicate this polygon.
+ if (r_colors_raw.has(index)) {
+ const HashMap<PolygonId, Color> *color_arr = r_colors_raw.getptr(index);
+
+ if (color_arr->has(polygon_index)) {
+ this_vert_poly_color = color_arr->get(polygon_index);
+ } else if (color_arr->has(-1)) {
+ this_vert_poly_color = color_arr->get(-1);
+ } else {
+ print_error("invalid color detected: " + itos(index) + " polygon index: " + itos(polygon_index));
+ for (const PolygonId *pid = color_arr->next(nullptr); pid != nullptr; pid = color_arr->next(pid)) {
+ print_verbose("debug contents key: " + itos(*pid));
+
+ if (color_arr->has(*pid)) {
+ print_verbose("contents valid: " + color_arr->get(*pid));
+ }
+ }
+ }
+
+ // Now, check if we need to duplicate it.
+ for (const PolygonId *pid = color_arr->next(nullptr); pid != nullptr; pid = color_arr->next(pid)) {
+ if (*pid == polygon_index) {
+ continue;
+ }
+
+ const Color vert_poly_color = *color_arr->getptr(*pid);
+ if (!this_vert_poly_color.is_equal_approx(vert_poly_color)) {
+ // Yes this polygon need duplication.
+ need_duplication = true;
+ break;
+ }
+ }
+ }
+
+ // Take the UV1 and UV2 and see if we need to duplicate this polygon.
+ {
+ HashMap<int, HashMap<int, Vector2>> *uv_raw = &r_uv_1_raw;
+ Vector2 *this_vert_poly_uv = &this_vert_poly_uv1;
+ for (int kk = 0; kk < 2; kk++) {
+ if (uv_raw->has(index)) {
+ const HashMap<PolygonId, Vector2> *uvs = uv_raw->getptr(index);
+
+ if (uvs->has(polygon_index)) {
+ // This Polygon has its own uv.
+ (*this_vert_poly_uv) = *uvs->getptr(polygon_index);
+
+ // Check if we need to duplicate it.
+ for (const PolygonId *pid = uvs->next(nullptr); pid != nullptr; pid = uvs->next(pid)) {
+ if (*pid == polygon_index) {
+ continue;
+ }
+ const Vector2 vert_poly_uv = *uvs->getptr(*pid);
+ if (!vert_poly_uv.is_equal_approx(*this_vert_poly_uv)) {
+ // Yes this polygon need duplication.
+ need_duplication = true;
+ break;
+ }
+ }
+ } else if (uvs->has(-1)) {
+ // It has the default UV.
+ (*this_vert_poly_uv) = *uvs->getptr(-1);
+ } else if (uvs->size() > 0) {
+ // No uv, this is strange, just take the first and duplicate.
+ (*this_vert_poly_uv) = *uvs->getptr(*uvs->next(nullptr));
+ WARN_PRINT("No UVs for this polygon, while there is no default and some other polygons have it. This FBX file may be corrupted.");
+ }
+ }
+ uv_raw = &r_uv_2_raw;
+ this_vert_poly_uv = &this_vert_poly_uv2;
+ }
+ }
+
+ // If we want to duplicate it, Let's see if we already duplicated this
+ // vertex.
+ if (need_duplication) {
+ if (duplicated_vertices.has(index)) {
+ Vertex similar_vertex = -1;
+ // Let's see if one of the new vertices has the same data of this.
+ const Vector<int> *new_vertices = duplicated_vertices.getptr(index);
+ for (int j = 0; j < new_vertices->size(); j += 1) {
+ const Vertex new_vertex = (*new_vertices)[j];
+ bool same_uv1 = false;
+ bool same_uv2 = false;
+ bool same_normal = false;
+ bool same_color = false;
+
+ if (r_uv_1.has(new_vertex)) {
+ if ((this_vert_poly_uv1 - (*r_uv_1.getptr(new_vertex))).length_squared() <= CMP_EPSILON) {
+ same_uv1 = true;
+ }
+ }
+
+ if (r_uv_2.has(new_vertex)) {
+ if ((this_vert_poly_uv2 - (*r_uv_2.getptr(new_vertex))).length_squared() <= CMP_EPSILON) {
+ same_uv2 = true;
+ }
+ }
+
+ if (r_color.has(new_vertex)) {
+ if (this_vert_poly_color.is_equal_approx((*r_color.getptr(new_vertex)))) {
+ same_color = true;
+ }
+ }
+
+ if (r_normals.has(new_vertex)) {
+ if ((this_vert_poly_normal - (*r_normals.getptr(new_vertex))).length_squared() <= CMP_EPSILON) {
+ same_uv2 = true;
+ }
+ }
+
+ if (same_uv1 && same_uv2 && same_normal && same_color) {
+ similar_vertex = new_vertex;
+ break;
+ }
+ }
+
+ if (similar_vertex != -1) {
+ // Update polygon.
+ if (is_end_of_polygon(r_polygon_indices, pv)) {
+ r_polygon_indices[pv] = ~similar_vertex;
+ } else {
+ r_polygon_indices[pv] = similar_vertex;
+ }
+ need_duplication = false;
+ }
+ }
+ }
+
+ if (need_duplication) {
+ const Vertex old_index = index;
+ const Vertex new_index = r_vertices.size();
+
+ // Polygon index.
+ if (is_end_of_polygon(r_polygon_indices, pv)) {
+ r_polygon_indices[pv] = ~new_index;
+ } else {
+ r_polygon_indices[pv] = new_index;
+ }
+
+ // Vertex position.
+ r_vertices.push_back(r_vertices[old_index]);
+
+ // Normals
+ if (r_normals_raw.has(old_index)) {
+ r_normals.set(new_index, this_vert_poly_normal);
+ r_normals_raw.getptr(old_index)->erase(polygon_index);
+ r_normals_raw[new_index][polygon_index] = this_vert_poly_normal;
+ }
+
+ // Vertex Color
+ if (r_colors_raw.has(old_index)) {
+ r_color.set(new_index, this_vert_poly_color);
+ r_colors_raw.getptr(old_index)->erase(polygon_index);
+ r_colors_raw[new_index][polygon_index] = this_vert_poly_color;
+ }
+
+ // UV 0
+ if (r_uv_1_raw.has(old_index)) {
+ r_uv_1.set(new_index, this_vert_poly_uv1);
+ r_uv_1_raw.getptr(old_index)->erase(polygon_index);
+ r_uv_1_raw[new_index][polygon_index] = this_vert_poly_uv1;
+ }
+
+ // UV 1
+ if (r_uv_2_raw.has(old_index)) {
+ r_uv_2.set(new_index, this_vert_poly_uv2);
+ r_uv_2_raw.getptr(old_index)->erase(polygon_index);
+ r_uv_2_raw[new_index][polygon_index] = this_vert_poly_uv2;
+ }
+
+ // Morphs
+ for (const String *mname = r_morphs.next(nullptr); mname != nullptr; mname = r_morphs.next(mname)) {
+ MorphVertexData *d = r_morphs.getptr(*mname);
+ // This can't never happen.
+ CRASH_COND(d == nullptr);
+ if (d->vertices.size() > old_index) {
+ d->vertices.push_back(d->vertices[old_index]);
+ }
+ if (d->normals.size() > old_index) {
+ d->normals.push_back(d->normals[old_index]);
+ }
+ }
+
+ if (vertex_weights.has(old_index)) {
+ vertex_weights.set(new_index, vertex_weights[old_index]);
+ }
+
+ duplicated_vertices[old_index].push_back(new_index);
+ } else {
+ if (r_normals_raw.has(index) &&
+ r_normals.has(index) == false) {
+ r_normals.set(index, this_vert_poly_normal);
+ }
+
+ if (r_colors_raw.has(index) && r_color.has(index) == false) {
+ r_color.set(index, this_vert_poly_color);
+ }
+
+ if (r_uv_1_raw.has(index) &&
+ r_uv_1.has(index) == false) {
+ r_uv_1.set(index, this_vert_poly_uv1);
+ }
+
+ if (r_uv_2_raw.has(index) &&
+ r_uv_2.has(index) == false) {
+ r_uv_2.set(index, this_vert_poly_uv2);
+ }
+ }
+ }
+}
+
+void FBXMeshData::add_vertex(
+ const ImportState &state,
+ Ref<SurfaceTool> p_surface_tool,
+ real_t p_scale,
+ Vertex p_vertex,
+ const std::vector<Vector3> &p_vertices_position,
+ const HashMap<int, Vector3> &p_normals,
+ const HashMap<int, Vector2> &p_uvs_0,
+ const HashMap<int, Vector2> &p_uvs_1,
+ const HashMap<int, Color> &p_colors,
+ const Vector3 &p_morph_value,
+ const Vector3 &p_morph_normal) {
+ ERR_FAIL_INDEX_MSG(p_vertex, (Vertex)p_vertices_position.size(), "FBX file is corrupted, the position of the vertex can't be retrieved.");
+
+ if (p_normals.has(p_vertex) && !state.is_blender_fbx) {
+ p_surface_tool->set_normal(p_normals[p_vertex] + p_morph_normal);
+ }
+
+ if (p_uvs_0.has(p_vertex)) {
+ //print_verbose("uv1: [" + itos(p_vertex) + "] " + p_uvs_0[p_vertex]);
+ // Inverts Y UV.
+ p_surface_tool->set_uv(Vector2(p_uvs_0[p_vertex].x, 1 - p_uvs_0[p_vertex].y));
+ }
+
+ if (p_uvs_1.has(p_vertex)) {
+ //print_verbose("uv2: [" + itos(p_vertex) + "] " + p_uvs_1[p_vertex]);
+ // Inverts Y UV.
+ p_surface_tool->set_uv2(Vector2(p_uvs_1[p_vertex].x, 1 - p_uvs_1[p_vertex].y));
+ }
+
+ if (p_colors.has(p_vertex)) {
+ p_surface_tool->set_color(p_colors[p_vertex]);
+ }
+
+ // TODO what about binormals?
+ // TODO there is other?
+
+ if (vertex_weights.has(p_vertex)) {
+ // Let's extract the weight info.
+ const VertexWeightMapping *vm = vertex_weights.getptr(p_vertex);
+ const Vector<int> &bones = vm->bones;
+
+ // the bug is that the bone idx is wrong because it is not ref'ing the skin.
+
+ if (bones.size() > RS::ARRAY_WEIGHTS_SIZE) {
+ print_error("[weight overflow detected]");
+ }
+
+ p_surface_tool->set_weights(vm->weights);
+ // 0 1 2 3 4 5 6 7 < local skeleton / skin for mesh
+ // 0 1 2 3 4 5 6 7 8 9 10 < actual skeleton with all joints
+ p_surface_tool->set_bones(bones);
+ }
+
+ // The surface tool want the vertex position as last thing.
+ p_surface_tool->add_vertex((p_vertices_position[p_vertex] + p_morph_value) * p_scale);
+}
+
+void FBXMeshData::triangulate_polygon(SurfaceData *surface, const Vector<int> &p_polygon_vertex, const std::vector<Vector3> &p_vertices) const {
+ Ref<SurfaceTool> st(surface->surface_tool);
+ const int polygon_vertex_count = p_polygon_vertex.size();
+ //const Vector<Vertex>& p_surface_vertex_map
+ if (polygon_vertex_count == 1) {
+ // point to triangle
+ st->add_index(p_polygon_vertex[0]);
+ st->add_index(p_polygon_vertex[0]);
+ st->add_index(p_polygon_vertex[0]);
+ return;
+ } else if (polygon_vertex_count == 2) {
+ // line to triangle
+ st->add_index(p_polygon_vertex[1]);
+ st->add_index(p_polygon_vertex[1]);
+ st->add_index(p_polygon_vertex[0]);
+ return;
+ } else if (polygon_vertex_count == 3) {
+ // triangle to triangle
+ st->add_index(p_polygon_vertex[0]);
+ st->add_index(p_polygon_vertex[2]);
+ st->add_index(p_polygon_vertex[1]);
+ return;
+ } else if (polygon_vertex_count == 4) {
+ // quad to triangle - this code is awesome for import times
+ // it prevents triangles being generated slowly
+ st->add_index(p_polygon_vertex[0]);
+ st->add_index(p_polygon_vertex[2]);
+ st->add_index(p_polygon_vertex[1]);
+ st->add_index(p_polygon_vertex[2]);
+ st->add_index(p_polygon_vertex[0]);
+ st->add_index(p_polygon_vertex[3]);
+ return;
+ } else {
+ // non triangulated - we must run the triangulation algorithm
+ bool is_simple_convex = false;
+ // this code is 'slow' but required it triangulates all the unsupported geometry.
+ // Doesn't allow for bigger polygons because those are unlikely be convex
+ if (polygon_vertex_count <= 6) {
+ // Start from true, check if it's false.
+ is_simple_convex = true;
+ Vector3 first_vec;
+ for (int i = 0; i < polygon_vertex_count; i += 1) {
+ const Vector3 p1 = p_vertices[surface->vertices_map[p_polygon_vertex[i]]];
+ const Vector3 p2 = p_vertices[surface->vertices_map[p_polygon_vertex[(i + 1) % polygon_vertex_count]]];
+ const Vector3 p3 = p_vertices[surface->vertices_map[p_polygon_vertex[(i + 2) % polygon_vertex_count]]];
+
+ const Vector3 edge1 = p1 - p2;
+ const Vector3 edge2 = p3 - p2;
+
+ const Vector3 res = edge1.normalized().cross(edge2.normalized()).normalized();
+ if (i == 0) {
+ first_vec = res;
+ } else {
+ if (first_vec.dot(res) < 0.0) {
+ // Ok we found an angle that is not the same dir of the
+ // others.
+ is_simple_convex = false;
+ break;
+ }
+ }
+ }
+ }
+
+ if (is_simple_convex) {
+ // This is a convex polygon, so just triangulate it.
+ for (int i = 0; i < (polygon_vertex_count - 2); i += 1) {
+ st->add_index(p_polygon_vertex[2 + i]);
+ st->add_index(p_polygon_vertex[1 + i]);
+ st->add_index(p_polygon_vertex[0]);
+ }
+ return;
+ }
+ }
+
+ {
+ // This is a concave polygon.
+
+ std::vector<Vector3> poly_vertices(polygon_vertex_count);
+ for (int i = 0; i < polygon_vertex_count; i += 1) {
+ poly_vertices[i] = p_vertices[surface->vertices_map[p_polygon_vertex[i]]];
+ }
+
+ const Vector3 poly_norm = get_poly_normal(poly_vertices);
+ if (poly_norm.length_squared() <= CMP_EPSILON) {
+ ERR_FAIL_COND_MSG(poly_norm.length_squared() <= CMP_EPSILON, "The normal of this poly was not computed. Is this FBX file corrupted.");
+ }
+
+ // Select the plan coordinate.
+ int axis_1_coord = 0;
+ int axis_2_coord = 1;
+ {
+ real_t inv = poly_norm.z;
+
+ const real_t axis_x = ABS(poly_norm.x);
+ const real_t axis_y = ABS(poly_norm.y);
+ const real_t axis_z = ABS(poly_norm.z);
+
+ if (axis_x > axis_y) {
+ if (axis_x > axis_z) {
+ // For the most part the normal point toward X.
+ axis_1_coord = 1;
+ axis_2_coord = 2;
+ inv = poly_norm.x;
+ }
+ } else if (axis_y > axis_z) {
+ // For the most part the normal point toward Y.
+ axis_1_coord = 2;
+ axis_2_coord = 0;
+ inv = poly_norm.y;
+ }
+
+ // Swap projection axes to take the negated projection vector into account
+ if (inv < 0.0f) {
+ SWAP(axis_1_coord, axis_2_coord);
+ }
+ }
+
+ TPPLPoly tppl_poly;
+ tppl_poly.Init(polygon_vertex_count);
+ std::vector<Vector2> projected_vertices(polygon_vertex_count);
+ for (int i = 0; i < polygon_vertex_count; i += 1) {
+ const Vector2 pv(poly_vertices[i][axis_1_coord], poly_vertices[i][axis_2_coord]);
+ projected_vertices[i] = pv;
+ tppl_poly.GetPoint(i) = pv;
+ }
+ tppl_poly.SetOrientation(TPPL_ORIENTATION_CCW);
+
+ List<TPPLPoly> out_poly;
+
+ TPPLPartition tppl_partition;
+ if (tppl_partition.Triangulate_OPT(&tppl_poly, &out_poly) == 0) { // Good result.
+ if (tppl_partition.Triangulate_EC(&tppl_poly, &out_poly) == 0) { // Medium result.
+ if (tppl_partition.Triangulate_MONO(&tppl_poly, &out_poly) == 0) { // Really poor result.
+ ERR_FAIL_MSG("The triangulation of this polygon failed, please try to triangulate your mesh or check if it has broken polygons.");
+ }
+ }
+ }
+
+ std::vector<Vector2> tris(out_poly.size());
+ for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) {
+ TPPLPoly &tp = I->get();
+
+ ERR_FAIL_COND_MSG(tp.GetNumPoints() != 3, "The triangulator returned more points, how this is possible?");
+ // Find Index
+ for (int i = 2; i >= 0; i -= 1) {
+ const Vector2 vertex = tp.GetPoint(i);
+ bool done = false;
+ // Find Index
+ for (int y = 0; y < polygon_vertex_count; y += 1) {
+ if ((projected_vertices[y] - vertex).length_squared() <= CMP_EPSILON) {
+ // This seems the right vertex
+ st->add_index(p_polygon_vertex[y]);
+ done = true;
+ break;
+ }
+ }
+ ERR_FAIL_COND(done == false);
+ }
+ }
+ }
+}
+
+void FBXMeshData::gen_weight_info(Ref<SurfaceTool> st, Vertex vertex_id) const {
+ if (vertex_weights.is_empty()) {
+ return;
+ }
+
+ if (vertex_weights.has(vertex_id)) {
+ // Let's extract the weight info.
+ const VertexWeightMapping *vm = vertex_weights.getptr(vertex_id);
+ st->set_weights(vm->weights);
+ st->set_bones(vm->bones);
+ }
+}
+
+int FBXMeshData::get_vertex_from_polygon_vertex(const std::vector<int> &p_polygon_indices, int p_index) const {
+ if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) {
+ return -1;
+ }
+
+ const int vertex = p_polygon_indices[p_index];
+ if (vertex >= 0) {
+ return vertex;
+ } else {
+ // Negative numbers are the end of the face, reversing the bits is
+ // possible to obtain the positive correct vertex number.
+ return ~vertex;
+ }
+}
+
+bool FBXMeshData::is_end_of_polygon(const std::vector<int> &p_polygon_indices, int p_index) const {
+ if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) {
+ return false;
+ }
+
+ const int vertex = p_polygon_indices[p_index];
+
+ // If the index is negative this is the end of the Polygon.
+ return vertex < 0;
+}
+
+bool FBXMeshData::is_start_of_polygon(const std::vector<int> &p_polygon_indices, int p_index) const {
+ if (p_index < 0 || p_index >= (int)p_polygon_indices.size()) {
+ return false;
+ }
+
+ if (p_index == 0) {
+ return true;
+ }
+
+ // If the previous indices is negative this is the begin of a new Polygon.
+ return p_polygon_indices[p_index - 1] < 0;
+}
+
+int FBXMeshData::count_polygons(const std::vector<int> &p_polygon_indices) const {
+ // The negative numbers define the end of the polygon. Counting the amount of
+ // negatives the numbers of polygons are obtained.
+ int count = 0;
+ for (size_t i = 0; i < p_polygon_indices.size(); i += 1) {
+ if (p_polygon_indices[i] < 0) {
+ count += 1;
+ }
+ }
+ return count;
+}
+
+template <class R, class T>
+HashMap<int, R> FBXMeshData::extract_per_vertex_data(
+ int p_vertex_count,
+ const std::vector<FBXDocParser::MeshGeometry::Edge> &p_edge_map,
+ const std::vector<int> &p_mesh_indices,
+ const FBXDocParser::MeshGeometry::MappingData<T> &p_mapping_data,
+ R (*collector_function)(const Vector<VertexData<T>> *p_vertex_data, R p_fall_back),
+ R p_fall_back) const {
+ /* When index_to_direct is set
+ * index size is 184 ( contains index for the data array [values 0, 96] )
+ * data size is 96 (contains uv coordinates)
+ * this means index is simple data reduction basically
+ */
+ ////
+ if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct && p_mapping_data.index.size() == 0) {
+ print_verbose("debug count: index size: " + itos(p_mapping_data.index.size()) + ", data size: " + itos(p_mapping_data.data.size()));
+ print_verbose("vertex indices count: " + itos(p_mesh_indices.size()));
+ print_verbose("Edge map size: " + itos(p_edge_map.size()));
+ }
+
+ ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct && p_mapping_data.index.size() == 0, (HashMap<int, R>()), "FBX importer needs to map correctly to this field, please specify the override index name to fix this problem!");
+ ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index && p_mapping_data.index.size() == 0, (HashMap<int, R>()), "The FBX seems corrupted");
+
+ // Aggregate vertex data.
+ HashMap<Vertex, Vector<VertexData<T>>> aggregate_vertex_data;
+
+ switch (p_mapping_data.map_type) {
+ case FBXDocParser::MeshGeometry::MapType::none: {
+ // No data nothing to do.
+ return (HashMap<int, R>());
+ }
+ case FBXDocParser::MeshGeometry::MapType::vertex: {
+ ERR_FAIL_COND_V_MSG(p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct, (HashMap<int, R>()), "We will support in future");
+
+ if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) {
+ // The data is mapped per vertex directly.
+ ERR_FAIL_COND_V_MSG((int)p_mapping_data.data.size() != p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR01");
+ for (size_t vertex_index = 0; vertex_index < p_mapping_data.data.size(); vertex_index += 1) {
+ aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[vertex_index] });
+ }
+ } else {
+ // The data is mapped per vertex using a reference.
+ // The indices array, contains a *reference_id for each vertex.
+ // * Note that the reference_id is the id of data into the data array.
+ //
+ // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html
+ ERR_FAIL_COND_V_MSG((int)p_mapping_data.index.size() != p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR02");
+ for (size_t vertex_index = 0; vertex_index < p_mapping_data.index.size(); vertex_index += 1) {
+ ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[vertex_index], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR03.");
+ aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[p_mapping_data.index[vertex_index]] });
+ }
+ }
+ } break;
+ case FBXDocParser::MeshGeometry::MapType::polygon_vertex: {
+ if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct) {
+ // The data is mapped using each index from the indexes array then direct to the data (data reduction algorithm)
+ ERR_FAIL_COND_V_MSG((int)p_mesh_indices.size() != (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR04");
+ int polygon_id = -1;
+ for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.index.size(); polygon_vertex_index += 1) {
+ if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) {
+ polygon_id += 1;
+ }
+ const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index);
+ ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR05");
+ ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR06");
+ const int index_to_direct = p_mapping_data.index[polygon_vertex_index];
+ T value = p_mapping_data.data[index_to_direct];
+ aggregate_vertex_data[vertex_index].push_back({ polygon_id, value });
+ }
+ } else if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) {
+ // The data are mapped per polygon vertex directly.
+ ERR_FAIL_COND_V_MSG((int)p_mesh_indices.size() != (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR04");
+ int polygon_id = -1;
+ for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.data.size(); polygon_vertex_index += 1) {
+ if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) {
+ polygon_id += 1;
+ }
+ const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index);
+ ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR05");
+ ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR06");
+
+ aggregate_vertex_data[vertex_index].push_back({ polygon_id, p_mapping_data.data[polygon_vertex_index] });
+ }
+ } else {
+ // The data is mapped per polygon_vertex using a reference.
+ // The indices array, contains a *reference_id for each polygon_vertex.
+ // * Note that the reference_id is the id of data into the data array.
+ //
+ // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html
+ ERR_FAIL_COND_V_MSG(p_mesh_indices.size() != p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR7");
+ int polygon_id = -1;
+ for (size_t polygon_vertex_index = 0; polygon_vertex_index < p_mapping_data.index.size(); polygon_vertex_index += 1) {
+ if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) {
+ polygon_id += 1;
+ }
+ const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index);
+ ERR_FAIL_COND_V_MSG(vertex_index < 0, (HashMap<int, R>()), "FBX file corrupted: #ERR8");
+ ERR_FAIL_COND_V_MSG(vertex_index >= p_vertex_count, (HashMap<int, R>()), "FBX file seems corrupted: #ERR9.");
+ ERR_FAIL_COND_V_MSG(p_mapping_data.index[polygon_vertex_index] < 0, (HashMap<int, R>()), "FBX file seems corrupted: #ERR10.");
+ ERR_FAIL_COND_V_MSG(p_mapping_data.index[polygon_vertex_index] >= (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR11.");
+ aggregate_vertex_data[vertex_index].push_back({ polygon_id, p_mapping_data.data[p_mapping_data.index[polygon_vertex_index]] });
+ }
+ }
+ } break;
+ case FBXDocParser::MeshGeometry::MapType::polygon: {
+ if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) {
+ // The data are mapped per polygon directly.
+ const int polygon_count = count_polygons(p_mesh_indices);
+ ERR_FAIL_COND_V_MSG(polygon_count != (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR12");
+
+ // Advance each polygon vertex, each new polygon advance the polygon index.
+ int polygon_index = -1;
+ for (size_t polygon_vertex_index = 0;
+ polygon_vertex_index < p_mesh_indices.size();
+ polygon_vertex_index += 1) {
+ if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) {
+ polygon_index += 1;
+ ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR13");
+ }
+
+ const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index);
+ ERR_FAIL_INDEX_V_MSG(vertex_index, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR14");
+
+ aggregate_vertex_data[vertex_index].push_back({ polygon_index, p_mapping_data.data[polygon_index] });
+ }
+ ERR_FAIL_COND_V_MSG((polygon_index + 1) != polygon_count, (HashMap<int, R>()), "FBX file seems corrupted: #ERR16. Not all Polygons are present in the file.");
+ } else {
+ // The data is mapped per polygon using a reference.
+ // The indices array, contains a *reference_id for each polygon.
+ // * Note that the reference_id is the id of data into the data array.
+ //
+ // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html
+ const int polygon_count = count_polygons(p_mesh_indices);
+ ERR_FAIL_COND_V_MSG(polygon_count != (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR17");
+
+ // Advance each polygon vertex, each new polygon advance the polygon index.
+ int polygon_index = -1;
+ for (size_t polygon_vertex_index = 0;
+ polygon_vertex_index < p_mesh_indices.size();
+ polygon_vertex_index += 1) {
+ if (is_start_of_polygon(p_mesh_indices, polygon_vertex_index)) {
+ polygon_index += 1;
+ ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR18");
+ ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[polygon_index], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR19");
+ }
+
+ const int vertex_index = get_vertex_from_polygon_vertex(p_mesh_indices, polygon_vertex_index);
+ ERR_FAIL_INDEX_V_MSG(vertex_index, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR20");
+
+ aggregate_vertex_data[vertex_index].push_back({ polygon_index, p_mapping_data.data[p_mapping_data.index[polygon_index]] });
+ }
+ ERR_FAIL_COND_V_MSG((polygon_index + 1) != polygon_count, (HashMap<int, R>()), "FBX file seems corrupted: #ERR22. Not all Polygons are present in the file.");
+ }
+ } break;
+ case FBXDocParser::MeshGeometry::MapType::edge: {
+ if (p_mapping_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) {
+ // The data are mapped per edge directly.
+ ERR_FAIL_COND_V_MSG(p_edge_map.size() != p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR23");
+ for (size_t edge_index = 0; edge_index < p_mapping_data.data.size(); edge_index += 1) {
+ const FBXDocParser::MeshGeometry::Edge edge = FBXDocParser::MeshGeometry::get_edge(p_edge_map, edge_index);
+ ERR_FAIL_INDEX_V_MSG(edge.vertex_0, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR24");
+ ERR_FAIL_INDEX_V_MSG(edge.vertex_1, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR25");
+ ERR_FAIL_INDEX_V_MSG(edge.vertex_0, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR26");
+ ERR_FAIL_INDEX_V_MSG(edge.vertex_1, (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR27");
+ aggregate_vertex_data[edge.vertex_0].push_back({ -1, p_mapping_data.data[edge_index] });
+ aggregate_vertex_data[edge.vertex_1].push_back({ -1, p_mapping_data.data[edge_index] });
+ }
+ } else {
+ // The data is mapped per edge using a reference.
+ // The indices array, contains a *reference_id for each polygon.
+ // * Note that the reference_id is the id of data into the data array.
+ //
+ // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html
+ ERR_FAIL_COND_V_MSG(p_edge_map.size() != p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file seems corrupted: #ERR28");
+ for (size_t edge_index = 0; edge_index < p_mapping_data.data.size(); edge_index += 1) {
+ const FBXDocParser::MeshGeometry::Edge edge = FBXDocParser::MeshGeometry::get_edge(p_edge_map, edge_index);
+ ERR_FAIL_INDEX_V_MSG(edge.vertex_0, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR29");
+ ERR_FAIL_INDEX_V_MSG(edge.vertex_1, p_vertex_count, (HashMap<int, R>()), "FBX file corrupted: #ERR30");
+ ERR_FAIL_INDEX_V_MSG(edge.vertex_0, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR31");
+ ERR_FAIL_INDEX_V_MSG(edge.vertex_1, (int)p_mapping_data.index.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR32");
+ ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[edge.vertex_0], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR33");
+ ERR_FAIL_INDEX_V_MSG(p_mapping_data.index[edge.vertex_1], (int)p_mapping_data.data.size(), (HashMap<int, R>()), "FBX file corrupted: #ERR34");
+ aggregate_vertex_data[edge.vertex_0].push_back({ -1, p_mapping_data.data[p_mapping_data.index[edge_index]] });
+ aggregate_vertex_data[edge.vertex_1].push_back({ -1, p_mapping_data.data[p_mapping_data.index[edge_index]] });
+ }
+ }
+ } break;
+ case FBXDocParser::MeshGeometry::MapType::all_the_same: {
+ // No matter the mode, no matter the data size; The first always win
+ // and is set to all the vertices.
+ ERR_FAIL_COND_V_MSG(p_mapping_data.data.size() <= 0, (HashMap<int, R>()), "FBX file seems corrupted: #ERR35");
+ if (p_mapping_data.data.size() > 0) {
+ for (int vertex_index = 0; vertex_index < p_vertex_count; vertex_index += 1) {
+ aggregate_vertex_data[vertex_index].push_back({ -1, p_mapping_data.data[0] });
+ }
+ }
+ } break;
+ }
+
+ if (aggregate_vertex_data.size() == 0) {
+ return (HashMap<int, R>());
+ }
+
+ // A map is used because turns out that the some FBX file are not well organized
+ // with vertices well compacted. Using a map allows avoid those issues.
+ HashMap<Vertex, R> result;
+
+ // Aggregate the collected data.
+ for (const Vertex *index = aggregate_vertex_data.next(nullptr); index != nullptr; index = aggregate_vertex_data.next(index)) {
+ Vector<VertexData<T>> *aggregated_vertex = aggregate_vertex_data.getptr(*index);
+ // This can't be null because we are just iterating.
+ CRASH_COND(aggregated_vertex == nullptr);
+
+ ERR_FAIL_INDEX_V_MSG(0, aggregated_vertex->size(), (HashMap<int, R>()), "The FBX file is corrupted, No valid data for this vertex index.");
+ result[*index] = collector_function(aggregated_vertex, p_fall_back);
+ }
+
+ // Sanitize the data now, if the file is broken we can try import it anyway.
+ bool problem_found = false;
+ for (size_t i = 0; i < p_mesh_indices.size(); i += 1) {
+ const Vertex vertex = get_vertex_from_polygon_vertex(p_mesh_indices, i);
+ if (result.has(vertex) == false) {
+ result[vertex] = p_fall_back;
+ problem_found = true;
+ }
+ }
+ if (problem_found) {
+ WARN_PRINT("Some data is missing, this FBX file may be corrupted: #WARN0.");
+ }
+
+ return result;
+}
+
+template <class T>
+HashMap<int, T> FBXMeshData::extract_per_polygon(
+ int p_vertex_count,
+ const std::vector<int> &p_polygon_indices,
+ const FBXDocParser::MeshGeometry::MappingData<T> &p_fbx_data,
+ T p_fallback_value) const {
+ ERR_FAIL_COND_V_MSG(p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct && p_fbx_data.data.size() == 0, (HashMap<int, T>()), "invalid index to direct array");
+ ERR_FAIL_COND_V_MSG(p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index && p_fbx_data.index.size() == 0, (HashMap<int, T>()), "The FBX seems corrupted");
+
+ const int polygon_count = count_polygons(p_polygon_indices);
+
+ // Aggregate vertex data.
+ HashMap<int, Vector<T>> aggregate_polygon_data;
+
+ switch (p_fbx_data.map_type) {
+ case FBXDocParser::MeshGeometry::MapType::none: {
+ // No data nothing to do.
+ return (HashMap<int, T>());
+ }
+ case FBXDocParser::MeshGeometry::MapType::vertex: {
+ ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per vertex. This should not happen.");
+ } break;
+ case FBXDocParser::MeshGeometry::MapType::polygon_vertex: {
+ ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per polygon vertex. This should not happen.");
+ } break;
+ case FBXDocParser::MeshGeometry::MapType::polygon: {
+ if (p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::index_to_direct) {
+ // The data is stored efficiently index_to_direct allows less data in the FBX file.
+ for (int polygon_index = 0;
+ polygon_index < polygon_count;
+ polygon_index += 1) {
+ if (p_fbx_data.index.size() == 0) {
+ ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR62");
+ aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[polygon_index]);
+ } else {
+ ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR62");
+
+ const int index_to_direct = p_fbx_data.index[polygon_index];
+ T value = p_fbx_data.data[index_to_direct];
+ aggregate_polygon_data[polygon_index].push_back(value);
+ }
+ }
+ } else if (p_fbx_data.ref_type == FBXDocParser::MeshGeometry::ReferenceType::direct) {
+ // The data are mapped per polygon directly.
+ ERR_FAIL_COND_V_MSG(polygon_count != (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR51");
+
+ // Advance each polygon vertex, each new polygon advance the polygon index.
+ for (int polygon_index = 0;
+ polygon_index < polygon_count;
+ polygon_index += 1) {
+ ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR52");
+ aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[polygon_index]);
+ }
+ } else {
+ // The data is mapped per polygon using a reference.
+ // The indices array, contains a *reference_id for each polygon.
+ // * Note that the reference_id is the id of data into the data array.
+ //
+ // https://help.autodesk.com/view/FBX/2017/ENU/?guid=__cpp_ref_class_fbx_layer_element_html
+ ERR_FAIL_COND_V_MSG(polygon_count != (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file seems corrupted: #ERR52");
+
+ // Advance each polygon vertex, each new polygon advance the polygon index.
+ for (int polygon_index = 0;
+ polygon_index < polygon_count;
+ polygon_index += 1) {
+ ERR_FAIL_INDEX_V_MSG(polygon_index, (int)p_fbx_data.index.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR53");
+ ERR_FAIL_INDEX_V_MSG(p_fbx_data.index[polygon_index], (int)p_fbx_data.data.size(), (HashMap<int, T>()), "FBX file is corrupted: #ERR54");
+ aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[p_fbx_data.index[polygon_index]]);
+ }
+ }
+ } break;
+ case FBXDocParser::MeshGeometry::MapType::edge: {
+ ERR_FAIL_V_MSG((HashMap<int, T>()), "This data can't be extracted and organized per polygon, since into the FBX is mapped per edge. This should not happen.");
+ } break;
+ case FBXDocParser::MeshGeometry::MapType::all_the_same: {
+ // No matter the mode, no matter the data size; The first always win
+ // and is set to all the vertices.
+ ERR_FAIL_COND_V_MSG(p_fbx_data.data.size() <= 0, (HashMap<int, T>()), "FBX file seems corrupted: #ERR55");
+ if (p_fbx_data.data.size() > 0) {
+ for (int polygon_index = 0; polygon_index < polygon_count; polygon_index += 1) {
+ aggregate_polygon_data[polygon_index].push_back(p_fbx_data.data[0]);
+ }
+ }
+ } break;
+ }
+
+ if (aggregate_polygon_data.size() == 0) {
+ return (HashMap<int, T>());
+ }
+
+ // A map is used because turns out that the some FBX file are not well organized
+ // with vertices well compacted. Using a map allows avoid those issues.
+ HashMap<int, T> polygons;
+
+ // Take the first value for each vertex.
+ for (const Vertex *index = aggregate_polygon_data.next(nullptr); index != nullptr; index = aggregate_polygon_data.next(index)) {
+ Vector<T> *aggregated_polygon = aggregate_polygon_data.getptr(*index);
+ // This can't be null because we are just iterating.
+ CRASH_COND(aggregated_polygon == nullptr);
+
+ ERR_FAIL_INDEX_V_MSG(0, (int)aggregated_polygon->size(), (HashMap<int, T>()), "The FBX file is corrupted, No valid data for this polygon index.");
+
+ // Validate the final value.
+ polygons[*index] = (*aggregated_polygon)[0];
+ }
+
+ // Sanitize the data now, if the file is broken we can try import it anyway.
+ bool problem_found = false;
+ for (int polygon_i = 0; polygon_i < polygon_count; polygon_i += 1) {
+ if (polygons.has(polygon_i) == false) {
+ polygons[polygon_i] = p_fallback_value;
+ problem_found = true;
+ }
+ }
+ if (problem_found) {
+ WARN_PRINT("Some data is missing, this FBX file may be corrupted: #WARN1.");
+ }
+
+ return polygons;
+}
+
+void FBXMeshData::extract_morphs(const FBXDocParser::MeshGeometry *mesh_geometry, HashMap<String, MorphVertexData> &r_data) {
+ r_data.clear();
+
+ const int vertex_count = mesh_geometry->get_vertices().size();
+
+ for (const FBXDocParser::BlendShape *blend_shape : mesh_geometry->get_blend_shapes()) {
+ for (const FBXDocParser::BlendShapeChannel *blend_shape_channel : blend_shape->BlendShapeChannels()) {
+ const std::vector<const FBXDocParser::ShapeGeometry *> &shape_geometries = blend_shape_channel->GetShapeGeometries();
+ for (const FBXDocParser::ShapeGeometry *shape_geometry : shape_geometries) {
+ String morph_name = ImportUtils::FBXAnimMeshName(shape_geometry->Name()).c_str();
+ if (morph_name.is_empty()) {
+ morph_name = "morph";
+ }
+
+ // TODO we have only these??
+ const std::vector<unsigned int> &morphs_vertex_indices = shape_geometry->GetIndices();
+ const std::vector<Vector3> &morphs_vertices = shape_geometry->GetVertices();
+ const std::vector<Vector3> &morphs_normals = shape_geometry->GetNormals();
+
+ ERR_FAIL_COND_MSG((int)morphs_vertex_indices.size() > vertex_count, "The FBX file is corrupted: #ERR103");
+ ERR_FAIL_COND_MSG(morphs_vertex_indices.size() != morphs_vertices.size(), "The FBX file is corrupted: #ERR104");
+ ERR_FAIL_COND_MSG((int)morphs_vertices.size() > vertex_count, "The FBX file is corrupted: #ERR105");
+ ERR_FAIL_COND_MSG(morphs_normals.size() != 0 && morphs_normals.size() != morphs_vertices.size(), "The FBX file is corrupted: #ERR106");
+
+ if (r_data.has(morph_name) == false) {
+ // This morph doesn't exist yet.
+ // Create it.
+ MorphVertexData md;
+ md.vertices.resize(vertex_count);
+ md.normals.resize(vertex_count);
+ r_data.set(morph_name, md);
+ }
+
+ MorphVertexData *data = r_data.getptr(morph_name);
+ Vector3 *data_vertices_ptr = data->vertices.ptrw();
+ Vector3 *data_normals_ptr = data->normals.ptrw();
+
+ for (int i = 0; i < (int)morphs_vertex_indices.size(); i += 1) {
+ const Vertex vertex = morphs_vertex_indices[i];
+
+ ERR_FAIL_INDEX_MSG(vertex, vertex_count, "The blend shapes of this FBX file are corrupted. It has a not valid vertex.");
+
+ data_vertices_ptr[vertex] = morphs_vertices[i];
+
+ if (morphs_normals.size() != 0) {
+ data_normals_ptr[vertex] = morphs_normals[i];
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/modules/fbx/data/fbx_mesh_data.h b/modules/fbx/data/fbx_mesh_data.h
new file mode 100644
index 0000000000..eec7f38cd6
--- /dev/null
+++ b/modules/fbx/data/fbx_mesh_data.h
@@ -0,0 +1,200 @@
+/*************************************************************************/
+/* fbx_mesh_data.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 FBX_MESH_DATA_H
+#define FBX_MESH_DATA_H
+
+#include "core/templates/hash_map.h"
+#include "core/templates/local_vector.h"
+#include "core/templates/ordered_hash_map.h"
+#include "editor/import/resource_importer_scene.h"
+#include "scene/3d/importer_mesh_instance_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/resources/surface_tool.h"
+
+#include "fbx_bone.h"
+#include "fbx_parser/FBXMeshGeometry.h"
+#include "import_state.h"
+#include "tools/import_utils.h"
+
+struct FBXNode;
+struct FBXMeshData;
+struct FBXBone;
+struct ImportState;
+
+typedef int Vertex;
+typedef int SurfaceId;
+typedef int PolygonId;
+typedef int DataIndex;
+
+struct SurfaceData {
+ Ref<SurfaceTool> surface_tool;
+ OrderedHashMap<Vertex, int> lookup_table; // proposed fix is to replace lookup_table[vertex_id] to give the position of the vertices_map[int] index.
+ LocalVector<Vertex> vertices_map; // this must be ordered the same as insertion <-- slow to do find() operation.
+ Ref<Material> material;
+ HashMap<PolygonId, Vector<DataIndex>> surface_polygon_vertex;
+ Array morphs;
+};
+
+struct VertexWeightMapping {
+ Vector<float> weights;
+ Vector<int> bones;
+ // This extra vector is used because the bone id is computed in a second step.
+ // TODO Get rid of this extra step is a good idea.
+ Vector<Ref<FBXBone>> bones_ref;
+};
+
+template <class T>
+struct VertexData {
+ int polygon_index;
+ T data;
+};
+
+// Caches mesh information and instantiates meshes for you using helper functions.
+struct FBXMeshData : RefCounted {
+ struct MorphVertexData {
+ // TODO we have only these??
+ /// Each element is a vertex. Not supposed to be void.
+ Vector<Vector3> vertices;
+ /// Each element is a vertex. Not supposed to be void.
+ Vector<Vector3> normals;
+ };
+
+ // FIXME: remove this is a hack for testing only
+ mutable const FBXDocParser::MeshGeometry *mesh_geometry = nullptr;
+
+ Ref<FBXNode> mesh_node = nullptr;
+ /// vertex id, Weight Info
+ /// later: perf we can use array here
+ HashMap<int, VertexWeightMapping> vertex_weights;
+
+ // translate fbx mesh data from document context to FBX Mesh Geometry Context
+ bool valid_weight_indexes = false;
+
+ 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;
+
+ /* mesh maximum weight count */
+ bool valid_weight_count = false;
+ int max_weight_count = 0;
+ uint64_t armature_id = 0;
+ bool valid_armature_id = false;
+ ImporterMeshInstance3D *godot_mesh_instance = nullptr;
+
+private:
+ void sanitize_vertex_weights(const ImportState &state);
+
+ /// Make sure to reorganize the vertices so that the correct UV is taken.
+ /// This step is needed because differently from the normal, that can be
+ /// combined, the UV may need its own triangle because sometimes they have
+ /// really different UV for the same vertex but different polygon.
+ /// This function make sure to add another vertex for those UVS.
+ void reorganize_vertices(
+ std::vector<int> &r_polygon_indices,
+ std::vector<Vector3> &r_vertices,
+ HashMap<int, Vector3> &r_normals,
+ HashMap<int, Vector2> &r_uv_1,
+ HashMap<int, Vector2> &r_uv_2,
+ HashMap<int, Color> &r_color,
+ HashMap<String, MorphVertexData> &r_morphs,
+ HashMap<int, HashMap<int, Vector3>> &r_normals_raw,
+ HashMap<int, HashMap<int, Color>> &r_colors_raw,
+ HashMap<int, HashMap<int, Vector2>> &r_uv_1_raw,
+ HashMap<int, HashMap<int, Vector2>> &r_uv_2_raw);
+
+ void add_vertex(
+ const ImportState &state,
+ Ref<SurfaceTool> p_surface_tool,
+ real_t p_scale,
+ int p_vertex,
+ const std::vector<Vector3> &p_vertices_position,
+ const HashMap<int, Vector3> &p_normals,
+ const HashMap<int, Vector2> &p_uvs_0,
+ const HashMap<int, Vector2> &p_uvs_1,
+ const HashMap<int, Color> &p_colors,
+ const Vector3 &p_morph_value = Vector3(),
+ const Vector3 &p_morph_normal = Vector3());
+
+ void triangulate_polygon(SurfaceData *surface, const Vector<int> &p_polygon_vertex, const std::vector<Vector3> &p_vertices) const;
+
+ /// This function is responsible to convert the FBX polygon vertex to
+ /// vertex index.
+ /// The polygon vertices are stored in an array with some negative
+ /// values. The negative values define the last face index.
+ /// For example the following `face_array` contains two faces, the former
+ /// with 3 vertices and the latter with a line:
+ /// [0,2,-2,3,-5]
+ /// Parsed as:
+ /// [0, 2, 1, 3, 4]
+ /// The negative values are computed using this formula: `(-value) - 1`
+ ///
+ /// Returns the vertex index from the polygon vertex.
+ /// Returns -1 if `p_index` is invalid.
+ int get_vertex_from_polygon_vertex(const std::vector<int> &p_face_indices, int p_index) const;
+
+ /// Returns true if this polygon_vertex_index is the end of a new polygon.
+ bool is_end_of_polygon(const std::vector<int> &p_face_indices, int p_index) const;
+
+ /// Returns true if this polygon_vertex_index is the begin of a new polygon.
+ bool is_start_of_polygon(const std::vector<int> &p_face_indices, int p_index) const;
+
+ /// Returns the number of polygons.
+ int count_polygons(const std::vector<int> &p_face_indices) const;
+
+ /// Used to extract data from the `MappingData` aligned with vertex.
+ /// Useful to extract normal/uvs/colors/tangents/etc...
+ /// If the function fails somehow, it returns an hollow vector and print an error.
+ template <class R, class T>
+ HashMap<int, R> extract_per_vertex_data(
+ int p_vertex_count,
+ const std::vector<FBXDocParser::MeshGeometry::Edge> &p_edges,
+ const std::vector<int> &p_mesh_indices,
+ const FBXDocParser::MeshGeometry::MappingData<T> &p_mapping_data,
+ R (*collector_function)(const Vector<VertexData<T>> *p_vertex_data, R p_fall_back),
+ R p_fall_back) const;
+
+ /// Used to extract data from the `MappingData` organized per polygon.
+ /// Useful to extract the material
+ /// If the function fails somehow, it returns an hollow vector and print an error.
+ template <class T>
+ HashMap<int, T> extract_per_polygon(
+ int p_vertex_count,
+ const std::vector<int> &p_face_indices,
+ const FBXDocParser::MeshGeometry::MappingData<T> &p_fbx_data,
+ T p_fallback_value) const;
+
+ /// Extracts the morph data and organizes it per vertices.
+ /// The returned `MorphVertexData` arrays are never something different
+ /// then the `vertex_count`.
+ void extract_morphs(const FBXDocParser::MeshGeometry *mesh_geometry, HashMap<String, MorphVertexData> &r_data);
+};
+
+#endif // FBX_MESH_DATA_H
diff --git a/modules/fbx/data/fbx_node.h b/modules/fbx/data/fbx_node.h
new file mode 100644
index 0000000000..75461e397d
--- /dev/null
+++ b/modules/fbx/data/fbx_node.h
@@ -0,0 +1,63 @@
+/*************************************************************************/
+/* fbx_node.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 FBX_NODE_H
+#define FBX_NODE_H
+
+#include "fbx_skeleton.h"
+#include "model_abstraction.h"
+#include "pivot_transform.h"
+
+#include "fbx_parser/FBXDocument.h"
+
+class Node3D;
+struct PivotTransform;
+
+struct FBXNode : RefCounted, ModelAbstraction {
+ uint64_t current_node_id = 0;
+ String node_name = String();
+ Node3D *godot_node = nullptr;
+
+ // used to parent the skeleton once the tree is built.
+ Ref<FBXSkeleton> skeleton_node = Ref<FBXSkeleton>();
+
+ void set_parent(Ref<FBXNode> p_parent) {
+ fbx_parent = p_parent;
+ }
+
+ void set_pivot_transform(Ref<PivotTransform> p_pivot_transform) {
+ pivot_transform = p_pivot_transform;
+ }
+
+ Ref<PivotTransform> pivot_transform = Ref<PivotTransform>(); // local and global xform data
+ Ref<FBXNode> fbx_parent = Ref<FBXNode>(); // parent node
+};
+
+#endif // FBX_NODE_H
diff --git a/modules/fbx/data/fbx_skeleton.cpp b/modules/fbx/data/fbx_skeleton.cpp
new file mode 100644
index 0000000000..11eed2576f
--- /dev/null
+++ b/modules/fbx/data/fbx_skeleton.cpp
@@ -0,0 +1,130 @@
+/*************************************************************************/
+/* fbx_skeleton.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 "fbx_skeleton.h"
+
+#include "import_state.h"
+
+#include "tools/import_utils.h"
+
+void FBXSkeleton::init_skeleton(const ImportState &state) {
+ int skeleton_bone_count = skeleton_bones.size();
+
+ if (skeleton == nullptr && skeleton_bone_count > 0) {
+ skeleton = memnew(Skeleton3D);
+
+ if (fbx_node.is_valid()) {
+ // cache skeleton attachment for later during node creation
+ // can't be done until after node hierarchy is built
+ if (fbx_node->godot_node != state.root) {
+ fbx_node->skeleton_node = Ref<FBXSkeleton>(this);
+ print_verbose("cached armature skeleton attachment for node " + fbx_node->node_name);
+ } else {
+ // root node must never be a skeleton to prevent cyclic skeletons from being allowed (skeleton in a skeleton)
+ fbx_node->godot_node->add_child(skeleton);
+ skeleton->set_owner(state.root_owner);
+ skeleton->set_name("Skeleton3D");
+ print_verbose("created armature skeleton for root");
+ }
+ } else {
+ memfree(skeleton);
+ skeleton = nullptr;
+ print_error("[doc] skeleton has no valid node to parent nodes to - erasing");
+ skeleton_bones.clear();
+ return;
+ }
+ }
+
+ // Make the bone name uniques.
+ for (int x = 0; x < skeleton_bone_count; x++) {
+ Ref<FBXBone> bone = skeleton_bones[x];
+ if (bone.is_valid()) {
+ // Make sure the bone name is unique.
+ const String bone_name = bone->bone_name;
+ int same_name_count = 0;
+ for (int y = x + 1; y < skeleton_bone_count; y++) {
+ Ref<FBXBone> other_bone = skeleton_bones[y];
+ if (other_bone.is_valid()) {
+ if (other_bone->bone_name == bone_name) {
+ same_name_count += 1;
+ other_bone->bone_name += "_" + itos(same_name_count);
+ }
+ }
+ }
+ }
+ }
+
+ Map<int, Ref<FBXBone>> bone_map;
+ // implement fbx cluster skin logic here this is where it goes
+ int bone_count = 0;
+ for (int x = 0; x < skeleton_bone_count; x++) {
+ Ref<FBXBone> bone = skeleton_bones[x];
+ if (bone.is_valid()) {
+ skeleton->add_bone(bone->bone_name);
+ bone->godot_bone_id = bone_count;
+ bone->fbx_skeleton = Ref<FBXSkeleton>(this);
+ bone_map.insert(bone_count, bone);
+ print_verbose("added bone " + itos(bone->bone_id) + " " + bone->bone_name);
+ bone_count++;
+ }
+ }
+
+ ERR_FAIL_COND_MSG(skeleton->get_bone_count() != bone_count, "Not all bones got added, is the file corrupted?");
+
+ 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)) {
+ Ref<FBXBone> parent_bone = state.fbx_bone_map[bone->parent_bone_id];
+ int bone_id = skeleton->find_bone(parent_bone->bone_name);
+ if (bone_id != -1) {
+ skeleton->set_bone_parent(bone_index, bone_id);
+ } else {
+ print_error("invalid bone parent: " + parent_bone->bone_name);
+ }
+ } else {
+ if (bone->godot_bone_id != -1) {
+ skeleton->set_bone_parent(bone_index, -1); // no parent for this bone
+ }
+ }
+ }
+}
diff --git a/modules/fbx/data/fbx_skeleton.h b/modules/fbx/data/fbx_skeleton.h
new file mode 100644
index 0000000000..b6103df949
--- /dev/null
+++ b/modules/fbx/data/fbx_skeleton.h
@@ -0,0 +1,53 @@
+/*************************************************************************/
+/* fbx_skeleton.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 FBX_SKELETON_H
+#define FBX_SKELETON_H
+
+#include "fbx_bone.h"
+#include "fbx_node.h"
+#include "model_abstraction.h"
+
+#include "core/object/ref_counted.h"
+#include "scene/3d/skeleton_3d.h"
+
+struct FBXNode;
+struct ImportState;
+struct FBXBone;
+
+struct FBXSkeleton : RefCounted {
+ Ref<FBXNode> fbx_node = Ref<FBXNode>();
+ Vector<Ref<FBXBone>> skeleton_bones = Vector<Ref<FBXBone>>();
+ Skeleton3D *skeleton = nullptr;
+
+ void init_skeleton(const ImportState &state);
+};
+
+#endif // FBX_SKELETON_H
diff --git a/modules/fbx/data/import_state.h b/modules/fbx/data/import_state.h
new file mode 100644
index 0000000000..9ba60eaacf
--- /dev/null
+++ b/modules/fbx/data/import_state.h
@@ -0,0 +1,112 @@
+/*************************************************************************/
+/* import_state.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 IMPORT_STATE_H
+#define IMPORT_STATE_H
+
+#include "fbx_mesh_data.h"
+#include "tools/import_utils.h"
+#include "tools/validation_tools.h"
+
+#include "pivot_transform.h"
+
+#include "core/io/resource_importer.h"
+#include "core/templates/vector.h"
+#include "editor/import/resource_importer_scene.h"
+#include "editor/project_settings_editor.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/animation/animation_player.h"
+#include "scene/resources/animation.h"
+#include "scene/resources/surface_tool.h"
+
+#include "modules/fbx/fbx_parser/FBXDocument.h"
+#include "modules/fbx/fbx_parser/FBXImportSettings.h"
+#include "modules/fbx/fbx_parser/FBXMeshGeometry.h"
+#include "modules/fbx/fbx_parser/FBXParser.h"
+#include "modules/fbx/fbx_parser/FBXTokenizer.h"
+#include "modules/fbx/fbx_parser/FBXUtil.h"
+
+struct FBXBone;
+struct FBXMeshData;
+struct FBXNode;
+struct FBXSkeleton;
+
+struct ImportState {
+ bool enable_material_import = true;
+ bool enable_animation_import = true;
+ bool is_blender_fbx = false;
+
+ Map<StringName, Ref<Texture>> cached_image_searches;
+ Map<uint64_t, Ref<Material>> cached_materials;
+
+ String path = String();
+ Node3D *root_owner = nullptr;
+ Node3D *root = nullptr;
+ real_t scale = 0.01;
+ Ref<FBXNode> fbx_root_node = Ref<FBXNode>();
+ // skeleton map - merged automatically when they are on the same x node in the tree so we can merge them automatically.
+ Map<uint64_t, Ref<FBXSkeleton>> skeleton_map = Map<uint64_t, Ref<FBXSkeleton>>();
+
+ // nodes on the same level get merged automatically.
+ //Map<uint64_t, Skeleton3D *> armature_map;
+ AnimationPlayer *animation_player = nullptr;
+
+ // Generation 4 - Raw document accessing for bone/skin/joint/kLocators
+ // joints are not necessarily bones but must be merged into the skeleton
+ // (bone id), bone
+ Map<uint64_t, Ref<FBXBone>> fbx_bone_map = Map<uint64_t, Ref<FBXBone>>(); // this is the bone name and setup information required for joints
+ // this will never contain joints only bones attached to a mesh.
+
+ // Generation 4 - Raw document for creating the nodes transforms in the scene
+ // this is a list of the nodes in the scene
+ // (id, node)
+ List<Ref<FBXNode>> fbx_node_list = List<Ref<FBXNode>>();
+
+ // All nodes which have been created in the scene
+ // this will not contain the root node of the scene
+ Map<uint64_t, Ref<FBXNode>> fbx_target_map = Map<uint64_t, Ref<FBXNode>>();
+
+ // mesh nodes which are created in node / mesh step - used for populating skin poses in MeshSkins
+ Map<uint64_t, Ref<FBXNode>> MeshNodes = Map<uint64_t, Ref<FBXNode>>();
+ // mesh skin map
+ Map<uint64_t, Ref<Skin>> MeshSkins = Map<uint64_t, Ref<Skin>>();
+
+ // this is the container for the mesh weight information and eventually
+ // any mesh data
+ // but not the skin, just stuff important for rendering
+ // skin is applied to mesh instance so not really required to be in here yet.
+ // maybe later
+ // fbx mesh id, FBXMeshData
+ Map<uint64_t, Ref<FBXMeshData>> renderer_mesh_data = Map<uint64_t, Ref<FBXMeshData>>();
+};
+
+#endif // IMPORT_STATE_H
diff --git a/modules/fbx/data/model_abstraction.h b/modules/fbx/data/model_abstraction.h
new file mode 100644
index 0000000000..528960ab49
--- /dev/null
+++ b/modules/fbx/data/model_abstraction.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* model_abstraction.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 MODEL_ABSTRACTION_H
+#define MODEL_ABSTRACTION_H
+
+#include "modules/fbx/fbx_parser/FBXDocument.h"
+
+struct ModelAbstraction {
+ mutable const FBXDocParser::Model *fbx_model = nullptr;
+
+ void set_model(const FBXDocParser::Model *p_model) {
+ fbx_model = p_model;
+ }
+
+ bool has_model() const {
+ return fbx_model != nullptr;
+ }
+
+ const FBXDocParser::Model *get_model() const {
+ return fbx_model;
+ }
+};
+
+#endif // MODEL_ABSTRACTION_H
diff --git a/modules/fbx/data/pivot_transform.cpp b/modules/fbx/data/pivot_transform.cpp
new file mode 100644
index 0000000000..4cf42257a4
--- /dev/null
+++ b/modules/fbx/data/pivot_transform.cpp
@@ -0,0 +1,307 @@
+/*************************************************************************/
+/* pivot_transform.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 "pivot_transform.h"
+
+#include "tools/import_utils.h"
+
+void PivotTransform::ReadTransformChain() {
+ const FBXDocParser::PropertyTable *props = fbx_model;
+ const FBXDocParser::Model::RotOrder &rot = fbx_model->RotationOrder();
+ const FBXDocParser::TransformInheritance &inheritType = fbx_model->InheritType();
+ inherit_type = inheritType; // copy the inherit type we need it in the second step.
+ print_verbose("Model: " + String(fbx_model->Name().c_str()) + " Has inherit type: " + itos(fbx_model->InheritType()));
+ bool ok = false;
+ raw_pre_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "PreRotation", ok));
+ if (ok) {
+ pre_rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(raw_pre_rotation));
+ print_verbose("valid pre_rotation: " + raw_pre_rotation + " euler conversion: " + (pre_rotation.get_euler() * (180 / Math_PI)));
+ }
+ raw_post_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "PostRotation", ok));
+ if (ok) {
+ post_rotation = ImportUtils::EulerToQuaternion(FBXDocParser::Model::RotOrder_EulerXYZ, ImportUtils::deg2rad(raw_post_rotation));
+ print_verbose("valid post_rotation: " + raw_post_rotation + " euler conversion: " + (pre_rotation.get_euler() * (180 / Math_PI)));
+ }
+ const Vector3 &RotationPivot = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "RotationPivot", ok));
+ if (ok) {
+ rotation_pivot = ImportUtils::FixAxisConversions(RotationPivot);
+ }
+ const Vector3 &RotationOffset = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "RotationOffset", ok));
+ if (ok) {
+ rotation_offset = ImportUtils::FixAxisConversions(RotationOffset);
+ }
+ const Vector3 &ScalingOffset = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "ScalingOffset", ok));
+ if (ok) {
+ scaling_offset = ImportUtils::FixAxisConversions(ScalingOffset);
+ }
+ const Vector3 &ScalingPivot = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "ScalingPivot", ok));
+ if (ok) {
+ scaling_pivot = ImportUtils::FixAxisConversions(ScalingPivot);
+ }
+ const Vector3 &Translation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Translation", ok));
+ if (ok) {
+ translation = ImportUtils::FixAxisConversions(Translation);
+ }
+ raw_rotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Rotation", ok));
+ if (ok) {
+ rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(raw_rotation));
+ }
+ const Vector3 &Scaling = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "Lcl Scaling", ok));
+ if (ok) {
+ scaling = Scaling;
+ } else {
+ scaling = Vector3(1, 1, 1);
+ }
+ const Vector3 &GeometricScaling = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricScaling", ok));
+ if (ok) {
+ geometric_scaling = GeometricScaling;
+ } else {
+ geometric_scaling = Vector3(1, 1, 1);
+ }
+
+ const Vector3 &GeometricRotation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricRotation", ok));
+ if (ok) {
+ geometric_rotation = ImportUtils::EulerToQuaternion(rot, ImportUtils::deg2rad(GeometricRotation));
+ } else {
+ geometric_rotation = Quaternion();
+ }
+
+ const Vector3 &GeometricTranslation = ImportUtils::safe_import_vector3(FBXDocParser::PropertyGet<Vector3>(props, "GeometricTranslation", ok));
+ if (ok) {
+ geometric_translation = ImportUtils::FixAxisConversions(GeometricTranslation);
+ } else {
+ geometric_translation = Vector3(0, 0, 0);
+ }
+
+ if (geometric_rotation != Quaternion()) {
+ print_error("geometric rotation is unsupported!");
+ //CRASH_COND(true);
+ }
+
+ if (!geometric_scaling.is_equal_approx(Vector3(1, 1, 1))) {
+ print_error("geometric scaling is unsupported!");
+ //CRASH_COND(true);
+ }
+
+ if (!geometric_translation.is_equal_approx(Vector3(0, 0, 0))) {
+ print_error("geometric translation is unsupported.");
+ //CRASH_COND(true);
+ }
+}
+
+Transform3D PivotTransform::ComputeLocalTransform(Vector3 p_translation, Quaternion p_rotation, Vector3 p_scaling) const {
+ Transform3D T, Roff, Rp, Soff, Sp, S;
+
+ // Here I assume this is the operation which needs done.
+ // Its WorldTransform * V
+
+ // Origin pivots
+ T.set_origin(p_translation);
+ Roff.set_origin(rotation_offset);
+ Rp.set_origin(rotation_pivot);
+ Soff.set_origin(scaling_offset);
+ Sp.set_origin(scaling_pivot);
+
+ // Scaling node
+ S.scale(p_scaling);
+ // Rotation pivots
+ Transform3D Rpre = Transform3D(pre_rotation);
+ Transform3D R = Transform3D(p_rotation);
+ Transform3D Rpost = Transform3D(post_rotation);
+
+ return T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse();
+}
+
+Transform3D PivotTransform::ComputeGlobalTransform(Transform3D t) const {
+ Vector3 pos = t.origin;
+ Vector3 scale = t.basis.get_scale();
+ Quaternion rot = t.basis.get_rotation_quaternion();
+ return ComputeGlobalTransform(pos, rot, scale);
+}
+
+Transform3D PivotTransform::ComputeLocalTransform(Transform3D t) const {
+ Vector3 pos = t.origin;
+ Vector3 scale = t.basis.get_scale();
+ Quaternion rot = t.basis.get_rotation_quaternion();
+ return ComputeLocalTransform(pos, rot, scale);
+}
+
+Transform3D PivotTransform::ComputeGlobalTransform(Vector3 p_translation, Quaternion p_rotation, Vector3 p_scaling) const {
+ Transform3D T, Roff, Rp, Soff, Sp, S;
+
+ // Here I assume this is the operation which needs done.
+ // Its WorldTransform * V
+
+ // Origin pivots
+ T.set_origin(p_translation);
+ Roff.set_origin(rotation_offset);
+ Rp.set_origin(rotation_pivot);
+ Soff.set_origin(scaling_offset);
+ Sp.set_origin(scaling_pivot);
+
+ // Scaling node
+ S.scale(p_scaling);
+
+ // Rotation pivots
+ Transform3D Rpre = Transform3D(pre_rotation);
+ Transform3D R = Transform3D(p_rotation);
+ Transform3D Rpost = Transform3D(post_rotation);
+
+ Transform3D parent_global_xform;
+ Transform3D parent_local_scaling_m;
+
+ if (parent_transform.is_valid()) {
+ parent_global_xform = parent_transform->GlobalTransform;
+ parent_local_scaling_m = parent_transform->Local_Scaling_Matrix;
+ }
+
+ Transform3D local_rotation_m, parent_global_rotation_m;
+ Quaternion parent_global_rotation = parent_global_xform.basis.get_rotation_quaternion();
+ parent_global_rotation_m.basis.set_quaternion(parent_global_rotation);
+ local_rotation_m = Rpre * R * Rpost;
+
+ //Basis parent_global_rotation = Basis(parent_global_xform.get_basis().get_rotation_quaternion().normalized());
+
+ Transform3D local_shear_scaling, parent_shear_scaling, parent_shear_rotation, parent_shear_translation;
+ Vector3 parent_translation = parent_global_xform.get_origin();
+ parent_shear_translation.origin = parent_translation;
+ parent_shear_rotation = parent_shear_translation.affine_inverse() * parent_global_xform;
+ parent_shear_scaling = parent_global_rotation_m.affine_inverse() * parent_shear_rotation;
+ local_shear_scaling = S;
+
+ // Inherit type handler - we don't care about T here, just reordering RSrs etc.
+ Transform3D global_rotation_scale;
+ if (inherit_type == FBXDocParser::Transform_RrSs) {
+ global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_shear_scaling * local_shear_scaling;
+ } else if (inherit_type == FBXDocParser::Transform_RSrs) {
+ global_rotation_scale = parent_global_rotation_m * parent_shear_scaling * local_rotation_m * local_shear_scaling;
+ } else if (inherit_type == FBXDocParser::Transform_Rrs) {
+ Transform3D parent_global_shear_m_noLocal = parent_shear_scaling * parent_local_scaling_m.affine_inverse();
+ global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_global_shear_m_noLocal * local_shear_scaling;
+ }
+ Transform3D local_transform = T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse();
+ //Transform3D local_translation_pivoted = Transform3D(Basis(), LocalTransform.origin);
+
+ ERR_FAIL_COND_V_MSG(local_transform.basis.determinant() == 0, Transform3D(), "Det == 0 prevented in scene file");
+
+ // manual hack to force SSC not to be compensated for - until we can handle it properly with tests
+ return parent_global_xform * local_transform;
+}
+
+void PivotTransform::ComputePivotTransform() {
+ Transform3D T, Roff, Rp, Soff, Sp, S;
+
+ // Here I assume this is the operation which needs done.
+ // Its WorldTransform * V
+
+ // Origin pivots
+ T.set_origin(translation);
+ Roff.set_origin(rotation_offset);
+ Rp.set_origin(rotation_pivot);
+ Soff.set_origin(scaling_offset);
+ Sp.set_origin(scaling_pivot);
+
+ // Scaling node
+ if (!scaling.is_equal_approx(Vector3())) {
+ S.scale(scaling);
+ } else {
+ S.scale(Vector3(1, 1, 1));
+ }
+ Local_Scaling_Matrix = S; // copy for when node / child is looking for the value of this.
+
+ // Rotation pivots
+ Transform3D Rpre = Transform3D(pre_rotation);
+ Transform3D R = Transform3D(rotation);
+ Transform3D Rpost = Transform3D(post_rotation);
+
+ Transform3D parent_global_xform;
+ Transform3D parent_local_scaling_m;
+
+ if (parent_transform.is_valid()) {
+ parent_global_xform = parent_transform->GlobalTransform;
+ parent_local_scaling_m = parent_transform->Local_Scaling_Matrix;
+ }
+
+ Transform3D local_rotation_m, parent_global_rotation_m;
+ Quaternion parent_global_rotation = parent_global_xform.basis.get_rotation_quaternion();
+ parent_global_rotation_m.basis.set_quaternion(parent_global_rotation);
+ local_rotation_m = Rpre * R * Rpost;
+
+ //Basis parent_global_rotation = Basis(parent_global_xform.get_basis().get_rotation_quaternion().normalized());
+
+ Transform3D local_shear_scaling, parent_shear_scaling, parent_shear_rotation, parent_shear_translation;
+ Vector3 parent_translation = parent_global_xform.get_origin();
+ parent_shear_translation.origin = parent_translation;
+ parent_shear_rotation = parent_shear_translation.affine_inverse() * parent_global_xform;
+ parent_shear_scaling = parent_global_rotation_m.affine_inverse() * parent_shear_rotation;
+ local_shear_scaling = S;
+
+ // Inherit type handler - we don't care about T here, just reordering RSrs etc.
+ Transform3D global_rotation_scale;
+ if (inherit_type == FBXDocParser::Transform_RrSs) {
+ global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_shear_scaling * local_shear_scaling;
+ } else if (inherit_type == FBXDocParser::Transform_RSrs) {
+ global_rotation_scale = parent_global_rotation_m * parent_shear_scaling * local_rotation_m * local_shear_scaling;
+ } else if (inherit_type == FBXDocParser::Transform_Rrs) {
+ Transform3D parent_global_shear_m_noLocal = parent_shear_scaling * parent_local_scaling_m.inverse();
+ global_rotation_scale = parent_global_rotation_m * local_rotation_m * parent_global_shear_m_noLocal * local_shear_scaling;
+ }
+ LocalTransform = Transform3D();
+ LocalTransform = T * Roff * Rp * Rpre * R * Rpost.affine_inverse() * Rp.affine_inverse() * Soff * Sp * S * Sp.affine_inverse();
+
+ ERR_FAIL_COND_MSG(LocalTransform.basis.determinant() == 0, "invalid scale reset");
+
+ Transform3D local_translation_pivoted = Transform3D(Basis(), LocalTransform.origin);
+ GlobalTransform = Transform3D();
+ //GlobalTransform = parent_global_xform * LocalTransform;
+ Transform3D global_origin = Transform3D(Basis(), parent_translation);
+ GlobalTransform = (global_origin * local_translation_pivoted) * global_rotation_scale;
+
+ ImportUtils::debug_xform("local xform calculation", LocalTransform);
+ print_verbose("scale of node: " + S.basis.get_scale_local());
+ print_verbose("---------------------------------------------------------------");
+}
+
+void PivotTransform::Execute() {
+ ReadTransformChain();
+ ComputePivotTransform();
+
+ ImportUtils::debug_xform("global xform: ", GlobalTransform);
+
+ if (LocalTransform.basis.determinant() == 0) {
+ print_error("Serious det == 0!");
+ }
+
+ if (GlobalTransform.basis.determinant() == 0) {
+ print_error("Serious! node has det == 0!");
+ }
+
+ computed_global_xform = true;
+}
diff --git a/modules/fbx/data/pivot_transform.h b/modules/fbx/data/pivot_transform.h
new file mode 100644
index 0000000000..099b268075
--- /dev/null
+++ b/modules/fbx/data/pivot_transform.h
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* pivot_transform.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 PIVOT_TRANSFORM_H
+#define PIVOT_TRANSFORM_H
+
+#include "core/math/transform_3d.h"
+#include "core/object/ref_counted.h"
+
+#include "model_abstraction.h"
+
+#include "fbx_parser/FBXDocument.h"
+#include "tools/import_utils.h"
+
+enum TransformationComp {
+ TransformationComp_Translation,
+ TransformationComp_Scaling,
+ TransformationComp_Rotation,
+ TransformationComp_RotationOffset,
+ TransformationComp_RotationPivot,
+ TransformationComp_PreRotation,
+ TransformationComp_PostRotation,
+ TransformationComp_ScalingOffset,
+ TransformationComp_ScalingPivot,
+ TransformationComp_GeometricTranslation,
+ TransformationComp_GeometricRotation,
+ TransformationComp_GeometricScaling,
+ TransformationComp_MAXIMUM
+};
+// Abstract away pivot data so its simpler to handle
+struct PivotTransform : RefCounted, ModelAbstraction {
+ // at the end we want to keep geometric_ everything, post and pre rotation
+ // these are used during animation data processing / keyframe ingestion the rest can be simplified down / out.
+ Quaternion pre_rotation = Quaternion();
+ Quaternion post_rotation = Quaternion();
+ Quaternion rotation = Quaternion();
+ Quaternion geometric_rotation = Quaternion();
+ Vector3 rotation_pivot = Vector3();
+ Vector3 rotation_offset = Vector3();
+ Vector3 scaling_offset = Vector3(1.0, 1.0, 1.0);
+ Vector3 scaling_pivot = Vector3(1.0, 1.0, 1.0);
+ Vector3 translation = Vector3();
+ Vector3 scaling = Vector3(1.0, 1.0, 1.0);
+ Vector3 geometric_scaling = Vector3(1.0, 1.0, 1.0);
+ Vector3 geometric_translation = Vector3();
+
+ Vector3 raw_rotation = Vector3();
+ Vector3 raw_post_rotation = Vector3();
+ Vector3 raw_pre_rotation = Vector3();
+
+ /* Read pivots from the document */
+ void ReadTransformChain();
+
+ void debug_pivot_xform(String p_name) {
+ print_verbose("debugging node name: " + p_name);
+ print_verbose("raw rotation: " + raw_rotation * (180 / Math_PI));
+ print_verbose("raw pre_rotation " + raw_pre_rotation * (180 / Math_PI));
+ print_verbose("raw post_rotation " + raw_post_rotation * (180 / Math_PI));
+ }
+
+ Transform3D ComputeGlobalTransform(Transform3D t) const;
+ Transform3D ComputeLocalTransform(Transform3D t) const;
+ Transform3D ComputeGlobalTransform(Vector3 p_translation, Quaternion p_rotation, Vector3 p_scaling) const;
+ Transform3D ComputeLocalTransform(Vector3 p_translation, Quaternion p_rotation, Vector3 p_scaling) const;
+
+ /* Extract into xforms and calculate once */
+ void ComputePivotTransform();
+
+ /* Execute the command for the pivot generation */
+ void Execute();
+
+ void set_parent(Ref<PivotTransform> p_parent) {
+ parent_transform = p_parent;
+ }
+
+ bool computed_global_xform = false;
+ Ref<PivotTransform> parent_transform = Ref<PivotTransform>();
+ //Transform chain[TransformationComp_MAXIMUM];
+
+ // cached for later use
+ Transform3D GlobalTransform = Transform3D();
+ Transform3D LocalTransform = Transform3D();
+ Transform3D Local_Scaling_Matrix = Transform3D(); // used for inherit type.
+ Transform3D GeometricTransform = Transform3D(); // 3DS max only
+ FBXDocParser::TransformInheritance inherit_type = FBXDocParser::TransformInheritance_MAX; // maya fbx requires this - sorry <3
+};
+
+#endif // PIVOT_TRANSFORM_H
diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp
new file mode 100644
index 0000000000..b11c145599
--- /dev/null
+++ b/modules/fbx/editor_scene_importer_fbx.cpp
@@ -0,0 +1,1470 @@
+/*************************************************************************/
+/* editor_scene_importer_fbx.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 "editor_scene_importer_fbx.h"
+
+#include "data/fbx_anim_container.h"
+#include "data/fbx_material.h"
+#include "data/fbx_mesh_data.h"
+#include "data/fbx_skeleton.h"
+#include "tools/import_utils.h"
+
+#include "core/io/image_loader.h"
+#include "editor/editor_log.h"
+#include "editor/editor_node.h"
+#include "editor/import/resource_importer_scene.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"
+
+#include "fbx_parser/FBXDocument.h"
+#include "fbx_parser/FBXImportSettings.h"
+#include "fbx_parser/FBXMeshGeometry.h"
+#include "fbx_parser/FBXParser.h"
+#include "fbx_parser/FBXProperties.h"
+#include "fbx_parser/FBXTokenizer.h"
+
+#include <string>
+
+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";
+ Vector<String> exts;
+ exts.push_back(fbx_str);
+ _register_project_setting_import(fbx_str, import_setting_string, exts, r_extensions, true);
+}
+
+void EditorSceneFormatImporterFBX::_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 {
+ const String use_generic = "use_" + generic;
+ _GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true);
+ if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) {
+ for (int32_t i = 0; i < exts.size(); i++) {
+ r_extensions->push_back(exts[i]);
+ }
+ }
+}
+
+uint32_t EditorSceneFormatImporterFBX::get_import_flags() const {
+ return IMPORT_SCENE;
+}
+
+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()) {
+ EditorLog *log = EditorNode::get_log();
+ log->clear();
+ }
+ Error err;
+ FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
+
+ ERR_FAIL_COND_V(!f, nullptr);
+
+ {
+ PackedByteArray data;
+ // broadphase tokenizing pass in which we identify the core
+ // syntax elements of FBX (brackets, commas, key:value mappings)
+ FBXDocParser::TokenList tokens;
+
+ bool is_binary = false;
+ data.resize(f->get_length());
+
+ ERR_FAIL_COND_V(data.size() < 64, nullptr);
+
+ f->get_buffer(data.ptrw(), data.size());
+ PackedByteArray fbx_header;
+ fbx_header.resize(64);
+ for (int32_t byte_i = 0; byte_i < 64; byte_i++) {
+ fbx_header.ptrw()[byte_i] = data.ptr()[byte_i];
+ }
+
+ String fbx_header_string;
+ if (fbx_header.size() >= 0) {
+ fbx_header_string.parse_utf8((const char *)fbx_header.ptr(), fbx_header.size());
+ }
+
+ print_verbose("[doc] opening fbx file: " + p_path);
+ print_verbose("[doc] fbx header: " + fbx_header_string);
+ bool corrupt = false;
+
+ // safer to check this way as there can be different formatted headers
+ if (fbx_header_string.find("Kaydara FBX Binary", 0) != -1) {
+ is_binary = true;
+ print_verbose("[doc] is binary");
+
+ FBXDocParser::TokenizeBinary(tokens, (const char *)data.ptrw(), (size_t)data.size(), corrupt);
+
+ } else {
+ print_verbose("[doc] is ascii");
+ FBXDocParser::Tokenize(tokens, (const char *)data.ptrw(), (size_t)data.size(), corrupt);
+ }
+
+ if (corrupt) {
+ for (FBXDocParser::TokenPtr token : tokens) {
+ delete token;
+ }
+ tokens.clear();
+ ERR_PRINT(vformat("Cannot import FBX file: %s the file is corrupt so we safely exited parsing the file.", p_path));
+ return memnew(Node3D);
+ }
+
+ // The import process explained:
+ // 1. Tokens are made, these are then taken into the 'parser' below
+ // 2. The parser constructs 'Elements' and all 'real' FBX Types.
+ // 3. This creates a problem: shared_ptr ownership, should Elements later 'take ownership'
+ // 4. No, it shouldn't so we should either a.) use weak ref for elements; but this is not correct.
+
+ // use this information to construct a very rudimentary
+ // parse-tree representing the FBX scope structure
+ FBXDocParser::Parser parser(tokens, is_binary);
+
+ if (parser.IsCorrupt()) {
+ for (FBXDocParser::TokenPtr token : tokens) {
+ delete token;
+ }
+ tokens.clear();
+ ERR_PRINT(vformat("Cannot import FBX file: %s the file is corrupt so we safely exited parsing the file.", p_path));
+ return memnew(Node3D);
+ }
+
+ FBXDocParser::ImportSettings settings;
+ settings.strictMode = false;
+
+ // this function leaks a lot
+ FBXDocParser::Document doc(parser, settings);
+
+ // yeah so closing the file is a good idea (prevents readonly states)
+ f->close();
+
+ // safety for version handling
+ if (doc.IsSafeToImport()) {
+ bool is_blender_fbx = false;
+ const FBXDocParser::PropertyTable &import_props = doc.GetMetadataProperties();
+ const FBXDocParser::PropertyPtr app_name = import_props.Get("Original|ApplicationName");
+ const FBXDocParser::PropertyPtr app_vendor = import_props.Get("Original|ApplicationVendor");
+ const FBXDocParser::PropertyPtr app_version = import_props.Get("Original|ApplicationVersion");
+ //
+ if (app_name) {
+ const FBXDocParser::TypedProperty<std::string> *app_name_string = dynamic_cast<const FBXDocParser::TypedProperty<std::string> *>(app_name);
+ if (app_name_string) {
+ print_verbose("FBX App Name: " + String(app_name_string->Value().c_str()));
+ }
+ }
+
+ if (app_vendor) {
+ const FBXDocParser::TypedProperty<std::string> *app_vendor_string = dynamic_cast<const FBXDocParser::TypedProperty<std::string> *>(app_vendor);
+ if (app_vendor_string) {
+ print_verbose("FBX App Vendor: " + String(app_vendor_string->Value().c_str()));
+ is_blender_fbx = app_vendor_string->Value().find("Blender") != std::string::npos;
+ }
+ }
+
+ if (app_version) {
+ const FBXDocParser::TypedProperty<std::string> *app_version_string = dynamic_cast<const FBXDocParser::TypedProperty<std::string> *>(app_version);
+ if (app_version_string) {
+ print_verbose("FBX App Version: " + String(app_version_string->Value().c_str()));
+ }
+ }
+
+ if (is_blender_fbx) {
+ WARN_PRINT("We don't officially support Blender FBX animations yet, due to issues with upstream Blender,\n"
+ "so please wait for us to work around remaining issues. We will continue to import the file but it may be broken.\n"
+ "For minimal breakage, please export FBX from Blender with -Z forward, and Y up.");
+ }
+
+ Node3D *spatial = _generate_scene(p_path, &doc, p_flags, p_bake_fps, 8, is_blender_fbx);
+ // todo: move to document shutdown (will need to be validated after moving; this code has been validated already)
+ for (FBXDocParser::TokenPtr token : tokens) {
+ if (token) {
+ delete token;
+ token = nullptr;
+ }
+ }
+
+ return spatial;
+
+ } else {
+ for (FBXDocParser::TokenPtr token : tokens) {
+ delete token;
+ }
+ tokens.clear();
+
+ ERR_PRINT(vformat("Cannot import FBX file: %s. It uses file format %d which is unsupported by Godot. Please re-export it or convert it to a newer format.", p_path, doc.FBXVersion()));
+ }
+ }
+
+ return memnew(Node3D);
+}
+
+template <class T>
+struct EditorSceneFormatImporterAssetImportInterpolate {
+ T lerp(const T &a, const T &b, float c) const {
+ return a + (b - a) * c;
+ }
+
+ T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) {
+ const float t2 = t * t;
+ const float t3 = t2 * t;
+
+ return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
+ }
+
+ T bezier(T start, T control_1, T control_2, T end, float t) {
+ /* Formula from Wikipedia article on Bezier curves. */
+ const real_t omt = (1.0 - t);
+ const real_t omt2 = omt * omt;
+ const real_t omt3 = omt2 * omt;
+ const real_t t2 = t * t;
+ const real_t t3 = t2 * t;
+
+ return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
+ }
+};
+
+//thank you for existing, partial specialization
+template <>
+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());
+
+ return a.slerp(b, c).normalized();
+ }
+
+ Quaternion catmull_rom(const Quaternion &p0, const Quaternion &p1, const Quaternion &p2, const Quaternion &p3, float c) {
+ ERR_FAIL_COND_V(!p1.is_normalized(), Quaternion());
+ ERR_FAIL_COND_V(!p2.is_normalized(), Quaternion());
+
+ return p1.slerp(p2, c).normalized();
+ }
+
+ Quaternion bezier(Quaternion start, Quaternion control_1, Quaternion control_2, Quaternion end, float t) {
+ ERR_FAIL_COND_V(!start.is_normalized(), Quaternion());
+ ERR_FAIL_COND_V(!end.is_normalized(), Quaternion());
+
+ return start.slerp(end, t).normalized();
+ }
+};
+
+template <class T>
+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;
+ for (int i = 0; i < p_times.size(); i++) {
+ if (p_times[i] > p_time) {
+ break;
+ }
+ idx++;
+ }
+
+ EditorSceneFormatImporterAssetImportInterpolate<T> interp;
+
+ switch (p_interp) {
+ case AssetImportAnimation::INTERP_LINEAR: {
+ if (idx == -1) {
+ return p_values[0];
+ } else if (idx >= p_times.size() - 1) {
+ return p_values[p_times.size() - 1];
+ }
+
+ float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
+
+ return interp.lerp(p_values[idx], p_values[idx + 1], c);
+
+ } break;
+ case AssetImportAnimation::INTERP_STEP: {
+ if (idx == -1) {
+ return p_values[0];
+ } else if (idx >= p_times.size() - 1) {
+ return p_values[p_times.size() - 1];
+ }
+
+ return p_values[idx];
+
+ } break;
+ case AssetImportAnimation::INTERP_CATMULLROMSPLINE: {
+ if (idx == -1) {
+ return p_values[1];
+ } else if (idx >= p_times.size() - 1) {
+ return p_values[1 + p_times.size() - 1];
+ }
+
+ float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
+
+ return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c);
+
+ } break;
+ case AssetImportAnimation::INTERP_CUBIC_SPLINE: {
+ if (idx == -1) {
+ return p_values[1];
+ } else if (idx >= p_times.size() - 1) {
+ return p_values[(p_times.size() - 1) * 3 + 1];
+ }
+
+ float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
+
+ T from = p_values[idx * 3 + 1];
+ T c1 = from + p_values[idx * 3 + 2];
+ T to = p_values[idx * 3 + 4];
+ T c2 = to + p_values[idx * 3 + 3];
+
+ return interp.bezier(from, c1, c2, to, c);
+
+ } break;
+ }
+
+ ERR_FAIL_V(p_values[0]);
+}
+
+Node3D *EditorSceneFormatImporterFBX::_generate_scene(
+ const String &p_path,
+ const FBXDocParser::Document *p_document,
+ const uint32_t p_flags,
+ int p_bake_fps,
+ const int32_t p_max_bone_weights,
+ bool p_is_blender_fbx) {
+ ImportState state;
+ state.is_blender_fbx = p_is_blender_fbx;
+ state.path = p_path;
+ state.animation_player = nullptr;
+
+ // create new root node for scene
+ Node3D *scene_root = memnew(Node3D);
+ state.root = memnew(Node3D);
+ state.root_owner = scene_root; // the real scene root... sorry compatibility code is painful...
+
+ state.root->set_name("RootNode");
+ scene_root->add_child(state.root);
+ state.root->set_owner(scene_root);
+
+ state.fbx_root_node.instantiate();
+ state.fbx_root_node->godot_node = state.root;
+
+ // Size relative to cm.
+ const real_t fbx_unit_scale = p_document->GlobalSettingsPtr()->UnitScaleFactor();
+
+ print_verbose("FBX unit scale import value: " + rtos(fbx_unit_scale));
+ // Set FBX file scale is relative to CM must be converted to M
+ state.scale = fbx_unit_scale / 100.0;
+ print_verbose("FBX unit scale is: " + rtos(state.scale));
+
+ // Enabled by default.
+ state.enable_material_import = true;
+ // Enabled by default.
+ state.enable_animation_import = true;
+ Ref<FBXNode> root_node;
+ root_node.instantiate();
+
+ // make sure fake noFBXDocParser::PropertyPtr ptrde always has a transform too ;)
+ Ref<PivotTransform> pivot_transform;
+ pivot_transform.instantiate();
+ root_node->pivot_transform = pivot_transform;
+ root_node->node_name = "root node";
+ root_node->current_node_id = 0;
+ root_node->godot_node = state.root;
+
+ // cache this node onto the fbx_target map.
+ state.fbx_target_map.insert(0, root_node);
+
+ // cache basic node information from FBX document
+ // grabs all FBX bones
+ BuildDocumentBones(Ref<FBXBone>(), state, p_document, 0L);
+ BuildDocumentNodes(Ref<PivotTransform>(), state, p_document, 0L, nullptr);
+
+ // Build document skinning information
+
+ // Algorithm is this:
+ // Get Deformer: object with "Skin" class.
+ // Deformer:: has link to Geometry:: (correct mesh for skin)
+ // Deformer:: has Source which is the SubDeformer:: (e.g. the Cluster)
+ // Notes at the end it configures the vertex weight mapping.
+
+ for (uint64_t skin_id : p_document->GetSkinIDs()) {
+ // Validate the parser
+ FBXDocParser::LazyObject *lazy_skin = p_document->GetObject(skin_id);
+ ERR_CONTINUE_MSG(lazy_skin == nullptr, "invalid lazy object [serious parser bug]");
+
+ // Validate the parser
+ const FBXDocParser::Skin *skin = lazy_skin->Get<FBXDocParser::Skin>();
+ ERR_CONTINUE_MSG(skin == nullptr, "invalid skin added to skin list [parser bug]");
+
+ const std::vector<const FBXDocParser::Connection *> source_to_destination = p_document->GetConnectionsBySourceSequenced(skin_id);
+ FBXDocParser::MeshGeometry *mesh = nullptr;
+ uint64_t mesh_id = 0;
+
+ // Most likely only contains the mesh link for the skin
+ // The mesh geometry.
+ for (const FBXDocParser::Connection *con : source_to_destination) {
+ // do something
+ print_verbose("src: " + itos(con->src));
+ FBXDocParser::Object *ob = con->DestinationObject();
+ mesh = dynamic_cast<FBXDocParser::MeshGeometry *>(ob);
+
+ if (mesh) {
+ mesh_id = mesh->ID();
+ break;
+ }
+ }
+
+ // Validate the mesh exists and was retrieved
+ ERR_CONTINUE_MSG(mesh_id == 0, "mesh id is invalid");
+ const std::vector<const FBXDocParser::Cluster *> clusters = skin->Clusters();
+
+ // NOTE: this will ONLY work on skinned bones (it is by design.)
+ // A cluster is a skinned bone so SKINS won't contain unskinned bones so we need to pre-add all bones and parent them in a step beforehand.
+ for (const FBXDocParser::Cluster *cluster : clusters) {
+ ERR_CONTINUE_MSG(cluster == nullptr, "invalid bone cluster");
+ const uint64_t deformer_id = cluster->ID();
+ std::vector<const FBXDocParser::Connection *> connections = p_document->GetConnectionsByDestinationSequenced(deformer_id);
+
+ // Weight data always has a node in the scene lets grab the limb's node in the scene :) (reverse set to true since it's the opposite way around)
+ const FBXDocParser::ModelLimbNode *limb_node = ProcessDOMConnection<FBXDocParser::ModelLimbNode>(p_document, deformer_id, true);
+
+ ERR_CONTINUE_MSG(limb_node == nullptr, "unable to resolve model for skinned bone");
+
+ const uint64_t model_id = limb_node->ID();
+
+ // This will never happen, so if it does you know you fucked up.
+ ERR_CONTINUE_MSG(!state.fbx_bone_map.has(model_id), "missing LimbNode detected");
+
+ // new bone instance
+ Ref<FBXBone> bone_element = state.fbx_bone_map[model_id];
+
+ //
+ // Bone Weight Information Configuration
+ //
+
+ // Cache Weight Information into bone for later usage if you want the raw data.
+ const std::vector<unsigned int> &indexes = cluster->GetIndices();
+ const std::vector<float> &weights = cluster->GetWeights();
+ Ref<FBXMeshData> mesh_vertex_data;
+
+ // this data will pre-exist if vertex weight information is found
+ if (state.renderer_mesh_data.has(mesh_id)) {
+ mesh_vertex_data = state.renderer_mesh_data[mesh_id];
+ } else {
+ mesh_vertex_data.instantiate();
+ state.renderer_mesh_data.insert(mesh_id, mesh_vertex_data);
+ }
+
+ mesh_vertex_data->armature_id = bone_element->armature_id;
+ mesh_vertex_data->valid_armature_id = true;
+
+ //print_verbose("storing mesh vertex data for mesh to use later");
+ ERR_CONTINUE_MSG(indexes.size() != weights.size(), "[doc] error mismatch between weight info");
+
+ for (size_t idx = 0; idx < indexes.size(); idx++) {
+ const size_t vertex_index = indexes[idx];
+ const real_t influence_weight = weights[idx];
+
+ VertexWeightMapping &vm = mesh_vertex_data->vertex_weights[vertex_index];
+ vm.weights.push_back(influence_weight);
+ vm.bones.push_back(0); // bone id is pushed on here during sanitization phase
+ vm.bones_ref.push_back(bone_element);
+ }
+
+ for (const int *vertex_index = mesh_vertex_data->vertex_weights.next(nullptr);
+ vertex_index != nullptr;
+ vertex_index = mesh_vertex_data->vertex_weights.next(vertex_index)) {
+ VertexWeightMapping *vm = mesh_vertex_data->vertex_weights.getptr(*vertex_index);
+ const int influence_count = vm->weights.size();
+ if (influence_count > mesh_vertex_data->max_weight_count) {
+ mesh_vertex_data->max_weight_count = influence_count;
+ mesh_vertex_data->valid_weight_count = true;
+ }
+ }
+
+ if (mesh_vertex_data->max_weight_count > 4) {
+ if (mesh_vertex_data->max_weight_count > 8) {
+ ERR_PRINT("[doc] Serious: maximum bone influences is 8 in this branch.");
+ }
+ // Clamp to 8 bone vertex influences.
+ mesh_vertex_data->max_weight_count = 8;
+ print_verbose("[doc] Using 8 vertex bone influences configuration.");
+ } else {
+ mesh_vertex_data->max_weight_count = 4;
+ print_verbose("[doc] Using 4 vertex bone influences configuration.");
+ }
+ }
+ }
+
+ // do we globally allow for import of materials
+ // (prevents overwrite of materials; so you can handle them explicitly)
+ if (state.enable_material_import) {
+ const std::vector<uint64_t> &materials = p_document->GetMaterialIDs();
+
+ for (uint64_t material_id : materials) {
+ FBXDocParser::LazyObject *lazy_material = p_document->GetObject(material_id);
+ FBXDocParser::Material *mat = (FBXDocParser::Material *)lazy_material->Get<FBXDocParser::Material>();
+ ERR_CONTINUE_MSG(!mat, "Could not convert fbx material by id: " + itos(material_id));
+
+ Ref<FBXMaterial> material;
+ material.instantiate();
+ material->set_imported_material(mat);
+
+ Ref<StandardMaterial3D> godot_material = material->import_material(state);
+
+ state.cached_materials.insert(material_id, godot_material);
+ }
+ }
+
+ // build skin and skeleton information
+ print_verbose("[doc] Skeleton3D Bone count: " + itos(state.fbx_bone_map.size()));
+
+ // Importing bones using document based method from FBX directly
+ // We do not use the assimp bone format to determine this information anymore.
+ if (state.fbx_bone_map.size() > 0) {
+ // We are using a single skeleton only method here
+ // this is because we really have no concept of skeletons in FBX
+ // their are bones in a scene but they have no specific armature
+ // we can detect armatures but the issue lies in the complexity
+ // we opted to merge the entire scene onto one skeleton for now
+ // if we need to change this we have an archive of the old code.
+
+ // bind pose normally only has 1 per mesh but can have more than one
+ // this is the point of skins
+ // in FBX first bind pose is the master for the first skin
+
+ // In order to handle the FBX skeleton we must also inverse any parent transforms on the bones
+ // just to rule out any parent node transforms in the bone data
+ // this is trivial to do and allows us to use the single skeleton method and merge them
+ // 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 (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;
+ if (state.skeleton_map.has(armature_id)) {
+ fbx_skeleton_inst = state.skeleton_map[armature_id];
+ } else {
+ fbx_skeleton_inst.instantiate();
+ state.skeleton_map.insert(armature_id, fbx_skeleton_inst);
+ }
+
+ print_verbose("populating skeleton with bone: " + bone->bone_name);
+
+ //// populate bone skeleton - since fbx has no DOM for the skeleton just a node.
+ //bone->bone_skeleton = fbx_skeleton_inst;
+
+ // now populate bone on the armature node list
+ fbx_skeleton_inst->skeleton_bones.push_back(bone);
+
+ CRASH_COND_MSG(!state.fbx_target_map.has(armature_id), "invalid armature [serious]");
+
+ Ref<FBXNode> node = state.fbx_target_map[armature_id];
+
+ CRASH_COND_MSG(node.is_null(), "invalid node [serious]");
+ CRASH_COND_MSG(node->pivot_transform.is_null(), "invalid pivot transform [serious]");
+ fbx_skeleton_inst->fbx_node = node;
+
+ ERR_CONTINUE_MSG(fbx_skeleton_inst->fbx_node.is_null(), "invalid skeleton node [serious]");
+
+ // we need to have a valid armature id and the model configured for the bone to be assigned fully.
+ // happens once per skeleton
+
+ if (state.fbx_target_map.has(armature_id) && !fbx_skeleton_inst->fbx_node->has_model()) {
+ print_verbose("allocated fbx skeleton primary / armature node for the level: " + fbx_skeleton_inst->fbx_node->node_name);
+ } else if (!state.fbx_target_map.has(armature_id) && !fbx_skeleton_inst->fbx_node->has_model()) {
+ print_error("bones are not mapped to an armature node for armature id: " + itos(armature_id) + " bone: " + bone->bone_name);
+ // this means bone will be removed and not used, which is safe actually and no skeleton will be created.
+ }
+ }
+
+ // setup skeleton instances if required :)
+ 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");
+ }
+
+ // This list is not populated
+ for (Map<uint64_t, Ref<FBXNode>>::Element *skin_mesh = state.MeshNodes.front(); skin_mesh; skin_mesh = skin_mesh->next()) {
+ }
+ }
+
+ // build godot node tree
+ if (state.fbx_node_list.size() > 0) {
+ for (List<Ref<FBXNode>>::Element *node_element = state.fbx_node_list.front();
+ node_element;
+ node_element = node_element->next()) {
+ Ref<FBXNode> fbx_node = node_element->get();
+ ImporterMeshInstance3D *mesh_node = nullptr;
+ Ref<FBXMeshData> mesh_data_precached;
+
+ // check for valid geometry
+ if (fbx_node->fbx_model == nullptr) {
+ print_error("[doc] fundamental flaw, submit bug immediately with full import log with verbose logging on");
+ } else {
+ const std::vector<const FBXDocParser::Geometry *> &geometry = fbx_node->fbx_model->GetGeometry();
+ for (const FBXDocParser::Geometry *mesh : geometry) {
+ print_verbose("[doc] [" + itos(mesh->ID()) + "] mesh: " + fbx_node->node_name);
+
+ if (mesh == nullptr) {
+ continue;
+ }
+
+ const FBXDocParser::MeshGeometry *mesh_geometry = dynamic_cast<const FBXDocParser::MeshGeometry *>(mesh);
+ if (mesh_geometry) {
+ uint64_t mesh_id = mesh_geometry->ID();
+
+ // this data will pre-exist if vertex weight information is found
+ if (state.renderer_mesh_data.has(mesh_id)) {
+ mesh_data_precached = state.renderer_mesh_data[mesh_id];
+ } else {
+ mesh_data_precached.instantiate();
+ state.renderer_mesh_data.insert(mesh_id, mesh_data_precached);
+ }
+
+ mesh_data_precached->mesh_node = fbx_node;
+
+ // mesh node, mesh id
+ mesh_node = mesh_data_precached->create_fbx_mesh(state, mesh_geometry, fbx_node->fbx_model, false);
+ if (!state.MeshNodes.has(mesh_id)) {
+ state.MeshNodes.insert(mesh_id, fbx_node);
+ }
+ }
+
+ const FBXDocParser::ShapeGeometry *shape_geometry = dynamic_cast<const FBXDocParser::ShapeGeometry *>(mesh);
+ if (shape_geometry != nullptr) {
+ print_verbose("[doc] valid shape geometry converted");
+ }
+ }
+ }
+
+ Ref<FBXSkeleton> node_skeleton = fbx_node->skeleton_node;
+
+ if (node_skeleton.is_valid()) {
+ Skeleton3D *skel = node_skeleton->skeleton;
+ fbx_node->godot_node = skel;
+ } else if (mesh_node == nullptr) {
+ fbx_node->godot_node = memnew(Node3D);
+ } else {
+ fbx_node->godot_node = mesh_node;
+ }
+
+ fbx_node->godot_node->set_name(fbx_node->node_name);
+
+ // assign parent if valid
+ if (fbx_node->fbx_parent.is_valid()) {
+ fbx_node->fbx_parent->godot_node->add_child(fbx_node->godot_node);
+ fbx_node->godot_node->set_owner(state.root_owner);
+ }
+
+ // Node Transform debug, set local xform data.
+ fbx_node->godot_node->set_transform(get_unscaled_transform(fbx_node->pivot_transform->LocalTransform, state.scale));
+
+ // populate our mesh node reference
+ if (mesh_node != nullptr && mesh_data_precached.is_valid()) {
+ mesh_data_precached->godot_mesh_instance = mesh_node;
+ }
+ }
+ }
+
+ 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>();
+
+ ERR_CONTINUE_MSG(mesh->mesh_node.is_null(), "invalid mesh allocation");
+
+ const FBXDocParser::Skin *mesh_skin = mesh_geometry->DeformerSkin();
+
+ if (!mesh_skin) {
+ continue; // safe to continue
+ }
+
+ //
+ // Skin bone configuration
+ //
+
+ //
+ // Get Mesh Node Xform only
+ //
+ //ERR_CONTINUE_MSG(!state.fbx_target_map.has(mesh_id), "invalid xform for the skin pose: " + itos(mesh_id));
+ //Ref<FBXNode> mesh_node_xform_data = state.fbx_target_map[mesh_id];
+
+ if (!mesh_skin) {
+ continue; // not a deformer.
+ }
+
+ if (mesh_skin->Clusters().size() == 0) {
+ continue; // possibly buggy mesh
+ }
+
+ // Lookup skin or create it if it's not found.
+ Ref<Skin> skin;
+ if (!state.MeshSkins.has(mesh_id)) {
+ print_verbose("Created new skin");
+ skin.instantiate();
+ state.MeshSkins.insert(mesh_id, skin);
+ } else {
+ print_verbose("Grabbed skin");
+ skin = state.MeshSkins[mesh_id];
+ }
+
+ for (const FBXDocParser::Cluster *cluster : mesh_skin->Clusters()) {
+ // node or bone this cluster targets (in theory will only be a bone target)
+ uint64_t skin_target_id = cluster->TargetNode()->ID();
+
+ print_verbose("adding cluster [" + itos(cluster->ID()) + "] " + String(cluster->Name().c_str()) + " for target: [" + itos(skin_target_id) + "] " + String(cluster->TargetNode()->Name().c_str()));
+ ERR_CONTINUE_MSG(!state.fbx_bone_map.has(skin_target_id), "no bone found by that ID? locator");
+
+ const Ref<FBXBone> bone = state.fbx_bone_map[skin_target_id];
+ const Ref<FBXSkeleton> skeleton = bone->fbx_skeleton;
+ const Ref<FBXNode> skeleton_node = skeleton->fbx_node;
+
+ skin->add_named_bind(
+ bone->bone_name,
+ get_unscaled_transform(
+ skeleton_node->pivot_transform->GlobalTransform.affine_inverse() * cluster->TransformLink().affine_inverse(), state.scale));
+ }
+
+ print_verbose("cluster name / id: " + String(mesh_skin->Name().c_str()) + " [" + itos(mesh_skin->ID()) + "]");
+ print_verbose("skeleton has " + itos(state.fbx_bone_map.size()) + " binds");
+ print_verbose("fbx skin has " + itos(mesh_skin->Clusters().size()) + " binds");
+ }
+
+ // mesh data iteration for populating skeleton mapping
+ 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;
+ const uint64_t armature = mesh->armature_id;
+
+ if (mesh_weights > 0) {
+ // this is a bug, it means the weights were found but the skeleton wasn't
+ ERR_CONTINUE_MSG(!valid_armature, "[doc] fbx armature is missing");
+ } else {
+ continue; // safe to continue not a bug just a normal mesh
+ }
+
+ if (state.skeleton_map.has(armature)) {
+ skeleton = state.skeleton_map[armature];
+ print_verbose("[doc] armature mesh to skeleton mapping has been allocated");
+ } else {
+ print_error("[doc] unable to find armature mapping");
+ }
+
+ ERR_CONTINUE_MSG(!mesh_instance, "[doc] invalid mesh mapping for skeleton assignment");
+ ERR_CONTINUE_MSG(skeleton.is_null(), "[doc] unable to resolve the correct skeleton but we have weights!");
+
+ mesh_instance->set_skeleton_path(mesh_instance->get_path_to(skeleton->skeleton));
+ print_verbose("[doc] allocated skeleton to mesh " + mesh_instance->get_name());
+
+ // do we have a mesh skin for this mesh
+ ERR_CONTINUE_MSG(!state.MeshSkins.has(mesh_id), "no skin found for mesh");
+
+ Ref<Skin> mesh_skin = state.MeshSkins[mesh_id];
+
+ ERR_CONTINUE_MSG(mesh_skin.is_null(), "invalid skin stored in map");
+ print_verbose("[doc] allocated skin to mesh " + mesh_instance->get_name());
+ mesh_instance->set_skin(mesh_skin);
+ }
+
+ // build skin and skeleton information
+ print_verbose("[doc] Skeleton3D Bone count: " + itos(state.fbx_bone_map.size()));
+ const FBXDocParser::FileGlobalSettings *FBXSettings = p_document->GlobalSettingsPtr();
+
+ // Configure constraints
+ // NOTE: constraints won't be added quite yet, we don't have a real need for them *yet*. (they can be supported later on)
+ // const std::vector<uint64_t> fbx_constraints = p_document->GetConstraintStackIDs();
+
+ // get the animation FPS
+ float fps_setting = ImportUtils::get_fbx_fps(FBXSettings);
+
+ // enable animation import, only if local animation is enabled
+ if (state.enable_animation_import && (p_flags & IMPORT_ANIMATION)) {
+ // document animation stack list - get by ID so we can unload any non used animation stack
+ const std::vector<uint64_t> animation_stack = p_document->GetAnimationStackIDs();
+
+ for (uint64_t anim_id : animation_stack) {
+ FBXDocParser::LazyObject *lazyObject = p_document->GetObject(anim_id);
+ const FBXDocParser::AnimationStack *stack = lazyObject->Get<FBXDocParser::AnimationStack>();
+
+ if (stack != nullptr) {
+ String animation_name = ImportUtils::FBXNodeToName(stack->Name());
+ print_verbose("Valid animation stack has been found: " + animation_name);
+ // ReferenceTime is the same for some animations?
+ // LocalStop time is the start and end time
+ float r_start = CONVERT_FBX_TIME(stack->ReferenceStart());
+ float r_stop = CONVERT_FBX_TIME(stack->ReferenceStop());
+ float start_time = CONVERT_FBX_TIME(stack->LocalStart());
+ float end_time = CONVERT_FBX_TIME(stack->LocalStop());
+ float duration = end_time - start_time;
+
+ print_verbose("r_start " + rtos(r_start) + ", r_stop " + rtos(r_stop));
+ print_verbose("start_time" + rtos(start_time) + " end_time " + rtos(end_time));
+ print_verbose("anim duration : " + rtos(duration));
+
+ // we can safely create the animation player
+ if (state.animation_player == nullptr) {
+ print_verbose("Creating animation player");
+ state.animation_player = memnew(AnimationPlayer);
+ state.root->add_child(state.animation_player, true);
+ state.animation_player->set_owner(state.root_owner);
+ }
+
+ Ref<Animation> animation;
+ animation.instantiate();
+ animation->set_name(animation_name);
+ animation->set_length(duration);
+
+ print_verbose("Animation length: " + rtos(animation->get_length()) + " seconds");
+
+ // i think assimp was duplicating things, this lets me know to just reference or ignore this to prevent duplicate information in tracks
+ // this would mean that we would be doing three times as much work per track if my theory is correct.
+ // this was not the case but this is a good sanity check for the animation handler from the document.
+ // it also lets us know if the FBX specification massively changes the animation system, in theory such a change would make this show
+ // an fbx specification error, so best keep it in
+ // the overhead is tiny.
+ Map<uint64_t, const FBXDocParser::AnimationCurve *> CheckForDuplication;
+
+ const std::vector<const FBXDocParser::AnimationLayer *> &layers = stack->Layers();
+ print_verbose("FBX Animation layers: " + itos(layers.size()));
+ for (const FBXDocParser::AnimationLayer *layer : layers) {
+ std::vector<const FBXDocParser::AnimationCurveNode *> node_list = layer->Nodes();
+ print_verbose("Layer: " + ImportUtils::FBXNodeToName(layer->Name()) + ", " + " AnimCurveNode count " + itos(node_list.size()));
+
+ // first thing to do here is that i need to first get the animcurvenode to a Vector3
+ // we now need to put this into the track information for godot.
+ // to do this we need to know which track is what?
+
+ // target id, [ track name, [time index, vector] ]
+ // new map needs to be [ track name, keyframe_data ]
+ Map<uint64_t, Map<StringName, FBXTrack>> AnimCurveNodes;
+
+ // struct AnimTrack {
+ // // Animation track can be
+ // // visible, T, R, S
+ // Map<StringName, Map<uint64_t, Vector3> > animation_track;
+ // };
+
+ // Map<uint64_t, AnimTrack> AnimCurveNodes;
+
+ // so really, what does this mean to make an animtion track.
+ // we need to know what object the curves are for.
+ // we need the target ID and the target name for the track reduction.
+
+ FBXDocParser::Model::RotOrder quaternion_rotation_order = FBXDocParser::Model::RotOrder_EulerXYZ;
+
+ // T:: R:: S:: Visible:: Custom::
+ for (const FBXDocParser::AnimationCurveNode *curve_node : node_list) {
+ // when Curves() is called the curves are actually read, we could replace this with our own ProcessDomConnection code here if required.
+ // We may need to do this but ideally we use Curves
+ // note: when you call this there might be a delay in opening it
+ // uses mutable type to 'cache' the response until the AnimationCurveNode is cleaned up.
+ std::map<std::string, const FBXDocParser::AnimationCurve *> curves = curve_node->Curves();
+ const FBXDocParser::Object *object = curve_node->Target();
+ const FBXDocParser::Model *target = curve_node->TargetAsModel();
+ if (target == nullptr) {
+ if (object != nullptr) {
+ print_error("[doc] warning failed to find a target Model for curve: " + String(object->Name().c_str()));
+ } else {
+ //print_error("[doc] failed to resolve object");
+ continue;
+ }
+
+ continue;
+ } else {
+ //print_verbose("[doc] applied rotation order: " + itos(target->RotationOrder()));
+ quaternion_rotation_order = target->RotationOrder();
+ }
+
+ uint64_t target_id = target->ID();
+ String target_name = ImportUtils::FBXNodeToName(target->Name());
+
+ const FBXDocParser::PropertyTable *properties = curve_node;
+ bool got_x = false, got_y = false, got_z = false;
+ float offset_x = FBXDocParser::PropertyGet<float>(properties, "d|X", got_x);
+ float offset_y = FBXDocParser::PropertyGet<float>(properties, "d|Y", got_y);
+ float offset_z = FBXDocParser::PropertyGet<float>(properties, "d|Z", got_z);
+
+ String curve_node_name = ImportUtils::FBXNodeToName(curve_node->Name());
+
+ // Reduce all curves for this node into a single container
+ // T, R, S is what we expect, although other tracks are possible
+ // like for example visibility tracks.
+
+ // We are not ordered here, we don't care about ordering, this happens automagically by godot when we insert with the
+ // key time :), so order is unimportant because the insertion will happen at a time index
+ // good to know: we do not need a list of these in another format :)
+ //Map<String, Vector<const Assimp::FBX::AnimationCurve *> > unordered_track;
+
+ // T
+ // R
+ // S
+ // Map[String, List<VECTOR>]
+
+ // So this is a reduction of the animation curve nodes
+ // We build this as a lookup, this is essentially our 'animation track'
+ //AnimCurveNodes.insert(curve_node_name, Map<uint64_t, Vector3>());
+
+ // create the animation curve information with the target id
+ // so the point of this makes a track with the name "T" for example
+ // the target ID is also set here, this means we don't need to do anything extra when we are in the 'create all animation tracks' step
+ FBXTrack &keyframe_map = AnimCurveNodes[target_id][StringName(curve_node_name)];
+
+ if (got_x && got_y && got_z) {
+ Vector3 default_value = Vector3(offset_x, offset_y, offset_z);
+ keyframe_map.default_value = default_value;
+ keyframe_map.has_default = true;
+ //print_verbose("track name: " + curve_node_name);
+ //print_verbose("xyz default: " + default_value);
+ }
+ // target id, [ track name, [time index, vector] ]
+ // Map<uint64_t, Map<StringName, Map<uint64_t, Vector3> > > AnimCurveNodes;
+
+ // we probably need the target id here.
+ // so map[uint64_t map]...
+ // Map<uint64_t, Vector3D> translation_keys, rotation_keys, scale_keys;
+
+ // extra const required by C++11 colon/Range operator
+ // note: do not use C++17 syntax here for dicts.
+ // this is banned in Godot.
+ for (std::pair<const std::string, const FBXDocParser::AnimationCurve *> &kvp : curves) {
+ const String curve_element = ImportUtils::FBXNodeToName(kvp.first);
+ const FBXDocParser::AnimationCurve *curve = kvp.second;
+ String curve_name = ImportUtils::FBXNodeToName(curve->Name());
+ uint64_t curve_id = curve->ID();
+
+ if (CheckForDuplication.has(curve_id)) {
+ print_error("(FBX spec changed?) We found a duplicate curve being used for an alternative node - report to godot issue tracker");
+ } else {
+ CheckForDuplication.insert(curve_id, curve);
+ }
+
+ // FBX has no name for AnimCurveNode::, most of the time, not seen any with valid name here.
+ const std::map<int64_t, float> &track_time = curve->GetValueTimeTrack();
+
+ if (track_time.size() > 0) {
+ for (std::pair<int64_t, float> keyframe : track_time) {
+ if (curve_element == "d|X") {
+ keyframe_map.keyframes[keyframe.first].x = keyframe.second;
+ } else if (curve_element == "d|Y") {
+ keyframe_map.keyframes[keyframe.first].y = keyframe.second;
+ } else if (curve_element == "d|Z") {
+ keyframe_map.keyframes[keyframe.first].z = keyframe.second;
+ } else {
+ //print_error("FBX Unsupported element: " + curve_element);
+ }
+
+ //print_verbose("[" + itos(target_id) + "] Keyframe added: " + itos(keyframe_map.size()));
+
+ //print_verbose("Keyframe t:" + rtos(animation_track_time) + " v: " + rtos(keyframe.second));
+ }
+ }
+ }
+ }
+
+ // Map<uint64_t, Map<StringName, Map<uint64_t, Vector3> > > AnimCurveNodes;
+ // add this animation track here
+
+ // target id, [ track name, [time index, vector] ]
+ //std::map<uint64_t, std::map<StringName, FBXTrack > > AnimCurveNodes;
+ 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;
+
+ 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.
+ // remember that state.fbx_bone_map[target_id] will create a new entry EVEN if you only read.
+ // this would break node animation targets, so if you change this be warned. :)
+ if (state.fbx_bone_map.has(target_id)) {
+ bone = state.fbx_bone_map[target_id];
+ }
+
+ Transform3D target_transform;
+
+ if (state.fbx_target_map.has(target_id)) {
+ Ref<FBXNode> node_ref = state.fbx_target_map[target_id];
+ target_transform = node_ref->pivot_transform->GlobalTransform;
+ //print_verbose("[doc] allocated animation node transform");
+ }
+
+ //int size_targets = state.fbx_target_map.size();
+ //print_verbose("Target ID map: " + itos(size_targets));
+ //print_verbose("[doc] debug bone map size: " + itos(state.fbx_bone_map.size()));
+
+ // 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);
+ 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);
+ track_path = node_path;
+ //print_verbose("[doc] node animation path: " + node_path);
+ }
+ } else {
+ // note: this could actually be unsafe this means we should be careful about continuing here, if we see bizarre effects later we should disable this.
+ // I am not sure if this is unsafe or not, testing will tell us this.
+ print_error("[doc] invalid fbx target detected for this track");
+ continue;
+ }
+
+ // everything in FBX and Maya is a node therefore if this happens something is seriously broken.
+ if (!state.fbx_target_map.has(target_id)) {
+ print_error("unable to resolve this to an FBX object.");
+ continue;
+ }
+
+ Ref<FBXNode> target_node = state.fbx_target_map[target_id];
+ 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;
+ FBXTrack &translation_keys = track_data[StringName("T")];
+ FBXTrack &rotation_keys = track_data[StringName("R")];
+ FBXTrack &scale_keys = track_data[StringName("S")];
+
+ double increment = 1.0f / fps_setting;
+ double time = 0.0f;
+
+ bool last = false;
+
+ Vector<Vector3> pos_values;
+ Vector<float> pos_times;
+ Vector<Vector3> scale_values;
+ Vector<float> scale_times;
+ Vector<Quaternion> rot_values;
+ Vector<float> rot_times;
+
+ double max_duration = 0;
+ double anim_length = animation->get_length();
+
+ for (std::pair<int64_t, Vector3> position_key : translation_keys.keyframes) {
+ pos_values.push_back(position_key.second * state.scale);
+ double animation_track_time = CONVERT_FBX_TIME(position_key.first);
+
+ if (animation_track_time > max_duration) {
+ max_duration = animation_track_time;
+ }
+
+ //print_verbose("pos keyframe: t:" + rtos(animation_track_time) + " value " + position_key.second);
+ pos_times.push_back(animation_track_time);
+ }
+
+ for (std::pair<int64_t, Vector3> scale_key : scale_keys.keyframes) {
+ scale_values.push_back(scale_key.second);
+ double animation_track_time = CONVERT_FBX_TIME(scale_key.first);
+
+ if (animation_track_time > max_duration) {
+ max_duration = animation_track_time;
+ }
+ //print_verbose("scale keyframe t:" + rtos(animation_track_time));
+ scale_times.push_back(animation_track_time);
+ }
+
+ //
+ // Pre and Post keyframe rotation handler
+ // -- Required because Maya and Autodesk <3 the pain when it comes to implementing animation code! enjoy <3
+
+ bool got_pre = false;
+ bool got_post = false;
+
+ Quaternion post_rotation;
+ Quaternion pre_rotation;
+
+ // Rotation matrix
+ const Vector3 &PreRotation = FBXDocParser::PropertyGet<Vector3>(props, "PreRotation", got_pre);
+ const Vector3 &PostRotation = FBXDocParser::PropertyGet<Vector3>(props, "PostRotation", got_post);
+
+ FBXDocParser::Model::RotOrder rot_order = model->RotationOrder();
+ if (got_pre) {
+ pre_rotation = ImportUtils::EulerToQuaternion(rot_order, ImportUtils::deg2rad(PreRotation));
+ }
+ if (got_post) {
+ post_rotation = ImportUtils::EulerToQuaternion(rot_order, ImportUtils::deg2rad(PostRotation));
+ }
+
+ Quaternion lastQuaternion = Quaternion();
+
+ for (std::pair<int64_t, Vector3> rotation_key : rotation_keys.keyframes) {
+ double animation_track_time = CONVERT_FBX_TIME(rotation_key.first);
+
+ //print_verbose("euler rotation key: " + rotation_key.second);
+ Quaternion rot_key_value = ImportUtils::EulerToQuaternion(quaternion_rotation_order, ImportUtils::deg2rad(rotation_key.second));
+
+ if (lastQuaternion != Quaternion() && rot_key_value.dot(lastQuaternion) < 0) {
+ rot_key_value.x = -rot_key_value.x;
+ rot_key_value.y = -rot_key_value.y;
+ rot_key_value.z = -rot_key_value.z;
+ rot_key_value.w = -rot_key_value.w;
+ }
+ // pre_post rotation possibly could fix orientation
+ Quaternion final_rotation = pre_rotation * rot_key_value * post_rotation;
+
+ lastQuaternion = final_rotation;
+
+ if (animation_track_time > max_duration) {
+ max_duration = animation_track_time;
+ }
+
+ rot_values.push_back(final_rotation.normalized());
+ rot_times.push_back(animation_track_time);
+ }
+
+ bool valid_rest = false;
+ Transform3D bone_rest;
+ int skeleton_bone = -1;
+ if (state.fbx_bone_map.has(target_id)) {
+ if (bone.is_valid() && bone->fbx_skeleton.is_valid()) {
+ skeleton_bone = bone->godot_bone_id;
+ if (skeleton_bone >= 0) {
+ bone_rest = bone->fbx_skeleton->skeleton->get_bone_rest(skeleton_bone);
+ valid_rest = true;
+ }
+ }
+
+ if (!valid_rest) {
+ print_verbose("invalid rest!");
+ }
+ }
+
+ const Vector3 def_pos = translation_keys.has_default ? (translation_keys.default_value * state.scale) : bone_rest.origin;
+ const Quaternion def_rot = rotation_keys.has_default ? ImportUtils::EulerToQuaternion(quaternion_rotation_order, ImportUtils::deg2rad(rotation_keys.default_value)) : bone_rest.basis.get_rotation_quaternion();
+ 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;
+ Vector3 scale = def_scale;
+
+ if (pos_values.size()) {
+ pos = _interpolate_track<Vector3>(pos_times, pos_values, time,
+ AssetImportAnimation::INTERP_LINEAR);
+ }
+
+ if (rot_values.size()) {
+ rot = _interpolate_track<Quaternion>(rot_times, rot_values, time,
+ AssetImportAnimation::INTERP_LINEAR);
+ }
+
+ if (scale_values.size()) {
+ scale = _interpolate_track<Vector3>(scale_times, scale_values, time,
+ AssetImportAnimation::INTERP_LINEAR);
+ }
+
+ 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);
+ }
+
+ if (last) {
+ break;
+ }
+
+ time += increment;
+ if (time > anim_length) {
+ last = true;
+ time = anim_length;
+ break;
+ }
+ }
+ }
+ }
+ state.animation_player->add_animation(animation_name, animation);
+ }
+ }
+
+ // AnimStack elements contain start stop time and name of animation
+ // AnimLayer is the current active layer of the animation (multiple layers can be active we only support 1)
+ // AnimCurveNode has a OP link back to the model which is the real node.
+ // AnimCurveNode has a direct link to AnimationCurve (of which it may have more than one)
+
+ // Store animation stack in list
+ // iterate over all AnimStacks like the cache node algorithm recursively
+ // this can then be used with ProcessDomConnection<> to link from
+ // AnimStack:: <-- (OO) --> AnimLayer:: <-- (OO) --> AnimCurveNode:: (which can OP resolve) to Model::
+ }
+
+ //
+ // Cleanup operations - explicit to prevent errors on shutdown - found that ref to ref does behave badly sometimes.
+ //
+
+ state.renderer_mesh_data.clear();
+ state.MeshSkins.clear();
+ state.fbx_target_map.clear();
+ state.fbx_node_list.clear();
+
+ 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 (KeyValue<uint64_t, Ref<FBXSkeleton>> &element : state.skeleton_map) {
+ Ref<FBXSkeleton> skel = element.value;
+ skel->fbx_node.unref();
+ skel->skeleton_bones.clear();
+ }
+
+ state.fbx_bone_map.clear();
+ state.skeleton_map.clear();
+ state.fbx_root_node.unref();
+
+ return scene_root;
+}
+
+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");
+ // FBX can do an join like this
+ // Model -> SubDeformer (bone) -> Deformer (skin pose)
+ // This is important because we need to somehow link skin back to bone id in skeleton :)
+ // The rules are:
+ // A subdeformer will exist if 'limbnode' class tag present
+ // The subdeformer will not necessarily have a deformer as joints do not have one
+ for (const FBXDocParser::Connection *con : conns) {
+ // goto: bone creation
+ //print_verbose("con: " + String(con->PropertyName().c_str()));
+
+ // ignore object-property links we want the object to object links nothing else
+ if (con->PropertyName().length()) {
+ continue;
+ }
+
+ // convert connection source object into Object base class
+ const FBXDocParser::Object *const object = con->SourceObject();
+
+ if (nullptr == object) {
+ print_verbose("failed to convert source object for Model link");
+ continue;
+ }
+
+ // FBX Model::Cube, Model::Bone001, etc elements
+ // This detects if we can cast the object into this model structure.
+ const FBXDocParser::Model *const model = dynamic_cast<const FBXDocParser::Model *>(object);
+
+ // declare our bone element reference (invalid, unless we create a bone in this step)
+ // this lets us pass valid armature information into children objects and this is why we moved this up here
+ // previously this was created .instantiated() on the same line.
+ Ref<FBXBone> bone_element;
+
+ if (model != nullptr) {
+ // model marked with limb node / casted.
+ const FBXDocParser::ModelLimbNode *const limb_node = dynamic_cast<const FBXDocParser::ModelLimbNode *>(model);
+ if (limb_node != nullptr) {
+ // Write bone into bone list for FBX
+
+ ERR_FAIL_COND_MSG(state.fbx_bone_map.has(limb_node->ID()), "[serious] duplicate LimbNode detected");
+
+ bool parent_is_bone = state.fbx_bone_map.find(p_id);
+ bone_element.instantiate();
+
+ // used to build the bone hierarchy in the skeleton
+ bone_element->parent_bone_id = parent_is_bone ? p_id : 0;
+ bone_element->valid_parent = parent_is_bone;
+ bone_element->limb_node = limb_node;
+
+ // parent is a node and this is the first bone
+ if (!parent_is_bone) {
+ uint64_t armature_id = p_id;
+ bone_element->valid_armature_id = true;
+ bone_element->armature_id = armature_id;
+ print_verbose("[doc] valid armature has been configured for first child: " + itos(armature_id));
+ } else if (p_parent_bone.is_valid()) {
+ if (p_parent_bone->valid_armature_id) {
+ bone_element->valid_armature_id = true;
+ bone_element->armature_id = p_parent_bone->armature_id;
+ print_verbose("[doc] bone has valid armature id:" + itos(bone_element->armature_id));
+ } else {
+ print_error("[doc] unassigned armature id: " + String(limb_node->Name().c_str()));
+ }
+ } else {
+ print_error("[doc] error is this a bone? " + String(limb_node->Name().c_str()));
+ }
+
+ if (!parent_is_bone) {
+ print_verbose("[doc] Root bone: " + bone_element->bone_name);
+ }
+
+ uint64_t limb_id = limb_node->ID();
+ bone_element->bone_id = limb_id;
+ bone_element->bone_name = ImportUtils::FBXNodeToName(model->Name());
+ bone_element->parent_bone = p_parent_bone;
+
+ // insert limb by ID into list.
+ state.fbx_bone_map.insert(limb_node->ID(), bone_element);
+ }
+
+ // recursion call - child nodes
+ BuildDocumentBones(bone_element, state, p_doc, model->ID());
+ }
+ }
+}
+
+void EditorSceneFormatImporterFBX::BuildDocumentNodes(
+ Ref<PivotTransform> parent_transform,
+ ImportState &state,
+ const FBXDocParser::Document *p_doc,
+ uint64_t id,
+ Ref<FBXNode> parent_node) {
+ // tree
+ // here we get the node 0 on the root by default
+ const std::vector<const FBXDocParser::Connection *> &conns = p_doc->GetConnectionsByDestinationSequenced(id, "Model");
+
+ // branch
+ for (const FBXDocParser::Connection *con : conns) {
+ // ignore object-property links
+ if (con->PropertyName().length()) {
+ // really important we document why this is ignored.
+ print_verbose("ignoring property link - no docs on why this is ignored");
+ continue;
+ }
+
+ // convert connection source object into Object base class
+ // Source objects can exist with 'null connections' this means that we only for sure know the source exists.
+ const FBXDocParser::Object *const source_object = con->SourceObject();
+
+ if (nullptr == source_object) {
+ print_verbose("failed to convert source object for Model link");
+ continue;
+ }
+
+ // FBX Model::Cube, Model::Bone001, etc elements
+ // This detects if we can cast the object into this model structure.
+ const FBXDocParser::Model *const model = dynamic_cast<const FBXDocParser::Model *>(source_object);
+ // model is the current node
+ if (nullptr != model) {
+ uint64_t current_node_id = model->ID();
+
+ Ref<FBXNode> new_node;
+ new_node.instantiate();
+ new_node->current_node_id = current_node_id;
+ new_node->node_name = ImportUtils::FBXNodeToName(model->Name());
+
+ Ref<PivotTransform> fbx_transform;
+ fbx_transform.instantiate();
+ fbx_transform->set_parent(parent_transform);
+ fbx_transform->set_model(model);
+ fbx_transform->debug_pivot_xform("name: " + new_node->node_name);
+ fbx_transform->Execute();
+
+ new_node->set_pivot_transform(fbx_transform);
+
+ // check if this node is a bone
+ if (state.fbx_bone_map.has(current_node_id)) {
+ Ref<FBXBone> bone = state.fbx_bone_map[current_node_id];
+ if (bone.is_valid()) {
+ bone->set_node(new_node);
+ print_verbose("allocated bone data: " + bone->bone_name);
+ }
+ }
+
+ // set the model, we can't just assign this safely
+ new_node->set_model(model);
+
+ if (parent_node.is_valid()) {
+ new_node->set_parent(parent_node);
+ } else {
+ new_node->set_parent(state.fbx_root_node);
+ }
+
+ CRASH_COND_MSG(new_node->pivot_transform.is_null(), "invalid fbx target map pivot transform [serious]");
+
+ // populate lookup tables with references
+ // [fbx_node_id, fbx_node]
+
+ state.fbx_node_list.push_back(new_node);
+ if (!state.fbx_target_map.has(new_node->current_node_id)) {
+ state.fbx_target_map[new_node->current_node_id] = new_node;
+ }
+
+ // print node name
+ print_verbose("[doc] new node " + new_node->node_name);
+
+ // sub branches
+ BuildDocumentNodes(new_node->pivot_transform, state, p_doc, current_node_id, new_node);
+ }
+ }
+}
diff --git a/modules/fbx/editor_scene_importer_fbx.h b/modules/fbx/editor_scene_importer_fbx.h
new file mode 100644
index 0000000000..7845e079c2
--- /dev/null
+++ b/modules/fbx/editor_scene_importer_fbx.h
@@ -0,0 +1,134 @@
+/*************************************************************************/
+/* editor_scene_importer_fbx.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 EDITOR_SCENE_IMPORTER_FBX_H
+#define EDITOR_SCENE_IMPORTER_FBX_H
+
+#ifdef TOOLS_ENABLED
+
+#include "data/import_state.h"
+#include "tools/import_utils.h"
+
+#include "core/io/resource_importer.h"
+#include "core/string/ustring.h"
+#include "core/templates/local_vector.h"
+#include "core/templates/vector.h"
+#include "core/variant/dictionary.h"
+#include "editor/import/resource_importer_scene.h"
+#include "editor/project_settings_editor.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/animation/animation_player.h"
+#include "scene/resources/animation.h"
+#include "scene/resources/surface_tool.h"
+
+#include "fbx_parser/FBXDocument.h"
+#include "fbx_parser/FBXImportSettings.h"
+#include "fbx_parser/FBXMeshGeometry.h"
+#include "fbx_parser/FBXUtil.h"
+
+#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
+
+class EditorSceneFormatImporterFBX : public EditorSceneFormatImporter {
+private:
+ GDCLASS(EditorSceneFormatImporterFBX, EditorSceneFormatImporter);
+
+ struct AssetImportAnimation {
+ enum Interpolation {
+ INTERP_LINEAR,
+ INTERP_STEP,
+ INTERP_CATMULLROMSPLINE,
+ INTERP_CUBIC_SPLINE
+ };
+ };
+
+ // ------------------------------------------------------------------------------------------------
+ template <typename T>
+ const T *ProcessDOMConnection(
+ const FBXDocParser::Document *doc,
+ uint64_t current_element,
+ bool reverse_lookup = false) {
+ const std::vector<const FBXDocParser::Connection *> &conns = reverse_lookup ? doc->GetConnectionsByDestinationSequenced(current_element) : doc->GetConnectionsBySourceSequenced(current_element);
+ //print_verbose("[doc] looking for " + String(element_to_find));
+ // using the temp pattern here so we can debug before it returns
+ // in some cases we return too early, with 'deformer object base class' in wrong place
+ // in assimp this means we can accidentally return too early...
+ const T *return_obj = nullptr;
+
+ for (const FBXDocParser::Connection *con : conns) {
+ const FBXDocParser::Object *source_object = con->SourceObject();
+ const FBXDocParser::Object *dest_object = con->DestinationObject();
+ if (source_object && dest_object != nullptr) {
+ //print_verbose("[doc] connection name: " + String(source_object->Name().c_str()) + ", dest: " + String(dest_object->Name().c_str()));
+ const T *temp = dynamic_cast<const T *>(reverse_lookup ? source_object : dest_object);
+ if (temp) {
+ return_obj = temp;
+ }
+ }
+ }
+
+ if (return_obj != nullptr) {
+ //print_verbose("[doc] returned valid element");
+ //print_verbose("Found object for bone");
+ return return_obj;
+ }
+
+ // safe to return nothing, need to use nullptr here as nullptr is used internally for FBX document.
+ return nullptr;
+ }
+
+ void BuildDocumentBones(Ref<FBXBone> p_parent_bone,
+ ImportState &state, const FBXDocParser::Document *p_doc,
+ uint64_t p_id);
+
+ void BuildDocumentNodes(Ref<PivotTransform> parent_transform, ImportState &state, const FBXDocParser::Document *doc, uint64_t id, Ref<FBXNode> fbx_parent);
+
+ Node3D *_generate_scene(const String &p_path, const FBXDocParser::Document *p_document,
+ const uint32_t p_flags,
+ int p_bake_fps,
+ const int32_t p_max_bone_weights,
+ bool p_is_blender_fbx);
+
+ template <class T>
+ T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp);
+ 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:
+ EditorSceneFormatImporterFBX() {}
+ ~EditorSceneFormatImporterFBX() {}
+
+ virtual void get_extensions(List<String> *r_extensions) const override;
+ virtual uint32_t get_import_flags() const override;
+ virtual Node3D *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr) override;
+};
+
+#endif // TOOLS_ENABLED
+#endif // EDITOR_SCENE_IMPORTER_FBX_H
diff --git a/modules/fbx/fbx_parser/ByteSwapper.h b/modules/fbx/fbx_parser/ByteSwapper.h
new file mode 100644
index 0000000000..08d38147d5
--- /dev/null
+++ b/modules/fbx/fbx_parser/ByteSwapper.h
@@ -0,0 +1,283 @@
+/*************************************************************************/
+/* ByteSwapper.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file Helper class tp perform various byte order swappings
+ (e.g. little to big endian) */
+#ifndef BYTE_SWAPPER_H
+#define BYTE_SWAPPER_H
+
+#include <stdint.h>
+#include <algorithm>
+#include <locale>
+
+namespace FBXDocParser {
+// --------------------------------------------------------------------------------------
+/** Defines some useful byte order swap routines.
+ *
+ * This is required to read big-endian model formats on little-endian machines,
+ * and vice versa. Direct use of this class is DEPRECATED. Use #StreamReader instead. */
+// --------------------------------------------------------------------------------------
+class ByteSwap {
+ ByteSwap() {}
+
+public:
+ // ----------------------------------------------------------------------
+ /** Swap two bytes of data
+ * @param[inout] _szOut A void* to save the reintcasts for the caller. */
+ static inline void Swap2(void *_szOut) {
+ uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
+ std::swap(szOut[0], szOut[1]);
+ }
+
+ // ----------------------------------------------------------------------
+ /** Swap four bytes of data
+ * @param[inout] _szOut A void* to save the reintcasts for the caller. */
+ static inline void Swap4(void *_szOut) {
+ uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
+ std::swap(szOut[0], szOut[3]);
+ std::swap(szOut[1], szOut[2]);
+ }
+
+ // ----------------------------------------------------------------------
+ /** Swap eight bytes of data
+ * @param[inout] _szOut A void* to save the reintcasts for the caller. */
+ static inline void Swap8(void *_szOut) {
+ uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
+ std::swap(szOut[0], szOut[7]);
+ std::swap(szOut[1], szOut[6]);
+ std::swap(szOut[2], szOut[5]);
+ std::swap(szOut[3], szOut[4]);
+ }
+
+ // ----------------------------------------------------------------------
+ /** ByteSwap a float. Not a joke.
+ * @param[inout] fOut ehm. .. */
+ static inline void Swap(float *fOut) {
+ Swap4(fOut);
+ }
+
+ // ----------------------------------------------------------------------
+ /** ByteSwap a double. Not a joke.
+ * @param[inout] fOut ehm. .. */
+ static inline void Swap(double *fOut) {
+ Swap8(fOut);
+ }
+
+ // ----------------------------------------------------------------------
+ /** ByteSwap an int16t. Not a joke.
+ * @param[inout] fOut ehm. .. */
+ static inline void Swap(int16_t *fOut) {
+ Swap2(fOut);
+ }
+
+ static inline void Swap(uint16_t *fOut) {
+ Swap2(fOut);
+ }
+
+ // ----------------------------------------------------------------------
+ /** ByteSwap an int32t. Not a joke.
+ * @param[inout] fOut ehm. .. */
+ static inline void Swap(int32_t *fOut) {
+ Swap4(fOut);
+ }
+
+ static inline void Swap(uint32_t *fOut) {
+ Swap4(fOut);
+ }
+
+ // ----------------------------------------------------------------------
+ /** ByteSwap an int64t. Not a joke.
+ * @param[inout] fOut ehm. .. */
+ static inline void Swap(int64_t *fOut) {
+ Swap8(fOut);
+ }
+
+ static inline void Swap(uint64_t *fOut) {
+ Swap8(fOut);
+ }
+
+ // ----------------------------------------------------------------------
+ //! Templatized ByteSwap
+ //! \returns param tOut as swapped
+ template <typename Type>
+ static inline Type Swapped(Type tOut) {
+ return _swapper<Type, sizeof(Type)>()(tOut);
+ }
+
+private:
+ template <typename T, size_t size>
+ struct _swapper;
+};
+
+template <typename T>
+struct ByteSwap::_swapper<T, 2> {
+ T operator()(T tOut) {
+ Swap2(&tOut);
+ return tOut;
+ }
+};
+
+template <typename T>
+struct ByteSwap::_swapper<T, 4> {
+ T operator()(T tOut) {
+ Swap4(&tOut);
+ return tOut;
+ }
+};
+
+template <typename T>
+struct ByteSwap::_swapper<T, 8> {
+ T operator()(T tOut) {
+ Swap8(&tOut);
+ return tOut;
+ }
+};
+
+// --------------------------------------------------------------------------------------
+// ByteSwap macros for BigEndian/LittleEndian support
+// --------------------------------------------------------------------------------------
+#if (defined AI_BUILD_BIG_ENDIAN)
+#define AI_LE(t) (t)
+#define AI_BE(t) ByteSwap::Swapped(t)
+#define AI_LSWAP2(p)
+#define AI_LSWAP4(p)
+#define AI_LSWAP8(p)
+#define AI_LSWAP2P(p)
+#define AI_LSWAP4P(p)
+#define AI_LSWAP8P(p)
+#define LE_NCONST const
+#define AI_SWAP2(p) ByteSwap::Swap2(&(p))
+#define AI_SWAP4(p) ByteSwap::Swap4(&(p))
+#define AI_SWAP8(p) ByteSwap::Swap8(&(p))
+#define AI_SWAP2P(p) ByteSwap::Swap2((p))
+#define AI_SWAP4P(p) ByteSwap::Swap4((p))
+#define AI_SWAP8P(p) ByteSwap::Swap8((p))
+#define BE_NCONST
+#else
+#define AI_BE(t) (t)
+#define AI_LE(t) ByteSwap::Swapped(t)
+#define AI_SWAP2(p)
+#define AI_SWAP4(p)
+#define AI_SWAP8(p)
+#define AI_SWAP2P(p)
+#define AI_SWAP4P(p)
+#define AI_SWAP8P(p)
+#define BE_NCONST const
+#define AI_LSWAP2(p) ByteSwap::Swap2(&(p))
+#define AI_LSWAP4(p) ByteSwap::Swap4(&(p))
+#define AI_LSWAP8(p) ByteSwap::Swap8(&(p))
+#define AI_LSWAP2P(p) ByteSwap::Swap2((p))
+#define AI_LSWAP4P(p) ByteSwap::Swap4((p))
+#define AI_LSWAP8P(p) ByteSwap::Swap8((p))
+#define LE_NCONST
+#endif
+
+namespace Intern {
+
+// --------------------------------------------------------------------------------------------
+template <typename T, bool doit>
+struct ByteSwapper {
+ void operator()(T *inout) {
+ ByteSwap::Swap(inout);
+ }
+};
+
+template <typename T>
+struct ByteSwapper<T, false> {
+ void operator()(T *) {
+ }
+};
+
+// --------------------------------------------------------------------------------------------
+template <bool SwapEndianess, typename T, bool RuntimeSwitch>
+struct Getter {
+ void operator()(T *inout, bool le) {
+ le = !le;
+ if (le) {
+ ByteSwapper<T, (sizeof(T) > 1 ? true : false)>()(inout);
+ } else {
+ ByteSwapper<T, false>()(inout);
+ }
+ }
+};
+
+template <bool SwapEndianess, typename T>
+struct Getter<SwapEndianess, T, false> {
+ void operator()(T *inout, bool /*le*/) {
+ // static branch
+ ByteSwapper<T, (SwapEndianess && sizeof(T) > 1)>()(inout);
+ }
+};
+} // namespace Intern
+} // namespace FBXDocParser
+
+#endif // BYTE_SWAPPER_H
diff --git a/modules/fbx/fbx_parser/CREDITS b/modules/fbx/fbx_parser/CREDITS
new file mode 100644
index 0000000000..62b449614e
--- /dev/null
+++ b/modules/fbx/fbx_parser/CREDITS
@@ -0,0 +1,183 @@
+===============================================================
+Open Asset Import Library (Assimp)
+Developers and Contributors
+===============================================================
+
+The following is a non-exhaustive list of all constributors over the years.
+If you think your name should be listed here, drop us a line and we'll add you.
+
+- Alexander Gessler,
+3DS-, BLEND-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL-, RAW-, OFF-, MS3D-, Q3D- and LWO-Loader, Assimp-Viewer, assimp-cmd, -noboost, Website (Design).
+
+- Thomas Schulze,
+X-, Collada-, BVH-Loader, Postprocessing framework. Data structure & Interface design, documentation.
+
+- Kim Kulling,
+Obj-, Q3BSD-, OpenGEX-Loader, Logging system, CMake-build-environment, Linux-build, Website ( Admin ), Coverity ( Admin ), Glitter ( Admin ).
+
+- R.Schmidt,
+Linux build, eclipse support.
+
+- Matthias Gubisch,
+Assimp.net
+Visual Studio 9 support, bugfixes.
+
+- Mark Sibly
+B3D-Loader, Assimp testing
+
+- Jonathan Klein
+Ogre Loader, VC2010 fixes and CMake fixes.
+
+- Sebastian Hempel,
+PyAssimp (first version)
+Compile-Bugfixes for mingw, add environment for static library support in make.
+
+- Jonathan Pokrass
+Supplied a bugfix concerning the scaling in the md3 loader.
+
+- Andrew Galante,
+Submitted patches to make Assimp compile with GCC-4, a makefile and the xcode3 workspace.
+
+- Andreas Nagel
+First Assimp testing & verification under Windows Vista 64 Bit.
+
+- Marius Schr�der
+Allowed us to use many of his models for screenshots and testing.
+
+- Christian Schubert
+Supplied various XFiles for testing purposes.
+
+- Tizian Wieland
+Searched the web for hundreds of test models for internal use
+
+- John Connors
+Supplied patches for linux and SCons.
+
+- T. R.
+The GUY who performed some of the CSM mocaps.
+
+- Andy Maloney
+Contributed fixes for the documentation and the doxygen markup
+
+- Zhao Lei
+Contributed several bugfixes fixing memory leaks and improving float parsing
+
+- sueastside
+Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date.
+
+- Tobias Rittig
+Collada testing with Cinema 4D
+
+- Brad Grantham
+Improvements in OpenGL-Sample.
+
+- Robert Ramirez
+Add group loading feature to Obj-Loader.
+
+- Chris Maiwald
+Many bugreports, improving Assimp's portability, regular testing & feedback.
+
+- Stepan Hrbek
+Bugreport and fix for a obj-materialloader crash.
+
+- David Nadlinger
+D bindings, CMake install support.
+
+- Dario Accornero
+Contributed several patches regarding Mac OS/XCode targets, bug reports.
+
+- Martin Walser (Samhayne)
+Contributed the 'SimpleTexturedOpenGl' sample.
+
+- Matthias Fauconneau
+Contributed a fix for the Q3-BSP loader.
+
+- Jørgen P. Tjernø
+Contributed updated and improved xcode workspaces
+
+- drparallax
+Contributed the /samples/SimpleAssimpViewX sample
+
+- Carsten Fuchs
+Contributed a fix for the Normalize method in aiQuaternion.
+
+- dbburgess
+Contributes a Android-specific build issue: log the hardware architecture for ARM.
+
+- alfiereinre7
+Contributes a obj-fileparser fix: missing tokens in the obj-token list.
+
+- Roman Kharitonov
+Contributes a fix for the configure script environment.
+
+- Ed Diana
+Contributed AssimpDelphi (/port/AssimpDelphi).
+
+- rdb
+Contributes a bundle of fixes and improvements for the bsp-importer.
+
+- Mick P
+For contributing the De-bone postprocessing step and filing various bug reports.
+
+- Rosen Diankov
+Contributed patches to build assimp debian packages using cmake.
+
+- Mark Page
+Contributed a patch to fix the VertexTriangleAdjacency postprocessing step.
+
+- IOhannes
+Contributed the Debian build fixes ( architecture macro ).
+
+- gellule
+Several LWO and LWS fixes (pivoting).
+
+- Marcel Metz
+GCC/Linux fixes for the SimpleOpenGL sample.
+
+- Brian Miller
+Bugfix for a compiler fix for iOS on arm.
+
+- Séverin Lemaignan
+Rewrite of PyAssimp, distutils and Python3 support
+
+- albert-wang
+Bugfixes for the collada parser
+
+- Ya ping Jin
+Bugfixes for uv-tanget calculation.
+
+- Jonne Nauha
+Ogre Binary format support
+
+- Filip Wasil, Tieto Poland Sp. z o.o.
+Android JNI asset extraction support
+
+- Richard Steffen
+Contributed ExportProperties interface
+Contributed X File exporter
+Contributed Step (stp) exporter
+
+- Thomas Iorns (mesilliac)
+Initial FBX Export support
+
+For a more detailed list just check: https://github.com/assimp/assimp/network/members
+
+
+========
+Patreons
+========
+
+Huge thanks to our Patreons!
+
+- migenius
+- Marcus
+- Cort
+- elect
+- Steffen
+
+
+===================
+Commercial Sponsors
+===================
+
+- MyDidimo (mydidimo.com): Sponsored development of FBX Export support
diff --git a/modules/fbx/fbx_parser/FBXAnimation.cpp b/modules/fbx/fbx_parser/FBXAnimation.cpp
new file mode 100644
index 0000000000..0fbff035fd
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXAnimation.cpp
@@ -0,0 +1,273 @@
+/*************************************************************************/
+/* FBXAnimation.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXAnimation.cpp
+ * @brief Assimp::FBX::AnimationCurve, Assimp::FBX::AnimationCurveNode,
+ * Assimp::FBX::AnimationLayer, Assimp::FBX::AnimationStack
+ */
+
+#include "FBXCommon.h"
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXParser.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurve::AnimationCurve(uint64_t id, const ElementPtr element, const std::string &name, const Document & /*doc*/) :
+ Object(id, element, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+ const ElementPtr KeyTime = GetRequiredElement(sc, "KeyTime");
+ const ElementPtr KeyValueFloat = GetRequiredElement(sc, "KeyValueFloat");
+
+ // note preserved keys and values for legacy FBXConverter.cpp
+ // we can remove this once the animation system is written
+ // and clean up this code so we do not have copies everywhere.
+ ParseVectorDataArray(keys, KeyTime);
+ ParseVectorDataArray(values, KeyValueFloat);
+
+ if (keys.size() != values.size()) {
+ DOMError("the number of key times does not match the number of keyframe values", KeyTime);
+ }
+
+ // put the two lists into the map, underlying container is really just a dictionary
+ // these will always match, if not an error will throw and the file will not import
+ // this is useful because we then can report something and fix this later if it becomes an issue
+ // at this point we do not need a different count of these elements so this makes the
+ // most sense to do.
+ for (size_t x = 0; x < keys.size(); x++) {
+ keyvalues[keys[x]] = values[x];
+ }
+
+ const ElementPtr KeyAttrDataFloat = sc->GetElement("KeyAttrDataFloat");
+ if (KeyAttrDataFloat) {
+ ParseVectorDataArray(attributes, KeyAttrDataFloat);
+ }
+
+ const ElementPtr KeyAttrFlags = sc->GetElement("KeyAttrFlags");
+ if (KeyAttrFlags) {
+ ParseVectorDataArray(flags, KeyAttrFlags);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurve::~AnimationCurve() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNode::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*/) :
+ Object(id, element, name), target(), doc(doc) {
+ // find target node
+ const char *whitelist[] = { "Model", "NodeAttribute", "Deformer" };
+ const std::vector<const Connection *> &conns = doc.GetConnectionsBySourceSequenced(ID(), whitelist, 3);
+
+ for (const Connection *con : conns) {
+ // link should go for a property
+ if (!con->PropertyName().length()) {
+ continue;
+ }
+
+ Object *object = con->DestinationObject();
+
+ if (!object) {
+ DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring", element);
+ continue;
+ }
+
+ target = object;
+ prop = con->PropertyName();
+ break;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNode::~AnimationCurveNode() {
+ curves.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+const AnimationMap &AnimationCurveNode::Curves() const {
+ /* Lazy loaded animation curves, will only load if required */
+ if (curves.empty()) {
+ // resolve attached animation curves
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve");
+
+ for (const Connection *con : conns) {
+ // So the advantage of having this STL boilerplate is that it's dead simple once you get it.
+ // The other advantage is casting is guaranteed to be safe and nullptr will be returned in the last step if it fails.
+ Object *ob = con->SourceObject();
+ AnimationCurve *anim_curve = dynamic_cast<AnimationCurve *>(ob);
+ ERR_CONTINUE_MSG(!anim_curve, "Failed to convert animation curve from object");
+
+ curves.insert(std::make_pair(con->PropertyName(), anim_curve));
+ }
+ }
+
+ return curves;
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationLayer::AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Object(id, element, name), doc(doc) {
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationLayer::~AnimationLayer() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+const AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_prop_whitelist,
+ size_t whitelist_size /*= 0*/) const {
+ AnimationCurveNodeList nodes;
+
+ // resolve attached animation nodes
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurveNode");
+ nodes.reserve(conns.size());
+
+ for (const Connection *con : conns) {
+ // link should not go to a property
+ if (con->PropertyName().length()) {
+ continue;
+ }
+
+ Object *ob = con->SourceObject();
+
+ if (!ob) {
+ DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring", element);
+ continue;
+ }
+
+ const AnimationCurveNode *anim = dynamic_cast<AnimationCurveNode *>(ob);
+ if (!anim) {
+ DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode", element);
+ continue;
+ }
+
+ if (target_prop_whitelist) {
+ const char *s = anim->TargetProperty().c_str();
+ bool ok = false;
+ for (size_t i = 0; i < whitelist_size; ++i) {
+ if (!strcmp(s, target_prop_whitelist[i])) {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok) {
+ continue;
+ }
+ }
+ nodes.push_back(anim);
+ }
+
+ return nodes;
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationStack::AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Object(id, element, name) {
+ // resolve attached animation layers
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationLayer");
+ layers.reserve(conns.size());
+
+ for (const Connection *con : conns) {
+ // link should not go to a property
+ if (con->PropertyName().length()) {
+ continue;
+ }
+
+ Object *ob = con->SourceObject();
+ if (!ob) {
+ DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring", element);
+ continue;
+ }
+
+ const AnimationLayer *anim = dynamic_cast<const AnimationLayer *>(ob);
+
+ if (!anim) {
+ DOMWarning("source object for ->AnimationStack link is not an AnimationLayer", element);
+ continue;
+ }
+
+ layers.push_back(anim);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationStack::~AnimationStack() {
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp
new file mode 100644
index 0000000000..d6abcbb00a
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp
@@ -0,0 +1,442 @@
+/*************************************************************************/
+/* FBXBinaryTokenizer.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+/** @file FBXBinaryTokenizer.cpp
+ * @brief Implementation of a fake lexer for binary fbx files -
+ * we emit tokens so the parser needs almost no special handling
+ * for binary files.
+ */
+
+#include "ByteSwapper.h"
+#include "FBXTokenizer.h"
+#include "core/string/print_string.h"
+
+#include <stdint.h>
+
+namespace FBXDocParser {
+// ------------------------------------------------------------------------------------------------
+Token::Token(const char *sbegin, const char *send, TokenType type, size_t offset) :
+ sbegin(sbegin),
+ send(send),
+ type(type),
+ line(offset),
+ column(BINARY_MARKER) {
+#ifdef DEBUG_ENABLED
+ // contents is bad.. :/
+ contents = std::string(sbegin, static_cast<size_t>(send - sbegin));
+#endif
+ // calc length
+ // measure from sBegin to sEnd and validate?
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// signal tokenization error
+void TokenizeError(const std::string &message, size_t offset) {
+ print_error("[FBX-Tokenize] " + String(message.c_str()) + ", offset " + itos(offset));
+}
+
+// ------------------------------------------------------------------------------------------------
+size_t Offset(const char *begin, const char *cursor) {
+ //ai_assert(begin <= cursor);
+
+ return cursor - begin;
+}
+
+// ------------------------------------------------------------------------------------------------
+void TokenizeError(const std::string &message, const char *begin, const char *cursor) {
+ TokenizeError(message, Offset(begin, cursor));
+}
+
+// ------------------------------------------------------------------------------------------------
+uint32_t ReadWord(const char *input, const char *&cursor, const char *end) {
+ const size_t k_to_read = sizeof(uint32_t);
+ if (Offset(cursor, end) < k_to_read) {
+ TokenizeError("cannot ReadWord, out of bounds", input, cursor);
+ }
+
+ uint32_t word;
+ ::memcpy(&word, cursor, 4);
+ AI_SWAP4(word);
+
+ cursor += k_to_read;
+
+ return word;
+}
+
+// ------------------------------------------------------------------------------------------------
+uint64_t ReadDoubleWord(const char *input, const char *&cursor, const char *end) {
+ const size_t k_to_read = sizeof(uint64_t);
+ if (Offset(cursor, end) < k_to_read) {
+ TokenizeError("cannot ReadDoubleWord, out of bounds", input, cursor);
+ }
+
+ uint64_t dword /*= *reinterpret_cast<const uint64_t*>(cursor)*/;
+ ::memcpy(&dword, cursor, sizeof(uint64_t));
+ AI_SWAP8(dword);
+
+ cursor += k_to_read;
+
+ return dword;
+}
+
+// ------------------------------------------------------------------------------------------------
+uint8_t ReadByte(const char *input, const char *&cursor, const char *end) {
+ if (Offset(cursor, end) < sizeof(uint8_t)) {
+ TokenizeError("cannot ReadByte, out of bounds", input, cursor);
+ }
+
+ uint8_t word; /* = *reinterpret_cast< const uint8_t* >( cursor )*/
+ ::memcpy(&word, cursor, sizeof(uint8_t));
+ ++cursor;
+
+ return word;
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int ReadString(const char *&sbegin_out, const char *&send_out, const char *input,
+ const char *&cursor, const char *end, bool long_length = false, bool allow_null = false) {
+ const uint32_t len_len = long_length ? 4 : 1;
+ if (Offset(cursor, end) < len_len) {
+ TokenizeError("cannot ReadString, out of bounds reading length", input, cursor);
+ }
+
+ const uint32_t length = long_length ? ReadWord(input, cursor, end) : ReadByte(input, cursor, end);
+
+ if (Offset(cursor, end) < length) {
+ TokenizeError("cannot ReadString, length is out of bounds", input, cursor);
+ }
+
+ sbegin_out = cursor;
+ cursor += length;
+
+ send_out = cursor;
+
+ if (!allow_null) {
+ for (unsigned int i = 0; i < length; ++i) {
+ if (sbegin_out[i] == '\0') {
+ TokenizeError("failed ReadString, unexpected NUL character in string", input, cursor);
+ }
+ }
+ }
+
+ return length;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ReadData(const char *&sbegin_out, const char *&send_out, const char *input, const char *&cursor, const char *end, bool &corrupt) {
+ if (Offset(cursor, end) < 1) {
+ TokenizeError("cannot ReadData, out of bounds reading length", input, cursor);
+ corrupt = true;
+ return;
+ }
+
+ const char type = *cursor;
+ sbegin_out = cursor++;
+
+ switch (type) {
+ // 16 bit int
+ case 'Y':
+ cursor += 2;
+ break;
+
+ // 1 bit bool flag (yes/no)
+ case 'C':
+ cursor += 1;
+ break;
+
+ // 32 bit int
+ case 'I':
+ // <- fall through
+
+ // float
+ case 'F':
+ cursor += 4;
+ break;
+
+ // double
+ case 'D':
+ cursor += 8;
+ break;
+
+ // 64 bit int
+ case 'L':
+ cursor += 8;
+ break;
+
+ // note: do not write cursor += ReadWord(...cursor) as this would be UB
+
+ // raw binary data
+ case 'R': {
+ const uint32_t length = ReadWord(input, cursor, end);
+ cursor += length;
+ break;
+ }
+
+ case 'b':
+ // TODO: what is the 'b' type code? Right now we just skip over it /
+ // take the full range we could get
+ cursor = end;
+ break;
+
+ // array of *
+ case 'f':
+ case 'd':
+ case 'l':
+ case 'i':
+ case 'c': {
+ const uint32_t length = ReadWord(input, cursor, end);
+ const uint32_t encoding = ReadWord(input, cursor, end);
+
+ const uint32_t comp_len = ReadWord(input, cursor, end);
+
+ // compute length based on type and check against the stored value
+ if (encoding == 0) {
+ uint32_t stride = 0;
+ switch (type) {
+ case 'f':
+ case 'i':
+ stride = 4;
+ break;
+
+ case 'd':
+ case 'l':
+ stride = 8;
+ break;
+
+ case 'c':
+ stride = 1;
+ break;
+
+ default:
+ break;
+ };
+ //ai_assert(stride > 0);
+ if (length * stride != comp_len) {
+ TokenizeError("cannot ReadData, calculated data stride differs from what the file claims", input, cursor);
+ }
+ }
+ // zip/deflate algorithm (encoding==1)? take given length. anything else? die
+ else if (encoding != 1) {
+ TokenizeError("cannot ReadData, unknown encoding", input, cursor);
+ }
+ cursor += comp_len;
+ break;
+ } // string
+ case 'S': {
+ const char *sb, *se;
+ // 0 characters can legally happen in such strings
+ ReadString(sb, se, input, cursor, end, true, true);
+ break;
+ }
+ default:
+ corrupt = true; // must exit
+ TokenizeError("cannot ReadData, unexpected type code: " + std::string(&type, 1), input, cursor);
+ return;
+ }
+
+ if (cursor > end) {
+ corrupt = true; // must exit
+ TokenizeError("cannot ReadData, the remaining size is too small for the data type: " + std::string(&type, 1), input, cursor);
+ return;
+ }
+
+ // the type code is contained in the returned range
+ send_out = cursor;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor, const char *end, bool const is64bits, bool &corrupt) {
+ // the first word contains the offset at which this block ends
+ const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
+
+ // we may get 0 if reading reached the end of the file -
+ // fbx files have a mysterious extra footer which I don't know
+ // how to extract any information from, but at least it always
+ // starts with a 0.
+ if (!end_offset) {
+ return false;
+ }
+
+ if (end_offset > Offset(input, end)) {
+ TokenizeError("block offset is out of range", input, cursor);
+ corrupt = true;
+ return false;
+ } else if (end_offset < Offset(input, cursor)) {
+ TokenizeError("block offset is negative out of range", input, cursor);
+ corrupt = true;
+ return false;
+ }
+
+ // the second data word contains the number of properties in the scope
+ const uint64_t prop_count = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
+
+ // the third data word contains the length of the property list
+ const uint64_t prop_length = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
+
+ // now comes the name of the scope/key
+ const char *sbeg = nullptr, *send = nullptr;
+ ReadString(sbeg, send, input, cursor, end);
+
+ output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, cursor)));
+
+ // now come the individual properties
+ const char *begin_cursor = cursor;
+ for (unsigned int i = 0; i < prop_count; ++i) {
+ ReadData(sbeg, send, input, cursor, begin_cursor + prop_length, corrupt);
+ if (corrupt) {
+ return false;
+ }
+
+ output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor)));
+
+ if (i != prop_count - 1) {
+ output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_COMMA, Offset(input, cursor)));
+ }
+ }
+
+ if (Offset(begin_cursor, cursor) != prop_length) {
+ TokenizeError("property length not reached, something is wrong", input, cursor);
+ corrupt = true;
+ return false;
+ }
+
+ // at the end of each nested block, there is a NUL record to indicate
+ // that the sub-scope exists (i.e. to distinguish between P: and P : {})
+ // this NUL record is 13 bytes long on 32 bit version and 25 bytes long on 64 bit.
+ const size_t sentinel_block_length = is64bits ? (sizeof(uint64_t) * 3 + 1) : (sizeof(uint32_t) * 3 + 1);
+
+ if (Offset(input, cursor) < end_offset) {
+ if (end_offset - Offset(input, cursor) < sentinel_block_length) {
+ TokenizeError("insufficient padding bytes at block end", input, cursor);
+ }
+
+ output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_OPEN_BRACKET, Offset(input, cursor)));
+
+ // XXX this is vulnerable to stack overflowing ..
+ while (Offset(input, cursor) < end_offset - sentinel_block_length) {
+ ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits, corrupt);
+ if (corrupt) {
+ return false;
+ }
+ }
+ output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor)));
+
+ for (unsigned int i = 0; i < sentinel_block_length; ++i) {
+ if (cursor[i] != '\0') {
+ TokenizeError("failed to read nested block sentinel, expected all bytes to be 0", input, cursor);
+ corrupt = true;
+ return false;
+ }
+ }
+ cursor += sentinel_block_length;
+ }
+
+ if (Offset(input, cursor) != end_offset) {
+ TokenizeError("scope length not reached, something is wrong", input, cursor);
+ corrupt = true;
+ return false;
+ }
+
+ return true;
+}
+} // anonymous namespace
+
+// ------------------------------------------------------------------------------------------------
+// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
+void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, bool &corrupt) {
+ if (length < 0x1b) {
+ //TokenizeError("file is too short",0);
+ }
+
+ if (strncmp(input, "Kaydara FBX Binary", 18)) {
+ TokenizeError("magic bytes not found", 0);
+ }
+
+ const char *cursor = input + 18;
+ /*Result ignored*/ ReadByte(input, cursor, input + length);
+ /*Result ignored*/ ReadByte(input, cursor, input + length);
+ /*Result ignored*/ ReadByte(input, cursor, input + length);
+ /*Result ignored*/ ReadByte(input, cursor, input + length);
+ /*Result ignored*/ ReadByte(input, cursor, input + length);
+ const uint32_t version = ReadWord(input, cursor, input + length);
+ print_verbose("FBX Version: " + itos(version));
+ //ASSIMP_LOG_DEBUG_F("FBX version: ", version);
+ const bool is64bits = version >= 7500;
+ const char *end = input + length;
+ while (cursor < end) {
+ if (!ReadScope(output_tokens, input, cursor, input + length, is64bits, corrupt)) {
+ break;
+ }
+ }
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXCommon.h b/modules/fbx/fbx_parser/FBXCommon.h
new file mode 100644
index 0000000000..611bf22d3b
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXCommon.h
@@ -0,0 +1,110 @@
+/*************************************************************************/
+/* FBXCommon.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+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.
+ */
+#ifndef FBX_COMMON_H
+#define FBX_COMMON_H
+
+#include <string>
+
+namespace FBXDocParser {
+const std::string NULL_RECORD = { // 13 null bytes
+ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
+}; // who knows why
+const std::string SEPARATOR = { '\x00', '\x01' }; // for use inside strings
+const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import
+const int64_t SECOND = 46186158000; // FBX's kTime unit
+
+// rotation order. We'll probably use EulerXYZ for everything
+enum RotOrder {
+ RotOrder_EulerXYZ = 0,
+ RotOrder_EulerXZY,
+ RotOrder_EulerYZX,
+ RotOrder_EulerYXZ,
+ RotOrder_EulerZXY,
+ RotOrder_EulerZYX,
+
+ RotOrder_SphericXYZ,
+
+ RotOrder_MAX // end-of-enum sentinel
+};
+
+enum TransformInheritance {
+ Transform_RrSs = 0,
+ Transform_RSrs = 1,
+ Transform_Rrs = 2,
+ TransformInheritance_MAX // end-of-enum sentinel
+};
+} // namespace FBXDocParser
+
+#endif // FBX_COMMON_H
diff --git a/modules/fbx/fbx_parser/FBXDeformer.cpp b/modules/fbx/fbx_parser/FBXDeformer.cpp
new file mode 100644
index 0000000000..4220ba62a7
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXDeformer.cpp
@@ -0,0 +1,271 @@
+/*************************************************************************/
+/* FBXDeformer.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXNoteAttribute.cpp
+ * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXMeshGeometry.h"
+#include "FBXParser.h"
+#include "core/math/math_funcs.h"
+#include "core/math/transform_3d.h"
+
+#include <iostream>
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Deformer::Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name) {
+}
+
+// ------------------------------------------------------------------------------------------------
+Deformer::~Deformer() {
+}
+
+Constraint::Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name) {
+}
+
+Constraint::~Constraint() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Cluster::Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Deformer(id, element, doc, name), valid_transformAssociateModel(false) {
+ const ScopePtr sc = GetRequiredScope(element);
+ // for( auto element : sc.Elements())
+ // {
+ // std::cout << "cluster element: " << element.first << std::endl;
+ // }
+ //
+ // element: Indexes
+ // element: Transform
+ // element: TransformAssociateModel
+ // element: TransformLink
+ // element: UserData
+ // element: Version
+ // element: Weights
+
+ const ElementPtr Indexes = sc->GetElement("Indexes");
+ const ElementPtr Weights = sc->GetElement("Weights");
+
+ const ElementPtr TransformAssociateModel = sc->GetElement("TransformAssociateModel");
+ if (TransformAssociateModel != nullptr) {
+ //Transform t = ReadMatrix(*TransformAssociateModel);
+ link_mode = SkinLinkMode_Additive;
+ valid_transformAssociateModel = true;
+ } else {
+ link_mode = SkinLinkMode_Normalized;
+ valid_transformAssociateModel = false;
+ }
+
+ const ElementPtr Transform = GetRequiredElement(sc, "Transform", element);
+ const ElementPtr TransformLink = GetRequiredElement(sc, "TransformLink", element);
+
+ // todo: check if we need this
+ //const Element& TransformAssociateModel = GetRequiredElement(sc, "TransformAssociateModel", &element);
+
+ transform = ReadMatrix(Transform);
+ transformLink = ReadMatrix(TransformLink);
+
+ // it is actually possible that there be Deformer's with no weights
+ if (!!Indexes != !!Weights) {
+ DOMError("either Indexes or Weights are missing from Cluster", element);
+ }
+
+ if (Indexes) {
+ ParseVectorDataArray(indices, Indexes);
+ ParseVectorDataArray(weights, Weights);
+ }
+
+ if (indices.size() != weights.size()) {
+ DOMError("sizes of index and weight array don't match up", element);
+ }
+
+ // read assigned node
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Model");
+ for (const Connection *con : conns) {
+ const Model *mod = ProcessSimpleConnection<Model>(*con, false, "Model -> Cluster", element);
+ if (mod) {
+ node = mod;
+ break;
+ }
+ }
+
+ if (!node) {
+ DOMError("failed to read target Node for Cluster", element);
+ node = nullptr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Cluster::~Cluster() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Skin::Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Deformer(id, element, doc, name), accuracy(0.0f) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ // keep this it is used for debugging and any FBX format changes
+ // for (auto element : sc.Elements()) {
+ // std::cout << "skin element: " << element.first << std::endl;
+ // }
+
+ const ElementPtr Link_DeformAcuracy = sc->GetElement("Link_DeformAcuracy");
+ if (Link_DeformAcuracy) {
+ accuracy = ParseTokenAsFloat(GetRequiredToken(Link_DeformAcuracy, 0));
+ }
+
+ const ElementPtr SkinType = sc->GetElement("SkinningType");
+
+ if (SkinType) {
+ std::string skin_type = ParseTokenAsString(GetRequiredToken(SkinType, 0));
+
+ if (skin_type == "Linear") {
+ skinType = Skin_Linear;
+ } else if (skin_type == "Rigid") {
+ skinType = Skin_Rigid;
+ } else if (skin_type == "DualQuaternion") {
+ skinType = Skin_DualQuaternion;
+ } else if (skin_type == "Blend") {
+ skinType = Skin_Blend;
+ } else {
+ print_error("[doc:skin] could not find valid skin type: " + String(skin_type.c_str()));
+ }
+ }
+
+ // resolve assigned clusters
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
+
+ //
+
+ clusters.reserve(conns.size());
+ for (const Connection *con : conns) {
+ const Cluster *cluster = ProcessSimpleConnection<Cluster>(*con, false, "Cluster -> Skin", element);
+ if (cluster) {
+ clusters.push_back(cluster);
+ continue;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Skin::~Skin() {
+}
+// ------------------------------------------------------------------------------------------------
+BlendShape::BlendShape(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Deformer(id, element, doc, name) {
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
+ blendShapeChannels.reserve(conns.size());
+ for (const Connection *con : conns) {
+ const BlendShapeChannel *bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element);
+ if (bspc) {
+ blendShapeChannels.push_back(bspc);
+ continue;
+ }
+ }
+}
+// ------------------------------------------------------------------------------------------------
+BlendShape::~BlendShape() {
+}
+// ------------------------------------------------------------------------------------------------
+BlendShapeChannel::BlendShapeChannel(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Deformer(id, element, doc, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+ const ElementPtr DeformPercent = sc->GetElement("DeformPercent");
+ if (DeformPercent) {
+ percent = ParseTokenAsFloat(GetRequiredToken(DeformPercent, 0));
+ }
+ const ElementPtr FullWeights = sc->GetElement("FullWeights");
+ if (FullWeights) {
+ ParseVectorDataArray(fullWeights, FullWeights);
+ }
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Geometry");
+ shapeGeometries.reserve(conns.size());
+ for (const Connection *con : conns) {
+ const ShapeGeometry *const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element);
+ if (sg) {
+ shapeGeometries.push_back(sg);
+ continue;
+ }
+ }
+}
+// ------------------------------------------------------------------------------------------------
+BlendShapeChannel::~BlendShapeChannel() {
+}
+// ------------------------------------------------------------------------------------------------
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXDocument.cpp b/modules/fbx/fbx_parser/FBXDocument.cpp
new file mode 100644
index 0000000000..92c62e68b5
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXDocument.cpp
@@ -0,0 +1,636 @@
+/*************************************************************************/
+/* FBXDocument.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the*
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXDocument.cpp
+ * @brief Implementation of the FBX DOM classes
+ */
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXImportSettings.h"
+#include "FBXMeshGeometry.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+#include "FBXUtil.h"
+
+#include <algorithm>
+#include <functional>
+#include <iostream>
+#include <map>
+#include <memory>
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+LazyObject::LazyObject(uint64_t id, const ElementPtr element, const Document &doc) :
+ doc(doc), element(element), id(id) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject::~LazyObject() {
+ object.reset();
+}
+
+ObjectPtr LazyObject::LoadObject() {
+ if (IsBeingConstructed() || FailedToConstruct()) {
+ return nullptr;
+ }
+
+ if (object) {
+ return object.get();
+ }
+
+ TokenPtr key = element->KeyToken();
+ ERR_FAIL_COND_V(!key, nullptr);
+ const TokenList &tokens = element->Tokens();
+
+ if (tokens.size() < 3) {
+ //DOMError("expected at least 3 tokens: id, name and class tag",&element);
+ return nullptr;
+ }
+
+ const char *err = nullptr;
+ std::string name = ParseTokenAsString(tokens[1], err);
+ if (err) {
+ DOMError(err, element);
+ }
+
+ // small fix for binary reading: binary fbx files don't use
+ // prefixes such as Model:: in front of their names. The
+ // loading code expects this at many places, though!
+ // so convert the binary representation (a 0x0001) to the
+ // double colon notation.
+ if (tokens[1]->IsBinary()) {
+ for (size_t i = 0; i < name.length(); ++i) {
+ if (name[i] == 0x0 && name[i + 1] == 0x1) {
+ name = name.substr(i + 2) + "::" + name.substr(0, i);
+ }
+ }
+ }
+
+ const std::string classtag = ParseTokenAsString(tokens[2], err);
+ if (err) {
+ DOMError(err, element);
+ }
+
+ // prevent recursive calls
+ flags |= BEING_CONSTRUCTED;
+
+ // this needs to be relatively fast since it happens a lot,
+ // so avoid constructing strings all the time.
+ const char *obtype = key->begin();
+ const size_t length = static_cast<size_t>(key->end() - key->begin());
+
+ if (!strncmp(obtype, "Pose", length)) {
+ object.reset(new FbxPose(id, element, doc, name));
+ } else if (!strncmp(obtype, "Geometry", length)) {
+ if (!strcmp(classtag.c_str(), "Mesh")) {
+ object.reset(new MeshGeometry(id, element, name, doc));
+ }
+ if (!strcmp(classtag.c_str(), "Shape")) {
+ object.reset(new ShapeGeometry(id, element, name, doc));
+ }
+ if (!strcmp(classtag.c_str(), "Line")) {
+ object.reset(new LineGeometry(id, element, name, doc));
+ }
+ } else if (!strncmp(obtype, "NodeAttribute", length)) {
+ if (!strcmp(classtag.c_str(), "Camera")) {
+ object.reset(new Camera(id, element, doc, name));
+ } else if (!strcmp(classtag.c_str(), "CameraSwitcher")) {
+ object.reset(new CameraSwitcher(id, element, doc, name));
+ } else if (!strcmp(classtag.c_str(), "Light")) {
+ object.reset(new Light(id, element, doc, name));
+ } else if (!strcmp(classtag.c_str(), "Null")) {
+ object.reset(new Null(id, element, doc, name));
+ } else if (!strcmp(classtag.c_str(), "LimbNode")) {
+ // This is an older format for bones
+ // this is what blender uses I believe
+ object.reset(new LimbNode(id, element, doc, name));
+ }
+ } else if (!strncmp(obtype, "Constraint", length)) {
+ object.reset(new Constraint(id, element, doc, name));
+ } else if (!strncmp(obtype, "Deformer", length)) {
+ if (!strcmp(classtag.c_str(), "Cluster")) {
+ object.reset(new Cluster(id, element, doc, name));
+ } else if (!strcmp(classtag.c_str(), "Skin")) {
+ object.reset(new Skin(id, element, doc, name));
+ } else if (!strcmp(classtag.c_str(), "BlendShape")) {
+ object.reset(new BlendShape(id, element, doc, name));
+ } else if (!strcmp(classtag.c_str(), "BlendShapeChannel")) {
+ object.reset(new BlendShapeChannel(id, element, doc, name));
+ }
+ } else if (!strncmp(obtype, "Model", length)) {
+ // Model is normal node
+
+ // LimbNode model is a 'bone' node.
+ if (!strcmp(classtag.c_str(), "LimbNode")) {
+ object.reset(new ModelLimbNode(id, element, doc, name));
+
+ } else if (strcmp(classtag.c_str(), "IKEffector") && strcmp(classtag.c_str(), "FKEffector")) {
+ // FK and IK effectors are not supported.
+ object.reset(new Model(id, element, doc, name));
+ }
+ } else if (!strncmp(obtype, "Material", length)) {
+ object.reset(new Material(id, element, doc, name));
+ } else if (!strncmp(obtype, "Texture", length)) {
+ object.reset(new Texture(id, element, doc, name));
+ } else if (!strncmp(obtype, "LayeredTexture", length)) {
+ object.reset(new LayeredTexture(id, element, doc, name));
+ } else if (!strncmp(obtype, "Video", length)) {
+ object.reset(new Video(id, element, doc, name));
+ } else if (!strncmp(obtype, "AnimationStack", length)) {
+ object.reset(new AnimationStack(id, element, name, doc));
+ } else if (!strncmp(obtype, "AnimationLayer", length)) {
+ object.reset(new AnimationLayer(id, element, name, doc));
+ } else if (!strncmp(obtype, "AnimationCurve", length)) {
+ object.reset(new AnimationCurve(id, element, name, doc));
+ } else if (!strncmp(obtype, "AnimationCurveNode", length)) {
+ object.reset(new AnimationCurveNode(id, element, name, doc));
+ } else {
+ ERR_FAIL_V_MSG(nullptr, "FBX contains unsupported object: " + String(obtype));
+ }
+
+ flags &= ~BEING_CONSTRUCTED;
+
+ return object.get();
+}
+
+// ------------------------------------------------------------------------------------------------
+Object::Object(uint64_t id, const ElementPtr element, const std::string &name) :
+ PropertyTable(element), element(element), name(name), id(id) {
+}
+
+// ------------------------------------------------------------------------------------------------
+Object::~Object() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+FileGlobalSettings::FileGlobalSettings(const Document &doc) :
+ PropertyTable(), doc(doc) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+FileGlobalSettings::~FileGlobalSettings() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Document::Document(const Parser &parser, const ImportSettings &settings) :
+ settings(settings), parser(parser) {
+ // Cannot use array default initialization syntax because vc8 fails on it
+ for (unsigned int &timeStamp : creationTimeStamp) {
+ timeStamp = 0;
+ }
+
+ // we must check if we can read the header version safely, if its outdated then drop it.
+ if (ReadHeader()) {
+ SafeToImport = true;
+ ReadPropertyTemplates();
+
+ ReadGlobalSettings();
+
+ // This order is important, connections need parsed objects to check
+ // whether connections are ok or not. Objects may not be evaluated yet,
+ // though, since this may require valid connections.
+ ReadObjects();
+ ReadConnections();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Document::~Document() {
+ for (PropertyTemplateMap::value_type v : templates) {
+ delete v.second;
+ }
+
+ for (ObjectMap::value_type &v : objects) {
+ delete v.second;
+ }
+
+ for (ConnectionMap::value_type &v : src_connections) {
+ delete v.second;
+ }
+
+ // clear globals import pointer
+ globals.reset();
+}
+
+// ------------------------------------------------------------------------------------------------
+static const unsigned int LowerSupportedVersion = 7100;
+static const unsigned int UpperSupportedVersion = 7700;
+
+bool Document::ReadHeader() {
+ // Read ID objects from "Objects" section
+ ScopePtr sc = parser.GetRootScope();
+ ElementPtr ehead = sc->GetElement("FBXHeaderExtension");
+ if (!ehead || !ehead->Compound()) {
+ DOMError("no FBXHeaderExtension dictionary found");
+ }
+
+ if (parser.IsCorrupt()) {
+ DOMError("File is corrupt");
+ return false;
+ }
+
+ const ScopePtr shead = ehead->Compound();
+ fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead, "FBXVersion", ehead), 0));
+
+ // While we may have some success with newer files, we don't support
+ // the older 6.n fbx format
+ if (fbxVersion < LowerSupportedVersion) {
+ DOMWarning("unsupported, old format version, FBX 2015-2020, you must re-export in a more modern version of your original modelling application");
+ return false;
+ }
+ if (fbxVersion > UpperSupportedVersion) {
+ DOMWarning("unsupported, newer format version, supported are only FBX 2015, up to FBX 2020"
+ " trying to read it nevertheless");
+ }
+
+ const ElementPtr ecreator = shead->GetElement("Creator");
+ if (ecreator) {
+ creator = ParseTokenAsString(GetRequiredToken(ecreator, 0));
+ }
+
+ // Scene Info
+ const ElementPtr scene_info = shead->GetElement("SceneInfo");
+
+ if (scene_info) {
+ metadata_properties.Setup(scene_info);
+ }
+
+ const ElementPtr etimestamp = shead->GetElement("CreationTimeStamp");
+ if (etimestamp && etimestamp->Compound()) {
+ const ScopePtr stimestamp = etimestamp->Compound();
+ creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Year"), 0));
+ creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Month"), 0));
+ creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Day"), 0));
+ creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Hour"), 0));
+ creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Minute"), 0));
+ creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Second"), 0));
+ creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp, "Millisecond"), 0));
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadGlobalSettings() {
+ ERR_FAIL_COND_MSG(globals != nullptr, "Global settings is already setup this is a serious error and should be reported");
+
+ globals = std::make_shared<FileGlobalSettings>(*this);
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadObjects() {
+ // read ID objects from "Objects" section
+ const ScopePtr sc = parser.GetRootScope();
+ const ElementPtr eobjects = sc->GetElement("Objects");
+ if (!eobjects || !eobjects->Compound()) {
+ DOMError("no Objects dictionary found");
+ }
+
+ // add a dummy entry to represent the Model::RootNode object (id 0),
+ // which is only indirectly defined in the input file
+ objects[0] = new LazyObject(0L, eobjects, *this);
+
+ const ScopePtr sobjects = eobjects->Compound();
+ for (const ElementMap::value_type &iter : sobjects->Elements()) {
+ // extract ID
+ const TokenList &tok = iter.second->Tokens();
+
+ if (tok.empty()) {
+ DOMError("expected ID after object key", iter.second);
+ }
+
+ const char *err;
+ const uint64_t id = ParseTokenAsID(tok[0], err);
+ if (err) {
+ DOMError(err, iter.second);
+ }
+
+ // id=0 is normally implicit
+ if (id == 0L) {
+ DOMError("encountered object with implicitly defined id 0", iter.second);
+ }
+
+ if (objects.find(id) != objects.end()) {
+ DOMWarning("encountered duplicate object id, ignoring first occurrence", iter.second);
+ }
+
+ objects[id] = new LazyObject(id, iter.second, *this);
+
+ // grab all animation stacks upfront since there is no listing of them
+ if (!strcmp(iter.first.c_str(), "AnimationStack")) {
+ animationStacks.push_back(id);
+ } else if (!strcmp(iter.first.c_str(), "Constraint")) {
+ constraints.push_back(id);
+ } else if (!strcmp(iter.first.c_str(), "Pose")) {
+ bind_poses.push_back(id);
+ } else if (!strcmp(iter.first.c_str(), "Material")) {
+ materials.push_back(id);
+ } else if (!strcmp(iter.first.c_str(), "Deformer")) {
+ TokenPtr key = iter.second->KeyToken();
+ ERR_CONTINUE_MSG(!key, "[parser bug] invalid token key for deformer");
+ const TokenList &tokens = iter.second->Tokens();
+ const std::string class_tag = ParseTokenAsString(tokens[2], err);
+
+ if (err) {
+ DOMError(err, iter.second);
+ }
+
+ if (class_tag == "Skin") {
+ //print_verbose("registered skin:" + itos(id));
+ skins.push_back(id);
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadPropertyTemplates() {
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadConnections() {
+ const ScopePtr sc = parser.GetRootScope();
+
+ // read property templates from "Definitions" section
+ const ElementPtr econns = sc->GetElement("Connections");
+ if (!econns || !econns->Compound()) {
+ DOMError("no Connections dictionary found");
+ }
+
+ uint64_t insertionOrder = 0l;
+ const ScopePtr sconns = econns->Compound();
+ const ElementCollection conns = sconns->GetCollection("C");
+ for (ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
+ const ElementPtr el = (*it).second;
+ const std::string &type = ParseTokenAsString(GetRequiredToken(el, 0));
+
+ // PP = property-property connection, ignored for now
+ // (tokens: "PP", ID1, "Property1", ID2, "Property2")
+ if (type == "PP") {
+ continue;
+ }
+
+ const uint64_t src = ParseTokenAsID(GetRequiredToken(el, 1));
+ const uint64_t dest = ParseTokenAsID(GetRequiredToken(el, 2));
+
+ // OO = object-object connection
+ // OP = object-property connection, in which case the destination property follows the object ID
+ const std::string &prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el, 3)) : "");
+
+ if (objects.find(src) == objects.end()) {
+ DOMWarning("source object for connection does not exist", el);
+ continue;
+ }
+
+ // dest may be 0 (root node) but we added a dummy object before
+ if (objects.find(dest) == objects.end()) {
+ DOMWarning("destination object for connection does not exist", el);
+ continue;
+ }
+
+ // add new connection
+ const Connection *const c = new Connection(insertionOrder++, src, dest, prop, *this);
+ src_connections.insert(ConnectionMap::value_type(src, c));
+ dest_connections.insert(ConnectionMap::value_type(dest, c));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<const AnimationStack *> &Document::AnimationStacks() const {
+ if (!animationStacksResolved.empty() || animationStacks.empty()) {
+ return animationStacksResolved;
+ }
+
+ animationStacksResolved.reserve(animationStacks.size());
+ for (uint64_t id : animationStacks) {
+ LazyObject *lazy = GetObject(id);
+
+ // Two things happen here:
+ // We cast internally an Object PTR to an Animation Stack PTR
+ // We return invalid weak_ptrs for objects which are invalid
+
+ const AnimationStack *stack = lazy->Get<AnimationStack>();
+ ERR_CONTINUE_MSG(!stack, "invalid ptr to AnimationStack - conversion failure");
+
+ // We push back the weak reference :) to keep things simple, as ownership is on the parser side so it won't be cleaned up.
+ animationStacksResolved.push_back(stack);
+ }
+
+ return animationStacksResolved;
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject *Document::GetObject(uint64_t id) const {
+ ObjectMap::const_iterator it = objects.find(id);
+ return it == objects.end() ? nullptr : (*it).second;
+}
+
+#define MAX_CLASSNAMES 6
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsSequenced(uint64_t id, const ConnectionMap &conns) const {
+ std::vector<const Connection *> temp;
+
+ const std::pair<ConnectionMap::const_iterator, ConnectionMap::const_iterator> range =
+ conns.equal_range(id);
+
+ temp.reserve(std::distance(range.first, range.second));
+ for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
+ temp.push_back((*it).second);
+ }
+
+ std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare));
+
+ return temp; // NRVO should handle this
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsSequenced(uint64_t id, bool is_src,
+ const ConnectionMap &conns,
+ const char *const *classnames,
+ size_t count) const
+
+{
+ size_t lengths[MAX_CLASSNAMES];
+
+ const size_t c = count;
+ for (size_t i = 0; i < c; ++i) {
+ lengths[i] = strlen(classnames[i]);
+ }
+
+ std::vector<const Connection *> temp;
+ const std::pair<ConnectionMap::const_iterator, ConnectionMap::const_iterator> range =
+ conns.equal_range(id);
+
+ temp.reserve(std::distance(range.first, range.second));
+ for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
+ TokenPtr key = (is_src ? (*it).second->LazyDestinationObject() : (*it).second->LazySourceObject())->GetElement()->KeyToken();
+
+ const char *obtype = key->begin();
+
+ for (size_t i = 0; i < c; ++i) {
+ //ai_assert(classnames[i]);
+ if (static_cast<size_t>(std::distance(key->begin(), key->end())) == lengths[i] && !strncmp(classnames[i], obtype, lengths[i])) {
+ obtype = nullptr;
+ break;
+ }
+ }
+
+ if (obtype) {
+ continue;
+ }
+
+ temp.push_back((*it).second);
+ }
+
+ std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare));
+ return temp; // NRVO should handle this
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t source) const {
+ return GetConnectionsSequenced(source, ConnectionsBySource());
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t src, const char *classname) const {
+ const char *arr[] = { classname };
+ return GetConnectionsBySourceSequenced(src, arr, 1);
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t source,
+ const char *const *classnames, size_t count) const {
+ return GetConnectionsSequenced(source, true, ConnectionsBySource(), classnames, count);
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
+ const char *classname) const {
+ const char *arr[] = { classname };
+ return GetConnectionsByDestinationSequenced(dest, arr, 1);
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const {
+ return GetConnectionsSequenced(dest, ConnectionsByDestination());
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
+ const char *const *classnames, size_t count) const {
+ return GetConnectionsSequenced(dest, false, ConnectionsByDestination(), classnames, count);
+}
+
+// ------------------------------------------------------------------------------------------------
+Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string &prop,
+ const Document &doc) :
+ insertionOrder(insertionOrder), prop(prop), src(src), dest(dest), doc(doc) {
+}
+
+// ------------------------------------------------------------------------------------------------
+Connection::~Connection() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject *Connection::LazySourceObject() const {
+ LazyObject *const lazy = doc.GetObject(src);
+ return lazy;
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject *Connection::LazyDestinationObject() const {
+ LazyObject *const lazy = doc.GetObject(dest);
+ return lazy;
+}
+
+// ------------------------------------------------------------------------------------------------
+Object *Connection::SourceObject() const {
+ LazyObject *lazy = doc.GetObject(src);
+ //ai_assert(lazy);
+ return lazy->LoadObject();
+}
+
+// ------------------------------------------------------------------------------------------------
+Object *Connection::DestinationObject() const {
+ LazyObject *lazy = doc.GetObject(dest);
+ //ai_assert(lazy);
+ return lazy->LoadObject();
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXDocument.h b/modules/fbx/fbx_parser/FBXDocument.h
new file mode 100644
index 0000000000..539d633331
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXDocument.h
@@ -0,0 +1,1252 @@
+/*************************************************************************/
+/* FBXDocument.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. */
+/*************************************************************************/
+
+/** @file FBXDocument.h
+ * @brief FBX DOM
+ */
+#ifndef FBX_DOCUMENT_H
+#define FBX_DOCUMENT_H
+
+#include "FBXCommon.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+#include "core/math/transform_3d.h"
+#include "core/math/vector2.h"
+#include "core/math/vector3.h"
+#include "core/string/print_string.h"
+#include <stdint.h>
+#include <numeric>
+
+#define _AI_CONCAT(a, b) a##b
+#define AI_CONCAT(a, b) _AI_CONCAT(a, b)
+
+namespace FBXDocParser {
+
+class Parser;
+class Object;
+struct ImportSettings;
+class Connection;
+
+class PropertyTable;
+class Document;
+class Material;
+class ShapeGeometry;
+class LineGeometry;
+class Geometry;
+
+class Video;
+
+class AnimationCurve;
+class AnimationCurveNode;
+class AnimationLayer;
+class AnimationStack;
+
+class BlendShapeChannel;
+class BlendShape;
+class Skin;
+class Cluster;
+
+typedef Object *ObjectPtr;
+#define new_Object new Object
+
+/** Represents a delay-parsed FBX objects. Many objects in the scene
+ * are not needed by assimp, so it makes no sense to parse them
+ * upfront. */
+class LazyObject {
+public:
+ LazyObject(uint64_t id, const ElementPtr element, const Document &doc);
+ ~LazyObject();
+
+ ObjectPtr LoadObject();
+
+ /* Casting weak pointers to their templated type safely and preserving ref counting and safety
+ * with lock() keyword to prevent leaking memory
+ */
+ template <typename T>
+ const T *Get() {
+ ObjectPtr ob = LoadObject();
+ return dynamic_cast<const T *>(ob);
+ }
+
+ uint64_t ID() const {
+ return id;
+ }
+
+ bool IsBeingConstructed() const {
+ return (flags & BEING_CONSTRUCTED) != 0;
+ }
+
+ bool FailedToConstruct() const {
+ return (flags & FAILED_TO_CONSTRUCT) != 0;
+ }
+
+ ElementPtr GetElement() const {
+ return element;
+ }
+
+ const Document &GetDocument() const {
+ return doc;
+ }
+
+private:
+ const Document &doc;
+ ElementPtr element = nullptr;
+ std::shared_ptr<Object> object = nullptr;
+ const uint64_t id = 0;
+
+ enum Flags {
+ BEING_CONSTRUCTED = 0x1,
+ FAILED_TO_CONSTRUCT = 0x2
+ };
+
+ unsigned int flags = 0;
+};
+
+/** Base class for in-memory (DOM) representations of FBX objects */
+class Object : public PropertyTable {
+public:
+ Object(uint64_t id, const ElementPtr element, const std::string &name);
+
+ virtual ~Object();
+
+ ElementPtr SourceElement() const {
+ return element;
+ }
+
+ const std::string &Name() const {
+ return name;
+ }
+
+ uint64_t ID() const {
+ return id;
+ }
+
+protected:
+ const ElementPtr element = nullptr;
+ const std::string name;
+ const uint64_t id;
+};
+
+/** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table,
+ * fixed members are added by deriving classes. */
+class NodeAttribute : public Object {
+public:
+ NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~NodeAttribute();
+};
+
+/** DOM base class for FBX camera settings attached to a node */
+class CameraSwitcher : public NodeAttribute {
+public:
+ CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~CameraSwitcher();
+
+ int CameraID() const {
+ return cameraId;
+ }
+
+ const std::string &CameraName() const {
+ return cameraName;
+ }
+
+ const std::string &CameraIndexName() const {
+ return cameraIndexName;
+ }
+
+private:
+ int cameraId = 0;
+ std::string cameraName;
+ std::string cameraIndexName;
+};
+
+#define fbx_stringize(a) #a
+
+#define fbx_simple_property(name, type, default_value) \
+ type name() const { \
+ return PropertyGet<type>(this, fbx_stringize(name), (default_value)); \
+ }
+
+// XXX improve logging
+#define fbx_simple_enum_property(name, type, default_value) \
+ type name() const { \
+ const int ival = PropertyGet<int>(this, fbx_stringize(name), static_cast<int>(default_value)); \
+ if (ival < 0 || ival >= AI_CONCAT(type, _MAX)) { \
+ return static_cast<type>(default_value); \
+ } \
+ return static_cast<type>(ival); \
+ }
+
+class FbxPoseNode;
+class FbxPose : public Object {
+public:
+ FbxPose(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ const std::vector<FbxPoseNode *> &GetBindPoses() const {
+ return pose_nodes;
+ }
+
+ virtual ~FbxPose();
+
+private:
+ std::vector<FbxPoseNode *> pose_nodes;
+};
+
+class FbxPoseNode {
+public:
+ FbxPoseNode(const ElementPtr element, const Document &doc, const std::string &name) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ // get pose node transform
+ const ElementPtr Transform = GetRequiredElement(sc, "Matrix", element);
+ transform = ReadMatrix(Transform);
+
+ // get node id this pose node is for
+ const ElementPtr NodeId = sc->GetElement("Node3D");
+ if (NodeId) {
+ target_id = ParseTokenAsInt64(GetRequiredToken(NodeId, 0));
+ }
+
+ print_verbose("added posenode " + itos(target_id) + " transform: " + transform);
+ }
+ virtual ~FbxPoseNode() {
+ }
+
+ uint64_t GetNodeID() const {
+ return target_id;
+ }
+
+ Transform3D GetBindPose() const {
+ return transform;
+ }
+
+private:
+ uint64_t target_id = 0;
+ Transform3D transform;
+};
+
+/** DOM base class for FBX cameras attached to a node */
+class Camera : public NodeAttribute {
+public:
+ Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~Camera();
+
+ fbx_simple_property(Position, Vector3, Vector3(0, 0, 0));
+ fbx_simple_property(UpVector, Vector3, Vector3(0, 1, 0));
+ fbx_simple_property(InterestPosition, Vector3, Vector3(0, 0, 0));
+
+ fbx_simple_property(AspectWidth, float, 1.0f);
+ fbx_simple_property(AspectHeight, float, 1.0f);
+ fbx_simple_property(FilmWidth, float, 1.0f);
+ fbx_simple_property(FilmHeight, float, 1.0f);
+
+ fbx_simple_property(NearPlane, float, 0.1f);
+ fbx_simple_property(FarPlane, float, 100.0f);
+
+ fbx_simple_property(FilmAspectRatio, float, 1.0f);
+ fbx_simple_property(ApertureMode, int, 0);
+
+ fbx_simple_property(FieldOfView, float, 1.0f);
+ fbx_simple_property(FocalLength, float, 1.0f);
+};
+
+/** DOM base class for FBX null markers attached to a node */
+class Null : public NodeAttribute {
+public:
+ Null(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~Null();
+};
+
+/** DOM base class for FBX limb node markers attached to a node */
+class LimbNode : public NodeAttribute {
+public:
+ LimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~LimbNode();
+};
+
+/** DOM base class for FBX lights attached to a node */
+class Light : public NodeAttribute {
+public:
+ Light(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~Light();
+
+ enum Type {
+ Type_Point,
+ Type_Directional,
+ Type_Spot,
+ Type_Area,
+ Type_Volume,
+
+ Type_MAX // end-of-enum sentinel
+ };
+
+ enum Decay {
+ Decay_None,
+ Decay_Linear,
+ Decay_Quadratic,
+ Decay_Cubic,
+
+ Decay_MAX // end-of-enum sentinel
+ };
+
+ fbx_simple_property(Color, Vector3, Vector3(1, 1, 1));
+ fbx_simple_enum_property(LightType, Type, 0);
+ fbx_simple_property(CastLightOnObject, bool, false);
+ fbx_simple_property(DrawVolumetricLight, bool, true);
+ fbx_simple_property(DrawGroundProjection, bool, true);
+ fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false);
+ fbx_simple_property(Intensity, float, 100.0f);
+ fbx_simple_property(InnerAngle, float, 0.0f);
+ fbx_simple_property(OuterAngle, float, 45.0f);
+ fbx_simple_property(Fog, int, 50);
+ fbx_simple_enum_property(DecayType, Decay, 2);
+ fbx_simple_property(DecayStart, float, 1.0f);
+ fbx_simple_property(FileName, std::string, "");
+
+ fbx_simple_property(EnableNearAttenuation, bool, false);
+ fbx_simple_property(NearAttenuationStart, float, 0.0f);
+ fbx_simple_property(NearAttenuationEnd, float, 0.0f);
+ fbx_simple_property(EnableFarAttenuation, bool, false);
+ fbx_simple_property(FarAttenuationStart, float, 0.0f);
+ fbx_simple_property(FarAttenuationEnd, float, 0.0f);
+
+ fbx_simple_property(CastShadows, bool, true);
+ fbx_simple_property(ShadowColor, Vector3, Vector3(0, 0, 0));
+
+ fbx_simple_property(AreaLightShape, int, 0);
+
+ fbx_simple_property(LeftBarnDoor, float, 20.0f);
+ fbx_simple_property(RightBarnDoor, float, 20.0f);
+ fbx_simple_property(TopBarnDoor, float, 20.0f);
+ fbx_simple_property(BottomBarnDoor, float, 20.0f);
+ fbx_simple_property(EnableBarnDoor, bool, true);
+};
+
+class Model;
+
+typedef Model *ModelPtr;
+#define new_Model new Model
+
+/** DOM base class for FBX models (even though its semantics are more "node" than "model" */
+class Model : public Object {
+public:
+ enum RotOrder {
+ RotOrder_EulerXYZ = 0,
+ RotOrder_EulerXZY,
+ RotOrder_EulerYZX,
+ RotOrder_EulerYXZ,
+ RotOrder_EulerZXY,
+ RotOrder_EulerZYX,
+
+ RotOrder_SphericXYZ,
+
+ RotOrder_MAX // end-of-enum sentinel
+ };
+
+ Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~Model();
+
+ fbx_simple_property(QuaternionInterpolate, int, 0);
+
+ fbx_simple_property(RotationOffset, Vector3, Vector3());
+ fbx_simple_property(RotationPivot, Vector3, Vector3());
+ fbx_simple_property(ScalingOffset, Vector3, Vector3());
+ fbx_simple_property(ScalingPivot, Vector3, Vector3());
+ fbx_simple_property(TranslationActive, bool, false);
+ fbx_simple_property(TranslationMin, Vector3, Vector3());
+ fbx_simple_property(TranslationMax, Vector3, Vector3());
+
+ fbx_simple_property(TranslationMinX, bool, false);
+ fbx_simple_property(TranslationMaxX, bool, false);
+ fbx_simple_property(TranslationMinY, bool, false);
+ fbx_simple_property(TranslationMaxY, bool, false);
+ fbx_simple_property(TranslationMinZ, bool, false);
+ fbx_simple_property(TranslationMaxZ, bool, false);
+
+ fbx_simple_enum_property(RotationOrder, RotOrder, 0);
+ fbx_simple_property(RotationSpaceForLimitOnly, bool, false);
+ fbx_simple_property(RotationStiffnessX, float, 0.0f);
+ fbx_simple_property(RotationStiffnessY, float, 0.0f);
+ fbx_simple_property(RotationStiffnessZ, float, 0.0f);
+ fbx_simple_property(AxisLen, float, 0.0f);
+
+ fbx_simple_property(PreRotation, Vector3, Vector3());
+ fbx_simple_property(PostRotation, Vector3, Vector3());
+ fbx_simple_property(RotationActive, bool, false);
+
+ fbx_simple_property(RotationMin, Vector3, Vector3());
+ fbx_simple_property(RotationMax, Vector3, Vector3());
+
+ fbx_simple_property(RotationMinX, bool, false);
+ fbx_simple_property(RotationMaxX, bool, false);
+ fbx_simple_property(RotationMinY, bool, false);
+ fbx_simple_property(RotationMaxY, bool, false);
+ fbx_simple_property(RotationMinZ, bool, false);
+ fbx_simple_property(RotationMaxZ, bool, false);
+ fbx_simple_enum_property(InheritType, TransformInheritance, 0);
+
+ fbx_simple_property(ScalingActive, bool, false);
+ fbx_simple_property(ScalingMin, Vector3, Vector3());
+ fbx_simple_property(ScalingMax, Vector3, Vector3(1, 1, 1));
+ fbx_simple_property(ScalingMinX, bool, false);
+ fbx_simple_property(ScalingMaxX, bool, false);
+ fbx_simple_property(ScalingMinY, bool, false);
+ fbx_simple_property(ScalingMaxY, bool, false);
+ fbx_simple_property(ScalingMinZ, bool, false);
+ fbx_simple_property(ScalingMaxZ, bool, false);
+
+ fbx_simple_property(GeometricTranslation, Vector3, Vector3());
+ fbx_simple_property(GeometricRotation, Vector3, Vector3());
+ fbx_simple_property(GeometricScaling, Vector3, Vector3(1, 1, 1));
+
+ fbx_simple_property(MinDampRangeX, float, 0.0f);
+ fbx_simple_property(MinDampRangeY, float, 0.0f);
+ fbx_simple_property(MinDampRangeZ, float, 0.0f);
+ fbx_simple_property(MaxDampRangeX, float, 0.0f);
+ fbx_simple_property(MaxDampRangeY, float, 0.0f);
+ fbx_simple_property(MaxDampRangeZ, float, 0.0f);
+
+ fbx_simple_property(MinDampStrengthX, float, 0.0f);
+ fbx_simple_property(MinDampStrengthY, float, 0.0f);
+ fbx_simple_property(MinDampStrengthZ, float, 0.0f);
+ fbx_simple_property(MaxDampStrengthX, float, 0.0f);
+ fbx_simple_property(MaxDampStrengthY, float, 0.0f);
+ fbx_simple_property(MaxDampStrengthZ, float, 0.0f);
+
+ fbx_simple_property(PreferredAngleX, float, 0.0f);
+ fbx_simple_property(PreferredAngleY, float, 0.0f);
+ fbx_simple_property(PreferredAngleZ, float, 0.0f);
+
+ fbx_simple_property(Show, bool, true);
+ fbx_simple_property(LODBox, bool, false);
+ fbx_simple_property(Freeze, bool, false);
+
+ const std::string &Shading() const {
+ return shading;
+ }
+
+ const std::string &Culling() const {
+ return culling;
+ }
+
+ /** Get material links */
+ const std::vector<const Material *> &GetMaterials() const {
+ return materials;
+ }
+
+ /** Get geometry links */
+ const std::vector<const Geometry *> &GetGeometry() const {
+ return geometry;
+ }
+
+ /** Get node attachments */
+ const std::vector<const NodeAttribute *> &GetAttributes() const {
+ return attributes;
+ }
+
+ /** convenience method to check if the node has a Null node marker */
+ bool IsNull() const;
+
+private:
+ void ResolveLinks(const ElementPtr element, const Document &doc);
+
+private:
+ std::vector<const Material *> materials;
+ std::vector<const Geometry *> geometry;
+ std::vector<const NodeAttribute *> attributes;
+
+ std::string shading;
+ std::string culling;
+};
+
+class ModelLimbNode : public Model {
+public:
+ ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~ModelLimbNode();
+};
+
+/** DOM class for generic FBX textures */
+class Texture : public Object {
+public:
+ Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~Texture();
+
+ const std::string &Type() const {
+ return type;
+ }
+
+ const std::string &FileName() const {
+ return fileName;
+ }
+
+ const std::string &RelativeFilename() const {
+ return relativeFileName;
+ }
+
+ const std::string &AlphaSource() const {
+ return alphaSource;
+ }
+
+ const Vector2 &UVTranslation() const {
+ return uvTrans;
+ }
+
+ const Vector2 &UVScaling() const {
+ return uvScaling;
+ }
+
+ // return a 4-tuple
+ const unsigned int *Crop() const {
+ return crop;
+ }
+
+ const Video *Media() const {
+ return media;
+ }
+
+private:
+ Vector2 uvTrans;
+ Vector2 uvScaling;
+
+ std::string type;
+ std::string relativeFileName;
+ std::string fileName;
+ std::string alphaSource;
+
+ unsigned int crop[4] = { 0 };
+ const Video *media = nullptr;
+};
+
+/** DOM class for layered FBX textures */
+class LayeredTexture : public Object {
+public:
+ LayeredTexture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~LayeredTexture();
+
+ // Can only be called after construction of the layered texture object due to construction flag.
+ void fillTexture(const Document &doc);
+
+ enum BlendMode {
+ BlendMode_Translucent,
+ BlendMode_Additive,
+ BlendMode_Modulate,
+ BlendMode_Modulate2,
+ BlendMode_Over,
+ BlendMode_Normal,
+ BlendMode_Dissolve,
+ BlendMode_Darken,
+ BlendMode_ColorBurn,
+ BlendMode_LinearBurn,
+ BlendMode_DarkerColor,
+ BlendMode_Lighten,
+ BlendMode_Screen,
+ BlendMode_ColorDodge,
+ BlendMode_LinearDodge,
+ BlendMode_LighterColor,
+ BlendMode_SoftLight,
+ BlendMode_HardLight,
+ BlendMode_VividLight,
+ BlendMode_LinearLight,
+ BlendMode_PinLight,
+ BlendMode_HardMix,
+ BlendMode_Difference,
+ BlendMode_Exclusion,
+ BlendMode_Subtract,
+ BlendMode_Divide,
+ BlendMode_Hue,
+ BlendMode_Saturation,
+ BlendMode_Color,
+ BlendMode_Luminosity,
+ BlendMode_Overlay,
+ BlendMode_BlendModeCount
+ };
+
+ const Texture *getTexture(int index = 0) const {
+ return textures[index];
+ }
+ int textureCount() const {
+ return static_cast<int>(textures.size());
+ }
+ BlendMode GetBlendMode() const {
+ return blendMode;
+ }
+ float Alpha() {
+ return alpha;
+ }
+
+private:
+ std::vector<const Texture *> textures;
+ BlendMode blendMode = BlendMode::BlendMode_Additive;
+ float alpha = 0;
+};
+
+typedef std::map<std::string, const Texture *> TextureMap;
+typedef std::map<std::string, const LayeredTexture *> LayeredTextureMap;
+
+/** DOM class for generic FBX videos */
+class Video : public Object {
+public:
+ Video(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~Video();
+
+ const std::string &Type() const {
+ return type;
+ }
+
+ bool IsEmbedded() const {
+ return contentLength > 0;
+ }
+
+ const std::string &FileName() const {
+ return fileName;
+ }
+
+ const std::string &RelativeFilename() const {
+ return relativeFileName;
+ }
+
+ const uint8_t *Content() const {
+ return content;
+ }
+
+ uint64_t ContentLength() const {
+ return contentLength;
+ }
+
+ uint8_t *RelinquishContent() {
+ uint8_t *ptr = content;
+ content = nullptr;
+ return ptr;
+ }
+
+ bool operator==(const Video &other) const {
+ return (
+ type == other.type && relativeFileName == other.relativeFileName && fileName == other.fileName);
+ }
+
+ bool operator<(const Video &other) const {
+ return std::tie(type, relativeFileName, fileName) < std::tie(other.type, other.relativeFileName, other.fileName);
+ }
+
+private:
+ std::string type;
+ std::string relativeFileName;
+ std::string fileName;
+
+ uint64_t contentLength = 0;
+ uint8_t *content = nullptr;
+};
+
+/** DOM class for generic FBX materials */
+class Material : public Object {
+public:
+ Material(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~Material();
+
+ const std::string &GetShadingModel() const {
+ return shading;
+ }
+
+ bool IsMultilayer() const {
+ return multilayer;
+ }
+
+ const TextureMap &Textures() const {
+ return textures;
+ }
+
+ const LayeredTextureMap &LayeredTextures() const {
+ return layeredTextures;
+ }
+
+private:
+ std::string shading;
+ bool multilayer = false;
+
+ TextureMap textures;
+ LayeredTextureMap layeredTextures;
+};
+
+// signed int keys (this can happen!)
+typedef std::vector<int64_t> KeyTimeList;
+typedef std::vector<float> KeyValueList;
+
+/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefore) */
+class AnimationCurve : public Object {
+public:
+ AnimationCurve(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+ virtual ~AnimationCurve();
+
+ /** get list of keyframe positions (time).
+ * Invariant: |GetKeys()| > 0 */
+ const KeyTimeList &GetKeys() const {
+ return keys;
+ }
+
+ /** get list of keyframe values.
+ * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/
+ const KeyValueList &GetValues() const {
+ return values;
+ }
+
+ const std::map<int64_t, float> &GetValueTimeTrack() const {
+ return keyvalues;
+ }
+
+ const std::vector<float> &GetAttributes() const {
+ return attributes;
+ }
+
+ const std::vector<unsigned int> &GetFlags() const {
+ return flags;
+ }
+
+private:
+ KeyTimeList keys;
+ KeyValueList values;
+ std::vector<float> attributes;
+ std::map<int64_t, float> keyvalues;
+ std::vector<unsigned int> flags;
+};
+
+/* Typedef for pointers for the animation handler */
+typedef std::shared_ptr<AnimationCurve> AnimationCurvePtr;
+typedef std::weak_ptr<AnimationCurve> AnimationCurveWeakPtr;
+typedef std::map<std::string, const AnimationCurve *> AnimationMap;
+
+/* Animation Curve node ptr */
+typedef std::shared_ptr<AnimationCurveNode> AnimationCurveNodePtr;
+typedef std::weak_ptr<AnimationCurveNode> AnimationCurveNodeWeakPtr;
+
+/** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */
+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. */
+ 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);
+
+ virtual ~AnimationCurveNode();
+
+ 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.*/
+ Object *Target() const {
+ return target;
+ }
+
+ Model *TargetAsModel() const {
+ return dynamic_cast<Model *>(target);
+ }
+
+ NodeAttribute *TargetAsNodeAttribute() const {
+ return dynamic_cast<NodeAttribute *>(target);
+ }
+
+ /** Property of Target() that is being animated*/
+ const std::string &TargetProperty() const {
+ return prop;
+ }
+
+private:
+ Object *target = nullptr;
+ mutable AnimationMap curves;
+ std::string prop;
+ const Document &doc;
+};
+
+typedef std::vector<const AnimationCurveNode *> AnimationCurveNodeList;
+
+typedef std::shared_ptr<AnimationLayer> AnimationLayerPtr;
+typedef std::weak_ptr<AnimationLayer> AnimationLayerWeakPtr;
+typedef std::vector<const AnimationLayer *> AnimationLayerList;
+
+/** Represents a FBX animation layer (i.e. a list of node animations) */
+class AnimationLayer : public Object {
+public:
+ AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+ 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. */
+ const AnimationCurveNodeList Nodes(const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0) const;
+
+private:
+ const Document &doc;
+};
+
+/** Represents a FBX animation stack (i.e. a list of animation layers) */
+class AnimationStack : public Object {
+public:
+ AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+ virtual ~AnimationStack();
+
+ fbx_simple_property(LocalStart, int64_t, 0L);
+ fbx_simple_property(LocalStop, int64_t, 0L);
+ fbx_simple_property(ReferenceStart, int64_t, 0L);
+ fbx_simple_property(ReferenceStop, int64_t, 0L);
+
+ const AnimationLayerList &Layers() const {
+ return layers;
+ }
+
+private:
+ AnimationLayerList layers;
+};
+
+/** DOM class for deformers */
+class Deformer : public Object {
+public:
+ Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~Deformer();
+};
+
+/** Constraints are from Maya they can help us with BoneAttachments :) **/
+class Constraint : public Object {
+public:
+ Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~Constraint();
+};
+
+typedef std::vector<float> WeightArray;
+typedef std::vector<unsigned int> WeightIndexArray;
+
+/** DOM class for BlendShapeChannel deformers */
+class BlendShapeChannel : public Deformer {
+public:
+ BlendShapeChannel(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~BlendShapeChannel();
+
+ float DeformPercent() const {
+ return percent;
+ }
+
+ const WeightArray &GetFullWeights() const {
+ return fullWeights;
+ }
+
+ const std::vector<const ShapeGeometry *> &GetShapeGeometries() const {
+ return shapeGeometries;
+ }
+
+private:
+ float percent = 0;
+ WeightArray fullWeights;
+ std::vector<const ShapeGeometry *> shapeGeometries;
+};
+
+/** DOM class for BlendShape deformers */
+class BlendShape : public Deformer {
+public:
+ BlendShape(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~BlendShape();
+
+ const std::vector<const BlendShapeChannel *> &BlendShapeChannels() const {
+ return blendShapeChannels;
+ }
+
+private:
+ std::vector<const BlendShapeChannel *> blendShapeChannels;
+};
+
+/** DOM class for skin deformer clusters (aka sub-deformers) */
+class Cluster : public Deformer {
+public:
+ Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ 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). */
+ 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). */
+ const std::vector<unsigned int> &GetIndices() const {
+ return indices;
+ }
+
+ /** */
+ const Transform3D &GetTransform() const {
+ return transform;
+ }
+
+ const Transform3D &TransformLink() const {
+ return transformLink;
+ }
+
+ const Model *TargetNode() const {
+ return node;
+ }
+
+ const Transform3D &TransformAssociateModel() const {
+ return transformAssociateModel;
+ }
+
+ bool TransformAssociateModelValid() const {
+ return valid_transformAssociateModel;
+ }
+
+ // property is not in the fbx file
+ // if the cluster has an associate model
+ // we then have an additive type
+ enum SkinLinkMode {
+ SkinLinkMode_Normalized = 0,
+ SkinLinkMode_Additive = 1
+ };
+
+ SkinLinkMode GetLinkMode() {
+ return link_mode;
+ }
+
+private:
+ std::vector<float> weights;
+ std::vector<unsigned int> indices;
+
+ Transform3D transform;
+ Transform3D transformLink;
+ Transform3D transformAssociateModel;
+ SkinLinkMode link_mode;
+ bool valid_transformAssociateModel = false;
+ const Model *node = nullptr;
+};
+
+/** DOM class for skin deformers */
+class Skin : public Deformer {
+public:
+ Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~Skin();
+
+ float DeformAccuracy() const {
+ return accuracy;
+ }
+
+ const std::vector<const Cluster *> &Clusters() const {
+ return clusters;
+ }
+
+ enum SkinType {
+ Skin_Rigid = 0,
+ Skin_Linear,
+ Skin_DualQuaternion,
+ Skin_Blend
+ };
+
+ const SkinType &GetSkinType() const {
+ return skinType;
+ }
+
+private:
+ float accuracy = 0;
+ SkinType skinType = SkinType::Skin_Linear;
+ std::vector<const Cluster *> clusters;
+};
+
+/** Represents a link between two FBX objects. */
+class Connection {
+public:
+ Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string &prop, const Document &doc);
+ ~Connection();
+
+ // note: a connection ensures that the source and dest objects exist, but
+ // not that they have DOM representations, so the return value of one of
+ // these functions can still be nullptr.
+ Object *SourceObject() const;
+ Object *DestinationObject() const;
+
+ // these, however, are always guaranteed to be valid
+ LazyObject *LazySourceObject() const;
+ 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. */
+ const std::string &PropertyName() const {
+ return prop;
+ }
+
+ uint64_t InsertionOrder() const {
+ return insertionOrder;
+ }
+
+ int CompareTo(const Connection *c) const {
+ //ai_assert(nullptr != c);
+
+ // note: can't subtract because this would overflow uint64_t
+ if (InsertionOrder() > c->InsertionOrder()) {
+ return 1;
+ } else if (InsertionOrder() < c->InsertionOrder()) {
+ return -1;
+ }
+ return 0;
+ }
+
+ bool Compare(const Connection *c) const {
+ //ai_assert(nullptr != c);
+
+ return InsertionOrder() < c->InsertionOrder();
+ }
+
+public:
+ uint64_t insertionOrder = 0;
+ const std::string prop;
+
+ uint64_t src = 0, dest = 0;
+ const Document &doc;
+};
+
+// XXX again, unique_ptr would be useful. shared_ptr is too
+// bloated since the objects have a well-defined single owner
+// during their entire lifetime (Document). FBX files have
+// up to many thousands of objects (most of which we never use),
+// so the memory overhead for them should be kept at a minimum.
+typedef std::map<uint64_t, LazyObject *> ObjectMap;
+typedef std::map<std::string, const PropertyTable *> PropertyTemplateMap;
+typedef std::multimap<uint64_t, const Connection *> ConnectionMap;
+
+/** DOM class for global document settings, a single instance per document can
+ * be accessed via Document.Globals(). */
+class FileGlobalSettings : public PropertyTable {
+public:
+ FileGlobalSettings(const Document &doc);
+ virtual ~FileGlobalSettings();
+
+ const Document &GetDocument() const {
+ return doc;
+ }
+
+ fbx_simple_property(UpAxis, int, 1);
+ fbx_simple_property(UpAxisSign, int, 1);
+ fbx_simple_property(FrontAxis, int, 2);
+ fbx_simple_property(FrontAxisSign, int, 1);
+ fbx_simple_property(CoordAxis, int, 0);
+ fbx_simple_property(CoordAxisSign, int, 1);
+ fbx_simple_property(OriginalUpAxis, int, 0);
+ fbx_simple_property(OriginalUpAxisSign, int, 1);
+ fbx_simple_property(UnitScaleFactor, float, 1);
+ fbx_simple_property(OriginalUnitScaleFactor, float, 1);
+ fbx_simple_property(AmbientColor, Vector3, Vector3(0, 0, 0));
+ fbx_simple_property(DefaultCamera, std::string, "");
+
+ enum FrameRate {
+ FrameRate_DEFAULT = 0,
+ FrameRate_120 = 1,
+ FrameRate_100 = 2,
+ FrameRate_60 = 3,
+ FrameRate_50 = 4,
+ FrameRate_48 = 5,
+ FrameRate_30 = 6,
+ FrameRate_30_DROP = 7,
+ FrameRate_NTSC_DROP_FRAME = 8,
+ FrameRate_NTSC_FULL_FRAME = 9,
+ FrameRate_PAL = 10,
+ FrameRate_CINEMA = 11,
+ FrameRate_1000 = 12,
+ FrameRate_CINEMA_ND = 13,
+ FrameRate_CUSTOM = 14,
+
+ FrameRate_MAX // end-of-enum sentinel
+ };
+
+ fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT);
+ fbx_simple_property(TimeSpanStart, uint64_t, 0L);
+ fbx_simple_property(TimeSpanStop, uint64_t, 0L);
+ fbx_simple_property(CustomFrameRate, float, -1.0f);
+
+private:
+ const Document &doc;
+};
+
+/** DOM root for a FBX file */
+class Document {
+public:
+ Document(const Parser &parser, const ImportSettings &settings);
+
+ ~Document();
+
+ LazyObject *GetObject(uint64_t id) const;
+
+ bool IsSafeToImport() const {
+ return SafeToImport;
+ }
+
+ bool IsBinary() const {
+ return parser.IsBinary();
+ }
+
+ unsigned int FBXVersion() const {
+ return fbxVersion;
+ }
+
+ const std::string &Creator() const {
+ return creator;
+ }
+
+ // elements (in this order): Year, Month, Day, Hour, Second, Millisecond
+ const unsigned int *CreationTimeStamp() const {
+ return creationTimeStamp;
+ }
+
+ const FileGlobalSettings *GlobalSettingsPtr() const {
+ return globals.get();
+ }
+
+ const PropertyTable &GetMetadataProperties() const {
+ return metadata_properties;
+ }
+
+ const PropertyTemplateMap &Templates() const {
+ return templates;
+ }
+
+ const ObjectMap &Objects() const {
+ return objects;
+ }
+
+ const ImportSettings &Settings() const {
+ return settings;
+ }
+
+ const ConnectionMap &ConnectionsBySource() const {
+ return src_connections;
+ }
+
+ const ConnectionMap &ConnectionsByDestination() const {
+ return dest_connections;
+ }
+
+ // note: the implicit rule in all DOM classes is to always resolve
+ // from destination to source (since the FBX object hierarchy is,
+ // with very few exceptions, a DAG, this avoids cycles). In all
+ // cases that may involve back-facing edges in the object graph,
+ // use LazyObject::IsBeingConstructed() to check.
+
+ std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source) const;
+ std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest) const;
+
+ std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source, const char *classname) const;
+ std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest, const char *classname) const;
+
+ std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source,
+ const char *const *classnames, size_t count) const;
+ std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest,
+ const char *const *classnames,
+ size_t count) const;
+
+ const std::vector<const AnimationStack *> &AnimationStacks() const;
+ const std::vector<uint64_t> &GetAnimationStackIDs() const {
+ return animationStacks;
+ }
+
+ const std::vector<uint64_t> &GetConstraintStackIDs() const {
+ return constraints;
+ }
+
+ const std::vector<uint64_t> &GetBindPoseIDs() const {
+ return bind_poses;
+ };
+
+ const std::vector<uint64_t> &GetMaterialIDs() const {
+ return materials;
+ };
+
+ const std::vector<uint64_t> &GetSkinIDs() const {
+ return skins;
+ }
+
+private:
+ std::vector<const Connection *> GetConnectionsSequenced(uint64_t id, const ConnectionMap &) const;
+ std::vector<const Connection *> GetConnectionsSequenced(uint64_t id, bool is_src,
+ const ConnectionMap &,
+ const char *const *classnames,
+ size_t count) const;
+ bool ReadHeader();
+ void ReadObjects();
+ void ReadPropertyTemplates();
+ void ReadConnections();
+ void ReadGlobalSettings();
+
+private:
+ const ImportSettings &settings;
+
+ ObjectMap objects;
+ const Parser &parser;
+ bool SafeToImport = false;
+
+ PropertyTemplateMap templates;
+ ConnectionMap src_connections;
+ ConnectionMap dest_connections;
+
+ unsigned int fbxVersion = 0;
+ std::string creator;
+ unsigned int creationTimeStamp[7] = { 0 };
+
+ std::vector<uint64_t> animationStacks;
+ std::vector<uint64_t> bind_poses;
+ // constraints aren't in the tree / at least they are not easy to access.
+ std::vector<uint64_t> constraints;
+ std::vector<uint64_t> materials;
+ std::vector<uint64_t> skins;
+ mutable std::vector<const AnimationStack *> animationStacksResolved;
+ PropertyTable metadata_properties;
+ std::shared_ptr<FileGlobalSettings> globals = nullptr;
+};
+} // namespace FBXDocParser
+
+namespace std {
+template <>
+struct hash<const FBXDocParser::Video> {
+ std::size_t operator()(const FBXDocParser::Video &video) const {
+ using std::hash;
+ using std::size_t;
+ using std::string;
+
+ size_t res = 17;
+ res = res * 31 + hash<string>()(video.Name());
+ res = res * 31 + hash<string>()(video.RelativeFilename());
+ res = res * 31 + hash<string>()(video.Type());
+
+ return res;
+ }
+};
+} // namespace std
+
+#endif // FBX_DOCUMENT_H
diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.cpp b/modules/fbx/fbx_parser/FBXDocumentUtil.cpp
new file mode 100644
index 0000000000..4a33024969
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXDocumentUtil.cpp
@@ -0,0 +1,141 @@
+/*************************************************************************/
+/* FBXDocumentUtil.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXDocumentUtil.cpp
+ * @brief Implementation of the FBX DOM utility functions declared in FBXDocumentUtil.h
+ */
+
+#include "FBXDocumentUtil.h"
+#include "FBXDocument.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+#include "FBXUtil.h"
+#include "core/string/print_string.h"
+
+namespace FBXDocParser {
+namespace Util {
+
+void DOMError(const std::string &message) {
+ print_error("[FBX-DOM]" + String(message.c_str()));
+}
+
+void DOMError(const std::string &message, const Token *token) {
+ print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMError(const std::string &message, const std::shared_ptr<Token> token) {
+ print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMError(const std::string &message, const Element *element /*= nullptr*/) {
+ if (element) {
+ DOMError(message, element->KeyToken());
+ }
+ print_error("[FBX-DOM] " + String(message.c_str()));
+}
+
+void DOMError(const std::string &message, const std::shared_ptr<Element> element /*= nullptr*/) {
+ if (element) {
+ DOMError(message, element->KeyToken());
+ }
+ print_error("[FBX-DOM] " + String(message.c_str()));
+}
+
+void DOMWarning(const std::string &message) {
+ print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
+}
+
+void DOMWarning(const std::string &message, const Token *token) {
+ print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMWarning(const std::string &message, const Element *element /*= nullptr*/) {
+ if (element) {
+ DOMWarning(message, element->KeyToken());
+ return;
+ }
+ print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
+}
+
+void DOMWarning(const std::string &message, const std::shared_ptr<Token> token) {
+ print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMWarning(const std::string &message, const std::shared_ptr<Element> element /*= nullptr*/) {
+ if (element) {
+ DOMWarning(message, element->KeyToken());
+ return;
+ }
+ print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
+}
+
+} // namespace Util
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.h b/modules/fbx/fbx_parser/FBXDocumentUtil.h
new file mode 100644
index 0000000000..0489ce10ce
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXDocumentUtil.h
@@ -0,0 +1,134 @@
+/*************************************************************************/
+/* FBXDocumentUtil.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2012, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXDocumentUtil.h
+ * @brief FBX internal utilities used by the DOM reading code
+ */
+#ifndef FBX_DOCUMENT_UTIL_H
+#define FBX_DOCUMENT_UTIL_H
+
+#include "FBXDocument.h"
+#include <memory>
+#include <string>
+
+struct Token;
+struct Element;
+
+namespace FBXDocParser {
+namespace Util {
+
+// Parser errors
+void DOMError(const std::string &message);
+void DOMError(const std::string &message, const Token *token);
+void DOMError(const std::string &message, const Element *element);
+void DOMError(const std::string &message, const std::shared_ptr<Element> element);
+void DOMError(const std::string &message, const std::shared_ptr<Token> token);
+
+// Parser warnings
+void DOMWarning(const std::string &message);
+void DOMWarning(const std::string &message, const Token *token);
+void DOMWarning(const std::string &message, const Element *element);
+void DOMWarning(const std::string &message, const std::shared_ptr<Token> token);
+void DOMWarning(const std::string &message, const std::shared_ptr<Element> element);
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+const T *ProcessSimpleConnection(const Connection &con,
+ bool is_object_property_conn,
+ const char *name,
+ const ElementPtr element,
+ 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",
+ 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",
+ element);
+ return nullptr;
+ }
+
+ if (is_object_property_conn && propNameOut) {
+ // note: this is ok, the return value of PropertyValue() is guaranteed to
+ // remain valid and unchanged as long as the document exists.
+ *propNameOut = con.PropertyName().c_str();
+ }
+
+ // Cast Object to AnimationPlayer for example using safe functions, which return nullptr etc
+ Object *ob = con.SourceObject();
+ ERR_FAIL_COND_V_MSG(!ob, nullptr, "Failed to load object from SourceObject ptr");
+ return dynamic_cast<const T *>(ob);
+}
+} // namespace Util
+} // namespace FBXDocParser
+
+#endif // FBX_DOCUMENT_UTIL_H
diff --git a/modules/fbx/fbx_parser/FBXImportSettings.h b/modules/fbx/fbx_parser/FBXImportSettings.h
new file mode 100644
index 0000000000..bc22386957
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXImportSettings.h
@@ -0,0 +1,162 @@
+/*************************************************************************/
+/* FBXImportSettings.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXImportSettings.h
+ * @brief FBX importer runtime configuration
+ */
+#ifndef FBX_IMPORT_SETTINGS_H
+#define FBX_IMPORT_SETTINGS_H
+
+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.*/
+ 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.*/
+ 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.*/
+ bool readAllMaterials = true;
+
+ /** import materials (true) or skip them and assign a default
+ * material.*/
+ bool readMaterials = true;
+
+ /** import embedded textures?*/
+ bool readTextures = true;
+
+ /** import cameras?*/
+ bool readCameras = true;
+
+ /** import light sources?*/
+ bool readLights = true;
+
+ /** import animations (i.e. animation curves, the node
+ * 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
+ **/
+ bool preservePivots = true;
+
+ /** do not import animation curves that specify a constant
+ * values matching the corresponding node transformation.*/
+ bool optimizeEmptyAnimationCurves = true;
+
+ /** use legacy naming for embedded textures eg: (*0, *1, *2).*/
+ bool useLegacyEmbeddedTextureNaming = false;
+
+ /** Empty bones shall be removed.*/
+ bool removeEmptyBones = true;
+
+ /** Set to true to perform a conversion from cm to meter after
+ * the import.*/
+ bool convertToMeters = false;
+};
+} // namespace FBXDocParser
+
+#endif // FBX_IMPORT_SETTINGS_H
diff --git a/modules/fbx/fbx_parser/FBXMaterial.cpp b/modules/fbx/fbx_parser/FBXMaterial.cpp
new file mode 100644
index 0000000000..bf8922267e
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXMaterial.cpp
@@ -0,0 +1,388 @@
+/*************************************************************************/
+/* FBXMaterial.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXMaterial.cpp
+ * @brief Assimp::FBX::Material and Assimp::FBX::Texture implementation
+ */
+
+#include "ByteSwapper.h"
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXImportSettings.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+
+#include "FBXUtil.h"
+#include <algorithm> // std::transform
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Material::Material(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ const ElementPtr ShadingModel = sc->GetElement("ShadingModel");
+ const ElementPtr MultiLayer = sc->GetElement("MultiLayer");
+
+ if (MultiLayer) {
+ multilayer = !!ParseTokenAsInt(GetRequiredToken(MultiLayer, 0));
+ }
+
+ if (ShadingModel) {
+ shading = ParseTokenAsString(GetRequiredToken(ShadingModel, 0));
+ } else {
+ DOMWarning("shading mode not specified, assuming phong", element);
+ shading = "phong";
+ }
+
+ std::string templateName;
+
+ if (shading == "phong") {
+ templateName = "Material.Phong";
+ } else if (shading == "lambert") {
+ templateName = "Material.Lambert";
+ } else if (shading == "unknown") {
+ templateName = "Material.StingRay";
+ } else {
+ DOMWarning("shading mode not recognized: " + shading, element);
+ }
+
+ // resolve texture links
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
+ for (const Connection *con : conns) {
+ // texture link to properties, not objects
+ if (!con->PropertyName().length()) {
+ continue;
+ }
+
+ Object *ob = con->SourceObject();
+ if (!ob) {
+ DOMWarning("failed to read source object for texture link, ignoring", element);
+ continue;
+ }
+
+ const Texture *tex = dynamic_cast<const Texture *>(ob);
+ if (!tex) {
+ LayeredTexture *layeredTexture = dynamic_cast<LayeredTexture *>(ob);
+
+ if (!layeredTexture) {
+ DOMWarning("source object for texture link is not a texture or layered texture, ignoring", element);
+ continue;
+ }
+
+ const std::string &prop = con->PropertyName();
+ if (layeredTextures.find(prop) != layeredTextures.end()) {
+ DOMWarning("duplicate layered texture link: " + prop, element);
+ }
+
+ layeredTextures[prop] = layeredTexture;
+ layeredTexture->fillTexture(doc);
+ } else {
+ const std::string &prop = con->PropertyName();
+ if (textures.find(prop) != textures.end()) {
+ DOMWarning("duplicate texture link: " + prop, element);
+ }
+
+ textures[prop] = tex;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Material::~Material() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Texture::Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name), uvScaling(1.0f, 1.0f) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ const ElementPtr Type = sc->GetElement("Type");
+ const ElementPtr FileName = sc->GetElement("FileName");
+ const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename");
+ const ElementPtr ModelUVTranslation = sc->GetElement("ModelUVTranslation");
+ const ElementPtr ModelUVScaling = sc->GetElement("ModelUVScaling");
+ const ElementPtr Texture_Alpha_Source = sc->GetElement("Texture_Alpha_Source");
+ const ElementPtr Cropping = sc->GetElement("Cropping");
+
+ if (Type) {
+ type = ParseTokenAsString(GetRequiredToken(Type, 0));
+ }
+
+ if (FileName) {
+ fileName = ParseTokenAsString(GetRequiredToken(FileName, 0));
+ }
+
+ if (RelativeFilename) {
+ relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0));
+ }
+
+ if (ModelUVTranslation) {
+ uvTrans = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 0)),
+ ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 1)));
+ }
+
+ if (ModelUVScaling) {
+ uvScaling = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 0)),
+ ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 1)));
+ }
+
+ if (Cropping) {
+ crop[0] = ParseTokenAsInt(GetRequiredToken(Cropping, 0));
+ crop[1] = ParseTokenAsInt(GetRequiredToken(Cropping, 1));
+ crop[2] = ParseTokenAsInt(GetRequiredToken(Cropping, 2));
+ crop[3] = ParseTokenAsInt(GetRequiredToken(Cropping, 3));
+ } else {
+ // vc8 doesn't support the crop() syntax in initialization lists
+ // (and vc9 WARNS about the new (i.e. compliant) behaviour).
+ crop[0] = crop[1] = crop[2] = crop[3] = 0;
+ }
+
+ if (Texture_Alpha_Source) {
+ alphaSource = ParseTokenAsString(GetRequiredToken(Texture_Alpha_Source, 0));
+ }
+
+ // 3DS Max and FBX SDK use "Scaling" and "Translation" instead of "ModelUVScaling" and "ModelUVTranslation". Use these properties if available.
+ bool ok = true;
+ const Vector3 &scaling = PropertyGet<Vector3>(this, "Scaling", ok);
+ if (ok) {
+ uvScaling.x = scaling.x;
+ uvScaling.y = scaling.y;
+ }
+
+ const Vector3 &trans = PropertyGet<Vector3>(this, "Translation", ok);
+ if (ok) {
+ uvTrans.x = trans.x;
+ uvTrans.y = trans.y;
+ }
+
+ // resolve video links
+ if (doc.Settings().readTextures) {
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
+ for (const Connection *con : conns) {
+ const Object *const ob = con->SourceObject();
+ if (!ob) {
+ DOMWarning("failed to read source object for texture link, ignoring", element);
+ continue;
+ }
+
+ const Video *const video = dynamic_cast<const Video *>(ob);
+ if (video) {
+ media = video;
+ }
+ }
+ }
+}
+
+Texture::~Texture() {
+}
+
+LayeredTexture::LayeredTexture(uint64_t id, const ElementPtr element, const Document & /*doc*/, const std::string &name) :
+ Object(id, element, name), blendMode(BlendMode_Modulate), alpha(1) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ ElementPtr BlendModes = sc->GetElement("BlendModes");
+ ElementPtr Alphas = sc->GetElement("Alphas");
+
+ if (BlendModes != nullptr) {
+ blendMode = (BlendMode)ParseTokenAsInt(GetRequiredToken(BlendModes, 0));
+ }
+ if (Alphas != nullptr) {
+ alpha = ParseTokenAsFloat(GetRequiredToken(Alphas, 0));
+ }
+}
+
+LayeredTexture::~LayeredTexture() {
+}
+
+void LayeredTexture::fillTexture(const Document &doc) {
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
+ for (size_t i = 0; i < conns.size(); ++i) {
+ const Connection *con = conns.at(i);
+
+ const Object *const ob = con->SourceObject();
+ if (!ob) {
+ DOMWarning("failed to read source object for texture link, ignoring", element);
+ continue;
+ }
+
+ const Texture *const tex = dynamic_cast<const Texture *>(ob);
+
+ textures.push_back(tex);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Video::Video(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ const ElementPtr Type = sc->GetElement("Type");
+ // File Version 7500 Crashes if this is not checked fully.
+ // As of writing this comment 7700 exists, in August 2020
+ ElementPtr FileName = nullptr;
+ if (HasElement(sc, "Filename")) {
+ FileName = (ElementPtr)sc->GetElement("Filename");
+ } else if (HasElement(sc, "FileName")) {
+ FileName = (ElementPtr)sc->GetElement("FileName");
+ } else {
+ print_error("file has invalid video material returning...");
+ return;
+ }
+ const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename");
+ const ElementPtr Content = sc->GetElement("Content");
+
+ if (Type) {
+ type = ParseTokenAsString(GetRequiredToken(Type, 0));
+ }
+
+ if (FileName) {
+ fileName = ParseTokenAsString(GetRequiredToken(FileName, 0));
+ }
+
+ if (RelativeFilename) {
+ relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0));
+ }
+
+ if (Content && !Content->Tokens().empty()) {
+ //this field is omitted when the embedded texture is already loaded, let's ignore if it's not found
+ try {
+ const Token *token = GetRequiredToken(Content, 0);
+ const char *data = token->begin();
+ if (!token->IsBinary()) {
+ if (*data != '"') {
+ DOMError("embedded content is not surrounded by quotation marks", element);
+ } else {
+ size_t targetLength = 0;
+ const size_t numTokens = Content->Tokens().size();
+ // First time compute size (it could be large like 64Gb and it is good to allocate it once)
+ for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) {
+ const Token *dataToken = GetRequiredToken(Content, tokenIdx);
+ size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes
+ const char *base64data = dataToken->begin() + 1;
+ const size_t outLength = Util::ComputeDecodedSizeBase64(base64data, tokenLength);
+ if (outLength == 0) {
+ DOMError("Corrupted embedded content found", element);
+ }
+ targetLength += outLength;
+ }
+ if (targetLength == 0) {
+ DOMError("Corrupted embedded content found", element);
+ } else {
+ content = new uint8_t[targetLength];
+ contentLength = static_cast<uint64_t>(targetLength);
+ size_t dst_offset = 0;
+ for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) {
+ const Token *dataToken = GetRequiredToken(Content, tokenIdx);
+ ERR_FAIL_COND(!dataToken);
+ size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes
+ const char *base64data = dataToken->begin() + 1;
+ dst_offset += Util::DecodeBase64(base64data, tokenLength, content + dst_offset, targetLength - dst_offset);
+ }
+ if (targetLength != dst_offset) {
+ delete[] content;
+ contentLength = 0;
+ DOMError("Corrupted embedded content found", element);
+ }
+ }
+ }
+ } else if (static_cast<size_t>(token->end() - data) < 5) {
+ DOMError("binary data array is too short, need five (5) bytes for type signature and element count", element);
+ } else if (*data != 'R') {
+ DOMWarning("video content is not raw binary data, ignoring", element);
+ } else {
+ // read number of elements
+ uint32_t len = 0;
+ ::memcpy(&len, data + 1, sizeof(len));
+ AI_SWAP4(len);
+
+ contentLength = len;
+
+ content = new uint8_t[len];
+ ::memcpy(content, data + 5, len);
+ }
+ } catch (...) {
+ // //we don't need the content data for contents that has already been loaded
+ // ASSIMP_LOG_VERBOSE_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ",
+ // runtimeError.what());
+ }
+ }
+}
+
+Video::~Video() {
+ if (content) {
+ delete[] content;
+ }
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXMeshGeometry.cpp b/modules/fbx/fbx_parser/FBXMeshGeometry.cpp
new file mode 100644
index 0000000000..2cc25a0690
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXMeshGeometry.cpp
@@ -0,0 +1,485 @@
+/*************************************************************************/
+/* FBXMeshGeometry.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXMeshGeometry.cpp
+ * @brief Assimp::FBX::MeshGeometry implementation
+ */
+
+#include <functional>
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXImportSettings.h"
+#include "FBXMeshGeometry.h"
+#include "core/math/vector3.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Geometry::Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Object(id, element, name) {
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
+ for (const Connection *con : conns) {
+ const Skin *sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
+ if (sk) {
+ skin = sk;
+ }
+ const BlendShape *bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry",
+ element);
+ if (bsp) {
+ blendShapes.push_back(bsp);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Geometry::~Geometry() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<const BlendShape *> &Geometry::get_blend_shapes() const {
+ return blendShapes;
+}
+
+// ------------------------------------------------------------------------------------------------
+const Skin *Geometry::DeformerSkin() const {
+ return skin;
+}
+
+// ------------------------------------------------------------------------------------------------
+MeshGeometry::MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Geometry(id, element, name, doc) {
+ print_verbose("mesh name: " + String(name.c_str()));
+
+ ScopePtr sc = element->Compound();
+ ERR_FAIL_COND_MSG(sc == nullptr, "failed to read geometry, prevented crash");
+ ERR_FAIL_COND_MSG(!HasElement(sc, "Vertices"), "Detected mesh with no vertices, didn't populate the mesh");
+
+ // must have Mesh elements:
+ const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element);
+ const ElementPtr PolygonVertexIndex = GetRequiredElement(sc, "PolygonVertexIndex", element);
+
+ if (HasElement(sc, "Edges")) {
+ const ElementPtr element_edges = GetRequiredElement(sc, "Edges", element);
+ ParseVectorDataArray(m_edges, element_edges);
+ }
+
+ // read mesh data into arrays
+ ParseVectorDataArray(m_vertices, Vertices);
+ ParseVectorDataArray(m_face_indices, PolygonVertexIndex);
+
+ ERR_FAIL_COND_MSG(m_vertices.empty(), "mesh with no vertices in FBX file, did you mean to delete it?");
+ ERR_FAIL_COND_MSG(m_face_indices.empty(), "mesh has no faces, was this intended?");
+
+ // Retrieve layer elements, for all of the mesh
+ const ElementCollection &Layer = sc->GetCollection("Layer");
+
+ // Store all layers
+ std::vector<std::tuple<int, std::string>> valid_layers;
+
+ // now read the sub mesh information from the geometry (normals, uvs, etc)
+ for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) {
+ const ScopePtr layer = GetRequiredScope(it->second);
+ const ElementCollection &LayerElement = layer->GetCollection("LayerElement");
+ for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) {
+ std::string layer_name = eit->first;
+ ElementPtr element_layer = eit->second;
+ const ScopePtr layer_element = GetRequiredScope(element_layer);
+
+ // Actual usable 'type' LayerElementUV, LayerElementNormal, etc
+ const ElementPtr Type = GetRequiredElement(layer_element, "Type");
+ const ElementPtr TypedIndex = GetRequiredElement(layer_element, "TypedIndex");
+ const std::string &type = ParseTokenAsString(GetRequiredToken(Type, 0));
+ const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex, 0));
+
+ // we only need the layer name and the typed index.
+ valid_layers.push_back(std::tuple<int, std::string>(typedIndex, type));
+ }
+ }
+
+ // get object / mesh directly from the FBX by the element ID.
+ const ScopePtr top = GetRequiredScope(element);
+
+ // iterate over all layers for the mesh (uvs, normals, smoothing groups, colors, etc)
+ for (size_t x = 0; x < valid_layers.size(); x++) {
+ const int layer_id = std::get<0>(valid_layers[x]);
+ const std::string &layer_type_name = std::get<1>(valid_layers[x]);
+
+ // Get collection of elements from the XLayerMap (example: LayerElementUV)
+ // this must contain our proper elements.
+
+ // This is stupid, because it means we select them ALL not just the one we want.
+ // but it's fine we can match by id.
+
+ const ElementCollection &candidates = top->GetCollection(layer_type_name);
+
+ ElementMap::const_iterator iter;
+ for (iter = candidates.first; iter != candidates.second; ++iter) {
+ const ScopePtr layer_scope = GetRequiredScope(iter->second);
+ TokenPtr layer_token = GetRequiredToken(iter->second, 0);
+ const int index = ParseTokenAsInt(layer_token);
+
+ ERR_FAIL_COND_MSG(layer_scope == nullptr, "prevented crash, layer scope is invalid");
+
+ if (index == layer_id) {
+ const std::string &MappingInformationType = ParseTokenAsString(GetRequiredToken(
+ GetRequiredElement(layer_scope, "MappingInformationType"), 0));
+
+ const std::string &ReferenceInformationType = ParseTokenAsString(GetRequiredToken(
+ GetRequiredElement(layer_scope, "ReferenceInformationType"), 0));
+
+ if (layer_type_name == "LayerElementUV") {
+ if (index == 0) {
+ m_uv_0 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV");
+ } else if (index == 1) {
+ m_uv_1 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV");
+ }
+ } else if (layer_type_name == "LayerElementMaterial") {
+ m_material_allocation_ids = resolve_vertex_data_array<int>(layer_scope, MappingInformationType, ReferenceInformationType, "Materials");
+ } else if (layer_type_name == "LayerElementNormal") {
+ m_normals = resolve_vertex_data_array<Vector3>(layer_scope, MappingInformationType, ReferenceInformationType, "Normals");
+ } else if (layer_type_name == "LayerElementColor") {
+ m_colors = resolve_vertex_data_array<Color>(layer_scope, MappingInformationType, ReferenceInformationType, "Colors", "ColorIndex");
+ // NOTE: this is a useful sanity check to ensure you're getting any color data which is not default.
+ // const Color first_color_check = m_colors.data[0];
+ // bool colors_are_all_the_same = true;
+ // size_t i = 1;
+ // for(i = 1; i < m_colors.data.size(); i++)
+ // {
+ // const Color current_color = m_colors.data[i];
+ // if(current_color.is_equal_approx(first_color_check))
+ // {
+ // continue;
+ // }
+ // else
+ // {
+ // colors_are_all_the_same = false;
+ // break;
+ // }
+ // }
+ //
+ // if(colors_are_all_the_same)
+ // {
+ // print_error("Color serialisation is not working for vertex colors some should be different in the test asset.");
+ // }
+ // else
+ // {
+ // print_verbose("Color array has unique colors at index: " + itos(i));
+ // }
+ }
+ }
+ }
+ }
+
+ print_verbose("Mesh statistics \nuv_0: " + m_uv_0.debug_info() + "\nuv_1: " + m_uv_1.debug_info() + "\nvertices: " + itos(m_vertices.size()));
+
+ // Compose the edge of the mesh.
+ // You can see how the edges are stored into the FBX here: https://gist.github.com/AndreaCatania/da81840f5aa3b2feedf189e26c5a87e6
+ for (size_t i = 0; i < m_edges.size(); i += 1) {
+ ERR_FAIL_INDEX_MSG((size_t)m_edges[i], m_face_indices.size(), "The edge is pointing to a weird location in the face indices. The FBX is corrupted.");
+ int polygon_vertex_0 = m_face_indices[m_edges[i]];
+ int polygon_vertex_1;
+ if (polygon_vertex_0 < 0) {
+ // The polygon_vertex_0 points to the end of a polygon, so it's
+ // connected with the beginning of polygon in the edge list.
+
+ // Fist invert the vertex.
+ polygon_vertex_0 = ~polygon_vertex_0;
+
+ // Search the start vertex of the polygon.
+ // Iterate from the polygon_vertex_index backward till the start of
+ // the polygon is found.
+ ERR_FAIL_COND_MSG(m_edges[i] - 1 < 0, "The polygon is not yet started and we already need the final vertex. This FBX is corrupted.");
+ bool found_it = false;
+ for (int x = m_edges[i] - 1; x >= 0; x -= 1) {
+ if (x == 0) {
+ // This for sure is the start.
+ polygon_vertex_1 = m_face_indices[x];
+ found_it = true;
+ break;
+ } else if (m_face_indices[x] < 0) {
+ // This is the end of the previous polygon, so the next is
+ // the start of the polygon we need.
+ polygon_vertex_1 = m_face_indices[x + 1];
+ found_it = true;
+ break;
+ }
+ }
+ // As the algorithm above, this check is useless. Because the first
+ // ever vertex is always considered the beginning of a polygon.
+ ERR_FAIL_COND_MSG(found_it == false, "Was not possible to find the first vertex of this polygon. FBX file is corrupted.");
+
+ } else {
+ ERR_FAIL_INDEX_MSG((size_t)(m_edges[i] + 1), m_face_indices.size(), "FBX The other FBX edge seems to point to an invalid vertices. This FBX file is corrupted.");
+ // Take the next vertex
+ polygon_vertex_1 = m_face_indices[m_edges[i] + 1];
+ }
+
+ if (polygon_vertex_1 < 0) {
+ // We don't care if the `polygon_vertex_1` is the end of the polygon,
+ // for `polygon_vertex_1` so we can just invert it.
+ polygon_vertex_1 = ~polygon_vertex_1;
+ }
+
+ ERR_FAIL_COND_MSG(polygon_vertex_0 == polygon_vertex_1, "The vertices of this edge can't be the same, Is this a point???. This FBX file is corrupted.");
+
+ // Just create the edge.
+ edge_map.push_back({ polygon_vertex_0, polygon_vertex_1 });
+ }
+}
+
+MeshGeometry::~MeshGeometry() {
+ // empty
+}
+
+const std::vector<Vector3> &MeshGeometry::get_vertices() const {
+ return m_vertices;
+}
+
+const std::vector<MeshGeometry::Edge> &MeshGeometry::get_edge_map() const {
+ return edge_map;
+}
+
+const std::vector<int> &MeshGeometry::get_polygon_indices() const {
+ return m_face_indices;
+}
+
+const std::vector<int> &MeshGeometry::get_edges() const {
+ return m_edges;
+}
+
+const MeshGeometry::MappingData<Vector3> &MeshGeometry::get_normals() const {
+ return m_normals;
+}
+
+const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_0() const {
+ //print_verbose("get uv_0 " + m_uv_0.debug_info() );
+ return m_uv_0;
+}
+
+const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_1() const {
+ //print_verbose("get uv_1 " + m_uv_1.debug_info() );
+ return m_uv_1;
+}
+
+const MeshGeometry::MappingData<Color> &MeshGeometry::get_colors() const {
+ return m_colors;
+}
+
+const MeshGeometry::MappingData<int> &MeshGeometry::get_material_allocation_id() const {
+ return m_material_allocation_ids;
+}
+
+int MeshGeometry::get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b) {
+ for (size_t i = 0; i < p_map.size(); i += 1) {
+ if ((p_map[i].vertex_0 == p_vertex_a && p_map[i].vertex_1 == p_vertex_b) || (p_map[i].vertex_1 == p_vertex_a && p_map[i].vertex_0 == p_vertex_b)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+MeshGeometry::Edge MeshGeometry::get_edge(const std::vector<Edge> &p_map, int p_id) {
+ ERR_FAIL_INDEX_V_MSG((size_t)p_id, p_map.size(), Edge({ -1, -1 }), "ID not found.");
+ return p_map[p_id];
+}
+
+template <class T>
+MeshGeometry::MappingData<T> MeshGeometry::resolve_vertex_data_array(
+ const ScopePtr source,
+ const std::string &MappingInformationType,
+ const std::string &ReferenceInformationType,
+ const std::string &dataElementName,
+ const std::string &indexOverride) {
+ ERR_FAIL_COND_V_MSG(source == nullptr, MappingData<T>(), "Invalid scope operator preventing memory corruption");
+
+ // UVIndex, MaterialIndex, NormalIndex, etc..
+ std::string indexDataElementName;
+
+ if (indexOverride != "") {
+ // Colors should become ColorIndex
+ indexDataElementName = indexOverride;
+ } else {
+ // Some indexes will exist.
+ indexDataElementName = dataElementName + "Index";
+ }
+
+ // goal: expand everything to be per vertex
+
+ ReferenceType l_ref_type = ReferenceType::direct;
+
+ // Read the reference type into the enumeration
+ if (ReferenceInformationType == "IndexToDirect") {
+ l_ref_type = ReferenceType::index_to_direct;
+ } else if (ReferenceInformationType == "Index") {
+ // set non legacy index to direct mapping
+ l_ref_type = ReferenceType::index;
+ } else if (ReferenceInformationType == "Direct") {
+ l_ref_type = ReferenceType::direct;
+ } else {
+ ERR_FAIL_V_MSG(MappingData<T>(), "invalid reference type has the FBX format changed?");
+ }
+
+ MapType l_map_type = MapType::none;
+
+ if (MappingInformationType == "None") {
+ l_map_type = MapType::none;
+ } else if (MappingInformationType == "ByVertice") {
+ l_map_type = MapType::vertex;
+ } else if (MappingInformationType == "ByPolygonVertex") {
+ l_map_type = MapType::polygon_vertex;
+ } else if (MappingInformationType == "ByPolygon") {
+ l_map_type = MapType::polygon;
+ } else if (MappingInformationType == "ByEdge") {
+ l_map_type = MapType::edge;
+ } else if (MappingInformationType == "AllSame") {
+ l_map_type = MapType::all_the_same;
+ } else {
+ print_error("invalid mapping type: " + String(MappingInformationType.c_str()));
+ }
+
+ // create mapping data
+ MeshGeometry::MappingData<T> tempData;
+ tempData.map_type = l_map_type;
+ tempData.ref_type = l_ref_type;
+
+ // parse data into array
+ ParseVectorDataArray(tempData.data, GetRequiredElement(source, dataElementName));
+
+ // index array won't always exist
+ const ElementPtr element = GetOptionalElement(source, indexDataElementName);
+ if (element) {
+ ParseVectorDataArray(tempData.index, element);
+ }
+
+ return tempData;
+}
+// ------------------------------------------------------------------------------------------------
+ShapeGeometry::ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Geometry(id, element, name, doc) {
+ const ScopePtr sc = element->Compound();
+ if (nullptr == sc) {
+ DOMError("failed to read Geometry object (class: Shape), no data scope found");
+ }
+ const ElementPtr Indexes = GetRequiredElement(sc, "Indexes", element);
+ const ElementPtr Normals = GetRequiredElement(sc, "Normals", element);
+ const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element);
+ ParseVectorDataArray(m_indices, Indexes);
+ ParseVectorDataArray(m_vertices, Vertices);
+ ParseVectorDataArray(m_normals, Normals);
+}
+
+// ------------------------------------------------------------------------------------------------
+ShapeGeometry::~ShapeGeometry() {
+ // empty
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<Vector3> &ShapeGeometry::GetVertices() const {
+ return m_vertices;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<Vector3> &ShapeGeometry::GetNormals() const {
+ return m_normals;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<unsigned int> &ShapeGeometry::GetIndices() const {
+ return m_indices;
+}
+// ------------------------------------------------------------------------------------------------
+LineGeometry::LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Geometry(id, element, name, doc) {
+ const ScopePtr sc = element->Compound();
+ if (!sc) {
+ DOMError("failed to read Geometry object (class: Line), no data scope found");
+ }
+ const ElementPtr Points = GetRequiredElement(sc, "Points", element);
+ const ElementPtr PointsIndex = GetRequiredElement(sc, "PointsIndex", element);
+ ParseVectorDataArray(m_vertices, Points);
+ ParseVectorDataArray(m_indices, PointsIndex);
+}
+
+// ------------------------------------------------------------------------------------------------
+LineGeometry::~LineGeometry() {
+ // empty
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<Vector3> &LineGeometry::GetVertices() const {
+ return m_vertices;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<int> &LineGeometry::GetIndices() const {
+ return m_indices;
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXMeshGeometry.h b/modules/fbx/fbx_parser/FBXMeshGeometry.h
new file mode 100644
index 0000000000..26fc1914d1
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXMeshGeometry.h
@@ -0,0 +1,263 @@
+/*************************************************************************/
+/* FBXMeshGeometry.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef FBX_MESH_GEOMETRY_H
+#define FBX_MESH_GEOMETRY_H
+
+#include "core/math/color.h"
+#include "core/math/vector2.h"
+#include "core/math/vector3.h"
+#include "core/templates/vector.h"
+
+#include "FBXDocument.h"
+#include "FBXParser.h"
+
+#include <iostream>
+
+#define AI_MAX_NUMBER_OF_TEXTURECOORDS 4
+#define AI_MAX_NUMBER_OF_COLOR_SETS 8
+
+namespace FBXDocParser {
+
+/*
+ * DOM base class for all kinds of FBX geometry
+ */
+class Geometry : public Object {
+public:
+ Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+ virtual ~Geometry();
+
+ /** Get the Skin attached to this geometry or nullptr */
+ const Skin *DeformerSkin() const;
+
+ const std::vector<const BlendShape *> &get_blend_shapes() const;
+
+ size_t get_blend_shape_count() const {
+ return blendShapes.size();
+ }
+
+private:
+ const Skin *skin = nullptr;
+ std::vector<const BlendShape *> blendShapes;
+};
+
+typedef std::vector<int> MatIndexArray;
+
+/// Map Geometry stores the FBX file information.
+///
+/// # FBX doc.
+/// ## Reference type declared:
+/// - Direct (directly related to the mapping information type)
+/// - IndexToDirect (Map with key value, meaning depends on the MappingInformationType)
+///
+/// ## Map Type:
+/// * None The mapping is undetermined.
+/// * ByVertex There will be one mapping coordinate for each surface control point/vertex (ControlPoint is a vertex).
+/// * If you have direct reference type vertices[x]
+/// * If you have IndexToDirect reference type the UV
+/// * ByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part. (Sorted by polygon, referencing vertex)
+/// * ByPolygon There can be only one mapping coordinate for the whole polygon.
+/// * One mapping per polygon polygon x has this normal x
+/// * For each vertex of the polygon then set the normal to x
+/// * ByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements. (Mapping is referencing the edge id)
+/// * AllSame There can be only one mapping coordinate for the whole surface.
+class MeshGeometry : public Geometry {
+public:
+ enum class MapType {
+ none = 0, // No mapping type. Stored as "None".
+ vertex, // Maps per vertex. Stored as "ByVertice".
+ polygon_vertex, // Maps per polygon vertex. Stored as "ByPolygonVertex".
+ polygon, // Maps per polygon. Stored as "ByPolygon".
+ edge, // Maps per edge. Stored as "ByEdge".
+ all_the_same // Uaps to everything. Stored as "AllSame".
+ };
+
+ enum class ReferenceType {
+ direct = 0,
+ index = 1,
+ index_to_direct = 2
+ };
+
+ template <class T>
+ struct MappingData {
+ MapType map_type = MapType::none;
+ ReferenceType ref_type = ReferenceType::direct;
+ std::vector<T> data;
+ /// The meaning of the indices depends from the `MapType`.
+ /// If `ref_type` is `direct` this map is hollow.
+ std::vector<int> index;
+
+ String debug_info() const {
+ return "indexes: " + itos(index.size()) + " data: " + itos(data.size());
+ }
+ };
+
+ struct Edge {
+ int vertex_0 = 0, vertex_1 = 0;
+ Edge(int v0, int v1) :
+ vertex_0(v0), vertex_1(v1) {}
+ Edge() {}
+ };
+
+public:
+ MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+
+ virtual ~MeshGeometry();
+
+ const std::vector<Vector3> &get_vertices() const;
+ const std::vector<Edge> &get_edge_map() const;
+ const std::vector<int> &get_polygon_indices() const;
+ const std::vector<int> &get_edges() const;
+ const MappingData<Vector3> &get_normals() const;
+ const MappingData<Vector2> &get_uv_0() const;
+ const MappingData<Vector2> &get_uv_1() const;
+ const MappingData<Color> &get_colors() const;
+ const MappingData<int> &get_material_allocation_id() const;
+
+ /// Returns -1 if the vertices doesn't form an edge. Vertex order, doesn't
+ // matter.
+ static int get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b);
+ // Returns the edge point bu that ID, or the edge with -1 vertices if the
+ // id is not valid.
+ static Edge get_edge(const std::vector<Edge> &p_map, int p_id);
+
+private:
+ // Read directly from the FBX file.
+ std::vector<Vector3> m_vertices;
+ std::vector<Edge> edge_map;
+ std::vector<int> m_face_indices;
+ std::vector<int> m_edges;
+ MappingData<Vector3> m_normals;
+ MappingData<Vector2> m_uv_0; // first uv coordinates
+ MappingData<Vector2> m_uv_1; // second uv coordinates
+ MappingData<Color> m_colors; // colors for the mesh
+ MappingData<int> m_material_allocation_ids; // slot of material used
+
+ template <class T>
+ MappingData<T> resolve_vertex_data_array(
+ const ScopePtr source,
+ const std::string &MappingInformationType,
+ const std::string &ReferenceInformationType,
+ const std::string &dataElementName,
+ const std::string &indexOverride = "");
+};
+
+/*
+ * DOM class for FBX geometry of type "Shape"
+ */
+class ShapeGeometry : public Geometry {
+public:
+ /** The class constructor */
+ ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+
+ /** The class destructor */
+ virtual ~ShapeGeometry();
+
+ /** Get a list of all vertex points, non-unique*/
+ const std::vector<Vector3> &GetVertices() const;
+
+ /** Get a list of all vertex normals or an empty array if
+ * no normals are specified. */
+ const std::vector<Vector3> &GetNormals() const;
+
+ /** Return list of vertex indices. */
+ const std::vector<unsigned int> &GetIndices() const;
+
+private:
+ std::vector<Vector3> m_vertices;
+ std::vector<Vector3> m_normals;
+ std::vector<unsigned int> m_indices;
+};
+/**
+ * DOM class for FBX geometry of type "Line"
+ */
+class LineGeometry : public Geometry {
+public:
+ /** The class constructor */
+ LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+
+ /** The class destructor */
+ virtual ~LineGeometry();
+
+ /** Get a list of all vertex points, non-unique*/
+ const std::vector<Vector3> &GetVertices() const;
+
+ /** Return list of vertex indices. */
+ const std::vector<int> &GetIndices() const;
+
+private:
+ std::vector<Vector3> m_vertices;
+ std::vector<int> m_indices;
+};
+} // namespace FBXDocParser
+
+#endif // FBX_MESH_GEOMETRY_H
diff --git a/modules/fbx/fbx_parser/FBXModel.cpp b/modules/fbx/fbx_parser/FBXModel.cpp
new file mode 100644
index 0000000000..03c9de0c35
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXModel.cpp
@@ -0,0 +1,171 @@
+/*************************************************************************/
+/* FBXModel.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXModel.cpp
+ * @brief Assimp::FBX::Model implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXMeshGeometry.h"
+#include "FBXParser.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Model::Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name), shading("Y") {
+ const ScopePtr sc = GetRequiredScope(element);
+ const ElementPtr Shading = sc->GetElement("Shading");
+ const ElementPtr Culling = sc->GetElement("Culling");
+
+ if (Shading) {
+ shading = GetRequiredToken(Shading, 0)->StringContents();
+ }
+
+ if (Culling) {
+ culling = ParseTokenAsString(GetRequiredToken(Culling, 0));
+ }
+
+ ResolveLinks(element, doc);
+}
+
+// ------------------------------------------------------------------------------------------------
+Model::~Model() {
+}
+
+ModelLimbNode::ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Model(id, element, doc, name){};
+
+ModelLimbNode::~ModelLimbNode() {
+}
+
+// ------------------------------------------------------------------------------------------------
+void Model::ResolveLinks(const ElementPtr element, const Document &doc) {
+ const char *const arr[] = { "Geometry", "Material", "NodeAttribute" };
+
+ // resolve material
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), arr, 3);
+
+ materials.reserve(conns.size());
+ geometry.reserve(conns.size());
+ attributes.reserve(conns.size());
+ for (const Connection *con : conns) {
+ // material and geometry links should be Object-Object connections
+ if (con->PropertyName().length()) {
+ continue;
+ }
+
+ const Object *const ob = con->SourceObject();
+ if (!ob) {
+ //DOMWarning("failed to read source object for incoming Model link, ignoring",&element);
+ continue;
+ }
+
+ const Material *const mat = dynamic_cast<const Material *>(ob);
+ if (mat) {
+ materials.push_back(mat);
+ continue;
+ }
+
+ const Geometry *const geo = dynamic_cast<const Geometry *>(ob);
+ if (geo) {
+ geometry.push_back(geo);
+ continue;
+ }
+
+ const NodeAttribute *const att = dynamic_cast<const NodeAttribute *>(ob);
+ if (att) {
+ attributes.push_back(att);
+ continue;
+ }
+
+ DOMWarning("source object for model link is neither Material, NodeAttribute nor Geometry, ignoring", element);
+ continue;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Model::IsNull() const {
+ const std::vector<const NodeAttribute *> &attrs = GetAttributes();
+ for (const NodeAttribute *att : attrs) {
+ const Null *null_tag = dynamic_cast<const Null *>(att);
+ if (null_tag) {
+ return true;
+ }
+ }
+
+ return false;
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXNodeAttribute.cpp b/modules/fbx/fbx_parser/FBXNodeAttribute.cpp
new file mode 100644
index 0000000000..15184a0f5d
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXNodeAttribute.cpp
@@ -0,0 +1,174 @@
+/*************************************************************************/
+/* FBXNodeAttribute.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXNoteAttribute.cpp
+ * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXParser.h"
+#include <iostream>
+
+namespace FBXDocParser {
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+NodeAttribute::NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name) {
+}
+
+// ------------------------------------------------------------------------------------------------
+NodeAttribute::~NodeAttribute() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+CameraSwitcher::CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ NodeAttribute(id, element, doc, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+ const ElementPtr CameraId = sc->GetElement("CameraId");
+ const ElementPtr CameraName = sc->GetElement("CameraName");
+ const ElementPtr CameraIndexName = sc->GetElement("CameraIndexName");
+
+ if (CameraId) {
+ cameraId = ParseTokenAsInt(GetRequiredToken(CameraId, 0));
+ }
+
+ if (CameraName) {
+ cameraName = GetRequiredToken(CameraName, 0)->StringContents();
+ }
+
+ if (CameraIndexName && CameraIndexName->Tokens().size()) {
+ cameraIndexName = GetRequiredToken(CameraIndexName, 0)->StringContents();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+CameraSwitcher::~CameraSwitcher() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Camera::Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ NodeAttribute(id, element, doc, name) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Camera::~Camera() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Light::Light(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ NodeAttribute(id, element, doc, name) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Light::~Light() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Null::Null(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ NodeAttribute(id, element, doc, name) {
+}
+
+// ------------------------------------------------------------------------------------------------
+Null::~Null() {
+}
+
+// ------------------------------------------------------------------------------------------------
+LimbNode::LimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ NodeAttribute(id, element, doc, name) {
+ //std::cout << "limb node: " << name << std::endl;
+ //const Scope &sc = GetRequiredScope(element);
+
+ //const ElementPtr const TypeFlag = sc["TypeFlags"];
+
+ // keep this it can dump new properties for you
+ // for( auto element : sc.Elements())
+ // {
+ // std::cout << "limbnode element: " << element.first << std::endl;
+ // }
+
+ // if(TypeFlag)
+ // {
+ // // std::cout << "type flag: " << GetRequiredToken(*TypeFlag, 0).StringContents() << std::endl;
+ // }
+}
+
+// ------------------------------------------------------------------------------------------------
+LimbNode::~LimbNode() {
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXParseTools.h b/modules/fbx/fbx_parser/FBXParseTools.h
new file mode 100644
index 0000000000..b4003bbec5
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXParseTools.h
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* FBXParseTools.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 FBX_PARSE_TOOLS_H
+#define FBX_PARSE_TOOLS_H
+
+#include "core/error/error_macros.h"
+#include "core/string/ustring.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <locale>
+
+template <class char_t>
+inline bool IsNewLine(char_t c) {
+ return c == '\n' || c == '\r';
+}
+template <class char_t>
+inline bool IsSpace(char_t c) {
+ return (c == (char_t)' ' || c == (char_t)'\t');
+}
+
+template <class char_t>
+inline bool IsSpaceOrNewLine(char_t c) {
+ return IsNewLine(c) || IsSpace(c);
+}
+
+template <class char_t>
+inline bool IsLineEnd(char_t c) {
+ return (c == (char_t)'\r' || c == (char_t)'\n' || c == (char_t)'\0' || c == (char_t)'\f');
+}
+
+// ------------------------------------------------------------------------------------
+// Special version of the function, providing higher accuracy and safety
+// It is mainly used by fast_atof to prevent ugly and unwanted integer overflows.
+// ------------------------------------------------------------------------------------
+inline uint64_t strtoul10_64(const char *in, bool &errored, const char **out = nullptr, unsigned int *max_inout = nullptr) {
+ unsigned int cur = 0;
+ uint64_t value = 0;
+
+ errored = *in < '0' || *in > '9';
+ ERR_FAIL_COND_V_MSG(errored, 0, "The string cannot be converted parser error");
+
+ for (;;) {
+ if (*in < '0' || *in > '9') {
+ break;
+ }
+
+ const uint64_t new_value = (value * (uint64_t)10) + ((uint64_t)(*in - '0'));
+
+ // numeric overflow, we rely on you
+ if (new_value < value) {
+ //WARN_PRINT( "Converting the string \" " + in + " \" into a value resulted in overflow." );
+ return 0;
+ }
+
+ value = new_value;
+
+ ++in;
+ ++cur;
+
+ if (max_inout && *max_inout == cur) {
+ if (out) { /* skip to end */
+ while (*in >= '0' && *in <= '9') {
+ ++in;
+ }
+ *out = in;
+ }
+
+ return value;
+ }
+ }
+ if (out) {
+ *out = in;
+ }
+
+ if (max_inout) {
+ *max_inout = cur;
+ }
+
+ return value;
+}
+
+#endif // FBX_PARSE_TOOLS_H
diff --git a/modules/fbx/fbx_parser/FBXParser.cpp b/modules/fbx/fbx_parser/FBXParser.cpp
new file mode 100644
index 0000000000..dbc9a0e46d
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXParser.cpp
@@ -0,0 +1,1322 @@
+/*************************************************************************/
+/* FBXParser.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXParser.cpp
+ * @brief Implementation of the FBX parser and the rudimentary DOM that we use
+ */
+
+#include <stdlib.h> /* strtol */
+#include <zlib.h>
+
+#include "ByteSwapper.h"
+#include "FBXParseTools.h"
+#include "FBXParser.h"
+#include "FBXTokenizer.h"
+#include "core/math/math_defs.h"
+#include "core/math/transform_3d.h"
+#include "core/math/vector3.h"
+#include "core/string/print_string.h"
+
+using namespace FBXDocParser;
+namespace {
+
+// Initially, we did reinterpret_cast, breaking strict aliasing rules.
+// This actually caused trouble on Android, so let's be safe this time.
+// https://github.com/assimp/assimp/issues/24
+template <typename T>
+T SafeParse(const char *data, const char *end) {
+ // Actual size validation happens during Tokenization so
+ // this is valid as an assertion.
+ (void)(end);
+ //ai_assert(static_cast<size_t>(end - data) >= sizeof(T));
+ T result = static_cast<T>(0);
+ ::memcpy(&result, data, sizeof(T));
+ return result;
+}
+} // namespace
+
+namespace FBXDocParser {
+
+// ------------------------------------------------------------------------------------------------
+Element::Element(const TokenPtr key_token, Parser &parser) :
+ key_token(key_token) {
+ TokenPtr n = nullptr;
+ do {
+ n = parser.AdvanceToNextToken();
+ if (n == nullptr) {
+ continue;
+ }
+
+ if (!n) {
+ print_error("unexpected end of file, expected closing bracket" + String(parser.LastToken()->StringContents().c_str()));
+ }
+
+ if (n && n->Type() == TokenType_DATA) {
+ tokens.push_back(n);
+ TokenPtr prev = n;
+ n = parser.AdvanceToNextToken();
+
+ if (n == nullptr) {
+ break;
+ }
+
+ if (!n) {
+ print_error("unexpected end of file, expected bracket, comma or key" + String(parser.LastToken()->StringContents().c_str()));
+ parser.corrupt = true;
+ return;
+ }
+
+ const TokenType ty = n->Type();
+
+ // some exporters are missing a comma on the next line
+ if (ty == TokenType_DATA && prev->Type() == TokenType_DATA && (n->Line() == prev->Line() + 1)) {
+ tokens.push_back(n);
+ continue;
+ }
+
+ if (ty != TokenType_OPEN_BRACKET && ty != TokenType_CLOSE_BRACKET && ty != TokenType_COMMA && ty != TokenType_KEY) {
+ print_error("unexpected token; expected bracket, comma or key" + String(n->StringContents().c_str()));
+ parser.corrupt = true;
+ return;
+ }
+ }
+
+ if (n && n->Type() == TokenType_OPEN_BRACKET) {
+ compound = new_Scope(parser);
+ parser.scopes.push_back(compound);
+
+ if (parser.corrupt) {
+ return;
+ }
+
+ // current token should be a TOK_CLOSE_BRACKET
+ n = parser.CurrentToken();
+
+ if (n && n->Type() != TokenType_CLOSE_BRACKET) {
+ print_error("expected closing bracket" + String(n->StringContents().c_str()));
+ parser.corrupt = true;
+ return;
+ }
+
+ parser.AdvanceToNextToken();
+ return;
+ }
+ } while (n && n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET);
+}
+
+// ------------------------------------------------------------------------------------------------
+Element::~Element() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Scope::Scope(Parser &parser, bool topLevel) {
+ if (!topLevel) {
+ TokenPtr t = parser.CurrentToken();
+ if (t->Type() != TokenType_OPEN_BRACKET) {
+ print_error("expected open bracket" + String(t->StringContents().c_str()));
+ parser.corrupt = true;
+ return;
+ }
+ }
+
+ TokenPtr n = parser.AdvanceToNextToken();
+ if (n == nullptr) {
+ print_error("unexpected end of file");
+ parser.corrupt = true;
+ return;
+ }
+
+ // note: empty scopes are allowed
+ while (n && n->Type() != TokenType_CLOSE_BRACKET) {
+ if (n->Type() != TokenType_KEY) {
+ print_error("unexpected token, expected TOK_KEY" + String(n->StringContents().c_str()));
+ parser.corrupt = true;
+ return;
+ }
+
+ const std::string str = n->StringContents();
+
+ if (parser.corrupt) {
+ return;
+ }
+ // std::multimap<std::string, ElementPtr> (key and value)
+ elements.insert(ElementMap::value_type(str, new_Element(n, parser)));
+
+ // Element() should stop at the next Key token (or right after a Close token)
+ n = parser.CurrentToken();
+ if (n == nullptr) {
+ if (topLevel) {
+ return;
+ }
+
+ //print_error("unexpected end of file" + String(parser.LastToken()->StringContents().c_str()));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Scope::~Scope() {
+ for (ElementMap::value_type &v : elements) {
+ delete v.second;
+ v.second = nullptr;
+ }
+
+ elements.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+Parser::Parser(const TokenList &tokens, bool is_binary) :
+ corrupt(false), tokens(tokens), cursor(tokens.begin()), is_binary(is_binary) {
+ root = new_Scope(*this, true);
+ scopes.push_back(root);
+}
+
+// ------------------------------------------------------------------------------------------------
+Parser::~Parser() {
+ for (ScopePtr scope : scopes) {
+ delete scope;
+ scope = nullptr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::AdvanceToNextToken() {
+ last = current;
+ if (cursor == tokens.end()) {
+ current = nullptr;
+ } else {
+ current = *cursor++;
+ }
+ return current;
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::CurrentToken() const {
+ return current;
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::LastToken() const {
+ return last;
+}
+
+// ------------------------------------------------------------------------------------------------
+uint64_t ParseTokenAsID(const TokenPtr t, const char *&err_out) {
+ ERR_FAIL_COND_V_MSG(t == nullptr, 0L, "Invalid token passed to ParseTokenAsID");
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return 0L;
+ }
+
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'L') {
+ err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)";
+ return 0L;
+ }
+
+ uint64_t id = SafeParse<uint64_t>(data + 1, t->end());
+ return id;
+ }
+
+ // XXX: should use size_t here
+ unsigned int length = static_cast<unsigned int>(t->end() - t->begin());
+ //ai_assert(length > 0);
+
+ const char *out = nullptr;
+ bool errored = false;
+
+ const uint64_t id = strtoul10_64(t->begin(), errored, &out, &length);
+ if (errored || out > t->end()) {
+ err_out = "failed to parse ID (text)";
+ return 0L;
+ }
+
+ return id;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsID() with print_error handling
+uint64_t ParseTokenAsID(const TokenPtr t) {
+ const char *err = nullptr;
+ const uint64_t i = ParseTokenAsID(t, err);
+ if (err) {
+ print_error(String(err) + " " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+size_t ParseTokenAsDim(const TokenPtr t, const char *&err_out) {
+ // same as ID parsing, except there is a trailing asterisk
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return 0;
+ }
+
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'L') {
+ err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)";
+ return 0;
+ }
+
+ uint64_t id = SafeParse<uint64_t>(data + 1, t->end());
+ AI_SWAP8(id);
+ return static_cast<size_t>(id);
+ }
+
+ if (*t->begin() != '*') {
+ err_out = "expected asterisk before array dimension";
+ return 0;
+ }
+
+ // XXX: should use size_t here
+ unsigned int length = static_cast<unsigned int>(t->end() - t->begin());
+ if (length == 0) {
+ err_out = "expected valid integer number after asterisk";
+ return 0;
+ }
+
+ const char *out = nullptr;
+ bool errored = false;
+ const size_t id = static_cast<size_t>(strtoul10_64(t->begin() + 1, errored, &out, &length));
+ if (errored || out > t->end()) {
+ print_error("failed to parse id");
+ err_out = "failed to parse ID";
+ return 0;
+ }
+
+ return id;
+}
+
+// ------------------------------------------------------------------------------------------------
+float ParseTokenAsFloat(const TokenPtr t, const char *&err_out) {
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return 0.0f;
+ }
+
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'F' && data[0] != 'D') {
+ err_out = "failed to parse F(loat) or D(ouble), unexpected data type (binary)";
+ return 0.0f;
+ }
+
+ if (data[0] == 'F') {
+ return SafeParse<float>(data + 1, t->end());
+ } else {
+ return static_cast<float>(SafeParse<double>(data + 1, t->end()));
+ }
+ }
+
+// need to copy the input string to a temporary buffer
+// first - next in the fbx token stream comes ',',
+// which fast_atof could interpret as decimal point.
+#define MAX_FLOAT_LENGTH 31
+ char temp[MAX_FLOAT_LENGTH + 1];
+ const size_t length = static_cast<size_t>(t->end() - t->begin());
+ std::copy(t->begin(), t->end(), temp);
+ temp[std::min(static_cast<size_t>(MAX_FLOAT_LENGTH), length)] = '\0';
+
+ return atof(temp);
+}
+
+// ------------------------------------------------------------------------------------------------
+int ParseTokenAsInt(const TokenPtr t, const char *&err_out) {
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return 0;
+ }
+
+ // binary files are simple to parse
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'I') {
+ err_out = "failed to parse I(nt), unexpected data type (binary)";
+ return 0;
+ }
+
+ int32_t ival = SafeParse<int32_t>(data + 1, t->end());
+ AI_SWAP4(ival);
+ return static_cast<int>(ival);
+ }
+
+ // ASCII files are unsafe.
+ const size_t length = static_cast<size_t>(t->end() - t->begin());
+ if (length == 0) {
+ err_out = "expected valid integer number after asterisk";
+ ERR_FAIL_V_MSG(0, "expected valid integer number after asterisk");
+ }
+
+ // must not be null for strtol to work
+ char *out = (char *)t->end();
+ // string begin, end ptr ref, base 10
+ const int value = strtol(t->begin(), &out, 10);
+ if (out == nullptr || out != t->end()) {
+ err_out = "failed to parse ID";
+ ERR_FAIL_V_MSG(0, "failed to parse ID");
+ }
+
+ return value;
+}
+
+// ------------------------------------------------------------------------------------------------
+int64_t ParseTokenAsInt64(const TokenPtr t, const char *&err_out) {
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return 0L;
+ }
+
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'L') {
+ err_out = "failed to parse Int64, unexpected data type";
+ return 0L;
+ }
+
+ int64_t id = SafeParse<int64_t>(data + 1, t->end());
+ AI_SWAP8(id);
+ return id;
+ }
+
+ // XXX: should use size_t here
+ unsigned int length = static_cast<unsigned int>(t->end() - t->begin());
+ //ai_assert(length > 0);
+
+ char *out = nullptr;
+ const int64_t id = strtol(t->begin(), &out, length);
+ if (out > t->end()) {
+ err_out = "failed to parse Int64 (text)";
+ return 0L;
+ }
+
+ return id;
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string ParseTokenAsString(const TokenPtr t, const char *&err_out) {
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return "";
+ }
+
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'S') {
+ err_out = "failed to parse String, unexpected data type (binary)";
+ return "";
+ }
+
+ // read string length
+ int32_t len = SafeParse<int32_t>(data + 1, t->end());
+ AI_SWAP4(len);
+
+ //ai_assert(t.end() - data == 5 + len);
+ return std::string(data + 5, len);
+ }
+
+ const size_t length = static_cast<size_t>(t->end() - t->begin());
+ if (length < 2) {
+ err_out = "token is too short to hold a string";
+ return "";
+ }
+
+ const char *s = t->begin(), *e = t->end() - 1;
+ if (*s != '\"' || *e != '\"') {
+ err_out = "expected double quoted string";
+ return "";
+ }
+
+ return std::string(s + 1, length - 2);
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// read the type code and element count of a binary data array and stop there
+void ReadBinaryDataArrayHead(const char *&data, const char *end, char &type, uint32_t &count,
+ const ElementPtr el) {
+ TokenPtr token = el->KeyToken();
+ if (static_cast<size_t>(end - data) < 5) {
+ print_error("binary data array is too short, need five (5) bytes for type signature and element count: " + String(token->StringContents().c_str()));
+ }
+
+ // data type
+ type = *data;
+
+ // read number of elements
+ uint32_t len = SafeParse<uint32_t>(data + 1, end);
+ AI_SWAP4(len);
+
+ count = len;
+ data += 5;
+}
+
+// ------------------------------------------------------------------------------------------------
+// read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header)
+void ReadBinaryDataArray(char type, uint32_t count, const char *&data, const char *end,
+ std::vector<char> &buff,
+ const ElementPtr /*el*/) {
+ uint32_t encmode = SafeParse<uint32_t>(data, end);
+ AI_SWAP4(encmode);
+ data += 4;
+
+ // next comes the compressed length
+ uint32_t comp_len = SafeParse<uint32_t>(data, end);
+ AI_SWAP4(comp_len);
+ data += 4;
+
+ //ai_assert(data + comp_len == end);
+
+ // determine the length of the uncompressed data by looking at the type signature
+ uint32_t stride = 0;
+ switch (type) {
+ case 'f':
+ case 'i':
+ stride = 4;
+ break;
+
+ case 'd':
+ case 'l':
+ stride = 8;
+ break;
+ }
+
+ const uint32_t full_length = stride * count;
+ buff.resize(full_length);
+
+ if (encmode == 0) {
+ //ai_assert(full_length == comp_len);
+
+ // plain data, no compression
+ std::copy(data, end, buff.begin());
+ } else if (encmode == 1) {
+ // zlib/deflate, next comes ZIP head (0x78 0x01)
+ // see https://www.ietf.org/rfc/rfc1950.txt
+
+ z_stream zstream;
+ zstream.opaque = Z_NULL;
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.data_type = Z_BINARY;
+
+ // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib
+ if (Z_OK != inflateInit(&zstream)) {
+ print_error("failure initializing zlib");
+ }
+
+ zstream.next_in = reinterpret_cast<Bytef *>(const_cast<char *>(data));
+ zstream.avail_in = comp_len;
+
+ zstream.avail_out = static_cast<uInt>(buff.size());
+ zstream.next_out = reinterpret_cast<Bytef *>(&*buff.begin());
+ const int ret = inflate(&zstream, Z_FINISH);
+
+ if (ret != Z_STREAM_END && ret != Z_OK) {
+ print_error("failure decompressing compressed data section");
+ }
+
+ // terminate zlib
+ inflateEnd(&zstream);
+ }
+#ifdef ASSIMP_BUILD_DEBUG
+ else {
+ // runtime check for this happens at tokenization stage
+ //ai_assert(false);
+ }
+#endif
+
+ data += comp_len;
+ //ai_assert(data == end);
+}
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+// read an array of float3 tuples
+void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el) {
+ out.resize(0);
+
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ if (tok.empty()) {
+ print_error("unexpected empty element" + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (count % 3 != 0) {
+ print_error("number of floats is not a multiple of three (3) (binary)" + String(token->StringContents().c_str()));
+ }
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'd' && type != 'f') {
+ print_error("expected float or double array (binary)" + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+ const uint32_t count3 = count / 3;
+ out.reserve(count3);
+
+ if (type == 'd') {
+ const double *d = reinterpret_cast<const double *>(&buff[0]);
+ for (unsigned int i = 0; i < count3; ++i, d += 3) {
+ out.push_back(Vector3(static_cast<real_t>(d[0]),
+ static_cast<real_t>(d[1]),
+ static_cast<real_t>(d[2])));
+ }
+ } else if (type == 'f') {
+ const float *f = reinterpret_cast<const float *>(&buff[0]);
+ for (unsigned int i = 0; i < count3; ++i, f += 3) {
+ out.push_back(Vector3(f[0], f[1], f[2]));
+ }
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // may throw bad_alloc if the input is rubbish, but this need
+ // not to be prevented - importing would fail but we wouldn't
+ // crash since assimp handles this case properly.
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ if (a->Tokens().size() % 3 != 0) {
+ print_error("number of floats is not a multiple of three (3)" + String(token->StringContents().c_str()));
+ } else {
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ Vector3 v;
+ v.x = ParseTokenAsFloat(*it++);
+ v.y = ParseTokenAsFloat(*it++);
+ v.z = ParseTokenAsFloat(*it++);
+
+ out.push_back(v);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of color4 tuples
+void ParseVectorDataArray(std::vector<Color> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+
+ TokenPtr token = el->KeyToken();
+
+ if (tok.empty()) {
+ print_error("unexpected empty element" + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (count % 4 != 0) {
+ print_error("number of floats is not a multiple of four (4) (binary)" + String(token->StringContents().c_str()));
+ }
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'd' && type != 'f') {
+ print_error("expected float or double array (binary)" + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+ const uint32_t count4 = count / 4;
+ out.reserve(count4);
+
+ if (type == 'd') {
+ const double *d = reinterpret_cast<const double *>(&buff[0]);
+ for (unsigned int i = 0; i < count4; ++i, d += 4) {
+ out.push_back(Color(static_cast<float>(d[0]),
+ static_cast<float>(d[1]),
+ static_cast<float>(d[2]),
+ static_cast<float>(d[3])));
+ }
+ } else if (type == 'f') {
+ const float *f = reinterpret_cast<const float *>(&buff[0]);
+ for (unsigned int i = 0; i < count4; ++i, f += 4) {
+ out.push_back(Color(f[0], f[1], f[2], f[3]));
+ }
+ }
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray() above
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ if (a->Tokens().size() % 4 != 0) {
+ print_error("number of floats is not a multiple of four (4)" + String(token->StringContents().c_str()));
+ }
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ Color v;
+ v.r = ParseTokenAsFloat(*it++);
+ v.g = ParseTokenAsFloat(*it++);
+ v.b = ParseTokenAsFloat(*it++);
+ v.a = ParseTokenAsFloat(*it++);
+
+ out.push_back(v);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of float2 tuples
+void ParseVectorDataArray(std::vector<Vector2> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ if (tok.empty()) {
+ print_error("unexpected empty element" + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (count % 2 != 0) {
+ print_error("number of floats is not a multiple of two (2) (binary)" + String(token->StringContents().c_str()));
+ }
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'd' && type != 'f') {
+ print_error("expected float or double array (binary)" + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+ const uint32_t count2 = count / 2;
+ out.reserve(count2);
+
+ if (type == 'd') {
+ const double *d = reinterpret_cast<const double *>(&buff[0]);
+ for (unsigned int i = 0; i < count2; ++i, d += 2) {
+ out.push_back(Vector2(static_cast<float>(d[0]),
+ static_cast<float>(d[1])));
+ }
+ } else if (type == 'f') {
+ const float *f = reinterpret_cast<const float *>(&buff[0]);
+ for (unsigned int i = 0; i < count2; ++i, f += 2) {
+ out.push_back(Vector2(f[0], f[1]));
+ }
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray() above
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ if (a->Tokens().size() % 2 != 0) {
+ print_error("number of floats is not a multiple of two (2)" + String(token->StringContents().c_str()));
+ } else {
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ Vector2 v;
+ v.x = ParseTokenAsFloat(*it++);
+ v.y = ParseTokenAsFloat(*it++);
+ out.push_back(v);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of ints
+void ParseVectorDataArray(std::vector<int> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ if (tok.empty()) {
+ print_error("unexpected empty element" + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'i') {
+ print_error("expected int array (binary)" + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * 4);
+
+ out.reserve(count);
+
+ const int32_t *ip = reinterpret_cast<const int32_t *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++ip) {
+ int32_t val = *ip;
+ AI_SWAP4(val);
+ out.push_back(val);
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray()
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ const int ival = ParseTokenAsInt(*it++);
+ out.push_back(ival);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of floats
+void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ if (tok.empty()) {
+ print_error("unexpected empty element: " + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'd' && type != 'f') {
+ print_error("expected float or double array (binary) " + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+ if (type == 'd') {
+ const double *d = reinterpret_cast<const double *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++d) {
+ out.push_back(static_cast<float>(*d));
+ }
+ } else if (type == 'f') {
+ const float *f = reinterpret_cast<const float *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++f) {
+ out.push_back(*f);
+ }
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray()
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ const float ival = ParseTokenAsFloat(*it++);
+ out.push_back(ival);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of uints
+void ParseVectorDataArray(std::vector<unsigned int> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+ const TokenPtr token = el->KeyToken();
+
+ ERR_FAIL_COND_MSG(!token, "invalid ParseVectorDataArrat token invalid");
+
+ if (tok.empty()) {
+ print_error("unexpected empty element: " + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'i') {
+ print_error("expected (u)int array (binary)" + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * 4);
+
+ out.reserve(count);
+
+ const int32_t *ip = reinterpret_cast<const int32_t *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++ip) {
+ int32_t val = *ip;
+ if (val < 0) {
+ print_error("encountered negative integer index (binary)");
+ }
+
+ out.push_back(val);
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray()
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ const int ival = ParseTokenAsInt(*it++);
+ if (ival < 0) {
+ print_error("encountered negative integer index");
+ }
+ out.push_back(static_cast<unsigned int>(ival));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of uint64_ts
+void ParseVectorDataArray(std::vector<uint64_t> &out, const ElementPtr el) {
+ out.resize(0);
+
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ ERR_FAIL_COND(!token);
+
+ if (tok.empty()) {
+ print_error("unexpected empty element " + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'l') {
+ print_error("expected long array (binary): " + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * 8);
+
+ out.reserve(count);
+
+ const uint64_t *ip = reinterpret_cast<const uint64_t *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++ip) {
+ uint64_t val = *ip;
+ AI_SWAP8(val);
+ out.push_back(val);
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray()
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ const uint64_t ival = ParseTokenAsID(*it++);
+
+ out.push_back(ival);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of int64_ts
+void ParseVectorDataArray(std::vector<int64_t> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ ERR_FAIL_COND(!token);
+ if (tok.empty()) {
+ print_error("unexpected empty element: " + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'l') {
+ print_error("expected long array (binary) " + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * 8);
+
+ out.reserve(count);
+
+ const int64_t *ip = reinterpret_cast<const int64_t *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++ip) {
+ int64_t val = *ip;
+ AI_SWAP8(val);
+ out.push_back(val);
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray()
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ const int64_t val = ParseTokenAsInt64(*it++);
+ out.push_back(val);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Transform3D ReadMatrix(const ElementPtr element) {
+ std::vector<float> values;
+ ParseVectorDataArray(values, element);
+
+ if (values.size() != 16) {
+ print_error("expected 16 matrix elements");
+ }
+
+ // clean values to prevent any IBM damage on inverse() / affine_inverse()
+ for (float &value : values) {
+ if (::Math::is_zero_approx(value)) {
+ value = 0;
+ }
+ }
+
+ Transform3D xform;
+ Basis basis;
+
+ basis.set(
+ Vector3(values[0], values[1], values[2]),
+ Vector3(values[4], values[5], values[6]),
+ Vector3(values[8], values[9], values[10]));
+
+ xform.basis = basis;
+ xform.origin = Vector3(values[12], values[13], values[14]);
+ // determine if we need to think about this with dynamic rotation order?
+ // for example:
+ // xform.basis = z_axis * y_axis * x_axis;
+ //xform.basis.transpose();
+
+ print_verbose("xform verbose basis: " + (xform.basis.get_euler() * (180 / Math_PI)) + " xform origin:" + xform.origin);
+
+ return xform;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsString() with print_error handling
+std::string ParseTokenAsString(const TokenPtr t) {
+ ERR_FAIL_COND_V(!t, "");
+ const char *err;
+ const std::string &i = ParseTokenAsString(t, err);
+ if (err) {
+ print_error(String(err) + ", " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// extract a required element from a scope, abort if the element cannot be found
+ElementPtr GetRequiredElement(const ScopePtr sc, const std::string &index, const ElementPtr element /*= nullptr*/) {
+ const ElementPtr el = sc->GetElement(index);
+ TokenPtr token = el->KeyToken();
+ ERR_FAIL_COND_V(!token, nullptr);
+ if (!el) {
+ print_error("did not find required element \"" + String(index.c_str()) + "\" " + String(token->StringContents().c_str()));
+ }
+ return el;
+}
+
+bool HasElement(const ScopePtr sc, const std::string &index) {
+ const ElementPtr el = sc->GetElement(index);
+ if (nullptr == el) {
+ return false;
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// extract a required element from a scope, abort if the element cannot be found
+ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element /*= nullptr*/) {
+ const ElementPtr el = sc->GetElement(index);
+ return el;
+}
+
+// ------------------------------------------------------------------------------------------------
+// extract required compound scope
+ScopePtr GetRequiredScope(const ElementPtr el) {
+ if (el) {
+ ScopePtr s = el->Compound();
+ TokenPtr token = el->KeyToken();
+ ERR_FAIL_COND_V(!token, nullptr);
+ if (s) {
+ return s;
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "expected compound scope " + String(token->StringContents().c_str()));
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "Invalid element supplied to parser");
+}
+
+// ------------------------------------------------------------------------------------------------
+// extract optional compound scope
+ScopePtr GetOptionalScope(const ElementPtr el) {
+ if (el) {
+ ScopePtr s = el->Compound();
+ TokenPtr token = el->KeyToken();
+
+ if (token && s) {
+ return s;
+ }
+ }
+
+ return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// get token at a particular index
+TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index) {
+ if (el) {
+ const TokenList &x = el->Tokens();
+ TokenPtr token = el->KeyToken();
+
+ ERR_FAIL_COND_V(!token, nullptr);
+
+ if (index >= x.size()) {
+ ERR_FAIL_V_MSG(nullptr, "missing token at index: " + itos(index) + " " + String(token->StringContents().c_str()));
+ }
+
+ return x[index];
+ }
+
+ return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsDim() with print_error handling
+size_t ParseTokenAsDim(const TokenPtr t) {
+ const char *err;
+ const size_t i = ParseTokenAsDim(t, err);
+ if (err) {
+ print_error(String(err) + " " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsFloat() with print_error handling
+float ParseTokenAsFloat(const TokenPtr t) {
+ const char *err;
+ const float i = ParseTokenAsFloat(t, err);
+ if (err) {
+ print_error(String(err) + " " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsInt() with print_error handling
+int ParseTokenAsInt(const TokenPtr t) {
+ const char *err;
+ const int i = ParseTokenAsInt(t, err);
+ if (err) {
+ print_error(String(err) + " " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsInt64() with print_error handling
+int64_t ParseTokenAsInt64(const TokenPtr t) {
+ const char *err;
+ const int64_t i = ParseTokenAsInt64(t, err);
+ if (err) {
+ print_error(String(err) + " " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXParser.h b/modules/fbx/fbx_parser/FBXParser.h
new file mode 100644
index 0000000000..27db18bf8a
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXParser.h
@@ -0,0 +1,270 @@
+/*************************************************************************/
+/* FBXParser.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXParser.h
+ * @brief FBX parsing code
+ */
+#ifndef FBX_PARSER_H
+#define FBX_PARSER_H
+
+#include <stdint.h>
+#include <map>
+#include <memory>
+
+#include "core/math/color.h"
+#include "core/math/transform_3d.h"
+#include "core/math/vector2.h"
+#include "core/math/vector3.h"
+
+#include "FBXTokenizer.h"
+
+namespace FBXDocParser {
+
+class Scope;
+class Parser;
+class Element;
+
+typedef Element *ElementPtr;
+typedef Scope *ScopePtr;
+
+typedef std::vector<ScopePtr> ScopeList;
+typedef std::multimap<std::string, ElementPtr> ElementMap;
+typedef std::pair<ElementMap::const_iterator, ElementMap::const_iterator> ElementCollection;
+
+#define new_Scope new Scope
+#define new_Element new Element
+
+/** FBX data entity that consists of a key:value tuple.
+ *
+ * Example:
+ * @verbatim
+ * AnimationCurve: 23, "AnimCurve::", "" {
+ * [..]
+ * }
+ * @endverbatim
+ *
+ * As can be seen in this sample, elements can contain nested #Scope
+ * as their trailing member. **/
+class Element {
+public:
+ Element(TokenPtr key_token, Parser &parser);
+ ~Element();
+
+ ScopePtr Compound() const {
+ return compound;
+ }
+
+ TokenPtr KeyToken() const {
+ return key_token;
+ }
+
+ const TokenList &Tokens() const {
+ return tokens;
+ }
+
+private:
+ TokenList tokens;
+ ScopePtr compound = nullptr;
+ std::vector<ScopePtr> compound_scope;
+ TokenPtr key_token = nullptr;
+};
+
+/** FBX data entity that consists of a 'scope', a collection
+ * of not necessarily unique #Element instances.
+ *
+ * Example:
+ * @verbatim
+ * GlobalSettings: {
+ * Version: 1000
+ * Properties70:
+ * [...]
+ * }
+ * @endverbatim */
+class Scope {
+public:
+ Scope(Parser &parser, bool topLevel = false);
+ ~Scope();
+
+ ElementPtr GetElement(const std::string &index) const {
+ ElementMap::const_iterator it = elements.find(index);
+ return it == elements.end() ? nullptr : (*it).second;
+ }
+
+ ElementPtr FindElementCaseInsensitive(const std::string &elementName) const {
+ for (FBXDocParser::ElementMap::const_iterator element = elements.begin(); element != elements.end(); ++element) {
+ if (element->first.compare(elementName)) {
+ return element->second;
+ }
+ }
+
+ // nothing to reference / expired.
+ return nullptr;
+ }
+
+ ElementCollection GetCollection(const std::string &index) const {
+ return elements.equal_range(index);
+ }
+
+ const ElementMap &Elements() const {
+ return elements;
+ }
+
+private:
+ ElementMap elements;
+};
+
+/** FBX parsing class, takes a list of input tokens and generates a hierarchy
+ * of nested #Scope instances, representing the fbx DOM.*/
+class Parser {
+public:
+ /** Parse given a token list. Does not take ownership of the tokens -
+ * the objects must persist during the entire parser lifetime */
+ Parser(const TokenList &tokens, bool is_binary);
+ ~Parser();
+
+ ScopePtr GetRootScope() const {
+ return root;
+ }
+
+ bool IsBinary() const {
+ return is_binary;
+ }
+
+ bool IsCorrupt() const {
+ return corrupt;
+ }
+
+private:
+ friend class Scope;
+ friend class Element;
+
+ TokenPtr AdvanceToNextToken();
+ TokenPtr LastToken() const;
+ TokenPtr CurrentToken() const;
+
+private:
+ bool corrupt = false;
+ ScopeList scopes;
+ const TokenList &tokens;
+
+ TokenPtr last = nullptr, current = nullptr;
+ TokenList::const_iterator cursor;
+ ScopePtr root = nullptr;
+
+ const bool is_binary;
+};
+
+/* token parsing - this happens when building the DOM out of the parse-tree*/
+uint64_t ParseTokenAsID(const TokenPtr t, const char *&err_out);
+size_t ParseTokenAsDim(const TokenPtr t, const char *&err_out);
+float ParseTokenAsFloat(const TokenPtr t, const char *&err_out);
+int ParseTokenAsInt(const TokenPtr t, const char *&err_out);
+int64_t ParseTokenAsInt64(const TokenPtr t, const char *&err_out);
+std::string ParseTokenAsString(const TokenPtr t, const char *&err_out);
+
+/* wrapper around ParseTokenAsXXX() with DOMError handling */
+uint64_t ParseTokenAsID(const TokenPtr t);
+size_t ParseTokenAsDim(const TokenPtr t);
+float ParseTokenAsFloat(const TokenPtr t);
+int ParseTokenAsInt(const TokenPtr t);
+int64_t ParseTokenAsInt64(const TokenPtr t);
+std::string ParseTokenAsString(const TokenPtr t);
+
+/* read data arrays */
+void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<Color> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<Vector2> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<int> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<unsigned int> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<uint64_t> &out, const ElementPtr ep);
+void ParseVectorDataArray(std::vector<int64_t> &out, const ElementPtr el);
+bool HasElement(const ScopePtr sc, const std::string &index);
+
+// extract a required element from a scope, abort if the element cannot be found
+ElementPtr GetRequiredElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
+ScopePtr GetRequiredScope(const ElementPtr el); // New in 2020. (less likely to destroy application)
+ScopePtr GetOptionalScope(const ElementPtr el); // New in 2021. (even LESS likely to destroy application now)
+
+ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
+// extract required compound scope
+ScopePtr GetRequiredScope(const ElementPtr el);
+// get token at a particular index
+TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index);
+
+// ------------------------------------------------------------------------------------------------
+// read a 4x4 matrix from an array of 16 floats
+Transform3D ReadMatrix(const ElementPtr element);
+} // namespace FBXDocParser
+
+#endif // FBX_PARSER_H
diff --git a/modules/fbx/fbx_parser/FBXPose.cpp b/modules/fbx/fbx_parser/FBXPose.cpp
new file mode 100644
index 0000000000..6d80b85e38
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXPose.cpp
@@ -0,0 +1,104 @@
+/*************************************************************************/
+/* FBXPose.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXNoteAttribute.cpp
+ * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXParser.h"
+#include <iostream>
+
+namespace FBXDocParser {
+
+class FbxPoseNode;
+// ------------------------------------------------------------------------------------------------
+FbxPose::FbxPose(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+ //const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
+
+ const ElementCollection &PoseNodes = sc->GetCollection("PoseNode");
+ for (ElementMap::const_iterator it = PoseNodes.first; it != PoseNodes.second; ++it) {
+ std::string entry_name = (*it).first;
+ ElementPtr some_element = (*it).second;
+ FbxPoseNode *pose_node = new FbxPoseNode(some_element, doc, entry_name);
+ pose_nodes.push_back(pose_node);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+FbxPose::~FbxPose() {
+ pose_nodes.clear();
+ // empty
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXProperties.cpp b/modules/fbx/fbx_parser/FBXProperties.cpp
new file mode 100644
index 0000000000..b8c0f685ac
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXProperties.cpp
@@ -0,0 +1,246 @@
+/*************************************************************************/
+/* FBXProperties.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXProperties.cpp
+ * @brief Implementation of the FBX dynamic properties system
+ */
+
+#include "FBXProperties.h"
+#include "FBXDocumentUtil.h"
+#include "FBXParser.h"
+#include "FBXTokenizer.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Property::Property() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Property::~Property() {
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// read a typed property out of a FBX element. The return value is nullptr if the property cannot be read.
+PropertyPtr ReadTypedProperty(const ElementPtr element) {
+ //ai_assert(element.KeyToken().StringContents() == "P");
+
+ const TokenList &tok = element->Tokens();
+ //ai_assert(tok.size() >= 5);
+
+ const std::string &s = ParseTokenAsString(tok[1]);
+ const char *const cs = s.c_str();
+ if (!strcmp(cs, "KString")) {
+ return new TypedProperty<std::string>(ParseTokenAsString(tok[4]));
+ } else if (!strcmp(cs, "bool") || !strcmp(cs, "Bool")) {
+ return new TypedProperty<bool>(ParseTokenAsInt(tok[4]) != 0);
+ } else if (!strcmp(cs, "int") || !strcmp(cs, "Int") || !strcmp(cs, "enum") || !strcmp(cs, "Enum")) {
+ return new TypedProperty<int>(ParseTokenAsInt(tok[4]));
+ } else if (!strcmp(cs, "ULongLong")) {
+ return new TypedProperty<uint64_t>(ParseTokenAsID(tok[4]));
+ } 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")) {
+ return new TypedProperty<Vector3>(Vector3(
+ ParseTokenAsFloat(tok[4]),
+ ParseTokenAsFloat(tok[5]),
+ ParseTokenAsFloat(tok[6])));
+ } else if (!strcmp(cs, "double") || !strcmp(cs, "Number") || !strcmp(cs, "Float") || !strcmp(cs, "float") || !strcmp(cs, "FieldOfView") || !strcmp(cs, "UnitScaleFactor")) {
+ return new TypedProperty<float>(ParseTokenAsFloat(tok[4]));
+ }
+
+ return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// peek into an element and check if it contains a FBX property, if so return its name.
+std::string PeekPropertyName(const Element &element) {
+ //ai_assert(element.KeyToken().StringContents() == "P");
+ const TokenList &tok = element.Tokens();
+ if (tok.size() < 4) {
+ return "";
+ }
+
+ return ParseTokenAsString(tok[0]);
+}
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+PropertyTable::PropertyTable() :
+ element(nullptr) {
+}
+
+// Is used when dealing with FBX Objects not metadata.
+PropertyTable::PropertyTable(const ElementPtr element) :
+ element(element) {
+ Setup(element);
+}
+
+// ------------------------------------------------------------------------------------------------
+PropertyTable::~PropertyTable() {
+ for (PropertyMap::value_type &v : props) {
+ delete v.second;
+ }
+}
+
+void PropertyTable::Setup(ElementPtr ptr) {
+ const ScopePtr sc = GetRequiredScope(ptr);
+ const ElementPtr Properties70 = sc->GetElement("Properties70");
+ const ScopePtr scope = GetOptionalScope(Properties70);
+
+ // no scope, no care.
+ if (!scope) {
+ return; // NOTE: this is not an error this is actually a Object, without properties, here we will nullptr it.
+ }
+
+ for (const ElementMap::value_type &v : scope->Elements()) {
+ if (v.first != "P") {
+ DOMWarning("expected only P elements in property table", v.second);
+ continue;
+ }
+
+ const std::string &name = PeekPropertyName(*v.second);
+ if (!name.length()) {
+ DOMWarning("could not read property name", v.second);
+ continue;
+ }
+
+ LazyPropertyMap::const_iterator it = lazyProps.find(name);
+ if (it != lazyProps.end()) {
+ DOMWarning("duplicate property name, will hide previous value: " + name, v.second);
+ continue;
+ }
+
+ // since the above checks for duplicates we can be sure to insert the only match here.
+ lazyProps[name] = v.second;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+PropertyPtr PropertyTable::Get(const std::string &name) const {
+ PropertyMap::const_iterator it = props.find(name);
+ if (it == props.end()) {
+ // hasn't been parsed yet?
+ LazyPropertyMap::const_iterator lit = lazyProps.find(name);
+ if (lit != lazyProps.end()) {
+ props[name] = ReadTypedProperty(lit->second);
+ it = props.find(name);
+
+ //ai_assert(it != props.end());
+ }
+
+ if (it == props.end()) {
+ // check property template
+ return nullptr;
+ }
+ }
+
+ return (*it).second;
+}
+
+DirectPropertyMap PropertyTable::GetUnparsedProperties() const {
+ DirectPropertyMap result;
+
+ // Loop through all the lazy properties (which is all the properties)
+ for (const LazyPropertyMap::value_type &element : lazyProps) {
+ // Skip parsed properties
+ if (props.end() != props.find(element.first)) {
+ continue;
+ }
+
+ // Read the element's value.
+ // Wrap the naked pointer (since the call site is required to acquire ownership)
+ // std::unique_ptr from C++11 would be preferred both as a wrapper and a return value.
+ Property *prop = ReadTypedProperty(element.second);
+
+ // Element could not be read. Skip it.
+ if (!prop) {
+ continue;
+ }
+
+ // Add to result
+ result[element.first] = prop;
+ }
+
+ return result;
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXProperties.h b/modules/fbx/fbx_parser/FBXProperties.h
new file mode 100644
index 0000000000..bfd27ac94e
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXProperties.h
@@ -0,0 +1,212 @@
+/*************************************************************************/
+/* FBXProperties.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXProperties.h
+ * @brief FBX dynamic properties
+ */
+#ifndef FBX_PROPERTIES_H
+#define FBX_PROPERTIES_H
+
+#include "FBXParser.h"
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace FBXDocParser {
+
+// Forward declarations
+class Element;
+
+/** Represents a dynamic property. Type info added by deriving classes,
+ * see #TypedProperty.
+ Example:
+ @verbatim
+ P: "ShininessExponent", "double", "Number", "",0.5
+ @endvebatim
+*/
+class Property {
+protected:
+ Property();
+
+public:
+ virtual ~Property();
+
+public:
+ template <typename T>
+ const T *As() const {
+ return dynamic_cast<const T *>(this);
+ }
+};
+
+template <typename T>
+class TypedProperty : public Property {
+public:
+ explicit TypedProperty(const T &value) :
+ value(value) {
+ // empty
+ }
+
+ const T &Value() const {
+ return value;
+ }
+
+private:
+ T value;
+};
+
+#define new_Property new Property
+typedef Property *PropertyPtr;
+typedef std::map<std::string, PropertyPtr> DirectPropertyMap;
+typedef std::map<std::string, PropertyPtr> PropertyMap;
+typedef std::map<std::string, ElementPtr> LazyPropertyMap;
+
+/**
+ * Represents a property table as can be found in the newer FBX files (Properties60, Properties70)
+ */
+class PropertyTable {
+public:
+ // in-memory property table with no source element
+ PropertyTable();
+ PropertyTable(const ElementPtr element);
+ virtual ~PropertyTable();
+
+ PropertyPtr Get(const std::string &name) const;
+ void Setup(ElementPtr ptr);
+
+ // PropertyTable's need not be coupled with FBX elements so this can be NULL
+ ElementPtr GetElement() {
+ return element;
+ }
+
+ PropertyMap &GetProperties() {
+ return props;
+ }
+
+ const LazyPropertyMap &GetLazyProperties() {
+ return lazyProps;
+ }
+
+ DirectPropertyMap GetUnparsedProperties() const;
+
+private:
+ LazyPropertyMap lazyProps;
+ mutable PropertyMap props;
+ ElementPtr element = nullptr;
+};
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline T PropertyGet(const PropertyTable *in, const std::string &name, const T &defaultValue) {
+ PropertyPtr prop = in->Get(name);
+ if (nullptr == prop) {
+ return defaultValue;
+ }
+
+ // strong typing, no need to be lenient
+ const TypedProperty<T> *const tprop = prop->As<TypedProperty<T>>();
+ if (nullptr == tprop) {
+ return defaultValue;
+ }
+
+ return tprop->Value();
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline T PropertyGet(const PropertyTable *in, const std::string &name, bool &result, bool useTemplate = false) {
+ PropertyPtr prop = in->Get(name);
+ if (nullptr == prop) {
+ if (nullptr == in) {
+ result = false;
+ return T();
+ }
+ prop = in->Get(name);
+ if (nullptr == prop) {
+ result = false;
+ return T();
+ }
+ }
+
+ // strong typing, no need to be lenient
+ const TypedProperty<T> *const tprop = prop->As<TypedProperty<T>>();
+ if (nullptr == tprop) {
+ result = false;
+ return T();
+ }
+
+ result = true;
+ return tprop->Value();
+}
+} // namespace FBXDocParser
+
+#endif // FBX_PROPERTIES_H
diff --git a/modules/fbx/fbx_parser/FBXTokenizer.cpp b/modules/fbx/fbx_parser/FBXTokenizer.cpp
new file mode 100644
index 0000000000..81c5b128e8
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXTokenizer.cpp
@@ -0,0 +1,253 @@
+/*************************************************************************/
+/* FBXTokenizer.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXTokenizer.cpp
+ * @brief Implementation of the FBX broadphase lexer
+ */
+
+// tab width for logging columns
+#define ASSIMP_FBX_TAB_WIDTH 4
+
+#include "FBXTokenizer.h"
+#include "core/string/print_string.h"
+
+namespace FBXDocParser {
+
+// ------------------------------------------------------------------------------------------------
+Token::Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column) :
+ sbegin(p_sbegin),
+ send(p_send),
+ type(p_type),
+ line(p_line),
+ column(p_column) {
+#ifdef DEBUG_ENABLED
+ contents = std::string(sbegin, static_cast<size_t>(send - sbegin));
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+Token::~Token() {
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+void TokenizeError(const std::string &message, unsigned int line, unsigned int column) {
+ print_error("[FBX-Tokenize]" + String(message.c_str()) + " " + itos(line) + ":" + itos(column));
+}
+
+// process a potential data token up to 'cur', adding it to 'output_tokens'.
+// ------------------------------------------------------------------------------------------------
+void ProcessDataToken(TokenList &output_tokens, const char *&start, const char *&end,
+ unsigned int line,
+ unsigned int column,
+ TokenType type = TokenType_DATA,
+ bool must_have_token = false) {
+ if (start && end) {
+ // sanity check:
+ // tokens should have no whitespace outside quoted text and [start,end] should
+ // properly delimit the valid range.
+ bool in_double_quotes = false;
+ for (const char *c = start; c != end + 1; ++c) {
+ if (*c == '\"') {
+ in_double_quotes = !in_double_quotes;
+ }
+
+ if (!in_double_quotes && IsSpaceOrNewLine(*c)) {
+ TokenizeError("unexpected whitespace in token", line, column);
+ }
+ }
+
+ if (in_double_quotes) {
+ TokenizeError("non-terminated double quotes", line, column);
+ }
+
+ output_tokens.push_back(new_Token(start, end + 1, type, line, column));
+ } else if (must_have_token) {
+ TokenizeError("unexpected character, expected data token", line, column);
+ }
+
+ start = end = nullptr;
+}
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+void Tokenize(TokenList &output_tokens, const char *input, size_t length, bool &corrupt) {
+ // line and column numbers numbers are one-based
+ unsigned int line = 1;
+ unsigned int column = 1;
+
+ bool comment = false;
+ bool in_double_quotes = false;
+ bool pending_data_token = false;
+
+ const char *token_begin = nullptr, *token_end = nullptr;
+
+ // input (starting string), *cur the current string, column +=
+ // modified to fix strlen() and stop buffer overflow
+ for (size_t x = 0; x < length; x++) {
+ const char c = input[x];
+ const char *cur = &input[x];
+ column += (c == '\t' ? ASSIMP_FBX_TAB_WIDTH : 1);
+
+ if (IsLineEnd(c)) {
+ comment = false;
+
+ column = 0;
+ ++line;
+ }
+
+ if (comment) {
+ continue;
+ }
+
+ if (in_double_quotes) {
+ if (c == '\"') {
+ in_double_quotes = false;
+ token_end = cur;
+
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+ pending_data_token = false;
+ }
+ continue;
+ }
+
+ switch (c) {
+ case '\"':
+ if (token_begin) {
+ TokenizeError("unexpected double-quote", line, column);
+ corrupt = true;
+ return;
+ }
+ token_begin = cur;
+ in_double_quotes = true;
+ continue;
+
+ case ';':
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+ comment = true;
+ continue;
+
+ case '{':
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+ output_tokens.push_back(new_Token(cur, cur + 1, TokenType_OPEN_BRACKET, line, column));
+ continue;
+
+ case '}':
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+ output_tokens.push_back(new_Token(cur, cur + 1, TokenType_CLOSE_BRACKET, line, column));
+ continue;
+
+ case ',':
+ if (pending_data_token) {
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_DATA, true);
+ }
+ output_tokens.push_back(new_Token(cur, cur + 1, TokenType_COMMA, line, column));
+ continue;
+
+ case ':':
+ if (pending_data_token) {
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_KEY, true);
+ } else {
+ TokenizeError("unexpected colon", line, column);
+ }
+ continue;
+ }
+
+ if (IsSpaceOrNewLine(c)) {
+ if (token_begin) {
+ // peek ahead and check if the next token is a colon in which
+ // case this counts as KEY token.
+ TokenType type = TokenType_DATA;
+ for (const char *peek = cur; *peek && IsSpaceOrNewLine(*peek); ++peek) {
+ if (*peek == ':') {
+ type = TokenType_KEY;
+ cur = peek;
+ break;
+ }
+ }
+
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column, type);
+ }
+
+ pending_data_token = false;
+ } else {
+ token_end = cur;
+ if (!token_begin) {
+ token_begin = cur;
+ }
+
+ pending_data_token = true;
+ }
+ }
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXTokenizer.h b/modules/fbx/fbx_parser/FBXTokenizer.h
new file mode 100644
index 0000000000..184d0fd894
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXTokenizer.h
@@ -0,0 +1,203 @@
+/*************************************************************************/
+/* FBXTokenizer.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXTokenizer.h
+ * @brief FBX lexer
+ */
+#ifndef FBX_TOKENIZER_H
+#define FBX_TOKENIZER_H
+
+#include "FBXParseTools.h"
+#include "core/string/ustring.h"
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace FBXDocParser {
+/** Rough classification for text FBX tokens used for constructing the
+ * basic scope hierarchy. */
+enum TokenType {
+ // {
+ TokenType_OPEN_BRACKET = 0,
+
+ // }
+ TokenType_CLOSE_BRACKET,
+
+ // '"blablubb"', '2', '*14' - very general token class,
+ // further processing happens at a later stage.
+ TokenType_DATA,
+
+ //
+ TokenType_BINARY_DATA,
+
+ // ,
+ TokenType_COMMA,
+
+ // blubb:
+ TokenType_KEY
+};
+
+/** Represents a single token in a FBX file. Tokens are
+ * classified by the #TokenType enumerated types.
+ *
+ * Offers iterator protocol. Tokens are immutable. */
+class Token {
+private:
+ static const unsigned int BINARY_MARKER = static_cast<unsigned int>(-1);
+
+public:
+ /** construct a textual token */
+ Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column);
+
+ /** construct a binary token */
+ Token(const char *p_sbegin, const char *p_send, TokenType p_type, size_t p_offset);
+ ~Token();
+
+public:
+ std::string StringContents() const {
+ return std::string(begin(), end());
+ }
+
+ bool IsBinary() const {
+ return column == BINARY_MARKER;
+ }
+
+ const char *begin() const {
+ return sbegin;
+ }
+
+ const char *end() const {
+ return send;
+ }
+
+ TokenType Type() const {
+ return type;
+ }
+
+ size_t Offset() const {
+ return offset;
+ }
+
+ unsigned int Line() const {
+ return static_cast<unsigned int>(line);
+ }
+
+ unsigned int Column() const {
+ return column;
+ }
+
+private:
+#ifdef DEBUG_ENABLED
+ // full string copy for the sole purpose that it nicely appears
+ // in msvc's debugger window.
+ std::string contents;
+#endif
+
+ const char *sbegin = nullptr;
+ const char *send = nullptr;
+ const TokenType type;
+
+ union {
+ size_t line;
+ size_t offset;
+ };
+ const unsigned int column = 0;
+};
+
+// Fixed leak by using shared_ptr for tokens
+typedef Token *TokenPtr;
+typedef std::vector<TokenPtr> TokenList;
+
+#define new_Token new Token
+
+/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens.
+ *
+ * Skips over comments and generates line and column numbers.
+ *
+ * @param output_tokens Receives a list of all tokens in the input data.
+ * @param input_buffer Textual input buffer to be processed, 0-terminated.
+ * @print_error if something goes wrong */
+void Tokenize(TokenList &output_tokens, const char *input, size_t length, bool &corrupt);
+
+/** Tokenizer function for binary FBX files.
+ *
+ * Emits a token list suitable for direct parsing.
+ *
+ * @param output_tokens Receives a list of all tokens in the input data.
+ * @param input_buffer Binary input buffer to be processed.
+ * @param length Length of input buffer, in bytes. There is no 0-terminal.
+ * @print_error if something goes wrong */
+void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length, bool &corrupt);
+} // namespace FBXDocParser
+
+#endif // FBX_TOKENIZER_H
diff --git a/modules/fbx/fbx_parser/FBXUtil.cpp b/modules/fbx/fbx_parser/FBXUtil.cpp
new file mode 100644
index 0000000000..df46bd85c7
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXUtil.cpp
@@ -0,0 +1,222 @@
+/*************************************************************************/
+/* FBXUtil.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXUtil.cpp
+ * @brief Implementation of internal FBX utility functions
+ */
+
+#include "FBXUtil.h"
+#include "FBXTokenizer.h"
+#include <cstring>
+#include <string>
+
+namespace FBXDocParser {
+namespace Util {
+
+// ------------------------------------------------------------------------------------------------
+const char *TokenTypeString(TokenType t) {
+ switch (t) {
+ case TokenType_OPEN_BRACKET:
+ return "TOK_OPEN_BRACKET";
+
+ case TokenType_CLOSE_BRACKET:
+ return "TOK_CLOSE_BRACKET";
+
+ case TokenType_DATA:
+ return "TOK_DATA";
+
+ case TokenType_COMMA:
+ return "TOK_COMMA";
+
+ case TokenType_KEY:
+ return "TOK_KEY";
+
+ case TokenType_BINARY_DATA:
+ return "TOK_BINARY_DATA";
+ }
+
+ //ai_assert(false);
+ return "";
+}
+
+// Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
+static const uint8_t base64DecodeTable[128] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255,
+ 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
+ 255, 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, 255, 255, 255, 255, 255
+};
+
+uint8_t DecodeBase64(char ch) {
+ const uint8_t idx = static_cast<uint8_t>(ch);
+ if (idx > 127) {
+ return 255;
+ }
+ return base64DecodeTable[idx];
+}
+
+size_t ComputeDecodedSizeBase64(const char *in, size_t inLength) {
+ if (inLength < 2) {
+ return 0;
+ }
+ const size_t equals = size_t(in[inLength - 1] == '=') + size_t(in[inLength - 2] == '=');
+ const size_t full_length = (inLength * 3) >> 2; // div by 4
+ if (full_length < equals) {
+ return 0;
+ }
+ return full_length - equals;
+}
+
+size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength) {
+ if (maxOutLength == 0 || inLength < 2) {
+ return 0;
+ }
+ const size_t realLength = inLength - size_t(in[inLength - 1] == '=') - size_t(in[inLength - 2] == '=');
+ size_t dst_offset = 0;
+ int val = 0, valb = -8;
+ for (size_t src_offset = 0; src_offset < realLength; ++src_offset) {
+ const uint8_t table_value = Util::DecodeBase64(in[src_offset]);
+ if (table_value == 255) {
+ return 0;
+ }
+ val = (val << 6) + table_value;
+ valb += 6;
+ if (valb >= 0) {
+ out[dst_offset++] = static_cast<uint8_t>((val >> valb) & 0xFF);
+ valb -= 8;
+ val &= 0xFFF;
+ }
+ }
+ return dst_offset;
+}
+
+static const char to_base64_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+char EncodeBase64(char byte) {
+ return to_base64_string[(size_t)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.
+ */
+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);
+ char b2 = (bytes[1] & 0x0F) << 2 | ((bytes[2] & 0xC0) >> 6);
+ char b3 = (bytes[2] & 0x3F);
+
+ out_string[string_pos + 0] = EncodeBase64(b0);
+ out_string[string_pos + 1] = EncodeBase64(b1);
+ out_string[string_pos + 2] = EncodeBase64(b2);
+ out_string[string_pos + 3] = EncodeBase64(b3);
+}
+
+std::string EncodeBase64(const char *data, size_t length) {
+ // calculate extra bytes needed to get a multiple of 3
+ size_t extraBytes = 3 - length % 3;
+
+ // number of base64 bytes
+ size_t encodedBytes = 4 * (length + extraBytes) / 3;
+
+ std::string encoded_string(encodedBytes, '=');
+
+ // read blocks of 3 bytes
+ for (size_t ib3 = 0; ib3 < length / 3; ib3++) {
+ const size_t iByte = ib3 * 3;
+ const size_t iEncodedByte = ib3 * 4;
+ const char *currData = &data[iByte];
+
+ EncodeByteBlock(currData, encoded_string, iEncodedByte);
+ }
+
+ // if size of data is not a multiple of 3, also encode the final bytes (and add zeros where needed)
+ if (extraBytes > 0) {
+ char finalBytes[4] = { 0, 0, 0, 0 };
+ memcpy(&finalBytes[0], &data[length - length % 3], length % 3);
+
+ const size_t iEncodedByte = encodedBytes - 4;
+ EncodeByteBlock(&finalBytes[0], encoded_string, iEncodedByte);
+
+ // add '=' at the end
+ for (size_t i = 0; i < 4 * extraBytes / 3; i++) {
+ encoded_string[encodedBytes - i - 1] = '=';
+ }
+ }
+ return encoded_string;
+}
+} // namespace Util
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXUtil.h b/modules/fbx/fbx_parser/FBXUtil.h
new file mode 100644
index 0000000000..dab2a4201e
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXUtil.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* FBXUtil.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. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file FBXUtil.h
+ * @brief FBX utility functions for internal use
+ */
+#ifndef FBX_UTIL_H
+#define FBX_UTIL_H
+
+#include "FBXTokenizer.h"
+#include <stdint.h>
+
+namespace FBXDocParser {
+
+namespace Util {
+
+/** Get a string representation for a #TokenType. */
+const char *TokenTypeString(TokenType t);
+
+/** Decode a single Base64-encoded character.
+ *
+ * @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)*/
+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)*/
+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*/
+std::string EncodeBase64(const char *data, size_t length);
+} // namespace Util
+} // namespace FBXDocParser
+
+#endif // FBX_UTIL_H
diff --git a/modules/fbx/fbx_parser/LICENSE b/modules/fbx/fbx_parser/LICENSE
new file mode 100644
index 0000000000..b42fc6efe6
--- /dev/null
+++ b/modules/fbx/fbx_parser/LICENSE
@@ -0,0 +1,39 @@
+The files in this folder were originally from ASSIMP, but have been heavily modified to fix bugs and match coding
+conventions of the Godot Engine project. We have kept a copy of the applicable licenses in the folder as required by
+the license.
+
+Open Asset Import Library (assimp)
+
+Copyright (c) 2006-2020, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the
+following conditions are met:
+
+* Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior
+ written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/modules/assimp/register_types.cpp b/modules/fbx/register_types.cpp
index 6cb0fc982f..d5e520a060 100644
--- a/modules/assimp/register_types.cpp
+++ b/modules/fbx/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,22 +31,22 @@
#include "register_types.h"
#include "editor/editor_node.h"
-#include "editor_scene_importer_assimp.h"
+#include "editor_scene_importer_fbx.h"
#ifdef TOOLS_ENABLED
static void _editor_init() {
- Ref<EditorSceneImporterAssimp> import_assimp;
- import_assimp.instance();
- ResourceImporterScene::get_singleton()->add_importer(import_assimp);
+ Ref<EditorSceneFormatImporterFBX> import_fbx;
+ import_fbx.instantiate();
+ ResourceImporterScene::get_singleton()->add_importer(import_fbx);
}
#endif
-void register_assimp_types() {
+void register_fbx_types() {
#ifdef TOOLS_ENABLED
ClassDB::APIType prev_api = ClassDB::get_current_api();
ClassDB::set_current_api(ClassDB::API_EDITOR);
- ClassDB::register_class<EditorSceneImporterAssimp>();
+ GDREGISTER_CLASS(EditorSceneFormatImporterFBX);
ClassDB::set_current_api(prev_api);
@@ -54,5 +54,5 @@ void register_assimp_types() {
#endif
}
-void unregister_assimp_types() {
+void unregister_fbx_types() {
}
diff --git a/modules/gdnative/net/register_types.h b/modules/fbx/register_types.h
index 70b266f9b9..e5741afd72 100644
--- a/modules/gdnative/net/register_types.h
+++ b/modules/fbx/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,10 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef NET_REGISTER_TYPES_H
-#define NET_REGISTER_TYPES_H
+#ifndef FBX_REGISTER_TYPES_H
+#define FBX_REGISTER_TYPES_H
-void register_net_types();
-void unregister_net_types();
+void register_fbx_types();
+void unregister_fbx_types();
-#endif // NET_REGISTER_TYPES_H
+#endif // FBX_REGISTER_TYPES_H
diff --git a/modules/fbx/tools/import_utils.cpp b/modules/fbx/tools/import_utils.cpp
new file mode 100644
index 0000000000..bb95d120af
--- /dev/null
+++ b/modules/fbx/tools/import_utils.cpp
@@ -0,0 +1,151 @@
+/*************************************************************************/
+/* import_utils.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 "import_utils.h"
+
+Vector3 ImportUtils::deg2rad(const Vector3 &p_rotation) {
+ return p_rotation / 180.0 * Math_PI;
+}
+
+Vector3 ImportUtils::rad2deg(const Vector3 &p_rotation) {
+ return p_rotation / Math_PI * 180.0;
+}
+
+Basis ImportUtils::EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation) {
+ Basis ret;
+
+ // FBX is using intrinsic euler, we can convert intrinsic to extrinsic (the one used in godot
+ // 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(p_rotation, Basis::EULER_ORDER_XYZ);
+ break;
+
+ case FBXDocParser::Model::RotOrder_EulerXZY:
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_XZY);
+ break;
+
+ case FBXDocParser::Model::RotOrder_EulerYZX:
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_YZX);
+ break;
+
+ case FBXDocParser::Model::RotOrder_EulerYXZ:
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_YXZ);
+ break;
+
+ case FBXDocParser::Model::RotOrder_EulerZXY:
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_ZXY);
+ break;
+
+ case FBXDocParser::Model::RotOrder_EulerZYX:
+ ret.set_euler(p_rotation, Basis::EULER_ORDER_ZYX);
+ break;
+
+ case FBXDocParser::Model::RotOrder_SphericXYZ:
+ // TODO do this.
+ break;
+
+ default:
+ // If you land here, Please integrate all enums.
+ CRASH_NOW_MSG("This is not unreachable.");
+ }
+
+ return ret;
+}
+
+Quaternion ImportUtils::EulerToQuaternion(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation) {
+ return ImportUtils::EulerToBasis(mode, p_rotation);
+}
+
+Vector3 ImportUtils::BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basis &p_rotation) {
+ // FBX is using intrinsic euler, we can convert intrinsic to extrinsic (the one used in godot
+ // 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(Basis::EULER_ORDER_XYZ);
+
+ case FBXDocParser::Model::RotOrder_EulerXZY:
+ return p_rotation.get_euler(Basis::EULER_ORDER_XZY);
+
+ case FBXDocParser::Model::RotOrder_EulerYZX:
+ return p_rotation.get_euler(Basis::EULER_ORDER_YZX);
+
+ case FBXDocParser::Model::RotOrder_EulerYXZ:
+ return p_rotation.get_euler(Basis::EULER_ORDER_YXZ);
+
+ case FBXDocParser::Model::RotOrder_EulerZXY:
+ return p_rotation.get_euler(Basis::EULER_ORDER_ZXY);
+
+ case FBXDocParser::Model::RotOrder_EulerZYX:
+ return p_rotation.get_euler(Basis::EULER_ORDER_ZYX);
+
+ case FBXDocParser::Model::RotOrder_SphericXYZ:
+ // TODO
+ return Vector3();
+
+ default:
+ // If you land here, Please integrate all enums.
+ CRASH_NOW_MSG("This is not unreachable.");
+ return Vector3();
+ }
+}
+
+Vector3 ImportUtils::QuaternionToEuler(FBXDocParser::Model::RotOrder mode, const Quaternion &p_rotation) {
+ return BasisToEuler(mode, p_rotation);
+}
+
+Transform3D get_unscaled_transform(const Transform3D &p_initial, real_t p_scale) {
+ Transform3D unscaled = Transform3D(p_initial.basis, p_initial.origin * p_scale);
+ ERR_FAIL_COND_V_MSG(unscaled.basis.determinant() == 0, Transform3D(), "det is zero unscaled?");
+ return unscaled;
+}
+
+Vector3 get_poly_normal(const std::vector<Vector3> &p_vertices) {
+ ERR_FAIL_COND_V_MSG(p_vertices.size() < 3, Vector3(0, 0, 0), "At least 3 vertices are necessary");
+ // Using long double to make sure that normal is computed for even really tiny objects.
+ typedef long double ldouble;
+ ldouble x = 0.0;
+ ldouble y = 0.0;
+ ldouble z = 0.0;
+ for (size_t i = 0; i < p_vertices.size(); i += 1) {
+ const Vector3 current = p_vertices[i];
+ const Vector3 next = p_vertices[(i + 1) % p_vertices.size()];
+ x += (ldouble(current.y) - ldouble(next.y)) * (ldouble(current.z) + ldouble(next.z));
+ y += (ldouble(current.z) - ldouble(next.z)) * (ldouble(current.x) + ldouble(next.x));
+ z += (ldouble(current.x) - ldouble(next.x)) * (ldouble(current.y) + ldouble(next.y));
+ }
+ const ldouble l2 = x * x + y * y + z * z;
+ if (l2 == 0.0) {
+ return (p_vertices[0] - p_vertices[1]).normalized().cross((p_vertices[0] - p_vertices[2]).normalized()).normalized();
+ } else {
+ const double l = Math::sqrt(double(l2));
+ return Vector3(x / l, y / l, z / l);
+ }
+}
diff --git a/modules/fbx/tools/import_utils.h b/modules/fbx/tools/import_utils.h
new file mode 100644
index 0000000000..88c71fb87e
--- /dev/null
+++ b/modules/fbx/tools/import_utils.h
@@ -0,0 +1,400 @@
+/*************************************************************************/
+/* import_utils.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 IMPORT_UTILS_FBX_IMPORTER_H
+#define IMPORT_UTILS_FBX_IMPORTER_H
+
+#include "core/io/image_loader.h"
+
+#include "data/import_state.h"
+#include "fbx_parser/FBXDocument.h"
+
+#include <string>
+
+#define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000LL
+
+/**
+ * Import Utils
+ * Conversion tools / glue code to convert from FBX to Godot
+ */
+class ImportUtils {
+public:
+ /// Convert a vector from degrees to radians.
+ static Vector3 deg2rad(const Vector3 &p_rotation);
+
+ /// Convert a vector from radians to degrees.
+ static Vector3 rad2deg(const Vector3 &p_rotation);
+
+ /// Converts rotation order vector (in rad) to quaternion.
+ static Basis EulerToBasis(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation);
+
+ /// Converts rotation order vector (in rad) to quaternion.
+ static Quaternion EulerToQuaternion(FBXDocParser::Model::RotOrder mode, const Vector3 &p_rotation);
+
+ /// Converts basis into rotation order vector (in rad).
+ static Vector3 BasisToEuler(FBXDocParser::Model::RotOrder mode, const Basis &p_rotation);
+
+ /// Converts quaternion into rotation order vector (in rad).
+ static Vector3 QuaternionToEuler(FBXDocParser::Model::RotOrder mode, const Quaternion &p_rotation);
+
+ static void debug_xform(String name, const Transform3D &t) {
+ print_verbose(name + " " + t.origin + " rotation: " + (t.basis.get_euler() * (180 / Math_PI)));
+ }
+
+ static String FBXNodeToName(const std::string &name) {
+ // strip Model:: prefix, avoiding ambiguities (i.e. don't strip if
+ // this causes ambiguities, well possible between empty identifiers,
+ // such as "Model::" and ""). Make sure the behaviour is consistent
+ // across multiple calls to FixNodeName().
+
+ // We must remove this from the name
+ // Some bones have this
+ // SubDeformer::
+ // Meshes, Joints have this, some other IK elements too.
+ // Model::
+
+ String node_name = String(name.c_str());
+
+ if (node_name.substr(0, 7) == "Model::") {
+ node_name = node_name.substr(7, node_name.length() - 7);
+ return node_name.replace(":", "");
+ }
+
+ if (node_name.substr(0, 13) == "SubDeformer::") {
+ node_name = node_name.substr(13, node_name.length() - 13);
+ return node_name.replace(":", "");
+ }
+
+ if (node_name.substr(0, 11) == "AnimStack::") {
+ node_name = node_name.substr(11, node_name.length() - 11);
+ return node_name.replace(":", "");
+ }
+
+ if (node_name.substr(0, 15) == "AnimCurveNode::") {
+ node_name = node_name.substr(15, node_name.length() - 15);
+ return node_name.replace(":", "");
+ }
+
+ if (node_name.substr(0, 11) == "AnimCurve::") {
+ node_name = node_name.substr(11, node_name.length() - 11);
+ return node_name.replace(":", "");
+ }
+
+ if (node_name.substr(0, 10) == "Geometry::") {
+ node_name = node_name.substr(10, node_name.length() - 10);
+ return node_name.replace(":", "");
+ }
+
+ if (node_name.substr(0, 10) == "Material::") {
+ node_name = node_name.substr(10, node_name.length() - 10);
+ return node_name.replace(":", "");
+ }
+
+ if (node_name.substr(0, 9) == "Texture::") {
+ node_name = node_name.substr(9, node_name.length() - 9);
+ return node_name.replace(":", "");
+ }
+
+ return node_name.replace(":", "");
+ }
+
+ static std::string FBXAnimMeshName(const std::string &name) {
+ if (name.length()) {
+ size_t indexOf = name.find_first_of("::");
+ if (indexOf != std::string::npos && indexOf < name.size() - 2) {
+ return name.substr(indexOf + 2);
+ }
+ }
+ return name.length() ? name : "AnimMesh";
+ }
+
+ static Vector3 safe_import_vector3(const Vector3 &p_vec) {
+ Vector3 vector = p_vec;
+ if (Math::is_zero_approx(vector.x)) {
+ vector.x = 0;
+ }
+
+ if (Math::is_zero_approx(vector.y)) {
+ vector.y = 0;
+ }
+
+ if (Math::is_zero_approx(vector.z)) {
+ vector.z = 0;
+ }
+ return vector;
+ }
+
+ static void debug_xform(String name, const Basis &t) {
+ //print_verbose(name + " rotation: " + (t.get_euler() * (180 / Math_PI)));
+ }
+
+ static Vector3 FixAxisConversions(Vector3 input) {
+ return Vector3(input.x, input.y, input.z);
+ }
+
+ static void AlignMeshAxes(std::vector<Vector3> &vertex_data) {
+ for (size_t x = 0; x < vertex_data.size(); x++) {
+ vertex_data[x] = FixAxisConversions(vertex_data[x]);
+ }
+ }
+
+ struct AssetImportFbx {
+ enum ETimeMode {
+ TIME_MODE_DEFAULT = 0,
+ TIME_MODE_120 = 1,
+ TIME_MODE_100 = 2,
+ TIME_MODE_60 = 3,
+ TIME_MODE_50 = 4,
+ TIME_MODE_48 = 5,
+ TIME_MODE_30 = 6,
+ TIME_MODE_30_DROP = 7,
+ TIME_MODE_NTSC_DROP_FRAME = 8,
+ TIME_MODE_NTSC_FULL_FRAME = 9,
+ TIME_MODE_PAL = 10,
+ TIME_MODE_CINEMA = 11,
+ TIME_MODE_1000 = 12,
+ TIME_MODE_CINEMA_ND = 13,
+ TIME_MODE_CUSTOM = 14,
+ TIME_MODE_TIME_MODE_COUNT = 15
+ };
+ enum UpAxis {
+ UP_VECTOR_AXIS_X = 1,
+ UP_VECTOR_AXIS_Y = 2,
+ UP_VECTOR_AXIS_Z = 3
+ };
+ enum FrontAxis {
+ FRONT_PARITY_EVEN = 1,
+ FRONT_PARITY_ODD = 2,
+ };
+
+ enum CoordAxis {
+ COORD_RIGHT = 0,
+ COORD_LEFT = 1
+ };
+ };
+
+ /** 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:
+ return 24;
+ case AssetImportFbx::TIME_MODE_120:
+ return 120;
+ case AssetImportFbx::TIME_MODE_100:
+ return 100;
+ case AssetImportFbx::TIME_MODE_60:
+ return 60;
+ case AssetImportFbx::TIME_MODE_50:
+ return 50;
+ case AssetImportFbx::TIME_MODE_48:
+ return 48;
+ case AssetImportFbx::TIME_MODE_30:
+ return 30;
+ case AssetImportFbx::TIME_MODE_30_DROP:
+ return 30;
+ case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME:
+ return 29.9700262f;
+ case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME:
+ return 29.9700262f;
+ case AssetImportFbx::TIME_MODE_PAL:
+ return 25;
+ case AssetImportFbx::TIME_MODE_CINEMA:
+ return 24;
+ case AssetImportFbx::TIME_MODE_1000:
+ return 1000;
+ case AssetImportFbx::TIME_MODE_CINEMA_ND:
+ return 23.976f;
+ case AssetImportFbx::TIME_MODE_CUSTOM:
+ return -1;
+ }
+ return 0;
+ }
+
+ static float get_fbx_fps(const FBXDocParser::FileGlobalSettings *FBXSettings) {
+ int time_mode = FBXSettings->TimeMode();
+
+ // get the animation FPS
+ float frames_per_second = get_fbx_fps(time_mode);
+
+ // handle animation custom FPS time.
+ if (time_mode == ImportUtils::AssetImportFbx::TIME_MODE_CUSTOM) {
+ print_verbose("FBX Animation has custom FPS setting");
+ frames_per_second = FBXSettings->CustomFrameRate();
+
+ // not our problem this is the modeller, we can print as an error so they can fix the source.
+ if (frames_per_second == 0) {
+ print_error("Custom animation time in file is set to 0 value, animation won't play, please edit your file to correct the FPS value");
+ }
+ }
+ return frames_per_second;
+ }
+
+ /**
+ * 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)
+ */
+ // static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) {
+ // ERR_FAIL_COND(texture.is_null());
+ // ERR_FAIL_COND(map_mode == nullptr);
+ // aiTextureMapMode tex_mode = map_mode[0];
+
+ // int32_t flags = Texture::FLAGS_DEFAULT;
+ // if (tex_mode == aiTextureMapMode_Wrap) {
+ // //Default
+ // } else if (tex_mode == aiTextureMapMode_Clamp) {
+ // flags = flags & ~Texture::FLAG_REPEAT;
+ // } else if (tex_mode == aiTextureMapMode_Mirror) {
+ // flags = flags | Texture::FLAG_MIRRORED_REPEAT;
+ // }
+ // texture->set_flags(flags);
+ // }
+
+ /**
+ * 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);
+
+ // // if our cache contains this image then don't bother
+ // if (match) {
+ // return match->get();
+ // }
+
+ // Vector<String> split_path = p_path.get_basename().split("*");
+ // if (split_path.size() == 2) {
+ // size_t texture_idx = split_path[1].to_int();
+ // ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Image>());
+ // aiTexture *tex = p_scene->mTextures[texture_idx];
+ // String filename = AssimpUtils::get_raw_string_from_assimp(tex->mFilename);
+ // filename = filename.get_file();
+ // print_verbose("Open Asset Import: Loading embedded texture " + filename);
+ // if (tex->mHeight == 0) {
+ // if (tex->CheckFormat("png")) {
+ // Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
+ // ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+ // state.path_to_image_cache.insert(p_path, img);
+ // return img;
+ // } else if (tex->CheckFormat("jpg")) {
+ // Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
+ // ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+ // state.path_to_image_cache.insert(p_path, img);
+ // return img;
+ // } else if (tex->CheckFormat("dds")) {
+ // ERR_FAIL_COND_V_MSG(true, Ref<Image>(), "Open Asset Import: Embedded dds not implemented");
+ // }
+ // } else {
+ // Ref<Image> img;
+ // img.instantiate();
+ // PoolByteArray arr;
+ // uint32_t size = tex->mWidth * tex->mHeight;
+ // arr.resize(size);
+ // memcpy(arr.write().ptr(), tex->pcData, size);
+ // ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Image>());
+ // //ARGB8888 to RGBA8888
+ // for (int32_t i = 0; i < arr.size() / 4; i++) {
+ // arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0];
+ // arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1];
+ // arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2];
+ // arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3];
+ // }
+ // img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr);
+ // ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+ // state.path_to_image_cache.insert(p_path, img);
+ // return img;
+ // }
+ // return Ref<Image>();
+ // } else {
+ // Ref<Texture> texture = ResourceLoader::load(p_path);
+ // ERR_FAIL_COND_V(texture.is_null(), Ref<Image>());
+ // Ref<Image> image = texture->get_image();
+ // ERR_FAIL_COND_V(image.is_null(), Ref<Image>());
+ // state.path_to_image_cache.insert(p_path, image);
+ // return image;
+ // }
+
+ // return Ref<Image>();
+ //}
+
+ // /* create texture from assimp data, if found in path */
+ // static bool CreateAssimpTexture(
+ // AssimpImporter::ImportState &state,
+ // aiString texture_path,
+ // String &filename,
+ // String &path,
+ // AssimpImageData &image_state) {
+ // filename = get_raw_string_from_assimp(texture_path);
+ // path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
+ // bool found = false;
+ // find_texture_path(state.path, path, found);
+ // if (found) {
+ // image_state.raw_image = AssimpUtils::load_image(state, state.assimp_scene, path);
+ // if (image_state.raw_image.is_valid()) {
+ // image_state.texture.instantiate();
+ // image_state.texture->create_from_image(image_state.raw_image);
+ // image_state.texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
+ // return true;
+ // }
+ // }
+
+ // return false;
+ // }
+ // /** GetAssimpTexture
+ // * Designed to retrieve textures for you
+ // */
+ // static bool GetAssimpTexture(
+ // AssimpImporter::ImportState &state,
+ // aiMaterial *ai_material,
+ // aiTextureType texture_type,
+ // String &filename,
+ // String &path,
+ // AssimpImageData &image_state) {
+ // aiString ai_filename = aiString();
+ // if (AI_SUCCESS == ai_material->GetTexture(texture_type, 0, &ai_filename, nullptr, nullptr, nullptr, nullptr, image_state.map_mode)) {
+ // return CreateAssimpTexture(state, ai_filename, filename, path, image_state);
+ // }
+
+ // return false;
+ // }
+};
+
+// Apply the transforms so the basis will have scale 1.
+Transform3D get_unscaled_transform(const Transform3D &p_initial, real_t p_scale);
+
+/// Uses the Newell's method to compute any polygon normal.
+/// The polygon must be at least size of 3 or bigger.
+Vector3 get_poly_normal(const std::vector<Vector3> &p_vertices);
+
+#endif // IMPORT_UTILS_FBX_IMPORTER_H
diff --git a/modules/fbx/tools/validation_tools.cpp b/modules/fbx/tools/validation_tools.cpp
new file mode 100644
index 0000000000..9dbd8bf544
--- /dev/null
+++ b/modules/fbx/tools/validation_tools.cpp
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* validation_tools.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 "validation_tools.h"
+
+#ifdef TOOLS_ENABLED
+
+#include "core/string/print_string.h"
+#include "core/string/ustring.h"
+
+ValidationTracker::Entries *ValidationTracker::entries_singleton = memnew(ValidationTracker::Entries);
+
+// for printing our CSV to dump validation problems of files
+// later we can make some agnostic tooling for this but this is fine for the time being.
+void ValidationTracker::Entries::add_validation_error(String asset_path, String message) {
+ print_error(message);
+ // note: implementation is static
+ validation_entries[asset_path].push_back(message);
+}
+
+#endif // TOOLS_ENABLED
diff --git a/modules/fbx/tools/validation_tools.h b/modules/fbx/tools/validation_tools.h
new file mode 100644
index 0000000000..12d644ee94
--- /dev/null
+++ b/modules/fbx/tools/validation_tools.h
@@ -0,0 +1,92 @@
+/*************************************************************************/
+/* validation_tools.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 FBX_VALIDATION_TOOLS_H
+#define FBX_VALIDATION_TOOLS_H
+
+#ifdef TOOLS_ENABLED
+
+#include "core/io/file_access.h"
+#include "core/string/print_string.h"
+#include "core/templates/local_vector.h"
+#include "core/templates/map.h"
+
+class ValidationTracker {
+protected:
+ struct Entries {
+ Map<String, LocalVector<String>> validation_entries = Map<String, LocalVector<String>>();
+
+ // for printing our CSV to dump validation problems of files
+ // later we can make some agnostic tooling for this but this is fine for the time being.
+ void add_validation_error(String asset_path, String message);
+ void print_to_csv() {
+ print_verbose("Exporting assset validation log please wait");
+ String massive_log_file;
+
+ String csv_header = "file_path, error message, extra data\n";
+ massive_log_file += csv_header;
+
+ 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;
+ }
+ }
+
+ String path = "asset_validation_errors.csv";
+ Error err;
+ FileAccess *file = FileAccess::open(path, FileAccess::WRITE, &err);
+ if (!file || err) {
+ if (file) {
+ memdelete(file);
+ }
+ print_error("ValidationTracker Error - failed to create file - path: %s\n" + path);
+ return;
+ }
+
+ file->store_string(massive_log_file);
+ if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
+ print_error("ValidationTracker Error - failed to write to file - path: %s\n" + path);
+ }
+ file->close();
+ memdelete(file);
+ }
+ };
+ // asset path, error messages
+ static Entries *entries_singleton;
+
+public:
+ static Entries *get_singleton() {
+ return entries_singleton;
+ }
+};
+
+#endif // TOOLS_ENABLED
+#endif // FBX_VALIDATION_TOOLS_H
diff --git a/modules/freetype/SCsub b/modules/freetype/SCsub
index bfc1658bb4..476cb9cf2a 100644
--- a/modules/freetype/SCsub
+++ b/modules/freetype/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_freetype = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_freetype"]:
thirdparty_dir = "#thirdparty/freetype/"
thirdparty_sources = [
@@ -77,6 +80,7 @@ if env["builtin_freetype"]:
# Forcibly undefine this macro so SIMD is not used in this file,
# since currently unsupported in WASM
tmp_env = env_freetype.Clone()
+ tmp_env.disable_warnings()
tmp_env.Append(CPPFLAGS=["-U__OPTIMIZE__"])
sfnt = tmp_env.Object(sfnt)
thirdparty_sources += [sfnt]
@@ -84,6 +88,7 @@ if env["builtin_freetype"]:
env_thirdparty = env_freetype.Clone()
env_thirdparty.disable_warnings()
lib = env_thirdparty.add_library("freetype_builtin", thirdparty_sources)
+ thirdparty_obj += lib
# Needs to be appended to arrive after libscene in the linker call,
# but we don't want it to arrive *after* system libs, so manual hack
@@ -98,5 +103,13 @@ if env["builtin_freetype"]:
if not inserted:
env.Append(LIBS=[lib])
+
# Godot source files
-env_freetype.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_freetype.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/freetype/register_types.cpp b/modules/freetype/register_types.cpp
index 15fa193183..e4e6a4c146 100644
--- a/modules/freetype/register_types.cpp
+++ b/modules/freetype/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/freetype/register_types.h b/modules/freetype/register_types.h
index aa8088d2e8..7a4f64b54b 100644
--- a/modules/freetype/register_types.h
+++ b/modules/freetype/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/freetype/uwpdef.h b/modules/freetype/uwpdef.h
index 2f9b2f4a53..f829edea67 100644
--- a/modules/freetype/uwpdef.h
+++ b/modules/freetype/uwpdef.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub
index 0e2291c1f9..f7f21a433e 100644
--- a/modules/gdnative/SCsub
+++ b/modules/gdnative/SCsub
@@ -16,8 +16,6 @@ env_gdnative.Prepend(CPPPATH=["#modules/gdnative/include/"])
Export("env_gdnative")
-SConscript("net/SCsub")
-SConscript("xr/SCsub")
SConscript("pluginscript/SCsub")
SConscript("videodecoder/SCsub")
@@ -30,5 +28,3 @@ _, gensource = env_gdnative.CommandNoCache(
env.Run(gdnative_builders.build_gdnative_api_struct, "Generating GDNative API."),
)
env_gdnative.add_source_files(env.modules_sources, [gensource])
-
-env.use_ptrcall = True
diff --git a/modules/gdnative/android/android_gdn.cpp b/modules/gdnative/android/android_gdn.cpp
index bc39be1813..fe3b3e7e12 100644
--- a/modules/gdnative/android/android_gdn.cpp
+++ b/modules/gdnative/android/android_gdn.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -48,7 +48,7 @@ extern "C" {
JNIEnv *GDAPI godot_android_get_env() {
#ifdef __ANDROID__
- return ThreadAndroid::get_env();
+ return get_jni_env();
#else
return nullptr;
#endif
diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py
index 7603e7d69d..026a84a70f 100644
--- a/modules/gdnative/config.py
+++ b/modules/gdnative/config.py
@@ -3,22 +3,16 @@ def can_build(env, platform):
def configure(env):
- env.use_ptrcall = True
+ pass
def get_doc_classes():
return [
- "XRInterfaceGDNative",
"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 44d9e32ed8..4bc149b119 100644
--- a/modules/gdnative/doc_classes/GDNative.xml
+++ b/modules/gdnative/doc_classes/GDNative.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GDNative" inherits="Reference" version="4.0">
+<class name="GDNative" inherits="RefCounted" version="4.0">
<brief_description>
</brief_description>
<description>
@@ -8,26 +8,20 @@
</tutorials>
<methods>
<method name="call_native">
- <return type="Variant">
- </return>
- <argument index="0" name="calling_type" type="StringName">
- </argument>
- <argument index="1" name="procedure_name" type="StringName">
- </argument>
- <argument index="2" name="arguments" type="Array">
- </argument>
+ <return type="Variant" />
+ <argument index="0" name="calling_type" type="StringName" />
+ <argument index="1" name="procedure_name" type="StringName" />
+ <argument index="2" name="arguments" type="Array" />
<description>
</description>
</method>
<method name="initialize">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
</description>
</method>
<method name="terminate">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
</description>
</method>
@@ -36,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 05cda05f9f..21df640ebc 100644
--- a/modules/gdnative/doc_classes/GDNativeLibrary.xml
+++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml
@@ -4,23 +4,21 @@
An external library containing functions or script classes to use in Godot.
</brief_description>
<description>
- 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.
+ 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">
- <return type="PackedStringArray">
- </return>
+ <return type="PackedStringArray" />
<description>
Returns paths to all dependency libraries for the current platform and architecture.
</description>
</method>
<method name="get_current_library_path" qualifiers="const">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Returns the path to the dynamic library file for the current platform and architecture.
</description>
@@ -47,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/MultiplayerPeerGDNative.xml b/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml
deleted file mode 100644
index 9f33d32e81..0000000000
--- a/modules/gdnative/doc_classes/MultiplayerPeerGDNative.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="MultiplayerPeerGDNative" inherits="NetworkedMultiplayerPeer" version="4.0">
- <brief_description>
- </brief_description>
- <description>
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/gdnative/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml
index f2e9cac6dc..221374a7a4 100644
--- a/modules/gdnative/doc_classes/NativeScript.xml
+++ b/modules/gdnative/doc_classes/NativeScript.xml
@@ -8,45 +8,37 @@
</tutorials>
<methods>
<method name="get_class_documentation" qualifiers="const">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Returns the documentation string that was previously set with [code]godot_nativescript_set_class_documentation[/code].
</description>
</method>
<method name="get_method_documentation" qualifiers="const">
- <return type="String">
- </return>
- <argument index="0" name="method" type="StringName">
- </argument>
+ <return type="String" />
+ <argument index="0" name="method" type="StringName" />
<description>
Returns the documentation string that was previously set with [code]godot_nativescript_set_method_documentation[/code].
</description>
</method>
<method name="get_property_documentation" qualifiers="const">
- <return type="String">
- </return>
- <argument index="0" name="path" type="StringName">
- </argument>
+ <return type="String" />
+ <argument index="0" name="path" type="StringName" />
<description>
Returns the documentation string that was previously set with [code]godot_nativescript_set_property_documentation[/code].
</description>
</method>
<method name="get_signal_documentation" qualifiers="const">
- <return type="String">
- </return>
- <argument index="0" name="signal_name" type="StringName">
- </argument>
+ <return type="String" />
+ <argument index="0" name="signal_name" type="StringName" />
<description>
Returns the documentation string that was previously set with [code]godot_nativescript_set_signal_documentation[/code].
</description>
</method>
<method name="new" qualifiers="vararg">
- <return type="Variant">
- </return>
+ <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>
@@ -60,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 9616101090..ec80ade394 100644
--- a/modules/gdnative/doc_classes/PluginScript.xml
+++ b/modules/gdnative/doc_classes/PluginScript.xml
@@ -8,13 +8,10 @@
</tutorials>
<methods>
<method name="new" qualifiers="vararg">
- <return type="Variant">
- </return>
+ <return type="Variant" />
<description>
Returns a new instance of the script.
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/gdnative/doc_classes/VideoStreamGDNative.xml b/modules/gdnative/doc_classes/VideoStreamGDNative.xml
index 153988bad8..dc64e8fc18 100644
--- a/modules/gdnative/doc_classes/VideoStreamGDNative.xml
+++ b/modules/gdnative/doc_classes/VideoStreamGDNative.xml
@@ -11,22 +11,17 @@
</tutorials>
<methods>
<method name="get_file">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Returns the video file handled by this [VideoStreamGDNative].
</description>
</method>
<method name="set_file">
- <return type="void">
- </return>
- <argument index="0" name="file" type="String">
- </argument>
+ <return type="void" />
+ <argument index="0" name="file" type="String" />
<description>
Sets the video file that this [VideoStreamGDNative] resource handles. The supported extensions depend on the GDNative plugins used to expose video formats.
</description>
</method>
</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/doc_classes/XRInterfaceGDNative.xml b/modules/gdnative/doc_classes/XRInterfaceGDNative.xml
deleted file mode 100644
index 13de815793..0000000000
--- a/modules/gdnative/doc_classes/XRInterfaceGDNative.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="XRInterfaceGDNative" inherits="XRInterface" version="4.0">
- <brief_description>
- GDNative wrapper for an XR interface.
- </brief_description>
- <description>
- This is a wrapper class for GDNative implementations of the XR interface. To use a GDNative XR interface, simply instantiate this object and set your GDNative library containing the XR interface implementation.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- </methods>
- <constants>
- </constants>
-</class>
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index bb2da70c3a..9445fac1c6 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,11 +30,12 @@
#include "gdnative.h"
-#include "core/global_constants.h"
+#include "core/config/project_settings.h"
+#include "core/core_constants.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/io/file_access_encrypted.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "scene/main/scene_tree.h"
@@ -51,7 +52,7 @@ extern const godot_gdnative_core_api_struct api_struct;
Map<String, Vector<Ref<GDNative>>> GDNativeLibrary::loaded_libraries;
GDNativeLibrary::GDNativeLibrary() {
- config_file.instance();
+ config_file.instantiate();
symbol_prefix = default_symbol_prefix;
load_once = default_load_once;
@@ -110,6 +111,16 @@ bool GDNativeLibrary::_get(const StringName &p_name, Variant &r_property) const
return false;
}
+void GDNativeLibrary::reset_state() {
+ config_file.instantiate();
+ current_library_path = "";
+ current_dependencies.clear();
+ symbol_prefix = default_symbol_prefix;
+ load_once = default_load_once;
+ singleton = default_singleton;
+ reloadable = default_reloadable;
+}
+
void GDNativeLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
// set entries
List<String> entry_key_list;
@@ -118,9 +129,7 @@ void GDNativeLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
config_file->get_section_keys("entry", &entry_key_list);
}
- for (List<String>::Element *E = entry_key_list.front(); E; E = E->next()) {
- String key = E->get();
-
+ for (const String &key : entry_key_list) {
PropertyInfo prop;
prop.type = Variant::STRING;
@@ -136,9 +145,7 @@ void GDNativeLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
config_file->get_section_keys("dependencies", &dependency_key_list);
}
- for (List<String>::Element *E = dependency_key_list.front(); E; E = E->next()) {
- String key = E->get();
-
+ for (const String &key : dependency_key_list) {
PropertyInfo prop;
prop.type = Variant::STRING;
@@ -149,6 +156,8 @@ void GDNativeLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
}
void GDNativeLibrary::set_config_file(Ref<ConfigFile> p_config_file) {
+ ERR_FAIL_COND(p_config_file.is_null());
+
set_singleton(p_config_file->get_value("general", "singleton", default_singleton));
set_load_once(p_config_file->get_value("general", "load_once", default_load_once));
set_symbol_prefix(p_config_file->get_value("general", "symbol_prefix", default_symbol_prefix));
@@ -162,9 +171,7 @@ void GDNativeLibrary::set_config_file(Ref<ConfigFile> p_config_file) {
p_config_file->get_section_keys("entry", &entry_keys);
}
- for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) {
- String key = E->get();
-
+ for (const String &key : entry_keys) {
Vector<String> tags = key.split(".");
bool skip = false;
@@ -194,9 +201,7 @@ void GDNativeLibrary::set_config_file(Ref<ConfigFile> p_config_file) {
p_config_file->get_section_keys("dependencies", &dependency_keys);
}
- for (List<String>::Element *E = dependency_keys.front(); E; E = E->next()) {
- String key = E->get();
-
+ for (const String &key : dependency_keys) {
Vector<String> tags = key.split(".");
bool skip = false;
@@ -239,7 +244,7 @@ void GDNativeLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_symbol_prefix", "symbol_prefix"), &GDNativeLibrary::set_symbol_prefix);
ClassDB::bind_method(D_METHOD("set_reloadable", "reloadable"), &GDNativeLibrary::set_reloadable);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile", 0), "set_config_file", "get_config_file");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile", PROPERTY_USAGE_NONE), "set_config_file", "get_config_file");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");
@@ -286,7 +291,7 @@ bool GDNative::initialize() {
}
String lib_path = library->get_current_library_path();
- if (lib_path.empty()) {
+ if (lib_path.is_empty()) {
ERR_PRINT("No library set for this platform");
return false;
}
@@ -323,9 +328,18 @@ bool GDNative::initialize() {
// On OSX the exported libraries are located under the Frameworks directory.
// So we need to replace the library path.
String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
- if (!FileAccess::exists(path)) {
+ DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+
+ if (!da->file_exists(path) && !da->dir_exists(path)) {
path = OS::get_singleton()->get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(lib_path.get_file());
}
+
+ if (da->dir_exists(path)) { // Target library is a ".framework", add library base name to the path.
+ path = path.plus_file(path.get_file().get_basename());
+ }
+
+ memdelete(da);
+
#else
String path = ProjectSettings::get_singleton()->globalize_path(lib_path);
#endif
@@ -508,9 +522,9 @@ Error GDNative::get_symbol(StringName p_procedure_name, void *&r_handle, bool p_
return result;
}
-RES GDNativeLibraryResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES GDNativeLibraryResourceLoader::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) {
Ref<GDNativeLibrary> lib;
- lib.instance();
+ lib.instantiate();
Ref<ConfigFile> config = lib->get_config_file();
diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h
index 6d26c2141d..0cc6487ea4 100644
--- a/modules/gdnative/gdnative.h
+++ b/modules/gdnative/gdnative.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,10 +31,10 @@
#ifndef GDNATIVE_H
#define GDNATIVE_H
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/os/thread_safe.h"
-#include "core/resource.h"
#include "gdnative/gdnative.h"
#include "gdnative_api_struct.gen.h"
@@ -63,6 +63,8 @@ class GDNativeLibrary : public Resource {
bool reloadable;
public:
+ virtual void reset_state() override;
+
GDNativeLibrary();
~GDNativeLibrary();
@@ -136,8 +138,8 @@ struct GDNativeCallRegistry {
Vector<StringName> get_native_call_types();
};
-class GDNative : public Reference {
- GDCLASS(GDNative, Reference);
+class GDNative : public RefCounted {
+ GDCLASS(GDNative, RefCounted);
Ref<GDNativeLibrary> library;
@@ -166,7 +168,7 @@ public:
class GDNativeLibraryResourceLoader : public ResourceFormatLoader {
public:
- virtual RES load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads = false, float *r_progress = nullptr, bool p_no_cache = false);
+ virtual RES load(const String &p_path, const String &p_original_path, Error *r_error, 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;
diff --git a/modules/gdnative/gdnative/aabb.cpp b/modules/gdnative/gdnative/aabb.cpp
index d5970e8004..c42b874b4b 100644
--- a/modules/gdnative/gdnative/aabb.cpp
+++ b/modules/gdnative/gdnative/aabb.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,195 +31,19 @@
#include "gdnative/aabb.h"
#include "core/math/aabb.h"
-#include "core/variant.h"
+
+static_assert(sizeof(godot_aabb) == sizeof(AABB), "AABB size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_aabb) == sizeof(AABB), "AABB size mismatch");
-
-void GDAPI godot_aabb_new(godot_aabb *r_dest, const godot_vector3 *p_pos, const godot_vector3 *p_size) {
- const Vector3 *pos = (const Vector3 *)p_pos;
- const Vector3 *size = (const Vector3 *)p_size;
- AABB *dest = (AABB *)r_dest;
- *dest = AABB(*pos, *size);
-}
-
-godot_vector3 GDAPI godot_aabb_get_position(const godot_aabb *p_self) {
- godot_vector3 raw_ret;
- const AABB *self = (const AABB *)p_self;
- Vector3 *ret = (Vector3 *)&raw_ret;
- *ret = self->position;
- return raw_ret;
-}
-
-void GDAPI godot_aabb_set_position(const godot_aabb *p_self, const godot_vector3 *p_v) {
- AABB *self = (AABB *)p_self;
- const Vector3 *v = (const Vector3 *)p_v;
- self->position = *v;
-}
-
-godot_vector3 GDAPI godot_aabb_get_size(const godot_aabb *p_self) {
- godot_vector3 raw_ret;
- const AABB *self = (const AABB *)p_self;
- Vector3 *ret = (Vector3 *)&raw_ret;
- *ret = self->size;
- return raw_ret;
-}
-
-void GDAPI godot_aabb_set_size(const godot_aabb *p_self, const godot_vector3 *p_v) {
- AABB *self = (AABB *)p_self;
- const Vector3 *v = (const Vector3 *)p_v;
- self->size = *v;
-}
-
-godot_string GDAPI godot_aabb_as_string(const godot_aabb *p_self) {
- godot_string ret;
- const AABB *self = (const AABB *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_aabb GDAPI godot_aabb_abs(const godot_aabb *p_self) {
- godot_aabb dest;
- const AABB *self = (const AABB *)p_self;
- *((AABB *)&dest) = self->abs();
- return dest;
-}
-
-godot_real GDAPI godot_aabb_get_area(const godot_aabb *p_self) {
- const AABB *self = (const AABB *)p_self;
- return self->get_area();
-}
-
-godot_bool GDAPI godot_aabb_has_no_area(const godot_aabb *p_self) {
- const AABB *self = (const AABB *)p_self;
- return self->has_no_area();
-}
-
-godot_bool GDAPI godot_aabb_has_no_surface(const godot_aabb *p_self) {
- const AABB *self = (const AABB *)p_self;
- return self->has_no_surface();
-}
-
-godot_bool GDAPI godot_aabb_intersects(const godot_aabb *p_self, const godot_aabb *p_with) {
- const AABB *self = (const AABB *)p_self;
- const AABB *with = (const AABB *)p_with;
- return self->intersects(*with);
-}
-
-godot_bool GDAPI godot_aabb_encloses(const godot_aabb *p_self, const godot_aabb *p_with) {
- const AABB *self = (const AABB *)p_self;
- const AABB *with = (const AABB *)p_with;
- return self->encloses(*with);
-}
-
-godot_aabb GDAPI godot_aabb_merge(const godot_aabb *p_self, const godot_aabb *p_with) {
- godot_aabb dest;
- const AABB *self = (const AABB *)p_self;
- const AABB *with = (const AABB *)p_with;
- *((AABB *)&dest) = self->merge(*with);
- return dest;
-}
-
-godot_aabb GDAPI godot_aabb_intersection(const godot_aabb *p_self, const godot_aabb *p_with) {
- godot_aabb dest;
- const AABB *self = (const AABB *)p_self;
- const AABB *with = (const AABB *)p_with;
- *((AABB *)&dest) = self->intersection(*with);
- return dest;
-}
-
-godot_bool GDAPI godot_aabb_intersects_plane(const godot_aabb *p_self, const godot_plane *p_plane) {
- const AABB *self = (const AABB *)p_self;
- const Plane *plane = (const Plane *)p_plane;
- return self->intersects_plane(*plane);
-}
-
-godot_bool GDAPI godot_aabb_intersects_segment(const godot_aabb *p_self, const godot_vector3 *p_from, const godot_vector3 *p_to) {
- const AABB *self = (const AABB *)p_self;
- const Vector3 *from = (const Vector3 *)p_from;
- const Vector3 *to = (const Vector3 *)p_to;
- return self->intersects_segment(*from, *to);
-}
-
-godot_bool GDAPI godot_aabb_has_point(const godot_aabb *p_self, const godot_vector3 *p_point) {
- const AABB *self = (const AABB *)p_self;
- const Vector3 *point = (const Vector3 *)p_point;
- return self->has_point(*point);
-}
-
-godot_vector3 GDAPI godot_aabb_get_support(const godot_aabb *p_self, const godot_vector3 *p_dir) {
- godot_vector3 dest;
- const AABB *self = (const AABB *)p_self;
- const Vector3 *dir = (const Vector3 *)p_dir;
- *((Vector3 *)&dest) = self->get_support(*dir);
- return dest;
-}
-
-godot_vector3 GDAPI godot_aabb_get_longest_axis(const godot_aabb *p_self) {
- godot_vector3 dest;
- const AABB *self = (const AABB *)p_self;
- *((Vector3 *)&dest) = self->get_longest_axis();
- return dest;
-}
-
-godot_int GDAPI godot_aabb_get_longest_axis_index(const godot_aabb *p_self) {
- const AABB *self = (const AABB *)p_self;
- return self->get_longest_axis_index();
-}
-
-godot_real GDAPI godot_aabb_get_longest_axis_size(const godot_aabb *p_self) {
- const AABB *self = (const AABB *)p_self;
- return self->get_longest_axis_size();
-}
-
-godot_vector3 GDAPI godot_aabb_get_shortest_axis(const godot_aabb *p_self) {
- godot_vector3 dest;
- const AABB *self = (const AABB *)p_self;
- *((Vector3 *)&dest) = self->get_shortest_axis();
- return dest;
-}
-
-godot_int GDAPI godot_aabb_get_shortest_axis_index(const godot_aabb *p_self) {
- const AABB *self = (const AABB *)p_self;
- return self->get_shortest_axis_index();
-}
-
-godot_real GDAPI godot_aabb_get_shortest_axis_size(const godot_aabb *p_self) {
- const AABB *self = (const AABB *)p_self;
- return self->get_shortest_axis_size();
-}
-
-godot_aabb GDAPI godot_aabb_expand(const godot_aabb *p_self, const godot_vector3 *p_to_point) {
- godot_aabb dest;
- const AABB *self = (const AABB *)p_self;
- const Vector3 *to_point = (const Vector3 *)p_to_point;
- *((AABB *)&dest) = self->expand(*to_point);
- return dest;
-}
-
-godot_aabb GDAPI godot_aabb_grow(const godot_aabb *p_self, const godot_real p_by) {
- godot_aabb dest;
- const AABB *self = (const AABB *)p_self;
-
- *((AABB *)&dest) = self->grow(p_by);
- return dest;
-}
-
-godot_vector3 GDAPI godot_aabb_get_endpoint(const godot_aabb *p_self, const godot_int p_idx) {
- godot_vector3 dest;
- const AABB *self = (const AABB *)p_self;
-
- *((Vector3 *)&dest) = self->get_endpoint(p_idx);
- return dest;
+void GDAPI godot_aabb_new(godot_aabb *p_self) {
+ memnew_placement(p_self, AABB);
}
-godot_bool GDAPI godot_aabb_operator_equal(const godot_aabb *p_self, const godot_aabb *p_b) {
- const AABB *self = (const AABB *)p_self;
- const AABB *b = (const AABB *)p_b;
- return *self == *b;
+void GDAPI godot_aabb_new_copy(godot_aabb *r_dest, const godot_aabb *p_src) {
+ memnew_placement(r_dest, AABB(*(AABB *)p_src));
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/array.cpp b/modules/gdnative/gdnative/array.cpp
index 59953f5182..76e131dc06 100644
--- a/modules/gdnative/gdnative/array.cpp
+++ b/modules/gdnative/gdnative/array.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,358 +30,35 @@
#include "gdnative/array.h"
-#include "core/array.h"
#include "core/os/memory.h"
+#include "core/variant/array.h"
-#include "core/color.h"
-
-#include "core/variant.h"
+static_assert(sizeof(godot_array) == sizeof(Array), "Array size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_array) == sizeof(Array), "Array size mismatch");
-
-void GDAPI godot_array_new(godot_array *r_dest) {
- Array *dest = (Array *)r_dest;
- memnew_placement(dest, Array);
+void GDAPI godot_array_new(godot_array *p_self) {
+ memnew_placement(p_self, Array);
}
void GDAPI godot_array_new_copy(godot_array *r_dest, const godot_array *p_src) {
- Array *dest = (Array *)r_dest;
- const Array *src = (const Array *)p_src;
- memnew_placement(dest, Array(*src));
-}
-
-void GDAPI godot_array_new_packed_color_array(godot_array *r_dest, const godot_packed_color_array *p_pca) {
- Array *dest = (Array *)r_dest;
- Vector<Color> *pca = (Vector<Color> *)p_pca;
- memnew_placement(dest, Array);
- dest->resize(pca->size());
-
- for (int i = 0; i < dest->size(); i++) {
- Variant v = pca->operator[](i);
- dest->operator[](i) = v;
- }
-}
-
-void GDAPI godot_array_new_packed_vector3_array(godot_array *r_dest, const godot_packed_vector3_array *p_pv3a) {
- Array *dest = (Array *)r_dest;
- Vector<Vector3> *pca = (Vector<Vector3> *)p_pv3a;
- memnew_placement(dest, Array);
- dest->resize(pca->size());
-
- for (int i = 0; i < dest->size(); i++) {
- Variant v = pca->operator[](i);
- dest->operator[](i) = v;
- }
-}
-
-void GDAPI godot_array_new_packed_vector2_array(godot_array *r_dest, const godot_packed_vector2_array *p_pv2a) {
- Array *dest = (Array *)r_dest;
- Vector<Vector2> *pca = (Vector<Vector2> *)p_pv2a;
- memnew_placement(dest, Array);
- dest->resize(pca->size());
-
- for (int i = 0; i < dest->size(); i++) {
- Variant v = pca->operator[](i);
- dest->operator[](i) = v;
- }
-}
-
-void GDAPI godot_array_new_packed_string_array(godot_array *r_dest, const godot_packed_string_array *p_psa) {
- Array *dest = (Array *)r_dest;
- Vector<String> *pca = (Vector<String> *)p_psa;
- memnew_placement(dest, Array);
- dest->resize(pca->size());
-
- for (int i = 0; i < dest->size(); i++) {
- Variant v = pca->operator[](i);
- dest->operator[](i) = v;
- }
-}
-
-void GDAPI godot_array_new_packed_float32_array(godot_array *r_dest, const godot_packed_float32_array *p_pra) {
- Array *dest = (Array *)r_dest;
- Vector<float> *pca = (Vector<float> *)p_pra;
- memnew_placement(dest, Array);
- dest->resize(pca->size());
-
- for (int i = 0; i < dest->size(); i++) {
- Variant v = pca->operator[](i);
- dest->operator[](i) = v;
- }
-}
-
-void GDAPI godot_array_new_packed_float64_array(godot_array *r_dest, const godot_packed_float64_array *p_pra) {
- Array *dest = (Array *)r_dest;
- Vector<double> *pca = (Vector<double> *)p_pra;
- memnew_placement(dest, Array);
- dest->resize(pca->size());
-
- for (int i = 0; i < dest->size(); i++) {
- Variant v = pca->operator[](i);
- dest->operator[](i) = v;
- }
-}
-
-void GDAPI godot_array_new_packed_int32_array(godot_array *r_dest, const godot_packed_int32_array *p_pia) {
- Array *dest = (Array *)r_dest;
- Vector<int32_t> *pca = (Vector<int32_t> *)p_pia;
- memnew_placement(dest, Array);
- dest->resize(pca->size());
-
- for (int i = 0; i < dest->size(); i++) {
- Variant v = pca->operator[](i);
- dest->operator[](i) = v;
- }
-}
-
-void GDAPI godot_array_new_packed_int64_array(godot_array *r_dest, const godot_packed_int64_array *p_pia) {
- Array *dest = (Array *)r_dest;
- Vector<int64_t> *pca = (Vector<int64_t> *)p_pia;
- memnew_placement(dest, Array);
- dest->resize(pca->size());
-
- for (int i = 0; i < dest->size(); i++) {
- Variant v = pca->operator[](i);
- dest->operator[](i) = v;
- }
-}
-
-void GDAPI godot_array_new_packed_byte_array(godot_array *r_dest, const godot_packed_byte_array *p_pba) {
- Array *dest = (Array *)r_dest;
- Vector<uint8_t> *pca = (Vector<uint8_t> *)p_pba;
- memnew_placement(dest, Array);
- dest->resize(pca->size());
-
- for (int i = 0; i < dest->size(); i++) {
- Variant v = pca->operator[](i);
- dest->operator[](i) = v;
- }
-}
-
-void GDAPI godot_array_set(godot_array *p_self, const godot_int p_idx, const godot_variant *p_value) {
- Array *self = (Array *)p_self;
- Variant *val = (Variant *)p_value;
- self->operator[](p_idx) = *val;
-}
-
-godot_variant GDAPI godot_array_get(const godot_array *p_self, const godot_int p_idx) {
- godot_variant raw_dest;
- Variant *dest = (Variant *)&raw_dest;
- const Array *self = (const Array *)p_self;
- memnew_placement(dest, Variant(self->operator[](p_idx)));
- return raw_dest;
-}
-
-godot_variant GDAPI *godot_array_operator_index(godot_array *p_self, const godot_int p_idx) {
- Array *self = (Array *)p_self;
- return (godot_variant *)&self->operator[](p_idx);
-}
-
-const godot_variant GDAPI *godot_array_operator_index_const(const godot_array *p_self, const godot_int p_idx) {
- const Array *self = (const Array *)p_self;
- return (const godot_variant *)&self->operator[](p_idx);
-}
-
-void GDAPI godot_array_append(godot_array *p_self, const godot_variant *p_value) {
- Array *self = (Array *)p_self;
- Variant *val = (Variant *)p_value;
- self->append(*val);
-}
-
-void GDAPI godot_array_clear(godot_array *p_self) {
- Array *self = (Array *)p_self;
- self->clear();
-}
-
-godot_int GDAPI godot_array_count(const godot_array *p_self, const godot_variant *p_value) {
- const Array *self = (const Array *)p_self;
- const Variant *val = (const Variant *)p_value;
- return self->count(*val);
-}
-
-godot_bool GDAPI godot_array_empty(const godot_array *p_self) {
- const Array *self = (const Array *)p_self;
- return self->empty();
-}
-
-void GDAPI godot_array_erase(godot_array *p_self, const godot_variant *p_value) {
- Array *self = (Array *)p_self;
- const Variant *val = (const Variant *)p_value;
- self->erase(*val);
-}
-
-godot_variant GDAPI godot_array_front(const godot_array *p_self) {
- const Array *self = (const Array *)p_self;
- godot_variant v;
- Variant *val = (Variant *)&v;
- memnew_placement(val, Variant);
- *val = self->front();
- return v;
-}
-
-godot_variant GDAPI godot_array_back(const godot_array *p_self) {
- const Array *self = (const Array *)p_self;
- godot_variant v;
- Variant *val = (Variant *)&v;
- memnew_placement(val, Variant);
- *val = self->back();
- return v;
-}
-
-godot_int GDAPI godot_array_find(const godot_array *p_self, const godot_variant *p_what, const godot_int p_from) {
- const Array *self = (const Array *)p_self;
- const Variant *val = (const Variant *)p_what;
- return self->find(*val, p_from);
-}
-
-godot_int GDAPI godot_array_find_last(const godot_array *p_self, const godot_variant *p_what) {
- const Array *self = (const Array *)p_self;
- const Variant *val = (const Variant *)p_what;
- return self->find_last(*val);
-}
-
-godot_bool GDAPI godot_array_has(const godot_array *p_self, const godot_variant *p_value) {
- const Array *self = (const Array *)p_self;
- const Variant *val = (const Variant *)p_value;
- return self->has(*val);
-}
-
-godot_int GDAPI godot_array_hash(const godot_array *p_self) {
- const Array *self = (const Array *)p_self;
- return self->hash();
-}
-
-void GDAPI godot_array_insert(godot_array *p_self, const godot_int p_pos, const godot_variant *p_value) {
- Array *self = (Array *)p_self;
- const Variant *val = (const Variant *)p_value;
- self->insert(p_pos, *val);
-}
-
-void GDAPI godot_array_invert(godot_array *p_self) {
- Array *self = (Array *)p_self;
- self->invert();
-}
-
-godot_variant GDAPI godot_array_pop_back(godot_array *p_self) {
- Array *self = (Array *)p_self;
- godot_variant v;
- Variant *val = (Variant *)&v;
- memnew_placement(val, Variant);
- *val = self->pop_back();
- return v;
-}
-
-godot_variant GDAPI godot_array_pop_front(godot_array *p_self) {
- Array *self = (Array *)p_self;
- godot_variant v;
- Variant *val = (Variant *)&v;
- memnew_placement(val, Variant);
- *val = self->pop_front();
- return v;
-}
-
-void GDAPI godot_array_push_back(godot_array *p_self, const godot_variant *p_value) {
- Array *self = (Array *)p_self;
- const Variant *val = (const Variant *)p_value;
- self->push_back(*val);
-}
-
-void GDAPI godot_array_push_front(godot_array *p_self, const godot_variant *p_value) {
- Array *self = (Array *)p_self;
- const Variant *val = (const Variant *)p_value;
- self->push_front(*val);
-}
-
-void GDAPI godot_array_remove(godot_array *p_self, const godot_int p_idx) {
- Array *self = (Array *)p_self;
- self->remove(p_idx);
-}
-
-void GDAPI godot_array_resize(godot_array *p_self, const godot_int p_size) {
- Array *self = (Array *)p_self;
- self->resize(p_size);
-}
-
-godot_int GDAPI godot_array_rfind(const godot_array *p_self, const godot_variant *p_what, const godot_int p_from) {
- const Array *self = (const Array *)p_self;
- const Variant *val = (const Variant *)p_what;
- return self->rfind(*val, p_from);
-}
-
-godot_int GDAPI godot_array_size(const godot_array *p_self) {
- const Array *self = (const Array *)p_self;
- return self->size();
-}
-
-void GDAPI godot_array_sort(godot_array *p_self) {
- Array *self = (Array *)p_self;
- self->sort();
-}
-
-void GDAPI godot_array_sort_custom(godot_array *p_self, godot_object *p_obj, const godot_string *p_func) {
- Array *self = (Array *)p_self;
- const String *func = (const String *)p_func;
- self->sort_custom((Object *)p_obj, *func);
-}
-
-godot_int GDAPI godot_array_bsearch(godot_array *p_self, const godot_variant *p_value, const godot_bool p_before) {
- Array *self = (Array *)p_self;
- return self->bsearch(*(const Variant *)p_value, p_before);
-}
-
-godot_int GDAPI godot_array_bsearch_custom(godot_array *p_self, const godot_variant *p_value, godot_object *p_obj, const godot_string *p_func, const godot_bool p_before) {
- Array *self = (Array *)p_self;
- const String *func = (const String *)p_func;
- return self->bsearch_custom(*(const Variant *)p_value, (Object *)p_obj, *func, p_before);
+ memnew_placement(r_dest, Array(*(Array *)p_src));
}
void GDAPI godot_array_destroy(godot_array *p_self) {
((Array *)p_self)->~Array();
}
-godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep) {
- const Array *self = (const Array *)p_self;
- godot_array res;
- Array *val = (Array *)&res;
- memnew_placement(val, Array);
- *val = self->duplicate(p_deep);
- return res;
-}
-
-godot_array GDAPI godot_array_slice(const godot_array *p_self, const godot_int p_begin, const godot_int p_end, const godot_int p_step, const godot_bool p_deep) {
- const Array *self = (const Array *)p_self;
- godot_array res;
- Array *val = (Array *)&res;
- memnew_placement(val, Array);
- *val = self->slice(p_begin, p_end, p_step, p_deep);
- return res;
-}
-
-godot_variant GDAPI godot_array_max(const godot_array *p_self) {
- const Array *self = (const Array *)p_self;
- godot_variant v;
- Variant *val = (Variant *)&v;
- memnew_placement(val, Variant);
- *val = self->max();
- return v;
+godot_variant GDAPI *godot_array_operator_index(godot_array *p_self, godot_int p_index) {
+ Array *self = (Array *)p_self;
+ return (godot_variant *)&self->operator[](p_index);
}
-godot_variant GDAPI godot_array_min(const godot_array *p_self) {
+const godot_variant GDAPI *godot_array_operator_index_const(const godot_array *p_self, godot_int p_index) {
const Array *self = (const Array *)p_self;
- godot_variant v;
- Variant *val = (Variant *)&v;
- memnew_placement(val, Variant);
- *val = self->min();
- return v;
-}
-
-void GDAPI godot_array_shuffle(godot_array *p_self) {
- Array *self = (Array *)p_self;
- self->shuffle();
+ return (const godot_variant *)&self->operator[](p_index);
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/basis.cpp b/modules/gdnative/gdnative/basis.cpp
index 990fd3795d..4641f0bacc 100644
--- a/modules/gdnative/gdnative/basis.cpp
+++ b/modules/gdnative/gdnative/basis.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,266 +31,29 @@
#include "gdnative/basis.h"
#include "core/math/basis.h"
-#include "core/variant.h"
+
+static_assert(sizeof(godot_basis) == sizeof(Basis), "Basis size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_basis) == sizeof(Basis), "Basis size mismatch");
-
-void GDAPI godot_basis_new_with_rows(godot_basis *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis) {
- const Vector3 *x_axis = (const Vector3 *)p_x_axis;
- const Vector3 *y_axis = (const Vector3 *)p_y_axis;
- const Vector3 *z_axis = (const Vector3 *)p_z_axis;
- Basis *dest = (Basis *)r_dest;
- *dest = Basis(*x_axis, *y_axis, *z_axis);
+void GDAPI godot_basis_new(godot_basis *p_self) {
+ memnew_placement(p_self, Basis);
}
-void GDAPI godot_basis_new_with_axis_and_angle(godot_basis *r_dest, const godot_vector3 *p_axis, const godot_real p_phi) {
- const Vector3 *axis = (const Vector3 *)p_axis;
- Basis *dest = (Basis *)r_dest;
- *dest = Basis(*axis, p_phi);
+void GDAPI godot_basis_new_copy(godot_basis *r_dest, const godot_basis *p_src) {
+ memnew_placement(r_dest, Basis(*(Basis *)p_src));
}
-void GDAPI godot_basis_new_with_euler(godot_basis *r_dest, const godot_vector3 *p_euler) {
- const Vector3 *euler = (const Vector3 *)p_euler;
- Basis *dest = (Basis *)r_dest;
- *dest = Basis(*euler);
-}
-
-godot_string GDAPI godot_basis_as_string(const godot_basis *p_self) {
- godot_string ret;
- const Basis *self = (const Basis *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_basis GDAPI godot_basis_inverse(const godot_basis *p_self) {
- godot_basis dest;
- const Basis *self = (const Basis *)p_self;
- *((Basis *)&dest) = self->inverse();
- return dest;
-}
-
-godot_basis GDAPI godot_basis_transposed(const godot_basis *p_self) {
- godot_basis dest;
- const Basis *self = (const Basis *)p_self;
- *((Basis *)&dest) = self->transposed();
- return dest;
-}
-
-godot_basis GDAPI godot_basis_orthonormalized(const godot_basis *p_self) {
- godot_basis dest;
- const Basis *self = (const Basis *)p_self;
- *((Basis *)&dest) = self->orthonormalized();
- return dest;
-}
-
-godot_real GDAPI godot_basis_determinant(const godot_basis *p_self) {
- const Basis *self = (const Basis *)p_self;
- return self->determinant();
-}
-
-godot_basis GDAPI godot_basis_rotated(const godot_basis *p_self, const godot_vector3 *p_axis, const godot_real p_phi) {
- godot_basis dest;
- const Basis *self = (const Basis *)p_self;
- const Vector3 *axis = (const Vector3 *)p_axis;
- *((Basis *)&dest) = self->rotated(*axis, p_phi);
- return dest;
-}
-
-godot_basis GDAPI godot_basis_scaled(const godot_basis *p_self, const godot_vector3 *p_scale) {
- godot_basis dest;
- const Basis *self = (const Basis *)p_self;
- const Vector3 *scale = (const Vector3 *)p_scale;
- *((Basis *)&dest) = self->scaled(*scale);
- return dest;
-}
-
-godot_vector3 GDAPI godot_basis_get_scale(const godot_basis *p_self) {
- godot_vector3 dest;
- const Basis *self = (const Basis *)p_self;
- *((Vector3 *)&dest) = self->get_scale();
- return dest;
-}
-
-godot_quat GDAPI godot_basis_get_quat(const godot_basis *p_self) {
- godot_quat dest;
- const Basis *self = (const Basis *)p_self;
- *((Quat *)&dest) = self->get_quat();
- return dest;
-}
-
-void GDAPI godot_basis_set_quat(godot_basis *p_self, const godot_quat *p_quat) {
- Basis *self = (Basis *)p_self;
- const Quat *quat = (const Quat *)p_quat;
- self->set_quat(*quat);
-}
-
-void GDAPI godot_basis_set_axis_angle_scale(godot_basis *p_self, const godot_vector3 *p_axis, godot_real p_phi, const godot_vector3 *p_scale) {
- Basis *self = (Basis *)p_self;
- const Vector3 *axis = (const Vector3 *)p_axis;
- const Vector3 *scale = (const Vector3 *)p_scale;
- self->set_axis_angle_scale(*axis, p_phi, *scale);
-}
-
-void GDAPI godot_basis_set_euler_scale(godot_basis *p_self, const godot_vector3 *p_euler, const godot_vector3 *p_scale) {
- Basis *self = (Basis *)p_self;
- const Vector3 *euler = (const Vector3 *)p_euler;
- const Vector3 *scale = (const Vector3 *)p_scale;
- self->set_euler_scale(*euler, *scale);
-}
-
-void GDAPI godot_basis_set_quat_scale(godot_basis *p_self, const godot_quat *p_quat, const godot_vector3 *p_scale) {
+godot_vector3 GDAPI *godot_basis_operator_index(godot_basis *p_self, godot_int p_index) {
Basis *self = (Basis *)p_self;
- const Quat *quat = (const Quat *)p_quat;
- const Vector3 *scale = (const Vector3 *)p_scale;
- self->set_quat_scale(*quat, *scale);
-}
-
-godot_vector3 GDAPI godot_basis_get_euler(const godot_basis *p_self) {
- godot_vector3 dest;
- const Basis *self = (const Basis *)p_self;
- *((Vector3 *)&dest) = self->get_euler();
- return dest;
-}
-
-godot_real GDAPI godot_basis_tdotx(const godot_basis *p_self, const godot_vector3 *p_with) {
- const Basis *self = (const Basis *)p_self;
- const Vector3 *with = (const Vector3 *)p_with;
- return self->tdotx(*with);
-}
-
-godot_real GDAPI godot_basis_tdoty(const godot_basis *p_self, const godot_vector3 *p_with) {
- const Basis *self = (const Basis *)p_self;
- const Vector3 *with = (const Vector3 *)p_with;
- return self->tdoty(*with);
-}
-
-godot_real GDAPI godot_basis_tdotz(const godot_basis *p_self, const godot_vector3 *p_with) {
- const Basis *self = (const Basis *)p_self;
- const Vector3 *with = (const Vector3 *)p_with;
- return self->tdotz(*with);
-}
-
-godot_vector3 GDAPI godot_basis_xform(const godot_basis *p_self, const godot_vector3 *p_v) {
- godot_vector3 dest;
- const Basis *self = (const Basis *)p_self;
- const Vector3 *v = (const Vector3 *)p_v;
- *((Vector3 *)&dest) = self->xform(*v);
- return dest;
-}
-
-godot_vector3 GDAPI godot_basis_xform_inv(const godot_basis *p_self, const godot_vector3 *p_v) {
- godot_vector3 dest;
- const Basis *self = (const Basis *)p_self;
- const Vector3 *v = (const Vector3 *)p_v;
- *((Vector3 *)&dest) = self->xform_inv(*v);
- return dest;
-}
-
-godot_int GDAPI godot_basis_get_orthogonal_index(const godot_basis *p_self) {
- const Basis *self = (const Basis *)p_self;
- return self->get_orthogonal_index();
-}
-
-void GDAPI godot_basis_new(godot_basis *r_dest) {
- Basis *dest = (Basis *)r_dest;
- *dest = Basis();
-}
-
-void GDAPI godot_basis_new_with_euler_quat(godot_basis *r_dest, const godot_quat *p_euler) {
- Basis *dest = (Basis *)r_dest;
- const Quat *euler = (const Quat *)p_euler;
- *dest = Basis(*euler);
-}
-
-// p_elements is a pointer to an array of 3 (!!) vector3
-void GDAPI godot_basis_get_elements(const godot_basis *p_self, godot_vector3 *p_elements) {
- const Basis *self = (const Basis *)p_self;
- Vector3 *elements = (Vector3 *)p_elements;
- elements[0] = self->elements[0];
- elements[1] = self->elements[1];
- elements[2] = self->elements[2];
-}
-
-godot_vector3 GDAPI godot_basis_get_axis(const godot_basis *p_self, const godot_int p_axis) {
- godot_vector3 dest;
- Vector3 *d = (Vector3 *)&dest;
- const Basis *self = (const Basis *)p_self;
- *d = self->get_axis(p_axis);
- return dest;
-}
-
-void GDAPI godot_basis_set_axis(godot_basis *p_self, const godot_int p_axis, const godot_vector3 *p_value) {
- Basis *self = (Basis *)p_self;
- const Vector3 *value = (const Vector3 *)p_value;
- self->set_axis(p_axis, *value);
-}
-
-godot_vector3 GDAPI godot_basis_get_row(const godot_basis *p_self, const godot_int p_row) {
- godot_vector3 dest;
- Vector3 *d = (Vector3 *)&dest;
- const Basis *self = (const Basis *)p_self;
- *d = self->get_row(p_row);
- return dest;
-}
-
-void GDAPI godot_basis_set_row(godot_basis *p_self, const godot_int p_row, const godot_vector3 *p_value) {
- Basis *self = (Basis *)p_self;
- const Vector3 *value = (const Vector3 *)p_value;
- self->set_row(p_row, *value);
-}
-
-godot_bool GDAPI godot_basis_operator_equal(const godot_basis *p_self, const godot_basis *p_b) {
- const Basis *self = (const Basis *)p_self;
- const Basis *b = (const Basis *)p_b;
- return *self == *b;
-}
-
-godot_basis GDAPI godot_basis_operator_add(const godot_basis *p_self, const godot_basis *p_b) {
- godot_basis raw_dest;
- Basis *dest = (Basis *)&raw_dest;
- const Basis *self = (const Basis *)p_self;
- const Basis *b = (const Basis *)p_b;
- *dest = *self + *b;
- return raw_dest;
-}
-
-godot_basis GDAPI godot_basis_operator_subtract(const godot_basis *p_self, const godot_basis *p_b) {
- godot_basis raw_dest;
- Basis *dest = (Basis *)&raw_dest;
- const Basis *self = (const Basis *)p_self;
- const Basis *b = (const Basis *)p_b;
- *dest = *self - *b;
- return raw_dest;
-}
-
-godot_basis GDAPI godot_basis_operator_multiply_vector(const godot_basis *p_self, const godot_basis *p_b) {
- godot_basis raw_dest;
- Basis *dest = (Basis *)&raw_dest;
- const Basis *self = (const Basis *)p_self;
- const Basis *b = (const Basis *)p_b;
- *dest = *self * *b;
- return raw_dest;
-}
-
-godot_basis GDAPI godot_basis_operator_multiply_scalar(const godot_basis *p_self, const godot_real p_b) {
- godot_basis raw_dest;
- Basis *dest = (Basis *)&raw_dest;
- const Basis *self = (const Basis *)p_self;
- *dest = *self * p_b;
- return raw_dest;
+ return (godot_vector3 *)&self->operator[](p_index);
}
-godot_basis GDAPI godot_basis_slerp(const godot_basis *p_self, const godot_basis *p_b, const godot_real p_t) {
- godot_basis raw_dest;
- Basis *dest = (Basis *)&raw_dest;
+const godot_vector3 GDAPI *godot_basis_operator_index_const(const godot_basis *p_self, godot_int p_index) {
const Basis *self = (const Basis *)p_self;
- const Basis *b = (const Basis *)p_b;
- *dest = self->slerp(*b, p_t);
- return raw_dest;
+ return (const godot_vector3 *)&self->operator[](p_index);
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/callable.cpp b/modules/gdnative/gdnative/callable.cpp
index 868b324227..85274e5e22 100644
--- a/modules/gdnative/gdnative/callable.cpp
+++ b/modules/gdnative/gdnative/callable.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,36 +30,21 @@
#include "gdnative/callable.h"
-#include "core/callable.h"
-#include "core/resource.h"
-#include "core/variant.h"
+#include "core/variant/callable.h"
+#include "core/variant/variant.h"
+
+static_assert(sizeof(godot_callable) == sizeof(Callable), "Callable size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_callable) == sizeof(Callable), "Callable size mismatch");
-static_assert(sizeof(godot_signal) == sizeof(Signal), "Signal size mismatch");
-
-// Callable
-
-void GDAPI godot_callable_new_with_object(godot_callable *r_dest, const godot_object *p_object, const godot_string_name *p_method) {
- Callable *dest = (Callable *)r_dest;
- const Object *object = (const Object *)p_object;
- const StringName *method = (const StringName *)p_method;
- memnew_placement(dest, Callable(object, *method));
-}
-
-void GDAPI godot_callable_new_with_object_id(godot_callable *r_dest, uint64_t p_objectid, const godot_string_name *p_method) {
- Callable *dest = (Callable *)r_dest;
- const StringName *method = (const StringName *)p_method;
- memnew_placement(dest, Callable(ObjectID(p_objectid), *method));
+void GDAPI godot_callable_new(godot_callable *p_self) {
+ memnew_placement(p_self, Callable);
}
void GDAPI godot_callable_new_copy(godot_callable *r_dest, const godot_callable *p_src) {
- Callable *dest = (Callable *)r_dest;
- const Callable *src = (const Callable *)p_src;
- memnew_placement(dest, Callable(*src));
+ memnew_placement(r_dest, Callable(*(Callable *)p_src));
}
void GDAPI godot_callable_destroy(godot_callable *p_self) {
@@ -67,186 +52,6 @@ void GDAPI godot_callable_destroy(godot_callable *p_self) {
self->~Callable();
}
-godot_int GDAPI godot_callable_call(const godot_callable *p_self, const godot_variant **p_arguments, godot_int p_argcount, godot_variant *r_return_value) {
- const Callable *self = (const Callable *)p_self;
- const Variant **arguments = (const Variant **)p_arguments;
- Variant *return_value = (Variant *)r_return_value;
- Variant ret;
- Callable::CallError err;
- self->call(arguments, p_argcount, ret, err);
- if (return_value)
- (*return_value) = ret;
- return (godot_int)err.error;
-}
-
-void GDAPI godot_callable_call_deferred(const godot_callable *p_self, const godot_variant **p_arguments, godot_int p_argcount) {
- const Callable *self = (const Callable *)p_self;
- const Variant **arguments = (const Variant **)p_arguments;
- self->call_deferred(arguments, p_argcount);
-}
-
-godot_bool GDAPI godot_callable_is_null(const godot_callable *p_self) {
- const Callable *self = (const Callable *)p_self;
- return self->is_null();
-}
-
-godot_bool GDAPI godot_callable_is_custom(const godot_callable *p_self) {
- const Callable *self = (const Callable *)p_self;
- return self->is_custom();
-}
-
-godot_bool GDAPI godot_callable_is_standard(const godot_callable *p_self) {
- const Callable *self = (const Callable *)p_self;
- return self->is_standard();
-}
-
-godot_object GDAPI *godot_callable_get_object(const godot_callable *p_self) {
- const Callable *self = (const Callable *)p_self;
- return (godot_object *)self->get_object();
-}
-
-uint64_t GDAPI godot_callable_get_object_id(const godot_callable *p_self) {
- const Callable *self = (const Callable *)p_self;
- return (uint64_t)self->get_object_id();
-}
-
-godot_string_name GDAPI godot_callable_get_method(const godot_callable *p_self) {
- godot_string_name raw_dest;
- const Callable *self = (const Callable *)p_self;
- StringName *dest = (StringName *)&raw_dest;
- memnew_placement(dest, StringName(self->get_method()));
- return raw_dest;
-}
-
-uint32_t GDAPI godot_callable_hash(const godot_callable *p_self) {
- const Callable *self = (const Callable *)p_self;
- return self->hash();
-}
-
-godot_string GDAPI godot_callable_as_string(const godot_callable *p_self) {
- godot_string ret;
- const Callable *self = (const Callable *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_bool GDAPI godot_callable_operator_equal(const godot_callable *p_self, const godot_callable *p_other) {
- const Callable *self = (const Callable *)p_self;
- const Callable *other = (const Callable *)p_other;
- return *self == *other;
-}
-
-godot_bool GDAPI godot_callable_operator_less(const godot_callable *p_self, const godot_callable *p_other) {
- const Callable *self = (const Callable *)p_self;
- const Callable *other = (const Callable *)p_other;
- return *self < *other;
-}
-
-// Signal
-
-void GDAPI godot_signal_new_with_object(godot_signal *r_dest, const godot_object *p_object, const godot_string_name *p_name) {
- Signal *dest = (Signal *)r_dest;
- const Object *object = (const Object *)p_object;
- const StringName *name = (const StringName *)p_name;
- memnew_placement(dest, Signal(object, *name));
-}
-
-void GDAPI godot_signal_new_with_object_id(godot_signal *r_dest, uint64_t p_objectid, const godot_string_name *p_name) {
- Signal *dest = (Signal *)r_dest;
- const StringName *name = (const StringName *)p_name;
- memnew_placement(dest, Signal(ObjectID(p_objectid), *name));
-}
-
-void GDAPI godot_signal_new_copy(godot_signal *r_dest, const godot_signal *p_src) {
- Signal *dest = (Signal *)r_dest;
- const Signal *src = (const Signal *)p_src;
- memnew_placement(dest, Signal(*src));
-}
-
-void GDAPI godot_signal_destroy(godot_signal *p_self) {
- Signal *self = (Signal *)p_self;
- self->~Signal();
-}
-
-godot_int GDAPI godot_signal_emit(const godot_signal *p_self, const godot_variant **p_arguments, godot_int p_argcount) {
- const Signal *self = (const Signal *)p_self;
- const Variant **arguments = (const Variant **)p_arguments;
- return (godot_int)self->emit(arguments, p_argcount);
-}
-
-godot_int GDAPI godot_signal_connect(godot_signal *p_self, const godot_callable *p_callable, const godot_array *p_binds, uint32_t p_flags) {
- Signal *self = (Signal *)p_self;
- const Callable *callable = (const Callable *)p_callable;
- const Array *binds_ar = (const Array *)p_binds;
- Vector<Variant> binds;
- for (int i = 0; i < binds_ar->size(); i++) {
- binds.push_back(binds_ar->get(i));
- }
- return (godot_int)self->connect(*callable, binds, p_flags);
-}
-
-void GDAPI godot_signal_disconnect(godot_signal *p_self, const godot_callable *p_callable) {
- Signal *self = (Signal *)p_self;
- const Callable *callable = (const Callable *)p_callable;
- self->disconnect(*callable);
-}
-
-godot_bool GDAPI godot_signal_is_null(const godot_signal *p_self) {
- const Signal *self = (const Signal *)p_self;
- return self->is_null();
-}
-
-godot_bool GDAPI godot_signal_is_connected(const godot_signal *p_self, const godot_callable *p_callable) {
- const Signal *self = (const Signal *)p_self;
- const Callable *callable = (const Callable *)p_callable;
- return self->is_connected(*callable);
-}
-
-godot_array GDAPI godot_signal_get_connections(const godot_signal *p_self) {
- godot_array raw_dest;
- const Signal *self = (const Signal *)p_self;
- Array *dest = (Array *)&raw_dest;
- memnew_placement(dest, Array(self->get_connections()));
- return raw_dest;
-}
-
-godot_object GDAPI *godot_signal_get_object(const godot_signal *p_self) {
- const Signal *self = (const Signal *)p_self;
- return (godot_object *)self->get_object();
-}
-
-uint64_t GDAPI godot_signal_get_object_id(const godot_signal *p_self) {
- const Signal *self = (const Signal *)p_self;
- return (uint64_t)self->get_object_id();
-}
-
-godot_string_name GDAPI godot_signal_get_name(const godot_signal *p_self) {
- godot_string_name raw_dest;
- const Signal *self = (const Signal *)p_self;
- StringName *dest = (StringName *)&raw_dest;
- memnew_placement(dest, StringName(self->get_name()));
- return raw_dest;
-}
-
-godot_string GDAPI godot_signal_as_string(const godot_signal *p_self) {
- godot_string ret;
- const Signal *self = (const Signal *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_bool GDAPI godot_signal_operator_equal(const godot_signal *p_self, const godot_signal *p_other) {
- const Signal *self = (const Signal *)p_self;
- const Signal *other = (const Signal *)p_other;
- return *self == *other;
-}
-
-godot_bool GDAPI godot_signal_operator_less(const godot_signal *p_self, const godot_signal *p_other) {
- const Signal *self = (const Signal *)p_self;
- const Signal *other = (const Signal *)p_other;
- return *self < *other;
-}
-
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/color.cpp b/modules/gdnative/gdnative/color.cpp
index c75e74daba..502f89c027 100644
--- a/modules/gdnative/gdnative/color.cpp
+++ b/modules/gdnative/gdnative/color.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,186 +30,30 @@
#include "gdnative/color.h"
-#include "core/color.h"
-#include "core/variant.h"
+#include "core/math/color.h"
+
+static_assert(sizeof(godot_color) == sizeof(Color), "Color size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_color) == sizeof(Color), "Color size mismatch");
-
-void GDAPI godot_color_new_rgba(godot_color *r_dest, const godot_real p_r, const godot_real p_g, const godot_real p_b, const godot_real p_a) {
- Color *dest = (Color *)r_dest;
- *dest = Color(p_r, p_g, p_b, p_a);
-}
-
-void GDAPI godot_color_new_rgb(godot_color *r_dest, const godot_real p_r, const godot_real p_g, const godot_real p_b) {
- Color *dest = (Color *)r_dest;
- *dest = Color(p_r, p_g, p_b);
-}
-
-godot_real godot_color_get_r(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->r;
-}
-
-void godot_color_set_r(godot_color *p_self, const godot_real val) {
- Color *self = (Color *)p_self;
- self->r = val;
-}
-
-godot_real godot_color_get_g(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->g;
-}
-
-void godot_color_set_g(godot_color *p_self, const godot_real val) {
- Color *self = (Color *)p_self;
- self->g = val;
-}
-
-godot_real godot_color_get_b(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->b;
-}
-
-void godot_color_set_b(godot_color *p_self, const godot_real val) {
- Color *self = (Color *)p_self;
- self->b = val;
+void GDAPI godot_color_new(godot_color *p_self) {
+ memnew_placement(p_self, Color);
}
-godot_real godot_color_get_a(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->a;
+void GDAPI godot_color_new_copy(godot_color *r_dest, const godot_color *p_src) {
+ memnew_placement(r_dest, Color(*(Color *)p_src));
}
-void godot_color_set_a(godot_color *p_self, const godot_real val) {
+float GDAPI *godot_color_operator_index(godot_color *p_self, godot_int p_index) {
Color *self = (Color *)p_self;
- self->a = val;
-}
-
-godot_real godot_color_get_h(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->get_h();
-}
-
-godot_real godot_color_get_s(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->get_s();
-}
-
-godot_real godot_color_get_v(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->get_v();
-}
-
-godot_string GDAPI godot_color_as_string(const godot_color *p_self) {
- godot_string ret;
- const Color *self = (const Color *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->to_rgba32();
-}
-
-godot_int GDAPI godot_color_to_abgr32(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->to_abgr32();
-}
-
-godot_int GDAPI godot_color_to_abgr64(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->to_abgr64();
-}
-
-godot_int GDAPI godot_color_to_argb64(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->to_argb64();
-}
-
-godot_int GDAPI godot_color_to_rgba64(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->to_rgba64();
-}
-
-godot_int GDAPI godot_color_to_argb32(const godot_color *p_self) {
- const Color *self = (const Color *)p_self;
- return self->to_argb32();
-}
-
-godot_color GDAPI godot_color_inverted(const godot_color *p_self) {
- godot_color dest;
- const Color *self = (const Color *)p_self;
- *((Color *)&dest) = self->inverted();
- return dest;
-}
-
-godot_color GDAPI godot_color_contrasted(const godot_color *p_self) {
- godot_color dest;
- const Color *self = (const Color *)p_self;
- *((Color *)&dest) = self->contrasted();
- return dest;
-}
-
-godot_color GDAPI godot_color_lerp(const godot_color *p_self, const godot_color *p_b, const godot_real p_t) {
- godot_color dest;
- const Color *self = (const Color *)p_self;
- const Color *b = (const Color *)p_b;
- *((Color *)&dest) = self->lerp(*b, p_t);
- return dest;
-}
-
-godot_color GDAPI godot_color_blend(const godot_color *p_self, const godot_color *p_over) {
- godot_color dest;
- const Color *self = (const Color *)p_self;
- const Color *over = (const Color *)p_over;
- *((Color *)&dest) = self->blend(*over);
- return dest;
-}
-
-godot_color GDAPI godot_color_darkened(const godot_color *p_self, const godot_real p_amount) {
- godot_color dest;
- const Color *self = (const Color *)p_self;
- *((Color *)&dest) = self->darkened(p_amount);
- return dest;
-}
-
-godot_color GDAPI godot_color_from_hsv(const godot_color *p_self, const godot_real p_h, const godot_real p_s, const godot_real p_v, const godot_real p_a) {
- godot_color dest;
- const Color *self = (const Color *)p_self;
- *((Color *)&dest) = self->from_hsv(p_h, p_s, p_v, p_a);
- return dest;
-}
-
-godot_color GDAPI godot_color_lightened(const godot_color *p_self, const godot_real p_amount) {
- godot_color dest;
- const Color *self = (const Color *)p_self;
- *((Color *)&dest) = self->lightened(p_amount);
- return dest;
-}
-
-godot_string GDAPI godot_color_to_html(const godot_color *p_self, const godot_bool p_with_alpha) {
- godot_string dest;
- const Color *self = (const Color *)p_self;
-
- memnew_placement(&dest, String(self->to_html(p_with_alpha)));
- return dest;
-}
-
-godot_bool GDAPI godot_color_operator_equal(const godot_color *p_self, const godot_color *p_b) {
- const Color *self = (const Color *)p_self;
- const Color *b = (const Color *)p_b;
- return *self == *b;
+ return (float *)&self->operator[](p_index);
}
-godot_bool GDAPI godot_color_operator_less(const godot_color *p_self, const godot_color *p_b) {
+const float GDAPI *godot_color_operator_index_const(const godot_color *p_self, godot_int p_index) {
const Color *self = (const Color *)p_self;
- const Color *b = (const Color *)p_b;
- return *self < *b;
+ return (const float *)&self->operator[](p_index);
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/dictionary.cpp b/modules/gdnative/gdnative/dictionary.cpp
index a126974815..2bfad6e695 100644
--- a/modules/gdnative/gdnative/dictionary.cpp
+++ b/modules/gdnative/gdnative/dictionary.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,26 +30,21 @@
#include "gdnative/dictionary.h"
-#include "core/variant.h"
-// core/variant.h before to avoid compile errors with MSVC
-#include "core/dictionary.h"
-#include "core/io/json.h"
+#include "core/variant/dictionary.h"
+#include "core/variant/variant.h"
+
+static_assert(sizeof(godot_dictionary) == sizeof(Dictionary), "Dictionary size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_dictionary) == sizeof(Dictionary), "Dictionary size mismatch");
-
-void GDAPI godot_dictionary_new(godot_dictionary *r_dest) {
- Dictionary *dest = (Dictionary *)r_dest;
- memnew_placement(dest, Dictionary);
+void GDAPI godot_dictionary_new(godot_dictionary *p_self) {
+ memnew_placement(p_self, Dictionary);
}
void GDAPI godot_dictionary_new_copy(godot_dictionary *r_dest, const godot_dictionary *p_src) {
- Dictionary *dest = (Dictionary *)r_dest;
- const Dictionary *src = (const Dictionary *)p_src;
- memnew_placement(dest, Dictionary(*src));
+ memnew_placement(r_dest, Dictionary(*(Dictionary *)p_src));
}
void GDAPI godot_dictionary_destroy(godot_dictionary *p_self) {
@@ -57,133 +52,14 @@ void GDAPI godot_dictionary_destroy(godot_dictionary *p_self) {
self->~Dictionary();
}
-godot_dictionary GDAPI godot_dictionary_duplicate(const godot_dictionary *p_self, const godot_bool p_deep) {
- const Dictionary *self = (const Dictionary *)p_self;
- godot_dictionary res;
- Dictionary *val = (Dictionary *)&res;
- memnew_placement(val, Dictionary);
- *val = self->duplicate(p_deep);
- return res;
-}
-
-godot_int GDAPI godot_dictionary_size(const godot_dictionary *p_self) {
- const Dictionary *self = (const Dictionary *)p_self;
- return self->size();
-}
-
-godot_bool GDAPI godot_dictionary_empty(const godot_dictionary *p_self) {
- const Dictionary *self = (const Dictionary *)p_self;
- return self->empty();
-}
-
-void GDAPI godot_dictionary_clear(godot_dictionary *p_self) {
- Dictionary *self = (Dictionary *)p_self;
- self->clear();
-}
-
-godot_bool GDAPI godot_dictionary_has(const godot_dictionary *p_self, const godot_variant *p_key) {
- const Dictionary *self = (const Dictionary *)p_self;
- const Variant *key = (const Variant *)p_key;
- return self->has(*key);
-}
-
-godot_bool GDAPI godot_dictionary_has_all(const godot_dictionary *p_self, const godot_array *p_keys) {
- const Dictionary *self = (const Dictionary *)p_self;
- const Array *keys = (const Array *)p_keys;
- return self->has_all(*keys);
-}
-
-void GDAPI godot_dictionary_erase(godot_dictionary *p_self, const godot_variant *p_key) {
- Dictionary *self = (Dictionary *)p_self;
- const Variant *key = (const Variant *)p_key;
- self->erase(*key);
-}
-
-godot_int GDAPI godot_dictionary_hash(const godot_dictionary *p_self) {
- const Dictionary *self = (const Dictionary *)p_self;
- return self->hash();
-}
-
-godot_array GDAPI godot_dictionary_keys(const godot_dictionary *p_self) {
- godot_array dest;
- const Dictionary *self = (const Dictionary *)p_self;
- memnew_placement(&dest, Array(self->keys()));
- return dest;
-}
-
-godot_array GDAPI godot_dictionary_values(const godot_dictionary *p_self) {
- godot_array dest;
- const Dictionary *self = (const Dictionary *)p_self;
- memnew_placement(&dest, Array(self->values()));
- return dest;
-}
-
-godot_variant GDAPI godot_dictionary_get(const godot_dictionary *p_self, const godot_variant *p_key) {
- godot_variant raw_dest;
- Variant *dest = (Variant *)&raw_dest;
- const Dictionary *self = (const Dictionary *)p_self;
- const Variant *key = (const Variant *)p_key;
- memnew_placement(dest, Variant(self->operator[](*key)));
- return raw_dest;
-}
-
-void GDAPI godot_dictionary_set(godot_dictionary *p_self, const godot_variant *p_key, const godot_variant *p_value) {
- Dictionary *self = (Dictionary *)p_self;
- const Variant *key = (const Variant *)p_key;
- const Variant *value = (const Variant *)p_value;
- self->operator[](*key) = *value;
-}
-
godot_variant GDAPI *godot_dictionary_operator_index(godot_dictionary *p_self, const godot_variant *p_key) {
Dictionary *self = (Dictionary *)p_self;
- const Variant *key = (const Variant *)p_key;
- return (godot_variant *)&self->operator[](*key);
+ return (godot_variant *)&self->operator[](*((const Variant *)p_key));
}
const godot_variant GDAPI *godot_dictionary_operator_index_const(const godot_dictionary *p_self, const godot_variant *p_key) {
const Dictionary *self = (const Dictionary *)p_self;
- const Variant *key = (const Variant *)p_key;
- return (const godot_variant *)&self->operator[](*key);
-}
-
-godot_variant GDAPI *godot_dictionary_next(const godot_dictionary *p_self, const godot_variant *p_key) {
- Dictionary *self = (Dictionary *)p_self;
- const Variant *key = (const Variant *)p_key;
- return (godot_variant *)self->next(key);
-}
-
-godot_bool GDAPI godot_dictionary_operator_equal(const godot_dictionary *p_self, const godot_dictionary *p_b) {
- const Dictionary *self = (const Dictionary *)p_self;
- const Dictionary *b = (const Dictionary *)p_b;
- return *self == *b;
-}
-
-godot_string GDAPI godot_dictionary_to_json(const godot_dictionary *p_self) {
- godot_string raw_dest;
- String *dest = (String *)&raw_dest;
- const Dictionary *self = (const Dictionary *)p_self;
- memnew_placement(dest, String(JSON::print(Variant(*self))));
- return raw_dest;
-}
-
-// GDNative core 1.1
-
-godot_bool GDAPI godot_dictionary_erase_with_return(godot_dictionary *p_self, const godot_variant *p_key) {
- Dictionary *self = (Dictionary *)p_self;
- const Variant *key = (const Variant *)p_key;
- return self->erase(*key);
-}
-
-godot_variant GDAPI godot_dictionary_get_with_default(const godot_dictionary *p_self, const godot_variant *p_key, const godot_variant *p_default) {
- const Dictionary *self = (const Dictionary *)p_self;
- const Variant *key = (const Variant *)p_key;
- const Variant *def = (const Variant *)p_default;
-
- godot_variant raw_dest;
- Variant *dest = (Variant *)&raw_dest;
- memnew_placement(dest, Variant(self->get(*key, *def)));
-
- return raw_dest;
+ return (const godot_variant *)&self->operator[](*((const Variant *)p_key));
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/gdnative.cpp b/modules/gdnative/gdnative/gdnative.cpp
index e94190b07b..e0de1a0505 100644
--- a/modules/gdnative/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative/gdnative.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,12 +30,12 @@
#include "gdnative/gdnative.h"
-#include "core/class_db.h"
-#include "core/engine.h"
-#include "core/error_macros.h"
-#include "core/global_constants.h"
+#include "core/config/engine.h"
+#include "core/core_constants.h"
+#include "core/error/error_macros.h"
+#include "core/object/class_db.h"
#include "core/os/os.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "modules/gdnative/gdnative.h"
@@ -99,12 +99,12 @@ godot_class_constructor GDAPI godot_get_class_constructor(const char *p_classnam
godot_dictionary GDAPI godot_get_global_constants() {
godot_dictionary constants;
- godot_dictionary_new(&constants);
+ memnew_placement(&constants, Dictionary);
Dictionary *p_constants = (Dictionary *)&constants;
- const int constants_count = GlobalConstants::get_global_constant_count();
+ const int constants_count = CoreConstants::get_global_constant_count();
for (int i = 0; i < constants_count; ++i) {
- const char *name = GlobalConstants::get_global_constant_name(i);
- int value = GlobalConstants::get_global_constant_value(i);
+ const char *name = CoreConstants::get_global_constant_name(i);
+ int value = CoreConstants::get_global_constant_value(i);
(*p_constants)[name] = value;
}
return constants;
@@ -127,16 +127,15 @@ void GDAPI godot_free(void *p_ptr) {
memfree(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(const godot_string *p_message) {
- print_line(*(String *)p_message);
+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, 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/node_path.cpp b/modules/gdnative/gdnative/node_path.cpp
index 88ed650ebe..57d67b9abb 100644
--- a/modules/gdnative/gdnative/node_path.cpp
+++ b/modules/gdnative/gdnative/node_path.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,25 +30,20 @@
#include "gdnative/node_path.h"
-#include "core/node_path.h"
-#include "core/variant.h"
+#include "core/string/node_path.h"
+
+static_assert(sizeof(godot_node_path) == sizeof(NodePath), "NodePath size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_node_path) == sizeof(NodePath), "NodePath size mismatch");
-
-void GDAPI godot_node_path_new(godot_node_path *r_dest, const godot_string *p_from) {
- NodePath *dest = (NodePath *)r_dest;
- const String *from = (const String *)p_from;
- memnew_placement(dest, NodePath(*from));
+void GDAPI godot_node_path_new(godot_node_path *p_self) {
+ memnew_placement(p_self, NodePath);
}
void GDAPI godot_node_path_new_copy(godot_node_path *r_dest, const godot_node_path *p_src) {
- NodePath *dest = (NodePath *)r_dest;
- const NodePath *src = (const NodePath *)p_src;
- memnew_placement(dest, NodePath(*src));
+ memnew_placement(r_dest, NodePath(*(NodePath *)p_src));
}
void GDAPI godot_node_path_destroy(godot_node_path *p_self) {
@@ -56,71 +51,6 @@ void GDAPI godot_node_path_destroy(godot_node_path *p_self) {
self->~NodePath();
}
-godot_string GDAPI godot_node_path_as_string(const godot_node_path *p_self) {
- godot_string ret;
- const NodePath *self = (const NodePath *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_bool GDAPI godot_node_path_is_absolute(const godot_node_path *p_self) {
- const NodePath *self = (const NodePath *)p_self;
- return self->is_absolute();
-}
-
-godot_int GDAPI godot_node_path_get_name_count(const godot_node_path *p_self) {
- const NodePath *self = (const NodePath *)p_self;
- return self->get_name_count();
-}
-
-godot_string GDAPI godot_node_path_get_name(const godot_node_path *p_self, const godot_int p_idx) {
- godot_string dest;
- const NodePath *self = (const NodePath *)p_self;
-
- memnew_placement(&dest, String(self->get_name(p_idx)));
- return dest;
-}
-
-godot_int GDAPI godot_node_path_get_subname_count(const godot_node_path *p_self) {
- const NodePath *self = (const NodePath *)p_self;
- return self->get_subname_count();
-}
-
-godot_string GDAPI godot_node_path_get_subname(const godot_node_path *p_self, const godot_int p_idx) {
- godot_string dest;
- const NodePath *self = (const NodePath *)p_self;
-
- memnew_placement(&dest, String(self->get_subname(p_idx)));
- return dest;
-}
-
-godot_string GDAPI godot_node_path_get_concatenated_subnames(const godot_node_path *p_self) {
- godot_string dest;
- const NodePath *self = (const NodePath *)p_self;
- memnew_placement(&dest, String(self->get_concatenated_subnames()));
- return dest;
-}
-
-godot_bool GDAPI godot_node_path_is_empty(const godot_node_path *p_self) {
- const NodePath *self = (const NodePath *)p_self;
- return self->is_empty();
-}
-
-godot_bool GDAPI godot_node_path_operator_equal(const godot_node_path *p_self, const godot_node_path *p_b) {
- const NodePath *self = (const NodePath *)p_self;
- const NodePath *b = (const NodePath *)p_b;
- return *self == *b;
-}
-
-godot_node_path godot_node_path_get_as_property_path(const godot_node_path *p_self) {
- const NodePath *self = (const NodePath *)p_self;
- godot_node_path res;
- NodePath *val = (NodePath *)&res;
- memnew_placement(val, NodePath);
- *val = self->get_as_property_path();
- return res;
-}
-
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/gdnative/packed_arrays.cpp b/modules/gdnative/gdnative/packed_arrays.cpp
index de93c1d9b3..f03c94aeb8 100644
--- a/modules/gdnative/gdnative/packed_arrays.cpp
+++ b/modules/gdnative/gdnative/packed_arrays.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,997 +30,289 @@
#include "gdnative/packed_arrays.h"
-#include "core/array.h"
+#include "core/variant/variant.h"
-#include "core/variant.h"
-
-#include "core/color.h"
#include "core/math/vector2.h"
-#include "core/math/vector3.h"
+#include "core/math/vector3i.h"
+
+static_assert(sizeof(godot_packed_byte_array) == sizeof(PackedByteArray), "PackedByteArray size mismatch");
+static_assert(sizeof(godot_packed_int32_array) == sizeof(PackedInt32Array), "PackedInt32Array size mismatch");
+static_assert(sizeof(godot_packed_int64_array) == sizeof(PackedInt64Array), "PackedInt64Array size mismatch");
+static_assert(sizeof(godot_packed_float32_array) == sizeof(PackedFloat32Array), "PackedFloat32Array size mismatch");
+static_assert(sizeof(godot_packed_float64_array) == sizeof(PackedFloat64Array), "PackedFloat64Array size mismatch");
+static_assert(sizeof(godot_packed_string_array) == sizeof(PackedStringArray), "PackedStringArray size mismatch");
+static_assert(sizeof(godot_packed_vector2_array) == sizeof(PackedVector2Array), "PackedVector2Array size mismatch");
+static_assert(sizeof(godot_packed_vector2i_array) == sizeof(Vector<Vector2i>), "Vector<Vector2i> size mismatch");
+static_assert(sizeof(godot_packed_vector3_array) == sizeof(PackedVector3Array), "PackedVector3Array size mismatch");
+static_assert(sizeof(godot_packed_vector3i_array) == sizeof(Vector<Vector3i>), "Vector<Vector3i> size mismatch");
+static_assert(sizeof(godot_packed_color_array) == sizeof(PackedColorArray), "PackedColorArray size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_packed_byte_array) == sizeof(Vector<uint8_t>), "Vector<uint8_t> size mismatch");
-static_assert(sizeof(godot_packed_int32_array) == sizeof(Vector<int32_t>), "Vector<int32_t> size mismatch");
-static_assert(sizeof(godot_packed_int64_array) == sizeof(Vector<int64_t>), "Vector<int64_t> size mismatch");
-static_assert(sizeof(godot_packed_float32_array) == sizeof(Vector<float>), "Vector<float> size mismatch");
-static_assert(sizeof(godot_packed_float64_array) == sizeof(Vector<double>), "Vector<double> size mismatch");
-static_assert(sizeof(godot_packed_string_array) == sizeof(Vector<String>), "Vector<String> size mismatch");
-static_assert(sizeof(godot_packed_vector2_array) == sizeof(Vector<Vector2>), "Vector<Vector2> size mismatch");
-static_assert(sizeof(godot_packed_vector3_array) == sizeof(Vector<Vector3>), "Vector<Vector3> size mismatch");
-static_assert(sizeof(godot_packed_color_array) == sizeof(Vector<Color>), "Vector<Color> size mismatch");
-
-#define memnew_placement_custom(m_placement, m_class, m_constr) _post_initialize(new (m_placement, sizeof(m_class), "") m_constr)
-
// byte
-void GDAPI godot_packed_byte_array_new(godot_packed_byte_array *r_dest) {
- Vector<uint8_t> *dest = (Vector<uint8_t> *)r_dest;
- memnew_placement(dest, Vector<uint8_t>);
+void GDAPI godot_packed_byte_array_new(godot_packed_byte_array *p_self) {
+ memnew_placement(p_self, PackedByteArray);
}
void GDAPI godot_packed_byte_array_new_copy(godot_packed_byte_array *r_dest, const godot_packed_byte_array *p_src) {
- Vector<uint8_t> *dest = (Vector<uint8_t> *)r_dest;
- const Vector<uint8_t> *src = (const Vector<uint8_t> *)p_src;
- memnew_placement(dest, Vector<uint8_t>(*src));
+ memnew_placement(r_dest, PackedByteArray(*(PackedByteArray *)p_src));
}
-void GDAPI godot_packed_byte_array_new_with_array(godot_packed_byte_array *r_dest, const godot_array *p_a) {
- Vector<uint8_t> *dest = (Vector<uint8_t> *)r_dest;
- Array *a = (Array *)p_a;
- memnew_placement(dest, Vector<uint8_t>);
-
- dest->resize(a->size());
- for (int i = 0; i < a->size(); i++) {
- dest->set(i, (*a)[i]);
- }
-}
-
-const uint8_t GDAPI *godot_packed_byte_array_ptr(const godot_packed_byte_array *p_self) {
- const Vector<uint8_t> *self = (const Vector<uint8_t> *)p_self;
- return self->ptr();
-}
-
-uint8_t GDAPI *godot_packed_byte_array_ptrw(godot_packed_byte_array *p_self) {
- Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
- return self->ptrw();
-}
-
-void GDAPI godot_packed_byte_array_append(godot_packed_byte_array *p_self, const uint8_t p_data) {
- Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
- self->push_back(p_data);
-}
-
-void GDAPI godot_packed_byte_array_append_array(godot_packed_byte_array *p_self, const godot_packed_byte_array *p_array) {
- Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
- Vector<uint8_t> *array = (Vector<uint8_t> *)p_array;
- self->append_array(*array);
-}
-
-godot_error GDAPI godot_packed_byte_array_insert(godot_packed_byte_array *p_self, const godot_int p_idx, const uint8_t p_data) {
- Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
- return (godot_error)self->insert(p_idx, p_data);
-}
-
-godot_bool GDAPI godot_packed_byte_array_has(godot_packed_byte_array *p_self, const uint8_t p_value) {
- Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
- return (godot_bool)self->has(p_value);
-}
-
-void GDAPI godot_packed_byte_array_sort(godot_packed_byte_array *p_self) {
- Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
- self->sort();
-}
-
-void GDAPI godot_packed_byte_array_invert(godot_packed_byte_array *p_self) {
- Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
- self->invert();
-}
-
-void GDAPI godot_packed_byte_array_push_back(godot_packed_byte_array *p_self, const uint8_t p_data) {
- Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
- self->push_back(p_data);
-}
-
-void GDAPI godot_packed_byte_array_remove(godot_packed_byte_array *p_self, const godot_int p_idx) {
- Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
- self->remove(p_idx);
-}
-
-void GDAPI godot_packed_byte_array_resize(godot_packed_byte_array *p_self, const godot_int p_size) {
- Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
- self->resize(p_size);
-}
-
-void GDAPI godot_packed_byte_array_set(godot_packed_byte_array *p_self, const godot_int p_idx, const uint8_t p_data) {
- Vector<uint8_t> *self = (Vector<uint8_t> *)p_self;
- self->set(p_idx, p_data);
-}
-
-uint8_t GDAPI godot_packed_byte_array_get(const godot_packed_byte_array *p_self, const godot_int p_idx) {
- const Vector<uint8_t> *self = (const Vector<uint8_t> *)p_self;
- return self->get(p_idx);
+void GDAPI godot_packed_byte_array_destroy(godot_packed_byte_array *p_self) {
+ ((PackedByteArray *)p_self)->~PackedByteArray();
}
-godot_int GDAPI godot_packed_byte_array_size(const godot_packed_byte_array *p_self) {
- const Vector<uint8_t> *self = (const Vector<uint8_t> *)p_self;
- return self->size();
+uint8_t GDAPI *godot_packed_byte_array_operator_index(godot_packed_byte_array *p_self, godot_int p_index) {
+ PackedByteArray *self = (PackedByteArray *)p_self;
+ return (uint8_t *)&self->operator[](p_index);
}
-godot_bool GDAPI godot_packed_byte_array_empty(const godot_packed_byte_array *p_self) {
- const Vector<uint8_t> *self = (const Vector<uint8_t> *)p_self;
- return self->empty();
-}
-
-void GDAPI godot_packed_byte_array_destroy(godot_packed_byte_array *p_self) {
- ((Vector<uint8_t> *)p_self)->~Vector();
+const uint8_t GDAPI *godot_packed_byte_array_operator_index_const(const godot_packed_byte_array *p_self, godot_int p_index) {
+ const PackedByteArray *self = (const PackedByteArray *)p_self;
+ return (const uint8_t *)&self->operator[](p_index);
}
// int32
-void GDAPI godot_packed_int32_array_new(godot_packed_int32_array *r_dest) {
- Vector<int32_t> *dest = (Vector<int32_t> *)r_dest;
- memnew_placement(dest, Vector<int32_t>);
+void GDAPI godot_packed_int32_array_new(godot_packed_int32_array *p_self) {
+ memnew_placement(p_self, PackedInt32Array);
}
void GDAPI godot_packed_int32_array_new_copy(godot_packed_int32_array *r_dest, const godot_packed_int32_array *p_src) {
- Vector<int32_t> *dest = (Vector<int32_t> *)r_dest;
- const Vector<int32_t> *src = (const Vector<int32_t> *)p_src;
- memnew_placement(dest, Vector<int32_t>(*src));
-}
-
-void GDAPI godot_packed_int32_array_new_with_array(godot_packed_int32_array *r_dest, const godot_array *p_a) {
- Vector<int32_t> *dest = (Vector<int32_t> *)r_dest;
- Array *a = (Array *)p_a;
- memnew_placement(dest, Vector<int32_t>);
-
- dest->resize(a->size());
- for (int i = 0; i < a->size(); i++) {
- dest->set(i, (*a)[i]);
- }
-}
-
-const int32_t GDAPI *godot_packed_int32_array_ptr(const godot_packed_int32_array *p_self) {
- const Vector<int32_t> *self = (const Vector<int32_t> *)p_self;
- return self->ptr();
-}
-
-int32_t GDAPI *godot_packed_int32_array_ptrw(godot_packed_int32_array *p_self) {
- Vector<int32_t> *self = (Vector<int32_t> *)p_self;
- return self->ptrw();
-}
-
-void GDAPI godot_packed_int32_array_append(godot_packed_int32_array *p_self, const int32_t p_data) {
- Vector<int32_t> *self = (Vector<int32_t> *)p_self;
- self->push_back(p_data);
-}
-
-void GDAPI godot_packed_int32_array_append_array(godot_packed_int32_array *p_self, const godot_packed_int32_array *p_array) {
- Vector<int32_t> *self = (Vector<int32_t> *)p_self;
- Vector<int32_t> *array = (Vector<int32_t> *)p_array;
- self->append_array(*array);
-}
-
-godot_error GDAPI godot_packed_int32_array_insert(godot_packed_int32_array *p_self, const godot_int p_idx, const int32_t p_data) {
- Vector<int32_t> *self = (Vector<int32_t> *)p_self;
- return (godot_error)self->insert(p_idx, p_data);
-}
-
-godot_bool GDAPI godot_packed_int32_array_has(godot_packed_int32_array *p_self, const int32_t p_value) {
- Vector<int32_t> *self = (Vector<int32_t> *)p_self;
- return (godot_bool)self->has(p_value);
+ memnew_placement(r_dest, PackedInt32Array(*(PackedInt32Array *)p_src));
}
-void GDAPI godot_packed_int32_array_sort(godot_packed_int32_array *p_self) {
- Vector<int32_t> *self = (Vector<int32_t> *)p_self;
- self->sort();
-}
-
-void GDAPI godot_packed_int32_array_invert(godot_packed_int32_array *p_self) {
- Vector<int32_t> *self = (Vector<int32_t> *)p_self;
- self->invert();
-}
-
-void GDAPI godot_packed_int32_array_push_back(godot_packed_int32_array *p_self, const int32_t p_data) {
- Vector<int32_t> *self = (Vector<int32_t> *)p_self;
- self->push_back(p_data);
-}
-
-void GDAPI godot_packed_int32_array_remove(godot_packed_int32_array *p_self, const godot_int p_idx) {
- Vector<int32_t> *self = (Vector<int32_t> *)p_self;
- self->remove(p_idx);
-}
-
-void GDAPI godot_packed_int32_array_resize(godot_packed_int32_array *p_self, const godot_int p_size) {
- Vector<int32_t> *self = (Vector<int32_t> *)p_self;
- self->resize(p_size);
-}
-
-void GDAPI godot_packed_int32_array_set(godot_packed_int32_array *p_self, const godot_int p_idx, const int32_t p_data) {
- Vector<int32_t> *self = (Vector<int32_t> *)p_self;
- self->set(p_idx, p_data);
-}
-
-int32_t GDAPI godot_packed_int32_array_get(const godot_packed_int32_array *p_self, const godot_int p_idx) {
- const Vector<int32_t> *self = (const Vector<int32_t> *)p_self;
- return self->get(p_idx);
-}
-
-godot_int GDAPI godot_packed_int32_array_size(const godot_packed_int32_array *p_self) {
- const Vector<int32_t> *self = (const Vector<int32_t> *)p_self;
- return self->size();
+void GDAPI godot_packed_int32_array_destroy(godot_packed_int32_array *p_self) {
+ ((PackedInt32Array *)p_self)->~PackedInt32Array();
}
-godot_bool GDAPI godot_packed_int32_array_empty(const godot_packed_int32_array *p_self) {
- const Vector<int32_t> *self = (const Vector<int32_t> *)p_self;
- return self->empty();
+int32_t GDAPI *godot_packed_int32_array_operator_index(godot_packed_int32_array *p_self, godot_int p_index) {
+ PackedInt32Array *self = (PackedInt32Array *)p_self;
+ return (int32_t *)&self->operator[](p_index);
}
-void GDAPI godot_packed_int32_array_destroy(godot_packed_int32_array *p_self) {
- ((Vector<int32_t> *)p_self)->~Vector();
+const int32_t GDAPI *godot_packed_int32_array_operator_index_const(const godot_packed_int32_array *p_self, godot_int p_index) {
+ const PackedInt32Array *self = (const PackedInt32Array *)p_self;
+ return (const int32_t *)&self->operator[](p_index);
}
// int64
-void GDAPI godot_packed_int64_array_new(godot_packed_int64_array *r_dest) {
- Vector<int64_t> *dest = (Vector<int64_t> *)r_dest;
- memnew_placement(dest, Vector<int64_t>);
+void GDAPI godot_packed_int64_array_new(godot_packed_int64_array *p_self) {
+ memnew_placement(p_self, PackedInt64Array);
}
void GDAPI godot_packed_int64_array_new_copy(godot_packed_int64_array *r_dest, const godot_packed_int64_array *p_src) {
- Vector<int64_t> *dest = (Vector<int64_t> *)r_dest;
- const Vector<int64_t> *src = (const Vector<int64_t> *)p_src;
- memnew_placement(dest, Vector<int64_t>(*src));
+ memnew_placement(r_dest, PackedInt64Array(*(PackedInt64Array *)p_src));
}
-void GDAPI godot_packed_int64_array_new_with_array(godot_packed_int64_array *r_dest, const godot_array *p_a) {
- Vector<int64_t> *dest = (Vector<int64_t> *)r_dest;
- Array *a = (Array *)p_a;
- memnew_placement(dest, Vector<int64_t>);
-
- dest->resize(a->size());
- for (int i = 0; i < a->size(); i++) {
- dest->set(i, (*a)[i]);
- }
-}
-
-const int64_t GDAPI *godot_packed_int64_array_ptr(const godot_packed_int64_array *p_self) {
- const Vector<int64_t> *self = (const Vector<int64_t> *)p_self;
- return self->ptr();
-}
-
-int64_t GDAPI *godot_packed_int64_array_ptrw(godot_packed_int64_array *p_self) {
- Vector<int64_t> *self = (Vector<int64_t> *)p_self;
- return self->ptrw();
-}
-
-void GDAPI godot_packed_int64_array_append(godot_packed_int64_array *p_self, const int64_t p_data) {
- Vector<int64_t> *self = (Vector<int64_t> *)p_self;
- self->push_back(p_data);
-}
-
-void GDAPI godot_packed_int64_array_append_array(godot_packed_int64_array *p_self, const godot_packed_int64_array *p_array) {
- Vector<int64_t> *self = (Vector<int64_t> *)p_self;
- Vector<int64_t> *array = (Vector<int64_t> *)p_array;
- self->append_array(*array);
-}
-
-godot_error GDAPI godot_packed_int64_array_insert(godot_packed_int64_array *p_self, const godot_int p_idx, const int64_t p_data) {
- Vector<int64_t> *self = (Vector<int64_t> *)p_self;
- return (godot_error)self->insert(p_idx, p_data);
-}
-
-godot_bool GDAPI godot_packed_int64_array_has(godot_packed_int64_array *p_self, const int64_t p_value) {
- Vector<int64_t> *self = (Vector<int64_t> *)p_self;
- return (godot_bool)self->has(p_value);
-}
-
-void GDAPI godot_packed_int64_array_sort(godot_packed_int64_array *p_self) {
- Vector<int64_t> *self = (Vector<int64_t> *)p_self;
- self->sort();
-}
-
-void GDAPI godot_packed_int64_array_invert(godot_packed_int64_array *p_self) {
- Vector<int64_t> *self = (Vector<int64_t> *)p_self;
- self->invert();
-}
-
-void GDAPI godot_packed_int64_array_push_back(godot_packed_int64_array *p_self, const int64_t p_data) {
- Vector<int64_t> *self = (Vector<int64_t> *)p_self;
- self->push_back(p_data);
-}
-
-void GDAPI godot_packed_int64_array_remove(godot_packed_int64_array *p_self, const godot_int p_idx) {
- Vector<int64_t> *self = (Vector<int64_t> *)p_self;
- self->remove(p_idx);
-}
-
-void GDAPI godot_packed_int64_array_resize(godot_packed_int64_array *p_self, const godot_int p_size) {
- Vector<int64_t> *self = (Vector<int64_t> *)p_self;
- self->resize(p_size);
-}
-
-void GDAPI godot_packed_int64_array_set(godot_packed_int64_array *p_self, const godot_int p_idx, const int64_t p_data) {
- Vector<int64_t> *self = (Vector<int64_t> *)p_self;
- self->set(p_idx, p_data);
-}
-
-int64_t GDAPI godot_packed_int64_array_get(const godot_packed_int64_array *p_self, const godot_int p_idx) {
- const Vector<int64_t> *self = (const Vector<int64_t> *)p_self;
- return self->get(p_idx);
-}
-
-godot_int GDAPI godot_packed_int64_array_size(const godot_packed_int64_array *p_self) {
- const Vector<int64_t> *self = (const Vector<int64_t> *)p_self;
- return self->size();
+void GDAPI godot_packed_int64_array_destroy(godot_packed_int64_array *p_self) {
+ ((PackedInt64Array *)p_self)->~PackedInt64Array();
}
-godot_bool GDAPI godot_packed_int64_array_empty(const godot_packed_int64_array *p_self) {
- const Vector<int64_t> *self = (const Vector<int64_t> *)p_self;
- return self->empty();
+int64_t GDAPI *godot_packed_int64_array_operator_index(godot_packed_int64_array *p_self, godot_int p_index) {
+ PackedInt64Array *self = (PackedInt64Array *)p_self;
+ return (int64_t *)&self->operator[](p_index);
}
-void GDAPI godot_packed_int64_array_destroy(godot_packed_int64_array *p_self) {
- ((Vector<int64_t> *)p_self)->~Vector();
+const int64_t GDAPI *godot_packed_int64_array_operator_index_const(const godot_packed_int64_array *p_self, godot_int p_index) {
+ const PackedInt64Array *self = (const PackedInt64Array *)p_self;
+ return (const int64_t *)&self->operator[](p_index);
}
// float32
-void GDAPI godot_packed_float32_array_new(godot_packed_float32_array *r_dest) {
- Vector<float> *dest = (Vector<float> *)r_dest;
- memnew_placement(dest, Vector<float>);
+void GDAPI godot_packed_float32_array_new(godot_packed_float32_array *p_self) {
+ memnew_placement(p_self, PackedFloat32Array);
}
void GDAPI godot_packed_float32_array_new_copy(godot_packed_float32_array *r_dest, const godot_packed_float32_array *p_src) {
- Vector<float> *dest = (Vector<float> *)r_dest;
- const Vector<float> *src = (const Vector<float> *)p_src;
- memnew_placement(dest, Vector<float>(*src));
+ memnew_placement(r_dest, PackedFloat32Array(*(PackedFloat32Array *)p_src));
}
-void GDAPI godot_packed_float32_array_new_with_array(godot_packed_float32_array *r_dest, const godot_array *p_a) {
- Vector<float> *dest = (Vector<float> *)r_dest;
- Array *a = (Array *)p_a;
- memnew_placement(dest, Vector<float>);
-
- dest->resize(a->size());
- for (int i = 0; i < a->size(); i++) {
- dest->set(i, (*a)[i]);
- }
-}
-
-const float GDAPI *godot_packed_float32_array_ptr(const godot_packed_float32_array *p_self) {
- const Vector<float> *self = (const Vector<float> *)p_self;
- return self->ptr();
-}
-
-float GDAPI *godot_packed_float32_array_ptrw(godot_packed_float32_array *p_self) {
- Vector<float> *self = (Vector<float> *)p_self;
- return self->ptrw();
-}
-
-void GDAPI godot_packed_float32_array_append(godot_packed_float32_array *p_self, const float p_data) {
- Vector<float> *self = (Vector<float> *)p_self;
- self->push_back(p_data);
-}
-
-void GDAPI godot_packed_float32_array_append_array(godot_packed_float32_array *p_self, const godot_packed_float32_array *p_array) {
- Vector<float> *self = (Vector<float> *)p_self;
- Vector<float> *array = (Vector<float> *)p_array;
- self->append_array(*array);
-}
-
-godot_error GDAPI godot_packed_float32_array_insert(godot_packed_float32_array *p_self, const godot_int p_idx, const float p_data) {
- Vector<float> *self = (Vector<float> *)p_self;
- return (godot_error)self->insert(p_idx, p_data);
-}
-
-godot_bool GDAPI godot_packed_float32_array_has(godot_packed_float32_array *p_self, const float p_value) {
- Vector<float> *self = (Vector<float> *)p_self;
- return (godot_bool)self->has(p_value);
-}
-
-void GDAPI godot_packed_float32_array_sort(godot_packed_float32_array *p_self) {
- Vector<float> *self = (Vector<float> *)p_self;
- self->sort();
-}
-
-void GDAPI godot_packed_float32_array_invert(godot_packed_float32_array *p_self) {
- Vector<float> *self = (Vector<float> *)p_self;
- self->invert();
-}
-
-void GDAPI godot_packed_float32_array_push_back(godot_packed_float32_array *p_self, const float p_data) {
- Vector<float> *self = (Vector<float> *)p_self;
- self->push_back(p_data);
-}
-
-void GDAPI godot_packed_float32_array_remove(godot_packed_float32_array *p_self, const godot_int p_idx) {
- Vector<float> *self = (Vector<float> *)p_self;
- self->remove(p_idx);
-}
-
-void GDAPI godot_packed_float32_array_resize(godot_packed_float32_array *p_self, const godot_int p_size) {
- Vector<float> *self = (Vector<float> *)p_self;
- self->resize(p_size);
-}
-
-void GDAPI godot_packed_float32_array_set(godot_packed_float32_array *p_self, const godot_int p_idx, const float p_data) {
- Vector<float> *self = (Vector<float> *)p_self;
- self->set(p_idx, p_data);
-}
-
-float GDAPI godot_packed_float32_array_get(const godot_packed_float32_array *p_self, const godot_int p_idx) {
- const Vector<float> *self = (const Vector<float> *)p_self;
- return self->get(p_idx);
-}
-
-godot_int GDAPI godot_packed_float32_array_size(const godot_packed_float32_array *p_self) {
- const Vector<float> *self = (const Vector<float> *)p_self;
- return self->size();
+void GDAPI godot_packed_float32_array_destroy(godot_packed_float32_array *p_self) {
+ ((PackedFloat32Array *)p_self)->~PackedFloat32Array();
}
-godot_bool GDAPI godot_packed_float32_array_empty(const godot_packed_float32_array *p_self) {
- const Vector<float> *self = (const Vector<float> *)p_self;
- return self->empty();
+float GDAPI *godot_packed_float32_array_operator_index(godot_packed_float32_array *p_self, godot_int p_index) {
+ PackedFloat32Array *self = (PackedFloat32Array *)p_self;
+ return (float *)&self->operator[](p_index);
}
-void GDAPI godot_packed_float32_array_destroy(godot_packed_float32_array *p_self) {
- ((Vector<float> *)p_self)->~Vector();
+const float GDAPI *godot_packed_float32_array_operator_index_const(const godot_packed_float32_array *p_self, godot_int p_index) {
+ const PackedFloat32Array *self = (const PackedFloat32Array *)p_self;
+ return (const float *)&self->operator[](p_index);
}
// float64
-void GDAPI godot_packed_float64_array_new(godot_packed_float64_array *r_dest) {
- Vector<double> *dest = (Vector<double> *)r_dest;
- memnew_placement(dest, Vector<double>);
+void GDAPI godot_packed_float64_array_new(godot_packed_float64_array *p_self) {
+ memnew_placement(p_self, PackedFloat64Array);
}
void GDAPI godot_packed_float64_array_new_copy(godot_packed_float64_array *r_dest, const godot_packed_float64_array *p_src) {
- Vector<double> *dest = (Vector<double> *)r_dest;
- const Vector<double> *src = (const Vector<double> *)p_src;
- memnew_placement(dest, Vector<double>(*src));
-}
-
-void GDAPI godot_packed_float64_array_new_with_array(godot_packed_float64_array *r_dest, const godot_array *p_a) {
- Vector<double> *dest = (Vector<double> *)r_dest;
- Array *a = (Array *)p_a;
- memnew_placement(dest, Vector<double>);
-
- dest->resize(a->size());
- for (int i = 0; i < a->size(); i++) {
- dest->set(i, (*a)[i]);
- }
+ memnew_placement(r_dest, PackedFloat64Array(*(PackedFloat64Array *)p_src));
}
-const double GDAPI *godot_packed_float64_array_ptr(const godot_packed_float64_array *p_self) {
- const Vector<double> *self = (const Vector<double> *)p_self;
- return self->ptr();
-}
-
-double GDAPI *godot_packed_float64_array_ptrw(godot_packed_float64_array *p_self) {
- Vector<double> *self = (Vector<double> *)p_self;
- return self->ptrw();
-}
-
-void GDAPI godot_packed_float64_array_append(godot_packed_float64_array *p_self, const double p_data) {
- Vector<double> *self = (Vector<double> *)p_self;
- self->push_back(p_data);
-}
-
-void GDAPI godot_packed_float64_array_append_array(godot_packed_float64_array *p_self, const godot_packed_float64_array *p_array) {
- Vector<double> *self = (Vector<double> *)p_self;
- Vector<double> *array = (Vector<double> *)p_array;
- self->append_array(*array);
-}
-
-godot_error GDAPI godot_packed_float64_array_insert(godot_packed_float64_array *p_self, const godot_int p_idx, const double p_data) {
- Vector<double> *self = (Vector<double> *)p_self;
- return (godot_error)self->insert(p_idx, p_data);
-}
-
-godot_bool GDAPI godot_packed_float64_array_has(godot_packed_float64_array *p_self, const double p_value) {
- Vector<double> *self = (Vector<double> *)p_self;
- return (godot_bool)self->has(p_value);
-}
-
-void GDAPI godot_packed_float64_array_sort(godot_packed_float64_array *p_self) {
- Vector<double> *self = (Vector<double> *)p_self;
- self->sort();
-}
-
-void GDAPI godot_packed_float64_array_invert(godot_packed_float64_array *p_self) {
- Vector<double> *self = (Vector<double> *)p_self;
- self->invert();
-}
-
-void GDAPI godot_packed_float64_array_push_back(godot_packed_float64_array *p_self, const double p_data) {
- Vector<double> *self = (Vector<double> *)p_self;
- self->push_back(p_data);
-}
-
-void GDAPI godot_packed_float64_array_remove(godot_packed_float64_array *p_self, const godot_int p_idx) {
- Vector<double> *self = (Vector<double> *)p_self;
- self->remove(p_idx);
-}
-
-void GDAPI godot_packed_float64_array_resize(godot_packed_float64_array *p_self, const godot_int p_size) {
- Vector<double> *self = (Vector<double> *)p_self;
- self->resize(p_size);
-}
-
-void GDAPI godot_packed_float64_array_set(godot_packed_float64_array *p_self, const godot_int p_idx, const double p_data) {
- Vector<double> *self = (Vector<double> *)p_self;
- self->set(p_idx, p_data);
-}
-
-double GDAPI godot_packed_float64_array_get(const godot_packed_float64_array *p_self, const godot_int p_idx) {
- const Vector<double> *self = (const Vector<double> *)p_self;
- return self->get(p_idx);
-}
-
-godot_int GDAPI godot_packed_float64_array_size(const godot_packed_float64_array *p_self) {
- const Vector<double> *self = (const Vector<double> *)p_self;
- return self->size();
+void GDAPI godot_packed_float64_array_destroy(godot_packed_float64_array *p_self) {
+ ((PackedFloat64Array *)p_self)->~PackedFloat64Array();
}
-godot_bool GDAPI godot_packed_float64_array_empty(const godot_packed_float64_array *p_self) {
- const Vector<double> *self = (const Vector<double> *)p_self;
- return self->empty();
+double GDAPI *godot_packed_float64_array_operator_index(godot_packed_float64_array *p_self, godot_int p_index) {
+ PackedFloat64Array *self = (PackedFloat64Array *)p_self;
+ return (double *)&self->operator[](p_index);
}
-void GDAPI godot_packed_float64_array_destroy(godot_packed_float64_array *p_self) {
- ((Vector<double> *)p_self)->~Vector();
+const double GDAPI *godot_packed_float64_array_operator_index_const(const godot_packed_float64_array *p_self, godot_int p_index) {
+ const PackedFloat64Array *self = (const PackedFloat64Array *)p_self;
+ return (const double *)&self->operator[](p_index);
}
// string
-void GDAPI godot_packed_string_array_new(godot_packed_string_array *r_dest) {
- Vector<String> *dest = (Vector<String> *)r_dest;
- memnew_placement(dest, Vector<String>);
+void GDAPI godot_packed_string_array_new(godot_packed_string_array *p_self) {
+ memnew_placement(p_self, PackedStringArray);
}
void GDAPI godot_packed_string_array_new_copy(godot_packed_string_array *r_dest, const godot_packed_string_array *p_src) {
- Vector<String> *dest = (Vector<String> *)r_dest;
- const Vector<String> *src = (const Vector<String> *)p_src;
- memnew_placement(dest, Vector<String>(*src));
-}
-
-void GDAPI godot_packed_string_array_new_with_array(godot_packed_string_array *r_dest, const godot_array *p_a) {
- Vector<String> *dest = (Vector<String> *)r_dest;
- Array *a = (Array *)p_a;
- memnew_placement(dest, Vector<String>);
-
- dest->resize(a->size());
- for (int i = 0; i < a->size(); i++) {
- dest->set(i, (*a)[i]);
- }
-}
-
-const godot_string GDAPI *godot_packed_string_array_ptr(const godot_packed_string_array *p_self) {
- const Vector<String> *self = (const Vector<String> *)p_self;
- return (const godot_string *)self->ptr();
+ memnew_placement(r_dest, PackedStringArray(*(PackedStringArray *)p_src));
}
-godot_string GDAPI *godot_packed_string_array_ptrw(godot_packed_string_array *p_self) {
- Vector<String> *self = (Vector<String> *)p_self;
- return (godot_string *)self->ptrw();
-}
-
-void GDAPI godot_packed_string_array_append(godot_packed_string_array *p_self, const godot_string *p_data) {
- Vector<String> *self = (Vector<String> *)p_self;
- String &s = *(String *)p_data;
- self->push_back(s);
-}
-
-void GDAPI godot_packed_string_array_append_array(godot_packed_string_array *p_self, const godot_packed_string_array *p_array) {
- Vector<String> *self = (Vector<String> *)p_self;
- Vector<String> *array = (Vector<String> *)p_array;
- self->append_array(*array);
-}
-
-godot_error GDAPI godot_packed_string_array_insert(godot_packed_string_array *p_self, const godot_int p_idx, const godot_string *p_data) {
- Vector<String> *self = (Vector<String> *)p_self;
- String &s = *(String *)p_data;
- return (godot_error)self->insert(p_idx, s);
-}
-
-godot_bool GDAPI godot_packed_string_array_has(godot_packed_string_array *p_self, const godot_string *p_value) {
- Vector<String> *self = (Vector<String> *)p_self;
- String &s = *(String *)p_value;
- return (godot_bool)self->has(s);
-}
-
-void GDAPI godot_packed_string_array_sort(godot_packed_string_array *p_self) {
- Vector<String> *self = (Vector<String> *)p_self;
- self->sort();
-}
-
-void GDAPI godot_packed_string_array_invert(godot_packed_string_array *p_self) {
- Vector<String> *self = (Vector<String> *)p_self;
- self->invert();
-}
-
-void GDAPI godot_packed_string_array_push_back(godot_packed_string_array *p_self, const godot_string *p_data) {
- Vector<String> *self = (Vector<String> *)p_self;
- String &s = *(String *)p_data;
- self->push_back(s);
-}
-
-void GDAPI godot_packed_string_array_remove(godot_packed_string_array *p_self, const godot_int p_idx) {
- Vector<String> *self = (Vector<String> *)p_self;
- self->remove(p_idx);
-}
-
-void GDAPI godot_packed_string_array_resize(godot_packed_string_array *p_self, const godot_int p_size) {
- Vector<String> *self = (Vector<String> *)p_self;
- self->resize(p_size);
-}
-
-void GDAPI godot_packed_string_array_set(godot_packed_string_array *p_self, const godot_int p_idx, const godot_string *p_data) {
- Vector<String> *self = (Vector<String> *)p_self;
- String &s = *(String *)p_data;
- self->set(p_idx, s);
-}
-
-godot_string GDAPI godot_packed_string_array_get(const godot_packed_string_array *p_self, const godot_int p_idx) {
- const Vector<String> *self = (const Vector<String> *)p_self;
- godot_string str;
- String *s = (String *)&str;
- memnew_placement(s, String);
- *s = self->get(p_idx);
- return str;
-}
-
-godot_int GDAPI godot_packed_string_array_size(const godot_packed_string_array *p_self) {
- const Vector<String> *self = (const Vector<String> *)p_self;
- return self->size();
+void GDAPI godot_packed_string_array_destroy(godot_packed_string_array *p_self) {
+ ((PackedStringArray *)p_self)->~PackedStringArray();
}
-godot_bool GDAPI godot_packed_string_array_empty(const godot_packed_string_array *p_self) {
- const Vector<String> *self = (const Vector<String> *)p_self;
- return self->empty();
+godot_string GDAPI *godot_packed_string_array_operator_index(godot_packed_string_array *p_self, godot_int p_index) {
+ PackedStringArray *self = (PackedStringArray *)p_self;
+ return (godot_string *)&self->operator[](p_index);
}
-void GDAPI godot_packed_string_array_destroy(godot_packed_string_array *p_self) {
- ((Vector<String> *)p_self)->~Vector();
+const godot_string GDAPI *godot_packed_string_array_operator_index_const(const godot_packed_string_array *p_self, godot_int p_index) {
+ const PackedStringArray *self = (const PackedStringArray *)p_self;
+ return (const godot_string *)&self->operator[](p_index);
}
// vector2
-void GDAPI godot_packed_vector2_array_new(godot_packed_vector2_array *r_dest) {
- Vector<Vector2> *dest = (Vector<Vector2> *)r_dest;
- memnew_placement(dest, Vector<Vector2>);
+void GDAPI godot_packed_vector2_array_new(godot_packed_vector2_array *p_self) {
+ memnew_placement(p_self, PackedVector2Array);
}
void GDAPI godot_packed_vector2_array_new_copy(godot_packed_vector2_array *r_dest, const godot_packed_vector2_array *p_src) {
- Vector<Vector2> *dest = (Vector<Vector2> *)r_dest;
- const Vector<Vector2> *src = (const Vector<Vector2> *)p_src;
- memnew_placement(dest, Vector<Vector2>(*src));
-}
-
-void GDAPI godot_packed_vector2_array_new_with_array(godot_packed_vector2_array *r_dest, const godot_array *p_a) {
- Vector<Vector2> *dest = (Vector<Vector2> *)r_dest;
- Array *a = (Array *)p_a;
- memnew_placement(dest, Vector<Vector2>);
-
- dest->resize(a->size());
- for (int i = 0; i < a->size(); i++) {
- dest->set(i, (*a)[i]);
- }
-}
-
-const godot_vector2 GDAPI *godot_packed_vector2_array_ptr(const godot_packed_vector2_array *p_self) {
- const Vector<Vector2> *self = (const Vector<Vector2> *)p_self;
- return (const godot_vector2 *)self->ptr();
-}
-
-godot_vector2 GDAPI *godot_packed_vector2_array_ptrw(godot_packed_vector2_array *p_self) {
- Vector<Vector2> *self = (Vector<Vector2> *)p_self;
- return (godot_vector2 *)self->ptrw();
-}
-
-void GDAPI godot_packed_vector2_array_append(godot_packed_vector2_array *p_self, const godot_vector2 *p_data) {
- Vector<Vector2> *self = (Vector<Vector2> *)p_self;
- Vector2 &s = *(Vector2 *)p_data;
- self->push_back(s);
-}
-
-void GDAPI godot_packed_vector2_array_append_array(godot_packed_vector2_array *p_self, const godot_packed_vector2_array *p_array) {
- Vector<Vector2> *self = (Vector<Vector2> *)p_self;
- Vector<Vector2> *array = (Vector<Vector2> *)p_array;
- self->append_array(*array);
-}
-
-godot_error GDAPI godot_packed_vector2_array_insert(godot_packed_vector2_array *p_self, const godot_int p_idx, const godot_vector2 *p_data) {
- Vector<Vector2> *self = (Vector<Vector2> *)p_self;
- Vector2 &s = *(Vector2 *)p_data;
- return (godot_error)self->insert(p_idx, s);
-}
-
-godot_bool GDAPI godot_packed_vector2_array_has(godot_packed_vector2_array *p_self, const godot_vector2 *p_value) {
- Vector<Vector2> *self = (Vector<Vector2> *)p_self;
- Vector2 &v = *(Vector2 *)p_value;
- return (godot_bool)self->has(v);
+ memnew_placement(r_dest, PackedVector2Array(*(PackedVector2Array *)p_src));
}
-void GDAPI godot_packed_vector2_array_sort(godot_packed_vector2_array *p_self) {
- Vector<Vector2> *self = (Vector<Vector2> *)p_self;
- self->sort();
-}
-
-void GDAPI godot_packed_vector2_array_invert(godot_packed_vector2_array *p_self) {
- Vector<Vector2> *self = (Vector<Vector2> *)p_self;
- self->invert();
+void GDAPI godot_packed_vector2_array_destroy(godot_packed_vector2_array *p_self) {
+ ((PackedVector2Array *)p_self)->~PackedVector2Array();
}
-void GDAPI godot_packed_vector2_array_push_back(godot_packed_vector2_array *p_self, const godot_vector2 *p_data) {
- Vector<Vector2> *self = (Vector<Vector2> *)p_self;
- Vector2 &s = *(Vector2 *)p_data;
- self->push_back(s);
+godot_vector2 GDAPI *godot_packed_vector2_array_operator_index(godot_packed_vector2_array *p_self, godot_int p_index) {
+ PackedVector2Array *self = (PackedVector2Array *)p_self;
+ return (godot_vector2 *)&self->operator[](p_index);
}
-void GDAPI godot_packed_vector2_array_remove(godot_packed_vector2_array *p_self, const godot_int p_idx) {
- Vector<Vector2> *self = (Vector<Vector2> *)p_self;
- self->remove(p_idx);
+const godot_vector2 GDAPI *godot_packed_vector2_array_operator_index_const(const godot_packed_vector2_array *p_self, godot_int p_index) {
+ const PackedVector2Array *self = (const PackedVector2Array *)p_self;
+ return (const godot_vector2 *)&self->operator[](p_index);
}
-void GDAPI godot_packed_vector2_array_resize(godot_packed_vector2_array *p_self, const godot_int p_size) {
- Vector<Vector2> *self = (Vector<Vector2> *)p_self;
- self->resize(p_size);
-}
+// vector2i
-void GDAPI godot_packed_vector2_array_set(godot_packed_vector2_array *p_self, const godot_int p_idx, const godot_vector2 *p_data) {
- Vector<Vector2> *self = (Vector<Vector2> *)p_self;
- Vector2 &s = *(Vector2 *)p_data;
- self->set(p_idx, s);
+void GDAPI godot_packed_vector2i_array_new(godot_packed_vector2i_array *p_self) {
+ memnew_placement(p_self, Vector<Vector2i>);
}
-godot_vector2 GDAPI godot_packed_vector2_array_get(const godot_packed_vector2_array *p_self, const godot_int p_idx) {
- const Vector<Vector2> *self = (const Vector<Vector2> *)p_self;
- godot_vector2 v;
- Vector2 *s = (Vector2 *)&v;
- *s = self->get(p_idx);
- return v;
+void GDAPI godot_packed_vector2i_array_new_copy(godot_packed_vector2i_array *r_dest, const godot_packed_vector2i_array *p_src) {
+ memnew_placement(r_dest, Vector<Vector2i>(*(Vector<Vector2i> *)p_src));
}
-godot_int GDAPI godot_packed_vector2_array_size(const godot_packed_vector2_array *p_self) {
- const Vector<Vector2> *self = (const Vector<Vector2> *)p_self;
- return self->size();
+void GDAPI godot_packed_vector2i_array_destroy(godot_packed_vector2i_array *p_self) {
+ ((Vector<Vector2i> *)p_self)->~Vector();
}
-godot_bool GDAPI godot_packed_vector2_array_empty(const godot_packed_vector2_array *p_self) {
- const Vector<Vector2> *self = (const Vector<Vector2> *)p_self;
- return self->empty();
+godot_vector2i GDAPI *godot_packed_vector2i_array_operator_index(godot_packed_vector2i_array *p_self, godot_int p_index) {
+ Vector<Vector2i> *self = (Vector<Vector2i> *)p_self;
+ return (godot_vector2i *)&self->operator[](p_index);
}
-void GDAPI godot_packed_vector2_array_destroy(godot_packed_vector2_array *p_self) {
- ((Vector<Vector2> *)p_self)->~Vector();
+const godot_vector2i GDAPI *godot_packed_vector2i_array_operator_index_const(const godot_packed_vector2i_array *p_self, godot_int p_index) {
+ const Vector<Vector2i> *self = (const Vector<Vector2i> *)p_self;
+ return (const godot_vector2i *)&self->operator[](p_index);
}
// vector3
-void GDAPI godot_packed_vector3_array_new(godot_packed_vector3_array *r_dest) {
- Vector<Vector3> *dest = (Vector<Vector3> *)r_dest;
- memnew_placement(dest, Vector<Vector3>);
+void GDAPI godot_packed_vector3_array_new(godot_packed_vector3_array *p_self) {
+ memnew_placement(p_self, PackedVector3Array);
}
void GDAPI godot_packed_vector3_array_new_copy(godot_packed_vector3_array *r_dest, const godot_packed_vector3_array *p_src) {
- Vector<Vector3> *dest = (Vector<Vector3> *)r_dest;
- const Vector<Vector3> *src = (const Vector<Vector3> *)p_src;
- memnew_placement(dest, Vector<Vector3>(*src));
-}
-
-void GDAPI godot_packed_vector3_array_new_with_array(godot_packed_vector3_array *r_dest, const godot_array *p_a) {
- Vector<Vector3> *dest = (Vector<Vector3> *)r_dest;
- Array *a = (Array *)p_a;
- memnew_placement(dest, Vector<Vector3>);
-
- dest->resize(a->size());
- for (int i = 0; i < a->size(); i++) {
- dest->set(i, (*a)[i]);
- }
+ memnew_placement(r_dest, PackedVector3Array(*(PackedVector3Array *)p_src));
}
-const godot_vector3 GDAPI *godot_packed_vector3_array_ptr(const godot_packed_vector3_array *p_self) {
- const Vector<Vector3> *self = (const Vector<Vector3> *)p_self;
- return (const godot_vector3 *)self->ptr();
-}
-
-godot_vector3 GDAPI *godot_packed_vector3_array_ptrw(godot_packed_vector3_array *p_self) {
- Vector<Vector3> *self = (Vector<Vector3> *)p_self;
- return (godot_vector3 *)self->ptrw();
-}
-
-void GDAPI godot_packed_vector3_array_append(godot_packed_vector3_array *p_self, const godot_vector3 *p_data) {
- Vector<Vector3> *self = (Vector<Vector3> *)p_self;
- Vector3 &s = *(Vector3 *)p_data;
- self->push_back(s);
-}
-
-void GDAPI godot_packed_vector3_array_append_array(godot_packed_vector3_array *p_self, const godot_packed_vector3_array *p_array) {
- Vector<Vector3> *self = (Vector<Vector3> *)p_self;
- Vector<Vector3> *array = (Vector<Vector3> *)p_array;
- self->append_array(*array);
-}
-
-godot_error GDAPI godot_packed_vector3_array_insert(godot_packed_vector3_array *p_self, const godot_int p_idx, const godot_vector3 *p_data) {
- Vector<Vector3> *self = (Vector<Vector3> *)p_self;
- Vector3 &s = *(Vector3 *)p_data;
- return (godot_error)self->insert(p_idx, s);
-}
-
-godot_bool GDAPI godot_packed_vector3_array_has(godot_packed_vector3_array *p_self, const godot_vector3 *p_value) {
- Vector<Vector3> *self = (Vector<Vector3> *)p_self;
- Vector3 &v = *(Vector3 *)p_value;
- return (godot_bool)self->has(v);
-}
-
-void GDAPI godot_packed_vector3_array_sort(godot_packed_vector3_array *p_self) {
- Vector<Vector3> *self = (Vector<Vector3> *)p_self;
- self->sort();
+void GDAPI godot_packed_vector3_array_destroy(godot_packed_vector3_array *p_self) {
+ ((PackedVector3Array *)p_self)->~PackedVector3Array();
}
-void GDAPI godot_packed_vector3_array_invert(godot_packed_vector3_array *p_self) {
- Vector<Vector3> *self = (Vector<Vector3> *)p_self;
- self->invert();
+godot_vector3 GDAPI *godot_packed_vector3_array_operator_index(godot_packed_vector3_array *p_self, godot_int p_index) {
+ PackedVector3Array *self = (PackedVector3Array *)p_self;
+ return (godot_vector3 *)&self->operator[](p_index);
}
-void GDAPI godot_packed_vector3_array_push_back(godot_packed_vector3_array *p_self, const godot_vector3 *p_data) {
- Vector<Vector3> *self = (Vector<Vector3> *)p_self;
- Vector3 &s = *(Vector3 *)p_data;
- self->push_back(s);
+const godot_vector3 GDAPI *godot_packed_vector3_array_operator_index_const(const godot_packed_vector3_array *p_self, godot_int p_index) {
+ const PackedVector3Array *self = (const PackedVector3Array *)p_self;
+ return (const godot_vector3 *)&self->operator[](p_index);
}
-void GDAPI godot_packed_vector3_array_remove(godot_packed_vector3_array *p_self, const godot_int p_idx) {
- Vector<Vector3> *self = (Vector<Vector3> *)p_self;
- self->remove(p_idx);
-}
+// vector3i
-void GDAPI godot_packed_vector3_array_resize(godot_packed_vector3_array *p_self, const godot_int p_size) {
- Vector<Vector3> *self = (Vector<Vector3> *)p_self;
- self->resize(p_size);
+void GDAPI godot_packed_vector3i_array_new(godot_packed_vector3i_array *p_self) {
+ memnew_placement(p_self, Vector<Vector3i>);
}
-void GDAPI godot_packed_vector3_array_set(godot_packed_vector3_array *p_self, const godot_int p_idx, const godot_vector3 *p_data) {
- Vector<Vector3> *self = (Vector<Vector3> *)p_self;
- Vector3 &s = *(Vector3 *)p_data;
- self->set(p_idx, s);
+void GDAPI godot_packed_vector3i_array_new_copy(godot_packed_vector3i_array *r_dest, const godot_packed_vector3i_array *p_src) {
+ memnew_placement(r_dest, Vector<Vector3i>(*(Vector<Vector3i> *)p_src));
}
-godot_vector3 GDAPI godot_packed_vector3_array_get(const godot_packed_vector3_array *p_self, const godot_int p_idx) {
- const Vector<Vector3> *self = (const Vector<Vector3> *)p_self;
- godot_vector3 v;
- Vector3 *s = (Vector3 *)&v;
- *s = self->get(p_idx);
- return v;
+void GDAPI godot_packed_vector3i_array_destroy(godot_packed_vector3i_array *p_self) {
+ ((Vector<Vector3i> *)p_self)->~Vector();
}
-godot_int GDAPI godot_packed_vector3_array_size(const godot_packed_vector3_array *p_self) {
- const Vector<Vector3> *self = (const Vector<Vector3> *)p_self;
- return self->size();
+godot_vector3i GDAPI *godot_packed_vector3i_array_operator_index(godot_packed_vector3i_array *p_self, godot_int p_index) {
+ Vector<Vector3i> *self = (Vector<Vector3i> *)p_self;
+ return (godot_vector3i *)&self->operator[](p_index);
}
-godot_bool GDAPI godot_packed_vector3_array_empty(const godot_packed_vector3_array *p_self) {
- const Vector<Vector3> *self = (const Vector<Vector3> *)p_self;
- return self->empty();
-}
-
-void GDAPI godot_packed_vector3_array_destroy(godot_packed_vector3_array *p_self) {
- ((Vector<Vector3> *)p_self)->~Vector();
+const godot_vector3i GDAPI *godot_packed_vector3i_array_operator_index_const(const godot_packed_vector3i_array *p_self, godot_int p_index) {
+ const Vector<Vector3i> *self = (const Vector<Vector3i> *)p_self;
+ return (const godot_vector3i *)&self->operator[](p_index);
}
// color
-void GDAPI godot_packed_color_array_new(godot_packed_color_array *r_dest) {
- Vector<Color> *dest = (Vector<Color> *)r_dest;
- memnew_placement(dest, Vector<Color>);
+void GDAPI godot_packed_color_array_new(godot_packed_color_array *p_self) {
+ memnew_placement(p_self, PackedColorArray);
}
void GDAPI godot_packed_color_array_new_copy(godot_packed_color_array *r_dest, const godot_packed_color_array *p_src) {
- Vector<Color> *dest = (Vector<Color> *)r_dest;
- const Vector<Color> *src = (const Vector<Color> *)p_src;
- memnew_placement(dest, Vector<Color>(*src));
-}
-
-void GDAPI godot_packed_color_array_new_with_array(godot_packed_color_array *r_dest, const godot_array *p_a) {
- Vector<Color> *dest = (Vector<Color> *)r_dest;
- Array *a = (Array *)p_a;
- memnew_placement(dest, Vector<Color>);
-
- dest->resize(a->size());
- for (int i = 0; i < a->size(); i++) {
- dest->set(i, (*a)[i]);
- }
-}
-
-const godot_color GDAPI *godot_packed_color_array_ptr(const godot_packed_color_array *p_self) {
- const Vector<Color> *self = (const Vector<Color> *)p_self;
- return (const godot_color *)self->ptr();
-}
-
-godot_color GDAPI *godot_packed_color_array_ptrw(godot_packed_color_array *p_self) {
- Vector<Color> *self = (Vector<Color> *)p_self;
- return (godot_color *)self->ptrw();
-}
-
-void GDAPI godot_packed_color_array_append(godot_packed_color_array *p_self, const godot_color *p_data) {
- Vector<Color> *self = (Vector<Color> *)p_self;
- Color &s = *(Color *)p_data;
- self->push_back(s);
-}
-
-void GDAPI godot_packed_color_array_append_array(godot_packed_color_array *p_self, const godot_packed_color_array *p_array) {
- Vector<Color> *self = (Vector<Color> *)p_self;
- Vector<Color> *array = (Vector<Color> *)p_array;
- self->append_array(*array);
-}
-
-godot_error GDAPI godot_packed_color_array_insert(godot_packed_color_array *p_self, const godot_int p_idx, const godot_color *p_data) {
- Vector<Color> *self = (Vector<Color> *)p_self;
- Color &s = *(Color *)p_data;
- return (godot_error)self->insert(p_idx, s);
-}
-
-godot_bool GDAPI godot_packed_color_array_has(godot_packed_color_array *p_self, const godot_color *p_value) {
- Vector<Color> *self = (Vector<Color> *)p_self;
- Color &c = *(Color *)p_value;
- return (godot_bool)self->has(c);
+ memnew_placement(r_dest, PackedColorArray(*(PackedColorArray *)p_src));
}
-void GDAPI godot_packed_color_array_sort(godot_packed_color_array *p_self) {
- Vector<Color> *self = (Vector<Color> *)p_self;
- self->sort();
-}
-
-void GDAPI godot_packed_color_array_invert(godot_packed_color_array *p_self) {
- Vector<Color> *self = (Vector<Color> *)p_self;
- self->invert();
-}
-
-void GDAPI godot_packed_color_array_push_back(godot_packed_color_array *p_self, const godot_color *p_data) {
- Vector<Color> *self = (Vector<Color> *)p_self;
- Color &s = *(Color *)p_data;
- self->push_back(s);
-}
-
-void GDAPI godot_packed_color_array_remove(godot_packed_color_array *p_self, const godot_int p_idx) {
- Vector<Color> *self = (Vector<Color> *)p_self;
- self->remove(p_idx);
-}
-
-void GDAPI godot_packed_color_array_resize(godot_packed_color_array *p_self, const godot_int p_size) {
- Vector<Color> *self = (Vector<Color> *)p_self;
- self->resize(p_size);
-}
-
-void GDAPI godot_packed_color_array_set(godot_packed_color_array *p_self, const godot_int p_idx, const godot_color *p_data) {
- Vector<Color> *self = (Vector<Color> *)p_self;
- Color &s = *(Color *)p_data;
- self->set(p_idx, s);
-}
-
-godot_color GDAPI godot_packed_color_array_get(const godot_packed_color_array *p_self, const godot_int p_idx) {
- const Vector<Color> *self = (const Vector<Color> *)p_self;
- godot_color v;
- Color *s = (Color *)&v;
- *s = self->get(p_idx);
- return v;
-}
-
-godot_int GDAPI godot_packed_color_array_size(const godot_packed_color_array *p_self) {
- const Vector<Color> *self = (const Vector<Color> *)p_self;
- return self->size();
+void GDAPI godot_packed_color_array_destroy(godot_packed_color_array *p_self) {
+ ((PackedColorArray *)p_self)->~PackedColorArray();
}
-godot_bool GDAPI godot_packed_color_array_empty(const godot_packed_color_array *p_self) {
- const Vector<Color> *self = (const Vector<Color> *)p_self;
- return self->empty();
+godot_color GDAPI *godot_packed_color_array_operator_index(godot_packed_color_array *p_self, godot_int p_index) {
+ PackedColorArray *self = (PackedColorArray *)p_self;
+ return (godot_color *)&self->operator[](p_index);
}
-void GDAPI godot_packed_color_array_destroy(godot_packed_color_array *p_self) {
- ((Vector<Color> *)p_self)->~Vector();
+const godot_color GDAPI *godot_packed_color_array_operator_index_const(const godot_packed_color_array *p_self, godot_int p_index) {
+ const PackedColorArray *self = (const PackedColorArray *)p_self;
+ return (const godot_color *)&self->operator[](p_index);
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/plane.cpp b/modules/gdnative/gdnative/plane.cpp
index d4ed8d00f4..8b8e84e3c1 100644
--- a/modules/gdnative/gdnative/plane.cpp
+++ b/modules/gdnative/gdnative/plane.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,139 +31,19 @@
#include "gdnative/plane.h"
#include "core/math/plane.h"
-#include "core/variant.h"
+
+static_assert(sizeof(godot_plane) == sizeof(Plane), "Plane size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_plane) == sizeof(Plane), "Plane size mismatch");
-
-void GDAPI godot_plane_new_with_reals(godot_plane *r_dest, const godot_real p_a, const godot_real p_b, const godot_real p_c, const godot_real p_d) {
- Plane *dest = (Plane *)r_dest;
- *dest = Plane(p_a, p_b, p_c, p_d);
-}
-
-void GDAPI godot_plane_new_with_vectors(godot_plane *r_dest, const godot_vector3 *p_v1, const godot_vector3 *p_v2, const godot_vector3 *p_v3) {
- const Vector3 *v1 = (const Vector3 *)p_v1;
- const Vector3 *v2 = (const Vector3 *)p_v2;
- const Vector3 *v3 = (const Vector3 *)p_v3;
- Plane *dest = (Plane *)r_dest;
- *dest = Plane(*v1, *v2, *v3);
-}
-
-void GDAPI godot_plane_new_with_normal(godot_plane *r_dest, const godot_vector3 *p_normal, const godot_real p_d) {
- const Vector3 *normal = (const Vector3 *)p_normal;
- Plane *dest = (Plane *)r_dest;
- *dest = Plane(*normal, p_d);
-}
-
-godot_string GDAPI godot_plane_as_string(const godot_plane *p_self) {
- godot_string ret;
- const Plane *self = (const Plane *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_plane GDAPI godot_plane_normalized(const godot_plane *p_self) {
- godot_plane dest;
- const Plane *self = (const Plane *)p_self;
- *((Plane *)&dest) = self->normalized();
- return dest;
-}
-
-godot_vector3 GDAPI godot_plane_center(const godot_plane *p_self) {
- godot_vector3 dest;
- const Plane *self = (const Plane *)p_self;
- *((Vector3 *)&dest) = self->center();
- return dest;
-}
-
-godot_bool GDAPI godot_plane_is_point_over(const godot_plane *p_self, const godot_vector3 *p_point) {
- const Plane *self = (const Plane *)p_self;
- const Vector3 *point = (const Vector3 *)p_point;
- return self->is_point_over(*point);
-}
-
-godot_real GDAPI godot_plane_distance_to(const godot_plane *p_self, const godot_vector3 *p_point) {
- const Plane *self = (const Plane *)p_self;
- const Vector3 *point = (const Vector3 *)p_point;
- return self->distance_to(*point);
-}
-
-godot_bool GDAPI godot_plane_has_point(const godot_plane *p_self, const godot_vector3 *p_point, const godot_real p_epsilon) {
- const Plane *self = (const Plane *)p_self;
- const Vector3 *point = (const Vector3 *)p_point;
- return self->has_point(*point, p_epsilon);
-}
-
-godot_vector3 GDAPI godot_plane_project(const godot_plane *p_self, const godot_vector3 *p_point) {
- godot_vector3 dest;
- const Plane *self = (const Plane *)p_self;
- const Vector3 *point = (const Vector3 *)p_point;
- *((Vector3 *)&dest) = self->project(*point);
- return dest;
-}
-
-godot_bool GDAPI godot_plane_intersect_3(const godot_plane *p_self, godot_vector3 *r_dest, const godot_plane *p_b, const godot_plane *p_c) {
- const Plane *self = (const Plane *)p_self;
- const Plane *b = (const Plane *)p_b;
- const Plane *c = (const Plane *)p_c;
- Vector3 *dest = (Vector3 *)r_dest;
- return self->intersect_3(*b, *c, dest);
-}
-
-godot_bool GDAPI godot_plane_intersects_ray(const godot_plane *p_self, godot_vector3 *r_dest, const godot_vector3 *p_from, const godot_vector3 *p_dir) {
- const Plane *self = (const Plane *)p_self;
- const Vector3 *from = (const Vector3 *)p_from;
- const Vector3 *dir = (const Vector3 *)p_dir;
- Vector3 *dest = (Vector3 *)r_dest;
- return self->intersects_ray(*from, *dir, dest);
-}
-
-godot_bool GDAPI godot_plane_intersects_segment(const godot_plane *p_self, godot_vector3 *r_dest, const godot_vector3 *p_begin, const godot_vector3 *p_end) {
- const Plane *self = (const Plane *)p_self;
- const Vector3 *begin = (const Vector3 *)p_begin;
- const Vector3 *end = (const Vector3 *)p_end;
- Vector3 *dest = (Vector3 *)r_dest;
- return self->intersects_segment(*begin, *end, dest);
-}
-
-godot_plane GDAPI godot_plane_operator_neg(const godot_plane *p_self) {
- godot_plane raw_dest;
- Plane *dest = (Plane *)&raw_dest;
- const Plane *self = (const Plane *)p_self;
- *dest = -(*self);
- return raw_dest;
-}
-
-godot_bool GDAPI godot_plane_operator_equal(const godot_plane *p_self, const godot_plane *p_b) {
- const Plane *self = (const Plane *)p_self;
- const Plane *b = (const Plane *)p_b;
- return *self == *b;
-}
-
-void GDAPI godot_plane_set_normal(godot_plane *p_self, const godot_vector3 *p_normal) {
- Plane *self = (Plane *)p_self;
- const Vector3 *normal = (const Vector3 *)p_normal;
- self->set_normal(*normal);
-}
-
-godot_vector3 GDAPI godot_plane_get_normal(const godot_plane *p_self) {
- const Plane *self = (const Plane *)p_self;
- const Vector3 normal = self->get_normal();
- godot_vector3 *v3 = (godot_vector3 *)&normal;
- return *v3;
-}
-
-godot_real GDAPI godot_plane_get_d(const godot_plane *p_self) {
- const Plane *self = (const Plane *)p_self;
- return self->d;
+void GDAPI godot_plane_new(godot_plane *p_self) {
+ memnew_placement(p_self, Plane);
}
-void GDAPI godot_plane_set_d(godot_plane *p_self, const godot_real p_d) {
- Plane *self = (Plane *)p_self;
- self->d = p_d;
+void GDAPI godot_plane_new_copy(godot_plane *r_dest, const godot_plane *p_src) {
+ memnew_placement(r_dest, Plane(*(Plane *)p_src));
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/quat.cpp b/modules/gdnative/gdnative/quat.cpp
deleted file mode 100644
index de6308ad2a..0000000000
--- a/modules/gdnative/gdnative/quat.cpp
+++ /dev/null
@@ -1,237 +0,0 @@
-/*************************************************************************/
-/* quat.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "gdnative/quat.h"
-
-#include "core/math/quat.h"
-#include "core/variant.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static_assert(sizeof(godot_quat) == sizeof(Quat), "Quat size mismatch");
-
-void GDAPI godot_quat_new(godot_quat *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_z, const godot_real p_w) {
- Quat *dest = (Quat *)r_dest;
- *dest = Quat(p_x, p_y, p_z, p_w);
-}
-
-void GDAPI godot_quat_new_with_axis_angle(godot_quat *r_dest, const godot_vector3 *p_axis, const godot_real p_angle) {
- const Vector3 *axis = (const Vector3 *)p_axis;
- Quat *dest = (Quat *)r_dest;
- *dest = Quat(*axis, p_angle);
-}
-
-void GDAPI godot_quat_new_with_basis(godot_quat *r_dest, const godot_basis *p_basis) {
- const Basis *basis = (const Basis *)p_basis;
- Quat *dest = (Quat *)r_dest;
- *dest = Quat(*basis);
-}
-
-void GDAPI godot_quat_new_with_euler(godot_quat *r_dest, const godot_vector3 *p_euler) {
- const Vector3 *euler = (const Vector3 *)p_euler;
- Quat *dest = (Quat *)r_dest;
- *dest = Quat(*euler);
-}
-
-godot_real GDAPI godot_quat_get_x(const godot_quat *p_self) {
- const Quat *self = (const Quat *)p_self;
- return self->x;
-}
-
-void GDAPI godot_quat_set_x(godot_quat *p_self, const godot_real val) {
- Quat *self = (Quat *)p_self;
- self->x = val;
-}
-
-godot_real GDAPI godot_quat_get_y(const godot_quat *p_self) {
- const Quat *self = (const Quat *)p_self;
- return self->y;
-}
-
-void GDAPI godot_quat_set_y(godot_quat *p_self, const godot_real val) {
- Quat *self = (Quat *)p_self;
- self->y = val;
-}
-
-godot_real GDAPI godot_quat_get_z(const godot_quat *p_self) {
- const Quat *self = (const Quat *)p_self;
- return self->z;
-}
-
-void GDAPI godot_quat_set_z(godot_quat *p_self, const godot_real val) {
- Quat *self = (Quat *)p_self;
- self->z = val;
-}
-
-godot_real GDAPI godot_quat_get_w(const godot_quat *p_self) {
- const Quat *self = (const Quat *)p_self;
- return self->w;
-}
-
-void GDAPI godot_quat_set_w(godot_quat *p_self, const godot_real val) {
- Quat *self = (Quat *)p_self;
- self->w = val;
-}
-
-godot_string GDAPI godot_quat_as_string(const godot_quat *p_self) {
- godot_string ret;
- const Quat *self = (const Quat *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_real GDAPI godot_quat_length(const godot_quat *p_self) {
- const Quat *self = (const Quat *)p_self;
- return self->length();
-}
-
-godot_real GDAPI godot_quat_length_squared(const godot_quat *p_self) {
- const Quat *self = (const Quat *)p_self;
- return self->length_squared();
-}
-
-godot_quat GDAPI godot_quat_normalized(const godot_quat *p_self) {
- godot_quat dest;
- const Quat *self = (const Quat *)p_self;
- *((Quat *)&dest) = self->normalized();
- return dest;
-}
-
-godot_bool GDAPI godot_quat_is_normalized(const godot_quat *p_self) {
- const Quat *self = (const Quat *)p_self;
- return self->is_normalized();
-}
-
-godot_quat GDAPI godot_quat_inverse(const godot_quat *p_self) {
- godot_quat dest;
- const Quat *self = (const Quat *)p_self;
- *((Quat *)&dest) = self->inverse();
- return dest;
-}
-
-godot_real GDAPI godot_quat_dot(const godot_quat *p_self, const godot_quat *p_b) {
- const Quat *self = (const Quat *)p_self;
- const Quat *b = (const Quat *)p_b;
- return self->dot(*b);
-}
-
-godot_vector3 GDAPI godot_quat_xform(const godot_quat *p_self, const godot_vector3 *p_v) {
- godot_vector3 dest;
- const Quat *self = (const Quat *)p_self;
- const Vector3 *v = (const Vector3 *)p_v;
- *((Vector3 *)&dest) = self->xform(*v);
- return dest;
-}
-
-godot_quat GDAPI godot_quat_slerp(const godot_quat *p_self, const godot_quat *p_b, const godot_real p_t) {
- godot_quat dest;
- const Quat *self = (const Quat *)p_self;
- const Quat *b = (const Quat *)p_b;
- *((Quat *)&dest) = self->slerp(*b, p_t);
- return dest;
-}
-
-godot_quat GDAPI godot_quat_slerpni(const godot_quat *p_self, const godot_quat *p_b, const godot_real p_t) {
- godot_quat dest;
- const Quat *self = (const Quat *)p_self;
- const Quat *b = (const Quat *)p_b;
- *((Quat *)&dest) = self->slerpni(*b, p_t);
- return dest;
-}
-
-godot_quat GDAPI godot_quat_cubic_slerp(const godot_quat *p_self, const godot_quat *p_b, const godot_quat *p_pre_a, const godot_quat *p_post_b, const godot_real p_t) {
- godot_quat dest;
- const Quat *self = (const Quat *)p_self;
- const Quat *b = (const Quat *)p_b;
- const Quat *pre_a = (const Quat *)p_pre_a;
- const Quat *post_b = (const Quat *)p_post_b;
- *((Quat *)&dest) = self->cubic_slerp(*b, *pre_a, *post_b, p_t);
- return dest;
-}
-
-godot_quat GDAPI godot_quat_operator_multiply(const godot_quat *p_self, const godot_real p_b) {
- godot_quat raw_dest;
- Quat *dest = (Quat *)&raw_dest;
- const Quat *self = (const Quat *)p_self;
- *dest = *self * p_b;
- return raw_dest;
-}
-
-godot_quat GDAPI godot_quat_operator_add(const godot_quat *p_self, const godot_quat *p_b) {
- godot_quat raw_dest;
- Quat *dest = (Quat *)&raw_dest;
- const Quat *self = (const Quat *)p_self;
- const Quat *b = (const Quat *)p_b;
- *dest = *self + *b;
- return raw_dest;
-}
-
-godot_quat GDAPI godot_quat_operator_subtract(const godot_quat *p_self, const godot_quat *p_b) {
- godot_quat raw_dest;
- Quat *dest = (Quat *)&raw_dest;
- const Quat *self = (const Quat *)p_self;
- const Quat *b = (const Quat *)p_b;
- *dest = *self - *b;
- return raw_dest;
-}
-
-godot_quat GDAPI godot_quat_operator_divide(const godot_quat *p_self, const godot_real p_b) {
- godot_quat raw_dest;
- Quat *dest = (Quat *)&raw_dest;
- const Quat *self = (const Quat *)p_self;
- *dest = *self / p_b;
- return raw_dest;
-}
-
-godot_bool GDAPI godot_quat_operator_equal(const godot_quat *p_self, const godot_quat *p_b) {
- const Quat *self = (const Quat *)p_self;
- const Quat *b = (const Quat *)p_b;
- return *self == *b;
-}
-
-godot_quat GDAPI godot_quat_operator_neg(const godot_quat *p_self) {
- godot_quat raw_dest;
- Quat *dest = (Quat *)&raw_dest;
- const Quat *self = (const Quat *)p_self;
- *dest = -(*self);
- return raw_dest;
-}
-
-void GDAPI godot_quat_set_axis_angle(godot_quat *p_self, const godot_vector3 *p_axis, const godot_real p_angle) {
- Quat *self = (Quat *)p_self;
- const Vector3 *axis = (const Vector3 *)p_axis;
- self->set_axis_angle(*axis, p_angle);
-}
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/modules/gdnative/gdnative/quaternion.cpp b/modules/gdnative/gdnative/quaternion.cpp
new file mode 100644
index 0000000000..62bcbbd382
--- /dev/null
+++ b/modules/gdnative/gdnative/quaternion.cpp
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* quaternion.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 "gdnative/quaternion.h"
+
+#include "core/math/quaternion.h"
+
+static_assert(sizeof(godot_quaternion) == sizeof(Quaternion), "Quaternion size mismatch");
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void GDAPI godot_quaternion_new(godot_quaternion *p_self) {
+ memnew_placement(p_self, Quaternion);
+}
+
+void GDAPI godot_quaternion_new_copy(godot_quaternion *r_dest, const godot_quaternion *p_src) {
+ memnew_placement(r_dest, Quaternion(*(Quaternion *)p_src));
+}
+
+godot_real_t GDAPI *godot_quaternion_operator_index(godot_quaternion *p_self, godot_int p_index) {
+ Quaternion *self = (Quaternion *)p_self;
+ return (godot_real_t *)&self->operator[](p_index);
+}
+
+const godot_real_t GDAPI *godot_quaternion_operator_index_const(const godot_quaternion *p_self, godot_int p_index) {
+ const Quaternion *self = (const Quaternion *)p_self;
+ return (const godot_real_t *)&self->operator[](p_index);
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/gdnative/gdnative/rect2.cpp b/modules/gdnative/gdnative/rect2.cpp
index 516f4d75ce..a196a63188 100644
--- a/modules/gdnative/gdnative/rect2.cpp
+++ b/modules/gdnative/gdnative/rect2.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,300 +30,29 @@
#include "gdnative/rect2.h"
-#include "core/math/transform_2d.h"
-#include "core/variant.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "core/math/rect2.h"
static_assert(sizeof(godot_rect2) == sizeof(Rect2), "Rect2 size mismatch");
static_assert(sizeof(godot_rect2i) == sizeof(Rect2i), "Rect2i size mismatch");
-// Rect2
-
-void GDAPI godot_rect2_new_with_position_and_size(godot_rect2 *r_dest, const godot_vector2 *p_pos, const godot_vector2 *p_size) {
- const Vector2 *position = (const Vector2 *)p_pos;
- const Vector2 *size = (const Vector2 *)p_size;
- Rect2 *dest = (Rect2 *)r_dest;
- *dest = Rect2(*position, *size);
-}
-
-void GDAPI godot_rect2_new(godot_rect2 *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_width, const godot_real p_height) {
- Rect2 *dest = (Rect2 *)r_dest;
- *dest = Rect2(p_x, p_y, p_width, p_height);
-}
-
-godot_string GDAPI godot_rect2_as_string(const godot_rect2 *p_self) {
- godot_string ret;
- const Rect2 *self = (const Rect2 *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_rect2i GDAPI godot_rect2_as_rect2i(const godot_rect2 *p_self) {
- godot_rect2i dest;
- const Rect2 *self = (const Rect2 *)p_self;
- *((Rect2i *)&dest) = Rect2i(*self);
- return dest;
-}
-
-godot_real GDAPI godot_rect2_get_area(const godot_rect2 *p_self) {
- const Rect2 *self = (const Rect2 *)p_self;
- return self->get_area();
-}
-
-godot_bool GDAPI godot_rect2_intersects(const godot_rect2 *p_self, const godot_rect2 *p_b) {
- const Rect2 *self = (const Rect2 *)p_self;
- const Rect2 *b = (const Rect2 *)p_b;
- return self->intersects(*b);
-}
-
-godot_bool GDAPI godot_rect2_encloses(const godot_rect2 *p_self, const godot_rect2 *p_b) {
- const Rect2 *self = (const Rect2 *)p_self;
- const Rect2 *b = (const Rect2 *)p_b;
- return self->encloses(*b);
-}
-
-godot_bool GDAPI godot_rect2_has_no_area(const godot_rect2 *p_self) {
- const Rect2 *self = (const Rect2 *)p_self;
- return self->has_no_area();
-}
-
-godot_rect2 GDAPI godot_rect2_clip(const godot_rect2 *p_self, const godot_rect2 *p_b) {
- godot_rect2 dest;
- const Rect2 *self = (const Rect2 *)p_self;
- const Rect2 *b = (const Rect2 *)p_b;
- *((Rect2 *)&dest) = self->clip(*b);
- return dest;
-}
-
-godot_rect2 GDAPI godot_rect2_merge(const godot_rect2 *p_self, const godot_rect2 *p_b) {
- godot_rect2 dest;
- const Rect2 *self = (const Rect2 *)p_self;
- const Rect2 *b = (const Rect2 *)p_b;
- *((Rect2 *)&dest) = self->merge(*b);
- return dest;
-}
-
-godot_bool GDAPI godot_rect2_has_point(const godot_rect2 *p_self, const godot_vector2 *p_point) {
- const Rect2 *self = (const Rect2 *)p_self;
- const Vector2 *point = (const Vector2 *)p_point;
- return self->has_point(*point);
-}
-
-godot_rect2 GDAPI godot_rect2_grow(const godot_rect2 *p_self, const godot_real p_by) {
- godot_rect2 dest;
- const Rect2 *self = (const Rect2 *)p_self;
-
- *((Rect2 *)&dest) = self->grow(p_by);
- return dest;
-}
-
-godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const godot_real p_left, const godot_real p_top, const godot_real p_right, const godot_real p_bottom) {
- godot_rect2 dest;
- const Rect2 *self = (const Rect2 *)p_self;
- *((Rect2 *)&dest) = self->grow_individual(p_left, p_top, p_right, p_bottom);
- return dest;
-}
-
-godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by) {
- godot_rect2 dest;
- const Rect2 *self = (const Rect2 *)p_self;
- *((Rect2 *)&dest) = self->grow_margin((Margin)p_margin, p_by);
- return dest;
-}
-
-godot_rect2 GDAPI godot_rect2_abs(const godot_rect2 *p_self) {
- godot_rect2 dest;
- const Rect2 *self = (const Rect2 *)p_self;
- *((Rect2 *)&dest) = self->abs();
- return dest;
-}
-
-godot_rect2 GDAPI godot_rect2_expand(const godot_rect2 *p_self, const godot_vector2 *p_to) {
- godot_rect2 dest;
- const Rect2 *self = (const Rect2 *)p_self;
- const Vector2 *to = (const Vector2 *)p_to;
- *((Rect2 *)&dest) = self->expand(*to);
- return dest;
-}
-
-godot_bool GDAPI godot_rect2_operator_equal(const godot_rect2 *p_self, const godot_rect2 *p_b) {
- const Rect2 *self = (const Rect2 *)p_self;
- const Rect2 *b = (const Rect2 *)p_b;
- return *self == *b;
-}
-
-godot_vector2 GDAPI godot_rect2_get_position(const godot_rect2 *p_self) {
- godot_vector2 dest;
- Vector2 *d = (Vector2 *)&dest;
- const Rect2 *self = (const Rect2 *)p_self;
- *d = self->get_position();
- return dest;
-}
-
-godot_vector2 GDAPI godot_rect2_get_size(const godot_rect2 *p_self) {
- godot_vector2 dest;
- Vector2 *d = (Vector2 *)&dest;
- const Rect2 *self = (const Rect2 *)p_self;
- *d = self->get_size();
- return dest;
-}
-
-void GDAPI godot_rect2_set_position(godot_rect2 *p_self, const godot_vector2 *p_pos) {
- Rect2 *self = (Rect2 *)p_self;
- const Vector2 *position = (const Vector2 *)p_pos;
- self->set_position(*position);
-}
-
-void GDAPI godot_rect2_set_size(godot_rect2 *p_self, const godot_vector2 *p_size) {
- Rect2 *self = (Rect2 *)p_self;
- const Vector2 *size = (const Vector2 *)p_size;
- self->set_size(*size);
-}
-
-// Rect2i
-
-void GDAPI godot_rect2i_new_with_position_and_size(godot_rect2i *r_dest, const godot_vector2i *p_pos, const godot_vector2i *p_size) {
- const Vector2i *position = (const Vector2i *)p_pos;
- const Vector2i *size = (const Vector2i *)p_size;
- Rect2i *dest = (Rect2i *)r_dest;
- *dest = Rect2i(*position, *size);
-}
-
-void GDAPI godot_rect2i_new(godot_rect2i *r_dest, const godot_int p_x, const godot_int p_y, const godot_int p_width, const godot_int p_height) {
- Rect2i *dest = (Rect2i *)r_dest;
- *dest = Rect2i(p_x, p_y, p_width, p_height);
-}
-
-godot_string GDAPI godot_rect2i_as_string(const godot_rect2i *p_self) {
- godot_string ret;
- const Rect2i *self = (const Rect2i *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_rect2 GDAPI godot_rect2i_as_rect2(const godot_rect2i *p_self) {
- godot_rect2 dest;
- const Rect2i *self = (const Rect2i *)p_self;
- *((Rect2 *)&dest) = Rect2(*self);
- return dest;
-}
-
-godot_int GDAPI godot_rect2i_get_area(const godot_rect2i *p_self) {
- const Rect2i *self = (const Rect2i *)p_self;
- return self->get_area();
-}
-
-godot_bool GDAPI godot_rect2i_intersects(const godot_rect2i *p_self, const godot_rect2i *p_b) {
- const Rect2i *self = (const Rect2i *)p_self;
- const Rect2i *b = (const Rect2i *)p_b;
- return self->intersects(*b);
-}
-
-godot_bool GDAPI godot_rect2i_encloses(const godot_rect2i *p_self, const godot_rect2i *p_b) {
- const Rect2i *self = (const Rect2i *)p_self;
- const Rect2i *b = (const Rect2i *)p_b;
- return self->encloses(*b);
-}
-
-godot_bool GDAPI godot_rect2i_has_no_area(const godot_rect2i *p_self) {
- const Rect2i *self = (const Rect2i *)p_self;
- return self->has_no_area();
-}
-
-godot_rect2i GDAPI godot_rect2i_clip(const godot_rect2i *p_self, const godot_rect2i *p_b) {
- godot_rect2i dest;
- const Rect2i *self = (const Rect2i *)p_self;
- const Rect2i *b = (const Rect2i *)p_b;
- *((Rect2i *)&dest) = self->clip(*b);
- return dest;
-}
-
-godot_rect2i GDAPI godot_rect2i_merge(const godot_rect2i *p_self, const godot_rect2i *p_b) {
- godot_rect2i dest;
- const Rect2i *self = (const Rect2i *)p_self;
- const Rect2i *b = (const Rect2i *)p_b;
- *((Rect2i *)&dest) = self->merge(*b);
- return dest;
-}
-
-godot_bool GDAPI godot_rect2i_has_point(const godot_rect2i *p_self, const godot_vector2i *p_point) {
- const Rect2i *self = (const Rect2i *)p_self;
- const Vector2i *point = (const Vector2i *)p_point;
- return self->has_point(*point);
-}
-
-godot_rect2i GDAPI godot_rect2i_grow(const godot_rect2i *p_self, const godot_int p_by) {
- godot_rect2i dest;
- const Rect2i *self = (const Rect2i *)p_self;
-
- *((Rect2i *)&dest) = self->grow(p_by);
- return dest;
-}
-
-godot_rect2i GDAPI godot_rect2i_grow_individual(const godot_rect2i *p_self, const godot_int p_left, const godot_int p_top, const godot_int p_right, const godot_int p_bottom) {
- godot_rect2i dest;
- const Rect2i *self = (const Rect2i *)p_self;
- *((Rect2i *)&dest) = self->grow_individual(p_left, p_top, p_right, p_bottom);
- return dest;
-}
-
-godot_rect2i GDAPI godot_rect2i_grow_margin(const godot_rect2i *p_self, const godot_int p_margin, const godot_int p_by) {
- godot_rect2i dest;
- const Rect2i *self = (const Rect2i *)p_self;
- *((Rect2i *)&dest) = self->grow_margin((Margin)p_margin, p_by);
- return dest;
-}
-
-godot_rect2i GDAPI godot_rect2i_abs(const godot_rect2i *p_self) {
- godot_rect2i dest;
- const Rect2i *self = (const Rect2i *)p_self;
- *((Rect2i *)&dest) = self->abs();
- return dest;
-}
-
-godot_rect2i GDAPI godot_rect2i_expand(const godot_rect2i *p_self, const godot_vector2i *p_to) {
- godot_rect2i dest;
- const Rect2i *self = (const Rect2i *)p_self;
- const Vector2i *to = (const Vector2i *)p_to;
- *((Rect2i *)&dest) = self->expand(*to);
- return dest;
-}
-
-godot_bool GDAPI godot_rect2i_operator_equal(const godot_rect2i *p_self, const godot_rect2i *p_b) {
- const Rect2i *self = (const Rect2i *)p_self;
- const Rect2i *b = (const Rect2i *)p_b;
- return *self == *b;
-}
+#ifdef __cplusplus
+extern "C" {
+#endif
-godot_vector2i GDAPI godot_rect2i_get_position(const godot_rect2i *p_self) {
- godot_vector2i dest;
- Vector2i *d = (Vector2i *)&dest;
- const Rect2i *self = (const Rect2i *)p_self;
- *d = self->get_position();
- return dest;
+void GDAPI godot_rect2_new(godot_rect2 *p_self) {
+ memnew_placement(p_self, Rect2);
}
-godot_vector2i GDAPI godot_rect2i_get_size(const godot_rect2i *p_self) {
- godot_vector2i dest;
- Vector2i *d = (Vector2i *)&dest;
- const Rect2i *self = (const Rect2i *)p_self;
- *d = self->get_size();
- return dest;
+void GDAPI godot_rect2_new_copy(godot_rect2 *r_dest, const godot_rect2 *p_src) {
+ memnew_placement(r_dest, Rect2(*(Rect2 *)p_src));
}
-void GDAPI godot_rect2i_set_position(godot_rect2i *p_self, const godot_vector2i *p_pos) {
- Rect2i *self = (Rect2i *)p_self;
- const Vector2i *position = (const Vector2i *)p_pos;
- self->set_position(*position);
+void GDAPI godot_rect2i_new(godot_rect2i *p_self) {
+ memnew_placement(p_self, Rect2i);
}
-void GDAPI godot_rect2i_set_size(godot_rect2i *p_self, const godot_vector2i *p_size) {
- Rect2i *self = (Rect2i *)p_self;
- const Vector2i *size = (const Vector2i *)p_size;
- self->set_size(*size);
+void GDAPI godot_rect2i_new_copy(godot_rect2i *r_dest, const godot_rect2i *p_src) {
+ memnew_placement(r_dest, Rect2i(*(Rect2i *)p_src));
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/rid.cpp b/modules/gdnative/gdnative/rid.cpp
index d7a63f33a7..f8599afcf9 100644
--- a/modules/gdnative/gdnative/rid.cpp
+++ b/modules/gdnative/gdnative/rid.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,45 +30,21 @@
#include "gdnative/rid.h"
-#include "core/resource.h"
-#include "core/rid.h"
-#include "core/variant.h"
+#include "core/os/memory.h"
+#include "core/templates/rid.h"
+
+static_assert(sizeof(godot_rid) == sizeof(RID), "RID size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_rid) == sizeof(RID), "RID size mismatch");
-
-void GDAPI godot_rid_new(godot_rid *r_dest) {
- RID *dest = (RID *)r_dest;
- memnew_placement(dest, RID);
-}
-
-godot_int GDAPI godot_rid_get_id(const godot_rid *p_self) {
- const RID *self = (const RID *)p_self;
- return self->get_id();
-}
-
-void GDAPI godot_rid_new_with_resource(godot_rid *r_dest, const godot_object *p_from) {
- const Resource *res_from = Object::cast_to<Resource>((Object *)p_from);
- godot_rid_new(r_dest);
- if (res_from) {
- RID *dest = (RID *)r_dest;
- *dest = RID(res_from->get_rid());
- }
-}
-
-godot_bool GDAPI godot_rid_operator_equal(const godot_rid *p_self, const godot_rid *p_b) {
- const RID *self = (const RID *)p_self;
- const RID *b = (const RID *)p_b;
- return *self == *b;
+void GDAPI godot_rid_new(godot_rid *p_self) {
+ memnew_placement(p_self, RID);
}
-godot_bool GDAPI godot_rid_operator_less(const godot_rid *p_self, const godot_rid *p_b) {
- const RID *self = (const RID *)p_self;
- const RID *b = (const RID *)p_b;
- return *self < *b;
+void GDAPI godot_rid_new_copy(godot_rid *r_dest, const godot_rid *p_src) {
+ memnew_placement(r_dest, RID(*(RID *)p_src));
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/signal.cpp b/modules/gdnative/gdnative/signal.cpp
new file mode 100644
index 0000000000..5963c0e6c6
--- /dev/null
+++ b/modules/gdnative/gdnative/signal.cpp
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* signal.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 "gdnative/signal.h"
+
+#include "core/variant/callable.h"
+#include "core/variant/variant.h"
+
+static_assert(sizeof(godot_signal) == sizeof(Signal), "Signal size mismatch");
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void GDAPI godot_signal_new(godot_signal *p_self) {
+ memnew_placement(p_self, Signal);
+}
+
+void GDAPI godot_signal_new_copy(godot_signal *r_dest, const godot_signal *p_src) {
+ memnew_placement(r_dest, Signal(*(Signal *)p_src));
+}
+
+void GDAPI godot_signal_destroy(godot_signal *p_self) {
+ Signal *self = (Signal *)p_self;
+ self->~Signal();
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/gdnative/gdnative/string.cpp b/modules/gdnative/gdnative/string.cpp
index 1fa19f4ff5..1ad1ea8bdf 100644
--- a/modules/gdnative/gdnative/string.cpp
+++ b/modules/gdnative/gdnative/string.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,67 +30,22 @@
#include "gdnative/string.h"
-#include "core/string_name.h"
-#include "core/ustring.h"
-#include "core/variant.h"
+#include "core/string/ustring.h"
-#include <string.h>
+static_assert(sizeof(godot_string) == sizeof(String), "String size mismatch");
+static_assert(sizeof(godot_char_type) == sizeof(char32_t), "char32_t size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_char16_string) == sizeof(Char16String), "Char16String size mismatch");
-static_assert(sizeof(godot_char_string) == sizeof(CharString), "CharString size mismatch");
-static_assert(sizeof(godot_string) == sizeof(String), "String size mismatch");
-static_assert(sizeof(godot_char_type) == sizeof(char32_t), "char32_t size mismatch");
-
-godot_int GDAPI godot_char_string_length(const godot_char_string *p_cs) {
- const CharString *cs = (const CharString *)p_cs;
-
- return cs->length();
-}
-
-const char GDAPI *godot_char_string_get_data(const godot_char_string *p_cs) {
- const CharString *cs = (const CharString *)p_cs;
-
- return cs->get_data();
-}
-
-void GDAPI godot_char_string_destroy(godot_char_string *p_cs) {
- CharString *cs = (CharString *)p_cs;
-
- cs->~CharString();
-}
-
-godot_int GDAPI godot_char16_string_length(const godot_char16_string *p_cs) {
- const Char16String *cs = (const Char16String *)p_cs;
-
- return cs->length();
-}
-
-const char16_t GDAPI *godot_char16_string_get_data(const godot_char16_string *p_cs) {
- const Char16String *cs = (const Char16String *)p_cs;
-
- return cs->get_data();
-}
-
-void GDAPI godot_char16_string_destroy(godot_char16_string *p_cs) {
- Char16String *cs = (Char16String *)p_cs;
-
- cs->~Char16String();
-}
-
void GDAPI godot_string_new(godot_string *r_dest) {
String *dest = (String *)r_dest;
memnew_placement(dest, String);
}
void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src) {
- String *dest = (String *)r_dest;
- const String *src = (const String *)p_src;
- memnew_placement(dest, String);
- *dest = String(*src);
+ memnew_placement(r_dest, String(*(String *)p_src));
}
void GDAPI godot_string_new_with_latin1_chars(godot_string *r_dest, const char *p_contents) {
@@ -167,1204 +122,48 @@ void GDAPI godot_string_new_with_wide_chars_and_len(godot_string *r_dest, const
}
}
-const godot_char_type GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx) {
+const char GDAPI *godot_string_to_latin1_chars(const godot_string *p_self) {
String *self = (String *)p_self;
- return &(self->operator[](p_idx));
+ return self->ascii(true).get_data();
}
-godot_char_type GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx) {
- const String *self = (const String *)p_self;
- return self->operator[](p_idx);
-}
-
-const godot_char_type GDAPI *godot_string_get_data(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- return self->get_data();
-}
-
-godot_bool GDAPI godot_string_operator_equal(const godot_string *p_self, const godot_string *p_b) {
- const String *self = (const String *)p_self;
- const String *b = (const String *)p_b;
- return *self == *b;
-}
-
-godot_bool GDAPI godot_string_operator_less(const godot_string *p_self, const godot_string *p_b) {
- const String *self = (const String *)p_self;
- const String *b = (const String *)p_b;
- return *self < *b;
-}
-
-godot_string GDAPI godot_string_operator_plus(const godot_string *p_self, const godot_string *p_b) {
- godot_string ret;
- const String *self = (const String *)p_self;
- const String *b = (const String *)p_b;
- memnew_placement(&ret, String(*self + *b));
- return ret;
-}
-
-void GDAPI godot_string_destroy(godot_string *p_self) {
+const char GDAPI *godot_string_to_utf8_chars(const godot_string *p_self) {
String *self = (String *)p_self;
- self->~String();
-}
-
-/* Standard size stuff */
-
-godot_int GDAPI godot_string_length(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->length();
-}
-
-/* Helpers */
-
-signed char GDAPI godot_string_casecmp_to(const godot_string *p_self, const godot_string *p_str) {
- const String *self = (const String *)p_self;
- const String *str = (const String *)p_str;
-
- return self->casecmp_to(*str);
-}
-
-signed char GDAPI godot_string_nocasecmp_to(const godot_string *p_self, const godot_string *p_str) {
- const String *self = (const String *)p_self;
- const String *str = (const String *)p_str;
-
- return self->nocasecmp_to(*str);
-}
-
-signed char GDAPI godot_string_naturalnocasecmp_to(const godot_string *p_self, const godot_string *p_str) {
- const String *self = (const String *)p_self;
- const String *str = (const String *)p_str;
-
- return self->naturalnocasecmp_to(*str);
-}
-
-godot_bool GDAPI godot_string_begins_with(const godot_string *p_self, const godot_string *p_string) {
- const String *self = (const String *)p_self;
- const String *string = (const String *)p_string;
-
- return self->begins_with(*string);
-}
-
-godot_bool GDAPI godot_string_begins_with_char_array(const godot_string *p_self, const char *p_char_array) {
- const String *self = (const String *)p_self;
-
- return self->begins_with(p_char_array);
-}
-
-godot_packed_string_array GDAPI godot_string_bigrams(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_packed_string_array ret;
- memnew_placement(&ret, Vector<String>(self->bigrams()));
- return ret;
-};
-
-godot_string GDAPI godot_string_chr(godot_char_type p_character) {
- godot_string result;
- memnew_placement(&result, String(String::chr(p_character)));
-
- return result;
-}
-
-godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_string *p_string) {
- const String *self = (const String *)p_self;
- const String *string = (const String *)p_string;
-
- return self->ends_with(*string);
-}
-
-godot_bool GDAPI godot_string_ends_with_char_array(const godot_string *p_self, const char *p_char_array) {
- const String *self = (const String *)p_self;
-
- return self->ends_with(p_char_array);
-}
-
-godot_int GDAPI godot_string_count(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to) {
- const String *self = (const String *)p_self;
- const String *what = (const String *)p_what;
-
- return self->count(*what, p_from, p_to);
-}
-
-godot_int GDAPI godot_string_countn(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to) {
- const String *self = (const String *)p_self;
- const String *what = (const String *)p_what;
-
- return self->countn(*what, p_from, p_to);
-}
-
-godot_int GDAPI godot_string_find(const godot_string *p_self, const godot_string *p_what) {
- const String *self = (const String *)p_self;
- const String *what = (const String *)p_what;
-
- return self->find(*what);
-}
-
-godot_int GDAPI godot_string_find_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
- const String *self = (const String *)p_self;
- const String *what = (const String *)p_what;
-
- return self->find(*what, p_from);
-}
-
-godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_packed_string_array *p_keys) {
- const String *self = (const String *)p_self;
- const Vector<String> *keys = (const Vector<String> *)p_keys;
- return self->findmk(*keys);
-}
-
-godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from) {
- const String *self = (const String *)p_self;
- const Vector<String> *keys = (const Vector<String> *)p_keys;
- return self->findmk(*keys, p_from);
-}
-
-godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from, godot_int *r_key) {
- const String *self = (const String *)p_self;
- const Vector<String> *keys = (const Vector<String> *)p_keys;
- int key;
- int ret = self->findmk(*keys, p_from, &key);
- if (r_key) {
- *r_key = key;
- }
- return ret;
-}
-
-godot_int GDAPI godot_string_findn(const godot_string *p_self, const godot_string *p_what) {
- const String *self = (const String *)p_self;
- const String *what = (const String *)p_what;
-
- return self->findn(*what);
-}
-
-godot_int GDAPI godot_string_findn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
- const String *self = (const String *)p_self;
- const String *what = (const String *)p_what;
-
- return self->findn(*what, p_from);
-}
-
-godot_string GDAPI godot_string_format(const godot_string *p_self, const godot_variant *p_values) {
- const String *self = (const String *)p_self;
- const Variant *values = (const Variant *)p_values;
- godot_string result;
- memnew_placement(&result, String(self->format(*values)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_format_with_custom_placeholder(const godot_string *p_self, const godot_variant *p_values, const char *p_placeholder) {
- const String *self = (const String *)p_self;
- const Variant *values = (const Variant *)p_values;
- String placeholder = String(p_placeholder);
- godot_string result;
- memnew_placement(&result, String(self->format(*values, placeholder)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot_int p_len) {
- godot_string result;
- memnew_placement(&result, String(String::hex_encode_buffer(p_buffer, p_len)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, const godot_string *p_string) {
- const String *self = (const String *)p_self;
- const String *content = (const String *)p_string;
- godot_string result;
- memnew_placement(&result, String(self->insert(p_at_pos, *content)));
-
- return result;
-}
-
-godot_bool GDAPI godot_string_is_numeric(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->is_numeric();
-}
-
-godot_bool GDAPI godot_string_is_subsequence_of(const godot_string *p_self, const godot_string *p_string) {
- const String *self = (const String *)p_self;
- const String *string = (const String *)p_string;
-
- return self->is_subsequence_of(*string);
-}
-
-godot_bool GDAPI godot_string_is_subsequence_ofi(const godot_string *p_self, const godot_string *p_string) {
- const String *self = (const String *)p_self;
- const String *string = (const String *)p_string;
-
- return self->is_subsequence_ofi(*string);
-}
-
-godot_string GDAPI godot_string_lpad(const godot_string *p_self, godot_int p_min_length) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->lpad(p_min_length)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_lpad_with_custom_character(const godot_string *p_self, godot_int p_min_length, const godot_string *p_character) {
- const String *self = (const String *)p_self;
- const String *character = (const String *)p_character;
- godot_string result;
- memnew_placement(&result, String(self->lpad(p_min_length, *character)));
-
- return result;
-}
-
-godot_bool GDAPI godot_string_match(const godot_string *p_self, const godot_string *p_wildcard) {
- const String *self = (const String *)p_self;
- const String *wildcard = (const String *)p_wildcard;
-
- return self->match(*wildcard);
-}
-
-godot_bool GDAPI godot_string_matchn(const godot_string *p_self, const godot_string *p_wildcard) {
- const String *self = (const String *)p_self;
- const String *wildcard = (const String *)p_wildcard;
-
- return self->matchn(*wildcard);
-}
-
-godot_string GDAPI godot_string_md5(const uint8_t *p_md5) {
- godot_string result;
- memnew_placement(&result, String(String::md5(p_md5)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_num(double p_num) {
- godot_string result;
- memnew_placement(&result, String(String::num(p_num)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_num_int64(int64_t p_num, godot_int p_base) {
- godot_string result;
- memnew_placement(&result, String(String::num_int64(p_num, p_base)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_num_int64_capitalized(int64_t p_num, godot_int p_base, godot_bool p_capitalize_hex) {
- godot_string result;
- memnew_placement(&result, String(String::num_int64(p_num, p_base, true)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_num_real(double p_num) {
- godot_string result;
- memnew_placement(&result, String(String::num_real(p_num)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_num_scientific(double p_num) {
- godot_string result;
- memnew_placement(&result, String(String::num_scientific(p_num)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_num_with_decimals(double p_num, godot_int p_decimals) {
- godot_string result;
- memnew_placement(&result, String(String::num(p_num, p_decimals)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_pad_decimals(const godot_string *p_self, godot_int p_digits) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->pad_decimals(p_digits)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_pad_zeros(const godot_string *p_self, godot_int p_digits) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->pad_zeros(p_digits)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_replace(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) {
- const String *self = (const String *)p_self;
- const String *key = (const String *)p_key;
- const String *with = (const String *)p_with;
- godot_string result;
- memnew_placement(&result, String(self->replace(*key, *with)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_replacen(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) {
- const String *self = (const String *)p_self;
- const String *key = (const String *)p_key;
- const String *with = (const String *)p_with;
- godot_string result;
- memnew_placement(&result, String(self->replacen(*key, *with)));
-
- return result;
-}
-
-godot_int GDAPI godot_string_rfind(const godot_string *p_self, const godot_string *p_what) {
- const String *self = (const String *)p_self;
- const String *what = (const String *)p_what;
-
- return self->rfind(*what);
-}
-
-godot_int GDAPI godot_string_rfindn(const godot_string *p_self, const godot_string *p_what) {
- const String *self = (const String *)p_self;
- const String *what = (const String *)p_what;
-
- return self->rfindn(*what);
-}
-
-godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
- const String *self = (const String *)p_self;
- const String *what = (const String *)p_what;
-
- return self->rfind(*what, p_from);
-}
-
-godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from) {
- const String *self = (const String *)p_self;
- const String *what = (const String *)p_what;
-
- return self->rfindn(*what, p_from);
-}
-
-godot_string GDAPI godot_string_replace_first(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with) {
- const String *self = (const String *)p_self;
- const String *key = (const String *)p_key;
- const String *with = (const String *)p_with;
- godot_string result;
- memnew_placement(&result, String(self->replace_first(*key, *with)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_rpad(const godot_string *p_self, godot_int p_min_length) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->rpad(p_min_length)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_rpad_with_custom_character(const godot_string *p_self, godot_int p_min_length, const godot_string *p_character) {
- const String *self = (const String *)p_self;
- const String *character = (const String *)p_character;
- godot_string result;
- memnew_placement(&result, String(self->rpad(p_min_length, *character)));
-
- return result;
-}
-
-godot_real GDAPI godot_string_similarity(const godot_string *p_self, const godot_string *p_string) {
- const String *self = (const String *)p_self;
- const String *string = (const String *)p_string;
-
- return self->similarity(*string);
-}
-
-godot_string GDAPI godot_string_sprintf(const godot_string *p_self, const godot_array *p_values, godot_bool *p_error) {
- const String *self = (const String *)p_self;
- const Array *values = (const Array *)p_values;
-
- godot_string result;
- String return_value = self->sprintf(*values, p_error);
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_substr(const godot_string *p_self, godot_int p_from, godot_int p_chars) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->substr(p_from, p_chars)));
-
- return result;
-}
-
-godot_int GDAPI godot_string_to_int(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->to_int();
-}
-
-double GDAPI godot_string_to_float(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->to_float();
-}
-
-godot_string GDAPI godot_string_capitalize(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->capitalize()));
-
- return result;
-}
-
-godot_string GDAPI godot_string_camelcase_to_underscore(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->camelcase_to_underscore(false)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_camelcase_to_underscore_lowercased(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->camelcase_to_underscore()));
-
- return result;
-}
-
-double GDAPI godot_string_char_to_float(const char *p_what) {
- return String::to_float(p_what);
-}
-
-double GDAPI godot_string_wchar_to_float(const wchar_t *p_str, const wchar_t **r_end) {
- return String::to_float(p_str, r_end);
-}
-
-godot_int GDAPI godot_string_char_to_int(const char *p_what) {
- return String::to_int(p_what);
-}
-
-godot_int GDAPI godot_string_wchar_to_int(const wchar_t *p_str) {
- return String::to_int(p_str);
-}
-
-godot_int GDAPI godot_string_char_to_int_with_len(const char *p_what, godot_int p_len) {
- return String::to_int(p_what, p_len);
-}
-
-godot_int GDAPI godot_string_wchar_to_int_with_len(const wchar_t *p_str, int p_len) {
- return String::to_int(p_str, p_len);
-}
-
-godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->hex_to_int(false);
-}
-
-godot_int GDAPI godot_string_hex_to_int_with_prefix(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->hex_to_int();
-}
-
-godot_string GDAPI godot_string_get_slice(const godot_string *p_self, const godot_string *p_splitter, godot_int p_slice) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
- godot_string result;
- memnew_placement(&result, String(self->get_slice(*splitter, p_slice)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, godot_char_type p_splitter, godot_int p_slice) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->get_slicec(p_splitter, p_slice)));
-
- return result;
-}
-
-godot_packed_string_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
- godot_packed_string_array ret;
- memnew_placement(&ret, Vector<String>(self->split(*splitter, false)));
- return ret;
-}
-
-godot_packed_string_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
- godot_packed_string_array ret;
- memnew_placement(&ret, Vector<String>(self->split(*splitter, true)));
- return ret;
-}
-
-godot_packed_string_array GDAPI godot_string_split_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
- godot_packed_string_array ret;
- memnew_placement(&ret, Vector<String>(self->split(*splitter, p_allow_empty, p_maxsplit)));
- return ret;
-}
-
-godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_splitter) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
-
- godot_packed_string_array ret;
- memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, false)));
- return ret;
-}
-
-godot_packed_string_array GDAPI godot_string_rsplit_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
-
- godot_packed_string_array ret;
- memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, true)));
- return ret;
-}
-
-godot_packed_string_array GDAPI godot_string_rsplit_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
-
- godot_packed_string_array ret;
- memnew_placement(&ret, Vector<String>(self->rsplit(*splitter, p_allow_empty, p_maxsplit)));
- return ret;
+ return self->utf8().get_data();
}
-godot_packed_float32_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
-
- godot_packed_float32_array ret;
- memnew_placement(&ret, Vector<float>(self->split_floats(*splitter, false)));
- return ret;
-}
-
-godot_packed_float32_array GDAPI godot_string_split_floats_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
-
- godot_packed_float32_array ret;
- memnew_placement(&ret, Vector<float>(self->split_floats(*splitter, true)));
- return ret;
-}
-
-godot_packed_float32_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
- const String *self = (const String *)p_self;
- const Vector<String> *splitters = (const Vector<String> *)p_splitters;
-
- godot_packed_float32_array ret;
- memnew_placement(&ret, Vector<float>(self->split_floats_mk(*splitters, false)));
- return ret;
-}
-
-godot_packed_float32_array GDAPI godot_string_split_floats_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
- const String *self = (const String *)p_self;
- const Vector<String> *splitters = (const Vector<String> *)p_splitters;
-
- godot_packed_float32_array ret;
- memnew_placement(&ret, Vector<float>(self->split_floats_mk(*splitters, true)));
- return ret;
-}
-
-godot_packed_int32_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
-
- godot_packed_int32_array ret;
- memnew_placement(&ret, Vector<int>(self->split_ints(*splitter, false)));
- return ret;
-}
-
-godot_packed_int32_array GDAPI godot_string_split_ints_allow_empty(const godot_string *p_self, const godot_string *p_splitter) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
-
- godot_packed_int32_array ret;
- memnew_placement(&ret, Vector<int>(self->split_ints(*splitter, true)));
- return ret;
-}
-
-godot_packed_int32_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
- const String *self = (const String *)p_self;
- const Vector<String> *splitters = (const Vector<String> *)p_splitters;
-
- godot_packed_int32_array ret;
- memnew_placement(&ret, Vector<int>(self->split_ints_mk(*splitters, false)));
- return ret;
-}
-
-godot_packed_int32_array GDAPI godot_string_split_ints_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters) {
- const String *self = (const String *)p_self;
- const Vector<String> *splitters = (const Vector<String> *)p_splitters;
-
- godot_packed_int32_array ret;
- memnew_placement(&ret, Vector<int>(self->split_ints_mk(*splitters, true)));
- return ret;
-}
-
-godot_packed_string_array GDAPI godot_string_split_spaces(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- godot_packed_string_array ret;
- memnew_placement(&ret, Vector<String>(self->split_spaces()));
- return ret;
-}
-
-godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, const godot_string *p_splitter) {
- const String *self = (const String *)p_self;
- const String *splitter = (const String *)p_splitter;
-
- return self->get_slice_count(*splitter);
-}
-
-godot_char_type GDAPI godot_string_char_lowercase(godot_char_type p_char) {
- return String::char_lowercase(p_char);
-}
-
-godot_char_type GDAPI godot_string_char_uppercase(godot_char_type p_char) {
- return String::char_uppercase(p_char);
-}
-
-godot_string GDAPI godot_string_to_lower(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->to_lower()));
-
- return result;
-}
-
-godot_string GDAPI godot_string_to_upper(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->to_upper()));
-
- return result;
-}
-
-godot_string GDAPI godot_string_get_basename(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->get_basename()));
-
- return result;
-}
-
-godot_string GDAPI godot_string_get_extension(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->get_extension()));
-
- return result;
-}
-
-godot_string GDAPI godot_string_left(const godot_string *p_self, godot_int p_pos) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->left(p_pos)));
-
- return result;
-}
-
-godot_char_type GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx) {
- const String *self = (const String *)p_self;
-
- return self->ord_at(p_idx);
-}
-
-godot_string GDAPI godot_string_plus_file(const godot_string *p_self, const godot_string *p_file) {
- const String *self = (const String *)p_self;
- const String *file = (const String *)p_file;
- godot_string result;
- memnew_placement(&result, String(self->plus_file(*file)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_right(const godot_string *p_self, godot_int p_pos) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->right(p_pos)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_repeat(const godot_string *p_self, godot_int p_count) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->repeat(p_count)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_strip_edges(const godot_string *p_self, godot_bool p_left, godot_bool p_right) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->strip_edges(p_left, p_right)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_strip_escapes(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->strip_escapes()));
-
- return result;
-}
-
-void GDAPI godot_string_erase(godot_string *p_self, godot_int p_pos, godot_int p_chars) {
- String *self = (String *)p_self;
-
- return self->erase(p_pos, p_chars);
-}
-
-godot_char_string GDAPI godot_string_ascii(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_char_string result;
-
- memnew_placement(&result, CharString(self->ascii()));
-
- return result;
-}
-
-godot_char_string GDAPI godot_string_latin1(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- godot_char_string result;
-
- memnew_placement(&result, CharString(self->ascii(true)));
-
- return result;
-}
-
-godot_char_string GDAPI godot_string_utf8(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- godot_char_string result;
-
- memnew_placement(&result, CharString(self->utf8()));
-
- return result;
-}
-
-godot_bool GDAPI godot_string_parse_utf8(godot_string *p_self, const char *p_utf8) {
+const char16_t GDAPI *godot_string_to_utf16_chars(const godot_string *p_self) {
String *self = (String *)p_self;
-
- return self->parse_utf8(p_utf8);
+ return self->utf16().get_data();
}
-godot_bool GDAPI godot_string_parse_utf8_with_len(godot_string *p_self, const char *p_utf8, godot_int p_len) {
+const char32_t GDAPI *godot_string_to_utf32_chars(const godot_string *p_self) {
String *self = (String *)p_self;
-
- return self->parse_utf8(p_utf8, p_len);
-}
-
-godot_string GDAPI godot_string_chars_to_utf8(const char *p_utf8) {
- godot_string result;
- memnew_placement(&result, String(String::utf8(p_utf8)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_chars_to_utf8_with_len(const char *p_utf8, godot_int p_len) {
- godot_string result;
- memnew_placement(&result, String(String::utf8(p_utf8, p_len)));
-
- return result;
-}
-
-godot_char16_string GDAPI godot_string_utf16(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- godot_char16_string result;
-
- memnew_placement(&result, Char16String(self->utf16()));
-
- return result;
+ return self->get_data();
}
-godot_bool GDAPI godot_string_parse_utf16(godot_string *p_self, const char16_t *p_utf16) {
+const wchar_t GDAPI *godot_string_to_wide_chars(const godot_string *p_self) {
String *self = (String *)p_self;
-
- return self->parse_utf16(p_utf16);
+ if (sizeof(wchar_t) == 2) {
+ return (const wchar_t *)self->utf16().get_data();
+ } else {
+ return (const wchar_t *)self->get_data();
+ }
}
-godot_bool GDAPI godot_string_parse_utf16_with_len(godot_string *p_self, const char16_t *p_utf16, godot_int p_len) {
+char32_t GDAPI *godot_string_operator_index(godot_string *p_self, godot_int p_index) {
String *self = (String *)p_self;
-
- return self->parse_utf16(p_utf16, p_len);
-}
-
-godot_string GDAPI godot_string_chars_to_utf16(const char16_t *p_utf16) {
- godot_string result;
- memnew_placement(&result, String(String::utf16(p_utf16)));
-
- return result;
-}
-
-godot_string GDAPI godot_string_chars_to_utf16_with_len(const char16_t *p_utf16, godot_int p_len) {
- godot_string result;
- memnew_placement(&result, String(String::utf16(p_utf16, p_len)));
-
- return result;
-}
-
-uint32_t GDAPI godot_string_hash(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->hash();
+ return self->ptrw();
}
-uint64_t GDAPI godot_string_hash64(const godot_string *p_self) {
+const char32_t GDAPI *godot_string_operator_index_const(const godot_string *p_self, godot_int p_index) {
const String *self = (const String *)p_self;
-
- return self->hash64();
-}
-
-uint32_t GDAPI godot_string_hash_chars(const char *p_cstr) {
- return String::hash(p_cstr);
-}
-
-uint32_t GDAPI godot_string_hash_chars_with_len(const char *p_cstr, godot_int p_len) {
- return String::hash(p_cstr, p_len);
+ return self->ptr();
}
-uint32_t GDAPI godot_string_hash_wide_chars(const wchar_t *p_str) {
- return String::hash(p_str);
-}
-
-uint32_t GDAPI godot_string_hash_wide_chars_with_len(const wchar_t *p_str, godot_int p_len) {
- return String::hash(p_str, p_len);
-}
-
-godot_packed_byte_array GDAPI godot_string_md5_buffer(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_packed_byte_array result;
- memnew_placement(&result, PackedByteArray(self->md5_buffer()));
- return result;
-}
-
-godot_string GDAPI godot_string_md5_text(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->md5_text()));
-
- return result;
-}
-
-godot_packed_byte_array GDAPI godot_string_sha1_buffer(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_packed_byte_array result;
- memnew_placement(&result, PackedByteArray(self->sha1_buffer()));
- return result;
-}
-
-godot_string GDAPI godot_string_sha1_text(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->sha1_text()));
-
- return result;
-}
-
-godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_packed_byte_array result;
- memnew_placement(&result, PackedByteArray(self->sha256_buffer()));
- return result;
-}
-
-godot_string GDAPI godot_string_sha256_text(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- memnew_placement(&result, String(self->sha256_text()));
-
- return result;
-}
-
-godot_bool godot_string_empty(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->empty();
-}
-
-// path functions
-godot_string GDAPI godot_string_get_base_dir(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->get_base_dir();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_get_file(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->get_file();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_humanize_size(size_t p_size) {
- godot_string result;
- String return_value = String::humanize_size(p_size);
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_bool GDAPI godot_string_is_abs_path(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->is_abs_path();
-}
-
-godot_bool GDAPI godot_string_is_rel_path(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->is_rel_path();
-}
-
-godot_bool GDAPI godot_string_is_resource_file(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->is_resource_file();
-}
-
-godot_string GDAPI godot_string_path_to(const godot_string *p_self, const godot_string *p_path) {
- const String *self = (const String *)p_self;
- String *path = (String *)p_path;
- godot_string result;
- String return_value = self->path_to(*path);
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_path_to_file(const godot_string *p_self, const godot_string *p_path) {
- const String *self = (const String *)p_self;
- String *path = (String *)p_path;
- godot_string result;
- String return_value = self->path_to_file(*path);
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_simplify_path(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->simplify_path();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_c_escape(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->c_escape();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_c_escape_multiline(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->c_escape_multiline();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_c_unescape(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->c_unescape();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_http_escape(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->http_escape();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_http_unescape(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->http_unescape();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_json_escape(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->json_escape();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_xml_escape(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->xml_escape();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_xml_escape_with_quotes(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->xml_escape(true);
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_xml_unescape(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->xml_unescape();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_percent_decode(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->percent_decode();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_percent_encode(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->percent_encode();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_join(const godot_string *p_self, const godot_packed_string_array *p_parts) {
- const String *self = (const String *)p_self;
- const Vector<String> *parts = (const Vector<String> *)p_parts;
- godot_string result;
- String return_value = self->join(*parts);
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_bool GDAPI godot_string_is_valid_filename(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->is_valid_filename();
-}
-
-godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->is_valid_float();
-}
-
-godot_bool GDAPI godot_string_is_valid_hex_number(const godot_string *p_self, godot_bool p_with_prefix) {
- const String *self = (const String *)p_self;
-
- return self->is_valid_hex_number(p_with_prefix);
-}
-
-godot_bool GDAPI godot_string_is_valid_html_color(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->is_valid_html_color();
-}
-
-godot_bool GDAPI godot_string_is_valid_identifier(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->is_valid_identifier();
-}
-
-godot_bool GDAPI godot_string_is_valid_integer(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->is_valid_integer();
-}
-
-godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self) {
- const String *self = (const String *)p_self;
-
- return self->is_valid_ip_address();
-}
-
-godot_string GDAPI godot_string_dedent(const godot_string *p_self) {
- const String *self = (const String *)p_self;
- godot_string result;
- String return_value = self->dedent();
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix) {
- const String *self = (const String *)p_self;
- String *prefix = (String *)p_prefix;
- godot_string result;
- String return_value = self->trim_prefix(*prefix);
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix) {
- const String *self = (const String *)p_self;
- String *suffix = (String *)p_suffix;
- godot_string result;
- String return_value = self->trim_suffix(*suffix);
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_lstrip(const godot_string *p_self, const godot_string *p_chars) {
- const String *self = (const String *)p_self;
- String *chars = (String *)p_chars;
- godot_string result;
- String return_value = self->lstrip(*chars);
- memnew_placement(&result, String(return_value));
-
- return result;
-}
-
-godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars) {
- const String *self = (const String *)p_self;
- String *chars = (String *)p_chars;
- godot_string result;
- String return_value = self->rstrip(*chars);
- memnew_placement(&result, String(return_value));
-
- return result;
+void GDAPI godot_string_destroy(godot_string *p_self) {
+ String *self = (String *)p_self;
+ self->~String();
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/string_name.cpp b/modules/gdnative/gdnative/string_name.cpp
index 7bbaaeeaa0..bd8f69674e 100644
--- a/modules/gdnative/gdnative/string_name.cpp
+++ b/modules/gdnative/gdnative/string_name.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,55 +30,26 @@
#include "gdnative/string_name.h"
-#include "core/string_name.h"
-#include "core/ustring.h"
+#include "core/string/string_name.h"
-#include <string.h>
+static_assert(sizeof(godot_string_name) == sizeof(StringName), "StringName size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_string_name) == sizeof(StringName), "StringName size mismatch");
-
-void GDAPI godot_string_name_new(godot_string_name *r_dest, const godot_string *p_name) {
+void GDAPI godot_string_name_new(godot_string_name *r_dest) {
StringName *dest = (StringName *)r_dest;
- const String *name = (const String *)p_name;
- memnew_placement(dest, StringName(*name));
+ memnew_placement(dest, StringName);
}
-void GDAPI godot_string_name_new_data(godot_string_name *r_dest, const char *p_name) {
- StringName *dest = (StringName *)r_dest;
- memnew_placement(dest, StringName(p_name));
+void GDAPI godot_string_name_new_copy(godot_string_name *r_dest, const godot_string_name *p_src) {
+ memnew_placement(r_dest, StringName(*(StringName *)p_src));
}
-godot_string GDAPI godot_string_name_get_name(const godot_string_name *p_self) {
- godot_string ret;
- const StringName *self = (const StringName *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-uint32_t GDAPI godot_string_name_get_hash(const godot_string_name *p_self) {
- const StringName *self = (const StringName *)p_self;
- return self->hash();
-}
-
-const void GDAPI *godot_string_name_get_data_unique_pointer(const godot_string_name *p_self) {
- const StringName *self = (const StringName *)p_self;
- return self->data_unique_pointer();
-}
-
-godot_bool GDAPI godot_string_name_operator_equal(const godot_string_name *p_self, const godot_string_name *p_other) {
- const StringName *self = (const StringName *)p_self;
- const StringName *other = (const StringName *)p_other;
- return self == other;
-}
-
-godot_bool GDAPI godot_string_name_operator_less(const godot_string_name *p_self, const godot_string_name *p_other) {
- const StringName *self = (const StringName *)p_self;
- const StringName *other = (const StringName *)p_other;
- return self < other;
+void GDAPI godot_string_name_new_with_latin1_chars(godot_string_name *r_dest, const char *p_contents) {
+ StringName *dest = (StringName *)r_dest;
+ memnew_placement(dest, StringName(p_contents));
}
void GDAPI godot_string_name_destroy(godot_string_name *p_self) {
diff --git a/modules/gdnative/gdnative/transform.cpp b/modules/gdnative/gdnative/transform.cpp
deleted file mode 100644
index d19de93e9b..0000000000
--- a/modules/gdnative/gdnative/transform.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
-/*************************************************************************/
-/* transform.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "gdnative/transform.h"
-
-#include "core/math/transform.h"
-#include "core/variant.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-static_assert(sizeof(godot_transform) == sizeof(Transform), "Transform size mismatch");
-
-void GDAPI godot_transform_new_with_axis_origin(godot_transform *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis, const godot_vector3 *p_origin) {
- const Vector3 *x_axis = (const Vector3 *)p_x_axis;
- const Vector3 *y_axis = (const Vector3 *)p_y_axis;
- const Vector3 *z_axis = (const Vector3 *)p_z_axis;
- const Vector3 *origin = (const Vector3 *)p_origin;
- Transform *dest = (Transform *)r_dest;
- dest->basis.set_axis(0, *x_axis);
- dest->basis.set_axis(1, *y_axis);
- dest->basis.set_axis(2, *z_axis);
- dest->origin = *origin;
-}
-
-void GDAPI godot_transform_new(godot_transform *r_dest, const godot_basis *p_basis, const godot_vector3 *p_origin) {
- const Basis *basis = (const Basis *)p_basis;
- const Vector3 *origin = (const Vector3 *)p_origin;
- Transform *dest = (Transform *)r_dest;
- *dest = Transform(*basis, *origin);
-}
-
-void GDAPI godot_transform_new_with_quat(godot_transform *r_dest, const godot_quat *p_quat) {
- const Quat *quat = (const Quat *)p_quat;
- Transform *dest = (Transform *)r_dest;
- *dest = Transform(*quat);
-}
-
-godot_basis GDAPI godot_transform_get_basis(const godot_transform *p_self) {
- godot_basis dest;
- const Transform *self = (const Transform *)p_self;
- *((Basis *)&dest) = self->basis;
- return dest;
-}
-
-void GDAPI godot_transform_set_basis(godot_transform *p_self, const godot_basis *p_v) {
- Transform *self = (Transform *)p_self;
- const Basis *v = (const Basis *)p_v;
- self->basis = *v;
-}
-
-godot_vector3 GDAPI godot_transform_get_origin(const godot_transform *p_self) {
- godot_vector3 dest;
- const Transform *self = (const Transform *)p_self;
- *((Vector3 *)&dest) = self->origin;
- return dest;
-}
-
-void GDAPI godot_transform_set_origin(godot_transform *p_self, const godot_vector3 *p_v) {
- Transform *self = (Transform *)p_self;
- const Vector3 *v = (const Vector3 *)p_v;
- self->origin = *v;
-}
-
-godot_string GDAPI godot_transform_as_string(const godot_transform *p_self) {
- godot_string ret;
- const Transform *self = (const Transform *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_transform GDAPI godot_transform_inverse(const godot_transform *p_self) {
- godot_transform dest;
- const Transform *self = (const Transform *)p_self;
- *((Transform *)&dest) = self->inverse();
- return dest;
-}
-
-godot_transform GDAPI godot_transform_affine_inverse(const godot_transform *p_self) {
- godot_transform dest;
- const Transform *self = (const Transform *)p_self;
- *((Transform *)&dest) = self->affine_inverse();
- return dest;
-}
-
-godot_transform GDAPI godot_transform_orthonormalized(const godot_transform *p_self) {
- godot_transform dest;
- const Transform *self = (const Transform *)p_self;
- *((Transform *)&dest) = self->orthonormalized();
- return dest;
-}
-
-godot_transform GDAPI godot_transform_rotated(const godot_transform *p_self, const godot_vector3 *p_axis, const godot_real p_phi) {
- godot_transform dest;
- const Transform *self = (const Transform *)p_self;
- const Vector3 *axis = (const Vector3 *)p_axis;
- *((Transform *)&dest) = self->rotated(*axis, p_phi);
- return dest;
-}
-
-godot_transform GDAPI godot_transform_scaled(const godot_transform *p_self, const godot_vector3 *p_scale) {
- godot_transform dest;
- const Transform *self = (const Transform *)p_self;
- const Vector3 *scale = (const Vector3 *)p_scale;
- *((Transform *)&dest) = self->scaled(*scale);
- return dest;
-}
-
-godot_transform GDAPI godot_transform_translated(const godot_transform *p_self, const godot_vector3 *p_ofs) {
- godot_transform dest;
- const Transform *self = (const Transform *)p_self;
- const Vector3 *ofs = (const Vector3 *)p_ofs;
- *((Transform *)&dest) = self->translated(*ofs);
- return dest;
-}
-
-godot_transform GDAPI godot_transform_looking_at(const godot_transform *p_self, const godot_vector3 *p_target, const godot_vector3 *p_up) {
- godot_transform dest;
- const Transform *self = (const Transform *)p_self;
- const Vector3 *target = (const Vector3 *)p_target;
- const Vector3 *up = (const Vector3 *)p_up;
- *((Transform *)&dest) = self->looking_at(*target, *up);
- return dest;
-}
-
-godot_plane GDAPI godot_transform_xform_plane(const godot_transform *p_self, const godot_plane *p_v) {
- godot_plane raw_dest;
- Plane *dest = (Plane *)&raw_dest;
- const Transform *self = (const Transform *)p_self;
- const Plane *v = (const Plane *)p_v;
- *dest = self->xform(*v);
- return raw_dest;
-}
-
-godot_plane GDAPI godot_transform_xform_inv_plane(const godot_transform *p_self, const godot_plane *p_v) {
- godot_plane raw_dest;
- Plane *dest = (Plane *)&raw_dest;
- const Transform *self = (const Transform *)p_self;
- const Plane *v = (const Plane *)p_v;
- *dest = self->xform_inv(*v);
- return raw_dest;
-}
-
-void GDAPI godot_transform_new_identity(godot_transform *r_dest) {
- Transform *dest = (Transform *)r_dest;
- *dest = Transform();
-}
-
-godot_bool GDAPI godot_transform_operator_equal(const godot_transform *p_self, const godot_transform *p_b) {
- const Transform *self = (const Transform *)p_self;
- const Transform *b = (const Transform *)p_b;
- return *self == *b;
-}
-
-godot_transform GDAPI godot_transform_operator_multiply(const godot_transform *p_self, const godot_transform *p_b) {
- godot_transform raw_dest;
- Transform *dest = (Transform *)&raw_dest;
- const Transform *self = (const Transform *)p_self;
- const Transform *b = (const Transform *)p_b;
- *dest = *self * *b;
- return raw_dest;
-}
-
-godot_vector3 GDAPI godot_transform_xform_vector3(const godot_transform *p_self, const godot_vector3 *p_v) {
- godot_vector3 raw_dest;
- Vector3 *dest = (Vector3 *)&raw_dest;
- const Transform *self = (const Transform *)p_self;
- const Vector3 *v = (const Vector3 *)p_v;
- *dest = self->xform(*v);
- return raw_dest;
-}
-
-godot_vector3 GDAPI godot_transform_xform_inv_vector3(const godot_transform *p_self, const godot_vector3 *p_v) {
- godot_vector3 raw_dest;
- Vector3 *dest = (Vector3 *)&raw_dest;
- const Transform *self = (const Transform *)p_self;
- const Vector3 *v = (const Vector3 *)p_v;
- *dest = self->xform_inv(*v);
- return raw_dest;
-}
-
-godot_aabb GDAPI godot_transform_xform_aabb(const godot_transform *p_self, const godot_aabb *p_v) {
- godot_aabb raw_dest;
- AABB *dest = (AABB *)&raw_dest;
- const Transform *self = (const Transform *)p_self;
- const AABB *v = (const AABB *)p_v;
- *dest = self->xform(*v);
- return raw_dest;
-}
-
-godot_aabb GDAPI godot_transform_xform_inv_aabb(const godot_transform *p_self, const godot_aabb *p_v) {
- godot_aabb raw_dest;
- AABB *dest = (AABB *)&raw_dest;
- const Transform *self = (const Transform *)p_self;
- const AABB *v = (const AABB *)p_v;
- *dest = self->xform_inv(*v);
- return raw_dest;
-}
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/modules/gdnative/gdnative/transform2d.cpp b/modules/gdnative/gdnative/transform2d.cpp
index c0f7878eb0..2864818831 100644
--- a/modules/gdnative/gdnative/transform2d.cpp
+++ b/modules/gdnative/gdnative/transform2d.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,179 +31,29 @@
#include "gdnative/transform2d.h"
#include "core/math/transform_2d.h"
-#include "core/variant.h"
+
+static_assert(sizeof(godot_transform2d) == sizeof(Transform2D), "Transform2D size mismatch");
#ifdef __cplusplus
extern "C" {
#endif
-static_assert(sizeof(godot_transform2d) == sizeof(Transform2D), "Transform2D size mismatch");
-
-void GDAPI godot_transform2d_new(godot_transform2d *r_dest, const godot_real p_rot, const godot_vector2 *p_pos) {
- const Vector2 *pos = (const Vector2 *)p_pos;
- Transform2D *dest = (Transform2D *)r_dest;
- *dest = Transform2D(p_rot, *pos);
-}
-
-void GDAPI godot_transform2d_new_axis_origin(godot_transform2d *r_dest, const godot_vector2 *p_x_axis, const godot_vector2 *p_y_axis, const godot_vector2 *p_origin) {
- const Vector2 *x_axis = (const Vector2 *)p_x_axis;
- const Vector2 *y_axis = (const Vector2 *)p_y_axis;
- const Vector2 *origin = (const Vector2 *)p_origin;
- Transform2D *dest = (Transform2D *)r_dest;
- *dest = Transform2D(x_axis->x, x_axis->y, y_axis->x, y_axis->y, origin->x, origin->y);
-}
-
-godot_string GDAPI godot_transform2d_as_string(const godot_transform2d *p_self) {
- godot_string ret;
- const Transform2D *self = (const Transform2D *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_transform2d GDAPI godot_transform2d_inverse(const godot_transform2d *p_self) {
- godot_transform2d dest;
- const Transform2D *self = (const Transform2D *)p_self;
- *((Transform2D *)&dest) = self->inverse();
- return dest;
-}
-
-godot_transform2d GDAPI godot_transform2d_affine_inverse(const godot_transform2d *p_self) {
- godot_transform2d dest;
- const Transform2D *self = (const Transform2D *)p_self;
- *((Transform2D *)&dest) = self->affine_inverse();
- return dest;
-}
-
-godot_real GDAPI godot_transform2d_get_rotation(const godot_transform2d *p_self) {
- const Transform2D *self = (const Transform2D *)p_self;
- return self->get_rotation();
-}
-
-godot_vector2 GDAPI godot_transform2d_get_origin(const godot_transform2d *p_self) {
- godot_vector2 dest;
- const Transform2D *self = (const Transform2D *)p_self;
- *((Vector2 *)&dest) = self->get_origin();
- return dest;
+void GDAPI godot_transform2d_new(godot_transform2d *p_self) {
+ memnew_placement(p_self, Transform2D);
}
-godot_vector2 GDAPI godot_transform2d_get_scale(const godot_transform2d *p_self) {
- godot_vector2 dest;
- const Transform2D *self = (const Transform2D *)p_self;
- *((Vector2 *)&dest) = self->get_scale();
- return dest;
+void GDAPI godot_transform2d_new_copy(godot_transform2d *r_dest, const godot_transform2d *p_src) {
+ memnew_placement(r_dest, Transform2D(*(Transform2D *)p_src));
}
-godot_transform2d GDAPI godot_transform2d_orthonormalized(const godot_transform2d *p_self) {
- godot_transform2d dest;
- const Transform2D *self = (const Transform2D *)p_self;
- *((Transform2D *)&dest) = self->orthonormalized();
- return dest;
-}
-
-godot_transform2d GDAPI godot_transform2d_rotated(const godot_transform2d *p_self, const godot_real p_phi) {
- godot_transform2d dest;
- const Transform2D *self = (const Transform2D *)p_self;
-
- *((Transform2D *)&dest) = self->rotated(p_phi);
- return dest;
-}
-
-godot_transform2d GDAPI godot_transform2d_scaled(const godot_transform2d *p_self, const godot_vector2 *p_scale) {
- godot_transform2d dest;
- const Transform2D *self = (const Transform2D *)p_self;
- const Vector2 *scale = (const Vector2 *)p_scale;
- *((Transform2D *)&dest) = self->scaled(*scale);
- return dest;
-}
-
-godot_transform2d GDAPI godot_transform2d_translated(const godot_transform2d *p_self, const godot_vector2 *p_offset) {
- godot_transform2d dest;
- const Transform2D *self = (const Transform2D *)p_self;
- const Vector2 *offset = (const Vector2 *)p_offset;
- *((Transform2D *)&dest) = self->translated(*offset);
- return dest;
-}
-
-godot_vector2 GDAPI godot_transform2d_xform_vector2(const godot_transform2d *p_self, const godot_vector2 *p_v) {
- godot_vector2 raw_dest;
- Vector2 *dest = (Vector2 *)&raw_dest;
- const Transform2D *self = (const Transform2D *)p_self;
- const Vector2 *v = (const Vector2 *)p_v;
- *dest = self->xform(*v);
- return raw_dest;
-}
-
-godot_vector2 GDAPI godot_transform2d_xform_inv_vector2(const godot_transform2d *p_self, const godot_vector2 *p_v) {
- godot_vector2 raw_dest;
- Vector2 *dest = (Vector2 *)&raw_dest;
- const Transform2D *self = (const Transform2D *)p_self;
- const Vector2 *v = (const Vector2 *)p_v;
- *dest = self->xform_inv(*v);
- return raw_dest;
-}
-
-godot_vector2 GDAPI godot_transform2d_basis_xform_vector2(const godot_transform2d *p_self, const godot_vector2 *p_v) {
- godot_vector2 raw_dest;
- Vector2 *dest = (Vector2 *)&raw_dest;
- const Transform2D *self = (const Transform2D *)p_self;
- const Vector2 *v = (const Vector2 *)p_v;
- *dest = self->basis_xform(*v);
- return raw_dest;
-}
-
-godot_vector2 GDAPI godot_transform2d_basis_xform_inv_vector2(const godot_transform2d *p_self, const godot_vector2 *p_v) {
- godot_vector2 raw_dest;
- Vector2 *dest = (Vector2 *)&raw_dest;
- const Transform2D *self = (const Transform2D *)p_self;
- const Vector2 *v = (const Vector2 *)p_v;
- *dest = self->basis_xform_inv(*v);
- return raw_dest;
-}
-
-godot_transform2d GDAPI godot_transform2d_interpolate_with(const godot_transform2d *p_self, const godot_transform2d *p_m, const godot_real p_c) {
- godot_transform2d dest;
- const Transform2D *self = (const Transform2D *)p_self;
- const Transform2D *m = (const Transform2D *)p_m;
- *((Transform2D *)&dest) = self->interpolate_with(*m, p_c);
- return dest;
-}
-
-godot_bool GDAPI godot_transform2d_operator_equal(const godot_transform2d *p_self, const godot_transform2d *p_b) {
- const Transform2D *self = (const Transform2D *)p_self;
- const Transform2D *b = (const Transform2D *)p_b;
- return *self == *b;
-}
-
-godot_transform2d GDAPI godot_transform2d_operator_multiply(const godot_transform2d *p_self, const godot_transform2d *p_b) {
- godot_transform2d raw_dest;
- Transform2D *dest = (Transform2D *)&raw_dest;
- const Transform2D *self = (const Transform2D *)p_self;
- const Transform2D *b = (const Transform2D *)p_b;
- *dest = *self * *b;
- return raw_dest;
-}
-
-void GDAPI godot_transform2d_new_identity(godot_transform2d *r_dest) {
- Transform2D *dest = (Transform2D *)r_dest;
- *dest = Transform2D();
-}
-
-godot_rect2 GDAPI godot_transform2d_xform_rect2(const godot_transform2d *p_self, const godot_rect2 *p_v) {
- godot_rect2 raw_dest;
- Rect2 *dest = (Rect2 *)&raw_dest;
- const Transform2D *self = (const Transform2D *)p_self;
- const Rect2 *v = (const Rect2 *)p_v;
- *dest = self->xform(*v);
- return raw_dest;
+godot_vector2 GDAPI *godot_transform2d_operator_index(godot_transform2d *p_self, godot_int p_index) {
+ Transform2D *self = (Transform2D *)p_self;
+ return (godot_vector2 *)&self->operator[](p_index);
}
-godot_rect2 GDAPI godot_transform2d_xform_inv_rect2(const godot_transform2d *p_self, const godot_rect2 *p_v) {
- godot_rect2 raw_dest;
- Rect2 *dest = (Rect2 *)&raw_dest;
+const godot_vector2 GDAPI *godot_transform2d_operator_index_const(const godot_transform2d *p_self, godot_int p_index) {
const Transform2D *self = (const Transform2D *)p_self;
- const Rect2 *v = (const Rect2 *)p_v;
- *dest = self->xform_inv(*v);
- return raw_dest;
+ return (const godot_vector2 *)&self->operator[](p_index);
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/transform_3d.cpp b/modules/gdnative/gdnative/transform_3d.cpp
new file mode 100644
index 0000000000..8bd2a68d63
--- /dev/null
+++ b/modules/gdnative/gdnative/transform_3d.cpp
@@ -0,0 +1,51 @@
+/*************************************************************************/
+/* transform_3d.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 "gdnative/transform_3d.h"
+
+#include "core/math/transform_3d.h"
+
+static_assert(sizeof(godot_transform3d) == sizeof(Transform3D), "Transform3D size mismatch");
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void GDAPI godot_transform3d_new(godot_transform3d *p_self) {
+ memnew_placement(p_self, Transform3D);
+}
+
+void GDAPI godot_transform3d_new_copy(godot_transform3d *r_dest, const godot_transform3d *p_src) {
+ memnew_placement(r_dest, Transform3D(*(Transform3D *)p_src));
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/gdnative/gdnative/variant.cpp b/modules/gdnative/gdnative/variant.cpp
index dac4feb0e5..ec9aaa0a55 100644
--- a/modules/gdnative/gdnative/variant.cpp
+++ b/modules/gdnative/gdnative/variant.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,8 @@
#include "gdnative/variant.h"
-#include "core/reference.h"
-#include "core/variant.h"
+#include "core/object/ref_counted.h"
+#include "core/variant/variant.h"
#ifdef __cplusplus
extern "C" {
@@ -48,23 +48,16 @@ static_assert(sizeof(godot_variant) == sizeof(Variant), "Variant size mismatch")
#pragma GCC optimize("-O0")
#endif
-#define memnew_placement_custom(m_placement, m_class, m_constr) _post_initialize(new (m_placement, sizeof(m_class), "") m_constr)
-
#if defined(__arm__) && defined(__GNUC__) && !defined(__clang__) && \
(__GNUC__ == 6 || (__GNUC__ == 7 && __GNUC_MINOR__ < 4) || (__GNUC__ == 8 && __GNUC_MINOR__ < 1))
#pragma GCC pop_options
#endif
-// Constructors
-
-godot_variant_type GDAPI godot_variant_get_type(const godot_variant *p_self) {
- const Variant *self = (const Variant *)p_self;
- return (godot_variant_type)self->get_type();
-}
+// Memory
void GDAPI godot_variant_new_copy(godot_variant *p_dest, const godot_variant *p_src) {
Variant *dest = (Variant *)p_dest;
- Variant *src = (Variant *)p_src;
+ const Variant *src = (const Variant *)p_src;
memnew_placement(dest, Variant(*src));
}
@@ -75,222 +68,217 @@ void GDAPI godot_variant_new_nil(godot_variant *r_dest) {
void GDAPI godot_variant_new_bool(godot_variant *r_dest, const godot_bool p_b) {
Variant *dest = (Variant *)r_dest;
- memnew_placement_custom(dest, Variant, Variant(p_b));
+ memnew_placement(dest, Variant(p_b));
}
-void GDAPI godot_variant_new_uint(godot_variant *r_dest, const uint64_t p_i) {
+void GDAPI godot_variant_new_int(godot_variant *r_dest, const godot_int p_i) {
Variant *dest = (Variant *)r_dest;
- memnew_placement_custom(dest, Variant, Variant(p_i));
+ memnew_placement(dest, Variant(p_i));
}
-void GDAPI godot_variant_new_int(godot_variant *r_dest, const int64_t p_i) {
+void GDAPI godot_variant_new_float(godot_variant *r_dest, const godot_float p_r) {
Variant *dest = (Variant *)r_dest;
- memnew_placement_custom(dest, Variant, Variant(p_i));
-}
-
-void GDAPI godot_variant_new_real(godot_variant *r_dest, const double p_r) {
- Variant *dest = (Variant *)r_dest;
- memnew_placement_custom(dest, Variant, Variant(p_r));
+ memnew_placement(dest, Variant(p_r));
}
void GDAPI godot_variant_new_string(godot_variant *r_dest, const godot_string *p_s) {
Variant *dest = (Variant *)r_dest;
- String *s = (String *)p_s;
- memnew_placement_custom(dest, Variant, Variant(*s));
+ const String *s = (const String *)p_s;
+ memnew_placement(dest, Variant(*s));
}
void GDAPI godot_variant_new_string_name(godot_variant *r_dest, const godot_string_name *p_s) {
Variant *dest = (Variant *)r_dest;
- StringName *s = (StringName *)p_s;
- memnew_placement_custom(dest, Variant, Variant(*s));
+ const StringName *s = (const StringName *)p_s;
+ memnew_placement(dest, Variant(*s));
}
void GDAPI godot_variant_new_vector2(godot_variant *r_dest, const godot_vector2 *p_v2) {
Variant *dest = (Variant *)r_dest;
- Vector2 *v2 = (Vector2 *)p_v2;
- memnew_placement_custom(dest, Variant, Variant(*v2));
+ const Vector2 *v2 = (const Vector2 *)p_v2;
+ memnew_placement(dest, Variant(*v2));
}
void GDAPI godot_variant_new_vector2i(godot_variant *r_dest, const godot_vector2i *p_v2) {
Variant *dest = (Variant *)r_dest;
- Vector2i *v2 = (Vector2i *)p_v2;
- memnew_placement_custom(dest, Variant, Variant(*v2));
+ const Vector2i *v2 = (const Vector2i *)p_v2;
+ memnew_placement(dest, Variant(*v2));
}
void GDAPI godot_variant_new_rect2(godot_variant *r_dest, const godot_rect2 *p_rect2) {
Variant *dest = (Variant *)r_dest;
- Rect2 *rect2 = (Rect2 *)p_rect2;
- memnew_placement_custom(dest, Variant, Variant(*rect2));
+ const Rect2 *rect2 = (const Rect2 *)p_rect2;
+ memnew_placement(dest, Variant(*rect2));
}
void GDAPI godot_variant_new_rect2i(godot_variant *r_dest, const godot_rect2i *p_rect2) {
Variant *dest = (Variant *)r_dest;
- Rect2i *rect2 = (Rect2i *)p_rect2;
- memnew_placement_custom(dest, Variant, Variant(*rect2));
+ const Rect2i *rect2 = (const Rect2i *)p_rect2;
+ memnew_placement(dest, Variant(*rect2));
}
void GDAPI godot_variant_new_vector3(godot_variant *r_dest, const godot_vector3 *p_v3) {
Variant *dest = (Variant *)r_dest;
- Vector3 *v3 = (Vector3 *)p_v3;
- memnew_placement_custom(dest, Variant, Variant(*v3));
+ const Vector3 *v3 = (const Vector3 *)p_v3;
+ memnew_placement(dest, Variant(*v3));
}
void GDAPI godot_variant_new_vector3i(godot_variant *r_dest, const godot_vector3i *p_v3) {
Variant *dest = (Variant *)r_dest;
- Vector3i *v3 = (Vector3i *)p_v3;
- memnew_placement_custom(dest, Variant, Variant(*v3));
+ const Vector3i *v3 = (const Vector3i *)p_v3;
+ memnew_placement(dest, Variant(*v3));
}
void GDAPI godot_variant_new_transform2d(godot_variant *r_dest, const godot_transform2d *p_t2d) {
Variant *dest = (Variant *)r_dest;
- Transform2D *t2d = (Transform2D *)p_t2d;
- memnew_placement_custom(dest, Variant, Variant(*t2d));
+ const Transform2D *t2d = (const Transform2D *)p_t2d;
+ memnew_placement(dest, Variant(*t2d));
}
void GDAPI godot_variant_new_plane(godot_variant *r_dest, const godot_plane *p_plane) {
Variant *dest = (Variant *)r_dest;
- Plane *plane = (Plane *)p_plane;
- memnew_placement_custom(dest, Variant, Variant(*plane));
+ const Plane *plane = (const Plane *)p_plane;
+ memnew_placement(dest, Variant(*plane));
}
-void GDAPI godot_variant_new_quat(godot_variant *r_dest, const godot_quat *p_quat) {
+void GDAPI godot_variant_new_quaternion(godot_variant *r_dest, const godot_quaternion *p_quaternion) {
Variant *dest = (Variant *)r_dest;
- Quat *quat = (Quat *)p_quat;
- memnew_placement_custom(dest, Variant, Variant(*quat));
+ const Quaternion *quaternion = (const Quaternion *)p_quaternion;
+ memnew_placement(dest, Variant(*quaternion));
}
void GDAPI godot_variant_new_aabb(godot_variant *r_dest, const godot_aabb *p_aabb) {
Variant *dest = (Variant *)r_dest;
- AABB *aabb = (AABB *)p_aabb;
- memnew_placement_custom(dest, Variant, Variant(*aabb));
+ const AABB *aabb = (const AABB *)p_aabb;
+ memnew_placement(dest, Variant(*aabb));
}
void GDAPI godot_variant_new_basis(godot_variant *r_dest, const godot_basis *p_basis) {
Variant *dest = (Variant *)r_dest;
- Basis *basis = (Basis *)p_basis;
- memnew_placement_custom(dest, Variant, Variant(*basis));
+ const Basis *basis = (const Basis *)p_basis;
+ memnew_placement(dest, Variant(*basis));
}
-void GDAPI godot_variant_new_transform(godot_variant *r_dest, const godot_transform *p_trans) {
+void GDAPI godot_variant_new_transform3d(godot_variant *r_dest, const godot_transform3d *p_trans) {
Variant *dest = (Variant *)r_dest;
- Transform *trans = (Transform *)p_trans;
- memnew_placement_custom(dest, Variant, Variant(*trans));
+ const Transform3D *trans = (const Transform3D *)p_trans;
+ memnew_placement(dest, Variant(*trans));
}
void GDAPI godot_variant_new_color(godot_variant *r_dest, const godot_color *p_color) {
Variant *dest = (Variant *)r_dest;
- Color *color = (Color *)p_color;
- memnew_placement_custom(dest, Variant, Variant(*color));
+ const Color *color = (const Color *)p_color;
+ memnew_placement(dest, Variant(*color));
}
void GDAPI godot_variant_new_node_path(godot_variant *r_dest, const godot_node_path *p_np) {
Variant *dest = (Variant *)r_dest;
- NodePath *np = (NodePath *)p_np;
- memnew_placement_custom(dest, Variant, Variant(*np));
+ const NodePath *np = (const NodePath *)p_np;
+ memnew_placement(dest, Variant(*np));
}
void GDAPI godot_variant_new_rid(godot_variant *r_dest, const godot_rid *p_rid) {
Variant *dest = (Variant *)r_dest;
- RID *rid = (RID *)p_rid;
- memnew_placement_custom(dest, Variant, Variant(*rid));
+ const RID *rid = (const RID *)p_rid;
+ memnew_placement(dest, Variant(*rid));
}
void GDAPI godot_variant_new_callable(godot_variant *r_dest, const godot_callable *p_cb) {
Variant *dest = (Variant *)r_dest;
- Callable *cb = (Callable *)p_cb;
- memnew_placement_custom(dest, Variant, Variant(*cb));
+ const Callable *cb = (const Callable *)p_cb;
+ memnew_placement(dest, Variant(*cb));
}
void GDAPI godot_variant_new_signal(godot_variant *r_dest, const godot_signal *p_signal) {
Variant *dest = (Variant *)r_dest;
- Signal *signal = (Signal *)p_signal;
- memnew_placement_custom(dest, Variant, Variant(*signal));
+ const Signal *signal = (const Signal *)p_signal;
+ memnew_placement(dest, Variant(*signal));
}
void GDAPI godot_variant_new_object(godot_variant *r_dest, const godot_object *p_obj) {
Variant *dest = (Variant *)r_dest;
- Object *obj = (Object *)p_obj;
- Reference *reference = Object::cast_to<Reference>(obj);
+ const Object *obj = (const Object *)p_obj;
+ const RefCounted *ref_counted = Object::cast_to<RefCounted>(obj);
REF ref;
- if (reference) {
- ref = REF(reference);
+ if (ref_counted) {
+ ref = REF(ref_counted);
}
if (!ref.is_null()) {
- memnew_placement_custom(dest, Variant, Variant(ref));
+ memnew_placement(dest, Variant(ref));
} else {
#if defined(DEBUG_METHODS_ENABLED)
- if (reference) {
- ERR_PRINT("Reference object has 0 refcount in godot_variant_new_object - you lost it somewhere.");
+ if (ref_counted) {
+ ERR_PRINT("RefCounted object has 0 refcount in godot_variant_new_object - you lost it somewhere.");
}
#endif
- memnew_placement_custom(dest, Variant, Variant(obj));
+ memnew_placement(dest, Variant(obj));
}
}
void GDAPI godot_variant_new_dictionary(godot_variant *r_dest, const godot_dictionary *p_dict) {
Variant *dest = (Variant *)r_dest;
- Dictionary *dict = (Dictionary *)p_dict;
- memnew_placement_custom(dest, Variant, Variant(*dict));
+ const Dictionary *dict = (const Dictionary *)p_dict;
+ memnew_placement(dest, Variant(*dict));
}
void GDAPI godot_variant_new_array(godot_variant *r_dest, const godot_array *p_arr) {
Variant *dest = (Variant *)r_dest;
- Array *arr = (Array *)p_arr;
- memnew_placement_custom(dest, Variant, Variant(*arr));
+ const Array *arr = (const Array *)p_arr;
+ memnew_placement(dest, Variant(*arr));
}
void GDAPI godot_variant_new_packed_byte_array(godot_variant *r_dest, const godot_packed_byte_array *p_pba) {
Variant *dest = (Variant *)r_dest;
- PackedByteArray *pba = (PackedByteArray *)p_pba;
- memnew_placement_custom(dest, Variant, Variant(*pba));
+ const PackedByteArray *pba = (const PackedByteArray *)p_pba;
+ memnew_placement(dest, Variant(*pba));
}
void GDAPI godot_variant_new_packed_int32_array(godot_variant *r_dest, const godot_packed_int32_array *p_pia) {
Variant *dest = (Variant *)r_dest;
- PackedInt32Array *pia = (PackedInt32Array *)p_pia;
- memnew_placement_custom(dest, Variant, Variant(*pia));
+ const PackedInt32Array *pia = (const PackedInt32Array *)p_pia;
+ memnew_placement(dest, Variant(*pia));
}
void GDAPI godot_variant_new_packed_int64_array(godot_variant *r_dest, const godot_packed_int64_array *p_pia) {
Variant *dest = (Variant *)r_dest;
- PackedInt64Array *pia = (PackedInt64Array *)p_pia;
- memnew_placement_custom(dest, Variant, Variant(*pia));
+ const PackedInt64Array *pia = (const PackedInt64Array *)p_pia;
+ memnew_placement(dest, Variant(*pia));
}
void GDAPI godot_variant_new_packed_float32_array(godot_variant *r_dest, const godot_packed_float32_array *p_pra) {
Variant *dest = (Variant *)r_dest;
- PackedFloat32Array *pra = (PackedFloat32Array *)p_pra;
- memnew_placement_custom(dest, Variant, Variant(*pra));
+ const PackedFloat32Array *pra = (const PackedFloat32Array *)p_pra;
+ memnew_placement(dest, Variant(*pra));
}
void GDAPI godot_variant_new_packed_float64_array(godot_variant *r_dest, const godot_packed_float64_array *p_pra) {
Variant *dest = (Variant *)r_dest;
- PackedFloat64Array *pra = (PackedFloat64Array *)p_pra;
- memnew_placement_custom(dest, Variant, Variant(*pra));
+ const PackedFloat64Array *pra = (const PackedFloat64Array *)p_pra;
+ memnew_placement(dest, Variant(*pra));
}
void GDAPI godot_variant_new_packed_string_array(godot_variant *r_dest, const godot_packed_string_array *p_psa) {
Variant *dest = (Variant *)r_dest;
- PackedStringArray *psa = (PackedStringArray *)p_psa;
- memnew_placement_custom(dest, Variant, Variant(*psa));
+ const PackedStringArray *psa = (const PackedStringArray *)p_psa;
+ memnew_placement(dest, Variant(*psa));
}
void GDAPI godot_variant_new_packed_vector2_array(godot_variant *r_dest, const godot_packed_vector2_array *p_pv2a) {
Variant *dest = (Variant *)r_dest;
- PackedVector2Array *pv2a = (PackedVector2Array *)p_pv2a;
- memnew_placement_custom(dest, Variant, Variant(*pv2a));
+ const PackedVector2Array *pv2a = (const PackedVector2Array *)p_pv2a;
+ memnew_placement(dest, Variant(*pv2a));
}
void GDAPI godot_variant_new_packed_vector3_array(godot_variant *r_dest, const godot_packed_vector3_array *p_pv3a) {
Variant *dest = (Variant *)r_dest;
- PackedVector3Array *pv3a = (PackedVector3Array *)p_pv3a;
- memnew_placement_custom(dest, Variant, Variant(*pv3a));
+ const PackedVector3Array *pv3a = (const PackedVector3Array *)p_pv3a;
+ memnew_placement(dest, Variant(*pv3a));
}
void GDAPI godot_variant_new_packed_color_array(godot_variant *r_dest, const godot_packed_color_array *p_pca) {
Variant *dest = (Variant *)r_dest;
- PackedColorArray *pca = (PackedColorArray *)p_pca;
- memnew_placement_custom(dest, Variant, Variant(*pca));
+ const PackedColorArray *pca = (const PackedColorArray *)p_pca;
+ memnew_placement(dest, Variant(*pca));
}
godot_bool GDAPI godot_variant_as_bool(const godot_variant *p_self) {
@@ -298,17 +286,12 @@ godot_bool GDAPI godot_variant_as_bool(const godot_variant *p_self) {
return self->operator bool();
}
-uint64_t GDAPI godot_variant_as_uint(const godot_variant *p_self) {
- const Variant *self = (const Variant *)p_self;
- return self->operator uint64_t();
-}
-
-int64_t GDAPI godot_variant_as_int(const godot_variant *p_self) {
+godot_int GDAPI godot_variant_as_int(const godot_variant *p_self) {
const Variant *self = (const Variant *)p_self;
return self->operator int64_t();
}
-double GDAPI godot_variant_as_real(const godot_variant *p_self) {
+godot_float GDAPI godot_variant_as_float(const godot_variant *p_self) {
const Variant *self = (const Variant *)p_self;
return self->operator double();
}
@@ -393,10 +376,10 @@ godot_plane GDAPI godot_variant_as_plane(const godot_variant *p_self) {
return raw_dest;
}
-godot_quat GDAPI godot_variant_as_quat(const godot_variant *p_self) {
- godot_quat raw_dest;
+godot_quaternion GDAPI godot_variant_as_quaternion(const godot_variant *p_self) {
+ godot_quaternion raw_dest;
const Variant *self = (const Variant *)p_self;
- Quat *dest = (Quat *)&raw_dest;
+ Quaternion *dest = (Quaternion *)&raw_dest;
*dest = *self;
return raw_dest;
}
@@ -417,10 +400,10 @@ godot_basis GDAPI godot_variant_as_basis(const godot_variant *p_self) {
return raw_dest;
}
-godot_transform GDAPI godot_variant_as_transform(const godot_variant *p_self) {
- godot_transform raw_dest;
+godot_transform3d GDAPI godot_variant_as_transform3d(const godot_variant *p_self) {
+ godot_transform3d raw_dest;
const Variant *self = (const Variant *)p_self;
- Transform *dest = (Transform *)&raw_dest;
+ Transform3D *dest = (Transform3D *)&raw_dest;
*dest = *self;
return raw_dest;
}
@@ -569,45 +552,204 @@ godot_packed_color_array GDAPI godot_variant_as_packed_color_array(const godot_v
return raw_dest;
}
-godot_variant GDAPI godot_variant_call(godot_variant *p_self, const godot_string *p_method, const godot_variant **p_args, const godot_int p_argcount, godot_variant_call_error *r_error) {
+void GDAPI godot_variant_destroy(godot_variant *p_self) {
Variant *self = (Variant *)p_self;
- String *method = (String *)p_method;
+ self->~Variant();
+}
+
+// Dynamic interaction.
+
+void GDAPI godot_variant_call(godot_variant *p_self, const godot_string_name *p_method, const godot_variant **p_args, const godot_int p_argcount, godot_variant *r_return, godot_variant_call_error *r_error) {
+ Variant *self = (Variant *)p_self;
+ const StringName *method = (const StringName *)p_method;
const Variant **args = (const Variant **)p_args;
- godot_variant raw_dest;
- Variant *dest = (Variant *)&raw_dest;
+ Variant ret;
Callable::CallError error;
- memnew_placement_custom(dest, Variant, Variant(self->call(*method, args, p_argcount, error)));
+ self->call(*method, args, p_argcount, ret, error);
+ memnew_placement(r_return, Variant(ret));
+
if (r_error) {
r_error->error = (godot_variant_call_error_error)error.error;
r_error->argument = error.argument;
r_error->expected = (godot_variant_type)error.expected;
}
- return raw_dest;
}
-godot_bool GDAPI godot_variant_has_method(const godot_variant *p_self, const godot_string *p_method) {
+void GDAPI godot_variant_call_with_cstring(godot_variant *p_self, const char *p_method, const godot_variant **p_args, const godot_int p_argcount, godot_variant *r_return, godot_variant_call_error *r_error) {
+ Variant *self = (Variant *)p_self;
+ const StringName method(p_method);
+ const Variant **args = (const Variant **)p_args;
+ Variant ret;
+ Callable::CallError error;
+ self->call(method, args, p_argcount, ret, error);
+ memnew_placement(r_return, Variant(ret));
+
+ if (r_error) {
+ r_error->error = (godot_variant_call_error_error)error.error;
+ r_error->argument = error.argument;
+ r_error->expected = (godot_variant_type)error.expected;
+ }
+}
+
+void GDAPI godot_variant_call_static(godot_variant_type p_type, const godot_string_name *p_method, const godot_variant **p_args, const godot_int p_argcount, godot_variant *r_return, godot_variant_call_error *r_error) {
+ Variant::Type type = (Variant::Type)p_type;
+ const StringName *method = (const StringName *)p_method;
+ const Variant **args = (const Variant **)p_args;
+ Variant ret;
+ Callable::CallError error;
+ Variant::call_static(type, *method, args, p_argcount, ret, error);
+ memnew_placement(r_return, Variant(ret));
+
+ if (r_error) {
+ r_error->error = (godot_variant_call_error_error)error.error;
+ r_error->argument = error.argument;
+ r_error->expected = (godot_variant_type)error.expected;
+ }
+}
+
+void GDAPI godot_variant_call_static_with_cstring(godot_variant_type p_type, const char *p_method, const godot_variant **p_args, const godot_int p_argcount, godot_variant *r_return, godot_variant_call_error *r_error) {
+ Variant::Type type = (Variant::Type)p_type;
+ const StringName method(p_method);
+ const Variant **args = (const Variant **)p_args;
+ Variant ret;
+ Callable::CallError error;
+ Variant::call_static(type, method, args, p_argcount, ret, error);
+ memnew_placement(r_return, Variant(ret));
+
+ if (r_error) {
+ r_error->error = (godot_variant_call_error_error)error.error;
+ r_error->argument = error.argument;
+ r_error->expected = (godot_variant_type)error.expected;
+ }
+}
+
+void GDAPI godot_variant_evaluate(godot_variant_operator p_op, const godot_variant *p_a, const godot_variant *p_b, godot_variant *r_return, bool *r_valid) {
+ Variant::Operator op = (Variant::Operator)p_op;
+ const Variant *a = (const Variant *)p_a;
+ const Variant *b = (const Variant *)p_b;
+ Variant *ret = (Variant *)r_return;
+ Variant::evaluate(op, *a, *b, *ret, *r_valid);
+}
+
+void GDAPI godot_variant_set(godot_variant *p_self, const godot_variant *p_key, const godot_variant *p_value, bool *r_valid) {
+ Variant *self = (Variant *)p_self;
+ const Variant *key = (const Variant *)p_key;
+ const Variant *value = (const Variant *)p_value;
+
+ self->set(*key, *value, r_valid);
+}
+
+void GDAPI godot_variant_set_named(godot_variant *p_self, const godot_string_name *p_key, const godot_variant *p_value, bool *r_valid) {
+ Variant *self = (Variant *)p_self;
+ const StringName *key = (const StringName *)p_key;
+ const Variant *value = (const Variant *)p_value;
+
+ self->set_named(*key, *value, *r_valid);
+}
+
+void GDAPI godot_variant_set_named_with_cstring(godot_variant *p_self, const char *p_key, const godot_variant *p_value, bool *r_valid) {
+ Variant *self = (Variant *)p_self;
+ const StringName key(p_key);
+ const Variant *value = (const Variant *)p_value;
+
+ self->set_named(key, *value, *r_valid);
+}
+
+void GDAPI godot_variant_set_keyed(godot_variant *p_self, const godot_variant *p_key, const godot_variant *p_value, bool *r_valid) {
+ Variant *self = (Variant *)p_self;
+ const Variant *key = (const Variant *)p_key;
+ const Variant *value = (const Variant *)p_value;
+
+ self->set_keyed(*key, *value, *r_valid);
+}
+
+void GDAPI godot_variant_set_indexed(godot_variant *p_self, godot_int p_index, const godot_variant *p_value, bool *r_valid, bool *r_oob) {
+ Variant *self = (Variant *)p_self;
+ const Variant *value = (const Variant *)p_value;
+
+ self->set_indexed(p_index, value, *r_valid, *r_oob);
+}
+
+godot_variant GDAPI godot_variant_get(const godot_variant *p_self, const godot_variant *p_key, bool *r_valid) {
const Variant *self = (const Variant *)p_self;
- const String *method = (const String *)p_method;
- return self->has_method(*method);
+ const Variant *key = (const Variant *)p_key;
+ Variant ret;
+
+ ret = self->get(*key, r_valid);
+ godot_variant result;
+ memnew_placement(&result, Variant(ret));
+ return result;
}
-godot_bool GDAPI godot_variant_operator_equal(const godot_variant *p_self, const godot_variant *p_other) {
+godot_variant GDAPI godot_variant_get_named(const godot_variant *p_self, const godot_string_name *p_key, bool *r_valid) {
const Variant *self = (const Variant *)p_self;
- const Variant *other = (const Variant *)p_other;
- return self->operator==(*other);
+ const StringName *key = (const StringName *)p_key;
+ Variant ret;
+
+ ret = self->get_named(*key, *r_valid);
+ godot_variant result;
+ memnew_placement(&result, Variant(ret));
+ return result;
}
-godot_bool GDAPI godot_variant_operator_less(const godot_variant *p_self, const godot_variant *p_other) {
+godot_variant GDAPI godot_variant_get_named_with_cstring(const godot_variant *p_self, const char *p_key, bool *r_valid) {
const Variant *self = (const Variant *)p_self;
- const Variant *other = (const Variant *)p_other;
- return self->operator<(*other);
+ const StringName *key = (const StringName *)p_key;
+ Variant ret;
+
+ ret = self->get_named(*key, *r_valid);
+ godot_variant result;
+ memnew_placement(&result, Variant(ret));
+ return result;
}
-uint32_t GDAPI godot_variant_hash(const godot_variant *p_self) {
+godot_variant GDAPI godot_variant_get_keyed(const godot_variant *p_self, const godot_variant *p_key, bool *r_valid) {
const Variant *self = (const Variant *)p_self;
- return self->hash();
+ const Variant *key = (const Variant *)p_key;
+ Variant ret;
+
+ ret = self->get_keyed(*key, *r_valid);
+ godot_variant result;
+ memnew_placement(&result, Variant(ret));
+ return result;
+}
+
+godot_variant GDAPI godot_variant_get_indexed(const godot_variant *p_self, godot_int p_index, bool *r_valid, bool *r_oob) {
+ const Variant *self = (const Variant *)p_self;
+ Variant ret;
+
+ ret = self->get_indexed(p_index, *r_valid, *r_oob);
+ godot_variant result;
+ memnew_placement(&result, Variant(ret));
+ return result;
}
+/// Iteration.
+bool GDAPI godot_variant_iter_init(const godot_variant *p_self, godot_variant *r_iter, bool *r_valid) {
+ const Variant *self = (const Variant *)p_self;
+ Variant *iter = (Variant *)r_iter;
+
+ return self->iter_init(*iter, *r_valid);
+}
+
+bool GDAPI godot_variant_iter_next(const godot_variant *p_self, godot_variant *r_iter, bool *r_valid) {
+ const Variant *self = (const Variant *)p_self;
+ Variant *iter = (Variant *)r_iter;
+
+ return self->iter_next(*iter, *r_valid);
+}
+
+godot_variant GDAPI godot_variant_iter_get(const godot_variant *p_self, godot_variant *r_iter, bool *r_valid) {
+ const Variant *self = (const Variant *)p_self;
+ Variant *iter = (Variant *)r_iter;
+
+ Variant result = self->iter_next(*iter, *r_valid);
+ godot_variant ret;
+ memnew_placement(&ret, Variant(result));
+ return ret;
+}
+
+/// Variant functions.
godot_bool GDAPI godot_variant_hash_compare(const godot_variant *p_self, const godot_variant *p_other) {
const Variant *self = (const Variant *)p_self;
const Variant *other = (const Variant *)p_other;
@@ -619,27 +761,511 @@ godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self) {
return self->booleanize();
}
-void GDAPI godot_variant_destroy(godot_variant *p_self) {
- Variant *self = (Variant *)p_self;
- self->~Variant();
+void GDAPI godot_variant_blend(const godot_variant *p_a, const godot_variant *p_b, float p_c, godot_variant *r_dst) {
+ const Variant *a = (const Variant *)p_a;
+ const Variant *b = (const Variant *)p_b;
+ Variant *dst = (Variant *)r_dst;
+ Variant::blend(*a, *b, p_c, *dst);
}
-// GDNative core 1.1
+void GDAPI godot_variant_interpolate(const godot_variant *p_a, const godot_variant *p_b, float p_c, godot_variant *r_dst) {
+ const Variant *a = (const Variant *)p_a;
+ const Variant *b = (const Variant *)p_b;
+ Variant *dst = (Variant *)r_dst;
+ Variant::interpolate(*a, *b, p_c, *dst);
+}
-godot_string GDAPI godot_variant_get_operator_name(godot_variant_operator p_op) {
- Variant::Operator op = (Variant::Operator)p_op;
- godot_string raw_dest;
- String *dest = (String *)&raw_dest;
- memnew_placement(dest, String(Variant::get_operator_name(op))); // operator = is overloaded by String
- return raw_dest;
+godot_variant GDAPI godot_variant_duplicate(const godot_variant *p_self, godot_bool p_deep) {
+ const Variant *self = (const Variant *)p_self;
+ Variant result = self->duplicate(p_deep);
+ godot_variant ret;
+ memnew_placement(&ret, Variant(result));
+ return ret;
}
-void GDAPI godot_variant_evaluate(godot_variant_operator p_op, const godot_variant *p_a, const godot_variant *p_b, godot_variant *r_ret, godot_bool *r_valid) {
- Variant::Operator op = (Variant::Operator)p_op;
- const Variant *a = (const Variant *)p_a;
- const Variant *b = (const Variant *)p_b;
+godot_string GDAPI godot_variant_stringify(const godot_variant *p_self) {
+ const Variant *self = (const Variant *)p_self;
+ String result = *self;
+ godot_string ret;
+ memnew_placement(&ret, String(result));
+ return ret;
+}
+
+// Discovery API
+
+/// Operators
+godot_validated_operator_evaluator GDAPI godot_variant_get_validated_operator_evaluator(godot_variant_operator p_operator, godot_variant_type p_type_a, godot_variant_type p_type_b) {
+ return (godot_validated_operator_evaluator)Variant::get_validated_operator_evaluator((Variant::Operator)p_operator, (Variant::Type)p_type_a, (Variant::Type)p_type_b);
+}
+
+godot_ptr_operator_evaluator GDAPI godot_variant_get_ptr_operator_evaluator(godot_variant_operator p_operator, godot_variant_type p_type_a, godot_variant_type p_type_b) {
+ return (godot_ptr_operator_evaluator)Variant::get_ptr_operator_evaluator((Variant::Operator)p_operator, (Variant::Type)p_type_a, (Variant::Type)p_type_b);
+}
+
+godot_variant_type GDAPI godot_variant_get_operator_return_type(godot_variant_operator p_operator, godot_variant_type p_type_a, godot_variant_type p_type_b) {
+ return (godot_variant_type)Variant::get_operator_return_type((Variant::Operator)p_operator, (Variant::Type)p_type_a, (Variant::Type)p_type_b);
+}
+
+godot_string GDAPI godot_variant_get_operator_name(godot_variant_operator p_operator) {
+ String op_name = Variant::get_operator_name((Variant::Operator)p_operator);
+ godot_string ret;
+ memnew_placement(&ret, String(op_name));
+ return ret;
+}
+
+/// Built-in Methods
+
+bool GDAPI godot_variant_has_builtin_method(godot_variant_type p_type, const godot_string_name *p_method) {
+ return Variant::has_builtin_method((Variant::Type)p_type, *((const StringName *)p_method));
+}
+
+bool GDAPI godot_variant_has_builtin_method_with_cstring(godot_variant_type p_type, const char *p_method) {
+ return Variant::has_builtin_method((Variant::Type)p_type, StringName(p_method));
+}
+
+godot_validated_builtin_method GDAPI godot_variant_get_validated_builtin_method(godot_variant_type p_type, const godot_string_name *p_method) {
+ return (godot_validated_builtin_method)Variant::get_validated_builtin_method((Variant::Type)p_type, *((const StringName *)p_method));
+}
+
+godot_validated_builtin_method GDAPI godot_variant_get_validated_builtin_method_with_cstring(godot_variant_type p_type, const char *p_method) {
+ return (godot_validated_builtin_method)Variant::get_validated_builtin_method((Variant::Type)p_type, StringName(p_method));
+}
+
+godot_ptr_builtin_method GDAPI godot_variant_get_ptr_builtin_method(godot_variant_type p_type, const godot_string_name *p_method) {
+ return (godot_ptr_builtin_method)Variant::get_ptr_builtin_method((Variant::Type)p_type, *((const StringName *)p_method));
+}
+
+godot_ptr_builtin_method GDAPI godot_variant_get_ptr_builtin_method_with_cstring(godot_variant_type p_type, const char *p_method) {
+ return (godot_ptr_builtin_method)Variant::get_ptr_builtin_method((Variant::Type)p_type, StringName(p_method));
+}
+
+int GDAPI godot_variant_get_builtin_method_argument_count(godot_variant_type p_type, const godot_string_name *p_method) {
+ return Variant::get_builtin_method_argument_count((Variant::Type)p_type, *((const StringName *)p_method));
+}
+
+int GDAPI godot_variant_get_builtin_method_argument_count_with_cstring(godot_variant_type p_type, const char *p_method) {
+ return Variant::get_builtin_method_argument_count((Variant::Type)p_type, StringName(p_method));
+}
+
+godot_variant_type GDAPI godot_variant_get_builtin_method_argument_type(godot_variant_type p_type, const godot_string_name *p_method, int p_argument) {
+ return (godot_variant_type)Variant::get_builtin_method_argument_type((Variant::Type)p_type, *((const StringName *)p_method), p_argument);
+}
+
+godot_variant_type GDAPI godot_variant_get_builtin_method_argument_type_with_cstring(godot_variant_type p_type, const char *p_method, int p_argument) {
+ return (godot_variant_type)Variant::get_builtin_method_argument_type((Variant::Type)p_type, StringName(p_method), p_argument);
+}
+
+godot_string GDAPI godot_variant_get_builtin_method_argument_name(godot_variant_type p_type, const godot_string_name *p_method, int p_argument) {
+ String name = Variant::get_builtin_method_argument_name((Variant::Type)p_type, *((const StringName *)p_method), p_argument);
+ return *(godot_string *)&name;
+}
+
+godot_string GDAPI godot_variant_get_builtin_method_argument_name_with_cstring(godot_variant_type p_type, const char *p_method, int p_argument) {
+ String name = Variant::get_builtin_method_argument_name((Variant::Type)p_type, StringName(p_method), p_argument);
+ return *(godot_string *)&name;
+}
+
+bool GDAPI godot_variant_has_builtin_method_return_value(godot_variant_type p_type, const godot_string_name *p_method) {
+ return Variant::has_builtin_method_return_value((Variant::Type)p_type, *((const StringName *)p_method));
+}
+
+bool GDAPI godot_variant_has_builtin_method_return_value_with_cstring(godot_variant_type p_type, const char *p_method) {
+ return Variant::has_builtin_method_return_value((Variant::Type)p_type, StringName(p_method));
+}
+
+godot_variant_type GDAPI godot_variant_get_builtin_method_return_type(godot_variant_type p_type, const godot_string_name *p_method) {
+ return (godot_variant_type)Variant::get_builtin_method_return_type((Variant::Type)p_type, *((const StringName *)p_method));
+}
+
+godot_variant_type GDAPI godot_variant_get_builtin_method_return_type_with_cstring(godot_variant_type p_type, const char *p_method) {
+ return (godot_variant_type)Variant::get_builtin_method_return_type((Variant::Type)p_type, StringName(p_method));
+}
+
+bool GDAPI godot_variant_is_builtin_method_const(godot_variant_type p_type, const godot_string_name *p_method) {
+ return Variant::is_builtin_method_const((Variant::Type)p_type, *((const StringName *)p_method));
+}
+
+bool GDAPI godot_variant_is_builtin_method_const_with_cstring(godot_variant_type p_type, const char *p_method) {
+ return Variant::is_builtin_method_const((Variant::Type)p_type, StringName(p_method));
+}
+
+bool GDAPI godot_variant_is_builtin_method_static(godot_variant_type p_type, const godot_string_name *p_method) {
+ return Variant::is_builtin_method_static((Variant::Type)p_type, *((const StringName *)p_method));
+}
+
+bool GDAPI godot_variant_is_builtin_method_static_with_cstring(godot_variant_type p_type, const char *p_method) {
+ return Variant::is_builtin_method_static((Variant::Type)p_type, StringName(p_method));
+}
+
+bool GDAPI godot_variant_is_builtin_method_vararg(godot_variant_type p_type, const godot_string_name *p_method) {
+ return Variant::is_builtin_method_vararg((Variant::Type)p_type, *((const StringName *)p_method));
+}
+
+bool GDAPI godot_variant_is_builtin_method_vararg_with_cstring(godot_variant_type p_type, const char *p_method) {
+ return Variant::is_builtin_method_vararg((Variant::Type)p_type, StringName(p_method));
+}
+
+int GDAPI godot_variant_get_builtin_method_count(godot_variant_type p_type) {
+ return Variant::get_builtin_method_count((Variant::Type)p_type);
+}
+
+void GDAPI godot_variant_get_builtin_method_list(godot_variant_type p_type, godot_string_name *r_list) {
+ List<StringName> list;
+ Variant::get_builtin_method_list((Variant::Type)p_type, &list);
+ int i = 0;
+ for (const StringName &E : list) {
+ memnew_placement(&r_list[i], StringName(E));
+ }
+}
+
+/// Constructors
+
+int GDAPI godot_variant_get_constructor_count(godot_variant_type p_type) {
+ return Variant::get_constructor_count((Variant::Type)p_type);
+}
+
+godot_validated_constructor GDAPI godot_variant_get_validated_constructor(godot_variant_type p_type, int p_constructor) {
+ return (godot_validated_constructor)Variant::get_validated_constructor((Variant::Type)p_type, p_constructor);
+}
+
+godot_ptr_constructor GDAPI godot_variant_get_ptr_constructor(godot_variant_type p_type, int p_constructor) {
+ return (godot_ptr_constructor)Variant::get_ptr_constructor((Variant::Type)p_type, p_constructor);
+}
+
+int GDAPI godot_variant_get_constructor_argument_count(godot_variant_type p_type, int p_constructor) {
+ return Variant::get_constructor_argument_count((Variant::Type)p_type, p_constructor);
+}
+
+godot_variant_type GDAPI godot_variant_get_constructor_argument_type(godot_variant_type p_type, int p_constructor, int p_argument) {
+ return (godot_variant_type)Variant::get_constructor_argument_type((Variant::Type)p_type, p_constructor, p_argument);
+}
+
+godot_string GDAPI godot_variant_get_constructor_argument_name(godot_variant_type p_type, int p_constructor, int p_argument) {
+ String name = Variant::get_constructor_argument_name((Variant::Type)p_type, p_constructor, p_argument);
+ godot_string ret;
+ memnew_placement(&ret, String(name));
+ return ret;
+}
+
+void GDAPI godot_variant_construct(godot_variant_type p_type, godot_variant *p_base, const godot_variant **p_args, int p_argcount, godot_variant_call_error *r_error) {
+ Variant::construct((Variant::Type)p_type, *((Variant *)p_base), (const Variant **)p_args, p_argcount, *((Callable::CallError *)r_error));
+}
+
+/// Properties.
+godot_variant_type GDAPI godot_variant_get_member_type(godot_variant_type p_type, const godot_string_name *p_member) {
+ return (godot_variant_type)Variant::get_member_type((Variant::Type)p_type, *((const StringName *)p_member));
+}
+
+godot_variant_type GDAPI godot_variant_get_member_type_with_cstring(godot_variant_type p_type, const char *p_member) {
+ return (godot_variant_type)Variant::get_member_type((Variant::Type)p_type, StringName(p_member));
+}
+
+int GDAPI godot_variant_get_member_count(godot_variant_type p_type) {
+ return Variant::get_member_count((Variant::Type)p_type);
+}
+
+void GDAPI godot_variant_get_member_list(godot_variant_type p_type, godot_string_name *r_list) {
+ List<StringName> members;
+ Variant::get_member_list((Variant::Type)p_type, &members);
+ int i = 0;
+ for (const StringName &E : members) {
+ memnew_placement(&r_list[i++], StringName(E));
+ }
+}
+
+godot_validated_setter GDAPI godot_variant_get_validated_setter(godot_variant_type p_type, const godot_string_name *p_member) {
+ return (godot_validated_setter)Variant::get_member_validated_setter((Variant::Type)p_type, *((const StringName *)p_member));
+}
+
+godot_validated_setter GDAPI godot_variant_get_validated_setter_with_cstring(godot_variant_type p_type, const char *p_member) {
+ return (godot_validated_setter)Variant::get_member_validated_setter((Variant::Type)p_type, StringName(p_member));
+}
+
+godot_validated_getter GDAPI godot_variant_get_validated_getter(godot_variant_type p_type, const godot_string_name *p_member) {
+ return (godot_validated_getter)Variant::get_member_validated_getter((Variant::Type)p_type, *((const StringName *)p_member));
+}
+
+godot_validated_getter GDAPI godot_variant_get_validated_getter_with_cstring(godot_variant_type p_type, const char *p_member) {
+ return (godot_validated_getter)Variant::get_member_validated_getter((Variant::Type)p_type, StringName(p_member));
+}
+
+godot_ptr_setter GDAPI godot_variant_get_ptr_setter(godot_variant_type p_type, const godot_string_name *p_member) {
+ return (godot_ptr_setter)Variant::get_member_ptr_setter((Variant::Type)p_type, *((const StringName *)p_member));
+}
+
+godot_ptr_setter GDAPI godot_variant_get_ptr_setter_with_cstring(godot_variant_type p_type, const char *p_member) {
+ return (godot_ptr_setter)Variant::get_member_ptr_setter((Variant::Type)p_type, StringName(p_member));
+}
+
+godot_ptr_getter GDAPI godot_variant_get_ptr_getter(godot_variant_type p_type, const godot_string_name *p_member) {
+ return (godot_ptr_getter)Variant::get_member_ptr_getter((Variant::Type)p_type, *((const StringName *)p_member));
+}
+
+godot_ptr_getter GDAPI godot_variant_get_ptr_getter_with_cstring(godot_variant_type p_type, const char *p_member) {
+ return (godot_ptr_getter)Variant::get_member_ptr_getter((Variant::Type)p_type, StringName(p_member));
+}
+
+/// Indexing.
+bool GDAPI godot_variant_has_indexing(godot_variant_type p_type) {
+ return Variant::has_indexing((Variant::Type)p_type);
+}
+
+godot_variant_type GDAPI godot_variant_get_indexed_element_type(godot_variant_type p_type) {
+ return (godot_variant_type)Variant::get_indexed_element_type((Variant::Type)p_type);
+}
+
+godot_validated_indexed_setter GDAPI godot_variant_get_validated_indexed_setter(godot_variant_type p_type) {
+ return (godot_validated_indexed_setter)Variant::get_member_validated_indexed_setter((Variant::Type)p_type);
+}
+
+godot_validated_indexed_getter GDAPI godot_variant_get_validated_indexed_getter(godot_variant_type p_type) {
+ return (godot_validated_indexed_getter)Variant::get_member_validated_indexed_getter((Variant::Type)p_type);
+}
+
+godot_ptr_indexed_setter GDAPI godot_variant_get_ptr_indexed_setter(godot_variant_type p_type) {
+ return (godot_ptr_indexed_setter)Variant::get_member_ptr_indexed_setter((Variant::Type)p_type);
+}
+
+godot_ptr_indexed_getter GDAPI godot_variant_get_ptr_indexed_getter(godot_variant_type p_type) {
+ return (godot_ptr_indexed_getter)Variant::get_member_ptr_indexed_getter((Variant::Type)p_type);
+}
+
+uint64_t GDAPI godot_variant_get_indexed_size(const godot_variant *p_self) {
+ const Variant *self = (const Variant *)p_self;
+ return self->get_indexed_size();
+}
+
+/// Keying.
+bool GDAPI godot_variant_is_keyed(godot_variant_type p_type) {
+ return Variant::is_keyed((Variant::Type)p_type);
+}
+
+godot_validated_keyed_setter GDAPI godot_variant_get_validated_keyed_setter(godot_variant_type p_type) {
+ return (godot_validated_keyed_setter)Variant::get_member_validated_keyed_setter((Variant::Type)p_type);
+}
+
+godot_validated_keyed_getter GDAPI godot_variant_get_validated_keyed_getter(godot_variant_type p_type) {
+ return (godot_validated_keyed_getter)Variant::get_member_validated_keyed_getter((Variant::Type)p_type);
+}
+
+godot_validated_keyed_checker GDAPI godot_variant_get_validated_keyed_checker(godot_variant_type p_type) {
+ return (godot_validated_keyed_checker)Variant::get_member_validated_keyed_checker((Variant::Type)p_type);
+}
+
+godot_ptr_keyed_setter GDAPI godot_variant_get_ptr_keyed_setter(godot_variant_type p_type) {
+ return (godot_ptr_keyed_setter)Variant::get_member_ptr_keyed_setter((Variant::Type)p_type);
+}
+
+godot_ptr_keyed_getter GDAPI godot_variant_get_ptr_keyed_getter(godot_variant_type p_type) {
+ return (godot_ptr_keyed_getter)Variant::get_member_ptr_keyed_getter((Variant::Type)p_type);
+}
+
+godot_ptr_keyed_checker GDAPI godot_variant_get_ptr_keyed_checker(godot_variant_type p_type) {
+ return (godot_ptr_keyed_checker)Variant::get_member_ptr_keyed_checker((Variant::Type)p_type);
+}
+
+/// Constants.
+int GDAPI godot_variant_get_constants_count(godot_variant_type p_type) {
+ return Variant::get_constants_count_for_type((Variant::Type)p_type);
+}
+
+void GDAPI godot_variant_get_constants_list(godot_variant_type p_type, godot_string_name *r_list) {
+ List<StringName> constants;
+ int i = 0;
+ Variant::get_constants_for_type((Variant::Type)p_type, &constants);
+ for (const StringName &E : constants) {
+ memnew_placement(&r_list[i++], StringName(E));
+ }
+}
+
+bool GDAPI godot_variant_has_constant(godot_variant_type p_type, const godot_string_name *p_constant) {
+ return Variant::has_constant((Variant::Type)p_type, *((const StringName *)p_constant));
+}
+
+bool GDAPI godot_variant_has_constant_with_cstring(godot_variant_type p_type, const char *p_constant) {
+ return Variant::has_constant((Variant::Type)p_type, StringName(p_constant));
+}
+
+godot_variant GDAPI godot_variant_get_constant_value(godot_variant_type p_type, const godot_string_name *p_constant) {
+ Variant constant = Variant::get_constant_value((Variant::Type)p_type, *((const StringName *)p_constant));
+ godot_variant ret;
+ memnew_placement(&ret, Variant(constant));
+ return ret;
+}
+
+godot_variant GDAPI godot_variant_get_constant_value_with_cstring(godot_variant_type p_type, const char *p_constant) {
+ Variant constant = Variant::get_constant_value((Variant::Type)p_type, StringName(p_constant));
+ godot_variant ret;
+ memnew_placement(&ret, Variant(constant));
+ return ret;
+}
+
+/// Utilities.
+bool GDAPI godot_variant_has_utility_function(const godot_string_name *p_function) {
+ return Variant::has_utility_function(*((const StringName *)p_function));
+}
+
+bool GDAPI godot_variant_has_utility_function_with_cstring(const char *p_function) {
+ return Variant::has_utility_function(StringName(p_function));
+}
+
+void GDAPI godot_variant_call_utility_function(const godot_string_name *p_function, godot_variant *r_ret, const godot_variant **p_args, int p_argument_count, godot_variant_call_error *r_error) {
+ const StringName *function = (const StringName *)p_function;
Variant *ret = (Variant *)r_ret;
- Variant::evaluate(op, *a, *b, *ret, *r_valid);
+ const Variant **args = (const Variant **)p_args;
+ Callable::CallError error;
+
+ Variant::call_utility_function(*function, ret, args, p_argument_count, error);
+
+ if (r_error) {
+ r_error->error = (godot_variant_call_error_error)error.error;
+ r_error->argument = error.argument;
+ r_error->expected = (godot_variant_type)error.expected;
+ }
+}
+
+void GDAPI godot_variant_call_utility_function_with_cstring(const char *p_function, godot_variant *r_ret, const godot_variant **p_args, int p_argument_count, godot_variant_call_error *r_error) {
+ Variant *ret = (Variant *)r_ret;
+ const Variant **args = (const Variant **)p_args;
+ Callable::CallError error;
+
+ Variant::call_utility_function(StringName(p_function), ret, args, p_argument_count, error);
+
+ if (r_error) {
+ r_error->error = (godot_variant_call_error_error)error.error;
+ r_error->argument = error.argument;
+ r_error->expected = (godot_variant_type)error.expected;
+ }
+}
+
+godot_ptr_utility_function GDAPI godot_variant_get_ptr_utility_function(const godot_string_name *p_function) {
+ return (godot_ptr_utility_function)Variant::get_ptr_utility_function(*((const StringName *)p_function));
+}
+
+godot_ptr_utility_function GDAPI godot_variant_get_ptr_utility_function_with_cstring(const char *p_function) {
+ return (godot_ptr_utility_function)Variant::get_ptr_utility_function(StringName(p_function));
+}
+
+godot_validated_utility_function GDAPI godot_variant_get_validated_utility_function(const godot_string_name *p_function) {
+ return (godot_validated_utility_function)Variant::get_validated_utility_function(*((const StringName *)p_function));
+}
+
+godot_validated_utility_function GDAPI godot_variant_get_validated_utility_function_with_cstring(const char *p_function) {
+ return (godot_validated_utility_function)Variant::get_validated_utility_function(StringName(p_function));
+}
+
+godot_variant_utility_function_type GDAPI godot_variant_get_utility_function_type(const godot_string_name *p_function) {
+ return (godot_variant_utility_function_type)Variant::get_utility_function_type(*((const StringName *)p_function));
+}
+
+godot_variant_utility_function_type GDAPI godot_variant_get_utility_function_type_with_cstring(const char *p_function) {
+ return (godot_variant_utility_function_type)Variant::get_utility_function_type(StringName(p_function));
+}
+
+int GDAPI godot_variant_get_utility_function_argument_count(const godot_string_name *p_function) {
+ return Variant::get_utility_function_argument_count(*((const StringName *)p_function));
+}
+
+int GDAPI godot_variant_get_utility_function_argument_count_with_cstring(const char *p_function) {
+ return Variant::get_utility_function_argument_count(StringName(p_function));
+}
+
+godot_variant_type GDAPI godot_variant_get_utility_function_argument_type(const godot_string_name *p_function, int p_argument) {
+ return (godot_variant_type)Variant::get_utility_function_argument_type(*((const StringName *)p_function), p_argument);
+}
+
+godot_variant_type GDAPI godot_variant_get_utility_function_argument_type_with_cstring(const char *p_function, int p_argument) {
+ return (godot_variant_type)Variant::get_utility_function_argument_type(StringName(p_function), p_argument);
+}
+
+godot_string GDAPI godot_variant_get_utility_function_argument_name(const godot_string_name *p_function, int p_argument) {
+ String argument_name = Variant::get_utility_function_argument_name(*((const StringName *)p_function), p_argument);
+ godot_string ret;
+ memnew_placement(&ret, String(argument_name));
+ return ret;
+}
+
+godot_string GDAPI godot_variant_get_utility_function_argument_name_with_cstring(const char *p_function, int p_argument) {
+ String argument_name = Variant::get_utility_function_argument_name(StringName(p_function), p_argument);
+ godot_string ret;
+ memnew_placement(&ret, String(argument_name));
+ return ret;
+}
+
+bool GDAPI godot_variant_has_utility_function_return_value(const godot_string_name *p_function) {
+ return Variant::has_utility_function_return_value(*((const StringName *)p_function));
+}
+
+bool GDAPI godot_variant_has_utility_function_return_value_with_cstring(const char *p_function) {
+ return Variant::has_utility_function_return_value(StringName(p_function));
+}
+
+godot_variant_type GDAPI godot_variant_get_utility_function_return_type(const godot_string_name *p_function) {
+ return (godot_variant_type)Variant::get_utility_function_return_type(*((const StringName *)p_function));
+}
+
+godot_variant_type GDAPI godot_variant_get_utility_function_return_type_with_cstring(const char *p_function) {
+ return (godot_variant_type)Variant::get_utility_function_return_type(StringName(p_function));
+}
+
+bool GDAPI godot_variant_is_utility_function_vararg(const godot_string_name *p_function) {
+ return Variant::is_utility_function_vararg(*((const StringName *)p_function));
+}
+
+bool GDAPI godot_variant_is_utility_function_vararg_with_cstring(const char *p_function) {
+ return Variant::is_utility_function_vararg(StringName(p_function));
+}
+
+int GDAPI godot_variant_get_utility_function_count() {
+ return Variant::get_utility_function_count();
+}
+
+void GDAPI godot_variant_get_utility_function_list(godot_string_name *r_functions) {
+ List<StringName> functions;
+ godot_string_name *func = r_functions;
+ Variant::get_utility_function_list(&functions);
+
+ for (const StringName &E : functions) {
+ memnew_placement(func++, StringName(E));
+ }
+}
+
+// Introspection.
+
+godot_variant_type GDAPI godot_variant_get_type(const godot_variant *p_self) {
+ const Variant *self = (const Variant *)p_self;
+ return (godot_variant_type)self->get_type();
+}
+
+bool GDAPI godot_variant_has_method(const godot_variant *p_self, const godot_string_name *p_method) {
+ const Variant *self = (const Variant *)p_self;
+ const StringName *method = (const StringName *)p_method;
+ return self->has_method(*method);
+}
+
+bool GDAPI godot_variant_has_member(godot_variant_type p_type, const godot_string_name *p_member) {
+ return Variant::has_member((Variant::Type)p_type, *((const StringName *)p_member));
+}
+
+bool GDAPI godot_variant_has_key(const godot_variant *p_self, const godot_variant *p_key, bool *r_valid) {
+ const Variant *self = (const Variant *)p_self;
+ const Variant *key = (const Variant *)p_key;
+ return self->has_key(*key, *r_valid);
+}
+
+godot_string GDAPI godot_variant_get_type_name(godot_variant_type p_type) {
+ String name = Variant::get_type_name((Variant::Type)p_type);
+ godot_string ret;
+ memnew_placement(&ret, String(name));
+ return ret;
+}
+
+bool GDAPI godot_variant_can_convert(godot_variant_type p_from, godot_variant_type p_to) {
+ return Variant::can_convert((Variant::Type)p_from, (Variant::Type)p_to);
+}
+
+bool GDAPI godot_variant_can_convert_strict(godot_variant_type p_from, godot_variant_type p_to) {
+ return Variant::can_convert_strict((Variant::Type)p_from, (Variant::Type)p_to);
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/vector2.cpp b/modules/gdnative/gdnative/vector2.cpp
index 1ee716df86..6a01a7ad59 100644
--- a/modules/gdnative/gdnative/vector2.cpp
+++ b/modules/gdnative/gdnative/vector2.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,430 +31,48 @@
#include "gdnative/vector2.h"
#include "core/math/vector2.h"
-#include "core/variant.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
static_assert(sizeof(godot_vector2) == sizeof(Vector2), "Vector2 size mismatch");
static_assert(sizeof(godot_vector2i) == sizeof(Vector2i), "Vector2i size mismatch");
-// Vector2
-
-void GDAPI godot_vector2_new(godot_vector2 *r_dest, const godot_real p_x, const godot_real p_y) {
- Vector2 *dest = (Vector2 *)r_dest;
- *dest = Vector2(p_x, p_y);
-}
-
-godot_string GDAPI godot_vector2_as_string(const godot_vector2 *p_self) {
- godot_string ret;
- const Vector2 *self = (const Vector2 *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_vector2i GDAPI godot_vector2_as_vector2i(const godot_vector2 *p_self) {
- godot_vector2i dest;
- const Vector2 *self = (const Vector2 *)p_self;
- *((Vector2i *)&dest) = Vector2i(*self);
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_normalized(const godot_vector2 *p_self) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- *((Vector2 *)&dest) = self->normalized();
- return dest;
-}
-
-godot_real GDAPI godot_vector2_length(const godot_vector2 *p_self) {
- const Vector2 *self = (const Vector2 *)p_self;
- return self->length();
-}
-
-godot_real GDAPI godot_vector2_angle(const godot_vector2 *p_self) {
- const Vector2 *self = (const Vector2 *)p_self;
- return self->angle();
-}
-
-godot_real GDAPI godot_vector2_length_squared(const godot_vector2 *p_self) {
- const Vector2 *self = (const Vector2 *)p_self;
- return self->length_squared();
-}
-
-godot_bool GDAPI godot_vector2_is_normalized(const godot_vector2 *p_self) {
- const Vector2 *self = (const Vector2 *)p_self;
- return self->is_normalized();
-}
-
-godot_vector2 GDAPI godot_vector2_direction_to(const godot_vector2 *p_self, const godot_vector2 *p_to) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *to = (const Vector2 *)p_to;
- *((Vector2 *)&dest) = self->direction_to(*to);
- return dest;
-}
-
-godot_real GDAPI godot_vector2_distance_to(const godot_vector2 *p_self, const godot_vector2 *p_to) {
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *to = (const Vector2 *)p_to;
- return self->distance_to(*to);
-}
-
-godot_real GDAPI godot_vector2_distance_squared_to(const godot_vector2 *p_self, const godot_vector2 *p_to) {
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *to = (const Vector2 *)p_to;
- return self->distance_squared_to(*to);
-}
-
-godot_real GDAPI godot_vector2_angle_to(const godot_vector2 *p_self, const godot_vector2 *p_to) {
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *to = (const Vector2 *)p_to;
- return self->angle_to(*to);
-}
-
-godot_real GDAPI godot_vector2_angle_to_point(const godot_vector2 *p_self, const godot_vector2 *p_to) {
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *to = (const Vector2 *)p_to;
- return self->angle_to_point(*to);
-}
-
-godot_vector2 GDAPI godot_vector2_lerp(const godot_vector2 *p_self, const godot_vector2 *p_b, const godot_real p_t) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *b = (const Vector2 *)p_b;
- *((Vector2 *)&dest) = self->lerp(*b, p_t);
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_cubic_interpolate(const godot_vector2 *p_self, const godot_vector2 *p_b, const godot_vector2 *p_pre_a, const godot_vector2 *p_post_b, const godot_real p_t) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *b = (const Vector2 *)p_b;
- const Vector2 *pre_a = (const Vector2 *)p_pre_a;
- const Vector2 *post_b = (const Vector2 *)p_post_b;
- *((Vector2 *)&dest) = self->cubic_interpolate(*b, *pre_a, *post_b, p_t);
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_move_toward(const godot_vector2 *p_self, const godot_vector2 *p_to, const godot_real p_delta) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *to = (const Vector2 *)p_to;
- *((Vector2 *)&dest) = self->move_toward(*to, p_delta);
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_rotated(const godot_vector2 *p_self, const godot_real p_phi) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
-
- *((Vector2 *)&dest) = self->rotated(p_phi);
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_tangent(const godot_vector2 *p_self) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- *((Vector2 *)&dest) = self->tangent();
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_floor(const godot_vector2 *p_self) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- *((Vector2 *)&dest) = self->floor();
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_sign(const godot_vector2 *p_self) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- *((Vector2 *)&dest) = self->sign();
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_snapped(const godot_vector2 *p_self, const godot_vector2 *p_by) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *by = (const Vector2 *)p_by;
- *((Vector2 *)&dest) = self->snapped(*by);
- return dest;
-}
-
-godot_real GDAPI godot_vector2_aspect(const godot_vector2 *p_self) {
- const Vector2 *self = (const Vector2 *)p_self;
- return self->aspect();
-}
-
-godot_real GDAPI godot_vector2_dot(const godot_vector2 *p_self, const godot_vector2 *p_with) {
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *with = (const Vector2 *)p_with;
- return self->dot(*with);
-}
-
-godot_vector2 GDAPI godot_vector2_slide(const godot_vector2 *p_self, const godot_vector2 *p_n) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *n = (const Vector2 *)p_n;
- *((Vector2 *)&dest) = self->slide(*n);
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_bounce(const godot_vector2 *p_self, const godot_vector2 *p_n) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *n = (const Vector2 *)p_n;
- *((Vector2 *)&dest) = self->bounce(*n);
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_reflect(const godot_vector2 *p_self, const godot_vector2 *p_n) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *n = (const Vector2 *)p_n;
- *((Vector2 *)&dest) = self->reflect(*n);
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_abs(const godot_vector2 *p_self) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
- *((Vector2 *)&dest) = self->abs();
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_clamped(const godot_vector2 *p_self, const godot_real p_length) {
- godot_vector2 dest;
- const Vector2 *self = (const Vector2 *)p_self;
-
- *((Vector2 *)&dest) = self->clamped(p_length);
- return dest;
-}
-
-godot_vector2 GDAPI godot_vector2_operator_add(const godot_vector2 *p_self, const godot_vector2 *p_b) {
- godot_vector2 raw_dest;
- Vector2 *dest = (Vector2 *)&raw_dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *b = (const Vector2 *)p_b;
- *dest = *self + *b;
- return raw_dest;
-}
-
-godot_vector2 GDAPI godot_vector2_operator_subtract(const godot_vector2 *p_self, const godot_vector2 *p_b) {
- godot_vector2 raw_dest;
- Vector2 *dest = (Vector2 *)&raw_dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *b = (const Vector2 *)p_b;
- *dest = *self - *b;
- return raw_dest;
-}
-
-godot_vector2 GDAPI godot_vector2_operator_multiply_vector(const godot_vector2 *p_self, const godot_vector2 *p_b) {
- godot_vector2 raw_dest;
- Vector2 *dest = (Vector2 *)&raw_dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *b = (const Vector2 *)p_b;
- *dest = *self * *b;
- return raw_dest;
-}
-
-godot_vector2 GDAPI godot_vector2_operator_multiply_scalar(const godot_vector2 *p_self, const godot_real p_b) {
- godot_vector2 raw_dest;
- Vector2 *dest = (Vector2 *)&raw_dest;
- const Vector2 *self = (const Vector2 *)p_self;
- *dest = *self * p_b;
- return raw_dest;
-}
-
-godot_vector2 GDAPI godot_vector2_operator_divide_vector(const godot_vector2 *p_self, const godot_vector2 *p_b) {
- godot_vector2 raw_dest;
- Vector2 *dest = (Vector2 *)&raw_dest;
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *b = (const Vector2 *)p_b;
- *dest = *self / *b;
- return raw_dest;
-}
-
-godot_vector2 GDAPI godot_vector2_operator_divide_scalar(const godot_vector2 *p_self, const godot_real p_b) {
- godot_vector2 raw_dest;
- Vector2 *dest = (Vector2 *)&raw_dest;
- const Vector2 *self = (const Vector2 *)p_self;
- *dest = *self / p_b;
- return raw_dest;
-}
+#ifdef __cplusplus
+extern "C" {
+#endif
-godot_bool GDAPI godot_vector2_operator_equal(const godot_vector2 *p_self, const godot_vector2 *p_b) {
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *b = (const Vector2 *)p_b;
- return *self == *b;
+void GDAPI godot_vector2_new(godot_vector2 *p_self) {
+ memnew_placement(p_self, Vector2);
}
-godot_bool GDAPI godot_vector2_operator_less(const godot_vector2 *p_self, const godot_vector2 *p_b) {
- const Vector2 *self = (const Vector2 *)p_self;
- const Vector2 *b = (const Vector2 *)p_b;
- return *self < *b;
+void GDAPI godot_vector2_new_copy(godot_vector2 *r_dest, const godot_vector2 *p_src) {
+ memnew_placement(r_dest, Vector2(*(Vector2 *)p_src));
}
-godot_vector2 GDAPI godot_vector2_operator_neg(const godot_vector2 *p_self) {
- godot_vector2 raw_dest;
- Vector2 *dest = (Vector2 *)&raw_dest;
- const Vector2 *self = (const Vector2 *)p_self;
- *dest = -(*self);
- return raw_dest;
+void GDAPI godot_vector2i_new(godot_vector2i *p_self) {
+ memnew_placement(p_self, Vector2i);
}
-void GDAPI godot_vector2_set_x(godot_vector2 *p_self, const godot_real p_x) {
- Vector2 *self = (Vector2 *)p_self;
- self->x = p_x;
+void GDAPI godot_vector2i_new_copy(godot_vector2i *r_dest, const godot_vector2i *p_src) {
+ memnew_placement(r_dest, Vector2i(*(Vector2i *)p_src));
}
-void GDAPI godot_vector2_set_y(godot_vector2 *p_self, const godot_real p_y) {
+godot_real_t GDAPI *godot_vector2_operator_index(godot_vector2 *p_self, godot_int p_index) {
Vector2 *self = (Vector2 *)p_self;
- self->y = p_y;
+ return (godot_real_t *)&self->operator[](p_index);
}
-godot_real GDAPI godot_vector2_get_x(const godot_vector2 *p_self) {
+const godot_real_t GDAPI *godot_vector2_operator_index_const(const godot_vector2 *p_self, godot_int p_index) {
const Vector2 *self = (const Vector2 *)p_self;
- return self->x;
-}
-
-godot_real GDAPI godot_vector2_get_y(const godot_vector2 *p_self) {
- const Vector2 *self = (const Vector2 *)p_self;
- return self->y;
-}
-
-// Vector2i
-
-void GDAPI godot_vector2i_new(godot_vector2i *r_dest, const godot_int p_x, const godot_int p_y) {
- Vector2i *dest = (Vector2i *)r_dest;
- *dest = Vector2i(p_x, p_y);
-}
-
-godot_string GDAPI godot_vector2i_as_string(const godot_vector2i *p_self) {
- godot_string ret;
- const Vector2i *self = (const Vector2i *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_vector2 GDAPI godot_vector2i_as_vector2(const godot_vector2i *p_self) {
- godot_vector2 dest;
- const Vector2i *self = (const Vector2i *)p_self;
- *((Vector2 *)&dest) = Vector2(*self);
- return dest;
+ return (const godot_real_t *)&self->operator[](p_index);
}
-godot_real GDAPI godot_vector2i_aspect(const godot_vector2i *p_self) {
- const Vector2i *self = (const Vector2i *)p_self;
- return self->aspect();
-}
-
-godot_vector2i GDAPI godot_vector2i_abs(const godot_vector2i *p_self) {
- godot_vector2i dest;
- const Vector2i *self = (const Vector2i *)p_self;
- *((Vector2i *)&dest) = self->abs();
- return dest;
-}
-
-godot_vector2i GDAPI godot_vector2i_sign(const godot_vector2i *p_self) {
- godot_vector2i dest;
- const Vector2i *self = (const Vector2i *)p_self;
- *((Vector2i *)&dest) = self->sign();
- return dest;
-}
-
-godot_vector2i GDAPI godot_vector2i_operator_add(const godot_vector2i *p_self, const godot_vector2i *p_b) {
- godot_vector2i raw_dest;
- Vector2i *dest = (Vector2i *)&raw_dest;
- const Vector2i *self = (const Vector2i *)p_self;
- const Vector2i *b = (const Vector2i *)p_b;
- *dest = *self + *b;
- return raw_dest;
-}
-
-godot_vector2i GDAPI godot_vector2i_operator_subtract(const godot_vector2i *p_self, const godot_vector2i *p_b) {
- godot_vector2i raw_dest;
- Vector2i *dest = (Vector2i *)&raw_dest;
- const Vector2i *self = (const Vector2i *)p_self;
- const Vector2i *b = (const Vector2i *)p_b;
- *dest = *self - *b;
- return raw_dest;
-}
-
-godot_vector2i GDAPI godot_vector2i_operator_multiply_vector(const godot_vector2i *p_self, const godot_vector2i *p_b) {
- godot_vector2i raw_dest;
- Vector2i *dest = (Vector2i *)&raw_dest;
- const Vector2i *self = (const Vector2i *)p_self;
- const Vector2i *b = (const Vector2i *)p_b;
- *dest = *self * *b;
- return raw_dest;
-}
-
-godot_vector2i GDAPI godot_vector2i_operator_multiply_scalar(const godot_vector2i *p_self, const godot_int p_b) {
- godot_vector2i raw_dest;
- Vector2i *dest = (Vector2i *)&raw_dest;
- const Vector2i *self = (const Vector2i *)p_self;
- *dest = *self * p_b;
- return raw_dest;
-}
-
-godot_vector2i GDAPI godot_vector2i_operator_divide_vector(const godot_vector2i *p_self, const godot_vector2i *p_b) {
- godot_vector2i raw_dest;
- Vector2i *dest = (Vector2i *)&raw_dest;
- const Vector2i *self = (const Vector2i *)p_self;
- const Vector2i *b = (const Vector2i *)p_b;
- *dest = *self / *b;
- return raw_dest;
-}
-
-godot_vector2i GDAPI godot_vector2i_operator_divide_scalar(const godot_vector2i *p_self, const godot_int p_b) {
- godot_vector2i raw_dest;
- Vector2i *dest = (Vector2i *)&raw_dest;
- const Vector2i *self = (const Vector2i *)p_self;
- *dest = *self / p_b;
- return raw_dest;
-}
-
-godot_bool GDAPI godot_vector2i_operator_equal(const godot_vector2i *p_self, const godot_vector2i *p_b) {
- const Vector2i *self = (const Vector2i *)p_self;
- const Vector2i *b = (const Vector2i *)p_b;
- return *self == *b;
-}
-
-godot_bool GDAPI godot_vector2i_operator_less(const godot_vector2i *p_self, const godot_vector2i *p_b) {
- const Vector2i *self = (const Vector2i *)p_self;
- const Vector2i *b = (const Vector2i *)p_b;
- return *self < *b;
-}
-
-godot_vector2i GDAPI godot_vector2i_operator_neg(const godot_vector2i *p_self) {
- godot_vector2i raw_dest;
- Vector2i *dest = (Vector2i *)&raw_dest;
- const Vector2i *self = (const Vector2i *)p_self;
- *dest = -(*self);
- return raw_dest;
-}
-
-void GDAPI godot_vector2i_set_x(godot_vector2i *p_self, const godot_int p_x) {
- Vector2i *self = (Vector2i *)p_self;
- self->x = p_x;
-}
-
-void GDAPI godot_vector2i_set_y(godot_vector2i *p_self, const godot_int p_y) {
+int32_t GDAPI *godot_vector2i_operator_index(godot_vector2i *p_self, godot_int p_index) {
Vector2i *self = (Vector2i *)p_self;
- self->y = p_y;
-}
-
-godot_int GDAPI godot_vector2i_get_x(const godot_vector2i *p_self) {
- const Vector2i *self = (const Vector2i *)p_self;
- return self->x;
+ return (int32_t *)&self->operator[](p_index);
}
-godot_int GDAPI godot_vector2i_get_y(const godot_vector2i *p_self) {
+const int32_t GDAPI *godot_vector2i_operator_index_const(const godot_vector2i *p_self, godot_int p_index) {
const Vector2i *self = (const Vector2i *)p_self;
- return self->y;
+ return (const int32_t *)&self->operator[](p_index);
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative/vector3.cpp b/modules/gdnative/gdnative/vector3.cpp
index 32cad30c17..fb426c8ac4 100644
--- a/modules/gdnative/gdnative/vector3.cpp
+++ b/modules/gdnative/gdnative/vector3.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,433 +30,49 @@
#include "gdnative/vector3.h"
-#include "core/variant.h"
-#include "core/vector.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "core/math/vector3.h"
static_assert(sizeof(godot_vector3) == sizeof(Vector3), "Vector3 size mismatch");
static_assert(sizeof(godot_vector3i) == sizeof(Vector3i), "Vector3i size mismatch");
-// Vector3
-
-void GDAPI godot_vector3_new(godot_vector3 *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_z) {
- Vector3 *dest = (Vector3 *)r_dest;
- *dest = Vector3(p_x, p_y, p_z);
-}
-
-godot_string GDAPI godot_vector3_as_string(const godot_vector3 *p_self) {
- godot_string ret;
- const Vector3 *self = (const Vector3 *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_vector3i GDAPI godot_vector3_as_vector3i(const godot_vector3 *p_self) {
- godot_vector3i dest;
- const Vector3 *self = (const Vector3 *)p_self;
- *((Vector3i *)&dest) = Vector3i(*self);
- return dest;
-}
-
-godot_int GDAPI godot_vector3_min_axis(const godot_vector3 *p_self) {
- const Vector3 *self = (const Vector3 *)p_self;
- return self->min_axis();
-}
-
-godot_int GDAPI godot_vector3_max_axis(const godot_vector3 *p_self) {
- const Vector3 *self = (const Vector3 *)p_self;
- return self->max_axis();
-}
-
-godot_real GDAPI godot_vector3_length(const godot_vector3 *p_self) {
- const Vector3 *self = (const Vector3 *)p_self;
- return self->length();
-}
-
-godot_real GDAPI godot_vector3_length_squared(const godot_vector3 *p_self) {
- const Vector3 *self = (const Vector3 *)p_self;
- return self->length_squared();
-}
-
-godot_bool GDAPI godot_vector3_is_normalized(const godot_vector3 *p_self) {
- const Vector3 *self = (const Vector3 *)p_self;
- return self->is_normalized();
-}
-
-godot_vector3 GDAPI godot_vector3_normalized(const godot_vector3 *p_self) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- *((Vector3 *)&dest) = self->normalized();
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_inverse(const godot_vector3 *p_self) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- *((Vector3 *)&dest) = self->inverse();
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_snapped(const godot_vector3 *p_self, const godot_vector3 *p_by) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *snap_axis = (const Vector3 *)p_by;
-
- *((Vector3 *)&dest) = self->snapped(*snap_axis);
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_rotated(const godot_vector3 *p_self, const godot_vector3 *p_axis, const godot_real p_phi) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *axis = (const Vector3 *)p_axis;
- *((Vector3 *)&dest) = self->rotated(*axis, p_phi);
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_lerp(const godot_vector3 *p_self, const godot_vector3 *p_b, const godot_real p_t) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- *((Vector3 *)&dest) = self->lerp(*b, p_t);
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_cubic_interpolate(const godot_vector3 *p_self, const godot_vector3 *p_b, const godot_vector3 *p_pre_a, const godot_vector3 *p_post_b, const godot_real p_t) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- const Vector3 *pre_a = (const Vector3 *)p_pre_a;
- const Vector3 *post_b = (const Vector3 *)p_post_b;
- *((Vector3 *)&dest) = self->cubic_interpolate(*b, *pre_a, *post_b, p_t);
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_move_toward(const godot_vector3 *p_self, const godot_vector3 *p_to, const godot_real p_delta) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *to = (const Vector3 *)p_to;
- *((Vector3 *)&dest) = self->move_toward(*to, p_delta);
- return dest;
-}
-
-godot_real GDAPI godot_vector3_dot(const godot_vector3 *p_self, const godot_vector3 *p_b) {
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- return self->dot(*b);
-}
-
-godot_vector3 GDAPI godot_vector3_cross(const godot_vector3 *p_self, const godot_vector3 *p_b) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- *((Vector3 *)&dest) = self->cross(*b);
- return dest;
-}
-
-godot_basis GDAPI godot_vector3_outer(const godot_vector3 *p_self, const godot_vector3 *p_b) {
- godot_basis dest;
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- *((Basis *)&dest) = self->outer(*b);
- return dest;
-}
-
-godot_basis GDAPI godot_vector3_to_diagonal_matrix(const godot_vector3 *p_self) {
- godot_basis dest;
- const Vector3 *self = (const Vector3 *)p_self;
- *((Basis *)&dest) = self->to_diagonal_matrix();
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_abs(const godot_vector3 *p_self) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- *((Vector3 *)&dest) = self->abs();
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_sign(const godot_vector3 *p_self) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- *((Vector3 *)&dest) = self->sign();
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_floor(const godot_vector3 *p_self) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- *((Vector3 *)&dest) = self->floor();
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_ceil(const godot_vector3 *p_self) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- *((Vector3 *)&dest) = self->ceil();
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_direction_to(const godot_vector3 *p_self, const godot_vector3 *p_to) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *to = (const Vector3 *)p_to;
- *((Vector3 *)&dest) = self->direction_to(*to);
- return dest;
-}
-
-godot_real GDAPI godot_vector3_distance_to(const godot_vector3 *p_self, const godot_vector3 *p_b) {
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- return self->distance_to(*b);
-}
-
-godot_real GDAPI godot_vector3_distance_squared_to(const godot_vector3 *p_self, const godot_vector3 *p_b) {
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- return self->distance_squared_to(*b);
-}
-
-godot_real GDAPI godot_vector3_angle_to(const godot_vector3 *p_self, const godot_vector3 *p_to) {
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *to = (const Vector3 *)p_to;
- return self->angle_to(*to);
-}
-
-godot_vector3 GDAPI godot_vector3_slide(const godot_vector3 *p_self, const godot_vector3 *p_n) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *n = (const Vector3 *)p_n;
- *((Vector3 *)&dest) = self->slide(*n);
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_bounce(const godot_vector3 *p_self, const godot_vector3 *p_n) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *n = (const Vector3 *)p_n;
- *((Vector3 *)&dest) = self->bounce(*n);
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_reflect(const godot_vector3 *p_self, const godot_vector3 *p_n) {
- godot_vector3 dest;
- const Vector3 *self = (const Vector3 *)p_self;
- const Vector3 *n = (const Vector3 *)p_n;
- *((Vector3 *)&dest) = self->reflect(*n);
- return dest;
-}
-
-godot_vector3 GDAPI godot_vector3_operator_add(const godot_vector3 *p_self, const godot_vector3 *p_b) {
- godot_vector3 raw_dest;
- Vector3 *dest = (Vector3 *)&raw_dest;
- Vector3 *self = (Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- *dest = *self + *b;
- return raw_dest;
-}
-
-godot_vector3 GDAPI godot_vector3_operator_subtract(const godot_vector3 *p_self, const godot_vector3 *p_b) {
- godot_vector3 raw_dest;
- Vector3 *dest = (Vector3 *)&raw_dest;
- Vector3 *self = (Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- *dest = *self - *b;
- return raw_dest;
-}
-
-godot_vector3 GDAPI godot_vector3_operator_multiply_vector(const godot_vector3 *p_self, const godot_vector3 *p_b) {
- godot_vector3 raw_dest;
- Vector3 *dest = (Vector3 *)&raw_dest;
- Vector3 *self = (Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- *dest = *self * *b;
- return raw_dest;
-}
-
-godot_vector3 GDAPI godot_vector3_operator_multiply_scalar(const godot_vector3 *p_self, const godot_real p_b) {
- godot_vector3 raw_dest;
- Vector3 *dest = (Vector3 *)&raw_dest;
- Vector3 *self = (Vector3 *)p_self;
- *dest = *self * p_b;
- return raw_dest;
-}
-
-godot_vector3 GDAPI godot_vector3_operator_divide_vector(const godot_vector3 *p_self, const godot_vector3 *p_b) {
- godot_vector3 raw_dest;
- Vector3 *dest = (Vector3 *)&raw_dest;
- Vector3 *self = (Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- *dest = *self / *b;
- return raw_dest;
-}
+#ifdef __cplusplus
+extern "C" {
+#endif
-godot_vector3 GDAPI godot_vector3_operator_divide_scalar(const godot_vector3 *p_self, const godot_real p_b) {
- godot_vector3 raw_dest;
- Vector3 *dest = (Vector3 *)&raw_dest;
- Vector3 *self = (Vector3 *)p_self;
- *dest = *self / p_b;
- return raw_dest;
+void GDAPI godot_vector3_new(godot_vector3 *p_self) {
+ memnew_placement(p_self, Vector3);
}
-godot_bool GDAPI godot_vector3_operator_equal(const godot_vector3 *p_self, const godot_vector3 *p_b) {
- Vector3 *self = (Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- return *self == *b;
+void GDAPI godot_vector3_new_copy(godot_vector3 *r_dest, const godot_vector3 *p_src) {
+ memnew_placement(r_dest, Vector3(*(Vector3 *)p_src));
}
-godot_bool GDAPI godot_vector3_operator_less(const godot_vector3 *p_self, const godot_vector3 *p_b) {
- Vector3 *self = (Vector3 *)p_self;
- const Vector3 *b = (const Vector3 *)p_b;
- return *self < *b;
+void GDAPI godot_vector3i_new(godot_vector3i *p_self) {
+ memnew_placement(p_self, Vector3i);
}
-godot_vector3 GDAPI godot_vector3_operator_neg(const godot_vector3 *p_self) {
- godot_vector3 raw_dest;
- Vector3 *dest = (Vector3 *)&raw_dest;
- const Vector3 *self = (const Vector3 *)p_self;
- *dest = -(*self);
- return raw_dest;
+void GDAPI godot_vector3i_new_copy(godot_vector3i *r_dest, const godot_vector3i *p_src) {
+ memnew_placement(r_dest, Vector3i(*(Vector3i *)p_src));
}
-void GDAPI godot_vector3_set_axis(godot_vector3 *p_self, const godot_vector3_axis p_axis, const godot_real p_val) {
+godot_real_t GDAPI *godot_vector3_operator_index(godot_vector3 *p_self, godot_int p_index) {
Vector3 *self = (Vector3 *)p_self;
- self->set_axis(p_axis, p_val);
+ return (godot_real_t *)&self->operator[](p_index);
}
-godot_real GDAPI godot_vector3_get_axis(const godot_vector3 *p_self, const godot_vector3_axis p_axis) {
+const godot_real_t GDAPI *godot_vector3_operator_index_const(const godot_vector3 *p_self, godot_int p_index) {
const Vector3 *self = (const Vector3 *)p_self;
- return self->get_axis(p_axis);
-}
-
-// Vector3i
-
-void GDAPI godot_vector3i_new(godot_vector3i *r_dest, const godot_int p_x, const godot_int p_y, const godot_int p_z) {
- Vector3i *dest = (Vector3i *)r_dest;
- *dest = Vector3i(p_x, p_y, p_z);
-}
-
-godot_string GDAPI godot_vector3i_as_string(const godot_vector3i *p_self) {
- godot_string ret;
- const Vector3i *self = (const Vector3i *)p_self;
- memnew_placement(&ret, String(*self));
- return ret;
-}
-
-godot_vector3 GDAPI godot_vector3i_as_vector3(const godot_vector3i *p_self) {
- godot_vector3 dest;
- const Vector3i *self = (const Vector3i *)p_self;
- *((Vector3 *)&dest) = Vector3(*self);
- return dest;
-}
-
-godot_int GDAPI godot_vector3i_min_axis(const godot_vector3i *p_self) {
- const Vector3i *self = (const Vector3i *)p_self;
- return self->min_axis();
-}
-
-godot_int GDAPI godot_vector3i_max_axis(const godot_vector3i *p_self) {
- const Vector3i *self = (const Vector3i *)p_self;
- return self->max_axis();
-}
-
-godot_vector3i GDAPI godot_vector3i_abs(const godot_vector3i *p_self) {
- godot_vector3i dest;
- const Vector3i *self = (const Vector3i *)p_self;
- *((Vector3i *)&dest) = self->abs();
- return dest;
-}
-
-godot_vector3i GDAPI godot_vector3i_sign(const godot_vector3i *p_self) {
- godot_vector3i dest;
- const Vector3i *self = (const Vector3i *)p_self;
- *((Vector3i *)&dest) = self->sign();
- return dest;
-}
-
-godot_vector3i GDAPI godot_vector3i_operator_add(const godot_vector3i *p_self, const godot_vector3i *p_b) {
- godot_vector3i raw_dest;
- Vector3i *dest = (Vector3i *)&raw_dest;
- Vector3i *self = (Vector3i *)p_self;
- const Vector3i *b = (const Vector3i *)p_b;
- *dest = *self + *b;
- return raw_dest;
-}
-
-godot_vector3i GDAPI godot_vector3i_operator_subtract(const godot_vector3i *p_self, const godot_vector3i *p_b) {
- godot_vector3i raw_dest;
- Vector3i *dest = (Vector3i *)&raw_dest;
- Vector3i *self = (Vector3i *)p_self;
- const Vector3i *b = (const Vector3i *)p_b;
- *dest = *self - *b;
- return raw_dest;
-}
-
-godot_vector3i GDAPI godot_vector3i_operator_multiply_vector(const godot_vector3i *p_self, const godot_vector3i *p_b) {
- godot_vector3i raw_dest;
- Vector3i *dest = (Vector3i *)&raw_dest;
- Vector3i *self = (Vector3i *)p_self;
- const Vector3i *b = (const Vector3i *)p_b;
- *dest = *self * *b;
- return raw_dest;
-}
-
-godot_vector3i GDAPI godot_vector3i_operator_multiply_scalar(const godot_vector3i *p_self, const godot_int p_b) {
- godot_vector3i raw_dest;
- Vector3i *dest = (Vector3i *)&raw_dest;
- Vector3i *self = (Vector3i *)p_self;
- *dest = *self * p_b;
- return raw_dest;
-}
-
-godot_vector3i GDAPI godot_vector3i_operator_divide_vector(const godot_vector3i *p_self, const godot_vector3i *p_b) {
- godot_vector3i raw_dest;
- Vector3i *dest = (Vector3i *)&raw_dest;
- Vector3i *self = (Vector3i *)p_self;
- const Vector3i *b = (const Vector3i *)p_b;
- *dest = *self / *b;
- return raw_dest;
-}
-
-godot_vector3i GDAPI godot_vector3i_operator_divide_scalar(const godot_vector3i *p_self, const godot_int p_b) {
- godot_vector3i raw_dest;
- Vector3i *dest = (Vector3i *)&raw_dest;
- Vector3i *self = (Vector3i *)p_self;
- *dest = *self / p_b;
- return raw_dest;
-}
-
-godot_bool GDAPI godot_vector3i_operator_equal(const godot_vector3i *p_self, const godot_vector3i *p_b) {
- Vector3i *self = (Vector3i *)p_self;
- const Vector3i *b = (const Vector3i *)p_b;
- return *self == *b;
-}
-
-godot_bool GDAPI godot_vector3i_operator_less(const godot_vector3i *p_self, const godot_vector3i *p_b) {
- Vector3i *self = (Vector3i *)p_self;
- const Vector3i *b = (const Vector3i *)p_b;
- return *self < *b;
-}
-
-godot_vector3i GDAPI godot_vector3i_operator_neg(const godot_vector3i *p_self) {
- godot_vector3i raw_dest;
- Vector3i *dest = (Vector3i *)&raw_dest;
- const Vector3i *self = (const Vector3i *)p_self;
- *dest = -(*self);
- return raw_dest;
+ return (const godot_real_t *)&self->operator[](p_index);
}
-void GDAPI godot_vector3i_set_axis(godot_vector3i *p_self, const godot_vector3_axis p_axis, const godot_int p_val) {
+int32_t GDAPI *godot_vector3i_operator_index(godot_vector3i *p_self, godot_int p_index) {
Vector3i *self = (Vector3i *)p_self;
- self->set_axis(p_axis, p_val);
+ return (int32_t *)&self->operator[](p_index);
}
-godot_int GDAPI godot_vector3i_get_axis(const godot_vector3i *p_self, const godot_vector3_axis p_axis) {
+const int32_t GDAPI *godot_vector3i_operator_index_const(const godot_vector3i *p_self, godot_int p_index) {
const Vector3i *self = (const Vector3i *)p_self;
- return self->get_axis(p_axis);
+ return (const int32_t *)&self->operator[](p_index);
}
#ifdef __cplusplus
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index 82bfbd23de..cf1c7dc01f 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -1,7627 +1,5108 @@
{
- "core": {
- "type": "CORE",
- "version": {
- "major": 4,
- "minor": 0
- },
- "next": null,
- "api": [
- {
- "name": "godot_aabb_new",
- "return_type": "void",
- "arguments": [
- ["godot_aabb *", "r_dest"],
- ["const godot_vector3 *", "p_pos"],
- ["const godot_vector3 *", "p_size"]
- ]
- },
- {
- "name": "godot_aabb_get_position",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_set_position",
- "return_type": "void",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_v"]
- ]
- },
- {
- "name": "godot_aabb_get_size",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_set_size",
- "return_type": "void",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_v"]
- ]
- },
- {
- "name": "godot_aabb_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_abs",
- "return_type": "godot_aabb",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_get_area",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_has_no_area",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_has_no_surface",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_intersects",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_aabb *", "p_with"]
- ]
- },
- {
- "name": "godot_aabb_encloses",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_aabb *", "p_with"]
- ]
- },
- {
- "name": "godot_aabb_merge",
- "return_type": "godot_aabb",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_aabb *", "p_with"]
- ]
- },
- {
- "name": "godot_aabb_intersection",
- "return_type": "godot_aabb",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_aabb *", "p_with"]
- ]
- },
- {
- "name": "godot_aabb_intersects_plane",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_plane *", "p_plane"]
- ]
- },
- {
- "name": "godot_aabb_intersects_segment",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_from"],
- ["const godot_vector3 *", "p_to"]
- ]
- },
- {
- "name": "godot_aabb_has_point",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_point"]
- ]
- },
- {
- "name": "godot_aabb_get_support",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_dir"]
- ]
- },
- {
- "name": "godot_aabb_get_longest_axis",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_get_longest_axis_index",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_get_longest_axis_size",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_get_shortest_axis",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_get_shortest_axis_index",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_get_shortest_axis_size",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_aabb *", "p_self"]
- ]
- },
- {
- "name": "godot_aabb_expand",
- "return_type": "godot_aabb",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_vector3 *", "p_to_point"]
- ]
- },
- {
- "name": "godot_aabb_grow",
- "return_type": "godot_aabb",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_real", "p_by"]
- ]
- },
- {
- "name": "godot_aabb_get_endpoint",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_aabb_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_aabb *", "p_self"],
- ["const godot_aabb *", "p_b"]
- ]
- },
- {
- "name": "godot_array_new",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "r_dest"]
- ]
- },
- {
- "name": "godot_array_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_array *", "p_src"]
- ]
- },
- {
- "name": "godot_array_new_packed_color_array",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_color_array *", "p_pca"]
- ]
- },
- {
- "name": "godot_array_new_packed_vector3_array",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_vector3_array *", "p_pv3a"]
- ]
- },
- {
- "name": "godot_array_new_packed_vector2_array",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_vector2_array *", "p_pv2a"]
- ]
- },
- {
- "name": "godot_array_new_packed_string_array",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_string_array *", "p_psa"]
- ]
- },
- {
- "name": "godot_array_new_packed_float32_array",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_float32_array *", "p_pra"]
- ]
- },
- {
- "name": "godot_array_new_packed_float64_array",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_float64_array *", "p_pra"]
- ]
- },
- {
- "name": "godot_array_new_packed_int32_array",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_int32_array *", "p_pia"]
- ]
- },
- {
- "name": "godot_array_new_packed_int64_array",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_int64_array *", "p_pia"]
- ]
- },
- {
- "name": "godot_array_new_packed_byte_array",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "r_dest"],
- ["const godot_packed_byte_array *", "p_pba"]
- ]
- },
- {
- "name": "godot_array_set",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const godot_variant *", "p_value"]
- ]
- },
- {
- "name": "godot_array_get",
- "return_type": "godot_variant",
- "arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_array_operator_index",
- "return_type": "godot_variant *",
- "arguments": [
- ["godot_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_array_operator_index_const",
- "return_type": "const godot_variant *",
- "arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_array_append",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
- ]
- },
- {
- "name": "godot_array_clear",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_count",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
- ]
- },
- {
- "name": "godot_array_duplicate",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_bool", "p_deep"]
- ]
- },
- {
- "name": "godot_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_erase",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
- ]
- },
- {
- "name": "godot_array_front",
- "return_type": "godot_variant",
- "arguments": [
- ["const godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_back",
- "return_type": "godot_variant",
- "arguments": [
- ["const godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_find",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_variant *", "p_what"],
- ["const godot_int", "p_from"]
- ]
- },
- {
- "name": "godot_array_find_last",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_variant *", "p_what"]
- ]
- },
- {
- "name": "godot_array_has",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
- ]
- },
- {
- "name": "godot_array_hash",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_insert",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"],
- ["const godot_int", "p_pos"],
- ["const godot_variant *", "p_value"]
- ]
- },
- {
- "name": "godot_array_invert",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_max",
- "return_type": "godot_variant",
- "arguments": [
- ["const godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_min",
- "return_type": "godot_variant",
- "arguments": [
- ["const godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_pop_back",
- "return_type": "godot_variant",
- "arguments": [
- ["godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_pop_front",
- "return_type": "godot_variant",
- "arguments": [
- ["godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_push_back",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
- ]
- },
- {
- "name": "godot_array_push_front",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"]
- ]
- },
- {
- "name": "godot_array_remove",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_array_resize",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"],
- ["const godot_int", "p_size"]
- ]
- },
- {
- "name": "godot_array_rfind",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_variant *", "p_what"],
- ["const godot_int", "p_from"]
- ]
- },
- {
- "name": "godot_array_size",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_shuffle",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_slice",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_array *", "p_self"],
- ["const godot_int", "p_begin"],
- ["const godot_int", "p_end"],
- ["const godot_int", "p_step"],
- ["const godot_bool", "p_deep"]
- ]
- },
- {
- "name": "godot_array_sort",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_array_sort_custom",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"],
- ["godot_object *", "p_obj"],
- ["const godot_string *", "p_func"]
- ]
- },
- {
- "name": "godot_array_bsearch",
- "return_type": "godot_int",
- "arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"],
- ["const godot_bool", "p_before"]
- ]
- },
- {
- "name": "godot_array_bsearch_custom",
- "return_type": "godot_int",
- "arguments": [
- ["godot_array *", "p_self"],
- ["const godot_variant *", "p_value"],
- ["godot_object *", "p_obj"],
- ["const godot_string *", "p_func"],
- ["const godot_bool", "p_before"]
- ]
- },
- {
- "name": "godot_array_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_array *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_new_with_rows",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "r_dest"],
- ["const godot_vector3 *", "p_x_axis"],
- ["const godot_vector3 *", "p_y_axis"],
- ["const godot_vector3 *", "p_z_axis"]
- ]
- },
- {
- "name": "godot_basis_new_with_axis_and_angle",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "r_dest"],
- ["const godot_vector3 *", "p_axis"],
- ["const godot_real", "p_phi"]
- ]
- },
- {
- "name": "godot_basis_new_with_euler",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "r_dest"],
- ["const godot_vector3 *", "p_euler"]
- ]
- },
- {
- "name": "godot_basis_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_basis *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_inverse",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_basis *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_transposed",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_basis *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_orthonormalized",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_basis *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_determinant",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_basis *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_rotated",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_vector3 *", "p_axis"],
- ["const godot_real", "p_phi"]
- ]
- },
- {
- "name": "godot_basis_scaled",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_vector3 *", "p_scale"]
- ]
- },
- {
- "name": "godot_basis_get_scale",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_basis *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_get_euler",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_basis *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_tdotx",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_vector3 *", "p_with"]
- ]
- },
- {
- "name": "godot_basis_tdoty",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_vector3 *", "p_with"]
- ]
- },
- {
- "name": "godot_basis_tdotz",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_vector3 *", "p_with"]
- ]
- },
- {
- "name": "godot_basis_xform",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_vector3 *", "p_v"]
- ]
- },
- {
- "name": "godot_basis_xform_inv",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_vector3 *", "p_v"]
- ]
- },
- {
- "name": "godot_basis_get_orthogonal_index",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_basis *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_new",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "r_dest"]
- ]
- },
- {
- "name": "godot_basis_new_with_euler_quat",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "r_dest"],
- ["const godot_quat *", "p_euler"]
- ]
- },
- {
- "name": "godot_basis_get_elements",
- "return_type": "void",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["godot_vector3 *", "p_elements"]
- ]
- },
- {
- "name": "godot_basis_get_axis",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_int", "p_axis"]
- ]
- },
- {
- "name": "godot_basis_set_axis",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "p_self"],
- ["const godot_int", "p_axis"],
- ["const godot_vector3 *", "p_value"]
- ]
- },
- {
- "name": "godot_basis_get_row",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_int", "p_row"]
- ]
- },
- {
- "name": "godot_basis_set_row",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "p_self"],
- ["const godot_int", "p_row"],
- ["const godot_vector3 *", "p_value"]
- ]
- },
- {
- "name": "godot_basis_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_basis *", "p_b"]
- ]
- },
- {
- "name": "godot_basis_operator_add",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_basis *", "p_b"]
- ]
- },
- {
- "name": "godot_basis_operator_subtract",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_basis *", "p_b"]
- ]
- },
- {
- "name": "godot_basis_operator_multiply_vector",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_basis *", "p_b"]
- ]
- },
- {
- "name": "godot_basis_operator_multiply_scalar",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_real", "p_b"]
- ]
- },
- {
- "name": "godot_basis_slerp",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_basis *", "p_self"],
- ["const godot_basis *", "p_b"],
- ["const godot_real", "p_t"]
- ]
- },
- {
- "name": "godot_basis_get_quat",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_basis *", "p_self"]
- ]
- },
- {
- "name": "godot_basis_set_quat",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "p_self"],
- ["const godot_quat *", "p_quat"]
- ]
- },
- {
- "name": "godot_basis_set_axis_angle_scale",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "p_self"],
- ["const godot_vector3 *", "p_axis"],
- ["godot_real", "p_phi"],
- ["const godot_vector3 *", "p_scale"]
- ]
- },
- {
- "name": "godot_basis_set_euler_scale",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "p_self"],
- ["const godot_vector3 *", "p_euler"],
- ["const godot_vector3 *", "p_scale"]
- ]
- },
- {
- "name": "godot_basis_set_quat_scale",
- "return_type": "void",
- "arguments": [
- ["godot_basis *", "p_self"],
- ["const godot_quat *", "p_quat"],
- ["const godot_vector3 *", "p_scale"]
- ]
- },
- {
- "name": "godot_callable_new_with_object",
- "return_type": "void",
- "arguments": [
- ["godot_callable *", "r_dest"],
- ["const godot_object *", "p_object"],
- ["const godot_string_name *", "p_method"]
- ]
- },
- {
- "name": "godot_callable_new_with_object_id",
- "return_type": "void",
- "arguments": [
- ["godot_callable *", "r_dest"],
- ["uint64_t", "p_objectid"],
- ["const godot_string_name *", "p_method"]
- ]
- },
- {
- "name": "godot_callable_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_callable *", "r_dest"],
- ["const godot_callable *", "p_src"]
- ]
- },
- {
- "name": "godot_callable_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_callable *", "p_self"]
- ]
- },
- {
- "name": "godot_callable_call",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_callable *", "p_self"],
- ["const godot_variant **", "p_arguments"],
- ["godot_int", "p_argcount"],
- ["godot_variant *", "r_return_value"]
- ]
- },
- {
- "name": "godot_callable_call_deferred",
- "return_type": "void",
- "arguments": [
- ["const godot_callable *", "p_self"],
- ["const godot_variant **", "p_arguments"],
- ["godot_int", "p_argcount"]
- ]
- },
- {
- "name": "godot_callable_is_null",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_callable *", "p_self"]
- ]
- },
- {
- "name": "godot_callable_is_custom",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_callable *", "p_self"]
- ]
- },
- {
- "name": "godot_callable_is_standard",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_callable *", "p_self"]
- ]
- },
- {
- "name": "godot_callable_get_object",
- "return_type": "godot_object *",
- "arguments": [
- ["const godot_callable *", "p_self"]
- ]
- },
- {
- "name": "godot_callable_get_object_id",
- "return_type": "uint64_t",
- "arguments": [
- ["const godot_callable *", "p_self"]
- ]
- },
- {
- "name": "godot_callable_get_method",
- "return_type": "godot_string_name",
- "arguments": [
- ["const godot_callable *", "p_self"]
- ]
- },
- {
- "name": "godot_callable_hash",
- "return_type": "uint32_t",
- "arguments": [
- ["const godot_callable *", "p_self"]
- ]
- },
- {
- "name": "godot_callable_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_callable *", "p_self"]
- ]
- },
- {
- "name": "godot_callable_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_callable *", "p_self"],
- ["const godot_callable *", "p_other"]
- ]
- },
- {
- "name": "godot_callable_operator_less",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_callable *", "p_self"],
- ["const godot_callable *", "p_other"]
- ]
- },
- {
- "name": "godot_signal_new_with_object",
- "return_type": "void",
- "arguments": [
- ["godot_signal *", "r_dest"],
- ["const godot_object *", "p_object"],
- ["const godot_string_name *", "p_method"]
- ]
- },
- {
- "name": "godot_signal_new_with_object_id",
- "return_type": "void",
- "arguments": [
- ["godot_signal *", "r_dest"],
- ["uint64_t", "p_objectid"],
- ["const godot_string_name *", "p_method"]
- ]
- },
- {
- "name": "godot_signal_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_signal *", "r_dest"],
- ["const godot_signal *", "p_src"]
- ]
- },
- {
- "name": "godot_signal_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_signal *", "p_self"]
- ]
- },
- {
- "name": "godot_signal_emit",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_signal *", "p_self"],
- ["const godot_variant **", "p_arguments"],
- ["godot_int", "p_argcount"]
- ]
- },
- {
- "name": "godot_signal_connect",
- "return_type": "godot_int",
- "arguments": [
- ["godot_signal *", "p_self"],
- ["const godot_callable *", "p_callable"],
- ["const godot_array *", "p_binds"],
- ["uint32_t", "p_flags"]
- ]
- },
- {
- "name": "godot_signal_disconnect",
- "return_type": "void",
- "arguments": [
- ["godot_signal *", "p_self"],
- ["const godot_callable *", "p_callable"]
- ]
- },
- {
- "name": "godot_signal_is_null",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_signal *", "p_self"]
- ]
- },
- {
- "name": "godot_signal_is_connected",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_signal *", "p_self"],
- ["const godot_callable *", "p_callable"]
- ]
- },
- {
- "name": "godot_signal_get_connections",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_signal *", "p_self"]
- ]
- },
- {
- "name": "godot_signal_get_object",
- "return_type": "godot_object *",
- "arguments": [
- ["const godot_signal *", "p_self"]
- ]
- },
- {
- "name": "godot_signal_get_object_id",
- "return_type": "uint64_t",
- "arguments": [
- ["const godot_signal *", "p_self"]
- ]
- },
- {
- "name": "godot_signal_get_name",
- "return_type": "godot_string_name",
- "arguments": [
- ["const godot_signal *", "p_self"]
- ]
- },
- {
- "name": "godot_signal_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_signal *", "p_self"]
- ]
- },
- {
- "name": "godot_signal_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_signal *", "p_self"],
- ["const godot_signal *", "p_other"]
- ]
- },
- {
- "name": "godot_signal_operator_less",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_signal *", "p_self"],
- ["const godot_signal *", "p_other"]
- ]
- },
- {
- "name": "godot_color_new_rgba",
- "return_type": "void",
- "arguments": [
- ["godot_color *", "r_dest"],
- ["const godot_real", "p_r"],
- ["const godot_real", "p_g"],
- ["const godot_real", "p_b"],
- ["const godot_real", "p_a"]
- ]
- },
- {
- "name": "godot_color_new_rgb",
- "return_type": "void",
- "arguments": [
- ["godot_color *", "r_dest"],
- ["const godot_real", "p_r"],
- ["const godot_real", "p_g"],
- ["const godot_real", "p_b"]
- ]
- },
- {
- "name": "godot_color_get_r",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_set_r",
- "return_type": "void",
- "arguments": [
- ["godot_color *", "p_self"],
- ["const godot_real", "r"]
- ]
- },
- {
- "name": "godot_color_get_g",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_set_g",
- "return_type": "void",
- "arguments": [
- ["godot_color *", "p_self"],
- ["const godot_real", "g"]
- ]
- },
- {
- "name": "godot_color_get_b",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_set_b",
- "return_type": "void",
- "arguments": [
- ["godot_color *", "p_self"],
- ["const godot_real", "b"]
- ]
- },
- {
- "name": "godot_color_get_a",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_set_a",
- "return_type": "void",
- "arguments": [
- ["godot_color *", "p_self"],
- ["const godot_real", "a"]
- ]
- },
- {
- "name": "godot_color_get_h",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_get_s",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_get_v",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_to_rgba32",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_to_argb32",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_inverted",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_contrasted",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_lerp",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_color *", "p_b"],
- ["const godot_real", "p_t"]
- ]
- },
- {
- "name": "godot_color_blend",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_color *", "p_over"]
- ]
- },
- {
- "name": "godot_color_to_html",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_bool", "p_with_alpha"]
- ]
- },
- {
- "name": "godot_color_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_color *", "p_b"]
- ]
- },
- {
- "name": "godot_color_operator_less",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_color *", "p_b"]
- ]
- },
- {
- "name": "godot_color_to_abgr32",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_to_abgr64",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_to_argb64",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_to_rgba64",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_color *", "p_self"]
- ]
- },
- {
- "name": "godot_color_darkened",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_real", "p_amount"]
- ]
- },
- {
- "name": "godot_color_from_hsv",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_real", "p_h"],
- ["const godot_real", "p_s"],
- ["const godot_real", "p_v"],
- ["const godot_real", "p_a"]
- ]
- },
- {
- "name": "godot_color_lightened",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_color *", "p_self"],
- ["const godot_real", "p_amount"]
- ]
- },
- {
- "name": "godot_dictionary_new",
- "return_type": "void",
- "arguments": [
- ["godot_dictionary *", "r_dest"]
- ]
- },
- {
- "name": "godot_dictionary_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_dictionary *", "r_dest"],
- ["const godot_dictionary *", "p_src"]
- ]
- },
- {
- "name": "godot_dictionary_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_dictionary *", "p_self"]
- ]
- },
- {
- "name": "godot_dictionary_size",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_dictionary *", "p_self"]
- ]
- },
- {
- "name": "godot_dictionary_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_dictionary *", "p_self"]
- ]
- },
- {
- "name": "godot_dictionary_clear",
- "return_type": "void",
- "arguments": [
- ["godot_dictionary *", "p_self"]
- ]
- },
- {
- "name": "godot_dictionary_has",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
- ]
- },
- {
- "name": "godot_dictionary_has_all",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_array *", "p_keys"]
- ]
- },
- {
- "name": "godot_dictionary_erase",
- "return_type": "void",
- "arguments": [
- ["godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
- ]
- },
- {
- "name": "godot_dictionary_hash",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_dictionary *", "p_self"]
- ]
- },
- {
- "name": "godot_dictionary_keys",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_dictionary *", "p_self"]
- ]
- },
- {
- "name": "godot_dictionary_values",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_dictionary *", "p_self"]
- ]
- },
- {
- "name": "godot_dictionary_get",
- "return_type": "godot_variant",
- "arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
- ]
- },
- {
- "name": "godot_dictionary_set",
- "return_type": "void",
- "arguments": [
- ["godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"],
- ["const godot_variant *", "p_value"]
- ]
- },
- {
- "name": "godot_dictionary_operator_index",
- "return_type": "godot_variant *",
- "arguments": [
- ["godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
- ]
- },
- {
- "name": "godot_dictionary_operator_index_const",
- "return_type": "const godot_variant *",
- "arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
- ]
- },
- {
- "name": "godot_dictionary_next",
- "return_type": "godot_variant *",
- "arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
- ]
- },
- {
- "name": "godot_dictionary_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_dictionary *", "p_b"]
- ]
- },
- {
- "name": "godot_dictionary_to_json",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_dictionary *", "p_self"]
- ]
- },
- {
- "name": "godot_dictionary_duplicate",
- "return_type": "godot_dictionary",
- "arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_bool", "p_deep"]
- ]
- },
- {
- "name": "godot_dictionary_get_with_default",
- "return_type": "godot_variant",
- "arguments": [
- ["const godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"],
- ["const godot_variant *", "p_default"]
- ]
- },
- {
- "name": "godot_dictionary_erase_with_return",
- "return_type": "bool",
- "arguments": [
- ["godot_dictionary *", "p_self"],
- ["const godot_variant *", "p_key"]
- ]
- },
- {
- "name": "godot_node_path_new",
- "return_type": "void",
- "arguments": [
- ["godot_node_path *", "r_dest"],
- ["const godot_string *", "p_from"]
- ]
- },
- {
- "name": "godot_node_path_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_node_path *", "r_dest"],
- ["const godot_node_path *", "p_src"]
- ]
- },
- {
- "name": "godot_node_path_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_node_path *", "p_self"]
- ]
- },
- {
- "name": "godot_node_path_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_node_path *", "p_self"]
- ]
- },
- {
- "name": "godot_node_path_is_absolute",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_node_path *", "p_self"]
- ]
- },
- {
- "name": "godot_node_path_get_name_count",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_node_path *", "p_self"]
- ]
- },
- {
- "name": "godot_node_path_get_name",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_node_path *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_node_path_get_subname_count",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_node_path *", "p_self"]
- ]
- },
- {
- "name": "godot_node_path_get_subname",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_node_path *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_node_path_get_concatenated_subnames",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_node_path *", "p_self"]
- ]
- },
- {
- "name": "godot_node_path_is_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_node_path *", "p_self"]
- ]
- },
- {
- "name": "godot_node_path_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_node_path *", "p_self"],
- ["const godot_node_path *", "p_b"]
- ]
- },
- {
- "name": "godot_node_path_get_as_property_path",
- "return_type": "godot_node_path",
- "arguments": [
- ["const godot_node_path *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_byte_array_new",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "r_dest"]
- ]
- },
- {
- "name": "godot_packed_byte_array_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "r_dest"],
- ["const godot_packed_byte_array *", "p_src"]
- ]
- },
- {
- "name": "godot_packed_byte_array_new_with_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "r_dest"],
- ["const godot_array *", "p_a"]
- ]
- },
- {
- "name": "godot_packed_byte_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_byte_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_byte_array_append",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"],
- ["const uint8_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_byte_array_append_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"],
- ["const godot_packed_byte_array *", "p_array"]
- ]
- },
- {
- "name": "godot_packed_byte_array_insert",
- "return_type": "godot_error",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const uint8_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_byte_array_has",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"],
- ["const uint8_t", "p_value"]
- ]
- },
- {
- "name": "godot_packed_byte_array_sort",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_byte_array_invert",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_byte_array_push_back",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"],
- ["const uint8_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_byte_array_remove",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_byte_array_resize",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"],
- ["const godot_int", "p_size"]
- ]
- },
- {
- "name": "godot_packed_byte_array_ptr",
- "return_type": "const uint8_t *",
- "arguments": [
- ["const godot_packed_byte_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_byte_array_ptrw",
- "return_type": "uint8_t *",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_byte_array_set",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const uint8_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_byte_array_get",
- "return_type": "uint8_t",
- "arguments": [
- ["const godot_packed_byte_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_byte_array_size",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_packed_byte_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_byte_array_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_byte_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int32_array_new",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "r_dest"]
- ]
- },
- {
- "name": "godot_packed_int32_array_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "r_dest"],
- ["const godot_packed_int32_array *", "p_src"]
- ]
- },
- {
- "name": "godot_packed_int32_array_new_with_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "r_dest"],
- ["const godot_array *", "p_a"]
- ]
- },
- {
- "name": "godot_packed_int32_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_int32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int32_array_append",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"],
- ["const int32_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_int32_array_append_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"],
- ["const godot_packed_int32_array *", "p_array"]
- ]
- },
- {
- "name": "godot_packed_int32_array_insert",
- "return_type": "godot_error",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const int32_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_int32_array_has",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"],
- ["const int32_t", "p_value"]
- ]
- },
- {
- "name": "godot_packed_int32_array_sort",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int32_array_invert",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int32_array_push_back",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"],
- ["const int32_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_int32_array_remove",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_int32_array_resize",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"],
- ["const godot_int", "p_size"]
- ]
- },
- {
- "name": "godot_packed_int32_array_ptr",
- "return_type": "const int32_t *",
- "arguments": [
- ["const godot_packed_int32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int32_array_ptrw",
- "return_type": "int32_t *",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int32_array_set",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const int32_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_int32_array_get",
- "return_type": "int32_t",
- "arguments": [
- ["const godot_packed_int32_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_int32_array_size",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_packed_int32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int32_array_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int64_array_new",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "r_dest"]
- ]
- },
- {
- "name": "godot_packed_int64_array_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "r_dest"],
- ["const godot_packed_int64_array *", "p_src"]
- ]
- },
- {
- "name": "godot_packed_int64_array_new_with_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "r_dest"],
- ["const godot_array *", "p_a"]
- ]
- },
- {
- "name": "godot_packed_int64_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_int64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int64_array_append",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"],
- ["const int64_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_int64_array_append_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"],
- ["const godot_packed_int64_array *", "p_array"]
- ]
- },
- {
- "name": "godot_packed_int64_array_insert",
- "return_type": "godot_error",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const int64_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_int64_array_has",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"],
- ["const int64_t", "p_value"]
- ]
- },
- {
- "name": "godot_packed_int64_array_sort",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int64_array_invert",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int64_array_push_back",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"],
- ["const int64_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_int64_array_remove",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_int64_array_resize",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"],
- ["const godot_int", "p_size"]
- ]
- },
- {
- "name": "godot_packed_int64_array_ptr",
- "return_type": "const int64_t *",
- "arguments": [
- ["const godot_packed_int64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int64_array_ptrw",
- "return_type": "int64_t *",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int64_array_set",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const int64_t", "p_data"]
- ]
- },
- {
- "name": "godot_packed_int64_array_get",
- "return_type": "int64_t",
- "arguments": [
- ["const godot_packed_int64_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_int64_array_size",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_packed_int64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_int64_array_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_int64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float32_array_new",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "r_dest"]
- ]
- },
- {
- "name": "godot_packed_float32_array_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "r_dest"],
- ["const godot_packed_float32_array *", "p_src"]
- ]
- },
- {
- "name": "godot_packed_float32_array_new_with_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "r_dest"],
- ["const godot_array *", "p_a"]
- ]
- },
- {
- "name": "godot_packed_float32_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_float32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float32_array_append",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"],
- ["const float", "p_data"]
- ]
- },
- {
- "name": "godot_packed_float32_array_append_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"],
- ["const godot_packed_float32_array *", "p_array"]
- ]
- },
- {
- "name": "godot_packed_float32_array_insert",
- "return_type": "godot_error",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const float", "p_data"]
- ]
- },
- {
- "name": "godot_packed_float32_array_has",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"],
- ["const float", "p_value"]
- ]
- },
- {
- "name": "godot_packed_float32_array_sort",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float32_array_invert",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float32_array_push_back",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"],
- ["const float", "p_data"]
- ]
- },
- {
- "name": "godot_packed_float32_array_remove",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_float32_array_resize",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"],
- ["const godot_int", "p_size"]
- ]
- },
- {
- "name": "godot_packed_float32_array_ptr",
- "return_type": "const float *",
- "arguments": [
- ["const godot_packed_float32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float32_array_ptrw",
- "return_type": "float *",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float32_array_set",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const float", "p_data"]
- ]
- },
- {
- "name": "godot_packed_float32_array_get",
- "return_type": "float",
- "arguments": [
- ["const godot_packed_float32_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_float32_array_size",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_packed_float32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float32_array_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float32_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float64_array_new",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "r_dest"]
- ]
- },
- {
- "name": "godot_packed_float64_array_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "r_dest"],
- ["const godot_packed_float64_array *", "p_src"]
- ]
- },
- {
- "name": "godot_packed_float64_array_new_with_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "r_dest"],
- ["const godot_array *", "p_a"]
- ]
- },
- {
- "name": "godot_packed_float64_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_float64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float64_array_append",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"],
- ["const double", "p_data"]
- ]
- },
- {
- "name": "godot_packed_float64_array_append_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"],
- ["const godot_packed_float64_array *", "p_array"]
- ]
- },
- {
- "name": "godot_packed_float64_array_insert",
- "return_type": "godot_error",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const double", "p_data"]
- ]
- },
- {
- "name": "godot_packed_float64_array_has",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"],
- ["const double", "p_value"]
- ]
- },
- {
- "name": "godot_packed_float64_array_sort",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float64_array_invert",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float64_array_push_back",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"],
- ["const double", "p_data"]
- ]
- },
- {
- "name": "godot_packed_float64_array_remove",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_float64_array_resize",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"],
- ["const godot_int", "p_size"]
- ]
- },
- {
- "name": "godot_packed_float64_array_ptr",
- "return_type": "const double *",
- "arguments": [
- ["const godot_packed_float64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float64_array_ptrw",
- "return_type": "double *",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float64_array_set",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const double", "p_data"]
- ]
- },
- {
- "name": "godot_packed_float64_array_get",
- "return_type": "double",
- "arguments": [
- ["const godot_packed_float64_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_float64_array_size",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_packed_float64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_float64_array_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_float64_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_string_array_new",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "r_dest"]
- ]
- },
- {
- "name": "godot_packed_string_array_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "r_dest"],
- ["const godot_packed_string_array *", "p_src"]
- ]
- },
- {
- "name": "godot_packed_string_array_new_with_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "r_dest"],
- ["const godot_array *", "p_a"]
- ]
- },
- {
- "name": "godot_packed_string_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_string_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_string_array_append",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "p_self"],
- ["const godot_string *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_string_array_append_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "p_self"],
- ["const godot_packed_string_array *", "p_array"]
- ]
- },
- {
- "name": "godot_packed_string_array_insert",
- "return_type": "godot_error",
- "arguments": [
- ["godot_packed_string_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const godot_string *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_string_array_has",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_packed_string_array *", "p_self"],
- ["const godot_string *", "p_value"]
- ]
- },
- {
- "name": "godot_packed_string_array_sort",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_string_array_invert",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_string_array_push_back",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "p_self"],
- ["const godot_string *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_string_array_remove",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_string_array_resize",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "p_self"],
- ["const godot_int", "p_size"]
- ]
- },
- {
- "name": "godot_packed_string_array_ptr",
- "return_type": "const godot_string *",
- "arguments": [
- ["const godot_packed_string_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_string_array_ptrw",
- "return_type": "godot_string *",
- "arguments": [
- ["godot_packed_string_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_string_array_set",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const godot_string *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_string_array_get",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_packed_string_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_string_array_size",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_packed_string_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_string_array_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_string_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_new",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "r_dest"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "r_dest"],
- ["const godot_packed_vector2_array *", "p_src"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_new_with_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "r_dest"],
- ["const godot_array *", "p_a"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_vector2_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_append",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"],
- ["const godot_vector2 *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_append_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"],
- ["const godot_packed_vector2_array *", "p_array"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_insert",
- "return_type": "godot_error",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const godot_vector2 *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_has",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"],
- ["const godot_vector2 *", "p_value"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_sort",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_invert",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_push_back",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"],
- ["const godot_vector2 *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_remove",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_resize",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"],
- ["const godot_int", "p_size"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_ptr",
- "return_type": "const godot_vector2 *",
- "arguments": [
- ["const godot_packed_vector2_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_ptrw",
- "return_type": "godot_vector2 *",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_set",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const godot_vector2 *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_get",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_packed_vector2_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_size",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_packed_vector2_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector2_array_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector2_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_new",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "r_dest"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "r_dest"],
- ["const godot_packed_vector3_array *", "p_src"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_new_with_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "r_dest"],
- ["const godot_array *", "p_a"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_vector3_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_append",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"],
- ["const godot_vector3 *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_append_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"],
- ["const godot_packed_vector3_array *", "p_array"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_insert",
- "return_type": "godot_error",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const godot_vector3 *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_has",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"],
- ["const godot_vector3 *", "p_value"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_sort",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_invert",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_push_back",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"],
- ["const godot_vector3 *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_remove",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_resize",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"],
- ["const godot_int", "p_size"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_ptr",
- "return_type": "const godot_vector3 *",
- "arguments": [
- ["const godot_packed_vector3_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_ptrw",
- "return_type": "godot_vector3 *",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_set",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const godot_vector3 *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_get",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_packed_vector3_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_size",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_packed_vector3_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_vector3_array_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_vector3_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_color_array_new",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "r_dest"]
- ]
- },
- {
- "name": "godot_packed_color_array_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "r_dest"],
- ["const godot_packed_color_array *", "p_src"]
- ]
- },
- {
- "name": "godot_packed_color_array_new_with_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "r_dest"],
- ["const godot_array *", "p_a"]
- ]
- },
- {
- "name": "godot_packed_color_array_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_packed_color_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_color_array_append",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "p_self"],
- ["const godot_color *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_color_array_append_array",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "p_self"],
- ["const godot_packed_color_array *", "p_array"]
- ]
- },
- {
- "name": "godot_packed_color_array_insert",
- "return_type": "godot_error",
- "arguments": [
- ["godot_packed_color_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const godot_color *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_color_array_has",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_packed_color_array *", "p_self"],
- ["const godot_color *", "p_value"]
- ]
- },
- {
- "name": "godot_packed_color_array_sort",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_color_array_invert",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_color_array_push_back",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "p_self"],
- ["const godot_color *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_color_array_remove",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_color_array_resize",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "p_self"],
- ["const godot_int", "p_size"]
- ]
- },
- {
- "name": "godot_packed_color_array_ptr",
- "return_type": "const godot_color *",
- "arguments": [
- ["const godot_packed_color_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_color_array_ptrw",
- "return_type": "godot_color *",
- "arguments": [
- ["godot_packed_color_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_color_array_set",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "p_self"],
- ["const godot_int", "p_idx"],
- ["const godot_color *", "p_data"]
- ]
- },
- {
- "name": "godot_packed_color_array_get",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_packed_color_array *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_packed_color_array_size",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_packed_color_array *", "p_self"]
- ]
- },
- {
- "name": "godot_packed_color_array_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_packed_color_array *", "p_self"]
- ]
- },
- {
- "name": "godot_plane_new_with_reals",
- "return_type": "void",
- "arguments": [
- ["godot_plane *", "r_dest"],
- ["const godot_real", "p_a"],
- ["const godot_real", "p_b"],
- ["const godot_real", "p_c"],
- ["const godot_real", "p_d"]
- ]
- },
- {
- "name": "godot_plane_new_with_vectors",
- "return_type": "void",
- "arguments": [
- ["godot_plane *", "r_dest"],
- ["const godot_vector3 *", "p_v1"],
- ["const godot_vector3 *", "p_v2"],
- ["const godot_vector3 *", "p_v3"]
- ]
- },
- {
- "name": "godot_plane_new_with_normal",
- "return_type": "void",
- "arguments": [
- ["godot_plane *", "r_dest"],
- ["const godot_vector3 *", "p_normal"],
- ["const godot_real", "p_d"]
- ]
- },
- {
- "name": "godot_plane_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_plane *", "p_self"]
- ]
- },
- {
- "name": "godot_plane_normalized",
- "return_type": "godot_plane",
- "arguments": [
- ["const godot_plane *", "p_self"]
- ]
- },
- {
- "name": "godot_plane_center",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_plane *", "p_self"]
- ]
- },
- {
- "name": "godot_plane_is_point_over",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_plane *", "p_self"],
- ["const godot_vector3 *", "p_point"]
- ]
- },
- {
- "name": "godot_plane_distance_to",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_plane *", "p_self"],
- ["const godot_vector3 *", "p_point"]
- ]
- },
- {
- "name": "godot_plane_has_point",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_plane *", "p_self"],
- ["const godot_vector3 *", "p_point"],
- ["const godot_real", "p_epsilon"]
- ]
- },
- {
- "name": "godot_plane_project",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_plane *", "p_self"],
- ["const godot_vector3 *", "p_point"]
- ]
- },
- {
- "name": "godot_plane_intersect_3",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_plane *", "p_self"],
- ["godot_vector3 *", "r_dest"],
- ["const godot_plane *", "p_b"],
- ["const godot_plane *", "p_c"]
- ]
- },
- {
- "name": "godot_plane_intersects_ray",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_plane *", "p_self"],
- ["godot_vector3 *", "r_dest"],
- ["const godot_vector3 *", "p_from"],
- ["const godot_vector3 *", "p_dir"]
- ]
- },
- {
- "name": "godot_plane_intersects_segment",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_plane *", "p_self"],
- ["godot_vector3 *", "r_dest"],
- ["const godot_vector3 *", "p_begin"],
- ["const godot_vector3 *", "p_end"]
- ]
- },
- {
- "name": "godot_plane_operator_neg",
- "return_type": "godot_plane",
- "arguments": [
- ["const godot_plane *", "p_self"]
- ]
- },
- {
- "name": "godot_plane_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_plane *", "p_self"],
- ["const godot_plane *", "p_b"]
- ]
- },
- {
- "name": "godot_plane_set_normal",
- "return_type": "void",
- "arguments": [
- ["godot_plane *", "p_self"],
- ["const godot_vector3 *", "p_normal"]
- ]
- },
- {
- "name": "godot_plane_get_normal",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_plane *", "p_self"]
- ]
- },
- {
- "name": "godot_plane_get_d",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_plane *", "p_self"]
- ]
- },
- {
- "name": "godot_plane_set_d",
- "return_type": "void",
- "arguments": [
- ["godot_plane *", "p_self"],
- ["const godot_real", "p_d"]
- ]
- },
- {
- "name": "godot_quat_new",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "r_dest"],
- ["const godot_real", "p_x"],
- ["const godot_real", "p_y"],
- ["const godot_real", "p_z"],
- ["const godot_real", "p_w"]
- ]
- },
- {
- "name": "godot_quat_new_with_axis_angle",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "r_dest"],
- ["const godot_vector3 *", "p_axis"],
- ["const godot_real", "p_angle"]
- ]
- },
- {
- "name": "godot_quat_new_with_basis",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "r_dest"],
- ["const godot_basis *", "p_basis"]
- ]
- },
- {
- "name": "godot_quat_new_with_euler",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "r_dest"],
- ["const godot_vector3 *", "p_euler"]
- ]
- },
- {
- "name": "godot_quat_get_x",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_quat *", "p_self"]
- ]
- },
- {
- "name": "godot_quat_set_x",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "p_self"],
- ["const godot_real", "val"]
- ]
- },
- {
- "name": "godot_quat_get_y",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_quat *", "p_self"]
- ]
- },
- {
- "name": "godot_quat_set_y",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "p_self"],
- ["const godot_real", "val"]
- ]
- },
- {
- "name": "godot_quat_get_z",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_quat *", "p_self"]
- ]
- },
- {
- "name": "godot_quat_set_z",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "p_self"],
- ["const godot_real", "val"]
- ]
- },
- {
- "name": "godot_quat_get_w",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_quat *", "p_self"]
- ]
- },
- {
- "name": "godot_quat_set_w",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "p_self"],
- ["const godot_real", "val"]
- ]
- },
- {
- "name": "godot_quat_set_axis_angle",
- "return_type": "void",
- "arguments": [
- ["godot_quat *", "p_self"],
- ["const godot_vector3 *", "p_axis"],
- ["const godot_real", "p_angle"]
- ]
- },
- {
- "name": "godot_quat_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_quat *", "p_self"]
- ]
- },
- {
- "name": "godot_quat_length",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_quat *", "p_self"]
- ]
- },
- {
- "name": "godot_quat_length_squared",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_quat *", "p_self"]
- ]
- },
- {
- "name": "godot_quat_normalized",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_quat *", "p_self"]
- ]
- },
- {
- "name": "godot_quat_is_normalized",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_quat *", "p_self"]
- ]
- },
- {
- "name": "godot_quat_inverse",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_quat *", "p_self"]
- ]
- },
- {
- "name": "godot_quat_dot",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"]
- ]
- },
- {
- "name": "godot_quat_xform",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_vector3 *", "p_v"]
- ]
- },
- {
- "name": "godot_quat_slerp",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"],
- ["const godot_real", "p_t"]
- ]
- },
- {
- "name": "godot_quat_slerpni",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"],
- ["const godot_real", "p_t"]
- ]
- },
- {
- "name": "godot_quat_cubic_slerp",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"],
- ["const godot_quat *", "p_pre_a"],
- ["const godot_quat *", "p_post_b"],
- ["const godot_real", "p_t"]
- ]
- },
- {
- "name": "godot_quat_operator_multiply",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_real", "p_b"]
- ]
- },
- {
- "name": "godot_quat_operator_add",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"]
- ]
- },
- {
- "name": "godot_quat_operator_subtract",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"]
- ]
- },
- {
- "name": "godot_quat_operator_divide",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_real", "p_b"]
- ]
- },
- {
- "name": "godot_quat_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_quat *", "p_self"],
- ["const godot_quat *", "p_b"]
- ]
- },
- {
- "name": "godot_quat_operator_neg",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_quat *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2_new_with_position_and_size",
- "return_type": "void",
- "arguments": [
- ["godot_rect2 *", "r_dest"],
- ["const godot_vector2 *", "p_pos"],
- ["const godot_vector2 *", "p_size"]
- ]
- },
- {
- "name": "godot_rect2_new",
- "return_type": "void",
- "arguments": [
- ["godot_rect2 *", "r_dest"],
- ["const godot_real", "p_x"],
- ["const godot_real", "p_y"],
- ["const godot_real", "p_width"],
- ["const godot_real", "p_height"]
- ]
- },
- {
- "name": "godot_rect2_as_rect2i",
- "return_type": "godot_rect2i",
- "arguments": [
- ["const godot_rect2 *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_rect2 *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2_get_area",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_rect2 *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2_grow_individual",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_real", "p_left"],
- ["const godot_real", "p_top"],
- ["const godot_real", "p_right"],
- ["const godot_real", "p_bottom"]
- ]
- },
- {
- "name": "godot_rect2_grow_margin",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_int", "p_margin"],
- ["const godot_real", "p_by"]
- ]
- },
- {
- "name": "godot_rect2_abs",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_rect2 *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2_intersects",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_rect2 *", "p_b"]
- ]
- },
- {
- "name": "godot_rect2_encloses",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_rect2 *", "p_b"]
- ]
- },
- {
- "name": "godot_rect2_has_no_area",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rect2 *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2_clip",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_rect2 *", "p_b"]
- ]
- },
- {
- "name": "godot_rect2_merge",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_rect2 *", "p_b"]
- ]
- },
- {
- "name": "godot_rect2_has_point",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_vector2 *", "p_point"]
- ]
- },
- {
- "name": "godot_rect2_grow",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_real", "p_by"]
- ]
- },
- {
- "name": "godot_rect2_expand",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
- ]
- },
- {
- "name": "godot_rect2_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rect2 *", "p_self"],
- ["const godot_rect2 *", "p_b"]
- ]
- },
- {
- "name": "godot_rect2_get_position",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_rect2 *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2_get_size",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_rect2 *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2_set_position",
- "return_type": "void",
- "arguments": [
- ["godot_rect2 *", "p_self"],
- ["const godot_vector2 *", "p_pos"]
- ]
- },
- {
- "name": "godot_rect2_set_size",
- "return_type": "void",
- "arguments": [
- ["godot_rect2 *", "p_self"],
- ["const godot_vector2 *", "p_size"]
- ]
- },
- {
- "name": "godot_rect2i_new_with_position_and_size",
- "return_type": "void",
- "arguments": [
- ["godot_rect2i *", "r_dest"],
- ["const godot_vector2i *", "p_pos"],
- ["const godot_vector2i *", "p_size"]
- ]
- },
- {
- "name": "godot_rect2i_new",
- "return_type": "void",
- "arguments": [
- ["godot_rect2i *", "r_dest"],
- ["const godot_int", "p_x"],
- ["const godot_int", "p_y"],
- ["const godot_int", "p_width"],
- ["const godot_int", "p_height"]
- ]
- },
- {
- "name": "godot_rect2i_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_rect2i *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2i_as_rect2",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_rect2i *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2i_get_area",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_rect2i *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2i_intersects",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rect2i *", "p_self"],
- ["const godot_rect2i *", "p_b"]
- ]
- },
- {
- "name": "godot_rect2i_encloses",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rect2i *", "p_self"],
- ["const godot_rect2i *", "p_b"]
- ]
- },
- {
- "name": "godot_rect2i_has_no_area",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rect2i *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2i_clip",
- "return_type": "godot_rect2i",
- "arguments": [
- ["const godot_rect2i *", "p_self"],
- ["const godot_rect2i *", "p_b"]
- ]
- },
- {
- "name": "godot_rect2i_merge",
- "return_type": "godot_rect2i",
- "arguments": [
- ["const godot_rect2i *", "p_self"],
- ["const godot_rect2i *", "p_b"]
- ]
- },
- {
- "name": "godot_rect2i_has_point",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rect2i *", "p_self"],
- ["const godot_vector2i *", "p_point"]
- ]
- },
- {
- "name": "godot_rect2i_grow",
- "return_type": "godot_rect2i",
- "arguments": [
- ["const godot_rect2i *", "p_self"],
- ["const godot_int", "p_by"]
- ]
- },
- {
- "name": "godot_rect2i_grow_individual",
- "return_type": "godot_rect2i",
- "arguments": [
- ["const godot_rect2i *", "p_self"],
- ["const godot_int", "p_left"],
- ["const godot_int", "p_top"],
- ["const godot_int", "p_right"],
- ["const godot_int", "p_bottom"]
- ]
- },
- {
- "name": "godot_rect2i_grow_margin",
- "return_type": "godot_rect2i",
- "arguments": [
- ["const godot_rect2i *", "p_self"],
- ["const godot_int", "p_margin"],
- ["const godot_int", "p_by"]
- ]
- },
- {
- "name": "godot_rect2i_abs",
- "return_type": "godot_rect2i",
- "arguments": [
- ["const godot_rect2i *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2i_expand",
- "return_type": "godot_rect2i",
- "arguments": [
- ["const godot_rect2i *", "p_self"],
- ["const godot_vector2i *", "p_to"]
- ]
- },
- {
- "name": "godot_rect2i_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rect2i *", "p_self"],
- ["const godot_rect2i *", "p_b"]
- ]
- },
- {
- "name": "godot_rect2i_get_position",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_rect2i *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2i_get_size",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_rect2i *", "p_self"]
- ]
- },
- {
- "name": "godot_rect2i_set_position",
- "return_type": "void",
- "arguments": [
- ["godot_rect2i *", "p_self"],
- ["const godot_vector2i *", "p_pos"]
- ]
- },
- {
- "name": "godot_rect2i_set_size",
- "return_type": "void",
- "arguments": [
- ["godot_rect2i *", "p_self"],
- ["const godot_vector2i *", "p_size"]
- ]
- },
- {
- "name": "godot_rid_new",
- "return_type": "void",
- "arguments": [
- ["godot_rid *", "r_dest"]
- ]
- },
- {
- "name": "godot_rid_get_id",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_rid *", "p_self"]
- ]
- },
- {
- "name": "godot_rid_new_with_resource",
- "return_type": "void",
- "arguments": [
- ["godot_rid *", "r_dest"],
- ["const godot_object *", "p_from"]
- ]
- },
- {
- "name": "godot_rid_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rid *", "p_self"],
- ["const godot_rid *", "p_b"]
- ]
- },
- {
- "name": "godot_rid_operator_less",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_rid *", "p_self"],
- ["const godot_rid *", "p_b"]
- ]
- },
- {
- "name": "godot_char_string_length",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_char_string *", "p_cs"]
- ]
- },
- {
- "name": "godot_char_string_get_data",
- "return_type": "const char *",
- "arguments": [
- ["const godot_char_string *", "p_cs"]
- ]
- },
- {
- "name": "godot_char_string_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_char_string *", "p_cs"]
- ]
- },
- {
- "name": "godot_char16_string_length",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_char16_string *", "p_cs"]
- ]
- },
- {
- "name": "godot_char16_string_get_data",
- "return_type": "const char16_t *",
- "arguments": [
- ["const godot_char16_string *", "p_cs"]
- ]
- },
- {
- "name": "godot_char16_string_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_char16_string *", "p_cs"]
- ]
- },
- {
- "name": "godot_string_new",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"]
- ]
- },
- {
- "name": "godot_string_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"],
- ["const godot_string *", "p_src"]
- ]
- },
- {
- "name": "godot_string_new_with_latin1_chars",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"],
- ["const char *", "p_contents"]
- ]
- },
- {
- "name": "godot_string_new_with_utf8_chars",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"],
- ["const char *", "p_contents"]
- ]
- },
- {
- "name": "godot_string_new_with_utf16_chars",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"],
- ["const char16_t *", "p_contents"]
- ]
- },
- {
- "name": "godot_string_new_with_utf32_chars",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"],
- ["const char32_t *", "p_contents"]
- ]
- },
- {
- "name": "godot_string_new_with_wide_chars",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"],
- ["const wchar_t *", "p_contents"]
- ]
- },
- {
- "name": "godot_string_new_with_latin1_chars_and_len",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"],
- ["const char *", "p_contents"],
- ["const int", "p_size"]
- ]
- },
- {
- "name": "godot_string_new_with_utf8_chars_and_len",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"],
- ["const char *", "p_contents"],
- ["const int", "p_size"]
- ]
- },
- {
- "name": "godot_string_new_with_utf16_chars_and_len",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"],
- ["const char16_t *", "p_contents"],
- ["const int", "p_size"]
- ]
- },
- {
- "name": "godot_string_new_with_utf32_chars_and_len",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"],
- ["const char32_t *", "p_contents"],
- ["const int", "p_size"]
- ]
- },
- {
- "name": "godot_string_new_with_wide_chars_and_len",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "r_dest"],
- ["const wchar_t *", "p_contents"],
- ["const int", "p_size"]
- ]
- },
- {
- "name": "godot_string_operator_index",
- "return_type": "const godot_char_type *",
- "arguments": [
- ["godot_string *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_string_operator_index_const",
- "return_type": "godot_char_type",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_string_get_data",
- "return_type": "const godot_char_type *",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_b"]
- ]
- },
- {
- "name": "godot_string_operator_less",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_b"]
- ]
- },
- {
- "name": "godot_string_operator_plus",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_b"]
- ]
- },
- {
- "name": "godot_string_count",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_what"],
- ["godot_int", "p_from"],
- ["godot_int", "p_to"]
- ]
- },
- {
- "name": "godot_string_countn",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_what"],
- ["godot_int", "p_from"],
- ["godot_int", "p_to"]
- ]
- },
- {
- "name": "godot_string_dedent",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_length",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_casecmp_to",
- "return_type": "signed char",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_str"]
- ]
- },
- {
- "name": "godot_string_nocasecmp_to",
- "return_type": "signed char",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_str"]
- ]
- },
- {
- "name": "godot_string_naturalnocasecmp_to",
- "return_type": "signed char",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_str"]
- ]
- },
- {
- "name": "godot_string_begins_with",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_string"]
- ]
- },
- {
- "name": "godot_string_begins_with_char_array",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const char *", "p_char_array"]
- ]
- },
- {
- "name": "godot_string_bigrams",
- "return_type": "godot_packed_string_array",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_chr",
- "return_type": "godot_string",
- "arguments": [
- ["godot_char_type", "p_character"]
- ]
- },
- {
- "name": "godot_string_ends_with",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_string"]
- ]
- },
- {
- "name": "godot_string_ends_with_char_array",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const char *", "p_char_array"]
- ]
- },
- {
- "name": "godot_string_find",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_what"]
- ]
- },
- {
- "name": "godot_string_find_from",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_what"],
- ["godot_int", "p_from"]
- ]
- },
- {
- "name": "godot_string_findmk",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_packed_string_array *", "p_keys"]
- ]
- },
- {
- "name": "godot_string_findmk_from",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_packed_string_array *", "p_keys"],
- ["godot_int", "p_from"]
- ]
- },
- {
- "name": "godot_string_findmk_from_in_place",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_packed_string_array *", "p_keys"],
- ["godot_int", "p_from"],
- ["godot_int *", "r_key"]
- ]
- },
- {
- "name": "godot_string_findn",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_what"]
- ]
- },
- {
- "name": "godot_string_findn_from",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_what"],
- ["godot_int", "p_from"]
- ]
- },
- {
- "name": "godot_string_format",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_variant *", "p_values"]
- ]
- },
- {
- "name": "godot_string_format_with_custom_placeholder",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_variant *", "p_values"],
- ["const char *", "p_placeholder"]
- ]
- },
- {
- "name": "godot_string_hex_encode_buffer",
- "return_type": "godot_string",
- "arguments": [
- ["const uint8_t *", "p_buffer"],
- ["godot_int", "p_len"]
- ]
- },
- {
- "name": "godot_string_insert",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_at_pos"],
- ["const godot_string *", "p_string"]
- ]
- },
- {
- "name": "godot_string_is_numeric",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_is_subsequence_of",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_string"]
- ]
- },
- {
- "name": "godot_string_is_subsequence_ofi",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_string"]
- ]
- },
- {
- "name": "godot_string_lpad",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_min_length"]
- ]
- },
- {
- "name": "godot_string_lpad_with_custom_character",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_min_length"],
- ["const godot_string *", "p_character"]
- ]
- },
- {
- "name": "godot_string_match",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_wildcard"]
- ]
- },
- {
- "name": "godot_string_matchn",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_wildcard"]
- ]
- },
- {
- "name": "godot_string_md5",
- "return_type": "godot_string",
- "arguments": [
- ["const uint8_t *", "p_md5"]
- ]
- },
- {
- "name": "godot_string_num",
- "return_type": "godot_string",
- "arguments": [
- ["double", "p_num"]
- ]
- },
- {
- "name": "godot_string_num_int64",
- "return_type": "godot_string",
- "arguments": [
- ["int64_t", "p_num"],
- ["godot_int", "p_base"]
- ]
- },
- {
- "name": "godot_string_num_int64_capitalized",
- "return_type": "godot_string",
- "arguments": [
- ["int64_t", "p_num"],
- ["godot_int", "p_base"],
- ["godot_bool", "p_capitalize_hex"]
- ]
- },
- {
- "name": "godot_string_num_real",
- "return_type": "godot_string",
- "arguments": [
- ["double", "p_num"]
- ]
- },
- {
- "name": "godot_string_num_scientific",
- "return_type": "godot_string",
- "arguments": [
- ["double", "p_num"]
- ]
- },
- {
- "name": "godot_string_num_with_decimals",
- "return_type": "godot_string",
- "arguments": [
- ["double", "p_num"],
- ["godot_int", "p_decimals"]
- ]
- },
- {
- "name": "godot_string_pad_decimals",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_digits"]
- ]
- },
- {
- "name": "godot_string_pad_zeros",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_digits"]
- ]
- },
- {
- "name": "godot_string_replace_first",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_key"],
- ["const godot_string *", "p_with"]
- ]
- },
- {
- "name": "godot_string_replace",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_key"],
- ["const godot_string *", "p_with"]
- ]
- },
- {
- "name": "godot_string_replacen",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_key"],
- ["const godot_string *", "p_with"]
- ]
- },
- {
- "name": "godot_string_rfind",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_what"]
- ]
- },
- {
- "name": "godot_string_rfindn",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_what"]
- ]
- },
- {
- "name": "godot_string_rfind_from",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_what"],
- ["godot_int", "p_from"]
- ]
- },
- {
- "name": "godot_string_rfindn_from",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_what"],
- ["godot_int", "p_from"]
- ]
- },
- {
- "name": "godot_string_rpad",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_min_length"]
- ]
- },
- {
- "name": "godot_string_rpad_with_custom_character",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_min_length"],
- ["const godot_string *", "p_character"]
- ]
- },
- {
- "name": "godot_string_similarity",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_string"]
- ]
- },
- {
- "name": "godot_string_sprintf",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_array *", "p_values"],
- ["godot_bool *", "p_error"]
- ]
- },
- {
- "name": "godot_string_substr",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_from"],
- ["godot_int", "p_chars"]
- ]
- },
- {
- "name": "godot_string_to_int",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_to_float",
- "return_type": "double",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_camelcase_to_underscore",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_camelcase_to_underscore_lowercased",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_capitalize",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_char_to_float",
- "return_type": "double",
- "arguments": [
- ["const char *", "p_what"]
- ]
- },
- {
- "name": "godot_string_wchar_to_float",
- "return_type": "double",
- "arguments": [
- ["const wchar_t *", "p_str"],
- ["const wchar_t **", "r_end"]
- ]
- },
- {
- "name": "godot_string_char_to_int",
- "return_type": "godot_int",
- "arguments": [
- ["const char *", "p_what"]
- ]
- },
- {
- "name": "godot_string_wchar_to_int",
- "return_type": "godot_int",
- "arguments": [
- ["const wchar_t *", "p_str"]
- ]
- },
- {
- "name": "godot_string_char_to_int_with_len",
- "return_type": "godot_int",
- "arguments": [
- ["const char *", "p_what"],
- ["godot_int", "p_len"]
- ]
- },
- {
- "name": "godot_string_wchar_to_int_with_len",
- "return_type": "godot_int",
- "arguments": [
- ["const wchar_t *", "p_str"],
- ["int", "p_len"]
- ]
- },
- {
- "name": "godot_string_hex_to_int",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_hex_to_int_with_prefix",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_get_slice_count",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_get_slice",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"],
- ["godot_int", "p_slice"]
- ]
- },
- {
- "name": "godot_string_get_slicec",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_char_type", "p_splitter"],
- ["godot_int", "p_slice"]
- ]
- },
- {
- "name": "godot_string_split",
- "return_type": "godot_packed_string_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_allow_empty",
- "return_type": "godot_packed_string_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_with_maxsplit",
- "return_type": "godot_packed_string_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"],
- ["const godot_bool", "p_allow_empty"],
- ["const godot_int", "p_maxsplit"]
- ]
- },
- {
- "name": "godot_string_rsplit",
- "return_type": "godot_packed_string_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_rsplit_allow_empty",
- "return_type": "godot_packed_string_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_rsplit_with_maxsplit",
- "return_type": "godot_packed_string_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"],
- ["const godot_bool", "p_allow_empty"],
- ["const godot_int", "p_maxsplit"]
- ]
- },
- {
- "name": "godot_string_split_floats",
- "return_type": "godot_packed_float32_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_floats_allow_empty",
- "return_type": "godot_packed_float32_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_floats_mk",
- "return_type": "godot_packed_float32_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_packed_string_array *", "p_splitters"]
- ]
- },
- {
- "name": "godot_string_split_floats_mk_allow_empty",
- "return_type": "godot_packed_float32_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_packed_string_array *", "p_splitters"]
- ]
- },
- {
- "name": "godot_string_split_ints",
- "return_type": "godot_packed_int32_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_ints_allow_empty",
- "return_type": "godot_packed_int32_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_splitter"]
- ]
- },
- {
- "name": "godot_string_split_ints_mk",
- "return_type": "godot_packed_int32_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_packed_string_array *", "p_splitters"]
- ]
- },
- {
- "name": "godot_string_split_ints_mk_allow_empty",
- "return_type": "godot_packed_int32_array",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_packed_string_array *", "p_splitters"]
- ]
- },
- {
- "name": "godot_string_split_spaces",
- "return_type": "godot_packed_string_array",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_lstrip",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_chars"]
- ]
- },
- {
- "name": "godot_string_rstrip",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_chars"]
- ]
- },
- {
- "name": "godot_string_trim_prefix",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_prefix"]
- ]
- },
- {
- "name": "godot_string_trim_suffix",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_suffix"]
- ]
- },
- {
- "name": "godot_string_char_lowercase",
- "return_type": "godot_char_type",
- "arguments": [
- ["godot_char_type", "p_char"]
- ]
- },
- {
- "name": "godot_string_char_uppercase",
- "return_type": "godot_char_type",
- "arguments": [
- ["godot_char_type", "p_char"]
- ]
- },
- {
- "name": "godot_string_to_lower",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_to_upper",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_get_basename",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_get_extension",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_left",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_pos"]
- ]
- },
- {
- "name": "godot_string_ord_at",
- "return_type": "godot_char_type",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_idx"]
- ]
- },
- {
- "name": "godot_string_plus_file",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_file"]
- ]
- },
- {
- "name": "godot_string_right",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_pos"]
- ]
- },
- {
- "name": "godot_string_repeat",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_int", "p_count"]
- ]
- },
- {
- "name": "godot_string_strip_edges",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_bool", "p_left"],
- ["godot_bool", "p_right"]
- ]
- },
- {
- "name": "godot_string_strip_escapes",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_erase",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "p_self"],
- ["godot_int", "p_pos"],
- ["godot_int", "p_chars"]
- ]
- },
- {
- "name": "godot_string_ascii",
- "return_type": "godot_char_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_latin1",
- "return_type": "godot_char_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_utf8",
- "return_type": "godot_char_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_parse_utf8",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_string *", "p_self"],
- ["const char *", "p_utf8"]
- ]
- },
- {
- "name": "godot_string_parse_utf8_with_len",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_string *", "p_self"],
- ["const char *", "p_utf8"],
- ["godot_int", "p_len"]
- ]
- },
- {
- "name": "godot_string_utf16",
- "return_type": "godot_char16_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_parse_utf16",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_string *", "p_self"],
- ["const char16_t *", "p_utf16"]
- ]
- },
- {
- "name": "godot_string_parse_utf16_with_len",
- "return_type": "godot_bool",
- "arguments": [
- ["godot_string *", "p_self"],
- ["const char16_t *", "p_utf16"],
- ["godot_int", "p_len"]
- ]
- },
- {
- "name": "godot_string_hash",
- "return_type": "uint32_t",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_hash64",
- "return_type": "uint64_t",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_hash_chars",
- "return_type": "uint32_t",
- "arguments": [
- ["const char *", "p_cstr"]
- ]
- },
- {
- "name": "godot_string_hash_chars_with_len",
- "return_type": "uint32_t",
- "arguments": [
- ["const char *", "p_cstr"],
- ["godot_int", "p_len"]
- ]
- },
- {
- "name": "godot_string_hash_wide_chars",
- "return_type": "uint32_t",
- "arguments": [
- ["const wchar_t *", "p_str"]
- ]
- },
- {
- "name": "godot_string_hash_wide_chars_with_len",
- "return_type": "uint32_t",
- "arguments": [
- ["const wchar_t *", "p_str"],
- ["godot_int", "p_len"]
- ]
- },
- {
- "name": "godot_string_md5_buffer",
- "return_type": "godot_packed_byte_array",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_md5_text",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_sha1_buffer",
- "return_type": "godot_packed_byte_array",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_sha1_text",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_sha256_buffer",
- "return_type": "godot_packed_byte_array",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_sha256_text",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_empty",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_get_base_dir",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_get_file",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_humanize_size",
- "return_type": "godot_string",
- "arguments": [
- ["size_t", "p_size"]
- ]
- },
- {
- "name": "godot_string_is_abs_path",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_is_rel_path",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_is_resource_file",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_path_to",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_path"]
- ]
- },
- {
- "name": "godot_string_path_to_file",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_string *", "p_path"]
- ]
- },
- {
- "name": "godot_string_simplify_path",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_c_escape",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_c_escape_multiline",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_c_unescape",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_http_escape",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_http_unescape",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_json_escape",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_xml_escape",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_xml_escape_with_quotes",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_xml_unescape",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_percent_decode",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_percent_encode",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_join",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["const godot_packed_string_array *", "p_parts"]
- ]
- },
- {
- "name": "godot_string_is_valid_filename",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_is_valid_float",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_is_valid_hex_number",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"],
- ["godot_bool", "p_with_prefix"]
- ]
- },
- {
- "name": "godot_string_is_valid_html_color",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_is_valid_identifier",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_is_valid_integer",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_is_valid_ip_address",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_string *", "p_self"]
- ]
- },
- {
- "name": "godot_string_name_new",
- "return_type": "void",
- "arguments": [
- ["godot_string_name *", "r_dest"],
- ["const godot_string *", "p_name"]
- ]
- },
- {
- "name": "godot_string_name_new_data",
- "return_type": "void",
- "arguments": [
- ["godot_string_name *", "r_dest"],
- ["const char *", "p_name"]
- ]
- },
- {
- "name": "godot_string_name_get_name",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_string_name *", "p_self"]
- ]
- },
- {
- "name": "godot_string_name_get_hash",
- "return_type": "uint32_t",
- "arguments": [
- ["const godot_string_name *", "p_self"]
- ]
- },
- {
- "name": "godot_string_name_get_data_unique_pointer",
- "return_type": "const void *",
- "arguments": [
- ["const godot_string_name *", "p_self"]
- ]
- },
- {
- "name": "godot_string_name_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string_name *", "p_self"],
- ["const godot_string_name *", "p_other"]
- ]
- },
- {
- "name": "godot_string_name_operator_less",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_string_name *", "p_self"],
- ["const godot_string_name *", "p_other"]
- ]
- },
- {
- "name": "godot_string_name_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_string_name *", "p_self"]
- ]
- },
- {
- "name": "godot_transform_new_with_axis_origin",
- "return_type": "void",
- "arguments": [
- ["godot_transform *", "r_dest"],
- ["const godot_vector3 *", "p_x_axis"],
- ["const godot_vector3 *", "p_y_axis"],
- ["const godot_vector3 *", "p_z_axis"],
- ["const godot_vector3 *", "p_origin"]
- ]
- },
- {
- "name": "godot_transform_new_with_quat",
- "return_type": "void",
- "arguments": [
- ["godot_transform *", "r_dest"],
- ["const godot_quat *", "p_quat"]
- ]
- },
- {
- "name": "godot_transform_new",
- "return_type": "void",
- "arguments": [
- ["godot_transform *", "r_dest"],
- ["const godot_basis *", "p_basis"],
- ["const godot_vector3 *", "p_origin"]
- ]
- },
- {
- "name": "godot_transform_get_basis",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_transform *", "p_self"]
- ]
- },
- {
- "name": "godot_transform_set_basis",
- "return_type": "void",
- "arguments": [
- ["godot_transform *", "p_self"],
- ["const godot_basis *", "p_v"]
- ]
- },
- {
- "name": "godot_transform_get_origin",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_transform *", "p_self"]
- ]
- },
- {
- "name": "godot_transform_set_origin",
- "return_type": "void",
- "arguments": [
- ["godot_transform *", "p_self"],
- ["const godot_vector3 *", "p_v"]
- ]
- },
- {
- "name": "godot_transform_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_transform *", "p_self"]
- ]
- },
- {
- "name": "godot_transform_inverse",
- "return_type": "godot_transform",
- "arguments": [
- ["const godot_transform *", "p_self"]
- ]
- },
- {
- "name": "godot_transform_affine_inverse",
- "return_type": "godot_transform",
- "arguments": [
- ["const godot_transform *", "p_self"]
- ]
- },
- {
- "name": "godot_transform_orthonormalized",
- "return_type": "godot_transform",
- "arguments": [
- ["const godot_transform *", "p_self"]
- ]
- },
- {
- "name": "godot_transform_rotated",
- "return_type": "godot_transform",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_vector3 *", "p_axis"],
- ["const godot_real", "p_phi"]
- ]
- },
- {
- "name": "godot_transform_scaled",
- "return_type": "godot_transform",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_vector3 *", "p_scale"]
- ]
- },
- {
- "name": "godot_transform_translated",
- "return_type": "godot_transform",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_vector3 *", "p_ofs"]
- ]
- },
- {
- "name": "godot_transform_looking_at",
- "return_type": "godot_transform",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_vector3 *", "p_target"],
- ["const godot_vector3 *", "p_up"]
- ]
- },
- {
- "name": "godot_transform_xform_plane",
- "return_type": "godot_plane",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_plane *", "p_v"]
- ]
- },
- {
- "name": "godot_transform_xform_inv_plane",
- "return_type": "godot_plane",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_plane *", "p_v"]
- ]
- },
- {
- "name": "godot_transform_new_identity",
- "return_type": "void",
- "arguments": [
- ["godot_transform *", "r_dest"]
- ]
- },
- {
- "name": "godot_transform_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_transform *", "p_b"]
- ]
- },
- {
- "name": "godot_transform_operator_multiply",
- "return_type": "godot_transform",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_transform *", "p_b"]
- ]
- },
- {
- "name": "godot_transform_xform_vector3",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_vector3 *", "p_v"]
- ]
- },
- {
- "name": "godot_transform_xform_inv_vector3",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_vector3 *", "p_v"]
- ]
- },
- {
- "name": "godot_transform_xform_aabb",
- "return_type": "godot_aabb",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_aabb *", "p_v"]
- ]
- },
- {
- "name": "godot_transform_xform_inv_aabb",
- "return_type": "godot_aabb",
- "arguments": [
- ["const godot_transform *", "p_self"],
- ["const godot_aabb *", "p_v"]
- ]
- },
- {
- "name": "godot_transform2d_new",
- "return_type": "void",
- "arguments": [
- ["godot_transform2d *", "r_dest"],
- ["const godot_real", "p_rot"],
- ["const godot_vector2 *", "p_pos"]
- ]
- },
- {
- "name": "godot_transform2d_new_axis_origin",
- "return_type": "void",
- "arguments": [
- ["godot_transform2d *", "r_dest"],
- ["const godot_vector2 *", "p_x_axis"],
- ["const godot_vector2 *", "p_y_axis"],
- ["const godot_vector2 *", "p_origin"]
- ]
- },
- {
- "name": "godot_transform2d_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_transform2d *", "p_self"]
- ]
- },
- {
- "name": "godot_transform2d_inverse",
- "return_type": "godot_transform2d",
- "arguments": [
- ["const godot_transform2d *", "p_self"]
- ]
- },
- {
- "name": "godot_transform2d_affine_inverse",
- "return_type": "godot_transform2d",
- "arguments": [
- ["const godot_transform2d *", "p_self"]
- ]
- },
- {
- "name": "godot_transform2d_get_rotation",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_transform2d *", "p_self"]
- ]
- },
- {
- "name": "godot_transform2d_get_origin",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_transform2d *", "p_self"]
- ]
- },
- {
- "name": "godot_transform2d_get_scale",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_transform2d *", "p_self"]
- ]
- },
- {
- "name": "godot_transform2d_orthonormalized",
- "return_type": "godot_transform2d",
- "arguments": [
- ["const godot_transform2d *", "p_self"]
- ]
- },
- {
- "name": "godot_transform2d_rotated",
- "return_type": "godot_transform2d",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_real", "p_phi"]
- ]
- },
- {
- "name": "godot_transform2d_scaled",
- "return_type": "godot_transform2d",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_vector2 *", "p_scale"]
- ]
- },
- {
- "name": "godot_transform2d_translated",
- "return_type": "godot_transform2d",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_vector2 *", "p_offset"]
- ]
- },
- {
- "name": "godot_transform2d_xform_vector2",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_vector2 *", "p_v"]
- ]
- },
- {
- "name": "godot_transform2d_xform_inv_vector2",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_vector2 *", "p_v"]
- ]
- },
- {
- "name": "godot_transform2d_basis_xform_vector2",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_vector2 *", "p_v"]
- ]
- },
- {
- "name": "godot_transform2d_basis_xform_inv_vector2",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_vector2 *", "p_v"]
- ]
- },
- {
- "name": "godot_transform2d_interpolate_with",
- "return_type": "godot_transform2d",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_transform2d *", "p_m"],
- ["const godot_real", "p_c"]
- ]
- },
- {
- "name": "godot_transform2d_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_transform2d *", "p_b"]
- ]
- },
- {
- "name": "godot_transform2d_operator_multiply",
- "return_type": "godot_transform2d",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_transform2d *", "p_b"]
- ]
- },
- {
- "name": "godot_transform2d_new_identity",
- "return_type": "void",
- "arguments": [
- ["godot_transform2d *", "r_dest"]
- ]
- },
- {
- "name": "godot_transform2d_xform_rect2",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_rect2 *", "p_v"]
- ]
- },
- {
- "name": "godot_transform2d_xform_inv_rect2",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_transform2d *", "p_self"],
- ["const godot_rect2 *", "p_v"]
- ]
- },
- {
- "name": "godot_variant_get_type",
- "return_type": "godot_variant_type",
- "arguments": [
- ["const godot_variant *", "p_v"]
- ]
- },
- {
- "name": "godot_variant_new_copy",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_variant *", "p_src"]
- ]
- },
- {
- "name": "godot_variant_new_nil",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"]
- ]
- },
- {
- "name": "godot_variant_new_bool",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_bool", "p_b"]
- ]
- },
- {
- "name": "godot_variant_new_uint",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const uint64_t", "p_i"]
- ]
- },
- {
- "name": "godot_variant_new_int",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const int64_t", "p_i"]
- ]
- },
- {
- "name": "godot_variant_new_real",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const double", "p_r"]
- ]
- },
- {
- "name": "godot_variant_new_string",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_string *", "p_s"]
- ]
- },
- {
- "name": "godot_variant_new_string_name",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_string_name *", "p_s"]
- ]
- },
- {
- "name": "godot_variant_new_vector2",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_vector2 *", "p_v2"]
- ]
- },
- {
- "name": "godot_variant_new_vector2i",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_vector2i *", "p_v2"]
- ]
- },
- {
- "name": "godot_variant_new_rect2",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_rect2 *", "p_rect2"]
- ]
- },
- {
- "name": "godot_variant_new_rect2i",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_rect2i *", "p_rect2"]
- ]
- },
- {
- "name": "godot_variant_new_vector3",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_vector3 *", "p_v3"]
- ]
- },
- {
- "name": "godot_variant_new_vector3i",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_vector3i *", "p_v3"]
- ]
- },
- {
- "name": "godot_variant_new_transform2d",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_transform2d *", "p_t2d"]
- ]
- },
- {
- "name": "godot_variant_new_plane",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_plane *", "p_plane"]
- ]
- },
- {
- "name": "godot_variant_new_quat",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_quat *", "p_quat"]
- ]
- },
- {
- "name": "godot_variant_new_aabb",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_aabb *", "p_aabb"]
- ]
- },
- {
- "name": "godot_variant_new_basis",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_basis *", "p_basis"]
- ]
- },
- {
- "name": "godot_variant_new_transform",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_transform *", "p_trans"]
- ]
- },
- {
- "name": "godot_variant_new_color",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_color *", "p_color"]
- ]
- },
- {
- "name": "godot_variant_new_node_path",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_node_path *", "p_np"]
- ]
- },
- {
- "name": "godot_variant_new_rid",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_rid *", "p_rid"]
- ]
- },
- {
- "name": "godot_variant_new_object",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_object *", "p_obj"]
- ]
- },
- {
- "name": "godot_variant_new_callable",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_callable *", "p_cb"]
- ]
- },
- {
- "name": "godot_variant_new_signal",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_signal *", "p_signal"]
- ]
- },
- {
- "name": "godot_variant_new_dictionary",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_dictionary *", "p_dict"]
- ]
- },
- {
- "name": "godot_variant_new_array",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_array *", "p_arr"]
- ]
- },
- {
- "name": "godot_variant_new_packed_byte_array",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_packed_byte_array *", "p_pba"]
- ]
- },
- {
- "name": "godot_variant_new_packed_int32_array",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_packed_int32_array *", "p_pia"]
- ]
- },
- {
- "name": "godot_variant_new_packed_int64_array",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_packed_int64_array *", "p_pia"]
- ]
- },
- {
- "name": "godot_variant_new_packed_float32_array",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_packed_float32_array *", "p_pra"]
- ]
- },
- {
- "name": "godot_variant_new_packed_float64_array",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_packed_float64_array *", "p_pra"]
- ]
- },
- {
- "name": "godot_variant_new_packed_string_array",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_packed_string_array *", "p_psa"]
- ]
- },
- {
- "name": "godot_variant_new_packed_vector2_array",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_packed_vector2_array *", "p_pv2a"]
- ]
- },
- {
- "name": "godot_variant_new_packed_vector3_array",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_packed_vector3_array *", "p_pv3a"]
- ]
- },
- {
- "name": "godot_variant_new_packed_color_array",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "r_dest"],
- ["const godot_packed_color_array *", "p_pca"]
- ]
- },
- {
- "name": "godot_variant_as_bool",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_uint",
- "return_type": "uint64_t",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_int",
- "return_type": "int64_t",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_real",
- "return_type": "double",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_string_name",
- "return_type": "godot_string_name",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_vector2",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_vector2i",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_rect2",
- "return_type": "godot_rect2",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_rect2i",
- "return_type": "godot_rect2i",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_vector3",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_vector3i",
- "return_type": "godot_vector3i",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_transform2d",
- "return_type": "godot_transform2d",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_plane",
- "return_type": "godot_plane",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_quat",
- "return_type": "godot_quat",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_aabb",
- "return_type": "godot_aabb",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_basis",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_transform",
- "return_type": "godot_transform",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_color",
- "return_type": "godot_color",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_node_path",
- "return_type": "godot_node_path",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_rid",
- "return_type": "godot_rid",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_object",
- "return_type": "godot_object *",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_callable",
- "return_type": "godot_callable",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_signal",
- "return_type": "godot_signal",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_dictionary",
- "return_type": "godot_dictionary",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_array",
- "return_type": "godot_array",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_packed_byte_array",
- "return_type": "godot_packed_byte_array",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_packed_int32_array",
- "return_type": "godot_packed_int32_array",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_packed_int64_array",
- "return_type": "godot_packed_int64_array",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_packed_float32_array",
- "return_type": "godot_packed_float32_array",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_packed_float64_array",
- "return_type": "godot_packed_float64_array",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_packed_string_array",
- "return_type": "godot_packed_string_array",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_packed_vector2_array",
- "return_type": "godot_packed_vector2_array",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_packed_vector3_array",
- "return_type": "godot_packed_vector3_array",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_as_packed_color_array",
- "return_type": "godot_packed_color_array",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_call",
- "return_type": "godot_variant",
- "arguments": [
- ["godot_variant *", "p_self"],
- ["const godot_string *", "p_method"],
- ["const godot_variant **", "p_args"],
- ["const godot_int", "p_argcount"],
- ["godot_variant_call_error *", "r_error"]
- ]
- },
- {
- "name": "godot_variant_has_method",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_variant *", "p_self"],
- ["const godot_string *", "p_method"]
- ]
- },
- {
- "name": "godot_variant_hash",
- "return_type": "uint32_t",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_variant *", "p_self"],
- ["const godot_variant *", "p_other"]
- ]
- },
- {
- "name": "godot_variant_operator_less",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_variant *", "p_self"],
- ["const godot_variant *", "p_other"]
- ]
- },
- {
- "name": "godot_variant_get_operator_name",
- "return_type": "godot_string",
- "arguments": [
- ["godot_variant_operator", "p_op"]
- ]
- },
- {
- "name": "godot_variant_evaluate",
- "return_type": "void",
- "arguments": [
- ["godot_variant_operator", "p_op"],
- ["const godot_variant *", "p_a"],
- ["const godot_variant *", "p_b"],
- ["godot_variant *", "r_ret"],
- ["godot_bool *", "r_valid"]
- ]
- },
- {
- "name": "godot_variant_hash_compare",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_variant *", "p_self"],
- ["const godot_variant *", "p_other"]
- ]
- },
- {
- "name": "godot_variant_booleanize",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_variant_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_variant *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_as_vector2i",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_sign",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_move_toward",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"],
- ["const godot_real", "p_delta"]
- ]
- },
- {
- "name": "godot_vector2_direction_to",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
- ]
- },
- {
- "name": "godot_vector2_new",
- "return_type": "void",
- "arguments": [
- ["godot_vector2 *", "r_dest"],
- ["const godot_real", "p_x"],
- ["const godot_real", "p_y"]
- ]
- },
- {
- "name": "godot_vector2_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_normalized",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_length",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_angle",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_length_squared",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_is_normalized",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_distance_to",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
- ]
- },
- {
- "name": "godot_vector2_distance_squared_to",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
- ]
- },
- {
- "name": "godot_vector2_angle_to",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
- ]
- },
- {
- "name": "godot_vector2_angle_to_point",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_to"]
- ]
- },
- {
- "name": "godot_vector2_lerp",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"],
- ["const godot_real", "p_t"]
- ]
- },
- {
- "name": "godot_vector2_cubic_interpolate",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"],
- ["const godot_vector2 *", "p_pre_a"],
- ["const godot_vector2 *", "p_post_b"],
- ["const godot_real", "p_t"]
- ]
- },
- {
- "name": "godot_vector2_rotated",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_real", "p_phi"]
- ]
- },
- {
- "name": "godot_vector2_tangent",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_floor",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_snapped",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_by"]
- ]
- },
- {
- "name": "godot_vector2_aspect",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_dot",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_with"]
- ]
- },
- {
- "name": "godot_vector2_slide",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_n"]
- ]
- },
- {
- "name": "godot_vector2_bounce",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_n"]
- ]
- },
- {
- "name": "godot_vector2_reflect",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_n"]
- ]
- },
- {
- "name": "godot_vector2_abs",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_clamped",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_real", "p_length"]
- ]
- },
- {
- "name": "godot_vector2_operator_add",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2_operator_subtract",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2_operator_multiply_vector",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2_operator_multiply_scalar",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_real", "p_b"]
- ]
- },
- {
- "name": "godot_vector2_operator_divide_vector",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2_operator_divide_scalar",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_real", "p_b"]
- ]
- },
- {
- "name": "godot_vector2_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2_operator_less",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_vector2 *", "p_self"],
- ["const godot_vector2 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2_operator_neg",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_set_x",
- "return_type": "void",
- "arguments": [
- ["godot_vector2 *", "p_self"],
- ["const godot_real", "p_x"]
- ]
- },
- {
- "name": "godot_vector2_set_y",
- "return_type": "void",
- "arguments": [
- ["godot_vector2 *", "p_self"],
- ["const godot_real", "p_y"]
- ]
- },
- {
- "name": "godot_vector2_get_x",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2_get_y",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2i_new",
- "return_type": "void",
- "arguments": [
- ["godot_vector2i *", "r_dest"],
- ["const godot_int", "p_x"],
- ["const godot_int", "p_y"]
- ]
- },
- {
- "name": "godot_vector2i_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_vector2i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2i_as_vector2",
- "return_type": "godot_vector2",
- "arguments": [
- ["const godot_vector2i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2i_aspect",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector2i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2i_abs",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_vector2i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2i_sign",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_vector2i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2i_operator_add",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_vector2i *", "p_self"],
- ["const godot_vector2i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2i_operator_subtract",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_vector2i *", "p_self"],
- ["const godot_vector2i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2i_operator_multiply_vector",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_vector2i *", "p_self"],
- ["const godot_vector2i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2i_operator_multiply_scalar",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_vector2i *", "p_self"],
- ["const godot_int", "p_b"]
- ]
- },
- {
- "name": "godot_vector2i_operator_divide_vector",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_vector2i *", "p_self"],
- ["const godot_vector2i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2i_operator_divide_scalar",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_vector2i *", "p_self"],
- ["const godot_int", "p_b"]
- ]
- },
- {
- "name": "godot_vector2i_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_vector2i *", "p_self"],
- ["const godot_vector2i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2i_operator_less",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_vector2i *", "p_self"],
- ["const godot_vector2i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector2i_operator_neg",
- "return_type": "godot_vector2i",
- "arguments": [
- ["const godot_vector2i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2i_set_x",
- "return_type": "void",
- "arguments": [
- ["godot_vector2i *", "p_self"],
- ["const godot_int", "p_x"]
- ]
- },
- {
- "name": "godot_vector2i_set_y",
- "return_type": "void",
- "arguments": [
- ["godot_vector2i *", "p_self"],
- ["const godot_int", "p_y"]
- ]
- },
- {
- "name": "godot_vector2i_get_x",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_vector2i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector2i_get_y",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_vector2i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_as_vector3i",
- "return_type": "godot_vector3i",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_sign",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_move_toward",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_to"],
- ["const godot_real", "p_delta"]
- ]
- },
- {
- "name": "godot_vector3_direction_to",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_to"]
- ]
- },
- {
- "name": "godot_vector3_new",
- "return_type": "void",
- "arguments": [
- ["godot_vector3 *", "r_dest"],
- ["const godot_real", "p_x"],
- ["const godot_real", "p_y"],
- ["const godot_real", "p_z"]
- ]
- },
- {
- "name": "godot_vector3_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_min_axis",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_max_axis",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_length",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_length_squared",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_is_normalized",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_normalized",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_inverse",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_snapped",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_by"]
- ]
- },
- {
- "name": "godot_vector3_rotated",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_axis"],
- ["const godot_real", "p_phi"]
- ]
- },
- {
- "name": "godot_vector3_lerp",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"],
- ["const godot_real", "p_t"]
- ]
- },
- {
- "name": "godot_vector3_cubic_interpolate",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"],
- ["const godot_vector3 *", "p_pre_a"],
- ["const godot_vector3 *", "p_post_b"],
- ["const godot_real", "p_t"]
- ]
- },
- {
- "name": "godot_vector3_dot",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_cross",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_outer",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_to_diagonal_matrix",
- "return_type": "godot_basis",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_abs",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_floor",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_ceil",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_distance_to",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_distance_squared_to",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_angle_to",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_to"]
- ]
- },
- {
- "name": "godot_vector3_slide",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_n"]
- ]
- },
- {
- "name": "godot_vector3_bounce",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_n"]
- ]
- },
- {
- "name": "godot_vector3_reflect",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_n"]
- ]
- },
- {
- "name": "godot_vector3_operator_add",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_operator_subtract",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_operator_multiply_vector",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_operator_multiply_scalar",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_real", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_operator_divide_vector",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_operator_divide_scalar",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_real", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_operator_less",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3 *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3_operator_neg",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3 *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3_set_axis",
- "return_type": "void",
- "arguments": [
- ["godot_vector3 *", "p_self"],
- ["const godot_vector3_axis", "p_axis"],
- ["const godot_real", "p_val"]
- ]
- },
- {
- "name": "godot_vector3_get_axis",
- "return_type": "godot_real",
- "arguments": [
- ["const godot_vector3 *", "p_self"],
- ["const godot_vector3_axis", "p_axis"]
- ]
- },
- {
- "name": "godot_vector3i_new",
- "return_type": "void",
- "arguments": [
- ["godot_vector3i *", "r_dest"],
- ["const godot_int", "p_x"],
- ["const godot_int", "p_y"],
- ["const godot_int", "p_z"]
- ]
- },
- {
- "name": "godot_vector3i_as_string",
- "return_type": "godot_string",
- "arguments": [
- ["const godot_vector3i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3i_as_vector3",
- "return_type": "godot_vector3",
- "arguments": [
- ["const godot_vector3i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3i_min_axis",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_vector3i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3i_max_axis",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_vector3i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3i_abs",
- "return_type": "godot_vector3i",
- "arguments": [
- ["const godot_vector3i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3i_sign",
- "return_type": "godot_vector3i",
- "arguments": [
- ["const godot_vector3i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3i_operator_add",
- "return_type": "godot_vector3i",
- "arguments": [
- ["const godot_vector3i *", "p_self"],
- ["const godot_vector3i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3i_operator_subtract",
- "return_type": "godot_vector3i",
- "arguments": [
- ["const godot_vector3i *", "p_self"],
- ["const godot_vector3i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3i_operator_multiply_vector",
- "return_type": "godot_vector3i",
- "arguments": [
- ["const godot_vector3i *", "p_self"],
- ["const godot_vector3i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3i_operator_multiply_scalar",
- "return_type": "godot_vector3i",
- "arguments": [
- ["const godot_vector3i *", "p_self"],
- ["const godot_int", "p_b"]
- ]
- },
- {
- "name": "godot_vector3i_operator_divide_vector",
- "return_type": "godot_vector3i",
- "arguments": [
- ["const godot_vector3i *", "p_self"],
- ["const godot_vector3i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3i_operator_divide_scalar",
- "return_type": "godot_vector3i",
- "arguments": [
- ["const godot_vector3i *", "p_self"],
- ["const godot_int", "p_b"]
- ]
- },
- {
- "name": "godot_vector3i_operator_equal",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_vector3i *", "p_self"],
- ["const godot_vector3i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3i_operator_less",
- "return_type": "godot_bool",
- "arguments": [
- ["const godot_vector3i *", "p_self"],
- ["const godot_vector3i *", "p_b"]
- ]
- },
- {
- "name": "godot_vector3i_operator_neg",
- "return_type": "godot_vector3i",
- "arguments": [
- ["const godot_vector3i *", "p_self"]
- ]
- },
- {
- "name": "godot_vector3i_set_axis",
- "return_type": "void",
- "arguments": [
- ["godot_vector3i *", "p_self"],
- ["const godot_vector3_axis", "p_axis"],
- ["const godot_int", "p_val"]
- ]
- },
- {
- "name": "godot_vector3i_get_axis",
- "return_type": "godot_int",
- "arguments": [
- ["const godot_vector3i *", "p_self"],
- ["const godot_vector3_axis", "p_axis"]
- ]
- },
- {
- "name": "godot_global_get_singleton",
- "return_type": "godot_object *",
- "arguments": [
- ["char *", "p_name"]
- ]
- },
- {
- "name": "godot_get_class_tag",
- "return_type": "void *",
- "arguments": [
- ["const godot_string_name *", "p_class"]
- ]
- },
- {
- "name": "godot_object_cast_to",
- "return_type": "godot_object *",
- "arguments": [
- ["const godot_object *", "p_object"],
- ["void *", "p_class_tag"]
- ]
- },
- {
- "name": "godot_object_get_instance_id",
- "return_type": "uint64_t",
- "arguments": [
- ["const godot_object *", "p_object"]
- ]
- },
- {
- "name": "godot_instance_from_id",
- "return_type": "godot_object *",
- "arguments": [
- ["uint64_t", "p_instance_id"]
- ]
- },
- {
- "name": "godot_object_destroy",
- "return_type": "void",
- "arguments": [
- ["godot_object *", "p_o"]
- ]
- },
- {
- "name": "godot_method_bind_get_method",
- "return_type": "godot_method_bind *",
- "arguments": [
- ["const char *", "p_classname"],
- ["const char *", "p_methodname"]
- ]
- },
- {
- "name": "godot_method_bind_ptrcall",
- "return_type": "void",
- "arguments": [
- ["godot_method_bind *", "p_method_bind"],
- ["godot_object *", "p_instance"],
- ["const void **", "p_args"],
- ["void *", "p_ret"]
- ]
- },
- {
- "name": "godot_method_bind_call",
- "return_type": "godot_variant",
- "arguments": [
- ["godot_method_bind *", "p_method_bind"],
- ["godot_object *", "p_instance"],
- ["const godot_variant **", "p_args"],
- ["const int", "p_arg_count"],
- ["godot_variant_call_error *", "p_call_error"]
- ]
- },
- {
- "name": "godot_get_class_constructor",
- "return_type": "godot_class_constructor",
- "arguments": [
- ["const char *", "p_classname"]
- ]
- },
- {
- "name": "godot_get_global_constants",
- "return_type": "godot_dictionary",
- "arguments": [
- ]
- },
- {
- "name": "godot_register_native_call_type",
- "return_type": "void",
- "arguments": [
- ["const char *", "call_type"],
- ["native_call_cb", "p_callback"]
- ]
- },
- {
- "name": "godot_alloc",
- "return_type": "void *",
- "arguments": [
- ["int", "p_bytes"]
- ]
- },
- {
- "name": "godot_realloc",
- "return_type": "void *",
- "arguments": [
- ["void *", "p_ptr"],
- ["int", "p_bytes"]
- ]
- },
- {
- "name": "godot_free",
- "return_type": "void",
- "arguments": [
- ["void *", "p_ptr"]
- ]
- },
- {
- "name": "godot_print_error",
- "return_type": "void",
- "arguments": [
- ["const char *", "p_description"],
- ["const char *", "p_function"],
- ["const char *", "p_file"],
- ["int", "p_line"]
- ]
- },
- {
- "name": "godot_print_warning",
- "return_type": "void",
- "arguments": [
- ["const char *", "p_description"],
- ["const char *", "p_function"],
- ["const char *", "p_file"],
- ["int", "p_line"]
- ]
- },
- {
- "name": "godot_print",
- "return_type": "void",
- "arguments": [
- ["const godot_string *", "p_message"]
- ]
- }
- ]
- },
- "extensions": [
- {
- "name": "nativescript",
- "type": "NATIVESCRIPT",
- "version": {
- "major": 4,
- "minor": 0
- },
- "next": null,
- "api": [
- {
- "name": "godot_nativescript_register_class",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_base"],
- ["godot_nativescript_instance_create_func", "p_create_func"],
- ["godot_nativescript_instance_destroy_func", "p_destroy_func"]
- ]
- },
- {
- "name": "godot_nativescript_register_tool_class",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_base"],
- ["godot_nativescript_instance_create_func", "p_create_func"],
- ["godot_nativescript_instance_destroy_func", "p_destroy_func"]
- ]
- },
- {
- "name": "godot_nativescript_register_method",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_function_name"],
- ["godot_nativescript_method_attributes", "p_attr"],
- ["godot_nativescript_instance_method", "p_method"]
- ]
- },
- {
- "name": "godot_nativescript_set_method_argument_information",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_function_name"],
- ["int", "p_num_args"],
- ["const godot_nativescript_method_argument *", "p_args"]
- ]
- },
- {
- "name": "godot_nativescript_register_property",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_path"],
- ["godot_nativescript_property_attributes *", "p_attr"],
- ["godot_nativescript_property_set_func", "p_set_func"],
- ["godot_nativescript_property_get_func", "p_get_func"]
- ]
- },
- {
- "name": "godot_nativescript_register_signal",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const godot_nativescript_signal *", "p_signal"]
- ]
- },
- {
- "name": "godot_nativescript_get_userdata",
- "return_type": "void *",
- "arguments": [
- ["godot_object *", "p_instance"]
- ]
- },
- {
- "name": "godot_nativescript_set_class_documentation",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["godot_string", "p_documentation"]
- ]
- },
- {
- "name": "godot_nativescript_set_method_documentation",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_function_name"],
- ["godot_string", "p_documentation"]
- ]
- },
- {
- "name": "godot_nativescript_set_property_documentation",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_path"],
- ["godot_string", "p_documentation"]
- ]
- },
- {
- "name": "godot_nativescript_set_signal_documentation",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const char *", "p_signal_name"],
- ["godot_string", "p_documentation"]
- ]
- },
- {
- "name": "godot_nativescript_set_global_type_tag",
- "return_type": "void",
- "arguments": [
- ["int", "p_idx"],
- ["const char *", "p_name"],
- ["const void *", "p_type_tag"]
- ]
- },
- {
- "name": "godot_nativescript_get_global_type_tag",
- "return_type": "const void *",
- "arguments": [
- ["int", "p_idx"],
- ["const char *", "p_name"]
- ]
- },
- {
- "name": "godot_nativescript_set_type_tag",
- "return_type": "void",
- "arguments": [
- ["void *", "p_gdnative_handle"],
- ["const char *", "p_name"],
- ["const void *", "p_type_tag"]
- ]
- },
- {
- "name": "godot_nativescript_get_type_tag",
- "return_type": "const void *",
- "arguments": [
- ["const godot_object *", "p_object"]
- ]
- },
- {
- "name": "godot_nativescript_register_instance_binding_data_functions",
- "return_type": "int",
- "arguments": [
- ["godot_nativescript_instance_binding_functions", "p_binding_functions"]
- ]
- },
- {
- "name": "godot_nativescript_unregister_instance_binding_data_functions",
- "return_type": "void",
- "arguments": [
- ["int", "p_idx"]
- ]
- },
- {
- "name": "godot_nativescript_get_instance_binding_data",
- "return_type": "void *",
- "arguments": [
- ["int", "p_idx"],
- ["godot_object *", "p_object"]
- ]
- },
- {
- "name": "godot_nativescript_profiling_add_data",
- "return_type": "void",
- "arguments": [
- ["const char *", "p_signature"],
- ["uint64_t", "p_line"]
- ]
- }
- ]
- },
- {
- "name": "pluginscript",
- "type": "PLUGINSCRIPT",
- "version": {
- "major": 1,
- "minor": 0
- },
- "next": null,
- "api": [
- {
- "name": "godot_pluginscript_register_language",
- "return_type": "void",
- "arguments": [
- ["const godot_pluginscript_language_desc *", "language_desc"]
- ]
- }
- ]
- },
- {
- "name": "android",
- "type": "ANDROID",
- "version": {
- "major": 1,
- "minor": 1
- },
- "next": null,
- "api": [
- {
- "name": "godot_android_get_env",
- "return_type": "JNIEnv*",
- "arguments": [
- ]
- },
- {
- "name": "godot_android_get_activity",
- "return_type": "jobject",
- "arguments": [
- ]
- },
- {
- "name": "godot_android_get_surface",
- "return_type": "jobject",
- "arguments": [
- ]
- },
- {
- "name": "godot_android_is_activity_resumed",
- "return_type": "bool",
- "arguments": [
- ]
- }
- ]
- },
- {
- "name": "xr",
- "type": "XR",
- "version": {
- "major": 1,
- "minor": 1
- },
- "next": null,
- "api": [
- {
- "name": "godot_xr_register_interface",
- "return_type": "void",
- "arguments": [
- ["const godot_xr_interface_gdnative *", "p_interface"]
- ]
- },
- {
- "name": "godot_xr_get_worldscale",
- "return_type": "godot_real",
- "arguments": []
- },
- {
- "name": "godot_xr_get_reference_frame",
- "return_type": "godot_transform",
- "arguments": []
- },
- {
- "name": "godot_xr_blit",
- "return_type": "void",
- "arguments": [
- ["godot_int", "p_eye"],
- ["godot_rid *", "p_render_target"],
- ["godot_rect2 *", "p_screen_rect"]
- ]
- },
- {
- "name": "godot_xr_get_texid",
- "return_type": "godot_int",
- "arguments": [
- ["godot_rid *", "p_render_target"]
- ]
- },
- {
- "name": "godot_xr_add_controller",
- "return_type": "godot_int",
- "arguments": [
- ["char *", "p_device_name"],
- ["godot_int", "p_hand"],
- ["godot_bool", "p_tracks_orientation"],
- ["godot_bool", "p_tracks_position"]
- ]
- },
- {
- "name": "godot_xr_remove_controller",
- "return_type": "void",
- "arguments": [
- ["godot_int", "p_controller_id"]
- ]
- },
- {
- "name": "godot_xr_set_controller_transform",
- "return_type": "void",
- "arguments": [
- ["godot_int", "p_controller_id"],
- ["godot_transform *", "p_transform"],
- ["godot_bool", "p_tracks_orientation"],
- ["godot_bool", "p_tracks_position"]
- ]
- },
- {
- "name": "godot_xr_set_controller_button",
- "return_type": "void",
- "arguments": [
- ["godot_int", "p_controller_id"],
- ["godot_int", "p_button"],
- ["godot_bool", "p_is_pressed"]
- ]
- },
- {
- "name": "godot_xr_set_controller_axis",
- "return_type": "void",
- "arguments": [
- ["godot_int", "p_controller_id"],
- ["godot_int", "p_exis"],
- ["godot_real", "p_value"],
- ["godot_bool", "p_can_be_negative"]
- ]
- },
- {
- "name": "godot_xr_get_controller_rumble",
- "return_type": "godot_real",
- "arguments": [
- ["godot_int", "p_controller_id"]
- ]
- }
- ]
- },
- {
- "name": "videodecoder",
- "type": "VIDEODECODER",
- "version": {
- "major": 0,
- "minor": 1
- },
- "next": null,
- "api": [
- {
- "name": "godot_videodecoder_file_read",
- "return_type": "godot_int",
- "arguments": [
- ["void *", "file_ptr"],
- ["uint8_t *", "buf"],
- ["int", "buf_size"]
- ]
- },
- {
- "name": "godot_videodecoder_file_seek",
- "return_type": "int64_t",
- "arguments": [
- [ "void *", "file_ptr"],
- ["int64_t", "pos"],
- ["int", "whence"]
- ]
- },
- {
- "name": "godot_videodecoder_register_decoder",
- "return_type": "void",
- "arguments": [
- ["const godot_videodecoder_interface_gdnative *", "p_interface"]
- ]
- }
- ]
- },
- {
- "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"]
- ]
- }
- ]
- }
- ]
+ "core": {
+ "type": "CORE",
+ "version": {
+ "major": 4,
+ "minor": 0
+ },
+ "next": null,
+ "api": [
+ {
+ "name": "godot_object_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_object *",
+ "p_o"
+ ]
+ ]
+ },
+ {
+ "name": "godot_global_get_singleton",
+ "return_type": "godot_object *",
+ "arguments": [
+ [
+ "char *",
+ "p_name"
+ ]
+ ]
+ },
+ {
+ "name": "godot_method_bind_get_method",
+ "return_type": "godot_method_bind *",
+ "arguments": [
+ [
+ "const char *",
+ "p_classname"
+ ],
+ [
+ "const char *",
+ "p_methodname"
+ ]
+ ]
+ },
+ {
+ "name": "godot_method_bind_ptrcall",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_method_bind *",
+ "p_method_bind"
+ ],
+ [
+ "godot_object *",
+ "p_instance"
+ ],
+ [
+ "const void **",
+ "p_args"
+ ],
+ [
+ "void *",
+ "p_ret"
+ ]
+ ]
+ },
+ {
+ "name": "godot_method_bind_call",
+ "return_type": "godot_variant",
+ "arguments": [
+ [
+ "godot_method_bind *",
+ "p_method_bind"
+ ],
+ [
+ "godot_object *",
+ "p_instance"
+ ],
+ [
+ "const godot_variant **",
+ "p_args"
+ ],
+ [
+ "const int",
+ "p_arg_count"
+ ],
+ [
+ "godot_variant_call_error *",
+ "p_call_error"
+ ]
+ ]
+ },
+ {
+ "name": "godot_get_class_constructor",
+ "return_type": "godot_class_constructor",
+ "arguments": [
+ [
+ "const char *",
+ "p_classname"
+ ]
+ ]
+ },
+ {
+ "name": "godot_get_global_constants",
+ "return_type": "godot_dictionary",
+ "arguments": []
+ },
+ {
+ "name": "godot_register_native_call_type",
+ "return_type": "void",
+ "arguments": [
+ [
+ "const char *",
+ "call_type"
+ ],
+ [
+ "native_call_cb",
+ "p_callback"
+ ]
+ ]
+ },
+ {
+ "name": "godot_alloc",
+ "return_type": "void *",
+ "arguments": [
+ [
+ "int",
+ "p_bytes"
+ ]
+ ]
+ },
+ {
+ "name": "godot_realloc",
+ "return_type": "void *",
+ "arguments": [
+ [
+ "void *",
+ "p_ptr"
+ ],
+ [
+ "int",
+ "p_bytes"
+ ]
+ ]
+ },
+ {
+ "name": "godot_free",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_ptr"
+ ]
+ ]
+ },
+ {
+ "name": "godot_print_error",
+ "return_type": "void",
+ "arguments": [
+ [
+ "const char *",
+ "p_description"
+ ],
+ [
+ "const char *",
+ "p_function"
+ ],
+ [
+ "const char *",
+ "p_file"
+ ],
+ [
+ "int",
+ "p_line"
+ ]
+ ]
+ },
+ {
+ "name": "godot_print_warning",
+ "return_type": "void",
+ "arguments": [
+ [
+ "const char *",
+ "p_description"
+ ],
+ [
+ "const char *",
+ "p_function"
+ ],
+ [
+ "const char *",
+ "p_file"
+ ],
+ [
+ "int",
+ "p_line"
+ ]
+ ]
+ },
+ {
+ "name": "godot_print_script_error",
+ "return_type": "void",
+ "arguments": [
+ [
+ "const char *",
+ "p_description"
+ ],
+ [
+ "const char *",
+ "p_function"
+ ],
+ [
+ "const char *",
+ "p_file"
+ ],
+ [
+ "int",
+ "p_line"
+ ]
+ ]
+ },
+ {
+ "name": "godot_get_class_tag",
+ "return_type": "void *",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_class"
+ ]
+ ]
+ },
+ {
+ "name": "godot_object_cast_to",
+ "return_type": "godot_object *",
+ "arguments": [
+ [
+ "const godot_object *",
+ "p_object"
+ ],
+ [
+ "void *",
+ "p_class_tag"
+ ]
+ ]
+ },
+ {
+ "name": "godot_instance_from_id",
+ "return_type": "godot_object *",
+ "arguments": [
+ [
+ "uint64_t",
+ "p_instance_id"
+ ]
+ ]
+ },
+ {
+ "name": "godot_object_get_instance_id",
+ "return_type": "uint64_t",
+ "arguments": [
+ [
+ "const godot_object *",
+ "p_object"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_variant *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_nil",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_bool",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_bool",
+ "p_b"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_int",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const int64_t",
+ "p_i"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_float",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const double",
+ "p_f"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_string",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_string *",
+ "p_s"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_string_name",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_string_name *",
+ "p_s"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_vector2",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_vector2 *",
+ "p_v2"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_vector2i",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_vector2i *",
+ "p_v2"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_rect2",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_rect2 *",
+ "p_rect2"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_rect2i",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_rect2i *",
+ "p_rect2"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_vector3",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_vector3 *",
+ "p_v3"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_vector3i",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_vector3i *",
+ "p_v3"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_transform2d",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_transform2d *",
+ "p_t2d"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_plane",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_plane *",
+ "p_plane"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_quaternion",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_quaternion *",
+ "p_quaternion"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_aabb",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_aabb *",
+ "p_aabb"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_basis",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_basis *",
+ "p_basis"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_transform3d",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_transform3d *",
+ "p_trans"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_color",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_color *",
+ "p_color"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_node_path",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_node_path *",
+ "p_np"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_rid",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_rid *",
+ "p_rid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_object",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_object *",
+ "p_obj"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_callable",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_callable *",
+ "p_cb"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_signal",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_signal *",
+ "p_signal"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_dictionary",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_dictionary *",
+ "p_dict"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_array",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_array *",
+ "p_arr"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_packed_byte_array",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_byte_array *",
+ "p_pba"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_packed_int32_array",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_int32_array *",
+ "p_pia"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_packed_int64_array",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_int64_array *",
+ "p_pia"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_packed_float32_array",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_float32_array *",
+ "p_pra"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_packed_float64_array",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_float64_array *",
+ "p_pra"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_packed_string_array",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_string_array *",
+ "p_psa"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_packed_vector2_array",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_vector2_array *",
+ "p_pv2a"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_packed_vector3_array",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_vector3_array *",
+ "p_pv3a"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_new_packed_color_array",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_color_array *",
+ "p_pca"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_bool",
+ "return_type": "godot_bool",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_int",
+ "return_type": "int64_t",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_float",
+ "return_type": "double",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_string",
+ "return_type": "godot_string",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_string_name",
+ "return_type": "godot_string_name",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_vector2",
+ "return_type": "godot_vector2",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_vector2i",
+ "return_type": "godot_vector2i",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_rect2",
+ "return_type": "godot_rect2",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_rect2i",
+ "return_type": "godot_rect2i",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_vector3",
+ "return_type": "godot_vector3",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_vector3i",
+ "return_type": "godot_vector3i",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_transform2d",
+ "return_type": "godot_transform2d",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_plane",
+ "return_type": "godot_plane",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_quaternion",
+ "return_type": "godot_quaternion",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_aabb",
+ "return_type": "godot_aabb",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_basis",
+ "return_type": "godot_basis",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_transform3d",
+ "return_type": "godot_transform3d",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_color",
+ "return_type": "godot_color",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_node_path",
+ "return_type": "godot_node_path",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_rid",
+ "return_type": "godot_rid",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_object",
+ "return_type": "godot_object *",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_callable",
+ "return_type": "godot_callable",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_signal",
+ "return_type": "godot_signal",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_dictionary",
+ "return_type": "godot_dictionary",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_array",
+ "return_type": "godot_array",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_packed_byte_array",
+ "return_type": "godot_packed_byte_array",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_packed_int32_array",
+ "return_type": "godot_packed_int32_array",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_packed_int64_array",
+ "return_type": "godot_packed_int64_array",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_packed_float32_array",
+ "return_type": "godot_packed_float32_array",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_packed_float64_array",
+ "return_type": "godot_packed_float64_array",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_packed_string_array",
+ "return_type": "godot_packed_string_array",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_packed_vector2_array",
+ "return_type": "godot_packed_vector2_array",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_packed_vector3_array",
+ "return_type": "godot_packed_vector3_array",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_as_packed_color_array",
+ "return_type": "godot_packed_color_array",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_call",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "p_self"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ],
+ [
+ "const godot_variant **",
+ "p_args"
+ ],
+ [
+ "const godot_int",
+ "p_argument_count"
+ ],
+ [
+ "godot_variant *",
+ "r_return"
+ ],
+ [
+ "godot_variant_call_error *",
+ "r_error"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_call_with_cstring",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "p_self"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ],
+ [
+ "const godot_variant **",
+ "p_args"
+ ],
+ [
+ "const godot_int",
+ "p_argument_count"
+ ],
+ [
+ "godot_variant *",
+ "r_return"
+ ],
+ [
+ "godot_variant_call_error *",
+ "r_error"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_evaluate",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant_operator",
+ "p_op"
+ ],
+ [
+ "const godot_variant *",
+ "p_a"
+ ],
+ [
+ "const godot_variant *",
+ "p_b"
+ ],
+ [
+ "godot_variant *",
+ "r_return"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_set",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "p_self"
+ ],
+ [
+ "const godot_variant *",
+ "p_key"
+ ],
+ [
+ "const godot_variant *",
+ "p_value"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_set_named",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "p_self"
+ ],
+ [
+ "const godot_string_name *",
+ "p_key"
+ ],
+ [
+ "const godot_variant *",
+ "p_value"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_set_named_with_cstring",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "p_self"
+ ],
+ [
+ "const char *",
+ "p_key"
+ ],
+ [
+ "const godot_variant *",
+ "p_value"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_set_keyed",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "p_self"
+ ],
+ [
+ "const godot_variant *",
+ "p_key"
+ ],
+ [
+ "const godot_variant *",
+ "p_value"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_set_indexed",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ],
+ [
+ "const godot_variant *",
+ "p_value"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ],
+ [
+ "bool *",
+ "r_oob"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get",
+ "return_type": "godot_variant",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "const godot_variant *",
+ "p_key"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_named",
+ "return_type": "godot_variant",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "const godot_string_name *",
+ "p_key"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_named_with_cstring",
+ "return_type": "godot_variant",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "const char *",
+ "p_key"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_keyed",
+ "return_type": "godot_variant",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "const godot_variant *",
+ "p_key"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_indexed",
+ "return_type": "godot_variant",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ],
+ [
+ "bool *",
+ "r_oob"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_iter_init",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "godot_variant *",
+ "r_iter"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_iter_next",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "godot_variant *",
+ "r_iter"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_iter_get",
+ "return_type": "godot_variant",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "godot_variant *",
+ "r_iter"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_hash_compare",
+ "return_type": "godot_bool",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "const godot_variant *",
+ "p_other"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_booleanize",
+ "return_type": "godot_bool",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_blend",
+ "return_type": "void",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_a"
+ ],
+ [
+ "const godot_variant *",
+ "p_b"
+ ],
+ [
+ "float",
+ "p_c"
+ ],
+ [
+ "godot_variant *",
+ "r_dst"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_interpolate",
+ "return_type": "void",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_a"
+ ],
+ [
+ "const godot_variant *",
+ "p_b"
+ ],
+ [
+ "float",
+ "p_c"
+ ],
+ [
+ "godot_variant *",
+ "r_dst"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_duplicate",
+ "return_type": "godot_variant",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "godot_bool",
+ "p_deep"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_stringify",
+ "return_type": "godot_string",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_operator_evaluator",
+ "return_type": "godot_validated_operator_evaluator",
+ "arguments": [
+ [
+ "godot_variant_operator",
+ "p_operator"
+ ],
+ [
+ "godot_variant_type",
+ "p_type_a"
+ ],
+ [
+ "godot_variant_type",
+ "p_type_b"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_operator_evaluator",
+ "return_type": "godot_ptr_operator_evaluator",
+ "arguments": [
+ [
+ "godot_variant_operator",
+ "p_operator"
+ ],
+ [
+ "godot_variant_type",
+ "p_type_a"
+ ],
+ [
+ "godot_variant_type",
+ "p_type_b"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_operator_return_type",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "godot_variant_operator",
+ "p_operator"
+ ],
+ [
+ "godot_variant_type",
+ "p_type_a"
+ ],
+ [
+ "godot_variant_type",
+ "p_type_b"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_operator_name",
+ "return_type": "godot_string",
+ "arguments": [
+ [
+ "godot_variant_operator",
+ "p_operator"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_builtin_method",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_builtin_method_with_cstring",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_builtin_method",
+ "return_type": "godot_validated_builtin_method",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_builtin_method_with_cstring",
+ "return_type": "godot_validated_builtin_method",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_builtin_method",
+ "return_type": "godot_ptr_builtin_method",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_builtin_method_with_cstring",
+ "return_type": "godot_ptr_builtin_method",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_builtin_method_argument_count",
+ "return_type": "int",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_builtin_method_argument_count_with_cstring",
+ "return_type": "int",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_builtin_method_argument_type",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ],
+ [
+ "int",
+ "p_argument"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_builtin_method_argument_type_with_cstring",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ],
+ [
+ "int",
+ "p_argument"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_builtin_method_argument_name",
+ "return_type": "godot_string",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ],
+ [
+ "int",
+ "p_argument"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_builtin_method_argument_name_with_cstring",
+ "return_type": "godot_string",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ],
+ [
+ "int",
+ "p_argument"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_builtin_method_return_value",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_builtin_method_return_value_with_cstring",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_builtin_method_return_type",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_builtin_method_return_type_with_cstring",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_is_builtin_method_const",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_is_builtin_method_const_with_cstring",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_is_builtin_method_static",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_is_builtin_method_static_with_cstring",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_is_builtin_method_vararg",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_is_builtin_method_vararg_with_cstring",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_builtin_method_count",
+ "return_type": "int",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_builtin_method_list",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "godot_string_name *",
+ "r_list"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_constructor_count",
+ "return_type": "int",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_constructor",
+ "return_type": "godot_validated_constructor",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "int",
+ "p_constructor"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_constructor",
+ "return_type": "godot_ptr_constructor",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "int",
+ "p_constructor"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_constructor_argument_count",
+ "return_type": "int",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "int",
+ "p_constructor"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_constructor_argument_type",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "int",
+ "p_constructor"
+ ],
+ [
+ "int",
+ "p_argument"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_constructor_argument_name",
+ "return_type": "godot_string",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "int",
+ "p_constructor"
+ ],
+ [
+ "int",
+ "p_argument"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_construct",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "godot_variant *",
+ "p_base"
+ ],
+ [
+ "const godot_variant **",
+ "p_args"
+ ],
+ [
+ "int",
+ "p_argument_count"
+ ],
+ [
+ "godot_variant_call_error *",
+ "r_error"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_member_type",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_member"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_member_type_with_cstring",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_member"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_member_count",
+ "return_type": "int",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_member_list",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "godot_string_name *",
+ "r_list"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_setter",
+ "return_type": "godot_validated_setter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_member"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_setter_with_cstring",
+ "return_type": "godot_validated_setter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_member"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_getter",
+ "return_type": "godot_validated_getter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_member"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_getter_with_cstring",
+ "return_type": "godot_validated_getter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_member"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_setter",
+ "return_type": "godot_ptr_setter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_member"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_setter_with_cstring",
+ "return_type": "godot_ptr_setter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_member"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_getter",
+ "return_type": "godot_ptr_getter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_member"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_getter_with_cstring",
+ "return_type": "godot_ptr_getter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_member"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_indexing",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_indexed_element_type",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_indexed_setter",
+ "return_type": "godot_validated_indexed_setter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_indexed_getter",
+ "return_type": "godot_validated_indexed_getter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_indexed_setter",
+ "return_type": "godot_ptr_indexed_setter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_indexed_getter",
+ "return_type": "godot_ptr_indexed_getter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_indexed_size",
+ "return_type": "uint64_t",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_is_keyed",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_keyed_setter",
+ "return_type": "godot_validated_keyed_setter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_keyed_getter",
+ "return_type": "godot_validated_keyed_getter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_keyed_checker",
+ "return_type": "godot_validated_keyed_checker",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_keyed_setter",
+ "return_type": "godot_ptr_keyed_setter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_keyed_getter",
+ "return_type": "godot_ptr_keyed_getter",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_keyed_checker",
+ "return_type": "godot_ptr_keyed_checker",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_constants_count",
+ "return_type": "int",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_constants_list",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "godot_string_name *",
+ "r_list"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_constant",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_constant"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_constant_with_cstring",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_constant"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_constant_value",
+ "return_type": "godot_variant",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_constant"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_constant_value_with_cstring",
+ "return_type": "godot_variant",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const char *",
+ "p_constant"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_utility_function",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_utility_function_with_cstring",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "const char *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_call_utility_function",
+ "return_type": "void",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_function"
+ ],
+ [
+ "godot_variant *",
+ "r_ret"
+ ],
+ [
+ "const godot_variant **",
+ "p_args"
+ ],
+ [
+ "int",
+ "p_argument_count"
+ ],
+ [
+ "godot_variant_call_error *",
+ "r_error"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_call_utility_function_with_cstring",
+ "return_type": "void",
+ "arguments": [
+ [
+ "const char *",
+ "p_function"
+ ],
+ [
+ "godot_variant *",
+ "r_ret"
+ ],
+ [
+ "const godot_variant **",
+ "p_args"
+ ],
+ [
+ "int",
+ "p_argument_count"
+ ],
+ [
+ "godot_variant_call_error *",
+ "r_error"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_utility_function",
+ "return_type": "godot_ptr_utility_function",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_ptr_utility_function_with_cstring",
+ "return_type": "godot_ptr_utility_function",
+ "arguments": [
+ [
+ "const char *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_utility_function",
+ "return_type": "godot_validated_utility_function",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_validated_utility_function_with_cstring",
+ "return_type": "godot_validated_utility_function",
+ "arguments": [
+ [
+ "const char *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_utility_function_type",
+ "return_type": "godot_variant_utility_function_type",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_utility_function_type_with_cstring",
+ "return_type": "godot_variant_utility_function_type",
+ "arguments": [
+ [
+ "const char *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_utility_function_argument_count",
+ "return_type": "int",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_utility_function_argument_count_with_cstring",
+ "return_type": "int",
+ "arguments": [
+ [
+ "const char *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_utility_function_argument_type",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_function"
+ ],
+ [
+ "int",
+ "p_argument"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_utility_function_argument_type_with_cstring",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "const char *",
+ "p_function"
+ ],
+ [
+ "int",
+ "p_argument"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_utility_function_argument_name",
+ "return_type": "godot_string",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_function"
+ ],
+ [
+ "int",
+ "p_argument"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_utility_function_argument_name_with_cstring",
+ "return_type": "godot_string",
+ "arguments": [
+ [
+ "const char *",
+ "p_function"
+ ],
+ [
+ "int",
+ "p_argument"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_utility_function_return_value",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_utility_function_return_value_with_cstring",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "const char *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_utility_function_return_type",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_utility_function_return_type_with_cstring",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "const char *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_is_utility_function_vararg",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "const godot_string_name *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_is_utility_function_vararg_with_cstring",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "const char *",
+ "p_function"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_utility_function_count",
+ "return_type": "int",
+ "arguments": []
+ },
+ {
+ "name": "godot_variant_get_utility_function_list",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string_name *",
+ "r_functions"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_type",
+ "return_type": "godot_variant_type",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_method",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "const godot_string_name *",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_member",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ],
+ [
+ "const godot_string_name *",
+ "p_member"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_has_key",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "const godot_variant *",
+ "p_self"
+ ],
+ [
+ "const godot_variant *",
+ "p_key"
+ ],
+ [
+ "bool *",
+ "r_valid"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_get_type_name",
+ "return_type": "godot_string",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_type"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_can_convert",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_from"
+ ],
+ [
+ "godot_variant_type",
+ "p_to"
+ ]
+ ]
+ },
+ {
+ "name": "godot_variant_can_convert_strict",
+ "return_type": "bool",
+ "arguments": [
+ [
+ "godot_variant_type",
+ "p_from"
+ ],
+ [
+ "godot_variant_type",
+ "p_to"
+ ]
+ ]
+ },
+ {
+ "name": "godot_aabb_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_aabb *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_aabb_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_aabb *",
+ "r_dest"
+ ],
+ [
+ "const godot_aabb *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_array_operator_index",
+ "return_type": "godot_variant *",
+ "arguments": [
+ [
+ "godot_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_array_operator_index_const",
+ "return_type": "const godot_variant *",
+ "arguments": [
+ [
+ "const godot_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_basis_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_basis *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_basis_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_basis *",
+ "r_dest"
+ ],
+ [
+ "const godot_basis *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_basis_operator_index",
+ "return_type": "godot_vector3 *",
+ "arguments": [
+ [
+ "godot_basis *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_basis_operator_index_const",
+ "return_type": "const godot_vector3 *",
+ "arguments": [
+ [
+ "const godot_basis *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_callable_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_callable *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_callable_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_callable *",
+ "r_dest"
+ ],
+ [
+ "const godot_callable *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_callable_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_callable *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_color_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_color *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_color_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_color *",
+ "r_dest"
+ ],
+ [
+ "const godot_color *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_color_operator_index",
+ "return_type": "float *",
+ "arguments": [
+ [
+ "godot_color *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_color_operator_index_const",
+ "return_type": "const float *",
+ "arguments": [
+ [
+ "const godot_color *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_dictionary_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_dictionary *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_dictionary_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_dictionary *",
+ "r_dest"
+ ],
+ [
+ "const godot_dictionary *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_dictionary_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_dictionary *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_dictionary_operator_index",
+ "return_type": "godot_variant *",
+ "arguments": [
+ [
+ "godot_dictionary *",
+ "p_self"
+ ],
+ [
+ "const godot_variant *",
+ "p_key"
+ ]
+ ]
+ },
+ {
+ "name": "godot_dictionary_operator_index_const",
+ "return_type": "const godot_variant *",
+ "arguments": [
+ [
+ "const godot_dictionary *",
+ "p_self"
+ ],
+ [
+ "const godot_variant *",
+ "p_key"
+ ]
+ ]
+ },
+ {
+ "name": "godot_node_path_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_node_path *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_node_path_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_node_path *",
+ "r_dest"
+ ],
+ [
+ "const godot_node_path *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_node_path_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_node_path *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_byte_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_byte_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_byte_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_byte_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_byte_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_byte_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_byte_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_byte_array_operator_index",
+ "return_type": "uint8_t *",
+ "arguments": [
+ [
+ "godot_packed_byte_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_byte_array_operator_index_const",
+ "return_type": "const uint8_t *",
+ "arguments": [
+ [
+ "const godot_packed_byte_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_int32_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_int32_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_int32_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_int32_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_operator_index",
+ "return_type": "int32_t *",
+ "arguments": [
+ [
+ "godot_packed_int32_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_int32_array_operator_index_const",
+ "return_type": "const int32_t *",
+ "arguments": [
+ [
+ "const godot_packed_int32_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_int64_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_int64_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_int64_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_int64_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_operator_index",
+ "return_type": "int64_t *",
+ "arguments": [
+ [
+ "godot_packed_int64_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_int64_array_operator_index_const",
+ "return_type": "const int64_t *",
+ "arguments": [
+ [
+ "const godot_packed_int64_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_float32_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_float32_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_float32_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_float32_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_operator_index",
+ "return_type": "float *",
+ "arguments": [
+ [
+ "godot_packed_float32_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_float32_array_operator_index_const",
+ "return_type": "const float *",
+ "arguments": [
+ [
+ "const godot_packed_float32_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_float64_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_float64_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_float64_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_float64_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_float64_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_float64_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_float64_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_float64_array_operator_index",
+ "return_type": "double *",
+ "arguments": [
+ [
+ "godot_packed_float64_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_float64_array_operator_index_const",
+ "return_type": "const double *",
+ "arguments": [
+ [
+ "const godot_packed_float64_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_string_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_string_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_string_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_string_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_string_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_string_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_string_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_string_array_operator_index",
+ "return_type": "godot_string *",
+ "arguments": [
+ [
+ "godot_packed_string_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_string_array_operator_index_const",
+ "return_type": "const godot_string *",
+ "arguments": [
+ [
+ "const godot_packed_string_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector2_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector2_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_vector2_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector2_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2_array_operator_index",
+ "return_type": "godot_vector2 *",
+ "arguments": [
+ [
+ "godot_packed_vector2_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2_array_operator_index_const",
+ "return_type": "const godot_vector2 *",
+ "arguments": [
+ [
+ "const godot_packed_vector2_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector2i_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector2i_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_vector2i_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector2i_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_operator_index",
+ "return_type": "godot_vector2i *",
+ "arguments": [
+ [
+ "godot_packed_vector2i_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector2i_array_operator_index_const",
+ "return_type": "const godot_vector2i *",
+ "arguments": [
+ [
+ "const godot_packed_vector2i_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector3_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector3_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_vector3_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector3_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3_array_operator_index",
+ "return_type": "godot_vector3 *",
+ "arguments": [
+ [
+ "godot_packed_vector3_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3_array_operator_index_const",
+ "return_type": "const godot_vector3 *",
+ "arguments": [
+ [
+ "const godot_packed_vector3_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3i_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector3i_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3i_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector3i_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_vector3i_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3i_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_vector3i_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3i_array_operator_index",
+ "return_type": "godot_vector3i *",
+ "arguments": [
+ [
+ "godot_packed_vector3i_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_vector3i_array_operator_index_const",
+ "return_type": "const godot_vector3i *",
+ "arguments": [
+ [
+ "const godot_packed_vector3i_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_color_array_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_color_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_color_array_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_color_array *",
+ "r_dest"
+ ],
+ [
+ "const godot_packed_color_array *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_color_array_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_packed_color_array *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_color_array_operator_index",
+ "return_type": "godot_color *",
+ "arguments": [
+ [
+ "godot_packed_color_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_packed_color_array_operator_index_const",
+ "return_type": "const godot_color *",
+ "arguments": [
+ [
+ "const godot_packed_color_array *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_plane_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_plane *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_plane_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_plane *",
+ "r_dest"
+ ],
+ [
+ "const godot_plane *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_quaternion_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_quaternion *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_quaternion_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_quaternion *",
+ "r_dest"
+ ],
+ [
+ "const godot_quaternion *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_quaternion_operator_index",
+ "return_type": "godot_real_t *",
+ "arguments": [
+ [
+ "godot_quaternion *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_quaternion_operator_index_const",
+ "return_type": "const godot_real_t *",
+ "arguments": [
+ [
+ "const godot_quaternion *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_rect2_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_rect2 *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_rect2_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_rect2 *",
+ "r_dest"
+ ],
+ [
+ "const godot_rect2 *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_rect2i_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_rect2i *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_rect2i_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_rect2i *",
+ "r_dest"
+ ],
+ [
+ "const godot_rect2i *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_rid_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_rid *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_rid_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_rid *",
+ "r_dest"
+ ],
+ [
+ "const godot_rid *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_signal_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_signal *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_signal_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_signal *",
+ "r_dest"
+ ],
+ [
+ "const godot_signal *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_signal_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_signal *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ],
+ [
+ "const godot_string *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_latin1_chars",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ],
+ [
+ "const char *",
+ "p_contents"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf8_chars",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ],
+ [
+ "const char *",
+ "p_contents"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf16_chars",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ],
+ [
+ "const char16_t *",
+ "p_contents"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf32_chars",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ],
+ [
+ "const char32_t *",
+ "p_contents"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_wide_chars",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ],
+ [
+ "const wchar_t *",
+ "p_contents"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_latin1_chars_and_len",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ],
+ [
+ "const char *",
+ "p_contents"
+ ],
+ [
+ "const int",
+ "p_size"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf8_chars_and_len",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ],
+ [
+ "const char *",
+ "p_contents"
+ ],
+ [
+ "const int",
+ "p_size"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf16_chars_and_len",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ],
+ [
+ "const char16_t *",
+ "p_contents"
+ ],
+ [
+ "const int",
+ "p_size"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_utf32_chars_and_len",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ],
+ [
+ "const char32_t *",
+ "p_contents"
+ ],
+ [
+ "const int",
+ "p_size"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_new_with_wide_chars_and_len",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string *",
+ "r_dest"
+ ],
+ [
+ "const wchar_t *",
+ "p_contents"
+ ],
+ [
+ "const int",
+ "p_size"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_to_latin1_chars",
+ "return_type": "const char *",
+ "arguments": [
+ [
+ "const godot_string *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_to_utf8_chars",
+ "return_type": "const char *",
+ "arguments": [
+ [
+ "const godot_string *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_to_utf16_chars",
+ "return_type": "const char16_t *",
+ "arguments": [
+ [
+ "const godot_string *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_to_utf32_chars",
+ "return_type": "const char32_t *",
+ "arguments": [
+ [
+ "const godot_string *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_to_wide_chars",
+ "return_type": "const wchar_t *",
+ "arguments": [
+ [
+ "const godot_string *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_operator_index",
+ "return_type": "char32_t *",
+ "arguments": [
+ [
+ "godot_string *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_operator_index_const",
+ "return_type": "const char32_t *",
+ "arguments": [
+ [
+ "const godot_string *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_name_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string_name *",
+ "r_dest"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_name_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string_name *",
+ "r_dest"
+ ],
+ [
+ "const godot_string_name *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_name_destroy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string_name *",
+ "p_self"
+ ]
+ ]
+ },
+ {
+ "name": "godot_string_name_new_with_latin1_chars",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_string_name *",
+ "r_dest"
+ ],
+ [
+ "const char *",
+ "p_contents"
+ ]
+ ]
+ },
+ {
+ "name": "godot_transform3d_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_transform3d *",
+ "r_dest"
+ ]
+ ]
+ },
+ {
+ "name": "godot_transform3d_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_transform3d *",
+ "r_dest"
+ ],
+ [
+ "const godot_transform3d *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_transform2d_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_transform2d *",
+ "r_dest"
+ ]
+ ]
+ },
+ {
+ "name": "godot_transform2d_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_transform2d *",
+ "r_dest"
+ ],
+ [
+ "const godot_transform2d *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_transform2d_operator_index",
+ "return_type": "godot_vector2 *",
+ "arguments": [
+ [
+ "godot_transform2d *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_transform2d_operator_index_const",
+ "return_type": "const godot_vector2 *",
+ "arguments": [
+ [
+ "const godot_transform2d *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector2_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_vector2 *",
+ "r_dest"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector2_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_vector2 *",
+ "r_dest"
+ ],
+ [
+ "const godot_vector2 *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector2_operator_index",
+ "return_type": "godot_real_t *",
+ "arguments": [
+ [
+ "godot_vector2 *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector2_operator_index_const",
+ "return_type": "const godot_real_t *",
+ "arguments": [
+ [
+ "const godot_vector2 *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector2i_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_vector2i *",
+ "r_dest"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector2i_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_vector2i *",
+ "r_dest"
+ ],
+ [
+ "const godot_vector2i *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector2i_operator_index",
+ "return_type": "int32_t *",
+ "arguments": [
+ [
+ "godot_vector2i *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector2i_operator_index_const",
+ "return_type": "const int32_t *",
+ "arguments": [
+ [
+ "const godot_vector2i *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector3_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_vector3 *",
+ "r_dest"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector3_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_vector3 *",
+ "r_dest"
+ ],
+ [
+ "const godot_vector3 *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector3_operator_index",
+ "return_type": "godot_real_t *",
+ "arguments": [
+ [
+ "godot_vector3 *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector3_operator_index_const",
+ "return_type": "const godot_real_t *",
+ "arguments": [
+ [
+ "const godot_vector3 *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector3i_new",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_vector3i *",
+ "r_dest"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector3i_new_copy",
+ "return_type": "void",
+ "arguments": [
+ [
+ "godot_vector3i *",
+ "r_dest"
+ ],
+ [
+ "const godot_vector3i *",
+ "p_src"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector3i_operator_index",
+ "return_type": "int32_t *",
+ "arguments": [
+ [
+ "godot_vector3i *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ },
+ {
+ "name": "godot_vector3i_operator_index_const",
+ "return_type": "const int32_t *",
+ "arguments": [
+ [
+ "const godot_vector3i *",
+ "p_self"
+ ],
+ [
+ "godot_int",
+ "p_index"
+ ]
+ ]
+ }
+ ]
+ },
+ "extensions": [
+ {
+ "name": "nativescript",
+ "type": "NATIVESCRIPT",
+ "version": {
+ "major": 4,
+ "minor": 0
+ },
+ "next": null,
+ "api": [
+ {
+ "name": "godot_nativescript_register_class",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_gdnative_handle"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "const char *",
+ "p_base"
+ ],
+ [
+ "godot_nativescript_instance_create_func",
+ "p_create_func"
+ ],
+ [
+ "godot_nativescript_instance_destroy_func",
+ "p_destroy_func"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_register_tool_class",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_gdnative_handle"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "const char *",
+ "p_base"
+ ],
+ [
+ "godot_nativescript_instance_create_func",
+ "p_create_func"
+ ],
+ [
+ "godot_nativescript_instance_destroy_func",
+ "p_destroy_func"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_register_method",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_gdnative_handle"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "const char *",
+ "p_function_name"
+ ],
+ [
+ "godot_nativescript_method_attributes",
+ "p_attr"
+ ],
+ [
+ "godot_nativescript_instance_method",
+ "p_method"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_method_argument_information",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_gdnative_handle"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "const char *",
+ "p_function_name"
+ ],
+ [
+ "int",
+ "p_num_args"
+ ],
+ [
+ "const godot_nativescript_method_argument *",
+ "p_args"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_register_property",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_gdnative_handle"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "const char *",
+ "p_path"
+ ],
+ [
+ "godot_nativescript_property_attributes *",
+ "p_attr"
+ ],
+ [
+ "godot_nativescript_property_set_func",
+ "p_set_func"
+ ],
+ [
+ "godot_nativescript_property_get_func",
+ "p_get_func"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_register_signal",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_gdnative_handle"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "const godot_nativescript_signal *",
+ "p_signal"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_get_userdata",
+ "return_type": "void *",
+ "arguments": [
+ [
+ "godot_object *",
+ "p_instance"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_class_documentation",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_gdnative_handle"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "godot_string",
+ "p_documentation"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_method_documentation",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_gdnative_handle"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "const char *",
+ "p_function_name"
+ ],
+ [
+ "godot_string",
+ "p_documentation"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_property_documentation",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_gdnative_handle"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "const char *",
+ "p_path"
+ ],
+ [
+ "godot_string",
+ "p_documentation"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_signal_documentation",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_gdnative_handle"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "const char *",
+ "p_signal_name"
+ ],
+ [
+ "godot_string",
+ "p_documentation"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_global_type_tag",
+ "return_type": "void",
+ "arguments": [
+ [
+ "int",
+ "p_idx"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "const void *",
+ "p_type_tag"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_get_global_type_tag",
+ "return_type": "const void *",
+ "arguments": [
+ [
+ "int",
+ "p_idx"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_set_type_tag",
+ "return_type": "void",
+ "arguments": [
+ [
+ "void *",
+ "p_gdnative_handle"
+ ],
+ [
+ "const char *",
+ "p_name"
+ ],
+ [
+ "const void *",
+ "p_type_tag"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_get_type_tag",
+ "return_type": "const void *",
+ "arguments": [
+ [
+ "const godot_object *",
+ "p_object"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_register_instance_binding_data_functions",
+ "return_type": "int",
+ "arguments": [
+ [
+ "godot_nativescript_instance_binding_functions",
+ "p_binding_functions"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_unregister_instance_binding_data_functions",
+ "return_type": "void",
+ "arguments": [
+ [
+ "int",
+ "p_idx"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_get_instance_binding_data",
+ "return_type": "void *",
+ "arguments": [
+ [
+ "int",
+ "p_idx"
+ ],
+ [
+ "godot_object *",
+ "p_object"
+ ]
+ ]
+ },
+ {
+ "name": "godot_nativescript_profiling_add_data",
+ "return_type": "void",
+ "arguments": [
+ [
+ "const char *",
+ "p_signature"
+ ],
+ [
+ "uint64_t",
+ "p_line"
+ ]
+ ]
+ }
+ ]
+ },
+ {
+ "name": "pluginscript",
+ "type": "PLUGINSCRIPT",
+ "version": {
+ "major": 1,
+ "minor": 0
+ },
+ "next": null,
+ "api": [
+ {
+ "name": "godot_pluginscript_register_language",
+ "return_type": "void",
+ "arguments": [
+ [
+ "const godot_pluginscript_language_desc *",
+ "language_desc"
+ ]
+ ]
+ }
+ ]
+ },
+ {
+ "name": "android",
+ "type": "ANDROID",
+ "version": {
+ "major": 1,
+ "minor": 1
+ },
+ "next": null,
+ "api": [
+ {
+ "name": "godot_android_get_env",
+ "return_type": "JNIEnv*",
+ "arguments": []
+ },
+ {
+ "name": "godot_android_get_activity",
+ "return_type": "jobject",
+ "arguments": []
+ },
+ {
+ "name": "godot_android_get_surface",
+ "return_type": "jobject",
+ "arguments": []
+ },
+ {
+ "name": "godot_android_is_activity_resumed",
+ "return_type": "bool",
+ "arguments": []
+ }
+ ]
+ },
+ {
+ "name": "videodecoder",
+ "type": "VIDEODECODER",
+ "version": {
+ "major": 0,
+ "minor": 1
+ },
+ "next": null,
+ "api": [
+ {
+ "name": "godot_videodecoder_file_read",
+ "return_type": "godot_int",
+ "arguments": [
+ [
+ "void *",
+ "file_ptr"
+ ],
+ [
+ "uint8_t *",
+ "buf"
+ ],
+ [
+ "int",
+ "buf_size"
+ ]
+ ]
+ },
+ {
+ "name": "godot_videodecoder_file_seek",
+ "return_type": "int64_t",
+ "arguments": [
+ [
+ "void *",
+ "file_ptr"
+ ],
+ [
+ "int64_t",
+ "pos"
+ ],
+ [
+ "int",
+ "whence"
+ ]
+ ]
+ },
+ {
+ "name": "godot_videodecoder_register_decoder",
+ "return_type": "void",
+ "arguments": [
+ [
+ "const godot_videodecoder_interface_gdnative *",
+ "p_interface"
+ ]
+ ]
+ }
+ ]
+ }
+ ]
}
diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py
index 28e4957b2f..6c96e23426 100644
--- a/modules/gdnative/gdnative_builders.py
+++ b/modules/gdnative/gdnative_builders.py
@@ -19,9 +19,7 @@ def _build_gdnative_api_struct_header(api):
"",
"#include <gdnative/gdnative.h>",
"#include <android/godot_android.h>",
- "#include <xr/godot_xr.h>",
"#include <nativescript/godot_nativescript.h>",
- "#include <net/godot_net.h>",
"#include <pluginscript/godot_pluginscript.h>",
"#include <videodecoder/godot_videodecoder.h>",
"",
diff --git a/modules/gdnative/gdnative_library_editor_plugin.cpp b/modules/gdnative/gdnative_library_editor_plugin.cpp
index f0f095ddf5..9dad13a615 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.cpp
+++ b/modules/gdnative/gdnative_library_editor_plugin.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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());
@@ -65,7 +65,7 @@ void GDNativeLibraryEditor::_update_tree() {
continue;
}
Map<String, NativePlatformConfig>::Element *E = platforms.find(filter_list->get_item_metadata(i));
- if (!text.empty()) {
+ if (!text.is_empty()) {
text += ", ";
}
text += E->get().name;
@@ -74,9 +74,9 @@ void GDNativeLibraryEditor::_update_tree() {
platform->set_text(0, E->get().name);
platform->set_metadata(0, E->get().library_extension);
- platform->set_custom_bg_color(0, get_theme_color("prop_category", "Editor"));
- platform->set_custom_bg_color(1, get_theme_color("prop_category", "Editor"));
- platform->set_custom_bg_color(2, get_theme_color("prop_category", "Editor"));
+ platform->set_custom_bg_color(0, get_theme_color(SNAME("prop_category"), SNAME("Editor")));
+ platform->set_custom_bg_color(1, get_theme_color(SNAME("prop_category"), SNAME("Editor")));
+ platform->set_custom_bg_color(2, get_theme_color(SNAME("prop_category"), SNAME("Editor")));
platform->set_selectable(0, false);
platform->set_expand_right(0, true);
@@ -87,31 +87,31 @@ void GDNativeLibraryEditor::_update_tree() {
bit->set_text(0, it->get());
bit->set_metadata(0, target);
bit->set_selectable(0, false);
- bit->set_custom_bg_color(0, get_theme_color("prop_subsection", "Editor"));
+ bit->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), SNAME("Editor")));
- bit->add_button(1, get_theme_icon("Folder", "EditorIcons"), BUTTON_SELECT_LIBRARY, false, TTR("Select the dynamic library for this entry"));
+ bit->add_button(1, get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")), BUTTON_SELECT_LIBRARY, false, TTR("Select the dynamic library for this entry"));
String file = entry_configs[target].library;
- if (!file.empty()) {
- bit->add_button(1, get_theme_icon("Clear", "EditorIcons"), BUTTON_CLEAR_LIBRARY, false, TTR("Clear"));
+ if (!file.is_empty()) {
+ bit->add_button(1, get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")), BUTTON_CLEAR_LIBRARY, false, TTR("Clear"));
}
bit->set_text(1, file);
- bit->add_button(2, get_theme_icon("Folder", "EditorIcons"), BUTTON_SELECT_DEPENDENCES, false, TTR("Select dependencies of the library for this entry"));
+ bit->add_button(2, get_theme_icon(SNAME("Folder"), SNAME("EditorIcons")), BUTTON_SELECT_DEPENDENCES, false, TTR("Select dependencies of the library for this entry"));
Array files = entry_configs[target].dependencies;
if (files.size()) {
- bit->add_button(2, get_theme_icon("Clear", "EditorIcons"), BUTTON_CLEAR_DEPENDENCES, false, TTR("Clear"));
+ bit->add_button(2, get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")), BUTTON_CLEAR_DEPENDENCES, false, TTR("Clear"));
}
bit->set_text(2, Variant(files));
- bit->add_button(3, get_theme_icon("MoveUp", "EditorIcons"), BUTTON_MOVE_UP, false, TTR("Move Up"));
- bit->add_button(3, get_theme_icon("MoveDown", "EditorIcons"), BUTTON_MOVE_DOWN, false, TTR("Move Down"));
- bit->add_button(3, get_theme_icon("Remove", "EditorIcons"), BUTTON_ERASE_ENTRY, false, TTR("Remove current entry"));
+ bit->add_button(3, get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons")), BUTTON_MOVE_UP, false, TTR("Move Up"));
+ bit->add_button(3, get_theme_icon(SNAME("MoveDown"), SNAME("EditorIcons")), BUTTON_MOVE_DOWN, false, TTR("Move Down"));
+ bit->add_button(3, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), BUTTON_ERASE_ENTRY, false, TTR("Remove current entry"));
}
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_custom_color(0, get_theme_color("accent_color", "Editor"));
+ 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());
@@ -131,7 +131,7 @@ void GDNativeLibraryEditor::_on_item_button(Object *item, int column, int id) {
EditorFileDialog::FileMode mode = EditorFileDialog::FILE_MODE_OPEN_FILE;
if (id == BUTTON_SELECT_DEPENDENCES) {
mode = EditorFileDialog::FILE_MODE_OPEN_FILES;
- } else if (treeItem->get_text(0) == "iOS") {
+ } else if (treeItem->get_text(0) == "iOS" || treeItem->get_text(0) == "macOS") {
mode = EditorFileDialog::FILE_MODE_OPEN_ANY;
}
@@ -195,7 +195,7 @@ void GDNativeLibraryEditor::_on_item_activated() {
void GDNativeLibraryEditor::_on_create_new_entry() {
String platform = new_architecture_dialog->get_meta("platform");
String entry = new_architecture_input->get_text().strip_edges();
- if (!entry.empty()) {
+ if (!entry.is_empty()) {
platforms[platform].entries.push_back(entry);
_update_tree();
}
@@ -245,10 +245,10 @@ 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();
- if (entry_configs[target].library.empty() && entry_configs[target].dependencies.empty()) {
+ 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;
}
@@ -257,7 +257,7 @@ void GDNativeLibraryEditor::_translate_to_config_file() {
}
}
- library->_change_notify();
+ library->notify_property_list_changed();
}
}
@@ -278,11 +278,10 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
platforms["X11"] = platform_linux;
NativePlatformConfig platform_osx;
- platform_osx.name = "Mac OSX";
+ platform_osx.name = "macOS";
platform_osx.entries.push_back("64");
- platform_osx.entries.push_back("32");
- platform_osx.library_extension = "*.dylib";
- platforms["OSX"] = platform_osx;
+ platform_osx.library_extension = "*.framework; Framework, *.dylib; Dynamic Library";
+ platforms["macOS"] = platform_osx;
NativePlatformConfig platform_haiku;
platform_haiku.name = "Haiku";
@@ -308,11 +307,11 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
platform_android.library_extension = "*.so";
platforms["Android"] = platform_android;
- // TODO: Javascript platform is not supported yet
- // NativePlatformConfig platform_html5;
- // platform_html5.name = "HTML5";
- // platform_html5.library_extension = "*.wasm";
- // platforms["Javascript"] = platform_html5;
+ NativePlatformConfig platform_html5;
+ platform_html5.name = "HTML5";
+ platform_html5.entries.push_back("wasm32");
+ platform_html5.library_extension = "*.wasm";
+ platforms["HTML5"] = platform_html5;
NativePlatformConfig platform_ios;
platform_ios.name = "iOS";
@@ -327,7 +326,7 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
VBoxContainer *container = memnew(VBoxContainer);
add_child(container);
- container->set_anchors_and_margins_preset(PRESET_WIDE);
+ container->set_anchors_and_offsets_preset(PRESET_WIDE);
HBoxContainer *hbox = memnew(HBoxContainer);
container->add_child(hbox);
@@ -342,9 +341,9 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
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;
}
@@ -357,12 +356,12 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
tree->set_column_titles_visible(true);
tree->set_columns(4);
tree->set_column_expand(0, false);
- tree->set_column_min_width(0, int(200 * EDSCALE));
+ tree->set_column_custom_minimum_width(0, int(200 * EDSCALE));
tree->set_column_title(0, TTR("Platform"));
tree->set_column_title(1, TTR("Dynamic Library"));
tree->set_column_title(2, TTR("Dependencies"));
tree->set_column_expand(3, false);
- tree->set_column_min_width(3, int(110 * EDSCALE));
+ tree->set_column_custom_minimum_width(3, int(110 * EDSCALE));
tree->connect("button_pressed", callable_mp(this, &GDNativeLibraryEditor::_on_item_button));
tree->connect("item_collapsed", callable_mp(this, &GDNativeLibraryEditor::_on_item_collapsed));
tree->connect("item_activated", callable_mp(this, &GDNativeLibraryEditor::_on_item_activated));
@@ -381,8 +380,8 @@ GDNativeLibraryEditor::GDNativeLibraryEditor() {
new_architecture_input = memnew(LineEdit);
new_architecture_dialog->add_child(new_architecture_input);
// new_architecture_dialog->set_custom_minimum_size(Vector2(300, 80) * EDSCALE);
- new_architecture_input->set_anchors_and_margins_preset(PRESET_HCENTER_WIDE, PRESET_MODE_MINSIZE, 5 * EDSCALE);
- new_architecture_dialog->get_ok()->connect("pressed", callable_mp(this, &GDNativeLibraryEditor::_on_create_new_entry));
+ new_architecture_input->set_anchors_and_offsets_preset(PRESET_HCENTER_WIDE, PRESET_MODE_MINSIZE, 5 * EDSCALE);
+ new_architecture_dialog->get_ok_button()->connect("pressed", callable_mp(this, &GDNativeLibraryEditor::_on_create_new_entry));
}
void GDNativeLibraryEditorPlugin::edit(Object *p_node) {
diff --git a/modules/gdnative/gdnative_library_editor_plugin.h b/modules/gdnative/gdnative_library_editor_plugin.h
index 180ab7707c..61afb1aaaa 100644
--- a/modules/gdnative/gdnative_library_editor_plugin.h
+++ b/modules/gdnative/gdnative_library_editor_plugin.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -95,9 +95,9 @@ public:
class GDNativeLibraryEditorPlugin : public EditorPlugin {
GDCLASS(GDNativeLibraryEditorPlugin, EditorPlugin);
- GDNativeLibraryEditor *library_editor;
- EditorNode *editor;
- Button *button;
+ GDNativeLibraryEditor *library_editor = nullptr;
+ EditorNode *editor = nullptr;
+ Button *button = nullptr;
public:
virtual String get_name() const override { return "GDNativeLibrary"; }
diff --git a/modules/gdnative/gdnative_library_singleton_editor.cpp b/modules/gdnative/gdnative_library_singleton_editor.cpp
index 409b6cbffe..f1b4a9a81b 100644
--- a/modules/gdnative/gdnative_library_singleton_editor.cpp
+++ b/modules/gdnative/gdnative_library_singleton_editor.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdnative/gdnative_library_singleton_editor.h b/modules/gdnative/gdnative_library_singleton_editor.h
index 1a213d8094..5bb823d920 100644
--- a/modules/gdnative/gdnative_library_singleton_editor.h
+++ b/modules/gdnative/gdnative_library_singleton_editor.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdnative/icons/GDNativeLibrary.svg b/modules/gdnative/icons/GDNativeLibrary.svg
index b494c7af6e..0ddfd4e6f2 100644
--- a/modules/gdnative/icons/GDNativeLibrary.svg
+++ b/modules/gdnative/icons/GDNativeLibrary.svg
@@ -1,5 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2l2.2578 0.56445a5 5 0 0 0 0.2793 0.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 0.68555 0.28516l0.5625 2.2539v-5.2695a2 2 0 0 1 -1 -1.7305 2 2 0 0 1 1 -1.7285v-0.27148h1 4.5762a5 5 0 0 0 -0.11328 -0.25195l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm2 7v1 5 1h5c0.55228 0 1-0.4477 1-1v-5c0-0.5523-0.44772-1-1-1v4l-1-1-1 1v-4h-3z" fill="#e0e0e0"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2l2.2578.56445a5 5 0 0 0 .2793.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 .68555.28516l.5625 2.2539v-5.2695a2 2 0 0 1 -1-1.7305 2 2 0 0 1 1-1.7285v-.27148h1 4.5762a5 5 0 0 0 -.11328-.25195l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm2 7v1 5 1h5c.55228 0 1-.4477 1-1v-5c0-.5523-.44772-1-1-1v4l-1-1-1 1v-4z" fill="#e0e0e0"/></svg>
diff --git a/modules/gdnative/icons/NativeScript.svg b/modules/gdnative/icons/NativeScript.svg
index fb9e135627..2224b36b29 100644
--- a/modules/gdnative/icons/NativeScript.svg
+++ b/modules/gdnative/icons/NativeScript.svg
@@ -1,5 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625h3v1 1h2v-0.95117a2 2 0 0 1 0 -0.048828 2 2 0 0 1 2 -2 2 2 0 0 1 2 2v1h5v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm-6 7v4 4h2a3 3 0 0 0 3 -3 3 3 0 0 0 -3 -3v-2h-2zm6 0v2h2v-2h-2zm3 2v6h2v-4a1 1 0 0 1 1 1v3h2v-3a3 3 0 0 0 -3 -3h-2zm-7 2a1 1 0 0 1 1 1 1 1 0 0 1 -1 1v-2zm4 0v4h2v-4h-2z" fill="#e0e0e0"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625h3v1 1h2v-.95117a2 2 0 0 1 0-.048828 2 2 0 0 1 2-2 2 2 0 0 1 2 2v1h5v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm-6 7v4 4h2a3 3 0 0 0 3-3 3 3 0 0 0 -3-3v-2zm6 0v2h2v-2zm3 2v6h2v-4a1 1 0 0 1 1 1v3h2v-3a3 3 0 0 0 -3-3zm-7 2a1 1 0 0 1 1 1 1 1 0 0 1 -1 1zm4 0v4h2v-4z" fill="#e0e0e0"/></svg>
diff --git a/modules/gdnative/include/android/godot_android.h b/modules/gdnative/include/android/godot_android.h
index 45d4eaff37..867ef9e03a 100644
--- a/modules/gdnative/include/android/godot_android.h
+++ b/modules/gdnative/include/android/godot_android.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdnative/include/gdnative/aabb.h b/modules/gdnative/include/gdnative/aabb.h
index c776297944..860675065d 100644
--- a/modules/gdnative/include/gdnative/aabb.h
+++ b/modules/gdnative/include/gdnative/aabb.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,9 +35,9 @@
extern "C" {
#endif
-#include <stdint.h>
+#include <gdnative/math_defs.h>
-#define GODOT_AABB_SIZE 24
+#define GODOT_AABB_SIZE (sizeof(godot_real_t) * 6)
#ifndef GODOT_CORE_API_GODOT_AABB_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_AABB_TYPE_DEFINED
@@ -46,72 +46,10 @@ typedef struct {
} godot_aabb;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
#include <gdnative/gdnative.h>
-#include <gdnative/plane.h>
-#include <gdnative/vector3.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void GDAPI godot_aabb_new(godot_aabb *r_dest, const godot_vector3 *p_pos, const godot_vector3 *p_size);
-
-godot_vector3 GDAPI godot_aabb_get_position(const godot_aabb *p_self);
-void GDAPI godot_aabb_set_position(const godot_aabb *p_self, const godot_vector3 *p_v);
-
-godot_vector3 GDAPI godot_aabb_get_size(const godot_aabb *p_self);
-void GDAPI godot_aabb_set_size(const godot_aabb *p_self, const godot_vector3 *p_v);
-
-godot_string GDAPI godot_aabb_as_string(const godot_aabb *p_self);
-
-godot_aabb GDAPI godot_aabb_abs(const godot_aabb *p_self);
-
-godot_real GDAPI godot_aabb_get_area(const godot_aabb *p_self);
-
-godot_bool GDAPI godot_aabb_has_no_area(const godot_aabb *p_self);
-
-godot_bool GDAPI godot_aabb_has_no_surface(const godot_aabb *p_self);
-
-godot_bool GDAPI godot_aabb_intersects(const godot_aabb *p_self, const godot_aabb *p_with);
-
-godot_bool GDAPI godot_aabb_encloses(const godot_aabb *p_self, const godot_aabb *p_with);
-
-godot_aabb GDAPI godot_aabb_merge(const godot_aabb *p_self, const godot_aabb *p_with);
-
-godot_aabb GDAPI godot_aabb_intersection(const godot_aabb *p_self, const godot_aabb *p_with);
-
-godot_bool GDAPI godot_aabb_intersects_plane(const godot_aabb *p_self, const godot_plane *p_plane);
-
-godot_bool GDAPI godot_aabb_intersects_segment(const godot_aabb *p_self, const godot_vector3 *p_from, const godot_vector3 *p_to);
-
-godot_bool GDAPI godot_aabb_has_point(const godot_aabb *p_self, const godot_vector3 *p_point);
-
-godot_vector3 GDAPI godot_aabb_get_support(const godot_aabb *p_self, const godot_vector3 *p_dir);
-
-godot_vector3 GDAPI godot_aabb_get_longest_axis(const godot_aabb *p_self);
-
-godot_int GDAPI godot_aabb_get_longest_axis_index(const godot_aabb *p_self);
-
-godot_real GDAPI godot_aabb_get_longest_axis_size(const godot_aabb *p_self);
-
-godot_vector3 GDAPI godot_aabb_get_shortest_axis(const godot_aabb *p_self);
-
-godot_int GDAPI godot_aabb_get_shortest_axis_index(const godot_aabb *p_self);
-
-godot_real GDAPI godot_aabb_get_shortest_axis_size(const godot_aabb *p_self);
-
-godot_aabb GDAPI godot_aabb_expand(const godot_aabb *p_self, const godot_vector3 *p_to_point);
-
-godot_aabb GDAPI godot_aabb_grow(const godot_aabb *p_self, const godot_real p_by);
-
-godot_vector3 GDAPI godot_aabb_get_endpoint(const godot_aabb *p_self, const godot_int p_idx);
-godot_bool GDAPI godot_aabb_operator_equal(const godot_aabb *p_self, const godot_aabb *p_b);
+void GDAPI godot_aabb_new(godot_aabb *p_self);
+void GDAPI godot_aabb_new_copy(godot_aabb *r_dest, const godot_aabb *p_src);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/gdnative/array.h b/modules/gdnative/include/gdnative/array.h
index 4db685873f..bf4b852449 100644
--- a/modules/gdnative/include/gdnative/array.h
+++ b/modules/gdnative/include/gdnative/array.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -46,101 +46,14 @@ typedef struct {
} godot_array;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
-#include <gdnative/packed_arrays.h>
-#include <gdnative/variant.h>
-
#include <gdnative/gdnative.h>
+#include <gdnative/variant_struct.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void GDAPI godot_array_new(godot_array *r_dest);
+void GDAPI godot_array_new(godot_array *p_self);
void GDAPI godot_array_new_copy(godot_array *r_dest, const godot_array *p_src);
-void GDAPI godot_array_new_packed_color_array(godot_array *r_dest, const godot_packed_color_array *p_pca);
-void GDAPI godot_array_new_packed_vector3_array(godot_array *r_dest, const godot_packed_vector3_array *p_pv3a);
-void GDAPI godot_array_new_packed_vector2_array(godot_array *r_dest, const godot_packed_vector2_array *p_pv2a);
-void GDAPI godot_array_new_packed_string_array(godot_array *r_dest, const godot_packed_string_array *p_psa);
-void GDAPI godot_array_new_packed_float32_array(godot_array *r_dest, const godot_packed_float32_array *p_pra);
-void GDAPI godot_array_new_packed_float64_array(godot_array *r_dest, const godot_packed_float64_array *p_pra);
-void GDAPI godot_array_new_packed_int32_array(godot_array *r_dest, const godot_packed_int32_array *p_pia);
-void GDAPI godot_array_new_packed_int64_array(godot_array *r_dest, const godot_packed_int64_array *p_pia);
-void GDAPI godot_array_new_packed_byte_array(godot_array *r_dest, const godot_packed_byte_array *p_pba);
-
-void GDAPI godot_array_set(godot_array *p_self, const godot_int p_idx, const godot_variant *p_value);
-
-godot_variant GDAPI godot_array_get(const godot_array *p_self, const godot_int p_idx);
-
-godot_variant GDAPI *godot_array_operator_index(godot_array *p_self, const godot_int p_idx);
-
-const godot_variant GDAPI *godot_array_operator_index_const(const godot_array *p_self, const godot_int p_idx);
-
-void GDAPI godot_array_append(godot_array *p_self, const godot_variant *p_value);
-
-void GDAPI godot_array_clear(godot_array *p_self);
-
-godot_int GDAPI godot_array_count(const godot_array *p_self, const godot_variant *p_value);
-
-godot_bool GDAPI godot_array_empty(const godot_array *p_self);
-
-void GDAPI godot_array_erase(godot_array *p_self, const godot_variant *p_value);
-
-godot_variant GDAPI godot_array_front(const godot_array *p_self);
-
-godot_variant GDAPI godot_array_back(const godot_array *p_self);
-
-godot_int GDAPI godot_array_find(const godot_array *p_self, const godot_variant *p_what, const godot_int p_from);
-
-godot_int GDAPI godot_array_find_last(const godot_array *p_self, const godot_variant *p_what);
-
-godot_bool GDAPI godot_array_has(const godot_array *p_self, const godot_variant *p_value);
-
-godot_int GDAPI godot_array_hash(const godot_array *p_self);
-
-void GDAPI godot_array_insert(godot_array *p_self, const godot_int p_pos, const godot_variant *p_value);
-
-void GDAPI godot_array_invert(godot_array *p_self);
-
-godot_variant GDAPI godot_array_pop_back(godot_array *p_self);
-
-godot_variant GDAPI godot_array_pop_front(godot_array *p_self);
-
-void GDAPI godot_array_push_back(godot_array *p_self, const godot_variant *p_value);
-
-void GDAPI godot_array_push_front(godot_array *p_self, const godot_variant *p_value);
-
-void GDAPI godot_array_remove(godot_array *p_self, const godot_int p_idx);
-
-void GDAPI godot_array_resize(godot_array *p_self, const godot_int p_size);
-
-godot_int GDAPI godot_array_rfind(const godot_array *p_self, const godot_variant *p_what, const godot_int p_from);
-
-godot_int GDAPI godot_array_size(const godot_array *p_self);
-
-void GDAPI godot_array_sort(godot_array *p_self);
-
-void GDAPI godot_array_sort_custom(godot_array *p_self, godot_object *p_obj, const godot_string *p_func);
-
-godot_int GDAPI godot_array_bsearch(godot_array *p_self, const godot_variant *p_value, const godot_bool p_before);
-
-godot_int GDAPI godot_array_bsearch_custom(godot_array *p_self, const godot_variant *p_value, godot_object *p_obj, const godot_string *p_func, const godot_bool p_before);
-
void GDAPI godot_array_destroy(godot_array *p_self);
-
-godot_array GDAPI godot_array_duplicate(const godot_array *p_self, const godot_bool p_deep);
-
-godot_array GDAPI godot_array_slice(const godot_array *p_self, const godot_int p_begin, const godot_int p_end, const godot_int p_step, const godot_bool p_deep);
-
-godot_variant GDAPI godot_array_max(const godot_array *p_self);
-
-godot_variant GDAPI godot_array_min(const godot_array *p_self);
-
-void GDAPI godot_array_shuffle(godot_array *p_self);
+godot_variant GDAPI *godot_array_operator_index(godot_array *p_self, godot_int p_index);
+const godot_variant GDAPI *godot_array_operator_index_const(const godot_array *p_self, godot_int p_index);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/gdnative/basis.h b/modules/gdnative/include/gdnative/basis.h
index c7425ebbfa..5477dbf811 100644
--- a/modules/gdnative/include/gdnative/basis.h
+++ b/modules/gdnative/include/gdnative/basis.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,9 +35,9 @@
extern "C" {
#endif
-#include <stdint.h>
+#include <gdnative/math_defs.h>
-#define GODOT_BASIS_SIZE 36
+#define GODOT_BASIS_SIZE (sizeof(godot_real_t) * 9)
#ifndef GODOT_CORE_API_GODOT_BASIS_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_BASIS_TYPE_DEFINED
@@ -46,88 +46,12 @@ typedef struct {
} godot_basis;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
#include <gdnative/gdnative.h>
-#include <gdnative/quat.h>
-#include <gdnative/vector3.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void GDAPI godot_basis_new_with_rows(godot_basis *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis);
-void GDAPI godot_basis_new_with_axis_and_angle(godot_basis *r_dest, const godot_vector3 *p_axis, const godot_real p_phi);
-void GDAPI godot_basis_new_with_euler(godot_basis *r_dest, const godot_vector3 *p_euler);
-void GDAPI godot_basis_new_with_euler_quat(godot_basis *r_dest, const godot_quat *p_euler);
-
-godot_string GDAPI godot_basis_as_string(const godot_basis *p_self);
-
-godot_basis GDAPI godot_basis_inverse(const godot_basis *p_self);
-
-godot_basis GDAPI godot_basis_transposed(const godot_basis *p_self);
-
-godot_basis GDAPI godot_basis_orthonormalized(const godot_basis *p_self);
-
-godot_real GDAPI godot_basis_determinant(const godot_basis *p_self);
-
-godot_basis GDAPI godot_basis_rotated(const godot_basis *p_self, const godot_vector3 *p_axis, const godot_real p_phi);
-
-godot_basis GDAPI godot_basis_scaled(const godot_basis *p_self, const godot_vector3 *p_scale);
-
-godot_vector3 GDAPI godot_basis_get_scale(const godot_basis *p_self);
-
-godot_vector3 GDAPI godot_basis_get_euler(const godot_basis *p_self);
-
-godot_quat GDAPI godot_basis_get_quat(const godot_basis *p_self);
-
-void GDAPI godot_basis_set_quat(godot_basis *p_self, const godot_quat *p_quat);
-
-void GDAPI godot_basis_set_axis_angle_scale(godot_basis *p_self, const godot_vector3 *p_axis, godot_real p_phi, const godot_vector3 *p_scale);
-
-void GDAPI godot_basis_set_euler_scale(godot_basis *p_self, const godot_vector3 *p_euler, const godot_vector3 *p_scale);
-
-void GDAPI godot_basis_set_quat_scale(godot_basis *p_self, const godot_quat *p_quat, const godot_vector3 *p_scale);
-
-godot_real GDAPI godot_basis_tdotx(const godot_basis *p_self, const godot_vector3 *p_with);
-
-godot_real GDAPI godot_basis_tdoty(const godot_basis *p_self, const godot_vector3 *p_with);
-
-godot_real GDAPI godot_basis_tdotz(const godot_basis *p_self, const godot_vector3 *p_with);
-
-godot_vector3 GDAPI godot_basis_xform(const godot_basis *p_self, const godot_vector3 *p_v);
-
-godot_vector3 GDAPI godot_basis_xform_inv(const godot_basis *p_self, const godot_vector3 *p_v);
-
-godot_int GDAPI godot_basis_get_orthogonal_index(const godot_basis *p_self);
-
-void GDAPI godot_basis_new(godot_basis *r_dest);
-
-// p_elements is a pointer to an array of 3 (!!) vector3
-void GDAPI godot_basis_get_elements(const godot_basis *p_self, godot_vector3 *p_elements);
-
-godot_vector3 GDAPI godot_basis_get_axis(const godot_basis *p_self, const godot_int p_axis);
-
-void GDAPI godot_basis_set_axis(godot_basis *p_self, const godot_int p_axis, const godot_vector3 *p_value);
-
-godot_vector3 GDAPI godot_basis_get_row(const godot_basis *p_self, const godot_int p_row);
-
-void GDAPI godot_basis_set_row(godot_basis *p_self, const godot_int p_row, const godot_vector3 *p_value);
-
-godot_bool GDAPI godot_basis_operator_equal(const godot_basis *p_self, const godot_basis *p_b);
-
-godot_basis GDAPI godot_basis_operator_add(const godot_basis *p_self, const godot_basis *p_b);
-
-godot_basis GDAPI godot_basis_operator_subtract(const godot_basis *p_self, const godot_basis *p_b);
-
-godot_basis GDAPI godot_basis_operator_multiply_vector(const godot_basis *p_self, const godot_basis *p_b);
-
-godot_basis GDAPI godot_basis_operator_multiply_scalar(const godot_basis *p_self, const godot_real p_b);
-godot_basis GDAPI godot_basis_slerp(const godot_basis *p_self, const godot_basis *p_b, const godot_real p_t);
+void GDAPI godot_basis_new(godot_basis *p_self);
+void GDAPI godot_basis_new_copy(godot_basis *r_dest, const godot_basis *p_src);
+godot_vector3 GDAPI *godot_basis_operator_index(godot_basis *p_self, godot_int p_index);
+const godot_vector3 GDAPI *godot_basis_operator_index_const(const godot_basis *p_self, godot_int p_index);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/gdnative/callable.h b/modules/gdnative/include/gdnative/callable.h
index dbb5d02590..1d52ca7a68 100644
--- a/modules/gdnative/include/gdnative/callable.h
+++ b/modules/gdnative/include/gdnative/callable.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,6 +37,7 @@ extern "C" {
#include <stdint.h>
+// Alignment hardcoded in `core/variant/callable.h`.
#define GODOT_CALLABLE_SIZE (16)
#ifndef GODOT_CORE_API_GODOT_CALLABLE_TYPE_DEFINED
@@ -46,79 +47,12 @@ typedef struct {
} godot_callable;
#endif
-#define GODOT_SIGNAL_SIZE (16)
-
-#ifndef GODOT_CORE_API_GODOT_SIGNAL_TYPE_DEFINED
-#define GODOT_CORE_API_GODOT_SIGNAL_TYPE_DEFINED
-typedef struct {
- uint8_t _dont_touch_that[GODOT_SIGNAL_SIZE];
-} godot_signal;
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
#include <gdnative/gdnative.h>
-#include <gdnative/string_name.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Callable
-
-void GDAPI godot_callable_new_with_object(godot_callable *r_dest, const godot_object *p_object, const godot_string_name *p_method);
-void GDAPI godot_callable_new_with_object_id(godot_callable *r_dest, uint64_t p_objectid, const godot_string_name *p_method);
+void GDAPI godot_callable_new(godot_callable *p_self);
void GDAPI godot_callable_new_copy(godot_callable *r_dest, const godot_callable *p_src);
-
void GDAPI godot_callable_destroy(godot_callable *p_self);
-godot_int GDAPI godot_callable_call(const godot_callable *p_self, const godot_variant **p_arguments, godot_int p_argcount, godot_variant *r_return_value);
-void GDAPI godot_callable_call_deferred(const godot_callable *p_self, const godot_variant **p_arguments, godot_int p_argcount);
-
-godot_bool GDAPI godot_callable_is_null(const godot_callable *p_self);
-godot_bool GDAPI godot_callable_is_custom(const godot_callable *p_self);
-godot_bool GDAPI godot_callable_is_standard(const godot_callable *p_self);
-
-godot_object GDAPI *godot_callable_get_object(const godot_callable *p_self);
-uint64_t GDAPI godot_callable_get_object_id(const godot_callable *p_self);
-godot_string_name GDAPI godot_callable_get_method(const godot_callable *p_self);
-
-uint32_t GDAPI godot_callable_hash(const godot_callable *p_self);
-
-godot_string GDAPI godot_callable_as_string(const godot_callable *p_self);
-
-godot_bool GDAPI godot_callable_operator_equal(const godot_callable *p_self, const godot_callable *p_other);
-godot_bool GDAPI godot_callable_operator_less(const godot_callable *p_self, const godot_callable *p_other);
-
-// Signal
-
-void GDAPI godot_signal_new_with_object(godot_signal *r_dest, const godot_object *p_object, const godot_string_name *p_name);
-void GDAPI godot_signal_new_with_object_id(godot_signal *r_dest, uint64_t p_objectid, const godot_string_name *p_name);
-void GDAPI godot_signal_new_copy(godot_signal *r_dest, const godot_signal *p_src);
-
-void GDAPI godot_signal_destroy(godot_signal *p_self);
-
-godot_int GDAPI godot_signal_emit(const godot_signal *p_self, const godot_variant **p_arguments, godot_int p_argcount);
-
-godot_int GDAPI godot_signal_connect(godot_signal *p_self, const godot_callable *p_callable, const godot_array *p_binds, uint32_t p_flags);
-void GDAPI godot_signal_disconnect(godot_signal *p_self, const godot_callable *p_callable);
-
-godot_bool GDAPI godot_signal_is_null(const godot_signal *p_self);
-godot_bool GDAPI godot_signal_is_connected(const godot_signal *p_self, const godot_callable *p_callable);
-
-godot_array GDAPI godot_signal_get_connections(const godot_signal *p_self);
-
-godot_object GDAPI *godot_signal_get_object(const godot_signal *p_self);
-uint64_t GDAPI godot_signal_get_object_id(const godot_signal *p_self);
-godot_string_name GDAPI godot_signal_get_name(const godot_signal *p_self);
-
-godot_string GDAPI godot_signal_as_string(const godot_signal *p_self);
-
-godot_bool GDAPI godot_signal_operator_equal(const godot_signal *p_self, const godot_signal *p_other);
-godot_bool GDAPI godot_signal_operator_less(const godot_signal *p_self, const godot_signal *p_other);
-
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/color.h b/modules/gdnative/include/gdnative/color.h
index e7737bf8e1..3334013147 100644
--- a/modules/gdnative/include/gdnative/color.h
+++ b/modules/gdnative/include/gdnative/color.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,9 +35,10 @@
extern "C" {
#endif
-#include <stdint.h>
+#include <gdnative/math_defs.h>
-#define GODOT_COLOR_SIZE 16
+// Colors should always use 32-bit floats, so don't use real_t here.
+#define GODOT_COLOR_SIZE (sizeof(float) * 4)
#ifndef GODOT_CORE_API_GODOT_COLOR_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_COLOR_TYPE_DEFINED
@@ -46,70 +47,12 @@ typedef struct {
} godot_color;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
#include <gdnative/gdnative.h>
-#include <gdnative/string.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void GDAPI godot_color_new_rgba(godot_color *r_dest, const godot_real p_r, const godot_real p_g, const godot_real p_b, const godot_real p_a);
-void GDAPI godot_color_new_rgb(godot_color *r_dest, const godot_real p_r, const godot_real p_g, const godot_real p_b);
-
-godot_real godot_color_get_r(const godot_color *p_self);
-void godot_color_set_r(godot_color *p_self, const godot_real r);
-
-godot_real godot_color_get_g(const godot_color *p_self);
-void godot_color_set_g(godot_color *p_self, const godot_real g);
-
-godot_real godot_color_get_b(const godot_color *p_self);
-void godot_color_set_b(godot_color *p_self, const godot_real b);
-
-godot_real godot_color_get_a(const godot_color *p_self);
-void godot_color_set_a(godot_color *p_self, const godot_real a);
-
-godot_real godot_color_get_h(const godot_color *p_self);
-godot_real godot_color_get_s(const godot_color *p_self);
-godot_real godot_color_get_v(const godot_color *p_self);
-
-godot_string GDAPI godot_color_as_string(const godot_color *p_self);
-
-godot_int GDAPI godot_color_to_rgba32(const godot_color *p_self);
-
-godot_int GDAPI godot_color_to_abgr32(const godot_color *p_self);
-
-godot_int GDAPI godot_color_to_abgr64(const godot_color *p_self);
-
-godot_int GDAPI godot_color_to_argb64(const godot_color *p_self);
-
-godot_int GDAPI godot_color_to_rgba64(const godot_color *p_self);
-
-godot_int GDAPI godot_color_to_argb32(const godot_color *p_self);
-
-godot_color GDAPI godot_color_inverted(const godot_color *p_self);
-
-godot_color GDAPI godot_color_contrasted(const godot_color *p_self);
-
-godot_color GDAPI godot_color_lerp(const godot_color *p_self, const godot_color *p_b, const godot_real p_t);
-
-godot_color GDAPI godot_color_blend(const godot_color *p_self, const godot_color *p_over);
-
-godot_color GDAPI godot_color_darkened(const godot_color *p_self, const godot_real p_amount);
-
-godot_color GDAPI godot_color_from_hsv(const godot_color *p_self, const godot_real p_h, const godot_real p_s, const godot_real p_v, const godot_real p_a);
-
-godot_color GDAPI godot_color_lightened(const godot_color *p_self, const godot_real p_amount);
-
-godot_string GDAPI godot_color_to_html(const godot_color *p_self, const godot_bool p_with_alpha);
-
-godot_bool GDAPI godot_color_operator_equal(const godot_color *p_self, const godot_color *p_b);
-godot_bool GDAPI godot_color_operator_less(const godot_color *p_self, const godot_color *p_b);
+void GDAPI godot_color_new(godot_color *p_self);
+void GDAPI godot_color_new_copy(godot_color *r_dest, const godot_color *p_src);
+float GDAPI *godot_color_operator_index(godot_color *p_self, godot_int p_index);
+const float GDAPI *godot_color_operator_index_const(const godot_color *p_self, godot_int p_index);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/gdnative/dictionary.h b/modules/gdnative/include/gdnative/dictionary.h
index 873efaa9bf..b9525fb5e6 100644
--- a/modules/gdnative/include/gdnative/dictionary.h
+++ b/modules/gdnative/include/gdnative/dictionary.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -46,62 +46,15 @@ typedef struct {
} godot_dictionary;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
-#include <gdnative/array.h>
#include <gdnative/gdnative.h>
-#include <gdnative/variant.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include <gdnative/variant_struct.h>
-void GDAPI godot_dictionary_new(godot_dictionary *r_dest);
+void GDAPI godot_dictionary_new(godot_dictionary *p_self);
void GDAPI godot_dictionary_new_copy(godot_dictionary *r_dest, const godot_dictionary *p_src);
void GDAPI godot_dictionary_destroy(godot_dictionary *p_self);
-
-godot_dictionary GDAPI godot_dictionary_duplicate(const godot_dictionary *p_self, const godot_bool p_deep);
-
-godot_int GDAPI godot_dictionary_size(const godot_dictionary *p_self);
-
-godot_bool GDAPI godot_dictionary_empty(const godot_dictionary *p_self);
-
-void GDAPI godot_dictionary_clear(godot_dictionary *p_self);
-
-godot_bool GDAPI godot_dictionary_has(const godot_dictionary *p_self, const godot_variant *p_key);
-
-godot_bool GDAPI godot_dictionary_has_all(const godot_dictionary *p_self, const godot_array *p_keys);
-
-void GDAPI godot_dictionary_erase(godot_dictionary *p_self, const godot_variant *p_key);
-
-godot_int GDAPI godot_dictionary_hash(const godot_dictionary *p_self);
-
-godot_array GDAPI godot_dictionary_keys(const godot_dictionary *p_self);
-
-godot_array GDAPI godot_dictionary_values(const godot_dictionary *p_self);
-
-godot_variant GDAPI godot_dictionary_get(const godot_dictionary *p_self, const godot_variant *p_key);
-void GDAPI godot_dictionary_set(godot_dictionary *p_self, const godot_variant *p_key, const godot_variant *p_value);
-
godot_variant GDAPI *godot_dictionary_operator_index(godot_dictionary *p_self, const godot_variant *p_key);
-
const godot_variant GDAPI *godot_dictionary_operator_index_const(const godot_dictionary *p_self, const godot_variant *p_key);
-godot_variant GDAPI *godot_dictionary_next(const godot_dictionary *p_self, const godot_variant *p_key);
-
-godot_bool GDAPI godot_dictionary_operator_equal(const godot_dictionary *p_self, const godot_dictionary *p_b);
-
-godot_string GDAPI godot_dictionary_to_json(const godot_dictionary *p_self);
-
-// GDNative core 1.1
-
-godot_bool GDAPI godot_dictionary_erase_with_return(godot_dictionary *p_self, const godot_variant *p_key);
-
-godot_variant GDAPI godot_dictionary_get_with_default(const godot_dictionary *p_self, const godot_variant *p_key, const godot_variant *p_default);
-
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/gdnative.h b/modules/gdnative/include/gdnative/gdnative.h
index 6a0a375da8..d8c290f6bd 100644
--- a/modules/gdnative/include/gdnative/gdnative.h
+++ b/modules/gdnative/include/gdnative/gdnative.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -53,7 +53,9 @@ extern "C" {
#endif
// This is for libraries *using* the header, NOT GODOT EXPOSING STUFF!!
-#ifdef _WIN32
+#ifdef __GNUC__
+#define GDN_EXPORT __attribute__((visibility("default")))
+#elif defined(_WIN32)
#define GDN_EXPORT __declspec(dllexport)
#else
#define GDN_EXPORT
@@ -62,8 +64,6 @@ extern "C" {
#include <stdbool.h>
#include <stdint.h>
-#define GODOT_API_VERSION 1
-
////// Error
typedef enum {
@@ -118,21 +118,6 @@ typedef enum {
GODOT_ERR_PRINTER_ON_FIRE, /// the parallel port printer is engulfed in flames
} godot_error;
-////// bool
-
-typedef bool godot_bool;
-
-#define GODOT_TRUE 1
-#define GODOT_FALSE 0
-
-/////// int
-
-typedef int64_t godot_int;
-
-/////// real
-
-typedef float godot_real;
-
/////// Object (forward declared)
typedef void godot_object;
@@ -164,9 +149,9 @@ typedef void godot_object;
#include <gdnative/plane.h>
-/////// Quat
+/////// Quaternion
-#include <gdnative/quat.h>
+#include <gdnative/quaternion.h>
/////// AABB
@@ -176,9 +161,9 @@ typedef void godot_object;
#include <gdnative/basis.h>
-/////// Transform
+/////// Transform3D
-#include <gdnative/transform.h>
+#include <gdnative/transform_3d.h>
/////// Color
@@ -215,7 +200,7 @@ void GDAPI godot_object_destroy(godot_object *p_o);
////// Singleton API
-godot_object GDAPI *godot_global_get_singleton(char *p_name); // result shouldn't be freed
+godot_object GDAPI *godot_global_get_singleton(char *p_name); // Result shouldn't be freed.
////// MethodBind API
@@ -281,12 +266,10 @@ void GDAPI *godot_alloc(int p_bytes);
void GDAPI *godot_realloc(void *p_ptr, int p_bytes);
void GDAPI godot_free(void *p_ptr);
-//print using Godot's error handler list
+// Helper print functions.
void GDAPI godot_print_error(const char *p_description, const char *p_function, const char *p_file, int p_line);
void GDAPI godot_print_warning(const char *p_description, const char *p_function, const char *p_file, int p_line);
-void GDAPI godot_print(const godot_string *p_message);
-
-// GDNATIVE CORE 1.0.2?
+void GDAPI godot_print_script_error(const char *p_description, const char *p_function, const char *p_file, int p_line);
//tags used for safe dynamic casting
void GDAPI *godot_get_class_tag(const godot_string_name *p_class);
@@ -301,4 +284,4 @@ uint64_t GDAPI godot_object_get_instance_id(const godot_object *p_object);
}
#endif
-#endif // GODOT_C_H
+#endif // GODOT_GDNATIVE_H
diff --git a/modules/gdnative/include/gdnative/math_defs.h b/modules/gdnative/include/gdnative/math_defs.h
new file mode 100644
index 0000000000..b5cf389506
--- /dev/null
+++ b/modules/gdnative/include/gdnative/math_defs.h
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* math_defs.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_GDNATIVE_MATH_DEFS_H
+#define GODOT_GDNATIVE_MATH_DEFS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+
+////// bool
+
+typedef bool godot_bool;
+
+#define GODOT_TRUE 1
+#define GODOT_FALSE 0
+
+/////// int
+
+typedef int64_t godot_int;
+
+/////// float
+
+typedef double godot_float;
+
+#ifdef REAL_T_IS_DOUBLE
+typedef double godot_real_t;
+#else
+typedef float godot_real_t;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // GODOT_C_H
diff --git a/modules/gdnative/include/gdnative/node_path.h b/modules/gdnative/include/gdnative/node_path.h
index 0cd0c3cb9c..a4607c0152 100644
--- a/modules/gdnative/include/gdnative/node_path.h
+++ b/modules/gdnative/include/gdnative/node_path.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -46,42 +46,12 @@ typedef struct {
} godot_node_path;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
#include <gdnative/gdnative.h>
-#include <gdnative/string.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void GDAPI godot_node_path_new(godot_node_path *r_dest, const godot_string *p_from);
+void GDAPI godot_node_path_new(godot_node_path *p_self);
void GDAPI godot_node_path_new_copy(godot_node_path *r_dest, const godot_node_path *p_src);
void GDAPI godot_node_path_destroy(godot_node_path *p_self);
-godot_string GDAPI godot_node_path_as_string(const godot_node_path *p_self);
-
-godot_bool GDAPI godot_node_path_is_absolute(const godot_node_path *p_self);
-
-godot_int GDAPI godot_node_path_get_name_count(const godot_node_path *p_self);
-
-godot_string GDAPI godot_node_path_get_name(const godot_node_path *p_self, const godot_int p_idx);
-
-godot_int GDAPI godot_node_path_get_subname_count(const godot_node_path *p_self);
-
-godot_string GDAPI godot_node_path_get_subname(const godot_node_path *p_self, const godot_int p_idx);
-
-godot_string GDAPI godot_node_path_get_concatenated_subnames(const godot_node_path *p_self);
-
-godot_bool GDAPI godot_node_path_is_empty(const godot_node_path *p_self);
-
-godot_bool GDAPI godot_node_path_operator_equal(const godot_node_path *p_self, const godot_node_path *p_b);
-
-godot_node_path godot_node_path_get_as_property_path(const godot_node_path *p_self);
-
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/packed_arrays.h b/modules/gdnative/include/gdnative/packed_arrays.h
index 6a1727d76f..f9e4ba3a8d 100644
--- a/modules/gdnative/include/gdnative/packed_arrays.h
+++ b/modules/gdnative/include/gdnative/packed_arrays.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -114,6 +114,17 @@ typedef struct {
} godot_packed_vector2_array;
#endif
+/////// PackedVector2iArray
+
+#define GODOT_PACKED_VECTOR2I_ARRAY_SIZE (2 * sizeof(void *))
+
+#ifndef GODOT_CORE_API_GODOT_PACKED_VECTOR2I_ARRAY_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_PACKED_VECTOR2I_ARRAY_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PACKED_VECTOR2I_ARRAY_SIZE];
+} godot_packed_vector2i_array;
+#endif
+
/////// PackedVector3Array
#define GODOT_PACKED_VECTOR3_ARRAY_SIZE (2 * sizeof(void *))
@@ -125,6 +136,17 @@ typedef struct {
} godot_packed_vector3_array;
#endif
+/////// PackedVector3iArray
+
+#define GODOT_PACKED_VECTOR3I_ARRAY_SIZE (2 * sizeof(void *))
+
+#ifndef GODOT_CORE_API_GODOT_PACKED_VECTOR3I_ARRAY_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_PACKED_VECTOR3I_ARRAY_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_PACKED_VECTOR3I_ARRAY_SIZE];
+} godot_packed_vector3i_array;
+#endif
+
/////// PackedColorArray
#define GODOT_PACKED_COLOR_ARRAY_SIZE (2 * sizeof(void *))
@@ -136,348 +158,98 @@ typedef struct {
} godot_packed_color_array;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
-#include <gdnative/array.h>
-#include <gdnative/color.h>
-#include <gdnative/vector2.h>
-#include <gdnative/vector3.h>
-
#include <gdnative/gdnative.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// byte
+// Byte.
-void GDAPI godot_packed_byte_array_new(godot_packed_byte_array *r_dest);
+void GDAPI godot_packed_byte_array_new(godot_packed_byte_array *p_self);
void GDAPI godot_packed_byte_array_new_copy(godot_packed_byte_array *r_dest, const godot_packed_byte_array *p_src);
-void GDAPI godot_packed_byte_array_new_with_array(godot_packed_byte_array *r_dest, const godot_array *p_a);
-
-const uint8_t GDAPI *godot_packed_byte_array_ptr(const godot_packed_byte_array *p_self);
-uint8_t GDAPI *godot_packed_byte_array_ptrw(godot_packed_byte_array *p_self);
-
-void GDAPI godot_packed_byte_array_append(godot_packed_byte_array *p_self, const uint8_t p_data);
-
-void GDAPI godot_packed_byte_array_append_array(godot_packed_byte_array *p_self, const godot_packed_byte_array *p_array);
-
-godot_error GDAPI godot_packed_byte_array_insert(godot_packed_byte_array *p_self, const godot_int p_idx, const uint8_t p_data);
-
-godot_bool GDAPI godot_packed_byte_array_has(godot_packed_byte_array *p_self, const uint8_t p_value);
-
-void GDAPI godot_packed_byte_array_sort(godot_packed_byte_array *p_self);
-
-void GDAPI godot_packed_byte_array_invert(godot_packed_byte_array *p_self);
-
-void GDAPI godot_packed_byte_array_push_back(godot_packed_byte_array *p_self, const uint8_t p_data);
-
-void GDAPI godot_packed_byte_array_remove(godot_packed_byte_array *p_self, const godot_int p_idx);
-
-void GDAPI godot_packed_byte_array_resize(godot_packed_byte_array *p_self, const godot_int p_size);
-
-void GDAPI godot_packed_byte_array_set(godot_packed_byte_array *p_self, const godot_int p_idx, const uint8_t p_data);
-uint8_t GDAPI godot_packed_byte_array_get(const godot_packed_byte_array *p_self, const godot_int p_idx);
-
-godot_int GDAPI godot_packed_byte_array_size(const godot_packed_byte_array *p_self);
-
-godot_bool GDAPI godot_packed_byte_array_empty(const godot_packed_byte_array *p_self);
-
void GDAPI godot_packed_byte_array_destroy(godot_packed_byte_array *p_self);
+uint8_t GDAPI *godot_packed_byte_array_operator_index(godot_packed_byte_array *p_self, godot_int p_index);
+const uint8_t GDAPI *godot_packed_byte_array_operator_index_const(const godot_packed_byte_array *p_self, godot_int p_index);
-// int32
+// Int32.
-void GDAPI godot_packed_int32_array_new(godot_packed_int32_array *r_dest);
+void GDAPI godot_packed_int32_array_new(godot_packed_int32_array *p_self);
void GDAPI godot_packed_int32_array_new_copy(godot_packed_int32_array *r_dest, const godot_packed_int32_array *p_src);
-void GDAPI godot_packed_int32_array_new_with_array(godot_packed_int32_array *r_dest, const godot_array *p_a);
-
-const int32_t GDAPI *godot_packed_int32_array_ptr(const godot_packed_int32_array *p_self);
-int32_t GDAPI *godot_packed_int32_array_ptrw(godot_packed_int32_array *p_self);
-
-void GDAPI godot_packed_int32_array_append(godot_packed_int32_array *p_self, const int32_t p_data);
-
-void GDAPI godot_packed_int32_array_append_array(godot_packed_int32_array *p_self, const godot_packed_int32_array *p_array);
-
-godot_error GDAPI godot_packed_int32_array_insert(godot_packed_int32_array *p_self, const godot_int p_idx, const int32_t p_data);
-
-godot_bool GDAPI godot_packed_int32_array_has(godot_packed_int32_array *p_self, const int32_t p_value);
-
-void GDAPI godot_packed_int32_array_sort(godot_packed_int32_array *p_self);
-
-void GDAPI godot_packed_int32_array_invert(godot_packed_int32_array *p_self);
-
-void GDAPI godot_packed_int32_array_push_back(godot_packed_int32_array *p_self, const int32_t p_data);
-
-void GDAPI godot_packed_int32_array_remove(godot_packed_int32_array *p_self, const godot_int p_idx);
-
-void GDAPI godot_packed_int32_array_resize(godot_packed_int32_array *p_self, const godot_int p_size);
-
-void GDAPI godot_packed_int32_array_set(godot_packed_int32_array *p_self, const godot_int p_idx, const int32_t p_data);
-int32_t GDAPI godot_packed_int32_array_get(const godot_packed_int32_array *p_self, const godot_int p_idx);
-
-godot_int GDAPI godot_packed_int32_array_size(const godot_packed_int32_array *p_self);
-
-godot_bool GDAPI godot_packed_int32_array_empty(const godot_packed_int32_array *p_self);
-
void GDAPI godot_packed_int32_array_destroy(godot_packed_int32_array *p_self);
+int32_t GDAPI *godot_packed_int32_array_operator_index(godot_packed_int32_array *p_self, godot_int p_index);
+const int32_t GDAPI *godot_packed_int32_array_operator_index_const(const godot_packed_int32_array *p_self, godot_int p_index);
-// int64
+// Int64.
-void GDAPI godot_packed_int64_array_new(godot_packed_int64_array *r_dest);
+void GDAPI godot_packed_int64_array_new(godot_packed_int64_array *p_self);
void GDAPI godot_packed_int64_array_new_copy(godot_packed_int64_array *r_dest, const godot_packed_int64_array *p_src);
-void GDAPI godot_packed_int64_array_new_with_array(godot_packed_int64_array *r_dest, const godot_array *p_a);
-
-const int64_t GDAPI *godot_packed_int64_array_ptr(const godot_packed_int64_array *p_self);
-int64_t GDAPI *godot_packed_int64_array_ptrw(godot_packed_int64_array *p_self);
-
-void GDAPI godot_packed_int64_array_append(godot_packed_int64_array *p_self, const int64_t p_data);
-
-void GDAPI godot_packed_int64_array_append_array(godot_packed_int64_array *p_self, const godot_packed_int64_array *p_array);
-
-godot_error GDAPI godot_packed_int64_array_insert(godot_packed_int64_array *p_self, const godot_int p_idx, const int64_t p_data);
-
-godot_bool GDAPI godot_packed_int64_array_has(godot_packed_int64_array *p_self, const int64_t p_value);
-
-void GDAPI godot_packed_int64_array_sort(godot_packed_int64_array *p_self);
-
-void GDAPI godot_packed_int64_array_invert(godot_packed_int64_array *p_self);
-
-void GDAPI godot_packed_int64_array_push_back(godot_packed_int64_array *p_self, const int64_t p_data);
-
-void GDAPI godot_packed_int64_array_remove(godot_packed_int64_array *p_self, const godot_int p_idx);
-
-void GDAPI godot_packed_int64_array_resize(godot_packed_int64_array *p_self, const godot_int p_size);
-
-void GDAPI godot_packed_int64_array_set(godot_packed_int64_array *p_self, const godot_int p_idx, const int64_t p_data);
-int64_t GDAPI godot_packed_int64_array_get(const godot_packed_int64_array *p_self, const godot_int p_idx);
-
-godot_int GDAPI godot_packed_int64_array_size(const godot_packed_int64_array *p_self);
-
-godot_bool GDAPI godot_packed_int64_array_empty(const godot_packed_int64_array *p_self);
-
void GDAPI godot_packed_int64_array_destroy(godot_packed_int64_array *p_self);
+int64_t GDAPI *godot_packed_int64_array_operator_index(godot_packed_int64_array *p_self, godot_int p_index);
+const int64_t GDAPI *godot_packed_int64_array_operator_index_const(const godot_packed_int64_array *p_self, godot_int p_index);
-// float32
+// Float32.
-void GDAPI godot_packed_float32_array_new(godot_packed_float32_array *r_dest);
+void GDAPI godot_packed_float32_array_new(godot_packed_float32_array *p_self);
void GDAPI godot_packed_float32_array_new_copy(godot_packed_float32_array *r_dest, const godot_packed_float32_array *p_src);
-void GDAPI godot_packed_float32_array_new_with_array(godot_packed_float32_array *r_dest, const godot_array *p_a);
-
-const float GDAPI *godot_packed_float32_array_ptr(const godot_packed_float32_array *p_self);
-float GDAPI *godot_packed_float32_array_ptrw(godot_packed_float32_array *p_self);
-
-void GDAPI godot_packed_float32_array_append(godot_packed_float32_array *p_self, const float p_data);
-
-void GDAPI godot_packed_float32_array_append_array(godot_packed_float32_array *p_self, const godot_packed_float32_array *p_array);
-
-godot_error GDAPI godot_packed_float32_array_insert(godot_packed_float32_array *p_self, const godot_int p_idx, const float p_data);
-
-godot_bool GDAPI godot_packed_float32_array_has(godot_packed_float32_array *p_self, const float p_value);
-
-void GDAPI godot_packed_float32_array_sort(godot_packed_float32_array *p_self);
-
-void GDAPI godot_packed_float32_array_invert(godot_packed_float32_array *p_self);
-
-void GDAPI godot_packed_float32_array_push_back(godot_packed_float32_array *p_self, const float p_data);
-
-void GDAPI godot_packed_float32_array_remove(godot_packed_float32_array *p_self, const godot_int p_idx);
-
-void GDAPI godot_packed_float32_array_resize(godot_packed_float32_array *p_self, const godot_int p_size);
-
-void GDAPI godot_packed_float32_array_set(godot_packed_float32_array *p_self, const godot_int p_idx, const float p_data);
-float GDAPI godot_packed_float32_array_get(const godot_packed_float32_array *p_self, const godot_int p_idx);
-
-godot_int GDAPI godot_packed_float32_array_size(const godot_packed_float32_array *p_self);
-
-godot_bool GDAPI godot_packed_float32_array_empty(const godot_packed_float32_array *p_self);
-
void GDAPI godot_packed_float32_array_destroy(godot_packed_float32_array *p_self);
+float GDAPI *godot_packed_float32_array_operator_index(godot_packed_float32_array *p_self, godot_int p_index);
+const float GDAPI *godot_packed_float32_array_operator_index_const(const godot_packed_float32_array *p_self, godot_int p_index);
-// float64
+// Float64.
-void GDAPI godot_packed_float64_array_new(godot_packed_float64_array *r_dest);
+void GDAPI godot_packed_float64_array_new(godot_packed_float64_array *p_self);
void GDAPI godot_packed_float64_array_new_copy(godot_packed_float64_array *r_dest, const godot_packed_float64_array *p_src);
-void GDAPI godot_packed_float64_array_new_with_array(godot_packed_float64_array *r_dest, const godot_array *p_a);
-
-const double GDAPI *godot_packed_float64_array_ptr(const godot_packed_float64_array *p_self);
-double GDAPI *godot_packed_float64_array_ptrw(godot_packed_float64_array *p_self);
-
-void GDAPI godot_packed_float64_array_append(godot_packed_float64_array *p_self, const double p_data);
-
-void GDAPI godot_packed_float64_array_append_array(godot_packed_float64_array *p_self, const godot_packed_float64_array *p_array);
-
-godot_error GDAPI godot_packed_float64_array_insert(godot_packed_float64_array *p_self, const godot_int p_idx, const double p_data);
-
-godot_bool GDAPI godot_packed_float64_array_has(godot_packed_float64_array *p_self, const double p_value);
-
-void GDAPI godot_packed_float64_array_sort(godot_packed_float64_array *p_self);
-
-void GDAPI godot_packed_float64_array_invert(godot_packed_float64_array *p_self);
-
-void GDAPI godot_packed_float64_array_push_back(godot_packed_float64_array *p_self, const double p_data);
-
-void GDAPI godot_packed_float64_array_remove(godot_packed_float64_array *p_self, const godot_int p_idx);
-
-void GDAPI godot_packed_float64_array_resize(godot_packed_float64_array *p_self, const godot_int p_size);
-
-void GDAPI godot_packed_float64_array_set(godot_packed_float64_array *p_self, const godot_int p_idx, const double p_data);
-double GDAPI godot_packed_float64_array_get(const godot_packed_float64_array *p_self, const godot_int p_idx);
-
-godot_int GDAPI godot_packed_float64_array_size(const godot_packed_float64_array *p_self);
-
-godot_bool GDAPI godot_packed_float64_array_empty(const godot_packed_float64_array *p_self);
-
void GDAPI godot_packed_float64_array_destroy(godot_packed_float64_array *p_self);
+double GDAPI *godot_packed_float64_array_operator_index(godot_packed_float64_array *p_self, godot_int p_index);
+const double GDAPI *godot_packed_float64_array_operator_index_const(const godot_packed_float64_array *p_self, godot_int p_index);
-// string
+// String.
-void GDAPI godot_packed_string_array_new(godot_packed_string_array *r_dest);
+void GDAPI godot_packed_string_array_new(godot_packed_string_array *p_self);
void GDAPI godot_packed_string_array_new_copy(godot_packed_string_array *r_dest, const godot_packed_string_array *p_src);
-void GDAPI godot_packed_string_array_new_with_array(godot_packed_string_array *r_dest, const godot_array *p_a);
-
-const godot_string GDAPI *godot_packed_string_array_ptr(const godot_packed_string_array *p_self);
-godot_string GDAPI *godot_packed_string_array_ptrw(godot_packed_string_array *p_self);
-
-void GDAPI godot_packed_string_array_append(godot_packed_string_array *p_self, const godot_string *p_data);
-
-void GDAPI godot_packed_string_array_append_array(godot_packed_string_array *p_self, const godot_packed_string_array *p_array);
-
-godot_error GDAPI godot_packed_string_array_insert(godot_packed_string_array *p_self, const godot_int p_idx, const godot_string *p_data);
-
-godot_bool GDAPI godot_packed_string_array_has(godot_packed_string_array *p_self, const godot_string *p_value);
-
-void GDAPI godot_packed_string_array_sort(godot_packed_string_array *p_self);
-
-void GDAPI godot_packed_string_array_invert(godot_packed_string_array *p_self);
-
-void GDAPI godot_packed_string_array_push_back(godot_packed_string_array *p_self, const godot_string *p_data);
-
-void GDAPI godot_packed_string_array_remove(godot_packed_string_array *p_self, const godot_int p_idx);
-
-void GDAPI godot_packed_string_array_resize(godot_packed_string_array *p_self, const godot_int p_size);
-
-void GDAPI godot_packed_string_array_set(godot_packed_string_array *p_self, const godot_int p_idx, const godot_string *p_data);
-godot_string GDAPI godot_packed_string_array_get(const godot_packed_string_array *p_self, const godot_int p_idx);
-
-godot_int GDAPI godot_packed_string_array_size(const godot_packed_string_array *p_self);
-
-godot_bool GDAPI godot_packed_string_array_empty(const godot_packed_string_array *p_self);
-
void GDAPI godot_packed_string_array_destroy(godot_packed_string_array *p_self);
+godot_string GDAPI *godot_packed_string_array_operator_index(godot_packed_string_array *p_self, godot_int p_index);
+const godot_string GDAPI *godot_packed_string_array_operator_index_const(const godot_packed_string_array *p_self, godot_int p_index);
-// vector2
+// Vector2.
-void GDAPI godot_packed_vector2_array_new(godot_packed_vector2_array *r_dest);
+void GDAPI godot_packed_vector2_array_new(godot_packed_vector2_array *p_self);
void GDAPI godot_packed_vector2_array_new_copy(godot_packed_vector2_array *r_dest, const godot_packed_vector2_array *p_src);
-void GDAPI godot_packed_vector2_array_new_with_array(godot_packed_vector2_array *r_dest, const godot_array *p_a);
-
-const godot_vector2 GDAPI *godot_packed_vector2_array_ptr(const godot_packed_vector2_array *p_self);
-godot_vector2 GDAPI *godot_packed_vector2_array_ptrw(godot_packed_vector2_array *p_self);
-
-void GDAPI godot_packed_vector2_array_append(godot_packed_vector2_array *p_self, const godot_vector2 *p_data);
-
-void GDAPI godot_packed_vector2_array_append_array(godot_packed_vector2_array *p_self, const godot_packed_vector2_array *p_array);
-
-godot_error GDAPI godot_packed_vector2_array_insert(godot_packed_vector2_array *p_self, const godot_int p_idx, const godot_vector2 *p_data);
-
-godot_bool GDAPI godot_packed_vector2_array_has(godot_packed_vector2_array *p_self, const godot_vector2 *p_value);
-
-void GDAPI godot_packed_vector2_array_sort(godot_packed_vector2_array *p_self);
-
-void GDAPI godot_packed_vector2_array_invert(godot_packed_vector2_array *p_self);
-
-void GDAPI godot_packed_vector2_array_push_back(godot_packed_vector2_array *p_self, const godot_vector2 *p_data);
-
-void GDAPI godot_packed_vector2_array_remove(godot_packed_vector2_array *p_self, const godot_int p_idx);
-
-void GDAPI godot_packed_vector2_array_resize(godot_packed_vector2_array *p_self, const godot_int p_size);
-
-void GDAPI godot_packed_vector2_array_set(godot_packed_vector2_array *p_self, const godot_int p_idx, const godot_vector2 *p_data);
-godot_vector2 GDAPI godot_packed_vector2_array_get(const godot_packed_vector2_array *p_self, const godot_int p_idx);
-
-godot_int GDAPI godot_packed_vector2_array_size(const godot_packed_vector2_array *p_self);
-
-godot_bool GDAPI godot_packed_vector2_array_empty(const godot_packed_vector2_array *p_self);
-
void GDAPI godot_packed_vector2_array_destroy(godot_packed_vector2_array *p_self);
+godot_vector2 GDAPI *godot_packed_vector2_array_operator_index(godot_packed_vector2_array *p_self, godot_int p_index);
+const godot_vector2 GDAPI *godot_packed_vector2_array_operator_index_const(const godot_packed_vector2_array *p_self, godot_int p_index);
-// vector3
-
-void GDAPI godot_packed_vector3_array_new(godot_packed_vector3_array *r_dest);
-void GDAPI godot_packed_vector3_array_new_copy(godot_packed_vector3_array *r_dest, const godot_packed_vector3_array *p_src);
-void GDAPI godot_packed_vector3_array_new_with_array(godot_packed_vector3_array *r_dest, const godot_array *p_a);
-
-const godot_vector3 GDAPI *godot_packed_vector3_array_ptr(const godot_packed_vector3_array *p_self);
-godot_vector3 GDAPI *godot_packed_vector3_array_ptrw(godot_packed_vector3_array *p_self);
-
-void GDAPI godot_packed_vector3_array_append(godot_packed_vector3_array *p_self, const godot_vector3 *p_data);
-
-void GDAPI godot_packed_vector3_array_append_array(godot_packed_vector3_array *p_self, const godot_packed_vector3_array *p_array);
-
-godot_error GDAPI godot_packed_vector3_array_insert(godot_packed_vector3_array *p_self, const godot_int p_idx, const godot_vector3 *p_data);
-
-godot_bool GDAPI godot_packed_vector3_array_has(godot_packed_vector3_array *p_self, const godot_vector3 *p_value);
-
-void GDAPI godot_packed_vector3_array_sort(godot_packed_vector3_array *p_self);
-
-void GDAPI godot_packed_vector3_array_invert(godot_packed_vector3_array *p_self);
-
-void GDAPI godot_packed_vector3_array_push_back(godot_packed_vector3_array *p_self, const godot_vector3 *p_data);
+// Vector2i.
-void GDAPI godot_packed_vector3_array_remove(godot_packed_vector3_array *p_self, const godot_int p_idx);
+void GDAPI godot_packed_vector2i_array_new(godot_packed_vector2i_array *p_self);
+void GDAPI godot_packed_vector2i_array_new_copy(godot_packed_vector2i_array *r_dest, const godot_packed_vector2i_array *p_src);
+void GDAPI godot_packed_vector2i_array_destroy(godot_packed_vector2i_array *p_self);
+godot_vector2i GDAPI *godot_packed_vector2i_array_operator_index(godot_packed_vector2i_array *p_self, godot_int p_index);
+const godot_vector2i GDAPI *godot_packed_vector2i_array_operator_index_const(const godot_packed_vector2i_array *p_self, godot_int p_index);
-void GDAPI godot_packed_vector3_array_resize(godot_packed_vector3_array *p_self, const godot_int p_size);
-
-void GDAPI godot_packed_vector3_array_set(godot_packed_vector3_array *p_self, const godot_int p_idx, const godot_vector3 *p_data);
-godot_vector3 GDAPI godot_packed_vector3_array_get(const godot_packed_vector3_array *p_self, const godot_int p_idx);
-
-godot_int GDAPI godot_packed_vector3_array_size(const godot_packed_vector3_array *p_self);
-
-godot_bool GDAPI godot_packed_vector3_array_empty(const godot_packed_vector3_array *p_self);
+// Vector3.
+void GDAPI godot_packed_vector3_array_new(godot_packed_vector3_array *p_self);
+void GDAPI godot_packed_vector3_array_new_copy(godot_packed_vector3_array *r_dest, const godot_packed_vector3_array *p_src);
void GDAPI godot_packed_vector3_array_destroy(godot_packed_vector3_array *p_self);
+godot_vector3 GDAPI *godot_packed_vector3_array_operator_index(godot_packed_vector3_array *p_self, godot_int p_index);
+const godot_vector3 GDAPI *godot_packed_vector3_array_operator_index_const(const godot_packed_vector3_array *p_self, godot_int p_index);
-// color
-
-void GDAPI godot_packed_color_array_new(godot_packed_color_array *r_dest);
-void GDAPI godot_packed_color_array_new_copy(godot_packed_color_array *r_dest, const godot_packed_color_array *p_src);
-void GDAPI godot_packed_color_array_new_with_array(godot_packed_color_array *r_dest, const godot_array *p_a);
-
-const godot_color GDAPI *godot_packed_color_array_ptr(const godot_packed_color_array *p_self);
-godot_color GDAPI *godot_packed_color_array_ptrw(godot_packed_color_array *p_self);
-
-void GDAPI godot_packed_color_array_append(godot_packed_color_array *p_self, const godot_color *p_data);
-
-void GDAPI godot_packed_color_array_append_array(godot_packed_color_array *p_self, const godot_packed_color_array *p_array);
+// Vector3i.
-godot_error GDAPI godot_packed_color_array_insert(godot_packed_color_array *p_self, const godot_int p_idx, const godot_color *p_data);
+void GDAPI godot_packed_vector3i_array_new(godot_packed_vector3i_array *p_self);
+void GDAPI godot_packed_vector3i_array_new_copy(godot_packed_vector3i_array *r_dest, const godot_packed_vector3i_array *p_src);
+void GDAPI godot_packed_vector3i_array_destroy(godot_packed_vector3i_array *p_self);
+godot_vector3i GDAPI *godot_packed_vector3i_array_operator_index(godot_packed_vector3i_array *p_self, godot_int p_index);
+const godot_vector3i GDAPI *godot_packed_vector3i_array_operator_index_const(const godot_packed_vector3i_array *p_self, godot_int p_index);
-godot_bool GDAPI godot_packed_color_array_has(godot_packed_color_array *p_self, const godot_color *p_value);
-
-void GDAPI godot_packed_color_array_sort(godot_packed_color_array *p_self);
-
-void GDAPI godot_packed_color_array_invert(godot_packed_color_array *p_self);
-
-void GDAPI godot_packed_color_array_push_back(godot_packed_color_array *p_self, const godot_color *p_data);
-
-void GDAPI godot_packed_color_array_remove(godot_packed_color_array *p_self, const godot_int p_idx);
-
-void GDAPI godot_packed_color_array_resize(godot_packed_color_array *p_self, const godot_int p_size);
-
-void GDAPI godot_packed_color_array_set(godot_packed_color_array *p_self, const godot_int p_idx, const godot_color *p_data);
-godot_color GDAPI godot_packed_color_array_get(const godot_packed_color_array *p_self, const godot_int p_idx);
-
-godot_int GDAPI godot_packed_color_array_size(const godot_packed_color_array *p_self);
-
-godot_bool GDAPI godot_packed_color_array_empty(const godot_packed_color_array *p_self);
+// Color.
+void GDAPI godot_packed_color_array_new(godot_packed_color_array *p_self);
+void GDAPI godot_packed_color_array_new_copy(godot_packed_color_array *r_dest, const godot_packed_color_array *p_src);
void GDAPI godot_packed_color_array_destroy(godot_packed_color_array *p_self);
+godot_color GDAPI *godot_packed_color_array_operator_index(godot_packed_color_array *p_self, godot_int p_index);
+const godot_color GDAPI *godot_packed_color_array_operator_index_const(const godot_packed_color_array *p_self, godot_int p_index);
#ifdef __cplusplus
}
#endif
-#endif // GODOT_POOL_ARRAYS_H
+#endif // GODOT_PACKED_ARRAYS_H
diff --git a/modules/gdnative/include/gdnative/plane.h b/modules/gdnative/include/gdnative/plane.h
index 9843056489..6cd0ed6307 100644
--- a/modules/gdnative/include/gdnative/plane.h
+++ b/modules/gdnative/include/gdnative/plane.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,9 +35,9 @@
extern "C" {
#endif
-#include <stdint.h>
+#include <gdnative/math_defs.h>
-#define GODOT_PLANE_SIZE 16
+#define GODOT_PLANE_SIZE (sizeof(godot_real_t) * 4)
#ifndef GODOT_CORE_API_GODOT_PLANE_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_PLANE_TYPE_DEFINED
@@ -46,53 +46,10 @@ typedef struct {
} godot_plane;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
#include <gdnative/gdnative.h>
-#include <gdnative/vector3.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void GDAPI godot_plane_new_with_reals(godot_plane *r_dest, const godot_real p_a, const godot_real p_b, const godot_real p_c, const godot_real p_d);
-void GDAPI godot_plane_new_with_vectors(godot_plane *r_dest, const godot_vector3 *p_v1, const godot_vector3 *p_v2, const godot_vector3 *p_v3);
-void GDAPI godot_plane_new_with_normal(godot_plane *r_dest, const godot_vector3 *p_normal, const godot_real p_d);
-
-godot_string GDAPI godot_plane_as_string(const godot_plane *p_self);
-
-godot_plane GDAPI godot_plane_normalized(const godot_plane *p_self);
-
-godot_vector3 GDAPI godot_plane_center(const godot_plane *p_self);
-
-godot_bool GDAPI godot_plane_is_point_over(const godot_plane *p_self, const godot_vector3 *p_point);
-
-godot_real GDAPI godot_plane_distance_to(const godot_plane *p_self, const godot_vector3 *p_point);
-
-godot_bool GDAPI godot_plane_has_point(const godot_plane *p_self, const godot_vector3 *p_point, const godot_real p_epsilon);
-
-godot_vector3 GDAPI godot_plane_project(const godot_plane *p_self, const godot_vector3 *p_point);
-
-godot_bool GDAPI godot_plane_intersect_3(const godot_plane *p_self, godot_vector3 *r_dest, const godot_plane *p_b, const godot_plane *p_c);
-
-godot_bool GDAPI godot_plane_intersects_ray(const godot_plane *p_self, godot_vector3 *r_dest, const godot_vector3 *p_from, const godot_vector3 *p_dir);
-
-godot_bool GDAPI godot_plane_intersects_segment(const godot_plane *p_self, godot_vector3 *r_dest, const godot_vector3 *p_begin, const godot_vector3 *p_end);
-
-godot_plane GDAPI godot_plane_operator_neg(const godot_plane *p_self);
-
-godot_bool GDAPI godot_plane_operator_equal(const godot_plane *p_self, const godot_plane *p_b);
-
-void GDAPI godot_plane_set_normal(godot_plane *p_self, const godot_vector3 *p_normal);
-
-godot_vector3 GDAPI godot_plane_get_normal(const godot_plane *p_self);
-
-godot_real GDAPI godot_plane_get_d(const godot_plane *p_self);
-void GDAPI godot_plane_set_d(godot_plane *p_self, const godot_real p_d);
+void GDAPI godot_plane_new(godot_plane *p_self);
+void GDAPI godot_plane_new_copy(godot_plane *r_dest, const godot_plane *p_src);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/gdnative/quat.h b/modules/gdnative/include/gdnative/quat.h
deleted file mode 100644
index d315b2d754..0000000000
--- a/modules/gdnative/include/gdnative/quat.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*************************************************************************/
-/* quat.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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_QUAT_H
-#define GODOT_QUAT_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-
-#define GODOT_QUAT_SIZE 16
-
-#ifndef GODOT_CORE_API_GODOT_QUAT_TYPE_DEFINED
-#define GODOT_CORE_API_GODOT_QUAT_TYPE_DEFINED
-typedef struct {
- uint8_t _dont_touch_that[GODOT_QUAT_SIZE];
-} godot_quat;
-#endif
-
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
-#include <gdnative/gdnative.h>
-#include <gdnative/vector3.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void GDAPI godot_quat_new(godot_quat *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_z, const godot_real p_w);
-void GDAPI godot_quat_new_with_axis_angle(godot_quat *r_dest, const godot_vector3 *p_axis, const godot_real p_angle);
-void GDAPI godot_quat_new_with_basis(godot_quat *r_dest, const godot_basis *p_basis);
-void GDAPI godot_quat_new_with_euler(godot_quat *r_dest, const godot_vector3 *p_euler);
-
-godot_real GDAPI godot_quat_get_x(const godot_quat *p_self);
-void GDAPI godot_quat_set_x(godot_quat *p_self, const godot_real val);
-
-godot_real GDAPI godot_quat_get_y(const godot_quat *p_self);
-void GDAPI godot_quat_set_y(godot_quat *p_self, const godot_real val);
-
-godot_real GDAPI godot_quat_get_z(const godot_quat *p_self);
-void GDAPI godot_quat_set_z(godot_quat *p_self, const godot_real val);
-
-godot_real GDAPI godot_quat_get_w(const godot_quat *p_self);
-void GDAPI godot_quat_set_w(godot_quat *p_self, const godot_real val);
-
-godot_string GDAPI godot_quat_as_string(const godot_quat *p_self);
-
-godot_real GDAPI godot_quat_length(const godot_quat *p_self);
-
-godot_real GDAPI godot_quat_length_squared(const godot_quat *p_self);
-
-godot_quat GDAPI godot_quat_normalized(const godot_quat *p_self);
-
-godot_bool GDAPI godot_quat_is_normalized(const godot_quat *p_self);
-
-godot_quat GDAPI godot_quat_inverse(const godot_quat *p_self);
-
-godot_real GDAPI godot_quat_dot(const godot_quat *p_self, const godot_quat *p_b);
-
-godot_vector3 GDAPI godot_quat_xform(const godot_quat *p_self, const godot_vector3 *p_v);
-
-godot_quat GDAPI godot_quat_slerp(const godot_quat *p_self, const godot_quat *p_b, const godot_real p_t);
-
-godot_quat GDAPI godot_quat_slerpni(const godot_quat *p_self, const godot_quat *p_b, const godot_real p_t);
-
-godot_quat GDAPI godot_quat_cubic_slerp(const godot_quat *p_self, const godot_quat *p_b, const godot_quat *p_pre_a, const godot_quat *p_post_b, const godot_real p_t);
-
-godot_quat GDAPI godot_quat_operator_multiply(const godot_quat *p_self, const godot_real p_b);
-
-godot_quat GDAPI godot_quat_operator_add(const godot_quat *p_self, const godot_quat *p_b);
-
-godot_quat GDAPI godot_quat_operator_subtract(const godot_quat *p_self, const godot_quat *p_b);
-
-godot_quat GDAPI godot_quat_operator_divide(const godot_quat *p_self, const godot_real p_b);
-
-godot_bool GDAPI godot_quat_operator_equal(const godot_quat *p_self, const godot_quat *p_b);
-
-godot_quat GDAPI godot_quat_operator_neg(const godot_quat *p_self);
-
-void GDAPI godot_quat_set_axis_angle(godot_quat *p_self, const godot_vector3 *p_axis, const godot_real p_angle);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // GODOT_QUAT_H
diff --git a/modules/gdnative/include/gdnative/quaternion.h b/modules/gdnative/include/gdnative/quaternion.h
new file mode 100644
index 0000000000..75754e6ab5
--- /dev/null
+++ b/modules/gdnative/include/gdnative/quaternion.h
@@ -0,0 +1,60 @@
+/*************************************************************************/
+/* quaternion.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_QUATERNION_H
+#define GODOT_QUATERNION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <gdnative/math_defs.h>
+
+#define GODOT_QUATERNION_SIZE (sizeof(godot_real_t) * 4)
+
+#ifndef GODOT_CORE_API_GODOT_QUATERNION_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_QUATERNION_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_QUATERNION_SIZE];
+} godot_quaternion;
+#endif
+
+#include <gdnative/gdnative.h>
+
+void GDAPI godot_quaternion_new(godot_quaternion *p_self);
+void GDAPI godot_quaternion_new_copy(godot_quaternion *r_dest, const godot_quaternion *p_src);
+godot_real_t GDAPI *godot_quaternion_operator_index(godot_quaternion *p_self, godot_int p_index);
+const godot_real_t GDAPI *godot_quaternion_operator_index_const(const godot_quaternion *p_self, godot_int p_index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // GODOT_QUATERNION_H
diff --git a/modules/gdnative/include/gdnative/rect2.h b/modules/gdnative/include/gdnative/rect2.h
index f317afc9da..326462be43 100644
--- a/modules/gdnative/include/gdnative/rect2.h
+++ b/modules/gdnative/include/gdnative/rect2.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,119 +35,32 @@
extern "C" {
#endif
-#include <stdint.h>
+#include <gdnative/math_defs.h>
+
+#define GODOT_RECT2_SIZE (sizeof(godot_real_t) * 4)
#ifndef GODOT_CORE_API_GODOT_RECT2_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_RECT2_TYPE_DEFINED
typedef struct godot_rect2 {
- uint8_t _dont_touch_that[16];
+ uint8_t _dont_touch_that[GODOT_RECT2_SIZE];
} godot_rect2;
#endif
+#define GODOT_RECT2I_SIZE (sizeof(int32_t) * 4)
+
#ifndef GODOT_CORE_API_GODOT_RECT2I_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_RECT2I_TYPE_DEFINED
typedef struct godot_rect2i {
- uint8_t _dont_touch_that[16];
+ uint8_t _dont_touch_that[GODOT_RECT2I_SIZE];
} godot_rect2i;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
#include <gdnative/gdnative.h>
-#include <gdnative/vector2.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Rect2
-
-void GDAPI godot_rect2_new_with_position_and_size(godot_rect2 *r_dest, const godot_vector2 *p_pos, const godot_vector2 *p_size);
-void GDAPI godot_rect2_new(godot_rect2 *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_width, const godot_real p_height);
-
-godot_string GDAPI godot_rect2_as_string(const godot_rect2 *p_self);
-
-godot_rect2i GDAPI godot_rect2_as_rect2i(const godot_rect2 *p_self);
-
-godot_real GDAPI godot_rect2_get_area(const godot_rect2 *p_self);
-
-godot_bool GDAPI godot_rect2_intersects(const godot_rect2 *p_self, const godot_rect2 *p_b);
-
-godot_bool GDAPI godot_rect2_encloses(const godot_rect2 *p_self, const godot_rect2 *p_b);
-
-godot_bool GDAPI godot_rect2_has_no_area(const godot_rect2 *p_self);
-
-godot_rect2 GDAPI godot_rect2_clip(const godot_rect2 *p_self, const godot_rect2 *p_b);
-
-godot_rect2 GDAPI godot_rect2_merge(const godot_rect2 *p_self, const godot_rect2 *p_b);
-
-godot_bool GDAPI godot_rect2_has_point(const godot_rect2 *p_self, const godot_vector2 *p_point);
-
-godot_rect2 GDAPI godot_rect2_grow(const godot_rect2 *p_self, const godot_real p_by);
-
-godot_rect2 GDAPI godot_rect2_grow_individual(const godot_rect2 *p_self, const godot_real p_left, const godot_real p_top, const godot_real p_right, const godot_real p_bottom);
-
-godot_rect2 GDAPI godot_rect2_grow_margin(const godot_rect2 *p_self, const godot_int p_margin, const godot_real p_by);
-
-godot_rect2 GDAPI godot_rect2_abs(const godot_rect2 *p_self);
-
-godot_rect2 GDAPI godot_rect2_expand(const godot_rect2 *p_self, const godot_vector2 *p_to);
-
-godot_bool GDAPI godot_rect2_operator_equal(const godot_rect2 *p_self, const godot_rect2 *p_b);
-
-godot_vector2 GDAPI godot_rect2_get_position(const godot_rect2 *p_self);
-
-godot_vector2 GDAPI godot_rect2_get_size(const godot_rect2 *p_self);
-
-void GDAPI godot_rect2_set_position(godot_rect2 *p_self, const godot_vector2 *p_pos);
-
-void GDAPI godot_rect2_set_size(godot_rect2 *p_self, const godot_vector2 *p_size);
-
-// Rect2I
-
-void GDAPI godot_rect2i_new_with_position_and_size(godot_rect2i *r_dest, const godot_vector2i *p_pos, const godot_vector2i *p_size);
-void GDAPI godot_rect2i_new(godot_rect2i *r_dest, const godot_int p_x, const godot_int p_y, const godot_int p_width, const godot_int p_height);
-
-godot_string GDAPI godot_rect2i_as_string(const godot_rect2i *p_self);
-
-godot_rect2 GDAPI godot_rect2i_as_rect2(const godot_rect2i *p_self);
-
-godot_int GDAPI godot_rect2i_get_area(const godot_rect2i *p_self);
-
-godot_bool GDAPI godot_rect2i_intersects(const godot_rect2i *p_self, const godot_rect2i *p_b);
-
-godot_bool GDAPI godot_rect2i_encloses(const godot_rect2i *p_self, const godot_rect2i *p_b);
-
-godot_bool GDAPI godot_rect2i_has_no_area(const godot_rect2i *p_self);
-
-godot_rect2i GDAPI godot_rect2i_clip(const godot_rect2i *p_self, const godot_rect2i *p_b);
-
-godot_rect2i GDAPI godot_rect2i_merge(const godot_rect2i *p_self, const godot_rect2i *p_b);
-
-godot_bool GDAPI godot_rect2i_has_point(const godot_rect2i *p_self, const godot_vector2i *p_point);
-
-godot_rect2i GDAPI godot_rect2i_grow(const godot_rect2i *p_self, const godot_int p_by);
-
-godot_rect2i GDAPI godot_rect2i_grow_individual(const godot_rect2i *p_self, const godot_int p_left, const godot_int p_top, const godot_int p_right, const godot_int p_bottom);
-
-godot_rect2i GDAPI godot_rect2i_grow_margin(const godot_rect2i *p_self, const godot_int p_margin, const godot_int p_by);
-
-godot_rect2i GDAPI godot_rect2i_abs(const godot_rect2i *p_self);
-
-godot_rect2i GDAPI godot_rect2i_expand(const godot_rect2i *p_self, const godot_vector2i *p_to);
-
-godot_bool GDAPI godot_rect2i_operator_equal(const godot_rect2i *p_self, const godot_rect2i *p_b);
-
-godot_vector2i GDAPI godot_rect2i_get_position(const godot_rect2i *p_self);
-
-godot_vector2i GDAPI godot_rect2i_get_size(const godot_rect2i *p_self);
-
-void GDAPI godot_rect2i_set_position(godot_rect2i *p_self, const godot_vector2i *p_pos);
-void GDAPI godot_rect2i_set_size(godot_rect2i *p_self, const godot_vector2i *p_size);
+void GDAPI godot_rect2_new(godot_rect2 *p_self);
+void GDAPI godot_rect2_new_copy(godot_rect2 *r_dest, const godot_rect2 *p_src);
+void GDAPI godot_rect2i_new(godot_rect2i *p_self);
+void GDAPI godot_rect2i_new_copy(godot_rect2i *r_dest, const godot_rect2i *p_src);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/gdnative/rid.h b/modules/gdnative/include/gdnative/rid.h
index 73b601dc04..bc832fbeb9 100644
--- a/modules/gdnative/include/gdnative/rid.h
+++ b/modules/gdnative/include/gdnative/rid.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -46,26 +46,10 @@ typedef struct {
} godot_rid;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
#include <gdnative/gdnative.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void GDAPI godot_rid_new(godot_rid *r_dest);
-
-godot_int GDAPI godot_rid_get_id(const godot_rid *p_self);
-
-void GDAPI godot_rid_new_with_resource(godot_rid *r_dest, const godot_object *p_from);
-
-godot_bool GDAPI godot_rid_operator_equal(const godot_rid *p_self, const godot_rid *p_b);
-
-godot_bool GDAPI godot_rid_operator_less(const godot_rid *p_self, const godot_rid *p_b);
+void GDAPI godot_rid_new(godot_rid *p_self);
+void GDAPI godot_rid_new_copy(godot_rid *r_dest, const godot_rid *p_src);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/gdnative/signal.h b/modules/gdnative/include/gdnative/signal.h
new file mode 100644
index 0000000000..41a76d0510
--- /dev/null
+++ b/modules/gdnative/include/gdnative/signal.h
@@ -0,0 +1,60 @@
+/*************************************************************************/
+/* signal.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_SIGNAL_H
+#define GODOT_SIGNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+// Alignment hardcoded in `core/variant/callable.h`.
+#define GODOT_SIGNAL_SIZE (16)
+
+#ifndef GODOT_CORE_API_GODOT_SIGNAL_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_SIGNAL_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_SIGNAL_SIZE];
+} godot_signal;
+#endif
+
+#include <gdnative/gdnative.h>
+
+void GDAPI godot_signal_new(godot_signal *p_self);
+void GDAPI godot_signal_new_copy(godot_signal *r_dest, const godot_signal *p_src);
+void GDAPI godot_signal_destroy(godot_signal *p_self);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/modules/gdnative/include/gdnative/string.h b/modules/gdnative/include/gdnative/string.h
index 0582d95f63..79de52c80f 100644
--- a/modules/gdnative/include/gdnative/string.h
+++ b/modules/gdnative/include/gdnative/string.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,60 +35,31 @@
extern "C" {
#endif
+#include <stddef.h>
#include <stdint.h>
-#include <wchar.h>
+
+#ifndef __cplusplus
+typedef uint16_t char16_t;
+typedef uint32_t char32_t;
+#endif
typedef char32_t godot_char_type;
#define GODOT_STRING_SIZE sizeof(void *)
-#define GODOT_CHAR_STRING_SIZE sizeof(void *)
-#define GODOT_CHAR16_STRING_SIZE sizeof(void *)
#ifndef GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_STRING_TYPE_DEFINED
typedef struct {
uint8_t _dont_touch_that[GODOT_STRING_SIZE];
} godot_string;
-
-#endif
-
-#ifndef GODOT_CORE_API_GODOT_CHAR_STRING_TYPE_DEFINED
-#define GODOT_CORE_API_GODOT_CHAR_STRING_TYPE_DEFINED
-typedef struct {
- uint8_t _dont_touch_that[GODOT_CHAR_STRING_SIZE];
-} godot_char_string;
-#endif
-
-#ifndef GODOT_CORE_API_GODOT_CHAR16_STRING_TYPE_DEFINED
-#define GODOT_CORE_API_GODOT_CHAR16_STRING_TYPE_DEFINED
-typedef struct {
- uint8_t _dont_touch_that[GODOT_CHAR16_STRING_SIZE];
-} godot_char16_string;
-#endif
-
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
#endif
-#include <gdnative/array.h>
#include <gdnative/gdnative.h>
-#include <gdnative/variant.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-godot_int GDAPI godot_char_string_length(const godot_char_string *p_cs);
-const char GDAPI *godot_char_string_get_data(const godot_char_string *p_cs);
-void GDAPI godot_char_string_destroy(godot_char_string *p_cs);
-
-godot_int GDAPI godot_char16_string_length(const godot_char16_string *p_cs);
-const char16_t GDAPI *godot_char16_string_get_data(const godot_char16_string *p_cs);
-void GDAPI godot_char16_string_destroy(godot_char16_string *p_cs);
+#include <gdnative/math_defs.h>
void GDAPI godot_string_new(godot_string *r_dest);
void GDAPI godot_string_new_copy(godot_string *r_dest, const godot_string *p_src);
+void GDAPI godot_string_destroy(godot_string *p_self);
void GDAPI godot_string_new_with_latin1_chars(godot_string *r_dest, const char *p_contents);
void GDAPI godot_string_new_with_utf8_chars(godot_string *r_dest, const char *p_contents);
@@ -102,197 +73,14 @@ void GDAPI godot_string_new_with_utf16_chars_and_len(godot_string *r_dest, const
void GDAPI godot_string_new_with_utf32_chars_and_len(godot_string *r_dest, const char32_t *p_contents, const int p_size);
void GDAPI godot_string_new_with_wide_chars_and_len(godot_string *r_dest, const wchar_t *p_contents, const int p_size);
-const godot_char_type GDAPI *godot_string_operator_index(godot_string *p_self, const godot_int p_idx);
-godot_char_type GDAPI godot_string_operator_index_const(const godot_string *p_self, const godot_int p_idx);
-const godot_char_type GDAPI *godot_string_get_data(const godot_string *p_self);
-
-godot_bool GDAPI godot_string_operator_equal(const godot_string *p_self, const godot_string *p_b);
-godot_bool GDAPI godot_string_operator_less(const godot_string *p_self, const godot_string *p_b);
-godot_string GDAPI godot_string_operator_plus(const godot_string *p_self, const godot_string *p_b);
-
-/* Standard size stuff */
-
-/*+++*/ godot_int GDAPI godot_string_length(const godot_string *p_self);
-
-/* Helpers */
-
-signed char GDAPI godot_string_casecmp_to(const godot_string *p_self, const godot_string *p_str);
-signed char GDAPI godot_string_nocasecmp_to(const godot_string *p_self, const godot_string *p_str);
-signed char GDAPI godot_string_naturalnocasecmp_to(const godot_string *p_self, const godot_string *p_str);
-
-godot_bool GDAPI godot_string_begins_with(const godot_string *p_self, const godot_string *p_string);
-godot_bool GDAPI godot_string_begins_with_char_array(const godot_string *p_self, const char *p_char_array);
-godot_packed_string_array GDAPI godot_string_bigrams(const godot_string *p_self);
-godot_string GDAPI godot_string_chr(godot_char_type p_character);
-godot_bool GDAPI godot_string_ends_with(const godot_string *p_self, const godot_string *p_string);
-godot_bool GDAPI godot_string_ends_with_char_array(const godot_string *p_self, const char *p_char_array);
-godot_int GDAPI godot_string_count(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to);
-godot_int GDAPI godot_string_countn(const godot_string *p_self, const godot_string *p_what, godot_int p_from, godot_int p_to);
-godot_int GDAPI godot_string_find(const godot_string *p_self, const godot_string *p_what);
-godot_int GDAPI godot_string_find_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
-godot_int GDAPI godot_string_findmk(const godot_string *p_self, const godot_packed_string_array *p_keys);
-godot_int GDAPI godot_string_findmk_from(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from);
-godot_int GDAPI godot_string_findmk_from_in_place(const godot_string *p_self, const godot_packed_string_array *p_keys, godot_int p_from, godot_int *r_key);
-godot_int GDAPI godot_string_findn(const godot_string *p_self, const godot_string *p_what);
-godot_int GDAPI godot_string_findn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
-godot_string GDAPI godot_string_format(const godot_string *p_self, const godot_variant *p_values);
-godot_string GDAPI godot_string_format_with_custom_placeholder(const godot_string *p_self, const godot_variant *p_values, const char *p_placeholder);
-godot_string GDAPI godot_string_hex_encode_buffer(const uint8_t *p_buffer, godot_int p_len);
-godot_int GDAPI godot_string_hex_to_int(const godot_string *p_self);
-godot_int GDAPI godot_string_hex_to_int_with_prefix(const godot_string *p_self);
-godot_string GDAPI godot_string_insert(const godot_string *p_self, godot_int p_at_pos, const godot_string *p_string);
-godot_bool GDAPI godot_string_is_numeric(const godot_string *p_self);
-godot_bool GDAPI godot_string_is_subsequence_of(const godot_string *p_self, const godot_string *p_string);
-godot_bool GDAPI godot_string_is_subsequence_ofi(const godot_string *p_self, const godot_string *p_string);
-godot_string GDAPI godot_string_lpad(const godot_string *p_self, godot_int p_min_length);
-godot_string GDAPI godot_string_lpad_with_custom_character(const godot_string *p_self, godot_int p_min_length, const godot_string *p_character);
-godot_bool GDAPI godot_string_match(const godot_string *p_self, const godot_string *p_wildcard);
-godot_bool GDAPI godot_string_matchn(const godot_string *p_self, const godot_string *p_wildcard);
-godot_string GDAPI godot_string_md5(const uint8_t *p_md5);
-godot_string GDAPI godot_string_num(double p_num);
-godot_string GDAPI godot_string_num_int64(int64_t p_num, godot_int p_base);
-godot_string GDAPI godot_string_num_int64_capitalized(int64_t p_num, godot_int p_base, godot_bool p_capitalize_hex);
-godot_string GDAPI godot_string_num_real(double p_num);
-godot_string GDAPI godot_string_num_scientific(double p_num);
-godot_string GDAPI godot_string_num_with_decimals(double p_num, godot_int p_decimals);
-godot_string GDAPI godot_string_pad_decimals(const godot_string *p_self, godot_int p_digits);
-godot_string GDAPI godot_string_pad_zeros(const godot_string *p_self, godot_int p_digits);
-godot_string GDAPI godot_string_replace_first(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with);
-godot_string GDAPI godot_string_replace(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with);
-godot_string GDAPI godot_string_replacen(const godot_string *p_self, const godot_string *p_key, const godot_string *p_with);
-godot_int GDAPI godot_string_rfind(const godot_string *p_self, const godot_string *p_what);
-godot_int GDAPI godot_string_rfindn(const godot_string *p_self, const godot_string *p_what);
-godot_int GDAPI godot_string_rfind_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
-godot_int GDAPI godot_string_rfindn_from(const godot_string *p_self, const godot_string *p_what, godot_int p_from);
-godot_string GDAPI godot_string_rpad(const godot_string *p_self, godot_int p_min_length);
-godot_string GDAPI godot_string_rpad_with_custom_character(const godot_string *p_self, godot_int p_min_length, const godot_string *p_character);
-godot_real GDAPI godot_string_similarity(const godot_string *p_self, const godot_string *p_string);
-godot_string GDAPI godot_string_sprintf(const godot_string *p_self, const godot_array *p_values, godot_bool *p_error);
-godot_string GDAPI godot_string_substr(const godot_string *p_self, godot_int p_from, godot_int p_chars);
-double GDAPI godot_string_to_float(const godot_string *p_self);
-godot_int GDAPI godot_string_to_int(const godot_string *p_self);
+const char GDAPI *godot_string_to_latin1_chars(const godot_string *p_self);
+const char GDAPI *godot_string_to_utf8_chars(const godot_string *p_self);
+const char16_t GDAPI *godot_string_to_utf16_chars(const godot_string *p_self);
+const char32_t GDAPI *godot_string_to_utf32_chars(const godot_string *p_self);
+const wchar_t GDAPI *godot_string_to_wide_chars(const godot_string *p_self);
-godot_string GDAPI godot_string_camelcase_to_underscore(const godot_string *p_self);
-godot_string GDAPI godot_string_camelcase_to_underscore_lowercased(const godot_string *p_self);
-godot_string GDAPI godot_string_capitalize(const godot_string *p_self);
-
-double GDAPI godot_string_char_to_float(const char *p_what);
-double GDAPI godot_string_wchar_to_float(const wchar_t *p_str, const wchar_t **r_end);
-
-godot_int GDAPI godot_string_char_to_int(const char *p_what);
-godot_int GDAPI godot_string_wchar_to_int(const wchar_t *p_str);
-
-godot_int GDAPI godot_string_char_to_int_with_len(const char *p_what, godot_int p_len);
-godot_int GDAPI godot_string_wchar_to_int_with_len(const wchar_t *p_str, int p_len);
-
-godot_int GDAPI godot_string_get_slice_count(const godot_string *p_self, const godot_string *p_splitter);
-godot_string GDAPI godot_string_get_slice(const godot_string *p_self, const godot_string *p_splitter, godot_int p_slice);
-godot_string GDAPI godot_string_get_slicec(const godot_string *p_self, godot_char_type p_splitter, godot_int p_slice);
-
-godot_packed_string_array GDAPI godot_string_split(const godot_string *p_self, const godot_string *p_splitter);
-godot_packed_string_array GDAPI godot_string_split_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
-godot_packed_string_array GDAPI godot_string_split_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit);
-
-godot_packed_string_array GDAPI godot_string_rsplit(const godot_string *p_self, const godot_string *p_splitter);
-godot_packed_string_array GDAPI godot_string_rsplit_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
-godot_packed_string_array GDAPI godot_string_rsplit_with_maxsplit(const godot_string *p_self, const godot_string *p_splitter, const godot_bool p_allow_empty, const godot_int p_maxsplit);
-
-godot_packed_float32_array GDAPI godot_string_split_floats(const godot_string *p_self, const godot_string *p_splitter);
-godot_packed_float32_array GDAPI godot_string_split_floats_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
-godot_packed_float32_array GDAPI godot_string_split_floats_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters);
-godot_packed_float32_array GDAPI godot_string_split_floats_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters);
-godot_packed_int32_array GDAPI godot_string_split_ints(const godot_string *p_self, const godot_string *p_splitter);
-godot_packed_int32_array GDAPI godot_string_split_ints_allow_empty(const godot_string *p_self, const godot_string *p_splitter);
-godot_packed_int32_array GDAPI godot_string_split_ints_mk(const godot_string *p_self, const godot_packed_string_array *p_splitters);
-godot_packed_int32_array GDAPI godot_string_split_ints_mk_allow_empty(const godot_string *p_self, const godot_packed_string_array *p_splitters);
-
-godot_packed_string_array GDAPI godot_string_split_spaces(const godot_string *p_self);
-
-godot_char_type GDAPI godot_string_char_lowercase(godot_char_type p_char);
-godot_char_type GDAPI godot_string_char_uppercase(godot_char_type p_char);
-godot_string GDAPI godot_string_to_lower(const godot_string *p_self);
-godot_string GDAPI godot_string_to_upper(const godot_string *p_self);
-
-godot_string GDAPI godot_string_get_basename(const godot_string *p_self);
-godot_string GDAPI godot_string_get_extension(const godot_string *p_self);
-godot_string GDAPI godot_string_left(const godot_string *p_self, godot_int p_pos);
-godot_char_type GDAPI godot_string_ord_at(const godot_string *p_self, godot_int p_idx);
-godot_string GDAPI godot_string_plus_file(const godot_string *p_self, const godot_string *p_file);
-godot_string GDAPI godot_string_right(const godot_string *p_self, godot_int p_pos);
-godot_string GDAPI godot_string_repeat(const godot_string *p_self, godot_int p_count);
-godot_string GDAPI godot_string_strip_edges(const godot_string *p_self, godot_bool p_left, godot_bool p_right);
-godot_string GDAPI godot_string_strip_escapes(const godot_string *p_self);
-
-void GDAPI godot_string_erase(godot_string *p_self, godot_int p_pos, godot_int p_chars);
-
-godot_char_string GDAPI godot_string_ascii(const godot_string *p_self);
-godot_char_string GDAPI godot_string_latin1(const godot_string *p_self);
-
-godot_char_string GDAPI godot_string_utf8(const godot_string *p_self);
-godot_bool GDAPI godot_string_parse_utf8(godot_string *p_self, const char *p_utf8);
-godot_bool GDAPI godot_string_parse_utf8_with_len(godot_string *p_self, const char *p_utf8, godot_int p_len);
-
-godot_char16_string GDAPI godot_string_utf16(const godot_string *p_self);
-godot_bool GDAPI godot_string_parse_utf16(godot_string *p_self, const char16_t *p_utf16);
-godot_bool GDAPI godot_string_parse_utf16_with_len(godot_string *p_self, const char16_t *p_utf16, godot_int p_len);
-
-uint32_t GDAPI godot_string_hash(const godot_string *p_self);
-uint64_t GDAPI godot_string_hash64(const godot_string *p_self);
-
-uint32_t GDAPI godot_string_hash_chars(const char *p_cstr);
-uint32_t GDAPI godot_string_hash_chars_with_len(const char *p_cstr, godot_int p_len);
-uint32_t GDAPI godot_string_hash_wide_chars(const wchar_t *p_str);
-uint32_t GDAPI godot_string_hash_wide_chars_with_len(const wchar_t *p_str, godot_int p_len);
-
-godot_packed_byte_array GDAPI godot_string_md5_buffer(const godot_string *p_self);
-godot_string GDAPI godot_string_md5_text(const godot_string *p_self);
-godot_packed_byte_array GDAPI godot_string_sha1_buffer(const godot_string *p_self);
-godot_string GDAPI godot_string_sha1_text(const godot_string *p_self);
-godot_packed_byte_array GDAPI godot_string_sha256_buffer(const godot_string *p_self);
-godot_string GDAPI godot_string_sha256_text(const godot_string *p_self);
-
-godot_bool godot_string_empty(const godot_string *p_self);
-
-// path functions
-godot_string GDAPI godot_string_get_base_dir(const godot_string *p_self);
-godot_string GDAPI godot_string_get_file(const godot_string *p_self);
-godot_string GDAPI godot_string_humanize_size(size_t p_size);
-godot_bool GDAPI godot_string_is_abs_path(const godot_string *p_self);
-godot_bool GDAPI godot_string_is_rel_path(const godot_string *p_self);
-godot_bool GDAPI godot_string_is_resource_file(const godot_string *p_self);
-godot_string GDAPI godot_string_path_to(const godot_string *p_self, const godot_string *p_path);
-godot_string GDAPI godot_string_path_to_file(const godot_string *p_self, const godot_string *p_path);
-godot_string GDAPI godot_string_simplify_path(const godot_string *p_self);
-
-godot_string GDAPI godot_string_c_escape(const godot_string *p_self);
-godot_string GDAPI godot_string_c_escape_multiline(const godot_string *p_self);
-godot_string GDAPI godot_string_c_unescape(const godot_string *p_self);
-godot_string GDAPI godot_string_http_escape(const godot_string *p_self);
-godot_string GDAPI godot_string_http_unescape(const godot_string *p_self);
-godot_string GDAPI godot_string_json_escape(const godot_string *p_self);
-godot_string GDAPI godot_string_xml_escape(const godot_string *p_self);
-godot_string GDAPI godot_string_xml_escape_with_quotes(const godot_string *p_self);
-godot_string GDAPI godot_string_xml_unescape(const godot_string *p_self);
-
-godot_string GDAPI godot_string_percent_decode(const godot_string *p_self);
-godot_string GDAPI godot_string_percent_encode(const godot_string *p_self);
-godot_string GDAPI godot_string_join(const godot_string *p_self, const godot_packed_string_array *p_parts);
-
-godot_bool GDAPI godot_string_is_valid_filename(const godot_string *p_self);
-godot_bool GDAPI godot_string_is_valid_float(const godot_string *p_self);
-godot_bool GDAPI godot_string_is_valid_hex_number(const godot_string *p_self, godot_bool p_with_prefix);
-godot_bool GDAPI godot_string_is_valid_html_color(const godot_string *p_self);
-godot_bool GDAPI godot_string_is_valid_identifier(const godot_string *p_self);
-godot_bool GDAPI godot_string_is_valid_integer(const godot_string *p_self);
-godot_bool GDAPI godot_string_is_valid_ip_address(const godot_string *p_self);
-
-godot_string GDAPI godot_string_dedent(const godot_string *p_self);
-godot_string GDAPI godot_string_trim_prefix(const godot_string *p_self, const godot_string *p_prefix);
-godot_string GDAPI godot_string_trim_suffix(const godot_string *p_self, const godot_string *p_suffix);
-godot_string GDAPI godot_string_lstrip(const godot_string *p_self, const godot_string *p_chars);
-godot_string GDAPI godot_string_rstrip(const godot_string *p_self, const godot_string *p_chars);
-
-void GDAPI godot_string_destroy(godot_string *p_self);
+char32_t GDAPI *godot_string_operator_index(godot_string *p_self, godot_int p_index);
+const char32_t GDAPI *godot_string_operator_index_const(const godot_string *p_self, godot_int p_index);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/gdnative/string_name.h b/modules/gdnative/include/gdnative/string_name.h
index f2555ab98f..346f626e81 100644
--- a/modules/gdnative/include/gdnative/string_name.h
+++ b/modules/gdnative/include/gdnative/string_name.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,30 +47,14 @@ typedef struct {
} godot_string_name;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
#include <gdnative/gdnative.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void GDAPI godot_string_name_new(godot_string_name *r_dest, const godot_string *p_name);
-void GDAPI godot_string_name_new_data(godot_string_name *r_dest, const char *p_name);
-
-godot_string GDAPI godot_string_name_get_name(const godot_string_name *p_self);
-
-uint32_t GDAPI godot_string_name_get_hash(const godot_string_name *p_self);
-const void GDAPI *godot_string_name_get_data_unique_pointer(const godot_string_name *p_self);
-
-godot_bool GDAPI godot_string_name_operator_equal(const godot_string_name *p_self, const godot_string_name *p_other);
-godot_bool GDAPI godot_string_name_operator_less(const godot_string_name *p_self, const godot_string_name *p_other);
-
+void GDAPI godot_string_name_new(godot_string_name *r_dest);
+void GDAPI godot_string_name_new_copy(godot_string_name *r_dest, const godot_string_name *p_src);
void GDAPI godot_string_name_destroy(godot_string_name *p_self);
+void GDAPI godot_string_name_new_with_latin1_chars(godot_string_name *r_dest, const char *p_contents);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/gdnative/include/gdnative/transform.h b/modules/gdnative/include/gdnative/transform.h
deleted file mode 100644
index bc51438b17..0000000000
--- a/modules/gdnative/include/gdnative/transform.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*************************************************************************/
-/* transform.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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_TRANSFORM_H
-#define GODOT_TRANSFORM_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-
-#define GODOT_TRANSFORM_SIZE 48
-
-#ifndef GODOT_CORE_API_GODOT_TRANSFORM_TYPE_DEFINED
-#define GODOT_CORE_API_GODOT_TRANSFORM_TYPE_DEFINED
-typedef struct {
- uint8_t _dont_touch_that[GODOT_TRANSFORM_SIZE];
-} godot_transform;
-#endif
-
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
-#include <gdnative/basis.h>
-#include <gdnative/gdnative.h>
-#include <gdnative/variant.h>
-#include <gdnative/vector3.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void GDAPI godot_transform_new_with_axis_origin(godot_transform *r_dest, const godot_vector3 *p_x_axis, const godot_vector3 *p_y_axis, const godot_vector3 *p_z_axis, const godot_vector3 *p_origin);
-void GDAPI godot_transform_new(godot_transform *r_dest, const godot_basis *p_basis, const godot_vector3 *p_origin);
-void GDAPI godot_transform_new_with_quat(godot_transform *r_dest, const godot_quat *p_quat);
-
-godot_basis GDAPI godot_transform_get_basis(const godot_transform *p_self);
-void GDAPI godot_transform_set_basis(godot_transform *p_self, const godot_basis *p_v);
-
-godot_vector3 GDAPI godot_transform_get_origin(const godot_transform *p_self);
-void GDAPI godot_transform_set_origin(godot_transform *p_self, const godot_vector3 *p_v);
-
-godot_string GDAPI godot_transform_as_string(const godot_transform *p_self);
-
-godot_transform GDAPI godot_transform_inverse(const godot_transform *p_self);
-
-godot_transform GDAPI godot_transform_affine_inverse(const godot_transform *p_self);
-
-godot_transform GDAPI godot_transform_orthonormalized(const godot_transform *p_self);
-
-godot_transform GDAPI godot_transform_rotated(const godot_transform *p_self, const godot_vector3 *p_axis, const godot_real p_phi);
-
-godot_transform GDAPI godot_transform_scaled(const godot_transform *p_self, const godot_vector3 *p_scale);
-
-godot_transform GDAPI godot_transform_translated(const godot_transform *p_self, const godot_vector3 *p_ofs);
-
-godot_transform GDAPI godot_transform_looking_at(const godot_transform *p_self, const godot_vector3 *p_target, const godot_vector3 *p_up);
-
-godot_plane GDAPI godot_transform_xform_plane(const godot_transform *p_self, const godot_plane *p_v);
-
-godot_plane GDAPI godot_transform_xform_inv_plane(const godot_transform *p_self, const godot_plane *p_v);
-
-void GDAPI godot_transform_new_identity(godot_transform *r_dest);
-
-godot_bool GDAPI godot_transform_operator_equal(const godot_transform *p_self, const godot_transform *p_b);
-
-godot_transform GDAPI godot_transform_operator_multiply(const godot_transform *p_self, const godot_transform *p_b);
-
-godot_vector3 GDAPI godot_transform_xform_vector3(const godot_transform *p_self, const godot_vector3 *p_v);
-
-godot_vector3 GDAPI godot_transform_xform_inv_vector3(const godot_transform *p_self, const godot_vector3 *p_v);
-
-godot_aabb GDAPI godot_transform_xform_aabb(const godot_transform *p_self, const godot_aabb *p_v);
-
-godot_aabb GDAPI godot_transform_xform_inv_aabb(const godot_transform *p_self, const godot_aabb *p_v);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // GODOT_TRANSFORM_H
diff --git a/modules/gdnative/include/gdnative/transform2d.h b/modules/gdnative/include/gdnative/transform2d.h
index 6b414ca7b2..5acb172081 100644
--- a/modules/gdnative/include/gdnative/transform2d.h
+++ b/modules/gdnative/include/gdnative/transform2d.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,9 +35,9 @@
extern "C" {
#endif
-#include <stdint.h>
+#include <gdnative/math_defs.h>
-#define GODOT_TRANSFORM2D_SIZE 24
+#define GODOT_TRANSFORM2D_SIZE (sizeof(godot_real_t) * 6)
#ifndef GODOT_CORE_API_GODOT_TRANSFORM2D_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_TRANSFORM2D_TYPE_DEFINED
@@ -46,61 +46,12 @@ typedef struct {
} godot_transform2d;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
#include <gdnative/gdnative.h>
-#include <gdnative/variant.h>
-#include <gdnative/vector2.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void GDAPI godot_transform2d_new(godot_transform2d *r_dest, const godot_real p_rot, const godot_vector2 *p_pos);
-void GDAPI godot_transform2d_new_axis_origin(godot_transform2d *r_dest, const godot_vector2 *p_x_axis, const godot_vector2 *p_y_axis, const godot_vector2 *p_origin);
-
-godot_string GDAPI godot_transform2d_as_string(const godot_transform2d *p_self);
-
-godot_transform2d GDAPI godot_transform2d_inverse(const godot_transform2d *p_self);
-
-godot_transform2d GDAPI godot_transform2d_affine_inverse(const godot_transform2d *p_self);
-
-godot_real GDAPI godot_transform2d_get_rotation(const godot_transform2d *p_self);
-
-godot_vector2 GDAPI godot_transform2d_get_origin(const godot_transform2d *p_self);
-
-godot_vector2 GDAPI godot_transform2d_get_scale(const godot_transform2d *p_self);
-
-godot_transform2d GDAPI godot_transform2d_orthonormalized(const godot_transform2d *p_self);
-
-godot_transform2d GDAPI godot_transform2d_rotated(const godot_transform2d *p_self, const godot_real p_phi);
-
-godot_transform2d GDAPI godot_transform2d_scaled(const godot_transform2d *p_self, const godot_vector2 *p_scale);
-
-godot_transform2d GDAPI godot_transform2d_translated(const godot_transform2d *p_self, const godot_vector2 *p_offset);
-
-godot_vector2 GDAPI godot_transform2d_xform_vector2(const godot_transform2d *p_self, const godot_vector2 *p_v);
-
-godot_vector2 GDAPI godot_transform2d_xform_inv_vector2(const godot_transform2d *p_self, const godot_vector2 *p_v);
-
-godot_vector2 GDAPI godot_transform2d_basis_xform_vector2(const godot_transform2d *p_self, const godot_vector2 *p_v);
-
-godot_vector2 GDAPI godot_transform2d_basis_xform_inv_vector2(const godot_transform2d *p_self, const godot_vector2 *p_v);
-
-godot_transform2d GDAPI godot_transform2d_interpolate_with(const godot_transform2d *p_self, const godot_transform2d *p_m, const godot_real p_c);
-
-godot_bool GDAPI godot_transform2d_operator_equal(const godot_transform2d *p_self, const godot_transform2d *p_b);
-
-godot_transform2d GDAPI godot_transform2d_operator_multiply(const godot_transform2d *p_self, const godot_transform2d *p_b);
-
-void GDAPI godot_transform2d_new_identity(godot_transform2d *r_dest);
-
-godot_rect2 GDAPI godot_transform2d_xform_rect2(const godot_transform2d *p_self, const godot_rect2 *p_v);
-godot_rect2 GDAPI godot_transform2d_xform_inv_rect2(const godot_transform2d *p_self, const godot_rect2 *p_v);
+void GDAPI godot_transform2d_new(godot_transform2d *p_self);
+void GDAPI godot_transform2d_new_copy(godot_transform2d *r_dest, const godot_transform2d *p_src);
+godot_vector2 GDAPI *godot_transform2d_operator_index(godot_transform2d *p_self, godot_int p_index);
+const godot_vector2 GDAPI *godot_transform2d_operator_index_const(const godot_transform2d *p_self, godot_int p_index);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/gdnative/transform_3d.h b/modules/gdnative/include/gdnative/transform_3d.h
new file mode 100644
index 0000000000..97ad451e9b
--- /dev/null
+++ b/modules/gdnative/include/gdnative/transform_3d.h
@@ -0,0 +1,60 @@
+/*************************************************************************/
+/* transform_3d.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_TRANSFORM3D_H
+#define GODOT_TRANSFORM3D_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <gdnative/math_defs.h>
+
+#define GODOT_TRANSFORM3D_SIZE (sizeof(godot_real_t) * 12)
+
+#ifndef GODOT_CORE_API_GODOT_TRANSFORM3D_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_TRANSFORM3D_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_TRANSFORM3D_SIZE];
+} godot_transform3d;
+#endif
+
+#include <gdnative/gdnative.h>
+
+void GDAPI godot_transform3d_new(godot_transform3d *p_self);
+void GDAPI godot_transform3d_new_copy(godot_transform3d *r_dest, const godot_transform3d *p_src);
+godot_vector3 GDAPI *godot_transform3d_operator_index(godot_transform3d *p_self, godot_int p_index);
+const godot_vector3 GDAPI *godot_transform3d_operator_index_const(const godot_transform3d *p_self, godot_int p_index);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // GODOT_TRANSFORM3D_H
diff --git a/modules/gdnative/include/gdnative/variant.h b/modules/gdnative/include/gdnative/variant.h
index 0a611b76e9..a88bd2878a 100644
--- a/modules/gdnative/include/gdnative/variant.h
+++ b/modules/gdnative/include/gdnative/variant.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,16 +35,8 @@
extern "C" {
#endif
-#include <stdint.h>
-
-#define GODOT_VARIANT_SIZE (16 + sizeof(int64_t))
-
-#ifndef GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED
-#define GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED
-typedef struct {
- uint8_t _dont_touch_that[GODOT_VARIANT_SIZE];
-} godot_variant;
-#endif
+#include <gdnative/math_defs.h>
+#include <gdnative/variant_struct.h>
typedef enum godot_variant_type {
GODOT_VARIANT_TYPE_NIL,
@@ -52,7 +44,7 @@ typedef enum godot_variant_type {
// atomic types
GODOT_VARIANT_TYPE_BOOL,
GODOT_VARIANT_TYPE_INT,
- GODOT_VARIANT_TYPE_REAL,
+ GODOT_VARIANT_TYPE_FLOAT,
GODOT_VARIANT_TYPE_STRING,
// math types
@@ -64,10 +56,10 @@ typedef enum godot_variant_type {
GODOT_VARIANT_TYPE_VECTOR3I,
GODOT_VARIANT_TYPE_TRANSFORM2D,
GODOT_VARIANT_TYPE_PLANE,
- GODOT_VARIANT_TYPE_QUAT,
+ GODOT_VARIANT_TYPE_QUATERNION,
GODOT_VARIANT_TYPE_AABB,
GODOT_VARIANT_TYPE_BASIS,
- GODOT_VARIANT_TYPE_TRANSFORM,
+ GODOT_VARIANT_TYPE_TRANSFORM3D,
// misc types
GODOT_VARIANT_TYPE_COLOR,
@@ -146,10 +138,35 @@ typedef enum godot_variant_operator {
GODOT_VARIANT_OP_MAX,
} godot_variant_operator;
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
+typedef enum godot_variant_utility_function_type {
+ GODOT_UTILITY_FUNC_TYPE_MATH,
+ GODOT_UTILITY_FUNC_TYPE_RANDOM,
+ GODOT_UTILITY_FUNC_TYPE_GENERAL,
+} godot_variant_utility_function_type;
+
+// Types for function pointers.
+typedef void (*godot_validated_operator_evaluator)(const godot_variant *p_left, const godot_variant *p_right, godot_variant *r_result);
+typedef void (*godot_ptr_operator_evaluator)(const void *p_left, const void *p_right, void *r_result);
+typedef void (*godot_validated_builtin_method)(godot_variant *p_base, const godot_variant **p_args, int p_argument_count, godot_variant *r_return);
+typedef void (*godot_ptr_builtin_method)(void *p_base, const void **p_args, void *r_return, int p_argument_count);
+typedef void (*godot_validated_constructor)(godot_variant *p_base, const godot_variant **p_args);
+typedef void (*godot_ptr_constructor)(void *p_base, const void **p_args);
+typedef void (*godot_validated_setter)(godot_variant *p_base, const godot_variant *p_value);
+typedef void (*godot_validated_getter)(const godot_variant *p_base, godot_variant *r_value);
+typedef void (*godot_ptr_setter)(void *p_base, const void *p_value);
+typedef void (*godot_ptr_getter)(const void *p_base, void *r_value);
+typedef void (*godot_validated_indexed_setter)(godot_variant *p_base, godot_int p_index, const godot_variant *p_value, bool *r_oob);
+typedef void (*godot_validated_indexed_getter)(const godot_variant *p_base, godot_int p_index, godot_variant *r_value, bool *r_oob);
+typedef void (*godot_ptr_indexed_setter)(void *p_base, godot_int p_index, const void *p_value);
+typedef void (*godot_ptr_indexed_getter)(const void *p_base, godot_int p_index, void *r_value);
+typedef void (*godot_validated_keyed_setter)(godot_variant *p_base, const godot_variant *p_key, const godot_variant *p_value, bool *r_valid);
+typedef void (*godot_validated_keyed_getter)(const godot_variant *p_base, const godot_variant *p_key, godot_variant *r_value, bool *r_valid);
+typedef bool (*godot_validated_keyed_checker)(const godot_variant *p_base, const godot_variant *p_key, bool *r_valid);
+typedef void (*godot_ptr_keyed_setter)(void *p_base, const void *p_key, const void *p_value);
+typedef void (*godot_ptr_keyed_getter)(const void *p_base, const void *p_key, void *r_value);
+typedef uint32_t (*godot_ptr_keyed_checker)(const godot_variant *p_base, const godot_variant *p_key);
+typedef void (*godot_validated_utility_function)(godot_variant *r_return, const godot_variant **p_arguments, int p_argument_count);
+typedef void (*godot_ptr_utility_function)(void *r_return, const void **p_arguments, int p_argument_count);
#include <gdnative/aabb.h>
#include <gdnative/array.h>
@@ -160,35 +177,29 @@ typedef enum godot_variant_operator {
#include <gdnative/node_path.h>
#include <gdnative/packed_arrays.h>
#include <gdnative/plane.h>
-#include <gdnative/quat.h>
+#include <gdnative/quaternion.h>
#include <gdnative/rect2.h>
#include <gdnative/rid.h>
+#include <gdnative/signal.h>
#include <gdnative/string.h>
#include <gdnative/string_name.h>
-#include <gdnative/transform.h>
#include <gdnative/transform2d.h>
+#include <gdnative/transform_3d.h>
#include <gdnative/variant.h>
#include <gdnative/vector2.h>
#include <gdnative/vector3.h>
#include <gdnative/gdnative.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-godot_variant_type GDAPI godot_variant_get_type(const godot_variant *p_v);
+// Memory.
void GDAPI godot_variant_new_copy(godot_variant *r_dest, const godot_variant *p_src);
void GDAPI godot_variant_new_nil(godot_variant *r_dest);
-
void GDAPI godot_variant_new_bool(godot_variant *r_dest, const godot_bool p_b);
-void GDAPI godot_variant_new_uint(godot_variant *r_dest, const uint64_t p_i);
-void GDAPI godot_variant_new_int(godot_variant *r_dest, const int64_t p_i);
-void GDAPI godot_variant_new_real(godot_variant *r_dest, const double p_r);
+void GDAPI godot_variant_new_int(godot_variant *r_dest, const godot_int p_i);
+void GDAPI godot_variant_new_float(godot_variant *r_dest, const godot_float p_f);
void GDAPI godot_variant_new_string(godot_variant *r_dest, const godot_string *p_s);
-void GDAPI godot_variant_new_string_name(godot_variant *r_dest, const godot_string_name *p_s);
void GDAPI godot_variant_new_vector2(godot_variant *r_dest, const godot_vector2 *p_v2);
void GDAPI godot_variant_new_vector2i(godot_variant *r_dest, const godot_vector2i *p_v2);
void GDAPI godot_variant_new_rect2(godot_variant *r_dest, const godot_rect2 *p_rect2);
@@ -197,16 +208,17 @@ void GDAPI godot_variant_new_vector3(godot_variant *r_dest, const godot_vector3
void GDAPI godot_variant_new_vector3i(godot_variant *r_dest, const godot_vector3i *p_v3);
void GDAPI godot_variant_new_transform2d(godot_variant *r_dest, const godot_transform2d *p_t2d);
void GDAPI godot_variant_new_plane(godot_variant *r_dest, const godot_plane *p_plane);
-void GDAPI godot_variant_new_quat(godot_variant *r_dest, const godot_quat *p_quat);
+void GDAPI godot_variant_new_quaternion(godot_variant *r_dest, const godot_quaternion *p_quaternion);
void GDAPI godot_variant_new_aabb(godot_variant *r_dest, const godot_aabb *p_aabb);
void GDAPI godot_variant_new_basis(godot_variant *r_dest, const godot_basis *p_basis);
-void GDAPI godot_variant_new_transform(godot_variant *r_dest, const godot_transform *p_trans);
+void GDAPI godot_variant_new_transform3d(godot_variant *r_dest, const godot_transform3d *p_trans);
void GDAPI godot_variant_new_color(godot_variant *r_dest, const godot_color *p_color);
+void GDAPI godot_variant_new_string_name(godot_variant *r_dest, const godot_string_name *p_s);
void GDAPI godot_variant_new_node_path(godot_variant *r_dest, const godot_node_path *p_np);
void GDAPI godot_variant_new_rid(godot_variant *r_dest, const godot_rid *p_rid);
+void GDAPI godot_variant_new_object(godot_variant *r_dest, const godot_object *p_obj);
void GDAPI godot_variant_new_callable(godot_variant *r_dest, const godot_callable *p_callable);
void GDAPI godot_variant_new_signal(godot_variant *r_dest, const godot_signal *p_signal);
-void GDAPI godot_variant_new_object(godot_variant *r_dest, const godot_object *p_obj);
void GDAPI godot_variant_new_dictionary(godot_variant *r_dest, const godot_dictionary *p_dict);
void GDAPI godot_variant_new_array(godot_variant *r_dest, const godot_array *p_arr);
void GDAPI godot_variant_new_packed_byte_array(godot_variant *r_dest, const godot_packed_byte_array *p_pba);
@@ -220,11 +232,9 @@ void GDAPI godot_variant_new_packed_vector3_array(godot_variant *r_dest, const g
void GDAPI godot_variant_new_packed_color_array(godot_variant *r_dest, const godot_packed_color_array *p_pca);
godot_bool GDAPI godot_variant_as_bool(const godot_variant *p_self);
-uint64_t GDAPI godot_variant_as_uint(const godot_variant *p_self);
-int64_t GDAPI godot_variant_as_int(const godot_variant *p_self);
-double GDAPI godot_variant_as_real(const godot_variant *p_self);
+godot_int GDAPI godot_variant_as_int(const godot_variant *p_self);
+godot_float GDAPI godot_variant_as_float(const godot_variant *p_self);
godot_string GDAPI godot_variant_as_string(const godot_variant *p_self);
-godot_string_name GDAPI godot_variant_as_string_name(const godot_variant *p_self);
godot_vector2 GDAPI godot_variant_as_vector2(const godot_variant *p_self);
godot_vector2i GDAPI godot_variant_as_vector2i(const godot_variant *p_self);
godot_rect2 GDAPI godot_variant_as_rect2(const godot_variant *p_self);
@@ -233,16 +243,17 @@ godot_vector3 GDAPI godot_variant_as_vector3(const godot_variant *p_self);
godot_vector3i GDAPI godot_variant_as_vector3i(const godot_variant *p_self);
godot_transform2d GDAPI godot_variant_as_transform2d(const godot_variant *p_self);
godot_plane GDAPI godot_variant_as_plane(const godot_variant *p_self);
-godot_quat GDAPI godot_variant_as_quat(const godot_variant *p_self);
+godot_quaternion GDAPI godot_variant_as_quaternion(const godot_variant *p_self);
godot_aabb GDAPI godot_variant_as_aabb(const godot_variant *p_self);
godot_basis GDAPI godot_variant_as_basis(const godot_variant *p_self);
-godot_transform GDAPI godot_variant_as_transform(const godot_variant *p_self);
+godot_transform3d GDAPI godot_variant_as_transform3d(const godot_variant *p_self);
godot_color GDAPI godot_variant_as_color(const godot_variant *p_self);
+godot_string_name GDAPI godot_variant_as_string_name(const godot_variant *p_self);
godot_node_path GDAPI godot_variant_as_node_path(const godot_variant *p_self);
godot_rid GDAPI godot_variant_as_rid(const godot_variant *p_self);
+godot_object GDAPI *godot_variant_as_object(const godot_variant *p_self);
godot_callable GDAPI godot_variant_as_callable(const godot_variant *p_self);
godot_signal GDAPI godot_variant_as_signal(const godot_variant *p_self);
-godot_object GDAPI *godot_variant_as_object(const godot_variant *p_self);
godot_dictionary GDAPI godot_variant_as_dictionary(const godot_variant *p_self);
godot_array GDAPI godot_variant_as_array(const godot_variant *p_self);
godot_packed_byte_array GDAPI godot_variant_as_packed_byte_array(const godot_variant *p_self);
@@ -255,24 +266,157 @@ godot_packed_vector2_array GDAPI godot_variant_as_packed_vector2_array(const god
godot_packed_vector3_array GDAPI godot_variant_as_packed_vector3_array(const godot_variant *p_self);
godot_packed_color_array GDAPI godot_variant_as_packed_color_array(const godot_variant *p_self);
-godot_variant GDAPI godot_variant_call(godot_variant *p_self, const godot_string *p_method, const godot_variant **p_args, const godot_int p_argcount, godot_variant_call_error *r_error);
-
-godot_bool GDAPI godot_variant_has_method(const godot_variant *p_self, const godot_string *p_method);
-
-godot_bool GDAPI godot_variant_operator_equal(const godot_variant *p_self, const godot_variant *p_other);
-godot_bool GDAPI godot_variant_operator_less(const godot_variant *p_self, const godot_variant *p_other);
+void GDAPI godot_variant_destroy(godot_variant *p_self);
-uint32_t GDAPI godot_variant_hash(const godot_variant *p_self);
+// Dynamic interaction.
+
+void GDAPI godot_variant_call(godot_variant *p_self, const godot_string_name *p_method, const godot_variant **p_args, const godot_int p_argument_count, godot_variant *r_return, godot_variant_call_error *r_error);
+void GDAPI godot_variant_call_with_cstring(godot_variant *p_self, const char *p_method, const godot_variant **p_args, const godot_int p_argument_count, godot_variant *r_return, godot_variant_call_error *r_error);
+void GDAPI godot_variant_call_static(godot_variant_type p_type, const godot_string_name *p_method, const godot_variant **p_args, const godot_int p_argument_count, godot_variant *r_return, godot_variant_call_error *r_error);
+void GDAPI godot_variant_call_static_with_cstring(godot_variant_type p_type, const char *p_method, const godot_variant **p_args, const godot_int p_argument_count, godot_variant *r_return, godot_variant_call_error *r_error);
+void GDAPI godot_variant_evaluate(godot_variant_operator p_op, const godot_variant *p_a, const godot_variant *p_b, godot_variant *r_return, bool *r_valid);
+void GDAPI godot_variant_set(godot_variant *p_self, const godot_variant *p_key, const godot_variant *p_value, bool *r_valid);
+void GDAPI godot_variant_set_named(godot_variant *p_self, const godot_string_name *p_name, const godot_variant *p_value, bool *r_valid);
+void GDAPI godot_variant_set_named_with_cstring(godot_variant *p_self, const char *p_name, const godot_variant *p_value, bool *r_valid);
+void GDAPI godot_variant_set_keyed(godot_variant *p_self, const godot_variant *p_key, const godot_variant *p_value, bool *r_valid);
+void GDAPI godot_variant_set_indexed(godot_variant *p_self, godot_int p_index, const godot_variant *p_value, bool *r_valid, bool *r_oob);
+godot_variant GDAPI godot_variant_get(const godot_variant *p_self, const godot_variant *p_key, bool *r_valid);
+godot_variant GDAPI godot_variant_get_named(const godot_variant *p_self, const godot_string_name *p_key, bool *r_valid);
+godot_variant GDAPI godot_variant_get_named_with_cstring(const godot_variant *p_self, const char *p_key, bool *r_valid);
+godot_variant GDAPI godot_variant_get_keyed(const godot_variant *p_self, const godot_variant *p_key, bool *r_valid);
+godot_variant GDAPI godot_variant_get_indexed(const godot_variant *p_self, godot_int p_index, bool *r_valid, bool *r_oob);
+/// Iteration.
+bool GDAPI godot_variant_iter_init(const godot_variant *p_self, godot_variant *r_iter, bool *r_valid);
+bool GDAPI godot_variant_iter_next(const godot_variant *p_self, godot_variant *r_iter, bool *r_valid);
+godot_variant GDAPI godot_variant_iter_get(const godot_variant *p_self, godot_variant *r_iter, bool *r_valid);
+
+/// Variant functions.
godot_bool GDAPI godot_variant_hash_compare(const godot_variant *p_self, const godot_variant *p_other);
-
godot_bool GDAPI godot_variant_booleanize(const godot_variant *p_self);
-
-void GDAPI godot_variant_destroy(godot_variant *p_self);
-
-// GDNative core 1.1
-
-godot_string GDAPI godot_variant_get_operator_name(godot_variant_operator p_op);
-void GDAPI godot_variant_evaluate(godot_variant_operator p_op, const godot_variant *p_a, const godot_variant *p_b, godot_variant *r_ret, godot_bool *r_valid);
+void GDAPI godot_variant_blend(const godot_variant *p_a, const godot_variant *p_b, float p_c, godot_variant *r_dst);
+void GDAPI godot_variant_interpolate(const godot_variant *p_a, const godot_variant *p_b, float p_c, godot_variant *r_dst);
+godot_variant GDAPI godot_variant_duplicate(const godot_variant *p_self, godot_bool p_deep);
+godot_string GDAPI godot_variant_stringify(const godot_variant *p_self);
+
+// Discovery API.
+
+/// Operators.
+godot_validated_operator_evaluator GDAPI godot_variant_get_validated_operator_evaluator(godot_variant_operator p_operator, godot_variant_type p_type_a, godot_variant_type p_type_b);
+godot_ptr_operator_evaluator GDAPI godot_variant_get_ptr_operator_evaluator(godot_variant_operator p_operator, godot_variant_type p_type_a, godot_variant_type p_type_b);
+godot_variant_type GDAPI godot_variant_get_operator_return_type(godot_variant_operator p_operator, godot_variant_type p_type_a, godot_variant_type p_type_b);
+godot_string GDAPI godot_variant_get_operator_name(godot_variant_operator p_operator);
+
+/// Built-in methods.
+bool GDAPI godot_variant_has_builtin_method(godot_variant_type p_type, const godot_string_name *p_method);
+bool GDAPI godot_variant_has_builtin_method_with_cstring(godot_variant_type p_type, const char *p_method);
+godot_validated_builtin_method GDAPI godot_variant_get_validated_builtin_method(godot_variant_type p_type, const godot_string_name *p_method);
+godot_validated_builtin_method GDAPI godot_variant_get_validated_builtin_method_with_cstring(godot_variant_type p_type, const char *p_method);
+godot_ptr_builtin_method GDAPI godot_variant_get_ptr_builtin_method(godot_variant_type p_type, const godot_string_name *p_method);
+godot_ptr_builtin_method GDAPI godot_variant_get_ptr_builtin_method_with_cstring(godot_variant_type p_type, const char *p_method);
+int GDAPI godot_variant_get_builtin_method_argument_count(godot_variant_type p_type, const godot_string_name *p_method);
+int GDAPI godot_variant_get_builtin_method_argument_count_with_cstring(godot_variant_type p_type, const char *p_method);
+godot_variant_type GDAPI godot_variant_get_builtin_method_argument_type(godot_variant_type p_type, const godot_string_name *p_method, int p_argument);
+godot_variant_type GDAPI godot_variant_get_builtin_method_argument_type_with_cstring(godot_variant_type p_type, const char *p_method, int p_argument);
+godot_string GDAPI godot_variant_get_builtin_method_argument_name(godot_variant_type p_type, const godot_string_name *p_method, int p_argument);
+godot_string GDAPI godot_variant_get_builtin_method_argument_name_with_cstring(godot_variant_type p_type, const char *p_method, int p_argument);
+bool GDAPI godot_variant_has_builtin_method_return_value(godot_variant_type p_type, const godot_string_name *p_method);
+bool GDAPI godot_variant_has_builtin_method_return_value_with_cstring(godot_variant_type p_type, const char *p_method);
+godot_variant_type GDAPI godot_variant_get_builtin_method_return_type(godot_variant_type p_type, const godot_string_name *p_method);
+godot_variant_type GDAPI godot_variant_get_builtin_method_return_type_with_cstring(godot_variant_type p_type, const char *p_method);
+bool GDAPI godot_variant_is_builtin_method_const(godot_variant_type p_type, const godot_string_name *p_method);
+bool GDAPI godot_variant_is_builtin_method_const_with_cstring(godot_variant_type p_type, const char *p_method);
+bool GDAPI godot_variant_is_builtin_method_static(godot_variant_type p_type, const godot_string_name *p_method);
+bool GDAPI godot_variant_is_builtin_method_static_with_cstring(godot_variant_type p_type, const char *p_method);
+bool GDAPI godot_variant_is_builtin_method_vararg(godot_variant_type p_type, const godot_string_name *p_method);
+bool GDAPI godot_variant_is_builtin_method_vararg_with_cstring(godot_variant_type p_type, const char *p_method);
+int GDAPI godot_variant_get_builtin_method_count(godot_variant_type p_type);
+void GDAPI godot_variant_get_builtin_method_list(godot_variant_type p_type, godot_string_name *r_list);
+
+/// Constructors.
+int GDAPI godot_variant_get_constructor_count(godot_variant_type p_type);
+godot_validated_constructor GDAPI godot_variant_get_validated_constructor(godot_variant_type p_type, int p_constructor);
+godot_ptr_constructor GDAPI godot_variant_get_ptr_constructor(godot_variant_type p_type, int p_constructor);
+int GDAPI godot_variant_get_constructor_argument_count(godot_variant_type p_type, int p_constructor);
+godot_variant_type GDAPI godot_variant_get_constructor_argument_type(godot_variant_type p_type, int p_constructor, int p_argument);
+godot_string GDAPI godot_variant_get_constructor_argument_name(godot_variant_type p_type, int p_constructor, int p_argument);
+void GDAPI godot_variant_construct(godot_variant_type p_type, godot_variant *p_base, const godot_variant **p_args, int p_argument_count, godot_variant_call_error *r_error);
+
+/// Properties.
+godot_variant_type GDAPI godot_variant_get_member_type(godot_variant_type p_type, const godot_string_name *p_member);
+godot_variant_type GDAPI godot_variant_get_member_type_with_cstring(godot_variant_type p_type, const char *p_member);
+int GDAPI godot_variant_get_member_count(godot_variant_type p_type);
+void GDAPI godot_variant_get_member_list(godot_variant_type p_type, godot_string_name *r_list);
+godot_validated_setter GDAPI godot_variant_get_validated_setter(godot_variant_type p_type, const godot_string_name *p_member);
+godot_validated_setter GDAPI godot_variant_get_validated_setter_with_cstring(godot_variant_type p_type, const char *p_member);
+godot_validated_getter GDAPI godot_variant_get_validated_getter(godot_variant_type p_type, const godot_string_name *p_member);
+godot_validated_getter GDAPI godot_variant_get_validated_getter_with_cstring(godot_variant_type p_type, const char *p_member);
+godot_ptr_setter GDAPI godot_variant_get_ptr_setter(godot_variant_type p_type, const godot_string_name *p_member);
+godot_ptr_setter GDAPI godot_variant_get_ptr_setter_with_cstring(godot_variant_type p_type, const char *p_member);
+godot_ptr_getter GDAPI godot_variant_get_ptr_getter(godot_variant_type p_type, const godot_string_name *p_member);
+godot_ptr_getter GDAPI godot_variant_get_ptr_getter_with_cstring(godot_variant_type p_type, const char *p_member);
+
+/// Indexing.
+bool GDAPI godot_variant_has_indexing(godot_variant_type p_type);
+godot_variant_type GDAPI godot_variant_get_indexed_element_type(godot_variant_type p_type);
+godot_validated_indexed_setter GDAPI godot_variant_get_validated_indexed_setter(godot_variant_type p_type);
+godot_validated_indexed_getter GDAPI godot_variant_get_validated_indexed_getter(godot_variant_type p_type);
+godot_ptr_indexed_setter GDAPI godot_variant_get_ptr_indexed_setter(godot_variant_type p_type);
+godot_ptr_indexed_getter GDAPI godot_variant_get_ptr_indexed_getter(godot_variant_type p_type);
+uint64_t GDAPI godot_variant_get_indexed_size(const godot_variant *p_self);
+
+/// Keying.
+bool GDAPI godot_variant_is_keyed(godot_variant_type p_type);
+godot_validated_keyed_setter GDAPI godot_variant_get_validated_keyed_setter(godot_variant_type p_type);
+godot_validated_keyed_getter GDAPI godot_variant_get_validated_keyed_getter(godot_variant_type p_type);
+godot_validated_keyed_checker GDAPI godot_variant_get_validated_keyed_checker(godot_variant_type p_type);
+godot_ptr_keyed_setter GDAPI godot_variant_get_ptr_keyed_setter(godot_variant_type p_type);
+godot_ptr_keyed_getter GDAPI godot_variant_get_ptr_keyed_getter(godot_variant_type p_type);
+godot_ptr_keyed_checker GDAPI godot_variant_get_ptr_keyed_checker(godot_variant_type p_type);
+
+/// Constants.
+int GDAPI godot_variant_get_constants_count(godot_variant_type p_type);
+void GDAPI godot_variant_get_constants_list(godot_variant_type p_type, godot_string_name *r_list);
+bool GDAPI godot_variant_has_constant(godot_variant_type p_type, const godot_string_name *p_constant);
+bool GDAPI godot_variant_has_constant_with_cstring(godot_variant_type p_type, const char *p_constant);
+godot_variant GDAPI godot_variant_get_constant_value(godot_variant_type p_type, const godot_string_name *p_constant);
+godot_variant GDAPI godot_variant_get_constant_value_with_cstring(godot_variant_type p_type, const char *p_constant);
+
+/// Utilities.
+bool GDAPI godot_variant_has_utility_function(const godot_string_name *p_function);
+bool GDAPI godot_variant_has_utility_function_with_cstring(const char *p_function);
+void GDAPI godot_variant_call_utility_function(const godot_string_name *p_function, godot_variant *r_ret, const godot_variant **p_args, int p_argument_count, godot_variant_call_error *r_error);
+void GDAPI godot_variant_call_utility_function_with_cstring(const char *p_function, godot_variant *r_ret, const godot_variant **p_args, int p_argument_count, godot_variant_call_error *r_error);
+godot_ptr_utility_function GDAPI godot_variant_get_ptr_utility_function(const godot_string_name *p_function);
+godot_ptr_utility_function GDAPI godot_variant_get_ptr_utility_function_with_cstring(const char *p_function);
+godot_validated_utility_function GDAPI godot_variant_get_validated_utility_function(const godot_string_name *p_function);
+godot_validated_utility_function GDAPI godot_variant_get_validated_utility_function_with_cstring(const char *p_function);
+godot_variant_utility_function_type GDAPI godot_variant_get_utility_function_type(const godot_string_name *p_function);
+godot_variant_utility_function_type GDAPI godot_variant_get_utility_function_type_with_cstring(const char *p_function);
+int GDAPI godot_variant_get_utility_function_argument_count(const godot_string_name *p_function);
+int GDAPI godot_variant_get_utility_function_argument_count_with_cstring(const char *p_function);
+godot_variant_type GDAPI godot_variant_get_utility_function_argument_type(const godot_string_name *p_function, int p_argument);
+godot_variant_type GDAPI godot_variant_get_utility_function_argument_type_with_cstring(const char *p_function, int p_argument);
+godot_string GDAPI godot_variant_get_utility_function_argument_name(const godot_string_name *p_function, int p_argument);
+godot_string GDAPI godot_variant_get_utility_function_argument_name_with_cstring(const char *p_function, int p_argument);
+bool GDAPI godot_variant_has_utility_function_return_value(const godot_string_name *p_function);
+bool GDAPI godot_variant_has_utility_function_return_value_with_cstring(const char *p_function);
+godot_variant_type GDAPI godot_variant_get_utility_function_return_type(const godot_string_name *p_function);
+godot_variant_type GDAPI godot_variant_get_utility_function_return_type_with_cstring(const char *p_function);
+bool GDAPI godot_variant_is_utility_function_vararg(const godot_string_name *p_function);
+bool GDAPI godot_variant_is_utility_function_vararg_with_cstring(const char *p_function);
+int GDAPI godot_variant_get_utility_function_count();
+void GDAPI godot_variant_get_utility_function_list(godot_string_name *r_functions);
+
+// Introspection.
+
+godot_variant_type GDAPI godot_variant_get_type(const godot_variant *p_self);
+bool GDAPI godot_variant_has_method(const godot_variant *p_self, const godot_string_name *p_method);
+bool GDAPI godot_variant_has_member(godot_variant_type p_type, const godot_string_name *p_member);
+bool GDAPI godot_variant_has_key(const godot_variant *p_self, const godot_variant *p_key, bool *r_valid);
+
+godot_string GDAPI godot_variant_get_type_name(godot_variant_type p_type);
+bool GDAPI godot_variant_can_convert(godot_variant_type p_from, godot_variant_type p_to);
+bool GDAPI godot_variant_can_convert_strict(godot_variant_type p_from, godot_variant_type p_to);
#ifdef __cplusplus
}
diff --git a/modules/etc/register_types.cpp b/modules/gdnative/include/gdnative/variant_struct.h
index 0972857808..321c76c206 100644
--- a/modules/etc/register_types.cpp
+++ b/modules/gdnative/include/gdnative/variant_struct.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* register_types.cpp */
+/* variant_struct.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,21 +28,26 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "register_types.h"
+#ifndef GODOT_VARIANT_STRUCT_H
+#define GODOT_VARIANT_STRUCT_H
-#include "image_etc.h"
-#include "texture_loader_pkm.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
-static Ref<ResourceFormatPKM> resource_loader_pkm;
+#include <gdnative/math_defs.h>
-void register_etc_types() {
- resource_loader_pkm.instance();
- ResourceLoader::add_resource_format_loader(resource_loader_pkm);
+#define GODOT_VARIANT_SIZE (sizeof(godot_real_t) * 4 + sizeof(int64_t))
- _register_etc_compress_func();
-}
+#ifndef GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED
+#define GODOT_CORE_API_GODOT_VARIANT_TYPE_DEFINED
+typedef struct {
+ uint8_t _dont_touch_that[GODOT_VARIANT_SIZE];
+} godot_variant;
+#endif
-void unregister_etc_types() {
- ResourceLoader::remove_resource_format_loader(resource_loader_pkm);
- resource_loader_pkm.unref();
+#ifdef __cplusplus
}
+#endif
+
+#endif
diff --git a/modules/gdnative/include/gdnative/vector2.h b/modules/gdnative/include/gdnative/vector2.h
index 35b02c5a75..00faffbad7 100644
--- a/modules/gdnative/include/gdnative/vector2.h
+++ b/modules/gdnative/include/gdnative/vector2.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,9 +35,9 @@
extern "C" {
#endif
-#include <stdint.h>
+#include <gdnative/math_defs.h>
-#define GODOT_VECTOR2_SIZE 8
+#define GODOT_VECTOR2_SIZE (sizeof(godot_real_t) * 2)
#ifndef GODOT_CORE_API_GODOT_VECTOR2_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_VECTOR2_TYPE_DEFINED
@@ -46,7 +46,7 @@ typedef struct {
} godot_vector2;
#endif
-#define GODOT_VECTOR2I_SIZE 8
+#define GODOT_VECTOR2I_SIZE (sizeof(int32_t) * 2)
#ifndef GODOT_CORE_API_GODOT_VECTOR2I_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_VECTOR2I_TYPE_DEFINED
@@ -55,140 +55,16 @@ typedef struct {
} godot_vector2i;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
#include <gdnative/gdnative.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Vector2
-
-void GDAPI godot_vector2_new(godot_vector2 *r_dest, const godot_real p_x, const godot_real p_y);
-
-godot_string GDAPI godot_vector2_as_string(const godot_vector2 *p_self);
-
-godot_vector2i GDAPI godot_vector2_as_vector2i(const godot_vector2 *p_self);
-
-godot_vector2 GDAPI godot_vector2_normalized(const godot_vector2 *p_self);
-
-godot_real GDAPI godot_vector2_length(const godot_vector2 *p_self);
-
-godot_real GDAPI godot_vector2_angle(const godot_vector2 *p_self);
-
-godot_real GDAPI godot_vector2_length_squared(const godot_vector2 *p_self);
-
-godot_bool GDAPI godot_vector2_is_normalized(const godot_vector2 *p_self);
-
-godot_vector2 GDAPI godot_vector2_direction_to(const godot_vector2 *p_self, const godot_vector2 *p_b);
-
-godot_real GDAPI godot_vector2_distance_to(const godot_vector2 *p_self, const godot_vector2 *p_to);
-
-godot_real GDAPI godot_vector2_distance_squared_to(const godot_vector2 *p_self, const godot_vector2 *p_to);
-
-godot_real GDAPI godot_vector2_angle_to(const godot_vector2 *p_self, const godot_vector2 *p_to);
-
-godot_real GDAPI godot_vector2_angle_to_point(const godot_vector2 *p_self, const godot_vector2 *p_to);
-
-godot_vector2 GDAPI godot_vector2_lerp(const godot_vector2 *p_self, const godot_vector2 *p_b, const godot_real p_t);
-
-godot_vector2 GDAPI godot_vector2_cubic_interpolate(const godot_vector2 *p_self, const godot_vector2 *p_b, const godot_vector2 *p_pre_a, const godot_vector2 *p_post_b, const godot_real p_t);
-
-godot_vector2 GDAPI godot_vector2_move_toward(const godot_vector2 *p_self, const godot_vector2 *p_to, const godot_real p_delta);
-
-godot_vector2 GDAPI godot_vector2_rotated(const godot_vector2 *p_self, const godot_real p_phi);
-
-godot_vector2 GDAPI godot_vector2_tangent(const godot_vector2 *p_self);
-
-godot_vector2 GDAPI godot_vector2_floor(const godot_vector2 *p_self);
-
-godot_vector2 GDAPI godot_vector2_sign(const godot_vector2 *p_self);
-
-godot_vector2 GDAPI godot_vector2_snapped(const godot_vector2 *p_self, const godot_vector2 *p_by);
-
-godot_real GDAPI godot_vector2_aspect(const godot_vector2 *p_self);
-
-godot_real GDAPI godot_vector2_dot(const godot_vector2 *p_self, const godot_vector2 *p_with);
-
-godot_vector2 GDAPI godot_vector2_slide(const godot_vector2 *p_self, const godot_vector2 *p_n);
-
-godot_vector2 GDAPI godot_vector2_bounce(const godot_vector2 *p_self, const godot_vector2 *p_n);
-
-godot_vector2 GDAPI godot_vector2_reflect(const godot_vector2 *p_self, const godot_vector2 *p_n);
-
-godot_vector2 GDAPI godot_vector2_abs(const godot_vector2 *p_self);
-
-godot_vector2 GDAPI godot_vector2_clamped(const godot_vector2 *p_self, const godot_real p_length);
-
-godot_vector2 GDAPI godot_vector2_operator_add(const godot_vector2 *p_self, const godot_vector2 *p_b);
-
-godot_vector2 GDAPI godot_vector2_operator_subtract(const godot_vector2 *p_self, const godot_vector2 *p_b);
-
-godot_vector2 GDAPI godot_vector2_operator_multiply_vector(const godot_vector2 *p_self, const godot_vector2 *p_b);
-
-godot_vector2 GDAPI godot_vector2_operator_multiply_scalar(const godot_vector2 *p_self, const godot_real p_b);
-
-godot_vector2 GDAPI godot_vector2_operator_divide_vector(const godot_vector2 *p_self, const godot_vector2 *p_b);
-
-godot_vector2 GDAPI godot_vector2_operator_divide_scalar(const godot_vector2 *p_self, const godot_real p_b);
-
-godot_bool GDAPI godot_vector2_operator_equal(const godot_vector2 *p_self, const godot_vector2 *p_b);
-
-godot_bool GDAPI godot_vector2_operator_less(const godot_vector2 *p_self, const godot_vector2 *p_b);
-
-godot_vector2 GDAPI godot_vector2_operator_neg(const godot_vector2 *p_self);
-
-void GDAPI godot_vector2_set_x(godot_vector2 *p_self, const godot_real p_x);
-
-void GDAPI godot_vector2_set_y(godot_vector2 *p_self, const godot_real p_y);
-
-godot_real GDAPI godot_vector2_get_x(const godot_vector2 *p_self);
-
-godot_real GDAPI godot_vector2_get_y(const godot_vector2 *p_self);
-
-// Vector2i
-
-void GDAPI godot_vector2i_new(godot_vector2i *r_dest, const godot_int p_x, const godot_int p_y);
-
-godot_string GDAPI godot_vector2i_as_string(const godot_vector2i *p_self);
-
-godot_vector2 GDAPI godot_vector2i_as_vector2(const godot_vector2i *p_self);
-
-godot_real GDAPI godot_vector2i_aspect(const godot_vector2i *p_self);
-
-godot_vector2i GDAPI godot_vector2i_abs(const godot_vector2i *p_self);
-
-godot_vector2i GDAPI godot_vector2i_sign(const godot_vector2i *p_self);
-
-godot_vector2i GDAPI godot_vector2i_operator_add(const godot_vector2i *p_self, const godot_vector2i *p_b);
-
-godot_vector2i GDAPI godot_vector2i_operator_subtract(const godot_vector2i *p_self, const godot_vector2i *p_b);
-
-godot_vector2i GDAPI godot_vector2i_operator_multiply_vector(const godot_vector2i *p_self, const godot_vector2i *p_b);
-
-godot_vector2i GDAPI godot_vector2i_operator_multiply_scalar(const godot_vector2i *p_self, const godot_int p_b);
-
-godot_vector2i GDAPI godot_vector2i_operator_divide_vector(const godot_vector2i *p_self, const godot_vector2i *p_b);
-
-godot_vector2i GDAPI godot_vector2i_operator_divide_scalar(const godot_vector2i *p_self, const godot_int p_b);
-
-godot_bool GDAPI godot_vector2i_operator_equal(const godot_vector2i *p_self, const godot_vector2i *p_b);
-
-godot_bool GDAPI godot_vector2i_operator_less(const godot_vector2i *p_self, const godot_vector2i *p_b);
-
-godot_vector2i GDAPI godot_vector2i_operator_neg(const godot_vector2i *p_self);
-
-void GDAPI godot_vector2i_set_x(godot_vector2i *p_self, const godot_int p_x);
-
-void GDAPI godot_vector2i_set_y(godot_vector2i *p_self, const godot_int p_y);
-
-godot_int GDAPI godot_vector2i_get_x(const godot_vector2i *p_self);
-
-godot_int GDAPI godot_vector2i_get_y(const godot_vector2i *p_self);
+void GDAPI godot_vector2_new(godot_vector2 *p_self);
+void GDAPI godot_vector2_new_copy(godot_vector2 *r_dest, const godot_vector2 *p_src);
+void GDAPI godot_vector2i_new(godot_vector2i *p_self);
+void GDAPI godot_vector2i_new_copy(godot_vector2i *r_dest, const godot_vector2i *p_src);
+godot_real_t GDAPI *godot_vector2_operator_index(godot_vector2 *p_self, godot_int p_index);
+const godot_real_t GDAPI *godot_vector2_operator_index_const(const godot_vector2 *p_self, godot_int p_index);
+int32_t GDAPI *godot_vector2i_operator_index(godot_vector2i *p_self, godot_int p_index);
+const int32_t GDAPI *godot_vector2i_operator_index_const(const godot_vector2i *p_self, godot_int p_index);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/gdnative/vector3.h b/modules/gdnative/include/gdnative/vector3.h
index 5127b8789b..7db093ce52 100644
--- a/modules/gdnative/include/gdnative/vector3.h
+++ b/modules/gdnative/include/gdnative/vector3.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,9 +35,9 @@
extern "C" {
#endif
-#include <stdint.h>
+#include <gdnative/math_defs.h>
-#define GODOT_VECTOR3_SIZE 12
+#define GODOT_VECTOR3_SIZE (sizeof(godot_real_t) * 3)
#ifndef GODOT_CORE_API_GODOT_VECTOR3_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_VECTOR3_TYPE_DEFINED
@@ -46,7 +46,7 @@ typedef struct {
} godot_vector3;
#endif
-#define GODOT_VECTOR3I_SIZE 12
+#define GODOT_VECTOR3I_SIZE (sizeof(int32_t) * 3)
#ifndef GODOT_CORE_API_GODOT_VECTOR3I_TYPE_DEFINED
#define GODOT_CORE_API_GODOT_VECTOR3I_TYPE_DEFINED
@@ -55,145 +55,16 @@ typedef struct {
} godot_vector3i;
#endif
-// reduce extern "C" nesting for VS2013
-#ifdef __cplusplus
-}
-#endif
-
-#include <gdnative/basis.h>
#include <gdnative/gdnative.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
- GODOT_VECTOR3_AXIS_X,
- GODOT_VECTOR3_AXIS_Y,
- GODOT_VECTOR3_AXIS_Z,
-} godot_vector3_axis;
-
-// Vector3
-
-void GDAPI godot_vector3_new(godot_vector3 *r_dest, const godot_real p_x, const godot_real p_y, const godot_real p_z);
-
-godot_string GDAPI godot_vector3_as_string(const godot_vector3 *p_self);
-
-godot_vector3i GDAPI godot_vector3_as_vector3i(const godot_vector3 *p_self);
-
-godot_int GDAPI godot_vector3_min_axis(const godot_vector3 *p_self);
-
-godot_int GDAPI godot_vector3_max_axis(const godot_vector3 *p_self);
-
-godot_real GDAPI godot_vector3_length(const godot_vector3 *p_self);
-
-godot_real GDAPI godot_vector3_length_squared(const godot_vector3 *p_self);
-
-godot_bool GDAPI godot_vector3_is_normalized(const godot_vector3 *p_self);
-
-godot_vector3 GDAPI godot_vector3_normalized(const godot_vector3 *p_self);
-
-godot_vector3 GDAPI godot_vector3_inverse(const godot_vector3 *p_self);
-
-godot_vector3 GDAPI godot_vector3_snapped(const godot_vector3 *p_self, const godot_vector3 *p_by);
-
-godot_vector3 GDAPI godot_vector3_rotated(const godot_vector3 *p_self, const godot_vector3 *p_axis, const godot_real p_phi);
-
-godot_vector3 GDAPI godot_vector3_lerp(const godot_vector3 *p_self, const godot_vector3 *p_b, const godot_real p_t);
-
-godot_vector3 GDAPI godot_vector3_cubic_interpolate(const godot_vector3 *p_self, const godot_vector3 *p_b, const godot_vector3 *p_pre_a, const godot_vector3 *p_post_b, const godot_real p_t);
-
-godot_vector3 GDAPI godot_vector3_move_toward(const godot_vector3 *p_self, const godot_vector3 *p_to, const godot_real p_delta);
-
-godot_real GDAPI godot_vector3_dot(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_vector3 GDAPI godot_vector3_cross(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_basis GDAPI godot_vector3_outer(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_basis GDAPI godot_vector3_to_diagonal_matrix(const godot_vector3 *p_self);
-
-godot_vector3 GDAPI godot_vector3_abs(const godot_vector3 *p_self);
-
-godot_vector3 GDAPI godot_vector3_sign(const godot_vector3 *p_self);
-
-godot_vector3 GDAPI godot_vector3_floor(const godot_vector3 *p_self);
-
-godot_vector3 GDAPI godot_vector3_ceil(const godot_vector3 *p_self);
-
-godot_vector3 GDAPI godot_vector3_direction_to(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_real GDAPI godot_vector3_distance_to(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_real GDAPI godot_vector3_distance_squared_to(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_real GDAPI godot_vector3_angle_to(const godot_vector3 *p_self, const godot_vector3 *p_to);
-
-godot_vector3 GDAPI godot_vector3_slide(const godot_vector3 *p_self, const godot_vector3 *p_n);
-
-godot_vector3 GDAPI godot_vector3_bounce(const godot_vector3 *p_self, const godot_vector3 *p_n);
-
-godot_vector3 GDAPI godot_vector3_reflect(const godot_vector3 *p_self, const godot_vector3 *p_n);
-
-godot_vector3 GDAPI godot_vector3_operator_add(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_vector3 GDAPI godot_vector3_operator_subtract(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_vector3 GDAPI godot_vector3_operator_multiply_vector(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_vector3 GDAPI godot_vector3_operator_multiply_scalar(const godot_vector3 *p_self, const godot_real p_b);
-
-godot_vector3 GDAPI godot_vector3_operator_divide_vector(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_vector3 GDAPI godot_vector3_operator_divide_scalar(const godot_vector3 *p_self, const godot_real p_b);
-
-godot_bool GDAPI godot_vector3_operator_equal(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_bool GDAPI godot_vector3_operator_less(const godot_vector3 *p_self, const godot_vector3 *p_b);
-
-godot_vector3 GDAPI godot_vector3_operator_neg(const godot_vector3 *p_self);
-
-void GDAPI godot_vector3_set_axis(godot_vector3 *p_self, const godot_vector3_axis p_axis, const godot_real p_val);
-
-godot_real GDAPI godot_vector3_get_axis(const godot_vector3 *p_self, const godot_vector3_axis p_axis);
-
-// Vector3i
-
-void GDAPI godot_vector3i_new(godot_vector3i *r_dest, const godot_int p_x, const godot_int p_y, const godot_int p_z);
-
-godot_string GDAPI godot_vector3i_as_string(const godot_vector3i *p_self);
-
-godot_vector3 GDAPI godot_vector3i_as_vector3(const godot_vector3i *p_self);
-
-godot_int GDAPI godot_vector3i_min_axis(const godot_vector3i *p_self);
-
-godot_int GDAPI godot_vector3i_max_axis(const godot_vector3i *p_self);
-
-godot_vector3i GDAPI godot_vector3i_abs(const godot_vector3i *p_self);
-
-godot_vector3i GDAPI godot_vector3i_sign(const godot_vector3i *p_self);
-
-godot_vector3i GDAPI godot_vector3i_operator_add(const godot_vector3i *p_self, const godot_vector3i *p_b);
-
-godot_vector3i GDAPI godot_vector3i_operator_subtract(const godot_vector3i *p_self, const godot_vector3i *p_b);
-
-godot_vector3i GDAPI godot_vector3i_operator_multiply_vector(const godot_vector3i *p_self, const godot_vector3i *p_b);
-
-godot_vector3i GDAPI godot_vector3i_operator_multiply_scalar(const godot_vector3i *p_self, const godot_int p_b);
-
-godot_vector3i GDAPI godot_vector3i_operator_divide_vector(const godot_vector3i *p_self, const godot_vector3i *p_b);
-
-godot_vector3i GDAPI godot_vector3i_operator_divide_scalar(const godot_vector3i *p_self, const godot_int p_b);
-
-godot_bool GDAPI godot_vector3i_operator_equal(const godot_vector3i *p_self, const godot_vector3i *p_b);
-
-godot_bool GDAPI godot_vector3i_operator_less(const godot_vector3i *p_self, const godot_vector3i *p_b);
-
-godot_vector3i GDAPI godot_vector3i_operator_neg(const godot_vector3i *p_self);
-
-void GDAPI godot_vector3i_set_axis(godot_vector3i *p_self, const godot_vector3_axis p_axis, const godot_int p_val);
-
-godot_int GDAPI godot_vector3i_get_axis(const godot_vector3i *p_self, const godot_vector3_axis p_axis);
+void GDAPI godot_vector3_new(godot_vector3 *p_self);
+void GDAPI godot_vector3_new_copy(godot_vector3 *r_dest, const godot_vector3 *p_src);
+void GDAPI godot_vector3i_new(godot_vector3i *p_self);
+void GDAPI godot_vector3i_new_copy(godot_vector3i *r_dest, const godot_vector3i *p_src);
+godot_real_t GDAPI *godot_vector3_operator_index(godot_vector3 *p_self, godot_int p_index);
+const godot_real_t GDAPI *godot_vector3_operator_index_const(const godot_vector3 *p_self, godot_int p_index);
+int32_t GDAPI *godot_vector3i_operator_index(godot_vector3i *p_self, godot_int p_index);
+const int32_t GDAPI *godot_vector3i_operator_index_const(const godot_vector3i *p_self, godot_int p_index);
#ifdef __cplusplus
}
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index 825033c99c..bc53a4001d 100644
--- a/modules/gdnative/include/nativescript/godot_nativescript.h
+++ b/modules/gdnative/include/nativescript/godot_nativescript.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,12 +39,8 @@ extern "C" {
typedef enum {
GODOT_METHOD_RPC_MODE_DISABLED,
- GODOT_METHOD_RPC_MODE_REMOTE,
- GODOT_METHOD_RPC_MODE_MASTER,
- GODOT_METHOD_RPC_MODE_PUPPET,
- GODOT_METHOD_RPC_MODE_REMOTESYNC,
- GODOT_METHOD_RPC_MODE_MASTERSYNC,
- GODOT_METHOD_RPC_MODE_PUPPETSYNC,
+ GODOT_METHOD_RPC_MODE_ANY_PEER,
+ GODOT_METHOD_RPC_MODE_AUTHORITY,
} godot_nativescript_method_rpc_mode;
typedef enum {
@@ -58,8 +54,10 @@ typedef enum {
GODOT_PROPERTY_HINT_FLAGS, ///< hint_text= "flag1,flag2,etc" (as bit flags)
GODOT_PROPERTY_HINT_LAYERS_2D_RENDER,
GODOT_PROPERTY_HINT_LAYERS_2D_PHYSICS,
+ GODOT_PROPERTY_HINT_LAYERS_2D_NAVIGATION,
GODOT_PROPERTY_HINT_LAYERS_3D_RENDER,
GODOT_PROPERTY_HINT_LAYERS_3D_PHYSICS,
+ GODOT_PROPERTY_HINT_LAYERS_3D_NAVIGATION,
GODOT_PROPERTY_HINT_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"
GODOT_PROPERTY_HINT_DIR, ///< a directory path must be passed
GODOT_PROPERTY_HINT_GLOBAL_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,"
@@ -85,7 +83,6 @@ typedef enum {
} godot_nativescript_property_hint;
typedef enum {
-
GODOT_PROPERTY_USAGE_STORAGE = 1,
GODOT_PROPERTY_USAGE_EDITOR = 2,
GODOT_PROPERTY_USAGE_NETWORK = 4,
@@ -105,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 42804112f2..0000000000
--- a/modules/gdnative/include/net/godot_net.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/*************************************************************************/
-/* godot_net.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 NetworkedMultiplayerPeer */
- 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 15e2df85cc..0000000000
--- a/modules/gdnative/include/net/godot_webrtc.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*************************************************************************/
-/* godot_webrtc.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 3
-#define GODOT_NET_WEBRTC_API_MINOR 2
-
-/* 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 *);
-
- 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/pluginscript/godot_pluginscript.h b/modules/gdnative/include/pluginscript/godot_pluginscript.h
index 406c3ba663..02ee4066d0 100644
--- a/modules/gdnative/include/pluginscript/godot_pluginscript.h
+++ b/modules/gdnative/include/pluginscript/godot_pluginscript.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -56,11 +56,12 @@ typedef struct {
int p_argcount, godot_variant_call_error *r_error);
void (*notification)(godot_pluginscript_instance_data *p_data, int p_notification);
+ godot_string (*to_string)(godot_pluginscript_instance_data *p_data, godot_bool *r_valid);
//this is used by script languages that keep a reference counter of their own
//you can make make Ref<> not die when it reaches zero, so deleting the reference
//depends entirely from the script.
- // Note: You can set those function pointer to nullptr if not needed.
+ // Note: You can set those function pointer to nullptr if not needed.
void (*refcount_incremented)(godot_pluginscript_instance_data *p_data);
bool (*refcount_decremented)(godot_pluginscript_instance_data *p_data); // return true if it can die
} godot_pluginscript_instance_desc;
@@ -72,6 +73,7 @@ typedef struct {
godot_string_name name;
godot_bool is_tool;
godot_string_name base;
+ godot_string icon_path;
// Member lines format: {<string>: <int>}
godot_dictionary member_lines;
@@ -119,17 +121,18 @@ typedef struct {
const char *name;
const char *type;
const char *extension;
- const char **recognized_extensions; // nullptr terminated array
+ const char **recognized_extensions; // nullptr terminated array
godot_pluginscript_language_data *(*init)();
void (*finish)(godot_pluginscript_language_data *p_data);
- const char **reserved_words; // nullptr terminated array
- const char **comment_delimiters; // nullptr terminated array
- const char **string_delimiters; // nullptr terminated array
+ const char **reserved_words; // nullptr terminated array
+ const char **comment_delimiters; // nullptr terminated array
+ const char **string_delimiters; // nullptr terminated array
godot_bool has_named_classes;
godot_bool supports_builtin_mode;
+ godot_bool can_inherit_from_file;
godot_string (*get_template_source_code)(godot_pluginscript_language_data *p_data, const godot_string *p_class_name, const godot_string *p_base_class_name);
- godot_bool (*validate)(godot_pluginscript_language_data *p_data, const godot_string *p_script, int *r_line_error, int *r_col_error, godot_string *r_test_error, const godot_string *p_path, godot_packed_string_array *r_functions);
+ godot_bool (*validate)(godot_pluginscript_language_data *p_data, const godot_string *p_script, const godot_string *p_path, godot_packed_string_array *r_functions, godot_array *r_errors); // errors = Array of Dictionary with "line", "column", "message" keys
int (*find_function)(godot_pluginscript_language_data *p_data, const godot_string *p_function, const godot_string *p_code); // Can be nullptr
godot_string (*make_function)(godot_pluginscript_language_data *p_data, const godot_string *p_class, const godot_string *p_name, const godot_packed_string_array *p_args);
godot_error (*complete_code)(godot_pluginscript_language_data *p_data, const godot_string *p_code, const godot_string *p_path, godot_object *p_owner, godot_array *r_options, godot_bool *r_force, godot_string *r_call_hint);
diff --git a/modules/gdnative/include/videodecoder/godot_videodecoder.h b/modules/gdnative/include/videodecoder/godot_videodecoder.h
index 16c92abd22..dc2cf5ec07 100644
--- a/modules/gdnative/include/videodecoder/godot_videodecoder.h
+++ b/modules/gdnative/include/videodecoder/godot_videodecoder.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -49,11 +49,11 @@ typedef struct
const char *(*get_plugin_name)();
const char **(*get_supported_extensions)(int *count);
godot_bool (*open_file)(void *, void *); // data struct, and a FileAccess pointer
- godot_real (*get_length)(const void *);
- godot_real (*get_playback_position)(const void *);
- void (*seek)(void *, godot_real);
+ godot_float (*get_length)(const void *);
+ godot_float (*get_playback_position)(const void *);
+ void (*seek)(void *, godot_float);
void (*set_audio_track)(void *, godot_int);
- void (*update)(void *, godot_real);
+ void (*update)(void *, godot_float);
godot_packed_byte_array *(*get_videoframe)(void *);
godot_int (*get_audioframe)(void *, float *, int);
godot_int (*get_channels)(const void *);
diff --git a/modules/gdnative/include/xr/godot_xr.h b/modules/gdnative/include/xr/godot_xr.h
deleted file mode 100644
index 22f7f021c4..0000000000
--- a/modules/gdnative/include/xr/godot_xr.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*************************************************************************/
-/* godot_xr.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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_NATIVEXR_H
-#define GODOT_NATIVEXR_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 GODOTVR_API_MAJOR 1
-#define GODOTVR_API_MINOR 1
-
-typedef struct {
- godot_gdnative_api_version version; /* version of our API */
- void *(*constructor)(godot_object *);
- void (*destructor)(void *);
- godot_string (*get_name)(const void *);
- godot_int (*get_capabilities)(const void *);
- godot_bool (*get_anchor_detection_is_enabled)(const void *);
- void (*set_anchor_detection_is_enabled)(void *, godot_bool);
- godot_bool (*is_stereo)(const void *);
- godot_bool (*is_initialized)(const void *);
- godot_bool (*initialize)(void *);
- void (*uninitialize)(void *);
- godot_vector2 (*get_render_targetsize)(const void *);
- godot_transform (*get_transform_for_eye)(void *, godot_int, godot_transform *);
- void (*fill_projection_for_eye)(void *, godot_real *, godot_int, godot_real, godot_real, godot_real);
- void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *);
- void (*process)(void *);
- godot_int (*get_external_texture_for_eye)(void *, godot_int);
- void (*notification)(void *, godot_int);
- godot_int (*get_camera_feed_id)(void *);
-} godot_xr_interface_gdnative;
-
-void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface);
-
-// helper functions to access XRServer data
-godot_real GDAPI godot_xr_get_worldscale();
-godot_transform GDAPI godot_xr_get_reference_frame();
-
-// helper functions for rendering
-void GDAPI godot_xr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect);
-godot_int GDAPI godot_xr_get_texid(godot_rid *p_render_target);
-
-// helper functions for updating XR controllers
-godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position);
-void GDAPI godot_xr_remove_controller(godot_int p_controller_id);
-void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position);
-void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed);
-void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real p_value, godot_bool p_can_be_negative);
-godot_real GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* !GODOT_NATIVEXR_H */
diff --git a/modules/gdnative/nativescript/api_generator.cpp b/modules/gdnative/nativescript/api_generator.cpp
index 8dbaec4e75..598f7c7ad0 100644
--- a/modules/gdnative/nativescript/api_generator.cpp
+++ b/modules/gdnative/nativescript/api_generator.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,11 +32,13 @@
#ifdef TOOLS_ENABLED
-#include "core/class_db.h"
-#include "core/engine.h"
-#include "core/global_constants.h"
-#include "core/os/file_access.h"
-#include "core/pair.h"
+#include "core/config/engine.h"
+#include "core/core_constants.h"
+#include "core/io/file_access.h"
+#include "core/object/class_db.h"
+#include "core/string/string_builder.h"
+#include "core/templates/pair.h"
+#include "core/variant/variant_parser.h"
// helper stuff
@@ -65,14 +67,15 @@ struct MethodAPI {
Map<int, Variant> default_arguments;
- int argument_count;
- bool has_varargs;
- bool is_editor;
- bool is_noscript;
- bool is_const;
- bool is_reverse;
- bool is_virtual;
- bool is_from_script;
+ int argument_count = 0;
+ bool has_varargs = false;
+ bool is_editor = false;
+ bool is_noscript = false;
+ bool is_const = false;
+ bool is_static = false; // For builtin types.
+ bool is_reverse = false;
+ bool is_virtual = false;
+ bool is_from_script = false;
};
struct PropertyAPI {
@@ -80,12 +83,14 @@ struct PropertyAPI {
String getter;
String setter;
String type;
- int index;
+ int index = 0;
};
struct ConstantAPI {
String constant_name;
- int constant_value;
+ int constant_value = 0;
+ Variant builtin_constant_value; // For builtin types;
+ String builtin_constant_type; // For builtin types;
};
struct SignalAPI {
@@ -100,23 +105,35 @@ struct EnumAPI {
List<Pair<int, String>> values;
};
+struct OperatorAPI { // For builtin types;
+ String name;
+ int oper = Variant::OP_MAX;
+ String other_type;
+ String return_type;
+};
+
struct ClassAPI {
String class_name;
String super_class_name;
- ClassDB::APIType api_type;
+ ClassDB::APIType api_type = ClassDB::API_NONE;
- bool is_singleton;
+ bool is_singleton = false;
String singleton_name;
- bool is_instanciable;
+ bool is_instantiable = false;
// @Unclear
- bool is_reference;
+ bool is_ref_counted = false;
+ bool has_indexing = false; // For builtin types.
+ String indexed_type; // For builtin types.
+ bool is_keyed = false; // For builtin types.
List<MethodAPI> methods;
+ List<MethodAPI> constructors; // For builtin types.
List<PropertyAPI> properties;
List<ConstantAPI> constants;
List<SignalAPI> signals_;
List<EnumAPI> enums;
+ List<OperatorAPI> operators; // For builtin types.
};
static String get_type_name(const PropertyInfo &info) {
@@ -127,7 +144,7 @@ static String get_type_name(const PropertyInfo &info) {
return info.class_name;
}
if (info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- return info.hint_string;
+ return info.class_name;
}
if (info.type == Variant::NIL && (info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) {
return "Variant";
@@ -173,20 +190,41 @@ List<ClassAPI> generate_c_api_classes() {
ClassDB::get_class_list(&classes);
classes.sort_custom<StringName::AlphCompare>();
- // Register global constants as a fake GlobalConstants singleton class
+ // Register global constants as a fake CoreConstants singleton class
{
ClassAPI global_constants_api;
- global_constants_api.class_name = "GlobalConstants";
+ global_constants_api.class_name = "CoreConstants";
global_constants_api.api_type = ClassDB::API_CORE;
global_constants_api.is_singleton = true;
- global_constants_api.singleton_name = "GlobalConstants";
- global_constants_api.is_instanciable = false;
- const int constants_count = GlobalConstants::get_global_constant_count();
+ global_constants_api.singleton_name = "CoreConstants";
+ global_constants_api.is_instantiable = false;
+ const int constants_count = CoreConstants::get_global_constant_count();
+
+ Map<StringName, EnumAPI> enum_api_map;
for (int i = 0; i < constants_count; ++i) {
- ConstantAPI constant_api;
- constant_api.constant_name = GlobalConstants::get_global_constant_name(i);
- constant_api.constant_value = GlobalConstants::get_global_constant_value(i);
- global_constants_api.constants.push_back(constant_api);
+ StringName enum_name = CoreConstants::get_global_constant_enum(i);
+ String name = String(CoreConstants::get_global_constant_name(i));
+ int value = CoreConstants::get_global_constant_value(i);
+
+ if (enum_name == StringName()) {
+ ConstantAPI constant_api;
+ constant_api.constant_name = name;
+ constant_api.constant_value = value;
+ global_constants_api.constants.push_back(constant_api);
+ } else {
+ EnumAPI enum_api;
+ if (enum_api_map.has(enum_name)) {
+ enum_api = enum_api_map[enum_name];
+ } else {
+ enum_api.name = String(enum_name);
+ }
+ enum_api.values.push_back(Pair(value, name));
+
+ enum_api_map[enum_name] = enum_api;
+ }
+ }
+ 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);
@@ -195,28 +233,28 @@ List<ClassAPI> generate_c_api_classes() {
for (List<StringName>::Element *e = classes.front(); e != nullptr; e = e->next()) {
StringName class_name = e->get();
+ if (!ClassDB::is_class_exposed(class_name)) {
+ continue;
+ }
+
ClassAPI class_api;
class_api.api_type = ClassDB::get_api_type(e->get());
class_api.class_name = class_name;
class_api.super_class_name = ClassDB::get_parent_class(class_name);
{
- String name = class_name;
- if (name.begins_with("_")) {
- name.remove(0);
- }
- class_api.is_singleton = Engine::get_singleton()->has_singleton(name);
+ class_api.is_singleton = Engine::get_singleton()->has_singleton(class_name);
if (class_api.is_singleton) {
- class_api.singleton_name = name;
+ class_api.singleton_name = class_name;
}
}
- class_api.is_instanciable = !class_api.is_singleton && ClassDB::can_instance(class_name);
+ class_api.is_instantiable = !class_api.is_singleton && ClassDB::can_instantiate(class_name);
{
List<StringName> inheriters;
- ClassDB::get_inheriters_from_class("Reference", &inheriters);
- bool is_reference = !!inheriters.find(class_name) || class_name == "Reference";
+ ClassDB::get_inheriters_from_class("RefCounted", &inheriters);
+ bool is_ref_counted = !!inheriters.find(class_name) || class_name == "RefCounted";
// @Unclear
- class_api.is_reference = !class_api.is_singleton && is_reference;
+ class_api.is_ref_counted = !class_api.is_singleton && is_ref_counted;
}
// constants
@@ -290,12 +328,14 @@ List<ClassAPI> generate_c_api_classes() {
property_api.type = p->get().name.get_slice(":", 1);
property_api.name = p->get().name.get_slice(":", 0);
} else {
- property_api.type = get_type_name(p->get());
+ MethodInfo minfo;
+ ClassDB::get_method_info(class_name, property_api.getter, &minfo, true, false);
+ property_api.type = get_type_name(minfo.return_val);
}
property_api.index = ClassDB::get_property_index(class_name, p->get().name);
- if (!property_api.setter.empty() || !property_api.getter.empty()) {
+ if (!property_api.setter.is_empty() || !property_api.getter.is_empty()) {
class_api.properties.push_back(property_api);
}
}
@@ -352,7 +392,7 @@ List<ClassAPI> generate_c_api_classes() {
arg_type = arg_info.name.get_slice(":", 1);
arg_name = arg_info.name.get_slice(":", 0);
} else if (arg_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- arg_type = arg_info.hint_string;
+ arg_type = arg_info.class_name;
} else if (arg_info.type == Variant::NIL) {
arg_type = "Variant";
} else if (arg_info.type == Variant::OBJECT) {
@@ -361,7 +401,7 @@ List<ClassAPI> generate_c_api_classes() {
arg_type = Variant::get_type_name(arg_info.type);
}
} else {
- arg_type = Variant::get_type_name(arg_info.type);
+ arg_type = get_type_name(arg_info);
}
method_api.argument_names.push_back(arg_name);
@@ -381,11 +421,11 @@ List<ClassAPI> generate_c_api_classes() {
List<EnumAPI> enums;
List<StringName> enum_names;
ClassDB::get_enum_list(class_name, &enum_names, true);
- for (List<StringName>::Element *E = enum_names.front(); E; E = E->next()) {
+ for (const StringName &E : enum_names) {
List<StringName> value_names;
EnumAPI enum_api;
- enum_api.name = E->get();
- ClassDB::get_enum_constants(class_name, E->get(), &value_names, true);
+ enum_api.name = E;
+ ClassDB::get_enum_constants(class_name, E, &value_names, true);
for (List<StringName>::Element *val_e = value_names.front(); val_e; val_e = val_e->next()) {
int int_val = ClassDB::get_integer_constant(class_name, val_e->get(), nullptr);
enum_api.values.push_back(Pair<int, String>(int_val, val_e->get()));
@@ -402,12 +442,200 @@ List<ClassAPI> generate_c_api_classes() {
}
/*
+ * Reads the builtin Variant API to a list
+ */
+List<ClassAPI> generate_c_builtin_api_types() {
+ List<ClassAPI> api;
+
+ // Special class for the utility methods.
+ {
+ ClassAPI utility_api;
+ utility_api.class_name = "Utilities";
+ utility_api.is_instantiable = false;
+
+ List<StringName> utility_functions;
+ Variant::get_utility_function_list(&utility_functions);
+ for (const StringName &E : utility_functions) {
+ const StringName &function_name = E;
+
+ MethodAPI function_api;
+ function_api.method_name = function_name;
+ function_api.has_varargs = Variant::is_utility_function_vararg(function_name);
+ function_api.argument_count = function_api.has_varargs ? 0 : Variant::get_utility_function_argument_count(function_name);
+ function_api.is_const = Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH;
+
+ for (int i = 0; i < function_api.argument_count; i++) {
+ function_api.argument_names.push_back(Variant::get_utility_function_argument_name(function_name, i));
+ Variant::Type arg_type = Variant::get_utility_function_argument_type(function_name, i);
+ function_api.argument_types.push_back(arg_type == Variant::NIL ? "Variant" : Variant::get_type_name(arg_type));
+ }
+
+ if (Variant::has_utility_function_return_value(function_name)) {
+ Variant::Type ret_type = Variant::get_utility_function_return_type(function_name);
+ function_api.return_type = ret_type == Variant::NIL ? "Variant" : Variant::get_type_name(ret_type);
+ } else {
+ function_api.return_type = "void";
+ }
+
+ utility_api.methods.push_back(function_api);
+ }
+
+ api.push_back(utility_api);
+ }
+
+ for (int t = 0; t < Variant::VARIANT_MAX; t++) {
+ Variant::Type type = (Variant::Type)t;
+
+ ClassAPI class_api;
+ class_api.class_name = Variant::get_type_name(type);
+ class_api.is_instantiable = true;
+ class_api.has_indexing = Variant::has_indexing(type);
+ class_api.indexed_type = Variant::get_type_name(Variant::get_indexed_element_type(type));
+ class_api.is_keyed = Variant::is_keyed(type);
+ // Types that are passed by reference.
+ switch (type) {
+ case Variant::OBJECT:
+ case Variant::DICTIONARY:
+ case Variant::ARRAY:
+ case Variant::PACKED_BYTE_ARRAY:
+ case Variant::PACKED_INT32_ARRAY:
+ case Variant::PACKED_INT64_ARRAY:
+ case Variant::PACKED_FLOAT32_ARRAY:
+ case Variant::PACKED_FLOAT64_ARRAY:
+ case Variant::PACKED_STRING_ARRAY:
+ case Variant::PACKED_VECTOR2_ARRAY:
+ case Variant::PACKED_VECTOR3_ARRAY:
+ case Variant::PACKED_COLOR_ARRAY:
+ class_api.is_ref_counted = true;
+ break;
+ default:
+ class_api.is_ref_counted = false;
+ break;
+ }
+
+ // Methods.
+
+ List<StringName> methods;
+ Variant::get_builtin_method_list(type, &methods);
+ for (const StringName &E : methods) {
+ const StringName &method_name = E;
+
+ MethodAPI method_api;
+
+ method_api.method_name = method_name;
+ method_api.argument_count = Variant::get_builtin_method_argument_count(type, method_name);
+ method_api.has_varargs = Variant::is_builtin_method_vararg(type, method_name);
+ method_api.is_const = Variant::is_builtin_method_const(type, method_name);
+ method_api.is_static = Variant::is_builtin_method_static(type, method_name);
+
+ for (int i = 0; i < method_api.argument_count; i++) {
+ method_api.argument_names.push_back(Variant::get_builtin_method_argument_name(type, method_name, i));
+ Variant::Type arg_type = Variant::get_builtin_method_argument_type(type, method_name, i);
+ method_api.argument_types.push_back(arg_type == Variant::NIL ? "Variant" : Variant::get_type_name(arg_type));
+ }
+
+ Vector<Variant> default_arguments = Variant::get_builtin_method_default_arguments(type, method_name);
+
+ int default_start = method_api.argument_names.size() - default_arguments.size();
+
+ for (int i = 0; i < default_arguments.size(); i++) {
+ method_api.default_arguments[default_start + i] = default_arguments[i];
+ }
+
+ if (Variant::has_builtin_method_return_value(type, method_name)) {
+ Variant::Type ret_type = Variant::get_builtin_method_return_type(type, method_name);
+ method_api.return_type = ret_type == Variant::NIL ? "Variant" : Variant::get_type_name(ret_type);
+ } else {
+ method_api.return_type = "void";
+ }
+
+ class_api.methods.push_back(method_api);
+ }
+
+ // Constructors.
+
+ for (int c = 0; c < Variant::get_constructor_count(type); c++) {
+ MethodAPI constructor_api;
+
+ constructor_api.method_name = Variant::get_type_name(type);
+ constructor_api.argument_count = Variant::get_constructor_argument_count(type, c);
+ constructor_api.return_type = Variant::get_type_name(type);
+
+ for (int i = 0; i < constructor_api.argument_count; i++) {
+ constructor_api.argument_names.push_back(Variant::get_constructor_argument_name(type, c, i));
+ Variant::Type arg_type = Variant::get_constructor_argument_type(type, c, i);
+ constructor_api.argument_types.push_back(arg_type == Variant::NIL ? "Variant" : Variant::get_type_name(arg_type));
+ }
+
+ class_api.constructors.push_back(constructor_api);
+ }
+
+ // Constants.
+
+ List<StringName> constants;
+ Variant::get_constants_for_type(type, &constants);
+ for (const StringName &E : constants) {
+ const StringName &constant_name = E;
+ ConstantAPI constant_api;
+
+ constant_api.constant_name = constant_name;
+ constant_api.builtin_constant_value = Variant::get_constant_value(type, constant_name);
+ constant_api.builtin_constant_type = Variant::get_type_name(constant_api.builtin_constant_value.get_type());
+
+ class_api.constants.push_back(constant_api);
+ }
+
+ // Members.
+
+ List<StringName> members;
+ Variant::get_member_list(type, &members);
+ for (const StringName &E : members) {
+ const StringName &member_name = E;
+
+ PropertyAPI member_api;
+ member_api.name = member_name;
+ Variant::Type member_type = Variant::get_member_type(type, member_name);
+ member_api.type = member_type == Variant::NIL ? "Variant" : Variant::get_type_name(member_type);
+
+ class_api.properties.push_back(member_api);
+ }
+
+ // Operators.
+
+ for (int op = 0; op < Variant::OP_MAX; op++) {
+ Variant::Operator oper = (Variant::Operator)op;
+
+ for (int ot = 0; ot < Variant::VARIANT_MAX; ot++) {
+ Variant::Type other_type = (Variant::Type)ot;
+
+ if (!Variant::get_validated_operator_evaluator(oper, type, other_type)) {
+ continue;
+ }
+
+ OperatorAPI oper_api;
+ oper_api.name = Variant::get_operator_name(oper);
+ oper_api.oper = oper;
+ oper_api.other_type = Variant::get_type_name(other_type);
+ oper_api.return_type = Variant::get_type_name(Variant::get_operator_return_type(oper, type, other_type));
+
+ class_api.operators.push_back(oper_api);
+ }
+ }
+
+ api.push_back(class_api);
+ }
+
+ return api;
+}
+
+/*
* Generates the JSON source from the API in p_api
*/
static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
// I'm sorry for the \t mess
List<String> source;
+ VariantWriter writer;
source.push_back("[\n");
@@ -421,9 +649,8 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
source.push_back(String("\t\t\"api_type\": \"") + (api.api_type == ClassDB::API_CORE ? "core" : (api.api_type == ClassDB::API_EDITOR ? "tools" : "none")) + "\",\n");
source.push_back(String("\t\t\"singleton\": ") + (api.is_singleton ? "true" : "false") + ",\n");
source.push_back("\t\t\"singleton_name\": \"" + api.singleton_name + "\",\n");
- source.push_back(String("\t\t\"instanciable\": ") + (api.is_instanciable ? "true" : "false") + ",\n");
- source.push_back(String("\t\t\"is_reference\": ") + (api.is_reference ? "true" : "false") + ",\n");
- // @Unclear
+ source.push_back(String("\t\t\"instantiable\": ") + (api.is_instantiable ? "true" : "false") + ",\n");
+ source.push_back(String("\t\t\"is_ref_counted\": ") + (api.is_ref_counted ? "true" : "false") + ",\n");
source.push_back("\t\t\"constants\": {\n");
for (List<ConstantAPI>::Element *e = api.constants.front(); e; e = e->next()) {
@@ -453,7 +680,12 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
source.push_back("\t\t\t\t\t\t\"name\": \"" + e->get().argument_names[i] + "\",\n");
source.push_back("\t\t\t\t\t\t\"type\": \"" + e->get().argument_types[i] + "\",\n");
source.push_back(String("\t\t\t\t\t\t\"has_default_value\": ") + (e->get().default_arguments.has(i) ? "true" : "false") + ",\n");
- source.push_back("\t\t\t\t\t\t\"default_value\": \"" + (e->get().default_arguments.has(i) ? (String)e->get().default_arguments[i] : "") + "\"\n");
+ String default_value;
+ if (e->get().default_arguments.has(i)) {
+ writer.write_to_string(e->get().default_arguments[i], default_value);
+ default_value = default_value.replace("\n", "").json_escape();
+ }
+ source.push_back("\t\t\t\t\t\t\"default_value\": \"" + default_value + "\"\n");
source.push_back(String("\t\t\t\t\t}") + ((i < e->get().argument_names.size() - 1) ? "," : "") + "\n");
}
source.push_back("\t\t\t\t]\n");
@@ -479,7 +711,12 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
source.push_back("\t\t\t\t\t\t\"name\": \"" + e->get().argument_names[i] + "\",\n");
source.push_back("\t\t\t\t\t\t\"type\": \"" + e->get().argument_types[i] + "\",\n");
source.push_back(String("\t\t\t\t\t\t\"has_default_value\": ") + (e->get().default_arguments.has(i) ? "true" : "false") + ",\n");
- source.push_back("\t\t\t\t\t\t\"default_value\": \"" + (e->get().default_arguments.has(i) ? (String)e->get().default_arguments[i] : "") + "\"\n");
+ String default_value;
+ if (e->get().default_arguments.has(i)) {
+ writer.write_to_string(e->get().default_arguments[i], default_value);
+ default_value = default_value.replace("\n", "").json_escape();
+ }
+ source.push_back("\t\t\t\t\t\t\"default_value\": \"" + default_value + "\"\n");
source.push_back(String("\t\t\t\t\t}") + ((i < e->get().argument_names.size() - 1) ? "," : "") + "\n");
}
source.push_back("\t\t\t\t]\n");
@@ -508,6 +745,173 @@ static List<String> generate_c_api_json(const List<ClassAPI> &p_api) {
return source;
}
+static int indent_level = 0;
+
+static void append_indented(StringBuilder &p_source, const String &p_text) {
+ for (int i = 0; i < indent_level; i++) {
+ p_source.append("\t");
+ }
+ p_source.append(p_text);
+ p_source.append("\n");
+}
+
+static void append_indented(StringBuilder &p_source, const char *p_text) {
+ for (int i = 0; i < indent_level; i++) {
+ p_source.append("\t");
+ }
+ p_source.append(p_text);
+ p_source.append("\n");
+}
+
+static void write_builtin_method(StringBuilder &p_source, const MethodAPI &p_method) {
+ VariantWriter writer;
+
+ append_indented(p_source, vformat(R"("name": "%s",)", p_method.method_name));
+ append_indented(p_source, vformat(R"("return_type": "%s",)", p_method.return_type));
+ append_indented(p_source, vformat(R"("is_const": %s,)", p_method.is_const ? "true" : "false"));
+ append_indented(p_source, vformat(R"("is_static": %s,)", p_method.is_static ? "true" : "false"));
+ append_indented(p_source, vformat(R"("has_varargs": %s,)", p_method.has_varargs ? "true" : "false"));
+
+ append_indented(p_source, R"("arguments": [)");
+ indent_level++;
+ for (int i = 0; i < p_method.argument_count; i++) {
+ append_indented(p_source, "{");
+ indent_level++;
+
+ append_indented(p_source, vformat(R"("name": "%s",)", p_method.argument_names[i]));
+ append_indented(p_source, vformat(R"("type": "%s",)", p_method.argument_types[i]));
+ append_indented(p_source, vformat(R"("has_default_value": %s,)", p_method.default_arguments.has(i) ? "true" : "false"));
+ String default_value;
+ if (p_method.default_arguments.has(i)) {
+ writer.write_to_string(p_method.default_arguments[i], default_value);
+ default_value = default_value.replace("\n", "").json_escape();
+ }
+ append_indented(p_source, vformat(R"("default_value": "%s")", default_value));
+
+ indent_level--;
+ append_indented(p_source, i < p_method.argument_count - 1 ? "}," : "}");
+ }
+ indent_level--;
+ append_indented(p_source, "]");
+}
+
+static List<String> generate_c_builtin_api_json(const List<ClassAPI> &p_api) {
+ StringBuilder source;
+
+ source.append("[\n");
+
+ indent_level = 1;
+
+ for (const List<ClassAPI>::Element *C = p_api.front(); C; C = C->next()) {
+ const ClassAPI &class_api = C->get();
+ append_indented(source, "{");
+ indent_level++;
+
+ append_indented(source, vformat(R"("name": "%s",)", class_api.class_name));
+ append_indented(source, vformat(R"("is_instantiable": %s,)", class_api.is_instantiable ? "true" : "false"));
+ append_indented(source, vformat(R"("is_ref_counted": %s,)", class_api.is_ref_counted ? "true" : "false"));
+ append_indented(source, vformat(R"("has_indexing": %s,)", class_api.has_indexing ? "true" : "false"));
+ append_indented(source, vformat(R"("indexed_type": "%s",)", class_api.has_indexing && class_api.indexed_type == "Nil" ? "Variant" : class_api.indexed_type));
+ append_indented(source, vformat(R"("is_keyed": %s,)", class_api.is_keyed ? "true" : "false"));
+
+ // Constructors.
+ append_indented(source, R"("constructors": [)");
+ indent_level++;
+ for (const List<MethodAPI>::Element *E = class_api.constructors.front(); E; E = E->next()) {
+ const MethodAPI &constructor = E->get();
+ append_indented(source, "{");
+ indent_level++;
+
+ write_builtin_method(source, constructor);
+
+ indent_level--;
+ append_indented(source, E->next() ? "}," : "}");
+ }
+ indent_level--;
+ append_indented(source, "],");
+
+ // Constants.
+ append_indented(source, R"("constants": [)");
+ indent_level++;
+ for (const List<ConstantAPI>::Element *E = class_api.constants.front(); E; E = E->next()) {
+ const ConstantAPI &constant = E->get();
+ append_indented(source, "{");
+ indent_level++;
+
+ append_indented(source, vformat(R"("name": "%s",)", constant.constant_name));
+ append_indented(source, vformat(R"("type": "%s",)", constant.builtin_constant_type));
+ append_indented(source, vformat(R"("value": "%s")", constant.builtin_constant_value.operator String()));
+
+ indent_level--;
+ append_indented(source, E->next() ? "}," : "}");
+ }
+ indent_level--;
+ append_indented(source, "],");
+
+ // Methods.
+ append_indented(source, R"("methods": [)");
+ indent_level++;
+ for (const List<MethodAPI>::Element *E = class_api.methods.front(); E; E = E->next()) {
+ const MethodAPI &method = E->get();
+ append_indented(source, "{");
+ indent_level++;
+
+ write_builtin_method(source, method);
+
+ indent_level--;
+ append_indented(source, E->next() ? "}," : "}");
+ }
+ indent_level--;
+ append_indented(source, "],");
+
+ // Members.
+ append_indented(source, R"("members": [)");
+ indent_level++;
+ for (const List<PropertyAPI>::Element *E = class_api.properties.front(); E; E = E->next()) {
+ const PropertyAPI &member = E->get();
+ append_indented(source, "{");
+ indent_level++;
+
+ append_indented(source, vformat(R"("name": "%s",)", member.name));
+ append_indented(source, vformat(R"("type": "%s")", member.type));
+
+ indent_level--;
+ append_indented(source, E->next() ? "}," : "}");
+ }
+ indent_level--;
+ append_indented(source, "],");
+
+ // Operators.
+ append_indented(source, R"("operators": [)");
+ indent_level++;
+ for (const List<OperatorAPI>::Element *E = class_api.operators.front(); E; E = E->next()) {
+ const OperatorAPI &oper = E->get();
+ append_indented(source, "{");
+ indent_level++;
+
+ append_indented(source, vformat(R"("name": "%s",)", oper.name));
+ append_indented(source, vformat(R"("operator": %d,)", oper.oper));
+ append_indented(source, vformat(R"("other_type": "%s",)", oper.other_type));
+ append_indented(source, vformat(R"("return_type": "%s")", oper.return_type));
+
+ indent_level--;
+ append_indented(source, E->next() ? "}," : "}");
+ }
+ indent_level--;
+ append_indented(source, "]");
+
+ indent_level--;
+ append_indented(source, C->next() ? "}," : "}");
+ }
+
+ indent_level--;
+ source.append("]\n");
+
+ List<String> result;
+ result.push_back(source.as_string());
+ return result;
+}
+
#endif
/*
@@ -526,3 +930,19 @@ Error generate_c_api(const String &p_path) {
return save_file(p_path, json_source);
#endif
}
+/*
+ * Saves the builtin Godot API to a JSON file located at
+ * p_path
+ */
+Error generate_c_builtin_api(const String &p_path) {
+#ifndef TOOLS_ENABLED
+ return ERR_BUG;
+#else
+
+ List<ClassAPI> api = generate_c_builtin_api_types();
+
+ List<String> json_source = generate_c_builtin_api_json(api);
+
+ return save_file(p_path, json_source);
+#endif
+}
diff --git a/modules/gdnative/nativescript/api_generator.h b/modules/gdnative/nativescript/api_generator.h
index edbb1d1f23..611abb2a2d 100644
--- a/modules/gdnative/nativescript/api_generator.h
+++ b/modules/gdnative/nativescript/api_generator.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,9 +31,10 @@
#ifndef API_GENERATOR_H
#define API_GENERATOR_H
+#include "core/string/ustring.h"
#include "core/typedefs.h"
-#include "core/ustring.h"
Error generate_c_api(const String &p_path);
+Error generate_c_builtin_api(const String &p_path);
#endif // API_GENERATOR_H
diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp
index e47548f3e9..dadd1a9d10 100644
--- a/modules/gdnative/nativescript/godot_nativescript.cpp
+++ b/modules/gdnative/nativescript/godot_nativescript.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,11 +30,11 @@
#include "nativescript/godot_nativescript.h"
-#include "core/class_db.h"
-#include "core/error_macros.h"
-#include "core/global_constants.h"
-#include "core/project_settings.h"
-#include "core/variant.h"
+#include "core/config/project_settings.h"
+#include "core/core_constants.h"
+#include "core/error/error_macros.h"
+#include "core/object/class_db.h"
+#include "core/variant/variant.h"
#include "gdnative/gdnative.h"
#include <stdint.h>
@@ -70,8 +70,7 @@ void GDAPI godot_nativescript_register_class(void *p_gdnative_handle, const char
const NativeScriptDesc *b = desc.base_data;
while (b) {
- desc.rpc_count += b->rpc_count;
- desc.rset_count += b->rset_count;
+ desc.rpc_methods.append_array(b->rpc_methods);
b = b->base_data;
}
@@ -94,8 +93,6 @@ void GDAPI godot_nativescript_register_tool_class(void *p_gdnative_handle, const
desc.destroy_func = p_destroy_func;
desc.is_tool = true;
desc.base = p_base;
- desc.rpc_count = 0;
- desc.rset_count = 0;
if (classes->has(p_base)) {
desc.base_data = &(*classes)[p_base];
@@ -103,8 +100,7 @@ void GDAPI godot_nativescript_register_tool_class(void *p_gdnative_handle, const
const NativeScriptDesc *b = desc.base_data;
while (b) {
- desc.rpc_count += b->rpc_count;
- desc.rset_count += b->rset_count;
+ desc.rpc_methods.append_array(b->rpc_methods);
b = b->base_data;
}
@@ -126,13 +122,16 @@ void GDAPI godot_nativescript_register_method(void *p_gdnative_handle, const cha
method.method = p_method;
method.rpc_mode = p_attr.rpc_type;
method.rpc_method_id = UINT16_MAX;
- if (p_attr.rpc_type != GODOT_METHOD_RPC_MODE_DISABLED) {
- method.rpc_method_id = E->get().rpc_count;
- E->get().rpc_count += 1;
- }
method.info = MethodInfo(p_function_name);
E->get().methods.insert(p_function_name, method);
+
+ if (p_attr.rpc_type != GODOT_METHOD_RPC_MODE_DISABLED) {
+ Multiplayer::RPCConfig nd;
+ nd.name = String(p_name);
+ nd.rpc_mode = Multiplayer::RPCMode(p_attr.rpc_type);
+ E->get().rpc_methods.push_back(nd);
+ }
}
void GDAPI godot_nativescript_register_property(void *p_gdnative_handle, const char *p_name, const char *p_path, godot_nativescript_property_attributes *p_attr, godot_nativescript_property_set_func p_set_func, godot_nativescript_property_get_func p_get_func) {
@@ -144,11 +143,6 @@ void GDAPI godot_nativescript_register_property(void *p_gdnative_handle, const c
NativeScriptDesc::Property property;
property.default_value = *(Variant *)&p_attr->default_value;
property.getter = p_get_func;
- property.rset_mode = p_attr->rset_type;
- if (p_attr->rset_type != GODOT_METHOD_RPC_MODE_DISABLED) {
- property.rset_property_id = E->get().rset_count;
- E->get().rset_count += 1;
- }
property.setter = p_set_func;
property.info = PropertyInfo((Variant::Type)p_attr->type,
p_path,
@@ -338,7 +332,7 @@ void GDAPI godot_nativescript_unregister_instance_binding_data_functions(int p_i
}
void GDAPI *godot_nativescript_get_instance_binding_data(int p_idx, godot_object *p_object) {
- return NativeScriptLanguage::get_singleton()->get_instance_binding_data(p_idx, (Object *)p_object);
+ return nullptr;
}
void GDAPI godot_nativescript_profiling_add_data(const char *p_signature, uint64_t p_time) {
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 632f4e5fee..368eb67fa6 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,12 +34,14 @@
#include "gdnative/gdnative.h"
+#include "core/config/project_settings.h"
+#include "core/core_constants.h"
#include "core/core_string_names.h"
-#include "core/global_constants.h"
+#include "core/io/file_access.h"
#include "core/io/file_access_encrypted.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
+
+#include "main/main.h"
#include "scene/main/scene_tree.h"
#include "scene/resources/resource_format_text.h"
@@ -96,10 +98,10 @@ void NativeScript::_update_placeholder(PlaceHolderScriptInstance *p_placeholder)
List<PropertyInfo> info;
get_script_property_list(&info);
Map<StringName, Variant> values;
- for (List<PropertyInfo>::Element *E = info.front(); E; E = E->next()) {
+ for (const PropertyInfo &E : info) {
Variant value;
- get_property_default_value(E->get().name, value);
- values[E->get().name] = value;
+ get_property_default_value(E.name, value);
+ values[E.name] = value;
}
p_placeholder->update(info, values);
@@ -112,9 +114,25 @@ void NativeScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder)
#endif
bool NativeScript::inherits_script(const Ref<Script> &p_script) const {
-#ifndef _MSC_VER
-#warning inheritance needs to be implemented in NativeScript
-#endif
+ Ref<NativeScript> ns = p_script;
+ if (ns.is_null()) {
+ return false;
+ }
+
+ const NativeScriptDesc *other_s = ns->get_script_desc();
+ if (!other_s) {
+ return false;
+ }
+
+ const NativeScriptDesc *s = get_script_desc();
+
+ while (s) {
+ if (s == other_s) {
+ return true;
+ }
+ s = s->base_data;
+ }
+
return false;
}
@@ -168,12 +186,12 @@ String NativeScript::get_script_class_icon_path() const {
return script_class_icon_path;
}
-bool NativeScript::can_instance() const {
+bool NativeScript::can_instantiate() const {
NativeScriptDesc *script_data = get_script_desc();
#ifdef TOOLS_ENABLED
// Only valid if this is either a tool script or a "regular" script.
- // (so an environment whre scripting is disabled (and not the editor) would not
+ // (so, an environment where scripting is disabled (and not the editor) would not
// create objects).
return script_data && (is_tool() || ScriptServer::is_scripting_enabled());
#else
@@ -342,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;
@@ -383,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;
@@ -413,245 +431,11 @@ void NativeScript::get_script_property_list(List<PropertyInfo> *p_list) const {
}
}
-Vector<ScriptNetData> NativeScript::get_rpc_methods() const {
- Vector<ScriptNetData> v;
-
- NativeScriptDesc *script_data = get_script_desc();
-
- while (script_data) {
- for (Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.front(); E; E = E->next()) {
- if (E->get().rpc_mode != GODOT_METHOD_RPC_MODE_DISABLED) {
- ScriptNetData nd;
- nd.name = E->key();
- nd.mode = MultiplayerAPI::RPCMode(E->get().rpc_mode);
- v.push_back(nd);
- }
- }
-
- script_data = script_data->base_data;
- }
-
- return v;
-}
-
-uint16_t NativeScript::get_rpc_method_id(const StringName &p_method) const {
- NativeScriptDesc *script_data = get_script_desc();
-
- while (script_data) {
- Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.find(p_method);
- if (E) {
- return E->get().rpc_method_id;
- }
-
- script_data = script_data->base_data;
- }
-
- return UINT16_MAX;
-}
-
-StringName NativeScript::get_rpc_method(uint16_t p_id) const {
- ERR_FAIL_COND_V(p_id == UINT16_MAX, StringName());
-
- NativeScriptDesc *script_data = get_script_desc();
-
- while (script_data) {
- for (Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.front(); E; E = E->next()) {
- if (E->get().rpc_method_id == p_id) {
- return E->key();
- }
- }
-
- script_data = script_data->base_data;
- }
-
- return StringName();
-}
-
-MultiplayerAPI::RPCMode NativeScript::get_rpc_mode_by_id(uint16_t p_id) const {
- ERR_FAIL_COND_V(p_id == UINT16_MAX, MultiplayerAPI::RPC_MODE_DISABLED);
-
- NativeScriptDesc *script_data = get_script_desc();
-
- while (script_data) {
- for (Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.front(); E; E = E->next()) {
- if (E->get().rpc_method_id == p_id) {
- switch (E->get().rpc_mode) {
- case GODOT_METHOD_RPC_MODE_DISABLED:
- return MultiplayerAPI::RPC_MODE_DISABLED;
- case GODOT_METHOD_RPC_MODE_REMOTE:
- return MultiplayerAPI::RPC_MODE_REMOTE;
- case GODOT_METHOD_RPC_MODE_MASTER:
- return MultiplayerAPI::RPC_MODE_MASTER;
- case GODOT_METHOD_RPC_MODE_PUPPET:
- return MultiplayerAPI::RPC_MODE_PUPPET;
- case GODOT_METHOD_RPC_MODE_REMOTESYNC:
- return MultiplayerAPI::RPC_MODE_REMOTESYNC;
- case GODOT_METHOD_RPC_MODE_MASTERSYNC:
- return MultiplayerAPI::RPC_MODE_MASTERSYNC;
- case GODOT_METHOD_RPC_MODE_PUPPETSYNC:
- return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
- default:
- return MultiplayerAPI::RPC_MODE_DISABLED;
- }
- }
- }
-
- script_data = script_data->base_data;
- }
-
- return MultiplayerAPI::RPC_MODE_DISABLED;
-}
-
-MultiplayerAPI::RPCMode NativeScript::get_rpc_mode(const StringName &p_method) const {
+const Vector<Multiplayer::RPCConfig> NativeScript::get_rpc_methods() const {
NativeScriptDesc *script_data = get_script_desc();
+ ERR_FAIL_COND_V(!script_data, Vector<Multiplayer::RPCConfig>());
- while (script_data) {
- Map<StringName, NativeScriptDesc::Method>::Element *E = script_data->methods.find(p_method);
- if (E) {
- switch (E->get().rpc_mode) {
- case GODOT_METHOD_RPC_MODE_DISABLED:
- return MultiplayerAPI::RPC_MODE_DISABLED;
- case GODOT_METHOD_RPC_MODE_REMOTE:
- return MultiplayerAPI::RPC_MODE_REMOTE;
- case GODOT_METHOD_RPC_MODE_MASTER:
- return MultiplayerAPI::RPC_MODE_MASTER;
- case GODOT_METHOD_RPC_MODE_PUPPET:
- return MultiplayerAPI::RPC_MODE_PUPPET;
- case GODOT_METHOD_RPC_MODE_REMOTESYNC:
- return MultiplayerAPI::RPC_MODE_REMOTESYNC;
- case GODOT_METHOD_RPC_MODE_MASTERSYNC:
- return MultiplayerAPI::RPC_MODE_MASTERSYNC;
- case GODOT_METHOD_RPC_MODE_PUPPETSYNC:
- return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
- default:
- return MultiplayerAPI::RPC_MODE_DISABLED;
- }
- }
-
- script_data = script_data->base_data;
- }
-
- return MultiplayerAPI::RPC_MODE_DISABLED;
-}
-
-Vector<ScriptNetData> NativeScript::get_rset_properties() const {
- Vector<ScriptNetData> v;
-
- NativeScriptDesc *script_data = get_script_desc();
-
- while (script_data) {
- for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.front(); E; E = E.next()) {
- if (E.get().rset_mode != GODOT_METHOD_RPC_MODE_DISABLED) {
- ScriptNetData nd;
- nd.name = E.key();
- nd.mode = MultiplayerAPI::RPCMode(E.get().rset_mode);
- v.push_back(nd);
- }
- }
- script_data = script_data->base_data;
- }
-
- return v;
-}
-
-uint16_t NativeScript::get_rset_property_id(const StringName &p_variable) const {
- NativeScriptDesc *script_data = get_script_desc();
-
- while (script_data) {
- OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.find(p_variable);
- if (E) {
- return E.get().rset_property_id;
- }
-
- script_data = script_data->base_data;
- }
-
- return UINT16_MAX;
-}
-
-StringName NativeScript::get_rset_property(uint16_t p_id) const {
- ERR_FAIL_COND_V(p_id == UINT16_MAX, StringName());
-
- NativeScriptDesc *script_data = get_script_desc();
-
- while (script_data) {
- for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.front(); E; E = E.next()) {
- if (E.get().rset_property_id == p_id) {
- return E.key();
- }
- }
-
- script_data = script_data->base_data;
- }
-
- return StringName();
-}
-
-MultiplayerAPI::RPCMode NativeScript::get_rset_mode_by_id(uint16_t p_id) const {
- ERR_FAIL_COND_V(p_id == UINT16_MAX, MultiplayerAPI::RPC_MODE_DISABLED);
-
- NativeScriptDesc *script_data = get_script_desc();
-
- while (script_data) {
- for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.front(); E; E = E.next()) {
- if (E.get().rset_property_id == p_id) {
- switch (E.get().rset_mode) {
- case GODOT_METHOD_RPC_MODE_DISABLED:
- return MultiplayerAPI::RPC_MODE_DISABLED;
- case GODOT_METHOD_RPC_MODE_REMOTE:
- return MultiplayerAPI::RPC_MODE_REMOTE;
- case GODOT_METHOD_RPC_MODE_MASTER:
- return MultiplayerAPI::RPC_MODE_MASTER;
- case GODOT_METHOD_RPC_MODE_PUPPET:
- return MultiplayerAPI::RPC_MODE_PUPPET;
- case GODOT_METHOD_RPC_MODE_REMOTESYNC:
- return MultiplayerAPI::RPC_MODE_REMOTESYNC;
- case GODOT_METHOD_RPC_MODE_MASTERSYNC:
- return MultiplayerAPI::RPC_MODE_MASTERSYNC;
- case GODOT_METHOD_RPC_MODE_PUPPETSYNC:
- return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
- default:
- return MultiplayerAPI::RPC_MODE_DISABLED;
- }
- }
- }
-
- script_data = script_data->base_data;
- }
-
- return MultiplayerAPI::RPC_MODE_DISABLED;
-}
-
-MultiplayerAPI::RPCMode NativeScript::get_rset_mode(const StringName &p_variable) const {
- NativeScriptDesc *script_data = get_script_desc();
-
- while (script_data) {
- OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.find(p_variable);
- if (E) {
- switch (E.get().rset_mode) {
- case GODOT_METHOD_RPC_MODE_DISABLED:
- return MultiplayerAPI::RPC_MODE_DISABLED;
- case GODOT_METHOD_RPC_MODE_REMOTE:
- return MultiplayerAPI::RPC_MODE_REMOTE;
- case GODOT_METHOD_RPC_MODE_MASTER:
- return MultiplayerAPI::RPC_MODE_MASTER;
- case GODOT_METHOD_RPC_MODE_PUPPET:
- return MultiplayerAPI::RPC_MODE_PUPPET;
- case GODOT_METHOD_RPC_MODE_REMOTESYNC:
- return MultiplayerAPI::RPC_MODE_REMOTESYNC;
- case GODOT_METHOD_RPC_MODE_MASTERSYNC:
- return MultiplayerAPI::RPC_MODE_MASTERSYNC;
- case GODOT_METHOD_RPC_MODE_PUPPETSYNC:
- return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
- default:
- return MultiplayerAPI::RPC_MODE_DISABLED;
- }
- }
-
- script_data = script_data->base_data;
- }
-
- return MultiplayerAPI::RPC_MODE_DISABLED;
+ return script_data->rpc_methods;
}
String NativeScript::get_class_documentation() const {
@@ -717,7 +501,7 @@ String NativeScript::get_property_documentation(const StringName &p_path) const
}
Variant NativeScript::_new(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
- if (lib_path.empty() || class_name.empty() || library.is_null()) {
+ if (lib_path.is_empty() || class_name.is_empty() || library.is_null()) {
r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
return Variant();
}
@@ -735,9 +519,9 @@ Variant NativeScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
Object *owner = nullptr;
if (!(script_data->base_native_type == "")) {
- owner = ClassDB::instance(script_data->base_native_type);
+ owner = ClassDB::instantiate(script_data->base_native_type);
} else {
- owner = memnew(Reference);
+ owner = memnew(RefCounted);
}
if (!owner) {
@@ -745,7 +529,7 @@ Variant NativeScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
return Variant();
}
- Reference *r = Object::cast_to<Reference>(owner);
+ RefCounted *r = Object::cast_to<RefCounted>(owner);
if (r) {
ref = REF(r);
}
@@ -1044,46 +828,10 @@ Ref<Script> NativeScriptInstance::get_script() const {
return script;
}
-Vector<ScriptNetData> NativeScriptInstance::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> NativeScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
-uint16_t NativeScriptInstance::get_rpc_method_id(const StringName &p_method) const {
- return script->get_rpc_method_id(p_method);
-}
-
-StringName NativeScriptInstance::get_rpc_method(uint16_t p_id) const {
- return script->get_rpc_method(p_id);
-}
-
-MultiplayerAPI::RPCMode NativeScriptInstance::get_rpc_mode_by_id(uint16_t p_id) const {
- return script->get_rpc_mode_by_id(p_id);
-}
-
-MultiplayerAPI::RPCMode NativeScriptInstance::get_rpc_mode(const StringName &p_method) const {
- return script->get_rpc_mode(p_method);
-}
-
-Vector<ScriptNetData> NativeScriptInstance::get_rset_properties() const {
- return script->get_rset_properties();
-}
-
-uint16_t NativeScriptInstance::get_rset_property_id(const StringName &p_variable) const {
- return script->get_rset_property_id(p_variable);
-}
-
-StringName NativeScriptInstance::get_rset_property(uint16_t p_id) const {
- return script->get_rset_property(p_id);
-}
-
-MultiplayerAPI::RPCMode NativeScriptInstance::get_rset_mode_by_id(uint16_t p_id) const {
- return script->get_rset_mode_by_id(p_id);
-}
-
-MultiplayerAPI::RPCMode NativeScriptInstance::get_rset_mode(const StringName &p_variable) const {
- return script->get_rset_mode(p_variable);
-}
-
ScriptLanguage *NativeScriptInstance::get_language() {
return NativeScriptLanguage::get_singleton();
}
@@ -1109,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);
@@ -1142,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);
}
@@ -1155,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);
@@ -1196,13 +944,6 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) {
NativeScriptLanguage::NativeScriptLanguage() {
NativeScriptLanguage::singleton = this;
-#ifndef NO_THREADS
- has_objects_to_register = false;
-#endif
-
-#ifdef DEBUG_ENABLED
- profiling = false;
-#endif
_init_call_type = "nativescript_init";
_init_call_name = "nativescript_init";
@@ -1216,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
@@ -1255,6 +996,17 @@ void NativeScriptLanguage::init() {
if (generate_c_api(E->next()->get()) != OK) {
ERR_PRINT("Failed to generate C API\n");
}
+ Main::cleanup(true);
+ exit(0);
+ }
+
+ E = args.find("--gdnative-generate-json-builtin-api");
+
+ if (E && E->next()) {
+ if (generate_c_builtin_api(E->next()->get()) != OK) {
+ ERR_PRINT("Failed to generate C builtin API\n");
+ }
+ Main::cleanup(true);
exit(0);
}
#endif
@@ -1283,6 +1035,10 @@ void NativeScriptLanguage::finish() {
void NativeScriptLanguage::get_reserved_words(List<String> *p_words) const {
}
+bool NativeScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+ return false;
+}
+
void NativeScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
}
@@ -1295,7 +1051,7 @@ Ref<Script> NativeScriptLanguage::get_template(const String &p_class_name, const
return Ref<NativeScript>(s);
}
-bool NativeScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
+bool NativeScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
return true;
}
@@ -1383,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
}
@@ -1401,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++;
}
@@ -1425,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++;
}
}
@@ -1518,6 +1271,8 @@ void NativeScriptLanguage::unregister_binding_functions(int p_idx) {
}
void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_object) {
+ return nullptr;
+#if 0
ERR_FAIL_INDEX_V(p_idx, binding_functions.size(), nullptr);
ERR_FAIL_COND_V_MSG(!binding_functions[p_idx].first, nullptr, "Tried to get binding data for a nativescript binding that does not exist.");
@@ -1547,9 +1302,12 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec
}
return (*binding_data)[p_idx];
+#endif
}
void *NativeScriptLanguage::alloc_instance_binding_data(Object *p_object) {
+ return nullptr;
+#if 0
Vector<void *> *binding_data = new Vector<void *>;
binding_data->resize(binding_functions.size());
@@ -1561,9 +1319,11 @@ void *NativeScriptLanguage::alloc_instance_binding_data(Object *p_object) {
binding_instances.insert(binding_data);
return (void *)binding_data;
+#endif
}
void NativeScriptLanguage::free_instance_binding_data(void *p_data) {
+#if 0
if (!p_data) {
return;
}
@@ -1583,9 +1343,11 @@ void NativeScriptLanguage::free_instance_binding_data(void *p_data) {
binding_instances.erase(&binding_data);
delete &binding_data;
+#endif
}
void NativeScriptLanguage::refcount_incremented_instance_binding(Object *p_object) {
+#if 0
void *data = p_object->get_script_instance_binding(lang_idx);
if (!data) {
@@ -1607,9 +1369,11 @@ void NativeScriptLanguage::refcount_incremented_instance_binding(Object *p_objec
binding_functions[i].second.refcount_incremented_instance_binding(binding_data[i], p_object);
}
}
+#endif
}
bool NativeScriptLanguage::refcount_decremented_instance_binding(Object *p_object) {
+#if 0
void *data = p_object->get_script_instance_binding(lang_idx);
if (!data) {
@@ -1635,6 +1399,8 @@ bool NativeScriptLanguage::refcount_decremented_instance_binding(Object *p_objec
}
return can_die;
+#endif
+ return false;
}
void NativeScriptLanguage::set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag) {
@@ -1668,7 +1434,7 @@ void NativeScriptLanguage::defer_init_library(Ref<GDNativeLibrary> lib, NativeSc
MutexLock lock(mutex);
libs_to_init.insert(lib);
scripts_to_register.insert(script);
- has_objects_to_register = true;
+ has_objects_to_register.set();
}
#endif
@@ -1682,7 +1448,7 @@ void NativeScriptLanguage::init_library(const Ref<GDNativeLibrary> &lib) {
if (!E) {
Ref<GDNative> gdn;
- gdn.instance();
+ gdn.instantiate();
gdn->set_library(lib);
// TODO check the return value?
@@ -1724,6 +1490,52 @@ void NativeScriptLanguage::unregister_script(NativeScript *script) {
S->get().erase(script);
if (S->get().size() == 0) {
library_script_users.erase(S);
+
+ Map<String, Ref<GDNative>>::Element *G = library_gdnatives.find(script->lib_path);
+ if (G && G->get()->get_library()->is_reloadable()) {
+ // ONLY if the library is marked as reloadable, and no more instances of its scripts exist do we unload the library
+
+ // First remove meta data related to the library
+ Map<String, Map<StringName, NativeScriptDesc>>::Element *L = library_classes.find(script->lib_path);
+ if (L) {
+ Map<StringName, NativeScriptDesc> classes = L->get();
+
+ for (KeyValue<StringName, NativeScriptDesc> &C : classes) {
+ // free property stuff first
+ 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);
+ }
+
+ if (P.get().setter.free_func) {
+ P.get().setter.free_func(P.get().setter.method_data);
+ }
+ }
+
+ // free method stuff
+ 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.value.create_func.free_func) {
+ C.value.create_func.free_func(C.value.create_func.method_data);
+ }
+
+ if (C.value.destroy_func.free_func) {
+ C.value.destroy_func.free_func(C.value.destroy_func.method_data);
+ }
+ }
+
+ library_classes.erase(script->lib_path);
+ }
+
+ // now unload the library
+ G->get()->terminate();
+ library_gdnatives.erase(G);
+ }
}
}
#ifndef NO_THREADS
@@ -1733,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)();
@@ -1751,7 +1563,7 @@ void NativeScriptLanguage::call_libraries_cb(const StringName &name) {
void NativeScriptLanguage::frame() {
#ifndef NO_THREADS
- if (has_objects_to_register) {
+ if (has_objects_to_register.is_set()) {
MutexLock lock(mutex);
for (Set<Ref<GDNativeLibrary>>::Element *L = libs_to_init.front(); L; L = L->next()) {
init_library(L->get());
@@ -1761,7 +1573,7 @@ void NativeScriptLanguage::frame() {
register_script(S->get());
}
scripts_to_register.clear();
- has_objects_to_register = false;
+ has_objects_to_register.clear();
}
#endif
@@ -1769,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
@@ -1800,7 +1612,7 @@ bool NativeScriptLanguage::handles_global_class_type(const String &p_type) const
}
String NativeScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
- if (!p_path.empty()) {
+ if (!p_path.is_empty()) {
Ref<NativeScript> script = ResourceLoader::load(p_path, "NativeScript");
if (script.is_valid()) {
if (r_base_type) {
@@ -1836,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;
@@ -1870,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;
@@ -1888,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) {
@@ -1932,7 +1744,7 @@ void NativeReloadNode::_notification(int p_what) {
#endif
}
-RES ResourceFormatLoaderNativeScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderNativeScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_no_cache) {
return ResourceFormatLoaderText::singleton->load(p_path, p_original_path, r_error);
}
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 145bf7dcb6..2364c6c0f6 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,15 +31,17 @@
#ifndef NATIVE_SCRIPT_H
#define NATIVE_SCRIPT_H
+#include "core/doc_data.h"
+#include "core/io/resource.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/oa_hash_map.h"
-#include "core/ordered_hash_map.h"
+#include "core/object/script_language.h"
#include "core/os/mutex.h"
#include "core/os/thread_safe.h"
-#include "core/resource.h"
-#include "core/script_language.h"
-#include "core/self_list.h"
+#include "core/templates/oa_hash_map.h"
+#include "core/templates/ordered_hash_map.h"
+#include "core/templates/safe_refcount.h"
+#include "core/templates/self_list.h"
#include "scene/main/node.h"
#include "modules/gdnative/gdnative.h"
@@ -50,8 +52,8 @@ struct NativeScriptDesc {
struct Method {
godot_nativescript_instance_method method;
MethodInfo info;
- int rpc_mode;
- uint16_t rpc_method_id;
+ int rpc_mode = 0;
+ uint16_t rpc_method_id = 0;
String documentation;
};
@@ -60,8 +62,6 @@ struct NativeScriptDesc {
godot_nativescript_property_get_func getter;
PropertyInfo info;
Variant default_value;
- int rset_mode;
- uint16_t rset_property_id;
String documentation;
};
@@ -70,14 +70,13 @@ struct NativeScriptDesc {
String documentation;
};
- uint16_t rpc_count = 0;
Map<StringName, Method> methods;
- uint16_t rset_count = 0;
+ Vector<Multiplayer::RPCConfig> rpc_methods;
OrderedHashMap<StringName, Property> properties;
Map<StringName, Signal> signals_; // QtCreator doesn't like the name signals
StringName base;
StringName base_native_type;
- NativeScriptDesc *base_data;
+ NativeScriptDesc *base_data = nullptr;
godot_nativescript_instance_create_func create_func;
godot_nativescript_instance_destroy_func destroy_func;
@@ -85,11 +84,11 @@ struct NativeScriptDesc {
const void *type_tag = nullptr;
- bool is_tool;
+ bool is_tool = false;
inline NativeScriptDesc() {
- zeromem(&create_func, sizeof(godot_nativescript_instance_create_func));
- zeromem(&destroy_func, sizeof(godot_nativescript_instance_destroy_func));
+ memset(&create_func, 0, sizeof(godot_nativescript_instance_create_func));
+ memset(&destroy_func, 0, sizeof(godot_nativescript_instance_destroy_func));
}
};
@@ -138,7 +137,7 @@ public:
void set_script_class_icon_path(String p_icon_path);
String get_script_class_icon_path() const;
- virtual bool can_instance() const override;
+ virtual bool can_instantiate() const override;
virtual Ref<Script> get_base_script() const override; //for script inheritance
@@ -152,6 +151,13 @@ public:
virtual void set_source_code(const String &p_code) override;
virtual Error reload(bool p_keep_state = false) override;
+#ifdef TOOLS_ENABLED
+ virtual const Vector<DocData::ClassDoc> &get_documentation() const override {
+ static Vector<DocData::ClassDoc> docs;
+ return docs;
+ }
+#endif // TOOLS_ENABLED
+
virtual bool has_method(const StringName &p_method) const override;
virtual MethodInfo get_method_info(const StringName &p_method) const override;
@@ -169,17 +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 Vector<ScriptNetData> get_rpc_methods() const override;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const override;
- virtual StringName get_rpc_method(uint16_t p_id) const override;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const override;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
-
- virtual Vector<ScriptNetData> get_rset_properties() const override;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const override;
- virtual StringName get_rset_property(uint16_t p_id) const override;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const override;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) 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;
@@ -217,17 +213,7 @@ public:
String to_string(bool *r_valid);
virtual Ref<Script> get_script() const;
- virtual Vector<ScriptNetData> get_rpc_methods() const;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
-
- virtual Vector<ScriptNetData> get_rset_properties() const;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
- virtual StringName get_rset_property(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
virtual ScriptLanguage *get_language();
@@ -246,7 +232,7 @@ class NativeScriptLanguage : public ScriptLanguage {
private:
static NativeScriptLanguage *singleton;
- int lang_idx;
+ int lang_idx = 0;
void _unload_stuff(bool p_reload = false);
@@ -254,7 +240,7 @@ private:
#ifndef NO_THREADS
Set<Ref<GDNativeLibrary>> libs_to_init;
Set<NativeScript *> scripts_to_register;
- volatile bool has_objects_to_register; // so that we don't lock mutex every frame - it's rarely needed
+ SafeFlag has_objects_to_register; // so that we don't lock mutex every frame - it's rarely needed
void defer_init_library(Ref<GDNativeLibrary> lib, NativeScript *script);
#endif
@@ -271,19 +257,18 @@ private:
struct ProfileData {
StringName signature;
- uint64_t call_count;
- uint64_t self_time;
- uint64_t total_time;
- uint64_t frame_call_count;
- uint64_t frame_self_time;
- uint64_t frame_total_time;
- uint64_t last_frame_call_count;
- uint64_t last_frame_self_time;
- uint64_t last_frame_total_time;
+ uint64_t call_count = 0;
+ uint64_t self_time = 0;
+ uint64_t total_time = 0;
+ uint64_t frame_call_count = 0;
+ uint64_t frame_self_time = 0;
+ uint64_t frame_total_time = 0;
+ uint64_t last_frame_call_count = 0;
+ uint64_t last_frame_self_time = 0;
+ uint64_t last_frame_total_time = 0;
};
Map<StringName, ProfileData> profile_data;
- bool profiling;
public:
// These two maps must only be touched on the main thread
@@ -309,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
@@ -327,10 +310,11 @@ public:
virtual Error execute_file(const String &p_path);
virtual void finish();
virtual void get_reserved_words(List<String> *p_words) const;
+ virtual bool is_control_flow_keyword(String p_keyword) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const;
+ virtual bool validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
@@ -394,7 +378,7 @@ public:
class ResourceFormatLoaderNativeScript : 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, bool p_no_cache = false);
+ 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;
diff --git a/modules/gdnative/nativescript/register_types.cpp b/modules/gdnative/nativescript/register_types.cpp
index ac8c7ab2fd..82a3459517 100644
--- a/modules/gdnative/nativescript/register_types.cpp
+++ b/modules/gdnative/nativescript/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -45,15 +45,15 @@ Ref<ResourceFormatSaverNativeScript> resource_saver_gdns;
void register_nativescript_types() {
native_script_language = memnew(NativeScriptLanguage);
- ClassDB::register_class<NativeScript>();
+ GDREGISTER_CLASS(NativeScript);
native_script_language->set_language_index(ScriptServer::get_language_count());
ScriptServer::register_language(native_script_language);
- resource_saver_gdns.instance();
+ resource_saver_gdns.instantiate();
ResourceSaver::add_resource_format_saver(resource_saver_gdns);
- resource_loader_gdns.instance();
+ resource_loader_gdns.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_gdns);
}
diff --git a/modules/gdnative/nativescript/register_types.h b/modules/gdnative/nativescript/register_types.h
index 088bf38dd5..d12ac9eda3 100644
--- a/modules/gdnative/nativescript/register_types.h
+++ b/modules/gdnative/nativescript/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
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 997eec6425..0000000000
--- a/modules/gdnative/net/multiplayer_peer_gdnative.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*************************************************************************/
-/* multiplayer_peer_gdnative.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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);
-}
-
-/* NetworkedMultiplayerPeer */
-void MultiplayerPeerGDNative::set_transfer_mode(TransferMode p_mode) {
- ERR_FAIL_COND(interface == nullptr);
- interface->set_transfer_mode(interface->data, (godot_int)p_mode);
-}
-
-NetworkedMultiplayerPeer::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);
-}
-
-NetworkedMultiplayerPeer::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_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/stream_peer_gdnative.cpp b/modules/gdnative/net/stream_peer_gdnative.cpp
deleted file mode 100644
index 9dcb184115..0000000000
--- a/modules/gdnative/net/stream_peer_gdnative.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*************************************************************************/
-/* stream_peer_gdnative.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "stream_peer_gdnative.h"
-
-StreamPeerGDNative::StreamPeerGDNative() {
- interface = nullptr;
-}
-
-StreamPeerGDNative::~StreamPeerGDNative() {
-}
-
-void StreamPeerGDNative::set_native_stream_peer(const godot_net_stream_peer *p_interface) {
- interface = p_interface;
-}
-
-void StreamPeerGDNative::_bind_methods() {
-}
-
-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));
-}
-
-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));
-}
-
-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));
-}
-
-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));
-}
-
-int StreamPeerGDNative::get_available_bytes() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_available_bytes(interface->data);
-}
-
-extern "C" {
-
-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);
-}
-}
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.cpp b/modules/gdnative/pluginscript/pluginscript_instance.cpp
index 7d17a7d5ab..feae81397e 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_instance.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
// Godot imports
#include "core/os/os.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
// PluginScript imports
#include "pluginscript_language.h"
@@ -93,44 +93,15 @@ void PluginScriptInstance::notification(int p_notification) {
_desc->notification(_data, p_notification);
}
-Vector<ScriptNetData> PluginScriptInstance::get_rpc_methods() const {
- return _script->get_rpc_methods();
-}
-
-uint16_t PluginScriptInstance::get_rpc_method_id(const StringName &p_variable) const {
- return _script->get_rpc_method_id(p_variable);
-}
-
-StringName PluginScriptInstance::get_rpc_method(uint16_t p_id) const {
- return _script->get_rpc_method(p_id);
-}
-
-MultiplayerAPI::RPCMode PluginScriptInstance::get_rpc_mode_by_id(uint16_t p_id) const {
- return _script->get_rpc_mode_by_id(p_id);
-}
-
-MultiplayerAPI::RPCMode PluginScriptInstance::get_rpc_mode(const StringName &p_method) const {
- return _script->get_rpc_mode(p_method);
+String PluginScriptInstance::to_string(bool *r_valid) {
+ godot_string ret = _desc->to_string(_data, r_valid);
+ String str_ret = *(String *)&ret;
+ godot_string_destroy(&ret);
+ return str_ret;
}
-Vector<ScriptNetData> PluginScriptInstance::get_rset_properties() const {
- return _script->get_rset_properties();
-}
-
-uint16_t PluginScriptInstance::get_rset_property_id(const StringName &p_variable) const {
- return _script->get_rset_property_id(p_variable);
-}
-
-StringName PluginScriptInstance::get_rset_property(uint16_t p_id) const {
- return _script->get_rset_property(p_id);
-}
-
-MultiplayerAPI::RPCMode PluginScriptInstance::get_rset_mode_by_id(uint16_t p_id) const {
- return _script->get_rset_mode_by_id(p_id);
-}
-
-MultiplayerAPI::RPCMode PluginScriptInstance::get_rset_mode(const StringName &p_variable) const {
- return _script->get_rset_mode(p_variable);
+const Vector<Multiplayer::RPCConfig> PluginScriptInstance::get_rpc_methods() const {
+ return _script->get_rpc_methods();
}
void PluginScriptInstance::refcount_incremented() {
diff --git a/modules/gdnative/pluginscript/pluginscript_instance.h b/modules/gdnative/pluginscript/pluginscript_instance.h
index 690d1a0432..81e711bafc 100644
--- a/modules/gdnative/pluginscript/pluginscript_instance.h
+++ b/modules/gdnative/pluginscript/pluginscript_instance.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define PLUGINSCRIPT_INSTANCE_H
// Godot imports
-#include "core/script_language.h"
+#include "core/object/script_language.h"
// PluginScript imports
#include <pluginscript/godot_pluginscript.h>
@@ -44,10 +44,10 @@ class PluginScriptInstance : public ScriptInstance {
private:
Ref<PluginScript> _script;
- Object *_owner;
+ Object *_owner = nullptr;
Variant _owner_variant;
- godot_pluginscript_instance_data *_data;
- const godot_pluginscript_instance_desc *_desc;
+ godot_pluginscript_instance_data *_data = nullptr;
+ const godot_pluginscript_instance_desc *_desc = nullptr;
public:
_FORCE_INLINE_ Object *get_owner() { return _owner; }
@@ -63,24 +63,13 @@ public:
virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
virtual void notification(int p_notification);
+ virtual String to_string(bool *r_valid);
virtual Ref<Script> get_script() const;
virtual ScriptLanguage *get_language();
- void set_path(const String &p_path);
-
- virtual Vector<ScriptNetData> get_rpc_methods() const;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
-
- virtual Vector<ScriptNetData> get_rset_properties() const;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
- virtual StringName get_rset_property(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(uint16_t p_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) 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_language.cpp b/modules/gdnative/pluginscript/pluginscript_language.cpp
index bccbe95033..79aba342c9 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_language.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,9 +29,9 @@
/*************************************************************************/
// Godot imports
-#include "core/os/file_access.h"
+#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
// PluginScript imports
#include "pluginscript_language.h"
#include "pluginscript_script.h"
@@ -77,6 +77,10 @@ void PluginScriptLanguage::get_reserved_words(List<String> *p_words) const {
}
}
+bool PluginScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+ return false;
+}
+
void PluginScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
if (_desc.comment_delimiters) {
const char **w = _desc.comment_delimiters;
@@ -108,20 +112,29 @@ Ref<Script> PluginScriptLanguage::get_template(const String &p_class_name, const
return script;
}
-bool PluginScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
+bool PluginScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
PackedStringArray functions;
+ Array errors;
if (_desc.validate) {
bool ret = _desc.validate(
_data,
(godot_string *)&p_script,
- &r_line_error,
- &r_col_error,
- (godot_string *)&r_test_error,
(godot_string *)&p_path,
- (godot_packed_string_array *)&functions);
+ (godot_packed_string_array *)&functions,
+ (godot_array *)&errors);
for (int i = 0; i < functions.size(); i++) {
r_functions->push_back(functions[i]);
}
+ if (r_errors) {
+ for (int i = 0; i < errors.size(); i++) {
+ Dictionary error = errors[i];
+ ScriptLanguage::ScriptError e;
+ e.line = error["line"];
+ e.column = error["column"];
+ e.message = error["message"];
+ r_errors->push_back(e);
+ }
+ }
return ret;
}
return true;
@@ -142,6 +155,10 @@ bool PluginScriptLanguage::supports_builtin_mode() const {
return _desc.supports_builtin_mode;
}
+bool PluginScriptLanguage::can_inherit_from_file() const {
+ return _desc.can_inherit_from_file;
+}
+
int PluginScriptLanguage::find_function(const String &p_function, const String &p_code) const {
if (_desc.find_function) {
return _desc.find_function(_data, (godot_string *)&p_function, (godot_string *)&p_code);
@@ -398,6 +415,32 @@ void PluginScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool
#endif
}
+bool PluginScriptLanguage::handles_global_class_type(const String &p_type) const {
+ return p_type == "PluginScript";
+}
+
+String PluginScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const {
+ if (!p_path.is_empty()) {
+ Ref<PluginScript> script = ResourceLoader::load(p_path, "PluginScript");
+ if (script.is_valid()) {
+ if (r_base_type) {
+ *r_base_type = script->get_instance_base_type();
+ }
+ if (r_icon_path) {
+ *r_icon_path = script->get_script_class_icon_path();
+ }
+ return script->get_script_class_name();
+ }
+ if (r_base_type) {
+ *r_base_type = String();
+ }
+ if (r_icon_path) {
+ *r_icon_path = String();
+ }
+ }
+ return String();
+}
+
void PluginScriptLanguage::lock() {
_lock.lock();
}
diff --git a/modules/gdnative/pluginscript/pluginscript_language.h b/modules/gdnative/pluginscript/pluginscript_language.h
index dd6758713f..26ab4a95e3 100644
--- a/modules/gdnative/pluginscript/pluginscript_language.h
+++ b/modules/gdnative/pluginscript/pluginscript_language.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,9 +34,9 @@
// Godot imports
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/map.h"
-#include "core/script_language.h"
-#include "core/self_list.h"
+#include "core/object/script_language.h"
+#include "core/templates/map.h"
+#include "core/templates/self_list.h"
// PluginScript imports
#include "pluginscript_loader.h"
#include <pluginscript/godot_pluginscript.h>
@@ -71,14 +71,15 @@ public:
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const;
+ virtual bool is_control_flow_keyword(String p_keyword) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const;
+ virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
- virtual bool can_inherit_from_file() { return true; }
+ virtual bool can_inherit_from_file() const;
virtual int find_function(const String &p_function, const String &p_code) const;
virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const;
virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_force, String &r_call_hint);
@@ -122,6 +123,11 @@ public:
virtual void frame();
+ /* GLOBAL CLASSES */
+
+ virtual bool handles_global_class_type(const String &p_type) const;
+ virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const;
+
void lock();
void unlock();
diff --git a/modules/gdnative/pluginscript/pluginscript_loader.cpp b/modules/gdnative/pluginscript/pluginscript_loader.cpp
index 4feee4f4a5..462452a897 100644
--- a/modules/gdnative/pluginscript/pluginscript_loader.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_loader.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,7 +29,7 @@
/*************************************************************************/
// Godot imports
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
// Pythonscript imports
#include "pluginscript_language.h"
#include "pluginscript_loader.h"
@@ -39,7 +39,7 @@ ResourceFormatLoaderPluginScript::ResourceFormatLoaderPluginScript(PluginScriptL
_language = language;
}
-RES ResourceFormatLoaderPluginScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderPluginScript::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) {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
diff --git a/modules/gdnative/pluginscript/pluginscript_loader.h b/modules/gdnative/pluginscript/pluginscript_loader.h
index 35fc79c2ca..e5d665c186 100644
--- a/modules/gdnative/pluginscript/pluginscript_loader.h
+++ b/modules/gdnative/pluginscript/pluginscript_loader.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,7 +34,7 @@
// Godot imports
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/script_language.h"
+#include "core/object/script_language.h"
class PluginScriptLanguage;
@@ -43,7 +43,7 @@ class ResourceFormatLoaderPluginScript : public ResourceFormatLoader {
public:
ResourceFormatLoaderPluginScript(PluginScriptLanguage *language);
- 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, bool p_no_cache = false);
+ 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;
diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp
index 87c6288806..04a293ddbd 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_script.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,7 +29,7 @@
/*************************************************************************/
// Godot imports
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
// PluginScript imports
#include "pluginscript_instance.h"
#include "pluginscript_script.h"
@@ -38,13 +38,13 @@
#ifdef DEBUG_ENABLED
#define __ASSERT_SCRIPT_REASON "Cannot retrieve PluginScript class for this script, is your code correct?"
-#define ASSERT_SCRIPT_VALID() \
- { \
- ERR_FAIL_COND_MSG(!can_instance(), __ASSERT_SCRIPT_REASON); \
+#define ASSERT_SCRIPT_VALID() \
+ { \
+ ERR_FAIL_COND_MSG(!can_instantiate(), __ASSERT_SCRIPT_REASON); \
}
-#define ASSERT_SCRIPT_VALID_V(ret) \
- { \
- ERR_FAIL_COND_V_MSG(!can_instance(), ret, __ASSERT_SCRIPT_REASON); \
+#define ASSERT_SCRIPT_VALID_V(ret) \
+ { \
+ ERR_FAIL_COND_V_MSG(!can_instantiate(), ret, __ASSERT_SCRIPT_REASON); \
}
#else
#define ASSERT_SCRIPT_VALID()
@@ -94,9 +94,9 @@ Variant PluginScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
Object *owner = nullptr;
if (get_instance_base_type() == "") {
- owner = memnew(Reference);
+ owner = memnew(RefCounted);
} else {
- owner = ClassDB::instance(get_instance_base_type());
+ owner = ClassDB::instantiate(get_instance_base_type());
}
if (!owner) {
@@ -104,7 +104,7 @@ Variant PluginScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
return Variant();
}
- Reference *r = Object::cast_to<Reference>(owner);
+ RefCounted *r = Object::cast_to<RefCounted>(owner);
if (r) {
ref = REF(r);
}
@@ -133,15 +133,26 @@ void PluginScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder)
#endif
-bool PluginScript::can_instance() const {
+bool PluginScript::can_instantiate() const {
bool can = _valid || (!_tool && !ScriptServer::is_scripting_enabled());
return can;
}
bool PluginScript::inherits_script(const Ref<Script> &p_script) const {
-#ifndef _MSC_VER
-#warning inheritance needs to be implemented in PluginScript
-#endif
+ Ref<PluginScript> ps = p_script;
+ if (ps.is_null()) {
+ return false;
+ }
+
+ const PluginScript *s = this;
+
+ while (s) {
+ if (s == p_script.ptr()) {
+ return true;
+ }
+ s = Object::cast_to<PluginScript>(s->_ref_base_parent.ptr());
+ }
+
return false;
}
@@ -198,7 +209,7 @@ ScriptInstance *PluginScript::instance_create(Object *p_this) {
StringName base_type = get_instance_base_type();
if (base_type) {
if (!ClassDB::is_parent_class(p_this->get_class_name(), base_type)) {
- String msg = "Script inherits from native type '" + String(base_type) + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'";
+ String msg = "Script inherits from native type '" + String(base_type) + "', so it can't be instantiated in object of type: '" + p_this->get_class() + "'";
// TODO: implement PluginscriptLanguage::debug_break_parse
// if (EngineDebugger::is_active()) {
// _language->debug_break_parse(get_path(), 0, msg);
@@ -212,6 +223,8 @@ ScriptInstance *PluginScript::instance_create(Object *p_this) {
}
bool PluginScript::instance_has(const Object *p_this) const {
+ ERR_FAIL_COND_V(!_language, false);
+
_language->lock();
bool hasit = _instances.has((Object *)p_this);
_language->unlock();
@@ -302,6 +315,7 @@ Error PluginScript::reload(bool p_keep_state) {
_data = manifest.data;
_name = *(StringName *)&manifest.name;
_tool = manifest.is_tool;
+ _icon_path = *(String *)&manifest.icon_path;
Dictionary *members = (Dictionary *)&manifest.member_lines;
for (const Variant *key = members->next(); key != nullptr; key = members->next(key)) {
@@ -309,10 +323,9 @@ Error PluginScript::reload(bool p_keep_state) {
}
Array *methods = (Array *)&manifest.methods;
_rpc_methods.clear();
- _rpc_variables.clear();
if (_ref_base_parent.is_valid()) {
+ /// XXX TODO Should this be _rpc_methods.append_array(...)
_rpc_methods = _ref_base_parent->get_rpc_methods();
- _rpc_variables = _ref_base_parent->get_rset_properties();
}
for (int i = 0; i < methods->size(); ++i) {
Dictionary v = (*methods)[i];
@@ -321,9 +334,10 @@ 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()) {
- ScriptNetData nd;
+ Multiplayer::RPCConfig nd;
nd.name = mi.name;
- nd.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);
}
@@ -331,7 +345,7 @@ Error PluginScript::reload(bool p_keep_state) {
}
// Sort so we are 100% that they are always the same.
- _rpc_methods.sort_custom<SortNetData>();
+ _rpc_methods.sort_custom<Multiplayer::SortRPCConfig>();
Array *signals = (Array *)&manifest.signals;
for (int i = 0; i < signals->size(); ++i) {
@@ -345,28 +359,8 @@ Error PluginScript::reload(bool p_keep_state) {
PropertyInfo pi = PropertyInfo::from_dict(v);
_properties_info[pi.name] = pi;
_properties_default_values[pi.name] = v["default_value"];
- // rset_mode is passed as an optional field and is not part of PropertyInfo
- Variant var = v["rset_mode"];
- if (var != Variant()) {
- ScriptNetData nd;
- nd.name = pi.name;
- nd.mode = MultiplayerAPI::RPCMode(int(var));
- if (_rpc_variables.find(nd) == -1) {
- _rpc_variables.push_back(nd);
- }
- }
}
- // Sort so we are 100% that they are always the same.
- _rpc_variables.sort_custom<SortNetData>();
-
-#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
@@ -440,10 +434,10 @@ Error PluginScript::load_source_code(const String &p_path) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + p_path + "'.");
- int len = f->get_len();
+ uint64_t len = f->get_length();
sourcef.resize(len + 1);
uint8_t *w = sourcef.ptrw();
- int r = f->get_buffer(w, len);
+ uint64_t r = f->get_buffer(w, len);
f->close();
memdelete(f);
ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN);
@@ -483,76 +477,10 @@ int PluginScript::get_member_line(const StringName &p_member) const {
return -1;
}
-Vector<ScriptNetData> PluginScript::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> PluginScript::get_rpc_methods() const {
return _rpc_methods;
}
-uint16_t PluginScript::get_rpc_method_id(const StringName &p_method) const {
- ASSERT_SCRIPT_VALID_V(UINT16_MAX);
- for (int i = 0; i < _rpc_methods.size(); i++) {
- if (_rpc_methods[i].name == p_method) {
- return i;
- }
- }
- return UINT16_MAX;
-}
-
-StringName PluginScript::get_rpc_method(const uint16_t p_rpc_method_id) const {
- ASSERT_SCRIPT_VALID_V(StringName());
- if (p_rpc_method_id >= _rpc_methods.size()) {
- return StringName();
- }
- return _rpc_methods[p_rpc_method_id].name;
-}
-
-MultiplayerAPI::RPCMode PluginScript::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
- ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
- if (p_rpc_method_id >= _rpc_methods.size()) {
- return MultiplayerAPI::RPC_MODE_DISABLED;
- }
- return _rpc_methods[p_rpc_method_id].mode;
-}
-
-MultiplayerAPI::RPCMode PluginScript::get_rpc_mode(const StringName &p_method) const {
- ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
- return get_rpc_mode_by_id(get_rpc_method_id(p_method));
-}
-
-Vector<ScriptNetData> PluginScript::get_rset_properties() const {
- return _rpc_variables;
-}
-
-uint16_t PluginScript::get_rset_property_id(const StringName &p_property) const {
- ASSERT_SCRIPT_VALID_V(UINT16_MAX);
- for (int i = 0; i < _rpc_variables.size(); i++) {
- if (_rpc_variables[i].name == p_property) {
- return i;
- }
- }
- return UINT16_MAX;
-}
-
-StringName PluginScript::get_rset_property(const uint16_t p_rset_property_id) const {
- ASSERT_SCRIPT_VALID_V(StringName());
- if (p_rset_property_id >= _rpc_variables.size()) {
- return StringName();
- }
- return _rpc_variables[p_rset_property_id].name;
-}
-
-MultiplayerAPI::RPCMode PluginScript::get_rset_mode_by_id(const uint16_t p_rset_property_id) const {
- ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
- if (p_rset_property_id >= _rpc_variables.size()) {
- return MultiplayerAPI::RPC_MODE_DISABLED;
- }
- return _rpc_variables[p_rset_property_id].mode;
-}
-
-MultiplayerAPI::RPCMode PluginScript::get_rset_mode(const StringName &p_variable) const {
- ASSERT_SCRIPT_VALID_V(MultiplayerAPI::RPC_MODE_DISABLED);
- return get_rset_mode_by_id(get_rset_property_id(p_variable));
-}
-
PluginScript::PluginScript() :
_script_list(this) {
}
diff --git a/modules/gdnative/pluginscript/pluginscript_script.h b/modules/gdnative/pluginscript/pluginscript_script.h
index 9cd38cd4b4..1a12a130d1 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.h
+++ b/modules/gdnative/pluginscript/pluginscript_script.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,9 @@
#define PLUGINSCRIPT_SCRIPT_H
// Godot imports
-#include "core/script_language.h"
+
+#include "core/doc_data.h"
+#include "core/object/script_language.h"
// PluginScript imports
#include "pluginscript_language.h"
#include <pluginscript/godot_pluginscript.h>
@@ -59,14 +61,14 @@ private:
Map<StringName, PropertyInfo> _properties_info;
Map<StringName, MethodInfo> _signals_info;
Map<StringName, MethodInfo> _methods_info;
- Vector<ScriptNetData> _rpc_methods;
- Vector<ScriptNetData> _rpc_variables;
+ Vector<Multiplayer::RPCConfig> _rpc_methods;
Set<Object *> _instances;
//exported members
String _source;
String _path;
StringName _name;
+ String _icon_path;
protected:
static void _bind_methods();
@@ -82,7 +84,15 @@ protected:
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
#endif
public:
- virtual bool can_instance() const override;
+ String get_script_class_name() const {
+ return _name;
+ }
+
+ String get_script_class_icon_path() const {
+ return _icon_path;
+ }
+
+ virtual bool can_instantiate() const override;
virtual Ref<Script> get_base_script() const override; //for script inheritance
@@ -97,6 +107,13 @@ public:
// TODO: load_source_code only allow utf-8 file, should handle bytecode as well ?
virtual Error load_source_code(const String &p_path);
+#ifdef TOOLS_ENABLED
+ virtual const Vector<DocData::ClassDoc> &get_documentation() const override {
+ static Vector<DocData::ClassDoc> docs;
+ return docs;
+ }
+#endif // TOOLS_ENABLED
+
virtual bool has_method(const StringName &p_method) const override;
virtual MethodInfo get_method_info(const StringName &p_method) const override;
@@ -119,17 +136,7 @@ public:
virtual int get_member_line(const StringName &p_member) const override;
- virtual Vector<ScriptNetData> get_rpc_methods() const override;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const override;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const override;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const override;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
-
- virtual Vector<ScriptNetData> get_rset_properties() const override;
- virtual uint16_t get_rset_property_id(const StringName &p_property) const override;
- virtual StringName get_rset_property(const uint16_t p_rset_property_id) const override;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const override;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) 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 97a48b0e89..7faacfdcb9 100644
--- a/modules/gdnative/pluginscript/register_types.cpp
+++ b/modules/gdnative/pluginscript/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,11 +30,11 @@
#include "register_types.h"
+#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/os/dir_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "scene/main/scene_tree.h"
#include "pluginscript_language.h"
@@ -107,7 +107,7 @@ void GDAPI godot_pluginscript_register_language(const godot_pluginscript_languag
}
void register_pluginscript_types() {
- ClassDB::register_class<PluginScript>();
+ GDREGISTER_CLASS(PluginScript);
}
void unregister_pluginscript_types() {
diff --git a/modules/gdnative/pluginscript/register_types.h b/modules/gdnative/pluginscript/register_types.h
index c6a64b4f40..2118f668e9 100644
--- a/modules/gdnative/pluginscript/register_types.h
+++ b/modules/gdnative/pluginscript/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp
index 3a2d0b09a3..a4ab5663ef 100644
--- a/modules/gdnative/register_types.cpp
+++ b/modules/gdnative/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,16 +35,14 @@
#include "gdnative.h"
#include "nativescript/register_types.h"
-#include "net/register_types.h"
#include "pluginscript/register_types.h"
#include "videodecoder/register_types.h"
-#include "xr/register_types.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_export.h"
@@ -79,9 +77,7 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty
List<String> entry_keys;
config->get_section_keys("entry", &entry_keys);
- for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) {
- String key = E->get();
-
+ for (const String &key : entry_keys) {
Vector<String> tags = key.split(".");
bool skip = false;
@@ -112,9 +108,7 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty
List<String> dependency_keys;
config->get_section_keys("dependencies", &dependency_keys);
- for (List<String>::Element *E = dependency_keys.front(); E; E = E->next()) {
- String key = E->get();
-
+ for (const String &key : dependency_keys) {
Vector<String> tags = key.split(".");
bool skip = false;
@@ -142,16 +136,14 @@ void GDNativeExportPlugin::_export_file(const String &p_path, const String &p_ty
}
}
- // Add symbols for staticaly linked libraries on iOS
+ // Add symbols for statically linked libraries on iOS
if (p_features.has("iOS")) {
bool should_fake_dynamic = false;
List<String> entry_keys;
config->get_section_keys("entry", &entry_keys);
- for (List<String>::Element *E = entry_keys.front(); E; E = E->next()) {
- String key = E->get();
-
+ for (const String &key : entry_keys) {
Vector<String> tags = key.split(".");
bool skip = false;
@@ -230,7 +222,7 @@ static void editor_init_callback() {
ProjectSettingsEditor::get_singleton()->get_tabs()->add_child(library_editor);
Ref<GDNativeExportPlugin> export_plugin;
- export_plugin.instance();
+ export_plugin.instantiate();
EditorExport::get_singleton()->add_export_plugin(export_plugin);
@@ -259,21 +251,19 @@ void register_gdnative_types() {
EditorNode::add_init_callback(editor_init_callback);
#endif
- ClassDB::register_class<GDNativeLibrary>();
- ClassDB::register_class<GDNative>();
+ GDREGISTER_CLASS(GDNativeLibrary);
+ GDREGISTER_CLASS(GDNative);
- resource_loader_gdnlib.instance();
+ resource_loader_gdnlib.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_gdnlib);
- resource_saver_gdnlib.instance();
+ resource_saver_gdnlib.instantiate();
ResourceSaver::add_resource_format_saver(resource_saver_gdnlib);
GDNativeCallRegistry::singleton = memnew(GDNativeCallRegistry);
GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall);
- register_net_types();
- register_xr_types();
register_nativescript_types();
register_pluginscript_types();
register_videodecoder_types();
@@ -298,7 +288,7 @@ void register_gdnative_types() {
Ref<GDNativeLibrary> lib = ResourceLoader::load(path);
Ref<GDNative> singleton;
- singleton.instance();
+ singleton.instantiate();
singleton->set_library(lib);
if (!singleton->initialize()) {
@@ -337,8 +327,6 @@ void unregister_gdnative_types() {
unregister_videodecoder_types();
unregister_pluginscript_types();
unregister_nativescript_types();
- unregister_xr_types();
- unregister_net_types();
memdelete(GDNativeCallRegistry::singleton);
@@ -363,7 +351,7 @@ void unregister_gdnative_types() {
print_line(String("aabb:\t") + itos(sizeof(AABB)));
print_line(String("rid:\t") + itos(sizeof(RID)));
print_line(String("string:\t") + itos(sizeof(String)));
- print_line(String("transform:\t") + itos(sizeof(Transform)));
+ print_line(String("transform:\t") + itos(sizeof(Transform3D)));
print_line(String("transfo2D:\t") + itos(sizeof(Transform2D)));
print_line(String("variant:\t") + itos(sizeof(Variant)));
print_line(String("vector2:\t") + itos(sizeof(Vector2)));
diff --git a/modules/gdnative/register_types.h b/modules/gdnative/register_types.h
index b5c182f8b7..662c638442 100644
--- a/modules/gdnative/register_types.h
+++ b/modules/gdnative/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdnative/tests/test_string.h b/modules/gdnative/tests/test_string.h
deleted file mode 100644
index aeb855a1c4..0000000000
--- a/modules/gdnative/tests/test_string.h
+++ /dev/null
@@ -1,1980 +0,0 @@
-/*************************************************************************/
-/* test_string.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 TEST_GDNATIVE_STRING_H
-#define TEST_GDNATIVE_STRING_H
-
-namespace TestGDNativeString {
-
-#include "gdnative/string.h"
-
-#include "tests/test_macros.h"
-
-int u32scmp(const char32_t *l, const char32_t *r) {
- for (; *l == *r && *l && *r; l++, r++)
- ;
- return *l - *r;
-}
-
-TEST_CASE("[GDNative String] Construct from Latin-1 char string") {
- godot_string s;
-
- godot_string_new_with_latin1_chars(&s, "Hello");
- CHECK(u32scmp(godot_string_get_data(&s), U"Hello") == 0);
- godot_string_destroy(&s);
-
- godot_string_new_with_latin1_chars_and_len(&s, "Hello", 3);
- CHECK(u32scmp(godot_string_get_data(&s), U"Hel") == 0);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Construct from wchar_t string") {
- godot_string s;
-
- godot_string_new_with_wide_chars(&s, L"Give me");
- CHECK(u32scmp(godot_string_get_data(&s), U"Give me") == 0);
- godot_string_destroy(&s);
-
- godot_string_new_with_wide_chars_and_len(&s, L"Give me", 3);
- CHECK(u32scmp(godot_string_get_data(&s), U"Giv") == 0);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Construct from UTF-8 char string") {
- static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
- static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x304A, 0 };
- static const uint8_t u8str[] = { 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 };
-
- godot_string s;
-
- godot_string_new_with_utf8_chars(&s, (const char *)u8str);
- CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
- godot_string_destroy(&s);
-
- godot_string_new_with_utf8_chars_and_len(&s, (const char *)u8str, 5);
- CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
- godot_string_destroy(&s);
-
- godot_string_new_with_utf32_chars(&s, u32str);
- godot_char_string cs = godot_string_utf8(&s);
- godot_string_parse_utf8(&s, godot_char_string_get_data(&cs));
- CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
- godot_string_destroy(&s);
- godot_char_string_destroy(&cs);
-
- godot_string_new_with_utf32_chars(&s, u32str);
- cs = godot_string_utf8(&s);
- godot_string_parse_utf8_with_len(&s, godot_char_string_get_data(&cs), godot_char_string_length(&cs));
- CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
- godot_string_destroy(&s);
- godot_char_string_destroy(&cs);
-}
-
-TEST_CASE("[GDNative String] Construct from UTF-8 string with BOM") {
- static const char32_t u32str[] = { 0x0045, 0x0020, 0x304A, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
- static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x304A, 0 };
- static const uint8_t u8str[] = { 0xEF, 0xBB, 0xBF, 0x45, 0x20, 0xE3, 0x81, 0x8A, 0xE3, 0x98, 0x8F, 0xE3, 0x82, 0x88, 0xE3, 0x81, 0x86, 0xF0, 0x9F, 0x8E, 0xA4, 0 };
-
- godot_string s;
-
- godot_string_new_with_utf8_chars(&s, (const char *)u8str);
- CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
- godot_string_destroy(&s);
-
- godot_string_new_with_utf8_chars_and_len(&s, (const char *)u8str, 8);
- CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Construct from UTF-16 string") {
- static const char32_t u32str[] = { 0x0045, 0x0020, 0x1F3A4, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
- static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x1F3A4, 0 };
- static const char16_t u16str[] = { 0x0045, 0x0020, 0xD83C, 0xDFA4, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 };
-
- godot_string s;
-
- godot_string_new_with_utf16_chars(&s, u16str);
- CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
- godot_string_destroy(&s);
-
- godot_string_new_with_utf16_chars_and_len(&s, u16str, 4);
- CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
- godot_string_destroy(&s);
-
- godot_string_new_with_utf32_chars(&s, u32str);
- godot_char16_string cs = godot_string_utf16(&s);
- godot_string_parse_utf16(&s, godot_char16_string_get_data(&cs));
- CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
- godot_string_destroy(&s);
- godot_char16_string_destroy(&cs);
-
- godot_string_new_with_utf32_chars(&s, u32str);
- cs = godot_string_utf16(&s);
- godot_string_parse_utf16_with_len(&s, godot_char16_string_get_data(&cs), godot_char16_string_length(&cs));
- CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
- godot_string_destroy(&s);
- godot_char16_string_destroy(&cs);
-}
-
-TEST_CASE("[GDNative String] Construct from UTF-16 string with BOM ") {
- static const char32_t u32str[] = { 0x0045, 0x0020, 0x1F3A4, 0x360F, 0x3088, 0x3046, 0x1F3A4, 0 };
- static const char32_t u32str_short[] = { 0x0045, 0x0020, 0x1F3A4, 0 };
- static const char16_t u16str[] = { 0xFEFF, 0x0045, 0x0020, 0xD83C, 0xDFA4, 0x360F, 0x3088, 0x3046, 0xD83C, 0xDFA4, 0 };
- static const char16_t u16str_swap[] = { 0xFFFE, 0x4500, 0x2000, 0x3CD8, 0xA4DF, 0x0F36, 0x8830, 0x4630, 0x3CD8, 0xA4DF, 0 };
-
- godot_string s;
-
- godot_string_new_with_utf16_chars(&s, u16str);
- CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
- godot_string_destroy(&s);
-
- godot_string_new_with_utf16_chars(&s, u16str_swap);
- CHECK(u32scmp(godot_string_get_data(&s), u32str) == 0);
- godot_string_destroy(&s);
-
- godot_string_new_with_utf16_chars_and_len(&s, u16str, 5);
- CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
- godot_string_destroy(&s);
-
- godot_string_new_with_utf16_chars_and_len(&s, u16str_swap, 5);
- CHECK(u32scmp(godot_string_get_data(&s), u32str_short) == 0);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Construct string copy") {
- godot_string s, t;
-
- godot_string_new_with_latin1_chars(&s, "Hello");
- godot_string_new_copy(&t, &s);
- CHECK(u32scmp(godot_string_get_data(&t), U"Hello") == 0);
- godot_string_destroy(&t);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Construct empty string") {
- godot_string s;
-
- godot_string_new(&s);
- CHECK(u32scmp(godot_string_get_data(&s), U"") == 0);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] ASCII/Latin-1") {
- godot_string s;
- godot_string_new_with_utf32_chars(&s, U"Primero Leche");
-
- godot_char_string cs = godot_string_ascii(&s);
- CHECK(strcmp(godot_char_string_get_data(&cs), "Primero Leche") == 0);
- godot_char_string_destroy(&cs);
-
- cs = godot_string_latin1(&s);
- CHECK(strcmp(godot_char_string_get_data(&cs), "Primero Leche") == 0);
- godot_char_string_destroy(&cs);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Comparisons (equal)") {
- godot_string s, t;
-
- godot_string_new_with_latin1_chars(&s, "Test Compare");
- godot_string_new_with_latin1_chars(&t, "Test Compare");
- CHECK(godot_string_operator_equal(&s, &t));
- godot_string_destroy(&s);
- godot_string_destroy(&t);
-}
-
-TEST_CASE("[GDNative String] Comparisons (operator <)") {
- godot_string s, t;
-
- godot_string_new_with_latin1_chars(&s, "Bees");
-
- godot_string_new_with_latin1_chars(&t, "Elephant");
- CHECK(godot_string_operator_less(&s, &t));
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "Amber");
- CHECK(!godot_string_operator_less(&s, &t));
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "Beatrix");
- CHECK(!godot_string_operator_less(&s, &t));
- godot_string_destroy(&t);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Concatenation (operator +)") {
- godot_string s, t, x;
-
- godot_string_new_with_latin1_chars(&s, "Hel");
- godot_string_new_with_latin1_chars(&t, "lo");
- x = godot_string_operator_plus(&s, &t);
- CHECK(u32scmp(godot_string_get_data(&x), U"Hello") == 0);
- godot_string_destroy(&x);
- godot_string_destroy(&s);
- godot_string_destroy(&t);
-}
-
-TEST_CASE("[GDNative String] Testing size and length of string") {
- godot_string s;
-
- godot_string_new_with_latin1_chars(&s, "Mellon");
- CHECK(godot_string_length(&s) == 6);
- godot_string_destroy(&s);
-
- godot_string_new_with_latin1_chars(&s, "Mellon1");
- CHECK(godot_string_length(&s) == 7);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Testing for empty string") {
- godot_string s;
-
- godot_string_new_with_latin1_chars(&s, "Mellon");
- CHECK(!godot_string_empty(&s));
- godot_string_destroy(&s);
-
- godot_string_new_with_latin1_chars(&s, "");
- CHECK(godot_string_empty(&s));
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Test chr") {
- godot_string s;
-
- s = godot_string_chr('H');
- CHECK(u32scmp(godot_string_get_data(&s), U"H") == 0);
- godot_string_destroy(&s);
-
- s = godot_string_chr(0x3012);
- CHECK(godot_string_operator_index_const(&s, 0) == 0x3012);
- godot_string_destroy(&s);
-
- ERR_PRINT_OFF
- s = godot_string_chr(0xd812);
- CHECK(godot_string_operator_index_const(&s, 0) == 0xfffd); // Unpaired UTF-16 surrogate
- godot_string_destroy(&s);
-
- s = godot_string_chr(0x20d812);
- CHECK(godot_string_operator_index_const(&s, 0) == 0xfffd); // Outside UTF-32 range
- godot_string_destroy(&s);
- ERR_PRINT_ON
-}
-
-TEST_CASE("[GDNative String] Operator []") {
- godot_string s;
-
- godot_string_new_with_latin1_chars(&s, "Hello");
- CHECK(*godot_string_operator_index(&s, 1) == 'e');
- CHECK(godot_string_operator_index_const(&s, 0) == 'H');
- CHECK(godot_string_ord_at(&s, 0) == 'H');
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Case function test") {
- godot_string s, t;
-
- godot_string_new_with_latin1_chars(&s, "MoMoNgA");
-
- t = godot_string_to_upper(&s);
- CHECK(u32scmp(godot_string_get_data(&t), U"MOMONGA") == 0);
- godot_string_destroy(&t);
-
- t = godot_string_to_lower(&s);
- CHECK(u32scmp(godot_string_get_data(&t), U"momonga") == 0);
- godot_string_destroy(&t);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Case compare function test") {
- godot_string s, t;
-
- godot_string_new_with_latin1_chars(&s, "MoMoNgA");
- godot_string_new_with_latin1_chars(&t, "momonga");
-
- CHECK(godot_string_casecmp_to(&s, &t) != 0);
- CHECK(godot_string_nocasecmp_to(&s, &t) == 0);
-
- godot_string_destroy(&s);
- godot_string_destroy(&t);
-}
-
-TEST_CASE("[GDNative String] Natural compare function test") {
- godot_string s, t;
-
- godot_string_new_with_latin1_chars(&s, "img2.png");
- godot_string_new_with_latin1_chars(&t, "img10.png");
-
- CHECK(godot_string_nocasecmp_to(&s, &t) > 0);
- CHECK(godot_string_naturalnocasecmp_to(&s, &t) < 0);
-
- godot_string_destroy(&s);
- godot_string_destroy(&t);
-}
-
-TEST_CASE("[GDNative String] hex_encode_buffer") {
- static const uint8_t u8str[] = { 0x45, 0xE3, 0x81, 0x8A, 0x8F, 0xE3 };
- godot_string s = godot_string_hex_encode_buffer(u8str, 6);
- CHECK(u32scmp(godot_string_get_data(&s), U"45e3818a8fe3") == 0);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Substr") {
- godot_string s, t;
- godot_string_new_with_latin1_chars(&s, "Killer Baby");
- t = godot_string_substr(&s, 3, 4);
- CHECK(u32scmp(godot_string_get_data(&t), U"ler ") == 0);
- godot_string_destroy(&t);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Find") {
- godot_string s, t;
- godot_string_new_with_latin1_chars(&s, "Pretty Woman Woman");
-
- godot_string_new_with_latin1_chars(&t, "Revenge of the Monster Truck");
- CHECK(godot_string_find(&s, &t) == -1);
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "tty");
- CHECK(godot_string_find(&s, &t) == 3);
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "Wo");
- CHECK(godot_string_find_from(&s, &t, 9) == 13);
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "man");
- CHECK(godot_string_rfind(&s, &t) == 15);
- godot_string_destroy(&t);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Find no case") {
- godot_string s, t;
- godot_string_new_with_latin1_chars(&s, "Pretty Whale Whale");
-
- godot_string_new_with_latin1_chars(&t, "WHA");
- CHECK(godot_string_findn(&s, &t) == 7);
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "WHA");
- CHECK(godot_string_findn_from(&s, &t, 9) == 13);
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "WHA");
- CHECK(godot_string_rfindn(&s, &t) == 13);
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "Revenge of the Monster SawFish");
- CHECK(godot_string_findn(&s, &t) == -1);
- godot_string_destroy(&t);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Find MK") {
- godot_packed_string_array keys;
- godot_packed_string_array_new(&keys);
-
-#define PUSH_KEY(x) \
- { \
- godot_string t; \
- godot_string_new_with_latin1_chars(&t, x); \
- godot_packed_string_array_push_back(&keys, &t); \
- godot_string_destroy(&t); \
- }
-
- PUSH_KEY("sty")
- PUSH_KEY("tty")
- PUSH_KEY("man")
-
- godot_string s;
- godot_string_new_with_latin1_chars(&s, "Pretty Woman");
- godot_int key = 0;
-
- CHECK(godot_string_findmk(&s, &keys) == 3);
- CHECK(godot_string_findmk_from_in_place(&s, &keys, 0, &key) == 3);
- CHECK(key == 1);
-
- CHECK(godot_string_findmk_from(&s, &keys, 5) == 9);
- CHECK(godot_string_findmk_from_in_place(&s, &keys, 5, &key) == 9);
- CHECK(key == 2);
-
- godot_string_destroy(&s);
- godot_packed_string_array_destroy(&keys);
-
-#undef PUSH_KEY
-}
-
-TEST_CASE("[GDNative String] Find and replace") {
- godot_string s, c, w;
- godot_string_new_with_latin1_chars(&s, "Happy Birthday, Anna!");
- godot_string_new_with_latin1_chars(&c, "Birthday");
- godot_string_new_with_latin1_chars(&w, "Halloween");
- godot_string t = godot_string_replace(&s, &c, &w);
- CHECK(u32scmp(godot_string_get_data(&t), U"Happy Halloween, Anna!") == 0);
- godot_string_destroy(&s);
- godot_string_destroy(&c);
- godot_string_destroy(&w);
-
- godot_string_new_with_latin1_chars(&c, "H");
- godot_string_new_with_latin1_chars(&w, "W");
- s = godot_string_replace_first(&t, &c, &w);
- godot_string_destroy(&t);
- godot_string_destroy(&c);
- godot_string_destroy(&w);
-
- CHECK(u32scmp(godot_string_get_data(&s), U"Wappy Halloween, Anna!") == 0);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Insertion") {
- godot_string s, t, r, u;
- godot_string_new_with_latin1_chars(&s, "Who is Frederic?");
- godot_string_new_with_latin1_chars(&t, "?");
- godot_string_new_with_latin1_chars(&r, " Chopin");
-
- u = godot_string_insert(&s, godot_string_find(&s, &t), &r);
- CHECK(u32scmp(godot_string_get_data(&u), U"Who is Frederic Chopin?") == 0);
-
- godot_string_destroy(&s);
- godot_string_destroy(&t);
- godot_string_destroy(&r);
- godot_string_destroy(&u);
-}
-
-TEST_CASE("[GDNative String] Number to string") {
- godot_string s;
- s = godot_string_num(3.141593);
- CHECK(u32scmp(godot_string_get_data(&s), U"3.141593") == 0);
- godot_string_destroy(&s);
-
- s = godot_string_num_with_decimals(3.141593, 3);
- CHECK(u32scmp(godot_string_get_data(&s), U"3.142") == 0);
- godot_string_destroy(&s);
-
- s = godot_string_num_real(3.141593);
- CHECK(u32scmp(godot_string_get_data(&s), U"3.141593") == 0);
- godot_string_destroy(&s);
-
- s = godot_string_num_scientific(30000000);
- CHECK(u32scmp(godot_string_get_data(&s), U"3e+07") == 0);
- godot_string_destroy(&s);
-
- s = godot_string_num_int64(3141593, 10);
- CHECK(u32scmp(godot_string_get_data(&s), U"3141593") == 0);
- godot_string_destroy(&s);
-
- s = godot_string_num_int64(0xA141593, 16);
- CHECK(u32scmp(godot_string_get_data(&s), U"a141593") == 0);
- godot_string_destroy(&s);
-
- s = godot_string_num_int64_capitalized(0xA141593, 16, true);
- CHECK(u32scmp(godot_string_get_data(&s), U"A141593") == 0);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] String to integer") {
- static const wchar_t *wnums[4] = { L"1237461283", L"- 22", L"0", L" - 1123412" };
- static const char *nums[4] = { "1237461283", "- 22", "0", " - 1123412" };
- static const int num[4] = { 1237461283, -22, 0, -1123412 };
-
- for (int i = 0; i < 4; i++) {
- godot_string s;
- godot_string_new_with_latin1_chars(&s, nums[i]);
- CHECK(godot_string_to_int(&s) == num[i]);
- godot_string_destroy(&s);
-
- CHECK(godot_string_char_to_int(nums[i]) == num[i]);
- CHECK(godot_string_wchar_to_int(wnums[i]) == num[i]);
- }
-}
-
-TEST_CASE("[GDNative String] Hex to integer") {
- static const char *nums[4] = { "0xFFAE", "22", "0", "AADDAD" };
- static const int64_t num[4] = { 0xFFAE, 0x22, 0, 0xAADDAD };
- static const bool wo_prefix[4] = { false, true, true, true };
- static const bool w_prefix[4] = { true, false, true, false };
-
- for (int i = 0; i < 4; i++) {
- godot_string s;
- godot_string_new_with_latin1_chars(&s, nums[i]);
- CHECK((godot_string_hex_to_int_with_prefix(&s) == num[i]) == w_prefix[i]);
- CHECK((godot_string_hex_to_int(&s) == num[i]) == wo_prefix[i]);
- godot_string_destroy(&s);
- }
-}
-
-TEST_CASE("[GDNative String] String to float") {
- static const wchar_t *wnums[4] = { L"-12348298412.2", L"0.05", L"2.0002", L" -0.0001" };
- static const char *nums[4] = { "-12348298412.2", "0.05", "2.0002", " -0.0001" };
- static const double num[4] = { -12348298412.2, 0.05, 2.0002, -0.0001 };
-
- for (int i = 0; i < 4; i++) {
- godot_string s;
- godot_string_new_with_latin1_chars(&s, nums[i]);
- CHECK(!(ABS(godot_string_to_float(&s) - num[i]) > 0.00001));
- godot_string_destroy(&s);
-
- CHECK(!(ABS(godot_string_char_to_float(nums[i]) - num[i]) > 0.00001));
- CHECK(!(ABS(godot_string_wchar_to_float(wnums[i], nullptr) - num[i]) > 0.00001));
- }
-}
-
-TEST_CASE("[GDNative String] CamelCase to underscore") {
- godot_string s, t;
- godot_string_new_with_latin1_chars(&s, "TestTestStringGD");
-
- t = godot_string_camelcase_to_underscore(&s);
- CHECK(u32scmp(godot_string_get_data(&t), U"Test_Test_String_GD") == 0);
- godot_string_destroy(&t);
-
- t = godot_string_camelcase_to_underscore_lowercased(&s);
- CHECK(u32scmp(godot_string_get_data(&t), U"test_test_string_gd") == 0);
- godot_string_destroy(&t);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Slicing") {
- godot_string s, c;
- godot_string_new_with_latin1_chars(&s, "Mars,Jupiter,Saturn,Uranus");
- godot_string_new_with_latin1_chars(&c, ",");
-
- const char32_t *slices[4] = { U"Mars", U"Jupiter", U"Saturn", U"Uranus" };
- for (int i = 0; i < godot_string_get_slice_count(&s, &c); i++) {
- godot_string t;
- t = godot_string_get_slice(&s, &c, i);
- CHECK(u32scmp(godot_string_get_data(&t), slices[i]) == 0);
- godot_string_destroy(&t);
-
- t = godot_string_get_slicec(&s, U',', i);
- CHECK(u32scmp(godot_string_get_data(&t), slices[i]) == 0);
- godot_string_destroy(&t);
- }
-
- godot_string_destroy(&c);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Splitting") {
- godot_string s, c;
- godot_string_new_with_latin1_chars(&s, "Mars,Jupiter,Saturn,Uranus");
- godot_string_new_with_latin1_chars(&c, ",");
-
- godot_packed_string_array l;
-
- const char32_t *slices_l[3] = { U"Mars", U"Jupiter", U"Saturn,Uranus" };
- const char32_t *slices_r[3] = { U"Mars,Jupiter", U"Saturn", U"Uranus" };
-
- l = godot_string_split_with_maxsplit(&s, &c, true, 2);
- CHECK(godot_packed_string_array_size(&l) == 3);
- for (int i = 0; i < godot_packed_string_array_size(&l); i++) {
- godot_string t = godot_packed_string_array_get(&l, i);
- CHECK(u32scmp(godot_string_get_data(&t), slices_l[i]) == 0);
- godot_string_destroy(&t);
- }
- godot_packed_string_array_destroy(&l);
-
- l = godot_string_rsplit_with_maxsplit(&s, &c, true, 2);
- CHECK(godot_packed_string_array_size(&l) == 3);
- for (int i = 0; i < godot_packed_string_array_size(&l); i++) {
- godot_string t = godot_packed_string_array_get(&l, i);
- CHECK(u32scmp(godot_string_get_data(&t), slices_r[i]) == 0);
- godot_string_destroy(&t);
- }
- godot_packed_string_array_destroy(&l);
- godot_string_destroy(&s);
-
- godot_string_new_with_latin1_chars(&s, "Mars Jupiter Saturn Uranus");
- const char32_t *slices_s[4] = { U"Mars", U"Jupiter", U"Saturn", U"Uranus" };
- l = godot_string_split_spaces(&s);
- for (int i = 0; i < godot_packed_string_array_size(&l); i++) {
- godot_string t = godot_packed_string_array_get(&l, i);
- CHECK(u32scmp(godot_string_get_data(&t), slices_s[i]) == 0);
- godot_string_destroy(&t);
- }
- godot_packed_string_array_destroy(&l);
- godot_string_destroy(&s);
-
- godot_string c1, c2;
- godot_string_new_with_latin1_chars(&c1, ";");
- godot_string_new_with_latin1_chars(&c2, " ");
-
- godot_string_new_with_latin1_chars(&s, "1.2;2.3 4.5");
- const double slices_d[3] = { 1.2, 2.3, 4.5 };
-
- godot_packed_float32_array lf = godot_string_split_floats_allow_empty(&s, &c1);
- CHECK(godot_packed_float32_array_size(&lf) == 2);
- for (int i = 0; i < godot_packed_float32_array_size(&lf); i++) {
- CHECK(ABS(godot_packed_float32_array_get(&lf, i) - slices_d[i]) <= 0.00001);
- }
- godot_packed_float32_array_destroy(&lf);
-
- godot_packed_string_array keys;
- godot_packed_string_array_new(&keys);
- godot_packed_string_array_push_back(&keys, &c1);
- godot_packed_string_array_push_back(&keys, &c2);
-
- lf = godot_string_split_floats_mk_allow_empty(&s, &keys);
- CHECK(godot_packed_float32_array_size(&lf) == 3);
- for (int i = 0; i < godot_packed_float32_array_size(&lf); i++) {
- CHECK(ABS(godot_packed_float32_array_get(&lf, i) - slices_d[i]) <= 0.00001);
- }
- godot_packed_float32_array_destroy(&lf);
-
- godot_string_destroy(&s);
- godot_string_new_with_latin1_chars(&s, "1;2 4");
- const int slices_i[3] = { 1, 2, 4 };
-
- godot_packed_int32_array li = godot_string_split_ints_allow_empty(&s, &c1);
- CHECK(godot_packed_int32_array_size(&li) == 2);
- for (int i = 0; i < godot_packed_int32_array_size(&li); i++) {
- CHECK(godot_packed_int32_array_get(&li, i) == slices_i[i]);
- }
- godot_packed_int32_array_destroy(&li);
-
- li = godot_string_split_ints_mk_allow_empty(&s, &keys);
- CHECK(godot_packed_int32_array_size(&li) == 3);
- for (int i = 0; i < godot_packed_int32_array_size(&li); i++) {
- CHECK(godot_packed_int32_array_get(&li, i) == slices_i[i]);
- }
- godot_packed_int32_array_destroy(&li);
-
- godot_string_destroy(&s);
- godot_string_destroy(&c);
- godot_string_destroy(&c1);
- godot_string_destroy(&c2);
- godot_packed_string_array_destroy(&keys);
-}
-
-TEST_CASE("[GDNative String] Erasing") {
- godot_string s, t;
- godot_string_new_with_latin1_chars(&s, "Josephine is such a cute girl!");
- godot_string_new_with_latin1_chars(&t, "cute ");
-
- godot_string_erase(&s, godot_string_find(&s, &t), godot_string_length(&t));
-
- CHECK(u32scmp(godot_string_get_data(&s), U"Josephine is such a girl!") == 0);
-
- godot_string_destroy(&s);
- godot_string_destroy(&t);
-}
-
-struct test_27_data {
- char const *data;
- char const *part;
- bool expected;
-};
-
-TEST_CASE("[GDNative String] Begins with") {
- test_27_data tc[] = {
- { "res://foobar", "res://", true },
- { "res", "res://", false },
- { "abc", "abc", true }
- };
- size_t count = sizeof(tc) / sizeof(tc[0]);
- bool state = true;
- for (size_t i = 0; state && i < count; ++i) {
- godot_string s;
- godot_string_new_with_latin1_chars(&s, tc[i].data);
-
- state = godot_string_begins_with_char_array(&s, tc[i].part) == tc[i].expected;
- if (state) {
- godot_string t;
- godot_string_new_with_latin1_chars(&t, tc[i].part);
- state = godot_string_begins_with(&s, &t) == tc[i].expected;
- godot_string_destroy(&t);
- }
- godot_string_destroy(&s);
-
- CHECK(state);
- if (!state) {
- break;
- }
- };
- CHECK(state);
-}
-
-TEST_CASE("[GDNative String] Ends with") {
- test_27_data tc[] = {
- { "res://foobar", "foobar", true },
- { "res", "res://", false },
- { "abc", "abc", true }
- };
- size_t count = sizeof(tc) / sizeof(tc[0]);
- bool state = true;
- for (size_t i = 0; state && i < count; ++i) {
- godot_string s;
- godot_string_new_with_latin1_chars(&s, tc[i].data);
-
- state = godot_string_ends_with_char_array(&s, tc[i].part) == tc[i].expected;
- if (state) {
- godot_string t;
- godot_string_new_with_latin1_chars(&t, tc[i].part);
- state = godot_string_ends_with(&s, &t) == tc[i].expected;
- godot_string_destroy(&t);
- }
- godot_string_destroy(&s);
-
- CHECK(state);
- if (!state) {
- break;
- }
- };
- CHECK(state);
-}
-
-TEST_CASE("[GDNative String] format") {
- godot_string value_format, t;
- godot_string_new_with_latin1_chars(&value_format, "red=\"$red\" green=\"$green\" blue=\"$blue\" alpha=\"$alpha\"");
-
- godot_variant key_v, val_v;
- godot_dictionary value_dictionary;
- godot_dictionary_new(&value_dictionary);
-
- godot_string_new_with_latin1_chars(&t, "red");
- godot_variant_new_string(&key_v, &t);
- godot_string_destroy(&t);
- godot_variant_new_int(&val_v, 10);
- godot_dictionary_set(&value_dictionary, &key_v, &val_v);
- godot_variant_destroy(&key_v);
- godot_variant_destroy(&val_v);
-
- godot_string_new_with_latin1_chars(&t, "green");
- godot_variant_new_string(&key_v, &t);
- godot_string_destroy(&t);
- godot_variant_new_int(&val_v, 20);
- godot_dictionary_set(&value_dictionary, &key_v, &val_v);
- godot_variant_destroy(&key_v);
- godot_variant_destroy(&val_v);
-
- godot_string_new_with_latin1_chars(&t, "blue");
- godot_variant_new_string(&key_v, &t);
- godot_string_destroy(&t);
- godot_string_new_with_latin1_chars(&t, "bla");
- godot_variant_new_string(&val_v, &t);
- godot_string_destroy(&t);
- godot_dictionary_set(&value_dictionary, &key_v, &val_v);
- godot_variant_destroy(&key_v);
- godot_variant_destroy(&val_v);
-
- godot_string_new_with_latin1_chars(&t, "alpha");
- godot_variant_new_string(&key_v, &t);
- godot_string_destroy(&t);
- godot_variant_new_real(&val_v, 0.4);
- godot_dictionary_set(&value_dictionary, &key_v, &val_v);
- godot_variant_destroy(&key_v);
- godot_variant_destroy(&val_v);
-
- godot_variant dict_v;
- godot_variant_new_dictionary(&dict_v, &value_dictionary);
- godot_string s = godot_string_format_with_custom_placeholder(&value_format, &dict_v, "$_");
-
- CHECK(u32scmp(godot_string_get_data(&s), U"red=\"10\" green=\"20\" blue=\"bla\" alpha=\"0.4\"") == 0);
-
- godot_dictionary_destroy(&value_dictionary);
- godot_string_destroy(&s);
- godot_variant_destroy(&dict_v);
- godot_string_destroy(&value_format);
-}
-
-TEST_CASE("[GDNative String] sprintf") {
- //godot_string GDAPI (const godot_string *p_self, const godot_array *p_values, godot_bool *p_error);
- godot_string format, output;
- godot_array args;
- bool error;
-
-#define ARRAY_PUSH_STRING(x) \
- { \
- godot_variant v; \
- godot_string t; \
- godot_string_new_with_latin1_chars(&t, x); \
- godot_variant_new_string(&v, &t); \
- godot_string_destroy(&t); \
- godot_array_push_back(&args, &v); \
- godot_variant_destroy(&v); \
- }
-
-#define ARRAY_PUSH_INT(x) \
- { \
- godot_variant v; \
- godot_variant_new_int(&v, x); \
- godot_array_push_back(&args, &v); \
- godot_variant_destroy(&v); \
- }
-
-#define ARRAY_PUSH_REAL(x) \
- { \
- godot_variant v; \
- godot_variant_new_real(&v, x); \
- godot_array_push_back(&args, &v); \
- godot_variant_destroy(&v); \
- }
-
- godot_array_new(&args);
-
- // %%
- godot_string_new_with_latin1_chars(&format, "fish %% frog");
- godot_array_clear(&args);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish % frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
- //////// INTS
-
- // Int
- godot_string_new_with_latin1_chars(&format, "fish %d frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(5);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Int left padded with zeroes.
- godot_string_new_with_latin1_chars(&format, "fish %05d frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(5);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 00005 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Int left padded with spaces.
- godot_string_new_with_latin1_chars(&format, "fish %5d frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(5);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Int right padded with spaces.
- godot_string_new_with_latin1_chars(&format, "fish %-5d frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(5);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 5 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Int with sign (positive).
- godot_string_new_with_latin1_chars(&format, "fish %+d frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(5);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish +5 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Negative int.
- godot_string_new_with_latin1_chars(&format, "fish %d frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(-5);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish -5 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Hex (lower)
- godot_string_new_with_latin1_chars(&format, "fish %x frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(45);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 2d frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Hex (upper)
- godot_string_new_with_latin1_chars(&format, "fish %X frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(45);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 2D frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Octal
- godot_string_new_with_latin1_chars(&format, "fish %o frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 143 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
- ////// REALS
-
- // Real
- godot_string_new_with_latin1_chars(&format, "fish %f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_REAL(99.99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Real left-padded
- godot_string_new_with_latin1_chars(&format, "fish %11f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_REAL(99.99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Real right-padded
- godot_string_new_with_latin1_chars(&format, "fish %-11f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_REAL(99.99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Real given int.
- godot_string_new_with_latin1_chars(&format, "fish %f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_REAL(99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.000000 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Real with sign (positive).
- godot_string_new_with_latin1_chars(&format, "fish %+f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_REAL(99.99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish +99.990000 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Real with 1 decimals.
- godot_string_new_with_latin1_chars(&format, "fish %.1f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_REAL(99.99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 100.0 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Real with 12 decimals.
- godot_string_new_with_latin1_chars(&format, "fish %.12f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_REAL(99.99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990000000000 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Real with no decimals.
- godot_string_new_with_latin1_chars(&format, "fish %.f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_REAL(99.99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 100 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- /////// Strings.
-
- // String
- godot_string_new_with_latin1_chars(&format, "fish %s frog");
- godot_array_clear(&args);
- ARRAY_PUSH_STRING("cheese");
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // String left-padded
- godot_string_new_with_latin1_chars(&format, "fish %10s frog");
- godot_array_clear(&args);
- ARRAY_PUSH_STRING("cheese");
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // String right-padded
- godot_string_new_with_latin1_chars(&format, "fish %-10s frog");
- godot_array_clear(&args);
- ARRAY_PUSH_STRING("cheese");
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- ///// Characters
-
- // Character as string.
- godot_string_new_with_latin1_chars(&format, "fish %c frog");
- godot_array_clear(&args);
- ARRAY_PUSH_STRING("A");
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish A frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Character as int.
- godot_string_new_with_latin1_chars(&format, "fish %c frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(65);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish A frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- ///// Dynamic width
-
- // String dynamic width
- godot_string_new_with_latin1_chars(&format, "fish %*s frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(10);
- ARRAY_PUSH_STRING("cheese");
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- REQUIRE(u32scmp(godot_string_get_data(&output), U"fish cheese frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Int dynamic width
- godot_string_new_with_latin1_chars(&format, "fish %*d frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(10);
- ARRAY_PUSH_INT(99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- REQUIRE(u32scmp(godot_string_get_data(&output), U"fish 99 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Float dynamic width
- godot_string_new_with_latin1_chars(&format, "fish %*.*f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_INT(10);
- ARRAY_PUSH_INT(3);
- ARRAY_PUSH_REAL(99.99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error == false);
- CHECK(u32scmp(godot_string_get_data(&output), U"fish 99.990 frog") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- ///// Errors
-
- // More formats than arguments.
- godot_string_new_with_latin1_chars(&format, "fish %s %s frog");
- godot_array_clear(&args);
- ARRAY_PUSH_STRING("cheese");
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error);
- CHECK(u32scmp(godot_string_get_data(&output), U"not enough arguments for format string") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // More arguments than formats.
- godot_string_new_with_latin1_chars(&format, "fish %s frog");
- godot_array_clear(&args);
- ARRAY_PUSH_STRING("hello");
- ARRAY_PUSH_STRING("cheese");
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error);
- CHECK(u32scmp(godot_string_get_data(&output), U"not all arguments converted during string formatting") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Incomplete format.
- godot_string_new_with_latin1_chars(&format, "fish %10");
- godot_array_clear(&args);
- ARRAY_PUSH_STRING("cheese");
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error);
- CHECK(u32scmp(godot_string_get_data(&output), U"incomplete format") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Bad character in format string
- godot_string_new_with_latin1_chars(&format, "fish %&f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_STRING("cheese");
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error);
- CHECK(u32scmp(godot_string_get_data(&output), U"unsupported format character") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Too many decimals.
- godot_string_new_with_latin1_chars(&format, "fish %2.2.2f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_REAL(99.99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error);
- CHECK(u32scmp(godot_string_get_data(&output), U"too many decimal points in format") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // * not a number
- godot_string_new_with_latin1_chars(&format, "fish %*f frog");
- godot_array_clear(&args);
- ARRAY_PUSH_STRING("cheese");
- ARRAY_PUSH_REAL(99.99);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error);
- CHECK(u32scmp(godot_string_get_data(&output), U"* wants number") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Character too long.
- godot_string_new_with_latin1_chars(&format, "fish %c frog");
- godot_array_clear(&args);
- ARRAY_PUSH_STRING("sc");
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error);
- CHECK(u32scmp(godot_string_get_data(&output), U"%c requires number or single-character string") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- // Character bad type.
- godot_string_new_with_latin1_chars(&format, "fish %c frog");
- godot_array_clear(&args);
- godot_array t;
- godot_array_new(&t);
- godot_variant v;
- godot_variant_new_array(&v, &t);
- godot_array_destroy(&t);
- godot_array_push_back(&args, &v);
- godot_variant_destroy(&v);
- output = godot_string_sprintf(&format, &args, &error);
- REQUIRE(error);
- CHECK(u32scmp(godot_string_get_data(&output), U"%c requires number or single-character string") == 0);
- godot_string_destroy(&format);
- godot_string_destroy(&output);
-
- godot_array_destroy(&args);
-#undef ARRAY_PUSH_INT
-#undef ARRAY_PUSH_REAL
-#undef ARRAY_PUSH_STRING
-}
-
-TEST_CASE("[GDNative String] is_numeric") {
-#define IS_NUM_TEST(x, r) \
- { \
- godot_string t; \
- godot_string_new_with_latin1_chars(&t, x); \
- CHECK(godot_string_is_numeric(&t) == r); \
- godot_string_destroy(&t); \
- }
-
- IS_NUM_TEST("12", true);
- IS_NUM_TEST("1.2", true);
- IS_NUM_TEST("AF", false);
- IS_NUM_TEST("-12", true);
- IS_NUM_TEST("-1.2", true);
-
-#undef IS_NUM_TEST
-}
-
-TEST_CASE("[GDNative String] pad") {
- godot_string s, c;
- godot_string_new_with_latin1_chars(&s, "test");
- godot_string_new_with_latin1_chars(&c, "x");
-
- godot_string l = godot_string_lpad_with_custom_character(&s, 10, &c);
- CHECK(u32scmp(godot_string_get_data(&l), U"xxxxxxtest") == 0);
- godot_string_destroy(&l);
-
- godot_string r = godot_string_rpad_with_custom_character(&s, 10, &c);
- CHECK(u32scmp(godot_string_get_data(&r), U"testxxxxxx") == 0);
- godot_string_destroy(&r);
-
- godot_string_destroy(&s);
- godot_string_destroy(&c);
-
- godot_string_new_with_latin1_chars(&s, "10.10");
- c = godot_string_pad_decimals(&s, 4);
- CHECK(u32scmp(godot_string_get_data(&c), U"10.1000") == 0);
- godot_string_destroy(&c);
- c = godot_string_pad_zeros(&s, 4);
- CHECK(u32scmp(godot_string_get_data(&c), U"0010.10") == 0);
- godot_string_destroy(&c);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] is_subsequence_of") {
- godot_string a, t;
- godot_string_new_with_latin1_chars(&a, "is subsequence of");
-
- godot_string_new_with_latin1_chars(&t, "sub");
- CHECK(godot_string_is_subsequence_of(&t, &a));
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "Sub");
- CHECK(!godot_string_is_subsequence_of(&t, &a));
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "Sub");
- CHECK(godot_string_is_subsequence_ofi(&t, &a));
- godot_string_destroy(&t);
-
- godot_string_destroy(&a);
-}
-
-TEST_CASE("[GDNative String] match") {
- godot_string s, t;
- godot_string_new_with_latin1_chars(&s, "*.png");
-
- godot_string_new_with_latin1_chars(&t, "img1.png");
- CHECK(godot_string_match(&t, &s));
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "img1.jpeg");
- CHECK(!godot_string_match(&t, &s));
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "img1.Png");
- CHECK(!godot_string_match(&t, &s));
- CHECK(godot_string_matchn(&t, &s));
- godot_string_destroy(&t);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] IPVX address to string") {
- godot_string ip;
-
- godot_string_new_with_latin1_chars(&ip, "192.168.0.1");
- CHECK(godot_string_is_valid_ip_address(&ip));
- godot_string_destroy(&ip);
-
- godot_string_new_with_latin1_chars(&ip, "192.368.0.1");
- CHECK(!godot_string_is_valid_ip_address(&ip));
- godot_string_destroy(&ip);
-
- godot_string_new_with_latin1_chars(&ip, "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
- CHECK(godot_string_is_valid_ip_address(&ip));
- godot_string_destroy(&ip);
-
- godot_string_new_with_latin1_chars(&ip, "2001:0db8:85j3:0000:0000:8a2e:0370:7334");
- CHECK(!godot_string_is_valid_ip_address(&ip));
- godot_string_destroy(&ip);
-
- godot_string_new_with_latin1_chars(&ip, "2001:0db8:85f345:0000:0000:8a2e:0370:7334");
- CHECK(!godot_string_is_valid_ip_address(&ip));
- godot_string_destroy(&ip);
-
- godot_string_new_with_latin1_chars(&ip, "2001:0db8::0:8a2e:370:7334");
- CHECK(godot_string_is_valid_ip_address(&ip));
- godot_string_destroy(&ip);
-
- godot_string_new_with_latin1_chars(&ip, "::ffff:192.168.0.1");
- CHECK(godot_string_is_valid_ip_address(&ip));
- godot_string_destroy(&ip);
-}
-
-TEST_CASE("[GDNative String] Capitalize against many strings") {
-#define CAP_TEST(i, o) \
- godot_string_new_with_latin1_chars(&input, i); \
- godot_string_new_with_latin1_chars(&output, o); \
- test = godot_string_capitalize(&input); \
- CHECK(u32scmp(godot_string_get_data(&output), godot_string_get_data(&test)) == 0); \
- godot_string_destroy(&input); \
- godot_string_destroy(&output); \
- godot_string_destroy(&test);
-
- godot_string input, output, test;
-
- CAP_TEST("bytes2var", "Bytes 2 Var");
- CAP_TEST("linear2db", "Linear 2 Db");
- CAP_TEST("vector3", "Vector 3");
- CAP_TEST("sha256", "Sha 256");
- CAP_TEST("2db", "2 Db");
- CAP_TEST("PascalCase", "Pascal Case");
- CAP_TEST("PascalPascalCase", "Pascal Pascal Case");
- CAP_TEST("snake_case", "Snake Case");
- CAP_TEST("snake_snake_case", "Snake Snake Case");
- CAP_TEST("sha256sum", "Sha 256 Sum");
- CAP_TEST("cat2dog", "Cat 2 Dog");
- CAP_TEST("function(name)", "Function(name)");
- CAP_TEST("snake_case_function(snake_case_arg)", "Snake Case Function(snake Case Arg)");
- CAP_TEST("snake_case_function( snake_case_arg )", "Snake Case Function( Snake Case Arg )");
-
-#undef CAP_TEST
-}
-
-TEST_CASE("[GDNative String] lstrip and rstrip") {
-#define LSTRIP_TEST(x, y, z) \
- { \
- godot_string xx, yy, zz, rr; \
- godot_string_new_with_latin1_chars(&xx, x); \
- godot_string_new_with_latin1_chars(&yy, y); \
- godot_string_new_with_latin1_chars(&zz, z); \
- rr = godot_string_lstrip(&xx, &yy); \
- state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
- godot_string_destroy(&xx); \
- godot_string_destroy(&yy); \
- godot_string_destroy(&zz); \
- godot_string_destroy(&rr); \
- }
-
-#define RSTRIP_TEST(x, y, z) \
- { \
- godot_string xx, yy, zz, rr; \
- godot_string_new_with_latin1_chars(&xx, x); \
- godot_string_new_with_latin1_chars(&yy, y); \
- godot_string_new_with_latin1_chars(&zz, z); \
- rr = godot_string_rstrip(&xx, &yy); \
- state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
- godot_string_destroy(&xx); \
- godot_string_destroy(&yy); \
- godot_string_destroy(&zz); \
- godot_string_destroy(&rr); \
- }
-
-#define LSTRIP_UTF8_TEST(x, y, z) \
- { \
- godot_string xx, yy, zz, rr; \
- godot_string_new_with_utf8_chars(&xx, x); \
- godot_string_new_with_utf8_chars(&yy, y); \
- godot_string_new_with_utf8_chars(&zz, z); \
- rr = godot_string_lstrip(&xx, &yy); \
- state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
- godot_string_destroy(&xx); \
- godot_string_destroy(&yy); \
- godot_string_destroy(&zz); \
- godot_string_destroy(&rr); \
- }
-
-#define RSTRIP_UTF8_TEST(x, y, z) \
- { \
- godot_string xx, yy, zz, rr; \
- godot_string_new_with_utf8_chars(&xx, x); \
- godot_string_new_with_utf8_chars(&yy, y); \
- godot_string_new_with_utf8_chars(&zz, z); \
- rr = godot_string_rstrip(&xx, &yy); \
- state = state && (u32scmp(godot_string_get_data(&rr), godot_string_get_data(&zz)) == 0); \
- godot_string_destroy(&xx); \
- godot_string_destroy(&yy); \
- godot_string_destroy(&zz); \
- godot_string_destroy(&rr); \
- }
-
- bool state = true;
-
- // strip none
- LSTRIP_TEST("abc", "", "abc");
- RSTRIP_TEST("abc", "", "abc");
- // strip one
- LSTRIP_TEST("abc", "a", "bc");
- RSTRIP_TEST("abc", "c", "ab");
- // strip lots
- LSTRIP_TEST("bababbababccc", "ab", "ccc");
- RSTRIP_TEST("aaabcbcbcbbcbbc", "cb", "aaa");
- // strip empty string
- LSTRIP_TEST("", "", "");
- RSTRIP_TEST("", "", "");
- // strip to empty string
- LSTRIP_TEST("abcabcabc", "bca", "");
- RSTRIP_TEST("abcabcabc", "bca", "");
- // don't strip wrong end
- LSTRIP_TEST("abc", "c", "abc");
- LSTRIP_TEST("abca", "a", "bca");
- RSTRIP_TEST("abc", "a", "abc");
- RSTRIP_TEST("abca", "a", "abc");
- // in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
- // and the same second as "ÿ" (\u00ff)
- LSTRIP_UTF8_TEST("¿", "µÿ", "¿");
- RSTRIP_UTF8_TEST("¿", "µÿ", "¿");
- LSTRIP_UTF8_TEST("µ¿ÿ", "µÿ", "¿ÿ");
- RSTRIP_UTF8_TEST("µ¿ÿ", "µÿ", "µ¿");
-
- // the above tests repeated with additional superfluous strip chars
-
- // strip none
- LSTRIP_TEST("abc", "qwjkl", "abc");
- RSTRIP_TEST("abc", "qwjkl", "abc");
- // strip one
- LSTRIP_TEST("abc", "qwajkl", "bc");
- RSTRIP_TEST("abc", "qwcjkl", "ab");
- // strip lots
- LSTRIP_TEST("bababbababccc", "qwabjkl", "ccc");
- RSTRIP_TEST("aaabcbcbcbbcbbc", "qwcbjkl", "aaa");
- // strip empty string
- LSTRIP_TEST("", "qwjkl", "");
- RSTRIP_TEST("", "qwjkl", "");
- // strip to empty string
- LSTRIP_TEST("abcabcabc", "qwbcajkl", "");
- RSTRIP_TEST("abcabcabc", "qwbcajkl", "");
- // don't strip wrong end
- LSTRIP_TEST("abc", "qwcjkl", "abc");
- LSTRIP_TEST("abca", "qwajkl", "bca");
- RSTRIP_TEST("abc", "qwajkl", "abc");
- RSTRIP_TEST("abca", "qwajkl", "abc");
- // in utf-8 "¿" (\u00bf) has the same first byte as "µ" (\u00b5)
- // and the same second as "ÿ" (\u00ff)
- LSTRIP_UTF8_TEST("¿", "qwaµÿjkl", "¿");
- RSTRIP_UTF8_TEST("¿", "qwaµÿjkl", "¿");
- LSTRIP_UTF8_TEST("µ¿ÿ", "qwaµÿjkl", "¿ÿ");
- RSTRIP_UTF8_TEST("µ¿ÿ", "qwaµÿjkl", "µ¿");
-
- CHECK(state);
-
-#undef LSTRIP_TEST
-#undef RSTRIP_TEST
-#undef LSTRIP_UTF8_TEST
-#undef RSTRIP_UTF8_TEST
-}
-
-TEST_CASE("[GDNative String] Cyrillic to_lower()") {
- godot_string upper, lower, test;
- godot_string_new_with_utf8_chars(&upper, "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ");
- godot_string_new_with_utf8_chars(&lower, "абвгдеёжзийклмнопрстуфхцчшщъыьэюя");
-
- test = godot_string_to_lower(&upper);
-
- CHECK((u32scmp(godot_string_get_data(&test), godot_string_get_data(&lower)) == 0));
-
- godot_string_destroy(&upper);
- godot_string_destroy(&lower);
- godot_string_destroy(&test);
-}
-
-TEST_CASE("[GDNative String] Count and countn functionality") {
-#define COUNT_TEST(x, y, r) \
- { \
- godot_string s, t; \
- godot_string_new_with_latin1_chars(&s, x); \
- godot_string_new_with_latin1_chars(&t, y); \
- state = state && (godot_string_count(&s, &t, 0, 0) == r); \
- godot_string_destroy(&s); \
- godot_string_destroy(&t); \
- }
-
-#define COUNTR_TEST(x, y, a, b, r) \
- { \
- godot_string s, t; \
- godot_string_new_with_latin1_chars(&s, x); \
- godot_string_new_with_latin1_chars(&t, y); \
- state = state && (godot_string_count(&s, &t, a, b) == r); \
- godot_string_destroy(&s); \
- godot_string_destroy(&t); \
- }
-
-#define COUNTN_TEST(x, y, r) \
- { \
- godot_string s, t; \
- godot_string_new_with_latin1_chars(&s, x); \
- godot_string_new_with_latin1_chars(&t, y); \
- state = state && (godot_string_countn(&s, &t, 0, 0) == r); \
- godot_string_destroy(&s); \
- godot_string_destroy(&t); \
- }
-
-#define COUNTNR_TEST(x, y, a, b, r) \
- { \
- godot_string s, t; \
- godot_string_new_with_latin1_chars(&s, x); \
- godot_string_new_with_latin1_chars(&t, y); \
- state = state && (godot_string_countn(&s, &t, a, b) == r); \
- godot_string_destroy(&s); \
- godot_string_destroy(&t); \
- }
- bool state = true;
-
- COUNT_TEST("", "Test", 0);
- COUNT_TEST("Test", "", 0);
- COUNT_TEST("Test", "test", 0);
- COUNT_TEST("Test", "TEST", 0);
- COUNT_TEST("TEST", "TEST", 1);
- COUNT_TEST("Test", "Test", 1);
- COUNT_TEST("aTest", "Test", 1);
- COUNT_TEST("Testa", "Test", 1);
- COUNT_TEST("TestTestTest", "Test", 3);
- COUNT_TEST("TestTestTest", "TestTest", 1);
- COUNT_TEST("TestGodotTestGodotTestGodot", "Test", 3);
-
- COUNTR_TEST("TestTestTestTest", "Test", 4, 8, 1);
- COUNTR_TEST("TestTestTestTest", "Test", 4, 12, 2);
- COUNTR_TEST("TestTestTestTest", "Test", 4, 16, 3);
- COUNTR_TEST("TestTestTestTest", "Test", 4, 0, 3);
-
- COUNTN_TEST("Test", "test", 1);
- COUNTN_TEST("Test", "TEST", 1);
- COUNTN_TEST("testTest-Testatest", "tEst", 4);
- COUNTNR_TEST("testTest-TeStatest", "tEsT", 4, 16, 2);
-
- CHECK(state);
-
-#undef COUNT_TEST
-#undef COUNTR_TEST
-#undef COUNTN_TEST
-#undef COUNTNR_TEST
-}
-
-TEST_CASE("[GDNative String] Bigrams") {
- godot_string s, t;
- godot_string_new_with_latin1_chars(&s, "abcd");
- godot_packed_string_array bigr = godot_string_bigrams(&s);
- godot_string_destroy(&s);
-
- CHECK(godot_packed_string_array_size(&bigr) == 3);
-
- t = godot_packed_string_array_get(&bigr, 0);
- CHECK(u32scmp(godot_string_get_data(&t), U"ab") == 0);
- godot_string_destroy(&t);
-
- t = godot_packed_string_array_get(&bigr, 1);
- CHECK(u32scmp(godot_string_get_data(&t), U"bc") == 0);
- godot_string_destroy(&t);
-
- t = godot_packed_string_array_get(&bigr, 2);
- CHECK(u32scmp(godot_string_get_data(&t), U"cd") == 0);
- godot_string_destroy(&t);
-
- godot_packed_string_array_destroy(&bigr);
-}
-
-TEST_CASE("[GDNative String] c-escape/unescape") {
- godot_string s;
- godot_string_new_with_latin1_chars(&s, "\\1\a2\b\f3\n45\r6\t7\v8\'9\?0\"");
- godot_string t = godot_string_c_escape(&s);
- godot_string u = godot_string_c_unescape(&t);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] dedent") {
- godot_string s, t;
- godot_string_new_with_latin1_chars(&s, " aaa\n bbb");
- godot_string_new_with_latin1_chars(&t, "aaa\nbbb");
- godot_string u = godot_string_dedent(&s);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Path functions") {
- static const char *path[4] = { "C:\\Godot\\project\\test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot\\test.doc" };
- static const char *base_dir[4] = { "C:\\Godot\\project", "/Godot/project", "../Godot/project", "Godot" };
- static const char *base_name[4] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test" };
- static const char *ext[4] = { "tscn", "xscn", "scn", "doc" };
- static const char *file[4] = { "test.tscn", "test.xscn", "test.scn", "test.doc" };
- static const bool abs[4] = { true, true, false, false };
-
- for (int i = 0; i < 4; i++) {
- godot_string s, t, u, f;
- godot_string_new_with_latin1_chars(&s, path[i]);
-
- t = godot_string_get_base_dir(&s);
- godot_string_new_with_latin1_chars(&u, base_dir[i]);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- t = godot_string_get_basename(&s);
- godot_string_new_with_latin1_chars(&u, base_name[i]);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- t = godot_string_get_extension(&s);
- godot_string_new_with_latin1_chars(&u, ext[i]);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- t = godot_string_get_file(&s);
- godot_string_new_with_latin1_chars(&u, file[i]);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- godot_string s_simp;
- s_simp = godot_string_simplify_path(&s);
- t = godot_string_get_base_dir(&s_simp);
- godot_string_new_with_latin1_chars(&u, file[i]);
- f = godot_string_plus_file(&t, &u);
- CHECK(u32scmp(godot_string_get_data(&f), godot_string_get_data(&s_simp)) == 0);
- godot_string_destroy(&f);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
- godot_string_destroy(&s_simp);
-
- CHECK(godot_string_is_abs_path(&s) == abs[i]);
- CHECK(godot_string_is_rel_path(&s) != abs[i]);
-
- godot_string_destroy(&s);
- }
-
- static const char *file_name[3] = { "test.tscn", "test://.xscn", "?tes*t.scn" };
- static const bool valid[3] = { true, false, false };
- for (int i = 0; i < 3; i++) {
- godot_string s;
- godot_string_new_with_latin1_chars(&s, file_name[i]);
- CHECK(godot_string_is_valid_filename(&s) == valid[i]);
- godot_string_destroy(&s);
- }
-}
-
-TEST_CASE("[GDNative String] hash") {
- godot_string a, b, c;
- godot_string_new_with_latin1_chars(&a, "Test");
- godot_string_new_with_latin1_chars(&b, "Test");
- godot_string_new_with_latin1_chars(&c, "West");
- CHECK(godot_string_hash(&a) == godot_string_hash(&b));
- CHECK(godot_string_hash(&a) != godot_string_hash(&c));
-
- CHECK(godot_string_hash64(&a) == godot_string_hash64(&b));
- CHECK(godot_string_hash64(&a) != godot_string_hash64(&c));
-
- godot_string_destroy(&a);
- godot_string_destroy(&b);
- godot_string_destroy(&c);
-}
-
-TEST_CASE("[GDNative String] http_escape/unescape") {
- godot_string s, t, u;
- godot_string_new_with_latin1_chars(&s, "Godot Engine:'docs'");
- godot_string_new_with_latin1_chars(&t, "Godot%20Engine%3A%27docs%27");
-
- u = godot_string_http_escape(&s);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
-
- u = godot_string_http_unescape(&t);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
- godot_string_destroy(&u);
-
- godot_string_destroy(&s);
- godot_string_destroy(&t);
-}
-
-TEST_CASE("[GDNative String] percent_encode/decode") {
- godot_string s, t, u;
- godot_string_new_with_latin1_chars(&s, "Godot Engine:'docs'");
- godot_string_new_with_latin1_chars(&t, "Godot%20Engine%3a%27docs%27");
-
- u = godot_string_percent_encode(&s);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
-
- u = godot_string_percent_decode(&t);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
- godot_string_destroy(&u);
-
- godot_string_destroy(&s);
- godot_string_destroy(&t);
-}
-
-TEST_CASE("[GDNative String] xml_escape/unescape") {
- godot_string s, t, u;
- godot_string_new_with_latin1_chars(&s, "\"Test\" <test@test&'test'>");
-
- t = godot_string_xml_escape_with_quotes(&s);
- u = godot_string_xml_unescape(&t);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- t = godot_string_xml_escape(&s);
- u = godot_string_xml_unescape(&t);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Strip escapes") {
- godot_string s, t, u;
- godot_string_new_with_latin1_chars(&s, "\t\tTest Test\r\n Test");
- godot_string_new_with_latin1_chars(&t, "Test Test Test");
-
- u = godot_string_strip_escapes(&s);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
-
- godot_string_destroy(&t);
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Strip edges") {
- godot_string s, t, u;
- godot_string_new_with_latin1_chars(&s, "\t Test Test ");
-
- godot_string_new_with_latin1_chars(&t, "Test Test ");
- u = godot_string_strip_edges(&s, true, false);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "\t Test Test");
- u = godot_string_strip_edges(&s, false, true);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "Test Test");
- u = godot_string_strip_edges(&s, true, true);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Similarity") {
- godot_string a, b, c;
- godot_string_new_with_latin1_chars(&a, "Test");
- godot_string_new_with_latin1_chars(&b, "West");
- godot_string_new_with_latin1_chars(&c, "Toad");
-
- CHECK(godot_string_similarity(&a, &b) > godot_string_similarity(&a, &c));
-
- godot_string_destroy(&a);
- godot_string_destroy(&b);
- godot_string_destroy(&c);
-}
-
-TEST_CASE("[GDNative String] Trim") {
- godot_string s, t, u, p;
- godot_string_new_with_latin1_chars(&s, "aaaTestbbb");
-
- godot_string_new_with_latin1_chars(&p, "aaa");
- godot_string_new_with_latin1_chars(&t, "Testbbb");
- u = godot_string_trim_prefix(&s, &p);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
- godot_string_destroy(&p);
-
- godot_string_new_with_latin1_chars(&p, "bbb");
- godot_string_new_with_latin1_chars(&t, "aaaTest");
- u = godot_string_trim_suffix(&s, &p);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
- godot_string_destroy(&p);
-
- godot_string_new_with_latin1_chars(&p, "Test");
- u = godot_string_trim_suffix(&s, &p);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&s)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&p);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Right/Left") {
- godot_string s, t, u;
- godot_string_new_with_latin1_chars(&s, "aaaTestbbb");
- // ^
-
- godot_string_new_with_latin1_chars(&t, "tbbb");
- u = godot_string_right(&s, 6);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&t, "aaaTes");
- u = godot_string_left(&s, 6);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- godot_string_destroy(&s);
-}
-
-TEST_CASE("[GDNative String] Repeat") {
- godot_string t, u;
- godot_string_new_with_latin1_chars(&t, "ab");
-
- u = godot_string_repeat(&t, 4);
- CHECK(u32scmp(godot_string_get_data(&u), U"abababab") == 0);
- godot_string_destroy(&u);
-
- godot_string_destroy(&t);
-}
-
-TEST_CASE("[GDNative String] SHA1/SHA256/MD5") {
- godot_string s, t, sha1, sha256, md5;
- godot_string_new_with_latin1_chars(&s, "Godot");
- godot_string_new_with_latin1_chars(&sha1, "a1e91f39b9fce6a9998b14bdbe2aa2b39dc2d201");
- static uint8_t sha1_buf[20] = {
- 0xA1, 0xE9, 0x1F, 0x39, 0xB9, 0xFC, 0xE6, 0xA9, 0x99, 0x8B, 0x14, 0xBD, 0xBE, 0x2A, 0xA2, 0xB3,
- 0x9D, 0xC2, 0xD2, 0x01
- };
- godot_string_new_with_latin1_chars(&sha256, "2a02b2443f7985d89d09001086ae3dcfa6eb0f55c6ef170715d42328e16e6cb8");
- static uint8_t sha256_buf[32] = {
- 0x2A, 0x02, 0xB2, 0x44, 0x3F, 0x79, 0x85, 0xD8, 0x9D, 0x09, 0x00, 0x10, 0x86, 0xAE, 0x3D, 0xCF,
- 0xA6, 0xEB, 0x0F, 0x55, 0xC6, 0xEF, 0x17, 0x07, 0x15, 0xD4, 0x23, 0x28, 0xE1, 0x6E, 0x6C, 0xB8
- };
- godot_string_new_with_latin1_chars(&md5, "4a336d087aeb0390da10ee2ea7cb87f8");
- static uint8_t md5_buf[16] = {
- 0x4A, 0x33, 0x6D, 0x08, 0x7A, 0xEB, 0x03, 0x90, 0xDA, 0x10, 0xEE, 0x2E, 0xA7, 0xCB, 0x87, 0xF8
- };
-
- godot_packed_byte_array buf = godot_string_sha1_buffer(&s);
- CHECK(memcmp(sha1_buf, godot_packed_byte_array_ptr(&buf), 20) == 0);
- godot_packed_byte_array_destroy(&buf);
-
- t = godot_string_sha1_text(&s);
- CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&sha1)) == 0);
- godot_string_destroy(&t);
-
- buf = godot_string_sha256_buffer(&s);
- CHECK(memcmp(sha256_buf, godot_packed_byte_array_ptr(&buf), 32) == 0);
- godot_packed_byte_array_destroy(&buf);
-
- t = godot_string_sha256_text(&s);
- CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&sha256)) == 0);
- godot_string_destroy(&t);
-
- buf = godot_string_md5_buffer(&s);
- CHECK(memcmp(md5_buf, godot_packed_byte_array_ptr(&buf), 16) == 0);
- godot_packed_byte_array_destroy(&buf);
-
- t = godot_string_md5_text(&s);
- CHECK(u32scmp(godot_string_get_data(&t), godot_string_get_data(&md5)) == 0);
- godot_string_destroy(&t);
-
- godot_string_destroy(&s);
- godot_string_destroy(&sha1);
- godot_string_destroy(&sha256);
- godot_string_destroy(&md5);
-}
-
-TEST_CASE("[GDNative String] Join") {
- godot_string s, t, u;
- godot_string_new_with_latin1_chars(&s, ", ");
-
- godot_packed_string_array parts;
- godot_packed_string_array_new(&parts);
- godot_string_new_with_latin1_chars(&t, "One");
- godot_packed_string_array_push_back(&parts, &t);
- godot_string_destroy(&t);
- godot_string_new_with_latin1_chars(&t, "B");
- godot_packed_string_array_push_back(&parts, &t);
- godot_string_destroy(&t);
- godot_string_new_with_latin1_chars(&t, "C");
- godot_packed_string_array_push_back(&parts, &t);
- godot_string_destroy(&t);
-
- godot_string_new_with_latin1_chars(&u, "One, B, C");
- t = godot_string_join(&s, &parts);
- CHECK(u32scmp(godot_string_get_data(&u), godot_string_get_data(&t)) == 0);
- godot_string_destroy(&u);
- godot_string_destroy(&t);
-
- godot_string_destroy(&s);
- godot_packed_string_array_destroy(&parts);
-}
-
-TEST_CASE("[GDNative String] Is_*") {
- static const char *data[12] = { "-30", "100", "10.1", "10,1", "1e2", "1e-2", "1e2e3", "0xAB", "AB", "Test1", "1Test", "Test*1" };
- static bool isnum[12] = { true, true, true, false, false, false, false, false, false, false, false, false };
- static bool isint[12] = { true, true, false, false, false, false, false, false, false, false, false, false };
- static bool ishex[12] = { true, true, false, false, true, false, true, false, true, false, false, false };
- static bool ishex_p[12] = { false, false, false, false, false, false, false, true, false, false, false, false };
- static bool isflt[12] = { true, true, true, false, true, true, false, false, false, false, false, false };
- static bool isid[12] = { false, false, false, false, false, false, false, false, true, true, false, false };
-
- for (int i = 0; i < 12; i++) {
- godot_string s;
- godot_string_new_with_latin1_chars(&s, data[i]);
- CHECK(godot_string_is_numeric(&s) == isnum[i]);
- CHECK(godot_string_is_valid_integer(&s) == isint[i]);
- CHECK(godot_string_is_valid_hex_number(&s, false) == ishex[i]);
- CHECK(godot_string_is_valid_hex_number(&s, true) == ishex_p[i]);
- CHECK(godot_string_is_valid_float(&s) == isflt[i]);
- CHECK(godot_string_is_valid_identifier(&s) == isid[i]);
- godot_string_destroy(&s);
- }
-}
-
-TEST_CASE("[GDNative String] humanize_size") {
- godot_string s;
-
- s = godot_string_humanize_size(1000);
- CHECK(u32scmp(godot_string_get_data(&s), U"1000 B") == 0);
- godot_string_destroy(&s);
-
- s = godot_string_humanize_size(1025);
- CHECK(u32scmp(godot_string_get_data(&s), U"1.00 KiB") == 0);
- godot_string_destroy(&s);
-
- s = godot_string_humanize_size(1025300);
- CHECK(u32scmp(godot_string_get_data(&s), U"1001.2 KiB") == 0);
- godot_string_destroy(&s);
-
- s = godot_string_humanize_size(100523550);
- CHECK(u32scmp(godot_string_get_data(&s), U"95.86 MiB") == 0);
- godot_string_destroy(&s);
-
- s = godot_string_humanize_size(5345555000);
- CHECK(u32scmp(godot_string_get_data(&s), U"4.97 GiB") == 0);
- godot_string_destroy(&s);
-}
-
-} // namespace TestGDNativeString
-
-#endif // TEST_GDNATIVE_STRING_H
diff --git a/modules/gdnative/tests/test_variant.h b/modules/gdnative/tests/test_variant.h
new file mode 100644
index 0000000000..c506882283
--- /dev/null
+++ b/modules/gdnative/tests/test_variant.h
@@ -0,0 +1,205 @@
+/*************************************************************************/
+/* test_variant.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 TEST_GDNATIVE_VARIANT_H
+#define TEST_GDNATIVE_VARIANT_H
+
+#include <gdnative/gdnative.h>
+#include <gdnative/variant.h>
+
+#include "tests/test_macros.h"
+
+namespace TestGDNativeVariant {
+
+TEST_CASE("[GDNative Variant] New Variant with copy") {
+ godot_variant src;
+ godot_variant_new_int(&src, 42);
+
+ godot_variant copy;
+ godot_variant_new_copy(&copy, &src);
+
+ CHECK(godot_variant_as_int(&copy) == 42);
+ CHECK(godot_variant_get_type(&copy) == GODOT_VARIANT_TYPE_INT);
+
+ godot_variant_destroy(&src);
+ godot_variant_destroy(&copy);
+}
+
+TEST_CASE("[GDNative Variant] New Variant with Nil") {
+ godot_variant val;
+ godot_variant_new_nil(&val);
+
+ CHECK(godot_variant_get_type(&val) == GODOT_VARIANT_TYPE_NIL);
+
+ godot_variant_destroy(&val);
+}
+
+TEST_CASE("[GDNative Variant] New Variant with bool") {
+ godot_variant val;
+ godot_variant_new_bool(&val, true);
+
+ CHECK(godot_variant_as_bool(&val));
+ CHECK(godot_variant_get_type(&val) == GODOT_VARIANT_TYPE_BOOL);
+
+ godot_variant_destroy(&val);
+}
+
+TEST_CASE("[GDNative Variant] New Variant with float") {
+ godot_variant val;
+ godot_variant_new_float(&val, 4.2);
+
+ CHECK(godot_variant_as_float(&val) == 4.2);
+ CHECK(godot_variant_get_type(&val) == GODOT_VARIANT_TYPE_FLOAT);
+
+ godot_variant_destroy(&val);
+}
+
+TEST_CASE("[GDNative Variant] New Variant with String") {
+ String str = "something";
+
+ godot_variant val;
+ godot_variant_new_string(&val, (godot_string *)&str);
+ godot_string gd_str = godot_variant_as_string(&val);
+ String *result = (String *)&gd_str;
+
+ CHECK(*result == String("something"));
+ CHECK(godot_variant_get_type(&val) == GODOT_VARIANT_TYPE_STRING);
+
+ godot_variant_destroy(&val);
+ godot_string_destroy(&gd_str);
+}
+
+TEST_CASE("[GDNative Variant] Variant call") {
+ String str("something");
+ godot_variant self;
+ godot_variant_new_string(&self, (godot_string *)&str);
+
+ godot_variant ret;
+
+ godot_string_name method;
+ godot_string_name_new_with_latin1_chars(&method, "is_valid_identifier");
+
+ godot_variant_call_error error;
+ godot_variant_call(&self, &method, nullptr, 0, &ret, &error);
+
+ CHECK(godot_variant_get_type(&ret) == GODOT_VARIANT_TYPE_BOOL);
+ CHECK(godot_variant_as_bool(&ret));
+
+ godot_variant_destroy(&ret);
+ godot_variant_destroy(&self);
+ godot_string_name_destroy(&method);
+}
+
+TEST_CASE("[GDNative Variant] Variant evaluate") {
+ godot_variant one;
+ godot_variant_new_int(&one, 1);
+ godot_variant two;
+ godot_variant_new_int(&two, 2);
+
+ godot_variant three;
+ godot_variant_new_nil(&three);
+ bool valid = false;
+
+ godot_variant_evaluate(GODOT_VARIANT_OP_ADD, &one, &two, &three, &valid);
+
+ CHECK(godot_variant_get_type(&three) == GODOT_VARIANT_TYPE_INT);
+ CHECK(godot_variant_as_int(&three) == 3);
+ CHECK(valid);
+
+ godot_variant_destroy(&one);
+ godot_variant_destroy(&two);
+ godot_variant_destroy(&three);
+}
+
+TEST_CASE("[GDNative Variant] Variant set/get named") {
+ godot_string_name x;
+ godot_string_name_new_with_latin1_chars(&x, "x");
+
+ Vector2 vec(0, 0);
+ godot_variant self;
+ godot_variant_new_vector2(&self, (godot_vector2 *)&vec);
+
+ godot_variant set;
+ godot_variant_new_float(&set, 1.0);
+
+ bool set_valid = false;
+ godot_variant_set_named(&self, &x, &set, &set_valid);
+
+ bool get_valid = false;
+ godot_variant get = godot_variant_get_named(&self, &x, &get_valid);
+
+ CHECK(get_valid);
+ CHECK(set_valid);
+ CHECK(godot_variant_get_type(&get) == GODOT_VARIANT_TYPE_FLOAT);
+ CHECK(godot_variant_as_float(&get) == 1.0);
+
+ godot_string_name_destroy(&x);
+ godot_variant_destroy(&self);
+ godot_variant_destroy(&set);
+ godot_variant_destroy(&get);
+}
+
+TEST_CASE("[GDNative Variant] Get utility function argument name") {
+ godot_string_name function;
+ godot_string_name_new_with_latin1_chars(&function, "pow");
+
+ godot_string arg_name = godot_variant_get_utility_function_argument_name(&function, 0);
+
+ String *arg_name_str = (String *)&arg_name;
+
+ CHECK(*arg_name_str == "base");
+
+ godot_string_destroy(&arg_name);
+ godot_string_name_destroy(&function);
+}
+
+TEST_CASE("[GDNative Variant] Get utility function list") {
+ int count = godot_variant_get_utility_function_count();
+
+ godot_string_name *c_list = (godot_string_name *)godot_alloc(count * sizeof(godot_string_name));
+ godot_variant_get_utility_function_list(c_list);
+
+ List<StringName> cpp_list;
+ Variant::get_utility_function_list(&cpp_list);
+
+ godot_string_name *cur = c_list;
+
+ for (const StringName &E : cpp_list) {
+ const StringName &cpp_name = E;
+ StringName *c_name = (StringName *)cur++;
+
+ CHECK(*c_name == cpp_name);
+ }
+
+ godot_free(c_list);
+}
+} // namespace TestGDNativeVariant
+
+#endif // TEST_GDNATIVE_VARIANT_H
diff --git a/modules/gdnative/videodecoder/register_types.cpp b/modules/gdnative/videodecoder/register_types.cpp
index 4181d8813f..54a577a2b6 100644
--- a/modules/gdnative/videodecoder/register_types.cpp
+++ b/modules/gdnative/videodecoder/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,16 +30,16 @@
#include "register_types.h"
-#include "core/class_db.h"
+#include "core/object/class_db.h"
#include "video_stream_gdnative.h"
static Ref<ResourceFormatLoaderVideoStreamGDNative> resource_loader_vsgdnative;
void register_videodecoder_types() {
- resource_loader_vsgdnative.instance();
+ resource_loader_vsgdnative.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_vsgdnative, true);
- ClassDB::register_class<VideoStreamGDNative>();
+ GDREGISTER_CLASS(VideoStreamGDNative);
}
void unregister_videodecoder_types() {
diff --git a/modules/gdnative/videodecoder/register_types.h b/modules/gdnative/videodecoder/register_types.h
index b1a83d4071..809225c925 100644
--- a/modules/gdnative/videodecoder/register_types.h
+++ b/modules/gdnative/videodecoder/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
index fe7c10cad9..e249363016 100644
--- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp
+++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "video_stream_gdnative.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "servers/audio_server.h"
VideoDecoderServer *VideoDecoderServer::instance = nullptr;
@@ -47,11 +47,7 @@ godot_int GDAPI godot_videodecoder_file_read(void *ptr, uint8_t *buf, int buf_si
// if file exists
if (file) {
- long bytes_read = file->get_buffer(buf, buf_size);
- // No bytes to read => EOF
- if (bytes_read == 0) {
- return 0;
- }
+ int64_t bytes_read = file->get_buffer(buf, buf_size);
return bytes_read;
}
return -1;
@@ -62,41 +58,35 @@ int64_t GDAPI godot_videodecoder_file_seek(void *ptr, int64_t pos, int whence) {
FileAccess *file = reinterpret_cast<FileAccess *>(ptr);
if (file) {
- size_t len = file->get_len();
+ int64_t len = file->get_length();
switch (whence) {
case SEEK_SET: {
- // Just for explicitness
- size_t new_pos = static_cast<size_t>(pos);
- if (new_pos > len) {
+ if (pos > len) {
return -1;
}
- file->seek(new_pos);
- pos = static_cast<int64_t>(file->get_position());
- return pos;
+ file->seek(pos);
+ return file->get_position();
} break;
case SEEK_CUR: {
// Just in case it doesn't exist
- if (pos < 0 && (size_t)-pos > file->get_position()) {
+ if (pos < 0 && -pos > (int64_t)file->get_position()) {
return -1;
}
- pos = pos + static_cast<int>(file->get_position());
- file->seek(pos);
- pos = static_cast<int64_t>(file->get_position());
- return pos;
+ file->seek(file->get_position() + pos);
+ return file->get_position();
} break;
case SEEK_END: {
// Just in case something goes wrong
- if ((size_t)-pos > len) {
+ if (-pos > len) {
return -1;
}
file->seek_end(pos);
- pos = static_cast<int64_t>(file->get_position());
- return pos;
+ return file->get_position();
} break;
default: {
// Only 4 possible options, hence default = AVSEEK_SIZE
// Asks to return the length of file
- return static_cast<int64_t>(len);
+ return len;
} break;
}
}
@@ -132,7 +122,7 @@ bool VideoStreamPlaybackGDNative::open_file(const String &p_file) {
samples_decoded = 0;
Ref<Image> img;
- img.instance();
+ img.instantiate();
img->create((int)texture_size.width, false, (int)texture_size.height, Image::FORMAT_RGBA8);
texture->create_from_image(img);
@@ -195,7 +185,7 @@ void VideoStreamPlaybackGDNative::update_texture() {
Ref<Image> img = memnew(Image(texture_size.width, texture_size.height, 0, Image::FORMAT_RGBA8, *pba));
- texture->update(img, true);
+ texture->update(img);
}
// ctor and dtor
@@ -250,7 +240,7 @@ void VideoStreamPlaybackGDNative::play() {
playing = true;
- delay_compensation = ProjectSettings::get_singleton()->get("audio/video_delay_compensation_ms");
+ delay_compensation = ProjectSettings::get_singleton()->get("audio/video/video_delay_compensation_ms");
delay_compensation /= 1000.0;
}
@@ -351,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) {
@@ -360,7 +350,7 @@ void VideoStreamGDNative::set_audio_track(int p_track) {
/* --- NOTE ResourceFormatLoaderVideoStreamGDNative starts here. ----- */
-RES ResourceFormatLoaderVideoStreamGDNative::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderVideoStreamGDNative::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) {
diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.h b/modules/gdnative/videodecoder/video_stream_gdnative.h
index 408d4a2454..c605dbb433 100644
--- a/modules/gdnative/videodecoder/video_stream_gdnative.h
+++ b/modules/gdnative/videodecoder/video_stream_gdnative.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define VIDEO_STREAM_GDNATIVE_H
#include "../gdnative.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "scene/resources/texture.h"
#include "scene/resources/video_stream.h"
@@ -118,7 +118,7 @@ class VideoStreamPlaybackGDNative : public VideoStreamPlayback {
AudioMixCallback mix_callback = nullptr;
int num_channels = -1;
- float time = 0;
+ float time = 0.0;
bool seek_backward = false;
int mix_rate = 0;
double delay_compensation = 0;
@@ -196,7 +196,7 @@ public:
class ResourceFormatLoaderVideoStreamGDNative : 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, bool p_no_cache = false);
+ 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;
diff --git a/modules/gdnative/xr/SCsub b/modules/gdnative/xr/SCsub
deleted file mode 100644
index 0b2db3b504..0000000000
--- a/modules/gdnative/xr/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/xr/xr_interface_gdnative.cpp b/modules/gdnative/xr/xr_interface_gdnative.cpp
deleted file mode 100644
index d03fc33935..0000000000
--- a/modules/gdnative/xr/xr_interface_gdnative.cpp
+++ /dev/null
@@ -1,420 +0,0 @@
-/*************************************************************************/
-/* xr_interface_gdnative.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "xr_interface_gdnative.h"
-#include "core/input/input.h"
-#include "servers/rendering/rendering_server_globals.h"
-#include "servers/xr/xr_positional_tracker.h"
-
-void XRInterfaceGDNative::_bind_methods() {
- ADD_PROPERTY_DEFAULT("interface_is_initialized", false);
- ADD_PROPERTY_DEFAULT("ar_is_anchor_detection_enabled", false);
-}
-
-XRInterfaceGDNative::XRInterfaceGDNative() {
- print_verbose("Construct gdnative interface\n");
-
- // we won't have our data pointer until our library gets set
- data = nullptr;
-
- interface = nullptr;
-}
-
-XRInterfaceGDNative::~XRInterfaceGDNative() {
- print_verbose("Destruct gdnative interface\n");
-
- if (interface != nullptr && is_initialized()) {
- uninitialize();
- };
-
- // cleanup after ourselves
- cleanup();
-}
-
-void XRInterfaceGDNative::cleanup() {
- if (interface != nullptr) {
- interface->destructor(data);
- data = nullptr;
- interface = nullptr;
- }
-}
-
-void XRInterfaceGDNative::set_interface(const godot_xr_interface_gdnative *p_interface) {
- // this should only be called once, just being paranoid..
- if (interface) {
- cleanup();
- }
-
- // bind to our interface
- interface = p_interface;
-
- // Now we do our constructing...
- data = interface->constructor((godot_object *)this);
-}
-
-StringName XRInterfaceGDNative::get_name() const {
- ERR_FAIL_COND_V(interface == nullptr, StringName());
-
- godot_string result = interface->get_name(data);
-
- StringName name = *(String *)&result;
-
- godot_string_destroy(&result);
-
- return name;
-}
-
-int XRInterfaceGDNative::get_capabilities() const {
- int capabilities;
-
- ERR_FAIL_COND_V(interface == nullptr, 0); // 0 = None
-
- capabilities = interface->get_capabilities(data);
-
- return capabilities;
-}
-
-bool XRInterfaceGDNative::get_anchor_detection_is_enabled() const {
- ERR_FAIL_COND_V(interface == nullptr, false);
-
- return interface->get_anchor_detection_is_enabled(data);
-}
-
-void XRInterfaceGDNative::set_anchor_detection_is_enabled(bool p_enable) {
- ERR_FAIL_COND(interface == nullptr);
-
- interface->set_anchor_detection_is_enabled(data, p_enable);
-}
-
-int XRInterfaceGDNative::get_camera_feed_id() {
- ERR_FAIL_COND_V(interface == nullptr, 0);
-
- return (unsigned int)interface->get_camera_feed_id(data);
-}
-
-bool XRInterfaceGDNative::is_stereo() {
- bool stereo;
-
- ERR_FAIL_COND_V(interface == nullptr, false);
-
- stereo = interface->is_stereo(data);
-
- return stereo;
-}
-
-bool XRInterfaceGDNative::is_initialized() const {
- ERR_FAIL_COND_V(interface == nullptr, false);
-
- return interface->is_initialized(data);
-}
-
-bool XRInterfaceGDNative::initialize() {
- ERR_FAIL_COND_V(interface == nullptr, false);
-
- bool initialized = interface->initialize(data);
-
- if (initialized) {
- // if we successfully initialize our interface and we don't have a primary interface yet, this becomes our primary interface
-
- XRServer *xr_server = XRServer::get_singleton();
- if ((xr_server != nullptr) && (xr_server->get_primary_interface() == nullptr)) {
- xr_server->set_primary_interface(this);
- };
- };
-
- return initialized;
-}
-
-void XRInterfaceGDNative::uninitialize() {
- ERR_FAIL_COND(interface == nullptr);
-
- XRServer *xr_server = XRServer::get_singleton();
- if (xr_server != nullptr) {
- // Whatever happens, make sure this is no longer our primary interface
- xr_server->clear_primary_interface_if(this);
- }
-
- interface->uninitialize(data);
-}
-
-Size2 XRInterfaceGDNative::get_render_targetsize() {
- ERR_FAIL_COND_V(interface == nullptr, Size2());
-
- godot_vector2 result = interface->get_render_targetsize(data);
- Vector2 *vec = (Vector2 *)&result;
-
- return *vec;
-}
-
-Transform XRInterfaceGDNative::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) {
- Transform *ret;
-
- ERR_FAIL_COND_V(interface == nullptr, Transform());
-
- godot_transform t = interface->get_transform_for_eye(data, (int)p_eye, (godot_transform *)&p_cam_transform);
-
- ret = (Transform *)&t;
-
- return *ret;
-}
-
-CameraMatrix XRInterfaceGDNative::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) {
- CameraMatrix cm;
-
- ERR_FAIL_COND_V(interface == nullptr, CameraMatrix());
-
- interface->fill_projection_for_eye(data, (godot_real *)cm.matrix, (godot_int)p_eye, p_aspect, p_z_near, p_z_far);
-
- return cm;
-}
-
-unsigned int XRInterfaceGDNative::get_external_texture_for_eye(XRInterface::Eyes p_eye) {
- ERR_FAIL_COND_V(interface == nullptr, 0);
-
- return (unsigned int)interface->get_external_texture_for_eye(data, (godot_int)p_eye);
-}
-
-void XRInterfaceGDNative::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) {
- ERR_FAIL_COND(interface == nullptr);
-
- interface->commit_for_eye(data, (godot_int)p_eye, (godot_rid *)&p_render_target, (godot_rect2 *)&p_screen_rect);
-}
-
-void XRInterfaceGDNative::process() {
- ERR_FAIL_COND(interface == nullptr);
-
- interface->process(data);
-}
-
-void XRInterfaceGDNative::notification(int p_what) {
- ERR_FAIL_COND(interface == nullptr);
-
- interface->notification(data, p_what);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////
-// some helper callbacks
-
-extern "C" {
-
-void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface) {
- // Must be on a version 4 plugin
- ERR_FAIL_COND_MSG(p_interface->version.major < 4, "GDNative XR interfaces build for Godot 3.x are not supported.");
-
- Ref<XRInterfaceGDNative> new_interface;
- new_interface.instance();
- new_interface->set_interface((const godot_xr_interface_gdnative *)p_interface);
- XRServer::get_singleton()->add_interface(new_interface);
-}
-
-godot_real GDAPI godot_xr_get_worldscale() {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, 1.0);
-
- return xr_server->get_world_scale();
-}
-
-godot_transform GDAPI godot_xr_get_reference_frame() {
- godot_transform reference_frame;
- Transform *reference_frame_ptr = (Transform *)&reference_frame;
-
- XRServer *xr_server = XRServer::get_singleton();
- if (xr_server != nullptr) {
- *reference_frame_ptr = xr_server->get_reference_frame();
- } else {
- godot_transform_new_identity(&reference_frame);
- }
-
- return reference_frame;
-}
-
-void GDAPI godot_xr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect) {
- // blits out our texture as is, handy for preview display of one of the eyes that is already rendered with lens distortion on an external HMD
- XRInterface::Eyes eye = (XRInterface::Eyes)p_eye;
-#if 0
- RID *render_target = (RID *)p_render_target;
-#endif
- Rect2 screen_rect = *(Rect2 *)p_rect;
-
- if (eye == XRInterface::EYE_LEFT) {
- screen_rect.size.x /= 2.0;
- } else if (p_eye == XRInterface::EYE_RIGHT) {
- screen_rect.size.x /= 2.0;
- screen_rect.position.x += screen_rect.size.x;
- }
-#ifndef _MSC_VER
-#warning this needs to be redone
-#endif
-#if 0
- RSG::rasterizer->blit_render_target_to_screen(*render_target, screen_rect, 0);
-#endif
-}
-
-godot_int GDAPI godot_xr_get_texid(godot_rid *p_render_target) {
- // In order to send off our textures to display on our hardware we need the opengl texture ID instead of the render target RID
- // This is a handy function to expose that.
-#if 0
- RID *render_target = (RID *)p_render_target;
-
- RID eye_texture = RSG::storage->render_target_get_texture(*render_target);
-#endif
-
-#ifndef _MSC_VER
-#warning need to obtain this ID again
-#endif
- uint32_t texid = 0; //RS::get_singleton()->texture_get_texid(eye_texture);
-
- return texid;
-}
-
-godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position) {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, 0);
-
- Input *input = Input::get_singleton();
- ERR_FAIL_NULL_V(input, 0);
-
- XRPositionalTracker *new_tracker = memnew(XRPositionalTracker);
- new_tracker->set_name(p_device_name);
- new_tracker->set_type(XRServer::TRACKER_CONTROLLER);
- if (p_hand == 1) {
- new_tracker->set_hand(XRPositionalTracker::TRACKER_LEFT_HAND);
- } else if (p_hand == 2) {
- new_tracker->set_hand(XRPositionalTracker::TRACKER_RIGHT_HAND);
- }
-
- // also register as joystick...
- int joyid = input->get_unused_joy_id();
- if (joyid != -1) {
- new_tracker->set_joy_id(joyid);
- input->joy_connection_changed(joyid, true, p_device_name, "");
- }
-
- if (p_tracks_orientation) {
- Basis orientation;
- new_tracker->set_orientation(orientation);
- }
- if (p_tracks_position) {
- Vector3 position;
- new_tracker->set_position(position);
- }
-
- // add our tracker to our server and remember its pointer
- xr_server->add_tracker(new_tracker);
-
- // note, this ID is only unique within controllers!
- return new_tracker->get_tracker_id();
-}
-
-void GDAPI godot_xr_remove_controller(godot_int p_controller_id) {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL(xr_server);
-
- Input *input = Input::get_singleton();
- ERR_FAIL_NULL(input);
-
- XRPositionalTracker *remove_tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id);
- if (remove_tracker != nullptr) {
- // unset our joystick if applicable
- int joyid = remove_tracker->get_joy_id();
- if (joyid != -1) {
- input->joy_connection_changed(joyid, false, "", "");
- remove_tracker->set_joy_id(-1);
- }
-
- // remove our tracker from our server
- xr_server->remove_tracker(remove_tracker);
- memdelete(remove_tracker);
- }
-}
-
-void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position) {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL(xr_server);
-
- XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id);
- if (tracker != nullptr) {
- Transform *transform = (Transform *)p_transform;
- if (p_tracks_orientation) {
- tracker->set_orientation(transform->basis);
- }
- if (p_tracks_position) {
- tracker->set_rw_position(transform->origin);
- }
- }
-}
-
-void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed) {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL(xr_server);
-
- Input *input = Input::get_singleton();
- ERR_FAIL_NULL(input);
-
- XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id);
- if (tracker != nullptr) {
- int joyid = tracker->get_joy_id();
- if (joyid != -1) {
- input->joy_button(joyid, p_button, p_is_pressed);
- }
- }
-}
-
-void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real p_value, godot_bool p_can_be_negative) {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL(xr_server);
-
- Input *input = Input::get_singleton();
- ERR_FAIL_NULL(input);
-
- XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id);
- if (tracker != nullptr) {
- int joyid = tracker->get_joy_id();
- if (joyid != -1) {
- Input::JoyAxis jx;
- jx.min = p_can_be_negative ? -1 : 0;
- jx.value = p_value;
- input->joy_axis(joyid, p_axis, jx);
- }
- }
-}
-
-godot_real GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id) {
- XRServer *xr_server = XRServer::get_singleton();
- ERR_FAIL_NULL_V(xr_server, 0.0);
-
- XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id);
- if (tracker != nullptr) {
- return tracker->get_rumble();
- }
-
- return 0.0;
-}
-}
diff --git a/modules/gdnative/xr/xr_interface_gdnative.h b/modules/gdnative/xr/xr_interface_gdnative.h
deleted file mode 100644
index de96487397..0000000000
--- a/modules/gdnative/xr/xr_interface_gdnative.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*************************************************************************/
-/* xr_interface_gdnative.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 XR_INTERFACE_GDNATIVE_H
-#define XR_INTERFACE_GDNATIVE_H
-
-#include "modules/gdnative/gdnative.h"
-#include "servers/xr/xr_interface.h"
-
-/**
- @authors Hinsbart & Karroffel & Mux213
-
- This subclass of our AR/VR interface forms a bridge to GDNative.
-*/
-
-class XRInterfaceGDNative : public XRInterface {
- GDCLASS(XRInterfaceGDNative, XRInterface);
-
- void cleanup();
-
-protected:
- const godot_xr_interface_gdnative *interface;
- void *data;
-
- static void _bind_methods();
-
-public:
- /** general interface information **/
- XRInterfaceGDNative();
- ~XRInterfaceGDNative();
-
- void set_interface(const godot_xr_interface_gdnative *p_interface);
-
- virtual StringName get_name() const override;
- virtual int get_capabilities() const override;
-
- virtual bool is_initialized() const override;
- virtual bool initialize() override;
- virtual void uninitialize() override;
-
- /** specific to AR **/
- virtual bool get_anchor_detection_is_enabled() const override;
- virtual void set_anchor_detection_is_enabled(bool p_enable) override;
- virtual int get_camera_feed_id() override;
-
- /** rendering and internal **/
- virtual Size2 get_render_targetsize() override;
- virtual bool is_stereo() override;
- virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) override;
-
- // we expose a Vector<float> version of this function to GDNative
- Vector<float> _get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far);
-
- // and a CameraMatrix version to XRServer
- virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
-
- virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override;
- virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override;
-
- virtual void process() override;
- virtual void notification(int p_what) override;
-};
-
-#endif // XR_INTERFACE_GDNATIVE_H
diff --git a/modules/gdnavigation/config.py b/modules/gdnavigation/config.py
deleted file mode 100644
index d22f9454ed..0000000000
--- a/modules/gdnavigation/config.py
+++ /dev/null
@@ -1,6 +0,0 @@
-def can_build(env, platform):
- return True
-
-
-def configure(env):
- pass
diff --git a/modules/gdscript/config.py b/modules/gdscript/config.py
index 6fc227e7f5..61ce6185a5 100644
--- a/modules/gdscript/config.py
+++ b/modules/gdscript/config.py
@@ -10,7 +10,6 @@ def get_doc_classes():
return [
"@GDScript",
"GDScript",
- "GDScriptFunctionState",
]
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index fc27892099..9c8adb4cf1 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -7,19 +7,15 @@
List of core built-in GDScript functions. Math functions and other utilities. Everything else is provided by objects. (Keywords: builtin, built in, global functions.)
</description>
<tutorials>
+ <link title="Random number generation">$DOCS_URL/tutorials/math/random_number_generation.html</link>
</tutorials>
<methods>
<method name="Color8">
- <return type="Color">
- </return>
- <argument index="0" name="r8" type="int">
- </argument>
- <argument index="1" name="g8" type="int">
- </argument>
- <argument index="2" name="b8" type="int">
- </argument>
- <argument index="3" name="a8" type="int" default="255">
- </argument>
+ <return type="Color" />
+ <argument index="0" name="r8" type="int" />
+ <argument index="1" name="g8" type="int" />
+ <argument index="2" name="b8" type="int" />
+ <argument index="3" name="a8" type="int" default="255" />
<description>
Returns a color constructed from integer red, green, blue, and alpha channels. Each channel should have 8 bits of information ranging from 0 to 255.
[code]r8[/code] red channel
@@ -31,150 +27,27 @@
[/codeblock]
</description>
</method>
- <method name="ColorN">
- <return type="Color">
- </return>
- <argument index="0" name="name" type="String">
- </argument>
- <argument index="1" name="alpha" type="float" default="1.0">
- </argument>
- <description>
- Returns a color according to the standardized [code]name[/code] with [code]alpha[/code] ranging from 0 to 1.
- [codeblock]
- red = ColorN("red", 1)
- [/codeblock]
- Supported color names are the same as the constants defined in [Color].
- </description>
- </method>
- <method name="abs">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the absolute value of parameter [code]s[/code] (i.e. positive value).
- [codeblock]
- # a is 1
- a = abs(-1)
- [/codeblock]
- </description>
- </method>
- <method name="acos">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the arc cosine of [code]s[/code] in radians. Use to get the angle of cosine [code]s[/code].
- [codeblock]
- # c is 0.523599 or 30 degrees if converted with rad2deg(s)
- c = acos(0.866025)
- [/codeblock]
- </description>
- </method>
- <method name="asin">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the arc sine of [code]s[/code] in radians. Use to get the angle of sine [code]s[/code].
- [codeblock]
- # s is 0.523599 or 30 degrees if converted with rad2deg(s)
- s = asin(0.5)
- [/codeblock]
- </description>
- </method>
<method name="assert">
- <return type="void">
- </return>
- <argument index="0" name="condition" type="bool">
- </argument>
- <argument index="1" name="message" type="String" default="&quot;&quot;">
- </argument>
+ <return type="void" />
+ <argument index="0" name="condition" type="bool" />
+ <argument index="1" name="message" type="String" default="&quot;&quot;" />
<description>
- Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated and the program is halted until you resume it. Only executes in debug builds, or when running the game from the editor. Use it for debugging purposes, to make sure a statement is [code]true[/code] during development.
+ Asserts that the [code]condition[/code] is [code]true[/code]. If the [code]condition[/code] is [code]false[/code], an error is generated. When running from the editor, the running project will also be paused until you resume it. This can be used as a stronger form of [method @GlobalScope.push_error] for reporting errors to project developers or add-on users.
+ [b]Note:[/b] For performance reasons, the code inside [method assert] is only executed in debug builds or when running the project from the editor. Don't include code that has side effects in an [method assert] call. Otherwise, the project will behave differently when exported in release mode.
The optional [code]message[/code] argument, if given, is shown in addition to the generic "Assertion failed" message. You can use this to provide additional details about why the assertion failed.
[codeblock]
- # Imagine we always want speed to be between 0 and 20
- speed = -10
+ # Imagine we always want speed to be between 0 and 20.
+ var speed = -10
assert(speed &lt; 20) # True, the program will continue
assert(speed &gt;= 0) # False, the program will stop
- assert(speed &gt;= 0 &amp;&amp; speed &lt; 20) # You can also combine the two conditional statements in one check
+ assert(speed &gt;= 0 and speed &lt; 20) # You can also combine the two conditional statements in one check
assert(speed &lt; 20, "speed = %f, but the speed limit is 20" % speed) # Show a message with clarifying details
[/codeblock]
</description>
</method>
- <method name="atan">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the arc tangent of [code]s[/code] in radians. Use it to get the angle from an angle's tangent in trigonometry: [code]atan(tan(angle)) == angle[/code].
- The method cannot know in which quadrant the angle should fall. See [method atan2] if you have both [code]y[/code] and [code]x[/code].
- [codeblock]
- a = atan(0.5) # a is 0.463648
- [/codeblock]
- </description>
- </method>
- <method name="atan2">
- <return type="float">
- </return>
- <argument index="0" name="y" type="float">
- </argument>
- <argument index="1" name="x" type="float">
- </argument>
- <description>
- Returns the arc tangent of [code]y/x[/code] in radians. Use to get the angle of tangent [code]y/x[/code]. 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.
- [codeblock]
- a = atan2(0, -1) # a is 3.141593
- [/codeblock]
- </description>
- </method>
- <method name="bytes2var">
- <return type="Variant">
- </return>
- <argument index="0" name="bytes" type="PackedByteArray">
- </argument>
- <argument index="1" name="allow_objects" type="bool" default="false">
- </argument>
- <description>
- Decodes a byte array back to a value. When [code]allow_objects[/code] is [code]true[/code] decoding objects is allowed.
- [b]WARNING:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution).
- </description>
- </method>
- <method name="cartesian2polar">
- <return type="Vector2">
- </return>
- <argument index="0" name="x" type="float">
- </argument>
- <argument index="1" name="y" type="float">
- </argument>
- <description>
- 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).
- </description>
- </method>
- <method name="ceil">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Rounds [code]s[/code] upward (towards positive infinity), returning the smallest whole number that is not less than [code]s[/code].
- [codeblock]
- i = ceil(1.45) # i is 2
- i = ceil(1.001) # i is 2
- [/codeblock]
- See also [method floor], [method round], and [method stepify].
- </description>
- </method>
<method name="char">
- <return type="String">
- </return>
- <argument index="0" name="code" type="int">
- </argument>
+ <return type="String" />
+ <argument index="0" name="char" type="int" />
<description>
Returns a character as a String of the given Unicode code point (which is compatible with ASCII code).
[codeblock]
@@ -182,38 +55,12 @@
a = char(65 + 32) # a is "a"
a = char(8364) # a is "€"
[/codeblock]
- This is the inverse of [method ord].
- </description>
- </method>
- <method name="clamp">
- <return type="float">
- </return>
- <argument index="0" name="value" type="float">
- </argument>
- <argument index="1" name="min" type="float">
- </argument>
- <argument index="2" name="max" type="float">
- </argument>
- <description>
- Clamps [code]value[/code] and returns a value not less than [code]min[/code] and not more than [code]max[/code].
- [codeblock]
- speed = 1000
- # a is 20
- a = clamp(speed, 1, 20)
-
- speed = -10
- # a is 1
- a = clamp(speed, 1, 20)
- [/codeblock]
</description>
</method>
<method name="convert">
- <return type="Variant">
- </return>
- <argument index="0" name="what" type="Variant">
- </argument>
- <argument index="1" name="type" type="int">
- </argument>
+ <return type="Variant" />
+ <argument index="0" name="what" type="Variant" />
+ <argument index="1" name="type" type="int" />
<description>
Converts from a type to another in the best way possible. The [code]type[/code] parameter uses the [enum Variant.Type] values.
[codeblock]
@@ -226,186 +73,15 @@
[/codeblock]
</description>
</method>
- <method name="cos">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the cosine of angle [code]s[/code] in radians.
- [codeblock]
- # Prints 1 then -1
- print(cos(PI * 2))
- print(cos(PI))
- [/codeblock]
- </description>
- </method>
- <method name="cosh">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the hyperbolic cosine of [code]s[/code] in radians.
- [codeblock]
- # Prints 1.543081
- print(cosh(1))
- [/codeblock]
- </description>
- </method>
- <method name="db2linear">
- <return type="float">
- </return>
- <argument index="0" name="db" type="float">
- </argument>
- <description>
- Converts from decibels to linear energy (audio).
- </description>
- </method>
- <method name="dectime">
- <return type="float">
- </return>
- <argument index="0" name="value" type="float">
- </argument>
- <argument index="1" name="amount" type="float">
- </argument>
- <argument index="2" name="step" type="float">
- </argument>
- <description>
- Returns the result of [code]value[/code] decreased by [code]step[/code] * [code]amount[/code].
- [codeblock]
- # a = 59
- a = dectime(60, 10, 0.1))
- [/codeblock]
- </description>
- </method>
- <method name="deg2rad">
- <return type="float">
- </return>
- <argument index="0" name="deg" type="float">
- </argument>
- <description>
- Converts an angle expressed in degrees to radians.
- [codeblock]
- # r is 3.141593
- r = deg2rad(180)
- [/codeblock]
- </description>
- </method>
<method name="dict2inst">
- <return type="Object">
- </return>
- <argument index="0" name="dict" type="Dictionary">
- </argument>
+ <return type="Object" />
+ <argument index="0" name="dictionary" type="Dictionary" />
<description>
- Converts a previously converted instance to a dictionary, back into an instance. Useful for deserializing.
- </description>
- </method>
- <method name="ease">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <argument index="1" name="curve" type="float">
- </argument>
- <description>
- Easing function, based on exponent. The curve values are: 0 is constant, 1 is linear, 0 to 1 is ease-in, 1+ is ease out. Negative values are in-out/out in.
- </description>
- </method>
- <method name="exp">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- The natural exponential function. It raises the mathematical constant [b]e[/b] to the power of [code]s[/code] and returns it.
- [b]e[/b] has an approximate value of 2.71828, and can be obtained with [code]exp(1)[/code].
- For exponents to other bases use the method [method pow].
- [codeblock]
- a = exp(2) # Approximately 7.39
- [/codeblock]
- </description>
- </method>
- <method name="floor">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Rounds [code]s[/code] downward (towards negative infinity), returning the largest whole number that is not more than [code]s[/code].
- [codeblock]
- # a is 2.0
- a = floor(2.99)
- # a is -3.0
- a = floor(-2.99)
- [/codeblock]
- See also [method ceil], [method round], and [method stepify].
- [b]Note:[/b] This method returns a float. If you need an integer, you can use [code]int(s)[/code] directly.
- </description>
- </method>
- <method name="fmod">
- <return type="float">
- </return>
- <argument index="0" name="a" type="float">
- </argument>
- <argument index="1" name="b" type="float">
- </argument>
- <description>
- Returns the floating-point remainder of [code]a/b[/code], keeping the sign of [code]a[/code].
- [codeblock]
- # Remainder is 1.5
- var remainder = fmod(7, 5.5)
- [/codeblock]
- For the integer remainder operation, use the % operator.
- </description>
- </method>
- <method name="fposmod">
- <return type="float">
- </return>
- <argument index="0" name="a" type="float">
- </argument>
- <argument index="1" name="b" type="float">
- </argument>
- <description>
- Returns the floating-point modulus of [code]a/b[/code] that wraps equally in positive and negative.
- [codeblock]
- for i in 7:
- var x = 0.5 * i - 1.5
- print("%4.1f %4.1f %4.1f" % [x, fmod(x, 1.5), fposmod(x, 1.5)])
- [/codeblock]
- Produces:
- [codeblock]
- -1.5 -0.0 0.0
- -1.0 -1.0 0.5
- -0.5 -0.5 1.0
- 0.0 0.0 0.0
- 0.5 0.5 0.5
- 1.0 1.0 1.0
- 1.5 0.0 0.0
- [/codeblock]
- </description>
- </method>
- <method name="funcref">
- <return type="FuncRef">
- </return>
- <argument index="0" name="instance" type="Object">
- </argument>
- <argument index="1" name="funcname" type="String">
- </argument>
- <description>
- Returns a reference to the specified function [code]funcname[/code] in the [code]instance[/code] node. As functions aren't first-class objects in GDscript, use [code]funcref[/code] to store a [FuncRef] in a variable and call it later.
- [codeblock]
- func foo():
- return("bar")
-
- a = funcref(self, "foo")
- print(a.call_func()) # Prints bar
- [/codeblock]
+ Converts a dictionary (previously created with [method inst2dict]) back to an instance. Useful for deserializing.
</description>
</method>
<method name="get_stack">
- <return type="Array">
- </return>
+ <return type="Array" />
<description>
Returns an array of dictionaries representing the current call stack.
[codeblock]
@@ -422,25 +98,12 @@
[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]
- </description>
- </method>
- <method name="hash">
- <return type="int">
- </return>
- <argument index="0" name="var" type="Variant">
- </argument>
- <description>
- Returns the integer hash of the variable passed.
- [codeblock]
- print(hash("a")) # Prints 177670
- [/codeblock]
+ [b]Note:[/b] Not supported for calling from threads. Instead, this will return an empty array.
</description>
</method>
<method name="inst2dict">
- <return type="Dictionary">
- </return>
- <argument index="0" name="inst" type="Object">
- </argument>
+ <return type="Dictionary" />
+ <argument index="0" name="instance" type="Object" />
<description>
Returns the passed instance converted to a dictionary (useful for serializing).
[codeblock]
@@ -457,97 +120,9 @@
[/codeblock]
</description>
</method>
- <method name="instance_from_id">
- <return type="Object">
- </return>
- <argument index="0" name="instance_id" type="int">
- </argument>
- <description>
- Returns the Object that corresponds to [code]instance_id[/code]. All Objects have a unique instance ID.
- [codeblock]
- var foo = "bar"
- func _ready():
- var id = get_instance_id()
- var inst = instance_from_id(id)
- print(inst.foo) # Prints bar
- [/codeblock]
- </description>
- </method>
- <method name="inverse_lerp">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <argument index="2" name="weight" type="float">
- </argument>
- <description>
- Returns a normalized value considering the given range. This is the opposite of [method lerp].
- [codeblock]
- var middle = lerp(20, 30, 0.75)
- # `middle` is now 27.5.
- # Now, we pretend to have forgotten the original ratio and want to get it back.
- var ratio = inverse_lerp(20, 30, 27.5)
- # `ratio` is now 0.75.
- [/codeblock]
- </description>
- </method>
- <method name="is_equal_approx">
- <return type="bool">
- </return>
- <argument index="0" name="a" type="float">
- </argument>
- <argument index="1" name="b" type="float">
- </argument>
- <description>
- Returns [code]true[/code] if [code]a[/code] and [code]b[/code] are approximately equal to each other.
- Here, approximately equal means that [code]a[/code] and [code]b[/code] are within a small internal epsilon of each other, which scales with the magnitude of the numbers.
- Infinity values of the same sign are considered equal.
- </description>
- </method>
- <method name="is_inf">
- <return type="bool">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns whether [code]s[/code] is an infinity value (either positive infinity or negative infinity).
- </description>
- </method>
- <method name="is_instance_valid">
- <return type="bool">
- </return>
- <argument index="0" name="instance" type="Object">
- </argument>
- <description>
- Returns whether [code]instance[/code] is a valid object (e.g. has not been deleted from memory).
- </description>
- </method>
- <method name="is_nan">
- <return type="bool">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns whether [code]s[/code] is a NaN ("Not a Number" or invalid) value.
- </description>
- </method>
- <method name="is_zero_approx">
- <return type="bool">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns [code]true[/code] if [code]s[/code] is zero or almost zero.
- This method is faster than using [method is_equal_approx] with one value as zero.
- </description>
- </method>
<method name="len">
- <return type="int">
- </return>
- <argument index="0" name="var" type="Variant">
- </argument>
+ <return type="int" />
+ <argument index="0" name="var" type="Variant" />
<description>
Returns length of Variant [code]var[/code]. Length is the character count of String, element count of Array, size of Dictionary, etc.
[b]Note:[/b] Generates a fatal error if Variant can not provide a length.
@@ -557,68 +132,9 @@
[/codeblock]
</description>
</method>
- <method name="lerp">
- <return type="Variant">
- </return>
- <argument index="0" name="from" type="Variant">
- </argument>
- <argument index="1" name="to" type="Variant">
- </argument>
- <argument index="2" name="weight" type="float">
- </argument>
- <description>
- Linearly interpolates between two values by a normalized value. This is the opposite of [method inverse_lerp].
- If the [code]from[/code] and [code]to[/code] arguments are of type [int] or [float], the return value is a [float].
- If both are of the same vector type ([Vector2], [Vector3] or [Color]), the return value will be of the same type ([code]lerp[/code] then calls the vector type's [code]lerp[/code] method).
- [codeblock]
- lerp(0, 4, 0.75) # Returns 3.0
- lerp(Vector2(1, 5), Vector2(3, 2), 0.5) # Returns Vector2(2, 3.5)
- [/codeblock]
- </description>
- </method>
- <method name="lerp_angle">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <argument index="2" name="weight" type="float">
- </argument>
- <description>
- Linearly interpolates between two angles (in radians) by a normalized value.
- Similar to [method lerp], but interpolates correctly when the angles wrap around [constant @GDScript.TAU].
- [codeblock]
- extends Sprite
- var elapsed = 0.0
- func _process(delta):
- var min_angle = deg2rad(0.0)
- var max_angle = deg2rad(90.0)
- rotation = lerp_angle(min_angle, max_angle, elapsed)
- elapsed += delta
- [/codeblock]
- </description>
- </method>
- <method name="linear2db">
- <return type="float">
- </return>
- <argument index="0" name="nrg" type="float">
- </argument>
- <description>
- Converts from linear energy to decibels (audio). This can be used to implement volume sliders that behave as expected (since volume isn't linear). Example:
- [codeblock]
- # "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.set_bus_volume_db(AudioServer.get_bus_index("Master"), linear2db($Slider.value))
- [/codeblock]
- </description>
- </method>
<method name="load">
- <return type="Resource">
- </return>
- <argument index="0" name="path" type="String">
- </argument>
+ <return type="Resource" />
+ <argument index="0" name="path" type="String" />
<description>
Loads a resource from the filesystem located at [code]path[/code]. 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 or use [method preload].
[b]Note:[/b] 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.
@@ -630,358 +146,46 @@
This method is a simplified version of [method ResourceLoader.load], which can be used for more advanced scenarios.
</description>
</method>
- <method name="log">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Natural logarithm. The amount of time needed to reach a certain level of continuous growth.
- [b]Note:[/b] This is not the same as the "log" function on most calculators, which uses a base 10 logarithm.
- [codeblock]
- log(10) # Returns 2.302585
- [/codeblock]
- [b]Note:[/b] The logarithm of [code]0[/code] returns [code]-inf[/code], while negative values return [code]-nan[/code].
- </description>
- </method>
- <method name="max">
- <return type="float">
- </return>
- <argument index="0" name="a" type="float">
- </argument>
- <argument index="1" name="b" type="float">
- </argument>
- <description>
- Returns the maximum of two values.
- [codeblock]
- max(1, 2) # Returns 2
- max(-3.99, -4) # Returns -3.99
- [/codeblock]
- </description>
- </method>
- <method name="min">
- <return type="float">
- </return>
- <argument index="0" name="a" type="float">
- </argument>
- <argument index="1" name="b" type="float">
- </argument>
- <description>
- Returns the minimum of two values.
- [codeblock]
- min(1, 2) # Returns 1
- min(-3.99, -4) # Returns -4
- [/codeblock]
- </description>
- </method>
- <method name="move_toward">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <argument index="2" name="delta" type="float">
- </argument>
- <description>
- Moves [code]from[/code] toward [code]to[/code] by the [code]delta[/code] value.
- Use a negative [code]delta[/code] value to move away.
- [codeblock]
- move_toward(5, 10, 4) # Returns 9
- move_toward(10, 5, 4) # Returns 6
- move_toward(10, 5, -1.5) # Returns 11.5
- [/codeblock]
- </description>
- </method>
- <method name="nearest_po2">
- <return type="int">
- </return>
- <argument index="0" name="value" type="int">
- </argument>
- <description>
- Returns the nearest equal or larger power of 2 for integer [code]value[/code].
- In other words, returns the smallest value [code]a[/code] where [code]a = pow(2, n)[/code] such that [code]value &lt;= a[/code] for some non-negative integer [code]n[/code].
- [codeblock]
- nearest_po2(3) # Returns 4
- nearest_po2(4) # Returns 4
- nearest_po2(5) # Returns 8
-
- nearest_po2(0) # Returns 0 (this may not be what you expect)
- nearest_po2(-1) # Returns 0 (this may not be what you expect)
- [/codeblock]
- [b]WARNING:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for non-positive values of [code]value[/code] (in reality, 1 is the smallest integer power of 2).
- </description>
- </method>
- <method name="ord">
- <return type="int">
- </return>
- <argument index="0" name="char" type="String">
- </argument>
- <description>
- Returns an integer representing the Unicode code point of the given Unicode character [code]char[/code].
- [codeblock]
- a = ord("A") # a is 65
- a = ord("a") # a is 97
- a = ord("€") # a is 8364
- [/codeblock]
- This is the inverse of [method char].
- </description>
- </method>
- <method name="parse_json">
- <return type="Variant">
- </return>
- <argument index="0" name="json" type="String">
- </argument>
- <description>
- Parse JSON text to a Variant. (Use [method typeof] to check if the Variant's type is what you expect.)
- [b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, parsing a JSON text will convert all numerical values to [float] types.
- [b]Note:[/b] JSON objects do not preserve key order like Godot dictionaries, thus, you should not rely on keys being in a certain order if a dictionary is constructed from JSON. In contrast, JSON arrays retain the order of their elements:
- [codeblock]
- var p = JSON.parse('["hello", "world", "!"]')
- if typeof(p.result) == TYPE_ARRAY:
- print(p.result[0]) # Prints "hello"
- else:
- push_error("Unexpected results.")
- [/codeblock]
- See also [JSON] for an alternative way to parse JSON text.
- </description>
- </method>
- <method name="polar2cartesian">
- <return type="Vector2">
- </return>
- <argument index="0" name="r" type="float">
- </argument>
- <argument index="1" name="th" type="float">
- </argument>
- <description>
- 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).
- </description>
- </method>
- <method name="posmod">
- <return type="int">
- </return>
- <argument index="0" name="a" type="int">
- </argument>
- <argument index="1" name="b" type="int">
- </argument>
- <description>
- Returns the integer modulus of [code]a/b[/code] that wraps equally in positive and negative.
- [codeblock]
- for i in range(-3, 4):
- print("%2d %2d %2d" % [i, i % 3, posmod(i, 3)])
- [/codeblock]
- Produces:
- [codeblock]
- -3 0 0
- -2 -2 1
- -1 -1 2
- 0 0 0
- 1 1 1
- 2 2 2
- 3 0 0
- [/codeblock]
- </description>
- </method>
- <method name="pow">
- <return type="float">
- </return>
- <argument index="0" name="base" type="float">
- </argument>
- <argument index="1" name="exp" type="float">
- </argument>
- <description>
- Returns the result of [code]x[/code] raised to the power of [code]y[/code].
- [codeblock]
- pow(2, 5) # Returns 32
- [/codeblock]
- </description>
- </method>
<method name="preload">
- <return type="Resource">
- </return>
- <argument index="0" name="path" type="String">
- </argument>
+ <return type="Resource" />
+ <argument index="0" name="path" type="String" />
<description>
Returns a [Resource] from the filesystem located at [code]path[/code]. The resource is loaded during script parsing, i.e. is loaded with the script and [method preload] effectively acts as a reference to that resource. Note that the method requires a constant path. If you want to load a resource from a dynamic/variable path, use [method load].
[b]Note:[/b] Resource paths can be obtained by right clicking on a resource in the Assets Panel and choosing "Copy Path" or by dragging the file from the FileSystem dock into the script.
[codeblock]
# Instance a scene.
- var diamond = preload("res://diamond.tscn").instance()
+ var diamond = preload("res://diamond.tscn").instantiate()
[/codeblock]
</description>
</method>
- <method name="print" qualifiers="vararg">
- <return type="void">
- </return>
+ <method name="print_debug" qualifiers="vararg">
+ <return type="void" />
<description>
- Converts one or more arguments to strings in the best way possible and prints them to the console.
+ 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]
- a = [1, 2, 3]
- print("a", "b", a) # Prints ab[1, 2, 3]
+ Test print
+ At: res://test.gd:15:_process()
[/codeblock]
- [b]Note:[/b] Consider using [method push_error] and [method push_warning] to print error and warning messages instead of [method print]. This distinguishes them from print messages used for debugging purposes, while also displaying a stack trace when an error or warning is printed.
- </description>
- </method>
- <method name="print_debug" qualifiers="vararg">
- <return type="void">
- </return>
- <description>
- Like [method print], but prints only when used in debug mode.
+ [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">
- </return>
+ <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]
- </description>
- </method>
- <method name="printerr" qualifiers="vararg">
- <return type="void">
- </return>
- <description>
- Prints one or more arguments to strings in the best way possible to standard error line.
- [codeblock]
- printerr("prints to stderr")
- [/codeblock]
- </description>
- </method>
- <method name="printraw" qualifiers="vararg">
- <return type="void">
- </return>
- <description>
- Prints one or more arguments to strings in the best way possible to console. No newline is added at the end.
- [codeblock]
- printraw("A")
- printraw("B")
- # Prints AB
- [/codeblock]
- [b]Note:[/b] 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 [method print].
- </description>
- </method>
- <method name="prints" qualifiers="vararg">
- <return type="void">
- </return>
- <description>
- Prints one or more arguments to the console with a space between each argument.
- [codeblock]
- prints("A", "B", "C") # Prints A B C
- [/codeblock]
- </description>
- </method>
- <method name="printt" qualifiers="vararg">
- <return type="void">
- </return>
- <description>
- Prints one or more arguments to the console with a tab between each argument.
- [codeblock]
- printt("A", "B", "C") # Prints A B C
- [/codeblock]
- </description>
- </method>
- <method name="push_error">
- <return type="void">
- </return>
- <argument index="0" name="message" type="String">
- </argument>
- <description>
- Pushes an error message to Godot's built-in debugger and to the OS terminal.
- [codeblock]
- push_error("test error") # Prints "test error" to debugger and terminal as error call
- [/codeblock]
- [b]Note:[/b] 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.
- </description>
- </method>
- <method name="push_warning">
- <return type="void">
- </return>
- <argument index="0" name="message" type="String">
- </argument>
- <description>
- Pushes a warning message to Godot's built-in debugger and to the OS terminal.
- [codeblock]
- push_warning("test warning") # Prints "test warning" to debugger and terminal as warning call
- [/codeblock]
- </description>
- </method>
- <method name="rad2deg">
- <return type="float">
- </return>
- <argument index="0" name="rad" type="float">
- </argument>
- <description>
- Converts an angle expressed in radians to degrees.
- [codeblock]
- rad2deg(0.523599) # Returns 30
- [/codeblock]
- </description>
- </method>
- <method name="rand_range">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <description>
- Random range, any floating point value between [code]from[/code] and [code]to[/code].
- [codeblock]
- prints(rand_range(0, 1), rand_range(0, 1)) # Prints e.g. 0.135591 0.405263
- [/codeblock]
- </description>
- </method>
- <method name="rand_seed">
- <return type="Array">
- </return>
- <argument index="0" name="seed" type="int">
- </argument>
- <description>
- Random from seed: pass a [code]seed[/code], and an array with both number and new seed is returned. "Seed" here refers to the internal state of the pseudo random number generator. The internal state of the current implementation is 64 bits.
- </description>
- </method>
- <method name="randf">
- <return type="float">
- </return>
- <description>
- Returns a random floating point value on the interval [code][0, 1][/code].
- [codeblock]
- randf() # Returns e.g. 0.375671
- [/codeblock]
- </description>
- </method>
- <method name="randi">
- <return type="int">
- </return>
- <description>
- Returns a random unsigned 32 bit integer. Use remainder to obtain a random value in the interval [code][0, N - 1][/code] (where N is smaller than 2^32).
- [codeblock]
- randi() # Returns random integer between 0 and 2^32 - 1
- randi() % 20 # Returns random integer between 0 and 19
- randi() % 100 # Returns random integer between 0 and 99
- randi() % 100 + 1 # Returns random integer between 1 and 100
- [/codeblock]
- </description>
- </method>
- <method name="randomize">
- <return type="void">
- </return>
- <description>
- Randomizes the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time.
- [codeblock]
- func _ready():
- randomize()
- [/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">
- </return>
+ <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))
@@ -993,163 +197,24 @@
[2, 3, 4]
[0, 2, 4]
[/codeblock]
- </description>
- </method>
- <method name="range_lerp">
- <return type="float">
- </return>
- <argument index="0" name="value" type="float">
- </argument>
- <argument index="1" name="istart" type="float">
- </argument>
- <argument index="2" name="istop" type="float">
- </argument>
- <argument index="3" name="ostart" type="float">
- </argument>
- <argument index="4" name="ostop" type="float">
- </argument>
- <description>
- Maps a [code]value[/code] from range [code][istart, istop][/code] to [code][ostart, ostop][/code].
- [codeblock]
- range_lerp(75, 0, 100, -1, 1) # Returns 0.5
- [/codeblock]
- </description>
- </method>
- <method name="round">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Rounds [code]s[/code] to the nearest whole number, with halfway cases rounded away from zero.
- [codeblock]
- round(2.6) # Returns 3
- [/codeblock]
- See also [method floor], [method ceil], and [method stepify].
- </description>
- </method>
- <method name="seed">
- <return type="void">
- </return>
- <argument index="0" name="seed" type="int">
- </argument>
- <description>
- Sets seed for the random number generator.
- [codeblock]
- my_seed = "Godot Rocks"
- seed(my_seed.hash())
- [/codeblock]
- </description>
- </method>
- <method name="sign">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the sign of [code]s[/code]: -1 or 1. Returns 0 if [code]s[/code] is 0.
- [codeblock]
- sign(-6) # Returns -1
- sign(0) # Returns 0
- sign(6) # Returns 1
- [/codeblock]
- </description>
- </method>
- <method name="sin">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the sine of angle [code]s[/code] in radians.
- [codeblock]
- sin(0.523599) # Returns 0.5
- [/codeblock]
- </description>
- </method>
- <method name="sinh">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the hyperbolic sine of [code]s[/code].
- [codeblock]
- a = log(2.0) # Returns 0.693147
- sinh(a) # Returns 0.75
- [/codeblock]
- </description>
- </method>
- <method name="smoothstep">
- <return type="float">
- </return>
- <argument index="0" name="from" type="float">
- </argument>
- <argument index="1" name="to" type="float">
- </argument>
- <argument index="2" name="s" type="float">
- </argument>
- <description>
- Returns the result of smoothly interpolating the value of [code]s[/code] between [code]0[/code] and [code]1[/code], based on the where [code]s[/code] lies with respect to the edges [code]from[/code] and [code]to[/code].
- The return value is [code]0[/code] if [code]s &lt;= from[/code], and [code]1[/code] if [code]s &gt;= to[/code]. If [code]s[/code] lies between [code]from[/code] and [code]to[/code], the returned value follows an S-shaped curve that maps [code]s[/code] between [code]0[/code] and [code]1[/code].
- This S-shaped curve is the cubic Hermite interpolator, given by [code]f(s) = 3*s^2 - 2*s^3[/code].
+ To iterate over an [Array] backwards, use:
[codeblock]
- smoothstep(0, 2, -5.0) # Returns 0.0
- smoothstep(0, 2, 0.5) # Returns 0.15625
- smoothstep(0, 2, 1.0) # Returns 0.5
- smoothstep(0, 2, 2.0) # Returns 1.0
+ var array = [3, 6, 9]
+ var i := array.size() - 1
+ while i &gt;= 0:
+ print(array[i])
+ i -= 1
[/codeblock]
- </description>
- </method>
- <method name="sqrt">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the square root of [code]s[/code], where [code]s[/code] is a non-negative number.
- [codeblock]
- sqrt(9) # Returns 3
- [/codeblock]
- [b]Note:[/b]Negative values of [code]s[/code] return NaN. If you need negative inputs, use [code]System.Numerics.Complex[/code] in C#.
- </description>
- </method>
- <method name="step_decimals">
- <return type="int">
- </return>
- <argument index="0" name="step" type="float">
- </argument>
- <description>
- Returns the position of the first non-zero digit, after the decimal point. Note that the maximum return value is 10, which is a design decision in the implementation.
- [codeblock]
- # n is 0
- n = step_decimals(5)
- # n is 4
- n = step_decimals(1.0005)
- # n is 9
- n = step_decimals(0.000000005)
- [/codeblock]
- </description>
- </method>
- <method name="stepify">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <argument index="1" name="step" type="float">
- </argument>
- <description>
- Snaps float value [code]s[/code] to a given [code]step[/code]. This can also be used to round a floating point number to an arbitrary number of decimals.
+ Output:
[codeblock]
- stepify(100, 32) # Returns 96
- stepify(3.14159, 0.01) # Returns 3.14
+ 9
+ 6
+ 3
[/codeblock]
- See also [method ceil], [method floor], and [method round].
</description>
</method>
<method name="str" qualifiers="vararg">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Converts one or more arguments to string in the best way possible.
[codeblock]
@@ -1160,215 +225,27 @@
[/codeblock]
</description>
</method>
- <method name="str2var">
- <return type="Variant">
- </return>
- <argument index="0" name="string" type="String">
- </argument>
- <description>
- Converts a formatted string that was returned by [method var2str] to the original value.
- [codeblock]
- a = '{ "a": 1, "b": 2 }'
- b = str2var(a)
- print(b["a"]) # Prints 1
- [/codeblock]
- </description>
- </method>
- <method name="tan">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the tangent of angle [code]s[/code] in radians.
- [codeblock]
- tan(deg2rad(45)) # Returns 1
- [/codeblock]
- </description>
- </method>
- <method name="tanh">
- <return type="float">
- </return>
- <argument index="0" name="s" type="float">
- </argument>
- <description>
- Returns the hyperbolic tangent of [code]s[/code].
- [codeblock]
- a = log(2.0) # Returns 0.693147
- tanh(a) # Returns 0.6
- [/codeblock]
- </description>
- </method>
- <method name="to_json">
- <return type="String">
- </return>
- <argument index="0" name="var" type="Variant">
- </argument>
- <description>
- Converts a [Variant] [code]var[/code] to JSON text and return the result. Useful for serializing data to store or send over the network.
- [codeblock]
- # Both numbers below are integers.
- a = { "a": 1, "b": 2 }
- b = to_json(a)
- print(b) # {"a":1, "b":2}
- # Both numbers above are floats, even if they display without any decimal places.
- [/codeblock]
- [b]Note:[/b] The JSON specification does not define integer or float types, but only a [i]number[/i] type. Therefore, converting a [Variant] to JSON text will convert all numerical values to [float] types.
- See also [JSON] for an alternative way to convert a [Variant] to JSON text.
- </description>
- </method>
<method name="type_exists">
- <return type="bool">
- </return>
- <argument index="0" name="type" type="String">
- </argument>
- <description>
- Returns whether the given class exists in [ClassDB].
- [codeblock]
- type_exists("Sprite2D") # Returns true
- type_exists("Variant") # Returns false
- [/codeblock]
- </description>
- </method>
- <method name="typeof">
- <return type="int">
- </return>
- <argument index="0" name="what" type="Variant">
- </argument>
- <description>
- Returns the internal type of the given Variant object, using the [enum Variant.Type] values.
- [codeblock]
- p = parse_json('["a", "b", "c"]')
- if typeof(p) == TYPE_ARRAY:
- print(p[0]) # Prints a
- else:
- print("unexpected results")
- [/codeblock]
- </description>
- </method>
- <method name="validate_json">
- <return type="String">
- </return>
- <argument index="0" name="json" type="String">
- </argument>
- <description>
- Checks that [code]json[/code] is valid JSON data. Returns an empty string if valid, or an error message otherwise.
- [codeblock]
- j = to_json([1, 2, 3])
- v = validate_json(j)
- if not v:
- print("Valid JSON.")
- else:
- push_error("Invalid JSON: " + v)
- [/codeblock]
- </description>
- </method>
- <method name="var2bytes">
- <return type="PackedByteArray">
- </return>
- <argument index="0" name="var" type="Variant">
- </argument>
- <argument index="1" name="full_objects" type="bool" default="false">
- </argument>
- <description>
- Encodes a variable value to a byte array. When [code]full_objects[/code] is [code]true[/code] encoding objects is allowed (and can potentially include code).
- </description>
- </method>
- <method name="var2str">
- <return type="String">
- </return>
- <argument index="0" name="var" type="Variant">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="type" type="StringName" />
<description>
- Converts a Variant [code]var[/code] to a formatted string that can later be parsed using [method str2var].
- [codeblock]
- a = { "a": 1, "b": 2 }
- print(var2str(a))
- [/codeblock]
- prints
- [codeblock]
- {
- "a": 1,
- "b": 2
- }
- [/codeblock]
- </description>
- </method>
- <method name="weakref">
- <return type="WeakRef">
- </return>
- <argument index="0" name="obj" type="Object">
- </argument>
- <description>
- Returns a weak reference to an object.
- 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.
- </description>
- </method>
- <method name="wrapf">
- <return type="float">
- </return>
- <argument index="0" name="value" type="float">
- </argument>
- <argument index="1" name="min" type="float">
- </argument>
- <argument index="2" name="max" type="float">
- </argument>
- <description>
- Wraps float [code]value[/code] between [code]min[/code] and [code]max[/code].
- Usable for creating loop-alike behavior or infinite surfaces.
- [codeblock]
- # Infinite loop between 5.0 and 9.9
- value = wrapf(value + 0.1, 5.0, 10.0)
- [/codeblock]
- [codeblock]
- # Infinite rotation (in radians)
- angle = wrapf(angle + 0.1, 0.0, TAU)
- [/codeblock]
- [codeblock]
- # Infinite rotation (in radians)
- angle = wrapf(angle + 0.1, -PI, PI)
- [/codeblock]
- [b]Note:[/b] If [code]min[/code] is [code]0[/code], this is equivalent to [method fposmod], so prefer using that instead.
- [code]wrapf[/code] is more flexible than using the [method fposmod] approach by giving the user control over the minimum value.
- </description>
- </method>
- <method name="wrapi">
- <return type="int">
- </return>
- <argument index="0" name="value" type="int">
- </argument>
- <argument index="1" name="min" type="int">
- </argument>
- <argument index="2" name="max" type="int">
- </argument>
- <description>
- Wraps integer [code]value[/code] between [code]min[/code] and [code]max[/code].
- Usable for creating loop-alike behavior or infinite surfaces.
- [codeblock]
- # Infinite loop between 5 and 9
- frame = wrapi(frame + 1, 5, 10)
- [/codeblock]
- [codeblock]
- # result is -2
- var result = wrapi(-6, -5, -1)
- [/codeblock]
- [b]Note:[/b] If [code]min[/code] is [code]0[/code], this is equivalent to [method posmod], so prefer using that instead.
- [code]wrapi[/code] is more flexible than using the [method posmod] approach by giving the user control over the minimum value.
</description>
</method>
</methods>
<constants>
- <constant name="PI" value="3.141593">
- Constant that represents how many times the diameter of a circle fits around its perimeter. This is equivalent to [code]TAU / 2[/code].
+ <constant name="PI" value="3.14159265358979">
+ Constant that represents how many times the diameter of a circle fits around its perimeter. This is equivalent to [code]TAU / 2[/code], or 180 degrees in rotations.
</constant>
- <constant name="TAU" value="6.283185">
- The circle constant, the circumference of the unit circle in radians.
+ <constant name="TAU" value="6.28318530717959">
+ The circle constant, the circumference of the unit circle in radians. This is equivalent to [code]PI * 2[/code], or 360 degrees in rotations.
</constant>
<constant name="INF" value="inf">
- Positive infinity. For negative infinity, use -INF.
+ Positive floating-point infinity. This is the result of floating-point division when the divisor is [code]0.0[/code]. For negative infinity, use [code]-INF[/code]. Dividing by [code]-0.0[/code] will result in negative infinity if the numerator is positive, so dividing by [code]0.0[/code] is not the same as dividing by [code]-0.0[/code] (despite [code]0.0 == -0.0[/code] returning [code]true[/code]).
+ [b]Note:[/b] Numeric infinity is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer number by [code]0[/code] will not result in [constant INF] and will result in a run-time error instead.
</constant>
<constant name="NAN" value="nan">
- "Not a Number", an invalid value. [code]NaN[/code] has special properties, including that it is not equal to itself. It is output by some invalid operations, such as dividing zero by zero.
+ "Not a Number", an invalid floating-point value. [constant NAN] has special properties, including that it is not equal to itself ([code]NAN == NAN[/code] returns [code]false[/code]). It is output by some invalid operations, such as dividing floating-point [code]0.0[/code] by [code]0.0[/code].
+ [b]Note:[/b] "Not a Number" is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer [code]0[/code] by [code]0[/code] will not result in [constant NAN] and will result in a run-time error instead.
</constant>
</constants>
</class>
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index 631a102130..5acb29e748 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -8,19 +8,17 @@
[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">
- <return type="PackedByteArray">
- </return>
+ <return type="PackedByteArray" />
<description>
Returns byte code for the script source code.
</description>
</method>
<method name="new" qualifiers="vararg">
- <return type="Variant">
- </return>
+ <return type="Variant" />
<description>
Returns a new instance of the script.
For example:
@@ -32,6 +30,4 @@
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/gdscript/doc_classes/GDScriptFunctionState.xml b/modules/gdscript/doc_classes/GDScriptFunctionState.xml
deleted file mode 100644
index 5e369b32d9..0000000000
--- a/modules/gdscript/doc_classes/GDScriptFunctionState.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GDScriptFunctionState" inherits="Reference" version="4.0">
- <brief_description>
- State of a function call after yielding.
- </brief_description>
- <description>
- FIXME: Outdated docs as of GDScript rewrite in 4.0.
- Calling [code]yield[/code] within a function will cause that function to yield and return its current state as an object of this type. The yielded function call can then be resumed later by calling [method resume] on this state object.
- </description>
- <tutorials>
- </tutorials>
- <methods>
- <method name="is_valid" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="extended_check" type="bool" default="false">
- </argument>
- <description>
- Check whether the function call may be resumed. This is not the case if the function state was already resumed.
- If [code]extended_check[/code] is enabled, it also checks if the associated script and object still exist. The extended check is done in debug mode as part of [method GDScriptFunctionState.resume], but you can use this if you know you may be trying to resume without knowing for sure the object and/or script have survived up to that point.
- </description>
- </method>
- <method name="resume">
- <return type="Variant">
- </return>
- <argument index="0" name="arg" type="Variant" default="null">
- </argument>
- <description>
- Resume execution of the yielded function call.
- If handed an argument, return the argument from the [code]yield[/code] call in the yielded function call. You can pass e.g. an [Array] to hand multiple arguments.
- This function returns what the resumed function call returns, possibly another function state if yielded again.
- </description>
- </method>
- </methods>
- <signals>
- <signal name="completed">
- <argument index="0" name="result" type="Variant">
- </argument>
- <description>
- </description>
- </signal>
- </signals>
- <constants>
- </constants>
-</class>
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index 7f7410a92c..6529154e5c 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -45,7 +45,7 @@ static bool _is_bin_symbol(char32_t c) {
return (c == '0' || c == '1');
}
-Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) {
+Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
Dictionary color_map;
Type next_type = NONE;
@@ -64,6 +64,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
bool in_function_args = false;
bool in_member_variable = false;
bool in_node_path = false;
+ bool in_annotation = false;
bool is_hex_notation = false;
bool is_bin_notation = false;
bool expect_type = false;
@@ -269,19 +270,21 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
col = keywords[word];
} else if (member_keywords.has(word)) {
col = member_keywords[word];
+ }
+
+ if (col != Color()) {
for (int k = j - 1; k >= 0; k--) {
if (str[k] == '.') {
- col = Color(); //member indexing not allowed
+ col = Color(); // keyword & member indexing not allowed
break;
} else if (str[k] > 32) {
break;
}
}
- }
-
- if (col != Color()) {
- in_keyword = true;
- keyword_color = col;
+ if (col != Color()) {
+ in_keyword = true;
+ keyword_color = col;
+ }
}
}
@@ -355,9 +358,18 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line)
in_node_path = false;
}
+ if (!in_annotation && in_region == -1 && str[j] == '@') {
+ in_annotation = true;
+ } else if (in_region != -1 || is_a_symbol) {
+ in_annotation = false;
+ }
+
if (in_node_path) {
next_type = NODE_PATH;
color = node_path_color;
+ } else if (in_annotation) {
+ next_type = ANNOTATION;
+ color = annotation_color;
} else if (in_keyword) {
next_type = KEYWORD;
color = keyword_color;
@@ -436,36 +448,32 @@ void GDScriptSyntaxHighlighter::_update_cache() {
color_regions.clear();
color_region_cache.clear();
- font_color = text_edit->get_theme_color("font_color");
- symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color");
- function_color = EDITOR_GET("text_editor/highlighting/function_color");
- number_color = EDITOR_GET("text_editor/highlighting/number_color");
- member_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+ font_color = text_edit->get_theme_color(SNAME("font_color"));
+ symbol_color = EDITOR_GET("text_editor/theme/highlighting/symbol_color");
+ function_color = EDITOR_GET("text_editor/theme/highlighting/function_color");
+ number_color = EDITOR_GET("text_editor/theme/highlighting/number_color");
+ member_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color");
/* Engine types. */
- const Color types_color = EDITOR_GET("text_editor/highlighting/engine_type_color");
+ const Color types_color = EDITOR_GET("text_editor/theme/highlighting/engine_type_color");
List<StringName> types;
ClassDB::get_class_list(&types);
- for (List<StringName>::Element *E = types.front(); E; E = E->next()) {
- String n = E->get();
- if (n.begins_with("_")) {
- n = n.substr(1, n.length());
- }
- keywords[n] = types_color;
+ for (const StringName &E : types) {
+ keywords[E] = types_color;
}
/* User types. */
- const Color usertype_color = EDITOR_GET("text_editor/highlighting/user_type_color");
+ const Color usertype_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color");
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
- for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) {
- keywords[String(E->get())] = usertype_color;
+ for (const StringName &E : global_classes) {
+ keywords[E] = usertype_color;
}
/* Autoloads. */
- Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
- const ProjectSettings::AutoloadInfo &info = E->value();
+ OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
+ const ProjectSettings::AutoloadInfo &info = E.value();
if (info.is_singleton) {
keywords[info.name] = usertype_color;
}
@@ -474,38 +482,41 @@ void GDScriptSyntaxHighlighter::_update_cache() {
const GDScriptLanguage *gdscript = GDScriptLanguage::get_singleton();
/* Core types. */
- const Color basetype_color = EDITOR_GET("text_editor/highlighting/base_type_color");
+ const Color basetype_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color");
List<String> core_types;
gdscript->get_core_type_words(&core_types);
- for (List<String>::Element *E = core_types.front(); E; E = E->next()) {
- keywords[E->get()] = basetype_color;
+ for (const String &E : core_types) {
+ keywords[StringName(E)] = basetype_color;
}
/* Reserved words. */
- const Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color");
+ const Color keyword_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color");
+ const Color control_flow_keyword_color = EDITOR_GET("text_editor/theme/highlighting/control_flow_keyword_color");
List<String> keyword_list;
gdscript->get_reserved_words(&keyword_list);
- for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) {
- keywords[E->get()] = keyword_color;
+ for (const String &E : keyword_list) {
+ if (gdscript->is_control_flow_keyword(E)) {
+ keywords[StringName(E)] = control_flow_keyword_color;
+ } else {
+ keywords[StringName(E)] = keyword_color;
+ }
}
/* Comments */
- const Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color");
+ const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color");
List<String> comments;
gdscript->get_comment_delimiters(&comments);
- for (List<String>::Element *E = comments.front(); E; E = E->next()) {
- String comment = E->get();
+ 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 == "");
}
/* Strings */
- const Color string_color = EDITOR_GET("text_editor/highlighting/string_color");
+ const Color string_color = EDITOR_GET("text_editor/theme/highlighting/string_color");
List<String> strings;
gdscript->get_string_delimiters(&strings);
- for (List<String>::Element *E = strings.front(); E; E = E->next()) {
- String string = E->get();
+ 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 == "");
@@ -514,14 +525,14 @@ void GDScriptSyntaxHighlighter::_update_cache() {
const Ref<Script> script = _get_edited_resource();
if (script.is_valid()) {
/* Member types. */
- const Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color");
+ const Color member_variable_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color");
StringName instance_base = script->get_instance_base_type();
if (instance_base != StringName()) {
List<PropertyInfo> plist;
ClassDB::get_property_list(instance_base, &plist);
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- String name = E->get().name;
- if (E->get().usage & PROPERTY_USAGE_CATEGORY || E->get().usage & PROPERTY_USAGE_GROUP || E->get().usage & PROPERTY_USAGE_SUBGROUP) {
+ for (const PropertyInfo &E : plist) {
+ String name = E.name;
+ if (E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP) {
continue;
}
if (name.find("/") != -1) {
@@ -532,39 +543,47 @@ void GDScriptSyntaxHighlighter::_update_cache() {
List<String> clist;
ClassDB::get_integer_constant_list(instance_base, &clist);
- for (List<String>::Element *E = clist.front(); E; E = E->next()) {
- member_keywords[E->get()] = member_variable_color;
+ for (const String &E : clist) {
+ member_keywords[E] = member_variable_color;
}
}
}
const String text_edit_color_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
- const bool default_theme = text_edit_color_theme == "Default";
+ const bool godot_2_theme = text_edit_color_theme == "Godot 2";
- if (default_theme || EditorSettings::get_singleton()->is_dark_theme()) {
+ if (godot_2_theme || EditorSettings::get_singleton()->is_dark_theme()) {
function_definition_color = Color(0.4, 0.9, 1.0);
node_path_color = Color(0.39, 0.76, 0.35);
+ annotation_color = Color(1.0, 0.7, 0.45);
} else {
function_definition_color = Color(0.0, 0.65, 0.73);
node_path_color = Color(0.32, 0.55, 0.29);
+ annotation_color = Color(0.8, 0.5, 0.25);
}
- EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color);
- EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color);
- if (text_edit_color_theme == "Adaptive" || default_theme) {
+ EDITOR_DEF("text_editor/theme/highlighting/gdscript/function_definition_color", function_definition_color);
+ EDITOR_DEF("text_editor/theme/highlighting/gdscript/node_path_color", node_path_color);
+ EDITOR_DEF("text_editor/theme/highlighting/gdscript/annotation_color", annotation_color);
+ if (text_edit_color_theme == "Default" || godot_2_theme) {
EditorSettings::get_singleton()->set_initial_value(
- "text_editor/highlighting/gdscript/function_definition_color",
+ "text_editor/theme/highlighting/gdscript/function_definition_color",
function_definition_color,
true);
EditorSettings::get_singleton()->set_initial_value(
- "text_editor/highlighting/gdscript/node_path_color",
+ "text_editor/theme/highlighting/gdscript/node_path_color",
node_path_color,
true);
+ EditorSettings::get_singleton()->set_initial_value(
+ "text_editor/theme/highlighting/gdscript/annotation_color",
+ annotation_color,
+ true);
}
- function_definition_color = EDITOR_GET("text_editor/highlighting/gdscript/function_definition_color");
- node_path_color = EDITOR_GET("text_editor/highlighting/gdscript/node_path_color");
- type_color = EDITOR_GET("text_editor/highlighting/base_type_color");
+ function_definition_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/function_definition_color");
+ node_path_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/node_path_color");
+ annotation_color = EDITOR_GET("text_editor/theme/highlighting/gdscript/annotation_color");
+ type_color = EDITOR_GET("text_editor/theme/highlighting/base_type_color");
}
void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
@@ -597,6 +616,6 @@ void GDScriptSyntaxHighlighter::add_color_region(const String &p_start_key, cons
Ref<EditorSyntaxHighlighter> GDScriptSyntaxHighlighter::_create() const {
Ref<GDScriptSyntaxHighlighter> syntax_highlighter;
- syntax_highlighter.instance();
+ syntax_highlighter.instantiate();
return syntax_highlighter;
}
diff --git a/modules/gdscript/editor/gdscript_highlighter.h b/modules/gdscript/editor/gdscript_highlighter.h
index 49357f3d2e..07f21b34ae 100644
--- a/modules/gdscript/editor/gdscript_highlighter.h
+++ b/modules/gdscript/editor/gdscript_highlighter.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,18 +42,19 @@ private:
Color color;
String start_key;
String end_key;
- bool line_only;
+ bool line_only = false;
};
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,
REGION,
NODE_PATH,
+ ANNOTATION,
SYMBOL,
NUMBER,
FUNCTION,
@@ -72,13 +73,14 @@ private:
Color number_color;
Color member_color;
Color node_path_color;
+ Color annotation_color;
Color type_color;
void add_color_region(const String &p_start_key, const String &p_end_key, const Color &p_color, bool p_line_only = false);
public:
virtual void _update_cache() override;
- virtual Dictionary _get_line_syntax_highlighting(int p_line) override;
+ virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override;
virtual String _get_name() const override;
virtual Array _get_supported_languages() const override;
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
index 944ed859f5..9d0d91162c 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,7 +44,7 @@ Error GDScriptEditorTranslationParserPlugin::parse_file(const String &p_path, Ve
// Search strings in AssignmentNode -> text = "__", hint_tooltip = "__" etc.
Error err;
- RES loaded_res = ResourceLoader::load(p_path, "", false, &err);
+ RES loaded_res = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
if (err) {
ERR_PRINT("Failed to load " + p_path);
return err;
diff --git a/modules/gdscript/editor/gdscript_translation_parser_plugin.h b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
index 5ea416d4cc..caa80fc24c 100644
--- a/modules/gdscript/editor/gdscript_translation_parser_plugin.h
+++ b/modules/gdscript/editor/gdscript_translation_parser_plugin.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,16 +31,15 @@
#ifndef GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
#define GDSCRIPT_TRANSLATION_PARSER_PLUGIN_H
-#include "core/set.h"
+#include "core/templates/set.h"
#include "editor/editor_translation_parser.h"
#include "modules/gdscript/gdscript_parser.h"
-#include "modules/regex/regex.h"
class GDScriptEditorTranslationParserPlugin : public EditorTranslationParserPlugin {
GDCLASS(GDScriptEditorTranslationParserPlugin, EditorTranslationParserPlugin);
- Vector<String> *ids;
- Vector<Vector<String>> *ids_ctx_plural;
+ Vector<String> *ids = nullptr;
+ Vector<Vector<String>> *ids_ctx_plural = nullptr;
// List of patterns used for extracting translation strings.
StringName tr_func = "tr";
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index e70e3f7272..68da588c3d 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,19 +32,23 @@
#include <stdint.h>
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
+#include "core/core_constants.h"
#include "core/core_string_names.h"
-#include "core/engine.h"
-#include "core/global_constants.h"
+#include "core/io/file_access.h"
#include "core/io/file_access_encrypted.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "gdscript_analyzer.h"
#include "gdscript_cache.h"
#include "gdscript_compiler.h"
#include "gdscript_parser.h"
#include "gdscript_warning.h"
+#ifdef TESTS_ENABLED
+#include "tests/gdscript_test_runner.h"
+#endif
+
///////////////////////////
GDScriptNativeClass::GDScriptNativeClass(const StringName &p_name) {
@@ -68,19 +72,32 @@ void GDScriptNativeClass::_bind_methods() {
}
Variant GDScriptNativeClass::_new() {
- Object *o = instance();
+ Object *o = instantiate();
ERR_FAIL_COND_V_MSG(!o, Variant(), "Class type: '" + String(name) + "' is not instantiable.");
- Reference *ref = Object::cast_to<Reference>(o);
- if (ref) {
- return REF(ref);
+ RefCounted *rc = Object::cast_to<RefCounted>(o);
+ if (rc) {
+ return REF(rc);
} else {
return o;
}
}
-Object *GDScriptNativeClass::instance() {
- return ClassDB::instance(name);
+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) {
@@ -94,19 +111,19 @@ void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance
p_script->implicit_initializer->call(p_instance, nullptr, 0, r_error);
}
-GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error) {
+GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) {
/* STEP 1, CREATE */
GDScriptInstance *instance = memnew(GDScriptInstance);
- instance->base_ref = p_isref;
+ instance->base_ref_counted = p_is_ref_counted;
instance->members.resize(member_indices.size());
instance->script = Ref<GDScript>(this);
instance->owner = p_owner;
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);
@@ -131,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) {
@@ -166,13 +185,13 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr
ERR_FAIL_COND_V(_baseptr->native.is_null(), Variant());
if (_baseptr->native.ptr()) {
- owner = _baseptr->native->instance();
+ owner = _baseptr->native->instantiate();
} else {
- owner = memnew(Reference); //by default, no base means use reference
+ owner = memnew(RefCounted); //by default, no base means use reference
}
ERR_FAIL_COND_V_MSG(!owner, Variant(), "Can't inherit from a virtual class.");
- Reference *r = Object::cast_to<Reference>(owner);
+ RefCounted *r = Object::cast_to<RefCounted>(owner);
if (r) {
ref = REF(r);
}
@@ -192,7 +211,7 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr
}
}
-bool GDScript::can_instance() const {
+bool GDScript::can_instantiate() const {
#ifdef TOOLS_ENABLED
return valid && (tool || ScriptServer::is_scripting_enabled());
#else
@@ -219,7 +238,7 @@ StringName GDScript::get_instance_base_type() const {
}
struct _GDScriptMemberSort {
- int index;
+ int index = 0;
StringName name;
_FORCE_INLINE_ bool operator<(const _GDScriptMemberSort &p_member) const { return index < p_member.index; }
};
@@ -231,53 +250,71 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) {
}
#endif
-void GDScript::get_script_method_list(List<MethodInfo> *p_list) const {
+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++) {
- mi.arguments.push_back(func->get_argument_type(i));
+ PropertyInfo arginfo = func->get_argument_type(i);
+#ifdef TOOLS_ENABLED
+ arginfo.name = func->get_argument_name(i);
+#endif
+ mi.arguments.push_back(arginfo);
}
mi.return_val = func->get_return_type();
- p_list->push_back(mi);
+ r_list->push_back(mi);
+ }
+ if (!p_include_base) {
+ return;
}
current = current->_base;
}
}
-void GDScript::get_script_property_list(List<PropertyInfo> *p_list) const {
+void GDScript::get_script_method_list(List<MethodInfo> *r_list) const {
+ _get_script_method_list(r_list, true);
+}
+
+void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const {
const GDScript *sptr = this;
List<PropertyInfo> props;
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);
}
msort.sort();
- msort.invert();
+ msort.reverse();
for (int i = 0; i < msort.size(); i++) {
props.push_front(sptr->member_info[msort[i].name]);
}
+ if (!p_include_base) {
+ break;
+ }
sptr = sptr->_base;
}
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- p_list->push_back(E->get());
+ for (const PropertyInfo &E : props) {
+ r_list->push_back(E);
}
}
+void GDScript::get_script_property_list(List<PropertyInfo> *r_list) const {
+ _get_script_property_list(r_list, true);
+}
+
bool GDScript::has_method(const StringName &p_method) const {
return member_functions.has(p_method);
}
@@ -324,21 +361,21 @@ 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 instanced 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 instanced 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() + "'" + ".");
}
}
Callable::CallError unchecked_error;
- return _create_instance(nullptr, 0, p_this, Object::cast_to<Reference>(p_this) != nullptr, unchecked_error);
+ return _create_instance(nullptr, 0, p_this, Object::cast_to<RefCounted>(p_this) != nullptr, unchecked_error);
}
PlaceHolderScriptInstance *GDScript::placeholder_instance_create(Object *p_this) {
#ifdef TOOLS_ENABLED
PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(GDScriptLanguage::get_singleton(), Ref<Script>(this), p_this));
placeholders.insert(si);
- _update_exports();
+ _update_exports(nullptr, false, si);
return si;
#else
return nullptr;
@@ -375,17 +412,194 @@ 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 (List<PropertyInfo>::Element *E = members_cache.front(); E; E = E->next()) {
- propnames.push_back(E->get());
+ for (const PropertyInfo &E : members_cache) {
+ propnames.push_back(E);
+ }
+}
+
+void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) {
+ if (_owner) {
+ _owner->_add_doc(p_inner_class);
+ } else {
+ for (int i = 0; i < docs.size(); i++) {
+ if (docs[i].name == p_inner_class.name) {
+ docs.remove(i);
+ break;
+ }
+ }
+ docs.append(p_inner_class);
}
}
+
+void GDScript::_clear_doc() {
+ docs.clear();
+ doc = DocData::ClassDoc();
+}
+
+void GDScript::_update_doc() {
+ _clear_doc();
+
+ doc.script_path = "\"" + get_path().get_slice("://", 1) + "\"";
+ if (!name.is_empty()) {
+ doc.name = name;
+ } else {
+ doc.name = doc.script_path;
+ }
+
+ if (_owner) {
+ doc.name = _owner->doc.name + "." + doc.name;
+ doc.script_path = doc.script_path + "." + doc.name;
+ }
+
+ doc.is_script_doc = true;
+
+ if (base.is_valid() && base->is_valid()) {
+ if (base->doc.name != String()) {
+ doc.inherits = base->doc.name;
+ } else {
+ doc.inherits = base->get_instance_base_type();
+ }
+ } else if (native.is_valid()) {
+ doc.inherits = native->get_name();
+ }
+
+ doc.brief_description = doc_brief_description;
+ doc.description = doc_description;
+ doc.tutorials = doc_tutorials;
+
+ for (const KeyValue<String, DocData::EnumDoc> &E : doc_enums) {
+ if (E.value.description != "") {
+ doc.enums[E.key] = E.value.description;
+ }
+ }
+
+ List<MethodInfo> methods;
+ _get_script_method_list(&methods, false);
+ for (int i = 0; i < methods.size(); i++) {
+ // Ignore internal methods.
+ if (methods[i].name[0] == '@') {
+ continue;
+ }
+
+ DocData::MethodDoc method_doc;
+ const String &class_name = methods[i].name;
+ if (member_functions.has(class_name)) {
+ GDScriptFunction *fn = member_functions[class_name];
+
+ // Change class name if return type is script reference.
+ GDScriptDataType return_type = fn->get_return_type();
+ if (return_type.kind == GDScriptDataType::GDSCRIPT) {
+ methods[i].return_val.class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(return_type.script_type));
+ }
+
+ // Change class name if argument is script reference.
+ for (int j = 0; j < fn->get_argument_count(); j++) {
+ GDScriptDataType arg_type = fn->get_argument_type(j);
+ if (arg_type.kind == GDScriptDataType::GDSCRIPT) {
+ methods[i].arguments[j].class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(arg_type.script_type));
+ }
+ }
+ }
+ if (doc_functions.has(methods[i].name)) {
+ DocData::method_doc_from_methodinfo(method_doc, methods[i], doc_functions[methods[i].name]);
+ } else {
+ DocData::method_doc_from_methodinfo(method_doc, methods[i], String());
+ }
+ doc.methods.push_back(method_doc);
+ }
+
+ List<PropertyInfo> props;
+ _get_script_property_list(&props, false);
+ for (int i = 0; i < props.size(); i++) {
+ ScriptMemberInfo scr_member_info;
+ scr_member_info.propinfo = props[i];
+ scr_member_info.propinfo.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ if (member_indices.has(props[i].name)) {
+ const MemberInfo &mi = member_indices[props[i].name];
+ scr_member_info.setter = mi.setter;
+ scr_member_info.getter = mi.getter;
+ if (mi.data_type.kind == GDScriptDataType::GDSCRIPT) {
+ scr_member_info.propinfo.class_name = _get_gdscript_reference_class_name(
+ Object::cast_to<GDScript>(mi.data_type.script_type));
+ }
+ }
+ if (member_default_values.has(props[i].name)) {
+ scr_member_info.has_default_value = true;
+ scr_member_info.default_value = member_default_values[props[i].name];
+ }
+ if (doc_variables.has(props[i].name)) {
+ scr_member_info.doc_string = doc_variables[props[i].name];
+ }
+
+ DocData::PropertyDoc prop_doc;
+ DocData::property_doc_from_scriptmemberinfo(prop_doc, scr_member_info);
+ doc.properties.push_back(prop_doc);
+ }
+
+ List<MethodInfo> signals;
+ _get_script_signal_list(&signals, false);
+ 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], doc_signals[signals[i].name]);
+ } else {
+ DocData::signal_doc_from_methodinfo(signal_doc, signals[i], String());
+ }
+ doc.signals.push_back(signal_doc);
+ }
+
+ 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)) {
+ 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]);
+ }
+ }
+ }
+ 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) {
+ 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);
+ doc.constants.push_back(constant_doc);
+ break;
+ }
+ }
+ }
+ if (!is_enum) {
+ DocData::ConstantDoc constant_doc;
+ String doc_description;
+ 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);
+ doc.constants.push_back(constant_doc);
+ }
+ }
+
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ E.value->_update_doc();
+ }
+
+ _add_doc(doc);
+}
#endif
-bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
+bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderScriptInstance *p_instance_to_update) {
#ifdef TOOLS_ENABLED
static Vector<GDScript *> base_caches;
@@ -426,9 +640,9 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
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 == "" || 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);
@@ -522,15 +736,19 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) {
}
}
- if (placeholders.size()) { //hm :(
+ if ((changed || p_instance_to_update) && placeholders.size()) { //hm :(
// update placeholders if any
Map<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
- for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(propnames, values);
+ if (changed) {
+ for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
+ E->get()->update(propnames, values);
+ }
+ } else {
+ p_instance_to_update->update(propnames, values);
}
}
@@ -566,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();
}
}
@@ -598,10 +824,10 @@ Error GDScript::reload(bool p_keep_state) {
{
String source_path = path;
- if (source_path.empty()) {
+ if (source_path.is_empty()) {
source_path = get_path();
}
- if (!source_path.empty()) {
+ if (!source_path.is_empty()) {
MutexLock lock(GDScriptCache::singleton->lock);
if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) {
GDScriptCache::singleton->shallow_gdscript_cache[source_path] = this;
@@ -614,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.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);
}
@@ -626,10 +852,14 @@ 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(), false, ERR_HANDLER_SCRIPT);
+ e = e->next();
}
- // TODO: Show all error messages.
- _err_print_error("GDScript::reload", path.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_FAIL_V(ERR_PARSE_ERROR);
}
@@ -638,31 +868,34 @@ Error GDScript::reload(bool p_keep_state) {
GDScriptCompiler compiler;
err = compiler.compile(&parser, this, p_keep_state);
+#ifdef TOOLS_ENABLED
+ _update_doc();
+#endif
+
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.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;
}
}
#ifdef DEBUG_ENABLED
- for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) {
- const GDScriptWarning &warning = E->get();
+ 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();
@@ -676,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;
}
}
}
@@ -690,68 +923,10 @@ void GDScript::get_members(Set<StringName> *p_members) {
}
}
-Vector<ScriptNetData> GDScript::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> GDScript::get_rpc_methods() const {
return rpc_functions;
}
-uint16_t GDScript::get_rpc_method_id(const StringName &p_method) const {
- for (int i = 0; i < rpc_functions.size(); i++) {
- if (rpc_functions[i].name == p_method) {
- return i;
- }
- }
- return UINT16_MAX;
-}
-
-StringName GDScript::get_rpc_method(const uint16_t p_rpc_method_id) const {
- if (p_rpc_method_id >= rpc_functions.size()) {
- return StringName();
- }
- return rpc_functions[p_rpc_method_id].name;
-}
-
-MultiplayerAPI::RPCMode GDScript::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
- if (p_rpc_method_id >= rpc_functions.size()) {
- return MultiplayerAPI::RPC_MODE_DISABLED;
- }
- return rpc_functions[p_rpc_method_id].mode;
-}
-
-MultiplayerAPI::RPCMode GDScript::get_rpc_mode(const StringName &p_method) const {
- return get_rpc_mode_by_id(get_rpc_method_id(p_method));
-}
-
-Vector<ScriptNetData> GDScript::get_rset_properties() const {
- return rpc_variables;
-}
-
-uint16_t GDScript::get_rset_property_id(const StringName &p_variable) const {
- for (int i = 0; i < rpc_variables.size(); i++) {
- if (rpc_variables[i].name == p_variable) {
- return i;
- }
- }
- return UINT16_MAX;
-}
-
-StringName GDScript::get_rset_property(const uint16_t p_rset_member_id) const {
- if (p_rset_member_id >= rpc_variables.size()) {
- return StringName();
- }
- return rpc_variables[p_rset_member_id].name;
-}
-
-MultiplayerAPI::RPCMode GDScript::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
- if (p_rset_member_id >= rpc_variables.size()) {
- return MultiplayerAPI::RPC_MODE_DISABLED;
- }
- return rpc_variables[p_rset_member_id].mode;
-}
-
-MultiplayerAPI::RPCMode GDScript::get_rset_mode(const StringName &p_variable) const {
- return get_rset_mode_by_id(get_rset_property_id(p_variable));
-}
-
Variant GDScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *top = this;
while (top) {
@@ -812,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() {
@@ -838,10 +1013,10 @@ Error GDScript::load_source_code(const String &p_path) {
ERR_FAIL_COND_V(err, err);
}
- int len = f->get_len();
+ uint64_t len = f->get_length();
sourcef.resize(len + 1);
uint8_t *w = sourcef.ptrw();
- int r = f->get_buffer(w, len);
+ uint64_t r = f->get_buffer(w, len);
f->close();
memdelete(f);
ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN);
@@ -865,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;
}
}
@@ -911,42 +1086,54 @@ bool GDScript::has_script_signal(const StringName &p_signal) const {
return false;
}
-void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
- for (const Map<StringName, Vector<StringName>>::Element *E = _signals.front(); E; E = E->next()) {
+void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const {
+ 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_signals->push_back(mi);
+ r_list->push_back(mi);
+ }
+
+ if (!p_include_base) {
+ return;
}
if (base.is_valid()) {
- base->get_script_signal_list(r_signals);
+ base->get_script_signal_list(r_list);
}
#ifdef TOOLS_ENABLED
else if (base_cache.is_valid()) {
- base_cache->get_script_signal_list(r_signals);
+ base_cache->get_script_signal_list(r_list);
}
#endif
}
+void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
+ _get_script_signal_list(r_signals, true);
+}
+
+String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript) {
+ ERR_FAIL_NULL_V(p_gdscript, String());
+
+ String class_name;
+ while (p_gdscript) {
+ if (class_name == "") {
+ class_name = p_gdscript->get_script_class_name();
+ } else {
+ class_name = p_gdscript->get_script_class_name() + "." + class_name;
+ }
+ p_gdscript = p_gdscript->_owner;
+ }
+ return class_name;
+}
+
GDScript::GDScript() :
script_list(this) {
- valid = false;
- subclass_count = 0;
- initializer = nullptr;
- _base = nullptr;
- _owner = nullptr;
- tool = false;
-#ifdef TOOLS_ENABLED
- source_changed_cache = false;
- placeholder_fallback_enabled = false;
-#endif
-
#ifdef DEBUG_ENABLED
{
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
@@ -963,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);
}
@@ -991,34 +1178,20 @@ void GDScript::_save_orphaned_subclasses() {
void GDScript::_init_rpc_methods_properties() {
// Copy the base rpc methods so we don't mask their IDs.
rpc_functions.clear();
- rpc_variables.clear();
if (base.is_valid()) {
rpc_functions = base->rpc_functions;
- rpc_variables = base->rpc_variables;
}
GDScript *cscript = this;
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()) {
- if (E->get()->get_rpc_mode() != MultiplayerAPI::RPC_MODE_DISABLED) {
- ScriptNetData nd;
- nd.name = E->key();
- nd.mode = E->get()->get_rpc_mode();
- if (-1 == rpc_functions.find(nd)) {
- rpc_functions.push_back(nd);
- }
- }
- }
- // RSet
- for (Map<StringName, MemberInfo>::Element *E = cscript->member_indices.front(); E; E = E->next()) {
- if (E->get().rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
- ScriptNetData nd;
- nd.name = E->key();
- nd.mode = E->get().rpc_mode;
- if (-1 == rpc_variables.find(nd)) {
- rpc_variables.push_back(nd);
+ 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);
}
}
}
@@ -1035,8 +1208,7 @@ void GDScript::_init_rpc_methods_properties() {
}
// Sort so we are 100% that they are always the same.
- rpc_functions.sort_custom<SortNetData>();
- rpc_variables.sort_custom<SortNetData>();
+ rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
}
GDScript::~GDScript() {
@@ -1051,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.
@@ -1061,6 +1233,13 @@ GDScript::~GDScript() {
_save_orphaned_subclasses();
+#ifdef TOOLS_ENABLED
+ // Clearing inner class doc, script doc only cleared when the script source deleted.
+ if (_owner) {
+ _clear_doc();
+ }
+#endif
+
#ifdef DEBUG_ENABLED
{
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
@@ -1086,22 +1265,33 @@ 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.is_type(p_value)) {
- // Try conversion
- Callable::CallError ce;
- const Variant *value = &p_value;
- Variant converted = Variant::construct(member->data_type.builtin_type, &value, 1, ce);
- if (ce.error == Callable::CallError::CALL_OK) {
- members.write[member->index] = converted;
- return true;
- } else {
- return false;
+ if (member->data_type.has_type) {
+ if (member->data_type.builtin_type == Variant::ARRAY && member->data_type.has_container_element_type()) {
+ // Typed array.
+ if (p_value.get_type() == Variant::ARRAY) {
+ return VariantInternal::get_array(&members.write[member->index])->typed_assign(p_value);
+ } else {
+ return false;
+ }
+ } else if (!member->data_type.is_type(p_value)) {
+ // Try conversion
+ Callable::CallError ce;
+ const Variant *value = &p_value;
+ Variant converted;
+ Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce);
+ if (ce.error == Callable::CallError::CALL_OK) {
+ members.write[member->index] = converted;
+ return true;
+ } else {
+ return false;
+ }
}
- } else {
- members.write[member->index] = p_value;
}
+ members.write[member->index] = p_value;
}
return true;
}
@@ -1262,16 +1452,16 @@ 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);
}
msort.sort();
- msort.invert();
+ msort.reverse();
for (int i = 0; i < msort.size(); i++) {
props.push_front(sptr->member_info[msort[i].name]);
}
@@ -1279,19 +1469,19 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const
sptr = sptr->_base;
}
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- p_properties->push_back(E->get());
+ for (const PropertyInfo &E : props) {
+ p_properties->push_back(E);
}
}
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);
@@ -1376,46 +1566,10 @@ ScriptLanguage *GDScriptInstance::get_language() {
return GDScriptLanguage::get_singleton();
}
-Vector<ScriptNetData> GDScriptInstance::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> GDScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
-uint16_t GDScriptInstance::get_rpc_method_id(const StringName &p_method) const {
- return script->get_rpc_method_id(p_method);
-}
-
-StringName GDScriptInstance::get_rpc_method(const uint16_t p_rpc_method_id) const {
- return script->get_rpc_method(p_rpc_method_id);
-}
-
-MultiplayerAPI::RPCMode GDScriptInstance::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
- return script->get_rpc_mode_by_id(p_rpc_method_id);
-}
-
-MultiplayerAPI::RPCMode GDScriptInstance::get_rpc_mode(const StringName &p_method) const {
- return script->get_rpc_mode(p_method);
-}
-
-Vector<ScriptNetData> GDScriptInstance::get_rset_properties() const {
- return script->get_rset_properties();
-}
-
-uint16_t GDScriptInstance::get_rset_property_id(const StringName &p_variable) const {
- return script->get_rset_property_id(p_variable);
-}
-
-StringName GDScriptInstance::get_rset_property(const uint16_t p_rset_member_id) const {
- return script->get_rset_property(p_rset_member_id);
-}
-
-MultiplayerAPI::RPCMode GDScriptInstance::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
- return script->get_rset_mode_by_id(p_rset_member_id);
-}
-
-MultiplayerAPI::RPCMode GDScriptInstance::get_rset_mode(const StringName &p_variable) const {
- return script->get_rset_mode(p_variable);
-}
-
void GDScriptInstance::reload_members() {
#ifdef DEBUG_ENABLED
@@ -1425,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;
}
}
@@ -1437,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
@@ -1446,7 +1600,7 @@ void GDScriptInstance::reload_members() {
GDScriptInstance::GDScriptInstance() {
owner = nullptr;
- base_ref = false;
+ base_ref_counted = false;
}
GDScriptInstance::~GDScriptInstance() {
@@ -1500,31 +1654,25 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) {
void GDScriptLanguage::init() {
//populate global constants
- int gcc = GlobalConstants::get_global_constant_count();
+ int gcc = CoreConstants::get_global_constant_count();
for (int i = 0; i < gcc; i++) {
- _add_global(StaticCString::create(GlobalConstants::get_global_constant_name(i)), GlobalConstants::get_global_constant_value(i));
+ _add_global(StaticCString::create(CoreConstants::get_global_constant_name(i)), CoreConstants::get_global_constant_value(i));
}
_add_global(StaticCString::create("PI"), Math_PI);
_add_global(StaticCString::create("TAU"), Math_TAU);
- _add_global(StaticCString::create("INF"), Math_INF);
- _add_global(StaticCString::create("NAN"), Math_NAN);
+ _add_global(StaticCString::create("INF"), INFINITY);
+ _add_global(StaticCString::create("NAN"), NAN);
//populate native classes
List<StringName> class_list;
ClassDB::get_class_list(&class_list);
- for (List<StringName>::Element *E = class_list.front(); E; E = E->next()) {
- StringName n = E->get();
- String s = String(n);
- if (s.begins_with("_")) {
- n = s.substr(1, s.length());
- }
-
+ for (const StringName &n : class_list) {
if (globals.has(n)) {
continue;
}
- Ref<GDScriptNativeClass> nc = memnew(GDScriptNativeClass(E->get()));
+ Ref<GDScriptNativeClass> nc = memnew(GDScriptNativeClass(n));
_add_global(n, nc);
}
@@ -1532,9 +1680,13 @@ void GDScriptLanguage::init() {
List<Engine::Singleton> singletons;
Engine::get_singleton()->get_singletons(&singletons);
- for (List<Engine::Singleton>::Element *E = singletons.front(); E; E = E->next()) {
- _add_global(E->get().name, E->get().ptr);
+ for (const Engine::Singleton &E : singletons) {
+ _add_global(E.name, E.ptr);
}
+
+#ifdef TESTS_ENABLED
+ GDScriptTests::GDScriptTestRunner::handle_cmdline();
+#endif
}
String GDScriptLanguage::get_type() const {
@@ -1672,10 +1824,10 @@ void GDScriptLanguage::reload_all_scripts() {
scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
- for (List<Ref<GDScript>>::Element *E = scripts.front(); E; E = E->next()) {
- print_verbose("GDScript: Reloading: " + E->get()->get_path());
- E->get()->load_source_code(E->get()->get_path());
- E->get()->reload(true);
+ for (Ref<GDScript> &script : scripts) {
+ print_verbose("GDScript: Reloading: " + script->get_path());
+ script->load_source_code(script->get_path());
+ script->reload(true);
}
#endif
}
@@ -1704,21 +1856,21 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order
- for (List<Ref<GDScript>>::Element *E = scripts.front(); E; E = E->next()) {
- bool reload = E->get() == p_script || to_reload.has(E->get()->get_base());
+ for (Ref<GDScript> &script : scripts) {
+ bool reload = script == p_script || to_reload.has(script->get_base());
if (!reload) {
continue;
}
- to_reload.insert(E->get(), Map<ObjectID, List<Pair<StringName, Variant>>>());
+ to_reload.insert(script, Map<ObjectID, List<Pair<StringName, Variant>>>());
if (!p_soft_reload) {
//save state and remove script from instances
- Map<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[E->get()];
+ Map<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[script];
- while (E->get()->instances.front()) {
- Object *obj = E->get()->instances.front()->get();
+ while (script->instances.front()) {
+ Object *obj = script->instances.front()->get();
//save instance info
List<Pair<StringName, Variant>> state;
if (obj->get_script_instance()) {
@@ -1731,8 +1883,8 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
//same thing for placeholders
#ifdef TOOLS_ENABLED
- while (E->get()->placeholders.size()) {
- Object *obj = E->get()->placeholders.front()->get()->get_owner();
+ while (script->placeholders.size()) {
+ Object *obj = script->placeholders.front()->get()->get_owner();
//save instance info
if (obj->get_script_instance()) {
@@ -1742,27 +1894,27 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
obj->set_script(Variant());
} else {
// no instance found. Let's remove it so we don't loop forever
- E->get()->placeholders.erase(E->get()->placeholders.front()->get());
+ script->placeholders.erase(script->placeholders.front()->get());
}
}
#endif
- for (Map<ObjectID, List<Pair<StringName, Variant>>>::Element *F = E->get()->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;
}
@@ -1888,11 +2040,27 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const {
w++;
}
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- p_words->push_back(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)));
+ List<StringName> functions;
+ GDScriptUtilityFunctions::get_function_list(&functions);
+
+ for (const StringName &E : functions) {
+ p_words->push_back(String(E));
}
}
+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";
+}
+
bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
return p_type == "GDScript";
}
@@ -1914,9 +2082,9 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
if (err == OK) {
const GDScriptParser::ClassNode *c = parser.get_tree();
if (r_icon_path) {
- if (c->icon_path.empty() || c->icon_path.is_abs_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();
}
}
@@ -1926,7 +2094,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
GDScriptParser subparser;
while (subclass) {
if (subclass->extends_used) {
- if (!subclass->extends_path.empty()) {
+ if (!subclass->extends_path.is_empty()) {
if (subclass->extends.size() == 0) {
get_global_class_name(subclass->extends_path, r_base_type);
subclass = nullptr;
@@ -1940,11 +2108,11 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
}
String subsource = subfile->get_as_utf8_string();
- if (subsource.empty()) {
+ if (subsource.is_empty()) {
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();
}
@@ -1982,7 +2150,7 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
break;
}
} else {
- *r_base_type = "Reference";
+ *r_base_type = "RefCounted";
subclass = nullptr;
}
}
@@ -2052,22 +2220,22 @@ 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();
script->unreference();
}
- singleton = NULL;
+ singleton = nullptr;
}
void GDScriptLanguage::add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass) {
@@ -2090,7 +2258,7 @@ Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_na
/*************** RESOURCE ***************/
-RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderGDScript::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) {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
@@ -2102,7 +2270,7 @@ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_ori
if (script.is_null()) {
// Don't fail loading because of parsing error.
- script.instance();
+ script.instantiate();
}
if (r_error) {
@@ -2137,7 +2305,7 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S
ERR_FAIL_COND_MSG(!file, "Cannot open file '" + p_path + "'.");
String source = file->get_as_utf8_string();
- if (source.empty()) {
+ if (source.is_empty()) {
return;
}
@@ -2146,8 +2314,8 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S
return;
}
- for (const List<String>::Element *E = parser.get_dependencies().front(); E; E = E->next()) {
- p_dependencies->push_back(E->get());
+ for (const String &E : parser.get_dependencies()) {
+ p_dependencies->push_back(E);
}
}
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 79317ff846..ade4f247c9 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,13 +33,14 @@
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
+#include "core/doc_data.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/script_language.h"
+#include "core/object/script_language.h"
#include "gdscript_function.h"
-class GDScriptNativeClass : public Reference {
- GDCLASS(GDScriptNativeClass, Reference);
+class GDScriptNativeClass : public RefCounted {
+ GDCLASS(GDScriptNativeClass, RefCounted);
StringName name;
@@ -50,20 +51,19 @@ protected:
public:
_FORCE_INLINE_ const StringName &get_name() const { return name; }
Variant _new();
- Object *instance();
+ Object *instantiate();
GDScriptNativeClass(const StringName &p_name);
};
class GDScript : public Script {
GDCLASS(GDScript, Script);
- bool tool;
- bool valid;
+ bool tool = false;
+ bool valid = false;
struct MemberInfo {
- int index;
+ int index = 0;
StringName setter;
StringName getter;
- MultiplayerAPI::RPCMode rpc_mode;
GDScriptDataType data_type;
};
@@ -71,44 +71,55 @@ class GDScript : public Script {
friend class GDScriptFunction;
friend class GDScriptAnalyzer;
friend class GDScriptCompiler;
- friend class GDScriptFunctions;
friend class GDScriptLanguage;
+ friend struct GDScriptUtilityFunctionsDefinitions;
Ref<GDScriptNativeClass> native;
Ref<GDScript> base;
- GDScript *_base; //fast pointer access
- GDScript *_owner; //for subclasses
+ GDScript *_base = nullptr; //fast pointer access
+ GDScript *_owner = nullptr; //for subclasses
- Set<StringName> members; //members are just indices to the instanced script.
+ Set<StringName> members; //members are just indices to the instantiated script.
Map<StringName, Variant> constants;
Map<StringName, GDScriptFunction *> member_functions;
- Map<StringName, MemberInfo> member_indices; //members are just indices to the instanced 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<ScriptNetData> rpc_functions;
- Vector<ScriptNetData> rpc_variables;
+ Vector<Multiplayer::RPCConfig> rpc_functions;
#ifdef TOOLS_ENABLED
Map<StringName, int> member_lines;
-
Map<StringName, Variant> member_default_values;
-
List<PropertyInfo> members_cache;
Map<StringName, Variant> member_default_values_cache;
Ref<GDScript> base_cache;
Set<ObjectID> inheriters_cache;
- bool source_changed_cache;
- bool placeholder_fallback_enabled;
+ bool source_changed_cache = false;
+ bool placeholder_fallback_enabled = false;
void _update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames);
+ DocData::ClassDoc doc;
+ Vector<DocData::ClassDoc> docs;
+ String doc_brief_description;
+ String doc_description;
+ Vector<DocData::TutorialDoc> doc_tutorials;
+ Map<String, String> doc_functions;
+ Map<String, String> doc_variables;
+ Map<String, String> doc_constants;
+ Map<String, String> doc_signals;
+ Map<String, DocData::EnumDoc> doc_enums;
+ void _clear_doc();
+ void _update_doc();
+ void _add_doc(const DocData::ClassDoc &p_inner_class);
+
#endif
Map<StringName, PropertyInfo> member_info;
GDScriptFunction *implicit_initializer = nullptr;
GDScriptFunction *initializer = nullptr; //direct pointer to new , faster to locate
- int subclass_count;
+ int subclass_count = 0;
Set<Object *> instances;
//exported members
String source;
@@ -119,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_isref, 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;
@@ -136,11 +149,18 @@ class GDScript : public Script {
#endif
- bool _update_exports(bool *r_err = nullptr, bool p_recursive_call = false);
+ bool _update_exports(bool *r_err = nullptr, bool p_recursive_call = false, PlaceHolderScriptInstance *p_instance_to_update = nullptr);
void _save_orphaned_subclasses();
void _init_rpc_methods_properties();
+ void _get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const;
+ void _get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const;
+ void _get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const;
+
+ // This method will map the class name from "RefCounted" to "MyClass.InnerClass".
+ static String _get_gdscript_reference_class_name(const GDScript *p_gdscript);
+
protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
bool _set(const StringName &p_name, const Variant &p_value);
@@ -177,7 +197,7 @@ public:
StringName debug_get_member_by_index(int p_idx) const;
Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
- virtual bool can_instance() const override;
+ virtual bool can_instantiate() const override;
virtual Ref<Script> get_base_script() const override;
@@ -191,6 +211,12 @@ public:
virtual void set_source_code(const String &p_code) override;
virtual void update_exports() override;
+#ifdef TOOLS_ENABLED
+ virtual const Vector<DocData::ClassDoc> &get_documentation() const override {
+ return docs;
+ }
+#endif // TOOLS_ENABLED
+
virtual Error reload(bool p_keep_state = false) override;
void set_script_path(const String &p_path) { path = p_path; } //because subclasses need a path too...
@@ -221,17 +247,7 @@ public:
virtual void get_constants(Map<StringName, Variant> *p_constants) override;
virtual void get_members(Set<StringName> *p_members) override;
- virtual Vector<ScriptNetData> get_rpc_methods() const override;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const override;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const override;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const override;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
-
- virtual Vector<ScriptNetData> get_rset_properties() const override;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const override;
- virtual StringName get_rset_property(const uint16_t p_variable_id) const override;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const override;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) 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; }
@@ -244,8 +260,9 @@ public:
class GDScriptInstance : public ScriptInstance {
friend class GDScript;
friend class GDScriptFunction;
- friend class GDScriptFunctions;
+ friend class GDScriptLambdaCallable;
friend class GDScriptCompiler;
+ friend struct GDScriptUtilityFunctionsDefinitions;
ObjectID owner_id;
Object *owner;
@@ -254,7 +271,7 @@ class GDScriptInstance : public ScriptInstance {
Map<StringName, int> member_indices_cache; //used only for hot script reloading
#endif
Vector<Variant> members;
- bool base_ref;
+ bool base_ref_counted;
SelfList<GDScriptFunctionState>::List pending_func_states;
@@ -283,17 +300,7 @@ public:
void reload_members();
- virtual Vector<ScriptNetData> get_rpc_methods() const;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
-
- virtual Vector<ScriptNetData> get_rset_properties() const;
- virtual uint16_t get_rset_property_id(const StringName &p_variable) const;
- virtual StringName get_rset_property(const uint16_t p_variable_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
GDScriptInstance();
~GDScriptInstance();
@@ -434,17 +441,19 @@ public:
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const;
+ virtual bool is_control_flow_keyword(String p_keywords) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual String _get_processed_template(const String &p_template, const String &p_base_class_name) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const;
+ virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
- virtual bool can_inherit_from_file() { return true; }
+ virtual bool supports_documentation() const;
+ virtual bool can_inherit_from_file() const { return true; }
virtual int find_function(const String &p_function, const String &p_code) const;
virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const;
virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint);
@@ -502,7 +511,7 @@ public:
class ResourceFormatLoaderGDScript : 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, bool p_no_cache = false);
+ 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;
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 943a49060f..cd8fd361c5 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,50 +30,47 @@
#include "gdscript_analyzer.h"
-#include "core/class_db.h"
-#include "core/hash_map.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
-#include "core/os/file_access.h"
-#include "core/project_settings.h"
-#include "core/script_language.h"
+#include "core/object/class_db.h"
+#include "core/object/script_language.h"
+#include "core/templates/hash_map.h"
#include "gdscript.h"
+#include "gdscript_utility_functions.h"
-// TODO: Move this to a central location (maybe core?).
-static HashMap<StringName, StringName> underscore_map;
-static const char *underscore_classes[] = {
- "ClassDB",
- "Directory",
- "Engine",
- "File",
- "Geometry",
- "GodotSharp",
- "JSON",
- "Marshalls",
- "Mutex",
- "OS",
- "ResourceLoader",
- "ResourceSaver",
- "Semaphore",
- "Thread",
- "VisualScriptEditor",
- nullptr,
-};
-static StringName get_real_class_name(const StringName &p_source) {
- if (underscore_map.empty()) {
- const char **class_name = underscore_classes;
- while (*class_name != nullptr) {
- underscore_map[*class_name] = String("_") + *class_name;
- class_name++;
- }
- }
- if (underscore_map.has(p_source)) {
- return underscore_map[p_source];
- }
- return p_source;
-}
-
-void GDScriptAnalyzer::cleanup() {
- underscore_map.clear();
+static MethodInfo info_from_utility_func(const StringName &p_function) {
+ ERR_FAIL_COND_V(!Variant::has_utility_function(p_function), MethodInfo());
+
+ MethodInfo info(p_function);
+
+ if (Variant::has_utility_function_return_value(p_function)) {
+ info.return_val.type = Variant::get_utility_function_return_type(p_function);
+ if (info.return_val.type == Variant::NIL) {
+ info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ }
+
+ if (Variant::is_utility_function_vararg(p_function)) {
+ info.flags |= METHOD_FLAG_VARARG;
+ } else {
+ for (int i = 0; i < Variant::get_utility_function_argument_count(p_function); i++) {
+ PropertyInfo pi;
+#ifdef DEBUG_METHODS_ENABLED
+ pi.name = Variant::get_utility_function_argument_name(p_function, i);
+#else
+ pi.name = "arg" + itos(i + 1);
+#endif
+ pi.type = Variant::get_utility_function_argument_type(p_function, i);
+ if (pi.type == Variant::NIL) {
+ pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ info.arguments.push_back(pi);
+ }
+ }
+
+ return info;
}
static GDScriptParser::DataType make_callable_type(const MethodInfo &p_info) {
@@ -116,11 +113,10 @@ static GDScriptParser::DataType make_native_enum_type(const StringName &p_native
type.is_meta_type = true;
List<StringName> enum_values;
- StringName real_native_name = get_real_class_name(p_native_class);
- ClassDB::get_enum_constants(real_native_name, p_enum_name, &enum_values);
+ ClassDB::get_enum_constants(p_native_class, p_enum_name, &enum_values);
- for (const List<StringName>::Element *E = enum_values.front(); E != nullptr; E = E->next()) {
- type.enum_values[E->get()] = ClassDB::get_integer_constant(real_native_name, E->get());
+ for (const StringName &E : enum_values) {
+ type.enum_values[E] = ClassDB::get_integer_constant(p_native_class, E);
}
return type;
@@ -136,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
@@ -152,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.
@@ -162,12 +244,13 @@ 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) {
result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
result.kind = GDScriptParser::DataType::NATIVE;
- result.native_type = "Reference";
+ result.native_type = "RefCounted";
} else {
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -175,7 +258,10 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
int extends_index = 0;
- if (!p_class->extends_path.empty()) {
+ 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);
@@ -190,7 +276,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
base = parser->get_parser()->head->get_datatype();
} else {
- if (p_class->extends.empty()) {
+ if (p_class->extends.is_empty()) {
return ERR_PARSE_ERROR;
}
const StringName &name = p_class->extends[extends_index++];
@@ -233,7 +319,7 @@ Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class,
push_error(vformat(R"(Could not resolve super class inheritance from "%s".)", name), p_class);
return err;
}
- } else if (class_exists(name) && ClassDB::can_instance(get_real_class_name(name))) {
+ } else if (class_exists(name) && ClassDB::can_instantiate(name)) {
base.kind = GDScriptParser::DataType::NATIVE;
base.native_type = name;
} else {
@@ -342,7 +428,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
result.type_source = result.ANNOTATED_EXPLICIT;
result.builtin_type = Variant::OBJECT;
- if (p_type->type_chain.empty()) {
+ if (p_type->type_chain.is_empty()) {
// void.
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = Variant::NIL;
@@ -379,6 +465,16 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
result.kind = GDScriptParser::DataType::BUILTIN;
result.builtin_type = GDScriptParser::get_builtin_type(first);
+
+ if (result.builtin_type == Variant::ARRAY) {
+ 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);
+ }
+ }
} else if (class_exists(first)) {
// Native engine classes.
result.kind = GDScriptParser::DataType::NATIVE;
@@ -402,7 +498,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
return GDScriptParser::DataType();
}
result = ref->get_parser()->head->get_datatype();
- } else if (ClassDB::has_enum(get_real_class_name(parser->current_class->base_type.native_type), first)) {
+ } else if (ClassDB::has_enum(parser->current_class->base_type.native_type, first)) {
// Native enum in current class.
result = make_native_enum_type(parser->current_class->base_type.native_type, first);
} else {
@@ -429,8 +525,28 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
case GDScriptParser::ClassNode::Member::CONSTANT:
if (member.constant->get_datatype().is_meta_type) {
result = member.constant->get_datatype();
+ result.is_meta_type = false;
found = true;
break;
+ } else if (Ref<Script>(member.constant->initializer->reduced_value).is_valid()) {
+ Ref<GDScript> gdscript = member.constant->initializer->reduced_value;
+ if (gdscript.is_valid()) {
+ Ref<GDScriptParserRef> ref = get_parser_for(gdscript->get_path());
+ if (ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED) != OK) {
+ push_error(vformat(R"(Could not parse script from "%s".)", gdscript->get_path()), p_type);
+ return GDScriptParser::DataType();
+ }
+ result = ref->get_parser()->head->get_datatype();
+ result.is_meta_type = false;
+ } else {
+ Ref<GDScript> script = member.constant->initializer->reduced_value;
+ result.kind = GDScriptParser::DataType::SCRIPT;
+ result.builtin_type = Variant::OBJECT;
+ result.script_type = script;
+ result.script_path = script->get_path();
+ result.native_type = script->get_instance_base_type();
+ }
+ break;
}
[[fallthrough]];
default:
@@ -465,7 +581,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
} else if (result.kind == GDScriptParser::DataType::NATIVE) {
// Only enums allowed for native.
- if (ClassDB::has_enum(get_real_class_name(result.native_type), p_type->type_chain[1]->name)) {
+ if (ClassDB::has_enum(result.native_type, p_type->type_chain[1]->name)) {
if (p_type->type_chain.size() > 2) {
push_error(R"(Enums cannot contain nested types.)", p_type->type_chain[2]);
} else {
@@ -479,6 +595,10 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type
}
}
+ if (result.builtin_type != Variant::ARRAY && p_type->container_type != nullptr) {
+ push_error("Only arrays can specify the collection element type.", p_type);
+ }
+
p_type->set_datatype(result);
return result;
}
@@ -497,13 +617,29 @@ 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;
+ GDScriptParser::DataType specified_type;
+ if (member.variable->datatype_specifier != nullptr) {
+ specified_type = resolve_datatype(member.variable->datatype_specifier);
+ specified_type.is_meta_type = false;
+ }
+
if (member.variable->initializer != nullptr) {
member.variable->set_datatype(datatype); // Allow recursive usage.
reduce_expression(member.variable->initializer);
+ if ((member.variable->infer_datatype || (member.variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && member.variable->initializer->type == GDScriptParser::Node::ARRAY) {
+ // Typed array.
+ GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(member.variable->initializer);
+ // Can only infer typed array if it has elements.
+ if ((member.variable->infer_datatype && array->elements.size() > 0) || member.variable->datatype_specifier != nullptr) {
+ update_array_literal_element_type(specified_type, array);
+ }
+ }
datatype = member.variable->initializer->get_datatype();
if (datatype.type_source != GDScriptParser::DataType::UNDETECTED) {
datatype.type_source = GDScriptParser::DataType::INFERRED;
@@ -511,8 +647,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
}
if (member.variable->datatype_specifier != nullptr) {
- datatype = resolve_datatype(member.variable->datatype_specifier);
- datatype.is_meta_type = false;
+ datatype = specified_type;
if (member.variable->initializer != nullptr) {
if (!is_type_compatible(datatype, member.variable->initializer->get_datatype(), true)) {
@@ -522,6 +657,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
} else {
// TODO: Add warning.
mark_node_unsafe(member.variable->initializer);
+ member.variable->use_conversion_assign = true;
}
} else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
#ifdef DEBUG_ENABLED
@@ -531,6 +667,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
if (member.variable->initializer->get_datatype().is_variant()) {
// TODO: Warn unsafe assign.
mark_node_unsafe(member.variable->initializer);
+ member.variable->use_conversion_assign = true;
}
}
} else if (member.variable->infer_datatype) {
@@ -548,42 +685,45 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
datatype.is_constant = false;
member.variable->set_datatype(datatype);
- if (!datatype.has_no_type()) {
- // TODO: Move this out into a routine specific to validate annotations.
- if (member.variable->export_info.hint == PROPERTY_HINT_TYPE_STRING) {
- // @export annotation.
- switch (datatype.kind) {
- case GDScriptParser::DataType::BUILTIN:
- member.variable->export_info.hint_string = Variant::get_type_name(datatype.builtin_type);
- break;
- case GDScriptParser::DataType::NATIVE:
- if (ClassDB::is_parent_class(get_real_class_name(datatype.native_type), "Resource")) {
- member.variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
- member.variable->export_info.hint_string = get_real_class_name(datatype.native_type);
- } else {
- push_error(R"(Export type can only be built-in or a resource.)", member.variable);
- }
- break;
- default:
- // TODO: Allow custom user resources.
- push_error(R"(Export type can only be built-in or a resource.)", member.variable);
- break;
- }
- }
+
+ // Apply annotations.
+ for (GDScriptParser::AnnotationNode *&E : member.variable->annotations) {
+ E->apply(parser, member.variable);
}
} 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 datatype = member.constant->get_datatype();
+ GDScriptParser::DataType specified_type;
+
+ if (member.constant->datatype_specifier != nullptr) {
+ specified_type = resolve_datatype(member.constant->datatype_specifier);
+ specified_type.is_meta_type = false;
+ }
+
+ 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);
+
+ // Can only infer typed array if it has elements.
+ if (array->elements.size() > 0 || (member.constant->datatype_specifier != nullptr && specified_type.has_container_element_type())) {
+ update_array_literal_element_type(specified_type, array);
+ }
+ } else if (member.constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(member.constant->initializer));
+ }
+
if (!member.constant->initializer->is_constant) {
push_error(R"(Initializer for a constant must be a constant expression.)", member.constant->initializer);
}
if (member.constant->datatype_specifier != nullptr) {
- datatype = resolve_datatype(member.constant->datatype_specifier);
- datatype.is_meta_type = false;
+ datatype = specified_type;
if (!is_type_compatible(datatype, member.constant->initializer->get_datatype(), true)) {
push_error(vformat(R"(Value of type "%s" cannot be initialized to constant of type "%s".)", member.constant->initializer->get_datatype().to_string(), datatype.to_string()), member.constant->initializer);
@@ -597,8 +737,15 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
datatype.is_constant = true;
member.constant->set_datatype(datatype);
+
+ // Apply annotations.
+ for (GDScriptParser::AnnotationNode *&E : member.constant->annotations) {
+ E->apply(parser, member.constant);
+ }
} 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;
@@ -611,8 +758,15 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
signal_type.builtin_type = Variant::SIGNAL;
member.signal->set_datatype(signal_type);
+
+ // Apply annotations.
+ for (GDScriptParser::AnnotationNode *&E : member.signal->annotations) {
+ E->apply(parser, member.signal);
+ }
} 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;
@@ -653,12 +807,19 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
current_enum = nullptr;
member.m_enum->set_datatype(enum_type);
+
+ // Apply annotations.
+ for (GDScriptParser::AnnotationNode *&E : member.m_enum->annotations) {
+ E->apply(parser, member.m_enum);
+ }
} break;
case GDScriptParser::ClassNode::Member::FUNCTION:
resolve_function_signature(member.function);
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;
@@ -672,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 {
@@ -684,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;
@@ -713,14 +877,35 @@ 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);
+
+ // 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());
+ }
- resolve_function_body(member.function);
+ resolve_function_body(member.variable->setter);
+ }
+ }
+ }
}
parser->current_class = previous_class;
@@ -735,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);
+ }
+ }
+ }
+ }
}
}
@@ -815,13 +1063,14 @@ void GDScriptAnalyzer::resolve_node(GDScriptParser::Node *p_node) {
case GDScriptParser::Node::DICTIONARY:
case GDScriptParser::Node::GET_NODE:
case GDScriptParser::Node::IDENTIFIER:
+ case GDScriptParser::Node::LAMBDA:
case GDScriptParser::Node::LITERAL:
case GDScriptParser::Node::PRELOAD:
case GDScriptParser::Node::SELF:
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:
@@ -854,7 +1103,12 @@ void GDScriptAnalyzer::resolve_function_signature(GDScriptParser::FunctionNode *
parser->push_warning(p_function->parameters[i]->identifier, GDScriptWarning::UNUSED_PARAMETER, p_function->identifier->name, p_function->parameters[i]->identifier->name);
}
is_shadowing(p_function->parameters[i]->identifier, "function parameter");
-#endif
+#endif // DEBUG_ENABLED
+#ifdef TOOLS_ENABLED
+ if (p_function->parameters[i]->default_value && p_function->parameters[i]->default_value->is_constant) {
+ p_function->default_arg_values.push_back(p_function->parameters[i]->default_value->reduced_value);
+ }
+#endif // TOOLS_ENABLED
}
if (p_function->identifier->name == "_init") {
@@ -863,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);
@@ -972,17 +1229,19 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
if (!call->arguments[i]->is_constant) {
all_is_constant = false;
- } else {
+ } else if (all_is_constant) {
args.write[i] = call->arguments[i]->reduced_value;
}
GDScriptParser::DataType arg_type = call->arguments[i]->get_datatype();
- if (arg_type.kind != GDScriptParser::DataType::BUILTIN) {
- all_is_constant = false;
- push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
- } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) {
- all_is_constant = false;
- push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
+ if (!arg_type.is_variant()) {
+ if (arg_type.kind != GDScriptParser::DataType::BUILTIN) {
+ all_is_constant = false;
+ push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
+ } else if (arg_type.builtin_type != Variant::INT && arg_type.builtin_type != Variant::FLOAT) {
+ all_is_constant = false;
+ push_error(vformat(R"*(Invalid argument for "range()" call. Argument %d should be int or float but "%s" was given.)*", i + 1, arg_type.to_string()), call->arguments[i]);
+ }
}
}
@@ -1005,21 +1264,43 @@ void GDScriptAnalyzer::resolve_for(GDScriptParser::ForNode *p_for) {
}
}
- GDScriptParser::DataType list_type;
- list_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
- list_type.kind = GDScriptParser::DataType::BUILTIN;
- list_type.builtin_type = Variant::ARRAY;
- p_for->list->set_datatype(list_type);
+ if (p_for->list->is_constant) {
+ p_for->list->set_datatype(type_from_variant(p_for->list->reduced_value, p_for->list));
+ } else {
+ GDScriptParser::DataType list_type;
+ list_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+ list_type.kind = GDScriptParser::DataType::BUILTIN;
+ list_type.builtin_type = Variant::ARRAY;
+ p_for->list->set_datatype(list_type);
+ }
}
}
}
- 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());
@@ -1041,8 +1322,23 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
GDScriptParser::DataType type;
type.kind = GDScriptParser::DataType::VARIANT; // By default.
+ GDScriptParser::DataType specified_type;
+ if (p_variable->datatype_specifier != nullptr) {
+ specified_type = resolve_datatype(p_variable->datatype_specifier);
+ specified_type.is_meta_type = false;
+ }
+
if (p_variable->initializer != nullptr) {
reduce_expression(p_variable->initializer);
+ if ((p_variable->infer_datatype || (p_variable->datatype_specifier != nullptr && specified_type.has_container_element_type())) && p_variable->initializer->type == GDScriptParser::Node::ARRAY) {
+ // Typed array.
+ GDScriptParser::ArrayNode *array = static_cast<GDScriptParser::ArrayNode *>(p_variable->initializer);
+ // Can only infer typed array if it has elements.
+ if ((p_variable->infer_datatype && array->elements.size() > 0) || p_variable->datatype_specifier != nullptr) {
+ update_array_literal_element_type(specified_type, array);
+ }
+ }
+
type = p_variable->initializer->get_datatype();
if (p_variable->infer_datatype) {
@@ -1066,7 +1362,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
}
if (p_variable->datatype_specifier != nullptr) {
- type = resolve_datatype(p_variable->datatype_specifier);
+ type = specified_type;
type.is_meta_type = false;
if (p_variable->initializer != nullptr) {
@@ -1077,6 +1373,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
} else {
// TODO: Add warning.
mark_node_unsafe(p_variable->initializer);
+ p_variable->use_conversion_assign = true;
}
#ifdef DEBUG_ENABLED
} else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
@@ -1086,6 +1383,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
if (p_variable->initializer->get_datatype().is_variant()) {
// TODO: Warn unsafe assign.
mark_node_unsafe(p_variable->initializer);
+ p_variable->use_conversion_assign = true;
}
}
} else if (p_variable->infer_datatype) {
@@ -1112,19 +1410,26 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant) {
GDScriptParser::DataType type;
- reduce_expression(p_constant->initializer);
+ if (p_constant->initializer != nullptr) {
+ reduce_expression(p_constant->initializer);
+ if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) {
+ const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer));
+ } else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) {
+ const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer));
+ }
- if (!p_constant->initializer->is_constant) {
- push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
- }
+ if (!p_constant->initializer->is_constant) {
+ push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer);
+ }
- type = p_constant->initializer->get_datatype();
+ type = p_constant->initializer->get_datatype();
#ifdef DEBUG_ENABLED
- if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
- parser->push_warning(p_constant->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_constant->initializer)->function_name);
- }
+ if (p_constant->initializer->type == GDScriptParser::Node::CALL && type.kind == GDScriptParser::DataType::BUILTIN && type.builtin_type == Variant::NIL) {
+ parser->push_warning(p_constant->initializer, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_constant->initializer)->function_name);
+ }
#endif
+ }
if (p_constant->datatype_specifier != nullptr) {
GDScriptParser::DataType explicit_type = resolve_datatype(p_constant->datatype_specifier);
@@ -1159,7 +1464,10 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant
void GDScriptAnalyzer::resolve_assert(GDScriptParser::AssertNode *p_assert) {
reduce_expression(p_assert->condition);
if (p_assert->message != nullptr) {
- reduce_literal(p_assert->message);
+ reduce_expression(p_assert->message);
+ if (!p_assert->message->is_constant || p_assert->message->reduced_value.get_type() != Variant::STRING) {
+ push_error(R"(Expected constant string for assert error message.)", p_assert->message);
+ }
}
p_assert->set_datatype(p_assert->condition->get_datatype());
@@ -1280,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) {
@@ -1293,14 +1600,32 @@ 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 (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();
} else {
// Return type is null by default.
@@ -1310,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) {
@@ -1361,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));
@@ -1375,6 +1701,9 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
case GDScriptParser::Node::IDENTIFIER:
reduce_identifier(static_cast<GDScriptParser::IdentifierNode *>(p_expression));
break;
+ case GDScriptParser::Node::LAMBDA:
+ reduce_lambda(static_cast<GDScriptParser::LambdaNode *>(p_expression));
+ break;
case GDScriptParser::Node::LITERAL:
reduce_literal(static_cast<GDScriptParser::LiteralNode *>(p_expression));
break;
@@ -1422,22 +1751,9 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre
}
void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) {
- bool all_is_constant = true;
-
for (int i = 0; i < p_array->elements.size(); i++) {
GDScriptParser::ExpressionNode *element = p_array->elements[i];
reduce_expression(element);
- all_is_constant = all_is_constant && element->is_constant;
- }
-
- if (all_is_constant) {
- Array array;
- array.resize(p_array->elements.size());
- for (int i = 0; i < p_array->elements.size(); i++) {
- array[i] = p_array->elements[i]->reduced_value;
- }
- p_array->is_constant = true;
- p_array->reduced_value = array;
}
// It's array in any case.
@@ -1450,6 +1766,53 @@ void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) {
p_array->set_datatype(arr_type);
}
+// When an array literal is stored (or passed as function argument) to a typed context, we then assume the array is typed.
+// This function determines which type is that (if any).
+void GDScriptAnalyzer::update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal) {
+ GDScriptParser::DataType array_type = p_array_literal->get_datatype();
+ if (p_array_literal->elements.size() == 0) {
+ // Empty array literal, just make the same type as the storage.
+ array_type.set_container_element_type(p_base_type.get_container_element_type());
+ } else {
+ // Check if elements match.
+ bool all_same_type = true;
+ bool all_have_type = true;
+
+ GDScriptParser::DataType element_type;
+ for (int i = 0; i < p_array_literal->elements.size(); i++) {
+ if (i == 0) {
+ element_type = p_array_literal->elements[0]->get_datatype();
+ } else {
+ GDScriptParser::DataType this_element_type = p_array_literal->elements[i]->get_datatype();
+ if (this_element_type.has_no_type()) {
+ all_same_type = false;
+ all_have_type = false;
+ break;
+ } else if (element_type != this_element_type) {
+ if (!is_type_compatible(element_type, this_element_type, false)) {
+ if (is_type_compatible(this_element_type, element_type, false)) {
+ // This element is a super-type to the previous type, so we use the super-type.
+ element_type = this_element_type;
+ } else {
+ // It's incompatible.
+ all_same_type = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ 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);
+ }
+ }
+ // Update the type on the value itself.
+ p_array_literal->set_datatype(array_type);
+}
+
void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assignment) {
reduce_expression(p_assignment->assignee);
reduce_expression(p_assignment->assigned_value);
@@ -1458,27 +1821,37 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
return;
}
- if (p_assignment->assignee->get_datatype().is_constant) {
+ GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype();
+
+ // Check if assigned value is an array literal, so we can make it a typed array too if appropriate.
+ if (assignee_type.has_container_element_type() && p_assignment->assigned_value->type == GDScriptParser::Node::ARRAY) {
+ update_array_literal_element_type(assignee_type, static_cast<GDScriptParser::ArrayNode *>(p_assignment->assigned_value));
+ }
+
+ GDScriptParser::DataType assigned_value_type = p_assignment->assigned_value->get_datatype();
+
+ if (assignee_type.is_constant) {
push_error("Cannot assign a new value to a constant.", p_assignment->assignee);
}
- if (!p_assignment->assignee->get_datatype().is_variant() && !p_assignment->assigned_value->get_datatype().is_variant()) {
+ if (!assignee_type.is_variant() && assigned_value_type.is_hard_type()) {
bool compatible = true;
- GDScriptParser::DataType op_type = p_assignment->assigned_value->get_datatype();
+ GDScriptParser::DataType op_type = assigned_value_type;
if (p_assignment->operation != GDScriptParser::AssignmentNode::OP_NONE) {
- op_type = get_operation_type(p_assignment->variant_op, p_assignment->assignee->get_datatype(), p_assignment->assigned_value->get_datatype(), compatible, p_assignment->assigned_value);
+ op_type = get_operation_type(p_assignment->variant_op, assignee_type, assigned_value_type, compatible, p_assignment->assigned_value);
}
if (compatible) {
- compatible = is_type_compatible(p_assignment->assignee->get_datatype(), op_type, true);
+ compatible = is_type_compatible(assignee_type, op_type, true);
if (!compatible) {
- if (p_assignment->assignee->get_datatype().is_hard_type()) {
+ if (assignee_type.is_hard_type()) {
// Try reverse test since it can be a masked subtype.
- if (!is_type_compatible(op_type, p_assignment->assignee->get_datatype(), true)) {
- push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", p_assignment->assigned_value->get_datatype().to_string(), p_assignment->assignee->get_datatype().to_string()), p_assignment->assigned_value);
+ if (!is_type_compatible(op_type, assignee_type, true)) {
+ push_error(vformat(R"(Cannot assign a value of type "%s" to a target of type "%s".)", assigned_value_type.to_string(), assignee_type.to_string()), p_assignment->assigned_value);
} else {
// TODO: Add warning.
mark_node_unsafe(p_assignment);
+ p_assignment->use_conversion_assign = true;
}
} else {
// TODO: Warning in this case.
@@ -1486,12 +1859,15 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
}
} else {
- push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", p_assignment->assignee->get_datatype().to_string(), p_assignment->assigned_value->get_datatype().to_string()), p_assignment);
+ push_error(vformat(R"(Invalid operands "%s" and "%s" for assignment operator.)", assignee_type.to_string(), assigned_value_type.to_string()), p_assignment);
}
}
- if (p_assignment->assignee->get_datatype().has_no_type() || p_assignment->assigned_value->get_datatype().is_variant()) {
+ if (assignee_type.has_no_type() || assigned_value_type.is_variant()) {
mark_node_unsafe(p_assignment);
+ if (assignee_type.is_hard_type()) {
+ p_assignment->use_conversion_assign = true;
+ }
}
if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
@@ -1507,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 = p_assignment->assigned_value->get_datatype();
- 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 = p_assignment->assigned_value->get_datatype();
- 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;
@@ -1531,12 +1913,10 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
}
}
- GDScriptParser::DataType assignee_type = p_assignment->assignee->get_datatype();
- GDScriptParser::DataType assigned_type = p_assignment->assigned_value->get_datatype();
#ifdef DEBUG_ENABLED
- if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_type.kind == GDScriptParser::DataType::BUILTIN && assigned_type.builtin_type == Variant::NIL) {
+ if (p_assignment->assigned_value->type == GDScriptParser::Node::CALL && assigned_value_type.kind == GDScriptParser::DataType::BUILTIN && assigned_value_type.builtin_type == Variant::NIL) {
parser->push_warning(p_assignment->assigned_value, GDScriptWarning::VOID_ASSIGNMENT, static_cast<GDScriptParser::CallNode *>(p_assignment->assigned_value)->function_name);
- } else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_type.builtin_type == Variant::FLOAT) {
+ } else if (assignee_type.is_hard_type() && assignee_type.builtin_type == Variant::INT && assigned_value_type.builtin_type == Variant::FLOAT) {
parser->push_warning(p_assignment->assigned_value, GDScriptWarning::NARROWING_CONVERSION);
}
#endif
@@ -1549,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);
@@ -1607,7 +1996,7 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
if (p_binary_op->reduced_value.get_type() == Variant::STRING) {
push_error(vformat(R"(%s in operator %s.)", p_binary_op->reduced_value, Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
} else {
- push_error(vformat(R"(Invalid operands to operator %s, %s and %s.".)",
+ push_error(vformat(R"(Invalid operands to operator %s, %s and %s.)",
Variant::get_operator_name(p_binary_op->variant_op),
Variant::get_type_name(p_binary_op->left_operand->reduced_value.get_type()),
Variant::get_type_name(p_binary_op->right_operand->reduced_value.get_type())),
@@ -1643,10 +2032,10 @@ void GDScriptAnalyzer::reduce_binary_op(GDScriptParser::BinaryOpNode *p_binary_o
} else {
if (p_binary_op->variant_op < Variant::OP_MAX) {
bool valid = false;
- result = get_operation_type(p_binary_op->variant_op, p_binary_op->left_operand->get_datatype(), right_type, valid, p_binary_op);
+ result = get_operation_type(p_binary_op->variant_op, left_type, right_type, valid, p_binary_op);
if (!valid) {
- push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", p_binary_op->left_operand->get_datatype().to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
+ push_error(vformat(R"(Invalid operands "%s" and "%s" for "%s" operator.)", left_type.to_string(), right_type.to_string(), Variant::get_operator_name(p_binary_op->variant_op)), p_binary_op);
}
} else {
if (p_binary_op->operation == GDScriptParser::BinaryOpNode::OP_TYPE_TEST) {
@@ -1678,10 +2067,14 @@ 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++) {
reduce_expression(p_call->arguments[i]);
+ if (p_call->arguments[i]->type == GDScriptParser::Node::ARRAY) {
+ arrays[i] = static_cast<GDScriptParser::ArrayNode *>(p_call->arguments[i]);
+ }
all_is_constant = all_is_constant && p_call->arguments[i]->is_constant;
}
@@ -1692,7 +2085,6 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
// Call to name directly.
StringName function_name = p_call->function_name;
Variant::Type builtin_type = GDScriptParser::get_builtin_type(function_name);
- GDScriptFunctions::Function builtin_function = GDScriptParser::get_builtin_function(function_name);
if (builtin_type < Variant::VARIANT_MAX) {
// Is a builtin constructor.
@@ -1705,7 +2097,29 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
call_type.native_type = function_name; // "Object".
}
- if (all_is_constant) {
+ bool safe_to_fold = true;
+ switch (builtin_type) {
+ // Those are stored by reference so not suited for compile-time construction.
+ // Because in this case they would be the same reference in all constructed values.
+ case Variant::OBJECT:
+ case Variant::DICTIONARY:
+ case Variant::ARRAY:
+ case Variant::PACKED_BYTE_ARRAY:
+ case Variant::PACKED_INT32_ARRAY:
+ case Variant::PACKED_INT64_ARRAY:
+ case Variant::PACKED_FLOAT32_ARRAY:
+ case Variant::PACKED_FLOAT64_ARRAY:
+ case Variant::PACKED_STRING_ARRAY:
+ case Variant::PACKED_VECTOR2_ARRAY:
+ case Variant::PACKED_VECTOR3_ARRAY:
+ case Variant::PACKED_COLOR_ARRAY:
+ safe_to_fold = false;
+ break;
+ default:
+ break;
+ }
+
+ if (all_is_constant && safe_to_fold) {
// Construct here.
Vector<const Variant *> args;
for (int i = 0; i < p_call->arguments.size(); i++) {
@@ -1713,7 +2127,8 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
}
Callable::CallError err;
- Variant value = Variant::construct(builtin_type, (const Variant **)args.ptr(), args.size(), err);
+ Variant value;
+ Variant::construct(builtin_type, value, (const Variant **)args.ptr(), args.size(), err);
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
@@ -1765,9 +2180,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
Variant::get_constructor_list(builtin_type, &constructors);
bool match = false;
- for (const List<MethodInfo>::Element *E = constructors.front(); E != nullptr; E = E->next()) {
- const MethodInfo &info = E->get();
-
+ for (const MethodInfo &info : constructors) {
if (p_call->arguments.size() < info.arguments.size() - info.default_arguments.size()) {
continue;
}
@@ -1813,10 +2226,52 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
}
p_call->set_datatype(call_type);
return;
- } else if (builtin_function < GDScriptFunctions::FUNC_MAX) {
- MethodInfo function_info = GDScriptFunctions::get_info(builtin_function);
+ } else if (GDScriptUtilityFunctions::function_exists(function_name)) {
+ MethodInfo function_info = GDScriptUtilityFunctions::get_function_info(function_name);
+
+ if (all_is_constant && GDScriptUtilityFunctions::is_function_constant(function_name)) {
+ // Can call on compilation.
+ Vector<const Variant *> args;
+ for (int i = 0; i < p_call->arguments.size(); i++) {
+ args.push_back(&(p_call->arguments[i]->reduced_value));
+ }
+
+ Variant value;
+ Callable::CallError err;
+ GDScriptUtilityFunctions::get_function(function_name)(&value, (const Variant **)args.ptr(), args.size(), err);
+
+ switch (err.error) {
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
+ PropertyInfo wrong_arg = function_info.arguments[err.argument];
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
+ type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
+ p_call->arguments[err.argument]);
+ } break;
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD:
+ push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
+ break;
+ case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ break; // Can't happen in a builtin constructor.
+ case Callable::CallError::CALL_OK:
+ p_call->is_constant = true;
+ p_call->reduced_value = value;
+ break;
+ }
+ } else {
+ validate_call_arg(function_info, p_call);
+ }
+ p_call->set_datatype(type_from_property(function_info.return_val));
+ return;
+ } else if (Variant::has_utility_function(function_name)) {
+ MethodInfo function_info = info_from_utility_func(function_name);
- if (all_is_constant && GDScriptFunctions::is_deterministic(builtin_function)) {
+ if (all_is_constant && Variant::get_utility_function_type(function_name) == Variant::UTILITY_FUNC_TYPE_MATH) {
// Can call on compilation.
Vector<const Variant *> args;
for (int i = 0; i < p_call->arguments.size(); i++) {
@@ -1825,23 +2280,23 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
Variant value;
Callable::CallError err;
- GDScriptFunctions::call(builtin_function, (const Variant **)args.ptr(), args.size(), value, err);
+ Variant::call_utility_function(function_name, &value, (const Variant **)args.ptr(), args.size(), err);
switch (err.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
PropertyInfo wrong_arg = function_info.arguments[err.argument];
- push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", GDScriptFunctions::get_func_name(builtin_function), err.argument + 1,
+ push_error(vformat(R"*(Invalid argument for "%s()" function: argument %d should be %s but is %s.)*", function_name, err.argument + 1,
type_from_property(wrong_arg).to_string(), p_call->arguments[err.argument]->get_datatype().to_string()),
p_call->arguments[err.argument]);
} break;
case Callable::CallError::CALL_ERROR_INVALID_METHOD:
- push_error(vformat(R"(Invalid call for function "%s".)", GDScriptFunctions::get_func_name(builtin_function)), p_call);
+ push_error(vformat(R"(Invalid call for function "%s".)", function_name), p_call);
break;
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
- push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+ push_error(vformat(R"*(Too many arguments for "%s()" call. Expected at most %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
- push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", GDScriptFunctions::get_func_name(builtin_function), err.expected, p_call->arguments.size()), p_call);
+ push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
@@ -1864,12 +2319,20 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
if (p_call->is_super) {
base_type = parser->current_class->base_type;
+ base_type.is_meta_type = false;
is_self = true;
} else if (callee_type == GDScriptParser::Node::IDENTIFIER) {
base_type = parser->current_class->get_datatype();
+ base_type.is_meta_type = false;
is_self = true;
} else if (callee_type == GDScriptParser::Node::SUBSCRIPT) {
GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(p_call->callee);
+ if (subscript->base == nullptr) {
+ // Invalid syntax, error already set on parser.
+ p_call->set_datatype(call_type);
+ mark_node_unsafe(p_call);
+ return;
+ }
if (!subscript->is_attribute) {
// Invalid call. Error already sent in parser.
// TODO: Could check if Callable here.
@@ -1877,9 +2340,23 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
mark_node_unsafe(p_call);
return;
}
- reduce_expression(subscript->base);
+ if (subscript->attribute == nullptr) {
+ // Invalid call. Error already sent in parser.
+ p_call->set_datatype(call_type);
+ mark_node_unsafe(p_call);
+ return;
+ }
- base_type = subscript->base->get_datatype();
+ GDScriptParser::IdentifierNode *base_id = nullptr;
+ if (subscript->base->type == GDScriptParser::Node::IDENTIFIER) {
+ base_id = static_cast<GDScriptParser::IdentifierNode *>(subscript->base);
+ }
+ if (base_id && GDScriptParser::get_builtin_type(base_id->name) < Variant::VARIANT_MAX) {
+ base_type = make_builtin_meta_type(GDScriptParser::get_builtin_type(base_id->name));
+ } else {
+ reduce_expression(subscript->base);
+ base_type = subscript->base->get_datatype();
+ }
} else {
// Invalid call. Error already sent in parser.
// TODO: Could check if Callable here too.
@@ -1895,10 +2372,22 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
List<GDScriptParser::DataType> par_types;
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 (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.value);
+ }
+ }
validate_call_arg(par_types, default_arg_count, is_vararg, p_call);
if (is_self && parser->current_function != nullptr && parser->current_function->is_static && !is_static) {
push_error(vformat(R"*(Cannot call non-static function "%s()" from static function "%s()".)*", p_call->function_name, parser->current_function->identifier->name), p_call->callee);
+ } else if (!is_self && base_type.is_meta_type && !is_static) {
+ base_type.is_meta_type = false; // For `to_string()`.
+ push_error(vformat(R"*(Cannot call non-static function "%s()" on the class "%s" directly. Make an instance instead.)*", p_call->function_name, base_type.to_string()), p_call->callee);
+ } else if (is_self && !is_static && !lambda_stack.is_empty()) {
+ push_error(vformat(R"*(Cannot call non-static function "%s()" from a lambda function.)*", p_call->function_name), p_call->callee);
}
call_type = return_type;
@@ -1924,20 +2413,20 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
push_error(vformat(R"*(Name "%s" called as a function but is a "%s".)*", p_call->function_name, callee_datatype.to_string()), p_call->callee);
}
#ifdef DEBUG_ENABLED
- } else if (!is_self) {
+ } else if (!is_self && !(base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN)) {
parser->push_warning(p_call, GDScriptWarning::UNSAFE_METHOD_ACCESS, p_call->function_name, base_type.to_string());
mark_node_unsafe(p_call);
#endif
}
}
}
- if (!found && is_self) {
+ if (!found && (is_self || (base_type.is_hard_type() && base_type.kind == GDScriptParser::DataType::BUILTIN))) {
String base_name = is_self && !p_call->is_super ? "self" : base_type.to_string();
push_error(vformat(R"*(Function "%s()" not found in base %s.)*", p_call->function_name, base_name), p_call->is_super ? p_call : p_call->callee);
}
}
- 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);
}
@@ -1984,8 +2473,6 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) {
}
void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary) {
- bool all_is_constant = true;
-
HashMap<Variant, GDScriptParser::ExpressionNode *, VariantHasher, VariantComparator> elements;
for (int i = 0; i < p_dictionary->elements.size(); i++) {
@@ -1994,7 +2481,6 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti
reduce_expression(element.key);
}
reduce_expression(element.value);
- all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
if (element.key->is_constant) {
if (elements.has(element.key->reduced_value)) {
@@ -2005,16 +2491,6 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti
}
}
- if (all_is_constant) {
- Dictionary dict;
- for (int i = 0; i < p_dictionary->elements.size(); i++) {
- const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
- dict[element.key->reduced_value] = element.value->reduced_value;
- }
- p_dictionary->is_constant = true;
- p_dictionary->reduced_value = dict;
- }
-
// It's dictionary in any case.
GDScriptParser::DataType dict_type;
dict_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -2032,16 +2508,26 @@ void GDScriptAnalyzer::reduce_get_node(GDScriptParser::GetNodeNode *p_get_node)
result.native_type = "Node";
result.builtin_type = Variant::OBJECT;
- if (!ClassDB::is_parent_class(get_real_class_name(parser->current_class->base_type.native_type), result.native_type)) {
+ if (!ClassDB::is_parent_class(parser->current_class->base_type.native_type, result.native_type)) {
push_error(R"*(Cannot use shorthand "get_node()" notation ("$") on a class that isn't a node.)*", p_get_node);
+ } else if (!lambda_stack.is_empty()) {
+ push_error(R"*(Cannot use shorthand "get_node()" notation ("$") inside a lambda. Use a captured variable instead.)*", p_get_node);
}
p_get_node->set_datatype(result);
}
-GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name) {
+GDScriptParser::DataType GDScriptAnalyzer::make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source) {
Ref<GDScriptParserRef> ref = get_parser_for(ScriptServer::get_global_class_path(p_class_name));
- ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+ Error err = ref->raise_status(GDScriptParserRef::INTERFACE_SOLVED);
+
+ if (err) {
+ push_error(vformat(R"(Could not resolve class "%s", because of a parser error.)", p_class_name), p_source);
+ GDScriptParser::DataType type;
+ type.type_source = GDScriptParser::DataType::UNDETECTED;
+ type.kind = GDScriptParser::DataType::VARIANT;
+ return type;
+ }
GDScriptParser::DataType type;
type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
@@ -2073,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: {
@@ -2090,17 +2578,19 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
default: {
Callable::CallError temp;
- Variant dummy = Variant::construct(base.builtin_type, nullptr, 0, temp);
+ Variant dummy;
+ Variant::construct(base.builtin_type, dummy, nullptr, 0, temp);
List<PropertyInfo> properties;
dummy.get_property_list(&properties);
- for (const List<PropertyInfo>::Element *E = properties.front(); E != nullptr; E = E->next()) {
- const PropertyInfo &prop = E->get();
+ for (const PropertyInfo &prop : properties) {
if (prop.name == name) {
p_identifier->set_datatype(type_from_property(prop));
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);
+ }
}
}
}
@@ -2116,11 +2606,15 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
GDScriptParser::DataType result;
result.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
result.kind = GDScriptParser::DataType::ENUM_VALUE;
+ result.builtin_type = base.builtin_type;
result.native_type = base.native_type;
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);
@@ -2152,15 +2646,22 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
case GDScriptParser::ClassNode::Member::ENUM_VALUE:
p_identifier->is_constant = true;
p_identifier->reduced_value = member.enum_value.value;
+ p_identifier->source = GDScriptParser::IdentifierNode::MEMBER_CONSTANT;
break;
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);
p_identifier->set_datatype(make_callable_type(member.function->info));
break;
+ case GDScriptParser::ClassNode::Member::CLASS:
+ // For out-of-order resolution:
+ resolve_class_interface(member.m_class);
+ p_identifier->set_datatype(member.m_class->get_datatype());
+ break;
default:
break; // Type already set.
}
@@ -2172,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;
@@ -2189,13 +2710,16 @@ void GDScriptAnalyzer::reduce_identifier_from_base(GDScriptParser::IdentifierNod
}
// Check native members.
- const StringName &native = get_real_class_name(base.native_type);
+ 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)) {
@@ -2252,52 +2776,77 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
}
+ bool found_source = false;
// Check if identifier is local.
// If that's the case, the declaration already was solved before.
switch (p_identifier->source) {
case GDScriptParser::IdentifierNode::FUNCTION_PARAMETER:
p_identifier->set_datatype(p_identifier->parameter_source->get_datatype());
- return;
+ found_source = true;
+ break;
case GDScriptParser::IdentifierNode::LOCAL_CONSTANT:
case GDScriptParser::IdentifierNode::MEMBER_CONSTANT:
p_identifier->set_datatype(p_identifier->constant_source->get_datatype());
p_identifier->is_constant = true;
// TODO: Constant should have a value on the node itself.
p_identifier->reduced_value = p_identifier->constant_source->initializer->reduced_value;
- return;
+ found_source = true;
+ break;
case GDScriptParser::IdentifierNode::MEMBER_VARIABLE:
p_identifier->variable_source->usages++;
[[fallthrough]];
case GDScriptParser::IdentifierNode::LOCAL_VARIABLE:
p_identifier->set_datatype(p_identifier->variable_source->get_datatype());
- return;
+ found_source = true;
+ break;
case GDScriptParser::IdentifierNode::LOCAL_ITERATOR:
p_identifier->set_datatype(p_identifier->bind_source->get_datatype());
- return;
+ found_source = true;
+ break;
case GDScriptParser::IdentifierNode::LOCAL_BIND: {
GDScriptParser::DataType result = p_identifier->bind_source->get_datatype();
result.is_constant = true;
p_identifier->set_datatype(result);
- return;
- }
+ found_source = true;
+ } break;
case GDScriptParser::IdentifierNode::UNDEFINED_SOURCE:
break;
}
// Not a local, so check members.
- reduce_identifier_from_base(p_identifier);
- if (p_identifier->get_datatype().is_set()) {
- // Found.
+ if (!found_source) {
+ reduce_identifier_from_base(p_identifier);
+ if (p_identifier->source != GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->get_datatype().is_set()) {
+ // Found.
+ found_source = true;
+ }
+ }
+
+ if (found_source) {
+ // If the identifier is local, check if it's any kind of capture by comparing their source function.
+ // Only capture locals and members and enum values. Constants are still accessible from the lambda using the script reference.
+ if (p_identifier->source == GDScriptParser::IdentifierNode::UNDEFINED_SOURCE || p_identifier->source == GDScriptParser::IdentifierNode::MEMBER_CONSTANT || lambda_stack.is_empty()) {
+ return;
+ }
+
+ GDScriptParser::FunctionNode *function_test = lambda_stack.back()->get()->function;
+ while (function_test != nullptr && function_test != p_identifier->source_function && function_test->source_lambda != nullptr && !function_test->source_lambda->captures_indices.has(p_identifier->name)) {
+ function_test->source_lambda->captures_indices[p_identifier->name] = function_test->source_lambda->captures.size();
+ function_test->source_lambda->captures.push_back(p_identifier);
+ function_test = function_test->source_lambda->parent_function;
+ }
return;
}
StringName name = p_identifier->name;
p_identifier->source = GDScriptParser::IdentifierNode::UNDEFINED_SOURCE;
- // Check globals.
- if (GDScriptParser::get_builtin_type(name) < Variant::VARIANT_MAX) {
+ // Check globals. We make an exception for Variant::OBJECT because it's the base class for
+ // non-builtin types so we allow doing e.g. Object.new()
+ Variant::Type builtin_type = GDScriptParser::get_builtin_type(name);
+ if (builtin_type != Variant::OBJECT && builtin_type < Variant::VARIANT_MAX) {
if (can_be_builtin) {
- p_identifier->set_datatype(make_builtin_meta_type(GDScriptParser::get_builtin_type(name)));
+ p_identifier->set_datatype(make_builtin_meta_type(builtin_type));
return;
} else {
push_error(R"(Builtin type cannot be used as a name on its own.)", p_identifier);
@@ -2310,7 +2859,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
}
if (ScriptServer::is_global_class(name)) {
- p_identifier->set_datatype(make_global_class_meta_type(name));
+ p_identifier->set_datatype(make_global_class_meta_type(name, p_identifier));
return;
}
@@ -2357,7 +2906,7 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
// Not found.
// Check if it's a builtin function.
- if (parser->get_builtin_function(name) < GDScriptFunctions::FUNC_MAX) {
+ if (GDScriptUtilityFunctions::function_exists(name)) {
push_error(vformat(R"(Built-in function "%s" cannot be used as an identifier.)", name), p_identifier);
} else {
push_error(vformat(R"(Identifier "%s" not declared in the current scope.)", name), p_identifier);
@@ -2367,6 +2916,57 @@ void GDScriptAnalyzer::reduce_identifier(GDScriptParser::IdentifierNode *p_ident
p_identifier->set_datatype(dummy); // Just so type is set to something.
}
+void GDScriptAnalyzer::reduce_lambda(GDScriptParser::LambdaNode *p_lambda) {
+ // Lambda is always a Callable.
+ GDScriptParser::DataType lambda_type;
+ lambda_type.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
+ lambda_type.kind = GDScriptParser::DataType::BUILTIN;
+ lambda_type.builtin_type = Variant::CALLABLE;
+ p_lambda->set_datatype(lambda_type);
+
+ if (p_lambda->function == nullptr) {
+ return;
+ }
+
+ GDScriptParser::FunctionNode *previous_function = parser->current_function;
+ parser->current_function = p_lambda->function;
+
+ lambda_stack.push_back(p_lambda);
+
+ for (int i = 0; i < p_lambda->function->parameters.size(); i++) {
+ resolve_parameter(p_lambda->function->parameters[i]);
+ }
+
+ resolve_suite(p_lambda->function->body);
+
+ int captures_amount = p_lambda->captures.size();
+ if (captures_amount > 0) {
+ // Create space for lambda parameters.
+ // At the beginning to not mess with optional parameters.
+ int param_count = p_lambda->function->parameters.size();
+ p_lambda->function->parameters.resize(param_count + captures_amount);
+ for (int i = param_count - 1; i >= 0; i--) {
+ p_lambda->function->parameters.write[i + captures_amount] = p_lambda->function->parameters[i];
+ p_lambda->function->parameters_indices[p_lambda->function->parameters[i]->identifier->name] = i + captures_amount;
+ }
+
+ // Add captures as extra parameters at the beginning.
+ for (int i = 0; i < p_lambda->captures.size(); i++) {
+ GDScriptParser::IdentifierNode *capture = p_lambda->captures[i];
+ GDScriptParser::ParameterNode *capture_param = parser->alloc_node<GDScriptParser::ParameterNode>();
+ capture_param->identifier = capture;
+ capture_param->usages = capture->usages;
+ capture_param->set_datatype(capture->get_datatype());
+
+ p_lambda->function->parameters.write[i] = capture_param;
+ p_lambda->function->parameters_indices[capture->name] = i;
+ }
+ }
+
+ lambda_stack.pop_back();
+ parser->current_function = previous_function;
+}
+
void GDScriptAnalyzer::reduce_literal(GDScriptParser::LiteralNode *p_literal) {
p_literal->reduced_value = p_literal->value;
p_literal->is_constant = true;
@@ -2391,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();
@@ -2417,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 {
@@ -2425,25 +3028,6 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
GDScriptParser::DataType result_type;
- // Reduce index first. If it's a constant StringName, use attribute instead.
- if (!p_subscript->is_attribute) {
- if (p_subscript->index == nullptr) {
- return;
- }
- reduce_expression(p_subscript->index);
-
- if (p_subscript->index->is_constant && p_subscript->index->reduced_value.get_type() == Variant::STRING_NAME) {
- GDScriptParser::IdentifierNode *attribute = parser->alloc_node<GDScriptParser::IdentifierNode>();
- // Copy location for better error message.
- attribute->start_line = p_subscript->index->start_line;
- attribute->end_line = p_subscript->index->end_line;
- attribute->leftmost_column = p_subscript->index->leftmost_column;
- attribute->rightmost_column = p_subscript->index->rightmost_column;
- p_subscript->is_attribute = true;
- p_subscript->attribute = attribute;
- }
- }
-
if (p_subscript->is_attribute) {
if (p_subscript->attribute == nullptr) {
return;
@@ -2451,7 +3035,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
if (p_subscript->base->is_constant) {
// Just try to get it.
bool valid = false;
- Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, &valid);
+ Variant value = p_subscript->base->reduced_value.get_named(p_subscript->attribute->name, valid);
if (!valid) {
push_error(vformat(R"(Cannot get member "%s" from "%s".)", p_subscript->attribute->name, p_subscript->base->reduced_value), p_subscript->index);
} else {
@@ -2463,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 {
@@ -2486,7 +3070,10 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
}
}
} else {
- // Index was already reduced before.
+ if (p_subscript->index == nullptr) {
+ return;
+ }
+ reduce_expression(p_subscript->index);
if (p_subscript->base->is_constant && p_subscript->index->is_constant) {
// Just try to get it.
@@ -2531,7 +3118,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::RECT2:
case Variant::RECT2I:
case Variant::PLANE:
- case Variant::QUAT:
+ case Variant::QUATERNION:
case Variant::AABB:
case Variant::OBJECT:
error = index_type.builtin_type != Variant::STRING;
@@ -2542,8 +3129,8 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::VECTOR2I:
case Variant::VECTOR3:
case Variant::VECTOR3I:
- case Variant::TRANSFORM:
case Variant::TRANSFORM2D:
+ case Variant::TRANSFORM3D:
error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::FLOAT &&
index_type.builtin_type != Variant::STRING;
break;
@@ -2552,7 +3139,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
error = index_type.builtin_type != Variant::INT && index_type.builtin_type != Variant::STRING;
break;
// Don't support indexing, but we will check it later.
- case Variant::_RID:
+ case Variant::RID:
case Variant::BOOL:
case Variant::CALLABLE:
case Variant::FLOAT:
@@ -2583,9 +3170,12 @@ 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:
+ case Variant::RID:
case Variant::BOOL:
case Variant::CALLABLE:
case Variant::FLOAT:
@@ -2610,7 +3200,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
case Variant::PACKED_FLOAT64_ARRAY:
case Variant::VECTOR2:
case Variant::VECTOR3:
- case Variant::QUAT:
+ case Variant::QUATERNION:
result_type.builtin_type = Variant::FLOAT;
break;
// Return Color.
@@ -2639,16 +3229,25 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri
result_type.builtin_type = Variant::VECTOR3;
break;
// Depends on the index.
- case Variant::TRANSFORM:
+ case Variant::TRANSFORM3D:
case Variant::PLANE:
case Variant::COLOR:
- case Variant::ARRAY:
case Variant::DICTIONARY:
+ case Variant::OBJECT:
result_type.kind = GDScriptParser::DataType::VARIANT;
result_type.type_source = GDScriptParser::DataType::UNDETECTED;
break;
+ // Can have an element type.
+ case Variant::ARRAY:
+ if (base_type.has_container_element_type()) {
+ result_type = base_type.get_container_element_type();
+ result_type.type_source = base_type.type_source;
+ } else {
+ result_type.kind = GDScriptParser::DataType::VARIANT;
+ result_type.type_source = GDScriptParser::DataType::UNDETECTED;
+ }
+ break;
// Here for completeness.
- case Variant::OBJECT:
case Variant::VARIANT_MAX:
break;
}
@@ -2727,7 +3326,7 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
mark_node_unsafe(p_unary_op);
} else {
bool valid = false;
- result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), p_unary_op->operand->get_datatype(), valid, p_unary_op);
+ result = get_operation_type(p_unary_op->variant_op, p_unary_op->operand->get_datatype(), valid, p_unary_op);
if (!valid) {
push_error(vformat(R"(Invalid operand of type "%s" for unary operator "%s".)", p_unary_op->operand->get_datatype().to_string(), Variant::get_operator_name(p_unary_op->variant_op)), p_unary_op->operand);
@@ -2737,6 +3336,46 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op)
p_unary_op->set_datatype(result);
}
+void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) {
+ bool all_is_constant = true;
+
+ for (int i = 0; i < p_array->elements.size(); i++) {
+ GDScriptParser::ExpressionNode *element = p_array->elements[i];
+ all_is_constant = all_is_constant && element->is_constant;
+ if (!all_is_constant) {
+ return;
+ }
+ }
+
+ Array array;
+ array.resize(p_array->elements.size());
+ for (int i = 0; i < p_array->elements.size(); i++) {
+ array[i] = p_array->elements[i]->reduced_value;
+ }
+ p_array->is_constant = true;
+ p_array->reduced_value = array;
+}
+
+void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary) {
+ bool all_is_constant = true;
+
+ for (int i = 0; i < p_dictionary->elements.size(); i++) {
+ const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
+ all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant;
+ if (!all_is_constant) {
+ return;
+ }
+ }
+
+ Dictionary dict;
+ for (int i = 0; i < p_dictionary->elements.size(); i++) {
+ const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i];
+ dict[element.key->reduced_value] = element.value->reduced_value;
+ }
+ p_dictionary->is_constant = true;
+ p_dictionary->reduced_value = dict;
+}
+
GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType result;
result.is_constant = true;
@@ -2775,14 +3414,20 @@ 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;
// It should be okay to assume this exists, since we have a complete script already.
- for (const List<StringName>::Element *E = class_chain.front(); E; E = E->next()) {
- found = found->get_member(E->get()).m_class;
+ for (const StringName &E : class_chain) {
+ found = found->get_member(E).m_class;
}
result.class_type = found;
@@ -2829,11 +3474,40 @@ GDScriptParser::DataType GDScriptAnalyzer::type_from_property(const PropertyInfo
result.native_type = p_property.class_name == StringName() ? "Object" : p_property.class_name;
} else {
result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = p_property.type;
+ if (p_property.type == Variant::ARRAY && p_property.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ // Check element type.
+ StringName elem_type_name = p_property.hint_string;
+ GDScriptParser::DataType elem_type;
+ elem_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
+
+ Variant::Type elem_builtin_type = GDScriptParser::get_builtin_type(elem_type_name);
+ if (elem_builtin_type < Variant::VARIANT_MAX) {
+ // Builtin type.
+ elem_type.kind = GDScriptParser::DataType::BUILTIN;
+ elem_type.builtin_type = elem_builtin_type;
+ } else if (class_exists(elem_type_name)) {
+ elem_type.kind = GDScriptParser::DataType::NATIVE;
+ elem_type.builtin_type = Variant::OBJECT;
+ elem_type.native_type = p_property.hint_string;
+ } else if (ScriptServer::is_global_class(elem_type_name)) {
+ // Just load this as it shouldn't be a GDScript.
+ Ref<Script> script = ResourceLoader::load(ScriptServer::get_global_class_path(elem_type_name));
+ elem_type.kind = GDScriptParser::DataType::SCRIPT;
+ elem_type.builtin_type = Variant::OBJECT;
+ elem_type.native_type = script->get_instance_base_type();
+ elem_type.script_type = script;
+ } 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);
+ }
}
return result;
}
-bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) {
+bool GDScriptAnalyzer::get_function_signature(GDScriptParser::CallNode *p_source, GDScriptParser::DataType p_base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg) {
r_static = false;
r_vararg = false;
r_default_arg_count = 0;
@@ -2842,23 +3516,26 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GD
if (p_base_type.kind == GDScriptParser::DataType::BUILTIN) {
// Construct a base type to get methods.
Callable::CallError err;
- Variant dummy = Variant::construct(p_base_type.builtin_type, nullptr, 0, err);
+ Variant dummy;
+ Variant::construct(p_base_type.builtin_type, dummy, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
ERR_FAIL_V_MSG(false, "Could not construct base Variant type.");
}
List<MethodInfo> methods;
dummy.get_method_list(&methods);
- for (const List<MethodInfo>::Element *E = methods.front(); E != nullptr; E = E->next()) {
- if (E->get().name == p_function) {
- return function_signature_from_info(E->get(), r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ for (const MethodInfo &E : methods) {
+ if (E.name == p_function) {
+ function_signature_from_info(E, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ r_static = Variant::is_builtin_method_static(p_base_type.builtin_type, function_name);
+ return true;
}
}
return false;
}
- bool is_constructor = p_base_type.is_meta_type && p_function == "new";
+ bool is_constructor = (p_base_type.is_meta_type || (p_source->callee && p_source->callee->type == GDScriptParser::Node::IDENTIFIER)) && p_function == StaticCString::create("new");
if (is_constructor) {
function_name = "_init";
r_static = true;
@@ -2888,6 +3565,7 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GD
}
}
r_return_type = found_function->get_datatype();
+ r_return_type.is_meta_type = false;
r_return_type.is_coroutine = found_function->is_coroutine;
return true;
@@ -2933,11 +3611,13 @@ bool GDScriptAnalyzer::get_function_signature(GDScriptParser::Node *p_source, GD
return true;
}
- StringName real_native = get_real_class_name(base_native);
-
MethodInfo info;
- if (ClassDB::get_method_info(real_native, function_name, &info)) {
- return function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ if (ClassDB::get_method_info(base_native, function_name, &info)) {
+ bool valid = function_signature_from_info(info, r_return_type, r_par_types, r_default_arg_count, r_static, r_vararg);
+ if (valid && Engine::get_singleton()->has_singleton(base_native)) {
+ r_static = true;
+ }
+ return valid;
}
return false;
@@ -2948,8 +3628,8 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD
r_default_arg_count = p_info.default_arguments.size();
r_vararg = (p_info.flags & METHOD_FLAG_VARARG) != 0;
- for (const List<PropertyInfo>::Element *E = p_info.arguments.front(); E != nullptr; E = E->next()) {
- r_par_types.push_back(type_from_property(E->get()));
+ for (const PropertyInfo &E : p_info.arguments) {
+ r_par_types.push_back(type_from_property(E));
}
return true;
}
@@ -2957,8 +3637,8 @@ bool GDScriptAnalyzer::function_signature_from_info(const MethodInfo &p_info, GD
bool GDScriptAnalyzer::validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call) {
List<GDScriptParser::DataType> arg_types;
- for (const List<PropertyInfo>::Element *E = p_method.arguments.front(); E != nullptr; E = E->next()) {
- arg_types.push_back(type_from_property(E->get()));
+ for (const PropertyInfo &E : p_method.arguments) {
+ arg_types.push_back(type_from_property(E));
}
return validate_call_arg(arg_types, p_method.default_arguments.size(), (p_method.flags & METHOD_FLAG_VARARG) != 0, p_call);
@@ -3028,105 +3708,55 @@ bool GDScriptAnalyzer::is_shadowing(GDScriptParser::IdentifierNode *p_local, con
StringName parent = base_native;
while (parent != StringName()) {
- StringName real_class_name = get_real_class_name(parent);
- if (ClassDB::has_method(real_class_name, name, true)) {
+ if (ClassDB::has_method(parent, name, true)) {
parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "method", parent);
return true;
- } else if (ClassDB::has_signal(real_class_name, name, true)) {
+ } else if (ClassDB::has_signal(parent, name, true)) {
parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "signal", parent);
return true;
- } else if (ClassDB::has_property(real_class_name, name, true)) {
+ } else if (ClassDB::has_property(parent, name, true)) {
parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "property", parent);
return true;
- } else if (ClassDB::has_integer_constant(real_class_name, name, true)) {
+ } else if (ClassDB::has_integer_constant(parent, name, true)) {
parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "constant", parent);
return true;
- } else if (ClassDB::has_enum(real_class_name, name, true)) {
+ } else if (ClassDB::has_enum(parent, name, true)) {
parser->push_warning(p_local, GDScriptWarning::SHADOWED_VARIABLE_BASE_CLASS, p_context, p_local->name, "enum", parent);
return true;
}
- parent = ClassDB::get_parent_class(real_class_name);
+ parent = ClassDB::get_parent_class(parent);
}
return false;
}
#endif
-GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
- // This function creates dummy variant values and apply the operation to those. Less error-prone than keeping a table of valid operations.
+GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source) {
+ // Unary version.
+ GDScriptParser::DataType nil_type;
+ nil_type.builtin_type = Variant::NIL;
+ return get_operation_type(p_operation, p_a, nil_type, r_valid, p_source);
+}
+GDScriptParser::DataType GDScriptAnalyzer::get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source) {
GDScriptParser::DataType result;
result.kind = GDScriptParser::DataType::VARIANT;
Variant::Type a_type = p_a.builtin_type;
Variant::Type b_type = p_b.builtin_type;
- Variant a;
- REF a_ref;
- if (a_type == Variant::OBJECT) {
- a_ref.instance();
- a = a_ref;
- } else {
- Callable::CallError err;
- a = Variant::construct(a_type, nullptr, 0, err);
- if (err.error != Callable::CallError::CALL_OK) {
- r_valid = false;
- ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(a_type)));
- }
- }
- Variant b;
- REF b_ref;
- if (b_type == Variant::OBJECT) {
- b_ref.instance();
- b = b_ref;
- } else {
- Callable::CallError err;
- b = Variant::construct(b_type, nullptr, 0, err);
- if (err.error != Callable::CallError::CALL_OK) {
- r_valid = false;
- ERR_FAIL_V_MSG(result, vformat("Could not construct value of type %s", Variant::get_type_name(b_type)));
- }
- }
+ Variant::ValidatedOperatorEvaluator op_eval = Variant::get_validated_operator_evaluator(p_operation, a_type, b_type);
- // Avoid division by zero.
- switch (b_type) {
- case Variant::INT:
- b = 1;
- break;
- case Variant::FLOAT:
- b = 1.0;
- break;
- case Variant::VECTOR2:
- b = Vector2(1.0, 1.0);
- break;
- case Variant::VECTOR2I:
- b = Vector2i(1, 1);
- break;
- case Variant::VECTOR3:
- b = Vector3(1.0, 1.0, 1.0);
- break;
- case Variant::VECTOR3I:
- b = Vector3i(1, 1, 1);
- break;
- case Variant::COLOR:
- b = Color(1.0, 1.0, 1.0, 1.0);
- break;
- default:
- // No change needed.
- break;
- }
-
- // Avoid error in formatting operator (%) where it doesn't find a placeholder.
- if (a_type == Variant::STRING && b_type != Variant::ARRAY) {
- a = String("%s");
+ if (op_eval == nullptr) {
+ r_valid = false;
+ return result;
}
- Variant ret;
- Variant::evaluate(p_operation, a, b, ret, r_valid);
+ r_valid = true;
+ result.type_source = GDScriptParser::DataType::ANNOTATED_INFERRED;
- if (r_valid) {
- return type_from_variant(ret, p_source);
- }
+ result.kind = GDScriptParser::DataType::BUILTIN;
+ result.builtin_type = Variant::get_operator_return_type(p_operation, a_type, b_type);
return result;
}
@@ -3156,6 +3786,18 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
// Enum value is also integer.
valid = true;
}
+ if (valid && p_target.builtin_type == Variant::ARRAY && p_source.builtin_type == Variant::ARRAY) {
+ // Check the element type.
+ if (p_target.has_container_element_type()) {
+ if (!p_source.has_container_element_type()) {
+ // TODO: Maybe this is valid but unsafe?
+ // Variant array can't be appended to typed array.
+ valid = false;
+ } else {
+ valid = is_type_compatible(p_target.get_container_element_type(), p_source.get_container_element_type(), false);
+ }
+ }
+ }
return valid;
}
@@ -3163,6 +3805,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;
@@ -3227,16 +3874,12 @@ bool GDScriptAnalyzer::is_type_compatible(const GDScriptParser::DataType &p_targ
break; // Already solved before.
}
- // Get underscore-prefixed version for some classes.
- src_native = get_real_class_name(src_native);
-
switch (p_target.kind) {
case GDScriptParser::DataType::NATIVE: {
if (p_target.is_meta_type) {
return ClassDB::is_parent_class(src_native, GDScriptNativeClass::get_class_static());
}
- StringName tgt_native = get_real_class_name(p_target.native_type);
- return ClassDB::is_parent_class(src_native, tgt_native);
+ return ClassDB::is_parent_class(src_native, p_target.native_type);
}
case GDScriptParser::DataType::SCRIPT:
if (p_target.is_meta_type) {
@@ -3284,9 +3927,8 @@ void GDScriptAnalyzer::mark_node_unsafe(const GDScriptParser::Node *p_node) {
#endif
}
-bool GDScriptAnalyzer::class_exists(const StringName &p_class) {
- StringName real_name = get_real_class_name(p_class);
- return ClassDB::class_exists(real_name) && ClassDB::is_class_exposed(real_name);
+bool GDScriptAnalyzer::class_exists(const StringName &p_class) const {
+ return ClassDB::class_exists(p_class) && ClassDB::is_class_exposed(p_class);
}
Ref<GDScriptParserRef> GDScriptAnalyzer::get_parser_for(const String &p_path) {
@@ -3308,12 +3950,12 @@ Error GDScriptAnalyzer::resolve_inheritance() {
Error GDScriptAnalyzer::resolve_interface() {
resolve_class_interface(parser->head);
- return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+ return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::resolve_body() {
resolve_class_body(parser->head);
- return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+ return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::resolve_program() {
@@ -3322,14 +3964,13 @@ Error GDScriptAnalyzer::resolve_program() {
List<String> parser_keys;
depended_parsers.get_key_list(&parser_keys);
- for (const List<String>::Element *E = parser_keys.front(); E != nullptr; E = E->next()) {
- if (depended_parsers[E->get()].is_null()) {
+ for (const String &E : parser_keys) {
+ if (depended_parsers[E].is_null()) {
return ERR_PARSE_ERROR;
}
- depended_parsers[E->get()]->raise_status(GDScriptParserRef::FULLY_SOLVED);
+ depended_parsers[E]->raise_status(GDScriptParserRef::FULLY_SOLVED);
}
- depended_parsers.clear();
- return parser->errors.empty() ? OK : ERR_PARSE_ERROR;
+ return parser->errors.is_empty() ? OK : ERR_PARSE_ERROR;
}
Error GDScriptAnalyzer::analyze() {
diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h
index c3911cce76..ce4525190b 100644
--- a/modules/gdscript/gdscript_analyzer.h
+++ b/modules/gdscript/gdscript_analyzer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,9 +31,9 @@
#ifndef GDSCRIPT_ANALYZER_H
#define GDSCRIPT_ANALYZER_H
-#include "core/object.h"
-#include "core/reference.h"
-#include "core/set.h"
+#include "core/object/object.h"
+#include "core/object/ref_counted.h"
+#include "core/templates/set.h"
#include "gdscript_cache.h"
#include "gdscript_parser.h"
@@ -42,6 +42,13 @@ class GDScriptAnalyzer {
HashMap<String, Ref<GDScriptParserRef>> depended_parsers;
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);
@@ -71,17 +78,18 @@ 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);
void reduce_identifier(GDScriptParser::IdentifierNode *p_identifier, bool can_be_builtin = false);
void reduce_identifier_from_base(GDScriptParser::IdentifierNode *p_identifier, GDScriptParser::DataType *p_base = nullptr);
+ void reduce_lambda(GDScriptParser::LambdaNode *p_lambda);
void reduce_literal(GDScriptParser::LiteralNode *p_literal);
void reduce_preload(GDScriptParser::PreloadNode *p_preload);
void reduce_self(GDScriptParser::SelfNode *p_self);
@@ -89,20 +97,25 @@ class GDScriptAnalyzer {
void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op);
void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op);
+ void const_fold_array(GDScriptParser::ArrayNode *p_array);
+ void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary);
+
// Helpers.
GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source);
GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const;
GDScriptParser::DataType type_from_property(const PropertyInfo &p_property) const;
- GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name);
- bool get_function_signature(GDScriptParser::Node *p_source, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
+ GDScriptParser::DataType make_global_class_meta_type(const StringName &p_class_name, const GDScriptParser::Node *p_source);
+ bool get_function_signature(GDScriptParser::CallNode *p_source, GDScriptParser::DataType base_type, const StringName &p_function, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
bool function_signature_from_info(const MethodInfo &p_info, GDScriptParser::DataType &r_return_type, List<GDScriptParser::DataType> &r_par_types, int &r_default_arg_count, bool &r_static, bool &r_vararg);
bool validate_call_arg(const List<GDScriptParser::DataType> &p_par_types, int p_default_args_count, bool p_is_vararg, const GDScriptParser::CallNode *p_call);
bool validate_call_arg(const MethodInfo &p_method, const GDScriptParser::CallNode *p_call);
GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, const GDScriptParser::DataType &p_b, bool &r_valid, const GDScriptParser::Node *p_source);
+ GDScriptParser::DataType get_operation_type(Variant::Operator p_operation, const GDScriptParser::DataType &p_a, bool &r_valid, const GDScriptParser::Node *p_source);
+ void update_array_literal_element_type(const GDScriptParser::DataType &p_base_type, GDScriptParser::ArrayNode *p_array_literal);
bool is_type_compatible(const GDScriptParser::DataType &p_target, const GDScriptParser::DataType &p_source, bool p_allow_implicit_conversion = false) const;
void push_error(const String &p_message, const GDScriptParser::Node *p_origin);
void mark_node_unsafe(const GDScriptParser::Node *p_node);
- bool class_exists(const StringName &p_class);
+ bool class_exists(const StringName &p_class) const;
Ref<GDScriptParserRef> get_parser_for(const String &p_path);
#ifdef DEBUG_ENABLED
bool is_shadowing(GDScriptParser::IdentifierNode *p_local, const String &p_context);
@@ -115,8 +128,6 @@ public:
Error analyze();
GDScriptAnalyzer(GDScriptParser *p_parser);
-
- static void cleanup();
};
#endif // GDSCRIPT_ANALYZER_H
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 8f0ce99de6..6a7e4278d2 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,10 +40,6 @@ uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool
function->_argument_count++;
function->argument_types.push_back(p_type);
if (p_is_optional) {
- if (function->_default_arg_count == 0) {
- append(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
- }
- function->default_arguments.push_back(opcodes.size());
function->_default_arg_count++;
}
@@ -51,7 +47,8 @@ uint32_t GDScriptByteCodeGenerator::add_parameter(const StringName &p_name, bool
}
uint32_t GDScriptByteCodeGenerator::add_local(const StringName &p_name, const GDScriptDataType &p_type) {
- int stack_pos = increase_stack();
+ int stack_pos = locals.size() + RESERVED_STACK;
+ locals.push_back(StackSlot(p_type.builtin_type));
add_stack_identifier(p_name, stack_pos);
return stack_pos;
}
@@ -63,35 +60,102 @@ uint32_t GDScriptByteCodeGenerator::add_local_constant(const StringName &p_name,
}
uint32_t GDScriptByteCodeGenerator::add_or_get_constant(const Variant &p_constant) {
- if (constant_map.has(p_constant)) {
- return constant_map[p_constant];
- }
- int index = constant_map.size();
- constant_map[p_constant] = index;
- return index;
+ return get_constant_pos(p_constant);
}
uint32_t GDScriptByteCodeGenerator::add_or_get_name(const StringName &p_name) {
return get_name_map_pos(p_name);
}
-uint32_t GDScriptByteCodeGenerator::add_temporary() {
- current_temporaries++;
- return increase_stack();
+uint32_t GDScriptByteCodeGenerator::add_temporary(const GDScriptDataType &p_type) {
+ Variant::Type temp_type = Variant::NIL;
+ if (p_type.has_type) {
+ if (p_type.kind == GDScriptDataType::BUILTIN) {
+ switch (p_type.builtin_type) {
+ case Variant::NIL:
+ case Variant::BOOL:
+ case Variant::INT:
+ case Variant::FLOAT:
+ case Variant::STRING:
+ case Variant::VECTOR2:
+ case Variant::VECTOR2I:
+ case Variant::RECT2:
+ case Variant::RECT2I:
+ case Variant::VECTOR3:
+ case Variant::VECTOR3I:
+ case Variant::TRANSFORM2D:
+ case Variant::PLANE:
+ case Variant::QUATERNION:
+ case Variant::AABB:
+ case Variant::BASIS:
+ case Variant::TRANSFORM3D:
+ case Variant::COLOR:
+ case Variant::STRING_NAME:
+ case Variant::NODE_PATH:
+ case Variant::RID:
+ case Variant::OBJECT:
+ case Variant::CALLABLE:
+ case Variant::SIGNAL:
+ case Variant::DICTIONARY:
+ case Variant::ARRAY:
+ temp_type = p_type.builtin_type;
+ break;
+ case Variant::PACKED_BYTE_ARRAY:
+ case Variant::PACKED_INT32_ARRAY:
+ case Variant::PACKED_INT64_ARRAY:
+ case Variant::PACKED_FLOAT32_ARRAY:
+ case Variant::PACKED_FLOAT64_ARRAY:
+ case Variant::PACKED_STRING_ARRAY:
+ case Variant::PACKED_VECTOR2_ARRAY:
+ case Variant::PACKED_VECTOR3_ARRAY:
+ case Variant::PACKED_COLOR_ARRAY:
+ case Variant::VARIANT_MAX:
+ // Packed arrays are reference counted, so we don't use the pool for them.
+ temp_type = Variant::NIL;
+ break;
+ }
+ } else {
+ temp_type = Variant::OBJECT;
+ }
+ }
+
+ if (!temporaries_pool.has(temp_type)) {
+ temporaries_pool[temp_type] = List<int>();
+ }
+
+ List<int> &pool = temporaries_pool[temp_type];
+ if (pool.is_empty()) {
+ StackSlot new_temp(temp_type);
+ int idx = temporaries.size();
+ pool.push_back(idx);
+ temporaries.push_back(new_temp);
+ }
+ int slot = pool.front()->get();
+ pool.pop_front();
+ used_temporaries.push_back(slot);
+ return slot;
}
void GDScriptByteCodeGenerator::pop_temporary() {
- current_stack_size--;
- current_temporaries--;
+ ERR_FAIL_COND(used_temporaries.is_empty());
+ int slot_idx = used_temporaries.back()->get();
+ const StackSlot &slot = temporaries[slot_idx];
+ temporaries_pool[slot.type].push_back(slot_idx);
+ used_temporaries.pop_back();
}
-void GDScriptByteCodeGenerator::start_parameters() {}
+void GDScriptByteCodeGenerator::start_parameters() {
+ if (function->_default_arg_count > 0) {
+ append(GDScriptFunction::OPCODE_JUMP_TO_DEF_ARGUMENT);
+ function->default_arguments.push_back(opcodes.size());
+ }
+}
void GDScriptByteCodeGenerator::end_parameters() {
- function->default_arguments.invert();
+ function->default_arguments.reverse();
}
-void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCMode p_rpc_mode, 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();
@@ -106,12 +170,27 @@ void GDScriptByteCodeGenerator::write_start(GDScript *p_script, const StringName
function->_static = p_static;
function->return_type = p_return_type;
- function->rpc_mode = p_rpc_mode;
+ function->rpc_config = p_rpc_config;
function->_argument_count = 0;
}
GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
- append(GDScriptFunction::OPCODE_END);
+#ifdef DEBUG_ENABLED
+ if (!used_temporaries.is_empty()) {
+ ERR_PRINT("Non-zero temporary variables at end of function: " + itos(used_temporaries.size()));
+ }
+#endif
+ append(GDScriptFunction::OPCODE_END, 0);
+
+ for (int i = 0; i < temporaries.size(); i++) {
+ int stack_index = i + max_locals + RESERVED_STACK;
+ for (int j = 0; j < temporaries[i].bytecode_indices.size(); j++) {
+ opcodes.write[temporaries[i].bytecode_indices[j]] = stack_index | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ }
+ if (temporaries[i].type != Variant::NIL) {
+ function->temporary_slots[stack_index] = temporaries[i].type;
+ }
+ }
if (constant_map.size()) {
function->_constant_count = constant_map.size();
@@ -130,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();
@@ -151,18 +230,175 @@ GDScriptFunction *GDScriptByteCodeGenerator::write_end() {
}
if (function->default_arguments.size()) {
- function->_default_arg_count = function->default_arguments.size();
+ function->_default_arg_count = function->default_arguments.size() - 1;
function->_default_arg_ptr = &function->default_arguments[0];
} else {
function->_default_arg_count = 0;
function->_default_arg_ptr = nullptr;
}
+ if (operator_func_map.size()) {
+ 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 KeyValue<Variant::ValidatedOperatorEvaluator, int> &E : operator_func_map) {
+ function->operator_funcs.write[E.value] = E.key;
+ }
+ } else {
+ function->_operator_funcs_count = 0;
+ function->_operator_funcs_ptr = nullptr;
+ }
+
+ if (setters_map.size()) {
+ function->setters.resize(setters_map.size());
+ function->_setters_count = function->setters.size();
+ function->_setters_ptr = function->setters.ptr();
+ for (const KeyValue<Variant::ValidatedSetter, int> &E : setters_map) {
+ function->setters.write[E.value] = E.key;
+ }
+ } else {
+ function->_setters_count = 0;
+ function->_setters_ptr = nullptr;
+ }
+
+ if (getters_map.size()) {
+ function->getters.resize(getters_map.size());
+ function->_getters_count = function->getters.size();
+ function->_getters_ptr = function->getters.ptr();
+ for (const KeyValue<Variant::ValidatedGetter, int> &E : getters_map) {
+ function->getters.write[E.value] = E.key;
+ }
+ } else {
+ function->_getters_count = 0;
+ function->_getters_ptr = nullptr;
+ }
+
+ if (keyed_setters_map.size()) {
+ 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 KeyValue<Variant::ValidatedKeyedSetter, int> &E : keyed_setters_map) {
+ function->keyed_setters.write[E.value] = E.key;
+ }
+ } else {
+ function->_keyed_setters_count = 0;
+ function->_keyed_setters_ptr = nullptr;
+ }
+
+ if (keyed_getters_map.size()) {
+ 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 KeyValue<Variant::ValidatedKeyedGetter, int> &E : keyed_getters_map) {
+ function->keyed_getters.write[E.value] = E.key;
+ }
+ } else {
+ function->_keyed_getters_count = 0;
+ function->_keyed_getters_ptr = nullptr;
+ }
+
+ if (indexed_setters_map.size()) {
+ 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 KeyValue<Variant::ValidatedIndexedSetter, int> &E : indexed_setters_map) {
+ function->indexed_setters.write[E.value] = E.key;
+ }
+ } else {
+ function->_indexed_setters_count = 0;
+ function->_indexed_setters_ptr = nullptr;
+ }
+
+ if (indexed_getters_map.size()) {
+ 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 KeyValue<Variant::ValidatedIndexedGetter, int> &E : indexed_getters_map) {
+ function->indexed_getters.write[E.value] = E.key;
+ }
+ } else {
+ function->_indexed_getters_count = 0;
+ function->_indexed_getters_ptr = nullptr;
+ }
+
+ if (builtin_method_map.size()) {
+ 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 KeyValue<Variant::ValidatedBuiltInMethod, int> &E : builtin_method_map) {
+ function->builtin_methods.write[E.value] = E.key;
+ }
+ } else {
+ function->_builtin_methods_ptr = nullptr;
+ function->_builtin_methods_count = 0;
+ }
+
+ if (constructors_map.size()) {
+ function->constructors.resize(constructors_map.size());
+ function->_constructors_ptr = function->constructors.ptr();
+ function->_constructors_count = constructors_map.size();
+ for (const KeyValue<Variant::ValidatedConstructor, int> &E : constructors_map) {
+ function->constructors.write[E.value] = E.key;
+ }
+ } else {
+ function->_constructors_ptr = nullptr;
+ function->_constructors_count = 0;
+ }
+
+ if (utilities_map.size()) {
+ function->utilities.resize(utilities_map.size());
+ function->_utilities_ptr = function->utilities.ptr();
+ function->_utilities_count = utilities_map.size();
+ for (const KeyValue<Variant::ValidatedUtilityFunction, int> &E : utilities_map) {
+ function->utilities.write[E.value] = E.key;
+ }
+ } else {
+ function->_utilities_ptr = nullptr;
+ function->_utilities_count = 0;
+ }
+
+ if (gds_utilities_map.size()) {
+ 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 KeyValue<GDScriptUtilityFunctions::FunctionPtr, int> &E : gds_utilities_map) {
+ function->gds_utilities.write[E.value] = E.key;
+ }
+ } else {
+ function->_gds_utilities_ptr = nullptr;
+ function->_gds_utilities_count = 0;
+ }
+
+ if (method_bind_map.size()) {
+ function->methods.resize(method_bind_map.size());
+ function->_methods_ptr = function->methods.ptrw();
+ function->_methods_count = method_bind_map.size();
+ for (const KeyValue<MethodBind *, int> &E : method_bind_map) {
+ function->methods.write[E.value] = E.key;
+ }
+ } else {
+ function->_methods_ptr = nullptr;
+ function->_methods_count = 0;
+ }
+
+ if (lambdas_map.size()) {
+ function->lambdas.resize(lambdas_map.size());
+ function->_lambdas_ptr = function->lambdas.ptrw();
+ function->_lambdas_count = lambdas_map.size();
+ for (const KeyValue<GDScriptFunction *, int> &E : lambdas_map) {
+ function->lambdas.write[E.value] = E.key;
+ }
+ } else {
+ function->_lambdas_ptr = nullptr;
+ function->_lambdas_count = 0;
+ }
+
if (debug_stack) {
function->stack_debug = stack_debug;
}
- function->_stack_size = stack_max;
- function->_call_size = call_max;
+ function->_stack_size = RESERVED_STACK + max_locals + temporaries.size();
+ function->_instruction_args_size = instr_args_max;
+ function->_ptrcall_args_size = ptrcall_max;
ended = true;
return function;
@@ -178,37 +414,196 @@ void GDScriptByteCodeGenerator::set_initial_line(int p_line) {
function->_initial_line = p_line;
}
-void GDScriptByteCodeGenerator::write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
- append(GDScriptFunction::OPCODE_OPERATOR);
+#define HAS_BUILTIN_TYPE(m_var) \
+ (m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN)
+
+#define IS_BUILTIN_TYPE(m_var, m_type) \
+ (m_var.type.has_type && m_var.type.kind == GDScriptDataType::BUILTIN && m_var.type.builtin_type == m_type)
+
+void GDScriptByteCodeGenerator::write_type_adjust(const Address &p_target, Variant::Type p_new_type) {
+ switch (p_new_type) {
+ case Variant::BOOL:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_BOOL, 1);
+ break;
+ case Variant::INT:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_INT, 1);
+ break;
+ case Variant::FLOAT:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_FLOAT, 1);
+ break;
+ case Variant::STRING:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_STRING, 1);
+ break;
+ case Variant::VECTOR2:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR2, 1);
+ break;
+ case Variant::VECTOR2I:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR2I, 1);
+ break;
+ case Variant::RECT2:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_RECT2, 1);
+ break;
+ case Variant::RECT2I:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_RECT2I, 1);
+ break;
+ case Variant::VECTOR3:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3, 1);
+ break;
+ case Variant::VECTOR3I:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_VECTOR3I, 1);
+ break;
+ case Variant::TRANSFORM2D:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM2D, 1);
+ break;
+ case Variant::PLANE:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_PLANE, 1);
+ break;
+ case Variant::QUATERNION:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_QUATERNION, 1);
+ break;
+ case Variant::AABB:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_AABB, 1);
+ break;
+ case Variant::BASIS:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_BASIS, 1);
+ break;
+ case Variant::TRANSFORM3D:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_TRANSFORM, 1);
+ break;
+ case Variant::COLOR:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_COLOR, 1);
+ break;
+ case Variant::STRING_NAME:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_STRING_NAME, 1);
+ break;
+ case Variant::NODE_PATH:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_NODE_PATH, 1);
+ break;
+ case Variant::RID:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_RID, 1);
+ break;
+ case Variant::OBJECT:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_OBJECT, 1);
+ break;
+ case Variant::CALLABLE:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_CALLABLE, 1);
+ break;
+ case Variant::SIGNAL:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_SIGNAL, 1);
+ break;
+ case Variant::DICTIONARY:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_DICTIONARY, 1);
+ break;
+ case Variant::ARRAY:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_ARRAY, 1);
+ break;
+ case Variant::PACKED_BYTE_ARRAY:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_BYTE_ARRAY, 1);
+ break;
+ case Variant::PACKED_INT32_ARRAY:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_INT32_ARRAY, 1);
+ break;
+ case Variant::PACKED_INT64_ARRAY:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_INT64_ARRAY, 1);
+ break;
+ case Variant::PACKED_FLOAT32_ARRAY:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_FLOAT32_ARRAY, 1);
+ break;
+ case Variant::PACKED_FLOAT64_ARRAY:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_FLOAT64_ARRAY, 1);
+ break;
+ case Variant::PACKED_STRING_ARRAY:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_STRING_ARRAY, 1);
+ break;
+ case Variant::PACKED_VECTOR2_ARRAY:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY, 1);
+ break;
+ case Variant::PACKED_VECTOR3_ARRAY:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY, 1);
+ break;
+ case Variant::PACKED_COLOR_ARRAY:
+ append(GDScriptFunction::OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY, 1);
+ break;
+ case Variant::NIL:
+ case Variant::VARIANT_MAX:
+ return;
+ }
+ append(p_target);
+}
+
+void GDScriptByteCodeGenerator::write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) {
+ if (HAS_BUILTIN_TYPE(p_left_operand)) {
+ // Gather specific operator.
+ Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, Variant::NIL);
+
+ append(GDScriptFunction::OPCODE_OPERATOR_VALIDATED, 3);
+ append(p_left_operand);
+ append(Address());
+ append(p_target);
+ append(op_func);
+ return;
+ }
+
+ // No specific types, perform variant evaluation.
+ append(GDScriptFunction::OPCODE_OPERATOR, 3);
+ append(p_left_operand);
+ append(Address());
+ append(p_target);
append(p_operator);
+}
+
+void GDScriptByteCodeGenerator::write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) {
+ if (HAS_BUILTIN_TYPE(p_left_operand) && HAS_BUILTIN_TYPE(p_right_operand)) {
+ if (p_target.mode == Address::TEMPORARY) {
+ Variant::Type result_type = Variant::get_operator_return_type(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type);
+ Variant::Type temp_type = temporaries[p_target.address].type;
+ if (result_type != temp_type) {
+ write_type_adjust(p_target, result_type);
+ }
+ }
+
+ // Gather specific operator.
+ Variant::ValidatedOperatorEvaluator op_func = Variant::get_validated_operator_evaluator(p_operator, p_left_operand.type.builtin_type, p_right_operand.type.builtin_type);
+
+ append(GDScriptFunction::OPCODE_OPERATOR_VALIDATED, 3);
+ append(p_left_operand);
+ append(p_right_operand);
+ append(p_target);
+ append(op_func);
+ return;
+ }
+
+ // No specific types, perform variant evaluation.
+ append(GDScriptFunction::OPCODE_OPERATOR, 3);
append(p_left_operand);
append(p_right_operand);
append(p_target);
+ append(p_operator);
}
void GDScriptByteCodeGenerator::write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) {
- append(GDScriptFunction::OPCODE_EXTENDS_TEST);
+ append(GDScriptFunction::OPCODE_EXTENDS_TEST, 3);
append(p_source);
append(p_type);
append(p_target);
}
void GDScriptByteCodeGenerator::write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) {
- append(GDScriptFunction::OPCODE_IS_BUILTIN);
+ append(GDScriptFunction::OPCODE_IS_BUILTIN, 2);
append(p_source);
- append(p_type);
append(p_target);
+ append(p_type);
}
void GDScriptByteCodeGenerator::write_and_left_operand(const Address &p_left_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_left_operand);
logic_op_jump_pos1.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_right_operand);
logic_op_jump_pos2.push_back(opcodes.size());
append(0); // Jump target, will be patched.
@@ -216,29 +611,29 @@ void GDScriptByteCodeGenerator::write_and_right_operand(const Address &p_right_o
void GDScriptByteCodeGenerator::write_end_and(const Address &p_target) {
// If here means both operands are true.
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target);
// Jump away from the fail condition.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(opcodes.size() + 3);
// Here it means one of operands is false.
patch_jump(logic_op_jump_pos1.back()->get());
patch_jump(logic_op_jump_pos2.back()->get());
logic_op_jump_pos1.pop_back();
logic_op_jump_pos2.pop_back();
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
append(p_target);
}
void GDScriptByteCodeGenerator::write_or_left_operand(const Address &p_left_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF);
+ append(GDScriptFunction::OPCODE_JUMP_IF, 1);
append(p_left_operand);
logic_op_jump_pos1.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_operand) {
- append(GDScriptFunction::OPCODE_JUMP_IF);
+ append(GDScriptFunction::OPCODE_JUMP_IF, 1);
append(p_right_operand);
logic_op_jump_pos2.push_back(opcodes.size());
append(0); // Jump target, will be patched.
@@ -246,17 +641,17 @@ void GDScriptByteCodeGenerator::write_or_right_operand(const Address &p_right_op
void GDScriptByteCodeGenerator::write_end_or(const Address &p_target) {
// If here means both operands are false.
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
append(p_target);
// Jump away from the success condition.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(opcodes.size() + 3);
- // Here it means one of operands is false.
+ // Here it means one of operands is true.
patch_jump(logic_op_jump_pos1.back()->get());
patch_jump(logic_op_jump_pos2.back()->get());
logic_op_jump_pos1.pop_back();
logic_op_jump_pos2.pop_back();
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target);
}
@@ -265,18 +660,18 @@ void GDScriptByteCodeGenerator::write_start_ternary(const Address &p_target) {
}
void GDScriptByteCodeGenerator::write_ternary_condition(const Address &p_condition) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_condition);
ternary_jump_fail_pos.push_back(opcodes.size());
append(0); // Jump target, will be patched.
}
void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
- append(GDScriptFunction::OPCODE_ASSIGN);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(ternary_result.back()->get());
append(p_expr);
// Jump away from the false path.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
ternary_jump_skip_pos.push_back(opcodes.size());
append(0);
// Fail must jump here.
@@ -285,7 +680,7 @@ void GDScriptByteCodeGenerator::write_ternary_true_expr(const Address &p_expr) {
}
void GDScriptByteCodeGenerator::write_ternary_false_expr(const Address &p_expr) {
- append(GDScriptFunction::OPCODE_ASSIGN);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(ternary_result.back()->get());
append(p_expr);
}
@@ -296,127 +691,213 @@ void GDScriptByteCodeGenerator::write_end_ternary() {
}
void GDScriptByteCodeGenerator::write_set(const Address &p_target, const Address &p_index, const Address &p_source) {
- append(GDScriptFunction::OPCODE_SET);
+ 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) &&
+ 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);
+ append(p_target);
+ append(p_index);
+ append(p_source);
+ append(setter);
+ return;
+ } else if (Variant::get_member_validated_keyed_setter(p_target.type.builtin_type)) {
+ Variant::ValidatedKeyedSetter setter = Variant::get_member_validated_keyed_setter(p_target.type.builtin_type);
+ append(GDScriptFunction::OPCODE_SET_KEYED_VALIDATED, 3);
+ append(p_target);
+ append(p_index);
+ append(p_source);
+ append(setter);
+ return;
+ }
+ }
+
+ append(GDScriptFunction::OPCODE_SET_KEYED, 3);
append(p_target);
append(p_index);
append(p_source);
}
void GDScriptByteCodeGenerator::write_get(const Address &p_target, const Address &p_index, const Address &p_source) {
- append(GDScriptFunction::OPCODE_GET);
+ if (HAS_BUILTIN_TYPE(p_source)) {
+ if (IS_BUILTIN_TYPE(p_index, Variant::INT) && Variant::get_member_validated_indexed_getter(p_source.type.builtin_type)) {
+ // Use indexed getter instead.
+ Variant::ValidatedIndexedGetter getter = Variant::get_member_validated_indexed_getter(p_source.type.builtin_type);
+ append(GDScriptFunction::OPCODE_GET_INDEXED_VALIDATED, 3);
+ append(p_source);
+ append(p_index);
+ append(p_target);
+ append(getter);
+ return;
+ } else if (Variant::get_member_validated_keyed_getter(p_source.type.builtin_type)) {
+ Variant::ValidatedKeyedGetter getter = Variant::get_member_validated_keyed_getter(p_source.type.builtin_type);
+ append(GDScriptFunction::OPCODE_GET_KEYED_VALIDATED, 3);
+ append(p_source);
+ append(p_index);
+ append(p_target);
+ append(getter);
+ return;
+ }
+ }
+ append(GDScriptFunction::OPCODE_GET_KEYED, 3);
append(p_source);
append(p_index);
append(p_target);
}
void GDScriptByteCodeGenerator::write_set_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
- append(GDScriptFunction::OPCODE_SET_NAMED);
+ 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);
+ append(p_source);
+ append(setter);
+ return;
+ }
+ append(GDScriptFunction::OPCODE_SET_NAMED, 2);
append(p_target);
- append(p_name);
append(p_source);
+ append(p_name);
}
void GDScriptByteCodeGenerator::write_get_named(const Address &p_target, const StringName &p_name, const Address &p_source) {
- append(GDScriptFunction::OPCODE_GET_NAMED);
+ if (HAS_BUILTIN_TYPE(p_source) && Variant::get_member_validated_getter(p_source.type.builtin_type, p_name)) {
+ Variant::ValidatedGetter getter = Variant::get_member_validated_getter(p_source.type.builtin_type, p_name);
+ append(GDScriptFunction::OPCODE_GET_NAMED_VALIDATED, 2);
+ append(p_source);
+ append(p_target);
+ append(getter);
+ return;
+ }
+ append(GDScriptFunction::OPCODE_GET_NAMED, 2);
append(p_source);
- append(p_name);
append(p_target);
+ append(p_name);
}
void GDScriptByteCodeGenerator::write_set_member(const Address &p_value, const StringName &p_name) {
- append(GDScriptFunction::OPCODE_SET_MEMBER);
- append(p_name);
+ append(GDScriptFunction::OPCODE_SET_MEMBER, 1);
append(p_value);
+ append(p_name);
}
void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const StringName &p_name) {
- append(GDScriptFunction::OPCODE_GET_MEMBER);
- append(p_name);
+ append(GDScriptFunction::OPCODE_GET_MEMBER, 1);
append(p_target);
+ append(p_name);
}
-void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
- if (p_target.type.has_type && !p_source.type.has_type) {
- // Typed assignment.
- switch (p_target.type.kind) {
- case GDScriptDataType::BUILTIN: {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
- append(p_target.type.builtin_type);
- append(p_target);
- append(p_source);
- } break;
- case GDScriptDataType::NATIVE: {
- int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
- class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE);
- append(class_idx);
- append(p_target);
- append(p_source);
- } break;
- case GDScriptDataType::SCRIPT:
- case GDScriptDataType::GDSCRIPT: {
- Variant script = p_target.type.script_type;
- int idx = get_constant_pos(script);
-
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT);
- append(idx);
+void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_target, const Address &p_source) {
+ switch (p_target.type.kind) {
+ case GDScriptDataType::BUILTIN: {
+ if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
append(p_target);
append(p_source);
- } break;
- default: {
- ERR_PRINT("Compiler bug: unresolved assign.");
-
- // Shouldn't get here, but fail-safe to a regular assignment
- append(GDScriptFunction::OPCODE_ASSIGN);
+ } else {
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
append(p_target);
append(p_source);
+ append(p_target.type.builtin_type);
}
- }
- } else {
- if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
- // Need conversion..
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN);
- append(p_target.type.builtin_type);
+ } break;
+ case GDScriptDataType::NATIVE: {
+ int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
+ Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
+ class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE, 3);
append(p_target);
append(p_source);
- } else {
- // Either untyped assignment or already type-checked by the parser
- append(GDScriptFunction::OPCODE_ASSIGN);
+ append(class_idx);
+ } break;
+ case GDScriptDataType::SCRIPT:
+ case GDScriptDataType::GDSCRIPT: {
+ Variant script = p_target.type.script_type;
+ int idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT, 3);
+ append(p_target);
+ append(p_source);
+ append(idx);
+ } break;
+ default: {
+ ERR_PRINT("Compiler bug: unresolved assign.");
+
+ // Shouldn't get here, but fail-safe to a regular assignment
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(p_target);
append(p_source);
}
}
}
+void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
+ if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
+ append(p_target);
+ append(p_source);
+ } else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
+ // Need conversion.
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
+ append(p_target);
+ append(p_source);
+ append(p_target.type.builtin_type);
+ } else {
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ append(p_target);
+ append(p_source);
+ }
+}
+
void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) {
- append(GDScriptFunction::OPCODE_ASSIGN_TRUE);
+ append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target);
}
void GDScriptByteCodeGenerator::write_assign_false(const Address &p_target) {
- append(GDScriptFunction::OPCODE_ASSIGN_FALSE);
+ append(GDScriptFunction::OPCODE_ASSIGN_FALSE, 1);
append(p_target);
}
+void GDScriptByteCodeGenerator::write_assign_default_parameter(const Address &p_dst, const Address &p_src) {
+ write_assign(p_dst, p_src);
+ 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);
+ append(p_global);
+}
+
void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Address &p_source, const GDScriptDataType &p_type) {
+ int index = 0;
+
switch (p_type.kind) {
case GDScriptDataType::BUILTIN: {
- append(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
- append(p_type.builtin_type);
+ append(GDScriptFunction::OPCODE_CAST_TO_BUILTIN, 2);
+ index = p_type.builtin_type;
} break;
case GDScriptDataType::NATIVE: {
int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_type.native_type];
- class_idx |= (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_CAST_TO_NATIVE);
- append(class_idx);
+ Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
+ append(GDScriptFunction::OPCODE_CAST_TO_NATIVE, 3);
+ index = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
} break;
case GDScriptDataType::SCRIPT:
case GDScriptDataType::GDSCRIPT: {
Variant script = p_type.script_type;
- int idx = get_constant_pos(script);
-
- append(GDScriptFunction::OPCODE_CAST_TO_SCRIPT);
- append(idx);
+ int idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+ append(GDScriptFunction::OPCODE_CAST_TO_SCRIPT, 3);
+ index = idx;
} break;
default: {
return;
@@ -425,146 +906,406 @@ void GDScriptByteCodeGenerator::write_cast(const Address &p_target, const Addres
append(p_source);
append(p_target);
+ append(index);
}
void GDScriptByteCodeGenerator::write_call(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
- append(p_base);
- append(p_function_name);
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_SELF_BASE);
- append(p_function_name);
- append(p_arguments.size());
+ append(GDScriptFunction::OPCODE_CALL_SELF_BASE, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_ASYNC);
- append(p_arguments.size());
+ append(GDScriptFunction::OPCODE_CALL_ASYNC, 2 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
append(p_base);
+ append(p_target);
+ append(p_arguments.size());
append(p_function_name);
+}
+
+void GDScriptByteCodeGenerator::write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CALL_GDSCRIPT_UTILITY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function);
}
-void GDScriptByteCodeGenerator::write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CALL_BUILT_IN);
- append(p_function);
- append(p_arguments.size());
+void GDScriptByteCodeGenerator::write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) {
+ bool is_validated = true;
+ if (Variant::is_utility_function_vararg(p_function)) {
+ is_validated = true; // Vararg works fine with any argument, since they can be any type.
+ } else if (p_arguments.size() == Variant::get_utility_function_argument_count(p_function)) {
+ bool all_types_exact = true;
+ for (int i = 0; i < p_arguments.size(); i++) {
+ if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_utility_function_argument_type(p_function, i))) {
+ all_types_exact = false;
+ break;
+ }
+ }
+
+ is_validated = all_types_exact;
+ }
+
+ if (is_validated) {
+ append(GDScriptFunction::OPCODE_CALL_UTILITY_VALIDATED, 1 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ append(p_arguments.size());
+ append(Variant::get_validated_utility_function(p_function));
+ } else {
+ append(GDScriptFunction::OPCODE_CALL_UTILITY, 1 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ append(p_arguments.size());
+ append(p_function);
+ }
+}
+
+void GDScriptByteCodeGenerator::write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+ bool is_validated = false;
+
+ // Check if all types are correct.
+ if (Variant::is_builtin_method_vararg(p_type, p_method)) {
+ is_validated = true; // Vararg works fine with any argument, since they can be any type.
+ } else if (p_arguments.size() == Variant::get_builtin_method_argument_count(p_type, p_method)) {
+ bool all_types_exact = true;
+ for (int i = 0; i < p_arguments.size(); i++) {
+ if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_builtin_method_argument_type(p_type, p_method, i))) {
+ all_types_exact = false;
+ break;
+ }
+ }
+
+ is_validated = all_types_exact;
+ }
+
+ if (!is_validated) {
+ // Perform regular call.
+ write_call(p_target, p_base, p_method, p_arguments);
+ return;
+ }
+
+ if (p_target.mode == Address::TEMPORARY) {
+ Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
+ Variant::Type temp_type = temporaries[p_target.address].type;
+ if (result_type != temp_type) {
+ write_type_adjust(p_target, result_type);
+ }
+ }
+
+ append(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
+
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(Variant::get_validated_builtin_method(p_type, p_method));
}
-void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
- append(p_base);
- append(p_method->get_name());
+void GDScriptByteCodeGenerator::write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) {
+ bool is_validated = false;
+
+ // Check if all types are correct.
+ if (Variant::is_builtin_method_vararg(p_type, p_method)) {
+ is_validated = true; // Vararg works fine with any argument, since they can be any type.
+ } else if (p_arguments.size() == Variant::get_builtin_method_argument_count(p_type, p_method)) {
+ bool all_types_exact = true;
+ for (int i = 0; i < p_arguments.size(); i++) {
+ if (!IS_BUILTIN_TYPE(p_arguments[i], Variant::get_builtin_method_argument_type(p_type, p_method, i))) {
+ all_types_exact = false;
+ break;
+ }
+ }
+
+ is_validated = all_types_exact;
+ }
+
+ if (!is_validated) {
+ // Perform regular call.
+ append(GDScriptFunction::OPCODE_CALL_BUILTIN_STATIC, p_arguments.size() + 1);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ append(p_type);
+ append(p_method);
+ append(p_arguments.size());
+ return;
+ }
+
+ if (p_target.mode == Address::TEMPORARY) {
+ Variant::Type result_type = Variant::get_builtin_method_return_type(p_type, p_method);
+ Variant::Type temp_type = temporaries[p_target.address].type;
+ if (result_type != temp_type) {
+ write_type_adjust(p_target, result_type);
+ }
+ }
+
+ append(GDScriptFunction::OPCODE_CALL_BUILTIN_TYPE_VALIDATED, 2 + p_arguments.size());
+
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(Address()); // No base since it's static.
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(Variant::get_validated_builtin_method(p_type, p_method));
}
-void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
+void GDScriptByteCodeGenerator::write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
append(p_base);
- append(p_method->get_name());
+ append(p_target);
+ append(p_arguments.size());
+ append(p_method);
+}
+
+void GDScriptByteCodeGenerator::write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) {
+#define CASE_TYPE(m_type) \
+ case Variant::m_type: \
+ append(GDScriptFunction::OPCODE_CALL_PTRCALL_##m_type, 2 + p_arguments.size()); \
+ break
+
+ bool is_ptrcall = true;
+
+ if (p_method->has_return()) {
+ MethodInfo info;
+ ClassDB::get_method_info(p_method->get_instance_class(), p_method->get_name(), &info);
+ switch (info.return_val.type) {
+ CASE_TYPE(BOOL);
+ CASE_TYPE(INT);
+ CASE_TYPE(FLOAT);
+ CASE_TYPE(STRING);
+ CASE_TYPE(VECTOR2);
+ CASE_TYPE(VECTOR2I);
+ CASE_TYPE(RECT2);
+ CASE_TYPE(RECT2I);
+ CASE_TYPE(VECTOR3);
+ CASE_TYPE(VECTOR3I);
+ CASE_TYPE(TRANSFORM2D);
+ CASE_TYPE(PLANE);
+ CASE_TYPE(AABB);
+ CASE_TYPE(BASIS);
+ CASE_TYPE(TRANSFORM3D);
+ CASE_TYPE(COLOR);
+ CASE_TYPE(STRING_NAME);
+ CASE_TYPE(NODE_PATH);
+ CASE_TYPE(RID);
+ CASE_TYPE(QUATERNION);
+ CASE_TYPE(OBJECT);
+ CASE_TYPE(CALLABLE);
+ CASE_TYPE(SIGNAL);
+ CASE_TYPE(DICTIONARY);
+ CASE_TYPE(ARRAY);
+ CASE_TYPE(PACKED_BYTE_ARRAY);
+ CASE_TYPE(PACKED_INT32_ARRAY);
+ CASE_TYPE(PACKED_INT64_ARRAY);
+ CASE_TYPE(PACKED_FLOAT32_ARRAY);
+ CASE_TYPE(PACKED_FLOAT64_ARRAY);
+ CASE_TYPE(PACKED_STRING_ARRAY);
+ CASE_TYPE(PACKED_VECTOR2_ARRAY);
+ CASE_TYPE(PACKED_VECTOR3_ARRAY);
+ CASE_TYPE(PACKED_COLOR_ARRAY);
+ default:
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL_METHOD_BIND : GDScriptFunction::OPCODE_CALL_METHOD_BIND_RET, 2 + p_arguments.size());
+ is_ptrcall = false;
+ break;
+ }
+ } else {
+ append(GDScriptFunction::OPCODE_CALL_PTRCALL_NO_RETURN, 2 + p_arguments.size());
+ }
+
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_method);
+ if (is_ptrcall) {
+ alloc_ptrcall(p_arguments.size());
+ }
+
+#undef CASE_TYPE
}
void GDScriptByteCodeGenerator::write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+ append(p_target);
append(p_arguments.size());
- append(GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS);
append(p_function_name);
+}
+
+void GDScriptByteCodeGenerator::write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CALL_ASYNC, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(GDScriptFunction::ADDR_SELF);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
}
void GDScriptByteCodeGenerator::write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) {
- append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN);
- append(p_arguments.size());
- append(p_base);
- append(p_function_name);
+ append(p_target.mode == Address::NIL ? GDScriptFunction::OPCODE_CALL : GDScriptFunction::OPCODE_CALL_RETURN, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
+ append(p_base);
append(p_target);
- alloc_call(p_arguments.size());
+ append(p_arguments.size());
+ append(p_function_name);
+}
+
+void GDScriptByteCodeGenerator::write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) {
+ append(GDScriptFunction::OPCODE_CREATE_LAMBDA, 1 + p_captures.size());
+ for (int i = 0; i < p_captures.size(); i++) {
+ append(p_captures[i]);
+ }
+
+ append(p_target);
+ append(p_captures.size());
+ append(p_function);
}
void GDScriptByteCodeGenerator::write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT);
- append(p_type);
- append(p_arguments.size());
+ // Try to find an appropriate constructor.
+ bool all_have_type = true;
+ Vector<Variant::Type> arg_types;
+ for (int i = 0; i < p_arguments.size(); i++) {
+ if (!HAS_BUILTIN_TYPE(p_arguments[i])) {
+ all_have_type = false;
+ break;
+ }
+ arg_types.push_back(p_arguments[i].type.builtin_type);
+ }
+ if (all_have_type) {
+ int valid_constructor = -1;
+ for (int i = 0; i < Variant::get_constructor_count(p_type); i++) {
+ if (Variant::get_constructor_argument_count(p_type, i) != p_arguments.size()) {
+ continue;
+ }
+ int types_correct = true;
+ for (int j = 0; j < arg_types.size(); j++) {
+ if (arg_types[j] != Variant::get_constructor_argument_type(p_type, i, j)) {
+ types_correct = false;
+ break;
+ }
+ }
+ if (types_correct) {
+ valid_constructor = i;
+ break;
+ }
+ }
+ if (valid_constructor >= 0) {
+ append(GDScriptFunction::OPCODE_CONSTRUCT_VALIDATED, 1 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
+ append(p_arguments.size());
+ append(Variant::get_validated_constructor(p_type, valid_constructor));
+ return;
+ }
+ }
+
+ append(GDScriptFunction::OPCODE_CONSTRUCT, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
+ append(p_arguments.size());
+ append(p_type);
}
void GDScriptByteCodeGenerator::write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY);
+ append(GDScriptFunction::OPCODE_CONSTRUCT_ARRAY, 1 + p_arguments.size());
+ for (int i = 0; i < p_arguments.size(); i++) {
+ append(p_arguments[i]);
+ }
+ append(p_target);
append(p_arguments.size());
+}
+
+void GDScriptByteCodeGenerator::write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) {
+ append(GDScriptFunction::OPCODE_CONSTRUCT_TYPED_ARRAY, 2 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
+ if (p_element_type.script_type) {
+ Variant script_type = Ref<Script>(p_element_type.script_type);
+ int addr = get_constant_pos(script_type);
+ addr |= GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS;
+ append(addr);
+ } else {
+ append(Address()); // null.
+ }
+ append(p_arguments.size());
+ append(p_element_type.builtin_type);
+ append(p_element_type.native_type);
}
void GDScriptByteCodeGenerator::write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) {
- append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY);
- append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
+ append(GDScriptFunction::OPCODE_CONSTRUCT_DICTIONARY, 1 + p_arguments.size());
for (int i = 0; i < p_arguments.size(); i++) {
append(p_arguments[i]);
}
append(p_target);
+ append(p_arguments.size() / 2); // This is number of key-value pairs, so only half of actual arguments.
}
void GDScriptByteCodeGenerator::write_await(const Address &p_target, const Address &p_operand) {
- append(GDScriptFunction::OPCODE_AWAIT);
+ append(GDScriptFunction::OPCODE_AWAIT, 1);
append(p_operand);
- append(GDScriptFunction::OPCODE_AWAIT_RESUME);
+ append(GDScriptFunction::OPCODE_AWAIT_RESUME, 1);
append(p_target);
}
void GDScriptByteCodeGenerator::write_if(const Address &p_condition) {
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_condition);
if_jmp_addrs.push_back(opcodes.size());
append(0); // Jump destination, will be patched.
}
void GDScriptByteCodeGenerator::write_else() {
- append(GDScriptFunction::OPCODE_JUMP); // Jump from true if block;
+ append(GDScriptFunction::OPCODE_JUMP, 0); // Jump from true if block;
int else_jmp_addr = opcodes.size();
append(0); // Jump destination, will be patched.
@@ -578,41 +1319,144 @@ void GDScriptByteCodeGenerator::write_endif() {
if_jmp_addrs.pop_back();
}
-void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Address &p_list) {
- int counter_pos = increase_stack() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- int container_pos = increase_stack() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
+void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) {
+ Address counter(Address::LOCAL_VARIABLE, add_local("@counter_pos", p_iterator_type), p_iterator_type);
+ Address container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type);
- current_breaks_to_patch.push_back(List<int>());
+ // Store state.
+ for_counter_variables.push_back(counter);
+ for_container_variables.push_back(container);
+}
+
+void GDScriptByteCodeGenerator::write_for_assignment(const Address &p_variable, const Address &p_list) {
+ const Address &container = for_container_variables.back()->get();
// Assign container.
- append(GDScriptFunction::OPCODE_ASSIGN);
- append(container_pos);
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ append(container);
append(p_list);
+ for_iterator_variables.push_back(p_variable);
+}
+
+void GDScriptByteCodeGenerator::write_for() {
+ const Address &iterator = for_iterator_variables.back()->get();
+ const Address &counter = for_counter_variables.back()->get();
+ const Address &container = for_container_variables.back()->get();
+
+ current_breaks_to_patch.push_back(List<int>());
+
+ GDScriptFunction::Opcode begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN;
+ GDScriptFunction::Opcode iterate_opcode = GDScriptFunction::OPCODE_ITERATE;
+
+ if (container.type.has_type) {
+ if (container.type.kind == GDScriptDataType::BUILTIN) {
+ switch (container.type.builtin_type) {
+ case Variant::INT:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_INT;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_INT;
+ break;
+ case Variant::FLOAT:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_FLOAT;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_FLOAT;
+ break;
+ case Variant::VECTOR2:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR2;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR2;
+ break;
+ case Variant::VECTOR2I:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR2I;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR2I;
+ break;
+ case Variant::VECTOR3:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR3;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR3;
+ break;
+ case Variant::VECTOR3I:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_VECTOR3I;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_VECTOR3I;
+ break;
+ case Variant::STRING:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_STRING;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_STRING;
+ break;
+ case Variant::DICTIONARY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_DICTIONARY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_DICTIONARY;
+ break;
+ case Variant::ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_ARRAY;
+ break;
+ case Variant::PACKED_BYTE_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_BYTE_ARRAY;
+ break;
+ case Variant::PACKED_INT32_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_INT32_ARRAY;
+ break;
+ case Variant::PACKED_INT64_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_INT64_ARRAY;
+ break;
+ case Variant::PACKED_FLOAT32_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_FLOAT32_ARRAY;
+ break;
+ case Variant::PACKED_FLOAT64_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_FLOAT64_ARRAY;
+ break;
+ case Variant::PACKED_STRING_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_STRING_ARRAY;
+ break;
+ case Variant::PACKED_VECTOR2_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_VECTOR2_ARRAY;
+ break;
+ case Variant::PACKED_VECTOR3_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_VECTOR3_ARRAY;
+ break;
+ case Variant::PACKED_COLOR_ARRAY:
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_PACKED_COLOR_ARRAY;
+ break;
+ default:
+ break;
+ }
+ } else {
+ begin_opcode = GDScriptFunction::OPCODE_ITERATE_BEGIN_OBJECT;
+ iterate_opcode = GDScriptFunction::OPCODE_ITERATE_OBJECT;
+ }
+ }
+
// Begin loop.
- append(GDScriptFunction::OPCODE_ITERATE_BEGIN);
- append(counter_pos);
- append(container_pos);
+ append(begin_opcode, 3);
+ append(counter);
+ append(container);
+ append(iterator);
for_jmp_addrs.push_back(opcodes.size());
append(0); // End of loop address, will be patched.
- append(p_variable);
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(opcodes.size() + 6); // Skip over 'continue' code.
// Next iteration.
int continue_addr = opcodes.size();
continue_addrs.push_back(continue_addr);
- append(GDScriptFunction::OPCODE_ITERATE);
- append(counter_pos);
- append(container_pos);
+ append(iterate_opcode, 3);
+ append(counter);
+ append(container);
+ append(iterator);
for_jmp_addrs.push_back(opcodes.size());
append(0); // Jump destination, will be patched.
- append(p_variable);
}
void GDScriptByteCodeGenerator::write_endfor() {
// Jump back to loop check.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(continue_addrs.back()->get());
continue_addrs.pop_back();
@@ -623,12 +1467,15 @@ void GDScriptByteCodeGenerator::write_endfor() {
}
// Patch break statements.
- for (const List<int>::Element *E = current_breaks_to_patch.back()->get().front(); E; E = E->next()) {
- patch_jump(E->get());
+ for (const int &E : current_breaks_to_patch.back()->get()) {
+ patch_jump(E);
}
current_breaks_to_patch.pop_back();
- current_stack_size -= 2; // Remove loop temporaries.
+ // Pop state.
+ for_iterator_variables.pop_back();
+ for_counter_variables.pop_back();
+ for_container_variables.pop_back();
}
void GDScriptByteCodeGenerator::start_while_condition() {
@@ -638,7 +1485,7 @@ void GDScriptByteCodeGenerator::start_while_condition() {
void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
// Condition check.
- append(GDScriptFunction::OPCODE_JUMP_IF_NOT);
+ append(GDScriptFunction::OPCODE_JUMP_IF_NOT, 1);
append(p_condition);
while_jmp_addrs.push_back(opcodes.size());
append(0); // End of loop address, will be patched.
@@ -646,7 +1493,7 @@ void GDScriptByteCodeGenerator::write_while(const Address &p_condition) {
void GDScriptByteCodeGenerator::write_endwhile() {
// Jump back to loop check.
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(continue_addrs.back()->get());
continue_addrs.pop_back();
@@ -655,8 +1502,8 @@ void GDScriptByteCodeGenerator::write_endwhile() {
while_jmp_addrs.pop_back();
// Patch break statements.
- for (const List<int>::Element *E = current_breaks_to_patch.back()->get().front(); E; E = E->next()) {
- patch_jump(E->get());
+ for (const int &E : current_breaks_to_patch.back()->get()) {
+ patch_jump(E);
}
current_breaks_to_patch.pop_back();
}
@@ -667,8 +1514,8 @@ void GDScriptByteCodeGenerator::start_match() {
void GDScriptByteCodeGenerator::start_match_branch() {
// Patch continue statements.
- for (const List<int>::Element *E = match_continues_to_patch.back()->get().front(); E; E = E->next()) {
- patch_jump(E->get());
+ for (const int &E : match_continues_to_patch.back()->get()) {
+ patch_jump(E);
}
match_continues_to_patch.pop_back();
// Start a new list for next branch.
@@ -677,46 +1524,122 @@ void GDScriptByteCodeGenerator::start_match_branch() {
void GDScriptByteCodeGenerator::end_match() {
// Patch continue statements.
- for (const List<int>::Element *E = match_continues_to_patch.back()->get().front(); E; E = E->next()) {
- patch_jump(E->get());
+ for (const int &E : match_continues_to_patch.back()->get()) {
+ patch_jump(E);
}
match_continues_to_patch.pop_back();
}
void GDScriptByteCodeGenerator::write_break() {
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
current_breaks_to_patch.back()->get().push_back(opcodes.size());
append(0);
}
void GDScriptByteCodeGenerator::write_continue() {
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
append(continue_addrs.back()->get());
}
void GDScriptByteCodeGenerator::write_continue_match() {
- append(GDScriptFunction::OPCODE_JUMP);
+ append(GDScriptFunction::OPCODE_JUMP, 0);
match_continues_to_patch.back()->get().push_back(opcodes.size());
append(0);
}
void GDScriptByteCodeGenerator::write_breakpoint() {
- append(GDScriptFunction::OPCODE_BREAKPOINT);
+ append(GDScriptFunction::OPCODE_BREAKPOINT, 0);
}
void GDScriptByteCodeGenerator::write_newline(int p_line) {
- append(GDScriptFunction::OPCODE_LINE);
+ append(GDScriptFunction::OPCODE_LINE, 0);
append(p_line);
current_line = p_line;
}
void GDScriptByteCodeGenerator::write_return(const Address &p_return_value) {
- append(GDScriptFunction::OPCODE_RETURN);
- append(p_return_value);
+ if (!function->return_type.has_type || p_return_value.type.has_type) {
+ // Either the function is untyped or the return value is also typed.
+
+ // If this is a typed function, then we need to check for potential conversions.
+ if (function->return_type.has_type) {
+ if (function->return_type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) {
+ // Typed array.
+ const GDScriptDataType &element_type = function->return_type.get_container_element_type();
+
+ Variant script = function->return_type.script_type;
+ int script_idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+
+ append(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY, 2);
+ append(p_return_value);
+ append(script_idx);
+ append(element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT);
+ append(element_type.native_type);
+ } else if (function->return_type.kind == GDScriptDataType::BUILTIN && p_return_value.type.kind == GDScriptDataType::BUILTIN && function->return_type.builtin_type != p_return_value.type.builtin_type) {
+ // Add conversion.
+ append(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN, 1);
+ append(p_return_value);
+ append(function->return_type.builtin_type);
+ } else {
+ // Just assign.
+ append(GDScriptFunction::OPCODE_RETURN, 1);
+ append(p_return_value);
+ }
+ } else {
+ append(GDScriptFunction::OPCODE_RETURN, 1);
+ append(p_return_value);
+ }
+ } else {
+ switch (function->return_type.kind) {
+ case GDScriptDataType::BUILTIN: {
+ if (function->return_type.builtin_type == Variant::ARRAY && function->return_type.has_container_element_type()) {
+ const GDScriptDataType &element_type = function->return_type.get_container_element_type();
+
+ Variant script = function->return_type.script_type;
+ int script_idx = get_constant_pos(script);
+ script_idx |= (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+
+ append(GDScriptFunction::OPCODE_RETURN_TYPED_ARRAY, 2);
+ append(p_return_value);
+ append(script_idx);
+ append(element_type.kind == GDScriptDataType::BUILTIN ? element_type.builtin_type : Variant::OBJECT);
+ append(element_type.native_type);
+ } else {
+ append(GDScriptFunction::OPCODE_RETURN_TYPED_BUILTIN, 1);
+ append(p_return_value);
+ append(function->return_type.builtin_type);
+ }
+ } break;
+ case GDScriptDataType::NATIVE: {
+ append(GDScriptFunction::OPCODE_RETURN_TYPED_NATIVE, 2);
+ append(p_return_value);
+ int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[function->return_type.native_type];
+ Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
+ class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+ append(class_idx);
+ } break;
+ case GDScriptDataType::GDSCRIPT:
+ case GDScriptDataType::SCRIPT: {
+ Variant script = function->return_type.script_type;
+ int script_idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+
+ append(GDScriptFunction::OPCODE_RETURN_TYPED_SCRIPT, 2);
+ append(p_return_value);
+ append(script_idx);
+ } break;
+ default: {
+ ERR_PRINT("Compiler bug: unresolved return.");
+
+ // Shouldn't get here, but fail-safe to a regular return;
+ append(GDScriptFunction::OPCODE_RETURN, 1);
+ append(p_return_value);
+ } break;
+ }
+ }
}
void GDScriptByteCodeGenerator::write_assert(const Address &p_test, const Address &p_message) {
- append(GDScriptFunction::OPCODE_ASSERT);
+ append(GDScriptFunction::OPCODE_ASSERT, 2);
append(p_test);
append(p_message);
}
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 62438b6dd2..fbbf5802fd 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,21 @@
#include "gdscript_codegen.h"
+#include "gdscript_function.h"
+#include "gdscript_utility_functions.h"
+
class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
+ struct StackSlot {
+ Variant::Type type = Variant::NIL;
+ Vector<int> bytecode_indices;
+
+ StackSlot() = default;
+ StackSlot(Variant::Type p_type) :
+ type(p_type) {}
+ };
+
+ const static int RESERVED_STACK = 3; // For self, class, and nil.
+
bool ended = false;
GDScriptFunction *function = nullptr;
bool debug_stack = false;
@@ -41,26 +55,52 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
Vector<int> opcodes;
List<Map<StringName, int>> stack_id_stack;
Map<StringName, int> stack_identifiers;
+ List<int> stack_identifiers_counts;
Map<StringName, int> local_constants;
+ Vector<StackSlot> locals;
+ Vector<StackSlot> temporaries;
+ List<int> used_temporaries;
+ Map<Variant::Type, List<int>> temporaries_pool;
+
List<GDScriptFunction::StackDebug> stack_debug;
List<Map<StringName, int>> block_identifier_stack;
Map<StringName, int> block_identifiers;
- int current_stack_size = 0;
- int current_temporaries = 0;
+ int max_locals = 0;
+ int current_line = 0;
+ int instr_args_max = 0;
+ int ptrcall_max = 0;
+
+#ifdef DEBUG_ENABLED
+ List<int> temp_stack;
+#endif
HashMap<Variant, int, VariantHasher, VariantComparator> constant_map;
Map<StringName, int> name_map;
#ifdef TOOLS_ENABLED
Vector<StringName> named_globals;
#endif
- int current_line = 0;
- int stack_max = 0;
- int call_max = 0;
-
- List<int> if_jmp_addrs; // List since this can be nested.
+ Map<Variant::ValidatedOperatorEvaluator, int> operator_func_map;
+ Map<Variant::ValidatedSetter, int> setters_map;
+ Map<Variant::ValidatedGetter, int> getters_map;
+ Map<Variant::ValidatedKeyedSetter, int> keyed_setters_map;
+ Map<Variant::ValidatedKeyedGetter, int> keyed_getters_map;
+ Map<Variant::ValidatedIndexedSetter, int> indexed_setters_map;
+ Map<Variant::ValidatedIndexedGetter, int> indexed_getters_map;
+ Map<Variant::ValidatedBuiltInMethod, int> builtin_method_map;
+ Map<Variant::ValidatedConstructor, int> constructors_map;
+ Map<Variant::ValidatedUtilityFunction, int> utilities_map;
+ Map<GDScriptUtilityFunctions::FunctionPtr, int> gds_utilities_map;
+ Map<MethodBind *, int> method_bind_map;
+ Map<GDScriptFunction *, int> lambdas_map;
+
+ // Lists since these can be nested.
+ List<int> if_jmp_addrs;
List<int> for_jmp_addrs;
+ List<Address> for_iterator_variables;
+ List<Address> for_counter_variables;
+ List<Address> for_container_variables;
List<int> while_jmp_addrs;
List<int> continue_addrs;
@@ -76,6 +116,9 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
List<List<int>> match_continues_to_patch;
void add_stack_identifier(const StringName &p_id, int p_stackpos) {
+ if (locals.size() > max_locals) {
+ max_locals = locals.size();
+ }
stack_identifiers[p_id] = p_stackpos;
if (debug_stack) {
block_identifiers[p_id] = p_stackpos;
@@ -89,25 +132,33 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
}
void push_stack_identifiers() {
+ stack_identifiers_counts.push_back(locals.size());
stack_id_stack.push_back(stack_identifiers);
if (debug_stack) {
- block_identifier_stack.push_back(block_identifiers);
+ Map<StringName, int> block_ids(block_identifiers);
+ block_identifier_stack.push_back(block_ids);
block_identifiers.clear();
}
}
void pop_stack_identifiers() {
+ int current_locals = stack_identifiers_counts.back()->get();
+ stack_identifiers_counts.pop_back();
stack_identifiers = stack_id_stack.back()->get();
- current_stack_size = stack_identifiers.size() + current_temporaries;
stack_id_stack.pop_back();
-
+#ifdef DEBUG_ENABLED
+ if (!used_temporaries.is_empty()) {
+ ERR_PRINT("Leaving block with non-zero temporary variables: " + itos(used_temporaries.size()));
+ }
+#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();
@@ -127,58 +178,166 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
}
int get_constant_pos(const Variant &p_constant) {
- if (constant_map.has(p_constant))
+ if (constant_map.has(p_constant)) {
return constant_map[p_constant];
+ }
int pos = constant_map.size();
constant_map[p_constant] = pos;
return pos;
}
- void alloc_stack(int p_level) {
- if (p_level >= stack_max)
- stack_max = p_level + 1;
+ int get_operation_pos(const Variant::ValidatedOperatorEvaluator p_operation) {
+ if (operator_func_map.has(p_operation)) {
+ return operator_func_map[p_operation];
+ }
+ int pos = operator_func_map.size();
+ operator_func_map[p_operation] = pos;
+ return pos;
+ }
+
+ int get_setter_pos(const Variant::ValidatedSetter p_setter) {
+ if (setters_map.has(p_setter)) {
+ return setters_map[p_setter];
+ }
+ int pos = setters_map.size();
+ setters_map[p_setter] = pos;
+ return pos;
+ }
+
+ int get_getter_pos(const Variant::ValidatedGetter p_getter) {
+ if (getters_map.has(p_getter)) {
+ return getters_map[p_getter];
+ }
+ int pos = getters_map.size();
+ getters_map[p_getter] = pos;
+ return pos;
+ }
+
+ int get_keyed_setter_pos(const Variant::ValidatedKeyedSetter p_keyed_setter) {
+ if (keyed_setters_map.has(p_keyed_setter)) {
+ return keyed_setters_map[p_keyed_setter];
+ }
+ int pos = keyed_setters_map.size();
+ keyed_setters_map[p_keyed_setter] = pos;
+ return pos;
+ }
+
+ int get_keyed_getter_pos(const Variant::ValidatedKeyedGetter p_keyed_getter) {
+ if (keyed_getters_map.has(p_keyed_getter)) {
+ return keyed_getters_map[p_keyed_getter];
+ }
+ int pos = keyed_getters_map.size();
+ keyed_getters_map[p_keyed_getter] = pos;
+ return pos;
+ }
+
+ int get_indexed_setter_pos(const Variant::ValidatedIndexedSetter p_indexed_setter) {
+ if (indexed_setters_map.has(p_indexed_setter)) {
+ return indexed_setters_map[p_indexed_setter];
+ }
+ int pos = indexed_setters_map.size();
+ indexed_setters_map[p_indexed_setter] = pos;
+ return pos;
+ }
+
+ int get_indexed_getter_pos(const Variant::ValidatedIndexedGetter p_indexed_getter) {
+ if (indexed_getters_map.has(p_indexed_getter)) {
+ return indexed_getters_map[p_indexed_getter];
+ }
+ int pos = indexed_getters_map.size();
+ indexed_getters_map[p_indexed_getter] = pos;
+ return pos;
+ }
+
+ int get_builtin_method_pos(const Variant::ValidatedBuiltInMethod p_method) {
+ if (builtin_method_map.has(p_method)) {
+ return builtin_method_map[p_method];
+ }
+ int pos = builtin_method_map.size();
+ builtin_method_map[p_method] = pos;
+ return pos;
+ }
+
+ int get_constructor_pos(const Variant::ValidatedConstructor p_constructor) {
+ if (constructors_map.has(p_constructor)) {
+ return constructors_map[p_constructor];
+ }
+ int pos = constructors_map.size();
+ constructors_map[p_constructor] = pos;
+ return pos;
+ }
+
+ int get_utility_pos(const Variant::ValidatedUtilityFunction p_utility) {
+ if (utilities_map.has(p_utility)) {
+ return utilities_map[p_utility];
+ }
+ int pos = utilities_map.size();
+ utilities_map[p_utility] = pos;
+ return pos;
+ }
+
+ int get_gds_utility_pos(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) {
+ if (gds_utilities_map.has(p_gds_utility)) {
+ return gds_utilities_map[p_gds_utility];
+ }
+ int pos = gds_utilities_map.size();
+ gds_utilities_map[p_gds_utility] = pos;
+ return pos;
+ }
+
+ int get_method_bind_pos(MethodBind *p_method) {
+ if (method_bind_map.has(p_method)) {
+ return method_bind_map[p_method];
+ }
+ int pos = method_bind_map.size();
+ method_bind_map[p_method] = pos;
+ return pos;
}
- void alloc_call(int p_params) {
- if (p_params >= call_max)
- call_max = p_params;
+ int get_lambda_function_pos(GDScriptFunction *p_lambda_function) {
+ if (lambdas_map.has(p_lambda_function)) {
+ return lambdas_map[p_lambda_function];
+ }
+ int pos = lambdas_map.size();
+ lambdas_map[p_lambda_function] = pos;
+ return pos;
}
- int increase_stack() {
- int top = current_stack_size++;
- alloc_stack(current_stack_size);
- return top;
+ void alloc_ptrcall(int p_params) {
+ if (p_params >= ptrcall_max) {
+ ptrcall_max = p_params;
+ }
}
int address_of(const Address &p_address) {
switch (p_address.mode) {
case Address::SELF:
- return GDScriptFunction::ADDR_TYPE_SELF << GDScriptFunction::ADDR_BITS;
+ return GDScriptFunction::ADDR_SELF;
case Address::CLASS:
- return GDScriptFunction::ADDR_TYPE_CLASS << GDScriptFunction::ADDR_BITS;
+ return GDScriptFunction::ADDR_CLASS;
case Address::MEMBER:
return p_address.address | (GDScriptFunction::ADDR_TYPE_MEMBER << GDScriptFunction::ADDR_BITS);
- case Address::CLASS_CONSTANT:
- return p_address.address | (GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS);
- case Address::LOCAL_CONSTANT:
case Address::CONSTANT:
- return p_address.address | (GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS);
+ return p_address.address | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
case Address::LOCAL_VARIABLE:
- case Address::TEMPORARY:
case Address::FUNCTION_PARAMETER:
return p_address.address | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS);
- case Address::GLOBAL:
- return p_address.address | (GDScriptFunction::ADDR_TYPE_GLOBAL << GDScriptFunction::ADDR_BITS);
- case Address::NAMED_GLOBAL:
- return p_address.address | (GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL << GDScriptFunction::ADDR_BITS);
+ case Address::TEMPORARY:
+ temporaries.write[p_address.address].bytecode_indices.push_back(opcodes.size());
+ return -1;
case Address::NIL:
- return GDScriptFunction::ADDR_TYPE_NIL << GDScriptFunction::ADDR_BITS;
+ return GDScriptFunction::ADDR_NIL;
}
return -1; // Unreachable.
}
- void append(int code) {
- opcodes.push_back(code);
+ void append(GDScriptFunction::Opcode p_code, int p_argument_count) {
+ opcodes.push_back((p_code & GDScriptFunction::INSTR_MASK) | (p_argument_count << GDScriptFunction::INSTR_BITS));
+ instr_args_max = MAX(instr_args_max, p_argument_count);
+ }
+
+ void append(int p_code) {
+ opcodes.push_back(p_code);
}
void append(const Address &p_address) {
@@ -189,6 +348,58 @@ class GDScriptByteCodeGenerator : public GDScriptCodeGenerator {
opcodes.push_back(get_name_map_pos(p_name));
}
+ void append(const Variant::ValidatedOperatorEvaluator p_operation) {
+ opcodes.push_back(get_operation_pos(p_operation));
+ }
+
+ void append(const Variant::ValidatedSetter p_setter) {
+ opcodes.push_back(get_setter_pos(p_setter));
+ }
+
+ void append(const Variant::ValidatedGetter p_getter) {
+ opcodes.push_back(get_getter_pos(p_getter));
+ }
+
+ void append(const Variant::ValidatedKeyedSetter p_keyed_setter) {
+ opcodes.push_back(get_keyed_setter_pos(p_keyed_setter));
+ }
+
+ void append(const Variant::ValidatedKeyedGetter p_keyed_getter) {
+ opcodes.push_back(get_keyed_getter_pos(p_keyed_getter));
+ }
+
+ void append(const Variant::ValidatedIndexedSetter p_indexed_setter) {
+ opcodes.push_back(get_indexed_setter_pos(p_indexed_setter));
+ }
+
+ void append(const Variant::ValidatedIndexedGetter p_indexed_getter) {
+ opcodes.push_back(get_indexed_getter_pos(p_indexed_getter));
+ }
+
+ void append(const Variant::ValidatedBuiltInMethod p_method) {
+ opcodes.push_back(get_builtin_method_pos(p_method));
+ }
+
+ void append(const Variant::ValidatedConstructor p_constructor) {
+ opcodes.push_back(get_constructor_pos(p_constructor));
+ }
+
+ void append(const Variant::ValidatedUtilityFunction p_utility) {
+ opcodes.push_back(get_utility_pos(p_utility));
+ }
+
+ void append(const GDScriptUtilityFunctions::FunctionPtr p_gds_utility) {
+ opcodes.push_back(get_gds_utility_pos(p_gds_utility));
+ }
+
+ void append(MethodBind *p_method) {
+ opcodes.push_back(get_method_bind_pos(p_method));
+ }
+
+ void append(GDScriptFunction *p_lambda_function) {
+ opcodes.push_back(get_lambda_function_pos(p_lambda_function));
+ }
+
void patch_jump(int p_address) {
opcodes.write[p_address] = opcodes.size();
}
@@ -199,7 +410,7 @@ public:
virtual uint32_t add_local_constant(const StringName &p_name, const Variant &p_constant) override;
virtual uint32_t add_or_get_constant(const Variant &p_constant) override;
virtual uint32_t add_or_get_name(const StringName &p_name) override;
- virtual uint32_t add_temporary() override;
+ virtual uint32_t add_temporary(const GDScriptDataType &p_type) override;
virtual void pop_temporary() override;
virtual void start_parameters() override;
@@ -208,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::RPCMode p_rpc_mode, 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
@@ -216,7 +427,9 @@ public:
#endif
virtual void set_initial_line(int p_line) override;
- virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) override;
+ virtual void write_type_adjust(const Address &p_target, Variant::Type p_new_type) override;
+ virtual void write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) override;
+ virtual void write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) override;
virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) override;
virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) override;
virtual void write_and_left_operand(const Address &p_left_operand) override;
@@ -237,25 +450,37 @@ public:
virtual void write_set_member(const Address &p_value, const StringName &p_name) override;
virtual void write_get_member(const Address &p_target, const StringName &p_name) override;
virtual void write_assign(const Address &p_target, const Address &p_source) override;
+ virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) override;
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;
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
- virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) override;
- virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
- virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) override;
+ virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) override;
+ virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
+ virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) override;
virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) override;
+ virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) override;
virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) override;
virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) override;
+ virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) override;
virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) override;
virtual void write_await(const Address &p_target, const Address &p_operand) override;
virtual void write_if(const Address &p_condition) override;
virtual void write_else() override;
virtual void write_endif() override;
- virtual void write_for(const Address &p_variable, const Address &p_list) override;
+ virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) override;
+ virtual void write_for_assignment(const Address &p_variable, const Address &p_list) override;
+ virtual void write_for() override;
virtual void write_endfor() override;
virtual void start_while_condition() override;
virtual void write_while(const Address &p_condition) override;
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 57b95f5b21..bb0d9e9e9b 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,8 @@
#include "gdscript_cache.h"
-#include "core/os/file_access.h"
-#include "core/vector.h"
+#include "core/io/file_access.h"
+#include "core/templates/vector.h"
#include "gdscript.h"
#include "gdscript_analyzer.h"
#include "gdscript_parser.h"
@@ -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;
}
}
@@ -134,7 +128,7 @@ Ref<GDScriptParserRef> GDScriptCache::get_parser(const String &p_path, GDScriptP
return ref;
}
GDScriptParser *parser = memnew(GDScriptParser);
- ref.instance();
+ ref.instantiate();
ref->parser = parser;
ref->path = p_path;
singleton->parser_map[p_path] = ref.ptr();
@@ -153,9 +147,9 @@ String GDScriptCache::get_source_code(const String &p_path) {
ERR_FAIL_COND_V(err, "");
}
- int len = f->get_len();
+ uint64_t len = f->get_length();
source_file.resize(len + 1);
- int r = f->get_buffer(source_file.ptrw(), len);
+ uint64_t r = f->get_buffer(source_file.ptrw(), len);
f->close();
ERR_FAIL_COND_V(r != len, "");
source_file.write[len] = 0;
@@ -180,7 +174,7 @@ Ref<GDScript> GDScriptCache::get_shallow_script(const String &p_path, const Stri
}
Ref<GDScript> script;
- script.instance();
+ script.instantiate();
script->set_path(p_path, true);
script->set_script_path(p_path);
script->load_source_code(p_path);
@@ -200,7 +194,9 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro
if (singleton->full_gdscript_cache.has(p_path)) {
return singleton->full_gdscript_cache[p_path];
}
+
Ref<GDScript> script = get_shallow_script(p_path);
+ ERR_FAIL_COND_V(script.is_null(), Ref<GDScript>());
r_error = script->load_source_code(p_path);
diff --git a/modules/gdscript/gdscript_cache.h b/modules/gdscript/gdscript_cache.h
index 865df34051..9fb661d031 100644
--- a/modules/gdscript/gdscript_cache.h
+++ b/modules/gdscript/gdscript_cache.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,16 +31,16 @@
#ifndef GDSCRIPT_CACHE_H
#define GDSCRIPT_CACHE_H
-#include "core/hash_map.h"
+#include "core/object/ref_counted.h"
#include "core/os/mutex.h"
-#include "core/reference.h"
-#include "core/set.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/set.h"
#include "gdscript.h"
class GDScriptAnalyzer;
class GDScriptParser;
-class GDScriptParserRef : public Reference {
+class GDScriptParserRef : public RefCounted {
public:
enum Status {
EMPTY,
@@ -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 31e1e6ba23..e6ecc92d55 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,11 +31,11 @@
#ifndef GDSCRIPT_CODEGEN
#define GDSCRIPT_CODEGEN
-#include "core/io/multiplayer_api.h"
-#include "core/string_name.h"
-#include "core/variant.h"
+#include "core/multiplayer/multiplayer.h"
+#include "core/string/string_name.h"
+#include "core/variant/variant.h"
#include "gdscript_function.h"
-#include "gdscript_functions.h"
+#include "gdscript_utility_functions.h"
class GDScriptCodeGenerator {
public:
@@ -45,13 +45,9 @@ public:
CLASS,
MEMBER,
CONSTANT,
- CLASS_CONSTANT,
- LOCAL_CONSTANT,
LOCAL_VARIABLE,
FUNCTION_PARAMETER,
TEMPORARY,
- GLOBAL,
- NAMED_GLOBAL,
NIL,
};
AddressMode mode = NIL;
@@ -75,7 +71,7 @@ public:
virtual uint32_t add_local_constant(const StringName &p_name, const Variant &p_constant) = 0;
virtual uint32_t add_or_get_constant(const Variant &p_constant) = 0;
virtual uint32_t add_or_get_name(const StringName &p_name) = 0;
- virtual uint32_t add_temporary() = 0;
+ virtual uint32_t add_temporary(const GDScriptDataType &p_type) = 0;
virtual void pop_temporary() = 0;
virtual void start_parameters() = 0;
@@ -84,10 +80,7 @@ public:
virtual void start_block() = 0;
virtual void end_block() = 0;
- // virtual int get_max_stack_level() = 0;
- // virtual int get_max_function_arguments() = 0;
-
- virtual void write_start(GDScript *p_script, const StringName &p_function_name, bool p_static, MultiplayerAPI::RPCMode p_rpc_mode, 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
@@ -95,10 +88,9 @@ public:
#endif
virtual void set_initial_line(int p_line) = 0;
- // virtual void alloc_stack(int p_level) = 0; // Is this needed?
- // virtual void alloc_call(int p_arg_count) = 0; // This might be automatic from other functions.
-
- virtual void write_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) = 0;
+ virtual void write_type_adjust(const Address &p_target, Variant::Type p_new_type) = 0;
+ virtual void write_unary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand) = 0;
+ virtual void write_binary_operator(const Address &p_target, Variant::Operator p_operator, const Address &p_left_operand, const Address &p_right_operand) = 0;
virtual void write_type_test(const Address &p_target, const Address &p_source, const Address &p_type) = 0;
virtual void write_type_test_builtin(const Address &p_target, const Address &p_source, Variant::Type p_type) = 0;
virtual void write_and_left_operand(const Address &p_left_operand) = 0;
@@ -119,26 +111,37 @@ public:
virtual void write_set_member(const Address &p_value, const StringName &p_name) = 0;
virtual void write_get_member(const Address &p_target, const StringName &p_name) = 0;
virtual void write_assign(const Address &p_target, const Address &p_source) = 0;
+ virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) = 0;
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;
virtual void write_super_call(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_call_async(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
- virtual void write_call_builtin(const Address &p_target, GDScriptFunctions::Function p_function, const Vector<Address> &p_arguments) = 0;
- virtual void write_call_method_bind(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
- virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, const MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_utility(const Address &p_target, const StringName &p_function, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_gdscript_utility(const Address &p_target, GDScriptUtilityFunctions::FunctionPtr p_function, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_builtin_type(const Address &p_target, const Address &p_base, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_builtin_type_static(const Address &p_target, Variant::Type p_type, const StringName &p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_method_bind(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_ptrcall(const Address &p_target, const Address &p_base, MethodBind *p_method, const Vector<Address> &p_arguments) = 0;
virtual void write_call_self(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_call_self_async(const Address &p_target, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
virtual void write_call_script_function(const Address &p_target, const Address &p_base, const StringName &p_function_name, const Vector<Address> &p_arguments) = 0;
+ virtual void write_lambda(const Address &p_target, GDScriptFunction *p_function, const Vector<Address> &p_captures) = 0;
virtual void write_construct(const Address &p_target, Variant::Type p_type, const Vector<Address> &p_arguments) = 0;
virtual void write_construct_array(const Address &p_target, const Vector<Address> &p_arguments) = 0;
+ virtual void write_construct_typed_array(const Address &p_target, const GDScriptDataType &p_element_type, const Vector<Address> &p_arguments) = 0;
virtual void write_construct_dictionary(const Address &p_target, const Vector<Address> &p_arguments) = 0;
virtual void write_await(const Address &p_target, const Address &p_operand) = 0;
virtual void write_if(const Address &p_condition) = 0;
- // virtual void write_elseif(const Address &p_condition) = 0; This kind of makes things more difficult for no real benefit.
virtual void write_else() = 0;
virtual void write_endif() = 0;
- virtual void write_for(const Address &p_variable, const Address &p_list) = 0;
+ virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) = 0;
+ virtual void write_for_assignment(const Address &p_variable, const Address &p_list) = 0;
+ virtual void write_for() = 0;
virtual void write_endfor() = 0;
virtual void start_while_condition() = 0; // Used to allow a jump to the expression evaluation.
virtual void write_while(const Address &p_condition) = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index bad450c9f9..ab0fe5c37d 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,6 +33,9 @@
#include "gdscript.h"
#include "gdscript_byte_codegen.h"
#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) {
@@ -98,14 +101,17 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
} break;
case GDScriptParser::DataType::SCRIPT: {
result.kind = GDScriptDataType::SCRIPT;
- result.script_type = Ref<Script>(p_datatype.script_type).ptr();
+ result.script_type_ref = Ref<Script>(p_datatype.script_type);
+ result.script_type = result.script_type_ref.ptr();
result.native_type = result.script_type->get_instance_base_type();
} break;
case GDScriptParser::DataType::CLASS: {
// Locate class by constructing the path to it and following that path
GDScriptParser::ClassNode *class_type = p_datatype.class_type;
if (class_type) {
- if (class_type->fqcn.begins_with(main_script->path) || (!main_script->name.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) {
@@ -128,36 +134,79 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.native_type = script->get_instance_base_type();
} else {
result.kind = GDScriptDataType::GDSCRIPT;
- result.script_type = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path).ptr();
+ result.script_type_ref = GDScriptCache::get_shallow_script(p_datatype.script_path, main_script->path);
+ result.script_type = result.script_type_ref.ptr();
result.native_type = p_datatype.native_type;
}
}
} break;
+ case GDScriptParser::DataType::ENUM:
case GDScriptParser::DataType::ENUM_VALUE:
result.has_type = true;
result.kind = GDScriptDataType::BUILTIN;
result.builtin_type = Variant::INT;
break;
- case GDScriptParser::DataType::ENUM:
- result.has_type = true;
- result.kind = GDScriptDataType::BUILTIN;
- result.builtin_type = Variant::DICTIONARY;
- break;
case GDScriptParser::DataType::UNRESOLVED: {
ERR_PRINT("Parser bug: converting unresolved type.");
return GDScriptDataType();
}
}
+ if (p_datatype.has_container_element_type()) {
+ result.set_container_element_type(_gdtype_from_datatype(p_datatype.get_container_element_type()));
+ }
+
// Only hold strong reference to the script if it's not the owner of the
// element qualified with this type, to avoid cyclic references (leaks).
- if (result.script_type && result.script_type != p_owner) {
- result.script_type_ref = Ref<Script>(result.script_type);
+ if (result.script_type && result.script_type == p_owner) {
+ result.script_type_ref = Ref<Script>();
}
return result;
}
+static bool _is_exact_type(const PropertyInfo &p_par_type, const GDScriptDataType &p_arg_type) {
+ if (!p_arg_type.has_type) {
+ return false;
+ }
+ if (p_par_type.type == Variant::NIL) {
+ return false;
+ }
+ if (p_par_type.type == Variant::OBJECT) {
+ if (p_arg_type.kind == GDScriptDataType::BUILTIN) {
+ return false;
+ }
+ StringName class_name;
+ if (p_arg_type.kind == GDScriptDataType::NATIVE) {
+ class_name = p_arg_type.native_type;
+ } else {
+ class_name = p_arg_type.native_type == StringName() ? p_arg_type.script_type->get_instance_base_type() : p_arg_type.native_type;
+ }
+ return p_par_type.class_name == class_name || ClassDB::is_parent_class(class_name, p_par_type.class_name);
+ } else {
+ if (p_arg_type.kind != GDScriptDataType::BUILTIN) {
+ return false;
+ }
+ return p_par_type.type == p_arg_type.builtin_type;
+ }
+}
+
+static bool _have_exact_arguments(const MethodBind *p_method, const Vector<GDScriptCodeGenerator::Address> &p_arguments) {
+ if (p_method->get_argument_count() != p_arguments.size()) {
+ // ptrcall won't work with default arguments.
+ return false;
+ }
+ MethodInfo info;
+ ClassDB::get_method_info(p_method->get_instance_class(), p_method->get_name(), &info);
+ for (int i = 0; i < p_arguments.size(); i++) {
+ const PropertyInfo &prop = info.arguments[i];
+ if (!_is_exact_type(prop, p_arguments[i].type)) {
+ return false;
+ }
+ }
+ return true;
+}
+
GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &codegen, Error &r_error, const GDScriptParser::ExpressionNode *p_expression, bool p_root, bool p_initializer, const GDScriptCodeGenerator::Address &p_index_addr) {
if (p_expression->is_constant) {
return codegen.add_constant(p_expression->reduced_value);
@@ -209,36 +258,59 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
// Try class constants.
- GDScript *owner = codegen.script;
- while (owner) {
- GDScript *scr = owner;
- GDScriptNativeClass *nc = nullptr;
- while (scr) {
- if (scr->constants.has(identifier)) {
- return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CLASS_CONSTANT, gen->add_or_get_name(identifier)); // TODO: Get type here.
+ {
+ GDScript *owner = codegen.script;
+ while (owner) {
+ GDScript *scr = owner;
+ GDScriptNativeClass *nc = nullptr;
+ while (scr) {
+ if (scr->constants.has(identifier)) {
+ return codegen.add_constant(scr->constants[identifier]); // TODO: Get type here.
+ }
+ if (scr->native.is_valid()) {
+ nc = scr->native.ptr();
+ }
+ scr = scr->_base;
}
- if (scr->native.is_valid()) {
- nc = scr->native.ptr();
+
+ // Class C++ integer constant.
+ if (nc) {
+ bool success = false;
+ int constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
+ if (success) {
+ return codegen.add_constant(constant);
+ }
}
- scr = scr->_base;
+
+ owner = owner->_owner;
}
+ }
+
+ // Try signals and methods (can be made callables).
+ {
+ if (codegen.class_node->members_indices.has(identifier)) {
+ const GDScriptParser::ClassNode::Member &member = codegen.class_node->members[codegen.class_node->members_indices[identifier]];
+ if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ // Get like it was a property.
+ GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
+ GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
- // Class C++ integer constant.
- if (nc) {
- bool success = false;
- int constant = ClassDB::get_integer_constant(nc->get_name(), identifier, &success);
- if (success) {
- return codegen.add_constant(constant);
+ gen->write_get_named(temp, identifier, self);
+ return temp;
}
}
- owner = owner->_owner;
- }
+ // Try in native base.
+ GDScript *scr = codegen.script;
+ GDScriptNativeClass *nc = nullptr;
+ while (scr) {
+ if (scr->native.is_valid()) {
+ nc = scr->native.ptr();
+ }
+ scr = scr->_base;
+ }
- // Try signals and methods (can be made callables);
- if (codegen.class_node->members_indices.has(identifier)) {
- const GDScriptParser::ClassNode::Member &member = codegen.class_node->members[codegen.class_node->members_indices[identifier]];
- if (member.type == GDScriptParser::ClassNode::Member::FUNCTION || member.type == GDScriptParser::ClassNode::Member::SIGNAL) {
+ if (nc && (ClassDB::has_signal(nc->get_name(), identifier) || ClassDB::has_method(nc->get_name(), identifier))) {
// Get like it was a property.
GDScriptCodeGenerator::Address temp = codegen.add_temporary(); // TODO: Get type here.
GDScriptCodeGenerator::Address self(GDScriptCodeGenerator::Address::SELF);
@@ -248,9 +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];
- return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::GLOBAL, idx); // 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.
@@ -278,7 +362,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
#ifdef TOOLS_ENABLED
if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(identifier)) {
- return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::NAMED_GLOBAL, gen->add_or_get_name(identifier)); // TODO: Get type.
+ GDScriptCodeGenerator::Address global = codegen.add_temporary(); // TODO: Get type.
+ gen->write_store_named_global(global, identifier);
+ return global;
}
#endif
@@ -307,10 +393,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
Vector<GDScriptCodeGenerator::Address> values;
// Create the result temporary first since it's the last to be killed.
- GDScriptDataType array_type;
- array_type.has_type = true;
- array_type.kind = GDScriptDataType::BUILTIN;
- array_type.builtin_type = Variant::ARRAY;
+ GDScriptDataType array_type = _gdtype_from_datatype(an->get_datatype());
GDScriptCodeGenerator::Address result = codegen.add_temporary(array_type);
for (int i = 0; i < an->elements.size(); i++) {
@@ -321,7 +404,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
values.push_back(val);
}
- gen->write_construct_array(result, values);
+ if (array_type.has_container_element_type()) {
+ gen->write_construct_typed_array(result, array_type.get_container_element_type(), values);
+ } else {
+ gen->write_construct_array(result, values);
+ }
for (int i = 0; i < values.size(); i++) {
if (values[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
@@ -354,8 +441,8 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
}
break;
case GDScriptParser::DictionaryNode::LUA_TABLE:
- // Lua-style: key is an identifier interpreted as string.
- String key = static_cast<const GDScriptParser::IdentifierNode *>(dn->elements[i].key)->name;
+ // Lua-style: key is an identifier interpreted as StringName.
+ StringName key = dn->elements[i].key->reduced_value.operator StringName();
element = codegen.add_constant(key);
break;
}
@@ -395,12 +482,15 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
gen->pop_temporary();
}
- return source;
+ return result;
} break;
case GDScriptParser::Node::CALL: {
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++) {
@@ -411,15 +501,17 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
arguments.push_back(arg);
}
- if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != Variant::VARIANT_MAX) {
+ if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) != Variant::VARIANT_MAX) {
// Construct a built-in type.
Variant::Type vtype = GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
gen->write_construct(result, vtype, arguments);
- } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name) != GDScriptFunctions::FUNC_MAX) {
- // Built-in function.
- GDScriptFunctions::Function func = GDScriptParser::get_builtin_function(static_cast<GDScriptParser::IdentifierNode *>(call->callee)->name);
- gen->write_call_builtin(result, func, arguments);
+ } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
+ // Variant utility function.
+ gen->write_call_utility(result, call->function_name, arguments);
+ } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {
+ // GDScript utility function.
+ gen->write_call_gdscript_utility(result, GDScriptUtilityFunctions::get_function(call->function_name), arguments);
} else {
// Regular function.
const GDScriptParser::ExpressionNode *callee = call->callee;
@@ -430,28 +522,76 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} else {
if (callee->type == GDScriptParser::Node::IDENTIFIER) {
// Self function call.
- if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
+ if (ClassDB::has_method(codegen.script->native->get_name(), call->function_name)) {
+ // Native method, use faster path.
+ GDScriptCodeGenerator::Address self;
+ self.mode = GDScriptCodeGenerator::Address::SELF;
+ MethodBind *method = ClassDB::get_method(codegen.script->native->get_name(), call->function_name);
+
+ if (_have_exact_arguments(method, arguments)) {
+ // Exact arguments, use ptrcall.
+ gen->write_call_ptrcall(result, self, method, arguments);
+ } else {
+ // Not exact arguments, but still can use method bind call.
+ gen->write_call_method_bind(result, self, method, arguments);
+ }
+ } else if ((codegen.function_node && codegen.function_node->is_static) || call->function_name == "new") {
GDScriptCodeGenerator::Address self;
self.mode = GDScriptCodeGenerator::Address::CLASS;
- gen->write_call(result, self, call->function_name, arguments);
+ if (within_await) {
+ gen->write_call_async(result, self, call->function_name, arguments);
+ } else {
+ gen->write_call(return_addr, self, call->function_name, arguments);
+ }
} else {
- gen->write_call_self(result, call->function_name, arguments);
+ if (within_await) {
+ gen->write_call_self_async(result, call->function_name, arguments);
+ } else {
+ gen->write_call_self(return_addr, call->function_name, arguments);
+ }
}
} else if (callee->type == GDScriptParser::Node::SUBSCRIPT) {
const GDScriptParser::SubscriptNode *subscript = static_cast<const GDScriptParser::SubscriptNode *>(call->callee);
if (subscript->is_attribute) {
- GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
- if (r_error) {
- return GDScriptCodeGenerator::Address();
- }
- if (within_await) {
- gen->write_call_async(result, base, call->function_name, arguments);
+ // May be static built-in method call.
+ if (!call->is_super && subscript->base->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name) < Variant::VARIANT_MAX) {
+ gen->write_call_builtin_type_static(result, GDScriptParser::get_builtin_type(static_cast<GDScriptParser::IdentifierNode *>(subscript->base)->name), subscript->attribute->name, arguments);
} else {
- gen->write_call(result, base, call->function_name, arguments);
- }
- if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ GDScriptCodeGenerator::Address base = _parse_expression(codegen, r_error, subscript->base);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ if (within_await) {
+ gen->write_call_async(result, base, call->function_name, arguments);
+ } else if (base.type.has_type && base.type.kind != GDScriptDataType::BUILTIN) {
+ // Native method, use faster path.
+ StringName class_name;
+ if (base.type.kind == GDScriptDataType::NATIVE) {
+ class_name = base.type.native_type;
+ } else {
+ class_name = base.type.native_type == StringName() ? base.type.script_type->get_instance_base_type() : base.type.native_type;
+ }
+ if (ClassDB::class_exists(class_name) && ClassDB::has_method(class_name, call->function_name)) {
+ MethodBind *method = ClassDB::get_method(class_name, call->function_name);
+ if (_have_exact_arguments(method, arguments)) {
+ // Exact arguments, use ptrcall.
+ gen->write_call_ptrcall(result, base, method, arguments);
+ } else {
+ // Not exact arguments, but still can use method bind call.
+ gen->write_call_method_bind(result, base, method, arguments);
+ }
+ } else {
+ 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(return_addr, base, call->function_name, arguments);
+ }
+ if (base.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
}
} else {
_set_error("Cannot call something that isn't a function.", call->callee);
@@ -493,7 +633,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(get_node->get_datatype()));
MethodBind *get_node_method = ClassDB::get_method("Node", "get_node");
- gen->write_call_method_bind(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
+ gen->write_call_ptrcall(result, GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), get_node_method, args);
return result;
} break;
@@ -562,9 +702,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
name = subscript->attribute->name;
named = true;
} else {
- if (subscript->index->type == GDScriptParser::Node::LITERAL && static_cast<const GDScriptParser::LiteralNode *>(subscript->index)->value.get_type() == Variant::STRING) {
+ if (subscript->index->is_constant && subscript->index->reduced_value.get_type() == Variant::STRING_NAME) {
// Also, somehow, named (speed up anyway).
- name = static_cast<const GDScriptParser::LiteralNode *>(subscript->index)->value;
+ name = subscript->index->reduced_value;
named = true;
} else {
// Regular indexing.
@@ -593,14 +733,14 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::UNARY_OPERATOR: {
const GDScriptParser::UnaryOpNode *unary = static_cast<const GDScriptParser::UnaryOpNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary();
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(unary->get_datatype()));
GDScriptCodeGenerator::Address operand = _parse_expression(codegen, r_error, unary->operand);
if (r_error) {
return GDScriptCodeGenerator::Address();
}
- gen->write_operator(result, unary->variant_op, operand, GDScriptCodeGenerator::Address());
+ gen->write_unary_operator(result, unary->variant_op, operand);
if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -611,7 +751,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
case GDScriptParser::Node::BINARY_OPERATOR: {
const GDScriptParser::BinaryOpNode *binary = static_cast<const GDScriptParser::BinaryOpNode *>(p_expression);
- GDScriptCodeGenerator::Address result = codegen.add_temporary();
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(binary->get_datatype()));
switch (binary->operation) {
case GDScriptParser::BinaryOpNode::OP_LOGIC_AND: {
@@ -663,12 +803,16 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
gen->pop_temporary();
}
}
+
+ if (operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
} break;
default: {
GDScriptCodeGenerator::Address left_operand = _parse_expression(codegen, r_error, binary->left_operand);
GDScriptCodeGenerator::Address right_operand = _parse_expression(codegen, r_error, binary->right_operand);
- gen->write_operator(result, binary->variant_op, left_operand, right_operand);
+ gen->write_binary_operator(result, binary->variant_op, left_operand, right_operand);
if (right_operand.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -825,13 +969,13 @@ 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 {
gen->write_get(value, key, prev_base);
}
- gen->write_operator(value, assignment->variant_op, value, assigned);
+ gen->write_binary_operator(value, assignment->variant_op, value, assigned);
if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
}
@@ -844,6 +988,9 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
} else {
gen->write_set(prev_base, key, assigned);
}
+ if (key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
}
@@ -851,8 +998,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
assigned = prev_base;
// Set back the values into their bases.
- for (List<ChainInfo>::Element *E = set_chain.front(); E; E = E->next()) {
- const ChainInfo &info = E->get();
+ for (const ChainInfo &info : set_chain) {
if (!info.is_named) {
gen->write_set(info.base, info.key, assigned);
if (info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
@@ -878,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_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.
@@ -930,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_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());
@@ -950,25 +1109,57 @@ 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.
- gen->write_assign(target, op_result);
+ if (assignment->use_conversion_assign) {
+ gen->write_assign_with_conversion(target, to_assign);
+ } else {
+ 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.
} break;
+ case GDScriptParser::Node::LAMBDA: {
+ const GDScriptParser::LambdaNode *lambda = static_cast<const GDScriptParser::LambdaNode *>(p_expression);
+ GDScriptCodeGenerator::Address result = codegen.add_temporary(_gdtype_from_datatype(lambda->get_datatype()));
+
+ Vector<GDScriptCodeGenerator::Address> captures;
+ captures.resize(lambda->captures.size());
+ for (int i = 0; i < lambda->captures.size(); i++) {
+ captures.write[i] = _parse_expression(codegen, r_error, lambda->captures[i]);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+ }
+
+ GDScriptFunction *function = _parse_function(r_error, codegen.script, codegen.class_node, lambda->function, false, true);
+ if (r_error) {
+ return GDScriptCodeGenerator::Address();
+ }
+
+ gen->write_lambda(result, function, captures);
+
+ for (int i = 0; i < captures.size(); i++) {
+ if (captures[i].mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ }
+
+ return result;
+ } break;
default: {
ERR_FAIL_V_MSG(GDScriptCodeGenerator::Address(), "Bug in bytecode compiler, unexpected node in parse tree while parsing expression."); // Unreachable code.
} break;
@@ -995,7 +1186,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check type equality.
GDScriptCodeGenerator::Address type_equality_addr = codegen.add_temporary(equality_type);
- codegen.generator->write_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr);
+ codegen.generator->write_binary_operator(type_equality_addr, Variant::OP_EQUAL, p_type_addr, literal_type_addr);
codegen.generator->write_and_left_operand(type_equality_addr);
// Get literal.
@@ -1006,7 +1197,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check value equality.
GDScriptCodeGenerator::Address equality_addr = codegen.add_temporary(equality_type);
- codegen.generator->write_operator(equality_addr, Variant::OP_EQUAL, p_value_addr, literal_addr);
+ codegen.generator->write_binary_operator(equality_addr, Variant::OP_EQUAL, p_value_addr, literal_addr);
codegen.generator->write_and_right_operand(equality_addr);
// AND both together (reuse temporary location).
@@ -1055,14 +1246,14 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Evaluate expression type.
Vector<GDScriptCodeGenerator::Address> typeof_args;
typeof_args.push_back(expr_addr);
- codegen.generator->write_call_builtin(result_addr, GDScriptFunctions::TYPE_OF, typeof_args);
+ codegen.generator->write_call_utility(result_addr, "typeof", typeof_args);
// Check type equality.
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr);
+ codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, result_addr);
codegen.generator->write_and_left_operand(result_addr);
// Check value equality.
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_value_addr, expr_addr);
+ codegen.generator->write_binary_operator(equality_test_addr, Variant::OP_EQUAL, p_value_addr, expr_addr);
codegen.generator->write_and_right_operand(equality_test_addr);
// AND both type and value equality.
@@ -1108,7 +1299,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check type equality.
GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, array_type_addr);
+ codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, array_type_addr);
codegen.generator->write_and_left_operand(result_addr);
// Store pattern length in constant map.
@@ -1119,12 +1310,12 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
Vector<GDScriptCodeGenerator::Address> len_args;
len_args.push_back(p_value_addr);
- codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, len_args);
+ codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), len_args);
// Test length compatibility.
temp_type.builtin_type = Variant::BOOL;
GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, array_length_addr);
+ codegen.generator->write_binary_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, array_length_addr);
codegen.generator->write_and_right_operand(length_compat_addr);
// AND type and length check.
@@ -1173,7 +1364,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Also get type of element.
Vector<GDScriptCodeGenerator::Address> typeof_args;
typeof_args.push_back(element_addr);
- codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, typeof_args);
+ codegen.generator->write_call_utility(element_type_addr, "typeof", typeof_args);
// Try the pattern inside the element.
test_addr = _parse_match_pattern(codegen, r_error, p_pattern->array[i], element_addr, element_type_addr, p_previous_test, false, true);
@@ -1207,7 +1398,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Check type equality.
GDScriptCodeGenerator::Address result_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(result_addr, Variant::OP_EQUAL, p_type_addr, dict_type_addr);
+ codegen.generator->write_binary_operator(result_addr, Variant::OP_EQUAL, p_type_addr, dict_type_addr);
codegen.generator->write_and_left_operand(result_addr);
// Store pattern length in constant map.
@@ -1218,12 +1409,12 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
GDScriptCodeGenerator::Address value_length_addr = codegen.add_temporary(temp_type);
Vector<GDScriptCodeGenerator::Address> func_args;
func_args.push_back(p_value_addr);
- codegen.generator->write_call_builtin(value_length_addr, GDScriptFunctions::LEN, func_args);
+ codegen.generator->write_call_gdscript_utility(value_length_addr, GDScriptUtilityFunctions::get_function("len"), func_args);
// Test length compatibility.
temp_type.builtin_type = Variant::BOOL;
GDScriptCodeGenerator::Address length_compat_addr = codegen.add_temporary(temp_type);
- codegen.generator->write_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, dict_length_addr);
+ codegen.generator->write_binary_operator(length_compat_addr, p_pattern->rest_used ? Variant::OP_GREATER_EQUAL : Variant::OP_EQUAL, value_length_addr, dict_length_addr);
codegen.generator->write_and_right_operand(length_compat_addr);
// AND type and length check.
@@ -1287,7 +1478,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_match_pattern(CodeGen &c
// Also get type of value.
func_args.clear();
func_args.push_back(element_addr);
- codegen.generator->write_call_builtin(element_type_addr, GDScriptFunctions::TYPE_OF, func_args);
+ codegen.generator->write_call_utility(element_type_addr, "typeof", func_args);
// Try the pattern inside the value.
test_addr = _parse_match_pattern(codegen, r_error, element.value_pattern, element_addr, element_type_addr, test_addr, false, true);
@@ -1397,16 +1588,30 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
codegen.start_block();
// Evaluate the match expression.
- GDScriptCodeGenerator::Address value = _parse_expression(codegen, error, match->test);
+ GDScriptCodeGenerator::Address value = codegen.add_local("@match_value", _gdtype_from_datatype(match->test->get_datatype()));
+ GDScriptCodeGenerator::Address value_expr = _parse_expression(codegen, error, match->test);
if (error) {
return error;
}
+ // Assign to local.
+ // TODO: This can be improved by passing the target to parse_expression().
+ gen->write_assign(value, value_expr);
+
+ if (value_expr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+
// Then, let's save the type of the value in the stack too, so we can reuse for later comparisons.
- GDScriptCodeGenerator::Address type = codegen.add_temporary();
+ GDScriptDataType typeof_type;
+ typeof_type.has_type = true;
+ typeof_type.kind = GDScriptDataType::BUILTIN;
+ typeof_type.builtin_type = Variant::INT;
+ GDScriptCodeGenerator::Address type = codegen.add_local("@match_type", typeof_type);
+
Vector<GDScriptCodeGenerator::Address> typeof_args;
typeof_args.push_back(value);
- gen->write_call_builtin(type, GDScriptFunctions::TYPE_OF, typeof_args);
+ gen->write_call_utility(type, "typeof", typeof_args);
// Now we can actually start testing.
// For each branch.
@@ -1457,12 +1662,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
gen->write_endif();
}
- gen->pop_temporary();
-
- if (value.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- codegen.generator->pop_temporary();
- }
-
gen->end_match();
} break;
case GDScriptParser::Node::IF: {
@@ -1500,12 +1699,20 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
codegen.start_block();
GDScriptCodeGenerator::Address iterator = codegen.add_local(for_n->variable->name, _gdtype_from_datatype(for_n->variable->get_datatype()));
+ gen->start_for(iterator.type, _gdtype_from_datatype(for_n->list->get_datatype()));
+
GDScriptCodeGenerator::Address list = _parse_expression(codegen, error, for_n->list);
if (error) {
return error;
}
- gen->write_for(iterator, list);
+ gen->write_for_assignment(iterator, list);
+
+ if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ codegen.generator->pop_temporary();
+ }
+
+ gen->write_for();
error = _parse_block(codegen, for_n->loop);
if (error) {
@@ -1514,10 +1721,6 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
gen->write_endfor();
- if (list.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- codegen.generator->pop_temporary();
- }
-
codegen.end_block();
} break;
case GDScriptParser::Node::WHILE: {
@@ -1607,16 +1810,37 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
const GDScriptParser::VariableNode *lv = static_cast<const GDScriptParser::VariableNode *>(s);
// Should be already in stack when the block began.
GDScriptCodeGenerator::Address local = codegen.locals[lv->identifier->name];
+ GDScriptParser::DataType local_type = lv->get_datatype();
if (lv->initializer != nullptr) {
+ // For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
+ if (local_type.is_hard_type() && local_type.builtin_type == Variant::ARRAY) {
+ if (local_type.has_container_element_type()) {
+ codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ } else {
+ codegen.generator->write_construct_array(local, Vector<GDScriptCodeGenerator::Address>());
+ }
+ }
GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, lv->initializer);
if (error) {
return error;
}
- gen->write_assign(local, src_address);
+ if (lv->use_conversion_assign) {
+ gen->write_assign_with_conversion(local, src_address);
+ } else {
+ gen->write_assign(local, src_address);
+ }
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
+ } else if (lv->get_datatype().is_hard_type()) {
+ // Initialize with default for type.
+ if (local_type.has_container_element_type()) {
+ codegen.generator->write_construct_typed_array(local, _gdtype_from_datatype(local_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ } else if (local_type.kind == GDScriptParser::DataType::BUILTIN) {
+ codegen.generator->write_construct(local, local_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
+ }
+ // The `else` branch is for objects, in such case we leave it as `null`.
}
} break;
case GDScriptParser::Node::CONSTANT: {
@@ -1653,8 +1877,8 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
return OK;
}
-Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready) {
- Error error = OK;
+GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready, bool p_for_lambda) {
+ r_error = OK;
CodeGen codegen;
codegen.generator = memnew(GDScriptByteCodeGenerator);
@@ -1664,16 +1888,20 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
StringName func_name;
bool is_static = false;
- MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ Multiplayer::RPCConfig rpc_config;
GDScriptDataType return_type;
return_type.has_type = true;
return_type.kind = GDScriptDataType::BUILTIN;
return_type.builtin_type = Variant::NIL;
if (p_func) {
- func_name = p_func->identifier->name;
+ if (p_func->identifier) {
+ func_name = p_func->identifier->name;
+ } else {
+ func_name = "<anonymous lambda>";
+ }
is_static = p_func->is_static;
- rpc_mode = p_func->rpc_mode;
+ rpc_config = p_func->rpc_config;
return_type = _gdtype_from_datatype(p_func->get_datatype(), p_script);
} else {
if (p_for_ready) {
@@ -1684,7 +1912,7 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
}
codegen.function_name = func_name;
- codegen.generator->write_start(p_script, func_name, is_static, rpc_mode, return_type);
+ codegen.generator->write_start(p_script, func_name, is_static, rpc_config, return_type);
int optional_parameters = 0;
@@ -1702,11 +1930,11 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
}
// Parse initializer if applies.
- bool is_implicit_initializer = !p_for_ready && !p_func;
- bool is_initializer = p_func && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init;
- bool is_for_ready = p_for_ready || (p_func && String(p_func->identifier->name) == "_ready");
+ bool is_implicit_initializer = !p_for_ready && !p_func && !p_for_lambda;
+ bool is_initializer = p_func && !p_for_lambda && String(p_func->identifier->name) == GDScriptLanguage::get_singleton()->strings._init;
+ bool is_for_ready = p_for_ready || (p_func && !p_for_lambda && String(p_func->identifier->name) == "_ready");
- if (is_implicit_initializer || is_for_ready) {
+ if (!p_for_lambda && (is_implicit_initializer || is_for_ready)) {
// Initialize class fields.
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type != GDScriptParser::ClassNode::Member::VARIABLE) {
@@ -1718,21 +1946,45 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
continue;
}
+ GDScriptParser::DataType field_type = field->get_datatype();
+
+ GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype()));
if (field->initializer) {
// Emit proper line change.
codegen.generator->write_newline(field->initializer->start_line);
- GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, error, field->initializer, false, true);
- if (error) {
+ // For typed arrays we need to make sure this is already initialized correctly so typed assignment work.
+ if (field_type.is_hard_type() && field_type.builtin_type == Variant::ARRAY && field_type.has_container_element_type()) {
+ if (field_type.has_container_element_type()) {
+ codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ } else {
+ codegen.generator->write_construct_array(dst_address, Vector<GDScriptCodeGenerator::Address>());
+ }
+ }
+ GDScriptCodeGenerator::Address src_address = _parse_expression(codegen, r_error, field->initializer, false, true);
+ if (r_error) {
memdelete(codegen.generator);
- return error;
+ return nullptr;
}
- GDScriptCodeGenerator::Address dst_address(GDScriptCodeGenerator::Address::MEMBER, codegen.script->member_indices[field->identifier->name].index, _gdtype_from_datatype(field->get_datatype()));
- codegen.generator->write_assign(dst_address, src_address);
+ if (field->use_conversion_assign) {
+ codegen.generator->write_assign_with_conversion(dst_address, src_address);
+ } else {
+ codegen.generator->write_assign(dst_address, src_address);
+ }
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
+ } else if (field->get_datatype().is_hard_type()) {
+ codegen.generator->write_newline(field->start_line);
+
+ // Initialize with default for type.
+ if (field_type.has_container_element_type()) {
+ codegen.generator->write_construct_typed_array(dst_address, _gdtype_from_datatype(field_type.get_container_element_type(), codegen.script), Vector<GDScriptCodeGenerator::Address>());
+ } else if (field_type.kind == GDScriptParser::DataType::BUILTIN) {
+ codegen.generator->write_construct(dst_address, field_type.builtin_type, Vector<GDScriptCodeGenerator::Address>());
+ }
+ // The `else` branch is for objects, in such case we leave it as `null`.
}
}
}
@@ -1743,13 +1995,13 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
codegen.generator->start_parameters();
for (int i = p_func->parameters.size() - optional_parameters; i < p_func->parameters.size(); i++) {
const GDScriptParser::ParameterNode *parameter = p_func->parameters[i];
- GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, error, parameter->default_value, true);
- if (error) {
+ GDScriptCodeGenerator::Address src_addr = _parse_expression(codegen, r_error, parameter->default_value, true);
+ if (r_error) {
memdelete(codegen.generator);
- return error;
+ return nullptr;
}
GDScriptCodeGenerator::Address dst_addr = codegen.parameters[parameter->identifier->name];
- codegen.generator->write_assign(dst_addr, src_addr);
+ codegen.generator->write_assign_default_parameter(dst_addr, src_addr);
if (src_addr.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
@@ -1757,10 +2009,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
codegen.generator->end_parameters();
}
- Error err = _parse_block(codegen, p_func->body);
- if (err) {
+ r_error = _parse_block(codegen, p_func->body);
+ if (r_error) {
memdelete(codegen.generator);
- return err;
+ return nullptr;
}
}
@@ -1786,6 +2038,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
signature += "::" + String(func_name);
}
+ if (p_for_lambda) {
+ signature += "(lambda)";
+ }
+
codegen.generator->set_signature(signature);
}
#endif
@@ -1793,7 +2049,10 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
if (p_func) {
codegen.generator->set_initial_line(p_func->start_line);
#ifdef TOOLS_ENABLED
- p_script->member_lines[func_name] = p_func->start_line;
+ if (!p_for_lambda) {
+ p_script->member_lines[func_name] = p_func->start_line;
+ p_script->doc_functions[func_name] = p_func->doc_description;
+ }
#endif
} else {
codegen.generator->set_initial_line(0);
@@ -1807,84 +2066,44 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
p_script->implicit_initializer = gd_function;
}
- p_script->member_functions[func_name] = gd_function;
+ if (p_func) {
+ // if no return statement -> return type is void not unresolved Variant
+ if (p_func->body->has_return) {
+ gd_function->return_type = _gdtype_from_datatype(p_func->get_datatype());
+ } else {
+ gd_function->return_type = GDScriptDataType();
+ gd_function->return_type.has_type = true;
+ gd_function->return_type.kind = GDScriptDataType::BUILTIN;
+ gd_function->return_type.builtin_type = Variant::NIL;
+ }
+#ifdef TOOLS_ENABLED
+ gd_function->default_arg_values = p_func->default_arg_values;
+#endif
+ }
+
+ if (!p_for_lambda) {
+ p_script->member_functions[func_name] = gd_function;
+ }
memdelete(codegen.generator);
- return OK;
+ return gd_function;
}
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";
+ function = p_variable->getter;
}
- 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, p_variable->rpc_mode, 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;
- }
+ _parse_function(error, p_script, p_class, function);
- 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);
-
-#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) {
@@ -1904,13 +2123,31 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
}
+#ifdef TOOLS_ENABLED
+ p_script->doc_functions.clear();
+ p_script->doc_variables.clear();
+ p_script->doc_constants.clear();
+ p_script->doc_enums.clear();
+ p_script->doc_signals.clear();
+ p_script->doc_tutorials.clear();
+
+ p_script->doc_brief_description = p_class->doc_brief_description;
+ p_script->doc_description = p_class->doc_description;
+ for (int i = 0; i < p_class->doc_tutorials.size(); i++) {
+ DocData::TutorialDoc td;
+ td.title = p_class->doc_tutorials[i].first;
+ td.link = p_class->doc_tutorials[i].second;
+ p_script->doc_tutorials.append(td);
+ }
+#endif
+
p_script->native = Ref<GDScriptNativeClass>();
p_script->base = Ref<GDScript>();
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();
@@ -1921,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 != "") {
+ 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);
@@ -1956,13 +2200,15 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
if (err) {
return err;
}
- if (base.is_null() && !base->is_valid()) {
+ if (base.is_null() || !base->is_valid()) {
return ERR_COMPILATION_FAILED;
}
}
}
p_script->member_indices = base->member_indices;
+ native = base->native;
+ p_script->native = native;
} break;
default: {
_set_error("Parser bug: invalid inheritance.", p_class);
@@ -1999,7 +2245,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
break;
}
- minfo.rpc_mode = variable->rpc_mode;
minfo.data_type = _gdtype_from_datatype(variable->get_datatype(), p_script);
PropertyInfo prop_info = minfo.data_type;
@@ -2013,21 +2258,24 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
}
prop_info.hint = export_info.hint;
prop_info.hint_string = export_info.hint_string;
- prop_info.usage = export_info.usage;
-#ifdef TOOLS_ENABLED
- if (variable->initializer != nullptr && variable->initializer->type == GDScriptParser::Node::LITERAL) {
- p_script->member_default_values[name] = static_cast<const GDScriptParser::LiteralNode *>(variable->initializer)->value;
- }
-#endif
+ prop_info.usage = export_info.usage | PROPERTY_USAGE_SCRIPT_VARIABLE;
} else {
prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
}
+#ifdef TOOLS_ENABLED
+ p_script->doc_variables[name] = variable->doc_description;
+#endif
p_script->member_info[name] = prop_info;
p_script->member_indices[name] = minfo;
p_script->members.insert(name);
#ifdef TOOLS_ENABLED
+ if (variable->initializer != nullptr && variable->initializer->is_constant) {
+ p_script->member_default_values[name] = variable->initializer->reduced_value;
+ } else {
+ p_script->member_default_values.erase(name);
+ }
p_script->member_lines[name] = variable->start_line;
#endif
} break;
@@ -2038,8 +2286,10 @@ 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()) {
+ p_script->doc_constants[name] = constant->doc_description;
+ }
#endif
} break;
@@ -2050,6 +2300,15 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->constants.insert(name, enum_value.value);
#ifdef TOOLS_ENABLED
p_script->member_lines[name] = enum_value.identifier->start_line;
+ if (!p_script->doc_enums.has("@unnamed_enums")) {
+ p_script->doc_enums["@unnamed_enums"] = DocData::EnumDoc();
+ p_script->doc_enums["@unnamed_enums"].name = "@unnamed_enums";
+ }
+ DocData::ConstantDoc const_doc;
+ const_doc.name = enum_value.identifier->name;
+ const_doc.value = Variant(enum_value.value).operator String(); // TODO-DOC: enum value currently is int.
+ const_doc.description = enum_value.doc_description;
+ p_script->doc_enums["@unnamed_enums"].values.push_back(const_doc);
#endif
} break;
@@ -2057,34 +2316,17 @@ 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++) {
parameters_names.write[j] = signal->parameters[j]->identifier->name;
}
p_script->_signals[name] = parameters_names;
+#ifdef TOOLS_ENABLED
+ if (!signal->doc_description.is_empty()) {
+ p_script->doc_signals[name] = signal->doc_description;
+ }
+#endif
} break;
case GDScriptParser::ClassNode::Member::ENUM: {
@@ -2101,6 +2343,16 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
p_script->constants.insert(enum_n->identifier->name, new_enum);
#ifdef TOOLS_ENABLED
p_script->member_lines[enum_n->identifier->name] = enum_n->start_line;
+ p_script->doc_enums[enum_n->identifier->name] = DocData::EnumDoc();
+ p_script->doc_enums[enum_n->identifier->name].name = enum_n->identifier->name;
+ p_script->doc_enums[enum_n->identifier->name].description = enum_n->doc_description;
+ for (int j = 0; j < enum_n->values.size(); j++) {
+ DocData::ConstantDoc const_doc;
+ const_doc.name = enum_n->values[j].identifier->name;
+ const_doc.value = Variant(enum_n->values[j].value).operator String();
+ const_doc.description = enum_n->values[j].doc_description;
+ p_script->doc_enums[enum_n->identifier->name].values.push_back(const_doc);
+ }
#endif
} break;
default:
@@ -2154,7 +2406,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
if (!has_ready && function->identifier->name == "_ready") {
has_ready = true;
}
- Error err = _parse_function(p_script, p_class, function);
+ Error err = OK;
+ _parse_function(err, p_script, p_class, function);
if (err) {
return err;
}
@@ -2179,7 +2432,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
{
// Create an implicit constructor in any case.
- Error err = _parse_function(p_script, p_class, nullptr);
+ Error err = OK;
+ _parse_function(err, p_script, p_class, nullptr);
if (err) {
return err;
}
@@ -2187,7 +2441,8 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
if (!has_ready && p_class->onready_used) {
//create a _ready constructor
- Error err = _parse_function(p_script, p_class, nullptr, true);
+ Error err = OK;
+ _parse_function(err, p_script, p_class, nullptr, true);
if (err) {
return err;
}
@@ -2211,14 +2466,14 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
p_script->placeholders.erase(psi); //remove placeholder
GDScriptInstance *instance = memnew(GDScriptInstance);
- instance->base_ref = Object::cast_to<Reference>(E->get());
+ instance->base_ref_counted = Object::cast_to<RefCounted>(E->get());
instance->members.resize(p_script->member_indices.size());
instance->script = Ref<GDScript>(p_script);
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);
@@ -2228,7 +2483,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
p_script->initializer->call(instance, nullptr, 0, ce);
if (ce.error != Callable::CallError::CALL_OK) {
- //well, tough luck, not goinna do anything here
+ //well, tough luck, not gonna do anything here
}
}
#endif
@@ -2286,7 +2541,7 @@ void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::C
if (orphan_subclass.is_valid()) {
subclass = orphan_subclass;
} else {
- subclass.instance();
+ subclass.instantiate();
}
}
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index db02079d26..7d5bee93ac 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef GDSCRIPT_COMPILER_H
#define GDSCRIPT_COMPILER_H
-#include "core/set.h"
+#include "core/templates/set.h"
#include "gdscript.h"
#include "gdscript_codegen.h"
#include "gdscript_function.h"
@@ -51,23 +51,22 @@ class GDScriptCompiler {
GDScriptCodeGenerator *generator = nullptr;
Map<StringName, GDScriptCodeGenerator::Address> parameters;
Map<StringName, GDScriptCodeGenerator::Address> locals;
- List<Set<StringName>> locals_in_scope;
+ List<Map<StringName, GDScriptCodeGenerator::Address>> locals_stack;
GDScriptCodeGenerator::Address add_local(const StringName &p_name, const GDScriptDataType &p_type) {
uint32_t addr = generator->add_local(p_name, p_type);
locals[p_name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::LOCAL_VARIABLE, addr, p_type);
- locals_in_scope.back()->get().insert(p_name);
return locals[p_name];
}
GDScriptCodeGenerator::Address add_local_constant(const StringName &p_name, const Variant &p_value) {
uint32_t addr = generator->add_local_constant(p_name, p_value);
- locals[p_name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::LOCAL_CONSTANT, addr);
+ locals[p_name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::CONSTANT, addr);
return locals[p_name];
}
GDScriptCodeGenerator::Address add_temporary(const GDScriptDataType &p_type = GDScriptDataType()) {
- uint32_t addr = generator->add_temporary();
+ uint32_t addr = generator->add_temporary(p_type);
return GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::TEMPORARY, addr, p_type);
}
@@ -102,17 +101,14 @@ class GDScriptCompiler {
}
void start_block() {
- Set<StringName> scope;
- locals_in_scope.push_back(scope);
+ Map<StringName, GDScriptCodeGenerator::Address> old_locals = locals;
+ locals_stack.push_back(old_locals);
generator->start_block();
}
void end_block() {
- Set<StringName> &scope = locals_in_scope.back()->get();
- for (Set<StringName>::Element *E = scope.front(); E; E = E->next()) {
- locals.erase(E->get());
- }
- locals_in_scope.pop_back();
+ locals = locals_stack.back()->get();
+ locals_stack.pop_back();
generator->end_block();
}
};
@@ -132,13 +128,13 @@ class GDScriptCompiler {
GDScriptCodeGenerator::Address _parse_match_pattern(CodeGen &codegen, Error &r_error, const GDScriptParser::PatternNode *p_pattern, const GDScriptCodeGenerator::Address &p_value_addr, const GDScriptCodeGenerator::Address &p_type_addr, const GDScriptCodeGenerator::Address &p_previous_test, bool p_is_first, bool p_is_nested);
void _add_locals_in_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block);
Error _parse_block(CodeGen &codegen, const GDScriptParser::SuiteNode *p_block, bool p_add_locals = true);
- Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
+ GDScriptFunction *_parse_function(Error &r_error, GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false, bool p_for_lambda = false);
Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter);
Error _parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
void _make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
- int err_line;
- int err_column;
+ int err_line = 0;
+ int err_column = 0;
StringName source;
String error;
bool within_await = false;
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
new file mode 100644
index 0000000000..9287df2ea0
--- /dev/null
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -0,0 +1,1019 @@
+/*************************************************************************/
+/* gdscript_disassembler.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 DEBUG_ENABLED
+
+#include "gdscript_function.h"
+
+#include "core/string/string_builder.h"
+#include "gdscript.h"
+
+static String _get_variant_string(const Variant &p_variant) {
+ String txt;
+ if (p_variant.get_type() == Variant::STRING) {
+ txt = "\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::STRING_NAME) {
+ txt = "&\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::NODE_PATH) {
+ txt = "^\"" + String(p_variant) + "\"";
+ } else if (p_variant.get_type() == Variant::OBJECT) {
+ Object *obj = p_variant;
+ if (!obj) {
+ txt = "null";
+ } else {
+ GDScriptNativeClass *cls = Object::cast_to<GDScriptNativeClass>(obj);
+ if (cls) {
+ txt += cls->get_name();
+ txt += " (class)";
+ } else {
+ txt = obj->get_class();
+ if (obj->get_script_instance()) {
+ txt += "(" + obj->get_script_instance()->get_script()->get_path() + ")";
+ }
+ }
+ }
+ } else {
+ txt = p_variant;
+ }
+ return txt;
+}
+
+static String _disassemble_address(const GDScript *p_script, const GDScriptFunction &p_function, int p_address) {
+ int addr = p_address & GDScriptFunction::ADDR_MASK;
+
+ switch (p_address >> GDScriptFunction::ADDR_BITS) {
+ case GDScriptFunction::ADDR_TYPE_MEMBER: {
+ return "member(" + p_script->debug_get_member_by_index(addr) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_CONSTANT: {
+ return "const(" + _get_variant_string(p_function.get_constant(addr)) + ")";
+ } break;
+ case GDScriptFunction::ADDR_TYPE_STACK: {
+ switch (addr) {
+ case GDScriptFunction::ADDR_STACK_SELF:
+ return "self";
+ case GDScriptFunction::ADDR_STACK_CLASS:
+ return "class";
+ case GDScriptFunction::ADDR_STACK_NIL:
+ return "nil";
+ default:
+ return "stack(" + itos(addr) + ")";
+ }
+ } break;
+ }
+
+ return "<err>";
+}
+
+void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
+#define DADDR(m_ip) (_disassemble_address(_script, *this, _code_ptr[ip + m_ip]))
+
+ for (int ip = 0; ip < _code_size;) {
+ StringBuilder text;
+ int incr = 0;
+
+ text += " ";
+ text += itos(ip);
+ text += ": ";
+
+ // This makes the compiler complain if some opcode is unchecked in the switch.
+ Opcode code = Opcode(_code_ptr[ip] & INSTR_MASK);
+ int instr_var_args = (_code_ptr[ip] & INSTR_ARGS_MASK) >> INSTR_BITS;
+
+ switch (code) {
+ case OPCODE_OPERATOR: {
+ int operation = _code_ptr[ip + 4];
+
+ text += "operator ";
+
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " ";
+ text += Variant::get_operator_name(Variant::Operator(operation));
+ text += " ";
+ text += DADDR(2);
+
+ incr += 5;
+ } break;
+ case OPCODE_OPERATOR_VALIDATED: {
+ text += "validated operator ";
+
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " <operator function> ";
+ text += DADDR(2);
+
+ incr += 5;
+ } break;
+ case OPCODE_EXTENDS_TEST: {
+ text += "is object ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += " is ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_IS_BUILTIN: {
+ text += "is builtin ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " is ";
+ text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 3]));
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_KEYED: {
+ text += "set keyed ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "] = ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_KEYED_VALIDATED: {
+ text += "set keyed validated ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "] = ";
+ text += DADDR(3);
+
+ incr += 5;
+ } break;
+ case OPCODE_SET_INDEXED_VALIDATED: {
+ text += "set indexed validated ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "] = ";
+ text += DADDR(3);
+
+ incr += 5;
+ } break;
+ case OPCODE_GET_KEYED: {
+ text += "get keyed ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "]";
+
+ incr += 4;
+ } break;
+ case OPCODE_GET_KEYED_VALIDATED: {
+ text += "get keyed validated ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "]";
+
+ incr += 5;
+ } break;
+ case OPCODE_GET_INDEXED_VALIDATED: {
+ text += "get indexed validated ";
+ text += DADDR(3);
+ text += " = ";
+ text += DADDR(1);
+ text += "[";
+ text += DADDR(2);
+ text += "]";
+
+ incr += 5;
+ } break;
+ case OPCODE_SET_NAMED: {
+ text += "set_named ";
+ text += DADDR(1);
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 3]];
+ text += "\"] = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_NAMED_VALIDATED: {
+ text += "set_named validated ";
+ text += DADDR(1);
+ text += "[\"";
+ text += "<unknown name>";
+ text += "\"] = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_GET_NAMED: {
+ text += "get_named ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 3]];
+ text += "\"]";
+
+ incr += 4;
+ } break;
+ case OPCODE_GET_NAMED_VALIDATED: {
+ text += "get_named validated ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += "[\"";
+ text += "<unknown name>";
+ text += "\"]";
+
+ incr += 4;
+ } break;
+ case OPCODE_SET_MEMBER: {
+ text += "set_member ";
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 2]];
+ text += "\"] = ";
+ text += DADDR(1);
+
+ incr += 3;
+ } break;
+ case OPCODE_GET_MEMBER: {
+ text += "get_member ";
+ text += DADDR(1);
+ text += " = ";
+ text += "[\"";
+ text += _global_names_ptr[_code_ptr[ip + 2]];
+ text += "\"]";
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN_TRUE: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = true";
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSIGN_FALSE: {
+ text += "assign ";
+ text += DADDR(1);
+ text += " = false";
+
+ incr += 2;
+ } break;
+ case OPCODE_ASSIGN_TYPED_BUILTIN: {
+ text += "assign typed builtin (";
+ text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 3]);
+ text += ") ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_ASSIGN_TYPED_ARRAY: {
+ text += "assign typed array ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 3;
+ } break;
+ case OPCODE_ASSIGN_TYPED_NATIVE: {
+ text += "assign typed native (";
+ text += DADDR(3);
+ text += ") ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_ASSIGN_TYPED_SCRIPT: {
+ Variant script = _constants_ptr[_code_ptr[ip + 3]];
+ Script *sc = Object::cast_to<Script>(script.operator Object *());
+
+ text += "assign typed script (";
+ text += sc->get_path();
+ text += ") ";
+ text += DADDR(1);
+ text += " = ";
+ text += DADDR(2);
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_BUILTIN: {
+ text += "cast builtin ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " as ";
+ text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 1]));
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_NATIVE: {
+ text += "cast native ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " as ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_CAST_TO_SCRIPT: {
+ text += "cast ";
+ text += DADDR(2);
+ text += " = ";
+ text += DADDR(1);
+ text += " as ";
+ text += DADDR(3);
+
+ incr += 4;
+ } break;
+ case OPCODE_CONSTRUCT: {
+ Variant::Type t = Variant::Type(_code_ptr[ip + 3 + instr_var_args]);
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += "construct ";
+ text += DADDR(1 + argc);
+ text += " = ";
+
+ text += Variant::get_type_name(t) + "(";
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(i + 1);
+ }
+ text += ")";
+
+ incr = 3 + instr_var_args;
+ } break;
+ case OPCODE_CONSTRUCT_VALIDATED: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += "construct validated ";
+ text += DADDR(1 + argc);
+ text += " = ";
+
+ text += "<unknown type>(";
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(i + 1);
+ }
+ text += ")";
+
+ incr = 3 + instr_var_args;
+ } break;
+ case OPCODE_CONSTRUCT_ARRAY: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += " make_array ";
+ text += DADDR(1 + argc);
+ text += " = [";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+
+ text += "]";
+
+ incr += 3 + argc;
+ } break;
+ case OPCODE_CONSTRUCT_TYPED_ARRAY: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ Ref<Script> script_type = get_constant(_code_ptr[ip + argc + 2]);
+ Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + argc + 4];
+ StringName native_type = get_global_name(_code_ptr[ip + argc + 5]);
+
+ String type_name;
+ if (script_type.is_valid() && script_type->is_valid()) {
+ type_name = script_type->get_path();
+ } else if (native_type != StringName()) {
+ type_name = native_type;
+ } else {
+ type_name = Variant::get_type_name(builtin_type);
+ }
+
+ text += " make_typed_array (";
+ text += type_name;
+ text += ") ";
+
+ text += DADDR(1 + argc);
+ text += " = [";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+
+ text += "]";
+
+ incr += 3 + argc;
+ } break;
+ case OPCODE_CONSTRUCT_DICTIONARY: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += "make_dict ";
+ text += DADDR(1 + argc * 2);
+ text += " = {";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i * 2 + 0);
+ text += ": ";
+ text += DADDR(1 + i * 2 + 1);
+ }
+
+ text += "}";
+
+ incr += 3 + argc * 2;
+ } break;
+ case OPCODE_CALL:
+ case OPCODE_CALL_RETURN:
+ case OPCODE_CALL_ASYNC: {
+ bool ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_RETURN;
+ bool async = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_ASYNC;
+
+ if (ret) {
+ text += "call-ret ";
+ } else if (async) {
+ text += "call-async ";
+ } else {
+ text += "call ";
+ }
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ if (ret || async) {
+ text += DADDR(2 + argc) + " = ";
+ }
+
+ text += DADDR(1 + argc) + ".";
+ text += String(_global_names_ptr[_code_ptr[ip + 2 + instr_var_args]]);
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+ case OPCODE_CALL_METHOD_BIND:
+ case OPCODE_CALL_METHOD_BIND_RET: {
+ bool ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_METHOD_BIND_RET;
+
+ if (ret) {
+ text += "call-method_bind-ret ";
+ } else {
+ text += "call-method_bind ";
+ }
+
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ if (ret) {
+ text += DADDR(2 + argc) + " = ";
+ }
+
+ text += DADDR(1 + argc) + ".";
+ text += method->get_name();
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+ case OPCODE_CALL_BUILTIN_STATIC: {
+ Variant::Type type = (Variant::Type)_code_ptr[ip + 1 + instr_var_args];
+ int argc = _code_ptr[ip + 3 + instr_var_args];
+
+ text += "call built-in method static ";
+ text += DADDR(1 + argc);
+ text += " = ";
+ text += Variant::get_type_name(type);
+ text += ".";
+ text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]].operator String();
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr += 5 + argc;
+ } break;
+ case OPCODE_CALL_PTRCALL_NO_RETURN: {
+ text += "call-ptrcall (no return) ";
+
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]];
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += DADDR(1 + argc) + ".";
+ text += method->get_name();
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+
+#define DISASSEMBLE_PTRCALL(m_type) \
+ case OPCODE_CALL_PTRCALL_##m_type: { \
+ text += "call-ptrcall (return "; \
+ text += #m_type; \
+ text += ") "; \
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2 + instr_var_args]]; \
+ int argc = _code_ptr[ip + 1 + instr_var_args]; \
+ text += DADDR(2 + argc) + " = "; \
+ text += DADDR(1 + argc) + "."; \
+ text += method->get_name(); \
+ text += "("; \
+ for (int i = 0; i < argc; i++) { \
+ if (i > 0) \
+ text += ", "; \
+ text += DADDR(1 + i); \
+ } \
+ text += ")"; \
+ incr = 5 + argc; \
+ } break
+
+ DISASSEMBLE_PTRCALL(BOOL);
+ DISASSEMBLE_PTRCALL(INT);
+ DISASSEMBLE_PTRCALL(FLOAT);
+ DISASSEMBLE_PTRCALL(STRING);
+ DISASSEMBLE_PTRCALL(VECTOR2);
+ DISASSEMBLE_PTRCALL(VECTOR2I);
+ DISASSEMBLE_PTRCALL(RECT2);
+ DISASSEMBLE_PTRCALL(RECT2I);
+ DISASSEMBLE_PTRCALL(VECTOR3);
+ DISASSEMBLE_PTRCALL(VECTOR3I);
+ DISASSEMBLE_PTRCALL(TRANSFORM2D);
+ DISASSEMBLE_PTRCALL(PLANE);
+ DISASSEMBLE_PTRCALL(AABB);
+ DISASSEMBLE_PTRCALL(BASIS);
+ DISASSEMBLE_PTRCALL(TRANSFORM3D);
+ DISASSEMBLE_PTRCALL(COLOR);
+ DISASSEMBLE_PTRCALL(STRING_NAME);
+ DISASSEMBLE_PTRCALL(NODE_PATH);
+ DISASSEMBLE_PTRCALL(RID);
+ DISASSEMBLE_PTRCALL(QUATERNION);
+ DISASSEMBLE_PTRCALL(OBJECT);
+ DISASSEMBLE_PTRCALL(CALLABLE);
+ DISASSEMBLE_PTRCALL(SIGNAL);
+ DISASSEMBLE_PTRCALL(DICTIONARY);
+ DISASSEMBLE_PTRCALL(ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_BYTE_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_INT32_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_INT64_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_FLOAT32_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_FLOAT64_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_STRING_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_VECTOR2_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_VECTOR3_ARRAY);
+ DISASSEMBLE_PTRCALL(PACKED_COLOR_ARRAY);
+
+ case OPCODE_CALL_BUILTIN_TYPE_VALIDATED: {
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+
+ text += "call-builtin-method validated ";
+
+ text += DADDR(2 + argc) + " = ";
+
+ text += DADDR(1) + ".";
+ text += "<unknown method>";
+
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 5 + argc;
+ } break;
+ case OPCODE_CALL_UTILITY: {
+ text += "call-utility ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(1 + argc) + " = ";
+
+ text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]];
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CALL_UTILITY_VALIDATED: {
+ text += "call-utility ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(1 + argc) + " = ";
+
+ text += "<unknown function>";
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CALL_GDSCRIPT_UTILITY: {
+ text += "call-gscript-utility ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(1 + argc) + " = ";
+
+ text += "<unknown function>";
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_CALL_SELF_BASE: {
+ text += "call-self-base ";
+
+ int argc = _code_ptr[ip + 1 + instr_var_args];
+ text += DADDR(2 + argc) + " = ";
+
+ text += _global_names_ptr[_code_ptr[ip + 2 + instr_var_args]];
+ text += "(";
+
+ for (int i = 0; i < argc; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 4 + argc;
+ } break;
+ case OPCODE_AWAIT: {
+ text += "await ";
+ text += DADDR(1);
+
+ incr = 2;
+ } break;
+ case OPCODE_AWAIT_RESUME: {
+ text += "await resume ";
+ text += DADDR(1);
+
+ incr = 2;
+ } break;
+ case OPCODE_CREATE_LAMBDA: {
+ int captures_count = _code_ptr[ip + 1 + instr_var_args];
+ GDScriptFunction *lambda = _lambdas_ptr[_code_ptr[ip + 2 + instr_var_args]];
+
+ text += DADDR(1 + captures_count);
+ text += "create lambda from ";
+ text += lambda->name.operator String();
+ text += "function, captures (";
+
+ for (int i = 0; i < captures_count; i++) {
+ if (i > 0) {
+ text += ", ";
+ }
+ text += DADDR(1 + i);
+ }
+ text += ")";
+
+ incr = 3 + captures_count;
+ } break;
+ case OPCODE_JUMP: {
+ text += "jump ";
+ text += itos(_code_ptr[ip + 1]);
+
+ incr = 2;
+ } break;
+ case OPCODE_JUMP_IF: {
+ text += "jump-if ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
+ case OPCODE_JUMP_IF_NOT: {
+ text += "jump-if-not ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
+ case OPCODE_JUMP_TO_DEF_ARGUMENT: {
+ text += "jump-to-default-argument ";
+
+ incr = 1;
+ } break;
+ case OPCODE_RETURN: {
+ text += "return ";
+ text += DADDR(1);
+
+ incr = 2;
+ } break;
+ case OPCODE_RETURN_TYPED_BUILTIN: {
+ text += "return typed builtin (";
+ text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 2]);
+ text += ") ";
+ text += DADDR(1);
+
+ incr += 3;
+ } break;
+ case OPCODE_RETURN_TYPED_ARRAY: {
+ text += "return typed array ";
+ text += DADDR(1);
+
+ incr += 5;
+ } break;
+ case OPCODE_RETURN_TYPED_NATIVE: {
+ text += "return typed native (";
+ text += DADDR(2);
+ text += ") ";
+ text += DADDR(1);
+
+ incr += 3;
+ } break;
+ case OPCODE_RETURN_TYPED_SCRIPT: {
+ Variant script = _constants_ptr[_code_ptr[ip + 2]];
+ Script *sc = Object::cast_to<Script>(script.operator Object *());
+
+ text += "return typed script (";
+ text += sc->get_path();
+ text += ") ";
+ text += DADDR(1);
+
+ incr += 3;
+ } break;
+
+#define DISASSEMBLE_ITERATE(m_type) \
+ case OPCODE_ITERATE_##m_type: { \
+ text += "for-loop (typed "; \
+ text += #m_type; \
+ text += ") "; \
+ text += DADDR(3); \
+ text += " in "; \
+ text += DADDR(2); \
+ text += " counter "; \
+ text += DADDR(1); \
+ text += " end "; \
+ text += itos(_code_ptr[ip + 4]); \
+ incr += 5; \
+ } break
+
+#define DISASSEMBLE_ITERATE_BEGIN(m_type) \
+ case OPCODE_ITERATE_BEGIN_##m_type: { \
+ text += "for-init (typed "; \
+ text += #m_type; \
+ text += ") "; \
+ text += DADDR(3); \
+ text += " in "; \
+ text += DADDR(2); \
+ text += " counter "; \
+ text += DADDR(1); \
+ text += " end "; \
+ text += itos(_code_ptr[ip + 4]); \
+ incr += 5; \
+ } break
+
+#define DISASSEMBLE_ITERATE_TYPES(m_macro) \
+ m_macro(INT); \
+ m_macro(FLOAT); \
+ m_macro(VECTOR2); \
+ m_macro(VECTOR2I); \
+ m_macro(VECTOR3); \
+ m_macro(VECTOR3I); \
+ m_macro(STRING); \
+ m_macro(DICTIONARY); \
+ m_macro(ARRAY); \
+ m_macro(PACKED_BYTE_ARRAY); \
+ m_macro(PACKED_INT32_ARRAY); \
+ m_macro(PACKED_INT64_ARRAY); \
+ m_macro(PACKED_FLOAT32_ARRAY); \
+ m_macro(PACKED_FLOAT64_ARRAY); \
+ m_macro(PACKED_STRING_ARRAY); \
+ m_macro(PACKED_VECTOR2_ARRAY); \
+ m_macro(PACKED_VECTOR3_ARRAY); \
+ m_macro(PACKED_COLOR_ARRAY); \
+ m_macro(OBJECT)
+
+ case OPCODE_ITERATE_BEGIN: {
+ text += "for-init ";
+ text += DADDR(3);
+ text += " in ";
+ text += DADDR(2);
+ text += " counter ";
+ text += DADDR(1);
+ text += " end ";
+ text += itos(_code_ptr[ip + 4]);
+
+ incr += 5;
+ } break;
+ DISASSEMBLE_ITERATE_TYPES(DISASSEMBLE_ITERATE_BEGIN);
+ case OPCODE_ITERATE: {
+ text += "for-loop ";
+ text += DADDR(2);
+ text += " in ";
+ text += DADDR(2);
+ text += " counter ";
+ text += DADDR(1);
+ text += " end ";
+ text += itos(_code_ptr[ip + 4]);
+
+ 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);
+ text += " = ";
+ text += String(_global_names_ptr[_code_ptr[ip + 2]]);
+
+ incr += 3;
+ } break;
+ case OPCODE_LINE: {
+ int line = _code_ptr[ip + 1] - 1;
+ if (line >= 0 && line < p_code_lines.size()) {
+ text += "line ";
+ text += itos(line + 1);
+ text += ": ";
+ text += p_code_lines[line];
+ } else {
+ text += "";
+ }
+
+ incr += 2;
+ } break;
+
+#define DISASSEMBLE_TYPE_ADJUST(m_v_type) \
+ case OPCODE_TYPE_ADJUST_##m_v_type: { \
+ text += "type adjust ("; \
+ text += #m_v_type; \
+ text += ") "; \
+ text += DADDR(1); \
+ incr += 2; \
+ } break
+
+ DISASSEMBLE_TYPE_ADJUST(BOOL);
+ DISASSEMBLE_TYPE_ADJUST(INT);
+ DISASSEMBLE_TYPE_ADJUST(FLOAT);
+ DISASSEMBLE_TYPE_ADJUST(STRING);
+ DISASSEMBLE_TYPE_ADJUST(VECTOR2);
+ DISASSEMBLE_TYPE_ADJUST(VECTOR2I);
+ DISASSEMBLE_TYPE_ADJUST(RECT2);
+ DISASSEMBLE_TYPE_ADJUST(RECT2I);
+ DISASSEMBLE_TYPE_ADJUST(VECTOR3);
+ DISASSEMBLE_TYPE_ADJUST(VECTOR3I);
+ DISASSEMBLE_TYPE_ADJUST(TRANSFORM2D);
+ DISASSEMBLE_TYPE_ADJUST(PLANE);
+ DISASSEMBLE_TYPE_ADJUST(QUATERNION);
+ DISASSEMBLE_TYPE_ADJUST(AABB);
+ DISASSEMBLE_TYPE_ADJUST(BASIS);
+ DISASSEMBLE_TYPE_ADJUST(TRANSFORM);
+ DISASSEMBLE_TYPE_ADJUST(COLOR);
+ DISASSEMBLE_TYPE_ADJUST(STRING_NAME);
+ DISASSEMBLE_TYPE_ADJUST(NODE_PATH);
+ DISASSEMBLE_TYPE_ADJUST(RID);
+ DISASSEMBLE_TYPE_ADJUST(OBJECT);
+ DISASSEMBLE_TYPE_ADJUST(CALLABLE);
+ DISASSEMBLE_TYPE_ADJUST(SIGNAL);
+ DISASSEMBLE_TYPE_ADJUST(DICTIONARY);
+ DISASSEMBLE_TYPE_ADJUST(ARRAY);
+ DISASSEMBLE_TYPE_ADJUST(PACKED_BYTE_ARRAY);
+ DISASSEMBLE_TYPE_ADJUST(PACKED_INT32_ARRAY);
+ DISASSEMBLE_TYPE_ADJUST(PACKED_INT64_ARRAY);
+ DISASSEMBLE_TYPE_ADJUST(PACKED_FLOAT32_ARRAY);
+ DISASSEMBLE_TYPE_ADJUST(PACKED_FLOAT64_ARRAY);
+ DISASSEMBLE_TYPE_ADJUST(PACKED_STRING_ARRAY);
+ DISASSEMBLE_TYPE_ADJUST(PACKED_VECTOR2_ARRAY);
+ DISASSEMBLE_TYPE_ADJUST(PACKED_VECTOR3_ARRAY);
+ DISASSEMBLE_TYPE_ADJUST(PACKED_COLOR_ARRAY);
+
+ case OPCODE_ASSERT: {
+ text += "assert (";
+ text += DADDR(1);
+ text += ", ";
+ text += DADDR(2);
+ text += ")";
+
+ incr += 3;
+ } break;
+ case OPCODE_BREAKPOINT: {
+ text += "breakpoint";
+
+ incr += 1;
+ } break;
+ case OPCODE_END: {
+ text += "== END ==";
+
+ incr += 1;
+ } break;
+ }
+
+ ip += incr;
+ if (text.get_string_length() > 0) {
+ print_line(text.as_string());
+ }
+ }
+}
+
+#endif
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 2e372575da..71d2699c2e 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,16 +30,17 @@
#include "gdscript.h"
-#include "core/engine.h"
-#include "core/global_constants.h"
-#include "core/os/file_access.h"
+#include "core/config/engine.h"
+#include "core/core_constants.h"
+#include "core/io/file_access.h"
#include "gdscript_analyzer.h"
#include "gdscript_compiler.h"
#include "gdscript_parser.h"
#include "gdscript_tokenizer.h"
+#include "gdscript_utility_functions.h"
#ifdef TOOLS_ENABLED
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "editor/editor_file_system.h"
#include "editor/editor_settings.h"
#endif
@@ -103,7 +104,7 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str
_template = _get_processed_template(_template, p_base_class_name);
Ref<GDScript> script;
- script.instance();
+ script.instantiate();
script->set_source_code(_template);
return script;
@@ -122,15 +123,15 @@ static void get_function_names_recursively(const GDScriptParser::ClassNode *p_cl
for (int i = 0; i < p_class->members.size(); i++) {
if (p_class->members[i].type == GDScriptParser::ClassNode::Member::FUNCTION) {
const GDScriptParser::FunctionNode *function = p_class->members[i].function;
- r_funcs[function->start_line] = p_prefix.empty() ? String(function->identifier->name) : p_prefix + "." + String(function->identifier->name);
+ r_funcs[function->start_line] = p_prefix.is_empty() ? String(function->identifier->name) : p_prefix + "." + String(function->identifier->name);
} else if (p_class->members[i].type == GDScriptParser::ClassNode::Member::CLASS) {
String new_prefix = p_class->members[i].m_class->identifier->name;
- get_function_names_recursively(p_class->members[i].m_class, p_prefix.empty() ? new_prefix : p_prefix + "." + new_prefix, r_funcs);
+ get_function_names_recursively(p_class->members[i].m_class, p_prefix.is_empty() ? new_prefix : p_prefix + "." + new_prefix, r_funcs);
}
}
}
-bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
+bool GDScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
GDScriptParser parser;
GDScriptAnalyzer analyzer(&parser);
@@ -140,8 +141,8 @@ bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &
}
#ifdef DEBUG_ENABLED
if (r_warnings) {
- for (const List<GDScriptWarning>::Element *E = parser.get_warnings().front(); E; E = E->next()) {
- const GDScriptWarning &warn = E->get();
+ for (const GDScriptWarning &E : parser.get_warnings()) {
+ const GDScriptWarning &warn = E;
ScriptLanguage::Warning w;
w.start_line = warn.start_line;
w.end_line = warn.end_line;
@@ -155,10 +156,16 @@ bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &
}
#endif
if (err) {
- GDScriptParser::ParserError parse_error = parser.get_errors().front()->get();
- r_line_error = parse_error.line;
- r_col_error = parse_error.column;
- r_test_error = parse_error.message;
+ if (r_errors) {
+ for (const GDScriptParser::ParserError &E : parser.get_errors()) {
+ const GDScriptParser::ParserError &pe = E;
+ ScriptLanguage::ScriptError e;
+ e.line = pe.line;
+ e.column = pe.column;
+ e.message = pe.message;
+ r_errors->push_back(e);
+ }
+ }
return false;
} else {
const GDScriptParser::ClassNode *cl = parser.get_tree();
@@ -166,8 +173,8 @@ bool GDScriptLanguage::validate(const String &p_script, int &r_line_error, int &
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));
}
}
@@ -193,6 +200,10 @@ bool GDScriptLanguage::supports_builtin_mode() const {
return true;
}
+bool GDScriptLanguage::supports_documentation() const {
+ return true;
+}
+
int GDScriptLanguage::find_function(const String &p_function, const String &p_code) const {
GDScriptTokenizer tokenizer;
tokenizer.set_source_code(p_code);
@@ -308,9 +319,9 @@ void GDScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p
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]);
+ for (const Pair<StringName, int> &E : locals) {
+ p_locals->push_back(E.first);
+ p_values->push_back(_call_stack[l].stack[E.second]);
}
}
@@ -333,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));
}
}
@@ -359,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;
}
@@ -375,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;
@@ -383,8 +394,8 @@ void GDScriptLanguage::debug_get_globals(List<String> *p_globals, List<Variant>
}
bool skip = false;
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
- if (E->key() == GlobalConstants::get_global_constant_name(i)) {
+ for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
+ if (E.key == CoreConstants::get_global_constant_name(i)) {
skip = true;
break;
}
@@ -393,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);
}
}
@@ -407,11 +418,14 @@ void GDScriptLanguage::get_recognized_extensions(List<String> *p_extensions) con
}
void GDScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- p_functions->push_back(GDScriptFunctions::get_info(GDScriptFunctions::Function(i)));
+ List<StringName> functions;
+ GDScriptUtilityFunctions::get_function_list(&functions);
+
+ for (const StringName &E : functions) {
+ p_functions->push_back(GDScriptUtilityFunctions::get_function_info(E));
}
- //not really "functions", but..
+ // Not really "functions", but show in documentation.
{
MethodInfo mi;
mi.name = "preload";
@@ -443,12 +457,12 @@ void GDScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_const
Pair<String, Variant> infinity;
infinity.first = "INF";
- infinity.second = Math_INF;
+ infinity.second = INFINITY;
p_constants->push_back(infinity);
Pair<String, Variant> nan;
nan.first = "NAN";
- nan.second = Math_NAN;
+ nan.second = NAN;
p_constants->push_back(nan);
}
@@ -468,7 +482,7 @@ String GDScriptLanguage::make_function(const String &p_class, const String &p_na
s += p_args[i].get_slice(":", 0);
if (th) {
String type = p_args[i].get_slice(":", 1);
- if (!type.empty() && type != "var") {
+ if (!type.is_empty() && type != "var") {
s += ": " + type;
}
}
@@ -492,36 +506,6 @@ struct GDScriptCompletionIdentifier {
const GDScriptParser::ExpressionNode *assigned_expression = nullptr;
};
-// TODO: Move this to a central location (maybe core?).
-static const char *underscore_classes[] = {
- "ClassDB",
- "Directory",
- "Engine",
- "File",
- "Geometry",
- "GodotSharp",
- "JSON",
- "Marshalls",
- "Mutex",
- "OS",
- "ResourceLoader",
- "ResourceSaver",
- "Semaphore",
- "Thread",
- "VisualScriptEditor",
- nullptr,
-};
-static StringName _get_real_class_name(const StringName &p_source) {
- const char **class_name = underscore_classes;
- while (*class_name != nullptr) {
- if (p_source == *class_name) {
- return String("_") + p_source;
- }
- class_name++;
- }
- return p_source;
-}
-
static String _get_visual_datatype(const PropertyInfo &p_info, bool p_is_arg = true) {
if (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
String enum_name = p_info.class_name;
@@ -564,7 +548,7 @@ static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx, bool
int def_args = p_info.arguments.size() - p_info.default_arguments.size();
int i = 0;
- for (const List<PropertyInfo>::Element *E = p_info.arguments.front(); E; E = E->next()) {
+ for (const PropertyInfo &E : p_info.arguments) {
if (i > 0) {
arghint += ", ";
}
@@ -572,7 +556,7 @@ static String _make_arguments_hint(const MethodInfo &p_info, int p_arg_idx, bool
if (i == p_arg_idx) {
arghint += String::chr(0xFFFF);
}
- arghint += E->get().name + ": " + _get_visual_datatype(E->get(), true);
+ arghint += E.name + ": " + _get_visual_datatype(E, true);
if (i - def_args >= 0) {
arghint += String(" = ") + p_info.default_arguments[i - def_args].get_construct_string();
@@ -639,11 +623,11 @@ static String _make_arguments_hint(const GDScriptParser::FunctionNode *p_functio
}
static void _get_directory_contents(EditorFileSystemDirectory *p_dir, Map<String, ScriptCodeCompletionOption> &r_list) {
- const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
+ const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
for (int i = 0; i < p_dir->get_file_count(); i++) {
ScriptCodeCompletionOption option(p_dir->get_file_path(i), ScriptCodeCompletionOption::KIND_FILE_PATH);
- option.insert_text = quote_style + option.display + quote_style;
+ option.insert_text = option.display.quote(quote_style);
r_list.insert(option.display, option);
}
@@ -653,24 +637,24 @@ 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);
- slider1.insert_text = p_quote_style + slider1.display + p_quote_style;
+ slider1.insert_text = slider1.display.quote(p_quote_style);
r_result.insert(slider1.display, slider1);
ScriptCodeCompletionOption slider2("or_lesser", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
- slider2.insert_text = p_quote_style + slider2.display + p_quote_style;
+ slider2.insert_text = slider2.display.quote(p_quote_style);
r_result.insert(slider2.display, slider2);
}
} else if (p_annotation->name == "@export_exp_easing") {
if (p_argument == 0 || p_argument == 1) {
// Easing hint.
ScriptCodeCompletionOption hint1("attenuation", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
- hint1.insert_text = p_quote_style + hint1.display + p_quote_style;
+ hint1.insert_text = hint1.display.quote(p_quote_style);
r_result.insert(hint1.display, hint1);
ScriptCodeCompletionOption hint2("inout", ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
- hint2.insert_text = p_quote_style + hint2.display + p_quote_style;
+ hint2.insert_text = hint2.display.quote(p_quote_style);
r_result.insert(hint2.display, hint2);
}
} else if (p_annotation->name == "@export_node_path") {
@@ -678,11 +662,11 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
r_result.insert(node.display, node);
List<StringName> node_types;
ClassDB::get_inheriters_from_class("Node", &node_types);
- for (const List<StringName>::Element *E = node_types.front(); E != nullptr; E = E->next()) {
- if (!ClassDB::is_class_exposed(E->get())) {
+ for (const StringName &E : node_types) {
+ if (!ClassDB::is_class_exposed(E)) {
continue;
}
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CLASS);
+ ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS);
r_result.insert(option.display, option);
}
}
@@ -691,9 +675,9 @@ static void _find_annotation_arguments(const GDScriptParser::AnnotationNode *p_a
static void _list_available_types(bool p_inherit_only, GDScriptParser::CompletionContext &p_context, Map<String, ScriptCodeCompletionOption> &r_result) {
List<StringName> native_types;
ClassDB::get_class_list(&native_types);
- for (const List<StringName>::Element *E = native_types.front(); E != nullptr; E = E->next()) {
- if (ClassDB::is_class_exposed(E->get()) && !Engine::get_singleton()->has_singleton(E->get())) {
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CLASS);
+ for (const StringName &E : native_types) {
+ if (ClassDB::is_class_exposed(E) && !Engine::get_singleton()->has_singleton(E)) {
+ ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS);
r_result.insert(option.display, option);
}
}
@@ -703,8 +687,8 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
// Native enums from base class
List<StringName> enums;
ClassDB::get_enum_list(p_context.current_class->base_type.native_type, &enums);
- for (const List<StringName>::Element *E = enums.front(); E != nullptr; E = E->next()) {
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_ENUM);
+ for (const StringName &E : enums) {
+ ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_ENUM);
r_result.insert(option.display, option);
}
}
@@ -741,15 +725,15 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
// Global scripts
List<StringName> global_classes;
ScriptServer::get_global_class_list(&global_classes);
- for (const List<StringName>::Element *E = global_classes.front(); E != nullptr; E = E->next()) {
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CLASS);
+ for (const StringName &E : global_classes) {
+ ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CLASS);
r_result.insert(option.display, option);
}
// Autoload singletons
- Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (const Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E != nullptr; E = E->next()) {
- const ProjectSettings::AutoloadInfo &info = E->get();
+ OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
+ const ProjectSettings::AutoloadInfo &info = E.get();
if (!info.is_singleton || info.path.get_extension().to_lower() != "gd") {
continue;
}
@@ -760,7 +744,13 @@ static void _list_available_types(bool p_inherit_only, GDScriptParser::Completio
static void _find_identifiers_in_suite(const GDScriptParser::SuiteNode *p_suite, Map<String, ScriptCodeCompletionOption> &r_result) {
for (int i = 0; i < p_suite->locals.size(); i++) {
- ScriptCodeCompletionOption option(p_suite->locals[i].name, ScriptCodeCompletionOption::KIND_VARIABLE);
+ ScriptCodeCompletionOption option;
+ if (p_suite->locals[i].type == GDScriptParser::SuiteNode::Local::CONSTANT) {
+ option = ScriptCodeCompletionOption(p_suite->locals[i].name, ScriptCodeCompletionOption::KIND_CONSTANT);
+ option.default_value = p_suite->locals[i].constant->initializer->reduced_value;
+ } else {
+ option = ScriptCodeCompletionOption(p_suite->locals[i].name, ScriptCodeCompletionOption::KIND_VARIABLE);
+ }
r_result.insert(option.display, option);
}
if (p_suite->parent_block) {
@@ -791,7 +781,13 @@ 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;
+ }
break;
case GDScriptParser::ClassNode::Member::CLASS:
if (p_only_functions) {
@@ -872,34 +868,34 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
if (!_static) {
List<PropertyInfo> members;
scr->get_script_property_list(&members);
- for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
+ for (const PropertyInfo &E : members) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
r_result.insert(option.display, option);
}
}
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);
}
List<MethodInfo> signals;
scr->get_script_signal_list(&signals);
- for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_SIGNAL);
+ for (const MethodInfo &E : signals) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_SIGNAL);
r_result.insert(option.display, option);
}
}
List<MethodInfo> methods;
scr->get_script_method_list(&methods);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name.begins_with("@")) {
+ for (const MethodInfo &E : methods) {
+ if (E.name.begins_with("@")) {
continue;
}
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
- if (E->get().arguments.size()) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_FUNCTION);
+ if (E.arguments.size()) {
option.insert_text += "(";
} else {
option.insert_text += "()";
@@ -919,7 +915,7 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
}
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName type = _get_real_class_name(base_type.native_type);
+ StringName type = base_type.native_type;
if (!ClassDB::class_exists(type)) {
return;
}
@@ -927,22 +923,22 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
if (!p_only_functions) {
List<String> constants;
ClassDB::get_integer_constant_list(type, &constants);
- for (List<String>::Element *E = constants.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ for (const String &E : constants) {
+ ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CONSTANT);
r_result.insert(option.display, option);
}
if (!_static || Engine::get_singleton()->has_singleton(type)) {
List<PropertyInfo> pinfo;
ClassDB::get_property_list(type, &pinfo);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) {
+ for (const PropertyInfo &E : pinfo) {
+ if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_CATEGORY)) {
continue;
}
- if (E->get().name.find("/") != -1) {
+ if (E.name.find("/") != -1) {
continue;
}
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
r_result.insert(option.display, option);
}
}
@@ -952,12 +948,12 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<MethodInfo> methods;
bool is_autocompleting_getters = GLOBAL_GET("debug/gdscript/completion/autocomplete_setters_and_getters").booleanize();
ClassDB::get_method_list(type, &methods, false, !is_autocompleting_getters);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name.begins_with("_")) {
+ for (const MethodInfo &E : methods) {
+ if (E.name.begins_with("_")) {
continue;
}
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
- if (E->get().arguments.size()) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_FUNCTION);
+ if (E.arguments.size()) {
option.insert_text += "(";
} else {
option.insert_text += "()";
@@ -970,7 +966,8 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
} break;
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
- Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant tmp;
+ Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return;
}
@@ -983,9 +980,9 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
tmp.get_property_list(&members);
}
- for (List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
- if (String(E->get().name).find("/") == -1) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_MEMBER);
+ for (const PropertyInfo &E : members) {
+ if (String(E.name).find("/") == -1) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_MEMBER);
r_result.insert(option.display, option);
}
}
@@ -993,9 +990,9 @@ static void _find_identifiers_in_base(const GDScriptCompletionIdentifier &p_base
List<MethodInfo> methods;
tmp.get_method_list(&methods);
- for (const List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name, ScriptCodeCompletionOption::KIND_FUNCTION);
- if (E->get().arguments.size()) {
+ for (const MethodInfo &E : methods) {
+ ScriptCodeCompletionOption option(E.name, ScriptCodeCompletionOption::KIND_FUNCTION);
+ if (E.arguments.size()) {
option.insert_text += "(";
} else {
option.insert_text += "()";
@@ -1022,9 +1019,12 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
_find_identifiers_in_class(p_context.current_class, p_only_functions, (!p_context.current_function || p_context.current_function->is_static), false, r_result, p_recursion_depth + 1);
}
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- MethodInfo function = GDScriptFunctions::get_info(GDScriptFunctions::Function(i));
- ScriptCodeCompletionOption option(String(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))), ScriptCodeCompletionOption::KIND_FUNCTION);
+ List<StringName> functions;
+ GDScriptUtilityFunctions::get_function_list(&functions);
+
+ for (const StringName &E : functions) {
+ MethodInfo function = GDScriptUtilityFunctions::get_function_info(E);
+ ScriptCodeCompletionOption option(String(E), ScriptCodeCompletionOption::KIND_FUNCTION);
if (function.arguments.size() || (function.flags & METHOD_FLAG_VARARG)) {
option.insert_text += "(";
} else {
@@ -1038,7 +1038,7 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
}
static const char *_type_names[Variant::VARIANT_MAX] = {
- "null", "bool", "int", "float", "String", "StringName", "Vector2", "Vector2i", "Rect2", "Rect2i", "Vector3", "Vector3i", "Transform2D", "Plane", "Quat", "AABB", "Basis", "Transform",
+ "null", "bool", "int", "float", "String", "StringName", "Vector2", "Vector2i", "Rect2", "Rect2i", "Vector3", "Vector3i", "Transform2D", "Plane", "Quaternion", "AABB", "Basis", "Transform3D",
"Color", "NodePath", "RID", "Signal", "Callable", "Object", "Dictionary", "Array", "PackedByteArray", "PackedInt32Array", "PackedInt64Array", "PackedFloat32Array", "PackedFloat64Array", "PackedStringArray",
"PackedVector2Array", "PackedVector3Array", "PackedColorArray"
};
@@ -1050,11 +1050,9 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
}
static const char *_keywords[] = {
- "and", "in", "not", "or", "false", "PI", "TAU", "INF", "NAN", "self", "true", "as", "assert",
- "breakpoint", "class", "extends", "is", "func", "preload", "signal", "tool", "await",
- "const", "enum", "static", "super", "var", "break", "continue", "if", "elif",
- "else", "for", "pass", "return", "match", "while",
- 0
+ "false", "PI", "TAU", "INF", "NAN", "self", "true", "breakpoint", "tool", "super",
+ "break", "continue", "pass", "return",
+ nullptr
};
const char **kw = _keywords;
@@ -1064,22 +1062,58 @@ static void _find_identifiers(GDScriptParser::CompletionContext &p_context, bool
kw++;
}
- Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (const Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E != nullptr; E = E->next()) {
- if (!E->value().is_singleton) {
+ static const char *_keywords_with_space[] = {
+ "and", "in", "not", "or", "as", "class", "extends", "is", "func", "signal", "await",
+ "const", "enum", "static", "var", "if", "elif", "else", "for", "match", "while",
+ nullptr
+ };
+
+ const char **kws = _keywords_with_space;
+ while (*kws) {
+ ScriptCodeCompletionOption option(*kws, ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ option.insert_text += " ";
+ r_result.insert(option.display, option);
+ kws++;
+ }
+
+ static const char *_keywords_with_args[] = {
+ "assert", "preload",
+ nullptr
+ };
+
+ const char **kwa = _keywords_with_args;
+ while (*kwa) {
+ ScriptCodeCompletionOption option(*kwa, ScriptCodeCompletionOption::KIND_FUNCTION);
+ option.insert_text += "(";
+ r_result.insert(option.display, option);
+ 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) {
continue;
}
- ScriptCodeCompletionOption option(E->key(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ ScriptCodeCompletionOption option(E.key(), ScriptCodeCompletionOption::KIND_CONSTANT);
r_result.insert(option.display, option);
}
// 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);
}
@@ -1255,8 +1289,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
r_type.type.builtin_type = GDScriptParser::get_builtin_type(call->function_name);
found = true;
break;
- } else if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
- MethodInfo mi = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
+ } else if (GDScriptUtilityFunctions::function_exists(call->function_name)) {
+ MethodInfo mi = GDScriptUtilityFunctions::get_function_info(call->function_name);
r_type = _type_from_property(mi.return_val);
found = true;
break;
@@ -1304,10 +1338,7 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
native_type.kind = GDScriptParser::DataType::NATIVE;
native_type.native_type = native_type.script_type->get_instance_base_type();
if (!ClassDB::class_exists(native_type.native_type)) {
- native_type.native_type = String("_") + native_type.native_type;
- if (!ClassDB::class_exists(native_type.native_type)) {
- native_type.kind = GDScriptParser::DataType::UNRESOLVED;
- }
+ native_type.kind = GDScriptParser::DataType::UNRESOLVED;
}
}
}
@@ -1340,12 +1371,12 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
r_type = _type_from_variant(GDScriptLanguage::get_singleton()->get_named_globals_map()[which]);
found = true;
} else {
- Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
- String name = E->key();
+ for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
+ String name = E.key();
if (name == which) {
- String script = E->value().path;
+ String script = E.value().path;
if (!script.begins_with("res://")) {
script = "res://" + script;
@@ -1520,7 +1551,8 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
found = _guess_identifier_type_from_base(c, base, id, r_type);
} else if (!found && index.type.kind == GDScriptParser::DataType::BUILTIN) {
Callable::CallError err;
- Variant base_val = Variant::construct(base.type.builtin_type, nullptr, 0, err);
+ Variant base_val;
+ Variant::construct(base.type.builtin_type, base_val, nullptr, 0, err);
bool valid = false;
Variant res = base_val.get(index.value, &valid);
if (valid) {
@@ -1557,9 +1589,14 @@ static bool _guess_expression_type(GDScriptParser::CompletionContext &p_context,
Callable::CallError ce;
bool v1_use_value = p1.value.get_type() != Variant::NIL && p1.value.get_type() != Variant::OBJECT;
- Variant v1 = (v1_use_value) ? p1.value : Variant::construct(p1.type.builtin_type, nullptr, 0, ce);
+ Variant d1;
+ Variant::construct(p1.type.builtin_type, d1, nullptr, 0, ce);
+ Variant d2;
+ Variant::construct(p2.type.builtin_type, d2, nullptr, 0, ce);
+
+ Variant v1 = (v1_use_value) ? p1.value : d1;
bool v2_use_value = p2.value.get_type() != Variant::NIL && p2.value.get_type() != Variant::OBJECT;
- Variant v2 = (v2_use_value) ? p2.value : Variant::construct(p2.type.builtin_type, nullptr, 0, ce);
+ Variant v2 = (v2_use_value) ? p2.value : d2;
// avoid potential invalid ops
if ((op->variant_op == Variant::OP_DIVIDE || op->variant_op == Variant::OP_MODULE) && v2.get_type() == Variant::INT) {
v2 = 1;
@@ -1708,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()) {
@@ -1729,20 +1766,19 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
return true;
}
}
- base_type = base_type.class_type->base_type;
}
+ base_type = base_type.class_type->base_type;
break;
case GDScriptParser::DataType::NATIVE: {
if (id_type.is_set() && !id_type.is_variant()) {
base_type = GDScriptParser::DataType();
break;
}
- StringName real_native = _get_real_class_name(base_type.native_type);
MethodInfo info;
- if (ClassDB::get_method_info(real_native, p_context.current_function->identifier->name, &info)) {
- for (const List<PropertyInfo>::Element *E = info.arguments.front(); E; E = E->next()) {
- if (E->get().name == p_identifier) {
- r_type = _type_from_property(E->get());
+ if (ClassDB::get_method_info(base_type.native_type, p_context.current_function->identifier->name, &info)) {
+ for (const PropertyInfo &E : info.arguments) {
+ if (E.name == p_identifier) {
+ r_type = _type_from_property(E);
return true;
}
}
@@ -1808,13 +1844,12 @@ static bool _guess_identifier_type(GDScriptParser::CompletionContext &p_context,
}
// Check ClassDB.
- StringName class_name = _get_real_class_name(p_identifier);
- if (ClassDB::class_exists(class_name) && ClassDB::is_class_exposed(class_name)) {
+ if (ClassDB::class_exists(p_identifier) && ClassDB::is_class_exposed(p_identifier)) {
r_type.type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT;
r_type.type.kind = GDScriptParser::DataType::NATIVE;
r_type.type.native_type = p_identifier;
r_type.type.is_constant = true;
- r_type.type.is_meta_type = !Engine::get_singleton()->has_singleton(class_name);
+ r_type.type.is_meta_type = !Engine::get_singleton()->has_singleton(p_identifier);
r_type.value = Variant();
}
@@ -1904,8 +1939,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
if (!is_static) {
List<PropertyInfo> members;
scr->get_script_property_list(&members);
- for (const List<PropertyInfo>::Element *E = members.front(); E; E = E->next()) {
- const PropertyInfo &prop = E->get();
+ for (const PropertyInfo &prop : members) {
if (prop.name == p_identifier) {
r_type = _type_from_property(prop);
return true;
@@ -1924,7 +1958,7 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
}
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName class_name = _get_real_class_name(base_type.native_type);
+ StringName class_name = base_type.native_type;
if (!ClassDB::class_exists(class_name)) {
return false;
}
@@ -1949,7 +1983,8 @@ static bool _guess_identifier_type_from_base(GDScriptParser::CompletionContext &
} break;
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
- Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant tmp;
+ Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return false;
@@ -2067,8 +2102,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
if (scr.is_valid()) {
List<MethodInfo> methods;
scr->get_script_method_list(&methods);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- MethodInfo &mi = E->get();
+ for (const MethodInfo &mi : methods) {
if (mi.name == p_method) {
r_type = _type_from_property(mi.return_val);
return true;
@@ -2086,11 +2120,10 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
}
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName native = _get_real_class_name(base_type.native_type);
- if (!ClassDB::class_exists(native)) {
+ if (!ClassDB::class_exists(base_type.native_type)) {
return false;
}
- MethodBind *mb = ClassDB::get_method(native, p_method);
+ MethodBind *mb = ClassDB::get_method(base_type.native_type, p_method);
if (mb) {
r_type = _type_from_property(mb->get_return_info());
return true;
@@ -2099,7 +2132,8 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
} break;
case GDScriptParser::DataType::BUILTIN: {
Callable::CallError err;
- Variant tmp = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant tmp;
+ Variant::construct(base_type.builtin_type, tmp, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return false;
}
@@ -2107,8 +2141,7 @@ static bool _guess_method_return_type_from_base(GDScriptParser::CompletionContex
List<MethodInfo> methods;
tmp.get_method_list(&methods);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- MethodInfo &mi = E->get();
+ for (const MethodInfo &mi : methods) {
if (mi.name == p_method) {
r_type = _type_from_property(mi.return_val);
return true;
@@ -2136,9 +2169,9 @@ static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_co
r_result.insert(option.display, option);
}
} else {
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
- if (GlobalConstants::get_global_constant_enum(i) == current_enum) {
- ScriptCodeCompletionOption option(GlobalConstants::get_global_constant_name(i), ScriptCodeCompletionOption::KIND_ENUM);
+ for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
+ if (CoreConstants::get_global_constant_enum(i) == current_enum) {
+ ScriptCodeCompletionOption option(CoreConstants::get_global_constant_name(i), ScriptCodeCompletionOption::KIND_ENUM);
r_result.insert(option.display, option);
}
}
@@ -2153,8 +2186,8 @@ static void _find_enumeration_candidates(GDScriptParser::CompletionContext &p_co
List<StringName> enum_constants;
ClassDB::get_enum_constants(class_name, enum_name, &enum_constants);
- for (List<StringName>::Element *E = enum_constants.front(); E; E = E->next()) {
- String candidate = class_name + "." + E->get();
+ for (const StringName &E : enum_constants) {
+ String candidate = class_name + "." + E;
ScriptCodeCompletionOption option(candidate, ScriptCodeCompletionOption::KIND_ENUM);
r_result.insert(option.display, option);
}
@@ -2165,7 +2198,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
Variant base = p_base.value;
GDScriptParser::DataType base_type = p_base.type;
- const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
+ const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
while (base_type.is_set() && !base_type.is_variant()) {
switch (base_type.kind) {
@@ -2182,7 +2215,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
base_type = base_type.class_type->base_type;
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName class_name = _get_real_class_name(base_type.native_type);
+ StringName class_name = base_type.native_type;
if (!ClassDB::class_exists(class_name)) {
base_type.kind = GDScriptParser::DataType::UNRESOLVED;
break;
@@ -2198,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 (List<String>::Element *F = options.front(); F; F = F->next()) {
- ScriptCodeCompletionOption option(F->get(), 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);
}
}
@@ -2213,22 +2249,21 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
r_arghint = _make_arguments_hint(info, p_argidx);
- return;
}
- if (ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node") && p_argidx == 0) {
+ if (p_argidx == 0 && ClassDB::is_parent_class(class_name, "Node") && (p_method == "get_node" || p_method == "has_node")) {
// Get autoloads
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
+ for (const PropertyInfo &E : props) {
+ String s = E.name;
if (!s.begins_with("autoload/")) {
continue;
}
String name = s.get_slice("/", 1);
ScriptCodeCompletionOption option("/root/" + name, ScriptCodeCompletionOption::KIND_NODE_PATH);
- option.insert_text = quote_style + option.display + quote_style;
+ option.insert_text = option.display.quote(quote_style);
r_result.insert(option.display, option);
}
}
@@ -2237,14 +2272,14 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
// Get input actions
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String s = E->get().name;
+ for (const PropertyInfo &E : props) {
+ String s = E.name;
if (!s.begins_with("input/")) {
continue;
}
String name = s.get_slice("/", 1);
ScriptCodeCompletionOption option(name, ScriptCodeCompletionOption::KIND_CONSTANT);
- option.insert_text = quote_style + option.display + quote_style;
+ option.insert_text = option.display.quote(quote_style);
r_result.insert(option.display, option);
}
}
@@ -2254,7 +2289,7 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
case GDScriptParser::DataType::BUILTIN: {
if (base.get_type() == Variant::NIL) {
Callable::CallError err;
- base = Variant::construct(base_type.builtin_type, nullptr, 0, err);
+ Variant::construct(base_type.builtin_type, base, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
return;
}
@@ -2262,9 +2297,9 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
List<MethodInfo> methods;
base.get_method_list(&methods);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name == p_method) {
- r_arghint = _make_arguments_hint(E->get(), p_argidx);
+ for (const MethodInfo &E : methods) {
+ if (E.name == p_method) {
+ r_arghint = _make_arguments_hint(E, p_argidx);
return;
}
}
@@ -2279,8 +2314,6 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
}
static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, const GDScriptParser::Node *p_call, int p_argidx, Map<String, ScriptCodeCompletionOption> &r_result, bool &r_forced, String &r_arghint) {
- const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
-
if (p_call->type == GDScriptParser::Node::PRELOAD) {
if (p_argidx == 0 && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
_get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
@@ -2297,17 +2330,16 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
GDScriptParser::DataType base_type;
bool _static = false;
const GDScriptParser::CallNode *call = static_cast<const GDScriptParser::CallNode *>(p_call);
- GDScriptParser::Node::Type callee_type = GDScriptParser::Node::NONE;
+ GDScriptParser::Node::Type callee_type = call->get_callee_type();
GDScriptCompletionIdentifier connect_base;
- if (GDScriptParser::get_builtin_function(call->function_name) < GDScriptFunctions::FUNC_MAX) {
- MethodInfo info = GDScriptFunctions::get_info(GDScriptParser::get_builtin_function(call->function_name));
-
- if ((info.name == "load" || info.name == "preload") && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
- _get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), r_result);
- }
-
+ 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;
} else if (GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
@@ -2316,14 +2348,14 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
Variant::get_constructor_list(GDScriptParser::get_builtin_type(call->function_name), &constructors);
int i = 0;
- for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) {
- if (p_argidx >= E->get().arguments.size()) {
+ for (const MethodInfo &E : constructors) {
+ if (p_argidx >= E.arguments.size()) {
continue;
}
if (i > 0) {
r_arghint += "\n";
}
- r_arghint += _make_arguments_hint(E->get(), p_argidx);
+ r_arghint += _make_arguments_hint(E, p_argidx);
i++;
}
return;
@@ -2360,8 +2392,8 @@ static void _find_call_arguments(GDScriptParser::CompletionContext &p_context, c
r_forced = r_result.size() > 0;
}
-Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) {
- const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
+::Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path, Object *p_owner, List<ScriptCodeCompletionOption> *r_options, bool &r_forced, String &r_call_hint) {
+ const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
GDScriptParser parser;
GDScriptAnalyzer analyzer(&parser);
@@ -2382,9 +2414,9 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
case GDScriptParser::COMPLETION_ANNOTATION: {
List<MethodInfo> annotations;
parser.get_annotation_list(&annotations);
- for (const List<MethodInfo>::Element *E = annotations.front(); E != nullptr; E = E->next()) {
- ScriptCodeCompletionOption option(E->get().name.substr(1), ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
- if (E->get().arguments.size() > 0) {
+ for (const MethodInfo &E : annotations) {
+ ScriptCodeCompletionOption option(E.name.substr(1), ScriptCodeCompletionOption::KIND_PLAIN_TEXT);
+ if (E.arguments.size() > 0) {
option.insert_text += "(";
}
options.insert(option.display, option);
@@ -2402,8 +2434,13 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
case GDScriptParser::COMPLETION_BUILT_IN_TYPE_CONSTANT: {
List<StringName> constants;
Variant::get_constants_for_type(completion_context.builtin_type, &constants);
- for (const List<StringName>::Element *E = constants.front(); E != nullptr; E = E->next()) {
- ScriptCodeCompletionOption option(E->get(), ScriptCodeCompletionOption::KIND_CONSTANT);
+ for (const StringName &E : constants) {
+ ScriptCodeCompletionOption option(E, ScriptCodeCompletionOption::KIND_CONSTANT);
+ bool valid = false;
+ Variant default_value = Variant::get_constant_value(completion_context.builtin_type, E, &valid);
+ if (valid) {
+ option.default_value = default_value;
+ }
options.insert(option.display, option);
}
} break;
@@ -2463,7 +2500,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
break;
}
- if (!type.enumeration.empty()) {
+ if (!type.enumeration.is_empty()) {
_find_enumeration_candidates(completion_context, type.enumeration, options);
r_forced = options.size() > 0;
} else {
@@ -2568,7 +2605,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
break;
}
- StringName class_name = _get_real_class_name(native_type.native_type);
+ StringName class_name = native_type.native_type;
if (!ClassDB::class_exists(class_name)) {
break;
}
@@ -2577,8 +2614,7 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
List<MethodInfo> virtual_methods;
ClassDB::get_virtual_methods(class_name, &virtual_methods);
- for (List<MethodInfo>::Element *E = virtual_methods.front(); E; E = E->next()) {
- MethodInfo &mi = E->get();
+ for (const MethodInfo &mi : virtual_methods) {
String method_hint = mi.name;
if (method_hint.find(":") != -1) {
method_hint = method_hint.get_slice(":", 0);
@@ -2623,31 +2659,34 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
}
} 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 (List<String>::Element *E = opts.front(); E; E = E->next()) {
- String opt = E->get().strip_edges();
+ 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.
- Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
- String name = E->key();
- ScriptCodeCompletionOption option(quote_style + "/root/" + name + quote_style, ScriptCodeCompletionOption::KIND_NODE_PATH);
+ for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
+ String path = "/root/" + E.key();
+ ScriptCodeCompletionOption option(path.quote(quote_style), ScriptCodeCompletionOption::KIND_NODE_PATH);
options.insert(option.display, option);
}
}
@@ -2660,8 +2699,8 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
} 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;
@@ -2680,10 +2719,10 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_path
String GDScriptLanguage::_get_indentation() const {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", false);
+ bool use_space_indentation = EDITOR_DEF("text_editor/behavior/indent/type", false);
if (use_space_indentation) {
- int indent_size = EDITOR_DEF("text_editor/indent/size", 4);
+ int indent_size = EDITOR_DEF("text_editor/behavior/indent/size", 4);
String space_indent = "";
for (int i = 0; i < indent_size; i++) {
@@ -2770,6 +2809,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
if (base_type.class_type->has_member(p_symbol)) {
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;
return OK;
}
base_type = base_type.class_type->base_type;
@@ -2797,7 +2837,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
}
} break;
case GDScriptParser::DataType::NATIVE: {
- StringName class_name = _get_real_class_name(base_type.native_type);
+ StringName class_name = base_type.native_type;
if (!ClassDB::class_exists(class_name)) {
base_type.kind = GDScriptParser::DataType::UNRESOLVED;
break;
@@ -2812,8 +2852,8 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
List<MethodInfo> virtual_methods;
ClassDB::get_virtual_methods(class_name, &virtual_methods, true);
- for (List<MethodInfo>::Element *E = virtual_methods.front(); E; E = E->next()) {
- if (E->get().name == p_symbol) {
+ for (const MethodInfo &E : virtual_methods) {
+ if (E.name == p_symbol) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
r_result.class_name = base_type.native_type;
r_result.class_member = p_symbol;
@@ -2831,8 +2871,8 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
List<String> constants;
ClassDB::get_integer_constant_list(class_name, &constants, true);
- for (List<String>::Element *E = constants.front(); E; E = E->next()) {
- if (E->get() == p_symbol) {
+ for (const String &E : constants) {
+ if (E == p_symbol) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
r_result.class_name = base_type.native_type;
r_result.class_member = p_symbol;
@@ -2849,11 +2889,7 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
StringName parent = ClassDB::get_parent_class(class_name);
if (parent != StringName()) {
- if (String(parent).begins_with("_")) {
- base_type.native_type = String(parent).right(1);
- } else {
- base_type.native_type = parent;
- }
+ base_type.native_type = parent;
} else {
base_type.kind = GDScriptParser::DataType::UNRESOLVED;
}
@@ -2871,11 +2907,11 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
Variant v;
REF v_ref;
if (base_type.builtin_type == Variant::OBJECT) {
- v_ref.instance();
+ v_ref.instantiate();
v = v_ref;
} else {
Callable::CallError err;
- v = Variant::construct(base_type.builtin_type, NULL, 0, err);
+ Variant::construct(base_type.builtin_type, v, nullptr, 0, err);
if (err.error != Callable::CallError::CALL_OK) {
break;
}
@@ -2906,19 +2942,12 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
return ERR_CANT_RESOLVE;
}
-Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) {
- //before parsing, try the usual stuff
+::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) {
+ // Before parsing, try the usual stuff
if (ClassDB::class_exists(p_symbol)) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS;
r_result.class_name = p_symbol;
return OK;
- } else {
- String under_prefix = "_" + p_symbol;
- if (ClassDB::class_exists(under_prefix)) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS;
- r_result.class_name = p_symbol;
- return OK;
- }
}
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
@@ -2930,13 +2959,11 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
}
}
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- if (GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i)) == p_symbol) {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
- r_result.class_name = "@GDScript";
- r_result.class_member = p_symbol;
- return OK;
- }
+ if (GDScriptUtilityFunctions::function_exists(p_symbol)) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_METHOD;
+ r_result.class_name = "@GDScript";
+ r_result.class_member = p_symbol;
+ return OK;
}
if ("PI" == p_symbol || "TAU" == p_symbol || "INF" == p_symbol || "NAN" == p_symbol) {
@@ -2978,6 +3005,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
is_function = true;
[[fallthrough]];
}
+ case GDScriptParser::COMPLETION_CALL_ARGUMENTS:
case GDScriptParser::COMPLETION_IDENTIFIER: {
GDScriptParser::DataType base_type;
if (context.current_class) {
@@ -3010,9 +3038,9 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
if (!is_function) {
// Guess in autoloads as singletons.
if (ProjectSettings::get_singleton()->has_autoload(p_symbol)) {
- const ProjectSettings::AutoloadInfo &singleton = ProjectSettings::get_singleton()->get_autoload(p_symbol);
- if (singleton.is_singleton) {
- String script = singleton.path;
+ const ProjectSettings::AutoloadInfo &autoload = ProjectSettings::get_singleton()->get_autoload(p_symbol);
+ if (autoload.is_singleton) {
+ String script = autoload.path;
if (!script.ends_with(".gd")) {
// Not a script, try find the script anyway,
// may have some success.
@@ -3045,7 +3073,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
// proxy class remove the underscore.
if (r_result.class_name.begins_with("_")) {
- r_result.class_name = r_result.class_name.right(1);
+ r_result.class_name = r_result.class_name.substr(1);
}
return OK;
}
@@ -3055,7 +3083,7 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
// We cannot determine the exact nature of the identifier here
// Otherwise these codes would work
StringName enumName = ClassDB::get_integer_constant_enum("@GlobalScope", p_symbol, true);
- if (enumName != NULL) {
+ if (enumName != nullptr) {
r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
r_result.class_name = "@GlobalScope";
r_result.class_member = enumName;
@@ -3072,6 +3100,15 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
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 e59f99fc56..a3f0c7dfef 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,1565 +30,7 @@
#include "gdscript_function.h"
-#include "core/os/os.h"
#include "gdscript.h"
-#include "gdscript_functions.h"
-
-#ifdef DEBUG_ENABLED
-#include "core/string_builder.h"
-#endif
-
-Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const {
- int address = p_address & ADDR_MASK;
-
- //sequential table (jump table generated by compiler)
- switch ((p_address & ADDR_TYPE_MASK) >> ADDR_BITS) {
- case ADDR_TYPE_SELF: {
-#ifdef DEBUG_ENABLED
- if (unlikely(!p_instance)) {
- r_error = "Cannot access self without instance.";
- return nullptr;
- }
-#endif
- return &self;
- } break;
- case ADDR_TYPE_CLASS: {
- return &static_ref;
- } break;
- case ADDR_TYPE_MEMBER: {
-#ifdef DEBUG_ENABLED
- if (unlikely(!p_instance)) {
- r_error = "Cannot access member without instance.";
- return nullptr;
- }
-#endif
- //member indexing is O(1)
- return &p_instance->members.write[address];
- } break;
- case ADDR_TYPE_CLASS_CONSTANT: {
- //todo change to index!
- GDScript *s = p_script;
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
-#endif
- const StringName *sn = &_global_names_ptr[address];
-
- while (s) {
- GDScript *o = s;
- while (o) {
- Map<StringName, Variant>::Element *E = o->constants.find(*sn);
- if (E) {
- return &E->get();
- }
- o = o->_owner;
- }
- s = s->_base;
- }
-
- ERR_FAIL_V_MSG(nullptr, "GDScriptCompiler bug.");
- } break;
- case ADDR_TYPE_LOCAL_CONSTANT: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _constant_count, nullptr);
-#endif
- return &_constants_ptr[address];
- } break;
- case ADDR_TYPE_STACK:
- case ADDR_TYPE_STACK_VARIABLE: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _stack_size, nullptr);
-#endif
- return &p_stack[address];
- } break;
- case ADDR_TYPE_GLOBAL: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, GDScriptLanguage::get_singleton()->get_global_array_size(), nullptr);
-#endif
- return &GDScriptLanguage::get_singleton()->get_global_array()[address];
- } break;
-#ifdef TOOLS_ENABLED
- case ADDR_TYPE_NAMED_GLOBAL: {
-#ifdef DEBUG_ENABLED
- ERR_FAIL_INDEX_V(address, _global_names_count, nullptr);
-#endif
- StringName id = _global_names_ptr[address];
-
- if (GDScriptLanguage::get_singleton()->get_named_globals_map().has(id)) {
- return (Variant *)&GDScriptLanguage::get_singleton()->get_named_globals_map()[id];
- } else {
- r_error = "Autoload singleton '" + String(id) + "' has been removed.";
- return nullptr;
- }
- } break;
-#endif
- case ADDR_TYPE_NIL: {
- return &nil;
- } break;
- }
-
- ERR_FAIL_V_MSG(nullptr, "Bad code! (unknown addressing mode).");
- return nullptr;
-}
-
-#ifdef DEBUG_ENABLED
-static String _get_var_type(const Variant *p_var) {
- String basestr;
-
- if (p_var->get_type() == Variant::OBJECT) {
- bool was_freed;
- Object *bobj = p_var->get_validated_object_with_check(was_freed);
- if (!bobj) {
- if (was_freed) {
- basestr = "null instance";
- } else {
- basestr = "previously freed";
- }
- } else {
- if (bobj->get_script_instance()) {
- basestr = bobj->get_class() + " (" + bobj->get_script_instance()->get_script()->get_path().get_file() + ")";
- } else {
- basestr = bobj->get_class();
- }
- }
-
- } else {
- basestr = Variant::get_type_name(p_var->get_type());
- }
-
- return basestr;
-}
-#endif // DEBUG_ENABLED
-
-String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const {
- String err_text;
-
- if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
- int errorarg = p_err.argument;
- // Handle the Object to Object case separately as we don't have further class details.
-#ifdef DEBUG_ENABLED
- if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
- err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class.";
- } else
-#endif // DEBUG_ENABLED
- {
- err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
- }
- } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
- err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
- err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
- err_text = "Invalid call. Nonexistent " + p_where + ".";
- } else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
- err_text = "Attempt to call " + p_where + " on a null instance.";
- } else {
- err_text = "Bug, call error: #" + itos(p_err.error);
- }
-
- return err_text;
-}
-
-#if defined(__GNUC__)
-#define OPCODES_TABLE \
- static const void *switch_table_ops[] = { \
- &&OPCODE_OPERATOR, \
- &&OPCODE_EXTENDS_TEST, \
- &&OPCODE_IS_BUILTIN, \
- &&OPCODE_SET, \
- &&OPCODE_GET, \
- &&OPCODE_SET_NAMED, \
- &&OPCODE_GET_NAMED, \
- &&OPCODE_SET_MEMBER, \
- &&OPCODE_GET_MEMBER, \
- &&OPCODE_ASSIGN, \
- &&OPCODE_ASSIGN_TRUE, \
- &&OPCODE_ASSIGN_FALSE, \
- &&OPCODE_ASSIGN_TYPED_BUILTIN, \
- &&OPCODE_ASSIGN_TYPED_NATIVE, \
- &&OPCODE_ASSIGN_TYPED_SCRIPT, \
- &&OPCODE_CAST_TO_BUILTIN, \
- &&OPCODE_CAST_TO_NATIVE, \
- &&OPCODE_CAST_TO_SCRIPT, \
- &&OPCODE_CONSTRUCT, \
- &&OPCODE_CONSTRUCT_ARRAY, \
- &&OPCODE_CONSTRUCT_DICTIONARY, \
- &&OPCODE_CALL, \
- &&OPCODE_CALL_RETURN, \
- &&OPCODE_CALL_ASYNC, \
- &&OPCODE_CALL_BUILT_IN, \
- &&OPCODE_CALL_SELF_BASE, \
- &&OPCODE_AWAIT, \
- &&OPCODE_AWAIT_RESUME, \
- &&OPCODE_JUMP, \
- &&OPCODE_JUMP_IF, \
- &&OPCODE_JUMP_IF_NOT, \
- &&OPCODE_JUMP_TO_DEF_ARGUMENT, \
- &&OPCODE_RETURN, \
- &&OPCODE_ITERATE_BEGIN, \
- &&OPCODE_ITERATE, \
- &&OPCODE_ASSERT, \
- &&OPCODE_BREAKPOINT, \
- &&OPCODE_LINE, \
- &&OPCODE_END \
- }; \
- static_assert((sizeof(switch_table_ops) / sizeof(switch_table_ops[0]) == (OPCODE_END + 1)), "Opcodes in jump table aren't the same as opcodes in enum.");
-
-#define OPCODE(m_op) \
- m_op:
-#define OPCODE_WHILE(m_test)
-#define OPCODES_END \
- OPSEXIT:
-#define OPCODES_OUT \
- OPSOUT:
-#define DISPATCH_OPCODE goto *switch_table_ops[_code_ptr[ip]]
-#define OPCODE_SWITCH(m_test) DISPATCH_OPCODE;
-#define OPCODE_BREAK goto OPSEXIT
-#define OPCODE_OUT goto OPSOUT
-#else
-#define OPCODES_TABLE
-#define OPCODE(m_op) case m_op:
-#define OPCODE_WHILE(m_test) while (m_test)
-#define OPCODES_END
-#define OPCODES_OUT
-#define DISPATCH_OPCODE continue
-#define OPCODE_SWITCH(m_test) switch (m_test)
-#define OPCODE_BREAK break
-#define OPCODE_OUT break
-#endif
-
-Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state) {
- OPCODES_TABLE;
-
- if (!_code_ptr) {
- return Variant();
- }
-
- r_err.error = Callable::CallError::CALL_OK;
-
- Variant self;
- Variant static_ref;
- Variant retvalue;
- Variant *stack = nullptr;
- Variant **call_args;
- int defarg = 0;
-
-#ifdef DEBUG_ENABLED
-
- //GDScriptLanguage::get_singleton()->calls++;
-
-#endif
-
- uint32_t alloca_size = 0;
- GDScript *script;
- int ip = 0;
- int line = _initial_line;
-
- if (p_state) {
- //use existing (supplied) state (awaited)
- stack = (Variant *)p_state->stack.ptr();
- call_args = (Variant **)&p_state->stack.ptr()[sizeof(Variant) * p_state->stack_size]; //ptr() to avoid bounds check
- line = p_state->line;
- ip = p_state->ip;
- alloca_size = p_state->stack.size();
- script = p_state->script;
- p_instance = p_state->instance;
- defarg = p_state->defarg;
- self = p_state->self;
-
- } else {
- if (p_argcount != _argument_count) {
- if (p_argcount > _argument_count) {
- r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_err.argument = _argument_count;
-
- return Variant();
- } else if (p_argcount < _argument_count - _default_arg_count) {
- r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_err.argument = _argument_count - _default_arg_count;
- return Variant();
- } else {
- defarg = _argument_count - p_argcount;
- }
- }
-
- alloca_size = sizeof(Variant *) * _call_size + sizeof(Variant) * _stack_size;
-
- if (alloca_size) {
- uint8_t *aptr = (uint8_t *)alloca(alloca_size);
-
- if (_stack_size) {
- stack = (Variant *)aptr;
- for (int i = 0; i < p_argcount; i++) {
- if (!argument_types[i].has_type) {
- memnew_placement(&stack[i], Variant(*p_args[i]));
- continue;
- }
-
- if (!argument_types[i].is_type(*p_args[i], true)) {
- r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_err.argument = i;
- r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
- return Variant();
- }
- if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
- Variant arg = Variant::construct(argument_types[i].builtin_type, &p_args[i], 1, r_err);
- memnew_placement(&stack[i], Variant(arg));
- } else {
- memnew_placement(&stack[i], Variant(*p_args[i]));
- }
- }
- for (int i = p_argcount; i < _stack_size; i++) {
- memnew_placement(&stack[i], Variant);
- }
- } else {
- stack = nullptr;
- }
-
- if (_call_size) {
- call_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
- } else {
- call_args = nullptr;
- }
-
- } else {
- stack = nullptr;
- call_args = nullptr;
- }
-
- if (p_instance) {
- if (p_instance->base_ref && static_cast<Reference *>(p_instance->owner)->is_referenced()) {
- self = REF(static_cast<Reference *>(p_instance->owner));
- } else {
- self = p_instance->owner;
- }
- script = p_instance->script.ptr();
- } else {
- script = _script;
- }
- }
-
- static_ref = script;
-
- String err_text;
-
-#ifdef DEBUG_ENABLED
-
- if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line);
- }
-
-#define GD_ERR_BREAK(m_cond) \
- { \
- if (unlikely(m_cond)) { \
- _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \
- OPCODE_BREAK; \
- } \
- }
-
-#define CHECK_SPACE(m_space) \
- GD_ERR_BREAK((ip + m_space) > _code_size)
-
-#define GET_VARIANT_PTR(m_v, m_code_ofs) \
- Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text); \
- if (unlikely(!m_v)) \
- OPCODE_BREAK;
-
-#else
-#define GD_ERR_BREAK(m_cond)
-#define CHECK_SPACE(m_space)
-#define GET_VARIANT_PTR(m_v, m_code_ofs) \
- Variant *m_v; \
- m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, script, self, static_ref, stack, err_text);
-
-#endif
-
-#ifdef DEBUG_ENABLED
-
- uint64_t function_start_time = 0;
- uint64_t function_call_time = 0;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_start_time = OS::get_singleton()->get_ticks_usec();
- function_call_time = 0;
- profile.call_count++;
- profile.frame_call_count++;
- }
- bool exit_ok = false;
- bool awaited = false;
-#endif
-
-#ifdef DEBUG_ENABLED
- OPCODE_WHILE(ip < _code_size) {
- int last_opcode = _code_ptr[ip];
-#else
- OPCODE_WHILE(true) {
-#endif
-
- OPCODE_SWITCH(_code_ptr[ip]) {
- OPCODE(OPCODE_OPERATOR) {
- CHECK_SPACE(5);
-
- bool valid;
- Variant::Operator op = (Variant::Operator)_code_ptr[ip + 1];
- GD_ERR_BREAK(op >= Variant::OP_MAX);
-
- GET_VARIANT_PTR(a, 2);
- GET_VARIANT_PTR(b, 3);
- GET_VARIANT_PTR(dst, 4);
-
-#ifdef DEBUG_ENABLED
-
- Variant ret;
- Variant::evaluate(op, *a, *b, ret, valid);
-#else
- Variant::evaluate(op, *a, *b, *dst, valid);
-#endif
-#ifdef DEBUG_ENABLED
- if (!valid) {
- if (ret.get_type() == Variant::STRING) {
- //return a string when invalid with the error
- err_text = ret;
- err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
- } else {
- err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
- }
- OPCODE_BREAK;
- }
- *dst = ret;
-#endif
- ip += 5;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_EXTENDS_TEST) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(a, 1);
- GET_VARIANT_PTR(b, 2);
- GET_VARIANT_PTR(dst, 3);
-
-#ifdef DEBUG_ENABLED
- if (b->get_type() != Variant::OBJECT || b->operator Object *() == nullptr) {
- err_text = "Right operand of 'is' is not a class.";
- OPCODE_BREAK;
- }
-#endif
-
- bool extends_ok = false;
- if (a->get_type() == Variant::OBJECT && a->operator Object *() != nullptr) {
-#ifdef DEBUG_ENABLED
- bool was_freed;
- Object *obj_A = a->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Left operand of 'is' is a previously freed instance.";
- OPCODE_BREAK;
- }
-
- Object *obj_B = b->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Right operand of 'is' is a previously freed instance.";
- OPCODE_BREAK;
- }
-#else
-
- Object *obj_A = *a;
- Object *obj_B = *b;
-#endif // DEBUG_ENABLED
-
- GDScript *scr_B = Object::cast_to<GDScript>(obj_B);
-
- if (scr_B) {
- //if B is a script, the only valid condition is that A has an instance which inherits from the script
- //in other situation, this shoul return false.
-
- if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) {
- GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr());
- //bool found=false;
- while (cmp) {
- if (cmp == scr_B) {
- //inherits from script, all ok
- extends_ok = true;
- break;
- }
-
- cmp = cmp->_base;
- }
- }
-
- } else {
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B);
-
-#ifdef DEBUG_ENABLED
- if (!nc) {
- err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "').";
- OPCODE_BREAK;
- }
-#endif
- extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name());
- }
- }
-
- *dst = extends_ok;
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_IS_BUILTIN) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(value, 1);
- Variant::Type var_type = (Variant::Type)_code_ptr[ip + 2];
- GET_VARIANT_PTR(dst, 3);
-
- GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
-
- *dst = value->get_type() == var_type;
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_SET) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(dst, 1);
- GET_VARIANT_PTR(index, 2);
- GET_VARIANT_PTR(value, 3);
-
- bool valid;
- dst->set(*index, *value, &valid);
-
-#ifdef DEBUG_ENABLED
- if (!valid) {
- String v = index->operator String();
- if (v != "") {
- v = "'" + v + "'";
- } else {
- v = "of type '" + _get_var_type(index) + "'";
- }
- err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'";
- OPCODE_BREAK;
- }
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_GET) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(src, 1);
- GET_VARIANT_PTR(index, 2);
- GET_VARIANT_PTR(dst, 3);
-
- bool valid;
-#ifdef DEBUG_ENABLED
- //allow better error message in cases where src and dst are the same stack position
- Variant ret = src->get(*index, &valid);
-#else
- *dst = src->get(*index, &valid);
-
-#endif
-#ifdef DEBUG_ENABLED
- if (!valid) {
- String v = index->operator String();
- if (v != "") {
- v = "'" + v + "'";
- } else {
- v = "of type '" + _get_var_type(index) + "'";
- }
- err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "').";
- OPCODE_BREAK;
- }
- *dst = ret;
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_SET_NAMED) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(dst, 1);
- GET_VARIANT_PTR(value, 3);
-
- int indexname = _code_ptr[ip + 2];
-
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
-
- bool valid;
- dst->set_named(*index, *value, &valid);
-
-#ifdef DEBUG_ENABLED
- if (!valid) {
- String err_type;
- err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'.";
- OPCODE_BREAK;
- }
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_GET_NAMED) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(src, 1);
- GET_VARIANT_PTR(dst, 3);
-
- int indexname = _code_ptr[ip + 2];
-
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
-
- bool valid;
-#ifdef DEBUG_ENABLED
- //allow better error message in cases where src and dst are the same stack position
- Variant ret = src->get_named(*index, &valid);
-
-#else
- *dst = src->get_named(*index, &valid);
-#endif
-#ifdef DEBUG_ENABLED
- if (!valid) {
- if (src->has_method(*index)) {
- err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' or funcref(obj, \"" + index->operator String() + "\") ?";
- } else {
- err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
- }
- OPCODE_BREAK;
- }
- *dst = ret;
-#endif
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_SET_MEMBER) {
- CHECK_SPACE(3);
- int indexname = _code_ptr[ip + 1];
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
- GET_VARIANT_PTR(src, 2);
-
- bool valid;
-#ifndef DEBUG_ENABLED
- ClassDB::set_property(p_instance->owner, *index, *src, &valid);
-#else
- bool ok = ClassDB::set_property(p_instance->owner, *index, *src, &valid);
- if (!ok) {
- err_text = "Internal error setting property: " + String(*index);
- OPCODE_BREAK;
- } else if (!valid) {
- err_text = "Error setting property '" + String(*index) + "' with value of type " + Variant::get_type_name(src->get_type()) + ".";
- OPCODE_BREAK;
- }
-#endif
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_GET_MEMBER) {
- CHECK_SPACE(3);
- int indexname = _code_ptr[ip + 1];
- GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
- const StringName *index = &_global_names_ptr[indexname];
- GET_VARIANT_PTR(dst, 2);
-
-#ifndef DEBUG_ENABLED
- ClassDB::get_property(p_instance->owner, *index, *dst);
-#else
- bool ok = ClassDB::get_property(p_instance->owner, *index, *dst);
- if (!ok) {
- err_text = "Internal error getting property: " + String(*index);
- OPCODE_BREAK;
- }
-#endif
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN) {
- CHECK_SPACE(3);
- GET_VARIANT_PTR(dst, 1);
- GET_VARIANT_PTR(src, 2);
-
- *dst = *src;
-
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TRUE) {
- CHECK_SPACE(2);
- GET_VARIANT_PTR(dst, 1);
-
- *dst = true;
-
- ip += 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_FALSE) {
- CHECK_SPACE(2);
- GET_VARIANT_PTR(dst, 1);
-
- *dst = false;
-
- ip += 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(dst, 2);
- GET_VARIANT_PTR(src, 3);
-
- Variant::Type var_type = (Variant::Type)_code_ptr[ip + 1];
- GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
-
- if (src->get_type() != var_type) {
-#ifdef DEBUG_ENABLED
- if (Variant::can_convert_strict(src->get_type(), var_type)) {
-#endif // DEBUG_ENABLED
- Callable::CallError ce;
- *dst = Variant::construct(var_type, const_cast<const Variant **>(&src), 1, ce);
- } 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) + "'.";
- OPCODE_BREAK;
- }
- } else {
-#endif // DEBUG_ENABLED
- *dst = *src;
- }
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(dst, 2);
- GET_VARIANT_PTR(src, 3);
-
-#ifdef DEBUG_ENABLED
- GET_VARIANT_PTR(type, 1);
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
- 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() + "'.";
- 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() + "'.";
- OPCODE_BREAK;
- }
-#endif // DEBUG_ENABLED
- *dst = *src;
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(dst, 2);
- GET_VARIANT_PTR(src, 3);
-
-#ifdef DEBUG_ENABLED
- GET_VARIANT_PTR(type, 1);
- Script *base_type = Object::cast_to<Script>(type->operator Object *());
-
- GD_ERR_BREAK(!base_type);
-
- 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;
- }
-
- if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
- 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() + "'.";
- OPCODE_BREAK;
- }
-
- Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
- bool valid = false;
-
- while (src_type) {
- if (src_type == base_type) {
- valid = true;
- break;
- }
- src_type = src_type->get_base_script().ptr();
- }
-
- 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() + "'.";
- OPCODE_BREAK;
- }
- }
-#endif // DEBUG_ENABLED
-
- *dst = *src;
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CAST_TO_BUILTIN) {
- CHECK_SPACE(4);
- Variant::Type to_type = (Variant::Type)_code_ptr[ip + 1];
- GET_VARIANT_PTR(src, 2);
- GET_VARIANT_PTR(dst, 3);
-
- GD_ERR_BREAK(to_type < 0 || to_type >= Variant::VARIANT_MAX);
-
- Callable::CallError err;
- *dst = Variant::construct(to_type, (const Variant **)&src, 1, err);
-
-#ifdef DEBUG_ENABLED
- if (err.error != Callable::CallError::CALL_OK) {
- err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
- OPCODE_BREAK;
- }
-#endif
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CAST_TO_NATIVE) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(to_type, 1);
- GET_VARIANT_PTR(src, 2);
- GET_VARIANT_PTR(dst, 3);
-
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
- GD_ERR_BREAK(!nc);
-
-#ifdef DEBUG_ENABLED
- 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;
- }
-#endif
- Object *src_obj = src->operator Object *();
-
- if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
- *dst = Variant(); // invalid cast, assign NULL
- } else {
- *dst = *src;
- }
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CAST_TO_SCRIPT) {
- CHECK_SPACE(4);
- GET_VARIANT_PTR(to_type, 1);
- GET_VARIANT_PTR(src, 2);
- GET_VARIANT_PTR(dst, 3);
-
- Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
-
- GD_ERR_BREAK(!base_type);
-
-#ifdef DEBUG_ENABLED
- 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;
- }
-#endif
-
- bool valid = false;
-
- if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
- ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
-
- if (scr_inst) {
- Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
-
- while (src_type) {
- if (src_type == base_type) {
- valid = true;
- break;
- }
- src_type = src_type->get_base_script().ptr();
- }
- }
- }
-
- if (valid) {
- *dst = *src; // Valid cast, copy the source object
- } else {
- *dst = Variant(); // invalid cast, assign NULL
- }
-
- ip += 4;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CONSTRUCT) {
- CHECK_SPACE(2);
- Variant::Type t = Variant::Type(_code_ptr[ip + 1]);
- int argc = _code_ptr[ip + 2];
- CHECK_SPACE(argc + 2);
- Variant **argptrs = call_args;
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, 3 + i);
- argptrs[i] = v;
- }
-
- GET_VARIANT_PTR(dst, 3 + argc);
- Callable::CallError err;
- *dst = Variant::construct(t, (const Variant **)argptrs, argc, err);
-
-#ifdef DEBUG_ENABLED
- if (err.error != Callable::CallError::CALL_OK) {
- err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs);
- OPCODE_BREAK;
- }
-#endif
-
- ip += 4 + argc;
- //construct a basic type
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CONSTRUCT_ARRAY) {
- CHECK_SPACE(1);
- int argc = _code_ptr[ip + 1];
- Array array; //arrays are always shared
- array.resize(argc);
- CHECK_SPACE(argc + 2);
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, 2 + i);
- array[i] = *v;
- }
-
- GET_VARIANT_PTR(dst, 2 + argc);
-
- *dst = array;
-
- ip += 3 + argc;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CONSTRUCT_DICTIONARY) {
- CHECK_SPACE(1);
- int argc = _code_ptr[ip + 1];
- Dictionary dict; //arrays are always shared
-
- CHECK_SPACE(argc * 2 + 2);
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(k, 2 + i * 2 + 0);
- GET_VARIANT_PTR(v, 2 + i * 2 + 1);
- dict[*k] = *v;
- }
-
- GET_VARIANT_PTR(dst, 2 + argc * 2);
-
- *dst = dict;
-
- ip += 3 + argc * 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CALL_ASYNC)
- OPCODE(OPCODE_CALL_RETURN)
- OPCODE(OPCODE_CALL) {
- CHECK_SPACE(4);
- bool call_ret = _code_ptr[ip] != OPCODE_CALL;
-#ifdef DEBUG_ENABLED
- bool call_async = _code_ptr[ip] == OPCODE_CALL_ASYNC;
-#endif
-
- int argc = _code_ptr[ip + 1];
- GET_VARIANT_PTR(base, 2);
- int nameg = _code_ptr[ip + 3];
-
- GD_ERR_BREAK(nameg < 0 || nameg >= _global_names_count);
- const StringName *methodname = &_global_names_ptr[nameg];
-
- GD_ERR_BREAK(argc < 0);
- ip += 4;
- CHECK_SPACE(argc + 1);
- Variant **argptrs = call_args;
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, i);
- argptrs[i] = v;
- }
-
-#ifdef DEBUG_ENABLED
- uint64_t call_time = 0;
-
- if (GDScriptLanguage::get_singleton()->profiling) {
- call_time = OS::get_singleton()->get_ticks_usec();
- }
-
-#endif
- Callable::CallError err;
- if (call_ret) {
- GET_VARIANT_PTR(ret, argc);
- base->call_ptr(*methodname, (const Variant **)argptrs, argc, ret, err);
-#ifdef DEBUG_ENABLED
- if (!call_async && ret->get_type() == Variant::OBJECT) {
- // Check if getting a function state without await.
- bool was_freed = false;
- Object *obj = ret->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Got a freed object as a result of the call.";
- OPCODE_BREAK;
- }
- if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
- err_text = R"(Trying to call an async function without "await".)";
- OPCODE_BREAK;
- }
- }
-#endif
- } else {
- base->call_ptr(*methodname, (const Variant **)argptrs, argc, nullptr, err);
- }
-#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
- }
-
- if (err.error != Callable::CallError::CALL_OK) {
- String methodstr = *methodname;
- String basestr = _get_var_type(base);
-
- if (methodstr == "call") {
- if (argc >= 1) {
- methodstr = String(*argptrs[0]) + " (via call)";
- if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
- err.argument += 1;
- }
- }
- } else if (methodstr == "free") {
- if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
- if (base->is_ref()) {
- err_text = "Attempted to free a reference.";
- OPCODE_BREAK;
- } else if (base->get_type() == Variant::OBJECT) {
- err_text = "Attempted to free a locked object (calling or emitting).";
- OPCODE_BREAK;
- }
- }
- }
- err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
- OPCODE_BREAK;
- }
-#endif
-
- //_call_func(nullptr,base,*methodname,ip,argc,p_instance,stack);
- ip += argc + 1;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CALL_BUILT_IN) {
- CHECK_SPACE(4);
-
- GDScriptFunctions::Function func = GDScriptFunctions::Function(_code_ptr[ip + 1]);
- int argc = _code_ptr[ip + 2];
- GD_ERR_BREAK(argc < 0);
-
- ip += 3;
- CHECK_SPACE(argc + 1);
- Variant **argptrs = call_args;
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, i);
- argptrs[i] = v;
- }
-
- GET_VARIANT_PTR(dst, argc);
-
- Callable::CallError err;
-
- GDScriptFunctions::call(func, (const Variant **)argptrs, argc, *dst, err);
-
-#ifdef DEBUG_ENABLED
- if (err.error != Callable::CallError::CALL_OK) {
- String methodstr = GDScriptFunctions::get_func_name(func);
- if (dst->get_type() == Variant::STRING) {
- //call provided error string
- err_text = "Error calling built-in function '" + methodstr + "': " + String(*dst);
- } else {
- err_text = _get_call_error(err, "built-in function '" + methodstr + "'", (const Variant **)argptrs);
- }
- OPCODE_BREAK;
- }
-#endif
- ip += argc + 1;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_CALL_SELF_BASE) {
- CHECK_SPACE(2);
- int self_fun = _code_ptr[ip + 1];
-
-#ifdef DEBUG_ENABLED
- if (self_fun < 0 || self_fun >= _global_names_count) {
- err_text = "compiler bug, function name not found";
- OPCODE_BREAK;
- }
-#endif
- const StringName *methodname = &_global_names_ptr[self_fun];
-
- int argc = _code_ptr[ip + 2];
-
- CHECK_SPACE(2 + argc + 1);
-
- Variant **argptrs = call_args;
-
- for (int i = 0; i < argc; i++) {
- GET_VARIANT_PTR(v, i + 3);
- argptrs[i] = v;
- }
-
- GET_VARIANT_PTR(dst, argc + 3);
-
- const GDScript *gds = _script;
-
- const Map<StringName, GDScriptFunction *>::Element *E = nullptr;
- while (gds->base.ptr()) {
- gds = gds->base.ptr();
- E = gds->member_functions.find(*methodname);
- if (E) {
- break;
- }
- }
-
- Callable::CallError err;
-
- if (E) {
- *dst = E->get()->call(p_instance, (const Variant **)argptrs, argc, err);
- } else if (gds->native.ptr()) {
- if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
- MethodBind *mb = ClassDB::get_method(gds->native->get_name(), *methodname);
- if (!mb) {
- err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- } else {
- *dst = mb->call(p_instance->owner, (const Variant **)argptrs, argc, err);
- }
- } else {
- err.error = Callable::CallError::CALL_OK;
- }
- } else {
- if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
- err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- } else {
- err.error = Callable::CallError::CALL_OK;
- }
- }
-
- if (err.error != Callable::CallError::CALL_OK) {
- String methodstr = *methodname;
- err_text = _get_call_error(err, "function '" + methodstr + "'", (const Variant **)argptrs);
-
- OPCODE_BREAK;
- }
-
- ip += 4 + argc;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_AWAIT) {
- CHECK_SPACE(2);
-
- //do the oneshot connect
- GET_VARIANT_PTR(argobj, 1);
-
- Signal sig;
- bool is_signal = true;
-
- {
- Variant result = *argobj;
-
- if (argobj->get_type() == Variant::OBJECT) {
- bool was_freed = false;
- Object *obj = argobj->get_validated_object_with_check(was_freed);
-
- if (was_freed) {
- err_text = "Trying to await on a freed object.";
- OPCODE_BREAK;
- }
-
- // Is this even possible to be null at this point?
- if (obj) {
- if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
- static StringName completed = _scs_create("completed");
- result = Signal(obj, completed);
- }
- }
- }
-
- if (result.get_type() != Variant::SIGNAL) {
- 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;
- }
- }
-
- if (is_signal) {
- Ref<GDScriptFunctionState> gdfs = memnew(GDScriptFunctionState);
- gdfs->function = this;
-
- gdfs->state.stack.resize(alloca_size);
- //copy variant stack
- for (int i = 0; i < _stack_size; i++) {
- memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
- }
- gdfs->state.stack_size = _stack_size;
- gdfs->state.self = self;
- gdfs->state.alloca_size = alloca_size;
- gdfs->state.ip = ip + 2;
- gdfs->state.line = line;
- gdfs->state.script = _script;
- {
- MutexLock lock(GDScriptLanguage::get_singleton()->lock);
- _script->pending_func_states.add(&gdfs->scripts_list);
- if (p_instance) {
- gdfs->state.instance = p_instance;
- p_instance->pending_func_states.add(&gdfs->instances_list);
- } else {
- gdfs->state.instance = nullptr;
- }
- }
-#ifdef DEBUG_ENABLED
- gdfs->state.function_name = name;
- gdfs->state.script_path = _script->get_path();
-#endif
- gdfs->state.defarg = defarg;
- gdfs->function = this;
-
- retvalue = gdfs;
-
- Error err = sig.connect(Callable(gdfs.ptr(), "_signal_callback"), varray(gdfs), Object::CONNECT_ONESHOT);
- if (err != OK) {
- err_text = "Error connecting to signal: " + sig.get_name() + " during await.";
- OPCODE_BREAK;
- }
-
-#ifdef DEBUG_ENABLED
- exit_ok = true;
- awaited = true;
-#endif
- OPCODE_BREAK;
- }
- }
- DISPATCH_OPCODE; // Needed for synchronous calls (when result is immediately available).
-
- OPCODE(OPCODE_AWAIT_RESUME) {
- CHECK_SPACE(2);
-#ifdef DEBUG_ENABLED
- if (!p_state) {
- err_text = ("Invalid Resume (bug?)");
- OPCODE_BREAK;
- }
-#endif
- GET_VARIANT_PTR(result, 1);
- *result = p_state->result;
- ip += 2;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP) {
- CHECK_SPACE(2);
- int to = _code_ptr[ip + 1];
-
- GD_ERR_BREAK(to < 0 || to > _code_size);
- ip = to;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP_IF) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(test, 1);
-
- bool result = test->booleanize();
-
- if (result) {
- int to = _code_ptr[ip + 2];
- GD_ERR_BREAK(to < 0 || to > _code_size);
- ip = to;
- } else {
- ip += 3;
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP_IF_NOT) {
- CHECK_SPACE(3);
-
- GET_VARIANT_PTR(test, 1);
-
- bool result = test->booleanize();
-
- if (!result) {
- int to = _code_ptr[ip + 2];
- GD_ERR_BREAK(to < 0 || to > _code_size);
- ip = to;
- } else {
- ip += 3;
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_JUMP_TO_DEF_ARGUMENT) {
- CHECK_SPACE(2);
- ip = _default_arg_ptr[defarg];
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_RETURN) {
- CHECK_SPACE(2);
- GET_VARIANT_PTR(r, 1);
- retvalue = *r;
-#ifdef DEBUG_ENABLED
- exit_ok = true;
-#endif
- OPCODE_BREAK;
- }
-
- OPCODE(OPCODE_ITERATE_BEGIN) {
- CHECK_SPACE(8); //space for this a regular iterate
-
- GET_VARIANT_PTR(counter, 1);
- GET_VARIANT_PTR(container, 2);
-
- bool valid;
- if (!container->iter_init(*counter, valid)) {
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "'.";
- OPCODE_BREAK;
- }
-#endif
- int jumpto = _code_ptr[ip + 3];
- GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
- ip = jumpto;
- } else {
- GET_VARIANT_PTR(iterator, 4);
-
- *iterator = container->iter_get(*counter, valid);
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "'.";
- OPCODE_BREAK;
- }
-#endif
- ip += 5; //skip regular iterate which is always next
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ITERATE) {
- CHECK_SPACE(4);
-
- GET_VARIANT_PTR(counter, 1);
- GET_VARIANT_PTR(container, 2);
-
- bool valid;
- if (!container->iter_next(*counter, valid)) {
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?).";
- OPCODE_BREAK;
- }
-#endif
- int jumpto = _code_ptr[ip + 3];
- GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
- ip = jumpto;
- } else {
- GET_VARIANT_PTR(iterator, 4);
-
- *iterator = container->iter_get(*counter, valid);
-#ifdef DEBUG_ENABLED
- if (!valid) {
- err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?).";
- OPCODE_BREAK;
- }
-#endif
- ip += 5; //loop again
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_ASSERT) {
- CHECK_SPACE(3);
-
-#ifdef DEBUG_ENABLED
- GET_VARIANT_PTR(test, 1);
- bool result = test->booleanize();
-
- if (!result) {
- String message_str;
- if (_code_ptr[ip + 2] != 0) {
- GET_VARIANT_PTR(message, 2);
- message_str = *message;
- }
- if (message_str.empty()) {
- err_text = "Assertion failed.";
- } else {
- err_text = "Assertion failed: " + message_str;
- }
- OPCODE_BREAK;
- }
-
-#endif
- ip += 3;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_BREAKPOINT) {
-#ifdef DEBUG_ENABLED
- if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement", true);
- }
-#endif
- ip += 1;
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_LINE) {
- CHECK_SPACE(2);
-
- line = _code_ptr[ip + 1];
- ip += 2;
-
- if (EngineDebugger::is_active()) {
- // line
- bool do_break = false;
-
- if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
- if (EngineDebugger::get_script_debugger()->get_depth() <= 0) {
- EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
- }
- if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0) {
- do_break = true;
- }
- }
-
- if (EngineDebugger::get_script_debugger()->is_breakpoint(line, source)) {
- do_break = true;
- }
-
- if (do_break) {
- GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true);
- }
-
- EngineDebugger::get_singleton()->line_poll();
- }
- }
- DISPATCH_OPCODE;
-
- OPCODE(OPCODE_END) {
-#ifdef DEBUG_ENABLED
- exit_ok = true;
-#endif
- OPCODE_BREAK;
- }
-
-#if 0 // Enable for debugging.
- default: {
- err_text = "Illegal opcode " + itos(_code_ptr[ip]) + " at address " + itos(ip);
- OPCODE_BREAK;
- }
-#endif
- }
-
- OPCODES_END
-#ifdef DEBUG_ENABLED
- if (exit_ok) {
- OPCODE_OUT;
- }
- //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 != "") {
- err_file = p_instance->script->path;
- } else if (script) {
- err_file = script->path;
- }
- if (err_file == "") {
- 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 != "") {
- 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 (!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);
- }
-
-#endif
- OPCODE_OUT;
- }
-
- OPCODES_OUT
-#ifdef DEBUG_ENABLED
- if (GDScriptLanguage::get_singleton()->profiling) {
- uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time;
- profile.total_time += time_taken;
- profile.self_time += time_taken - function_call_time;
- profile.frame_total_time += time_taken;
- profile.frame_self_time += time_taken - function_call_time;
- GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
- }
-
- // Check if this is the last time the function is resuming from await
- // Will be true if never awaited as well
- // When it's the last resume it will postpone the exit from stack,
- // so the debugger knows which function triggered the resume of the next function (if any)
- if (!p_state || awaited) {
- if (EngineDebugger::is_active()) {
- GDScriptLanguage::get_singleton()->exit_function();
- }
-#endif
-
- if (_stack_size) {
- //free stack
- for (int i = 0; i < _stack_size; i++) {
- stack[i].~Variant();
- }
- }
-
-#ifdef DEBUG_ENABLED
- }
-#endif
-
- return retvalue;
-}
const int *GDScriptFunction::get_code() const {
return _code_ptr;
@@ -1635,14 +77,14 @@ int GDScriptFunction::get_max_stack_size() const {
}
struct _GDFKC {
- int order;
+ int order = 0;
List<int> pos;
};
struct _GDFKCS {
- int order;
+ int order = 0;
StringName id;
- int pos;
+ int pos = 0;
bool operator<(const _GDFKCS &p_r) const {
return order < p_r.order;
@@ -1652,8 +94,7 @@ struct _GDFKCS {
void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<StringName, int>> *r_stackvars) const {
int oc = 0;
Map<StringName, _GDFKC> sdmap;
- for (const List<StackDebug>::Element *E = stack_debug.front(); E; E = E->next()) {
- const StackDebug &sd = E->get();
+ for (const StackDebug &sd : stack_debug) {
if (sd.line > p_line) {
break;
}
@@ -1672,60 +113,46 @@ void GDScriptFunction::debug_get_stack_member_state(int p_line, List<Pair<String
ERR_CONTINUE(!sdmap.has(sd.identifier));
sdmap[sd.identifier].pos.pop_back();
- if (sdmap[sd.identifier].pos.empty()) {
+ if (sdmap[sd.identifier].pos.is_empty()) {
sdmap.erase(sd.identifier);
}
}
}
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);
}
stackpositions.sort();
- for (List<_GDFKCS>::Element *E = stackpositions.front(); E; E = E->next()) {
+ for (_GDFKCS &E : stackpositions) {
Pair<StringName, int> p;
- p.first = E->get().id;
- p.second = E->get().pos;
+ p.first = E.id;
+ p.second = E.pos;
r_stackvars->push_back(p);
}
}
-GDScriptFunction::GDScriptFunction() :
- function_list(this) {
- _stack_size = 0;
- _call_size = 0;
- rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+GDScriptFunction::GDScriptFunction() {
name = "<anonymous>";
#ifdef DEBUG_ENABLED
- _func_cname = nullptr;
-
{
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
-
GDScriptLanguage::get_singleton()->function_list.add(&function_list);
}
-
- profile.call_count = 0;
- profile.self_time = 0;
- profile.total_time = 0;
- profile.frame_call_count = 0;
- profile.frame_self_time = 0;
- profile.frame_total_time = 0;
- profile.last_frame_call_count = 0;
- profile.last_frame_self_time = 0;
- profile.last_frame_total_time = 0;
-
#endif
}
GDScriptFunction::~GDScriptFunction() {
+ for (int i = 0; i < lambdas.size(); i++) {
+ memdelete(lambdas[i]);
+ }
+
#ifdef DEBUG_ENABLED
MutexLock lock(GDScriptLanguage::get_singleton()->lock);
@@ -1820,7 +247,7 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
bool completed = true;
// If the return value is a GDScriptFunctionState reference,
- // then the function did awaited again after resuming.
+ // then the function did await again after resuming.
if (ret.is_ref()) {
GDScriptFunctionState *gdfs = Object::cast_to<GDScriptFunctionState>(ret);
if (gdfs && gdfs->function == function) {
@@ -1834,9 +261,9 @@ Variant GDScriptFunctionState::resume(const Variant &p_arg) {
if (completed) {
if (first_state.is_valid()) {
- first_state->emit_signal("completed", ret);
+ first_state->emit_signal(SNAME("completed"), ret);
} else {
- emit_signal("completed", ret);
+ emit_signal(SNAME("completed"), ret);
}
#ifdef DEBUG_ENABLED
@@ -1870,7 +297,6 @@ void GDScriptFunctionState::_bind_methods() {
GDScriptFunctionState::GDScriptFunctionState() :
scripts_list(this),
instances_list(this) {
- function = nullptr;
}
GDScriptFunctionState::~GDScriptFunctionState() {
@@ -1882,506 +308,3 @@ GDScriptFunctionState::~GDScriptFunctionState() {
instances_list.remove_from_list();
}
}
-
-#ifdef DEBUG_ENABLED
-static String _get_variant_string(const Variant &p_variant) {
- String txt;
- if (p_variant.get_type() == Variant::STRING) {
- txt = "\"" + String(p_variant) + "\"";
- } else if (p_variant.get_type() == Variant::STRING_NAME) {
- txt = "&\"" + String(p_variant) + "\"";
- } else if (p_variant.get_type() == Variant::NODE_PATH) {
- txt = "^\"" + String(p_variant) + "\"";
- } else if (p_variant.get_type() == Variant::OBJECT) {
- Object *obj = p_variant;
- if (!obj) {
- txt = "null";
- } else {
- GDScriptNativeClass *cls = Object::cast_to<GDScriptNativeClass>(obj);
- if (cls) {
- txt += cls->get_name();
- txt += " (class)";
- } else {
- txt = obj->get_class();
- if (obj->get_script_instance()) {
- txt += "(" + obj->get_script_instance()->get_script()->get_path() + ")";
- }
- }
- }
- } else {
- txt = p_variant;
- }
- return txt;
-}
-
-static String _disassemble_address(const GDScript *p_script, const GDScriptFunction &p_function, int p_address) {
- int addr = p_address & GDScriptFunction::ADDR_MASK;
-
- switch (p_address >> GDScriptFunction::ADDR_BITS) {
- case GDScriptFunction::ADDR_TYPE_SELF: {
- return "self";
- } break;
- case GDScriptFunction::ADDR_TYPE_CLASS: {
- return "class";
- } break;
- case GDScriptFunction::ADDR_TYPE_MEMBER: {
- return "member(" + p_script->debug_get_member_by_index(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT: {
- return "class_const(" + p_function.get_global_name(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT: {
- return "const(" + _get_variant_string(p_function.get_constant(addr)) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_STACK: {
- return "stack(" + itos(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_STACK_VARIABLE: {
- return "var_stack(" + itos(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_GLOBAL: {
- return "global(" + _get_variant_string(GDScriptLanguage::get_singleton()->get_global_array()[addr]) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_NAMED_GLOBAL: {
- return "named_global(" + p_function.get_global_name(addr) + ")";
- } break;
- case GDScriptFunction::ADDR_TYPE_NIL: {
- return "nil";
- } break;
- }
-
- return "<err>";
-}
-
-void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
-#define DADDR(m_ip) (_disassemble_address(_script, *this, _code_ptr[ip + m_ip]))
-
- for (int ip = 0; ip < _code_size;) {
- StringBuilder text;
- int incr = 0;
-
- text += " ";
- text += itos(ip);
- text += ": ";
-
- // This makes the compiler complain if some opcode is unchecked in the switch.
- Opcode code = Opcode(_code_ptr[ip]);
-
- switch (code) {
- case OPCODE_OPERATOR: {
- int operation = _code_ptr[ip + 1];
-
- text += "operator ";
-
- text += DADDR(4);
- text += " = ";
- text += DADDR(2);
- text += " ";
- text += Variant::get_operator_name(Variant::Operator(operation));
- text += " ";
- text += DADDR(3);
-
- incr += 5;
- } break;
- case OPCODE_EXTENDS_TEST: {
- text += "is object ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += " is ";
- text += DADDR(2);
-
- incr += 4;
- } break;
- case OPCODE_IS_BUILTIN: {
- text += "is builtin ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += " is ";
- text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 2]));
-
- incr += 4;
- } break;
- case OPCODE_SET: {
- text += "set ";
- text += DADDR(1);
- text += "[";
- text += DADDR(2);
- text += "] = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_GET: {
- text += "get ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += "[";
- text += DADDR(2);
- text += "]";
-
- incr += 4;
- } break;
- case OPCODE_SET_NAMED: {
- text += "set_named ";
- text += DADDR(1);
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 2]];
- text += "\"] = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_GET_NAMED: {
- text += "get_named ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(1);
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 2]];
- text += "\"]";
-
- incr += 4;
- } break;
- case OPCODE_SET_MEMBER: {
- text += "set_member ";
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 1]];
- text += "\"] = ";
- text += DADDR(2);
-
- incr += 3;
- } break;
- case OPCODE_GET_MEMBER: {
- text += "get_member ";
- text += DADDR(2);
- text += " = ";
- text += "[\"";
- text += _global_names_ptr[_code_ptr[ip + 1]];
- text += "\"]";
-
- incr += 3;
- } break;
- case OPCODE_ASSIGN: {
- text += "assign ";
- text += DADDR(1);
- text += " = ";
- text += DADDR(2);
-
- incr += 3;
- } break;
- case OPCODE_ASSIGN_TRUE: {
- text += "assign ";
- text += DADDR(1);
- text += " = true";
-
- incr += 2;
- } break;
- case OPCODE_ASSIGN_FALSE: {
- text += "assign ";
- text += DADDR(1);
- text += " = false";
-
- incr += 2;
- } break;
- case OPCODE_ASSIGN_TYPED_BUILTIN: {
- text += "assign typed builtin (";
- text += Variant::get_type_name((Variant::Type)_code_ptr[ip + 1]);
- text += ") ";
- text += DADDR(2);
- text += " = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_ASSIGN_TYPED_NATIVE: {
- Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
-
- text += "assign typed native (";
- text += nc->get_name().operator String();
- text += ") ";
- text += DADDR(2);
- text += " = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_ASSIGN_TYPED_SCRIPT: {
- Variant script = _constants_ptr[_code_ptr[ip + 1]];
- Script *sc = Object::cast_to<Script>(script.operator Object *());
-
- text += "assign typed script (";
- text += sc->get_path();
- text += ") ";
- text += DADDR(2);
- text += " = ";
- text += DADDR(3);
-
- incr += 4;
- } break;
- case OPCODE_CAST_TO_BUILTIN: {
- text += "cast builtin ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(2);
- text += " as ";
- text += Variant::get_type_name(Variant::Type(_code_ptr[ip + 1]));
-
- incr += 4;
- } break;
- case OPCODE_CAST_TO_NATIVE: {
- Variant class_name = _constants_ptr[_code_ptr[ip + 1]];
- GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(class_name.operator Object *());
-
- text += "cast native ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(2);
- text += " as ";
- text += nc->get_name();
-
- incr += 4;
- } break;
- case OPCODE_CAST_TO_SCRIPT: {
- text += "cast ";
- text += DADDR(3);
- text += " = ";
- text += DADDR(2);
- text += " as ";
- text += DADDR(1);
-
- incr += 4;
- } break;
- case OPCODE_CONSTRUCT: {
- Variant::Type t = Variant::Type(_code_ptr[ip + 1]);
- int argc = _code_ptr[ip + 2];
-
- text += "construct ";
- text += DADDR(3 + argc);
- text += " = ";
-
- text += Variant::get_type_name(t) + "(";
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(i + 3);
- }
- text += ")";
-
- incr = 4 + argc;
- } break;
- case OPCODE_CONSTRUCT_ARRAY: {
- int argc = _code_ptr[ip + 1];
- text += " make_array ";
- text += DADDR(2 + argc);
- text += " = [";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(2 + i);
- }
-
- text += "]";
-
- incr += 3 + argc;
- } break;
- case OPCODE_CONSTRUCT_DICTIONARY: {
- int argc = _code_ptr[ip + 1];
- text += "make_dict ";
- text += DADDR(2 + argc * 2);
- text += " = {";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(2 + i * 2 + 0);
- text += ": ";
- text += DADDR(2 + i * 2 + 1);
- }
-
- text += "}";
-
- incr += 3 + argc * 2;
- } break;
- case OPCODE_CALL:
- case OPCODE_CALL_RETURN:
- case OPCODE_CALL_ASYNC: {
- bool ret = _code_ptr[ip] == OPCODE_CALL_RETURN;
- bool async = _code_ptr[ip] == OPCODE_CALL_ASYNC;
-
- if (ret) {
- text += "call-ret ";
- } else if (async) {
- text += "call-async ";
- } else {
- text += "call ";
- }
-
- int argc = _code_ptr[ip + 1];
- if (ret || async) {
- text += DADDR(4 + argc) + " = ";
- }
-
- text += DADDR(2) + ".";
- text += String(_global_names_ptr[_code_ptr[ip + 3]]);
- text += "(";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(4 + i);
- }
- text += ")";
-
- incr = 5 + argc;
- } break;
- case OPCODE_CALL_BUILT_IN: {
- text += "call-built-in ";
-
- int argc = _code_ptr[ip + 2];
- text += DADDR(3 + argc) + " = ";
-
- text += GDScriptFunctions::get_func_name(GDScriptFunctions::Function(_code_ptr[ip + 1]));
- text += "(";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(3 + i);
- }
- text += ")";
-
- incr = 4 + argc;
- } break;
- case OPCODE_CALL_SELF_BASE: {
- text += "call-self-base ";
-
- int argc = _code_ptr[ip + 2];
- text += DADDR(3 + argc) + " = ";
-
- text += _global_names_ptr[_code_ptr[ip + 1]];
- text += "(";
-
- for (int i = 0; i < argc; i++) {
- if (i > 0)
- text += ", ";
- text += DADDR(3 + i);
- }
- text += ")";
-
- incr = 4 + argc;
- } break;
- case OPCODE_AWAIT: {
- text += "await ";
- text += DADDR(1);
-
- incr += 2;
- } break;
- case OPCODE_AWAIT_RESUME: {
- text += "await resume ";
- text += DADDR(1);
-
- incr = 2;
- } break;
- case OPCODE_JUMP: {
- text += "jump ";
- text += itos(_code_ptr[ip + 1]);
-
- incr = 2;
- } break;
- case OPCODE_JUMP_IF: {
- text += "jump-if ";
- text += DADDR(1);
- text += " to ";
- text += itos(_code_ptr[ip + 2]);
-
- incr = 3;
- } break;
- case OPCODE_JUMP_IF_NOT: {
- text += "jump-if-not ";
- text += DADDR(1);
- text += " to ";
- text += itos(_code_ptr[ip + 2]);
-
- incr = 3;
- } break;
- case OPCODE_JUMP_TO_DEF_ARGUMENT: {
- text += "jump-to-default-argument ";
-
- incr = 1;
- } break;
- case OPCODE_RETURN: {
- text += "return ";
- text += DADDR(1);
-
- incr = 2;
- } break;
- case OPCODE_ITERATE_BEGIN: {
- text += "for-init ";
- text += DADDR(4);
- text += " in ";
- text += DADDR(2);
- text += " counter ";
- text += DADDR(1);
- text += " end ";
- text += itos(_code_ptr[ip + 3]);
-
- incr += 5;
- } break;
- case OPCODE_ITERATE: {
- text += "for-loop ";
- text += DADDR(4);
- text += " in ";
- text += DADDR(2);
- text += " counter ";
- text += DADDR(1);
- text += " end ";
- text += itos(_code_ptr[ip + 3]);
-
- incr += 5;
- } break;
- case OPCODE_LINE: {
- int line = _code_ptr[ip + 1] - 1;
- if (line >= 0 && line < p_code_lines.size()) {
- text += "line ";
- text += itos(line + 1);
- text += ": ";
- text += p_code_lines[line];
- } else {
- text += "";
- }
-
- incr += 2;
- } break;
- case OPCODE_ASSERT: {
- text += "assert (";
- text += DADDR(1);
- text += ", ";
- text += DADDR(2);
- text += ")";
-
- incr += 3;
- } break;
- case OPCODE_BREAKPOINT: {
- text += "breakpoint";
-
- incr += 1;
- } break;
- case OPCODE_END: {
- text += "== END ==";
-
- incr += 1;
- } break;
- }
-
- ip += incr;
- if (text.get_string_length() > 0) {
- print_line(text.as_string());
- }
- }
-}
-#endif
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index c98ac09310..9d076a8e4c 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,18 +31,23 @@
#ifndef GDSCRIPT_FUNCTION_H
#define GDSCRIPT_FUNCTION_H
+#include "core/object/ref_counted.h"
+#include "core/object/script_language.h"
#include "core/os/thread.h"
-#include "core/pair.h"
-#include "core/reference.h"
-#include "core/script_language.h"
-#include "core/self_list.h"
-#include "core/string_name.h"
-#include "core/variant.h"
+#include "core/string/string_name.h"
+#include "core/templates/pair.h"
+#include "core/templates/self_list.h"
+#include "core/variant/variant.h"
+#include "gdscript_utility_functions.h"
class GDScriptInstance;
class GDScript;
-struct GDScriptDataType {
+class GDScriptDataType {
+private:
+ GDScriptDataType *container_element_type = nullptr;
+
+public:
enum Kind {
UNINITIALIZED,
BUILTIN,
@@ -70,7 +75,24 @@ struct GDScriptDataType {
case BUILTIN: {
Variant::Type var_type = p_variant.get_type();
bool valid = builtin_type == var_type;
- if (!valid && p_allow_implicit_conversion) {
+ if (valid && builtin_type == Variant::ARRAY && has_container_element_type()) {
+ Array array = p_variant;
+ if (array.is_typed()) {
+ Variant::Type array_builtin_type = (Variant::Type)array.get_typed_builtin();
+ StringName array_native_type = array.get_typed_class_name();
+ Ref<Script> array_script_type_ref = array.get_typed_script();
+
+ if (array_script_type_ref.is_valid()) {
+ valid = (container_element_type->kind == SCRIPT || container_element_type->kind == GDSCRIPT) && container_element_type->script_type == array_script_type_ref.ptr();
+ } else if (array_native_type != StringName()) {
+ valid = container_element_type->kind == NATIVE && container_element_type->native_type == array_native_type;
+ } else {
+ valid = container_element_type->kind == BUILTIN && container_element_type->builtin_type == array_builtin_type;
+ }
+ } else {
+ valid = false;
+ }
+ } else if (!valid && p_allow_implicit_conversion) {
valid = Variant::can_convert_strict(var_type, builtin_type);
}
return valid;
@@ -89,11 +111,7 @@ struct GDScriptDataType {
}
if (!ClassDB::is_parent_class(obj->get_class_name(), native_type)) {
- // Try with underscore prefix
- StringName underscore_native_type = "_" + native_type;
- if (!ClassDB::is_parent_class(obj->get_class_name(), underscore_native_type)) {
- return false;
- }
+ return false;
}
return true;
} break;
@@ -152,47 +170,220 @@ struct GDScriptDataType {
return info;
}
- GDScriptDataType() {}
+ void set_container_element_type(const GDScriptDataType &p_element_type) {
+ container_element_type = memnew(GDScriptDataType(p_element_type));
+ }
+
+ GDScriptDataType get_container_element_type() const {
+ ERR_FAIL_COND_V(container_element_type == nullptr, GDScriptDataType());
+ return *container_element_type;
+ }
+
+ bool has_container_element_type() const {
+ return container_element_type != nullptr;
+ }
+
+ void unset_container_element_type() {
+ if (container_element_type) {
+ memdelete(container_element_type);
+ }
+ container_element_type = nullptr;
+ }
+
+ GDScriptDataType() = default;
+
+ GDScriptDataType &operator=(const GDScriptDataType &p_other) {
+ kind = p_other.kind;
+ has_type = p_other.has_type;
+ builtin_type = p_other.builtin_type;
+ native_type = p_other.native_type;
+ script_type = p_other.script_type;
+ script_type_ref = p_other.script_type_ref;
+ unset_container_element_type();
+ if (p_other.has_container_element_type()) {
+ set_container_element_type(p_other.get_container_element_type());
+ }
+ return *this;
+ }
+
+ GDScriptDataType(const GDScriptDataType &p_other) {
+ *this = p_other;
+ }
+
+ ~GDScriptDataType() {
+ unset_container_element_type();
+ }
};
class GDScriptFunction {
public:
enum Opcode {
OPCODE_OPERATOR,
+ OPCODE_OPERATOR_VALIDATED,
OPCODE_EXTENDS_TEST,
OPCODE_IS_BUILTIN,
- OPCODE_SET,
- OPCODE_GET,
+ OPCODE_SET_KEYED,
+ OPCODE_SET_KEYED_VALIDATED,
+ OPCODE_SET_INDEXED_VALIDATED,
+ OPCODE_GET_KEYED,
+ OPCODE_GET_KEYED_VALIDATED,
+ OPCODE_GET_INDEXED_VALIDATED,
OPCODE_SET_NAMED,
+ OPCODE_SET_NAMED_VALIDATED,
OPCODE_GET_NAMED,
+ OPCODE_GET_NAMED_VALIDATED,
OPCODE_SET_MEMBER,
OPCODE_GET_MEMBER,
OPCODE_ASSIGN,
OPCODE_ASSIGN_TRUE,
OPCODE_ASSIGN_FALSE,
OPCODE_ASSIGN_TYPED_BUILTIN,
+ OPCODE_ASSIGN_TYPED_ARRAY,
OPCODE_ASSIGN_TYPED_NATIVE,
OPCODE_ASSIGN_TYPED_SCRIPT,
OPCODE_CAST_TO_BUILTIN,
OPCODE_CAST_TO_NATIVE,
OPCODE_CAST_TO_SCRIPT,
- OPCODE_CONSTRUCT, //only for basic types!!
+ OPCODE_CONSTRUCT, // Only for basic types!
+ OPCODE_CONSTRUCT_VALIDATED, // Only for basic types!
OPCODE_CONSTRUCT_ARRAY,
+ OPCODE_CONSTRUCT_TYPED_ARRAY,
OPCODE_CONSTRUCT_DICTIONARY,
OPCODE_CALL,
OPCODE_CALL_RETURN,
OPCODE_CALL_ASYNC,
- OPCODE_CALL_BUILT_IN,
+ OPCODE_CALL_UTILITY,
+ OPCODE_CALL_UTILITY_VALIDATED,
+ OPCODE_CALL_GDSCRIPT_UTILITY,
+ OPCODE_CALL_BUILTIN_TYPE_VALIDATED,
OPCODE_CALL_SELF_BASE,
+ OPCODE_CALL_METHOD_BIND,
+ OPCODE_CALL_METHOD_BIND_RET,
+ OPCODE_CALL_BUILTIN_STATIC,
+ // ptrcall have one instruction per return type.
+ OPCODE_CALL_PTRCALL_NO_RETURN,
+ OPCODE_CALL_PTRCALL_BOOL,
+ OPCODE_CALL_PTRCALL_INT,
+ OPCODE_CALL_PTRCALL_FLOAT,
+ OPCODE_CALL_PTRCALL_STRING,
+ OPCODE_CALL_PTRCALL_VECTOR2,
+ OPCODE_CALL_PTRCALL_VECTOR2I,
+ OPCODE_CALL_PTRCALL_RECT2,
+ OPCODE_CALL_PTRCALL_RECT2I,
+ OPCODE_CALL_PTRCALL_VECTOR3,
+ OPCODE_CALL_PTRCALL_VECTOR3I,
+ OPCODE_CALL_PTRCALL_TRANSFORM2D,
+ OPCODE_CALL_PTRCALL_PLANE,
+ OPCODE_CALL_PTRCALL_QUATERNION,
+ OPCODE_CALL_PTRCALL_AABB,
+ OPCODE_CALL_PTRCALL_BASIS,
+ OPCODE_CALL_PTRCALL_TRANSFORM3D,
+ OPCODE_CALL_PTRCALL_COLOR,
+ OPCODE_CALL_PTRCALL_STRING_NAME,
+ OPCODE_CALL_PTRCALL_NODE_PATH,
+ OPCODE_CALL_PTRCALL_RID,
+ OPCODE_CALL_PTRCALL_OBJECT,
+ OPCODE_CALL_PTRCALL_CALLABLE,
+ OPCODE_CALL_PTRCALL_SIGNAL,
+ OPCODE_CALL_PTRCALL_DICTIONARY,
+ OPCODE_CALL_PTRCALL_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_BYTE_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_INT32_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_INT64_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_FLOAT32_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_FLOAT64_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_STRING_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_VECTOR2_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_VECTOR3_ARRAY,
+ OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY,
OPCODE_AWAIT,
OPCODE_AWAIT_RESUME,
+ OPCODE_CREATE_LAMBDA,
OPCODE_JUMP,
OPCODE_JUMP_IF,
OPCODE_JUMP_IF_NOT,
OPCODE_JUMP_TO_DEF_ARGUMENT,
OPCODE_RETURN,
+ OPCODE_RETURN_TYPED_BUILTIN,
+ OPCODE_RETURN_TYPED_ARRAY,
+ OPCODE_RETURN_TYPED_NATIVE,
+ OPCODE_RETURN_TYPED_SCRIPT,
OPCODE_ITERATE_BEGIN,
+ OPCODE_ITERATE_BEGIN_INT,
+ OPCODE_ITERATE_BEGIN_FLOAT,
+ OPCODE_ITERATE_BEGIN_VECTOR2,
+ OPCODE_ITERATE_BEGIN_VECTOR2I,
+ OPCODE_ITERATE_BEGIN_VECTOR3,
+ OPCODE_ITERATE_BEGIN_VECTOR3I,
+ OPCODE_ITERATE_BEGIN_STRING,
+ OPCODE_ITERATE_BEGIN_DICTIONARY,
+ OPCODE_ITERATE_BEGIN_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY,
+ OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY,
+ OPCODE_ITERATE_BEGIN_OBJECT,
OPCODE_ITERATE,
+ OPCODE_ITERATE_INT,
+ OPCODE_ITERATE_FLOAT,
+ OPCODE_ITERATE_VECTOR2,
+ OPCODE_ITERATE_VECTOR2I,
+ OPCODE_ITERATE_VECTOR3,
+ OPCODE_ITERATE_VECTOR3I,
+ OPCODE_ITERATE_STRING,
+ OPCODE_ITERATE_DICTIONARY,
+ OPCODE_ITERATE_ARRAY,
+ OPCODE_ITERATE_PACKED_BYTE_ARRAY,
+ OPCODE_ITERATE_PACKED_INT32_ARRAY,
+ OPCODE_ITERATE_PACKED_INT64_ARRAY,
+ OPCODE_ITERATE_PACKED_FLOAT32_ARRAY,
+ OPCODE_ITERATE_PACKED_FLOAT64_ARRAY,
+ OPCODE_ITERATE_PACKED_STRING_ARRAY,
+ OPCODE_ITERATE_PACKED_VECTOR2_ARRAY,
+ 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,
+ OPCODE_TYPE_ADJUST_FLOAT,
+ OPCODE_TYPE_ADJUST_STRING,
+ OPCODE_TYPE_ADJUST_VECTOR2,
+ OPCODE_TYPE_ADJUST_VECTOR2I,
+ OPCODE_TYPE_ADJUST_RECT2,
+ OPCODE_TYPE_ADJUST_RECT2I,
+ OPCODE_TYPE_ADJUST_VECTOR3,
+ OPCODE_TYPE_ADJUST_VECTOR3I,
+ OPCODE_TYPE_ADJUST_TRANSFORM2D,
+ OPCODE_TYPE_ADJUST_PLANE,
+ OPCODE_TYPE_ADJUST_QUATERNION,
+ OPCODE_TYPE_ADJUST_AABB,
+ OPCODE_TYPE_ADJUST_BASIS,
+ OPCODE_TYPE_ADJUST_TRANSFORM,
+ OPCODE_TYPE_ADJUST_COLOR,
+ OPCODE_TYPE_ADJUST_STRING_NAME,
+ OPCODE_TYPE_ADJUST_NODE_PATH,
+ OPCODE_TYPE_ADJUST_RID,
+ OPCODE_TYPE_ADJUST_OBJECT,
+ OPCODE_TYPE_ADJUST_CALLABLE,
+ OPCODE_TYPE_ADJUST_SIGNAL,
+ OPCODE_TYPE_ADJUST_DICTIONARY,
+ OPCODE_TYPE_ADJUST_ARRAY,
+ OPCODE_TYPE_ADJUST_PACKED_BYTE_ARRAY,
+ OPCODE_TYPE_ADJUST_PACKED_INT32_ARRAY,
+ OPCODE_TYPE_ADJUST_PACKED_INT64_ARRAY,
+ OPCODE_TYPE_ADJUST_PACKED_FLOAT32_ARRAY,
+ OPCODE_TYPE_ADJUST_PACKED_FLOAT64_ARRAY,
+ OPCODE_TYPE_ADJUST_PACKED_STRING_ARRAY,
+ OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY,
+ OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY,
+ OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY,
OPCODE_ASSERT,
OPCODE_BREAKPOINT,
OPCODE_LINE,
@@ -203,16 +394,24 @@ public:
ADDR_BITS = 24,
ADDR_MASK = ((1 << ADDR_BITS) - 1),
ADDR_TYPE_MASK = ~ADDR_MASK,
- ADDR_TYPE_SELF = 0,
- ADDR_TYPE_CLASS = 1,
+ ADDR_TYPE_STACK = 0,
+ ADDR_TYPE_CONSTANT = 1,
ADDR_TYPE_MEMBER = 2,
- ADDR_TYPE_CLASS_CONSTANT = 3,
- ADDR_TYPE_LOCAL_CONSTANT = 4,
- ADDR_TYPE_STACK = 5,
- ADDR_TYPE_STACK_VARIABLE = 6,
- ADDR_TYPE_GLOBAL = 7,
- ADDR_TYPE_NAMED_GLOBAL = 8,
- ADDR_TYPE_NIL = 9
+ };
+
+ enum FixedAddresses {
+ ADDR_STACK_SELF = 0,
+ ADDR_STACK_CLASS = 1,
+ ADDR_STACK_NIL = 2,
+ ADDR_SELF = ADDR_STACK_SELF | (ADDR_TYPE_STACK << ADDR_BITS),
+ ADDR_CLASS = ADDR_STACK_CLASS | (ADDR_TYPE_STACK << ADDR_BITS),
+ ADDR_NIL = ADDR_STACK_NIL | (ADDR_TYPE_STACK << ADDR_BITS),
+ };
+
+ enum Instruction {
+ INSTR_BITS = 20,
+ INSTR_MASK = ((1 << INSTR_BITS) - 1),
+ INSTR_ARGS_MASK = ~INSTR_MASK,
};
struct StackDebug {
@@ -229,77 +428,120 @@ private:
StringName source;
mutable Variant nil;
- mutable Variant *_constants_ptr;
- int _constant_count;
- const StringName *_global_names_ptr;
- int _global_names_count;
- const int *_default_arg_ptr;
- int _default_arg_count;
- const int *_code_ptr;
- int _code_size;
- int _argument_count;
- int _stack_size;
- int _call_size;
- int _initial_line;
- bool _static;
- MultiplayerAPI::RPCMode rpc_mode;
-
- GDScript *_script;
+ mutable Variant *_constants_ptr = nullptr;
+ int _constant_count = 0;
+ const StringName *_global_names_ptr = nullptr;
+ int _global_names_count = 0;
+ const int *_default_arg_ptr = nullptr;
+ int _default_arg_count = 0;
+ int _operator_funcs_count = 0;
+ const Variant::ValidatedOperatorEvaluator *_operator_funcs_ptr = nullptr;
+ int _setters_count = 0;
+ const Variant::ValidatedSetter *_setters_ptr = nullptr;
+ int _getters_count = 0;
+ const Variant::ValidatedGetter *_getters_ptr = nullptr;
+ int _keyed_setters_count = 0;
+ const Variant::ValidatedKeyedSetter *_keyed_setters_ptr = nullptr;
+ int _keyed_getters_count = 0;
+ const Variant::ValidatedKeyedGetter *_keyed_getters_ptr = nullptr;
+ int _indexed_setters_count = 0;
+ const Variant::ValidatedIndexedSetter *_indexed_setters_ptr = nullptr;
+ int _indexed_getters_count = 0;
+ const Variant::ValidatedIndexedGetter *_indexed_getters_ptr = nullptr;
+ int _builtin_methods_count = 0;
+ const Variant::ValidatedBuiltInMethod *_builtin_methods_ptr = nullptr;
+ int _constructors_count = 0;
+ const Variant::ValidatedConstructor *_constructors_ptr = nullptr;
+ int _utilities_count = 0;
+ const Variant::ValidatedUtilityFunction *_utilities_ptr = nullptr;
+ int _gds_utilities_count = 0;
+ const GDScriptUtilityFunctions::FunctionPtr *_gds_utilities_ptr = nullptr;
+ int _methods_count = 0;
+ MethodBind **_methods_ptr = nullptr;
+ int _lambdas_count = 0;
+ GDScriptFunction **_lambdas_ptr = nullptr;
+ const int *_code_ptr = nullptr;
+ int _code_size = 0;
+ int _argument_count = 0;
+ int _stack_size = 0;
+ int _instruction_args_size = 0;
+ int _ptrcall_args_size = 0;
+
+ int _initial_line = 0;
+ bool _static = false;
+ Multiplayer::RPCConfig rpc_config;
+
+ GDScript *_script = nullptr;
StringName name;
Vector<Variant> constants;
Vector<StringName> global_names;
Vector<int> default_arguments;
+ Vector<Variant::ValidatedOperatorEvaluator> operator_funcs;
+ Vector<Variant::ValidatedSetter> setters;
+ Vector<Variant::ValidatedGetter> getters;
+ Vector<Variant::ValidatedKeyedSetter> keyed_setters;
+ Vector<Variant::ValidatedKeyedGetter> keyed_getters;
+ Vector<Variant::ValidatedIndexedSetter> indexed_setters;
+ Vector<Variant::ValidatedIndexedGetter> indexed_getters;
+ Vector<Variant::ValidatedBuiltInMethod> builtin_methods;
+ Vector<Variant::ValidatedConstructor> constructors;
+ Vector<Variant::ValidatedUtilityFunction> utilities;
+ Vector<GDScriptUtilityFunctions::FunctionPtr> gds_utilities;
+ Vector<MethodBind *> methods;
+ Vector<GDScriptFunction *> lambdas;
Vector<int> code;
Vector<GDScriptDataType> argument_types;
GDScriptDataType return_type;
+ Map<int, Variant::Type> temporary_slots;
+
#ifdef TOOLS_ENABLED
Vector<StringName> arg_names;
+ Vector<Variant> default_arg_values;
#endif
List<StackDebug> stack_debug;
- _FORCE_INLINE_ Variant *_get_variant(int p_address, GDScriptInstance *p_instance, GDScript *p_script, Variant &self, Variant &static_ref, Variant *p_stack, String &r_error) const;
+ _FORCE_INLINE_ Variant *_get_variant(int p_address, GDScriptInstance *p_instance, Variant *p_stack, String &r_error) const;
_FORCE_INLINE_ String _get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const;
friend class GDScriptLanguage;
- SelfList<GDScriptFunction> function_list;
+ SelfList<GDScriptFunction> function_list{ this };
#ifdef DEBUG_ENABLED
CharString func_cname;
- const char *_func_cname;
+ const char *_func_cname = nullptr;
struct Profile {
StringName signature;
- uint64_t call_count;
- uint64_t self_time;
- uint64_t total_time;
- uint64_t frame_call_count;
- uint64_t frame_self_time;
- uint64_t frame_total_time;
- uint64_t last_frame_call_count;
- uint64_t last_frame_self_time;
- uint64_t last_frame_total_time;
+ uint64_t call_count = 0;
+ uint64_t self_time = 0;
+ uint64_t total_time = 0;
+ uint64_t frame_call_count = 0;
+ uint64_t frame_self_time = 0;
+ uint64_t frame_total_time = 0;
+ uint64_t last_frame_call_count = 0;
+ uint64_t last_frame_self_time = 0;
+ uint64_t last_frame_total_time = 0;
} profile;
#endif
public:
struct CallState {
- GDScript *script;
- GDScriptInstance *instance;
+ GDScript *script = nullptr;
+ GDScriptInstance *instance = nullptr;
#ifdef DEBUG_ENABLED
StringName function_name;
String script_path;
#endif
Vector<uint8_t> stack;
- int stack_size;
- Variant self;
- uint32_t alloca_size;
- int ip;
- int line;
- int defarg;
+ int stack_size = 0;
+ uint32_t alloca_size = 0;
+ int ip = 0;
+ int line = 0;
+ int defarg = 0;
Variant result;
};
@@ -335,6 +577,11 @@ public:
ERR_FAIL_INDEX_V(p_idx, default_arguments.size(), Variant());
return default_arguments[p_idx];
}
+#ifdef TOOLS_ENABLED
+ const Vector<Variant> &get_default_arg_values() const {
+ return default_arg_values;
+ }
+#endif // TOOLS_ENABLED
Variant call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state = nullptr);
@@ -342,15 +589,15 @@ public:
void disassemble(const Vector<String> &p_code_lines) const;
#endif
- _FORCE_INLINE_ MultiplayerAPI::RPCMode get_rpc_mode() const { return rpc_mode; }
+ _FORCE_INLINE_ Multiplayer::RPCConfig get_rpc_config() const { return rpc_config; }
GDScriptFunction();
~GDScriptFunction();
};
-class GDScriptFunctionState : public Reference {
- GDCLASS(GDScriptFunctionState, Reference);
+class GDScriptFunctionState : public RefCounted {
+ GDCLASS(GDScriptFunctionState, RefCounted);
friend class GDScriptFunction;
- GDScriptFunction *function;
+ GDScriptFunction *function = nullptr;
GDScriptFunction::CallState state;
Variant _signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Ref<GDScriptFunctionState> first_state;
diff --git a/modules/gdscript/gdscript_functions.cpp b/modules/gdscript/gdscript_functions.cpp
deleted file mode 100644
index 31ce63bc6e..0000000000
--- a/modules/gdscript/gdscript_functions.cpp
+++ /dev/null
@@ -1,1964 +0,0 @@
-/*************************************************************************/
-/* gdscript_functions.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "gdscript_functions.h"
-
-#include "core/class_db.h"
-#include "core/func_ref.h"
-#include "core/io/json.h"
-#include "core/io/marshalls.h"
-#include "core/math/math_funcs.h"
-#include "core/os/os.h"
-#include "core/reference.h"
-#include "core/variant_parser.h"
-#include "gdscript.h"
-
-const char *GDScriptFunctions::get_func_name(Function p_func) {
- ERR_FAIL_INDEX_V(p_func, FUNC_MAX, "");
-
- static const char *_names[FUNC_MAX] = {
- "sin",
- "cos",
- "tan",
- "sinh",
- "cosh",
- "tanh",
- "asin",
- "acos",
- "atan",
- "atan2",
- "sqrt",
- "fmod",
- "fposmod",
- "posmod",
- "floor",
- "ceil",
- "round",
- "abs",
- "sign",
- "pow",
- "log",
- "exp",
- "is_nan",
- "is_inf",
- "is_equal_approx",
- "is_zero_approx",
- "ease",
- "step_decimals",
- "stepify",
- "lerp",
- "lerp_angle",
- "inverse_lerp",
- "range_lerp",
- "smoothstep",
- "move_toward",
- "dectime",
- "randomize",
- "randi",
- "randf",
- "rand_range",
- "seed",
- "rand_seed",
- "deg2rad",
- "rad2deg",
- "linear2db",
- "db2linear",
- "polar2cartesian",
- "cartesian2polar",
- "wrapi",
- "wrapf",
- "max",
- "min",
- "clamp",
- "nearest_po2",
- "weakref",
- "funcref",
- "convert",
- "typeof",
- "type_exists",
- "char",
- "ord",
- "str",
- "print",
- "printt",
- "prints",
- "printerr",
- "printraw",
- "print_debug",
- "push_error",
- "push_warning",
- "var2str",
- "str2var",
- "var2bytes",
- "bytes2var",
- "range",
- "load",
- "inst2dict",
- "dict2inst",
- "validate_json",
- "parse_json",
- "to_json",
- "hash",
- "Color8",
- "ColorN",
- "print_stack",
- "get_stack",
- "instance_from_id",
- "len",
- "is_instance_valid",
- };
-
- return _names[p_func];
-}
-
-void GDScriptFunctions::call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
-#ifdef DEBUG_ENABLED
-
-#define VALIDATE_ARG_COUNT(m_count) \
- if (p_arg_count < m_count) { \
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
- r_error.argument = m_count; \
- r_error.expected = m_count; \
- r_ret = Variant(); \
- return; \
- } \
- if (p_arg_count > m_count) { \
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
- r_error.argument = m_count; \
- r_error.expected = m_count; \
- r_ret = Variant(); \
- return; \
- }
-
-#define VALIDATE_ARG_NUM(m_arg) \
- if (!p_args[m_arg]->is_num()) { \
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
- r_error.argument = m_arg; \
- r_error.expected = Variant::FLOAT; \
- r_ret = Variant(); \
- return; \
- }
-
-#else
-
-#define VALIDATE_ARG_COUNT(m_count)
-#define VALIDATE_ARG_NUM(m_arg)
-#endif
-
- //using a switch, so the compiler generates a jumptable
-
- switch (p_func) {
- case MATH_SIN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::sin((double)*p_args[0]);
- } break;
- case MATH_COS: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::cos((double)*p_args[0]);
- } break;
- case MATH_TAN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::tan((double)*p_args[0]);
- } break;
- case MATH_SINH: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::sinh((double)*p_args[0]);
- } break;
- case MATH_COSH: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::cosh((double)*p_args[0]);
- } break;
- case MATH_TANH: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::tanh((double)*p_args[0]);
- } break;
- case MATH_ASIN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::asin((double)*p_args[0]);
- } break;
- case MATH_ACOS: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::acos((double)*p_args[0]);
- } break;
- case MATH_ATAN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::atan((double)*p_args[0]);
- } break;
- case MATH_ATAN2: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::atan2((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_SQRT: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::sqrt((double)*p_args[0]);
- } break;
- case MATH_FMOD: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::fmod((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_FPOSMOD: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::fposmod((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_POSMOD: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::posmod((int)*p_args[0], (int)*p_args[1]);
- } break;
- case MATH_FLOOR: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::floor((double)*p_args[0]);
- } break;
- case MATH_CEIL: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::ceil((double)*p_args[0]);
- } break;
- case MATH_ROUND: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::round((double)*p_args[0]);
- } break;
- case MATH_ABS: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() == Variant::INT) {
- int64_t i = *p_args[0];
- r_ret = ABS(i);
- } else if (p_args[0]->get_type() == Variant::FLOAT) {
- double r = *p_args[0];
- r_ret = Math::abs(r);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::FLOAT;
- r_ret = Variant();
- }
- } break;
- case MATH_SIGN: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() == Variant::INT) {
- int64_t i = *p_args[0];
- r_ret = i < 0 ? -1 : (i > 0 ? +1 : 0);
- } else if (p_args[0]->get_type() == Variant::FLOAT) {
- real_t r = *p_args[0];
- r_ret = r < 0.0 ? -1.0 : (r > 0.0 ? +1.0 : 0.0);
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::FLOAT;
- r_ret = Variant();
- }
- } break;
- case MATH_POW: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::pow((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_LOG: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::log((double)*p_args[0]);
- } break;
- case MATH_EXP: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::exp((double)*p_args[0]);
- } break;
- case MATH_ISNAN: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::is_nan((double)*p_args[0]);
- } break;
- case MATH_ISINF: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::is_inf((double)*p_args[0]);
- } break;
- case MATH_ISEQUALAPPROX: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::is_equal_approx((real_t)*p_args[0], (real_t)*p_args[1]);
- } break;
- case MATH_ISZEROAPPROX: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::is_zero_approx((real_t)*p_args[0]);
- } break;
- case MATH_EASE: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::ease((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_STEP_DECIMALS: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::step_decimals((double)*p_args[0]);
- } break;
- case MATH_STEPIFY: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::stepify((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_LERP: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(2);
- const double t = (double)*p_args[2];
- switch (p_args[0]->get_type() == p_args[1]->get_type() ? p_args[0]->get_type() : Variant::FLOAT) {
- case Variant::VECTOR2: {
- r_ret = ((Vector2)*p_args[0]).lerp((Vector2)*p_args[1], t);
- } break;
- case Variant::VECTOR3: {
- r_ret = (p_args[0]->operator Vector3()).lerp(p_args[1]->operator Vector3(), t);
- } break;
- case Variant::COLOR: {
- r_ret = ((Color)*p_args[0]).lerp((Color)*p_args[1], t);
- } break;
- default: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::lerp((double)*p_args[0], (double)*p_args[1], t);
- } break;
- }
- } break;
- case MATH_LERP_ANGLE: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::lerp_angle((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_INVERSE_LERP: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::inverse_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_RANGE_LERP: {
- VALIDATE_ARG_COUNT(5);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- VALIDATE_ARG_NUM(3);
- VALIDATE_ARG_NUM(4);
- r_ret = Math::range_lerp((double)*p_args[0], (double)*p_args[1], (double)*p_args[2], (double)*p_args[3], (double)*p_args[4]);
- } break;
- case MATH_SMOOTHSTEP: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::smoothstep((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_MOVE_TOWARD: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::move_toward((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_DECTIME: {
- VALIDATE_ARG_COUNT(3);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- r_ret = Math::dectime((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case MATH_RANDOMIZE: {
- VALIDATE_ARG_COUNT(0);
- Math::randomize();
- r_ret = Variant();
- } break;
- case MATH_RAND: {
- VALIDATE_ARG_COUNT(0);
- r_ret = Math::rand();
- } break;
- case MATH_RANDF: {
- VALIDATE_ARG_COUNT(0);
- r_ret = Math::randf();
- } break;
- case MATH_RANDOM: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- r_ret = Math::random((double)*p_args[0], (double)*p_args[1]);
- } break;
- case MATH_SEED: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- uint64_t seed = *p_args[0];
- Math::seed(seed);
- r_ret = Variant();
- } break;
- case MATH_RANDSEED: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- uint64_t seed = *p_args[0];
- int ret = Math::rand_from_seed(&seed);
- Array reta;
- reta.push_back(ret);
- reta.push_back(seed);
- r_ret = reta;
-
- } break;
- case MATH_DEG2RAD: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::deg2rad((double)*p_args[0]);
- } break;
- case MATH_RAD2DEG: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::rad2deg((double)*p_args[0]);
- } break;
- case MATH_LINEAR2DB: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::linear2db((double)*p_args[0]);
- } break;
- case MATH_DB2LINEAR: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- r_ret = Math::db2linear((double)*p_args[0]);
- } break;
- case MATH_POLAR2CARTESIAN: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- double r = *p_args[0];
- double th = *p_args[1];
- r_ret = Vector2(r * Math::cos(th), r * Math::sin(th));
- } break;
- case MATH_CARTESIAN2POLAR: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- double x = *p_args[0];
- double y = *p_args[1];
- r_ret = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x));
- } break;
- case MATH_WRAP: {
- VALIDATE_ARG_COUNT(3);
- r_ret = Math::wrapi((int64_t)*p_args[0], (int64_t)*p_args[1], (int64_t)*p_args[2]);
- } break;
- case MATH_WRAPF: {
- VALIDATE_ARG_COUNT(3);
- r_ret = Math::wrapf((double)*p_args[0], (double)*p_args[1], (double)*p_args[2]);
- } break;
- case LOGIC_MAX: {
- VALIDATE_ARG_COUNT(2);
- if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT) {
- int64_t a = *p_args[0];
- int64_t b = *p_args[1];
- r_ret = MAX(a, b);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
-
- real_t a = *p_args[0];
- real_t b = *p_args[1];
-
- r_ret = MAX(a, b);
- }
-
- } break;
- case LOGIC_MIN: {
- VALIDATE_ARG_COUNT(2);
- if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT) {
- int64_t a = *p_args[0];
- int64_t b = *p_args[1];
- r_ret = MIN(a, b);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
-
- real_t a = *p_args[0];
- real_t b = *p_args[1];
-
- r_ret = MIN(a, b);
- }
- } break;
- case LOGIC_CLAMP: {
- VALIDATE_ARG_COUNT(3);
- if (p_args[0]->get_type() == Variant::INT && p_args[1]->get_type() == Variant::INT && p_args[2]->get_type() == Variant::INT) {
- int64_t a = *p_args[0];
- int64_t b = *p_args[1];
- int64_t c = *p_args[2];
- r_ret = CLAMP(a, b, c);
- } else {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
-
- real_t a = *p_args[0];
- real_t b = *p_args[1];
- real_t c = *p_args[2];
-
- r_ret = CLAMP(a, b, c);
- }
- } break;
- case LOGIC_NEAREST_PO2: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- int64_t num = *p_args[0];
- r_ret = next_power_of_2(num);
- } break;
- case OBJ_WEAKREF: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() == Variant::OBJECT) {
- if (p_args[0]->is_ref()) {
- Ref<WeakRef> wref = memnew(WeakRef);
- REF r = *p_args[0];
- if (r.is_valid()) {
- wref->set_ref(r);
- }
- r_ret = wref;
- } else {
- Ref<WeakRef> wref = memnew(WeakRef);
- Object *obj = *p_args[0];
- if (obj) {
- wref->set_obj(obj);
- }
- r_ret = wref;
- }
- } else if (p_args[0]->get_type() == Variant::NIL) {
- Ref<WeakRef> wref = memnew(WeakRef);
- r_ret = wref;
- } else {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- return;
- }
- } break;
- case FUNC_FUNCREF: {
- VALIDATE_ARG_COUNT(2);
- if (p_args[0]->get_type() != Variant::OBJECT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- return;
- }
- if (p_args[1]->get_type() != Variant::STRING && p_args[1]->get_type() != Variant::NODE_PATH) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- Ref<FuncRef> fr = memnew(FuncRef);
-
- fr->set_instance(*p_args[0]);
- fr->set_function(*p_args[1]);
-
- r_ret = fr;
-
- } break;
- case TYPE_CONVERT: {
- VALIDATE_ARG_COUNT(2);
- VALIDATE_ARG_NUM(1);
- int type = *p_args[1];
- if (type < 0 || type >= Variant::VARIANT_MAX) {
- r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::INT;
- return;
-
- } else {
- r_ret = Variant::construct(Variant::Type(type), p_args, 1, r_error);
- }
- } break;
- case TYPE_OF: {
- VALIDATE_ARG_COUNT(1);
- r_ret = p_args[0]->get_type();
-
- } break;
- case TYPE_EXISTS: {
- VALIDATE_ARG_COUNT(1);
- r_ret = ClassDB::class_exists(*p_args[0]);
-
- } break;
- case TEXT_CHAR: {
- VALIDATE_ARG_COUNT(1);
- VALIDATE_ARG_NUM(0);
- char32_t result[2] = { *p_args[0], 0 };
- r_ret = String(result);
- } break;
- case TEXT_ORD: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- String str = p_args[0]->operator String();
-
- if (str.length() != 1) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = RTR("Expected a string of length 1 (a character).");
- return;
- }
-
- r_ret = str.get(0);
-
- } break;
- case TEXT_STR: {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
-
- return;
- }
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- String os = p_args[i]->operator String();
-
- if (i == 0) {
- str = os;
- } else {
- str += os;
- }
- }
-
- r_ret = str;
-
- } break;
- case TEXT_PRINT: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- str += p_args[i]->operator String();
- }
-
- print_line(str);
- r_ret = Variant();
-
- } break;
- case TEXT_PRINT_TABBED: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- if (i) {
- str += "\t";
- }
- str += p_args[i]->operator String();
- }
-
- print_line(str);
- r_ret = Variant();
-
- } break;
- case TEXT_PRINT_SPACED: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- if (i) {
- str += " ";
- }
- str += p_args[i]->operator String();
- }
-
- print_line(str);
- r_ret = Variant();
-
- } break;
-
- case TEXT_PRINTERR: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- str += p_args[i]->operator String();
- }
-
- print_error(str);
- r_ret = Variant();
-
- } break;
- case TEXT_PRINTRAW: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- str += p_args[i]->operator String();
- }
-
- OS::get_singleton()->print("%s", str.utf8().get_data());
- r_ret = Variant();
-
- } break;
- case TEXT_PRINT_DEBUG: {
- String str;
- for (int i = 0; i < p_arg_count; i++) {
- 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) + "()";
- }
-
- print_line(str);
- r_ret = Variant();
- } break;
- case PUSH_ERROR: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- break;
- }
-
- String message = *p_args[0];
- ERR_PRINT(message);
- r_ret = Variant();
- } break;
- case PUSH_WARNING: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- break;
- }
-
- String message = *p_args[0];
- WARN_PRINT(message);
- r_ret = Variant();
- } break;
- case VAR_TO_STR: {
- VALIDATE_ARG_COUNT(1);
- String vars;
- VariantWriter::write_to_string(*p_args[0], vars);
- r_ret = vars;
- } break;
- case STR_TO_VAR: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
- r_ret = *p_args[0];
-
- VariantParser::StreamString ss;
- ss.s = *p_args[0];
-
- String errs;
- int line;
- (void)VariantParser::parse(&ss, r_ret, errs, line);
- } break;
- case VAR_TO_BYTES: {
- bool full_objects = false;
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
- return;
- } else if (p_arg_count > 2) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 2;
- r_ret = Variant();
- } else if (p_arg_count == 2) {
- if (p_args[1]->get_type() != Variant::BOOL) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::BOOL;
- r_ret = Variant();
- return;
- }
- full_objects = *p_args[1];
- }
-
- PackedByteArray barr;
- int len;
- Error err = encode_variant(*p_args[0], nullptr, len, full_objects);
- if (err) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::NIL;
- r_ret = "Unexpected error encoding variable to bytes, likely unserializable type found (Object or RID).";
- return;
- }
-
- barr.resize(len);
- {
- uint8_t *w = barr.ptrw();
- encode_variant(*p_args[0], w, len, full_objects);
- }
- r_ret = barr;
- } break;
- case BYTES_TO_VAR: {
- bool allow_objects = false;
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
- return;
- } else if (p_arg_count > 2) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 2;
- r_ret = Variant();
- } else if (p_arg_count == 2) {
- if (p_args[1]->get_type() != Variant::BOOL) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::BOOL;
- r_ret = Variant();
- return;
- }
- allow_objects = *p_args[1];
- }
-
- if (p_args[0]->get_type() != Variant::PACKED_BYTE_ARRAY) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::PACKED_BYTE_ARRAY;
- r_ret = Variant();
- return;
- }
-
- PackedByteArray varr = *p_args[0];
- Variant ret;
- {
- const uint8_t *r = varr.ptr();
- Error err = decode_variant(ret, r, varr.size(), nullptr, allow_objects);
- if (err != OK) {
- r_ret = RTR("Not enough bytes for decoding bytes, or invalid format.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::PACKED_BYTE_ARRAY;
- return;
- }
- }
-
- r_ret = ret;
-
- } break;
- case GEN_RANGE: {
- switch (p_arg_count) {
- case 0: {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_error.expected = 1;
- r_ret = Variant();
-
- } break;
- case 1: {
- VALIDATE_ARG_NUM(0);
- int count = *p_args[0];
- Array arr;
- if (count <= 0) {
- r_ret = arr;
- return;
- }
- Error err = arr.resize(count);
- if (err != OK) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_ret = Variant();
- return;
- }
-
- for (int i = 0; i < count; i++) {
- arr[i] = i;
- }
-
- r_ret = arr;
- } break;
- case 2: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
-
- int from = *p_args[0];
- int to = *p_args[1];
-
- Array arr;
- if (from >= to) {
- r_ret = arr;
- return;
- }
- Error err = arr.resize(to - from);
- if (err != OK) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_ret = Variant();
- return;
- }
- for (int i = from; i < to; i++) {
- arr[i - from] = i;
- }
- r_ret = arr;
- } break;
- case 3: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
-
- int from = *p_args[0];
- int to = *p_args[1];
- int incr = *p_args[2];
- if (incr == 0) {
- r_ret = RTR("Step argument is zero!");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return;
- }
-
- Array arr;
- if (from >= to && incr > 0) {
- r_ret = arr;
- return;
- }
- if (from <= to && incr < 0) {
- r_ret = arr;
- return;
- }
-
- //calculate how many
- int count = 0;
- if (incr > 0) {
- count = ((to - from - 1) / incr) + 1;
- } else {
- count = ((from - to - 1) / -incr) + 1;
- }
-
- Error err = arr.resize(count);
-
- if (err != OK) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- r_ret = Variant();
- return;
- }
-
- if (incr > 0) {
- int idx = 0;
- for (int i = from; i < to; i += incr) {
- arr[idx++] = i;
- }
- } else {
- int idx = 0;
- for (int i = from; i > to; i += incr) {
- arr[idx++] = i;
- }
- }
-
- r_ret = arr;
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 3;
- r_error.expected = 3;
- r_ret = Variant();
-
- } break;
- }
-
- } break;
- case RESOURCE_LOAD: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- } else {
- r_ret = ResourceLoader::load(*p_args[0]);
- }
-
- } break;
- case INST2DICT: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() == Variant::NIL) {
- r_ret = Variant();
- } else if (p_args[0]->get_type() != Variant::OBJECT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_ret = Variant();
- } else {
- Object *obj = *p_args[0];
- if (!obj) {
- r_ret = Variant();
-
- } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = RTR("Not a script with an instance");
- return;
- } else {
- GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
- Ref<GDScript> base = ins->get_script();
- if (base.is_null()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = RTR("Not based on a script");
- return;
- }
-
- GDScript *p = base.ptr();
- Vector<StringName> sname;
-
- while (p->_owner) {
- sname.push_back(p->name);
- p = p->_owner;
- }
- sname.invert();
-
- if (!p->path.is_resource_file()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = Variant();
-
- r_ret = RTR("Not based on a resource file");
-
- return;
- }
-
- NodePath cp(sname, Vector<StringName>(), false);
-
- Dictionary d;
- 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];
- }
- }
- r_ret = d;
- }
- }
-
- } break;
- case DICT2INST: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::DICTIONARY) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::DICTIONARY;
- r_ret = Variant();
-
- return;
- }
-
- Dictionary d = *p_args[0];
-
- if (!d.has("@path")) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = RTR("Invalid instance dictionary format (missing @path)");
-
- return;
- }
-
- Ref<Script> scr = ResourceLoader::load(d["@path"]);
- if (!scr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = RTR("Invalid instance dictionary format (can't load script at @path)");
- return;
- }
-
- Ref<GDScript> gdscr = scr;
-
- if (!gdscr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- r_ret = RTR("Invalid instance dictionary format (invalid script at @path)");
- return;
- }
-
- NodePath sub;
- if (d.has("@subpath")) {
- sub = d["@subpath"];
- }
-
- for (int i = 0; i < sub.get_name_count(); i++) {
- gdscr = gdscr->subclasses[sub.get_name(i)];
- if (!gdscr.is_valid()) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- r_ret = RTR("Invalid instance dictionary (invalid subclasses)");
- return;
- }
- }
- r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error);
-
- if (r_error.error != Callable::CallError::CALL_OK) {
- r_ret = Variant();
- return;
- }
-
- 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()];
- }
- }
-
- } break;
- case VALIDATE_JSON: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- String errs;
- int errl;
-
- Error err = JSON::parse(*p_args[0], r_ret, errs, errl);
-
- if (err != OK) {
- r_ret = itos(errl) + ":" + errs;
- } else {
- r_ret = "";
- }
-
- } break;
- case PARSE_JSON: {
- VALIDATE_ARG_COUNT(1);
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::STRING;
- r_ret = Variant();
- return;
- }
-
- String errs;
- int errl;
-
- Error err = JSON::parse(*p_args[0], r_ret, errs, errl);
-
- if (err != OK) {
- r_ret = Variant();
- ERR_PRINT(vformat("Error parsing JSON at line %s: %s", errl, errs));
- }
-
- } break;
- case TO_JSON: {
- VALIDATE_ARG_COUNT(1);
-
- r_ret = JSON::print(*p_args[0]);
- } break;
- case HASH: {
- VALIDATE_ARG_COUNT(1);
- r_ret = p_args[0]->hash();
-
- } break;
- case COLOR8: {
- if (p_arg_count < 3) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 3;
- r_ret = Variant();
-
- return;
- }
- if (p_arg_count > 4) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 4;
- r_ret = Variant();
-
- return;
- }
-
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
-
- Color color((float)*p_args[0] / 255.0f, (float)*p_args[1] / 255.0f, (float)*p_args[2] / 255.0f);
-
- if (p_arg_count == 4) {
- VALIDATE_ARG_NUM(3);
- color.a = (float)*p_args[3] / 255.0f;
- }
-
- r_ret = color;
-
- } break;
- case COLORN: {
- if (p_arg_count < 1) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
- r_error.argument = 1;
- r_ret = Variant();
- return;
- }
-
- if (p_arg_count > 2) {
- r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
- r_error.argument = 2;
- r_ret = Variant();
- return;
- }
-
- if (p_args[0]->get_type() != Variant::STRING) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_ret = Variant();
- } else {
- Color color = Color::named(*p_args[0]);
- if (p_arg_count == 2) {
- VALIDATE_ARG_NUM(1);
- color.a = *p_args[1];
- }
- r_ret = color;
- }
-
- } break;
-
- case PRINT_STACK: {
- VALIDATE_ARG_COUNT(0);
-
- 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) + "'");
- };
- } break;
-
- case GET_STACK: {
- VALIDATE_ARG_COUNT(0);
-
- ScriptLanguage *script = GDScriptLanguage::get_singleton();
- Array ret;
- for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
- Dictionary frame;
- frame["source"] = script->debug_get_stack_level_source(i);
- frame["function"] = script->debug_get_stack_level_function(i);
- frame["line"] = script->debug_get_stack_level_line(i);
- ret.push_back(frame);
- };
- r_ret = ret;
- } break;
-
- case INSTANCE_FROM_ID: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::INT && p_args[0]->get_type() != Variant::FLOAT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::INT;
- r_ret = Variant();
- break;
- }
-
- ObjectID id = *p_args[0];
- r_ret = ObjectDB::get_instance(id);
-
- } break;
- case LEN: {
- VALIDATE_ARG_COUNT(1);
- switch (p_args[0]->get_type()) {
- case Variant::STRING: {
- String d = *p_args[0];
- r_ret = d.length();
- } break;
- case Variant::DICTIONARY: {
- Dictionary d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::ARRAY: {
- Array d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_BYTE_ARRAY: {
- Vector<uint8_t> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_INT32_ARRAY: {
- Vector<int32_t> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_INT64_ARRAY: {
- Vector<int64_t> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_FLOAT32_ARRAY: {
- Vector<float> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_FLOAT64_ARRAY: {
- Vector<double> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_STRING_ARRAY: {
- Vector<String> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_VECTOR2_ARRAY: {
- Vector<Vector2> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_VECTOR3_ARRAY: {
- Vector<Vector3> d = *p_args[0];
- r_ret = d.size();
- } break;
- case Variant::PACKED_COLOR_ARRAY: {
- Vector<Color> d = *p_args[0];
- r_ret = d.size();
- } break;
- default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
- r_ret = Variant();
- r_ret = RTR("Object can't provide a length.");
- }
- }
-
- } break;
- case IS_INSTANCE_VALID: {
- VALIDATE_ARG_COUNT(1);
- if (p_args[0]->get_type() != Variant::OBJECT) {
- r_ret = false;
- } else {
- Object *obj = p_args[0]->get_validated_object();
- r_ret = obj != nullptr;
- }
-
- } break;
- case FUNC_MAX: {
- ERR_FAIL();
- } break;
- }
-}
-
-bool GDScriptFunctions::is_deterministic(Function p_func) {
- //man i couldn't have chosen a worse function name,
- //way too controversial..
-
- switch (p_func) {
- case MATH_SIN:
- case MATH_COS:
- case MATH_TAN:
- case MATH_SINH:
- case MATH_COSH:
- case MATH_TANH:
- case MATH_ASIN:
- case MATH_ACOS:
- case MATH_ATAN:
- case MATH_ATAN2:
- case MATH_SQRT:
- case MATH_FMOD:
- case MATH_FPOSMOD:
- case MATH_POSMOD:
- case MATH_FLOOR:
- case MATH_CEIL:
- case MATH_ROUND:
- case MATH_ABS:
- case MATH_SIGN:
- case MATH_POW:
- case MATH_LOG:
- case MATH_EXP:
- case MATH_ISNAN:
- case MATH_ISINF:
- case MATH_EASE:
- case MATH_STEP_DECIMALS:
- case MATH_STEPIFY:
- case MATH_LERP:
- case MATH_INVERSE_LERP:
- case MATH_RANGE_LERP:
- case MATH_SMOOTHSTEP:
- case MATH_MOVE_TOWARD:
- case MATH_DECTIME:
- case MATH_DEG2RAD:
- case MATH_RAD2DEG:
- case MATH_LINEAR2DB:
- case MATH_DB2LINEAR:
- case MATH_POLAR2CARTESIAN:
- case MATH_CARTESIAN2POLAR:
- case MATH_WRAP:
- case MATH_WRAPF:
- case LOGIC_MAX:
- case LOGIC_MIN:
- case LOGIC_CLAMP:
- case LOGIC_NEAREST_PO2:
- case TYPE_CONVERT:
- case TYPE_OF:
- case TYPE_EXISTS:
- case TEXT_CHAR:
- case TEXT_ORD:
- case TEXT_STR:
- case COLOR8:
- case LEN:
- // enable for debug only, otherwise not desirable - case GEN_RANGE:
- return true;
- default:
- return false;
- }
-
- return false;
-}
-
-MethodInfo GDScriptFunctions::get_info(Function p_func) {
-#ifdef DEBUG_ENABLED
- //using a switch, so the compiler generates a jumptable
-
- switch (p_func) {
- case MATH_SIN: {
- MethodInfo mi("sin", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
-
- } break;
- case MATH_COS: {
- MethodInfo mi("cos", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_TAN: {
- MethodInfo mi("tan", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SINH: {
- MethodInfo mi("sinh", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_COSH: {
- MethodInfo mi("cosh", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_TANH: {
- MethodInfo mi("tanh", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ASIN: {
- MethodInfo mi("asin", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ACOS: {
- MethodInfo mi("acos", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ATAN: {
- MethodInfo mi("atan", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ATAN2: {
- MethodInfo mi("atan2", PropertyInfo(Variant::FLOAT, "y"), PropertyInfo(Variant::FLOAT, "x"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SQRT: {
- MethodInfo mi("sqrt", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_FMOD: {
- MethodInfo mi("fmod", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_FPOSMOD: {
- MethodInfo mi("fposmod", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_POSMOD: {
- MethodInfo mi("posmod", PropertyInfo(Variant::INT, "a"), PropertyInfo(Variant::INT, "b"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_FLOOR: {
- MethodInfo mi("floor", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_CEIL: {
- MethodInfo mi("ceil", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ROUND: {
- MethodInfo mi("round", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ABS: {
- MethodInfo mi("abs", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SIGN: {
- MethodInfo mi("sign", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_POW: {
- MethodInfo mi("pow", PropertyInfo(Variant::FLOAT, "base"), PropertyInfo(Variant::FLOAT, "exp"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_LOG: {
- MethodInfo mi("log", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_EXP: {
- MethodInfo mi("exp", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_ISNAN: {
- MethodInfo mi("is_nan", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_ISINF: {
- MethodInfo mi("is_inf", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_ISEQUALAPPROX: {
- MethodInfo mi("is_equal_approx", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_ISZEROAPPROX: {
- MethodInfo mi("is_zero_approx", PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- case MATH_EASE: {
- MethodInfo mi("ease", PropertyInfo(Variant::FLOAT, "s"), PropertyInfo(Variant::FLOAT, "curve"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_STEP_DECIMALS: {
- MethodInfo mi("step_decimals", PropertyInfo(Variant::FLOAT, "step"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_STEPIFY: {
- MethodInfo mi("stepify", PropertyInfo(Variant::FLOAT, "s"), PropertyInfo(Variant::FLOAT, "step"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_LERP: {
- MethodInfo mi("lerp", PropertyInfo(Variant::NIL, "from"), PropertyInfo(Variant::NIL, "to"), PropertyInfo(Variant::FLOAT, "weight"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case MATH_LERP_ANGLE: {
- MethodInfo mi("lerp_angle", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "weight"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_INVERSE_LERP: {
- MethodInfo mi("inverse_lerp", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "weight"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RANGE_LERP: {
- MethodInfo mi("range_lerp", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "istart"), PropertyInfo(Variant::FLOAT, "istop"), PropertyInfo(Variant::FLOAT, "ostart"), PropertyInfo(Variant::FLOAT, "ostop"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SMOOTHSTEP: {
- MethodInfo mi("smoothstep", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "s"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_MOVE_TOWARD: {
- MethodInfo mi("move_toward", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"), PropertyInfo(Variant::FLOAT, "delta"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_DECTIME: {
- MethodInfo mi("dectime", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "amount"), PropertyInfo(Variant::FLOAT, "step"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RANDOMIZE: {
- MethodInfo mi("randomize");
- mi.return_val.type = Variant::NIL;
- return mi;
- } break;
- case MATH_RAND: {
- MethodInfo mi("randi");
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_RANDF: {
- MethodInfo mi("randf");
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RANDOM: {
- MethodInfo mi("rand_range", PropertyInfo(Variant::FLOAT, "from"), PropertyInfo(Variant::FLOAT, "to"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_SEED: {
- MethodInfo mi("seed", PropertyInfo(Variant::INT, "seed"));
- mi.return_val.type = Variant::NIL;
- return mi;
- } break;
- case MATH_RANDSEED: {
- MethodInfo mi("rand_seed", PropertyInfo(Variant::INT, "seed"));
- mi.return_val.type = Variant::ARRAY;
- return mi;
- } break;
- case MATH_DEG2RAD: {
- MethodInfo mi("deg2rad", PropertyInfo(Variant::FLOAT, "deg"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_RAD2DEG: {
- MethodInfo mi("rad2deg", PropertyInfo(Variant::FLOAT, "rad"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_LINEAR2DB: {
- MethodInfo mi("linear2db", PropertyInfo(Variant::FLOAT, "nrg"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_DB2LINEAR: {
- MethodInfo mi("db2linear", PropertyInfo(Variant::FLOAT, "db"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case MATH_POLAR2CARTESIAN: {
- MethodInfo mi("polar2cartesian", PropertyInfo(Variant::FLOAT, "r"), PropertyInfo(Variant::FLOAT, "th"));
- mi.return_val.type = Variant::VECTOR2;
- return mi;
- } break;
- case MATH_CARTESIAN2POLAR: {
- MethodInfo mi("cartesian2polar", PropertyInfo(Variant::FLOAT, "x"), PropertyInfo(Variant::FLOAT, "y"));
- mi.return_val.type = Variant::VECTOR2;
- return mi;
- } break;
- case MATH_WRAP: {
- MethodInfo mi("wrapi", PropertyInfo(Variant::INT, "value"), PropertyInfo(Variant::INT, "min"), PropertyInfo(Variant::INT, "max"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case MATH_WRAPF: {
- MethodInfo mi("wrapf", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case LOGIC_MAX: {
- MethodInfo mi("max", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
-
- } break;
- case LOGIC_MIN: {
- MethodInfo mi("min", PropertyInfo(Variant::FLOAT, "a"), PropertyInfo(Variant::FLOAT, "b"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case LOGIC_CLAMP: {
- MethodInfo mi("clamp", PropertyInfo(Variant::FLOAT, "value"), PropertyInfo(Variant::FLOAT, "min"), PropertyInfo(Variant::FLOAT, "max"));
- mi.return_val.type = Variant::FLOAT;
- return mi;
- } break;
- case LOGIC_NEAREST_PO2: {
- MethodInfo mi("nearest_po2", PropertyInfo(Variant::INT, "value"));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case OBJ_WEAKREF: {
- MethodInfo mi("weakref", PropertyInfo(Variant::OBJECT, "obj"));
- mi.return_val.type = Variant::OBJECT;
- mi.return_val.class_name = "WeakRef";
-
- return mi;
-
- } break;
- case FUNC_FUNCREF: {
- MethodInfo mi("funcref", PropertyInfo(Variant::OBJECT, "instance"), PropertyInfo(Variant::STRING, "funcname"));
- mi.return_val.type = Variant::OBJECT;
- mi.return_val.class_name = "FuncRef";
- return mi;
-
- } break;
- case TYPE_CONVERT: {
- MethodInfo mi("convert", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::INT, "type"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case TYPE_OF: {
- MethodInfo mi("typeof", PropertyInfo(Variant::NIL, "what", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::INT;
- return mi;
-
- } break;
- case TYPE_EXISTS: {
- MethodInfo mi("type_exists", PropertyInfo(Variant::STRING, "type"));
- mi.return_val.type = Variant::BOOL;
- return mi;
-
- } break;
- case TEXT_CHAR: {
- MethodInfo mi("char", PropertyInfo(Variant::INT, "code"));
- mi.return_val.type = Variant::STRING;
- return mi;
-
- } break;
- case TEXT_ORD: {
- MethodInfo mi("ord", PropertyInfo(Variant::STRING, "char"));
- mi.return_val.type = Variant::INT;
- return mi;
-
- } break;
- case TEXT_STR: {
- MethodInfo mi("str");
- mi.return_val.type = Variant::STRING;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT: {
- MethodInfo mi("print");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT_TABBED: {
- MethodInfo mi("printt");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT_SPACED: {
- MethodInfo mi("prints");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINTERR: {
- MethodInfo mi("printerr");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINTRAW: {
- MethodInfo mi("printraw");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case TEXT_PRINT_DEBUG: {
- MethodInfo mi("print_debug");
- mi.return_val.type = Variant::NIL;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
-
- } break;
- case PUSH_ERROR: {
- MethodInfo mi(Variant::NIL, "push_error", PropertyInfo(Variant::STRING, "message"));
- mi.return_val.type = Variant::NIL;
- return mi;
-
- } break;
- case PUSH_WARNING: {
- MethodInfo mi(Variant::NIL, "push_warning", PropertyInfo(Variant::STRING, "message"));
- mi.return_val.type = Variant::NIL;
- return mi;
-
- } break;
- case VAR_TO_STR: {
- MethodInfo mi("var2str", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::STRING;
- return mi;
- } break;
- case STR_TO_VAR: {
- MethodInfo mi(Variant::NIL, "str2var", PropertyInfo(Variant::STRING, "string"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case VAR_TO_BYTES: {
- MethodInfo mi("var2bytes", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT), PropertyInfo(Variant::BOOL, "full_objects"));
- mi.default_arguments.push_back(false);
- mi.return_val.type = Variant::PACKED_BYTE_ARRAY;
- return mi;
- } break;
- case BYTES_TO_VAR: {
- MethodInfo mi(Variant::NIL, "bytes2var", PropertyInfo(Variant::PACKED_BYTE_ARRAY, "bytes"), PropertyInfo(Variant::BOOL, "allow_objects"));
- mi.default_arguments.push_back(false);
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case GEN_RANGE: {
- MethodInfo mi("range");
- mi.return_val.type = Variant::ARRAY;
- mi.flags |= METHOD_FLAG_VARARG;
- return mi;
- } break;
- case RESOURCE_LOAD: {
- MethodInfo mi("load", PropertyInfo(Variant::STRING, "path"));
- mi.return_val.type = Variant::OBJECT;
- mi.return_val.class_name = "Resource";
- return mi;
- } break;
- case INST2DICT: {
- MethodInfo mi("inst2dict", PropertyInfo(Variant::OBJECT, "inst"));
- mi.return_val.type = Variant::DICTIONARY;
- return mi;
- } break;
- case DICT2INST: {
- MethodInfo mi("dict2inst", PropertyInfo(Variant::DICTIONARY, "dict"));
- mi.return_val.type = Variant::OBJECT;
- return mi;
- } break;
- case VALIDATE_JSON: {
- MethodInfo mi("validate_json", PropertyInfo(Variant::STRING, "json"));
- mi.return_val.type = Variant::STRING;
- return mi;
- } break;
- case PARSE_JSON: {
- MethodInfo mi(Variant::NIL, "parse_json", PropertyInfo(Variant::STRING, "json"));
- mi.return_val.type = Variant::NIL;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
- } break;
- case TO_JSON: {
- MethodInfo mi("to_json", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::STRING;
- return mi;
- } break;
- case HASH: {
- MethodInfo mi("hash", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case COLOR8: {
- MethodInfo mi("Color8", PropertyInfo(Variant::INT, "r8"), PropertyInfo(Variant::INT, "g8"), PropertyInfo(Variant::INT, "b8"), PropertyInfo(Variant::INT, "a8"));
- mi.default_arguments.push_back(255);
- mi.return_val.type = Variant::COLOR;
- return mi;
- } break;
- case COLORN: {
- MethodInfo mi("ColorN", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::FLOAT, "alpha"));
- mi.default_arguments.push_back(1.0f);
- mi.return_val.type = Variant::COLOR;
- return mi;
- } break;
-
- case PRINT_STACK: {
- MethodInfo mi("print_stack");
- mi.return_val.type = Variant::NIL;
- return mi;
- } break;
- case GET_STACK: {
- MethodInfo mi("get_stack");
- mi.return_val.type = Variant::ARRAY;
- return mi;
- } break;
-
- case INSTANCE_FROM_ID: {
- MethodInfo mi("instance_from_id", PropertyInfo(Variant::INT, "instance_id"));
- mi.return_val.type = Variant::OBJECT;
- return mi;
- } break;
- case LEN: {
- MethodInfo mi("len", PropertyInfo(Variant::NIL, "var", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT));
- mi.return_val.type = Variant::INT;
- return mi;
- } break;
- case IS_INSTANCE_VALID: {
- MethodInfo mi("is_instance_valid", PropertyInfo(Variant::OBJECT, "instance"));
- mi.return_val.type = Variant::BOOL;
- return mi;
- } break;
- default: {
- ERR_FAIL_V(MethodInfo());
- } break;
- }
-#endif
- MethodInfo mi;
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- return mi;
-}
diff --git a/modules/gdscript/gdscript_functions.h b/modules/gdscript/gdscript_functions.h
deleted file mode 100644
index 2c6dc02913..0000000000
--- a/modules/gdscript/gdscript_functions.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/*************************************************************************/
-/* gdscript_functions.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 GDSCRIPT_FUNCTIONS_H
-#define GDSCRIPT_FUNCTIONS_H
-
-#include "core/variant.h"
-
-class GDScriptFunctions {
-public:
- enum Function {
- MATH_SIN,
- MATH_COS,
- MATH_TAN,
- MATH_SINH,
- MATH_COSH,
- MATH_TANH,
- MATH_ASIN,
- MATH_ACOS,
- MATH_ATAN,
- MATH_ATAN2,
- MATH_SQRT,
- MATH_FMOD,
- MATH_FPOSMOD,
- MATH_POSMOD,
- MATH_FLOOR,
- MATH_CEIL,
- MATH_ROUND,
- MATH_ABS,
- MATH_SIGN,
- MATH_POW,
- MATH_LOG,
- MATH_EXP,
- MATH_ISNAN,
- MATH_ISINF,
- MATH_ISEQUALAPPROX,
- MATH_ISZEROAPPROX,
- MATH_EASE,
- MATH_STEP_DECIMALS,
- MATH_STEPIFY,
- MATH_LERP,
- MATH_LERP_ANGLE,
- MATH_INVERSE_LERP,
- MATH_RANGE_LERP,
- MATH_SMOOTHSTEP,
- MATH_MOVE_TOWARD,
- MATH_DECTIME,
- MATH_RANDOMIZE,
- MATH_RAND,
- MATH_RANDF,
- MATH_RANDOM,
- MATH_SEED,
- MATH_RANDSEED,
- MATH_DEG2RAD,
- MATH_RAD2DEG,
- MATH_LINEAR2DB,
- MATH_DB2LINEAR,
- MATH_POLAR2CARTESIAN,
- MATH_CARTESIAN2POLAR,
- MATH_WRAP,
- MATH_WRAPF,
- LOGIC_MAX,
- LOGIC_MIN,
- LOGIC_CLAMP,
- LOGIC_NEAREST_PO2,
- OBJ_WEAKREF,
- FUNC_FUNCREF,
- TYPE_CONVERT,
- TYPE_OF,
- TYPE_EXISTS,
- TEXT_CHAR,
- TEXT_ORD,
- TEXT_STR,
- TEXT_PRINT,
- TEXT_PRINT_TABBED,
- TEXT_PRINT_SPACED,
- TEXT_PRINTERR,
- TEXT_PRINTRAW,
- TEXT_PRINT_DEBUG,
- PUSH_ERROR,
- PUSH_WARNING,
- VAR_TO_STR,
- STR_TO_VAR,
- VAR_TO_BYTES,
- BYTES_TO_VAR,
- GEN_RANGE,
- RESOURCE_LOAD,
- INST2DICT,
- DICT2INST,
- VALIDATE_JSON,
- PARSE_JSON,
- TO_JSON,
- HASH,
- COLOR8,
- COLORN,
- PRINT_STACK,
- GET_STACK,
- INSTANCE_FROM_ID,
- LEN,
- IS_INSTANCE_VALID,
- FUNC_MAX
- };
-
- static const char *get_func_name(Function p_func);
- static void call(Function p_func, const Variant **p_args, int p_arg_count, Variant &r_ret, Callable::CallError &r_error);
- static bool is_deterministic(Function p_func);
- static MethodInfo get_info(Function p_func);
-};
-
-#endif // GDSCRIPT_FUNCTIONS_H
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
new file mode 100644
index 0000000000..0bc109b6e1
--- /dev/null
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -0,0 +1,95 @@
+/*************************************************************************/
+/* gdscript_lambda_callable.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 "gdscript_lambda_callable.h"
+
+#include "core/templates/hashfuncs.h"
+#include "gdscript.h"
+
+bool GDScriptLambdaCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ // Lambda callables are only compared by reference.
+ return p_a == p_b;
+}
+
+bool GDScriptLambdaCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ // Lambda callables are only compared by reference.
+ return p_a < p_b;
+}
+
+uint32_t GDScriptLambdaCallable::hash() const {
+ return h;
+}
+
+String GDScriptLambdaCallable::get_as_text() const {
+ if (function->get_name() != StringName()) {
+ return function->get_name().operator String() + "(lambda)";
+ }
+ return "(anonymous lambda)";
+}
+
+CallableCustom::CompareEqualFunc GDScriptLambdaCallable::get_compare_equal_func() const {
+ return compare_equal;
+}
+
+CallableCustom::CompareLessFunc GDScriptLambdaCallable::get_compare_less_func() const {
+ return compare_less;
+}
+
+ObjectID GDScriptLambdaCallable::get_object() const {
+ return script->get_instance_id();
+}
+
+void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+ int captures_amount = captures.size();
+
+ if (captures_amount > 0) {
+ Vector<const Variant *> args;
+ args.resize(p_argcount + captures_amount);
+ for (int i = 0; i < captures_amount; i++) {
+ args.write[i] = &captures[i];
+ }
+ for (int i = 0; i < p_argcount; i++) {
+ args.write[i + captures_amount] = p_arguments[i];
+ }
+
+ r_return_value = function->call(nullptr, args.ptrw(), args.size(), r_call_error);
+ r_call_error.argument -= captures_amount;
+ } else {
+ r_return_value = function->call(nullptr, p_arguments, p_argcount, r_call_error);
+ }
+}
+
+GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures) {
+ script = p_script;
+ function = p_function;
+ captures = p_captures;
+
+ h = (uint32_t)hash_djb2_one_64((uint64_t)this);
+}
diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h
new file mode 100644
index 0000000000..336778d549
--- /dev/null
+++ b/modules/gdscript/gdscript_lambda_callable.h
@@ -0,0 +1,65 @@
+/*************************************************************************/
+/* gdscript_lambda_callable.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 GDSCRIPT_LAMBDA_CALLABLE
+#define GDSCRIPT_LAMBDA_CALLABLE
+
+#include "core/object/ref_counted.h"
+#include "core/templates/vector.h"
+#include "core/variant/callable.h"
+#include "core/variant/variant.h"
+
+class GDScript;
+class GDScriptFunction;
+class GDScriptInstance;
+
+class GDScriptLambdaCallable : public CallableCustom {
+ GDScriptFunction *function = nullptr;
+ Ref<GDScript> script;
+ uint32_t h;
+
+ Vector<Variant> captures;
+
+ static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+ static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
+
+public:
+ uint32_t hash() const override;
+ String get_as_text() const override;
+ CompareEqualFunc get_compare_equal_func() const override;
+ CompareLessFunc get_compare_less_func() const override;
+ ObjectID get_object() const override;
+ void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
+
+ GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures);
+ virtual ~GDScriptLambdaCallable() = default;
+};
+
+#endif // GDSCRIPT_LAMBDA_CALLABLE
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 2a69db130b..bde6783322 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,15 +30,15 @@
#include "gdscript_parser.h"
+#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
-#include "core/os/file_access.h"
-#include "core/project_settings.h"
#include "gdscript.h"
#ifdef DEBUG_ENABLED
#include "core/os/os.h"
-#include "core/string_builder.h"
+#include "core/string/string_builder.h"
#endif // DEBUG_ENABLED
#ifdef TOOLS_ENABLED
@@ -47,7 +47,7 @@
static HashMap<StringName, Variant::Type> builtin_types;
Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
- if (builtin_types.empty()) {
+ if (builtin_types.is_empty()) {
builtin_types["bool"] = Variant::BOOL;
builtin_types["int"] = Variant::INT;
builtin_types["float"] = Variant::FLOAT;
@@ -61,11 +61,11 @@ Variant::Type GDScriptParser::get_builtin_type(const StringName &p_type) {
builtin_types["Vector3i"] = Variant::VECTOR3I;
builtin_types["AABB"] = Variant::AABB;
builtin_types["Plane"] = Variant::PLANE;
- builtin_types["Quat"] = Variant::QUAT;
+ builtin_types["Quaternion"] = Variant::QUATERNION;
builtin_types["Basis"] = Variant::BASIS;
- builtin_types["Transform"] = Variant::TRANSFORM;
+ builtin_types["Transform3D"] = Variant::TRANSFORM3D;
builtin_types["Color"] = Variant::COLOR;
- builtin_types["RID"] = Variant::_RID;
+ builtin_types["RID"] = Variant::RID;
builtin_types["Object"] = Variant::OBJECT;
builtin_types["StringName"] = Variant::STRING_NAME;
builtin_types["NodePath"] = Variant::NODE_PATH;
@@ -98,32 +98,22 @@ void GDScriptParser::cleanup() {
builtin_types.clear();
}
-GDScriptFunctions::Function GDScriptParser::get_builtin_function(const StringName &p_name) {
- for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) {
- if (p_name == GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))) {
- return GDScriptFunctions::Function(i);
- }
- }
- return GDScriptFunctions::FUNC_MAX;
-}
-
void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const {
List<StringName> keys;
valid_annotations.get_key_list(&keys);
- for (const List<StringName>::Element *E = keys.front(); E != nullptr; E = E->next()) {
- r_annotations->push_back(valid_annotations[E->get()].info);
+ for (const StringName &E : keys) {
+ r_annotations->push_back(valid_annotations[E].info);
}
}
GDScriptParser::GDScriptParser() {
// Register valid annotations.
// TODO: Should this be static?
- // TODO: Validate applicable types (e.g. a VARIABLE annotation that only applies to string variables).
register_annotation(MethodInfo("@tool"), AnnotationInfo::SCRIPT, &GDScriptParser::tool_annotation);
register_annotation(MethodInfo("@icon", { Variant::STRING, "icon_path" }), AnnotationInfo::SCRIPT, &GDScriptParser::icon_annotation);
register_annotation(MethodInfo("@onready"), AnnotationInfo::VARIABLE, &GDScriptParser::onready_annotation);
// Export annotations.
- register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_TYPE_STRING, Variant::NIL>);
+ register_annotation(MethodInfo("@export"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NONE, Variant::NIL>);
register_annotation(MethodInfo("@export_enum", { Variant::STRING, "names" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_ENUM, Variant::INT>, 0, true);
register_annotation(MethodInfo("@export_file", { Variant::STRING, "filter" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FILE, Variant::STRING>, 1, true);
register_annotation(MethodInfo("@export_dir"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_DIR, Variant::STRING>);
@@ -132,22 +122,18 @@ GDScriptParser::GDScriptParser() {
register_annotation(MethodInfo("@export_multiline"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_MULTILINE_TEXT, Variant::STRING>);
register_annotation(MethodInfo("@export_placeholder"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_PLACEHOLDER_TEXT, Variant::STRING>);
register_annotation(MethodInfo("@export_range", { Variant::FLOAT, "min" }, { Variant::FLOAT, "max" }, { Variant::FLOAT, "step" }, { Variant::STRING, "slider1" }, { Variant::STRING, "slider2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_RANGE, Variant::FLOAT>, 3);
- register_annotation(MethodInfo("@export_exp_range", { Variant::FLOAT, "min" }, { Variant::FLOAT, "max" }, { Variant::FLOAT, "step" }, { Variant::STRING, "slider1" }, { Variant::STRING, "slider2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_RANGE, Variant::FLOAT>, 3);
register_annotation(MethodInfo("@export_exp_easing", { Variant::STRING, "hint1" }, { Variant::STRING, "hint2" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_EXP_EASING, Variant::FLOAT>, 2);
register_annotation(MethodInfo("@export_color_no_alpha"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_COLOR_NO_ALPHA, Variant::COLOR>);
register_annotation(MethodInfo("@export_node_path", { Variant::STRING, "type" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_NODE_PATH_VALID_TYPES, Variant::NODE_PATH>, 1, true);
register_annotation(MethodInfo("@export_flags", { Variant::STRING, "names" }), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_FLAGS, Variant::INT>, 0, true);
register_annotation(MethodInfo("@export_flags_2d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_RENDER, Variant::INT>);
register_annotation(MethodInfo("@export_flags_2d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_PHYSICS, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_2d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_2D_NAVIGATION, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_render"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_RENDER, Variant::INT>);
register_annotation(MethodInfo("@export_flags_3d_physics"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_PHYSICS, Variant::INT>);
+ register_annotation(MethodInfo("@export_flags_3d_navigation"), AnnotationInfo::VARIABLE, &GDScriptParser::export_annotations<PROPERTY_HINT_LAYERS_3D_NAVIGATION, Variant::INT>);
// Networking.
- register_annotation(MethodInfo("@remote"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_REMOTE>);
- register_annotation(MethodInfo("@master"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_MASTER>);
- register_annotation(MethodInfo("@puppet"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_PUPPET>);
- register_annotation(MethodInfo("@remotesync"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_REMOTESYNC>);
- register_annotation(MethodInfo("@mastersync"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_MASTERSYNC>);
- register_annotation(MethodInfo("@puppetsync"), AnnotationInfo::VARIABLE | AnnotationInfo::FUNCTION, &GDScriptParser::network_annotations<MultiplayerAPI::RPC_MODE_PUPPETSYNC>);
+ 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.
}
@@ -184,23 +170,25 @@ void GDScriptParser::push_error(const String &p_message, const Node *p_origin) {
#ifdef DEBUG_ENABLED
void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const String &p_symbol1, const String &p_symbol2, const String &p_symbol3, const String &p_symbol4) {
+ ERR_FAIL_COND(p_source == nullptr);
Vector<String> symbols;
- if (!p_symbol1.empty()) {
+ if (!p_symbol1.is_empty()) {
symbols.push_back(p_symbol1);
}
- if (!p_symbol2.empty()) {
+ if (!p_symbol2.is_empty()) {
symbols.push_back(p_symbol2);
}
- if (!p_symbol3.empty()) {
+ if (!p_symbol3.is_empty()) {
symbols.push_back(p_symbol3);
}
- if (!p_symbol4.empty()) {
+ if (!p_symbol4.is_empty()) {
symbols.push_back(p_symbol4);
}
push_warning(p_source, p_code, symbols);
}
void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_code, const Vector<String> &p_symbols) {
+ ERR_FAIL_COND(p_source == nullptr);
if (is_ignoring_warnings) {
return;
}
@@ -225,7 +213,7 @@ void GDScriptParser::push_warning(const Node *p_source, GDScriptWarning::Code p_
warning.rightmost_column = p_source->rightmost_column;
List<GDScriptWarning>::Element *before = nullptr;
- for (List<GDScriptWarning>::Element *E = warnings.front(); E != nullptr; E = E->next()) {
+ for (List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
if (E->get().start_line > warning.start_line) {
break;
}
@@ -291,7 +279,7 @@ void GDScriptParser::pop_completion_call() {
if (!for_completion) {
return;
}
- ERR_FAIL_COND_MSG(completion_call_stack.empty(), "Trying to pop empty completion call stack");
+ ERR_FAIL_COND_MSG(completion_call_stack.is_empty(), "Trying to pop empty completion call stack");
completion_call_stack.pop_back();
}
@@ -299,7 +287,7 @@ void GDScriptParser::set_last_completion_call_arg(int p_argument) {
if (!for_completion || passed_cursor) {
return;
}
- ERR_FAIL_COND_MSG(completion_call_stack.empty(), "Trying to set argument on empty completion call stack");
+ ERR_FAIL_COND_MSG(completion_call_stack.is_empty(), "Trying to set argument on empty completion call stack");
completion_call_stack.back()->get().argument = p_argument;
}
@@ -314,7 +302,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
int tab_size = 4;
#ifdef TOOLS_ENABLED
if (EditorSettings::get_singleton()) {
- tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size");
+ tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size");
}
#endif // TOOLS_ENABLED
@@ -349,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();
@@ -365,7 +370,7 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
}
#endif
- if (errors.empty()) {
+ if (errors.is_empty()) {
return OK;
} else {
return ERR_PARSE_ERROR;
@@ -373,10 +378,12 @@ Error GDScriptParser::parse(const String &p_source_code, const String &p_script_
}
GDScriptTokenizer::Token GDScriptParser::advance() {
+ lambda_ended = false; // Empty marker since we're past the end in any case.
+
if (current.type == GDScriptTokenizer::Token::TK_EOF) {
ERR_FAIL_COND_V_MSG(current.type == GDScriptTokenizer::Token::TK_EOF, current, "GDScript parser bug: Trying to advance past the end of stream.");
}
- if (for_completion && !completion_call_stack.empty()) {
+ if (for_completion && !completion_call_stack.is_empty()) {
if (completion_call.call == nullptr && tokenizer.is_past_cursor()) {
completion_call = completion_call_stack.back()->get();
passed_cursor = true;
@@ -399,7 +406,7 @@ bool GDScriptParser::match(GDScriptTokenizer::Token::Type p_token_type) {
return true;
}
-bool GDScriptParser::check(GDScriptTokenizer::Token::Type p_token_type) {
+bool GDScriptParser::check(GDScriptTokenizer::Token::Type p_token_type) const {
if (p_token_type == GDScriptTokenizer::Token::IDENTIFIER) {
return current.is_identifier();
}
@@ -414,7 +421,7 @@ bool GDScriptParser::consume(GDScriptTokenizer::Token::Type p_token_type, const
return false;
}
-bool GDScriptParser::is_at_end() {
+bool GDScriptParser::is_at_end() const {
return check(GDScriptTokenizer::Token::TK_EOF);
}
@@ -465,16 +472,34 @@ void GDScriptParser::pop_multiline() {
tokenizer.set_multiline_mode(multiline_stack.size() > 0 ? multiline_stack.back()->get() : false);
}
-bool GDScriptParser::is_statement_end() {
+bool GDScriptParser::is_statement_end_token() const {
return check(GDScriptTokenizer::Token::NEWLINE) || check(GDScriptTokenizer::Token::SEMICOLON) || check(GDScriptTokenizer::Token::TK_EOF);
}
+bool GDScriptParser::is_statement_end() const {
+ return lambda_ended || in_lambda || is_statement_end_token();
+}
+
void GDScriptParser::end_statement(const String &p_context) {
bool found = false;
while (is_statement_end() && !is_at_end()) {
// Remove sequential newlines/semicolons.
+ if (is_statement_end_token()) {
+ // Only consume if this is an actual token.
+ advance();
+ } else if (lambda_ended) {
+ lambda_ended = false; // Consume this "token".
+ found = true;
+ break;
+ } else {
+ if (!found) {
+ lambda_ended = true; // Mark the lambda as done since we found something else to end the statement.
+ found = true;
+ }
+ break;
+ }
+
found = true;
- advance();
}
if (!found && !is_at_end()) {
push_error(vformat(R"(Expected end of statement after %s, found "%s" instead.)", p_context, current.get_name()));
@@ -507,7 +532,7 @@ void GDScriptParser::parse_program() {
// Order here doesn't matter, but there should be only one of each at most.
switch (current.type) {
case GDScriptTokenizer::Token::CLASS_NAME:
- if (!annotation_stack.empty()) {
+ if (!annotation_stack.is_empty()) {
push_error(R"("class_name" should be used before annotations.)");
}
advance();
@@ -518,7 +543,7 @@ void GDScriptParser::parse_program() {
}
break;
case GDScriptTokenizer::Token::EXTENDS:
- if (!annotation_stack.empty()) {
+ if (!annotation_stack.is_empty()) {
push_error(R"("extends" should be used before annotations.)");
}
advance();
@@ -554,7 +579,18 @@ void GDScriptParser::parse_program() {
}
}
- parse_class_body();
+ parse_class_body(true);
+
+#ifdef TOOLS_ENABLED
+ 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)) {
+ get_class_doc_comment(class_doc_line, head->doc_brief_description, head->doc_description, head->doc_tutorials, false);
+ }
+#endif // TOOLS_ENABLED
if (!check(GDScriptTokenizer::Token::TK_EOF)) {
push_error("Expected end of file.");
@@ -579,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;
}
@@ -594,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;
@@ -666,11 +705,14 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
if (member == nullptr) {
return;
}
+#ifdef TOOLS_ENABLED
+ int doc_comment_line = member->start_line - 1;
+#endif // TOOLS_ENABLED
+
// Consume annotations.
- while (!annotation_stack.empty()) {
+ while (!annotation_stack.is_empty()) {
AnnotationNode *last_annotation = annotation_stack.back()->get();
if (last_annotation->applies_to(p_target)) {
- last_annotation->apply(this, member);
member->annotations.push_front(last_annotation);
annotation_stack.pop_back();
} else {
@@ -678,19 +720,51 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
clear_unused_annotations();
return;
}
+#ifdef TOOLS_ENABLED
+ if (last_annotation->start_line == doc_comment_line) {
+ doc_comment_line--;
+ }
+#endif // TOOLS_ENABLED
}
+
+#ifdef TOOLS_ENABLED
+ // Consume doc comments.
+ class_doc_line = MIN(class_doc_line, doc_comment_line - 1);
+ if (has_comment(doc_comment_line)) {
+ if constexpr (std::is_same_v<T, ClassNode>) {
+ get_class_doc_comment(doc_comment_line, member->doc_brief_description, member->doc_description, member->doc_tutorials, true);
+ } else {
+ member->doc_description = get_doc_comment(doc_comment_line);
+ }
+ }
+#endif // TOOLS_ENABLED
+
if (member->identifier != nullptr) {
- // Enums may be unnamed.
- // TODO: Consider names in outer scope too, for constants and classes (and static functions?)
- if (current_class->members_indices.has(member->identifier->name)) {
- push_error(vformat(R"(%s "%s" has the same name as a previously declared %s.)", p_member_kind.capitalize(), member->identifier->name, current_class->get_member(member->identifier->name).get_type_name()), member->identifier);
+ if (!((String)member->identifier->name).is_empty()) { // Enums may be unnamed.
+ List<MethodInfo> gdscript_funcs;
+ GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == member->identifier->name) {
+ push_error(vformat(R"(%s "%s" has the same name as a built-in function.)", p_member_kind.capitalize(), member->identifier->name), member->identifier);
+ return;
+ }
+ }
+ if (current_class->members_indices.has(member->identifier->name)) {
+ push_error(vformat(R"(%s "%s" has the same name as a previously declared %s.)", p_member_kind.capitalize(), member->identifier->name, current_class->get_member(member->identifier->name).get_type_name()), member->identifier);
+ } else if (Variant::has_utility_function(member->identifier->name)) {
+ push_error(vformat(R"(%s "%s" has the same name as a built-in function.)", p_member_kind.capitalize(), member->identifier->name), member->identifier);
+ } else if (ClassDB::class_exists(member->identifier->name)) {
+ push_error(vformat(R"(%s "%s" has the same name as a global class.)", p_member_kind.capitalize(), member->identifier->name), member->identifier);
+ } else {
+ current_class->add_member(member);
+ }
} else {
current_class->add_member(member);
}
}
}
-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) {
@@ -736,6 +810,9 @@ void GDScriptParser::parse_class_body() {
if (panic_mode) {
synchronize();
}
+ if (!p_is_multiline) {
+ class_end = true;
+ }
}
}
@@ -748,8 +825,27 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
return nullptr;
}
+ GDScriptParser::IdentifierNode *identifier = parse_identifier();
+
+ List<MethodInfo> gdscript_funcs;
+ GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == identifier->name) {
+ push_error(vformat(R"(Local var "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ }
+ }
+ if (Variant::has_utility_function(identifier->name)) {
+ push_error(vformat(R"(Local var "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ } else if (ClassDB::class_exists(identifier->name)) {
+ push_error(vformat(R"(Local var "%s" has the same name as a global class.)", identifier->name), identifier);
+ return nullptr;
+ }
+
VariableNode *variable = alloc_node<VariableNode>();
- variable->identifier = parse_identifier();
+ variable->identifier = identifier;
+ variable->export_info.name = identifier->name;
if (match(GDScriptTokenizer::Token::COLON)) {
if (check(GDScriptTokenizer::Token::NEWLINE)) {
@@ -783,6 +879,9 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
if (match(GDScriptTokenizer::Token::EQUAL)) {
// Initializer.
variable->initializer = parse_expression(false);
+ if (variable->initializer == nullptr) {
+ push_error(R"(Expected expression for variable initial value after "=".)");
+ }
variable->assignments++;
}
@@ -796,8 +895,6 @@ GDScriptParser::VariableNode *GDScriptParser::parse_variable(bool p_allow_proper
end_statement("variable declaration");
- variable->export_info.name = variable->identifier->name;
-
return variable;
}
@@ -882,7 +979,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();
@@ -890,9 +987,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);
@@ -907,11 +1027,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);
@@ -950,6 +1084,8 @@ GDScriptParser::ConstantNode *GDScriptParser::parse_constant() {
push_error(R"(Expected initializer expression for constant.)");
return nullptr;
}
+ } else {
+ return nullptr;
}
end_statement("constant declaration");
@@ -962,8 +1098,26 @@ GDScriptParser::ParameterNode *GDScriptParser::parse_parameter() {
return nullptr;
}
+ GDScriptParser::IdentifierNode *identifier = parse_identifier();
+
+ List<MethodInfo> gdscript_funcs;
+ GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == identifier->name) {
+ push_error(vformat(R"(Parameter "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ }
+ }
+ if (Variant::has_utility_function(identifier->name)) {
+ push_error(vformat(R"(Parameter "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ } else if (ClassDB::class_exists(identifier->name)) {
+ push_error(vformat(R"(Parameter "%s" has the same name as a global class.)", identifier->name), identifier);
+ return nullptr;
+ }
+
ParameterNode *parameter = alloc_node<ParameterNode>();
- parameter->identifier = parse_identifier();
+ parameter->identifier = identifier;
if (match(GDScriptTokenizer::Token::COLON)) {
if (check((GDScriptTokenizer::Token::EQUAL))) {
@@ -992,7 +1146,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.
@@ -1015,6 +1171,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.)*");
}
@@ -1038,16 +1195,35 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
HashMap<StringName, int> elements;
+ List<MethodInfo> gdscript_funcs;
+ GDScriptLanguage::get_singleton()->get_public_functions(&gdscript_funcs);
+
do {
if (check(GDScriptTokenizer::Token::BRACE_CLOSE)) {
break; // Allow trailing comma.
}
if (consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected identifier for enum key.)")) {
EnumNode::Value item;
- item.identifier = parse_identifier();
+ GDScriptParser::IdentifierNode *identifier = parse_identifier();
+
+ for (MethodInfo &info : gdscript_funcs) {
+ if (info.name == identifier->name) {
+ push_error(vformat(R"(Enum member "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ }
+ }
+ if (Variant::has_utility_function(identifier->name)) {
+ push_error(vformat(R"(Enum member "%s" has the same name as a built-in function.)", identifier->name), identifier);
+ return nullptr;
+ } else if (ClassDB::class_exists(identifier->name)) {
+ push_error(vformat(R"(Enum member "%s" has the same name as a global class.)", identifier->name), identifier);
+ return nullptr;
+ }
+ item.identifier = identifier;
item.parent_enum = enum_node;
item.line = previous.start_line;
item.leftmost_column = previous.leftmost_column;
+ item.rightmost_column = previous.rightmost_column;
if (elements.has(item.identifier->name)) {
push_error(vformat(R"(Name "%s" was already in this enum (at line %d).)", item.identifier->name, elements[item.identifier->name]), item.identifier);
@@ -1072,7 +1248,6 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
}
item.custom_value = value;
}
- item.rightmost_column = previous.rightmost_column;
item.index = enum_node->values.size();
enum_node->values.push_back(item);
@@ -1086,41 +1261,37 @@ GDScriptParser::EnumNode *GDScriptParser::parse_enum() {
pop_multiline();
consume(GDScriptTokenizer::Token::BRACE_CLOSE, R"(Expected closing "}" for enum.)");
- end_statement("enum");
-
- return enum_node;
-}
-
-GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
- bool _static = false;
- if (previous.type == GDScriptTokenizer::Token::STATIC) {
- // TODO: Improve message if user uses "static" with "var" or "const"
- if (!consume(GDScriptTokenizer::Token::FUNC, R"(Expected "func" after "static".)")) {
- return nullptr;
+#ifdef TOOLS_ENABLED
+ // Enum values documentation.
+ for (int i = 0; i < enum_node->values.size(); i++) {
+ if (i == enum_node->values.size() - 1) {
+ // If close bracket is same line as last value.
+ if (enum_node->values[i].line != previous.start_line && has_comment(enum_node->values[i].line)) {
+ if (named) {
+ enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true);
+ } else {
+ current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true));
+ }
+ }
+ } else {
+ // If two values are same line.
+ if (enum_node->values[i].line != enum_node->values[i + 1].line && has_comment(enum_node->values[i].line)) {
+ if (named) {
+ enum_node->values.write[i].doc_description = get_doc_comment(enum_node->values[i].line, true);
+ } else {
+ current_class->set_enum_value_doc(enum_node->values[i].identifier->name, get_doc_comment(enum_node->values[i].line, true));
+ }
+ }
}
- _static = true;
}
+#endif // TOOLS_ENABLED
- FunctionNode *function = alloc_node<FunctionNode>();
- make_completion_context(COMPLETION_OVERRIDE_METHOD, function);
-
- if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) {
- return nullptr;
- }
-
- FunctionNode *previous_function = current_function;
- current_function = function;
-
- function->identifier = parse_identifier();
- function->is_static = _static;
-
- push_multiline(true);
- consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after function name.)");
+ end_statement("enum");
- SuiteNode *body = alloc_node<SuiteNode>();
- SuiteNode *previous_suite = current_suite;
- current_suite = body;
+ return enum_node;
+}
+void GDScriptParser::parse_function_signature(FunctionNode *p_function, SuiteNode *p_body, const String &p_type) {
if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE) && !is_at_end()) {
bool default_used = false;
do {
@@ -1140,29 +1311,61 @@ GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
continue;
}
}
- if (function->parameters_indices.has(parameter->identifier->name)) {
- push_error(vformat(R"(Parameter with name "%s" was already declared for this function.)", parameter->identifier->name));
+ if (p_function->parameters_indices.has(parameter->identifier->name)) {
+ push_error(vformat(R"(Parameter with name "%s" was already declared for this %s.)", parameter->identifier->name, p_type));
} else {
- function->parameters_indices[parameter->identifier->name] = function->parameters.size();
- function->parameters.push_back(parameter);
- body->add_local(parameter);
+ p_function->parameters_indices[parameter->identifier->name] = p_function->parameters.size();
+ p_function->parameters.push_back(parameter);
+ p_body->add_local(parameter, current_function);
}
} while (match(GDScriptTokenizer::Token::COMMA));
}
pop_multiline();
- consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after function parameters.)*");
+ consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, vformat(R"*(Expected closing ")" after %s parameters.)*", p_type));
if (match(GDScriptTokenizer::Token::FORWARD_ARROW)) {
- make_completion_context(COMPLETION_TYPE_NAME_OR_VOID, function);
- function->return_type = parse_type(true);
- if (function->return_type == nullptr) {
+ make_completion_context(COMPLETION_TYPE_NAME_OR_VOID, p_function);
+ p_function->return_type = parse_type(true);
+ if (p_function->return_type == nullptr) {
push_error(R"(Expected return type or "void" after "->".)");
}
}
// TODO: Improve token consumption so it synchronizes to a statement boundary. This way we can get into the function body with unrecognized tokens.
- consume(GDScriptTokenizer::Token::COLON, R"(Expected ":" after function declaration.)");
+ consume(GDScriptTokenizer::Token::COLON, vformat(R"(Expected ":" after %s declaration.)", p_type));
+}
+
+GDScriptParser::FunctionNode *GDScriptParser::parse_function() {
+ bool _static = false;
+ if (previous.type == GDScriptTokenizer::Token::STATIC) {
+ // TODO: Improve message if user uses "static" with "var" or "const"
+ if (!consume(GDScriptTokenizer::Token::FUNC, R"(Expected "func" after "static".)")) {
+ return nullptr;
+ }
+ _static = true;
+ }
+
+ FunctionNode *function = alloc_node<FunctionNode>();
+ make_completion_context(COMPLETION_OVERRIDE_METHOD, function);
+
+ if (!consume(GDScriptTokenizer::Token::IDENTIFIER, R"(Expected function name after "func".)")) {
+ return nullptr;
+ }
+
+ FunctionNode *previous_function = current_function;
+ current_function = function;
+
+ function->identifier = parse_identifier();
+ function->is_static = _static;
+
+ SuiteNode *body = alloc_node<SuiteNode>();
+ SuiteNode *previous_suite = current_suite;
+ current_suite = body;
+
+ push_multiline(true);
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after function name.)");
+ parse_function_signature(function, body, "function");
current_suite = previous_suite;
function->body = parse_suite("function declaration", body);
@@ -1224,8 +1427,7 @@ GDScriptParser::AnnotationNode *GDScriptParser::parse_annotation(uint32_t p_vali
}
void GDScriptParser::clear_unused_annotations() {
- for (const List<AnnotationNode *>::Element *E = annotation_stack.front(); E != nullptr; E = E->next()) {
- AnnotationNode *annotation = E->get();
+ for (const AnnotationNode *annotation : annotation_stack) {
push_error(vformat(R"(Annotation "%s" does not precedes a valid target, so it will have no effect.)", annotation->name), annotation);
}
@@ -1248,29 +1450,37 @@ bool GDScriptParser::register_annotation(const MethodInfo &p_info, uint32_t p_ta
return true;
}
-GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, SuiteNode *p_suite) {
+GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context, SuiteNode *p_suite, bool p_for_lambda) {
SuiteNode *suite = p_suite != nullptr ? p_suite : alloc_node<SuiteNode>();
suite->parent_block = current_suite;
+ suite->parent_function = current_function;
current_suite = suite;
bool multiline = false;
- if (check(GDScriptTokenizer::Token::NEWLINE)) {
+ if (match(GDScriptTokenizer::Token::NEWLINE)) {
multiline = true;
}
if (multiline) {
- consume(GDScriptTokenizer::Token::NEWLINE, vformat(R"(Expected newline after %s.)", p_context));
-
if (!consume(GDScriptTokenizer::Token::INDENT, vformat(R"(Expected indented block after %s.)", p_context))) {
current_suite = suite->parent_block;
return suite;
}
}
+ 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) {
+ push_error("Too many statement errors.", suite);
+ break;
+ }
continue;
}
suite->statements.push_back(statement);
@@ -1283,7 +1493,7 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
if (local.type != SuiteNode::Local::UNDEFINED) {
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", local.get_name(), variable->identifier->name));
}
- current_suite->add_local(variable);
+ current_suite->add_local(variable, current_function);
break;
}
case Node::CONSTANT: {
@@ -1298,19 +1508,29 @@ GDScriptParser::SuiteNode *GDScriptParser::parse_suite(const String &p_context,
}
push_error(vformat(R"(There is already a %s named "%s" declared in this scope.)", name, constant->identifier->name));
}
- current_suite->add_local(constant);
+ current_suite->add_local(constant, current_function);
break;
}
default:
break;
}
- } while (multiline && !check(GDScriptTokenizer::Token::DEDENT) && !is_at_end());
+ } while ((multiline || previous.type == GDScriptTokenizer::Token::SEMICOLON) && !check(GDScriptTokenizer::Token::DEDENT) && !lambda_ended && !is_at_end());
if (multiline) {
- consume(GDScriptTokenizer::Token::DEDENT, vformat(R"(Missing unindent at the end of %s.)", p_context));
+ if (!lambda_ended) {
+ consume(GDScriptTokenizer::Token::DEDENT, vformat(R"(Missing unindent at the end of %s.)", p_context));
+
+ } else {
+ match(GDScriptTokenizer::Token::DEDENT);
+ }
+ } else if (previous.type == GDScriptTokenizer::Token::SEMICOLON) {
+ consume(GDScriptTokenizer::Token::NEWLINE, vformat(R"(Expected newline after ";" at the end of %s.)", p_context));
}
+ if (p_for_lambda) {
+ lambda_ended = true;
+ }
current_suite = suite->parent_block;
return suite;
}
@@ -1367,6 +1587,10 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
push_error(R"(Constructor cannot return a value.)");
}
n_return->return_value = parse_expression(false);
+ } else if (in_lambda && !is_statement_end_token()) {
+ // Try to parse it anyway as this might not be the statement end in a lambda.
+ // If this fails the expression will be nullptr, but that's the same as no return, so it's fine.
+ n_return->return_value = parse_expression(false);
}
result = n_return;
@@ -1395,10 +1619,18 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
default: {
// Expression statement.
ExpressionNode *expression = parse_expression(true); // Allow assignment here.
+ bool has_ended_lambda = false;
if (expression == nullptr) {
- push_error(vformat(R"(Expected statement, found "%s" instead.)", previous.get_name()));
+ if (in_lambda) {
+ // If it's not a valid expression beginning, it might be the continuation of the outer expression where this lambda is.
+ lambda_ended = true;
+ has_ended_lambda = true;
+ } else {
+ push_error(vformat(R"(Expected statement, found "%s" instead.)", previous.get_name()));
+ }
}
end_statement("expression");
+ lambda_ended = lambda_ended || has_ended_lambda;
result = expression;
#ifdef DEBUG_ENABLED
@@ -1419,9 +1651,13 @@ GDScriptParser::Node *GDScriptParser::parse_statement() {
}
#ifdef DEBUG_ENABLED
- if (unreachable) {
+ if (unreachable && result != nullptr) {
current_suite->has_unreachable_code = true;
- push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier->name);
+ if (current_function) {
+ push_warning(result, GDScriptWarning::UNREACHABLE_CODE, current_function->identifier ? current_function->identifier->name : "<anonymous lambda>");
+ } else {
+ // TODO: Properties setters and getters with unreachable code are not being warned
+ }
}
#endif
@@ -1445,12 +1681,9 @@ GDScriptParser::AssertNode *GDScriptParser::parse_assert() {
if (match(GDScriptTokenizer::Token::COMMA)) {
// Error message.
- if (consume(GDScriptTokenizer::Token::LITERAL, R"(Expected error message for assert after ",".)")) {
- assert->message = parse_literal();
- if (assert->message->value.get_type() != Variant::STRING) {
- push_error(R"(Expected string for assert error message.)");
- }
- } else {
+ assert->message = parse_expression(false);
+ if (assert->message == nullptr) {
+ push_error(R"(Expected error message for assert after ",".)");
return nullptr;
}
}
@@ -1492,6 +1725,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.
@@ -1506,7 +1743,7 @@ GDScriptParser::ForNode *GDScriptParser::parse_for() {
SuiteNode *suite = alloc_node<SuiteNode>();
if (n_for->variable) {
- suite->add_local(SuiteNode::Local(n_for->variable));
+ suite->add_local(SuiteNode::Local(n_for->variable, current_function));
}
suite->parent_for = n_for;
@@ -1583,6 +1820,7 @@ GDScriptParser::MatchNode *GDScriptParser::parse_match() {
while (!check(GDScriptTokenizer::Token::DEDENT) && !is_at_end()) {
MatchBranchNode *branch = parse_match_branch();
if (branch == nullptr) {
+ advance();
continue;
}
@@ -1642,11 +1880,13 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
branch->patterns.push_back(pattern);
} while (match(GDScriptTokenizer::Token::COMMA));
- if (branch->patterns.empty()) {
+ if (branch->patterns.is_empty()) {
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;
@@ -1660,8 +1900,8 @@ GDScriptParser::MatchBranchNode *GDScriptParser::parse_match_branch() {
List<StringName> binds;
branch->patterns[0]->binds.get_key_list(&binds);
- for (List<StringName>::Element *E = binds.front(); E != nullptr; E = E->next()) {
- SuiteNode::Local local(branch->patterns[0]->binds[E->get()]);
+ for (const StringName &E : binds) {
+ SuiteNode::Local local(branch->patterns[0]->binds[E], current_function);
suite->add_local(local);
}
}
@@ -1679,15 +1919,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();
@@ -1750,44 +1981,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;
}
@@ -1796,8 +2027,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;
@@ -1861,7 +2097,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
// Completion can appear whenever an expression is expected.
make_completion_context(COMPLETION_IDENTIFIER, nullptr);
- GDScriptTokenizer::Token token = advance();
+ GDScriptTokenizer::Token token = current;
ParseFunction prefix_rule = get_rule(token.type)->prefix;
if (prefix_rule == nullptr) {
@@ -1869,6 +2105,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_precedence(Precedence p_pr
return nullptr;
}
+ advance(); // Only consume the token if there's a valid rule.
+
ExpressionNode *previous_operand = (this->*prefix_rule)(nullptr, p_can_assign);
while (p_precedence <= get_rule(current.type)->precedence) {
@@ -1910,6 +2148,8 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_identifier(ExpressionNode
if (current_suite != nullptr && current_suite->has_local(identifier->name)) {
const SuiteNode::Local &declaration = current_suite->get_local(identifier->name);
+
+ identifier->source_function = declaration.source_function;
switch (declaration.type) {
case SuiteNode::Local::CONSTANT:
identifier->source = IdentifierNode::LOCAL_CONSTANT;
@@ -1963,6 +2203,9 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_self(ExpressionNode *p_pre
if (current_function && current_function->is_static) {
push_error(R"(Cannot use "self" inside a static function.)");
}
+ if (in_lambda) {
+ push_error(R"(Cannot use "self" inside a lambda.)");
+ }
SelfNode *self = alloc_node<SelfNode>();
self->current_class = current_class;
return self;
@@ -1980,10 +2223,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_builtin_constant(Expressio
constant->value = Math_TAU;
break;
case GDScriptTokenizer::Token::CONST_INF:
- constant->value = Math_INF;
+ constant->value = INFINITY;
break;
case GDScriptTokenizer::Token::CONST_NAN:
- constant->value = Math_NAN;
+ constant->value = NAN;
break;
default:
return nullptr; // Unreachable.
@@ -2001,22 +2244,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.
@@ -2025,6 +2280,17 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_unary_operator(ExpressionN
return operation;
}
+GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ // check that NOT is followed by IN by consuming it before calling parse_binary_operator which will only receive a plain IN
+ consume(GDScriptTokenizer::Token::IN, R"(Expected "in" after "not" in content-test operator.)");
+ ExpressionNode *in_operation = parse_binary_operator(p_previous_operand, p_can_assign);
+ UnaryOpNode *operation = alloc_node<UnaryOpNode>();
+ operation->operation = UnaryOpNode::OP_LOGIC_NOT;
+ operation->variant_op = Variant::OP_NOT;
+ operation->operand = in_operation;
+ return operation;
+}
+
GDScriptParser::ExpressionNode *GDScriptParser::parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign) {
GDScriptTokenizer::Token op = previous;
BinaryOpNode *operation = alloc_node<BinaryOpNode>();
@@ -2142,6 +2408,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;
}
@@ -2248,10 +2518,17 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
}
assignment->assignee = p_previous_operand;
assignment->assigned_value = parse_expression(false);
+ if (assignment->assigned_value == nullptr) {
+ push_error(R"(Expected an expression after "=".)");
+ }
#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
@@ -2260,9 +2537,15 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_assignment(ExpressionNode
GDScriptParser::ExpressionNode *GDScriptParser::parse_await(ExpressionNode *p_previous_operand, bool p_can_assign) {
AwaitNode *await = alloc_node<AwaitNode>();
- await->to_await = parse_precedence(PREC_AWAIT, false);
+ ExpressionNode *element = parse_precedence(PREC_AWAIT, false);
+ if (element == nullptr) {
+ push_error(R"(Expected signal or coroutine after "await".)");
+ }
+ 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;
}
@@ -2326,8 +2609,15 @@ 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 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;
}
if (!match(GDScriptTokenizer::Token::EQUAL)) {
if (match(GDScriptTokenizer::Token::COLON)) {
@@ -2337,6 +2627,14 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_dictionary(ExpressionNode
push_error(R"(Expected "=" after dictionary key.)");
}
}
+ 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)) {
@@ -2383,7 +2681,7 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_attribute(ExpressionNode *
if (for_completion) {
bool is_builtin = false;
- if (p_previous_operand->type == Node::IDENTIFIER) {
+ if (p_previous_operand && p_previous_operand->type == Node::IDENTIFIER) {
const IdentifierNode *id = static_cast<const IdentifierNode *>(p_previous_operand);
Variant::Type builtin_type = get_builtin_type(id->name);
if (builtin_type < Variant::VARIANT_MAX) {
@@ -2415,6 +2713,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.)");
@@ -2486,26 +2788,28 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_call(ExpressionNode *p_pre
}
}
- if (!check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
- // Arguments.
- push_completion_call(call);
- make_completion_context(COMPLETION_CALL_ARGUMENTS, call, 0, true);
- int argument_index = 0;
- do {
- make_completion_context(COMPLETION_CALL_ARGUMENTS, call, argument_index++, true);
- if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
- // Allow for trailing comma.
- break;
- }
- ExpressionNode *argument = parse_expression(false);
- if (argument == nullptr) {
- push_error(R"(Expected expression as the function argument.)");
- } else {
- call->arguments.push_back(argument);
- }
- } while (match(GDScriptTokenizer::Token::COMMA));
- pop_completion_call();
+ // Arguments.
+ CompletionType ct = COMPLETION_CALL_ARGUMENTS;
+ if (call->function_name == "load") {
+ ct = COMPLETION_RESOURCE_PATH;
}
+ push_completion_call(call);
+ int argument_index = 0;
+ do {
+ make_completion_context(ct, call, argument_index++, true);
+ if (check(GDScriptTokenizer::Token::PARENTHESIS_CLOSE)) {
+ // Allow for trailing comma.
+ break;
+ }
+ ExpressionNode *argument = parse_expression(false);
+ if (argument == nullptr) {
+ push_error(R"(Expected expression as the function argument.)");
+ } else {
+ call->arguments.push_back(argument);
+ }
+ ct = COMPLETION_CALL_ARGUMENTS;
+ } while (match(GDScriptTokenizer::Token::COMMA));
+ pop_completion_call();
pop_multiline();
consume(GDScriptTokenizer::Token::PARENTHESIS_CLOSE, R"*(Expected closing ")" after call arguments.)*");
@@ -2538,6 +2842,23 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_get_node(ExpressionNode *p
get_node->chain.push_back(identifier);
} while (match(GDScriptTokenizer::Token::SLASH));
return get_node;
+ } else if (match(GDScriptTokenizer::Token::SLASH)) {
+ GetNodeNode *get_node = alloc_node<GetNodeNode>();
+ IdentifierNode *identifier_root = alloc_node<IdentifierNode>();
+ get_node->chain.push_back(identifier_root);
+ int chain_position = 0;
+ do {
+ make_completion_context(COMPLETION_GET_NODE, get_node, chain_position++);
+ if (!current.is_node_name()) {
+ push_error(R"(Expect node path after "/".)");
+ return nullptr;
+ }
+ advance();
+ IdentifierNode *identifier = alloc_node<IdentifierNode>();
+ identifier->name = previous.get_identifier();
+ get_node->chain.push_back(identifier);
+ } while (match(GDScriptTokenizer::Token::SLASH));
+ return get_node;
} else {
push_error(R"(Expect node path as string or identifier after "$".)");
return nullptr;
@@ -2568,6 +2889,70 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_preload(ExpressionNode *p_
return preload;
}
+GDScriptParser::ExpressionNode *GDScriptParser::parse_lambda(ExpressionNode *p_previous_operand, bool p_can_assign) {
+ LambdaNode *lambda = alloc_node<LambdaNode>();
+ lambda->parent_function = current_function;
+ FunctionNode *function = alloc_node<FunctionNode>();
+ function->source_lambda = lambda;
+
+ function->is_static = current_function != nullptr ? current_function->is_static : false;
+
+ if (match(GDScriptTokenizer::Token::IDENTIFIER)) {
+ function->identifier = parse_identifier();
+ }
+
+ bool multiline_context = multiline_stack.back()->get();
+
+ // Reset the multiline stack since we don't want the multiline mode one in the lambda body.
+ push_multiline(false);
+ if (multiline_context) {
+ tokenizer.push_expression_indented_block();
+ }
+
+ push_multiline(true); // For the parameters.
+ if (function->identifier) {
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after lambda name.)");
+ } else {
+ consume(GDScriptTokenizer::Token::PARENTHESIS_OPEN, R"(Expected opening "(" after "func".)");
+ }
+
+ FunctionNode *previous_function = current_function;
+ current_function = function;
+
+ SuiteNode *body = alloc_node<SuiteNode>();
+ SuiteNode *previous_suite = current_suite;
+ current_suite = body;
+
+ parse_function_signature(function, body, "lambda");
+
+ current_suite = previous_suite;
+
+ bool previous_in_lambda = in_lambda;
+ in_lambda = true;
+
+ function->body = parse_suite("lambda declaration", body, true);
+
+ pop_multiline();
+
+ if (multiline_context) {
+ // If we're in multiline mode, we want to skip the spurious DEDENT and NEWLINE tokens.
+ while (check(GDScriptTokenizer::Token::DEDENT) || check(GDScriptTokenizer::Token::INDENT) || check(GDScriptTokenizer::Token::NEWLINE)) {
+ current = tokenizer.scan(); // Not advance() since we don't want to change the previous token.
+ }
+ tokenizer.pop_expression_indented_block();
+ }
+
+ current_function = previous_function;
+ in_lambda = previous_in_lambda;
+ lambda->function = function;
+ 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;
@@ -2604,6 +2989,19 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
type->type_chain.push_back(type_element);
+ if (match(GDScriptTokenizer::Token::BRACKET_OPEN)) {
+ // Typed collection (like Array[int]).
+ type->container_type = parse_type(false); // Don't allow void for array element type.
+ if (type->container_type == nullptr) {
+ push_error(R"(Expected type for collection after "[".)");
+ type = nullptr;
+ } else if (type->container_type->container_type != nullptr) {
+ push_error("Nested typed collections are not supported.");
+ }
+ consume(GDScriptTokenizer::Token::BRACKET_CLOSE, R"(Expected closing "]" after collection type.)");
+ return type;
+ }
+
int chain_index = 1;
while (match(GDScriptTokenizer::Token::PERIOD)) {
make_completion_context(COMPLETION_TYPE_ATTRIBUTE, type, chain_index++);
@@ -2616,6 +3014,218 @@ GDScriptParser::TypeNode *GDScriptParser::parse_type(bool p_allow_void) {
return type;
}
+#ifdef TOOLS_ENABLED
+static bool _in_codeblock(String p_line, bool p_already_in, int *r_block_begins = nullptr) {
+ int start_block = p_line.rfind("[codeblock]");
+ int end_block = p_line.rfind("[/codeblock]");
+
+ if (start_block != -1 && r_block_begins) {
+ *r_block_begins = start_block;
+ }
+
+ if (p_already_in) {
+ if (end_block == -1) {
+ return true;
+ } else if (start_block == -1) {
+ return false;
+ } else {
+ return start_block > end_block;
+ }
+ } else {
+ if (start_block == -1) {
+ return false;
+ } else if (end_block == -1) {
+ return true;
+ } else {
+ return start_block > end_block;
+ }
+ }
+}
+
+bool GDScriptParser::has_comment(int p_line) {
+ return tokenizer.get_comments().has(p_line);
+}
+
+String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
+ const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ ERR_FAIL_COND_V(!comments.has(p_line), String());
+
+ if (p_single_line) {
+ if (comments[p_line].comment.begins_with("##")) {
+ return comments[p_line].comment.trim_prefix("##").strip_edges();
+ }
+ return "";
+ }
+
+ String doc;
+
+ int line = p_line;
+ bool in_codeblock = false;
+
+ while (comments.has(line - 1)) {
+ if (!comments[line - 1].new_line || !comments[line - 1].comment.begins_with("##")) {
+ break;
+ }
+ line--;
+ }
+
+ int codeblock_begins = 0;
+ while (comments.has(line)) {
+ if (!comments[line].new_line || !comments[line].comment.begins_with("##")) {
+ break;
+ }
+ String doc_line = comments[line].comment.trim_prefix("##");
+
+ in_codeblock = _in_codeblock(doc_line, in_codeblock, &codeblock_begins);
+
+ if (in_codeblock) {
+ int i = 0;
+ for (; i < codeblock_begins; i++) {
+ if (doc_line[i] != ' ') {
+ break;
+ }
+ }
+ doc_line = doc_line.substr(i);
+ } else {
+ doc_line = doc_line.strip_edges();
+ }
+ String line_join = (in_codeblock) ? "\n" : " ";
+
+ doc = (doc.is_empty()) ? doc_line : doc + line_join + doc_line;
+ line++;
+ }
+
+ return doc;
+}
+
+void GDScriptParser::get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class) {
+ const Map<int, GDScriptTokenizer::CommentData> &comments = tokenizer.get_comments();
+ if (!comments.has(p_line)) {
+ return;
+ }
+ ERR_FAIL_COND(p_brief != "" || p_desc != "" || p_tutorials.size() != 0);
+
+ int line = p_line;
+ bool in_codeblock = false;
+ enum Mode {
+ BRIEF,
+ DESC,
+ TUTORIALS,
+ DONE,
+ };
+ Mode mode = BRIEF;
+
+ if (p_inner_class) {
+ while (comments.has(line - 1)) {
+ if (!comments[line - 1].new_line || !comments[line - 1].comment.begins_with("##")) {
+ break;
+ }
+ line--;
+ }
+ }
+
+ int codeblock_begins = 0;
+ while (comments.has(line)) {
+ if (!comments[line].new_line || !comments[line].comment.begins_with("##")) {
+ break;
+ }
+
+ String title, link; // For tutorials.
+ String doc_line = comments[line++].comment.trim_prefix("##");
+ String striped_line = doc_line.strip_edges();
+
+ // Set the read mode.
+ if (striped_line.begins_with("@desc:") && p_desc == "") {
+ mode = DESC;
+ striped_line = striped_line.trim_prefix("@desc:");
+ in_codeblock = _in_codeblock(doc_line, in_codeblock);
+
+ } else if (striped_line.begins_with("@tutorial")) {
+ int begin_scan = String("@tutorial").length();
+ if (begin_scan >= striped_line.length()) {
+ continue; // invalid syntax.
+ }
+
+ if (striped_line[begin_scan] == ':') { // No title.
+ // Syntax: ## @tutorial: https://godotengine.org/ // The title argument is optional.
+ title = "";
+ link = striped_line.trim_prefix("@tutorial:").strip_edges();
+
+ } else {
+ /* Syntax:
+ * @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++;
+ }
+ if (open_bracket_pos == striped_line.length() || striped_line[open_bracket_pos++] != '(') {
+ continue; // invalid syntax.
+ }
+ close_bracket_pos = open_bracket_pos;
+ while (close_bracket_pos < striped_line.length() && striped_line[close_bracket_pos] != ')') {
+ close_bracket_pos++;
+ }
+ if (close_bracket_pos == striped_line.length()) {
+ continue; // invalid syntax.
+ }
+
+ int colon_pos = close_bracket_pos + 1;
+ while (colon_pos < striped_line.length() && (striped_line[colon_pos] == ' ' || striped_line[colon_pos] == '\t')) {
+ colon_pos++;
+ }
+ if (colon_pos == striped_line.length() || striped_line[colon_pos++] != ':') {
+ continue; // invalid syntax.
+ }
+
+ title = striped_line.substr(open_bracket_pos, close_bracket_pos - open_bracket_pos).strip_edges();
+ link = striped_line.substr(colon_pos).strip_edges();
+ }
+
+ mode = TUTORIALS;
+ in_codeblock = false;
+ } else if (striped_line.is_empty()) {
+ continue;
+ } else {
+ // Tutorial docs are single line, we need a @tag after it.
+ if (mode == TUTORIALS) {
+ mode = DONE;
+ }
+
+ in_codeblock = _in_codeblock(doc_line, in_codeblock, &codeblock_begins);
+ }
+
+ if (in_codeblock) {
+ int i = 0;
+ for (; i < codeblock_begins; i++) {
+ if (doc_line[i] != ' ') {
+ break;
+ }
+ }
+ doc_line = doc_line.substr(i);
+ } else {
+ doc_line = striped_line;
+ }
+ String line_join = (in_codeblock) ? "\n" : " ";
+
+ switch (mode) {
+ case BRIEF:
+ p_brief = (p_brief.length() == 0) ? doc_line : p_brief + line_join + doc_line;
+ break;
+ case DESC:
+ p_desc = (p_desc.length() == 0) ? doc_line : p_desc + line_join + doc_line;
+ break;
+ case TUTORIALS:
+ p_tutorials.append(Pair<String, String>(title, link));
+ break;
+ case DONE:
+ return;
+ }
+ }
+}
+#endif // TOOLS_ENABLED
+
GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Type p_token_type) {
// Function table for expression parsing.
// clang-format destroys the alignment here, so turn off for the table.
@@ -2637,7 +3247,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
// Logical
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AND,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // OR,
- { &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // NOT,
+ { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_not_in_operator, PREC_CONTENT_TEST }, // NOT,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_AND }, // AMPERSAND_AMPERSAND,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_LOGIC_OR }, // PIPE_PIPE,
{ &GDScriptParser::parse_unary_operator, nullptr, PREC_NONE }, // BANG,
@@ -2649,8 +3259,8 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_SHIFT }, // LESS_LESS,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_BIT_SHIFT }, // GREATER_GREATER,
// Math
- { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION }, // PLUS,
- { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_SUBTRACTION }, // MINUS,
+ { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // PLUS,
+ { &GDScriptParser::parse_unary_operator, &GDScriptParser::parse_binary_operator, PREC_ADDITION_SUBTRACTION }, // MINUS,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // STAR,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // SLASH,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_FACTOR }, // PERCENT,
@@ -2687,7 +3297,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
{ nullptr, nullptr, PREC_NONE }, // CONST,
{ nullptr, nullptr, PREC_NONE }, // ENUM,
{ nullptr, nullptr, PREC_NONE }, // EXTENDS,
- { nullptr, nullptr, PREC_NONE }, // FUNC,
+ { &GDScriptParser::parse_lambda, nullptr, PREC_NONE }, // FUNC,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_CONTENT_TEST }, // IN,
{ nullptr, &GDScriptParser::parse_binary_operator, PREC_TYPE_TEST }, // IS,
{ nullptr, nullptr, PREC_NONE }, // NAMESPACE,
@@ -2699,7 +3309,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,
@@ -2736,7 +3346,7 @@ GDScriptParser::ParseRule *GDScriptParser::get_rule(GDScriptTokenizer::Token::Ty
// Avoid desync.
static_assert(sizeof(rules) / sizeof(rules[0]) == GDScriptTokenizer::Token::TK_MAX, "Amount of parse rules don't match the amount of token types.");
- // Let's assume this this never invalid, since nothing generates a TK_MAX.
+ // Let's assume this is never invalid, since nothing generates a TK_MAX.
return &rules[p_token_type];
}
@@ -2802,7 +3412,9 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
Callable::CallError error;
Vector<Variant> args = varray(string->name);
const Variant *name = args.ptr();
- p_annotation->resolved_arguments.push_back(Variant::construct(parameter.type, &(name), 1, error));
+ Variant r;
+ 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);
@@ -2824,7 +3436,9 @@ bool GDScriptParser::validate_annotation_arguments(AnnotationNode *p_annotation)
}
Callable::CallError error;
const Variant *args = &value;
- p_annotation->resolved_arguments.push_back(Variant::construct(parameter.type, &(args), 1, error));
+ Variant r;
+ 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);
@@ -2874,24 +3488,10 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
}
variable->exported = true;
- // TODO: Improving setting type, especially for range hints, which can be int or float.
+
variable->export_info.type = t_type;
variable->export_info.hint = t_hint;
- if (p_annotation->name == "@export") {
- if (variable->datatype_specifier == nullptr) {
- if (variable->initializer == nullptr) {
- push_error(R"(Cannot use "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
- return false;
- }
- if (variable->initializer->type != Node::LITERAL) {
- push_error(R"(To use "@export" annotation with type-less variable, the default value must be a literal.)", p_annotation);
- return false;
- }
- variable->export_info.type = static_cast<LiteralNode *>(variable->initializer)->value.get_type();
- } // else: Actual type will be set by the analyzer, which can infer the proper type.
- }
-
String hint_string;
for (int i = 0; i < p_annotation->resolved_arguments.size(); i++) {
if (i > 0) {
@@ -2902,6 +3502,86 @@ bool GDScriptParser::export_annotations(const AnnotationNode *p_annotation, Node
variable->export_info.hint_string = hint_string;
+ // This is called after tne analyzer is done finding the type, so this should be set here.
+ DataType export_type = variable->get_datatype();
+
+ if (p_annotation->name == "@export") {
+ if (variable->datatype_specifier == nullptr && variable->initializer == nullptr) {
+ push_error(R"(Cannot use simple "@export" annotation with variable without type or initializer, since type can't be inferred.)", p_annotation);
+ return false;
+ }
+
+ bool is_array = false;
+
+ if (export_type.builtin_type == Variant::ARRAY && export_type.has_container_element_type()) {
+ export_type = export_type.get_container_element_type(); // Use inner type for.
+ is_array = true;
+ }
+
+ if (export_type.is_variant() || export_type.has_no_type()) {
+ push_error(R"(Cannot use simple "@export" annotation because the type of the initialized value can't be inferred.)", p_annotation);
+ return false;
+ }
+
+ switch (export_type.kind) {
+ case GDScriptParser::DataType::BUILTIN:
+ variable->export_info.type = export_type.builtin_type;
+ variable->export_info.hint = PROPERTY_HINT_NONE;
+ variable->export_info.hint_string = Variant::get_type_name(export_type.builtin_type);
+ break;
+ case GDScriptParser::DataType::NATIVE:
+ if (ClassDB::is_parent_class(export_type.native_type, "Resource")) {
+ variable->export_info.type = Variant::OBJECT;
+ variable->export_info.hint = PROPERTY_HINT_RESOURCE_TYPE;
+ variable->export_info.hint_string = export_type.native_type;
+ } else {
+ push_error(R"(Export type can only be built-in, a resource, or an enum.)", variable);
+ return false;
+ }
+ break;
+ case GDScriptParser::DataType::ENUM: {
+ variable->export_info.type = Variant::INT;
+ variable->export_info.hint = PROPERTY_HINT_ENUM;
+
+ 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().capitalize().xml_escape();
+ enum_hint_string += ":";
+ enum_hint_string += String::num_int64(E->get()).xml_escape();
+
+ if (E->next()) {
+ enum_hint_string += ",";
+ }
+ }
+
+ variable->export_info.hint_string = enum_hint_string;
+ } break;
+ default:
+ // TODO: Allow custom user resources.
+ push_error(R"(Export type can only be built-in, a resource, or an enum.)", variable);
+ break;
+ }
+
+ if (is_array) {
+ String hint_prefix = itos(variable->export_info.type);
+ if (variable->export_info.hint) {
+ hint_prefix += "/" + itos(variable->export_info.hint);
+ }
+ variable->export_info.hint = PROPERTY_HINT_TYPE_STRING;
+ variable->export_info.hint_string = hint_prefix + ":" + variable->export_info.hint_string;
+ variable->export_info.type = Variant::ARRAY;
+ }
+ } else {
+ // Validate variable type with export.
+ if (!export_type.is_variant() && (export_type.kind != DataType::BUILTIN || export_type.builtin_type != t_type)) {
+ // Allow float/int conversion.
+ if ((t_type != Variant::FLOAT || export_type.builtin_type != Variant::INT) && (t_type != Variant::INT || export_type.builtin_type != Variant::FLOAT)) {
+ push_error(vformat(R"("%s" annotation requires a variable of type "%s" but type "%s" was given instead.)", p_annotation->name.operator String(), Variant::get_type_name(t_type), export_type.to_string()), variable);
+ return false;
+ }
+ }
+ }
+
return true;
}
@@ -2909,31 +3589,56 @@ 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));
- switch (p_node->type) {
- case Node::VARIABLE: {
- VariableNode *variable = static_cast<VariableNode *>(p_node);
- if (variable->rpc_mode != MultiplayerAPI::RPC_MODE_DISABLED) {
- push_error(R"(RPC annotations can only be used once per variable.)", p_annotation);
+ Multiplayer::RPCConfig rpc_config;
+ rpc_config.rpc_mode = t_mode;
+ if (p_annotation->resolved_arguments.size()) {
+ int last = p_annotation->resolved_arguments.size() - 1;
+ if (p_annotation->resolved_arguments[last].get_type() == Variant::INT) {
+ rpc_config.channel = p_annotation->resolved_arguments[last].operator int();
+ last -= 1;
+ }
+ if (last > 3) {
+ push_error(R"(Invalid RPC arguments. At most 4 arguments are allowed, where only the last argument can be an integer to specify the channel.')", p_annotation);
+ return false;
+ }
+ for (int i = last; i >= 0; i--) {
+ String mode = p_annotation->resolved_arguments[i].operator String();
+ 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 = Multiplayer::TRANSFER_MODE_RELIABLE;
+ } else if (mode == "unreliable") {
+ 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: 'call_local'/'call_remote' (local calls), 'any_peer'/'authority' (permission), 'reliable'/'unreliable'/'unreliable_ordered' (transfer mode).)", p_annotation);
}
- variable->rpc_mode = t_mode;
- break;
}
+ }
+ switch (p_node->type) {
case Node::FUNCTION: {
FunctionNode *function = static_cast<FunctionNode *>(p_node);
- if (function->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;
}
- function->rpc_mode = t_mode;
+ function->rpc_config = rpc_config;
break;
}
default:
return false; // Unreachable.
}
-
return true;
}
@@ -2987,6 +3692,9 @@ String GDScriptParser::DataType::to_string() const {
if (builtin_type == Variant::NIL) {
return "null";
}
+ if (builtin_type == Variant::ARRAY && has_container_element_type()) {
+ return vformat("Array[%s]", container_element_type->to_string());
+ }
return Variant::get_type_name(builtin_type);
case NATIVE:
if (is_meta_type) {
@@ -3006,11 +3714,11 @@ String GDScriptParser::DataType::to_string() const {
return script_type->get_class_name().operator String();
}
String name = script_type->get_name();
- if (!name.empty()) {
+ if (!name.is_empty()) {
return name;
}
name = script_path;
- if (!name.empty()) {
+ if (!name.is_empty()) {
return name;
}
return native_type.operator String();
@@ -3026,6 +3734,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
@@ -3055,7 +3796,7 @@ void GDScriptParser::TreePrinter::decrease_indent() {
}
void GDScriptParser::TreePrinter::push_line(const String &p_line) {
- if (!p_line.empty()) {
+ if (!p_line.is_empty()) {
push_text(p_line);
}
printed += "\n";
@@ -3070,7 +3811,7 @@ void GDScriptParser::TreePrinter::push_text(const String &p_text) {
printed += p_text;
}
-void GDScriptParser::TreePrinter::print_annotation(AnnotationNode *p_annotation) {
+void GDScriptParser::TreePrinter::print_annotation(const AnnotationNode *p_annotation) {
push_text(p_annotation->name);
push_text(" (");
for (int i = 0; i < p_annotation->arguments.size(); i++) {
@@ -3264,7 +4005,7 @@ void GDScriptParser::TreePrinter::print_class(ClassNode *p_class) {
if (p_class->extends_used) {
bool first = true;
push_text(" Extends ");
- if (!p_class->extends_path.empty()) {
+ if (!p_class->extends_path.is_empty()) {
push_text(vformat(R"("%s")", p_class->extends_path));
first = false;
}
@@ -3350,6 +4091,10 @@ void GDScriptParser::TreePrinter::print_dictionary(DictionaryNode *p_dictionary)
}
void GDScriptParser::TreePrinter::print_expression(ExpressionNode *p_expression) {
+ if (p_expression == nullptr) {
+ push_text("<invalid expression>");
+ return;
+ }
switch (p_expression->type) {
case Node::ARRAY:
print_array(static_cast<ArrayNode *>(p_expression));
@@ -3378,6 +4123,9 @@ void GDScriptParser::TreePrinter::print_expression(ExpressionNode *p_expression)
case Node::IDENTIFIER:
print_identifier(static_cast<IdentifierNode *>(p_expression));
break;
+ case Node::LAMBDA:
+ print_lambda(static_cast<LambdaNode *>(p_expression));
+ break;
case Node::LITERAL:
print_literal(static_cast<LiteralNode *>(p_expression));
break;
@@ -3437,12 +4185,17 @@ void GDScriptParser::TreePrinter::print_for(ForNode *p_for) {
decrease_indent();
}
-void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function) {
- for (const List<AnnotationNode *>::Element *E = p_function->annotations.front(); E != nullptr; E = E->next()) {
- print_annotation(E->get());
+void GDScriptParser::TreePrinter::print_function(FunctionNode *p_function, const String &p_context) {
+ for (const AnnotationNode *E : p_function->annotations) {
+ print_annotation(E);
+ }
+ push_text(p_context);
+ push_text(" ");
+ if (p_function->identifier) {
+ print_identifier(p_function->identifier);
+ } else {
+ push_text("<anonymous>");
}
- push_text("Function ");
- print_identifier(p_function->identifier);
push_text("( ");
for (int i = 0; i < p_function->parameters.size(); i++) {
if (i > 0) {
@@ -3496,6 +4249,18 @@ void GDScriptParser::TreePrinter::print_if(IfNode *p_if, bool p_is_elif) {
}
}
+void GDScriptParser::TreePrinter::print_lambda(LambdaNode *p_lambda) {
+ print_function(p_lambda->function, "Lambda");
+ push_text("| captures [ ");
+ for (int i = 0; i < p_lambda->captures.size(); i++) {
+ if (i > 0) {
+ push_text(" , ");
+ }
+ push_text(p_lambda->captures[i]->name.operator String());
+ }
+ push_line(" ]");
+}
+
void GDScriptParser::TreePrinter::print_literal(LiteralNode *p_literal) {
// Prefix for string types.
switch (p_literal->value.get_type()) {
@@ -3726,7 +4491,7 @@ void GDScriptParser::TreePrinter::print_ternary_op(TernaryOpNode *p_ternary_op)
}
void GDScriptParser::TreePrinter::print_type(TypeNode *p_type) {
- if (p_type->type_chain.empty()) {
+ if (p_type->type_chain.is_empty()) {
push_text("Void");
} else {
for (int i = 0; i < p_type->type_chain.size(); i++) {
@@ -3761,8 +4526,8 @@ void GDScriptParser::TreePrinter::print_unary_op(UnaryOpNode *p_unary_op) {
}
void GDScriptParser::TreePrinter::print_variable(VariableNode *p_variable) {
- for (const List<AnnotationNode *>::Element *E = p_variable->annotations.front(); E != nullptr; E = E->next()) {
- print_annotation(E->get());
+ for (const AnnotationNode *E : p_variable->annotations) {
+ print_annotation(E);
}
push_text("Variable ");
@@ -3794,7 +4559,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(" =");
@@ -3814,7 +4579,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(" =");
@@ -3846,14 +4611,14 @@ void GDScriptParser::TreePrinter::print_tree(const GDScriptParser &p_parser) {
if (p_parser.is_tool()) {
push_line("@tool");
}
- if (!p_parser.get_tree()->icon_path.empty()) {
+ if (!p_parser.get_tree()->icon_path.is_empty()) {
push_text(R"(@icon (")");
push_text(p_parser.get_tree()->icon_path);
push_line("\")");
}
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 4c9473c7bd..af9b973ada 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,23 +31,22 @@
#ifndef GDSCRIPT_PARSER_H
#define GDSCRIPT_PARSER_H
-#include "core/hash_map.h"
-#include "core/io/multiplayer_api.h"
-#include "core/list.h"
-#include "core/map.h"
-#include "core/reference.h"
-#include "core/resource.h"
-#include "core/script_language.h"
-#include "core/string_name.h"
-#include "core/ustring.h"
-#include "core/variant.h"
-#include "core/vector.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"
+#include "core/string/ustring.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/list.h"
+#include "core/templates/map.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
#include "gdscript_cache.h"
-#include "gdscript_functions.h"
#include "gdscript_tokenizer.h"
#ifdef DEBUG_ENABLED
-#include "core/string_builder.h"
+#include "core/string/string_builder.h"
#include "gdscript_warning.h"
#endif // DEBUG_ENABLED
@@ -77,6 +76,7 @@ public:
struct GetNodeNode;
struct IdentifierNode;
struct IfNode;
+ struct LambdaNode;
struct LiteralNode;
struct MatchNode;
struct MatchBranchNode;
@@ -95,7 +95,12 @@ public:
struct VariableNode;
struct WhileNode;
- struct DataType {
+ class DataType {
+ private:
+ // Private access so we can control memory management.
+ DataType *container_element_type = nullptr;
+
+ public:
enum Kind {
BUILTIN,
NATIVE,
@@ -105,7 +110,6 @@ public:
ENUM_VALUE, // Value from enumeration.
VARIANT, // Can be any type.
UNRESOLVED,
- // TODO: Enum
};
Kind kind = UNRESOLVED;
@@ -129,7 +133,7 @@ public:
ClassNode *class_type = nullptr;
MethodInfo method_info; // For callable/signals.
- HashMap<StringName, int> enum_values; // For enums.
+ Map<StringName, int> enum_values; // For enums.
_FORCE_INLINE_ bool is_set() const { return kind != UNRESOLVED; }
_FORCE_INLINE_ bool has_no_type() const { return type_source == UNDETECTED; }
@@ -137,6 +141,30 @@ public:
_FORCE_INLINE_ bool is_hard_type() const { return type_source > INFERRED; }
String to_string() const;
+ _FORCE_INLINE_ void set_container_element_type(const DataType &p_type) {
+ container_element_type = memnew(DataType(p_type));
+ }
+
+ _FORCE_INLINE_ DataType get_container_element_type() const {
+ ERR_FAIL_COND_V(container_element_type == nullptr, DataType());
+ return *container_element_type;
+ }
+
+ _FORCE_INLINE_ bool has_container_element_type() const {
+ return container_element_type != nullptr;
+ }
+
+ _FORCE_INLINE_ void unset_container_element_type() {
+ if (container_element_type) {
+ memdelete(container_element_type);
+ };
+ 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.
@@ -174,6 +202,37 @@ public:
bool operator!=(const DataType &p_other) const {
return !(this->operator==(p_other));
}
+
+ DataType &operator=(const DataType &p_other) {
+ kind = p_other.kind;
+ type_source = p_other.type_source;
+ is_constant = p_other.is_constant;
+ is_meta_type = p_other.is_meta_type;
+ is_coroutine = p_other.is_coroutine;
+ builtin_type = p_other.builtin_type;
+ native_type = p_other.native_type;
+ enum_type = p_other.enum_type;
+ script_type = p_other.script_type;
+ script_path = p_other.script_path;
+ class_type = p_other.class_type;
+ method_info = p_other.method_info;
+ enum_values = p_other.enum_values;
+ unset_container_element_type();
+ if (p_other.has_container_element_type()) {
+ set_container_element_type(p_other.get_container_element_type());
+ }
+ return *this;
+ }
+
+ DataType() = default;
+
+ DataType(const DataType &p_other) {
+ *this = p_other;
+ }
+
+ ~DataType() {
+ unset_container_element_type();
+ }
};
struct ParserError {
@@ -213,6 +272,7 @@ public:
GET_NODE,
IDENTIFIER,
IF,
+ LAMBDA,
LITERAL,
MATCH,
MATCH_BRANCH,
@@ -287,7 +347,7 @@ public:
struct AssertNode : public Node {
ExpressionNode *condition = nullptr;
- LiteralNode *message = nullptr;
+ ExpressionNode *message = nullptr;
AssertNode() {
type = ASSERT;
@@ -314,6 +374,7 @@ public:
Variant::Operator variant_op = Variant::OP_MAX;
ExpressionNode *assignee = nullptr;
ExpressionNode *assigned_value = nullptr;
+ bool use_conversion_assign = false;
AssignmentNode() {
type = ASSIGNMENT;
@@ -352,7 +413,7 @@ public:
OP_COMP_GREATER_EQUAL,
};
- OpType operation;
+ OpType operation = OpType::OP_ADDITION;
Variant::Operator variant_op = Variant::OP_MAX;
ExpressionNode *left_operand = nullptr;
ExpressionNode *right_operand = nullptr;
@@ -413,9 +474,16 @@ public:
int line = 0;
int leftmost_column = 0;
int rightmost_column = 0;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
};
+
IdentifierNode *identifier = nullptr;
Vector<Value> values;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
EnumNode() {
type = ENUM;
@@ -568,6 +636,17 @@ public:
Vector<StringName> extends; // List for indexing: extends A.B.C
DataType base_type;
String fqcn; // Fully-qualified class name. Identifies uniquely any class in the project.
+#ifdef TOOLS_ENABLED
+ String doc_description;
+ String doc_brief_description;
+ Vector<Pair<String, String>> doc_tutorials;
+
+ // EnumValue docs are parsed after itself, so we need a method to add/modify the doc property later.
+ void set_enum_value_doc(const StringName &p_name, const String &p_doc_description) {
+ ERR_FAIL_INDEX(members_indices[p_name], members.size());
+ members.write[members_indices[p_name]].enum_value.doc_description = p_doc_description;
+ }
+#endif // TOOLS_ENABLED
bool resolved_interface = false;
bool resolved_body = false;
@@ -602,6 +681,9 @@ public:
TypeNode *datatype_specifier = nullptr;
bool infer_datatype = false;
int usages = 0;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
ConstantNode() {
type = CONSTANT;
@@ -651,8 +733,13 @@ public:
SuiteNode *body = nullptr;
bool is_static = false;
bool is_coroutine = false;
- MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
+ Multiplayer::RPCConfig rpc_config;
MethodInfo info;
+ LambdaNode *source_lambda = nullptr;
+#ifdef TOOLS_ENABLED
+ Vector<Variant> default_arg_values;
+ String doc_description;
+#endif // TOOLS_ENABLED
bool resolved_signature = false;
bool resolved_body = false;
@@ -692,6 +779,7 @@ public:
VariableNode *variable_source;
IdentifierNode *bind_source;
};
+ FunctionNode *source_function = nullptr;
int usages = 0; // Useful for binds/iterator variable.
@@ -710,6 +798,21 @@ public:
}
};
+ struct LambdaNode : public ExpressionNode {
+ FunctionNode *function = nullptr;
+ FunctionNode *parent_function = nullptr;
+ Vector<IdentifierNode *> captures;
+ Map<StringName, int> captures_indices;
+
+ bool has_name() const {
+ return function && function->identifier;
+ }
+
+ LambdaNode() {
+ type = LAMBDA;
+ }
+ };
+
struct LiteralNode : public ExpressionNode {
Variant value;
@@ -729,7 +832,7 @@ public:
struct MatchBranchNode : public Node {
Vector<PatternNode *> patterns;
- SuiteNode *block;
+ SuiteNode *block = nullptr;
bool has_wildcard = false;
MatchBranchNode() {
@@ -820,6 +923,9 @@ public:
IdentifierNode *identifier = nullptr;
Vector<ParameterNode *> parameters;
HashMap<StringName, int> parameters_indices;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
SignalNode() {
type = SIGNAL;
@@ -860,6 +966,7 @@ public:
IdentifierNode *bind;
};
StringName name;
+ FunctionNode *source_function = nullptr;
int start_line = 0, end_line = 0;
int start_column = 0, end_column = 0;
@@ -869,10 +976,11 @@ public:
String get_name() const;
Local() {}
- Local(ConstantNode *p_constant) {
+ Local(ConstantNode *p_constant, FunctionNode *p_source_function) {
type = CONSTANT;
constant = p_constant;
name = p_constant->identifier->name;
+ source_function = p_source_function;
start_line = p_constant->start_line;
end_line = p_constant->end_line;
@@ -881,10 +989,11 @@ public:
leftmost_column = p_constant->leftmost_column;
rightmost_column = p_constant->rightmost_column;
}
- Local(VariableNode *p_variable) {
+ Local(VariableNode *p_variable, FunctionNode *p_source_function) {
type = VARIABLE;
variable = p_variable;
name = p_variable->identifier->name;
+ source_function = p_source_function;
start_line = p_variable->start_line;
end_line = p_variable->end_line;
@@ -893,10 +1002,11 @@ public:
leftmost_column = p_variable->leftmost_column;
rightmost_column = p_variable->rightmost_column;
}
- Local(ParameterNode *p_parameter) {
+ Local(ParameterNode *p_parameter, FunctionNode *p_source_function) {
type = PARAMETER;
parameter = p_parameter;
name = p_parameter->identifier->name;
+ source_function = p_source_function;
start_line = p_parameter->start_line;
end_line = p_parameter->end_line;
@@ -905,10 +1015,11 @@ public:
leftmost_column = p_parameter->leftmost_column;
rightmost_column = p_parameter->rightmost_column;
}
- Local(IdentifierNode *p_identifier) {
+ Local(IdentifierNode *p_identifier, FunctionNode *p_source_function) {
type = FOR_VARIABLE;
bind = p_identifier;
name = p_identifier->name;
+ source_function = p_source_function;
start_line = p_identifier->start_line;
end_line = p_identifier->end_line;
@@ -933,9 +1044,9 @@ public:
bool has_local(const StringName &p_name) const;
const Local &get_local(const StringName &p_name) const;
template <class T>
- void add_local(T *p_local) {
+ void add_local(T *p_local, FunctionNode *p_source_function) {
locals_indices[p_local->identifier->name] = locals.size();
- locals.push_back(Local(p_local));
+ locals.push_back(Local(p_local, p_source_function));
}
void add_local(const Local &p_local) {
locals_indices[p_local.name] = locals.size();
@@ -960,6 +1071,7 @@ public:
struct TypeNode : public Node {
Vector<IdentifierNode *> type_chain;
+ TypeNode *container_type = nullptr;
TypeNode() {
type = TYPE;
@@ -974,7 +1086,7 @@ public:
OP_LOGIC_NOT,
};
- OpType operation;
+ OpType operation = OP_POSITIVE;
Variant::Operator variant_op = Variant::OP_MAX;
ExpressionNode *operand = nullptr;
@@ -997,21 +1109,24 @@ 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;
};
bool exported = false;
bool onready = false;
PropertyInfo export_info;
- MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
int assignments = 0;
int usages = 0;
+ bool use_conversion_assign = false;
+#ifdef TOOLS_ENABLED
+ String doc_description;
+#endif // TOOLS_ENABLED
VariableNode() {
type = VARIABLE;
@@ -1105,6 +1220,8 @@ private:
CompletionCall completion_call;
List<CompletionCall> completion_call_stack;
bool passed_cursor = false;
+ bool in_lambda = false;
+ bool lambda_ended = false; // Marker for when a lambda ends, to apply an end of statement if needed.
typedef bool (GDScriptParser::*AnnotationAction)(const AnnotationNode *p_annotation, Node *p_target);
struct AnnotationInfo {
@@ -1142,8 +1259,7 @@ private:
PREC_BIT_XOR,
PREC_BIT_AND,
PREC_BIT_SHIFT,
- PREC_SUBTRACTION,
- PREC_ADDITION,
+ PREC_ADDITION_SUBTRACTION,
PREC_FACTOR,
PREC_SIGN,
PREC_BIT_NOT,
@@ -1193,10 +1309,11 @@ private:
GDScriptTokenizer::Token advance();
bool match(GDScriptTokenizer::Token::Type p_token_type);
- bool check(GDScriptTokenizer::Token::Type p_token_type);
+ bool check(GDScriptTokenizer::Token::Type p_token_type) const;
bool consume(GDScriptTokenizer::Token::Type p_token_type, const String &p_error_message);
- bool is_at_end();
- bool is_statement_end();
+ bool is_at_end() const;
+ bool is_statement_end_token() const;
+ bool is_statement_end() const;
void end_statement(const String &p_context);
void synchronize();
void push_multiline(bool p_state);
@@ -1207,14 +1324,15 @@ 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();
EnumNode *parse_enum();
ParameterNode *parse_parameter();
FunctionNode *parse_function();
- SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr);
+ void parse_function_signature(FunctionNode *p_function, SuiteNode *p_body, const String &p_type);
+ SuiteNode *parse_suite(const String &p_context, SuiteNode *p_suite = nullptr, bool p_for_lambda = false);
// Annotations
AnnotationNode *parse_annotation(uint32_t p_valid_targets);
bool register_annotation(const MethodInfo &p_info, uint32_t p_target_kinds, AnnotationAction p_apply, int p_optional_arguments = 0, bool p_is_vararg = false);
@@ -1226,7 +1344,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();
@@ -1256,6 +1374,7 @@ private:
ExpressionNode *parse_builtin_constant(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_unary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_binary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
+ ExpressionNode *parse_binary_not_in_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_ternary_operator(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_assignment(ExpressionNode *p_previous_operand, bool p_can_assign);
ExpressionNode *parse_array(ExpressionNode *p_previous_operand, bool p_can_assign);
@@ -1268,15 +1387,23 @@ private:
ExpressionNode *parse_await(ExpressionNode *p_previous_operand, bool p_can_assign);
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
+ // Doc comments.
+ int class_doc_line = 0x7FFFFFFF;
+ bool has_comment(int p_line);
+ String get_doc_comment(int p_line, bool p_single_line = false);
+ void get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class);
+#endif // TOOLS_ENABLED
public:
Error parse(const String &p_source_code, const String &p_script_path, bool p_for_completion);
ClassNode *get_tree() const { return head; }
bool is_tool() const { return _is_tool; }
static Variant::Type get_builtin_type(const StringName &p_type);
- static GDScriptFunctions::Function get_builtin_function(const StringName &p_name);
CompletionContext get_completion_context() const { return completion_context; }
CompletionCall get_completion_call() const { return completion_call; }
@@ -1308,7 +1435,7 @@ public:
void push_line(const String &p_line = String());
void push_text(const String &p_text);
- void print_annotation(AnnotationNode *p_annotation);
+ void print_annotation(const AnnotationNode *p_annotation);
void print_array(ArrayNode *p_array);
void print_assert(AssertNode *p_assert);
void print_assignment(AssignmentNode *p_assignment);
@@ -1322,10 +1449,11 @@ public:
void print_expression(ExpressionNode *p_expression);
void print_enum(EnumNode *p_enum);
void print_for(ForNode *p_for);
- void print_function(FunctionNode *p_function);
+ void print_function(FunctionNode *p_function, const String &p_context = "Function");
void print_get_node(GetNodeNode *p_get_node);
void print_if(IfNode *p_if, bool p_is_elif = false);
void print_identifier(IdentifierNode *p_identifier);
+ void print_lambda(LambdaNode *p_lambda);
void print_literal(LiteralNode *p_literal);
void print_match(MatchNode *p_match);
void print_match_branch(MatchBranchNode *p_match_branch);
diff --git a/modules/gdscript/gdscript_tokenizer.cpp b/modules/gdscript/gdscript_tokenizer.cpp
index 9a40aa50ac..3725fb58f5 100644
--- a/modules/gdscript/gdscript_tokenizer.cpp
+++ b/modules/gdscript/gdscript_tokenizer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "gdscript_tokenizer.h"
-#include "core/error_macros.h"
+#include "core/error/error_macros.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
@@ -221,7 +221,7 @@ String GDScriptTokenizer::get_token_name(Token::Type p_token_type) {
void GDScriptTokenizer::set_source_code(const String &p_source_code) {
source = p_source_code;
- if (source.empty()) {
+ if (source.is_empty()) {
_source = U"";
} else {
_source = source.ptr();
@@ -242,6 +242,16 @@ void GDScriptTokenizer::set_multiline_mode(bool p_state) {
multiline_mode = p_state;
}
+void GDScriptTokenizer::push_expression_indented_block() {
+ indent_stack_stack.push_back(indent_stack);
+}
+
+void GDScriptTokenizer::pop_expression_indented_block() {
+ ERR_FAIL_COND(indent_stack_stack.size() == 0);
+ indent_stack = indent_stack_stack.back()->get();
+ indent_stack_stack.pop_back();
+}
+
int GDScriptTokenizer::get_cursor_line() const {
return cursor_line;
}
@@ -287,7 +297,7 @@ void GDScriptTokenizer::push_paren(char32_t p_char) {
}
bool GDScriptTokenizer::pop_paren(char32_t p_expected) {
- if (paren_stack.empty()) {
+ if (paren_stack.is_empty()) {
return false;
}
char32_t actual = paren_stack.back()->get();
@@ -405,7 +415,7 @@ void GDScriptTokenizer::push_error(const Token &p_error) {
}
GDScriptTokenizer::Token GDScriptTokenizer::make_paren_error(char32_t p_paren) {
- if (paren_stack.empty()) {
+ if (paren_stack.is_empty()) {
return make_error(vformat("Closing \"%c\" doesn't have an opening counterpart.", p_paren));
}
Token error = make_error(vformat("Closing \"%c\" doesn't match the opening \"%c\".", p_paren, paren_stack.back()->get()));
@@ -633,6 +643,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
push_error(error);
}
previous_was_underscore = true;
+ } else {
+ previous_was_underscore = false;
}
_advance();
}
@@ -704,6 +716,8 @@ GDScriptTokenizer::Token GDScriptTokenizer::number() {
push_error(error);
}
previous_was_underscore = true;
+ } else {
+ previous_was_underscore = false;
}
_advance();
}
@@ -781,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();
@@ -898,6 +921,9 @@ GDScriptTokenizer::Token GDScriptTokenizer::string() {
_advance();
_advance();
break;
+ } else {
+ // Not a multiline string termination, add consumed quote.
+ result += quote_char;
}
} else {
// Ended single-line string.
@@ -1014,9 +1040,17 @@ void GDScriptTokenizer::check_indent() {
}
if (_peek() == '#') {
// Comment. Advance to the next line.
+#ifdef TOOLS_ENABLED
+ String comment;
+ while (_peek() != '\n' && !_is_at_end()) {
+ comment += _advance();
+ }
+ comments[line] = CommentData(comment, true);
+#else
while (_peek() != '\n' && !_is_at_end()) {
_advance();
}
+#endif // TOOLS_ENABLED
if (_is_at_end()) {
// Reached the end with an empty line, so just dedent as much as needed.
pending_indents -= indent_level();
@@ -1039,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;
@@ -1089,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.
@@ -1125,18 +1166,26 @@ void GDScriptTokenizer::_skip_whitespace() {
newline(!is_bol); // Don't create new line token if line is empty.
check_indent();
break;
- case '#':
+ case '#': {
// Comment.
+#ifdef TOOLS_ENABLED
+ String comment;
+ while (_peek() != '\n' && !_is_at_end()) {
+ comment += _advance();
+ }
+ comments[line] = CommentData(comment, is_bol);
+#else
while (_peek() != '\n' && !_is_at_end()) {
_advance();
}
+#endif // TOOLS_ENABLED
if (_is_at_end()) {
return;
}
_advance(); // Consume '\n'
newline(!is_bol);
check_indent();
- break;
+ } break;
default:
return;
}
@@ -1153,7 +1202,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
if (pending_newline) {
pending_newline = false;
if (!multiline_mode) {
- // Don't return newline tokens on multine mode.
+ // Don't return newline tokens on multiline mode.
return last_newline;
}
}
@@ -1407,7 +1456,7 @@ GDScriptTokenizer::Token GDScriptTokenizer::scan() {
GDScriptTokenizer::GDScriptTokenizer() {
#ifdef TOOLS_ENABLED
if (EditorSettings::get_singleton()) {
- tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size");
+ tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size");
}
#endif // TOOLS_ENABLED
}
diff --git a/modules/gdscript/gdscript_tokenizer.h b/modules/gdscript/gdscript_tokenizer.h
index 4453982d08..b4ee11fd9a 100644
--- a/modules/gdscript/gdscript_tokenizer.h
+++ b/modules/gdscript/gdscript_tokenizer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,10 +31,11 @@
#ifndef GDSCRIPT_TOKENIZER_H
#define GDSCRIPT_TOKENIZER_H
-#include "core/list.h"
-#include "core/set.h"
-#include "core/variant.h"
-#include "core/vector.h"
+#include "core/templates/list.h"
+#include "core/templates/map.h"
+#include "core/templates/set.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
class GDScriptTokenizer {
public:
@@ -177,10 +178,24 @@ public:
}
Token() {
- type = EMPTY;
}
};
+#ifdef TOOLS_ENABLED
+ struct CommentData {
+ String comment;
+ bool new_line = false;
+ CommentData() {}
+ CommentData(const String &p_comment, bool p_new_line) {
+ comment = p_comment;
+ new_line = p_new_line;
+ }
+ };
+ const Map<int, CommentData> &get_comments() const {
+ return comments;
+ }
+#endif // TOOLS_ENABLED
+
private:
String source;
const char32_t *_source = nullptr;
@@ -202,17 +217,23 @@ private:
Token last_newline;
int pending_indents = 0;
List<int> indent_stack;
+ List<List<int>> indent_stack_stack; // For lambdas, which require manipulating the indentation point.
List<char32_t> paren_stack;
char32_t indent_char = '\0';
int position = 0;
int length = 0;
+#ifdef TOOLS_ENABLED
+ Map<int, CommentData> comments;
+#endif // TOOLS_ENABLED
+
_FORCE_INLINE_ bool _is_at_end() { return position >= length; }
_FORCE_INLINE_ char32_t _peek(int p_offset = 0) { return position + p_offset >= 0 && position + p_offset < length ? _current[p_offset] : '\0'; }
int indent_level() const { return indent_stack.size(); }
- bool has_error() const { return !error_stack.empty(); }
+ 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();
@@ -244,6 +265,8 @@ public:
void set_multiline_mode(bool p_state);
bool is_past_cursor() const;
static String get_token_name(Token::Type p_token_type);
+ void push_expression_indented_block(); // For lambdas, or blocks inside expressions.
+ void pop_expression_indented_block(); // For lambdas, or blocks inside expressions.
GDScriptTokenizer();
};
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
new file mode 100644
index 0000000000..e997d3a51e
--- /dev/null
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -0,0 +1,731 @@
+/*************************************************************************/
+/* gdscript_utility_functions.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 "gdscript_utility_functions.h"
+
+#include "core/io/resource_loader.h"
+#include "core/object/class_db.h"
+#include "core/object/method_bind.h"
+#include "core/object/object.h"
+#include "core/templates/oa_hash_map.h"
+#include "core/templates/vector.h"
+#include "gdscript.h"
+
+#ifdef DEBUG_ENABLED
+
+#define VALIDATE_ARG_COUNT(m_count) \
+ if (p_arg_count < m_count) { \
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \
+ r_error.argument = m_count; \
+ r_error.expected = m_count; \
+ *r_ret = Variant(); \
+ return; \
+ } \
+ if (p_arg_count > m_count) { \
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \
+ r_error.argument = m_count; \
+ r_error.expected = m_count; \
+ *r_ret = Variant(); \
+ return; \
+ }
+
+#define VALIDATE_ARG_INT(m_arg) \
+ if (p_args[m_arg]->get_type() != Variant::INT) { \
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
+ r_error.argument = m_arg; \
+ r_error.expected = Variant::INT; \
+ *r_ret = Variant(); \
+ return; \
+ }
+
+#define VALIDATE_ARG_NUM(m_arg) \
+ if (!p_args[m_arg]->is_num()) { \
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \
+ r_error.argument = m_arg; \
+ r_error.expected = Variant::FLOAT; \
+ *r_ret = Variant(); \
+ return; \
+ }
+
+#else
+
+#define VALIDATE_ARG_COUNT(m_count)
+#define VALIDATE_ARG_INT(m_arg)
+#define VALIDATE_ARG_NUM(m_arg)
+
+#endif
+
+struct GDScriptUtilityFunctionsDefinitions {
+ static inline void convert(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(2);
+ VALIDATE_ARG_INT(1);
+ int type = *p_args[1];
+ if (type < 0 || type >= Variant::VARIANT_MAX) {
+ *r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants.");
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::INT;
+ return;
+
+ } else {
+ Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error);
+ }
+ }
+
+ static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ *r_ret = ClassDB::class_exists(*p_args[0]);
+ }
+
+ static inline void _char(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ VALIDATE_ARG_INT(0);
+ char32_t result[2] = { *p_args[0], 0 };
+ *r_ret = String(result);
+ }
+
+ static inline void str(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ if (p_arg_count < 1) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 1;
+ *r_ret = Variant();
+ return;
+ }
+
+ String str;
+ for (int i = 0; i < p_arg_count; i++) {
+ String os = p_args[i]->operator String();
+
+ if (i == 0) {
+ str = os;
+ } else {
+ str += os;
+ }
+ }
+ *r_ret = str;
+ }
+
+ static inline void range(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ switch (p_arg_count) {
+ case 0: {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 1;
+ r_error.expected = 1;
+ *r_ret = Variant();
+ } break;
+ case 1: {
+ VALIDATE_ARG_NUM(0);
+ int count = *p_args[0];
+ Array arr;
+ if (count <= 0) {
+ *r_ret = arr;
+ return;
+ }
+ Error err = arr.resize(count);
+ if (err != OK) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ *r_ret = Variant();
+ return;
+ }
+
+ for (int i = 0; i < count; i++) {
+ arr[i] = i;
+ }
+
+ *r_ret = arr;
+ } break;
+ case 2: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+
+ int from = *p_args[0];
+ int to = *p_args[1];
+
+ Array arr;
+ if (from >= to) {
+ *r_ret = arr;
+ return;
+ }
+ Error err = arr.resize(to - from);
+ if (err != OK) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ *r_ret = Variant();
+ return;
+ }
+ for (int i = from; i < to; i++) {
+ arr[i - from] = i;
+ }
+ *r_ret = arr;
+ } break;
+ case 3: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ VALIDATE_ARG_NUM(2);
+
+ int from = *p_args[0];
+ int to = *p_args[1];
+ int incr = *p_args[2];
+ if (incr == 0) {
+ *r_ret = RTR("Step argument is zero!");
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return;
+ }
+
+ Array arr;
+ if (from >= to && incr > 0) {
+ *r_ret = arr;
+ return;
+ }
+ if (from <= to && incr < 0) {
+ *r_ret = arr;
+ return;
+ }
+
+ // Calculate how many.
+ int count = 0;
+ if (incr > 0) {
+ count = ((to - from - 1) / incr) + 1;
+ } else {
+ count = ((from - to - 1) / -incr) + 1;
+ }
+
+ Error err = arr.resize(count);
+
+ if (err != OK) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ *r_ret = Variant();
+ return;
+ }
+
+ if (incr > 0) {
+ int idx = 0;
+ for (int i = from; i < to; i += incr) {
+ arr[idx++] = i;
+ }
+ } else {
+ int idx = 0;
+ for (int i = from; i > to; i += incr) {
+ arr[idx++] = i;
+ }
+ }
+
+ *r_ret = arr;
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument = 3;
+ r_error.expected = 3;
+ *r_ret = Variant();
+
+ } break;
+ }
+ }
+
+ static inline void load(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ if (p_args[0]->get_type() != Variant::STRING) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::STRING;
+ *r_ret = Variant();
+ } else {
+ *r_ret = ResourceLoader::load(*p_args[0]);
+ }
+ }
+
+ static inline void inst2dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+
+ if (p_args[0]->get_type() == Variant::NIL) {
+ *r_ret = Variant();
+ } else if (p_args[0]->get_type() != Variant::OBJECT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ *r_ret = Variant();
+ } else {
+ Object *obj = *p_args[0];
+ if (!obj) {
+ *r_ret = Variant();
+
+ } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = RTR("Not a script with an instance");
+ return;
+ } else {
+ GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance());
+ Ref<GDScript> base = ins->get_script();
+ if (base.is_null()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = RTR("Not based on a script");
+ return;
+ }
+
+ GDScript *p = base.ptr();
+ Vector<StringName> sname;
+
+ while (p->_owner) {
+ sname.push_back(p->name);
+ p = p->_owner;
+ }
+ sname.reverse();
+
+ if (!p->path.is_resource_file()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = Variant();
+
+ *r_ret = RTR("Not based on a resource file");
+
+ return;
+ }
+
+ NodePath cp(sname, Vector<StringName>(), false);
+
+ Dictionary d;
+ d["@subpath"] = cp;
+ d["@path"] = p->get_path();
+
+ 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;
+ }
+ }
+ }
+
+ static inline void dict2inst(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+
+ if (p_args[0]->get_type() != Variant::DICTIONARY) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::DICTIONARY;
+ *r_ret = Variant();
+
+ return;
+ }
+
+ Dictionary d = *p_args[0];
+
+ if (!d.has("@path")) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = RTR("Invalid instance dictionary format (missing @path)");
+
+ return;
+ }
+
+ Ref<Script> scr = ResourceLoader::load(d["@path"]);
+ if (!scr.is_valid()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = RTR("Invalid instance dictionary format (can't load script at @path)");
+ return;
+ }
+
+ Ref<GDScript> gdscr = scr;
+
+ if (!gdscr.is_valid()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = Variant();
+ *r_ret = RTR("Invalid instance dictionary format (invalid script at @path)");
+ return;
+ }
+
+ NodePath sub;
+ if (d.has("@subpath")) {
+ sub = d["@subpath"];
+ }
+
+ for (int i = 0; i < sub.get_name_count(); i++) {
+ gdscr = gdscr->subclasses[sub.get_name(i)];
+ if (!gdscr.is_valid()) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::OBJECT;
+ *r_ret = Variant();
+ *r_ret = RTR("Invalid instance dictionary (invalid subclasses)");
+ return;
+ }
+ }
+ *r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error);
+
+ if (r_error.error != Callable::CallError::CALL_OK) {
+ *r_ret = Variant();
+ return;
+ }
+
+ GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance());
+ Ref<GDScript> gd_ref = ins->get_script();
+
+ for (KeyValue<StringName, GDScript::MemberInfo> &E : gd_ref->member_indices) {
+ if (d.has(E.key)) {
+ ins->members.write[E.value.index] = d[E.key];
+ }
+ }
+ }
+
+ static inline void Color8(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ if (p_arg_count < 3) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_error.argument = 3;
+ *r_ret = Variant();
+ return;
+ }
+ if (p_arg_count > 4) {
+ r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_error.argument = 4;
+ *r_ret = Variant();
+ return;
+ }
+
+ VALIDATE_ARG_INT(0);
+ VALIDATE_ARG_INT(1);
+ VALIDATE_ARG_INT(2);
+
+ Color color((int64_t)*p_args[0] / 255.0f, (int64_t)*p_args[1] / 255.0f, (int64_t)*p_args[2] / 255.0f);
+
+ if (p_arg_count == 4) {
+ VALIDATE_ARG_INT(3);
+ color.a = (int64_t)*p_args[3] / 255.0f;
+ }
+
+ *r_ret = color;
+ }
+
+ static inline void print_debug(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ String str;
+ for (int i = 0; i < p_arg_count; i++) {
+ str += p_args[i]->operator String();
+ }
+
+ 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);
+ *r_ret = Variant();
+ }
+
+ 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;
+ for (int i = 0; i < script->debug_get_stack_level_count(); i++) {
+ Dictionary frame;
+ frame["source"] = script->debug_get_stack_level_source(i);
+ frame["function"] = script->debug_get_stack_level_function(i);
+ frame["line"] = script->debug_get_stack_level_line(i);
+ ret.push_back(frame);
+ };
+ *r_ret = ret;
+ }
+
+ static inline void len(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) {
+ VALIDATE_ARG_COUNT(1);
+ switch (p_args[0]->get_type()) {
+ case Variant::STRING: {
+ String d = *p_args[0];
+ *r_ret = d.length();
+ } break;
+ case Variant::DICTIONARY: {
+ Dictionary d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::ARRAY: {
+ Array d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ Vector<uint8_t> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_INT32_ARRAY: {
+ Vector<int32_t> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_INT64_ARRAY: {
+ Vector<int64_t> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_FLOAT32_ARRAY: {
+ Vector<float> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_FLOAT64_ARRAY: {
+ Vector<double> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_STRING_ARRAY: {
+ Vector<String> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_VECTOR2_ARRAY: {
+ Vector<Vector2> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_VECTOR3_ARRAY: {
+ Vector<Vector3> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ case Variant::PACKED_COLOR_ARRAY: {
+ Vector<Color> d = *p_args[0];
+ *r_ret = d.size();
+ } break;
+ default: {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ *r_ret = vformat(RTR("Value of type '%s' can't provide a length."), Variant::get_type_name(p_args[0]->get_type()));
+ }
+ }
+ }
+};
+
+struct GDScriptUtilityFunctionInfo {
+ GDScriptUtilityFunctions::FunctionPtr function;
+ MethodInfo info;
+ bool is_constant = false;
+};
+
+static OAHashMap<StringName, GDScriptUtilityFunctionInfo> utility_function_table;
+static List<StringName> utility_function_name_table;
+
+static void _register_function(const String &p_name, const MethodInfo &p_method_info, GDScriptUtilityFunctions::FunctionPtr p_function, bool p_is_const) {
+ StringName sname(p_name);
+
+ ERR_FAIL_COND(utility_function_table.has(sname));
+
+ GDScriptUtilityFunctionInfo function;
+ function.function = p_function;
+ function.info = p_method_info;
+ function.is_constant = p_is_const;
+
+ utility_function_table.insert(sname, function);
+ utility_function_name_table.push_back(sname);
+}
+
+#define REGISTER_FUNC(m_func, m_is_const, m_return_type, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = m_return_type; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_FUNC_NO_ARGS(m_func, m_is_const, m_return_type) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name); \
+ info.return_val.type = m_return_type; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_VARARG_FUNC(m_func, m_is_const, m_return_type) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name); \
+ info.return_val.type = m_return_type; \
+ info.flags |= METHOD_FLAG_VARARG; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_VARIANT_FUNC(m_func, m_is_const, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = Variant::NIL; \
+ info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_CLASS_FUNC(m_func, m_is_const, m_return_type, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = Variant::OBJECT; \
+ info.return_val.hint = PROPERTY_HINT_RESOURCE_TYPE; \
+ info.return_val.class_name = m_return_type; \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define REGISTER_FUNC_DEF(m_func, m_is_const, m_default, m_return_type, ...) \
+ { \
+ String name(#m_func); \
+ if (name.begins_with("_")) { \
+ name = name.substr(1, name.length() - 1); \
+ } \
+ MethodInfo info = MethodInfo(name, __VA_ARGS__); \
+ info.return_val.type = m_return_type; \
+ info.default_arguments.push_back(m_default); \
+ _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \
+ }
+
+#define ARG(m_name, m_type) \
+ PropertyInfo(m_type, m_name)
+
+#define VARARG(m_name) \
+ PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT)
+
+void GDScriptUtilityFunctions::register_functions() {
+ REGISTER_VARIANT_FUNC(convert, true, VARARG("what"), ARG("type", Variant::INT));
+ REGISTER_FUNC(type_exists, true, Variant::BOOL, ARG("type", Variant::STRING_NAME));
+ REGISTER_FUNC(_char, true, Variant::STRING, ARG("char", Variant::INT));
+ REGISTER_VARARG_FUNC(str, true, Variant::STRING);
+ REGISTER_VARARG_FUNC(range, false, Variant::ARRAY);
+ REGISTER_CLASS_FUNC(load, false, "Resource", ARG("path", Variant::STRING));
+ REGISTER_FUNC(inst2dict, false, Variant::DICTIONARY, ARG("instance", Variant::OBJECT));
+ REGISTER_FUNC(dict2inst, false, Variant::OBJECT, ARG("dictionary", Variant::DICTIONARY));
+ REGISTER_FUNC_DEF(Color8, true, 255, Variant::COLOR, ARG("r8", Variant::INT), ARG("g8", Variant::INT), ARG("b8", Variant::INT), ARG("a8", Variant::INT));
+ REGISTER_VARARG_FUNC(print_debug, false, Variant::NIL);
+ REGISTER_FUNC_NO_ARGS(print_stack, false, Variant::NIL);
+ REGISTER_FUNC_NO_ARGS(get_stack, false, Variant::ARRAY);
+ REGISTER_FUNC(len, true, Variant::INT, VARARG("var"));
+}
+
+void GDScriptUtilityFunctions::unregister_functions() {
+ utility_function_name_table.clear();
+ utility_function_table.clear();
+}
+
+GDScriptUtilityFunctions::FunctionPtr GDScriptUtilityFunctions::get_function(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, nullptr);
+ return info->function;
+}
+
+bool GDScriptUtilityFunctions::has_function_return_value(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, false);
+ return info->info.return_val.type != Variant::NIL || bool(info->info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT);
+}
+
+Variant::Type GDScriptUtilityFunctions::get_function_return_type(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, Variant::NIL);
+ return info->info.return_val.type;
+}
+
+StringName GDScriptUtilityFunctions::get_function_return_class(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, StringName());
+ return info->info.return_val.class_name;
+}
+
+Variant::Type GDScriptUtilityFunctions::get_function_argument_type(const StringName &p_function, int p_arg) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, Variant::NIL);
+ ERR_FAIL_COND_V(p_arg >= info->info.arguments.size(), Variant::NIL);
+ return info->info.arguments[p_arg].type;
+}
+
+int GDScriptUtilityFunctions::get_function_argument_count(const StringName &p_function, int p_arg) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, 0);
+ return info->info.arguments.size();
+}
+
+bool GDScriptUtilityFunctions::is_function_vararg(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, false);
+ return (bool)(info->info.flags & METHOD_FLAG_VARARG);
+}
+
+bool GDScriptUtilityFunctions::is_function_constant(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, false);
+ return info->is_constant;
+}
+
+bool GDScriptUtilityFunctions::function_exists(const StringName &p_function) {
+ return utility_function_table.has(p_function);
+}
+
+void GDScriptUtilityFunctions::get_function_list(List<StringName> *r_functions) {
+ for (const StringName &E : utility_function_name_table) {
+ r_functions->push_back(E);
+ }
+}
+
+MethodInfo GDScriptUtilityFunctions::get_function_info(const StringName &p_function) {
+ GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
+ ERR_FAIL_COND_V(!info, MethodInfo());
+ return info->info;
+}
diff --git a/modules/gdscript/gdscript_utility_functions.h b/modules/gdscript/gdscript_utility_functions.h
new file mode 100644
index 0000000000..c6d3718844
--- /dev/null
+++ b/modules/gdscript/gdscript_utility_functions.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* gdscript_utility_functions.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 GDSCRIPT_UTILITY_FUNCTIONS_H
+#define GDSCRIPT_UTILITY_FUNCTIONS_H
+
+#include "core/string/string_name.h"
+#include "core/variant/variant.h"
+
+class GDScriptUtilityFunctions {
+public:
+ typedef void (*FunctionPtr)(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error);
+
+ static FunctionPtr get_function(const StringName &p_function);
+ static bool has_function_return_value(const StringName &p_function);
+ static Variant::Type get_function_return_type(const StringName &p_function);
+ static StringName get_function_return_class(const StringName &p_function);
+ static Variant::Type get_function_argument_type(const StringName &p_function, int p_arg);
+ static int get_function_argument_count(const StringName &p_function, int p_arg);
+ static bool is_function_vararg(const StringName &p_function);
+ static bool is_function_constant(const StringName &p_function);
+
+ static bool function_exists(const StringName &p_function);
+ static void get_function_list(List<StringName> *r_functions);
+ static MethodInfo get_function_info(const StringName &p_function);
+
+ static void register_functions();
+ static void unregister_functions();
+};
+
+#endif // GDSCRIPT_UTILITY_FUNCTIONS_H
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
new file mode 100644
index 0000000000..6dd8c3e0dd
--- /dev/null
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -0,0 +1,3358 @@
+/*************************************************************************/
+/* gdscript_vm.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 "gdscript_function.h"
+
+#include "core/core_string_names.h"
+#include "core/os/os.h"
+#include "gdscript.h"
+#include "gdscript_lambda_callable.h"
+
+Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_instance, Variant *p_stack, String &r_error) const {
+ int address = p_address & ADDR_MASK;
+
+ //sequential table (jump table generated by compiler)
+ switch ((p_address & ADDR_TYPE_MASK) >> ADDR_BITS) {
+ case ADDR_TYPE_STACK: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _stack_size, nullptr);
+#endif
+ return &p_stack[address];
+ } break;
+ case ADDR_TYPE_CONSTANT: {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_INDEX_V(address, _constant_count, nullptr);
+#endif
+ return &_constants_ptr[address];
+ } break;
+ case ADDR_TYPE_MEMBER: {
+#ifdef DEBUG_ENABLED
+ if (unlikely(!p_instance)) {
+ r_error = "Cannot access member without instance.";
+ return nullptr;
+ }
+#endif
+ //member indexing is O(1)
+ return &p_instance->members.write[address];
+ } break;
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "Bad code! (unknown addressing mode).");
+ return nullptr;
+}
+
+#ifdef DEBUG_ENABLED
+static String _get_script_name(const Ref<Script> p_script) {
+ Ref<GDScript> gdscript = p_script;
+ if (gdscript.is_valid()) {
+ return gdscript->get_script_class_name();
+ } else if (p_script->get_name().is_empty()) {
+ return p_script->get_path().get_file();
+ } else {
+ return p_script->get_name();
+ }
+}
+
+static String _get_var_type(const Variant *p_var) {
+ String basestr;
+
+ if (p_var->get_type() == Variant::OBJECT) {
+ bool was_freed;
+ Object *bobj = p_var->get_validated_object_with_check(was_freed);
+ if (!bobj) {
+ if (was_freed) {
+ basestr = "previously freed";
+ } else {
+ basestr = "null instance";
+ }
+ } else {
+ basestr = bobj->get_class();
+ if (bobj->get_script_instance()) {
+ basestr += " (" + _get_script_name(bobj->get_script_instance()->get_script()) + ")";
+ }
+ }
+
+ } else {
+ if (p_var->get_type() == Variant::ARRAY) {
+ basestr = "Array";
+ const Array *p_array = VariantInternal::get_array(p_var);
+ Variant::Type builtin_type = (Variant::Type)p_array->get_typed_builtin();
+ StringName native_type = p_array->get_typed_class_name();
+ Ref<Script> script_type = p_array->get_typed_script();
+
+ if (script_type.is_valid() && script_type->is_valid()) {
+ basestr += "[" + _get_script_name(script_type) + "]";
+ } else if (native_type != StringName()) {
+ basestr += "[" + native_type.operator String() + "]";
+ } else if (builtin_type != Variant::NIL) {
+ basestr += "[" + Variant::get_type_name(builtin_type) + "]";
+ }
+ } else {
+ basestr = Variant::get_type_name(p_var->get_type());
+ }
+ }
+
+ return basestr;
+}
+#endif // DEBUG_ENABLED
+
+String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const String &p_where, const Variant **argptrs) const {
+ String err_text;
+
+ if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ int errorarg = p_err.argument;
+ // Handle the Object to Object case separately as we don't have further class details.
+#ifdef DEBUG_ENABLED
+ if (p_err.expected == Variant::OBJECT && argptrs[errorarg]->get_type() == p_err.expected) {
+ err_text = "Invalid type in " + p_where + ". The Object-derived class of argument " + itos(errorarg + 1) + " (" + _get_var_type(argptrs[errorarg]) + ") is not a subclass of the expected argument class.";
+ } else
+#endif // DEBUG_ENABLED
+ {
+ err_text = "Invalid type in " + p_where + ". Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(Variant::Type(p_err.expected)) + ".";
+ }
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
+ err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
+ err_text = "Invalid call to " + p_where + ". Expected " + itos(p_err.argument) + " arguments.";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+ err_text = "Invalid call. Nonexistent " + p_where + ".";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
+ err_text = "Attempt to call " + p_where + " on a null instance.";
+ } else {
+ err_text = "Bug, call error: #" + itos(p_err.error);
+ }
+
+ return err_text;
+}
+
+void (*type_init_function_table[])(Variant *) = {
+ nullptr, // NIL (shouldn't be called).
+ &VariantInitializer<bool>::init, // BOOL.
+ &VariantInitializer<int64_t>::init, // INT.
+ &VariantInitializer<double>::init, // FLOAT.
+ &VariantInitializer<String>::init, // STRING.
+ &VariantInitializer<Vector2>::init, // VECTOR2.
+ &VariantInitializer<Vector2i>::init, // VECTOR2I.
+ &VariantInitializer<Rect2>::init, // RECT2.
+ &VariantInitializer<Rect2i>::init, // RECT2I.
+ &VariantInitializer<Vector3>::init, // VECTOR3.
+ &VariantInitializer<Vector3i>::init, // VECTOR3I.
+ &VariantInitializer<Transform2D>::init, // TRANSFORM2D.
+ &VariantInitializer<Plane>::init, // PLANE.
+ &VariantInitializer<Quaternion>::init, // QUATERNION.
+ &VariantInitializer<AABB>::init, // AABB.
+ &VariantInitializer<Basis>::init, // BASIS.
+ &VariantInitializer<Transform3D>::init, // TRANSFORM3D.
+ &VariantInitializer<Color>::init, // COLOR.
+ &VariantInitializer<StringName>::init, // STRING_NAME.
+ &VariantInitializer<NodePath>::init, // NODE_PATH.
+ &VariantInitializer<RID>::init, // RID.
+ &VariantInitializer<Object *>::init, // OBJECT.
+ &VariantInitializer<Callable>::init, // CALLABLE.
+ &VariantInitializer<Signal>::init, // SIGNAL.
+ &VariantInitializer<Dictionary>::init, // DICTIONARY.
+ &VariantInitializer<Array>::init, // ARRAY.
+ &VariantInitializer<PackedByteArray>::init, // PACKED_BYTE_ARRAY.
+ &VariantInitializer<PackedInt32Array>::init, // PACKED_INT32_ARRAY.
+ &VariantInitializer<PackedInt64Array>::init, // PACKED_INT64_ARRAY.
+ &VariantInitializer<PackedFloat32Array>::init, // PACKED_FLOAT32_ARRAY.
+ &VariantInitializer<PackedFloat64Array>::init, // PACKED_FLOAT64_ARRAY.
+ &VariantInitializer<PackedStringArray>::init, // PACKED_STRING_ARRAY.
+ &VariantInitializer<PackedVector2Array>::init, // PACKED_VECTOR2_ARRAY.
+ &VariantInitializer<PackedVector3Array>::init, // PACKED_VECTOR3_ARRAY.
+ &VariantInitializer<PackedColorArray>::init, // PACKED_COLOR_ARRAY.
+};
+
+#if defined(__GNUC__)
+#define OPCODES_TABLE \
+ static const void *switch_table_ops[] = { \
+ &&OPCODE_OPERATOR, \
+ &&OPCODE_OPERATOR_VALIDATED, \
+ &&OPCODE_EXTENDS_TEST, \
+ &&OPCODE_IS_BUILTIN, \
+ &&OPCODE_SET_KEYED, \
+ &&OPCODE_SET_KEYED_VALIDATED, \
+ &&OPCODE_SET_INDEXED_VALIDATED, \
+ &&OPCODE_GET_KEYED, \
+ &&OPCODE_GET_KEYED_VALIDATED, \
+ &&OPCODE_GET_INDEXED_VALIDATED, \
+ &&OPCODE_SET_NAMED, \
+ &&OPCODE_SET_NAMED_VALIDATED, \
+ &&OPCODE_GET_NAMED, \
+ &&OPCODE_GET_NAMED_VALIDATED, \
+ &&OPCODE_SET_MEMBER, \
+ &&OPCODE_GET_MEMBER, \
+ &&OPCODE_ASSIGN, \
+ &&OPCODE_ASSIGN_TRUE, \
+ &&OPCODE_ASSIGN_FALSE, \
+ &&OPCODE_ASSIGN_TYPED_BUILTIN, \
+ &&OPCODE_ASSIGN_TYPED_ARRAY, \
+ &&OPCODE_ASSIGN_TYPED_NATIVE, \
+ &&OPCODE_ASSIGN_TYPED_SCRIPT, \
+ &&OPCODE_CAST_TO_BUILTIN, \
+ &&OPCODE_CAST_TO_NATIVE, \
+ &&OPCODE_CAST_TO_SCRIPT, \
+ &&OPCODE_CONSTRUCT, \
+ &&OPCODE_CONSTRUCT_VALIDATED, \
+ &&OPCODE_CONSTRUCT_ARRAY, \
+ &&OPCODE_CONSTRUCT_TYPED_ARRAY, \
+ &&OPCODE_CONSTRUCT_DICTIONARY, \
+ &&OPCODE_CALL, \
+ &&OPCODE_CALL_RETURN, \
+ &&OPCODE_CALL_ASYNC, \
+ &&OPCODE_CALL_UTILITY, \
+ &&OPCODE_CALL_UTILITY_VALIDATED, \
+ &&OPCODE_CALL_GDSCRIPT_UTILITY, \
+ &&OPCODE_CALL_BUILTIN_TYPE_VALIDATED, \
+ &&OPCODE_CALL_SELF_BASE, \
+ &&OPCODE_CALL_METHOD_BIND, \
+ &&OPCODE_CALL_METHOD_BIND_RET, \
+ &&OPCODE_CALL_BUILTIN_STATIC, \
+ &&OPCODE_CALL_PTRCALL_NO_RETURN, \
+ &&OPCODE_CALL_PTRCALL_BOOL, \
+ &&OPCODE_CALL_PTRCALL_INT, \
+ &&OPCODE_CALL_PTRCALL_FLOAT, \
+ &&OPCODE_CALL_PTRCALL_STRING, \
+ &&OPCODE_CALL_PTRCALL_VECTOR2, \
+ &&OPCODE_CALL_PTRCALL_VECTOR2I, \
+ &&OPCODE_CALL_PTRCALL_RECT2, \
+ &&OPCODE_CALL_PTRCALL_RECT2I, \
+ &&OPCODE_CALL_PTRCALL_VECTOR3, \
+ &&OPCODE_CALL_PTRCALL_VECTOR3I, \
+ &&OPCODE_CALL_PTRCALL_TRANSFORM2D, \
+ &&OPCODE_CALL_PTRCALL_PLANE, \
+ &&OPCODE_CALL_PTRCALL_QUATERNION, \
+ &&OPCODE_CALL_PTRCALL_AABB, \
+ &&OPCODE_CALL_PTRCALL_BASIS, \
+ &&OPCODE_CALL_PTRCALL_TRANSFORM3D, \
+ &&OPCODE_CALL_PTRCALL_COLOR, \
+ &&OPCODE_CALL_PTRCALL_STRING_NAME, \
+ &&OPCODE_CALL_PTRCALL_NODE_PATH, \
+ &&OPCODE_CALL_PTRCALL_RID, \
+ &&OPCODE_CALL_PTRCALL_OBJECT, \
+ &&OPCODE_CALL_PTRCALL_CALLABLE, \
+ &&OPCODE_CALL_PTRCALL_SIGNAL, \
+ &&OPCODE_CALL_PTRCALL_DICTIONARY, \
+ &&OPCODE_CALL_PTRCALL_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_BYTE_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_INT32_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_INT64_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_STRING_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_CALL_PTRCALL_PACKED_COLOR_ARRAY, \
+ &&OPCODE_AWAIT, \
+ &&OPCODE_AWAIT_RESUME, \
+ &&OPCODE_CREATE_LAMBDA, \
+ &&OPCODE_JUMP, \
+ &&OPCODE_JUMP_IF, \
+ &&OPCODE_JUMP_IF_NOT, \
+ &&OPCODE_JUMP_TO_DEF_ARGUMENT, \
+ &&OPCODE_RETURN, \
+ &&OPCODE_RETURN_TYPED_BUILTIN, \
+ &&OPCODE_RETURN_TYPED_ARRAY, \
+ &&OPCODE_RETURN_TYPED_NATIVE, \
+ &&OPCODE_RETURN_TYPED_SCRIPT, \
+ &&OPCODE_ITERATE_BEGIN, \
+ &&OPCODE_ITERATE_BEGIN_INT, \
+ &&OPCODE_ITERATE_BEGIN_FLOAT, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR2, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR2I, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR3, \
+ &&OPCODE_ITERATE_BEGIN_VECTOR3I, \
+ &&OPCODE_ITERATE_BEGIN_STRING, \
+ &&OPCODE_ITERATE_BEGIN_DICTIONARY, \
+ &&OPCODE_ITERATE_BEGIN_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_BYTE_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_INT32_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_INT64_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_STRING_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_PACKED_COLOR_ARRAY, \
+ &&OPCODE_ITERATE_BEGIN_OBJECT, \
+ &&OPCODE_ITERATE, \
+ &&OPCODE_ITERATE_INT, \
+ &&OPCODE_ITERATE_FLOAT, \
+ &&OPCODE_ITERATE_VECTOR2, \
+ &&OPCODE_ITERATE_VECTOR2I, \
+ &&OPCODE_ITERATE_VECTOR3, \
+ &&OPCODE_ITERATE_VECTOR3I, \
+ &&OPCODE_ITERATE_STRING, \
+ &&OPCODE_ITERATE_DICTIONARY, \
+ &&OPCODE_ITERATE_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_BYTE_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_INT32_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_INT64_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_STRING_ARRAY, \
+ &&OPCODE_ITERATE_PACKED_VECTOR2_ARRAY, \
+ &&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, \
+ &&OPCODE_TYPE_ADJUST_FLOAT, \
+ &&OPCODE_TYPE_ADJUST_STRING, \
+ &&OPCODE_TYPE_ADJUST_VECTOR2, \
+ &&OPCODE_TYPE_ADJUST_VECTOR2I, \
+ &&OPCODE_TYPE_ADJUST_RECT2, \
+ &&OPCODE_TYPE_ADJUST_RECT2I, \
+ &&OPCODE_TYPE_ADJUST_VECTOR3, \
+ &&OPCODE_TYPE_ADJUST_VECTOR3I, \
+ &&OPCODE_TYPE_ADJUST_TRANSFORM2D, \
+ &&OPCODE_TYPE_ADJUST_PLANE, \
+ &&OPCODE_TYPE_ADJUST_QUATERNION, \
+ &&OPCODE_TYPE_ADJUST_AABB, \
+ &&OPCODE_TYPE_ADJUST_BASIS, \
+ &&OPCODE_TYPE_ADJUST_TRANSFORM, \
+ &&OPCODE_TYPE_ADJUST_COLOR, \
+ &&OPCODE_TYPE_ADJUST_STRING_NAME, \
+ &&OPCODE_TYPE_ADJUST_NODE_PATH, \
+ &&OPCODE_TYPE_ADJUST_RID, \
+ &&OPCODE_TYPE_ADJUST_OBJECT, \
+ &&OPCODE_TYPE_ADJUST_CALLABLE, \
+ &&OPCODE_TYPE_ADJUST_SIGNAL, \
+ &&OPCODE_TYPE_ADJUST_DICTIONARY, \
+ &&OPCODE_TYPE_ADJUST_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_BYTE_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_INT32_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_INT64_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_FLOAT32_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_FLOAT64_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_STRING_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_VECTOR2_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_VECTOR3_ARRAY, \
+ &&OPCODE_TYPE_ADJUST_PACKED_COLOR_ARRAY, \
+ &&OPCODE_ASSERT, \
+ &&OPCODE_BREAKPOINT, \
+ &&OPCODE_LINE, \
+ &&OPCODE_END \
+ }; \
+ static_assert((sizeof(switch_table_ops) / sizeof(switch_table_ops[0]) == (OPCODE_END + 1)), "Opcodes in jump table aren't the same as opcodes in enum.");
+
+#define OPCODE(m_op) \
+ m_op:
+#define OPCODE_WHILE(m_test) \
+ OPSWHILE:
+#define OPCODES_END \
+ OPSEXIT:
+#define OPCODES_OUT \
+ OPSOUT:
+#define DISPATCH_OPCODE goto OPSWHILE
+#define OPCODE_SWITCH(m_test) goto *switch_table_ops[m_test];
+#define OPCODE_BREAK goto OPSEXIT
+#define OPCODE_OUT goto OPSOUT
+#else
+#define OPCODES_TABLE
+#define OPCODE(m_op) case m_op:
+#define OPCODE_WHILE(m_test) while (m_test)
+#define OPCODES_END
+#define OPCODES_OUT
+#define DISPATCH_OPCODE continue
+#define OPCODE_SWITCH(m_test) switch (m_test)
+#define OPCODE_BREAK break
+#define OPCODE_OUT break
+#endif
+
+// Helpers for VariantInternal methods in macros.
+#define OP_GET_BOOL get_bool
+#define OP_GET_INT get_int
+#define OP_GET_FLOAT get_float
+#define OP_GET_VECTOR2 get_vector2
+#define OP_GET_VECTOR2I get_vector2i
+#define OP_GET_VECTOR3 get_vector3
+#define OP_GET_VECTOR3I get_vector3i
+#define OP_GET_RECT2 get_rect2
+#define OP_GET_RECT2I get_rect2i
+#define OP_GET_QUATERNION get_quaternion
+#define OP_GET_COLOR get_color
+#define OP_GET_STRING get_string
+#define OP_GET_STRING_NAME get_string_name
+#define OP_GET_NODE_PATH get_node_path
+#define OP_GET_CALLABLE get_callable
+#define OP_GET_SIGNAL get_signal
+#define OP_GET_ARRAY get_array
+#define OP_GET_DICTIONARY get_dictionary
+#define OP_GET_PACKED_BYTE_ARRAY get_byte_array
+#define OP_GET_PACKED_INT32_ARRAY get_int32_array
+#define OP_GET_PACKED_INT64_ARRAY get_int64_array
+#define OP_GET_PACKED_FLOAT32_ARRAY get_float32_array
+#define OP_GET_PACKED_FLOAT64_ARRAY get_float64_array
+#define OP_GET_PACKED_STRING_ARRAY get_string_array
+#define OP_GET_PACKED_VECTOR2_ARRAY get_vector2_array
+#define OP_GET_PACKED_VECTOR3_ARRAY get_vector3_array
+#define OP_GET_PACKED_COLOR_ARRAY get_color_array
+#define OP_GET_TRANSFORM3D get_transform
+#define OP_GET_TRANSFORM2D get_transform2d
+#define OP_GET_PLANE get_plane
+#define OP_GET_AABB get_aabb
+#define OP_GET_BASIS get_basis
+#define OP_GET_RID get_rid
+
+Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_args, int p_argcount, Callable::CallError &r_err, CallState *p_state) {
+ OPCODES_TABLE;
+
+ if (!_code_ptr) {
+ return Variant();
+ }
+
+ r_err.error = Callable::CallError::CALL_OK;
+
+ Variant retvalue;
+ Variant *stack = nullptr;
+ Variant **instruction_args = nullptr;
+ const void **call_args_ptr = nullptr;
+ int defarg = 0;
+
+#ifdef DEBUG_ENABLED
+
+ //GDScriptLanguage::get_singleton()->calls++;
+
+#endif
+
+ uint32_t alloca_size = 0;
+ GDScript *script;
+ int ip = 0;
+ int line = _initial_line;
+
+ if (p_state) {
+ //use existing (supplied) state (awaited)
+ stack = (Variant *)p_state->stack.ptr();
+ instruction_args = (Variant **)&p_state->stack.ptr()[sizeof(Variant) * p_state->stack_size]; //ptr() to avoid bounds check
+ line = p_state->line;
+ ip = p_state->ip;
+ alloca_size = p_state->stack.size();
+ script = p_state->script;
+ p_instance = p_state->instance;
+ defarg = p_state->defarg;
+
+ } else {
+ if (p_argcount != _argument_count) {
+ if (p_argcount > _argument_count) {
+ r_err.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+ r_err.argument = _argument_count;
+
+ return Variant();
+ } else if (p_argcount < _argument_count - _default_arg_count) {
+ r_err.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+ r_err.argument = _argument_count - _default_arg_count;
+ return Variant();
+ } else {
+ defarg = _argument_count - p_argcount;
+ }
+ }
+
+ // Add 3 here for self, class, and nil.
+ alloca_size = sizeof(Variant *) * 3 + sizeof(Variant *) * _instruction_args_size + sizeof(Variant) * _stack_size;
+
+ uint8_t *aptr = (uint8_t *)alloca(alloca_size);
+ stack = (Variant *)aptr;
+
+ for (int i = 0; i < p_argcount; i++) {
+ if (!argument_types[i].has_type) {
+ memnew_placement(&stack[i + 3], Variant(*p_args[i]));
+ continue;
+ }
+
+ if (!argument_types[i].is_type(*p_args[i], true)) {
+ r_err.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_err.argument = i;
+ r_err.expected = argument_types[i].kind == GDScriptDataType::BUILTIN ? argument_types[i].builtin_type : Variant::OBJECT;
+ return Variant();
+ }
+ if (argument_types[i].kind == GDScriptDataType::BUILTIN) {
+ Variant arg;
+ Variant::construct(argument_types[i].builtin_type, arg, &p_args[i], 1, r_err);
+ memnew_placement(&stack[i + 3], Variant(arg));
+ } else {
+ memnew_placement(&stack[i + 3], Variant(*p_args[i]));
+ }
+ }
+ for (int i = p_argcount + 3; i < _stack_size; i++) {
+ memnew_placement(&stack[i], Variant);
+ }
+
+ memnew_placement(&stack[ADDR_STACK_NIL], Variant);
+
+ if (_instruction_args_size) {
+ instruction_args = (Variant **)&aptr[sizeof(Variant) * _stack_size];
+ } else {
+ instruction_args = nullptr;
+ }
+
+ if (p_instance) {
+ memnew_placement(&stack[ADDR_STACK_SELF], Variant(p_instance->owner));
+ script = p_instance->script.ptr();
+ } else {
+ memnew_placement(&stack[ADDR_STACK_SELF], Variant);
+ script = _script;
+ }
+ }
+ if (_ptrcall_args_size) {
+ call_args_ptr = (const void **)alloca(_ptrcall_args_size * sizeof(void *));
+ } else {
+ call_args_ptr = nullptr;
+ }
+
+ memnew_placement(&stack[ADDR_STACK_CLASS], Variant(script));
+
+ for (const KeyValue<int, Variant::Type> &E : temporary_slots) {
+ type_init_function_table[E.value](&stack[E.key]);
+ }
+
+ String err_text;
+
+#ifdef DEBUG_ENABLED
+
+ if (EngineDebugger::is_active()) {
+ GDScriptLanguage::get_singleton()->enter_function(p_instance, this, stack, &ip, &line);
+ }
+
+#define GD_ERR_BREAK(m_cond) \
+ { \
+ if (unlikely(m_cond)) { \
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition ' " _STR(m_cond) " ' is true. Breaking..:"); \
+ OPCODE_BREAK; \
+ } \
+ }
+
+#define CHECK_SPACE(m_space) \
+ GD_ERR_BREAK((ip + m_space) > _code_size)
+
+#define GET_VARIANT_PTR(m_v, m_code_ofs) \
+ Variant *m_v; \
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, stack, err_text); \
+ if (unlikely(!m_v)) \
+ OPCODE_BREAK;
+
+#else
+#define GD_ERR_BREAK(m_cond)
+#define CHECK_SPACE(m_space)
+#define GET_VARIANT_PTR(m_v, m_code_ofs) \
+ Variant *m_v; \
+ m_v = _get_variant(_code_ptr[ip + m_code_ofs], p_instance, stack, err_text);
+
+#endif
+
+#define GET_INSTRUCTION_ARG(m_v, m_idx) \
+ Variant *m_v = instruction_args[m_idx]
+
+#ifdef DEBUG_ENABLED
+
+ uint64_t function_start_time = 0;
+ uint64_t function_call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_start_time = OS::get_singleton()->get_ticks_usec();
+ function_call_time = 0;
+ profile.call_count++;
+ profile.frame_call_count++;
+ }
+ bool exit_ok = false;
+ bool awaited = false;
+#endif
+
+#ifdef DEBUG_ENABLED
+ OPCODE_WHILE(ip < _code_size) {
+ int last_opcode = _code_ptr[ip] & INSTR_MASK;
+#else
+ OPCODE_WHILE(true) {
+#endif
+ // Load arguments for the instruction before each instruction.
+ int instr_arg_count = ((_code_ptr[ip]) & INSTR_ARGS_MASK) >> INSTR_BITS;
+ for (int i = 0; i < instr_arg_count; i++) {
+ GET_VARIANT_PTR(v, i + 1);
+ instruction_args[i] = v;
+ }
+
+ OPCODE_SWITCH(_code_ptr[ip] & INSTR_MASK) {
+ OPCODE(OPCODE_OPERATOR) {
+ CHECK_SPACE(5);
+
+ bool valid;
+ Variant::Operator op = (Variant::Operator)_code_ptr[ip + 4];
+ GD_ERR_BREAK(op >= Variant::OP_MAX);
+
+ GET_INSTRUCTION_ARG(a, 0);
+ GET_INSTRUCTION_ARG(b, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+#ifdef DEBUG_ENABLED
+
+ Variant ret;
+ Variant::evaluate(op, *a, *b, ret, valid);
+#else
+ Variant::evaluate(op, *a, *b, *dst, valid);
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ if (ret.get_type() == Variant::STRING) {
+ //return a string when invalid with the error
+ err_text = ret;
+ err_text += " in operator '" + Variant::get_operator_name(op) + "'.";
+ } else {
+ err_text = "Invalid operands '" + Variant::get_type_name(a->get_type()) + "' and '" + Variant::get_type_name(b->get_type()) + "' in operator '" + Variant::get_operator_name(op) + "'.";
+ }
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_OPERATOR_VALIDATED) {
+ CHECK_SPACE(5);
+
+ int operator_idx = _code_ptr[ip + 4];
+ GD_ERR_BREAK(operator_idx < 0 || operator_idx >= _operator_funcs_count);
+ Variant::ValidatedOperatorEvaluator operator_func = _operator_funcs_ptr[operator_idx];
+
+ GET_INSTRUCTION_ARG(a, 0);
+ GET_INSTRUCTION_ARG(b, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ operator_func(a, b, dst);
+
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_EXTENDS_TEST) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(a, 0);
+ GET_INSTRUCTION_ARG(b, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+#ifdef DEBUG_ENABLED
+ if (b->get_type() != Variant::OBJECT || b->operator Object *() == nullptr) {
+ err_text = "Right operand of 'is' is not a class.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ bool extends_ok = false;
+ if (a->get_type() == Variant::OBJECT && a->operator Object *() != nullptr) {
+#ifdef DEBUG_ENABLED
+ bool was_freed;
+ Object *obj_A = a->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Left operand of 'is' is a previously freed instance.";
+ OPCODE_BREAK;
+ }
+
+ Object *obj_B = b->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Right operand of 'is' is a previously freed instance.";
+ OPCODE_BREAK;
+ }
+#else
+
+ Object *obj_A = *a;
+ Object *obj_B = *b;
+#endif // DEBUG_ENABLED
+
+ GDScript *scr_B = Object::cast_to<GDScript>(obj_B);
+
+ if (scr_B) {
+ //if B is a script, the only valid condition is that A has an instance which inherits from the script
+ //in other situation, this should return false.
+
+ if (obj_A->get_script_instance() && obj_A->get_script_instance()->get_language() == GDScriptLanguage::get_singleton()) {
+ GDScript *cmp = static_cast<GDScript *>(obj_A->get_script_instance()->get_script().ptr());
+ //bool found=false;
+ while (cmp) {
+ if (cmp == scr_B) {
+ //inherits from script, all ok
+ extends_ok = true;
+ break;
+ }
+
+ cmp = cmp->_base;
+ }
+ }
+
+ } else {
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(obj_B);
+
+#ifdef DEBUG_ENABLED
+ if (!nc) {
+ err_text = "Right operand of 'is' is not a class (type: '" + obj_B->get_class() + "').";
+ OPCODE_BREAK;
+ }
+#endif
+ extends_ok = ClassDB::is_parent_class(obj_A->get_class_name(), nc->get_name());
+ }
+ }
+
+ *dst = extends_ok;
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_IS_BUILTIN) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(value, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 3];
+
+ GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+ *dst = value->get_type() == var_type;
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_KEYED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(value, 2);
+
+ bool valid;
+ dst->set(*index, *value, &valid);
+
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_KEYED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(value, 2);
+
+ int index_setter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_setter < 0 || index_setter >= _keyed_setters_count);
+ const Variant::ValidatedKeyedSetter setter = _keyed_setters_ptr[index_setter];
+
+ bool valid;
+ setter(dst, index, value, &valid);
+
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Invalid set index " + v + " (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_INDEXED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(value, 2);
+
+ int index_setter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_setter < 0 || index_setter >= _indexed_setters_count);
+ const Variant::ValidatedIndexedSetter setter = _indexed_setters_ptr[index_setter];
+
+ int64_t int_index = *VariantInternal::get_int(index);
+
+ bool oob;
+ setter(dst, int_index, value, &oob);
+
+#ifdef DEBUG_ENABLED
+ if (oob) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Out of bounds set index " + v + " (on base: '" + _get_var_type(dst) + "')";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_KEYED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ bool valid;
+#ifdef DEBUG_ENABLED
+ // Allow better error message in cases where src and dst are the same stack position.
+ Variant ret = src->get(*index, &valid);
+#else
+ *dst = src->get(*index, &valid);
+
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "').";
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_KEYED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(key, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ int index_getter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_getter < 0 || index_getter >= _keyed_getters_count);
+ const Variant::ValidatedKeyedGetter getter = _keyed_getters_ptr[index_getter];
+
+ bool valid;
+#ifdef DEBUG_ENABLED
+ // Allow better error message in cases where src and dst are the same stack position.
+ Variant ret;
+ getter(src, key, &ret, &valid);
+#else
+ getter(src, key, dst, &valid);
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String v = key->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(key) + "'";
+ }
+ err_text = "Invalid get index " + v + " (on base: '" + _get_var_type(src) + "').";
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_INDEXED_VALIDATED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(index, 1);
+ GET_INSTRUCTION_ARG(dst, 2);
+
+ int index_getter = _code_ptr[ip + 4];
+ GD_ERR_BREAK(index_getter < 0 || index_getter >= _indexed_getters_count);
+ const Variant::ValidatedIndexedGetter getter = _indexed_getters_ptr[index_getter];
+
+ int64_t int_index = *VariantInternal::get_int(index);
+
+ bool oob;
+ getter(src, int_index, dst, &oob);
+
+#ifdef DEBUG_ENABLED
+ if (oob) {
+ String v = index->operator String();
+ if (v != "") {
+ v = "'" + v + "'";
+ } else {
+ v = "of type '" + _get_var_type(index) + "'";
+ }
+ err_text = "Out of bounds get index " + v + " (on base: '" + _get_var_type(src) + "')";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_NAMED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(value, 1);
+
+ int indexname = _code_ptr[ip + 3];
+
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+
+ bool valid;
+ dst->set_named(*index, *value, valid);
+
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ String err_type;
+ err_text = "Invalid set index '" + String(*index) + "' (on base: '" + _get_var_type(dst) + "') with value of type '" + _get_var_type(value) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_NAMED_VALIDATED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(value, 1);
+
+ int index_setter = _code_ptr[ip + 3];
+ GD_ERR_BREAK(index_setter < 0 || index_setter >= _setters_count);
+ const Variant::ValidatedSetter setter = _setters_ptr[index_setter];
+
+ setter(dst, value);
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_NAMED) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+
+ int indexname = _code_ptr[ip + 3];
+
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+
+ bool valid;
+#ifdef DEBUG_ENABLED
+ //allow better error message in cases where src and dst are the same stack position
+ Variant ret = src->get_named(*index, valid);
+
+#else
+ *dst = src->get_named(*index, valid);
+#endif
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ if (src->has_method(*index)) {
+ err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "'). Did you mean '." + index->operator String() + "()' or funcref(obj, \"" + index->operator String() + "\") ?";
+ } else {
+ err_text = "Invalid get index '" + index->operator String() + "' (on base: '" + _get_var_type(src) + "').";
+ }
+ OPCODE_BREAK;
+ }
+ *dst = ret;
+#endif
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_NAMED_VALIDATED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+
+ int index_getter = _code_ptr[ip + 3];
+ GD_ERR_BREAK(index_getter < 0 || index_getter >= _getters_count);
+ const Variant::ValidatedGetter getter = _getters_ptr[index_getter];
+
+ getter(src, dst);
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_SET_MEMBER) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(src, 0);
+ int indexname = _code_ptr[ip + 2];
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+
+ bool valid;
+#ifndef DEBUG_ENABLED
+ ClassDB::set_property(p_instance->owner, *index, *src, &valid);
+#else
+ bool ok = ClassDB::set_property(p_instance->owner, *index, *src, &valid);
+ if (!ok) {
+ err_text = "Internal error setting property: " + String(*index);
+ OPCODE_BREAK;
+ } else if (!valid) {
+ err_text = "Error setting property '" + String(*index) + "' with value of type " + Variant::get_type_name(src->get_type()) + ".";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_GET_MEMBER) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(dst, 0);
+ int indexname = _code_ptr[ip + 2];
+ GD_ERR_BREAK(indexname < 0 || indexname >= _global_names_count);
+ const StringName *index = &_global_names_ptr[indexname];
+#ifndef DEBUG_ENABLED
+ ClassDB::get_property(p_instance->owner, *index, *dst);
+#else
+ bool ok = ClassDB::get_property(p_instance->owner, *index, *dst);
+ if (!ok) {
+ err_text = "Internal error getting property: " + String(*index);
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+ *dst = *src;
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TRUE) {
+ CHECK_SPACE(2);
+ GET_INSTRUCTION_ARG(dst, 0);
+
+ *dst = true;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_FALSE) {
+ CHECK_SPACE(2);
+ GET_INSTRUCTION_ARG(dst, 0);
+
+ *dst = false;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_BUILTIN) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+ Variant::Type var_type = (Variant::Type)_code_ptr[ip + 3];
+ GD_ERR_BREAK(var_type < 0 || var_type >= Variant::VARIANT_MAX);
+
+ if (src->get_type() != var_type) {
+#ifdef DEBUG_ENABLED
+ if (Variant::can_convert_strict(src->get_type(), var_type)) {
+#endif // DEBUG_ENABLED
+ Callable::CallError ce;
+ Variant::construct(var_type, *dst, const_cast<const Variant **>(&src), 1, ce);
+ } 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) + "'.";
+ OPCODE_BREAK;
+ }
+ } else {
+#endif // DEBUG_ENABLED
+ *dst = *src;
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_ARRAY) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+ Array *dst_arr = VariantInternal::get_array(dst);
+
+ 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 '" + +"'.";
+#endif
+ OPCODE_BREAK;
+ }
+ if (!dst_arr->typed_assign(*src)) {
+#ifdef DEBUG_ENABLED
+ err_text = "Trying to assign a typed array with an array of different type.'";
+#endif
+ OPCODE_BREAK;
+ }
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_NATIVE) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+#ifdef DEBUG_ENABLED
+ GET_INSTRUCTION_ARG(type, 2);
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
+ 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() + "'.";
+ 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() + "'.";
+ OPCODE_BREAK;
+ }
+#endif // DEBUG_ENABLED
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ASSIGN_TYPED_SCRIPT) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(dst, 0);
+ GET_INSTRUCTION_ARG(src, 1);
+
+#ifdef DEBUG_ENABLED
+ GET_INSTRUCTION_ARG(type, 2);
+ Script *base_type = Object::cast_to<Script>(type->operator Object *());
+
+ GD_ERR_BREAK(!base_type);
+
+ 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;
+ }
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
+ 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() + "'.";
+ OPCODE_BREAK;
+ }
+
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+ bool valid = false;
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+
+ 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() + "'.";
+ OPCODE_BREAK;
+ }
+ }
+#endif // DEBUG_ENABLED
+
+ *dst = *src;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_BUILTIN) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ Variant::Type to_type = (Variant::Type)_code_ptr[ip + 3];
+
+ 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);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ err_text = "Invalid cast: could not convert value to '" + Variant::get_type_name(to_type) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_NATIVE) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ GET_INSTRUCTION_ARG(to_type, 2);
+
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(to_type->operator Object *());
+ 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;
+ }
+#endif
+ Object *src_obj = src->operator Object *();
+
+ if (src_obj && !ClassDB::is_parent_class(src_obj->get_class_name(), nc->get_name())) {
+ *dst = Variant(); // invalid cast, assign NULL
+ } else {
+ *dst = *src;
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CAST_TO_SCRIPT) {
+ CHECK_SPACE(4);
+ GET_INSTRUCTION_ARG(src, 0);
+ GET_INSTRUCTION_ARG(dst, 1);
+ GET_INSTRUCTION_ARG(to_type, 2);
+
+ Script *base_type = Object::cast_to<Script>(to_type->operator Object *());
+
+ 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;
+ }
+#endif
+
+ bool valid = false;
+
+ if (src->get_type() != Variant::NIL && src->operator Object *() != nullptr) {
+ ScriptInstance *scr_inst = src->operator Object *()->get_script_instance();
+
+ if (scr_inst) {
+ Script *src_type = src->operator Object *()->get_script_instance()->get_script().ptr();
+
+ while (src_type) {
+ if (src_type == base_type) {
+ valid = true;
+ break;
+ }
+ src_type = src_type->get_base_script().ptr();
+ }
+ }
+ }
+
+ if (valid) {
+ *dst = *src; // Valid cast, copy the source object
+ } else {
+ *dst = Variant(); // invalid cast, assign NULL
+ }
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT) {
+ CHECK_SPACE(2 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+
+ Variant::Type t = Variant::Type(_code_ptr[ip + 2]);
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ Callable::CallError err;
+ Variant::construct(t, *dst, (const Variant **)argptrs, argc, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ err_text = _get_call_error(err, "'" + Variant::get_type_name(t) + "' constructor", (const Variant **)argptrs);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT_VALIDATED) {
+ CHECK_SPACE(2 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+
+ int constructor_idx = _code_ptr[ip + 2];
+ GD_ERR_BREAK(constructor_idx < 0 || constructor_idx >= _constructors_count);
+ Variant::ValidatedConstructor constructor = _constructors_ptr[constructor_idx];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ constructor(dst, (const Variant **)argptrs);
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT_ARRAY) {
+ CHECK_SPACE(1 + instr_arg_count);
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ Array array;
+ array.resize(argc);
+
+ for (int i = 0; i < argc; i++) {
+ array[i] = *(instruction_args[i]);
+ }
+
+ GET_INSTRUCTION_ARG(dst, argc);
+ *dst = Variant(); // Clear potential previous typed array.
+
+ *dst = array;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT_TYPED_ARRAY) {
+ CHECK_SPACE(3 + instr_arg_count);
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+
+ GET_INSTRUCTION_ARG(script_type, argc + 1);
+ Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 2];
+ int native_type_idx = _code_ptr[ip + 3];
+ GD_ERR_BREAK(native_type_idx < 0 || native_type_idx >= _global_names_count);
+ const StringName native_type = _global_names_ptr[native_type_idx];
+
+ Array array;
+ array.set_typed(builtin_type, native_type, script_type);
+ array.resize(argc);
+
+ for (int i = 0; i < argc; i++) {
+ array[i] = *(instruction_args[i]);
+ }
+
+ GET_INSTRUCTION_ARG(dst, argc);
+ *dst = Variant(); // Clear potential previous typed array.
+
+ *dst = array;
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CONSTRUCT_DICTIONARY) {
+ CHECK_SPACE(2 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ Dictionary dict;
+
+ for (int i = 0; i < argc; i++) {
+ GET_INSTRUCTION_ARG(k, i * 2 + 0);
+ GET_INSTRUCTION_ARG(v, i * 2 + 1);
+ dict[*k] = *v;
+ }
+
+ GET_INSTRUCTION_ARG(dst, argc * 2);
+
+ *dst = dict;
+
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_ASYNC)
+ OPCODE(OPCODE_CALL_RETURN)
+ OPCODE(OPCODE_CALL) {
+ CHECK_SPACE(3 + instr_arg_count);
+ bool call_ret = (_code_ptr[ip] & INSTR_MASK) != OPCODE_CALL;
+#ifdef DEBUG_ENABLED
+ bool call_async = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_ASYNC;
+#endif
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ int methodname_idx = _code_ptr[ip + 2];
+ GD_ERR_BREAK(methodname_idx < 0 || methodname_idx >= _global_names_count);
+ const StringName *methodname = &_global_names_ptr[methodname_idx];
+
+ GET_INSTRUCTION_ARG(base, argc);
+ Variant **argptrs = instruction_args;
+
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+
+#endif
+ Callable::CallError err;
+ if (call_ret) {
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ base->call(*methodname, (const Variant **)argptrs, argc, *ret, err);
+#ifdef DEBUG_ENABLED
+ if (!call_async && ret->get_type() == Variant::OBJECT) {
+ // Check if getting a function state without await.
+ bool was_freed = false;
+ Object *obj = ret->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Got a freed object as a result of the call.";
+ OPCODE_BREAK;
+ }
+ if (obj && obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
+ err_text = R"(Trying to call an async function without "await".)";
+ OPCODE_BREAK;
+ }
+ }
+#endif
+ } else {
+ Variant ret;
+ base->call(*methodname, (const Variant **)argptrs, argc, ret, err);
+ }
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = *methodname;
+ String basestr = _get_var_type(base);
+ bool is_callable = false;
+
+ if (methodstr == "call") {
+ if (argc >= 1 && base->get_type() != Variant::CALLABLE) {
+ methodstr = String(*argptrs[0]) + " (via call)";
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ err.argument += 1;
+ }
+ } else {
+ methodstr = base->operator String() + " (Callable)";
+ is_callable = true;
+ }
+ } else if (methodstr == "free") {
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+ if (base->is_ref()) {
+ err_text = "Attempted to free a reference.";
+ OPCODE_BREAK;
+ } else if (base->get_type() == Variant::OBJECT) {
+ err_text = "Attempted to free a locked object (calling or emitting).";
+ OPCODE_BREAK;
+ }
+ }
+ } else if (methodstr == "call_recursive" && basestr == "TreeItem") {
+ if (argc >= 1) {
+ methodstr = String(*argptrs[0]) + " (via TreeItem.call_recursive)";
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ err.argument += 1;
+ }
+ }
+ }
+ err_text = _get_call_error(err, "function '" + methodstr + (is_callable ? "" : "' in base '" + basestr) + "'", (const Variant **)argptrs);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_METHOD_BIND)
+ OPCODE(OPCODE_CALL_METHOD_BIND_RET) {
+ CHECK_SPACE(3 + instr_arg_count);
+ bool call_ret = (_code_ptr[ip] & INSTR_MASK) == OPCODE_CALL_METHOD_BIND_RET;
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+ GET_INSTRUCTION_ARG(base, argc);
+
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *base_obj = base->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to call a function on a previously freed instance.";
+ OPCODE_BREAK;
+ } else if (!base_obj) {
+ err_text = "Trying to call a function on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *base_obj = base->operator Object *();
+#endif
+ Variant **argptrs = instruction_args;
+
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ Callable::CallError err;
+ if (call_ret) {
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ *ret = method->call(base_obj, (const Variant **)argptrs, argc, err);
+ } else {
+ method->call(base_obj, (const Variant **)argptrs, argc, err);
+ }
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = method->get_name();
+ String basestr = _get_var_type(base);
+
+ if (methodstr == "call") {
+ if (argc >= 1) {
+ methodstr = String(*argptrs[0]) + " (via call)";
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_ARGUMENT) {
+ err.argument += 1;
+ }
+ }
+ } else if (methodstr == "free") {
+ if (err.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
+ if (base->is_ref()) {
+ err_text = "Attempted to free a reference.";
+ OPCODE_BREAK;
+ } else if (base->get_type() == Variant::OBJECT) {
+ err_text = "Attempted to free a locked object (calling or emitting).";
+ OPCODE_BREAK;
+ }
+ }
+ }
+ err_text = _get_call_error(err, "function '" + methodstr + "' in base '" + basestr + "'", (const Variant **)argptrs);
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_BUILTIN_STATIC) {
+ CHECK_SPACE(4 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ GD_ERR_BREAK(_code_ptr[ip + 1] < 0 || _code_ptr[ip + 1] >= Variant::VARIANT_MAX);
+ Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 1];
+
+ int methodname_idx = _code_ptr[ip + 2];
+ GD_ERR_BREAK(methodname_idx < 0 || methodname_idx >= _global_names_count);
+ const StringName *methodname = &_global_names_ptr[methodname_idx];
+
+ int argc = _code_ptr[ip + 3];
+ GD_ERR_BREAK(argc < 0);
+
+ GET_INSTRUCTION_ARG(ret, argc);
+
+ const Variant **argptrs = const_cast<const Variant **>(instruction_args);
+
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ Callable::CallError err;
+ Variant::call_static(builtin_type, *methodname, argptrs, argc, *ret, err);
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+
+ if (err.error != Callable::CallError::CALL_OK) {
+ err_text = _get_call_error(err, "static function '" + methodname->operator String() + "' in type '" + Variant::get_type_name(builtin_type) + "'", argptrs);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 4;
+ }
+ DISPATCH_OPCODE;
+
+#ifdef DEBUG_ENABLED
+#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]; \
+ GD_ERR_BREAK(argc < 0); \
+ GET_INSTRUCTION_ARG(base, argc); \
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count); \
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]]; \
+ bool freed = false; \
+ Object *base_obj = base->get_validated_object_with_check(freed); \
+ if (freed) { \
+ err_text = "Trying to call a function on a previously freed instance."; \
+ OPCODE_BREAK; \
+ } else if (!base_obj) { \
+ err_text = "Trying to call a function on a null value."; \
+ OPCODE_BREAK; \
+ } \
+ const void **argptrs = call_args_ptr; \
+ for (int i = 0; i < argc; i++) { \
+ GET_INSTRUCTION_ARG(v, i); \
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v); \
+ } \
+ uint64_t call_time = 0; \
+ if (GDScriptLanguage::get_singleton()->profiling) { \
+ call_time = OS::get_singleton()->get_ticks_usec(); \
+ } \
+ GET_INSTRUCTION_ARG(ret, argc + 1); \
+ VariantInternal::initialize(ret, Variant::m_type); \
+ void *ret_opaque = VariantInternal::OP_GET_##m_type(ret); \
+ method->ptrcall(base_obj, argptrs, ret_opaque); \
+ if (GDScriptLanguage::get_singleton()->profiling) { \
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time; \
+ } \
+ ip += 3; \
+ } \
+ DISPATCH_OPCODE
+#else
+#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]]; \
+ Object *base_obj = *VariantInternal::get_object(base); \
+ const void **argptrs = call_args_ptr; \
+ for (int i = 0; i < argc; i++) { \
+ GET_INSTRUCTION_ARG(v, i); \
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v); \
+ } \
+ GET_INSTRUCTION_ARG(ret, argc + 1); \
+ VariantInternal::initialize(ret, Variant::m_type); \
+ void *ret_opaque = VariantInternal::OP_GET_##m_type(ret); \
+ method->ptrcall(base_obj, argptrs, ret_opaque); \
+ ip += 3; \
+ } \
+ DISPATCH_OPCODE
+#endif
+
+ OPCODE_CALL_PTR(BOOL);
+ OPCODE_CALL_PTR(INT);
+ OPCODE_CALL_PTR(FLOAT);
+ OPCODE_CALL_PTR(STRING);
+ OPCODE_CALL_PTR(VECTOR2);
+ OPCODE_CALL_PTR(VECTOR2I);
+ OPCODE_CALL_PTR(RECT2);
+ OPCODE_CALL_PTR(RECT2I);
+ OPCODE_CALL_PTR(VECTOR3);
+ OPCODE_CALL_PTR(VECTOR3I);
+ OPCODE_CALL_PTR(TRANSFORM2D);
+ OPCODE_CALL_PTR(PLANE);
+ OPCODE_CALL_PTR(QUATERNION);
+ OPCODE_CALL_PTR(AABB);
+ OPCODE_CALL_PTR(BASIS);
+ OPCODE_CALL_PTR(TRANSFORM3D);
+ OPCODE_CALL_PTR(COLOR);
+ OPCODE_CALL_PTR(STRING_NAME);
+ OPCODE_CALL_PTR(NODE_PATH);
+ OPCODE_CALL_PTR(RID);
+ OPCODE_CALL_PTR(CALLABLE);
+ OPCODE_CALL_PTR(SIGNAL);
+ OPCODE_CALL_PTR(DICTIONARY);
+ OPCODE_CALL_PTR(ARRAY);
+ OPCODE_CALL_PTR(PACKED_BYTE_ARRAY);
+ OPCODE_CALL_PTR(PACKED_INT32_ARRAY);
+ OPCODE_CALL_PTR(PACKED_INT64_ARRAY);
+ OPCODE_CALL_PTR(PACKED_FLOAT32_ARRAY);
+ OPCODE_CALL_PTR(PACKED_FLOAT64_ARRAY);
+ OPCODE_CALL_PTR(PACKED_STRING_ARRAY);
+ OPCODE_CALL_PTR(PACKED_VECTOR2_ARRAY);
+ OPCODE_CALL_PTR(PACKED_VECTOR3_ARRAY);
+ OPCODE_CALL_PTR(PACKED_COLOR_ARRAY);
+ OPCODE(OPCODE_CALL_PTRCALL_OBJECT) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+ GET_INSTRUCTION_ARG(base, argc);
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *base_obj = base->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to call a function on a previously freed instance.";
+ OPCODE_BREAK;
+ } else if (!base_obj) {
+ err_text = "Trying to call a function on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *base_obj = *VariantInternal::get_object(base);
+#endif
+
+ const void **argptrs = call_args_ptr;
+
+ for (int i = 0; i < argc; i++) {
+ GET_INSTRUCTION_ARG(v, i);
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v);
+ }
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ VariantInternal::initialize(ret, Variant::OBJECT);
+ Object **ret_opaque = VariantInternal::get_object(ret);
+ method->ptrcall(base_obj, argptrs, ret_opaque);
+ VariantInternal::object_assign(ret, *ret_opaque); // Set so ID is correct too.
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+ OPCODE(OPCODE_CALL_PTRCALL_NO_RETURN) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _methods_count);
+ MethodBind *method = _methods_ptr[_code_ptr[ip + 2]];
+
+ GET_INSTRUCTION_ARG(base, argc);
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *base_obj = base->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to call a function on a previously freed instance.";
+ OPCODE_BREAK;
+ } else if (!base_obj) {
+ err_text = "Trying to call a function on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *base_obj = *VariantInternal::get_object(base);
+#endif
+ const void **argptrs = call_args_ptr;
+
+ for (int i = 0; i < argc; i++) {
+ GET_INSTRUCTION_ARG(v, i);
+ argptrs[i] = VariantInternal::get_opaque_pointer((const Variant *)v);
+ }
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ VariantInternal::initialize(ret, Variant::NIL);
+ method->ptrcall(base_obj, argptrs, nullptr);
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_BUILTIN_TYPE_VALIDATED) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GET_INSTRUCTION_ARG(base, argc);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _builtin_methods_count);
+ Variant::ValidatedBuiltInMethod method = _builtin_methods_ptr[_code_ptr[ip + 2]];
+ Variant **argptrs = instruction_args;
+
+#ifdef DEBUG_ENABLED
+ uint64_t call_time = 0;
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ call_time = OS::get_singleton()->get_ticks_usec();
+ }
+#endif
+
+ GET_INSTRUCTION_ARG(ret, argc + 1);
+ method(base, (const Variant **)argptrs, argc, ret);
+
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ function_call_time += OS::get_singleton()->get_ticks_usec() - call_time;
+ }
+#endif
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_UTILITY) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _global_names_count);
+ StringName function = _global_names_ptr[_code_ptr[ip + 2]];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ Callable::CallError err;
+ Variant::call_utility_function(function, dst, (const Variant **)argptrs, argc, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = function;
+ if (dst->get_type() == Variant::STRING) {
+ // Call provided error string.
+ err_text = "Error calling utility function '" + methodstr + "': " + String(*dst);
+ } else {
+ err_text = _get_call_error(err, "utility function '" + methodstr + "'", (const Variant **)argptrs);
+ }
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_UTILITY_VALIDATED) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _utilities_count);
+ Variant::ValidatedUtilityFunction function = _utilities_ptr[_code_ptr[ip + 2]];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ function(dst, (const Variant **)argptrs, argc);
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_GDSCRIPT_UTILITY) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ GD_ERR_BREAK(_code_ptr[ip + 2] < 0 || _code_ptr[ip + 2] >= _gds_utilities_count);
+ GDScriptUtilityFunctions::FunctionPtr function = _gds_utilities_ptr[_code_ptr[ip + 2]];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ Callable::CallError err;
+ function(dst, (const Variant **)argptrs, argc, err);
+
+#ifdef DEBUG_ENABLED
+ if (err.error != Callable::CallError::CALL_OK) {
+ // TODO: Add this information in debug.
+ String methodstr = "<unknown function>";
+ if (dst->get_type() == Variant::STRING) {
+ // Call provided error string.
+ err_text = "Error calling GDScript utility function '" + methodstr + "': " + String(*dst);
+ } else {
+ err_text = _get_call_error(err, "GDScript utility function '" + methodstr + "'", (const Variant **)argptrs);
+ }
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CALL_SELF_BASE) {
+ CHECK_SPACE(3 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int argc = _code_ptr[ip + 1];
+ GD_ERR_BREAK(argc < 0);
+
+ int self_fun = _code_ptr[ip + 2];
+#ifdef DEBUG_ENABLED
+ if (self_fun < 0 || self_fun >= _global_names_count) {
+ err_text = "compiler bug, function name not found";
+ OPCODE_BREAK;
+ }
+#endif
+ const StringName *methodname = &_global_names_ptr[self_fun];
+
+ Variant **argptrs = instruction_args;
+
+ GET_INSTRUCTION_ARG(dst, argc);
+
+ const GDScript *gds = _script;
+
+ const Map<StringName, GDScriptFunction *>::Element *E = nullptr;
+ while (gds->base.ptr()) {
+ gds = gds->base.ptr();
+ E = gds->member_functions.find(*methodname);
+ if (E) {
+ break;
+ }
+ }
+
+ Callable::CallError err;
+
+ if (E) {
+ *dst = E->get()->call(p_instance, (const Variant **)argptrs, argc, err);
+ } else if (gds->native.ptr()) {
+ if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
+ MethodBind *mb = ClassDB::get_method(gds->native->get_name(), *methodname);
+ if (!mb) {
+ err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ } else {
+ *dst = mb->call(p_instance->owner, (const Variant **)argptrs, argc, err);
+ }
+ } else {
+ err.error = Callable::CallError::CALL_OK;
+ }
+ } else {
+ if (*methodname != GDScriptLanguage::get_singleton()->strings._init) {
+ err.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ } else {
+ err.error = Callable::CallError::CALL_OK;
+ }
+ }
+
+ if (err.error != Callable::CallError::CALL_OK) {
+ String methodstr = *methodname;
+ err_text = _get_call_error(err, "function '" + methodstr + "'", (const Variant **)argptrs);
+
+ OPCODE_BREAK;
+ }
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_AWAIT) {
+ CHECK_SPACE(2);
+
+ // Do the oneshot connect.
+ GET_INSTRUCTION_ARG(argobj, 0);
+
+ Signal sig;
+ bool is_signal = true;
+
+ {
+ Variant result = *argobj;
+
+ if (argobj->get_type() == Variant::OBJECT) {
+ bool was_freed = false;
+ Object *obj = argobj->get_validated_object_with_check(was_freed);
+
+ if (was_freed) {
+ err_text = "Trying to await on a freed object.";
+ OPCODE_BREAK;
+ }
+
+ // Is this even possible to be null at this point?
+ if (obj) {
+ if (obj->is_class_ptr(GDScriptFunctionState::get_class_ptr_static())) {
+ static StringName completed = _scs_create("completed");
+ result = Signal(obj, completed);
+ }
+ }
+ }
+
+ 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.
+ is_signal = false;
+ } else {
+ sig = result;
+ }
+ }
+
+ if (is_signal) {
+ Ref<GDScriptFunctionState> gdfs = memnew(GDScriptFunctionState);
+ gdfs->function = this;
+
+ gdfs->state.stack.resize(alloca_size);
+ //copy variant stack
+ for (int i = 0; i < _stack_size; i++) {
+ memnew_placement(&gdfs->state.stack.write[sizeof(Variant) * i], Variant(stack[i]));
+ }
+ gdfs->state.stack_size = _stack_size;
+ gdfs->state.alloca_size = alloca_size;
+ gdfs->state.ip = ip + 2;
+ gdfs->state.line = line;
+ gdfs->state.script = _script;
+ {
+ MutexLock lock(GDScriptLanguage::get_singleton()->lock);
+ _script->pending_func_states.add(&gdfs->scripts_list);
+ if (p_instance) {
+ gdfs->state.instance = p_instance;
+ p_instance->pending_func_states.add(&gdfs->instances_list);
+ } else {
+ gdfs->state.instance = nullptr;
+ }
+ }
+#ifdef DEBUG_ENABLED
+ gdfs->state.function_name = name;
+ gdfs->state.script_path = _script->get_path();
+#endif
+ gdfs->state.defarg = defarg;
+ gdfs->function = this;
+
+ retvalue = gdfs;
+
+ 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;
+ }
+
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+ awaited = true;
+#endif
+ OPCODE_BREAK;
+ }
+ }
+ DISPATCH_OPCODE; // Needed for synchronous calls (when result is immediately available).
+
+ OPCODE(OPCODE_AWAIT_RESUME) {
+ CHECK_SPACE(2);
+#ifdef DEBUG_ENABLED
+ if (!p_state) {
+ err_text = ("Invalid Resume (bug?)");
+ OPCODE_BREAK;
+ }
+#endif
+ GET_INSTRUCTION_ARG(result, 0);
+ *result = p_state->result;
+ ip += 2;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_CREATE_LAMBDA) {
+ CHECK_SPACE(2 + instr_arg_count);
+
+ ip += instr_arg_count;
+
+ int captures_count = _code_ptr[ip + 1];
+ GD_ERR_BREAK(captures_count < 0);
+
+ int lambda_index = _code_ptr[ip + 2];
+ GD_ERR_BREAK(lambda_index < 0 || lambda_index >= _lambdas_count);
+ GDScriptFunction *lambda = _lambdas_ptr[lambda_index];
+
+ Vector<Variant> captures;
+ captures.resize(captures_count);
+ for (int i = 0; i < captures_count; i++) {
+ GET_INSTRUCTION_ARG(arg, i);
+ captures.write[i] = *arg;
+ }
+
+ GDScriptLambdaCallable *callable = memnew(GDScriptLambdaCallable(Ref<GDScript>(script), lambda, captures));
+
+ GET_INSTRUCTION_ARG(result, captures_count);
+ *result = Callable(callable);
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP) {
+ CHECK_SPACE(2);
+ int to = _code_ptr[ip + 1];
+
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP_IF) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(test, 0);
+
+ bool result = test->booleanize();
+
+ if (result) {
+ int to = _code_ptr[ip + 2];
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ } else {
+ ip += 3;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP_IF_NOT) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(test, 0);
+
+ bool result = test->booleanize();
+
+ if (!result) {
+ int to = _code_ptr[ip + 2];
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ } else {
+ ip += 3;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_JUMP_TO_DEF_ARGUMENT) {
+ CHECK_SPACE(2);
+ ip = _default_arg_ptr[defarg];
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_RETURN) {
+ CHECK_SPACE(2);
+ GET_INSTRUCTION_ARG(r, 0);
+ retvalue = *r;
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+#endif
+ OPCODE_BREAK;
+ }
+
+ OPCODE(OPCODE_RETURN_TYPED_BUILTIN) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(r, 0);
+
+ Variant::Type ret_type = (Variant::Type)_code_ptr[ip + 2];
+ GD_ERR_BREAK(ret_type < 0 || ret_type >= Variant::VARIANT_MAX);
+
+ if (r->get_type() != ret_type) {
+ if (Variant::can_convert_strict(r->get_type(), ret_type)) {
+ Callable::CallError ce;
+ Variant::construct(ret_type, retvalue, const_cast<const Variant **>(&r), 1, ce);
+ } else {
+#ifdef DEBUG_ENABLED
+ err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)",
+ Variant::get_type_name(r->get_type()), Variant::get_type_name(ret_type));
+#endif // DEBUG_ENABLED
+
+ // Construct a base type anyway so type constraints are met.
+ Callable::CallError ce;
+ Variant::construct(ret_type, retvalue, nullptr, 0, ce);
+ OPCODE_BREAK;
+ }
+ } else {
+ retvalue = *r;
+ }
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+#endif // DEBUG_ENABLED
+ OPCODE_BREAK;
+ }
+
+ OPCODE(OPCODE_RETURN_TYPED_ARRAY) {
+ CHECK_SPACE(5);
+ GET_INSTRUCTION_ARG(r, 0);
+
+ GET_INSTRUCTION_ARG(script_type, 1);
+ Variant::Type builtin_type = (Variant::Type)_code_ptr[ip + 3];
+ int native_type_idx = _code_ptr[ip + 4];
+ GD_ERR_BREAK(native_type_idx < 0 || native_type_idx >= _global_names_count);
+ const StringName native_type = _global_names_ptr[native_type_idx];
+
+ if (r->get_type() != Variant::ARRAY) {
+#ifdef DEBUG_ENABLED
+ err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "Array[%s]".)",
+ Variant::get_type_name(r->get_type()), Variant::get_type_name(builtin_type));
+#endif
+ OPCODE_BREAK;
+ }
+
+ Array array;
+ array.set_typed(builtin_type, native_type, script_type);
+
+#ifdef DEBUG_ENABLED
+ bool valid = array.typed_assign(*VariantInternal::get_array(r));
+#else
+ array.typed_assign(*VariantInternal::get_array(r));
+#endif // DEBUG_ENABLED
+
+ // Assign the return value anyway since we want it to be the valid type.
+ retvalue = array;
+
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Trying to return a typed array with an array of different type.'";
+ OPCODE_BREAK;
+ }
+
+ exit_ok = true;
+#endif // DEBUG_ENABLED
+ OPCODE_BREAK;
+ }
+
+ OPCODE(OPCODE_RETURN_TYPED_NATIVE) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(r, 0);
+
+ GET_INSTRUCTION_ARG(type, 1);
+ GDScriptNativeClass *nc = Object::cast_to<GDScriptNativeClass>(type->operator Object *());
+ GD_ERR_BREAK(!nc);
+
+ if (r->get_type() != Variant::OBJECT && r->get_type() != Variant::NIL) {
+ err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)",
+ Variant::get_type_name(r->get_type()), nc->get_name());
+ OPCODE_BREAK;
+ }
+
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *ret_obj = r->get_validated_object_with_check(freed);
+
+ if (freed) {
+ err_text = "Trying to return a previously freed instance.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *ret_obj = r->operator Object *();
+#endif // DEBUG_ENABLED
+ if (ret_obj && !ClassDB::is_parent_class(ret_obj->get_class_name(), nc->get_name())) {
+#ifdef DEBUG_ENABLED
+ err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)",
+ ret_obj->get_class_name(), nc->get_name());
+#endif // DEBUG_ENABLED
+ OPCODE_BREAK;
+ }
+ retvalue = *r;
+
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+#endif // DEBUG_ENABLED
+ OPCODE_BREAK;
+ }
+
+ OPCODE(OPCODE_RETURN_TYPED_SCRIPT) {
+ CHECK_SPACE(3);
+ GET_INSTRUCTION_ARG(r, 0);
+
+ GET_INSTRUCTION_ARG(type, 1);
+ Script *base_type = Object::cast_to<Script>(type->operator Object *());
+ GD_ERR_BREAK(!base_type);
+
+ if (r->get_type() != Variant::OBJECT && r->get_type() != Variant::NIL) {
+#ifdef DEBUG_ENABLED
+ err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)",
+ Variant::get_type_name(r->get_type()), _get_script_name(Ref<Script>(base_type)));
+#endif // DEBUG_ENABLED
+ OPCODE_BREAK;
+ }
+
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *ret_obj = r->get_validated_object_with_check(freed);
+
+ if (freed) {
+ err_text = "Trying to return a previously freed instance.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *ret_obj = r->operator Object *();
+#endif // DEBUG_ENABLED
+
+ if (ret_obj) {
+ ScriptInstance *ret_inst = ret_obj->get_script_instance();
+ if (!ret_inst) {
+#ifdef DEBUG_ENABLED
+ err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)",
+ ret_obj->get_class_name(), _get_script_name(Ref<GDScript>(base_type)));
+#endif // DEBUG_ENABLED
+ OPCODE_BREAK;
+ }
+
+ Script *ret_type = ret_obj->get_script_instance()->get_script().ptr();
+ bool valid = false;
+
+ while (ret_type) {
+ if (ret_type == base_type) {
+ valid = true;
+ break;
+ }
+ ret_type = ret_type->get_base_script().ptr();
+ }
+
+ if (!valid) {
+#ifdef DEBUG_ENABLED
+ err_text = vformat(R"(Trying to return value of type "%s" from a function which the return type is "%s".)",
+ _get_script_name(ret_obj->get_script_instance()->get_script()), _get_script_name(Ref<GDScript>(base_type)));
+#endif // DEBUG_ENABLED
+ OPCODE_BREAK;
+ }
+ }
+ retvalue = *r;
+
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+#endif // DEBUG_ENABLED
+ OPCODE_BREAK;
+ }
+
+ OPCODE(OPCODE_ITERATE_BEGIN) {
+ CHECK_SPACE(8); // Space for this and a regular iterate.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ bool valid;
+ if (!container->iter_init(*counter, valid)) {
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+
+ *iterator = container->iter_get(*counter, valid);
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "'.";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5; // Skip regular iterate which is always next.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_INT) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ int64_t size = *VariantInternal::get_int(container);
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = 0;
+
+ if (size > 0) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::INT);
+ *VariantInternal::get_int(iterator) = 0;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_FLOAT) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ double size = *VariantInternal::get_float(container);
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_float(counter) = 0.0;
+
+ if (size > 0) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::FLOAT);
+ *VariantInternal::get_float(iterator) = 0;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR2) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector2 *bounds = VariantInternal::get_vector2(container);
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_float(counter) = bounds->x;
+
+ if (bounds->x < bounds->y) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::FLOAT);
+ *VariantInternal::get_float(iterator) = bounds->x;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR2I) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector2i *bounds = VariantInternal::get_vector2i(container);
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_int(counter) = bounds->x;
+
+ if (bounds->x < bounds->y) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::INT);
+ *VariantInternal::get_int(iterator) = bounds->x;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR3) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector3 *bounds = VariantInternal::get_vector3(container);
+ double from = bounds->x;
+ double to = bounds->y;
+ double step = bounds->z;
+
+ VariantInternal::initialize(counter, Variant::FLOAT);
+ *VariantInternal::get_float(counter) = from;
+
+ bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0);
+
+ if (do_continue) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::FLOAT);
+ *VariantInternal::get_float(iterator) = from;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_VECTOR3I) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Vector3i *bounds = VariantInternal::get_vector3i(container);
+ int64_t from = bounds->x;
+ int64_t to = bounds->y;
+ int64_t step = bounds->z;
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = from;
+
+ bool do_continue = from == to ? false : (from < to ? step > 0 : step < 0);
+
+ if (do_continue) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::INT);
+ *VariantInternal::get_int(iterator) = from;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_STRING) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ String *str = VariantInternal::get_string(container);
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = 0;
+
+ if (!str->is_empty()) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ VariantInternal::initialize(iterator, Variant::STRING);
+ *VariantInternal::get_string(iterator) = str->substr(0, 1);
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_DICTIONARY) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Dictionary *dict = VariantInternal::get_dictionary(container);
+ const Variant *next = dict->next(nullptr);
+
+ if (!dict->is_empty()) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *counter = *next;
+ *iterator = *next;
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_BEGIN_ARRAY) {
+ CHECK_SPACE(8); // Check space for iterate instruction too.
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ Array *array = VariantInternal::get_array(container);
+
+ VariantInternal::initialize(counter, Variant::INT);
+ *VariantInternal::get_int(counter) = 0;
+
+ if (!array->is_empty()) {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = array->get(0);
+
+ // Skip regular iterate.
+ ip += 5;
+ } else {
+ // Jump to end of loop.
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ }
+ }
+ DISPATCH_OPCODE;
+
+#define OPCODE_ITERATE_BEGIN_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_var_ret_type, m_ret_type, m_ret_get_func) \
+ OPCODE(OPCODE_ITERATE_BEGIN_PACKED_##m_var_type##_ARRAY) { \
+ CHECK_SPACE(8); \
+ GET_INSTRUCTION_ARG(counter, 0); \
+ GET_INSTRUCTION_ARG(container, 1); \
+ Vector<m_elem_type> *array = VariantInternal::m_get_func(container); \
+ VariantInternal::initialize(counter, Variant::INT); \
+ *VariantInternal::get_int(counter) = 0; \
+ if (!array->is_empty()) { \
+ GET_INSTRUCTION_ARG(iterator, 2); \
+ VariantInternal::initialize(iterator, Variant::m_var_ret_type); \
+ m_ret_type *it = VariantInternal::m_ret_get_func(iterator); \
+ *it = array->get(0); \
+ ip += 5; \
+ } else { \
+ int jumpto = _code_ptr[ip + 4]; \
+ GD_ERR_BREAK(jumpto<0 || jumpto> _code_size); \
+ ip = jumpto; \
+ } \
+ } \
+ DISPATCH_OPCODE
+
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(BYTE, uint8_t, get_byte_array, INT, int64_t, get_int);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(INT32, int32_t, get_int32_array, INT, int64_t, get_int);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(INT64, int64_t, get_int64_array, INT, int64_t, get_int);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(FLOAT32, float, get_float32_array, FLOAT, double, get_float);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(FLOAT64, double, get_float64_array, FLOAT, double, get_float);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(STRING, String, get_string_array, STRING, String, get_string);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, VECTOR2, Vector2, get_vector2);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, VECTOR3, Vector3, get_vector3);
+ OPCODE_ITERATE_BEGIN_PACKED_ARRAY(COLOR, Color, get_color_array, COLOR, Color, get_color);
+
+ OPCODE(OPCODE_ITERATE_BEGIN_OBJECT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *obj = container->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to iterate on a previously freed object.";
+ OPCODE_BREAK;
+ } else if (!obj) {
+ err_text = "Trying to iterate on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *obj = *VariantInternal::get_object(container);
+#endif
+ Array ref;
+ ref.push_back(*counter);
+ Variant vref;
+ VariantInternal::initialize(&vref, Variant::ARRAY);
+ *VariantInternal::get_array(&vref) = ref;
+
+ Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore.
+ args[0] = &vref;
+
+ Callable::CallError ce;
+ Variant has_next = obj->call(CoreStringNames::get_singleton()->_iter_init, (const Variant **)args, 1, ce);
+
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+ if (!has_next.booleanize()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = obj->call(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce);
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ bool valid;
+ if (!container->iter_next(*counter, valid)) {
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to iterate on object of type '" + Variant::get_type_name(container->get_type()) + "' (type changed since first iteration?).";
+ OPCODE_BREAK;
+ }
+#endif
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+
+ *iterator = container->iter_get(*counter, valid);
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ err_text = "Unable to obtain iterator object of type '" + Variant::get_type_name(container->get_type()) + "' (but was obtained on first iteration?).";
+ OPCODE_BREAK;
+ }
+#endif
+ ip += 5; //loop again
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_INT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ int64_t size = *VariantInternal::get_int(container);
+ int64_t *count = VariantInternal::get_int(counter);
+
+ (*count)++;
+
+ if (*count >= size) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_int(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_FLOAT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ double size = *VariantInternal::get_float(container);
+ double *count = VariantInternal::get_float(counter);
+
+ (*count)++;
+
+ if (*count >= size) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_float(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR2) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector2 *bounds = VariantInternal::get_vector2((const Variant *)container);
+ double *count = VariantInternal::get_float(counter);
+
+ (*count)++;
+
+ if (*count >= bounds->y) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_float(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR2I) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector2i *bounds = VariantInternal::get_vector2i((const Variant *)container);
+ int64_t *count = VariantInternal::get_int(counter);
+
+ (*count)++;
+
+ if (*count >= bounds->y) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_int(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR3) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector3 *bounds = VariantInternal::get_vector3((const Variant *)container);
+ double *count = VariantInternal::get_float(counter);
+
+ *count += bounds->z;
+
+ if ((bounds->z < 0 && *count <= bounds->y) || (bounds->z > 0 && *count >= bounds->y)) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_float(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_VECTOR3I) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Vector3i *bounds = VariantInternal::get_vector3i((const Variant *)container);
+ int64_t *count = VariantInternal::get_int(counter);
+
+ *count += bounds->z;
+
+ if ((bounds->z < 0 && *count <= bounds->y) || (bounds->z > 0 && *count >= bounds->y)) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_int(iterator) = *count;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_STRING) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const String *str = VariantInternal::get_string((const Variant *)container);
+ int64_t *idx = VariantInternal::get_int(counter);
+ (*idx)++;
+
+ if (*idx >= str->length()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *VariantInternal::get_string(iterator) = str->substr(*idx, 1);
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_DICTIONARY) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Dictionary *dict = VariantInternal::get_dictionary((const Variant *)container);
+ const Variant *next = dict->next(counter);
+
+ if (!next) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *counter = *next;
+ *iterator = *next;
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_ITERATE_ARRAY) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+ const Array *array = VariantInternal::get_array((const Variant *)container);
+ int64_t *idx = VariantInternal::get_int(counter);
+ (*idx)++;
+
+ if (*idx >= array->size()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = array->get(*idx);
+
+ ip += 5; // Loop again.
+ }
+ }
+ DISPATCH_OPCODE;
+
+#define OPCODE_ITERATE_PACKED_ARRAY(m_var_type, m_elem_type, m_get_func, m_ret_get_func) \
+ OPCODE(OPCODE_ITERATE_PACKED_##m_var_type##_ARRAY) { \
+ CHECK_SPACE(4); \
+ GET_INSTRUCTION_ARG(counter, 0); \
+ GET_INSTRUCTION_ARG(container, 1); \
+ const Vector<m_elem_type> *array = VariantInternal::m_get_func((const Variant *)container); \
+ int64_t *idx = VariantInternal::get_int(counter); \
+ (*idx)++; \
+ if (*idx >= array->size()) { \
+ int jumpto = _code_ptr[ip + 4]; \
+ GD_ERR_BREAK(jumpto<0 || jumpto> _code_size); \
+ ip = jumpto; \
+ } else { \
+ GET_INSTRUCTION_ARG(iterator, 2); \
+ *VariantInternal::m_ret_get_func(iterator) = array->get(*idx); \
+ ip += 5; \
+ } \
+ } \
+ DISPATCH_OPCODE
+
+ OPCODE_ITERATE_PACKED_ARRAY(BYTE, uint8_t, get_byte_array, get_int);
+ OPCODE_ITERATE_PACKED_ARRAY(INT32, int32_t, get_int32_array, get_int);
+ OPCODE_ITERATE_PACKED_ARRAY(INT64, int64_t, get_int64_array, get_int);
+ OPCODE_ITERATE_PACKED_ARRAY(FLOAT32, float, get_float32_array, get_float);
+ OPCODE_ITERATE_PACKED_ARRAY(FLOAT64, double, get_float64_array, get_float);
+ OPCODE_ITERATE_PACKED_ARRAY(STRING, String, get_string_array, get_string);
+ OPCODE_ITERATE_PACKED_ARRAY(VECTOR2, Vector2, get_vector2_array, get_vector2);
+ OPCODE_ITERATE_PACKED_ARRAY(VECTOR3, Vector3, get_vector3_array, get_vector3);
+ OPCODE_ITERATE_PACKED_ARRAY(COLOR, Color, get_color_array, get_color);
+
+ OPCODE(OPCODE_ITERATE_OBJECT) {
+ CHECK_SPACE(4);
+
+ GET_INSTRUCTION_ARG(counter, 0);
+ GET_INSTRUCTION_ARG(container, 1);
+
+#ifdef DEBUG_ENABLED
+ bool freed = false;
+ Object *obj = container->get_validated_object_with_check(freed);
+ if (freed) {
+ err_text = "Trying to iterate on a previously freed object.";
+ OPCODE_BREAK;
+ } else if (!obj) {
+ err_text = "Trying to iterate on a null value.";
+ OPCODE_BREAK;
+ }
+#else
+ Object *obj = *VariantInternal::get_object(container);
+#endif
+ Array ref;
+ ref.push_back(*counter);
+ Variant vref;
+ VariantInternal::initialize(&vref, Variant::ARRAY);
+ *VariantInternal::get_array(&vref) = ref;
+
+ Variant **args = instruction_args; // Overriding an instruction argument, but we don't need access to that anymore.
+ args[0] = &vref;
+
+ Callable::CallError ce;
+ Variant has_next = obj->call(CoreStringNames::get_singleton()->_iter_next, (const Variant **)args, 1, ce);
+
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_next" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+ if (!has_next.booleanize()) {
+ int jumpto = _code_ptr[ip + 4];
+ GD_ERR_BREAK(jumpto < 0 || jumpto > _code_size);
+ ip = jumpto;
+ } else {
+ GET_INSTRUCTION_ARG(iterator, 2);
+ *iterator = obj->call(CoreStringNames::get_singleton()->_iter_get, (const Variant **)args, 1, ce);
+#ifdef DEBUG_ENABLED
+ if (ce.error != Callable::CallError::CALL_OK) {
+ err_text = vformat(R"(There was an error calling "_iter_get" on iterator object of type %s.)", *container);
+ OPCODE_BREAK;
+ }
+#endif
+
+ ip += 5; // Loop again.
+ }
+ }
+ 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];
+ GD_ERR_BREAK(globalname_idx < 0 || globalname_idx >= _global_names_count);
+ const StringName *globalname = &_global_names_ptr[globalname_idx];
+
+ GET_INSTRUCTION_ARG(dst, 0);
+ *dst = GDScriptLanguage::get_singleton()->get_named_globals_map()[*globalname];
+
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+#define OPCODE_TYPE_ADJUST(m_v_type, m_c_type) \
+ OPCODE(OPCODE_TYPE_ADJUST_##m_v_type) { \
+ CHECK_SPACE(2); \
+ GET_INSTRUCTION_ARG(arg, 0); \
+ VariantTypeAdjust<m_c_type>::adjust(arg); \
+ ip += 2; \
+ } \
+ DISPATCH_OPCODE
+
+ OPCODE_TYPE_ADJUST(BOOL, bool);
+ OPCODE_TYPE_ADJUST(INT, int64_t);
+ OPCODE_TYPE_ADJUST(FLOAT, double);
+ OPCODE_TYPE_ADJUST(STRING, String);
+ OPCODE_TYPE_ADJUST(VECTOR2, Vector2);
+ OPCODE_TYPE_ADJUST(VECTOR2I, Vector2i);
+ OPCODE_TYPE_ADJUST(RECT2, Rect2);
+ OPCODE_TYPE_ADJUST(RECT2I, Rect2i);
+ OPCODE_TYPE_ADJUST(VECTOR3, Vector3);
+ OPCODE_TYPE_ADJUST(VECTOR3I, Vector3i);
+ OPCODE_TYPE_ADJUST(TRANSFORM2D, Transform2D);
+ OPCODE_TYPE_ADJUST(PLANE, Plane);
+ OPCODE_TYPE_ADJUST(QUATERNION, Quaternion);
+ OPCODE_TYPE_ADJUST(AABB, AABB);
+ OPCODE_TYPE_ADJUST(BASIS, Basis);
+ OPCODE_TYPE_ADJUST(TRANSFORM, Transform3D);
+ OPCODE_TYPE_ADJUST(COLOR, Color);
+ OPCODE_TYPE_ADJUST(STRING_NAME, StringName);
+ OPCODE_TYPE_ADJUST(NODE_PATH, NodePath);
+ OPCODE_TYPE_ADJUST(RID, RID);
+ OPCODE_TYPE_ADJUST(OBJECT, Object *);
+ OPCODE_TYPE_ADJUST(CALLABLE, Callable);
+ OPCODE_TYPE_ADJUST(SIGNAL, Signal);
+ OPCODE_TYPE_ADJUST(DICTIONARY, Dictionary);
+ OPCODE_TYPE_ADJUST(ARRAY, Array);
+ OPCODE_TYPE_ADJUST(PACKED_BYTE_ARRAY, PackedByteArray);
+ OPCODE_TYPE_ADJUST(PACKED_INT32_ARRAY, PackedInt32Array);
+ OPCODE_TYPE_ADJUST(PACKED_INT64_ARRAY, PackedInt64Array);
+ OPCODE_TYPE_ADJUST(PACKED_FLOAT32_ARRAY, PackedFloat32Array);
+ OPCODE_TYPE_ADJUST(PACKED_FLOAT64_ARRAY, PackedFloat64Array);
+ OPCODE_TYPE_ADJUST(PACKED_STRING_ARRAY, PackedStringArray);
+ OPCODE_TYPE_ADJUST(PACKED_VECTOR2_ARRAY, PackedVector2Array);
+ OPCODE_TYPE_ADJUST(PACKED_VECTOR3_ARRAY, PackedVector3Array);
+ OPCODE_TYPE_ADJUST(PACKED_COLOR_ARRAY, PackedColorArray);
+
+ OPCODE(OPCODE_ASSERT) {
+ CHECK_SPACE(3);
+
+#ifdef DEBUG_ENABLED
+ GET_INSTRUCTION_ARG(test, 0);
+ bool result = test->booleanize();
+
+ if (!result) {
+ String message_str;
+ if (_code_ptr[ip + 2] != 0) {
+ GET_INSTRUCTION_ARG(message, 1);
+ message_str = *message;
+ }
+ if (message_str.is_empty()) {
+ err_text = "Assertion failed.";
+ } else {
+ err_text = "Assertion failed: " + message_str;
+ }
+ OPCODE_BREAK;
+ }
+
+#endif
+ ip += 3;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_BREAKPOINT) {
+#ifdef DEBUG_ENABLED
+ if (EngineDebugger::is_active()) {
+ GDScriptLanguage::get_singleton()->debug_break("Breakpoint Statement", true);
+ }
+#endif
+ ip += 1;
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_LINE) {
+ CHECK_SPACE(2);
+
+ line = _code_ptr[ip + 1];
+ ip += 2;
+
+ if (EngineDebugger::is_active()) {
+ // line
+ bool do_break = false;
+
+ if (EngineDebugger::get_script_debugger()->get_lines_left() > 0) {
+ if (EngineDebugger::get_script_debugger()->get_depth() <= 0) {
+ EngineDebugger::get_script_debugger()->set_lines_left(EngineDebugger::get_script_debugger()->get_lines_left() - 1);
+ }
+ if (EngineDebugger::get_script_debugger()->get_lines_left() <= 0) {
+ do_break = true;
+ }
+ }
+
+ if (EngineDebugger::get_script_debugger()->is_breakpoint(line, source)) {
+ do_break = true;
+ }
+
+ if (do_break) {
+ GDScriptLanguage::get_singleton()->debug_break("Breakpoint", true);
+ }
+
+ EngineDebugger::get_singleton()->line_poll();
+ }
+ }
+ DISPATCH_OPCODE;
+
+ OPCODE(OPCODE_END) {
+#ifdef DEBUG_ENABLED
+ exit_ok = true;
+#endif
+ OPCODE_BREAK;
+ }
+
+#if 0 // Enable for debugging.
+ default: {
+ err_text = "Illegal opcode " + itos(_code_ptr[ip]) + " at address " + itos(ip);
+ OPCODE_BREAK;
+ }
+#endif
+ }
+
+ OPCODES_END
+#ifdef DEBUG_ENABLED
+ if (exit_ok) {
+ OPCODE_OUT;
+ }
+ //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 != "") {
+ err_file = p_instance->script->path;
+ } else if (script) {
+ err_file = script->path;
+ }
+ if (err_file == "") {
+ 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 != "") {
+ err_func = p_instance->script->name + "." + err_func;
+ }
+ int err_line = line;
+ if (err_text == "") {
+ 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(), false, ERR_HANDLER_SCRIPT);
+ }
+
+#endif
+ OPCODE_OUT;
+ }
+
+ OPCODES_OUT
+#ifdef DEBUG_ENABLED
+ if (GDScriptLanguage::get_singleton()->profiling) {
+ uint64_t time_taken = OS::get_singleton()->get_ticks_usec() - function_start_time;
+ profile.total_time += time_taken;
+ profile.self_time += time_taken - function_call_time;
+ profile.frame_total_time += time_taken;
+ profile.frame_self_time += time_taken - function_call_time;
+ GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
+ }
+
+ // Check if this is the last time the function is resuming from await
+ // Will be true if never awaited as well
+ // When it's the last resume it will postpone the exit from stack,
+ // so the debugger knows which function triggered the resume of the next function (if any)
+ if (!p_state || awaited) {
+ if (EngineDebugger::is_active()) {
+ GDScriptLanguage::get_singleton()->exit_function();
+ }
+#endif
+
+ if (_stack_size) {
+ //free stack
+ for (int i = 0; i < _stack_size; i++) {
+ stack[i].~Variant();
+ }
+ }
+
+#ifdef DEBUG_ENABLED
+ }
+#endif
+
+ return retvalue;
+}
diff --git a/modules/gdscript/gdscript_warning.cpp b/modules/gdscript/gdscript_warning.cpp
index 105facd9d0..7a483a16ba 100644
--- a/modules/gdscript/gdscript_warning.cpp
+++ b/modules/gdscript/gdscript_warning.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "gdscript_warning.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#ifdef DEBUG_ENABLED
@@ -145,6 +145,9 @@ 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 WARNING_MAX:
break; // Can't happen, but silences warning
}
@@ -190,6 +193,7 @@ String GDScriptWarning::get_name_from_code(Code p_code) {
"ASSERT_ALWAYS_TRUE",
"ASSERT_ALWAYS_FALSE",
"REDUNDANT_AWAIT",
+ "EMPTY_FILE",
};
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 e183d6f302..8de46b08c1 100644
--- a/modules/gdscript/gdscript_warning.h
+++ b/modules/gdscript/gdscript_warning.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,8 +33,8 @@
#ifdef DEBUG_ENABLED
-#include "core/ustring.h"
-#include "core/vector.h"
+#include "core/string/ustring.h"
+#include "core/templates/vector.h"
class GDScriptWarning {
public:
@@ -68,6 +68,7 @@ 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.
WARNING_MAX,
};
diff --git a/modules/gdscript/icons/GDScript.svg b/modules/gdscript/icons/GDScript.svg
index 953bb9ae9e..aa59125ea9 100644
--- a/modules/gdscript/icons/GDScript.svg
+++ b/modules/gdscript/icons/GDScript.svg
@@ -1,5 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2l2.2578 0.56445a5 5 0 0 0 0.2793 0.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 0.68555 0.28516l0.5625 2.2539h2l0.56445-2.2578a5 5 0 0 0 0.6875 -0.2793l1.9902 1.1934 1.4141-1.4141-1.1953-1.9941a5 5 0 0 0 0.28516 -0.68555l2.2539-0.5625v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm1 5a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2 -2 2 2 0 0 1 2 -2z" fill="#e0e0e0"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2l2.2578.56445a5 5 0 0 0 .2793.6875l-1.1934 1.9902 1.4141 1.4141 1.9941-1.1953a5 5 0 0 0 .68555.28516l.5625 2.2539h2l.56445-2.2578a5 5 0 0 0 .6875-.2793l1.9902 1.1934 1.4141-1.4141-1.1953-1.9941a5 5 0 0 0 .28516-.68555l2.2539-.5625v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm1 5a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2z" fill="#e0e0e0"/></svg>
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.cpp b/modules/gdscript/language_server/gdscript_extend_parser.cpp
index 668dfd4835..80f4721e2d 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.cpp
+++ b/modules/gdscript/language_server/gdscript_extend_parser.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,6 @@
#include "../gdscript.h"
#include "../gdscript_analyzer.h"
-#include "core/io/json.h"
#include "gdscript_language_protocol.h"
#include "gdscript_workspace.h"
@@ -40,8 +39,7 @@ void ExtendGDScriptParser::update_diagnostics() {
diagnostics.clear();
const List<ParserError> &errors = get_errors();
- for (const List<ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
- const ParserError &error = E->get();
+ for (const ParserError &error : errors) {
lsp::Diagnostic diagnostic;
diagnostic.severity = lsp::DiagnosticSeverity::Error;
diagnostic.message = error.message;
@@ -49,8 +47,9 @@ void ExtendGDScriptParser::update_diagnostics() {
diagnostic.code = -1;
lsp::Range range;
lsp::Position pos;
- int line = LINE_NUMBER_TO_INDEX(error.line);
- const String &line_text = get_lines()[line];
+ const PackedStringArray lines = get_lines();
+ int line = CLAMP(LINE_NUMBER_TO_INDEX(error.line), 0, lines.size() - 1);
+ const String &line_text = lines[line];
pos.line = line;
pos.character = line_text.length() - line_text.strip_edges(true, false).length();
range.start = pos;
@@ -61,8 +60,7 @@ void ExtendGDScriptParser::update_diagnostics() {
}
const List<GDScriptWarning> &warnings = get_warnings();
- for (const List<GDScriptWarning>::Element *E = warnings.front(); E; E = E->next()) {
- const GDScriptWarning &warning = E->get();
+ for (const GDScriptWarning &warning : warnings) {
lsp::Diagnostic diagnostic;
diagnostic.severity = lsp::DiagnosticSeverity::Warning;
diagnostic.message = "(" + warning.get_name() + "): " + warning.get_message();
@@ -147,14 +145,14 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
r_symbol.script_path = path;
r_symbol.children.clear();
r_symbol.name = p_class->identifier != nullptr ? String(p_class->identifier->name) : String();
- if (r_symbol.name.empty()) {
+ if (r_symbol.name.is_empty()) {
r_symbol.name = path.get_file();
}
r_symbol.kind = lsp::SymbolKind::Class;
r_symbol.deprecated = false;
- r_symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_class->start_line);
- r_symbol.range.start.character = LINE_NUMBER_TO_INDEX(p_class->start_column);
- r_symbol.range.end.line = LINE_NUMBER_TO_INDEX(p_class->end_line);
+ r_symbol.range.start.line = p_class->start_line;
+ r_symbol.range.start.character = p_class->start_column;
+ r_symbol.range.end.line = lines.size();
r_symbol.selectionRange.start.line = r_symbol.range.start.line;
r_symbol.detail = "class " + r_symbol.name;
bool is_root_class = &r_symbol == &class_symbol;
@@ -167,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 = lsp::SymbolKind::Variable;
+ 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);
@@ -182,7 +180,7 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
symbol.detail += ": " + m.get_datatype().to_string();
}
if (m.variable->initializer != nullptr && m.variable->initializer->is_constant) {
- symbol.detail += " = " + JSON::print(m.variable->initializer->reduced_value);
+ symbol.detail += " = " + m.variable->initializer->reduced_value.to_json_string();
}
symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(m.variable->start_line));
@@ -215,20 +213,20 @@ void ExtendGDScriptParser::parse_class_symbol(const GDScriptParser::ClassNode *p
String value_text;
if (default_value.get_type() == Variant::OBJECT) {
RES res = default_value;
- if (res.is_valid() && !res->get_path().empty()) {
+ if (res.is_valid() && !res->get_path().is_empty()) {
value_text = "preload(\"" + res->get_path() + "\")";
- if (symbol.documentation.empty()) {
+ if (symbol.documentation.is_empty()) {
if (Map<String, ExtendGDScriptParser *>::Element *S = GDScriptLanguageProtocol::get_singleton()->get_workspace()->scripts.find(res->get_path())) {
symbol.documentation = S->get()->class_symbol.documentation;
}
}
} else {
- value_text = JSON::print(default_value);
+ value_text = default_value.to_json_string();
}
} else {
- value_text = JSON::print(default_value);
+ value_text = default_value.to_json_string();
}
- if (!value_text.empty()) {
+ if (!value_text.is_empty()) {
symbol.detail += " = " + value_text;
}
@@ -271,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 += ")";
@@ -319,7 +317,7 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
const String uri = get_uri();
r_symbol.name = p_func->identifier->name;
- r_symbol.kind = lsp::SymbolKind::Function;
+ r_symbol.kind = p_func->is_static ? lsp::SymbolKind::Function : lsp::SymbolKind::Method;
r_symbol.detail = "func " + String(p_func->identifier->name) + "(";
r_symbol.deprecated = false;
r_symbol.range.start.line = LINE_NUMBER_TO_INDEX(p_func->start_line);
@@ -352,8 +350,7 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
parameters += ": " + parameter->get_datatype().to_string();
}
if (parameter->default_value != nullptr) {
- String value = JSON::print(parameter->default_value->reduced_value);
- parameters += " = " + value;
+ parameters += " = " + parameter->default_value->reduced_value.to_json_string();
}
}
r_symbol.detail += parameters + ")";
@@ -361,24 +358,73 @@ void ExtendGDScriptParser::parse_function_symbol(const GDScriptParser::FunctionN
r_symbol.detail += " -> " + p_func->get_datatype().to_string();
}
- for (int i = 0; i < p_func->body->locals.size(); i++) {
- const SuiteNode::Local &local = p_func->body->locals[i];
- lsp::DocumentSymbol symbol;
- symbol.name = local.name;
- symbol.kind = local.type == SuiteNode::Local::CONSTANT ? lsp::SymbolKind::Constant : lsp::SymbolKind::Variable;
- symbol.range.start.line = LINE_NUMBER_TO_INDEX(local.start_line);
- symbol.range.start.character = LINE_NUMBER_TO_INDEX(local.start_column);
- symbol.range.end.line = LINE_NUMBER_TO_INDEX(local.end_line);
- symbol.range.end.character = LINE_NUMBER_TO_INDEX(local.end_column);
- symbol.uri = uri;
- symbol.script_path = path;
- symbol.detail = SuiteNode::Local::CONSTANT ? "const " : "var ";
- symbol.detail += symbol.name;
- if (local.get_datatype().is_hard_type()) {
- symbol.detail += ": " + local.get_datatype().to_string();
+ List<GDScriptParser::SuiteNode *> function_nodes;
+
+ List<GDScriptParser::Node *> node_stack;
+ node_stack.push_back(p_func->body);
+
+ while (!node_stack.is_empty()) {
+ GDScriptParser::Node *node = node_stack[0];
+ node_stack.pop_front();
+
+ switch (node->type) {
+ case GDScriptParser::TypeNode::IF: {
+ GDScriptParser::IfNode *if_node = (GDScriptParser::IfNode *)node;
+ node_stack.push_back(if_node->true_block);
+ if (if_node->false_block) {
+ node_stack.push_back(if_node->false_block);
+ }
+ } break;
+
+ case GDScriptParser::TypeNode::FOR: {
+ GDScriptParser::ForNode *for_node = (GDScriptParser::ForNode *)node;
+ node_stack.push_back(for_node->loop);
+ } break;
+
+ case GDScriptParser::TypeNode::WHILE: {
+ GDScriptParser::WhileNode *while_node = (GDScriptParser::WhileNode *)node;
+ node_stack.push_back(while_node->loop);
+ } break;
+
+ case GDScriptParser::TypeNode::MATCH_BRANCH: {
+ GDScriptParser::MatchBranchNode *match_node = (GDScriptParser::MatchBranchNode *)node;
+ node_stack.push_back(match_node->block);
+ } break;
+
+ case GDScriptParser::TypeNode::SUITE: {
+ GDScriptParser::SuiteNode *suite_node = (GDScriptParser::SuiteNode *)node;
+ function_nodes.push_back(suite_node);
+ for (int i = 0; i < suite_node->statements.size(); ++i) {
+ node_stack.push_back(suite_node->statements[i]);
+ }
+ } break;
+
+ default:
+ continue;
+ }
+ }
+
+ for (List<GDScriptParser::SuiteNode *>::Element *N = function_nodes.front(); N; N = N->next()) {
+ const GDScriptParser::SuiteNode *suite_node = N->get();
+ for (int i = 0; i < suite_node->locals.size(); i++) {
+ const SuiteNode::Local &local = suite_node->locals[i];
+ lsp::DocumentSymbol symbol;
+ symbol.name = local.name;
+ symbol.kind = local.type == SuiteNode::Local::CONSTANT ? lsp::SymbolKind::Constant : lsp::SymbolKind::Variable;
+ symbol.range.start.line = LINE_NUMBER_TO_INDEX(local.start_line);
+ symbol.range.start.character = LINE_NUMBER_TO_INDEX(local.start_column);
+ symbol.range.end.line = LINE_NUMBER_TO_INDEX(local.end_line);
+ symbol.range.end.character = LINE_NUMBER_TO_INDEX(local.end_column);
+ symbol.uri = uri;
+ symbol.script_path = path;
+ symbol.detail = local.type == SuiteNode::Local::CONSTANT ? "const " : "var ";
+ symbol.detail += symbol.name;
+ if (local.get_datatype().is_hard_type()) {
+ symbol.detail += ": " + local.get_datatype().to_string();
+ }
+ symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(local.start_line));
+ r_symbol.children.push_back(symbol);
}
- symbol.documentation = parse_documentation(LINE_NUMBER_TO_INDEX(local.start_line));
- r_symbol.children.push_back(symbol);
}
}
@@ -419,8 +465,8 @@ String ExtendGDScriptParser::parse_documentation(int p_line, bool p_docs_down) {
}
String doc;
- for (List<String>::Element *E = doc_lines.front(); E; E = E->next()) {
- doc += E->get() + "\n";
+ for (const String &E : doc_lines) {
+ doc += E + "\n";
}
return doc;
}
@@ -445,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++) {
@@ -453,7 +499,7 @@ String ExtendGDScriptParser::get_text_for_lookup_symbol(const lsp::Position &p_c
String line = lines[i];
String first_part = line.substr(0, p_cursor.character);
String last_part = line.substr(p_cursor.character + 1, lines[i].length());
- if (!p_symbol.empty()) {
+ if (!p_symbol.is_empty()) {
String left_cursor_text;
for (int c = p_cursor.character - 1; c >= 0; c--) {
left_cursor_text = line.substr(c, p_cursor.character - c);
@@ -467,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;
@@ -486,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;
@@ -589,7 +638,7 @@ const lsp::DocumentSymbol *ExtendGDScriptParser::get_symbol_defined_at_line(int
}
const lsp::DocumentSymbol *ExtendGDScriptParser::get_member_symbol(const String &p_name, const String &p_subclass) const {
- if (p_subclass.empty()) {
+ if (p_subclass.is_empty()) {
const lsp::DocumentSymbol *const *ptr = members.getptr(p_name);
if (ptr) {
return *ptr;
@@ -611,7 +660,7 @@ const List<lsp::DocumentLink> &ExtendGDScriptParser::get_document_links() const
}
const Array &ExtendGDScriptParser::get_member_completions() {
- if (member_completions.empty()) {
+ if (member_completions.is_empty()) {
const String *name = members.next(nullptr);
while (name) {
const lsp::DocumentSymbol *symbol = members.get(*name);
@@ -647,7 +696,9 @@ Dictionary ExtendGDScriptParser::dump_function_api(const GDScriptParser::Functio
ERR_FAIL_NULL_V(p_func, func);
func["name"] = p_func->identifier->name;
func["return_type"] = p_func->get_datatype().to_string();
- func["rpc_mode"] = p_func->rpc_mode;
+ func["rpc_mode"] = p_func->rpc_config.rpc_mode;
+ func["rpc_transfer_mode"] = p_func->rpc_config.transfer_mode;
+ func["rpc_transfer_channel"] = p_func->rpc_config.channel;
Array parameters;
for (int i = 0; i < p_func->parameters.size(); i++) {
Dictionary arg;
@@ -723,8 +774,8 @@ Dictionary ExtendGDScriptParser::dump_class_api(const GDScriptParser::ClassNode
} break;
case ClassNode::Member::ENUM: {
Dictionary enum_dict;
- for (int j = 0; j < m.m_enum->values.size(); i++) {
- enum_dict[m.m_enum->values[i].identifier->name] = m.m_enum->values[i].value;
+ for (int j = 0; j < m.m_enum->values.size(); j++) {
+ enum_dict[m.m_enum->values[j].identifier->name] = m.m_enum->values[j].value;
}
Dictionary api;
diff --git a/modules/gdscript/language_server/gdscript_extend_parser.h b/modules/gdscript/language_server/gdscript_extend_parser.h
index 0c031d7883..5d7b16765b 100644
--- a/modules/gdscript/language_server/gdscript_extend_parser.h
+++ b/modules/gdscript/language_server/gdscript_extend_parser.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define GDSCRIPT_EXTEND_PARSER_H
#include "../gdscript_parser.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "lsp.hpp"
#ifndef LINE_NUMBER_TO_INDEX
@@ -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 2a67d2ff4f..5cf1e0fc5f 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.cpp
+++ b/modules/gdscript/language_server/gdscript_language_protocol.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,9 +30,8 @@
#include "gdscript_language_protocol.h"
-#include "core/io/json.h"
-#include "core/os/copymem.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
+#include "editor/doc_tools.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
@@ -95,7 +94,7 @@ Error GDScriptLanguageProtocol::LSPeer::handle_data() {
// Response
String output = GDScriptLanguageProtocol::get_singleton()->process_message(msg);
- if (!output.empty()) {
+ if (!output.is_empty()) {
res_queue.push_back(output.utf8());
}
}
@@ -104,7 +103,7 @@ Error GDScriptLanguageProtocol::LSPeer::handle_data() {
Error GDScriptLanguageProtocol::LSPeer::send_data() {
int sent = 0;
- if (!res_queue.empty()) {
+ if (!res_queue.is_empty()) {
CharString c_res = res_queue[0];
if (res_sent < c_res.size()) {
Error err = connection->put_partial_data((const uint8_t *)c_res.get_data() + res_sent, c_res.size() - res_sent - 1, sent);
@@ -129,18 +128,18 @@ Error GDScriptLanguageProtocol::on_client_connected() {
peer->connection = tcp_peer;
clients.set(next_client_id, peer);
next_client_id++;
- EditorNode::get_log()->add_message("Connection Taken", EditorLog::MSG_TYPE_EDITOR);
+ EditorNode::get_log()->add_message("[LSP] Connection Taken", EditorLog::MSG_TYPE_EDITOR);
return OK;
}
void GDScriptLanguageProtocol::on_client_disconnected(const int &p_client_id) {
clients.erase(p_client_id);
- EditorNode::get_log()->add_message("Disconnected", EditorLog::MSG_TYPE_EDITOR);
+ EditorNode::get_log()->add_message("[LSP] Disconnected", EditorLog::MSG_TYPE_EDITOR);
}
String GDScriptLanguageProtocol::process_message(const String &p_text) {
String ret = process_string(p_text);
- if (ret.empty()) {
+ if (ret.is_empty()) {
return ret;
} else {
return format_output(ret);
@@ -162,7 +161,7 @@ void GDScriptLanguageProtocol::_bind_methods() {
ClassDB::bind_method(D_METHOD("initialized", "params"), &GDScriptLanguageProtocol::initialized);
ClassDB::bind_method(D_METHOD("on_client_connected"), &GDScriptLanguageProtocol::on_client_connected);
ClassDB::bind_method(D_METHOD("on_client_disconnected"), &GDScriptLanguageProtocol::on_client_disconnected);
- ClassDB::bind_method(D_METHOD("notify_client", "method", "params"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1));
+ ClassDB::bind_method(D_METHOD("notify_client", "method", "params", "client_id"), &GDScriptLanguageProtocol::notify_client, DEFVAL(Variant()), DEFVAL(-1));
ClassDB::bind_method(D_METHOD("is_smart_resolve_enabled"), &GDScriptLanguageProtocol::is_smart_resolve_enabled);
ClassDB::bind_method(D_METHOD("get_text_document"), &GDScriptLanguageProtocol::get_text_document);
ClassDB::bind_method(D_METHOD("get_workspace"), &GDScriptLanguageProtocol::get_workspace);
@@ -194,7 +193,7 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
vformat("GDScriptLanguageProtocol: Can't initialize invalid peer '%d'.", latest_client_id));
Ref<LSPeer> peer = clients.get(latest_client_id);
if (peer != nullptr) {
- String msg = JSON::print(request);
+ String msg = Variant(request).to_json_string();
msg = format_output(msg);
(*peer)->res_queue.push_back(msg.utf8());
}
@@ -212,12 +211,12 @@ Dictionary GDScriptLanguageProtocol::initialize(const Dictionary &p_params) {
void GDScriptLanguageProtocol::initialized(const Variant &p_params) {
lsp::GodotCapabilities capabilities;
- DocData *doc = EditorHelp::get_doc_data();
- for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) {
+ DocTools *doc = EditorHelp::get_doc_data();
+ 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);
@@ -255,7 +254,7 @@ void GDScriptLanguageProtocol::poll() {
}
}
-Error GDScriptLanguageProtocol::start(int p_port, const IP_Address &p_bind_ip) {
+Error GDScriptLanguageProtocol::start(int p_port, const IPAddress &p_bind_ip) {
return server->listen(p_port, p_bind_ip);
}
@@ -280,7 +279,24 @@ void GDScriptLanguageProtocol::notify_client(const String &p_method, const Varia
ERR_FAIL_COND(peer == nullptr);
Dictionary message = make_notification(p_method, p_params);
- String msg = JSON::print(message);
+ String msg = Variant(message).to_json_string();
+ msg = format_output(msg);
+ 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());
}
@@ -294,10 +310,10 @@ bool GDScriptLanguageProtocol::is_goto_native_symbols_enabled() const {
}
GDScriptLanguageProtocol::GDScriptLanguageProtocol() {
- server.instance();
+ server.instantiate();
singleton = this;
- workspace.instance();
- text_document.instance();
+ workspace.instantiate();
+ text_document.instantiate();
set_scope("textDocument", text_document.ptr());
set_scope("completionItem", text_document.ptr());
set_scope("workspace", workspace.ptr());
diff --git a/modules/gdscript/language_server/gdscript_language_protocol.h b/modules/gdscript/language_server/gdscript_language_protocol.h
index cf5242e8c5..a4a63a23a8 100644
--- a/modules/gdscript/language_server/gdscript_language_protocol.h
+++ b/modules/gdscript/language_server/gdscript_language_protocol.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GDSCRIPT_PROTOCAL_SERVER_H
-#define GDSCRIPT_PROTOCAL_SERVER_H
+#ifndef GDSCRIPT_LANGUAGE_PROTOCOL_H
+#define GDSCRIPT_LANGUAGE_PROTOCOL_H
#include "core/io/stream_peer.h"
#include "core/io/stream_peer_tcp.h"
@@ -37,7 +37,13 @@
#include "gdscript_text_document.h"
#include "gdscript_workspace.h"
#include "lsp.hpp"
+
+#include "modules/modules_enabled.gen.h" // For jsonrpc.
+#ifdef MODULE_JSONRPC_ENABLED
#include "modules/jsonrpc/jsonrpc.h"
+#else
+#error "Can't build GDScript LSP without JSONRPC module."
+#endif
#define LSP_MAX_BUFFER_SIZE 4194304
#define LSP_MAX_CLIENTS 8
@@ -46,7 +52,7 @@ class GDScriptLanguageProtocol : public JSONRPC {
GDCLASS(GDScriptLanguageProtocol, JSONRPC)
private:
- struct LSPeer : Reference {
+ struct LSPeer : RefCounted {
Ref<StreamPeerTCP> connection;
uint8_t req_buf[LSP_MAX_BUFFER_SIZE];
@@ -69,10 +75,12 @@ private:
static GDScriptLanguageProtocol *singleton;
HashMap<int, Ref<LSPeer>> clients;
- Ref<TCP_Server> server;
+ Ref<TCPServer> server;
int latest_client_id = 0;
int next_client_id = 0;
+ int next_server_id = 0;
+
Ref<GDScriptTextDocument> text_document;
Ref<GDScriptWorkspace> workspace;
@@ -97,10 +105,11 @@ public:
_FORCE_INLINE_ bool is_initialized() const { return _initialized; }
void poll();
- Error start(int p_port, const IP_Address &p_bind_ip);
+ Error start(int p_port, const IPAddress &p_bind_ip);
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;
@@ -108,4 +117,4 @@ public:
GDScriptLanguageProtocol();
};
-#endif
+#endif // GDSCRIPT_LANGUAGE_PROTOCOL_H
diff --git a/modules/gdscript/language_server/gdscript_language_server.cpp b/modules/gdscript/language_server/gdscript_language_server.cpp
index 3387d262f8..41a2f9e4ad 100644
--- a/modules/gdscript/language_server/gdscript_language_server.cpp
+++ b/modules/gdscript/language_server/gdscript_language_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,18 +30,13 @@
#include "gdscript_language_server.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "core/os/os.h"
#include "editor/editor_log.h"
#include "editor/editor_node.h"
GDScriptLanguageServer::GDScriptLanguageServer() {
- thread = nullptr;
- thread_running = false;
- started = false;
-
- use_thread = false;
- port = 6008;
+ _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);
@@ -62,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();
}
@@ -82,14 +78,14 @@ 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, IP_Address("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) {
- ERR_FAIL_COND(thread != nullptr);
thread_running = true;
- thread = Thread::create(GDScriptLanguageServer::thread_main, this);
+ thread.start(GDScriptLanguageServer::thread_main, this);
}
set_process_internal(!use_thread);
started = true;
@@ -98,11 +94,9 @@ void GDScriptLanguageServer::start() {
void GDScriptLanguageServer::stop() {
if (use_thread) {
- ERR_FAIL_COND(nullptr == thread);
+ ERR_FAIL_COND(!thread.is_started());
thread_running = false;
- Thread::wait_to_finish(thread);
- memdelete(thread);
- thread = nullptr;
+ thread.wait_to_finish();
}
protocol.stop();
started = false;
@@ -110,7 +104,7 @@ void GDScriptLanguageServer::stop() {
}
void register_lsp_types() {
- ClassDB::register_class<GDScriptLanguageProtocol>();
- ClassDB::register_class<GDScriptTextDocument>();
- ClassDB::register_class<GDScriptWorkspace>();
+ GDREGISTER_CLASS(GDScriptLanguageProtocol);
+ GDREGISTER_CLASS(GDScriptTextDocument);
+ GDREGISTER_CLASS(GDScriptWorkspace);
}
diff --git a/modules/gdscript/language_server/gdscript_language_server.h b/modules/gdscript/language_server/gdscript_language_server.h
index 228d29bf42..f1413f0133 100644
--- a/modules/gdscript/language_server/gdscript_language_server.h
+++ b/modules/gdscript/language_server/gdscript_language_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,19 +40,18 @@ class GDScriptLanguageServer : public EditorPlugin {
GDScriptLanguageProtocol protocol;
- Thread *thread;
- bool thread_running;
- bool started;
- bool use_thread;
- int port;
+ Thread thread;
+ bool thread_running = false;
+ bool started = false;
+ bool use_thread = false;
+ String host = "127.0.0.1";
+ int port = 6008;
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 c6fe3169dc..92ce71f395 100644
--- a/modules/gdscript/language_server/gdscript_text_document.cpp
+++ b/modules/gdscript/language_server/gdscript_text_document.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,11 +40,14 @@
void GDScriptTextDocument::_bind_methods() {
ClassDB::bind_method(D_METHOD("didOpen"), &GDScriptTextDocument::didOpen);
+ ClassDB::bind_method(D_METHOD("didClose"), &GDScriptTextDocument::didClose);
ClassDB::bind_method(D_METHOD("didChange"), &GDScriptTextDocument::didChange);
+ ClassDB::bind_method(D_METHOD("didSave"), &GDScriptTextDocument::didSave);
ClassDB::bind_method(D_METHOD("nativeSymbol"), &GDScriptTextDocument::nativeSymbol);
ClassDB::bind_method(D_METHOD("documentSymbol"), &GDScriptTextDocument::documentSymbol);
ClassDB::bind_method(D_METHOD("completion"), &GDScriptTextDocument::completion);
ClassDB::bind_method(D_METHOD("resolve"), &GDScriptTextDocument::resolve);
+ ClassDB::bind_method(D_METHOD("rename"), &GDScriptTextDocument::rename);
ClassDB::bind_method(D_METHOD("foldingRange"), &GDScriptTextDocument::foldingRange);
ClassDB::bind_method(D_METHOD("codeLens"), &GDScriptTextDocument::codeLens);
ClassDB::bind_method(D_METHOD("documentLink"), &GDScriptTextDocument::documentLink);
@@ -61,6 +64,11 @@ void GDScriptTextDocument::didOpen(const Variant &p_param) {
sync_script_content(doc.uri, doc.text);
}
+void GDScriptTextDocument::didClose(const Variant &p_param) {
+ // Left empty on purpose. Godot does nothing special on closing a document,
+ // but it satisfies LSP clients that require didClose be implemented.
+}
+
void GDScriptTextDocument::didChange(const Variant &p_param) {
lsp::TextDocumentItem doc = load_document_item(p_param);
Dictionary dict = p_param;
@@ -73,6 +81,20 @@ void GDScriptTextDocument::didChange(const Variant &p_param) {
sync_script_content(doc.uri, doc.text);
}
+void GDScriptTextDocument::didSave(const Variant &p_param) {
+ lsp::TextDocumentItem doc = load_document_item(p_param);
+ Dictionary dict = p_param;
+ String text = dict["text"];
+
+ sync_script_content(doc.uri, text);
+
+ /*String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(doc.uri);
+
+ Ref<GDScript> script = ResourceLoader::load(path);
+ script->load_source_code(path);
+ script->reload(true);*/
+}
+
lsp::TextDocumentItem GDScriptTextDocument::load_document_item(const Variant &p_param) {
lsp::TextDocumentItem doc;
Dictionary params = p_param;
@@ -147,12 +169,11 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
List<ScriptCodeCompletionOption> options;
GDScriptLanguageProtocol::get_singleton()->get_workspace()->completion(params, &options);
- if (!options.empty()) {
+ if (!options.is_empty()) {
int i = 0;
arr.resize(options.size());
- for (const List<ScriptCodeCompletionOption>::Element *E = options.front(); E; E = E->next()) {
- const ScriptCodeCompletionOption &option = E->get();
+ for (const ScriptCodeCompletionOption &option : options) {
lsp::CompletionItem item;
item.label = option.display;
item.data = request_data;
@@ -196,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();
@@ -210,6 +231,14 @@ Array GDScriptTextDocument::completion(const Dictionary &p_params) {
return arr;
}
+Dictionary GDScriptTextDocument::rename(const Dictionary &p_params) {
+ lsp::TextDocumentPositionParams params;
+ params.load(p_params);
+ String new_name = p_params["newName"];
+
+ return GDScriptLanguageProtocol::get_singleton()->get_workspace()->rename(params, new_name);
+}
+
Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
lsp::CompletionItem item;
item.load(p_params);
@@ -257,13 +286,13 @@ Dictionary GDScriptTextDocument::resolve(const Dictionary &p_params) {
if ((item.kind == lsp::CompletionItemKind::Method || item.kind == lsp::CompletionItemKind::Function) && !item.label.ends_with("):")) {
item.insertText = item.label + "(";
- if (symbol && symbol->children.empty()) {
+ if (symbol && symbol->children.is_empty()) {
item.insertText += ")";
}
} else if (item.kind == lsp::CompletionItemKind::Event) {
if (params.context.triggerKind == lsp::CompletionTriggerKind::TriggerCharacter && (params.context.triggerCharacter == "(")) {
- const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", false) ? "'" : "\"";
- item.insertText = quote_style + item.label + quote_style;
+ const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\"";
+ item.insertText = item.label.quote(quote_style);
}
}
@@ -288,8 +317,8 @@ Array GDScriptTextDocument::documentLink(const Dictionary &p_params) {
List<lsp::DocumentLink> links;
GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_document_links(params.textDocument.uri, links);
- for (const List<lsp::DocumentLink>::Element *E = links.front(); E; E = E->next()) {
- ret.push_back(E->get().to_json());
+ for (const lsp::DocumentLink &E : links) {
+ ret.push_back(E.to_json());
}
return ret;
}
@@ -316,8 +345,8 @@ Variant GDScriptTextDocument::hover(const Dictionary &p_params) {
Array contents;
List<const lsp::DocumentSymbol *> list;
GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(params, list);
- for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
- if (const lsp::DocumentSymbol *s = E->get()) {
+ for (const lsp::DocumentSymbol *&E : list) {
+ if (const lsp::DocumentSymbol *s = E) {
contents.push_back(s->render().value);
}
}
@@ -341,7 +370,7 @@ Variant GDScriptTextDocument::declaration(const Dictionary &p_params) {
params.load(p_params);
List<const lsp::DocumentSymbol *> symbols;
Array arr = this->find_symbols(params, symbols);
- if (arr.empty() && !symbols.empty() && !symbols.front()->get()->native_class.empty()) { // Find a native symbol
+ if (arr.is_empty() && !symbols.is_empty() && !symbols.front()->get()->native_class.is_empty()) { // Find a native symbol
const lsp::DocumentSymbol *symbol = symbols.front()->get();
if (GDScriptLanguageProtocol::get_singleton()->is_goto_native_symbols_enabled()) {
String id;
@@ -367,7 +396,7 @@ Variant GDScriptTextDocument::declaration(const Dictionary &p_params) {
id = "class_global:" + symbol->native_class + ":" + symbol->name;
break;
}
- call_deferred("show_native_symbol_in_editor", id);
+ call_deferred(SNAME("show_native_symbol_in_editor"), id);
} else {
notify_client_show_symbol(symbol);
}
@@ -399,11 +428,23 @@ GDScriptTextDocument::~GDScriptTextDocument() {
void GDScriptTextDocument::sync_script_content(const String &p_path, const String &p_content) {
String path = GDScriptLanguageProtocol::get_singleton()->get_workspace()->get_file_path(p_path);
+ if (!path.begins_with("res://")) {
+ return;
+ }
GDScriptLanguageProtocol::get_singleton()->get_workspace()->parse_script(path, p_content);
+
+ EditorFileSystem::get_singleton()->update_file(path);
+ Error error;
+ Ref<GDScript> script = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &error);
+ if (error == OK) {
+ if (script->load_source_code(path) == OK) {
+ script->reload(true);
+ }
+ }
}
void GDScriptTextDocument::show_native_symbol_in_editor(const String &p_symbol_id) {
- ScriptEditor::get_singleton()->call_deferred("_help_class_goto", p_symbol_id);
+ ScriptEditor::get_singleton()->call_deferred(SNAME("_help_class_goto"), p_symbol_id);
DisplayServer::get_singleton()->window_move_to_foreground();
}
@@ -423,9 +464,9 @@ Array GDScriptTextDocument::find_symbols(const lsp::TextDocumentPositionParams &
} else if (GDScriptLanguageProtocol::get_singleton()->is_smart_resolve_enabled()) {
List<const lsp::DocumentSymbol *> list;
GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(p_location, list);
- for (List<const lsp::DocumentSymbol *>::Element *E = list.front(); E; E = E->next()) {
- if (const lsp::DocumentSymbol *s = E->get()) {
- if (!s->uri.empty()) {
+ for (const lsp::DocumentSymbol *&E : list) {
+ if (const lsp::DocumentSymbol *s = E) {
+ if (!s->uri.is_empty()) {
lsp::Location location;
location.uri = s->uri;
location.range = s->range;
diff --git a/modules/gdscript/language_server/gdscript_text_document.h b/modules/gdscript/language_server/gdscript_text_document.h
index b2fd0c31f9..9021c84a3f 100644
--- a/modules/gdscript/language_server/gdscript_text_document.h
+++ b/modules/gdscript/language_server/gdscript_text_document.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,19 +31,21 @@
#ifndef GDSCRIPT_TEXT_DOCUMENT_H
#define GDSCRIPT_TEXT_DOCUMENT_H
-#include "core/os/file_access.h"
-#include "core/reference.h"
+#include "core/io/file_access.h"
+#include "core/object/ref_counted.h"
#include "lsp.hpp"
-class GDScriptTextDocument : public Reference {
- GDCLASS(GDScriptTextDocument, Reference)
+class GDScriptTextDocument : public RefCounted {
+ GDCLASS(GDScriptTextDocument, RefCounted)
protected:
static void _bind_methods();
FileAccess *file_checker;
void didOpen(const Variant &p_param);
+ void didClose(const Variant &p_param);
void didChange(const Variant &p_param);
+ void didSave(const Variant &p_param);
void sync_script_content(const String &p_path, const String &p_content);
void show_native_symbol_in_editor(const String &p_symbol_id);
@@ -60,6 +62,7 @@ public:
Array documentSymbol(const Dictionary &p_params);
Array completion(const Dictionary &p_params);
Dictionary resolve(const Dictionary &p_params);
+ Dictionary rename(const Dictionary &p_params);
Array foldingRange(const Dictionary &p_params);
Array codeLens(const Dictionary &p_params);
Array documentLink(const Dictionary &p_params);
diff --git a/modules/gdscript/language_server/gdscript_workspace.cpp b/modules/gdscript/language_server/gdscript_workspace.cpp
index 776193e37c..932bfb2caa 100644
--- a/modules/gdscript/language_server/gdscript_workspace.cpp
+++ b/modules/gdscript/language_server/gdscript_workspace.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,8 +32,9 @@
#include "../gdscript.h"
#include "../gdscript_parser.h"
-#include "core/project_settings.h"
-#include "core/script_language.h"
+#include "core/config/project_settings.h"
+#include "core/object/script_language.h"
+#include "editor/doc_tools.h"
#include "editor/editor_file_system.h"
#include "editor/editor_help.h"
#include "editor/editor_node.h"
@@ -41,6 +42,8 @@
#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);
ClassDB::bind_method(D_METHOD("parse_local_script", "path"), &GDScriptWorkspace::parse_local_script);
@@ -50,6 +53,64 @@ 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) {
+ String function_signature = "func " + function;
+ Ref<Script> script = obj->get_script();
+
+ 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) {
+ Dictionary file = files[i];
+ String uri = file["uri"];
+ String path = get_file_path(uri);
+ parse_script(path, "");
+ }
+}
+
void GDScriptWorkspace::remove_cache_parser(const String &p_path) {
Map<String, ExtendGDScriptParser *>::Element *parser = parse_results.find(p_path);
Map<String, ExtendGDScriptParser *>::Element *script = scripts.find(p_path);
@@ -79,7 +140,7 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_native_symbol(const String &p_
if (const Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(class_name)) {
const lsp::DocumentSymbol &class_symbol = E->value();
- if (p_member.empty()) {
+ if (p_member.is_empty()) {
return &class_symbol;
} else {
for (int i = 0; i < class_symbol.children.size(); i++) {
@@ -104,11 +165,40 @@ const lsp::DocumentSymbol *GDScriptWorkspace::get_script_symbol(const String &p_
return nullptr;
}
+const lsp::DocumentSymbol *GDScriptWorkspace::get_parameter_symbol(const lsp::DocumentSymbol *p_parent, const String &symbol_identifier) {
+ for (int i = 0; i < p_parent->children.size(); ++i) {
+ const lsp::DocumentSymbol *parameter_symbol = &p_parent->children[i];
+ if (!parameter_symbol->detail.is_empty() && parameter_symbol->name == symbol_identifier) {
+ return parameter_symbol;
+ }
+ }
+
+ return nullptr;
+}
+
+const lsp::DocumentSymbol *GDScriptWorkspace::get_local_symbol(const ExtendGDScriptParser *p_parser, const String &p_symbol_identifier) {
+ const lsp::DocumentSymbol *class_symbol = &p_parser->get_symbols();
+
+ for (int i = 0; i < class_symbol->children.size(); ++i) {
+ if (class_symbol->children[i].kind == lsp::SymbolKind::Function || class_symbol->children[i].kind == lsp::SymbolKind::Class) {
+ const lsp::DocumentSymbol *function_symbol = &class_symbol->children[i];
+
+ for (int l = 0; l < function_symbol->children.size(); ++l) {
+ const lsp::DocumentSymbol *local = &function_symbol->children[l];
+ if (!local->detail.is_empty() && local->name == p_symbol_identifier) {
+ return local;
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
void GDScriptWorkspace::reload_all_workspace_scripts() {
List<String> paths;
list_script_files("res://", paths);
- for (List<String>::Element *E = paths.front(); E; E = E->next()) {
- const String &path = E->get();
+ for (const String &path : paths) {
Error err;
String content = FileAccess::get_file_as_string(path, &err);
ERR_CONTINUE(err != OK);
@@ -170,13 +260,15 @@ ExtendGDScriptParser *GDScriptWorkspace::get_parse_result(const String &p_path)
Array GDScriptWorkspace::symbol(const Dictionary &p_params) {
String query = p_params["query"];
Array arr;
- if (!query.empty()) {
- for (Map<String, ExtendGDScriptParser *>::Element *E = scripts.front(); E; E = E->next()) {
+ if (!query.is_empty()) {
+ 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)) {
- arr.push_back(script_symbols[i].to_json());
+ lsp::DocumentedSymbolInformation symbol = script_symbols[i];
+ symbol.location.uri = get_file_uri(symbol.location.uri);
+ arr.push_back(symbol.to_json());
}
}
}
@@ -189,16 +281,16 @@ Error GDScriptWorkspace::initialize() {
return OK;
}
- DocData *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();
+ DocTools *doc = EditorHelp::get_doc_data();
+ 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;
class_symbol.detail = String("<Native> class ") + class_name;
- if (!class_data.inherits.empty()) {
+ if (!class_data.inherits.is_empty()) {
class_symbol.detail += " extends " + class_data.inherits;
}
class_symbol.documentation = class_data.brief_description + "\n" + class_data.description;
@@ -218,18 +310,13 @@ Error GDScriptWorkspace::initialize() {
class_symbol.children.push_back(symbol);
}
- Vector<DocData::PropertyDoc> properties;
- properties.append_array(class_data.properties);
- const int theme_prop_start_idx = properties.size();
- properties.append_array(class_data.theme_properties);
-
for (int i = 0; i < class_data.properties.size(); i++) {
const DocData::PropertyDoc &data = class_data.properties[i];
lsp::DocumentSymbol symbol;
symbol.name = data.name;
symbol.native_class = class_name;
symbol.kind = lsp::SymbolKind::Property;
- symbol.detail = String(i >= theme_prop_start_idx ? "<Theme> var" : "var") + " " + class_name + "." + data.name;
+ symbol.detail = "var " + class_name + "." + data.name;
if (data.enumeration.length()) {
symbol.detail += ": " + data.enumeration;
} else {
@@ -239,8 +326,21 @@ Error GDScriptWorkspace::initialize() {
class_symbol.children.push_back(symbol);
}
+ for (int i = 0; i < class_data.theme_properties.size(); i++) {
+ const DocData::ThemeItemDoc &data = class_data.theme_properties[i];
+ lsp::DocumentSymbol symbol;
+ symbol.name = data.name;
+ symbol.native_class = class_name;
+ symbol.kind = lsp::SymbolKind::Property;
+ symbol.detail = "<Theme> var " + class_name + "." + data.name + ": " + data.type;
+ symbol.documentation = data.description;
+ class_symbol.children.push_back(symbol);
+ }
+
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);
@@ -262,7 +362,7 @@ Error GDScriptWorkspace::initialize() {
symbol_arg.kind = lsp::SymbolKind::Variable;
symbol_arg.detail = arg.type;
- if (!arg_default_value_started && !arg.default_value.empty()) {
+ if (!arg_default_value_started && !arg.default_value.is_empty()) {
arg_default_value_started = true;
}
String arg_str = arg.name + ": " + arg.type;
@@ -277,11 +377,11 @@ Error GDScriptWorkspace::initialize() {
symbol.children.push_back(symbol_arg);
}
if (data.qualifiers.find("vararg") != -1) {
- params += params.empty() ? "..." : ", ...";
+ params += params.is_empty() ? "..." : ", ...";
}
String return_type = data.return_type;
- if (return_type.empty()) {
+ if (return_type.is_empty()) {
return_type = "void";
}
symbol.detail = "func " + class_name + "." + data.name + "(" + params + ") -> " + return_type;
@@ -295,22 +395,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;
}
@@ -337,6 +440,50 @@ Error GDScriptWorkspace::parse_script(const String &p_path, const String &p_cont
return err;
}
+Dictionary GDScriptWorkspace::rename(const lsp::TextDocumentPositionParams &p_doc_pos, const String &new_name) {
+ Error err;
+ String path = get_file_path(p_doc_pos.textDocument.uri);
+
+ lsp::WorkspaceEdit edit;
+
+ List<String> paths;
+ list_script_files("res://", paths);
+
+ const lsp::DocumentSymbol *reference_symbol = resolve_symbol(p_doc_pos);
+ if (reference_symbol) {
+ String identifier = reference_symbol->name;
+
+ for (List<String>::Element *PE = paths.front(); PE; PE = PE->next()) {
+ PackedStringArray content = FileAccess::get_file_as_string(PE->get(), &err).split("\n");
+ for (int i = 0; i < content.size(); ++i) {
+ String line = content[i];
+
+ int character = line.find(identifier);
+ while (character > -1) {
+ lsp::TextDocumentPositionParams params;
+
+ lsp::TextDocumentIdentifier text_doc;
+ text_doc.uri = get_file_uri(PE->get());
+
+ params.textDocument = text_doc;
+ params.position.line = i;
+ params.position.character = character;
+
+ const lsp::DocumentSymbol *other_symbol = resolve_symbol(params);
+
+ if (other_symbol == reference_symbol) {
+ edit.add_change(text_doc.uri, i, character, character + identifier.length(), new_name);
+ }
+
+ character = line.find(identifier, character + 1);
+ }
+ }
+ }
+ }
+
+ return edit.to_json();
+}
+
Error GDScriptWorkspace::parse_local_script(const String &p_path) {
Error err;
String content = FileAccess::get_file_as_string(p_path, &err);
@@ -349,7 +496,7 @@ Error GDScriptWorkspace::parse_local_script(const String &p_path) {
String GDScriptWorkspace::get_file_path(const String &p_uri) const {
String path = p_uri;
path = path.replace(root_uri + "/", "res://");
- path = path.http_unescape();
+ path = path.uri_decode();
return path;
}
@@ -412,7 +559,7 @@ Node *GDScriptWorkspace::_get_owner_scene_node(String p_path) {
RES owner_res = ResourceLoader::load(owner_path);
if (Object::cast_to<PackedScene>(owner_res.ptr())) {
Ref<PackedScene> owner_packed_scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*owner_res));
- owner_scene_node = owner_packed_scene->instance();
+ owner_scene_node = owner_packed_scene->instantiate();
break;
}
}
@@ -427,15 +574,38 @@ void GDScriptWorkspace::completion(const lsp::CompletionParams &p_params, List<S
if (const ExtendGDScriptParser *parser = get_parse_result(path)) {
Node *owner_scene_node = _get_owner_scene_node(path);
+
+ Array stack;
+ Node *current = nullptr;
+ if (owner_scene_node != nullptr) {
+ stack.push_back(owner_scene_node);
+
+ while (!stack.is_empty()) {
+ current = stack.pop_back();
+ Ref<GDScript> script = current->get_script();
+ if (script.is_valid() && script->get_path() == path) {
+ break;
+ }
+ for (int i = 0; i < current->get_child_count(); ++i) {
+ stack.push_back(current->get_child(i));
+ }
+ }
+
+ Ref<GDScript> script = current->get_script();
+ if (!script.is_valid() || script->get_path() != path) {
+ current = owner_scene_node;
+ }
+ }
+
String code = parser->get_text_for_completion(p_params.position);
- GDScriptLanguage::get_singleton()->complete_code(code, path, owner_scene_node, r_options, forced, call_hint);
+ GDScriptLanguage::get_singleton()->complete_code(code, path, current, r_options, forced, call_hint);
if (owner_scene_node) {
memdelete(owner_scene_node);
}
}
}
-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);
@@ -447,39 +617,52 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_symbol(const lsp::TextDocu
}
lsp::Position pos = p_doc_pos.position;
- if (symbol_identifier.empty()) {
+ if (symbol_identifier.is_empty()) {
Vector2i offset;
symbol_identifier = parser->get_identifier_under_position(p_doc_pos.position, offset);
pos.character += offset.y;
}
- if (!symbol_identifier.empty()) {
+ if (!symbol_identifier.is_empty()) {
if (ScriptServer::is_global_class(symbol_identifier)) {
String class_path = ScriptServer::get_global_class_path(symbol_identifier);
symbol = get_script_symbol(class_path);
} 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()) {
target_script_path = ret.script->get_path();
+ } else if (!ret.class_path.is_empty()) {
+ target_script_path = ret.class_path;
}
if (const ExtendGDScriptParser *target_parser = get_parse_result(target_script_path)) {
symbol = target_parser->get_symbol_defined_at_line(LINE_NUMBER_TO_INDEX(ret.location));
+
+ if (symbol && symbol->kind == lsp::SymbolKind::Function && symbol->name != symbol_identifier) {
+ symbol = get_parameter_symbol(symbol, symbol_identifier);
+ }
}
} else {
String member = ret.class_member;
- if (member.empty() && symbol_identifier != ret.class_name) {
+ if (member.is_empty() && symbol_identifier != ret.class_name) {
member = symbol_identifier;
}
symbol = get_native_symbol(ret.class_name, member);
}
} else {
symbol = parser->get_member_symbol(symbol_identifier);
+
+ if (!symbol) {
+ symbol = get_local_symbol(parser, symbol_identifier);
+ }
}
}
}
@@ -504,8 +687,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);
@@ -528,7 +711,7 @@ void GDScriptWorkspace::resolve_related_symbols(const lsp::TextDocumentPositionP
const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::NativeSymbolInspectParams &p_params) {
if (Map<StringName, lsp::DocumentSymbol>::Element *E = native_symbols.find(p_params.native_class)) {
const lsp::DocumentSymbol &symbol = E->get();
- if (p_params.symbol_name.empty() || p_params.symbol_name == symbol.name) {
+ if (p_params.symbol_name.is_empty() || p_params.symbol_name == symbol.name) {
return &symbol;
}
@@ -545,8 +728,8 @@ const lsp::DocumentSymbol *GDScriptWorkspace::resolve_native_symbol(const lsp::N
void GDScriptWorkspace::resolve_document_links(const String &p_uri, List<lsp::DocumentLink> &r_list) {
if (const ExtendGDScriptParser *parser = get_parse_successed_script(get_file_path(p_uri))) {
const List<lsp::DocumentLink> &links = parser->get_document_links();
- for (const List<lsp::DocumentLink>::Element *E = links.front(); E; E = E->next()) {
- r_list.push_back(E->get());
+ for (const lsp::DocumentLink &E : links) {
+ r_list.push_back(E);
}
}
}
@@ -573,8 +756,7 @@ Error GDScriptWorkspace::resolve_signature(const lsp::TextDocumentPositionParams
GDScriptLanguageProtocol::get_singleton()->get_workspace()->resolve_related_symbols(text_pos, symbols);
}
- for (List<const lsp::DocumentSymbol *>::Element *E = symbols.front(); E; E = E->next()) {
- const lsp::DocumentSymbol *symbol = E->get();
+ for (const lsp::DocumentSymbol *const &symbol : symbols) {
if (symbol->kind == lsp::SymbolKind::Method || symbol->kind == lsp::SymbolKind::Function) {
lsp::SignatureInformation signature_info;
signature_info.label = symbol->detail;
@@ -606,12 +788,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 e45b06747d..6f5600b5cf 100644
--- a/modules/gdscript/language_server/gdscript_workspace.h
+++ b/modules/gdscript/language_server/gdscript_workspace.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,13 +32,13 @@
#define GDSCRIPT_WORKSPACE_H
#include "../gdscript_parser.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "editor/editor_file_system.h"
#include "gdscript_extend_parser.h"
#include "lsp.hpp"
-class GDScriptWorkspace : public Reference {
- GDCLASS(GDScriptWorkspace, Reference);
+class GDScriptWorkspace : public RefCounted {
+ GDCLASS(GDScriptWorkspace, RefCounted);
private:
void _get_owners(EditorFileSystemDirectory *efsd, String p_path, List<String> &owners);
@@ -52,6 +52,8 @@ protected:
const lsp::DocumentSymbol *get_native_symbol(const String &p_class, const String &p_member = "") const;
const lsp::DocumentSymbol *get_script_symbol(const String &p_path) const;
+ const lsp::DocumentSymbol *get_parameter_symbol(const lsp::DocumentSymbol *p_parent, const String &symbol_identifier);
+ const lsp::DocumentSymbol *get_local_symbol(const ExtendGDScriptParser *p_parser, const String &p_symbol_identifier);
void reload_all_workspace_scripts();
@@ -60,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;
@@ -83,12 +87,14 @@ 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);
Dictionary generate_script_api(const String &p_path);
Error resolve_signature(const lsp::TextDocumentPositionParams &p_doc_pos, lsp::SignatureHelp &r_signature);
+ void did_delete_files(const Dictionary &p_params);
+ Dictionary rename(const lsp::TextDocumentPositionParams &p_doc_pos, const String &new_name);
GDScriptWorkspace();
~GDScriptWorkspace();
diff --git a/modules/gdscript/language_server/lsp.hpp b/modules/gdscript/language_server/lsp.hpp
index cf27a1578c..b12d1f5f3b 100644
--- a/modules/gdscript/language_server/lsp.hpp
+++ b/modules/gdscript/language_server/lsp.hpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,9 +31,9 @@
#ifndef GODOT_LSP_H
#define GODOT_LSP_H
-#include "core/class_db.h"
-#include "core/list.h"
-#include "editor/doc_data.h"
+#include "core/doc_data.h"
+#include "core/object/class_db.h"
+#include "core/templates/list.h"
namespace lsp {
@@ -255,6 +255,72 @@ struct TextEdit {
};
/**
+ * The edits to be applied.
+ */
+struct WorkspaceEdit {
+ /**
+ * Holds changes to existing resources.
+ */
+ 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 (const KeyValue<String, Vector<TextEdit>> &E : changes) {
+ Array edits;
+ for (int i = 0; i < E.value.size(); ++i) {
+ Dictionary text_edit;
+ 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;
+ }
+ dict["changes"] = out_changes;
+
+ return dict;
+ }
+
+ _FORCE_INLINE_ void add_change(const String &uri, const int &line, const int &start_character, const int &end_character, const String &new_text) {
+ if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) {
+ Vector<TextEdit> edit_list = E->value();
+ for (int i = 0; i < edit_list.size(); ++i) {
+ TextEdit edit = edit_list[i];
+ if (edit.range.start.character == start_character) {
+ return;
+ }
+ }
+ }
+
+ TextEdit new_edit;
+ new_edit.newText = new_text;
+ new_edit.range.start.line = line;
+ new_edit.range.start.character = start_character;
+ new_edit.range.end.line = line;
+ new_edit.range.end.character = end_character;
+
+ if (Map<String, Vector<TextEdit>>::Element *E = changes.find(uri)) {
+ E->value().push_back(new_edit);
+ } else {
+ Vector<TextEdit> edit_list;
+ edit_list.push_back(new_edit);
+ changes.insert(uri, edit_list);
+ }
+ }
+};
+
+/**
* Represents a reference to a command.
* Provides a title which will be used to represent a command in the UI.
* Commands are identified by a string identifier.
@@ -292,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
@@ -486,7 +552,7 @@ struct TextDocumentSyncOptions {
* If present save notifications are sent to the server. If omitted the notification should not be
* sent.
*/
- bool save = false;
+ SaveOptions save;
Dictionary to_json() {
Dictionary dict;
@@ -494,7 +560,7 @@ struct TextDocumentSyncOptions {
dict["willSave"] = willSave;
dict["openClose"] = openClose;
dict["change"] = change;
- dict["save"] = save;
+ dict["save"] = save.to_json();
return dict;
}
};
@@ -547,7 +613,7 @@ struct TextDocumentItem {
* The version number of this document (it will increase after each
* change, including undo/redo).
*/
- int version;
+ int version = 0;
/**
* The content of the opened text document.
@@ -584,7 +650,7 @@ struct TextDocumentContentChangeEvent {
/**
* The length of the range that got replaced.
*/
- int rangeLength;
+ int rangeLength = 0;
/**
* The new text of the range/document.
@@ -601,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
@@ -656,12 +722,12 @@ struct Diagnostic {
* The diagnostic's severity. Can be omitted. If omitted it is up to the
* client to interpret diagnostics as error, warning, info or hint.
*/
- int severity;
+ int severity = 0;
/**
* The diagnostic's code, which might appear in the user interface.
*/
- int code;
+ int code = 0;
/**
* A human-readable string describing the source of this
@@ -687,7 +753,7 @@ struct Diagnostic {
dict["severity"] = severity;
dict["message"] = message;
dict["source"] = source;
- if (!relatedInformation.empty()) {
+ if (!relatedInformation.is_empty()) {
Array arr;
arr.resize(relatedInformation.size());
for (int i = 0; i < relatedInformation.size(); i++) {
@@ -766,7 +832,7 @@ struct MarkupContent {
// Use namespace instead of enumeration to follow the LSP specifications
// lsp::EnumName::EnumValue is OK but lsp::EnumValue is not
-// And here C++ compilers are unhappy with our enumeration name like Color, File, Reference etc.
+// And here C++ compilers are unhappy with our enumeration name like Color, File, RefCounted etc.
/**
* The kind of a completion entry.
*/
@@ -788,7 +854,7 @@ static const int Keyword = 14;
static const int Snippet = 15;
static const int Color = 16;
static const int File = 17;
-static const int Reference = 18;
+static const int RefCounted = 18;
static const int Folder = 19;
static const int EnumMember = 20;
static const int Constant = 21;
@@ -805,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
@@ -833,7 +899,7 @@ struct CompletionItem {
* an icon is chosen by the editor. The standardized set
* of available values is defined in `CompletionItemKind`.
*/
- int kind;
+ int kind = 0;
/**
* A human-readable string with additional information
@@ -891,7 +957,7 @@ struct CompletionItem {
* The format of the insert text. The format applies to both the `insertText` property
* and the `newText` property of a provided `textEdit`.
*/
- int insertTextFormat;
+ int insertTextFormat = 0;
/**
* An edit which is applied to a document when selecting this completion. When an edit is provided the value of
@@ -1003,7 +1069,7 @@ struct CompletionList {
* This list it not complete. Further typing should result in recomputing
* this list.
*/
- bool isIncomplete;
+ bool isIncomplete = false;
/**
* The completion items.
@@ -1018,32 +1084,32 @@ struct CompletionList {
* A symbol kind.
*/
namespace SymbolKind {
-static const int File = 0;
-static const int Module = 1;
-static const int Namespace = 2;
-static const int Package = 3;
-static const int Class = 4;
-static const int Method = 5;
-static const int Property = 6;
-static const int Field = 7;
-static const int Constructor = 8;
-static const int Enum = 9;
-static const int Interface = 10;
-static const int Function = 11;
-static const int Variable = 12;
-static const int Constant = 13;
-static const int String = 14;
-static const int Number = 15;
-static const int Boolean = 16;
-static const int Array = 17;
-static const int Object = 18;
-static const int Key = 19;
-static const int Null = 20;
-static const int EnumMember = 21;
-static const int Struct = 22;
-static const int Event = 23;
-static const int Operator = 24;
-static const int TypeParameter = 25;
+static const int File = 1;
+static const int Module = 2;
+static const int Namespace = 3;
+static const int Package = 4;
+static const int Class = 5;
+static const int Method = 6;
+static const int Property = 7;
+static const int Field = 8;
+static const int Constructor = 9;
+static const int Enum = 10;
+static const int Interface = 11;
+static const int Function = 12;
+static const int Variable = 13;
+static const int Constant = 14;
+static const int String = 15;
+static const int Number = 16;
+static const int Boolean = 17;
+static const int Array = 18;
+static const int Object = 19;
+static const int Key = 20;
+static const int Null = 21;
+static const int EnumMember = 22;
+static const int Struct = 23;
+static const int Event = 24;
+static const int Operator = 25;
+static const int TypeParameter = 26;
}; // namespace SymbolKind
/**
@@ -1191,7 +1257,7 @@ struct DocumentSymbol {
void symbol_tree_as_list(const String &p_uri, Vector<DocumentedSymbolInformation> &r_list, const String &p_container = "", bool p_join_name = false) const {
DocumentedSymbolInformation si;
- if (p_join_name && !p_container.empty()) {
+ if (p_join_name && !p_container.is_empty()) {
si.name = p_container + ">" + name;
} else {
si.name = name;
@@ -1266,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;
@@ -1281,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
@@ -1341,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
@@ -1363,8 +1441,8 @@ static const int TriggerForIncompleteCompletions = 3;
*/
struct CompletionContext {
/**
- * How the completion was triggered.
- */
+ * How the completion was triggered.
+ */
int triggerKind = CompletionTriggerKind::TriggerCharacter;
/**
@@ -1528,6 +1606,114 @@ struct SignatureHelp {
}
};
+/**
+ * A pattern to describe in which file operation requests or notifications
+ * the server is interested in.
+ */
+struct FileOperationPattern {
+ /**
+ * The glob pattern to match.
+ */
+ String glob = "**/*.gd";
+
+ /**
+ * Whether to match `file`s or `folder`s with this pattern.
+ *
+ * Matches both if undefined.
+ */
+ String matches = "file";
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["glob"] = glob;
+ dict["matches"] = matches;
+
+ return dict;
+ }
+};
+
+/**
+ * A filter to describe in which file operation requests or notifications
+ * the server is interested in.
+ */
+struct FileOperationFilter {
+ /**
+ * The actual file operation pattern.
+ */
+ FileOperationPattern pattern;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["pattern"] = pattern.to_json();
+
+ return dict;
+ }
+};
+
+/**
+ * The options to register for file operations.
+ */
+struct FileOperationRegistrationOptions {
+ /**
+ * The actual filters.
+ */
+ Vector<FileOperationFilter> filters;
+
+ FileOperationRegistrationOptions() {
+ filters.push_back(FileOperationFilter());
+ }
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ Array filts;
+ for (int i = 0; i < filters.size(); i++) {
+ filts.push_back(filters[i].to_json());
+ }
+ dict["filters"] = filts;
+
+ return dict;
+ }
+};
+
+/**
+ * The server is interested in file notifications/requests.
+ */
+struct FileOperations {
+ /**
+ * The server is interested in receiving didDeleteFiles file notifications.
+ */
+ FileOperationRegistrationOptions didDelete;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["didDelete"] = didDelete.to_json();
+
+ return dict;
+ }
+};
+
+/**
+ * Workspace specific server capabilities
+ */
+struct Workspace {
+ /**
+ * The server is interested in file notifications/requests.
+ */
+ FileOperations fileOperations;
+
+ Dictionary to_json() const {
+ Dictionary dict;
+
+ dict["fileOperations"] = fileOperations.to_json();
+
+ return dict;
+ }
+};
+
struct ServerCapabilities {
/**
* Defines how text documents are synced. Is either a detailed structure defining each notification or
@@ -1590,6 +1776,11 @@ struct ServerCapabilities {
bool workspaceSymbolProvider = true;
/**
+ * The server supports workspace folder.
+ */
+ Workspace workspace;
+
+ /**
* The server provides code actions. The `CodeActionOptions` return type is only
* valid if the client signals code action literal support via the property
* `textDocument.codeAction.codeActionLiteralSupport`.
@@ -1661,7 +1852,7 @@ struct ServerCapabilities {
signatureHelpProvider.triggerCharacters.push_back(",");
signatureHelpProvider.triggerCharacters.push_back("(");
dict["signatureHelpProvider"] = signatureHelpProvider.to_json();
- dict["codeLensProvider"] = false; // codeLensProvider.to_json();
+ //dict["codeLensProvider"] = codeLensProvider.to_json();
dict["documentOnTypeFormattingProvider"] = documentOnTypeFormattingProvider.to_json();
dict["renameProvider"] = renameProvider.to_json();
dict["documentLinkProvider"] = documentLinkProvider.to_json();
@@ -1676,6 +1867,7 @@ struct ServerCapabilities {
dict["documentHighlightProvider"] = documentHighlightProvider;
dict["documentSymbolProvider"] = documentSymbolProvider;
dict["workspaceSymbolProvider"] = workspaceSymbolProvider;
+ dict["workspace"] = workspace.to_json();
dict["codeActionProvider"] = codeActionProvider;
dict["documentFormattingProvider"] = documentFormattingProvider;
dict["documentRangeFormattingProvider"] = documentRangeFormattingProvider;
@@ -1714,7 +1906,7 @@ struct GodotNativeClassInfo {
struct GodotCapabilities {
/**
* Native class list
- */
+ */
List<GodotNativeClassInfo> native_classes;
Dictionary to_json() {
@@ -1781,7 +1973,6 @@ static String marked_documentation(const String &p_bbcode) {
}
return markdown;
}
-
} // namespace lsp
#endif
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index da4cbe34c7..c2b1981f31 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,14 +30,15 @@
#include "register_types.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/io/file_access_encrypted.h"
#include "core/io/resource_loader.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
#include "gdscript.h"
#include "gdscript_analyzer.h"
#include "gdscript_cache.h"
#include "gdscript_tokenizer.h"
+#include "gdscript_utility_functions.h"
#ifdef TESTS_ENABLED
#include "tests/test_gdscript.h"
@@ -59,7 +60,7 @@ GDScriptCache *gdscript_cache = nullptr;
#include "editor/gdscript_translation_parser_plugin.h"
#ifndef GDSCRIPT_NO_LSP
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "language_server/gdscript_language_server.h"
#endif // !GDSCRIPT_NO_LSP
@@ -84,19 +85,19 @@ public:
return;
}
- // TODO: Readd compiled GDScript on export.
+ // TODO: Re-add compiled GDScript on export.
return;
}
};
static void _editor_init() {
Ref<EditorExportGDScript> gd_export;
- gd_export.instance();
+ gd_export.instantiate();
EditorExport::get_singleton()->add_export_plugin(gd_export);
#ifdef TOOLS_ENABLED
Ref<GDScriptSyntaxHighlighter> gdscript_syntax_highlighter;
- gdscript_syntax_highlighter.instance();
+ gdscript_syntax_highlighter.instantiate();
ScriptEditor::get_singleton()->register_syntax_highlighter(gdscript_syntax_highlighter);
#endif
@@ -111,16 +112,15 @@ static void _editor_init() {
#endif // TOOLS_ENABLED
void register_gdscript_types() {
- ClassDB::register_class<GDScript>();
- ClassDB::register_virtual_class<GDScriptFunctionState>();
+ GDREGISTER_CLASS(GDScript);
script_language_gd = memnew(GDScriptLanguage);
ScriptServer::register_language(script_language_gd);
- resource_loader_gd.instance();
+ resource_loader_gd.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_gd);
- resource_saver_gd.instance();
+ resource_saver_gd.instantiate();
ResourceSaver::add_resource_format_saver(resource_saver_gd);
gdscript_cache = memnew(GDScriptCache);
@@ -128,9 +128,11 @@ void register_gdscript_types() {
#ifdef TOOLS_ENABLED
EditorNode::add_init_callback(_editor_init);
- gdscript_translation_parser_plugin.instance();
+ gdscript_translation_parser_plugin.instantiate();
EditorTranslationParser::get_singleton()->add_parser(gdscript_translation_parser_plugin, EditorTranslationParser::STANDARD);
#endif // TOOLS_ENABLED
+
+ GDScriptUtilityFunctions::register_functions();
}
void unregister_gdscript_types() {
@@ -156,24 +158,24 @@ void unregister_gdscript_types() {
#endif // TOOLS_ENABLED
GDScriptParser::cleanup();
- GDScriptAnalyzer::cleanup();
+ GDScriptUtilityFunctions::unregister_functions();
}
#ifdef TESTS_ENABLED
void test_tokenizer() {
- TestGDScript::test(TestGDScript::TestType::TEST_TOKENIZER);
+ GDScriptTests::test(GDScriptTests::TestType::TEST_TOKENIZER);
}
void test_parser() {
- TestGDScript::test(TestGDScript::TestType::TEST_PARSER);
+ GDScriptTests::test(GDScriptTests::TestType::TEST_PARSER);
}
void test_compiler() {
- TestGDScript::test(TestGDScript::TestType::TEST_COMPILER);
+ GDScriptTests::test(GDScriptTests::TestType::TEST_COMPILER);
}
void test_bytecode() {
- TestGDScript::test(TestGDScript::TestType::TEST_BYTECODE);
+ GDScriptTests::test(GDScriptTests::TestType::TEST_BYTECODE);
}
REGISTER_TEST_COMMAND("gdscript-tokenizer", &test_tokenizer);
diff --git a/modules/gdscript/register_types.h b/modules/gdscript/register_types.h
index 18e57c1211..ce1c03d1d0 100644
--- a/modules/gdscript/register_types.h
+++ b/modules/gdscript/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdscript/tests/README.md b/modules/gdscript/tests/README.md
new file mode 100644
index 0000000000..6e54085962
--- /dev/null
+++ b/modules/gdscript/tests/README.md
@@ -0,0 +1,8 @@
+# GDScript integration tests
+
+The `scripts/` folder contains integration tests in the form of GDScript files
+and output files.
+
+See the
+[Integration tests for GDScript documentation](https://docs.godotengine.org/en/latest/development/cpp/unit_testing.html#integration-tests-for-gdscript)
+for information about creating and running GDScript integration tests.
diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp
new file mode 100644
index 0000000000..d2e71efee7
--- /dev/null
+++ b/modules/gdscript/tests/gdscript_test_runner.cpp
@@ -0,0 +1,588 @@
+/*************************************************************************/
+/* gdscript_test_runner.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 "gdscript_test_runner.h"
+
+#include "../gdscript.h"
+#include "../gdscript_analyzer.h"
+#include "../gdscript_compiler.h"
+#include "../gdscript_parser.h"
+
+#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access_pack.h"
+#include "core/os/os.h"
+#include "core/string/string_builder.h"
+#include "scene/resources/packed_scene.h"
+
+#include "tests/test_macros.h"
+
+namespace GDScriptTests {
+
+void init_autoloads() {
+ OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+
+ // First pass, add the constants so they exist before any script is loaded.
+ for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
+ const ProjectSettings::AutoloadInfo &info = E.get();
+
+ if (info.is_singleton) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_global_constant(info.name, Variant());
+ }
+ }
+ }
+
+ // Second pass, load into global constants.
+ for (OrderedHashMap<StringName, ProjectSettings::AutoloadInfo>::Element E = autoloads.front(); E; E = E.next()) {
+ const ProjectSettings::AutoloadInfo &info = E.get();
+
+ if (!info.is_singleton) {
+ // Skip non-singletons since we don't have a scene tree here anyway.
+ continue;
+ }
+
+ RES res = ResourceLoader::load(info.path);
+ ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path);
+ Node *n = nullptr;
+ if (res->is_class("PackedScene")) {
+ Ref<PackedScene> ps = res;
+ n = ps->instantiate();
+ } else if (res->is_class("Script")) {
+ Ref<Script> script_res = res;
+ StringName ibt = script_res->get_instance_base_type();
+ bool valid_type = ClassDB::is_parent_class(ibt, "Node");
+ ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path);
+
+ Object *obj = ClassDB::instantiate(ibt);
+
+ ERR_CONTINUE_MSG(obj == nullptr,
+ "Cannot instance script for autoload, expected 'Node' inheritance, got: " +
+ String(ibt));
+
+ n = Object::cast_to<Node>(obj);
+ n->set_script(script_res);
+ }
+
+ ERR_CONTINUE_MSG(!n, "Path in autoload not a node or script: " + info.path);
+ n->set_name(info.name);
+
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->add_global_constant(info.name, n);
+ }
+ }
+}
+
+void init_language(const String &p_base_path) {
+ // Setup project settings since it's needed by the languages to get the global scripts.
+ // This also sets up the base resource path.
+ Error err = ProjectSettings::get_singleton()->setup(p_base_path, String(), true);
+ if (err) {
+ print_line("Could not load project settings.");
+ // Keep going since some scripts still work without this.
+ }
+
+ // Initialize the language for the test routine.
+ GDScriptLanguage::get_singleton()->init();
+ init_autoloads();
+}
+
+void finish_language() {
+ GDScriptLanguage::get_singleton()->finish();
+ ScriptServer::global_classes_clear();
+}
+
+StringName GDScriptTestRunner::test_function_name;
+
+GDScriptTestRunner::GDScriptTestRunner(const String &p_source_dir, bool p_init_language) {
+ test_function_name = StaticCString::create("test");
+ do_init_languages = p_init_language;
+
+ source_dir = p_source_dir;
+ if (!source_dir.ends_with("/")) {
+ source_dir += "/";
+ }
+
+ 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 printing to show results
+ _print_line_enabled = true;
+ _print_error_enabled = true;
+}
+
+GDScriptTestRunner::~GDScriptTestRunner() {
+ test_function_name = StringName();
+ if (do_init_languages) {
+ finish_language();
+ }
+}
+
+int GDScriptTestRunner::run_tests() {
+ if (!make_tests()) {
+ FAIL("An error occurred while making the tests.");
+ return -1;
+ }
+
+ if (!generate_class_index()) {
+ FAIL("An error occurred while generating class index.");
+ return -1;
+ }
+
+ int failed = 0;
+ for (int i = 0; i < tests.size(); i++) {
+ GDScriptTest test = tests[i];
+ GDScriptTest::TestResult result = test.run_test();
+
+ String expected = FileAccess::get_file_as_string(test.get_output_file());
+ INFO(test.get_source_file());
+ if (!result.passed) {
+ INFO(expected);
+ failed++;
+ }
+
+ CHECK_MESSAGE(result.passed, (result.passed ? String() : result.output));
+ }
+
+ return failed;
+}
+
+bool GDScriptTestRunner::generate_outputs() {
+ is_generating = true;
+
+ if (!make_tests()) {
+ print_line("Failed to generate a test output.");
+ return false;
+ }
+
+ if (!generate_class_index()) {
+ return false;
+ }
+
+ for (int i = 0; i < tests.size(); i++) {
+ OS::get_singleton()->print(".");
+ GDScriptTest test = tests[i];
+ bool result = test.generate_output();
+
+ if (!result) {
+ print_line("\nCould not generate output for " + test.get_source_file());
+ return false;
+ }
+ }
+ print_line("\nGenerated output files for " + itos(tests.size()) + " tests successfully.");
+
+ return true;
+}
+
+bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) {
+ Error err = OK;
+ DirAccessRef dir(DirAccess::open(p_dir, &err));
+
+ if (err != OK) {
+ return false;
+ }
+
+ String current_dir = dir->get_current_dir();
+
+ dir->list_dir_begin();
+ String next = dir->get_next();
+
+ while (!next.is_empty()) {
+ if (dir->current_is_dir()) {
+ if (next == "." || next == "..") {
+ next = dir->get_next();
+ continue;
+ }
+ if (!make_tests_for_dir(current_dir.plus_file(next))) {
+ return false;
+ }
+ } else {
+ if (next.get_extension().to_lower() == "gd") {
+ String out_file = next.get_basename() + ".out";
+ if (!is_generating && !dir->file_exists(out_file)) {
+ ERR_FAIL_V_MSG(false, "Could not find output file for " + next);
+ }
+ GDScriptTest test(current_dir.plus_file(next), current_dir.plus_file(out_file), source_dir);
+ tests.push_back(test);
+ }
+ }
+
+ next = dir->get_next();
+ }
+
+ dir->list_dir_end();
+
+ return true;
+}
+
+bool GDScriptTestRunner::make_tests() {
+ Error err = OK;
+ DirAccessRef dir(DirAccess::open(source_dir, &err));
+
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Could not open specified test directory.");
+
+ source_dir = dir->get_current_dir() + "/"; // Make it absolute path.
+ return make_tests_for_dir(dir->get_current_dir());
+}
+
+bool GDScriptTestRunner::generate_class_index() {
+ StringName gdscript_name = GDScriptLanguage::get_singleton()->get_name();
+ for (int i = 0; i < tests.size(); i++) {
+ GDScriptTest test = tests[i];
+ String base_type;
+
+ String class_name = GDScriptLanguage::get_singleton()->get_global_class_name(test.get_source_file(), &base_type);
+ if (class_name == String()) {
+ continue;
+ }
+ ERR_FAIL_COND_V_MSG(ScriptServer::is_global_class(class_name), false,
+ "Class name '" + class_name + "' from " + test.get_source_file() + " is already used in " + ScriptServer::get_global_class_path(class_name));
+
+ ScriptServer::add_global_class(class_name, base_type, gdscript_name, test.get_source_file());
+ }
+ return true;
+}
+
+GDScriptTest::GDScriptTest(const String &p_source_path, const String &p_output_path, const String &p_base_dir) {
+ source_file = p_source_path;
+ output_file = p_output_path;
+ base_dir = p_base_dir;
+ _print_handler.printfunc = print_handler;
+ _error_handler.errfunc = error_handler;
+}
+
+void GDScriptTestRunner::handle_cmdline() {
+ List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
+ // TODO: this could likely be ported to use test commands:
+ // https://github.com/godotengine/godot/pull/41355
+ // Currently requires to startup the whole engine, which is slow.
+ String test_cmd = "--gdscript-test";
+ String gen_cmd = "--gdscript-generate-tests";
+
+ for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
+ String &cmd = E->get();
+ if (cmd == test_cmd || cmd == gen_cmd) {
+ if (E->next() == nullptr) {
+ ERR_PRINT("Needed a path for the test files.");
+ exit(-1);
+ }
+
+ const String &path = E->next()->get();
+
+ GDScriptTestRunner runner(path, false);
+ int failed = 0;
+ if (cmd == test_cmd) {
+ failed = runner.run_tests();
+ } else {
+ bool completed = runner.generate_outputs();
+ failed = completed ? 0 : -1;
+ }
+ exit(failed);
+ }
+ }
+}
+
+void GDScriptTest::enable_stdout() {
+ // TODO: this could likely be handled by doctest or `tests/test_macros.h`.
+ OS::get_singleton()->set_stdout_enabled(true);
+ OS::get_singleton()->set_stderr_enabled(true);
+}
+
+void GDScriptTest::disable_stdout() {
+ // TODO: this could likely be handled by doctest or `tests/test_macros.h`.
+ OS::get_singleton()->set_stdout_enabled(false);
+ OS::get_singleton()->set_stderr_enabled(false);
+}
+
+void GDScriptTest::print_handler(void *p_this, const String &p_message, bool p_error) {
+ TestResult *result = (TestResult *)p_this;
+ 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, bool p_editor_notify, ErrorHandlerType p_type) {
+ ErrorHandlerData *data = (ErrorHandlerData *)p_this;
+ GDScriptTest *self = data->self;
+ TestResult *result = data->result;
+
+ result->status = GDTEST_RUNTIME_ERROR;
+
+ StringBuilder builder;
+ builder.append(">> ");
+ switch (p_type) {
+ case ERR_HANDLER_ERROR:
+ builder.append("ERROR");
+ break;
+ case ERR_HANDLER_WARNING:
+ builder.append("WARNING");
+ break;
+ case ERR_HANDLER_SCRIPT:
+ builder.append("SCRIPT ERROR");
+ break;
+ case ERR_HANDLER_SHADER:
+ builder.append("SHADER ERROR");
+ break;
+ default:
+ builder.append("Unknown error type");
+ break;
+ }
+
+ builder.append("\n>> on function: ");
+ builder.append(p_function);
+ builder.append("()\n>> ");
+ builder.append(String(p_file).trim_prefix(self->base_dir));
+ builder.append("\n>> ");
+ builder.append(itos(p_line));
+ builder.append("\n>> ");
+ builder.append(p_error);
+ if (strlen(p_explanation) > 0) {
+ builder.append("\n>> ");
+ builder.append(p_explanation);
+ }
+ builder.append("\n");
+
+ result->output = builder.as_string();
+}
+
+bool GDScriptTest::check_output(const String &p_output) const {
+ Error err = OK;
+ String expected = FileAccess::get_file_as_string(output_file, &err);
+
+ ERR_FAIL_COND_V_MSG(err != OK, false, "Error when opening the output file.");
+
+ String got = p_output.strip_edges(); // TODO: may be hacky.
+ got += "\n"; // Make sure to insert newline for CI static checks.
+
+ return got == expected;
+}
+
+String GDScriptTest::get_text_for_status(GDScriptTest::TestStatus p_status) const {
+ switch (p_status) {
+ case GDTEST_OK:
+ return "GDTEST_OK";
+ case GDTEST_LOAD_ERROR:
+ return "GDTEST_LOAD_ERROR";
+ case GDTEST_PARSER_ERROR:
+ return "GDTEST_PARSER_ERROR";
+ case GDTEST_ANALYZER_ERROR:
+ return "GDTEST_ANALYZER_ERROR";
+ case GDTEST_COMPILER_ERROR:
+ return "GDTEST_COMPILER_ERROR";
+ case GDTEST_RUNTIME_ERROR:
+ return "GDTEST_RUNTIME_ERROR";
+ }
+ return "";
+}
+
+GDScriptTest::TestResult GDScriptTest::execute_test_code(bool p_is_generating) {
+ disable_stdout();
+
+ TestResult result;
+ result.status = GDTEST_OK;
+ result.output = String();
+ result.passed = false;
+
+ Error err = OK;
+
+ // Create script.
+ Ref<GDScript> script;
+ script.instantiate();
+ script->set_path(source_file);
+ script->set_script_path(source_file);
+ err = script->load_source_code(source_file);
+ if (err != OK) {
+ enable_stdout();
+ result.status = GDTEST_LOAD_ERROR;
+ result.passed = false;
+ ERR_FAIL_V_MSG(result, "\nCould not load source code for: '" + source_file + "'");
+ }
+
+ // Test parsing.
+ GDScriptParser parser;
+ err = parser.parse(script->get_source_code(), source_file, false);
+ if (err != OK) {
+ enable_stdout();
+ result.status = GDTEST_PARSER_ERROR;
+ result.output = get_text_for_status(result.status) + "\n";
+
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (const GDScriptParser::ParserError &E : errors) {
+ result.output += E.message + "\n"; // TODO: line, column?
+ break; // Only the first error since the following might be cascading.
+ }
+ if (!p_is_generating) {
+ result.passed = check_output(result.output);
+ }
+ return result;
+ }
+
+ // Test type-checking.
+ GDScriptAnalyzer analyzer(&parser);
+ err = analyzer.analyze();
+ if (err != OK) {
+ enable_stdout();
+ result.status = GDTEST_ANALYZER_ERROR;
+ result.output = get_text_for_status(result.status) + "\n";
+
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (const GDScriptParser::ParserError &E : errors) {
+ result.output += E.message + "\n"; // TODO: line, column?
+ break; // Only the first error since the following might be cascading.
+ }
+ if (!p_is_generating) {
+ result.passed = check_output(result.output);
+ }
+ return result;
+ }
+
+ StringBuilder warning_string;
+ for (const GDScriptWarning &E : parser.get_warnings()) {
+ const GDScriptWarning warning = E;
+ warning_string.append(">> WARNING");
+ warning_string.append("\n>> Line: ");
+ warning_string.append(itos(warning.start_line));
+ warning_string.append("\n>> ");
+ warning_string.append(warning.get_name());
+ warning_string.append("\n>> ");
+ warning_string.append(warning.get_message());
+ warning_string.append("\n");
+ }
+ result.output += warning_string.as_string();
+
+ // Test compiling.
+ GDScriptCompiler compiler;
+ err = compiler.compile(&parser, script.ptr(), false);
+ if (err != OK) {
+ enable_stdout();
+ result.status = GDTEST_COMPILER_ERROR;
+ result.output = get_text_for_status(result.status) + "\n";
+ result.output = compiler.get_error();
+ if (!p_is_generating) {
+ result.passed = check_output(result.output);
+ }
+ 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) {
+ enable_stdout();
+ result.status = GDTEST_LOAD_ERROR;
+ result.output = "";
+ result.passed = false;
+ ERR_FAIL_V_MSG(result, "\nCould not find test function on: '" + source_file + "'");
+ }
+
+ script->reload();
+
+ // Create object instance for test.
+ Object *obj = ClassDB::instantiate(script->get_native()->get_name());
+ Ref<RefCounted> obj_ref;
+ if (obj->is_ref_counted()) {
+ obj_ref = Ref<RefCounted>(Object::cast_to<RefCounted>(obj));
+ }
+ obj->set_script(script);
+ GDScriptInstance *instance = static_cast<GDScriptInstance *>(obj->get_script_instance());
+
+ // Setup output handlers.
+ ErrorHandlerData error_data(&result, this);
+
+ _print_handler.userdata = &result;
+ _error_handler.userdata = &error_data;
+ add_print_handler(&_print_handler);
+ add_error_handler(&_error_handler);
+
+ // Call test function.
+ Callable::CallError call_err;
+ instance->call(GDScriptTestRunner::test_function_name, nullptr, 0, call_err);
+
+ // Tear down output handlers.
+ remove_print_handler(&_print_handler);
+ remove_error_handler(&_error_handler);
+
+ // Check results.
+ if (call_err.error != Callable::CallError::CALL_OK) {
+ enable_stdout();
+ result.status = GDTEST_LOAD_ERROR;
+ result.passed = false;
+ ERR_FAIL_V_MSG(result, "\nCould not call test function on: '" + source_file + "'");
+ }
+
+ result.output = get_text_for_status(result.status) + "\n" + result.output;
+ if (!p_is_generating) {
+ result.passed = check_output(result.output);
+ }
+
+ if (obj_ref.is_null()) {
+ memdelete(obj);
+ }
+
+ enable_stdout();
+ return result;
+}
+
+GDScriptTest::TestResult GDScriptTest::run_test() {
+ return execute_test_code(false);
+}
+
+bool GDScriptTest::generate_output() {
+ TestResult result = execute_test_code(true);
+ if (result.status == GDTEST_LOAD_ERROR) {
+ return false;
+ }
+
+ Error err = OK;
+ FileAccessRef out_file = FileAccess::open(output_file, FileAccess::WRITE, &err);
+ if (err != OK) {
+ return false;
+ }
+
+ String output = result.output.strip_edges(); // TODO: may be hacky.
+ output += "\n"; // Make sure to insert newline for CI static checks.
+
+ out_file->store_string(output);
+ out_file->close();
+
+ return true;
+}
+
+} // namespace GDScriptTests
diff --git a/modules/gdscript/tests/gdscript_test_runner.h b/modules/gdscript/tests/gdscript_test_runner.h
new file mode 100644
index 0000000000..98c57dc97c
--- /dev/null
+++ b/modules/gdscript/tests/gdscript_test_runner.h
@@ -0,0 +1,126 @@
+/*************************************************************************/
+/* gdscript_test_runner.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 GDSCRIPT_TEST_H
+#define GDSCRIPT_TEST_H
+
+#include "../gdscript.h"
+#include "core/error/error_macros.h"
+#include "core/string/print_string.h"
+#include "core/string/ustring.h"
+#include "core/templates/vector.h"
+
+namespace GDScriptTests {
+
+void init_autoloads();
+void init_language(const String &p_base_path);
+void finish_language();
+
+// Single test instance in a suite.
+class GDScriptTest {
+public:
+ enum TestStatus {
+ GDTEST_OK,
+ GDTEST_LOAD_ERROR,
+ GDTEST_PARSER_ERROR,
+ GDTEST_ANALYZER_ERROR,
+ GDTEST_COMPILER_ERROR,
+ GDTEST_RUNTIME_ERROR,
+ };
+
+ struct TestResult {
+ TestStatus status;
+ String output;
+ bool passed;
+ };
+
+private:
+ struct ErrorHandlerData {
+ TestResult *result;
+ GDScriptTest *self;
+ ErrorHandlerData(TestResult *p_result, GDScriptTest *p_this) {
+ result = p_result;
+ self = p_this;
+ }
+ };
+
+ String source_file;
+ String output_file;
+ String base_dir;
+
+ PrintHandlerList _print_handler;
+ ErrorHandlerList _error_handler;
+
+ void enable_stdout();
+ void disable_stdout();
+ bool check_output(const String &p_output) const;
+ String get_text_for_status(TestStatus p_status) const;
+
+ TestResult execute_test_code(bool p_is_generating);
+
+public:
+ static void print_handler(void *p_this, const String &p_message, bool p_error);
+ static void 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();
+
+ const String &get_source_file() const { return source_file; }
+ const String &get_output_file() const { return output_file; }
+
+ GDScriptTest(const String &p_source_path, const String &p_output_path, const String &p_base_dir);
+ GDScriptTest() :
+ GDScriptTest(String(), String(), String()) {} // Needed to use in Vector.
+};
+
+class GDScriptTestRunner {
+ String source_dir;
+ Vector<GDScriptTest> tests;
+
+ bool is_generating = false;
+ bool do_init_languages = false;
+
+ bool make_tests();
+ bool make_tests_for_dir(const String &p_dir);
+ bool generate_class_index();
+
+public:
+ static StringName test_function_name;
+
+ static void handle_cmdline();
+ int run_tests();
+ bool generate_outputs();
+
+ GDScriptTestRunner(const String &p_source_dir, bool p_init_language);
+ ~GDScriptTestRunner();
+};
+
+} // namespace GDScriptTests
+
+#endif // GDSCRIPT_TEST_H
diff --git a/modules/gdscript/tests/gdscript_test_runner_suite.h b/modules/gdscript/tests/gdscript_test_runner_suite.h
new file mode 100644
index 0000000000..cf4e61f07d
--- /dev/null
+++ b/modules/gdscript/tests/gdscript_test_runner_suite.h
@@ -0,0 +1,74 @@
+/*************************************************************************/
+/* gdscript_test_runner_suite.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 GDSCRIPT_TEST_RUNNER_SUITE_H
+#define GDSCRIPT_TEST_RUNNER_SUITE_H
+
+#include "gdscript_test_runner.h"
+#include "tests/test_macros.h"
+
+namespace GDScriptTests {
+
+TEST_SUITE("[Modules][GDScript]") {
+ // GDScript 2.0 is still under heavy construction.
+ // Allow the tests to fail, but do not ignore errors during development.
+ // Update the scripts and expected output as needed.
+ TEST_CASE("Script compilation and runtime") {
+ GDScriptTestRunner runner("modules/gdscript/tests/scripts", true);
+ int fail_count = runner.run_tests();
+ INFO("Make sure `*.out` files have expected results.");
+ REQUIRE_MESSAGE(fail_count == 0, "All GDScript tests should pass.");
+ }
+}
+
+TEST_CASE("[Modules][GDScript] Load source code dynamically and run it") {
+ Ref<GDScript> gdscript = memnew(GDScript);
+ gdscript->set_source_code(R"(
+extends RefCounted
+
+func _init():
+ set_meta("result", 42)
+)");
+ // A spurious `Condition "err" is true` message is printed (despite parsing being successful and returning `OK`).
+ // Silence it.
+ ERR_PRINT_OFF;
+ const Error error = gdscript->reload();
+ ERR_PRINT_ON;
+ CHECK_MESSAGE(error == OK, "The script should parse successfully.");
+
+ // Run the script by assigning it to a reference-counted object.
+ Ref<RefCounted> ref_counted = memnew(RefCounted);
+ ref_counted->set_script(gdscript);
+ CHECK_MESSAGE(int(ref_counted->get_meta("result")) == 42, "The script should assign object metadata successfully.");
+}
+
+} // namespace GDScriptTests
+
+#endif // GDSCRIPT_TEST_RUNNER_SUITE_H
diff --git a/modules/gdscript/tests/scripts/.gitignore b/modules/gdscript/tests/scripts/.gitignore
new file mode 100644
index 0000000000..94c5b1bf6b
--- /dev/null
+++ b/modules/gdscript/tests/scripts/.gitignore
@@ -0,0 +1,2 @@
+# Ignore metadata if someone open this on Godot.
+/.godot
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/analyzer/errors/missing_argument.out b/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.out
new file mode 100644
index 0000000000..fc2a891109
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/errors/missing_argument.out
@@ -0,0 +1,2 @@
+GDTEST_ANALYZER_ERROR
+Too few arguments for "args()" call. Expected at least 2 but received 1.
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/call_self_get_name.gd b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.gd
new file mode 100644
index 0000000000..d21d8bce96
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.gd
@@ -0,0 +1,9 @@
+extends Node
+
+func test():
+ set_name("TestNodeName")
+ if get_name() == &"TestNodeName":
+ print("Name is equal")
+ else:
+ print("Name is not equal")
+ print(get_name() is StringName)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out
new file mode 100644
index 0000000000..2c8cc9c03f
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+Name is equal
+true
diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.gd b/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.gd
new file mode 100644
index 0000000000..ac66b78220
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.gd
@@ -0,0 +1,3 @@
+func test():
+ print(Color.html_is_valid("00ffff"))
+ print("OK")
diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.out b/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.out
new file mode 100644
index 0000000000..ad6beeb646
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/call_static_builtin_function.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+true
+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/gdscript_to_preload.gd b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd
new file mode 100644
index 0000000000..fb0ace6a90
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.gd
@@ -0,0 +1,5 @@
+func test():
+ pass
+
+func something():
+ return "OK"
diff --git a/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out
new file mode 100644
index 0000000000..d73c5eb7cd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/gdscript_to_preload.out
@@ -0,0 +1 @@
+GDTEST_OK
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.gd b/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.gd
new file mode 100644
index 0000000000..4f4b7a4897
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.gd
@@ -0,0 +1,11 @@
+class InnerClass:
+ var val := "OK"
+ static func create_instance() -> InnerClass:
+ return new()
+
+func create_inner_instance() -> InnerClass:
+ return InnerClass.create_instance()
+
+func test():
+ var instance = create_inner_instance()
+ print(instance.val)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.out b/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.out
new file mode 100644
index 0000000000..1ccb591560
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/inner_class_as_return_type.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+OK
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/analyzer/features/use_preload_script_as_type.gd b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd
new file mode 100644
index 0000000000..5f73064cc0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.gd
@@ -0,0 +1,5 @@
+const preloaded : GDScript = preload("gdscript_to_preload.gd")
+
+func test():
+ var preloaded_instance: preloaded = preloaded.new()
+ print(preloaded_instance.something())
diff --git a/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.out b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.out
new file mode 100644
index 0000000000..1ccb591560
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/use_preload_script_as_type.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+OK
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_empty_assignee.gd b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.gd
new file mode 100644
index 0000000000..17c65ad60a
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.gd
@@ -0,0 +1,3 @@
+func test():
+ var a = 0
+ a =
diff --git a/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.out b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.out
new file mode 100644
index 0000000000..1369a7a0dc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/assignment_empty_assignee.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected an expression after "=".
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_closing_expr_paren.gd b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd
new file mode 100644
index 0000000000..8af5f123cc
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.gd
@@ -0,0 +1,2 @@
+func test():
+ var a = ("missing paren ->"
diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.out b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.out
new file mode 100644
index 0000000000..7326afa33d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_closing_expr_paren.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected closing ")" after grouping expression.
diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd b/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd
new file mode 100644
index 0000000000..0e5e5ce060
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_colon.gd
@@ -0,0 +1,3 @@
+func test():
+ if true # Missing colon here.
+ print("true")
diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_colon.out b/modules/gdscript/tests/scripts/parser/errors/missing_colon.out
new file mode 100644
index 0000000000..687b963bc8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_colon.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected ":" after "if" condition.
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
new file mode 100644
index 0000000000..7a35bf688c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.gd
@@ -0,0 +1,6 @@
+func args(a, b):
+ print(a)
+ print(b)
+
+func test():
+ args(1,2
diff --git a/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.out b/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.out
new file mode 100644
index 0000000000..34ea7ac323
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/missing_paren_after_args.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expected closing ")" after call arguments.
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.gd b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.gd
new file mode 100644
index 0000000000..9ad77f1432
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.gd
@@ -0,0 +1,3 @@
+func test():
+ print("Using spaces")
+ print("Using tabs")
diff --git a/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out
new file mode 100644
index 0000000000..31bed2dbc7
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/mixing_tabs_spaces.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+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
new file mode 100644
index 0000000000..df388a21de
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.gd
@@ -0,0 +1,5 @@
+extends Node
+
+
+func test():
+ var a = $ # Expected some node path.
diff --git a/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out
new file mode 100644
index 0000000000..b3dc181a22
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/nothing_after_dollar.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expect node path as string or identifier after "$".
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
new file mode 100644
index 0000000000..babe39068c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.gd
@@ -0,0 +1,5 @@
+extends Node
+
+
+func test():
+ $23 # Can't use number here.
diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out
new file mode 100644
index 0000000000..b3dc181a22
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expect node path as string or identifier after "$".
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
new file mode 100644
index 0000000000..b6b1cf3e52
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.gd
@@ -0,0 +1,5 @@
+extends Node
+
+
+func test():
+ $MyNode/23 # Can't use number here.
diff --git a/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out
new file mode 100644
index 0000000000..dcb4ccecb0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/errors/wrong_value_after_dollar_slash.out
@@ -0,0 +1,2 @@
+GDTEST_PARSER_ERROR
+Expect node path after "/".
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
new file mode 100644
index 0000000000..d50776c25c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.gd
@@ -0,0 +1,5 @@
+func test():
+ 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
new file mode 100644
index 0000000000..bd7f38f516
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/semicolon_as_end_statement.out
@@ -0,0 +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/str_preserves_case.gd b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.gd
new file mode 100644
index 0000000000..9e48a7f0da
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.gd
@@ -0,0 +1,7 @@
+func test():
+ var null_var = null
+ var true_var: bool = true
+ var false_var: bool = false
+ print(str(null_var))
+ print(str(true_var))
+ print(str(false_var))
diff --git a/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out
new file mode 100644
index 0000000000..abba38e87c
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/str_preserves_case.out
@@ -0,0 +1,4 @@
+GDTEST_OK
+null
+true
+false
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/trailing_comma_in_function_args.gd b/modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.gd
new file mode 100644
index 0000000000..6097b11b10
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.gd
@@ -0,0 +1,7 @@
+# See https://github.com/godotengine/godot/issues/41066.
+
+func f(p, ): ## <-- no errors
+ print(p)
+
+func test():
+ f(0, ) ## <-- no error
diff --git a/modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.out b/modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.out
new file mode 100644
index 0000000000..94e2ec2af8
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/trailing_comma_in_function_args.out
@@ -0,0 +1,2 @@
+GDTEST_OK
+0
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
new file mode 100644
index 0000000000..65013c4301
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/variable_declaration.gd
@@ -0,0 +1,20 @@
+var m1 # No init.
+var m2 = 22 # Init.
+var m3: String # No init, typed.
+var m4: String = "44" # Init, typed.
+
+
+func test():
+ 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"
+
+ loc5 = 55
+ loc7 = "77"
+
+ 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
new file mode 100644
index 0000000000..7817dd3169
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/features/variable_declaration.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+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_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
new file mode 100644
index 0000000000..013a2e4beb
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.gd
@@ -0,0 +1,4 @@
+func test():
+ 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/unused_variable.out b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.out
new file mode 100644
index 0000000000..270e0e69c0
--- /dev/null
+++ b/modules/gdscript/tests/scripts/parser/warnings/unused_variable.out
@@ -0,0 +1,5 @@
+GDTEST_OK
+>> WARNING
+>> Line: 2
+>> UNUSED_VARIABLE
+>> The local variable 'unused' is declared but never used in the block. If this is intended, prefix it with an underscore: '_unused'
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/project.godot b/modules/gdscript/tests/scripts/project.godot
new file mode 100644
index 0000000000..25b49c0abd
--- /dev/null
+++ b/modules/gdscript/tests/scripts/project.godot
@@ -0,0 +1,10 @@
+; This is not an actual project.
+; This config only exists to properly set up the test environment.
+; It also helps for opening Godot to edit the scripts, but please don't
+; let the editor changes be saved.
+
+config_version=4
+
+[application]
+
+config/name="GDScript Integration Test Suite"
diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
new file mode 100644
index 0000000000..10780b5379
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.gd
@@ -0,0 +1,5 @@
+func test():
+ var node := Node.new()
+ var inside_tree = node.is_inside_tree
+ node.free()
+ inside_tree.call()
diff --git a/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
new file mode 100644
index 0000000000..e585c374e2
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/errors/callable_call_after_free_object.out
@@ -0,0 +1,6 @@
+GDTEST_RUNTIME_ERROR
+>> SCRIPT ERROR
+>> on function: test()
+>> runtime/errors/callable_call_after_free_object.gd
+>> 5
+>> Attempt to call function 'null::is_inside_tree (Callable)' on a null instance.
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 68d9984b43..80eabc1596 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,10 +30,13 @@
#include "test_gdscript.h"
-#include "core/os/file_access.h"
+#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
+#include "core/io/file_access_pack.h"
#include "core/os/main_loop.h"
#include "core/os/os.h"
-#include "core/string_builder.h"
+#include "core/string/string_builder.h"
+#include "scene/resources/packed_scene.h"
#include "modules/gdscript/gdscript_analyzer.h"
#include "modules/gdscript/gdscript_compiler.h"
@@ -44,7 +47,7 @@
#include "editor/editor_settings.h"
#endif
-namespace TestGDScript {
+namespace GDScriptTests {
static void test_tokenizer(const String &p_code, const Vector<String> &p_lines) {
GDScriptTokenizer tokenizer;
@@ -53,7 +56,7 @@ static void test_tokenizer(const String &p_code, const Vector<String> &p_lines)
int tab_size = 4;
#ifdef TOOLS_ENABLED
if (EditorSettings::get_singleton()) {
- tab_size = EditorSettings::get_singleton()->get_setting("text_editor/indent/size");
+ tab_size = EditorSettings::get_singleton()->get_setting("text_editor/behavior/indent/size");
}
#endif // TOOLS_ENABLED
String tab = String(" ").repeat(tab_size);
@@ -63,7 +66,7 @@ static void test_tokenizer(const String &p_code, const Vector<String> &p_lines)
StringBuilder token;
token += " --> "; // Padding for line number.
- for (int l = current.start_line; l <= current.end_line; l++) {
+ for (int l = current.start_line; l <= current.end_line && l <= p_lines.size(); l++) {
print_line(vformat("%04d %s", l, p_lines[l - 1]).replace("\t", tab));
}
@@ -110,15 +113,25 @@ static void test_parser(const String &p_code, const String &p_script_path, const
if (err != OK) {
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
- for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
- const GDScriptParser::ParserError &error = E->get();
+ for (const GDScriptParser::ParserError &error : errors) {
print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
}
}
- GDScriptParser::TreePrinter printer;
+ GDScriptAnalyzer analyzer(&parser);
+ analyzer.analyze();
+ if (err != OK) {
+ const List<GDScriptParser::ParserError> &errors = parser.get_errors();
+ for (const GDScriptParser::ParserError &error : errors) {
+ print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
+ }
+ }
+
+#ifdef TOOLS_ENABLED
+ GDScriptParser::TreePrinter printer;
printer.print_tree(parser);
+#endif
}
static void test_compiler(const String &p_code, const String &p_script_path, const Vector<String> &p_lines) {
@@ -128,8 +141,7 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
if (err != OK) {
print_line("Error in parser:");
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
- for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
- const GDScriptParser::ParserError &error = E->get();
+ for (const GDScriptParser::ParserError &error : errors) {
print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
}
return;
@@ -141,8 +153,7 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
if (err != OK) {
print_line("Error in analyzer:");
const List<GDScriptParser::ParserError> &errors = parser.get_errors();
- for (const List<GDScriptParser::ParserError>::Element *E = errors.front(); E != nullptr; E = E->next()) {
- const GDScriptParser::ParserError &error = E->get();
+ for (const GDScriptParser::ParserError &error : errors) {
print_line(vformat("%02d:%02d: %s", error.line, error.column, error.message));
}
return;
@@ -150,7 +161,7 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
GDScriptCompiler compiler;
Ref<GDScript> script;
- script.instance();
+ script.instantiate();
script->set_path(p_script_path);
err = compiler.compile(&parser, script.ptr(), false);
@@ -161,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++) {
@@ -172,8 +183,9 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
signature += func->get_argument_name(i);
}
print_line(signature + ")");
-
+#ifdef TOOLS_ENABLED
func->disassemble(p_lines);
+#endif
print_line("");
print_line("");
}
@@ -182,7 +194,7 @@ static void test_compiler(const String &p_code, const String &p_script_path, con
void test(TestType p_type) {
List<String> cmdlargs = OS::get_singleton()->get_cmdline_args();
- if (cmdlargs.empty()) {
+ if (cmdlargs.is_empty()) {
return;
}
@@ -195,9 +207,12 @@ void test(TestType p_type) {
FileAccessRef fa = FileAccess::open(test, FileAccess::READ);
ERR_FAIL_COND_MSG(!fa, "Could not open file: " + test);
+ // Initialize the language for the test routine.
+ init_language(fa->get_path_absolute().get_base_dir());
+
Vector<uint8_t> buf;
- int flen = fa->get_len();
- buf.resize(fa->get_len() + 1);
+ uint64_t flen = fa->get_length();
+ buf.resize(flen + 1);
fa->get_buffer(buf.ptrw(), flen);
buf.write[flen] = 0;
@@ -226,6 +241,7 @@ void test(TestType p_type) {
case TEST_BYTECODE:
print_line("Not implemented.");
}
-}
-} // namespace TestGDScript
+ finish_language();
+}
+} // namespace GDScriptTests
diff --git a/modules/gdscript/tests/test_gdscript.h b/modules/gdscript/tests/test_gdscript.h
index 5aa962dcf8..c7ee5a2208 100644
--- a/modules/gdscript/tests/test_gdscript.h
+++ b/modules/gdscript/tests/test_gdscript.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,10 @@
#ifndef TEST_GDSCRIPT_H
#define TEST_GDSCRIPT_H
-namespace TestGDScript {
+#include "gdscript_test_runner.h"
+#include "tests/test_macros.h"
+
+namespace GDScriptTests {
enum TestType {
TEST_TOKENIZER,
@@ -42,6 +45,6 @@ enum TestType {
void test(TestType p_type);
-} // namespace TestGDScript
+} // namespace GDScriptTests
#endif // TEST_GDSCRIPT_H
diff --git a/modules/glslang/SCsub b/modules/glslang/SCsub
index c1d23a138b..1954a32697 100644
--- a/modules/glslang/SCsub
+++ b/modules/glslang/SCsub
@@ -6,49 +6,55 @@ Import("env_modules")
env_glslang = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_glslang"]:
thirdparty_dir = "#thirdparty/glslang/"
thirdparty_sources = [
- "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/CInterface/glslang_c_interface.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/pch.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/InReadableOrder.cpp",
- "SPIRV/GlslangToSpv.cpp",
- "SPIRV/SpvBuilder.cpp",
- "SPIRV/SpvTools.cpp",
+ "SPIRV/CInterface/spirv_c_interface.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",
]
if env["platform"] == "windows":
@@ -64,9 +70,20 @@ if env["builtin_glslang"]:
else:
env_glslang.Prepend(CPPPATH=[thirdparty_dir])
+ env_glslang.Append(CPPDEFINES=["ENABLE_OPT=0"])
+
env_thirdparty = env_glslang.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
+
+module_obj = []
+
+env_glslang.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Godot's own source files
-env_glslang.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp
index dd28c4ad4a..dd545ff431 100644
--- a/modules/glslang/register_types.cpp
+++ b/modules/glslang/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,116 +33,11 @@
#include "servers/rendering/rendering_device.h"
#include <SPIRV/GlslangToSpv.h>
+#include <StandAlone/ResourceLimits.h>
#include <glslang/Include/Types.h>
#include <glslang/Public/ShaderLang.h>
-static const TBuiltInResource default_builtin_resource = {
- /*maxLights*/ 32,
- /*maxClipPlanes*/ 6,
- /*maxTextureUnits*/ 32,
- /*maxTextureCoords*/ 32,
- /*maxVertexAttribs*/ 64,
- /*maxVertexUniformComponents*/ 4096,
- /*maxVaryingFloats*/ 64,
- /*maxVertexTextureImageUnits*/ 32,
- /*maxCombinedTextureImageUnits*/ 80,
- /*maxTextureImageUnits*/ 32,
- /*maxFragmentUniformComponents*/ 4096,
- /*maxDrawBuffers*/ 32,
- /*maxVertexUniformVectors*/ 128,
- /*maxVaryingVectors*/ 8,
- /*maxFragmentUniformVectors*/ 16,
- /*maxVertexOutputVectors*/ 16,
- /*maxFragmentInputVectors*/ 15,
- /*minProgramTexelOffset*/ -8,
- /*maxProgramTexelOffset*/ 7,
- /*maxClipDistances*/ 8,
- /*maxComputeWorkGroupCountX*/ 65535,
- /*maxComputeWorkGroupCountY*/ 65535,
- /*maxComputeWorkGroupCountZ*/ 65535,
- /*maxComputeWorkGroupSizeX*/ 1024,
- /*maxComputeWorkGroupSizeY*/ 1024,
- /*maxComputeWorkGroupSizeZ*/ 64,
- /*maxComputeUniformComponents*/ 1024,
- /*maxComputeTextureImageUnits*/ 16,
- /*maxComputeImageUniforms*/ 8,
- /*maxComputeAtomicCounters*/ 8,
- /*maxComputeAtomicCounterBuffers*/ 1,
- /*maxVaryingComponents*/ 60,
- /*maxVertexOutputComponents*/ 64,
- /*maxGeometryInputComponents*/ 64,
- /*maxGeometryOutputComponents*/ 128,
- /*maxFragmentInputComponents*/ 128,
- /*maxImageUnits*/ 8,
- /*maxCombinedImageUnitsAndFragmentOutputs*/ 8,
- /*maxCombinedShaderOutputResources*/ 8,
- /*maxImageSamples*/ 0,
- /*maxVertexImageUniforms*/ 0,
- /*maxTessControlImageUniforms*/ 0,
- /*maxTessEvaluationImageUniforms*/ 0,
- /*maxGeometryImageUniforms*/ 0,
- /*maxFragmentImageUniforms*/ 8,
- /*maxCombinedImageUniforms*/ 8,
- /*maxGeometryTextureImageUnits*/ 16,
- /*maxGeometryOutputVertices*/ 256,
- /*maxGeometryTotalOutputComponents*/ 1024,
- /*maxGeometryUniformComponents*/ 1024,
- /*maxGeometryVaryingComponents*/ 64,
- /*maxTessControlInputComponents*/ 128,
- /*maxTessControlOutputComponents*/ 128,
- /*maxTessControlTextureImageUnits*/ 16,
- /*maxTessControlUniformComponents*/ 1024,
- /*maxTessControlTotalOutputComponents*/ 4096,
- /*maxTessEvaluationInputComponents*/ 128,
- /*maxTessEvaluationOutputComponents*/ 128,
- /*maxTessEvaluationTextureImageUnits*/ 16,
- /*maxTessEvaluationUniformComponents*/ 1024,
- /*maxTessPatchComponents*/ 120,
- /*maxPatchVertices*/ 32,
- /*maxTessGenLevel*/ 64,
- /*maxViewports*/ 16,
- /*maxVertexAtomicCounters*/ 0,
- /*maxTessControlAtomicCounters*/ 0,
- /*maxTessEvaluationAtomicCounters*/ 0,
- /*maxGeometryAtomicCounters*/ 0,
- /*maxFragmentAtomicCounters*/ 8,
- /*maxCombinedAtomicCounters*/ 8,
- /*maxAtomicCounterBindings*/ 1,
- /*maxVertexAtomicCounterBuffers*/ 0,
- /*maxTessControlAtomicCounterBuffers*/ 0,
- /*maxTessEvaluationAtomicCounterBuffers*/ 0,
- /*maxGeometryAtomicCounterBuffers*/ 0,
- /*maxFragmentAtomicCounterBuffers*/ 1,
- /*maxCombinedAtomicCounterBuffers*/ 1,
- /*maxAtomicCounterBufferSize*/ 16384,
- /*maxTransformFeedbackBuffers*/ 4,
- /*maxTransformFeedbackInterleavedComponents*/ 64,
- /*maxCullDistances*/ 8,
- /*maxCombinedClipAndCullDistances*/ 8,
- /*maxSamples*/ 4,
- /*maxMeshOutputVerticesNV*/ 0,
- /*maxMeshOutputPrimitivesNV*/ 0,
- /*maxMeshWorkGroupSizeX_NV*/ 0,
- /*maxMeshWorkGroupSizeY_NV*/ 0,
- /*maxMeshWorkGroupSizeZ_NV*/ 0,
- /*maxTaskWorkGroupSizeX_NV*/ 0,
- /*maxTaskWorkGroupSizeY_NV*/ 0,
- /*maxTaskWorkGroupSizeZ_NV*/ 0,
- /*maxMeshViewCountNV*/ 0,
- /*limits*/ {
- /*nonInductiveForLoops*/ true,
- /*whileLoops*/ true,
- /*doWhileLoops*/ true,
- /*generalUniformIndexing*/ true,
- /*generalAttributeMatrixVectorIndexing*/ true,
- /*generalVaryingIndexing*/ true,
- /*generalSamplerIndexing*/ true,
- /*generalVariableIndexing*/ true,
- /*generalConstantMatrixVectorIndexing*/ true,
- }
-};
-
-static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error) {
+static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage, const String &p_source_code, RenderingDevice::ShaderLanguage p_language, String *r_error, const RenderingDevice::Capabilities *p_capabilities) {
Vector<uint8_t> ret;
ERR_FAIL_COND_V(p_language == RenderingDevice::SHADER_LANGUAGE_HLSL, ret);
@@ -156,26 +51,85 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
};
int ClientInputSemanticsVersion = 100; // maps to, say, #define VULKAN 100
+ bool check_subgroup_support = true; // assume we support subgroups
- glslang::EShTargetClientVersion VulkanClientVersion = glslang::EShTargetVulkan_1_0;
- glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_0;
+ glslang::EShTargetClientVersion ClientVersion = glslang::EShTargetVulkan_1_2;
+ glslang::EShTargetLanguageVersion TargetVersion = glslang::EShTargetSpv_1_5;
glslang::TShader::ForbidIncluder includer;
+ if (p_capabilities->device_family == RenderingDevice::DeviceFamily::DEVICE_VULKAN) {
+ if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 0) {
+ ClientVersion = glslang::EShTargetVulkan_1_0;
+ TargetVersion = glslang::EShTargetSpv_1_0;
+ check_subgroup_support = false; // subgroups are not supported in Vulkan 1.0
+ } else if (p_capabilities->version_major == 1 && p_capabilities->version_minor == 1) {
+ ClientVersion = glslang::EShTargetVulkan_1_1;
+ TargetVersion = glslang::EShTargetSpv_1_3;
+ } else {
+ // use defaults
+ }
+ } else {
+ // once we support other backends we'll need to do something here
+ if (r_error) {
+ (*r_error) = "GLSLANG - Unsupported device family";
+ }
+ return ret;
+ }
+
glslang::TShader shader(stages[p_stage]);
CharString cs = p_source_code.ascii();
const char *cs_strings = cs.get_data();
+ std::string preamble = "";
shader.setStrings(&cs_strings, 1);
shader.setEnvInput(glslang::EShSourceGlsl, stages[p_stage], glslang::EShClientVulkan, ClientInputSemanticsVersion);
- shader.setEnvClient(glslang::EShClientVulkan, VulkanClientVersion);
+ shader.setEnvClient(glslang::EShClientVulkan, ClientVersion);
shader.setEnvTarget(glslang::EShTargetSpv, TargetVersion);
+ if (check_subgroup_support) {
+ uint32_t stage_bit = 1 << p_stage;
+
+ if ((p_capabilities->subgroup_in_shaders & stage_bit) == stage_bit) {
+ // stage supports subgroups
+ preamble += "#define has_GL_KHR_shader_subgroup_basic 1\n";
+ if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_VOTE_BIT) {
+ preamble += "#define has_GL_KHR_shader_subgroup_vote 1\n";
+ }
+ if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_ARITHMETIC_BIT) {
+ preamble += "#define has_GL_KHR_shader_subgroup_arithmetic 1\n";
+ }
+ if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_BALLOT_BIT) {
+ preamble += "#define has_GL_KHR_shader_subgroup_ballot 1\n";
+ }
+ if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_BIT) {
+ preamble += "#define has_GL_KHR_shader_subgroup_shuffle 1\n";
+ }
+ if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_SHUFFLE_RELATIVE_BIT) {
+ preamble += "#define has_GL_KHR_shader_subgroup_shuffle_relative 1\n";
+ }
+ if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_CLUSTERED_BIT) {
+ preamble += "#define has_GL_KHR_shader_subgroup_clustered 1\n";
+ }
+ if (p_capabilities->subgroup_operations & RenderingDevice::SUBGROUP_QUAD_BIT) {
+ preamble += "#define has_GL_KHR_shader_subgroup_quad 1\n";
+ }
+ }
+ }
+
+ if (p_capabilities->supports_multiview) {
+ preamble += "#define has_VK_KHR_multiview 1\n";
+ }
+
+ if (preamble != "") {
+ shader.setPreamble(preamble.c_str());
+ }
+
EShMessages messages = (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
const int DefaultVersion = 100;
std::string pre_processed_code;
//preprocess
- if (!shader.preprocess(&default_builtin_resource, DefaultVersion, ENoProfile, false, false, messages, &pre_processed_code, includer)) {
+ if (!shader.preprocess(&glslang::DefaultTBuiltInResource, DefaultVersion, ENoProfile, false, false, messages, &pre_processed_code, includer)) {
if (r_error) {
(*r_error) = "Failed pre-process:\n";
(*r_error) += shader.getInfoLog();
@@ -190,7 +144,7 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
shader.setStrings(&cs_strings, 1);
//parse
- if (!shader.parse(&default_builtin_resource, DefaultVersion, false, messages)) {
+ if (!shader.parse(&glslang::DefaultTBuiltInResource, DefaultVersion, false, messages)) {
if (r_error) {
(*r_error) = "Failed parse:\n";
(*r_error) += shader.getInfoLog();
@@ -223,17 +177,24 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
ret.resize(SpirV.size() * sizeof(uint32_t));
{
uint8_t *w = ret.ptrw();
- copymem(w, &SpirV[0], SpirV.size() * sizeof(uint32_t));
+ memcpy(w, &SpirV[0], SpirV.size() * sizeof(uint32_t));
}
return ret;
}
+static String _get_cache_key_function_glsl(const RenderingDevice::Capabilities *p_capabilities) {
+ String version;
+ version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(p_capabilities->version_major) + ", minor=" + itos(p_capabilities->version_minor) + " , subgroup_size=" + itos(p_capabilities->subgroup_operations) + " , subgroup_ops=" + itos(p_capabilities->subgroup_operations) + " , subgroup_in_shaders=" + itos(p_capabilities->subgroup_in_shaders);
+ return version;
+}
+
void preregister_glslang_types() {
// initialize in case it's not initialized. This is done once per thread
// and it's safe to call multiple times
glslang::InitializeProcess();
- RenderingDevice::shader_set_compile_function(_compile_shader_glsl);
+ RenderingDevice::shader_set_compile_to_spirv_function(_compile_shader_glsl);
+ RenderingDevice::shader_set_get_cache_key_function(_get_cache_key_function_glsl);
}
void register_glslang_types() {
diff --git a/modules/glslang/register_types.h b/modules/glslang/register_types.h
index 2437e2b27a..a1264b77c9 100644
--- a/modules/glslang/register_types.h
+++ b/modules/glslang/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gltf/SCsub b/modules/gltf/SCsub
new file mode 100644
index 0000000000..5d03ee8361
--- /dev/null
+++ b/modules/gltf/SCsub
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_gltf = env_modules.Clone()
+env_gltf.Prepend(CPPPATH=["."])
+
+# Godot's own source files
+env_gltf.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/gltf/config.py b/modules/gltf/config.py
new file mode 100644
index 0000000000..a4736321fa
--- /dev/null
+++ b/modules/gltf/config.py
@@ -0,0 +1,31 @@
+def can_build(env, platform):
+ return not env["disable_3d"]
+
+
+def configure(env):
+ pass
+
+
+def get_doc_classes():
+ return [
+ "EditorSceneImporterGLTF",
+ "GLTFAccessor",
+ "GLTFAnimation",
+ "GLTFBufferView",
+ "GLTFCamera",
+ "GLTFDocument",
+ "GLTFLight",
+ "GLTFMesh",
+ "GLTFNode",
+ "GLTFSkeleton",
+ "GLTFSkin",
+ "GLTFSpecGloss",
+ "GLTFState",
+ "GLTFTexture",
+ "GLTFDocumentExtension",
+ "GLTFDocumentExtensionConvertImporterMesh",
+ ]
+
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml
new file mode 100644
index 0000000000..ae81cae81a
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFAccessor.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFAccessor" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="buffer_view" type="int" setter="set_buffer_view" getter="get_buffer_view" default="0">
+ </member>
+ <member name="byte_offset" type="int" setter="set_byte_offset" getter="get_byte_offset" default="0">
+ </member>
+ <member name="component_type" type="int" setter="set_component_type" getter="get_component_type" default="0">
+ </member>
+ <member name="count" type="int" setter="set_count" getter="get_count" default="0">
+ </member>
+ <member name="max" type="PackedFloat64Array" setter="set_max" getter="get_max" default="PackedFloat64Array()">
+ </member>
+ <member name="min" type="PackedFloat64Array" setter="set_min" getter="get_min" default="PackedFloat64Array()">
+ </member>
+ <member name="normalized" type="bool" setter="set_normalized" getter="get_normalized" default="false">
+ </member>
+ <member name="sparse_count" type="int" setter="set_sparse_count" getter="get_sparse_count" default="0">
+ </member>
+ <member name="sparse_indices_buffer_view" type="int" setter="set_sparse_indices_buffer_view" getter="get_sparse_indices_buffer_view" default="0">
+ </member>
+ <member name="sparse_indices_byte_offset" type="int" setter="set_sparse_indices_byte_offset" getter="get_sparse_indices_byte_offset" default="0">
+ </member>
+ <member name="sparse_indices_component_type" type="int" setter="set_sparse_indices_component_type" getter="get_sparse_indices_component_type" default="0">
+ </member>
+ <member name="sparse_values_buffer_view" type="int" setter="set_sparse_values_buffer_view" getter="get_sparse_values_buffer_view" default="0">
+ </member>
+ <member name="sparse_values_byte_offset" type="int" setter="set_sparse_values_byte_offset" getter="get_sparse_values_byte_offset" default="0">
+ </member>
+ <member name="type" type="int" setter="set_type" getter="get_type" default="0">
+ </member>
+ </members>
+</class>
diff --git a/modules/gltf/doc_classes/GLTFAnimation.xml b/modules/gltf/doc_classes/GLTFAnimation.xml
new file mode 100644
index 0000000000..70480c2b38
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFAnimation.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFAnimation" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="loop" type="bool" setter="set_loop" getter="get_loop" default="false">
+ </member>
+ </members>
+</class>
diff --git a/modules/gltf/doc_classes/GLTFBufferView.xml b/modules/gltf/doc_classes/GLTFBufferView.xml
new file mode 100644
index 0000000000..f58aa46508
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFBufferView.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFBufferView" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="buffer" type="int" setter="set_buffer" getter="get_buffer" default="-1">
+ </member>
+ <member name="byte_length" type="int" setter="set_byte_length" getter="get_byte_length" default="0">
+ </member>
+ <member name="byte_offset" type="int" setter="set_byte_offset" getter="get_byte_offset" default="0">
+ </member>
+ <member name="byte_stride" type="int" setter="set_byte_stride" getter="get_byte_stride" default="-1">
+ </member>
+ <member name="indices" type="bool" setter="set_indices" getter="get_indices" default="false">
+ </member>
+ </members>
+</class>
diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml
new file mode 100644
index 0000000000..3682df5951
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFCamera.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFCamera" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="depth_far" type="float" setter="set_depth_far" getter="get_depth_far" default="4000.0">
+ </member>
+ <member name="depth_near" type="float" setter="set_depth_near" getter="get_depth_near" default="0.05">
+ </member>
+ <member name="fov_size" type="float" setter="set_fov_size" getter="get_fov_size" default="75.0">
+ </member>
+ <member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true">
+ </member>
+ </members>
+</class>
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
new file mode 100644
index 0000000000..8d8e25e8b3
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFDocument" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <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>
+ <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
new file mode 100644
index 0000000000..b4f03cd1ed
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFLight.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFLight" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <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="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="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="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="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>
+</class>
diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml
new file mode 100644
index 0000000000..58853217e2
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFMesh.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFMesh" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="blend_weights" type="PackedFloat32Array" setter="set_blend_weights" getter="get_blend_weights" default="PackedFloat32Array()">
+ </member>
+ <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>
+</class>
diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml
new file mode 100644
index 0000000000..f27965ea07
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFNode.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFNode" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="camera" type="int" setter="set_camera" getter="get_camera" default="-1">
+ </member>
+ <member name="children" type="PackedInt32Array" setter="set_children" getter="get_children" default="PackedInt32Array()">
+ </member>
+ <member name="height" type="int" setter="set_height" getter="get_height" default="-1">
+ </member>
+ <member name="joint" type="bool" setter="set_joint" getter="get_joint" default="false">
+ </member>
+ <member name="light" type="int" setter="set_light" getter="get_light" default="-1">
+ </member>
+ <member name="mesh" type="int" setter="set_mesh" getter="get_mesh" default="-1">
+ </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)">
+ </member>
+ <member name="skeleton" type="int" setter="set_skeleton" getter="get_skeleton" default="-1">
+ </member>
+ <member name="skin" type="int" setter="set_skin" getter="get_skin" default="-1">
+ </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>
+</class>
diff --git a/modules/gltf/doc_classes/GLTFSkeleton.xml b/modules/gltf/doc_classes/GLTFSkeleton.xml
new file mode 100644
index 0000000000..037c3545a6
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFSkeleton.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFSkeleton" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_bone_attachment">
+ <return type="BoneAttachment3D" />
+ <argument index="0" name="idx" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="get_bone_attachment_count">
+ <return type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="get_godot_bone_node">
+ <return type="Dictionary" />
+ <description>
+ </description>
+ </method>
+ <method name="get_godot_skeleton">
+ <return type="Skeleton3D" />
+ <description>
+ </description>
+ </method>
+ <method name="get_unique_names">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_godot_bone_node">
+ <return type="void" />
+ <argument index="0" name="godot_bone_node" type="Dictionary" />
+ <description>
+ </description>
+ </method>
+ <method name="set_unique_names">
+ <return type="void" />
+ <argument index="0" name="unique_names" type="Array" />
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="joints" type="PackedInt32Array" setter="set_joints" getter="get_joints" default="PackedInt32Array()">
+ </member>
+ <member name="roots" type="PackedInt32Array" setter="set_roots" getter="get_roots" default="PackedInt32Array()">
+ </member>
+ </members>
+</class>
diff --git a/modules/gltf/doc_classes/GLTFSkin.xml b/modules/gltf/doc_classes/GLTFSkin.xml
new file mode 100644
index 0000000000..ad4f017584
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFSkin.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFSkin" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_inverse_binds">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_joint_i_to_bone_i">
+ <return type="Dictionary" />
+ <description>
+ </description>
+ </method>
+ <method name="get_joint_i_to_name">
+ <return type="Dictionary" />
+ <description>
+ </description>
+ </method>
+ <method name="set_inverse_binds">
+ <return type="void" />
+ <argument index="0" name="inverse_binds" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_joint_i_to_bone_i">
+ <return type="void" />
+ <argument index="0" name="joint_i_to_bone_i" type="Dictionary" />
+ <description>
+ </description>
+ </method>
+ <method name="set_joint_i_to_name">
+ <return type="void" />
+ <argument index="0" name="joint_i_to_name" type="Dictionary" />
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="godot_skin" type="Skin" setter="set_godot_skin" getter="get_godot_skin">
+ </member>
+ <member name="joints" type="PackedInt32Array" setter="set_joints" getter="get_joints" default="PackedInt32Array()">
+ </member>
+ <member name="joints_original" type="PackedInt32Array" setter="set_joints_original" getter="get_joints_original" default="PackedInt32Array()">
+ </member>
+ <member name="non_joints" type="PackedInt32Array" setter="set_non_joints" getter="get_non_joints" default="PackedInt32Array()">
+ </member>
+ <member name="roots" type="PackedInt32Array" setter="set_roots" getter="get_roots" default="PackedInt32Array()">
+ </member>
+ <member name="skeleton" type="int" setter="set_skeleton" getter="get_skeleton" default="-1">
+ </member>
+ <member name="skin_root" type="int" setter="set_skin_root" getter="get_skin_root" default="-1">
+ </member>
+ </members>
+</class>
diff --git a/modules/gltf/doc_classes/GLTFSpecGloss.xml b/modules/gltf/doc_classes/GLTFSpecGloss.xml
new file mode 100644
index 0000000000..6b8f86ed1c
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFSpecGloss.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFSpecGloss" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="diffuse_factor" type="Color" setter="set_diffuse_factor" getter="get_diffuse_factor" default="Color(1, 1, 1, 1)">
+ </member>
+ <member name="diffuse_img" type="Image" setter="set_diffuse_img" getter="get_diffuse_img">
+ </member>
+ <member name="gloss_factor" type="float" setter="set_gloss_factor" getter="get_gloss_factor" default="1.0">
+ </member>
+ <member name="spec_gloss_img" type="Image" setter="set_spec_gloss_img" getter="get_spec_gloss_img">
+ </member>
+ <member name="specular_factor" type="Color" setter="set_specular_factor" getter="get_specular_factor" default="Color(1, 1, 1, 1)">
+ </member>
+ </members>
+</class>
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
new file mode 100644
index 0000000000..6d03d0ecf8
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFState" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_accessors">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_animation_player">
+ <return type="AnimationPlayer" />
+ <argument index="0" name="idx" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="get_animation_players_count">
+ <return type="int" />
+ <argument index="0" name="idx" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="get_animations">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_buffer_views">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_cameras">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_images">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_lights">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_materials">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_meshes">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_nodes">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_scene_node">
+ <return type="Node" />
+ <argument index="0" name="idx" type="int" />
+ <description>
+ </description>
+ </method>
+ <method name="get_skeleton_to_node">
+ <return type="Dictionary" />
+ <description>
+ </description>
+ </method>
+ <method name="get_skeletons">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_skins">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_textures">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_unique_animation_names">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="get_unique_names">
+ <return type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_accessors">
+ <return type="void" />
+ <argument index="0" name="accessors" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_animations">
+ <return type="void" />
+ <argument index="0" name="animations" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_buffer_views">
+ <return type="void" />
+ <argument index="0" name="buffer_views" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_cameras">
+ <return type="void" />
+ <argument index="0" name="cameras" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_images">
+ <return type="void" />
+ <argument index="0" name="images" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_lights">
+ <return type="void" />
+ <argument index="0" name="lights" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_materials">
+ <return type="void" />
+ <argument index="0" name="materials" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_meshes">
+ <return type="void" />
+ <argument index="0" name="meshes" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_nodes">
+ <return type="void" />
+ <argument index="0" name="nodes" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_skeleton_to_node">
+ <return type="void" />
+ <argument index="0" name="skeleton_to_node" type="Dictionary" />
+ <description>
+ </description>
+ </method>
+ <method name="set_skeletons">
+ <return type="void" />
+ <argument index="0" name="skeletons" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_skins">
+ <return type="void" />
+ <argument index="0" name="skins" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_textures">
+ <return type="void" />
+ <argument index="0" name="textures" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_unique_animation_names">
+ <return type="void" />
+ <argument index="0" name="unique_animation_names" type="Array" />
+ <description>
+ </description>
+ </method>
+ <method name="set_unique_names">
+ <return type="void" />
+ <argument index="0" name="unique_names" type="Array" />
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="buffers" type="Array" setter="set_buffers" getter="get_buffers" default="[]">
+ </member>
+ <member name="glb_data" type="PackedByteArray" setter="set_glb_data" getter="get_glb_data" default="PackedByteArray()">
+ </member>
+ <member name="json" type="Dictionary" setter="set_json" getter="get_json" default="{}">
+ </member>
+ <member name="major_version" type="int" setter="set_major_version" getter="get_major_version" default="0">
+ </member>
+ <member name="minor_version" type="int" setter="set_minor_version" getter="get_minor_version" default="0">
+ </member>
+ <member name="root_nodes" type="Array" setter="set_root_nodes" getter="get_root_nodes" default="[]">
+ </member>
+ <member name="scene_name" type="String" setter="set_scene_name" getter="get_scene_name" default="&quot;&quot;">
+ </member>
+ <member name="use_named_skin_binds" type="bool" setter="set_use_named_skin_binds" getter="get_use_named_skin_binds" default="false">
+ </member>
+ </members>
+</class>
diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml
new file mode 100644
index 0000000000..7c88d2318e
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFTexture.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFTexture" inherits="Resource" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <member name="src_image" type="int" setter="set_src_image" getter="get_src_image" default="0">
+ </member>
+ </members>
+</class>
diff --git a/modules/gltf/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp
new file mode 100644
index 0000000000..25fda7ef3b
--- /dev/null
+++ b/modules/gltf/editor_scene_exporter_gltf_plugin.cpp
@@ -0,0 +1,98 @@
+/*************************************************************************/
+/* editor_scene_exporter_gltf_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#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"
+
+#include "editor/editor_node.h"
+
+String SceneExporterGLTFPlugin::get_name() const {
+ return "ConvertGLTF2";
+}
+
+bool SceneExporterGLTFPlugin::has_main_screen() const {
+ return false;
+}
+
+SceneExporterGLTFPlugin::SceneExporterGLTFPlugin(EditorNode *p_node) {
+ editor = p_node;
+ 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));
+ file_export_lib->set_title(TTR("Export Library"));
+ file_export_lib->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
+ file_export_lib->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ file_export_lib->clear_filters();
+ file_export_lib->add_filter("*.glb");
+ file_export_lib->add_filter("*.gltf");
+ file_export_lib->set_title(TTR("Export Mesh GLTF2"));
+ String gltf_scene_name = TTR("Export GLTF...");
+ add_tool_menu_item(gltf_scene_name, callable_mp(this, &SceneExporterGLTFPlugin::convert_scene_to_gltf2));
+}
+
+void SceneExporterGLTFPlugin::_gltf2_dialog_action(String p_file) {
+ Node *root = editor->get_tree()->get_edited_scene_root();
+ if (!root) {
+ editor->show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
+ return;
+ }
+ List<String> deps;
+ 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() {
+ Node *root = editor->get_tree()->get_edited_scene_root();
+ if (!root) {
+ editor->show_accept(TTR("This operation can't be done without a scene."), TTR("OK"));
+ return;
+ }
+ 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/etc/texture_loader_pkm.h b/modules/gltf/editor_scene_exporter_gltf_plugin.h
index 6507e0bdec..89a8e27053 100644
--- a/modules/etc/texture_loader_pkm.h
+++ b/modules/gltf/editor_scene_exporter_gltf_plugin.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* texture_loader_pkm.h */
+/* editor_scene_exporter_gltf_plugin.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,20 +28,26 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef TEXTURE_LOADER_PKM_H
-#define TEXTURE_LOADER_PKM_H
+#ifndef EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
+#define EDITOR_SCENE_EXPORTER_GLTF_PLUGIN_H
-#include "core/io/resource_loader.h"
-#include "scene/resources/texture.h"
+#if TOOLS_ENABLED
+#include "editor/editor_plugin.h"
-class ResourceFormatPKM : 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, bool p_no_cache = false);
- 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;
+#include "editor_scene_importer_gltf.h"
- virtual ~ResourceFormatPKM() {}
-};
+class SceneExporterGLTFPlugin : public EditorPlugin {
+ GDCLASS(SceneExporterGLTFPlugin, EditorPlugin);
+
+ EditorNode *editor = nullptr;
+ EditorFileDialog *file_export_lib = nullptr;
+ void _gltf2_dialog_action(String p_file);
+ void convert_scene_to_gltf2();
-#endif // TEXTURE_LOADER_PKM_H
+public:
+ virtual String get_name() const override;
+ 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/gdnative/net/webrtc_gdnative.cpp b/modules/gltf/editor_scene_importer_gltf.cpp
index a7355e4d12..1a172877a0 100644
--- a/modules/gdnative/net/webrtc_gdnative.cpp
+++ b/modules/gltf/editor_scene_importer_gltf.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_gdnative.cpp */
+/* editor_scene_importer_gltf.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,33 +28,38 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "modules/gdnative/gdnative.h"
-#include "modules/gdnative/include/net/godot_net.h"
+#if TOOLS_ENABLED
+#include "editor_scene_importer_gltf.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"
+#include "gltf_state.h"
-extern "C" {
+#include "scene/3d/node_3d.h"
+#include "scene/animation/animation_player.h"
+#include "scene/resources/animation.h"
-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
+uint32_t EditorSceneFormatImporterGLTF::get_import_flags() const {
+ return ImportFlags::IMPORT_SCENE | ImportFlags::IMPORT_ANIMATION;
}
-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 EditorSceneFormatImporterGLTF::get_extensions(List<String> *r_extensions) const {
+ r_extensions->push_back("gltf");
+ r_extensions->push_back("glb");
}
-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 ERR_UNAVAILABLE;
-#endif
+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<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> EditorSceneFormatImporterGLTF::import_animation(const String &p_path,
+ uint32_t p_flags,
+ int p_bake_fps) {
+ return Ref<Animation>();
}
+
+#endif // TOOLS_ENABLED
diff --git a/modules/gltf/editor_scene_importer_gltf.h b/modules/gltf/editor_scene_importer_gltf.h
new file mode 100644
index 0000000000..28963adc28
--- /dev/null
+++ b/modules/gltf/editor_scene_importer_gltf.h
@@ -0,0 +1,55 @@
+/*************************************************************************/
+/* editor_scene_importer_gltf.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 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;
+
+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, Error *r_err = nullptr) override;
+ virtual Ref<Animation> import_animation(const String &p_path,
+ uint32_t p_flags, int p_bake_fps) override;
+};
+#endif // TOOLS_ENABLED
+#endif // EDITOR_SCENE_IMPORTER_GLTF_H
diff --git a/modules/gltf/gltf_accessor.cpp b/modules/gltf/gltf_accessor.cpp
new file mode 100644
index 0000000000..85cec3fec4
--- /dev/null
+++ b/modules/gltf/gltf_accessor.cpp
@@ -0,0 +1,191 @@
+/*************************************************************************/
+/* gltf_accessor.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 "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);
+ ClassDB::bind_method(D_METHOD("get_byte_offset"), &GLTFAccessor::get_byte_offset);
+ ClassDB::bind_method(D_METHOD("set_byte_offset", "byte_offset"), &GLTFAccessor::set_byte_offset);
+ ClassDB::bind_method(D_METHOD("get_component_type"), &GLTFAccessor::get_component_type);
+ ClassDB::bind_method(D_METHOD("set_component_type", "component_type"), &GLTFAccessor::set_component_type);
+ ClassDB::bind_method(D_METHOD("get_normalized"), &GLTFAccessor::get_normalized);
+ ClassDB::bind_method(D_METHOD("set_normalized", "normalized"), &GLTFAccessor::set_normalized);
+ ClassDB::bind_method(D_METHOD("get_count"), &GLTFAccessor::get_count);
+ ClassDB::bind_method(D_METHOD("set_count", "count"), &GLTFAccessor::set_count);
+ ClassDB::bind_method(D_METHOD("get_type"), &GLTFAccessor::get_type);
+ ClassDB::bind_method(D_METHOD("set_type", "type"), &GLTFAccessor::set_type);
+ ClassDB::bind_method(D_METHOD("get_min"), &GLTFAccessor::get_min);
+ ClassDB::bind_method(D_METHOD("set_min", "min"), &GLTFAccessor::set_min);
+ ClassDB::bind_method(D_METHOD("get_max"), &GLTFAccessor::get_max);
+ ClassDB::bind_method(D_METHOD("set_max", "max"), &GLTFAccessor::set_max);
+ ClassDB::bind_method(D_METHOD("get_sparse_count"), &GLTFAccessor::get_sparse_count);
+ ClassDB::bind_method(D_METHOD("set_sparse_count", "sparse_count"), &GLTFAccessor::set_sparse_count);
+ ClassDB::bind_method(D_METHOD("get_sparse_indices_buffer_view"), &GLTFAccessor::get_sparse_indices_buffer_view);
+ ClassDB::bind_method(D_METHOD("set_sparse_indices_buffer_view", "sparse_indices_buffer_view"), &GLTFAccessor::set_sparse_indices_buffer_view);
+ ClassDB::bind_method(D_METHOD("get_sparse_indices_byte_offset"), &GLTFAccessor::get_sparse_indices_byte_offset);
+ ClassDB::bind_method(D_METHOD("set_sparse_indices_byte_offset", "sparse_indices_byte_offset"), &GLTFAccessor::set_sparse_indices_byte_offset);
+ ClassDB::bind_method(D_METHOD("get_sparse_indices_component_type"), &GLTFAccessor::get_sparse_indices_component_type);
+ ClassDB::bind_method(D_METHOD("set_sparse_indices_component_type", "sparse_indices_component_type"), &GLTFAccessor::set_sparse_indices_component_type);
+ ClassDB::bind_method(D_METHOD("get_sparse_values_buffer_view"), &GLTFAccessor::get_sparse_values_buffer_view);
+ ClassDB::bind_method(D_METHOD("set_sparse_values_buffer_view", "sparse_values_buffer_view"), &GLTFAccessor::set_sparse_values_buffer_view);
+ ClassDB::bind_method(D_METHOD("get_sparse_values_byte_offset"), &GLTFAccessor::get_sparse_values_byte_offset);
+ ClassDB::bind_method(D_METHOD("set_sparse_values_byte_offset", "sparse_values_byte_offset"), &GLTFAccessor::set_sparse_values_byte_offset);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "buffer_view"), "set_buffer_view", "get_buffer_view"); // GLTFBufferViewIndex
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "byte_offset"), "set_byte_offset", "get_byte_offset"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "component_type"), "set_component_type", "get_component_type"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "normalized"), "set_normalized", "get_normalized"); // bool
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "count"), "set_count", "get_count"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "type"), "set_type", "get_type"); // GLTFDocument::GLTFType
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "min"), "set_min", "get_min"); // Vector<real_t>
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_FLOAT64_ARRAY, "max"), "set_max", "get_max"); // Vector<real_t>
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_count"), "set_sparse_count", "get_sparse_count"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_indices_buffer_view"), "set_sparse_indices_buffer_view", "get_sparse_indices_buffer_view"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_indices_byte_offset"), "set_sparse_indices_byte_offset", "get_sparse_indices_byte_offset"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_indices_component_type"), "set_sparse_indices_component_type", "get_sparse_indices_component_type"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_values_buffer_view"), "set_sparse_values_buffer_view", "get_sparse_values_buffer_view"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sparse_values_byte_offset"), "set_sparse_values_byte_offset", "get_sparse_values_byte_offset"); // int
+}
+
+GLTFBufferViewIndex GLTFAccessor::get_buffer_view() {
+ return buffer_view;
+}
+
+void GLTFAccessor::set_buffer_view(GLTFBufferViewIndex p_buffer_view) {
+ buffer_view = p_buffer_view;
+}
+
+int GLTFAccessor::get_byte_offset() {
+ return byte_offset;
+}
+
+void GLTFAccessor::set_byte_offset(int p_byte_offset) {
+ byte_offset = p_byte_offset;
+}
+
+int GLTFAccessor::get_component_type() {
+ return component_type;
+}
+
+void GLTFAccessor::set_component_type(int p_component_type) {
+ component_type = p_component_type;
+}
+
+bool GLTFAccessor::get_normalized() {
+ return normalized;
+}
+
+void GLTFAccessor::set_normalized(bool p_normalized) {
+ normalized = p_normalized;
+}
+
+int GLTFAccessor::get_count() {
+ return count;
+}
+
+void GLTFAccessor::set_count(int p_count) {
+ count = p_count;
+}
+
+int GLTFAccessor::get_type() {
+ return (int)type;
+}
+
+void GLTFAccessor::set_type(int p_type) {
+ type = (GLTFDocument::GLTFType)p_type; // TODO: Register enum
+}
+
+Vector<double> GLTFAccessor::get_min() {
+ return min;
+}
+
+void GLTFAccessor::set_min(Vector<double> p_min) {
+ min = p_min;
+}
+
+Vector<double> GLTFAccessor::get_max() {
+ return max;
+}
+
+void GLTFAccessor::set_max(Vector<double> p_max) {
+ max = p_max;
+}
+
+int GLTFAccessor::get_sparse_count() {
+ return sparse_count;
+}
+
+void GLTFAccessor::set_sparse_count(int p_sparse_count) {
+ sparse_count = p_sparse_count;
+}
+
+int GLTFAccessor::get_sparse_indices_buffer_view() {
+ return sparse_indices_buffer_view;
+}
+
+void GLTFAccessor::set_sparse_indices_buffer_view(int p_sparse_indices_buffer_view) {
+ sparse_indices_buffer_view = p_sparse_indices_buffer_view;
+}
+
+int GLTFAccessor::get_sparse_indices_byte_offset() {
+ return sparse_indices_byte_offset;
+}
+
+void GLTFAccessor::set_sparse_indices_byte_offset(int p_sparse_indices_byte_offset) {
+ sparse_indices_byte_offset = p_sparse_indices_byte_offset;
+}
+
+int GLTFAccessor::get_sparse_indices_component_type() {
+ return sparse_indices_component_type;
+}
+
+void GLTFAccessor::set_sparse_indices_component_type(int p_sparse_indices_component_type) {
+ sparse_indices_component_type = p_sparse_indices_component_type;
+}
+
+int GLTFAccessor::get_sparse_values_buffer_view() {
+ return sparse_values_buffer_view;
+}
+
+void GLTFAccessor::set_sparse_values_buffer_view(int p_sparse_values_buffer_view) {
+ sparse_values_buffer_view = p_sparse_values_buffer_view;
+}
+
+int GLTFAccessor::get_sparse_values_byte_offset() {
+ return sparse_values_byte_offset;
+}
+
+void GLTFAccessor::set_sparse_values_byte_offset(int p_sparse_values_byte_offset) {
+ sparse_values_byte_offset = p_sparse_values_byte_offset;
+}
diff --git a/modules/gltf/gltf_accessor.h b/modules/gltf/gltf_accessor.h
new file mode 100644
index 0000000000..bec511f974
--- /dev/null
+++ b/modules/gltf/gltf_accessor.h
@@ -0,0 +1,105 @@
+/*************************************************************************/
+/* gltf_accessor.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 GLTF_ACCESSOR_H
+#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);
+ friend class GLTFDocument;
+
+private:
+ GLTFBufferViewIndex buffer_view = 0;
+ int byte_offset = 0;
+ int component_type = 0;
+ bool normalized = false;
+ int count = 0;
+ GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR;
+ Vector<double> min;
+ Vector<double> max;
+ int sparse_count = 0;
+ int sparse_indices_buffer_view = 0;
+ int sparse_indices_byte_offset = 0;
+ int sparse_indices_component_type = 0;
+ int sparse_values_buffer_view = 0;
+ int sparse_values_byte_offset = 0;
+
+protected:
+ static void _bind_methods();
+
+public:
+ GLTFBufferViewIndex get_buffer_view();
+ void set_buffer_view(GLTFBufferViewIndex p_buffer_view);
+
+ int get_byte_offset();
+ void set_byte_offset(int p_byte_offset);
+
+ int get_component_type();
+ void set_component_type(int p_component_type);
+
+ bool get_normalized();
+ void set_normalized(bool p_normalized);
+
+ int get_count();
+ void set_count(int p_count);
+
+ int get_type();
+ void set_type(int p_type);
+
+ Vector<double> get_min();
+ void set_min(Vector<double> p_min);
+
+ Vector<double> get_max();
+ void set_max(Vector<double> p_max);
+
+ int get_sparse_count();
+ void set_sparse_count(int p_sparse_count);
+
+ int get_sparse_indices_buffer_view();
+ void set_sparse_indices_buffer_view(int p_sparse_indices_buffer_view);
+
+ int get_sparse_indices_byte_offset();
+ void set_sparse_indices_byte_offset(int p_sparse_indices_byte_offset);
+
+ int get_sparse_indices_component_type();
+ void set_sparse_indices_component_type(int p_sparse_indices_component_type);
+
+ int get_sparse_values_buffer_view();
+ void set_sparse_values_buffer_view(int p_sparse_values_buffer_view);
+
+ int get_sparse_values_byte_offset();
+ void set_sparse_values_byte_offset(int p_sparse_values_byte_offset);
+};
+#endif // GLTF_ACCESSOR_H
diff --git a/modules/gltf/gltf_animation.cpp b/modules/gltf/gltf_animation.cpp
new file mode 100644
index 0000000000..889a8e8870
--- /dev/null
+++ b/modules/gltf/gltf_animation.cpp
@@ -0,0 +1,53 @@
+/*************************************************************************/
+/* gltf_animation.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 "gltf_animation.h"
+
+void GLTFAnimation::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_loop"), &GLTFAnimation::get_loop);
+ ClassDB::bind_method(D_METHOD("set_loop", "loop"), &GLTFAnimation::set_loop);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "get_loop"); // bool
+}
+
+bool GLTFAnimation::get_loop() const {
+ return loop;
+}
+
+void GLTFAnimation::set_loop(bool p_val) {
+ loop = p_val;
+}
+
+Map<int, GLTFAnimation::Track> &GLTFAnimation::get_tracks() {
+ return tracks;
+}
+
+GLTFAnimation::GLTFAnimation() {
+}
diff --git a/modules/gltf/gltf_animation.h b/modules/gltf/gltf_animation.h
new file mode 100644
index 0000000000..be0ed2d4c6
--- /dev/null
+++ b/modules/gltf/gltf_animation.h
@@ -0,0 +1,74 @@
+/*************************************************************************/
+/* gltf_animation.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 GLTF_ANIMATION_H
+#define GLTF_ANIMATION_H
+
+#include "core/io/resource.h"
+
+class GLTFAnimation : public Resource {
+ GDCLASS(GLTFAnimation, Resource);
+
+protected:
+ static void _bind_methods();
+
+public:
+ enum Interpolation {
+ INTERP_LINEAR,
+ INTERP_STEP,
+ INTERP_CATMULLROMSPLINE,
+ INTERP_CUBIC_SPLINE,
+ };
+
+ template <class T>
+ struct Channel {
+ Interpolation interpolation;
+ Vector<float> times;
+ Vector<T> values;
+ };
+
+ struct Track {
+ Channel<Vector3> position_track;
+ Channel<Quaternion> rotation_track;
+ Channel<Vector3> scale_track;
+ Vector<Channel<float>> weight_tracks;
+ };
+
+public:
+ bool get_loop() const;
+ void set_loop(bool p_val);
+ Map<int, GLTFAnimation::Track> &get_tracks();
+ GLTFAnimation();
+
+private:
+ bool loop = false;
+ Map<int, Track> tracks;
+};
+#endif // GLTF_ANIMATION_H
diff --git a/modules/gltf/gltf_buffer_view.cpp b/modules/gltf/gltf_buffer_view.cpp
new file mode 100644
index 0000000000..d00e0f040f
--- /dev/null
+++ b/modules/gltf/gltf_buffer_view.cpp
@@ -0,0 +1,92 @@
+/*************************************************************************/
+/* gltf_buffer_view.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 "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);
+ ClassDB::bind_method(D_METHOD("get_byte_offset"), &GLTFBufferView::get_byte_offset);
+ ClassDB::bind_method(D_METHOD("set_byte_offset", "byte_offset"), &GLTFBufferView::set_byte_offset);
+ ClassDB::bind_method(D_METHOD("get_byte_length"), &GLTFBufferView::get_byte_length);
+ ClassDB::bind_method(D_METHOD("set_byte_length", "byte_length"), &GLTFBufferView::set_byte_length);
+ ClassDB::bind_method(D_METHOD("get_byte_stride"), &GLTFBufferView::get_byte_stride);
+ ClassDB::bind_method(D_METHOD("set_byte_stride", "byte_stride"), &GLTFBufferView::set_byte_stride);
+ ClassDB::bind_method(D_METHOD("get_indices"), &GLTFBufferView::get_indices);
+ ClassDB::bind_method(D_METHOD("set_indices", "indices"), &GLTFBufferView::set_indices);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "buffer"), "set_buffer", "get_buffer"); // GLTFBufferIndex
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "byte_offset"), "set_byte_offset", "get_byte_offset"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "byte_length"), "set_byte_length", "get_byte_length"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "byte_stride"), "set_byte_stride", "get_byte_stride"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indices"), "set_indices", "get_indices"); // bool
+}
+
+GLTFBufferIndex GLTFBufferView::get_buffer() {
+ return buffer;
+}
+
+void GLTFBufferView::set_buffer(GLTFBufferIndex p_buffer) {
+ buffer = p_buffer;
+}
+
+int GLTFBufferView::get_byte_offset() {
+ return byte_offset;
+}
+
+void GLTFBufferView::set_byte_offset(int p_byte_offset) {
+ byte_offset = p_byte_offset;
+}
+
+int GLTFBufferView::get_byte_length() {
+ return byte_length;
+}
+
+void GLTFBufferView::set_byte_length(int p_byte_length) {
+ byte_length = p_byte_length;
+}
+
+int GLTFBufferView::get_byte_stride() {
+ return byte_stride;
+}
+
+void GLTFBufferView::set_byte_stride(int p_byte_stride) {
+ byte_stride = p_byte_stride;
+}
+
+bool GLTFBufferView::get_indices() {
+ return indices;
+}
+
+void GLTFBufferView::set_indices(bool p_indices) {
+ indices = p_indices;
+}
diff --git a/modules/gltf/gltf_buffer_view.h b/modules/gltf/gltf_buffer_view.h
new file mode 100644
index 0000000000..63af5e7c0d
--- /dev/null
+++ b/modules/gltf/gltf_buffer_view.h
@@ -0,0 +1,68 @@
+/*************************************************************************/
+/* gltf_buffer_view.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 GLTF_BUFFER_VIEW_H
+#define GLTF_BUFFER_VIEW_H
+
+#include "core/io/resource.h"
+#include "gltf_document.h"
+
+class GLTFBufferView : public Resource {
+ GDCLASS(GLTFBufferView, Resource);
+ friend class GLTFDocument;
+
+private:
+ GLTFBufferIndex buffer = -1;
+ int byte_offset = 0;
+ int byte_length = 0;
+ int byte_stride = -1;
+ bool indices = false;
+
+protected:
+ static void _bind_methods();
+
+public:
+ GLTFBufferIndex get_buffer();
+ void set_buffer(GLTFBufferIndex p_buffer);
+
+ int get_byte_offset();
+ void set_byte_offset(int p_byte_offset);
+
+ int get_byte_length();
+ void set_byte_length(int p_byte_length);
+
+ int get_byte_stride();
+ void set_byte_stride(int p_byte_stride);
+
+ bool get_indices();
+ void set_indices(bool p_indices);
+ // matrices need to be transformed to this
+};
+#endif // GLTF_BUFFER_VIEW_H
diff --git a/modules/gltf/gltf_camera.cpp b/modules/gltf/gltf_camera.cpp
new file mode 100644
index 0000000000..0f895fb989
--- /dev/null
+++ b/modules/gltf/gltf_camera.cpp
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/* gltf_camera.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 "gltf_camera.h"
+
+void GLTFCamera::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_perspective"), &GLTFCamera::get_perspective);
+ 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_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, "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
new file mode 100644
index 0000000000..843ff417a4
--- /dev/null
+++ b/modules/gltf/gltf_camera.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* gltf_camera.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 GLTF_CAMERA_H
+#define GLTF_CAMERA_H
+
+#include "core/io/resource.h"
+
+class GLTFCamera : public Resource {
+ GDCLASS(GLTFCamera, Resource);
+
+private:
+ bool perspective = true;
+ float fov_size = 75.0;
+ float depth_far = 4000.0;
+ float depth_near = 0.05;
+
+protected:
+ static void _bind_methods();
+
+public:
+ bool get_perspective() const { return perspective; }
+ 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_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
new file mode 100644
index 0000000000..fac1e61b18
--- /dev/null
+++ b/modules/gltf/gltf_document.cpp
@@ -0,0 +1,6976 @@
+/*************************************************************************/
+/* gltf_document.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 "gltf_document.h"
+
+#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"
+#include "gltf_skeleton.h"
+#include "gltf_skin.h"
+#include "gltf_spec_gloss.h"
+#include "gltf_state.h"
+#include "gltf_texture.h"
+
+#include "core/crypto/crypto_core.h"
+#include "core/error/error_macros.h"
+#include "core/io/dir_access.h"
+#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"
+#include "core/version_hash.gen.h"
+#include "drivers/png/png_driver_common.h"
+#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" // For csg, gridmap.
+
+#ifdef MODULE_CSG_ENABLED
+#include "modules/csg/csg_shape.h"
+#endif // MODULE_CSG_ENABLED
+#ifdef MODULE_GRIDMAP_ENABLED
+#include "modules/gridmap/grid_map.h"
+#endif // MODULE_GRIDMAP_ENABLED
+
+#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();
+
+ 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>());
+ }
+
+ /* STEP 1 CONVERT MESH INSTANCES */
+ _convert_mesh_instances(state);
+
+ /* STEP 2 SERIALIZE CAMERAS */
+ Error err = _serialize_cameras(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 3 CREATE SKINS */
+ err = _serialize_skins(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 5 SERIALIZE MESHES (we have enough info now) */
+ err = _serialize_meshes(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 6 SERIALIZE TEXTURES */
+ err = _serialize_materials(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 7 SERIALIZE ANIMATIONS */
+ err = _serialize_animations(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 8 SERIALIZE ACCESSORS */
+ err = _encode_accessors(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 9 SERIALIZE IMAGES */
+ err = _serialize_images(state, p_path);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 10 SERIALIZE TEXTURES */
+ err = _serialize_textures(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ for (GLTFBufferViewIndex i = 0; i < state->buffer_views.size(); i++) {
+ state->buffer_views.write[i]->buffer = 0;
+ }
+
+ /* STEP 11 SERIALIZE BUFFER VIEWS */
+ err = _encode_buffer_views(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 12 SERIALIZE NODES */
+ err = _serialize_nodes(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 13 SERIALIZE SCENE */
+ err = _serialize_scenes(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 14 SERIALIZE SCENE */
+ err = _serialize_lights(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 15 SERIALIZE EXTENSIONS */
+ err = _serialize_extensions(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 16 SERIALIZE VERSION */
+ err = _serialize_version(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 17 SERIALIZE FILE */
+ err = _serialize_file(state, p_path);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+ 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));
+
+ return OK;
+}
+
+Error GLTFDocument::_serialize_extensions(Ref<GLTFState> state) const {
+ const String texture_transform = "KHR_texture_transform";
+ const String punctual_lights = "KHR_lights_punctual";
+ Array extensions_used;
+ extensions_used.push_back(punctual_lights);
+ extensions_used.push_back(texture_transform);
+ state->json["extensionsUsed"] = extensions_used;
+ Array extensions_required;
+ extensions_required.push_back(texture_transform);
+ state->json["extensionsRequired"] = extensions_required;
+ return OK;
+}
+
+Error GLTFDocument::_serialize_scenes(Ref<GLTFState> state) {
+ Array scenes;
+ const int loaded_scene = 0;
+ state->json["scene"] = loaded_scene;
+
+ if (state->nodes.size()) {
+ Dictionary s;
+ if (!state->scene_name.is_empty()) {
+ s["name"] = state->scene_name;
+ }
+
+ Array nodes;
+ nodes.push_back(0);
+ s["nodes"] = nodes;
+ scenes.push_back(s);
+ }
+ state->json["scenes"] = scenes;
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) {
+ Error err;
+ FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
+ if (!f) {
+ return err;
+ }
+
+ Vector<uint8_t> array;
+ array.resize(f->get_length());
+ f->get_buffer(array.ptrw(), array.size());
+ String text;
+ text.parse_utf8((const char *)array.ptr(), array.size());
+
+ 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(), false, ERR_HANDLER_SCRIPT);
+ return err;
+ }
+ state->json = json.get_data();
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_glb(const String &p_path, Ref<GLTFState> state) {
+ Error err;
+ FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
+ if (!f) {
+ return err;
+ }
+
+ uint32_t magic = f->get_32();
+ ERR_FAIL_COND_V(magic != 0x46546C67, ERR_FILE_UNRECOGNIZED); //glTF
+ f->get_32(); // version
+ f->get_32(); // length
+
+ uint32_t chunk_length = f->get_32();
+ uint32_t chunk_type = f->get_32();
+
+ ERR_FAIL_COND_V(chunk_type != 0x4E4F534A, ERR_PARSE_ERROR); //JSON
+ Vector<uint8_t> json_data;
+ json_data.resize(chunk_length);
+ uint32_t len = f->get_buffer(json_data.ptrw(), chunk_length);
+ ERR_FAIL_COND_V(len != chunk_length, ERR_FILE_CORRUPT);
+
+ String text;
+ text.parse_utf8((const char *)json_data.ptr(), json_data.size());
+
+ 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(), false, ERR_HANDLER_SCRIPT);
+ return err;
+ }
+
+ state->json = json.get_data();
+
+ //data?
+
+ chunk_length = f->get_32();
+ chunk_type = f->get_32();
+
+ if (f->eof_reached()) {
+ return OK; //all good
+ }
+
+ ERR_FAIL_COND_V(chunk_type != 0x004E4942, ERR_PARSE_ERROR); //BIN
+
+ state->glb_data.resize(chunk_length);
+ len = f->get_buffer(state->glb_data.ptrw(), chunk_length);
+ ERR_FAIL_COND_V(len != chunk_length, ERR_FILE_CORRUPT);
+
+ return OK;
+}
+
+static Array _vec3_to_arr(const Vector3 &p_vec3) {
+ Array array;
+ array.resize(3);
+ array[0] = p_vec3.x;
+ array[1] = p_vec3.y;
+ array[2] = p_vec3.z;
+ return array;
+}
+
+static Vector3 _arr_to_vec3(const Array &p_array) {
+ ERR_FAIL_COND_V(p_array.size() != 3, Vector3());
+ return Vector3(p_array[0], p_array[1], p_array[2]);
+}
+
+static Array _quaternion_to_array(const Quaternion &p_quaternion) {
+ Array array;
+ array.resize(4);
+ array[0] = p_quaternion.x;
+ array[1] = p_quaternion.y;
+ array[2] = p_quaternion.z;
+ array[3] = p_quaternion.w;
+ return array;
+}
+
+static Quaternion _arr_to_quaternion(const Array &p_array) {
+ ERR_FAIL_COND_V(p_array.size() != 4, Quaternion());
+ return Quaternion(p_array[0], p_array[1], p_array[2], p_array[3]);
+}
+
+static Transform3D _arr_to_xform(const Array &p_array) {
+ ERR_FAIL_COND_V(p_array.size() != 16, Transform3D());
+
+ Transform3D xform;
+ xform.basis.set_axis(Vector3::AXIS_X, Vector3(p_array[0], p_array[1], p_array[2]));
+ xform.basis.set_axis(Vector3::AXIS_Y, Vector3(p_array[4], p_array[5], p_array[6]));
+ xform.basis.set_axis(Vector3::AXIS_Z, Vector3(p_array[8], p_array[9], p_array[10]));
+ xform.set_origin(Vector3(p_array[12], p_array[13], p_array[14]));
+
+ return xform;
+}
+
+static Vector<real_t> _xform_to_array(const Transform3D p_transform) {
+ Vector<real_t> array;
+ array.resize(16);
+ Vector3 axis_x = p_transform.get_basis().get_axis(Vector3::AXIS_X);
+ array.write[0] = axis_x.x;
+ array.write[1] = axis_x.y;
+ array.write[2] = axis_x.z;
+ array.write[3] = 0.0f;
+ Vector3 axis_y = p_transform.get_basis().get_axis(Vector3::AXIS_Y);
+ array.write[4] = axis_y.x;
+ array.write[5] = axis_y.y;
+ array.write[6] = axis_y.z;
+ array.write[7] = 0.0f;
+ Vector3 axis_z = p_transform.get_basis().get_axis(Vector3::AXIS_Z);
+ array.write[8] = axis_z.x;
+ array.write[9] = axis_z.y;
+ array.write[10] = axis_z.z;
+ array.write[11] = 0.0f;
+ Vector3 origin = p_transform.get_origin();
+ array.write[12] = origin.x;
+ array.write[13] = origin.y;
+ array.write[14] = origin.z;
+ array.write[15] = 1.0f;
+ return array;
+}
+
+Error GLTFDocument::_serialize_nodes(Ref<GLTFState> state) {
+ Array nodes;
+ for (int i = 0; i < state->nodes.size(); i++) {
+ Dictionary node;
+ Ref<GLTFNode> n = state->nodes[i];
+ Dictionary extensions;
+ node["extensions"] = extensions;
+ if (!n->get_name().is_empty()) {
+ node["name"] = n->get_name();
+ }
+ if (n->camera != -1) {
+ node["camera"] = n->camera;
+ }
+ if (n->light != -1) {
+ Dictionary lights_punctual;
+ extensions["KHR_lights_punctual"] = lights_punctual;
+ lights_punctual["light"] = n->light;
+ }
+ if (n->mesh != -1) {
+ node["mesh"] = n->mesh;
+ }
+ if (n->skin != -1) {
+ node["skin"] = n->skin;
+ }
+ if (n->skeleton != -1 && n->skin < 0) {
+ }
+ if (n->xform != Transform3D()) {
+ node["matrix"] = _xform_to_array(n->xform);
+ }
+
+ if (!n->rotation.is_equal_approx(Quaternion())) {
+ node["rotation"] = _quaternion_to_array(n->rotation);
+ }
+
+ if (!n->scale.is_equal_approx(Vector3(1.0f, 1.0f, 1.0f))) {
+ node["scale"] = _vec3_to_arr(n->scale);
+ }
+
+ if (!n->position.is_equal_approx(Vector3())) {
+ node["translation"] = _vec3_to_arr(n->position);
+ }
+ if (n->children.size()) {
+ Array children;
+ for (int j = 0; j < n->children.size(); j++) {
+ children.push_back(n->children[j]);
+ }
+ node["children"] = children;
+ }
+ nodes.push_back(node);
+ }
+ state->json["nodes"] = nodes;
+ return OK;
+}
+
+String GLTFDocument::_gen_unique_name(Ref<GLTFState> state, const String &p_name) {
+ const String s_name = p_name.validate_node_name();
+
+ String name;
+ int index = 1;
+ while (true) {
+ name = s_name;
+
+ if (index > 1) {
+ name += itos(index);
+ }
+ if (!state->unique_names.has(name)) {
+ break;
+ }
+ index++;
+ }
+
+ state->unique_names.insert(name);
+
+ return name;
+}
+
+String GLTFDocument::_sanitize_animation_name(const String &p_name) {
+ // Animations disallow the normal node invalid characters as well as "," and "["
+ // (See animation/animation_player.cpp::add_animation)
+
+ // TODO: Consider adding invalid_characters or a validate_animation_name to animation_player to mirror Node.
+ String name = p_name.validate_node_name();
+ name = name.replace(",", "");
+ name = name.replace("[", "");
+ return name;
+}
+
+String GLTFDocument::_gen_unique_animation_name(Ref<GLTFState> state, const String &p_name) {
+ const String s_name = _sanitize_animation_name(p_name);
+
+ String name;
+ int index = 1;
+ while (true) {
+ name = s_name;
+
+ if (index > 1) {
+ name += itos(index);
+ }
+ if (!state->unique_animation_names.has(name)) {
+ break;
+ }
+ index++;
+ }
+
+ state->unique_animation_names.insert(name);
+
+ return name;
+}
+
+String GLTFDocument::_sanitize_bone_name(const String &p_name) {
+ String name = p_name;
+ name = name.replace(":", "_");
+ name = name.replace("/", "_");
+ return name;
+}
+
+String GLTFDocument::_gen_unique_bone_name(Ref<GLTFState> state, const GLTFSkeletonIndex skel_i, const String &p_name) {
+ String s_name = _sanitize_bone_name(p_name);
+ if (s_name.is_empty()) {
+ s_name = "bone";
+ }
+ String name;
+ int index = 1;
+ while (true) {
+ name = s_name;
+
+ if (index > 1) {
+ name += "_" + itos(index);
+ }
+ if (!state->skeletons[skel_i]->unique_names.has(name)) {
+ break;
+ }
+ index++;
+ }
+
+ state->skeletons.write[skel_i]->unique_names.insert(name);
+
+ return name;
+}
+
+Error GLTFDocument::_parse_scenes(Ref<GLTFState> state) {
+ ERR_FAIL_COND_V(!state->json.has("scenes"), ERR_FILE_CORRUPT);
+ const Array &scenes = state->json["scenes"];
+ int loaded_scene = 0;
+ if (state->json.has("scene")) {
+ loaded_scene = state->json["scene"];
+ } else {
+ WARN_PRINT("The load-time scene is not defined in the glTF2 file. Picking the first scene.");
+ }
+
+ if (scenes.size()) {
+ ERR_FAIL_COND_V(loaded_scene >= scenes.size(), ERR_FILE_CORRUPT);
+ const Dictionary &s = scenes[loaded_scene];
+ ERR_FAIL_COND_V(!s.has("nodes"), ERR_UNAVAILABLE);
+ const Array &nodes = s["nodes"];
+ for (int j = 0; j < nodes.size(); j++) {
+ state->root_nodes.push_back(nodes[j]);
+ }
+
+ if (s.has("name") && !String(s["name"]).is_empty() && !((String)s["name"]).begins_with("Scene")) {
+ state->scene_name = _gen_unique_name(state, s["name"]);
+ } else {
+ state->scene_name = _gen_unique_name(state, state->filename);
+ }
+ }
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_nodes(Ref<GLTFState> state) {
+ ERR_FAIL_COND_V(!state->json.has("nodes"), ERR_FILE_CORRUPT);
+ const Array &nodes = state->json["nodes"];
+ for (int i = 0; i < nodes.size(); i++) {
+ Ref<GLTFNode> node;
+ node.instantiate();
+ const Dictionary &n = nodes[i];
+
+ if (n.has("name")) {
+ node->set_name(n["name"]);
+ }
+ if (n.has("camera")) {
+ node->camera = n["camera"];
+ }
+ if (n.has("mesh")) {
+ node->mesh = n["mesh"];
+ }
+ if (n.has("skin")) {
+ node->skin = n["skin"];
+ }
+ if (n.has("matrix")) {
+ node->xform = _arr_to_xform(n["matrix"]);
+ } else {
+ if (n.has("translation")) {
+ node->position = _arr_to_vec3(n["translation"]);
+ }
+ if (n.has("rotation")) {
+ node->rotation = _arr_to_quaternion(n["rotation"]);
+ }
+ if (n.has("scale")) {
+ node->scale = _arr_to_vec3(n["scale"]);
+ }
+
+ node->xform.basis.set_quaternion_scale(node->rotation, node->scale);
+ node->xform.origin = node->position;
+ }
+
+ if (n.has("extensions")) {
+ Dictionary extensions = n["extensions"];
+ if (extensions.has("KHR_lights_punctual")) {
+ Dictionary lights_punctual = extensions["KHR_lights_punctual"];
+ if (lights_punctual.has("light")) {
+ GLTFLightIndex light = lights_punctual["light"];
+ node->light = light;
+ }
+ }
+ }
+
+ if (n.has("children")) {
+ const Array &children = n["children"];
+ for (int j = 0; j < children.size(); j++) {
+ node->children.push_back(children[j]);
+ }
+ }
+
+ state->nodes.push_back(node);
+ }
+
+ // 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_V(child_i, state->nodes.size(), ERR_FILE_CORRUPT);
+ ERR_CONTINUE(state->nodes[child_i]->parent != -1); //node already has a parent, wtf.
+
+ state->nodes.write[child_i]->parent = node_i;
+ }
+ }
+
+ _compute_node_heights(state);
+
+ return OK;
+}
+
+void GLTFDocument::_compute_node_heights(Ref<GLTFState> state) {
+ state->root_nodes.clear();
+ for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); ++node_i) {
+ Ref<GLTFNode> node = state->nodes[node_i];
+ node->height = 0;
+
+ GLTFNodeIndex current_i = node_i;
+ while (current_i >= 0) {
+ const GLTFNodeIndex parent_i = state->nodes[current_i]->parent;
+ if (parent_i >= 0) {
+ ++node->height;
+ }
+ current_i = parent_i;
+ }
+
+ if (node->height == 0) {
+ state->root_nodes.push_back(node_i);
+ }
+ }
+}
+
+static Vector<uint8_t> _parse_base64_uri(const String &uri) {
+ int start = uri.find(",");
+ ERR_FAIL_COND_V(start == -1, Vector<uint8_t>());
+
+ CharString substr = uri.substr(start + 1).ascii();
+
+ int strlen = substr.length();
+
+ Vector<uint8_t> buf;
+ buf.resize(strlen / 4 * 3 + 1 + 1);
+
+ size_t len = 0;
+ ERR_FAIL_COND_V(CryptoCore::b64_decode(buf.ptrw(), buf.size(), &len, (unsigned char *)substr.get_data(), strlen) != OK, Vector<uint8_t>());
+
+ buf.resize(len);
+
+ return buf;
+}
+Error GLTFDocument::_encode_buffer_glb(Ref<GLTFState> state, const String &p_path) {
+ print_verbose("glTF: Total buffers: " + itos(state->buffers.size()));
+
+ if (!state->buffers.size()) {
+ return OK;
+ }
+ Array buffers;
+ if (state->buffers.size()) {
+ Vector<uint8_t> buffer_data = state->buffers[0];
+ Dictionary gltf_buffer;
+
+ gltf_buffer["byteLength"] = buffer_data.size();
+ buffers.push_back(gltf_buffer);
+ }
+
+ for (GLTFBufferIndex i = 1; i < state->buffers.size() - 1; i++) {
+ Vector<uint8_t> buffer_data = state->buffers[i];
+ Dictionary gltf_buffer;
+ String filename = p_path.get_basename().get_file() + itos(i) + ".bin";
+ String path = p_path.get_base_dir() + "/" + filename;
+ Error err;
+ FileAccessRef f = FileAccess::open(path, FileAccess::WRITE, &err);
+ if (!f) {
+ return err;
+ }
+ if (buffer_data.size() == 0) {
+ return OK;
+ }
+ f->create(FileAccess::ACCESS_RESOURCES);
+ f->store_buffer(buffer_data.ptr(), buffer_data.size());
+ f->close();
+ gltf_buffer["uri"] = filename;
+ gltf_buffer["byteLength"] = buffer_data.size();
+ buffers.push_back(gltf_buffer);
+ }
+ state->json["buffers"] = buffers;
+
+ return OK;
+}
+
+Error GLTFDocument::_encode_buffer_bins(Ref<GLTFState> state, const String &p_path) {
+ print_verbose("glTF: Total buffers: " + itos(state->buffers.size()));
+
+ if (!state->buffers.size()) {
+ return OK;
+ }
+ Array buffers;
+
+ for (GLTFBufferIndex i = 0; i < state->buffers.size(); i++) {
+ Vector<uint8_t> buffer_data = state->buffers[i];
+ Dictionary gltf_buffer;
+ String filename = p_path.get_basename().get_file() + itos(i) + ".bin";
+ String path = p_path.get_base_dir() + "/" + filename;
+ Error err;
+ FileAccessRef f = FileAccess::open(path, FileAccess::WRITE, &err);
+ if (!f) {
+ return err;
+ }
+ if (buffer_data.size() == 0) {
+ return OK;
+ }
+ f->create(FileAccess::ACCESS_RESOURCES);
+ f->store_buffer(buffer_data.ptr(), buffer_data.size());
+ f->close();
+ gltf_buffer["uri"] = filename;
+ gltf_buffer["byteLength"] = buffer_data.size();
+ buffers.push_back(gltf_buffer);
+ }
+ state->json["buffers"] = buffers;
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_buffers(Ref<GLTFState> state, const String &p_base_path) {
+ if (!state->json.has("buffers")) {
+ return OK;
+ }
+
+ const Array &buffers = state->json["buffers"];
+ for (GLTFBufferIndex i = 0; i < buffers.size(); i++) {
+ if (i == 0 && state->glb_data.size()) {
+ state->buffers.push_back(state->glb_data);
+
+ } else {
+ const Dictionary &buffer = buffers[i];
+ if (buffer.has("uri")) {
+ Vector<uint8_t> buffer_data;
+ String uri = buffer["uri"];
+
+ if (uri.begins_with("data:")) { // Embedded data using base64.
+ // Validate data MIME types and throw an error if it's one we don't know/support.
+ if (!uri.begins_with("data:application/octet-stream;base64") &&
+ !uri.begins_with("data:application/gltf-buffer;base64")) {
+ ERR_PRINT("glTF: Got buffer with an unknown URI data type: " + uri);
+ }
+ buffer_data = _parse_base64_uri(uri);
+ } else { // Relative path to an external image file.
+ uri = p_base_path.plus_file(uri).replace("\\", "/"); // Fix for Windows.
+ buffer_data = FileAccess::get_file_as_array(uri);
+ ERR_FAIL_COND_V_MSG(buffer.size() == 0, ERR_PARSE_ERROR, "glTF: Couldn't load binary file as an array: " + uri);
+ }
+
+ ERR_FAIL_COND_V(!buffer.has("byteLength"), ERR_PARSE_ERROR);
+ int byteLength = buffer["byteLength"];
+ ERR_FAIL_COND_V(byteLength < buffer_data.size(), ERR_PARSE_ERROR);
+ state->buffers.push_back(buffer_data);
+ }
+ }
+ }
+
+ print_verbose("glTF: Total buffers: " + itos(state->buffers.size()));
+
+ return OK;
+}
+
+Error GLTFDocument::_encode_buffer_views(Ref<GLTFState> state) {
+ Array buffers;
+ for (GLTFBufferViewIndex i = 0; i < state->buffer_views.size(); i++) {
+ Dictionary d;
+
+ Ref<GLTFBufferView> buffer_view = state->buffer_views[i];
+
+ d["buffer"] = buffer_view->buffer;
+ d["byteLength"] = buffer_view->byte_length;
+
+ d["byteOffset"] = buffer_view->byte_offset;
+
+ if (buffer_view->byte_stride != -1) {
+ d["byteStride"] = buffer_view->byte_stride;
+ }
+
+ // TODO Sparse
+ // d["target"] = buffer_view->indices;
+
+ ERR_FAIL_COND_V(!d.has("buffer"), ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(!d.has("byteLength"), ERR_INVALID_DATA);
+ 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;
+}
+
+Error GLTFDocument::_parse_buffer_views(Ref<GLTFState> state) {
+ if (!state->json.has("bufferViews")) {
+ return OK;
+ }
+ const Array &buffers = state->json["bufferViews"];
+ for (GLTFBufferViewIndex i = 0; i < buffers.size(); i++) {
+ const Dictionary &d = buffers[i];
+
+ Ref<GLTFBufferView> buffer_view;
+ buffer_view.instantiate();
+
+ ERR_FAIL_COND_V(!d.has("buffer"), ERR_PARSE_ERROR);
+ buffer_view->buffer = d["buffer"];
+ ERR_FAIL_COND_V(!d.has("byteLength"), ERR_PARSE_ERROR);
+ buffer_view->byte_length = d["byteLength"];
+
+ if (d.has("byteOffset")) {
+ buffer_view->byte_offset = d["byteOffset"];
+ }
+
+ if (d.has("byteStride")) {
+ buffer_view->byte_stride = d["byteStride"];
+ }
+
+ if (d.has("target")) {
+ const int target = d["target"];
+ buffer_view->indices = target == GLTFDocument::ELEMENT_ARRAY_BUFFER;
+ }
+
+ state->buffer_views.push_back(buffer_view);
+ }
+
+ print_verbose("glTF: Total buffer views: " + itos(state->buffer_views.size()));
+
+ return OK;
+}
+
+Error GLTFDocument::_encode_accessors(Ref<GLTFState> state) {
+ Array accessors;
+ for (GLTFAccessorIndex i = 0; i < state->accessors.size(); i++) {
+ Dictionary d;
+
+ Ref<GLTFAccessor> accessor = state->accessors[i];
+ d["componentType"] = accessor->component_type;
+ d["count"] = accessor->count;
+ d["type"] = _get_accessor_type_name(accessor->type);
+ d["byteOffset"] = accessor->byte_offset;
+ d["normalized"] = accessor->normalized;
+ d["max"] = accessor->max;
+ d["min"] = accessor->min;
+ d["bufferView"] = accessor->buffer_view; //optional because it may be sparse...
+
+ // Dictionary s;
+ // s["count"] = accessor->sparse_count;
+ // ERR_FAIL_COND_V(!s.has("count"), ERR_PARSE_ERROR);
+
+ // s["indices"] = accessor->sparse_accessors;
+ // ERR_FAIL_COND_V(!s.has("indices"), ERR_PARSE_ERROR);
+
+ // Dictionary si;
+
+ // si["bufferView"] = accessor->sparse_indices_buffer_view;
+
+ // ERR_FAIL_COND_V(!si.has("bufferView"), ERR_PARSE_ERROR);
+ // si["componentType"] = accessor->sparse_indices_component_type;
+
+ // if (si.has("byteOffset")) {
+ // si["byteOffset"] = accessor->sparse_indices_byte_offset;
+ // }
+
+ // ERR_FAIL_COND_V(!si.has("componentType"), ERR_PARSE_ERROR);
+ // s["indices"] = si;
+ // Dictionary sv;
+
+ // sv["bufferView"] = accessor->sparse_values_buffer_view;
+ // if (sv.has("byteOffset")) {
+ // sv["byteOffset"] = accessor->sparse_values_byte_offset;
+ // }
+ // ERR_FAIL_COND_V(!sv.has("bufferView"), ERR_PARSE_ERROR);
+ // s["values"] = sv;
+ // ERR_FAIL_COND_V(!s.has("values"), ERR_PARSE_ERROR);
+ // d["sparse"] = s;
+ 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()));
+
+ return OK;
+}
+
+String GLTFDocument::_get_accessor_type_name(const GLTFDocument::GLTFType p_type) {
+ if (p_type == GLTFDocument::TYPE_SCALAR) {
+ return "SCALAR";
+ }
+ if (p_type == GLTFDocument::TYPE_VEC2) {
+ return "VEC2";
+ }
+ if (p_type == GLTFDocument::TYPE_VEC3) {
+ return "VEC3";
+ }
+ if (p_type == GLTFDocument::TYPE_VEC4) {
+ return "VEC4";
+ }
+
+ if (p_type == GLTFDocument::TYPE_MAT2) {
+ return "MAT2";
+ }
+ if (p_type == GLTFDocument::TYPE_MAT3) {
+ return "MAT3";
+ }
+ if (p_type == GLTFDocument::TYPE_MAT4) {
+ return "MAT4";
+ }
+ ERR_FAIL_V("SCALAR");
+}
+
+GLTFDocument::GLTFType GLTFDocument::_get_type_from_str(const String &p_string) {
+ if (p_string == "SCALAR") {
+ return GLTFDocument::TYPE_SCALAR;
+ }
+
+ if (p_string == "VEC2") {
+ return GLTFDocument::TYPE_VEC2;
+ }
+ if (p_string == "VEC3") {
+ return GLTFDocument::TYPE_VEC3;
+ }
+ if (p_string == "VEC4") {
+ return GLTFDocument::TYPE_VEC4;
+ }
+
+ if (p_string == "MAT2") {
+ return GLTFDocument::TYPE_MAT2;
+ }
+ if (p_string == "MAT3") {
+ return GLTFDocument::TYPE_MAT3;
+ }
+ if (p_string == "MAT4") {
+ return GLTFDocument::TYPE_MAT4;
+ }
+
+ ERR_FAIL_V(GLTFDocument::TYPE_SCALAR);
+}
+
+Error GLTFDocument::_parse_accessors(Ref<GLTFState> state) {
+ if (!state->json.has("accessors")) {
+ return OK;
+ }
+ const Array &accessors = state->json["accessors"];
+ for (GLTFAccessorIndex i = 0; i < accessors.size(); i++) {
+ const Dictionary &d = accessors[i];
+
+ Ref<GLTFAccessor> accessor;
+ accessor.instantiate();
+
+ ERR_FAIL_COND_V(!d.has("componentType"), ERR_PARSE_ERROR);
+ accessor->component_type = d["componentType"];
+ ERR_FAIL_COND_V(!d.has("count"), ERR_PARSE_ERROR);
+ accessor->count = d["count"];
+ ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
+ accessor->type = _get_type_from_str(d["type"]);
+
+ if (d.has("bufferView")) {
+ accessor->buffer_view = d["bufferView"]; //optional because it may be sparse...
+ }
+
+ if (d.has("byteOffset")) {
+ accessor->byte_offset = d["byteOffset"];
+ }
+
+ if (d.has("normalized")) {
+ accessor->normalized = d["normalized"];
+ }
+
+ if (d.has("max")) {
+ accessor->max = d["max"];
+ }
+
+ if (d.has("min")) {
+ accessor->min = d["min"];
+ }
+
+ if (d.has("sparse")) {
+ //eeh..
+
+ const Dictionary &s = d["sparse"];
+
+ ERR_FAIL_COND_V(!s.has("count"), ERR_PARSE_ERROR);
+ accessor->sparse_count = s["count"];
+ ERR_FAIL_COND_V(!s.has("indices"), ERR_PARSE_ERROR);
+ const Dictionary &si = s["indices"];
+
+ ERR_FAIL_COND_V(!si.has("bufferView"), ERR_PARSE_ERROR);
+ accessor->sparse_indices_buffer_view = si["bufferView"];
+ ERR_FAIL_COND_V(!si.has("componentType"), ERR_PARSE_ERROR);
+ accessor->sparse_indices_component_type = si["componentType"];
+
+ if (si.has("byteOffset")) {
+ accessor->sparse_indices_byte_offset = si["byteOffset"];
+ }
+
+ ERR_FAIL_COND_V(!s.has("values"), ERR_PARSE_ERROR);
+ const Dictionary &sv = s["values"];
+
+ ERR_FAIL_COND_V(!sv.has("bufferView"), ERR_PARSE_ERROR);
+ accessor->sparse_values_buffer_view = sv["bufferView"];
+ if (sv.has("byteOffset")) {
+ accessor->sparse_values_byte_offset = sv["byteOffset"];
+ }
+ }
+
+ state->accessors.push_back(accessor);
+ }
+
+ print_verbose("glTF: Total accessors: " + itos(state->accessors.size()));
+
+ return OK;
+}
+
+double GLTFDocument::_filter_number(double p_float) {
+ if (Math::is_nan(p_float)) {
+ return 0.0f;
+ }
+ return p_float;
+}
+
+String GLTFDocument::_get_component_type_name(const uint32_t p_component) {
+ switch (p_component) {
+ case GLTFDocument::COMPONENT_TYPE_BYTE:
+ return "Byte";
+ case GLTFDocument::COMPONENT_TYPE_UNSIGNED_BYTE:
+ return "UByte";
+ case GLTFDocument::COMPONENT_TYPE_SHORT:
+ return "Short";
+ case GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT:
+ return "UShort";
+ case GLTFDocument::COMPONENT_TYPE_INT:
+ return "Int";
+ case GLTFDocument::COMPONENT_TYPE_FLOAT:
+ return "Float";
+ }
+
+ return "<Error>";
+}
+
+String GLTFDocument::_get_type_name(const GLTFType p_component) {
+ static const char *names[] = {
+ "float",
+ "vec2",
+ "vec3",
+ "vec4",
+ "mat2",
+ "mat3",
+ "mat4"
+ };
+
+ return names[p_component];
+}
+
+Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> state, const double *src, const int count, const GLTFType type, const int component_type, const bool normalized, const int byte_offset, const bool for_vertex, GLTFBufferViewIndex &r_accessor) {
+ const int component_count_for_type[7] = {
+ 1, 2, 3, 4, 4, 9, 16
+ };
+
+ const int component_count = component_count_for_type[type];
+ const int component_size = _get_component_type_size(component_type);
+ ERR_FAIL_COND_V(component_size == 0, FAILED);
+
+ int skip_every = 0;
+ int skip_bytes = 0;
+ //special case of alignments, as described in spec
+ switch (component_type) {
+ case COMPONENT_TYPE_BYTE:
+ case COMPONENT_TYPE_UNSIGNED_BYTE: {
+ if (type == TYPE_MAT2) {
+ skip_every = 2;
+ skip_bytes = 2;
+ }
+ if (type == TYPE_MAT3) {
+ skip_every = 3;
+ skip_bytes = 1;
+ }
+ } break;
+ case COMPONENT_TYPE_SHORT:
+ case COMPONENT_TYPE_UNSIGNED_SHORT: {
+ if (type == TYPE_MAT3) {
+ skip_every = 6;
+ skip_bytes = 4;
+ }
+ } break;
+ default: {
+ }
+ }
+
+ Ref<GLTFBufferView> bv;
+ bv.instantiate();
+ const uint32_t offset = bv->byte_offset = byte_offset;
+ Vector<uint8_t> &gltf_buffer = state->buffers.write[0];
+
+ int stride = _get_component_type_size(component_type);
+ if (for_vertex && stride % 4) {
+ stride += 4 - (stride % 4); //according to spec must be multiple of 4
+ }
+ //use to debug
+ print_verbose("glTF: encoding type " + _get_type_name(type) + " component type: " + _get_component_type_name(component_type) + " stride: " + itos(stride) + " amount " + itos(count));
+
+ print_verbose("glTF: encoding accessor offset " + itos(byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(gltf_buffer.size()) + " view len " + itos(bv->byte_length));
+
+ const int buffer_end = (stride * (count - 1)) + _get_component_type_size(component_type);
+ // TODO define bv->byte_stride
+ bv->byte_offset = gltf_buffer.size();
+
+ switch (component_type) {
+ case COMPONENT_TYPE_BYTE: {
+ Vector<int8_t> buffer;
+ buffer.resize(count * component_count);
+ int32_t dst_i = 0;
+ for (int i = 0; i < count; i++) {
+ for (int j = 0; j < component_count; j++) {
+ if (skip_every && j > 0 && (j % skip_every) == 0) {
+ dst_i += skip_bytes;
+ }
+ double d = *src;
+ if (normalized) {
+ buffer.write[dst_i] = d * 128.0;
+ } else {
+ buffer.write[dst_i] = d;
+ }
+ src++;
+ dst_i++;
+ }
+ }
+ int64_t old_size = gltf_buffer.size();
+ gltf_buffer.resize(old_size + (buffer.size() * sizeof(int8_t)));
+ memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(int8_t));
+ bv->byte_length = buffer.size() * sizeof(int8_t);
+ } break;
+ case COMPONENT_TYPE_UNSIGNED_BYTE: {
+ Vector<uint8_t> buffer;
+ buffer.resize(count * component_count);
+ int32_t dst_i = 0;
+ for (int i = 0; i < count; i++) {
+ for (int j = 0; j < component_count; j++) {
+ if (skip_every && j > 0 && (j % skip_every) == 0) {
+ dst_i += skip_bytes;
+ }
+ double d = *src;
+ if (normalized) {
+ buffer.write[dst_i] = d * 255.0;
+ } else {
+ buffer.write[dst_i] = d;
+ }
+ src++;
+ dst_i++;
+ }
+ }
+ gltf_buffer.append_array(buffer);
+ bv->byte_length = buffer.size() * sizeof(uint8_t);
+ } break;
+ case COMPONENT_TYPE_SHORT: {
+ Vector<int16_t> buffer;
+ buffer.resize(count * component_count);
+ int32_t dst_i = 0;
+ for (int i = 0; i < count; i++) {
+ for (int j = 0; j < component_count; j++) {
+ if (skip_every && j > 0 && (j % skip_every) == 0) {
+ dst_i += skip_bytes;
+ }
+ double d = *src;
+ if (normalized) {
+ buffer.write[dst_i] = d * 32768.0;
+ } else {
+ buffer.write[dst_i] = d;
+ }
+ src++;
+ dst_i++;
+ }
+ }
+ int64_t old_size = gltf_buffer.size();
+ gltf_buffer.resize(old_size + (buffer.size() * sizeof(int16_t)));
+ memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(int16_t));
+ bv->byte_length = buffer.size() * sizeof(int16_t);
+ } break;
+ case COMPONENT_TYPE_UNSIGNED_SHORT: {
+ Vector<uint16_t> buffer;
+ buffer.resize(count * component_count);
+ int32_t dst_i = 0;
+ for (int i = 0; i < count; i++) {
+ for (int j = 0; j < component_count; j++) {
+ if (skip_every && j > 0 && (j % skip_every) == 0) {
+ dst_i += skip_bytes;
+ }
+ double d = *src;
+ if (normalized) {
+ buffer.write[dst_i] = d * 65535.0;
+ } else {
+ buffer.write[dst_i] = d;
+ }
+ src++;
+ dst_i++;
+ }
+ }
+ int64_t old_size = gltf_buffer.size();
+ gltf_buffer.resize(old_size + (buffer.size() * sizeof(uint16_t)));
+ memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(uint16_t));
+ bv->byte_length = buffer.size() * sizeof(uint16_t);
+ } break;
+ case COMPONENT_TYPE_INT: {
+ Vector<int> buffer;
+ buffer.resize(count * component_count);
+ int32_t dst_i = 0;
+ for (int i = 0; i < count; i++) {
+ for (int j = 0; j < component_count; j++) {
+ if (skip_every && j > 0 && (j % skip_every) == 0) {
+ dst_i += skip_bytes;
+ }
+ double d = *src;
+ buffer.write[dst_i] = d;
+ src++;
+ dst_i++;
+ }
+ }
+ int64_t old_size = gltf_buffer.size();
+ gltf_buffer.resize(old_size + (buffer.size() * sizeof(int32_t)));
+ memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(int32_t));
+ bv->byte_length = buffer.size() * sizeof(int32_t);
+ } break;
+ case COMPONENT_TYPE_FLOAT: {
+ Vector<float> buffer;
+ buffer.resize(count * component_count);
+ int32_t dst_i = 0;
+ for (int i = 0; i < count; i++) {
+ for (int j = 0; j < component_count; j++) {
+ if (skip_every && j > 0 && (j % skip_every) == 0) {
+ dst_i += skip_bytes;
+ }
+ double d = *src;
+ buffer.write[dst_i] = d;
+ src++;
+ dst_i++;
+ }
+ }
+ int64_t old_size = gltf_buffer.size();
+ gltf_buffer.resize(old_size + (buffer.size() * sizeof(float)));
+ memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(float));
+ bv->byte_length = buffer.size() * sizeof(float);
+ } break;
+ }
+ ERR_FAIL_COND_V(buffer_end > bv->byte_length, ERR_INVALID_DATA);
+
+ ERR_FAIL_COND_V((int)(offset + buffer_end) > gltf_buffer.size(), ERR_INVALID_DATA);
+ r_accessor = bv->buffer = state->buffer_views.size();
+ state->buffer_views.push_back(bv);
+ return OK;
+}
+
+Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> state, double *dst, const GLTFBufferViewIndex p_buffer_view, const int skip_every, const int skip_bytes, const int element_size, const int count, const GLTFType type, const int component_count, const int component_type, const int component_size, const bool normalized, const int byte_offset, const bool for_vertex) {
+ const Ref<GLTFBufferView> bv = state->buffer_views[p_buffer_view];
+
+ int stride = element_size;
+ if (bv->byte_stride != -1) {
+ stride = bv->byte_stride;
+ }
+ if (for_vertex && stride % 4) {
+ stride += 4 - (stride % 4); //according to spec must be multiple of 4
+ }
+
+ ERR_FAIL_INDEX_V(bv->buffer, state->buffers.size(), ERR_PARSE_ERROR);
+
+ const uint32_t offset = bv->byte_offset + byte_offset;
+ Vector<uint8_t> buffer = state->buffers[bv->buffer]; //copy on write, so no performance hit
+ const uint8_t *bufptr = buffer.ptr();
+
+ //use to debug
+ print_verbose("glTF: type " + _get_type_name(type) + " component type: " + _get_component_type_name(component_type) + " stride: " + itos(stride) + " amount " + itos(count));
+ print_verbose("glTF: accessor offset " + itos(byte_offset) + " view offset: " + itos(bv->byte_offset) + " total buffer len: " + itos(buffer.size()) + " view len " + itos(bv->byte_length));
+
+ const int buffer_end = (stride * (count - 1)) + element_size;
+ ERR_FAIL_COND_V(buffer_end > bv->byte_length, ERR_PARSE_ERROR);
+
+ ERR_FAIL_COND_V((int)(offset + buffer_end) > buffer.size(), ERR_PARSE_ERROR);
+
+ //fill everything as doubles
+
+ for (int i = 0; i < count; i++) {
+ const uint8_t *src = &bufptr[offset + i * stride];
+
+ for (int j = 0; j < component_count; j++) {
+ if (skip_every && j > 0 && (j % skip_every) == 0) {
+ src += skip_bytes;
+ }
+
+ double d = 0;
+
+ switch (component_type) {
+ case COMPONENT_TYPE_BYTE: {
+ int8_t b = int8_t(*src);
+ if (normalized) {
+ d = (double(b) / 128.0);
+ } else {
+ d = double(b);
+ }
+ } break;
+ case COMPONENT_TYPE_UNSIGNED_BYTE: {
+ uint8_t b = *src;
+ if (normalized) {
+ d = (double(b) / 255.0);
+ } else {
+ d = double(b);
+ }
+ } break;
+ case COMPONENT_TYPE_SHORT: {
+ int16_t s = *(int16_t *)src;
+ if (normalized) {
+ d = (double(s) / 32768.0);
+ } else {
+ d = double(s);
+ }
+ } break;
+ case COMPONENT_TYPE_UNSIGNED_SHORT: {
+ uint16_t s = *(uint16_t *)src;
+ if (normalized) {
+ d = (double(s) / 65535.0);
+ } else {
+ d = double(s);
+ }
+ } break;
+ case COMPONENT_TYPE_INT: {
+ d = *(int *)src;
+ } break;
+ case COMPONENT_TYPE_FLOAT: {
+ d = *(float *)src;
+ } break;
+ }
+
+ *dst++ = d;
+ src += component_size;
+ }
+ }
+
+ return OK;
+}
+
+int GLTFDocument::_get_component_type_size(const int component_type) {
+ switch (component_type) {
+ case COMPONENT_TYPE_BYTE:
+ case COMPONENT_TYPE_UNSIGNED_BYTE:
+ return 1;
+ break;
+ case COMPONENT_TYPE_SHORT:
+ case COMPONENT_TYPE_UNSIGNED_SHORT:
+ return 2;
+ break;
+ case COMPONENT_TYPE_INT:
+ case COMPONENT_TYPE_FLOAT:
+ return 4;
+ break;
+ default: {
+ ERR_FAIL_V(0);
+ }
+ }
+ return 0;
+}
+
+Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ //spec, for reference:
+ //https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment
+
+ ERR_FAIL_INDEX_V(p_accessor, state->accessors.size(), Vector<double>());
+
+ const Ref<GLTFAccessor> a = state->accessors[p_accessor];
+
+ const int component_count_for_type[7] = {
+ 1, 2, 3, 4, 4, 9, 16
+ };
+
+ const int component_count = component_count_for_type[a->type];
+ const int component_size = _get_component_type_size(a->component_type);
+ ERR_FAIL_COND_V(component_size == 0, Vector<double>());
+ int element_size = component_count * component_size;
+
+ int skip_every = 0;
+ int skip_bytes = 0;
+ //special case of alignments, as described in spec
+ switch (a->component_type) {
+ case COMPONENT_TYPE_BYTE:
+ case COMPONENT_TYPE_UNSIGNED_BYTE: {
+ if (a->type == TYPE_MAT2) {
+ skip_every = 2;
+ skip_bytes = 2;
+ element_size = 8; //override for this case
+ }
+ if (a->type == TYPE_MAT3) {
+ skip_every = 3;
+ skip_bytes = 1;
+ element_size = 12; //override for this case
+ }
+ } break;
+ case COMPONENT_TYPE_SHORT:
+ case COMPONENT_TYPE_UNSIGNED_SHORT: {
+ if (a->type == TYPE_MAT3) {
+ skip_every = 6;
+ skip_bytes = 4;
+ element_size = 16; //override for this case
+ }
+ } break;
+ default: {
+ }
+ }
+
+ Vector<double> dst_buffer;
+ dst_buffer.resize(component_count * a->count);
+ double *dst = dst_buffer.ptrw();
+
+ if (a->buffer_view >= 0) {
+ ERR_FAIL_INDEX_V(a->buffer_view, state->buffer_views.size(), Vector<double>());
+
+ const Error err = _decode_buffer_view(state, dst, a->buffer_view, skip_every, skip_bytes, element_size, a->count, a->type, component_count, a->component_type, component_size, a->normalized, a->byte_offset, p_for_vertex);
+ if (err != OK) {
+ return Vector<double>();
+ }
+ } else {
+ //fill with zeros, as bufferview is not defined.
+ for (int i = 0; i < (a->count * component_count); i++) {
+ dst_buffer.write[i] = 0;
+ }
+ }
+
+ if (a->sparse_count > 0) {
+ // I could not find any file using this, so this code is so far untested
+ Vector<double> indices;
+ indices.resize(a->sparse_count);
+ const int indices_component_size = _get_component_type_size(a->sparse_indices_component_type);
+
+ Error err = _decode_buffer_view(state, indices.ptrw(), a->sparse_indices_buffer_view, 0, 0, indices_component_size, a->sparse_count, TYPE_SCALAR, 1, a->sparse_indices_component_type, indices_component_size, false, a->sparse_indices_byte_offset, false);
+ if (err != OK) {
+ return Vector<double>();
+ }
+
+ Vector<double> data;
+ data.resize(component_count * a->sparse_count);
+ err = _decode_buffer_view(state, data.ptrw(), a->sparse_values_buffer_view, skip_every, skip_bytes, element_size, a->sparse_count, a->type, component_count, a->component_type, component_size, a->normalized, a->sparse_values_byte_offset, p_for_vertex);
+ if (err != OK) {
+ return Vector<double>();
+ }
+
+ for (int i = 0; i < indices.size(); i++) {
+ const int write_offset = int(indices[i]) * component_count;
+
+ for (int j = 0; j < component_count; j++) {
+ dst[write_offset + j] = data[i * component_count + j];
+ }
+ }
+ }
+
+ return dst_buffer;
+}
+
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> state, const Vector<int32_t> p_attribs, const bool p_for_vertex) {
+ if (p_attribs.size() == 0) {
+ return -1;
+ }
+ const int element_count = 1;
+ const int ret_size = p_attribs.size();
+ Vector<double> attribs;
+ attribs.resize(ret_size);
+ Vector<double> type_max;
+ type_max.resize(element_count);
+ Vector<double> type_min;
+ type_min.resize(element_count);
+ for (int i = 0; i < p_attribs.size(); i++) {
+ attribs.write[i] = Math::snapped(p_attribs[i], 1.0);
+ if (i == 0) {
+ for (int32_t type_i = 0; type_i < element_count; type_i++) {
+ type_max.write[type_i] = attribs[(i * element_count) + type_i];
+ type_min.write[type_i] = attribs[(i * element_count) + type_i];
+ }
+ }
+ for (int32_t type_i = 0; type_i < element_count; type_i++) {
+ type_max.write[type_i] = MAX(attribs[(i * element_count) + type_i], type_max[type_i]);
+ type_min.write[type_i] = MIN(attribs[(i * element_count) + type_i], type_min[type_i]);
+ type_max.write[type_i] = _filter_number(type_max.write[type_i]);
+ type_min.write[type_i] = _filter_number(type_min.write[type_i]);
+ }
+ }
+
+ ERR_FAIL_COND_V(attribs.size() == 0, -1);
+
+ Ref<GLTFAccessor> accessor;
+ accessor.instantiate();
+ GLTFBufferIndex buffer_view_i;
+ int64_t size = state->buffers[0].size();
+ const GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR;
+ const int component_type = GLTFDocument::COMPONENT_TYPE_INT;
+
+ accessor->max = type_max;
+ accessor->min = type_min;
+ accessor->normalized = false;
+ accessor->count = ret_size;
+ accessor->type = type;
+ accessor->component_type = component_type;
+ accessor->byte_offset = 0;
+ Error err = _encode_buffer_view(state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ if (err != OK) {
+ return -1;
+ }
+ accessor->buffer_view = buffer_view_i;
+ state->accessors.push_back(accessor);
+ return state->accessors.size() - 1;
+}
+
+Vector<int> GLTFDocument::_decode_accessor_as_ints(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+ Vector<int> ret;
+
+ if (attribs.size() == 0) {
+ return ret;
+ }
+
+ const double *attribs_ptr = attribs.ptr();
+ const int ret_size = attribs.size();
+ ret.resize(ret_size);
+ {
+ for (int i = 0; i < ret_size; i++) {
+ ret.write[i] = int(attribs_ptr[i]);
+ }
+ }
+ return ret;
+}
+
+Vector<float> GLTFDocument::_decode_accessor_as_floats(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+ Vector<float> ret;
+
+ if (attribs.size() == 0) {
+ return ret;
+ }
+
+ const double *attribs_ptr = attribs.ptr();
+ const int ret_size = attribs.size();
+ ret.resize(ret_size);
+ {
+ for (int i = 0; i < ret_size; i++) {
+ ret.write[i] = float(attribs_ptr[i]);
+ }
+ }
+ return ret;
+}
+
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref<GLTFState> state, const Vector<Vector2> p_attribs, const bool p_for_vertex) {
+ if (p_attribs.size() == 0) {
+ return -1;
+ }
+ const int element_count = 2;
+
+ const int ret_size = p_attribs.size() * element_count;
+ Vector<double> attribs;
+ attribs.resize(ret_size);
+ Vector<double> type_max;
+ type_max.resize(element_count);
+ Vector<double> type_min;
+ type_min.resize(element_count);
+
+ for (int i = 0; i < p_attribs.size(); i++) {
+ Vector2 attrib = p_attribs[i];
+ attribs.write[(i * element_count) + 0] = Math::snapped(attrib.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(attrib.y, CMP_NORMALIZE_TOLERANCE);
+ _calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
+ }
+
+ ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
+
+ Ref<GLTFAccessor> accessor;
+ accessor.instantiate();
+ GLTFBufferIndex buffer_view_i;
+ int64_t size = state->buffers[0].size();
+ const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC2;
+ const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+
+ accessor->max = type_max;
+ accessor->min = type_min;
+ accessor->normalized = false;
+ accessor->count = p_attribs.size();
+ accessor->type = type;
+ accessor->component_type = component_type;
+ accessor->byte_offset = 0;
+ Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ if (err != OK) {
+ return -1;
+ }
+ accessor->buffer_view = buffer_view_i;
+ state->accessors.push_back(accessor);
+ return state->accessors.size() - 1;
+}
+
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref<GLTFState> state, const Vector<Color> p_attribs, const bool p_for_vertex) {
+ if (p_attribs.size() == 0) {
+ return -1;
+ }
+
+ const int ret_size = p_attribs.size() * 4;
+ Vector<double> attribs;
+ attribs.resize(ret_size);
+
+ const int element_count = 4;
+ Vector<double> type_max;
+ type_max.resize(element_count);
+ Vector<double> type_min;
+ type_min.resize(element_count);
+ for (int i = 0; i < p_attribs.size(); i++) {
+ Color attrib = p_attribs[i];
+ attribs.write[(i * element_count) + 0] = Math::snapped(attrib.r, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(attrib.g, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 2] = Math::snapped(attrib.b, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 3] = Math::snapped(attrib.a, CMP_NORMALIZE_TOLERANCE);
+
+ _calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
+ }
+
+ ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
+
+ Ref<GLTFAccessor> accessor;
+ accessor.instantiate();
+ GLTFBufferIndex buffer_view_i;
+ int64_t size = state->buffers[0].size();
+ const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+
+ accessor->max = type_max;
+ accessor->min = type_min;
+ accessor->normalized = false;
+ accessor->count = p_attribs.size();
+ accessor->type = type;
+ accessor->component_type = component_type;
+ accessor->byte_offset = 0;
+ Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ if (err != OK) {
+ return -1;
+ }
+ accessor->buffer_view = buffer_view_i;
+ state->accessors.push_back(accessor);
+ return state->accessors.size() - 1;
+}
+
+void GLTFDocument::_calc_accessor_min_max(int i, const int element_count, Vector<double> &type_max, Vector<double> attribs, Vector<double> &type_min) {
+ if (i == 0) {
+ for (int32_t type_i = 0; type_i < element_count; type_i++) {
+ type_max.write[type_i] = attribs[(i * element_count) + type_i];
+ type_min.write[type_i] = attribs[(i * element_count) + type_i];
+ }
+ }
+ for (int32_t type_i = 0; type_i < element_count; type_i++) {
+ type_max.write[type_i] = MAX(attribs[(i * element_count) + type_i], type_max[type_i]);
+ type_min.write[type_i] = MIN(attribs[(i * element_count) + type_i], type_min[type_i]);
+ type_max.write[type_i] = _filter_number(type_max.write[type_i]);
+ type_min.write[type_i] = _filter_number(type_min.write[type_i]);
+ }
+}
+
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref<GLTFState> state, const Vector<Color> p_attribs, const bool p_for_vertex) {
+ if (p_attribs.size() == 0) {
+ return -1;
+ }
+
+ const int ret_size = p_attribs.size() * 4;
+ Vector<double> attribs;
+ attribs.resize(ret_size);
+
+ const int element_count = 4;
+
+ Vector<double> type_max;
+ type_max.resize(element_count);
+ Vector<double> type_min;
+ type_min.resize(element_count);
+ for (int i = 0; i < p_attribs.size(); i++) {
+ Color attrib = p_attribs[i];
+ attribs.write[(i * element_count) + 0] = Math::snapped(attrib.r, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(attrib.g, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 2] = Math::snapped(attrib.b, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 3] = Math::snapped(attrib.a, CMP_NORMALIZE_TOLERANCE);
+
+ _calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
+ }
+
+ ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
+
+ Ref<GLTFAccessor> accessor;
+ accessor.instantiate();
+ GLTFBufferIndex buffer_view_i;
+ int64_t size = state->buffers[0].size();
+ const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+
+ accessor->max = type_max;
+ accessor->min = type_min;
+ accessor->normalized = false;
+ accessor->count = p_attribs.size();
+ accessor->type = type;
+ accessor->component_type = component_type;
+ accessor->byte_offset = 0;
+ Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ if (err != OK) {
+ return -1;
+ }
+ accessor->buffer_view = buffer_view_i;
+ state->accessors.push_back(accessor);
+ return state->accessors.size() - 1;
+}
+
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref<GLTFState> state, const Vector<Color> p_attribs, const bool p_for_vertex) {
+ if (p_attribs.size() == 0) {
+ return -1;
+ }
+
+ const int element_count = 4;
+ const int ret_size = p_attribs.size() * element_count;
+ Vector<double> attribs;
+ attribs.resize(ret_size);
+
+ Vector<double> type_max;
+ type_max.resize(element_count);
+ Vector<double> type_min;
+ type_min.resize(element_count);
+ for (int i = 0; i < p_attribs.size(); i++) {
+ Color attrib = p_attribs[i];
+ attribs.write[(i * element_count) + 0] = Math::snapped(attrib.r, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(attrib.g, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 2] = Math::snapped(attrib.b, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 3] = Math::snapped(attrib.a, CMP_NORMALIZE_TOLERANCE);
+ _calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
+ }
+ ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
+
+ Ref<GLTFAccessor> accessor;
+ accessor.instantiate();
+ GLTFBufferIndex buffer_view_i;
+ int64_t size = state->buffers[0].size();
+ const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const int component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
+
+ accessor->max = type_max;
+ accessor->min = type_min;
+ accessor->normalized = false;
+ accessor->count = p_attribs.size();
+ accessor->type = type;
+ accessor->component_type = component_type;
+ accessor->byte_offset = 0;
+ Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ if (err != OK) {
+ return -1;
+ }
+ accessor->buffer_view = buffer_view_i;
+ state->accessors.push_back(accessor);
+ return state->accessors.size() - 1;
+}
+
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref<GLTFState> state, const Vector<Quaternion> p_attribs, const bool p_for_vertex) {
+ if (p_attribs.size() == 0) {
+ return -1;
+ }
+ const int element_count = 4;
+
+ const int ret_size = p_attribs.size() * element_count;
+ Vector<double> attribs;
+ attribs.resize(ret_size);
+
+ Vector<double> type_max;
+ type_max.resize(element_count);
+ Vector<double> type_min;
+ type_min.resize(element_count);
+ for (int i = 0; i < p_attribs.size(); i++) {
+ Quaternion quaternion = p_attribs[i];
+ attribs.write[(i * element_count) + 0] = Math::snapped(quaternion.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(quaternion.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 2] = Math::snapped(quaternion.z, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 3] = Math::snapped(quaternion.w, CMP_NORMALIZE_TOLERANCE);
+
+ _calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
+ }
+
+ ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
+
+ Ref<GLTFAccessor> accessor;
+ accessor.instantiate();
+ GLTFBufferIndex buffer_view_i;
+ int64_t size = state->buffers[0].size();
+ const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC4;
+ const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+
+ accessor->max = type_max;
+ accessor->min = type_min;
+ accessor->normalized = false;
+ accessor->count = p_attribs.size();
+ accessor->type = type;
+ accessor->component_type = component_type;
+ accessor->byte_offset = 0;
+ Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ if (err != OK) {
+ return -1;
+ }
+ accessor->buffer_view = buffer_view_i;
+ state->accessors.push_back(accessor);
+ return state->accessors.size() - 1;
+}
+
+Vector<Vector2> GLTFDocument::_decode_accessor_as_vec2(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+ Vector<Vector2> ret;
+
+ if (attribs.size() == 0) {
+ return ret;
+ }
+
+ ERR_FAIL_COND_V(attribs.size() % 2 != 0, ret);
+ const double *attribs_ptr = attribs.ptr();
+ const int ret_size = attribs.size() / 2;
+ ret.resize(ret_size);
+ {
+ for (int i = 0; i < ret_size; i++) {
+ ret.write[i] = Vector2(attribs_ptr[i * 2 + 0], attribs_ptr[i * 2 + 1]);
+ }
+ }
+ return ret;
+}
+
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> state, const Vector<real_t> p_attribs, const bool p_for_vertex) {
+ if (p_attribs.size() == 0) {
+ return -1;
+ }
+ const int element_count = 1;
+ const int ret_size = p_attribs.size();
+ Vector<double> attribs;
+ attribs.resize(ret_size);
+
+ Vector<double> type_max;
+ type_max.resize(element_count);
+ Vector<double> type_min;
+ type_min.resize(element_count);
+
+ for (int i = 0; i < p_attribs.size(); i++) {
+ attribs.write[i] = Math::snapped(p_attribs[i], CMP_NORMALIZE_TOLERANCE);
+
+ _calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
+ }
+
+ ERR_FAIL_COND_V(!attribs.size(), -1);
+
+ Ref<GLTFAccessor> accessor;
+ accessor.instantiate();
+ GLTFBufferIndex buffer_view_i;
+ int64_t size = state->buffers[0].size();
+ const GLTFDocument::GLTFType type = GLTFDocument::TYPE_SCALAR;
+ const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+
+ accessor->max = type_max;
+ accessor->min = type_min;
+ accessor->normalized = false;
+ accessor->count = ret_size;
+ accessor->type = type;
+ accessor->component_type = component_type;
+ accessor->byte_offset = 0;
+ Error err = _encode_buffer_view(state, attribs.ptr(), attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ if (err != OK) {
+ return -1;
+ }
+ accessor->buffer_view = buffer_view_i;
+ state->accessors.push_back(accessor);
+ return state->accessors.size() - 1;
+}
+
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> state, const Vector<Vector3> p_attribs, const bool p_for_vertex) {
+ if (p_attribs.size() == 0) {
+ return -1;
+ }
+ const int element_count = 3;
+ const int ret_size = p_attribs.size() * element_count;
+ Vector<double> attribs;
+ attribs.resize(ret_size);
+
+ Vector<double> type_max;
+ type_max.resize(element_count);
+ Vector<double> type_min;
+ type_min.resize(element_count);
+ for (int i = 0; i < p_attribs.size(); i++) {
+ Vector3 attrib = p_attribs[i];
+ attribs.write[(i * element_count) + 0] = Math::snapped(attrib.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 1] = Math::snapped(attrib.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[(i * element_count) + 2] = Math::snapped(attrib.z, CMP_NORMALIZE_TOLERANCE);
+
+ _calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
+ }
+ ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
+
+ Ref<GLTFAccessor> accessor;
+ accessor.instantiate();
+ GLTFBufferIndex buffer_view_i;
+ int64_t size = state->buffers[0].size();
+ const GLTFDocument::GLTFType type = GLTFDocument::TYPE_VEC3;
+ const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+
+ accessor->max = type_max;
+ accessor->min = type_min;
+ accessor->normalized = false;
+ accessor->count = p_attribs.size();
+ accessor->type = type;
+ accessor->component_type = component_type;
+ accessor->byte_offset = 0;
+ Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ if (err != OK) {
+ return -1;
+ }
+ accessor->buffer_view = buffer_view_i;
+ state->accessors.push_back(accessor);
+ return state->accessors.size() - 1;
+}
+
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> state, const Vector<Transform3D> p_attribs, const bool p_for_vertex) {
+ if (p_attribs.size() == 0) {
+ return -1;
+ }
+ const int element_count = 16;
+ const int ret_size = p_attribs.size() * element_count;
+ Vector<double> attribs;
+ attribs.resize(ret_size);
+
+ Vector<double> type_max;
+ type_max.resize(element_count);
+ Vector<double> type_min;
+ type_min.resize(element_count);
+ for (int i = 0; i < p_attribs.size(); i++) {
+ Transform3D attrib = p_attribs[i];
+ Basis basis = attrib.get_basis();
+ Vector3 axis_0 = basis.get_axis(Vector3::AXIS_X);
+
+ attribs.write[i * element_count + 0] = Math::snapped(axis_0.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 1] = Math::snapped(axis_0.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 2] = Math::snapped(axis_0.z, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 3] = 0.0;
+
+ Vector3 axis_1 = basis.get_axis(Vector3::AXIS_Y);
+ attribs.write[i * element_count + 4] = Math::snapped(axis_1.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 5] = Math::snapped(axis_1.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 6] = Math::snapped(axis_1.z, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 7] = 0.0;
+
+ Vector3 axis_2 = basis.get_axis(Vector3::AXIS_Z);
+ attribs.write[i * element_count + 8] = Math::snapped(axis_2.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 9] = Math::snapped(axis_2.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 10] = Math::snapped(axis_2.z, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 11] = 0.0;
+
+ Vector3 origin = attrib.get_origin();
+ attribs.write[i * element_count + 12] = Math::snapped(origin.x, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 13] = Math::snapped(origin.y, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 14] = Math::snapped(origin.z, CMP_NORMALIZE_TOLERANCE);
+ attribs.write[i * element_count + 15] = 1.0;
+
+ _calc_accessor_min_max(i, element_count, type_max, attribs, type_min);
+ }
+ ERR_FAIL_COND_V(attribs.size() % element_count != 0, -1);
+
+ Ref<GLTFAccessor> accessor;
+ accessor.instantiate();
+ GLTFBufferIndex buffer_view_i;
+ int64_t size = state->buffers[0].size();
+ const GLTFDocument::GLTFType type = GLTFDocument::TYPE_MAT4;
+ const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+
+ accessor->max = type_max;
+ accessor->min = type_min;
+ accessor->normalized = false;
+ accessor->count = p_attribs.size();
+ accessor->type = type;
+ accessor->component_type = component_type;
+ accessor->byte_offset = 0;
+ Error err = _encode_buffer_view(state, attribs.ptr(), p_attribs.size(), type, component_type, accessor->normalized, size, p_for_vertex, buffer_view_i);
+ if (err != OK) {
+ return -1;
+ }
+ accessor->buffer_view = buffer_view_i;
+ state->accessors.push_back(accessor);
+ return state->accessors.size() - 1;
+}
+
+Vector<Vector3> GLTFDocument::_decode_accessor_as_vec3(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+ Vector<Vector3> ret;
+
+ if (attribs.size() == 0) {
+ return ret;
+ }
+
+ ERR_FAIL_COND_V(attribs.size() % 3 != 0, ret);
+ const double *attribs_ptr = attribs.ptr();
+ const int ret_size = attribs.size() / 3;
+ ret.resize(ret_size);
+ {
+ for (int i = 0; i < ret_size; i++) {
+ ret.write[i] = Vector3(attribs_ptr[i * 3 + 0], attribs_ptr[i * 3 + 1], attribs_ptr[i * 3 + 2]);
+ }
+ }
+ return ret;
+}
+
+Vector<Color> GLTFDocument::_decode_accessor_as_color(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+ Vector<Color> ret;
+
+ if (attribs.size() == 0) {
+ return ret;
+ }
+
+ const int type = state->accessors[p_accessor]->type;
+ ERR_FAIL_COND_V(!(type == TYPE_VEC3 || type == TYPE_VEC4), ret);
+ int vec_len = 3;
+ if (type == TYPE_VEC4) {
+ vec_len = 4;
+ }
+
+ ERR_FAIL_COND_V(attribs.size() % vec_len != 0, ret);
+ const double *attribs_ptr = attribs.ptr();
+ const int ret_size = attribs.size() / vec_len;
+ ret.resize(ret_size);
+ {
+ for (int i = 0; i < ret_size; i++) {
+ ret.write[i] = Color(attribs_ptr[i * vec_len + 0], attribs_ptr[i * vec_len + 1], attribs_ptr[i * vec_len + 2], vec_len == 4 ? attribs_ptr[i * 4 + 3] : 1.0);
+ }
+ }
+ return ret;
+}
+Vector<Quaternion> GLTFDocument::_decode_accessor_as_quaternion(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+ Vector<Quaternion> ret;
+
+ if (attribs.size() == 0) {
+ return ret;
+ }
+
+ ERR_FAIL_COND_V(attribs.size() % 4 != 0, ret);
+ const double *attribs_ptr = attribs.ptr();
+ const int ret_size = attribs.size() / 4;
+ ret.resize(ret_size);
+ {
+ for (int i = 0; i < ret_size; i++) {
+ ret.write[i] = Quaternion(attribs_ptr[i * 4 + 0], attribs_ptr[i * 4 + 1], attribs_ptr[i * 4 + 2], attribs_ptr[i * 4 + 3]).normalized();
+ }
+ }
+ return ret;
+}
+Vector<Transform2D> GLTFDocument::_decode_accessor_as_xform2d(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+ Vector<Transform2D> ret;
+
+ if (attribs.size() == 0) {
+ return ret;
+ }
+
+ ERR_FAIL_COND_V(attribs.size() % 4 != 0, ret);
+ ret.resize(attribs.size() / 4);
+ for (int i = 0; i < ret.size(); i++) {
+ ret.write[i][0] = Vector2(attribs[i * 4 + 0], attribs[i * 4 + 1]);
+ ret.write[i][1] = Vector2(attribs[i * 4 + 2], attribs[i * 4 + 3]);
+ }
+ return ret;
+}
+
+Vector<Basis> GLTFDocument::_decode_accessor_as_basis(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+ Vector<Basis> ret;
+
+ if (attribs.size() == 0) {
+ return ret;
+ }
+
+ ERR_FAIL_COND_V(attribs.size() % 9 != 0, ret);
+ ret.resize(attribs.size() / 9);
+ for (int i = 0; i < ret.size(); i++) {
+ ret.write[i].set_axis(0, Vector3(attribs[i * 9 + 0], attribs[i * 9 + 1], attribs[i * 9 + 2]));
+ ret.write[i].set_axis(1, Vector3(attribs[i * 9 + 3], attribs[i * 9 + 4], attribs[i * 9 + 5]));
+ ret.write[i].set_axis(2, Vector3(attribs[i * 9 + 6], attribs[i * 9 + 7], attribs[i * 9 + 8]));
+ }
+ return ret;
+}
+
+Vector<Transform3D> GLTFDocument::_decode_accessor_as_xform(Ref<GLTFState> state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
+ const Vector<double> attribs = _decode_accessor(state, p_accessor, p_for_vertex);
+ Vector<Transform3D> ret;
+
+ if (attribs.size() == 0) {
+ return ret;
+ }
+
+ ERR_FAIL_COND_V(attribs.size() % 16 != 0, ret);
+ ret.resize(attribs.size() / 16);
+ for (int i = 0; i < ret.size(); i++) {
+ ret.write[i].basis.set_axis(0, Vector3(attribs[i * 16 + 0], attribs[i * 16 + 1], attribs[i * 16 + 2]));
+ ret.write[i].basis.set_axis(1, Vector3(attribs[i * 16 + 4], attribs[i * 16 + 5], attribs[i * 16 + 6]));
+ ret.write[i].basis.set_axis(2, Vector3(attribs[i * 16 + 8], attribs[i * 16 + 9], attribs[i * 16 + 10]));
+ ret.write[i].set_origin(Vector3(attribs[i * 16 + 12], attribs[i * 16 + 13], attribs[i * 16 + 14]));
+ }
+ return ret;
+}
+
+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<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;
+ 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) {
+ case Mesh::PRIMITIVE_POINTS: {
+ primitive["mode"] = 0;
+ break;
+ }
+ case Mesh::PRIMITIVE_LINES: {
+ primitive["mode"] = 1;
+ break;
+ }
+ // case Mesh::PRIMITIVE_LINE_LOOP: {
+ // primitive["mode"] = 2;
+ // break;
+ // }
+ case Mesh::PRIMITIVE_LINE_STRIP: {
+ primitive["mode"] = 3;
+ break;
+ }
+ case Mesh::PRIMITIVE_TRIANGLES: {
+ primitive["mode"] = 4;
+ break;
+ }
+ case Mesh::PRIMITIVE_TRIANGLE_STRIP: {
+ primitive["mode"] = 5;
+ break;
+ }
+ // case Mesh::PRIMITIVE_TRIANGLE_FAN: {
+ // primitive["mode"] = 6;
+ // break;
+ // }
+ default: {
+ ERR_FAIL_V(FAILED);
+ }
+ }
+
+ 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];
+ if (a.size()) {
+ const int ret_size = a.size() / 4;
+ Vector<Color> attribs;
+ attribs.resize(ret_size);
+ for (int i = 0; i < ret_size; i++) {
+ Color out;
+ out.r = a[(i * 4) + 0];
+ out.g = a[(i * 4) + 1];
+ out.b = a[(i * 4) + 2];
+ out.a = a[(i * 4) + 3];
+ attribs.write[i] = out;
+ }
+ attributes["TANGENT"] = _encode_accessor_as_color(state, attribs, true);
+ }
+ }
+ {
+ Vector<Vector3> a = array[Mesh::ARRAY_NORMAL];
+ if (a.size()) {
+ const int ret_size = a.size();
+ Vector<Vector3> attribs;
+ attribs.resize(ret_size);
+ for (int i = 0; i < ret_size; i++) {
+ attribs.write[i] = Vector3(a[i]).normalized();
+ }
+ attributes["NORMAL"] = _encode_accessor_as_vec3(state, attribs, true);
+ }
+ }
+ {
+ Vector<Vector2> a = array[Mesh::ARRAY_TEX_UV];
+ if (a.size()) {
+ attributes["TEXCOORD_0"] = _encode_accessor_as_vec2(state, a, true);
+ }
+ }
+ {
+ Vector<Vector2> a = array[Mesh::ARRAY_TEX_UV2];
+ if (a.size()) {
+ 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()) {
+ attributes["COLOR_0"] = _encode_accessor_as_color(state, a, true);
+ }
+ }
+ Map<int, int> joint_i_to_bone_i;
+ for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); node_i++) {
+ GLTFSkinIndex skin_i = -1;
+ if (state->nodes[node_i]->mesh == gltf_mesh_i) {
+ skin_i = state->nodes[node_i]->skin;
+ }
+ if (skin_i != -1) {
+ joint_i_to_bone_i = state->skins[skin_i]->joint_i_to_bone_i;
+ break;
+ }
+ }
+ {
+ const Array &a = array[Mesh::ARRAY_BONES];
+ 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;
+ Vector<Color> attribs;
+ attribs.resize(ret_size);
+ {
+ for (int array_i = 0; array_i < attribs.size(); array_i++) {
+ int32_t joint_0 = a[(array_i * JOINT_GROUP_SIZE) + 0];
+ int32_t joint_1 = a[(array_i * JOINT_GROUP_SIZE) + 1];
+ int32_t joint_2 = a[(array_i * JOINT_GROUP_SIZE) + 2];
+ int32_t joint_3 = a[(array_i * JOINT_GROUP_SIZE) + 3];
+ attribs.write[array_i] = Color(joint_0, joint_1, joint_2, joint_3);
+ }
+ }
+ attributes["JOINTS_0"] = _encode_accessor_as_joints(state, attribs, true);
+ } else if ((a.size() / (JOINT_GROUP_SIZE * 2)) >= vertex_array.size()) {
+ Vector<Color> joints_0;
+ joints_0.resize(vertex_num);
+ Vector<Color> joints_1;
+ joints_1.resize(vertex_num);
+ int32_t weights_8_count = JOINT_GROUP_SIZE * 2;
+ 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];
+ joint_0.b = a[vertex_i * weights_8_count + 2];
+ joint_0.a = a[vertex_i * weights_8_count + 3];
+ joints_0.write[vertex_i] = joint_0;
+ Color joint_1;
+ joint_1.r = a[vertex_i * weights_8_count + 4];
+ joint_1.g = a[vertex_i * weights_8_count + 5];
+ joint_1.b = a[vertex_i * weights_8_count + 6];
+ joint_1.a = a[vertex_i * weights_8_count + 7];
+ joints_1.write[vertex_i] = joint_1;
+ }
+ attributes["JOINTS_0"] = _encode_accessor_as_joints(state, joints_0, true);
+ attributes["JOINTS_1"] = _encode_accessor_as_joints(state, joints_1, true);
+ }
+ }
+ {
+ 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()) {
+ int32_t vertex_count = vertex_array.size();
+ Vector<Color> attribs;
+ 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()) {
+ Vector<Color> weights_0;
+ weights_0.resize(vertex_num);
+ Vector<Color> weights_1;
+ weights_1.resize(vertex_num);
+ int32_t weights_8_count = JOINT_GROUP_SIZE * 2;
+ 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];
+ weight_0.b = a[vertex_i * weights_8_count + 2];
+ weight_0.a = a[vertex_i * weights_8_count + 3];
+ weights_0.write[vertex_i] = weight_0;
+ Color weight_1;
+ weight_1.r = a[vertex_i * weights_8_count + 4];
+ weight_1.g = a[vertex_i * weights_8_count + 5];
+ weight_1.b = a[vertex_i * weights_8_count + 6];
+ weight_1.a = a[vertex_i * weights_8_count + 7];
+ weights_1.write[vertex_i] = weight_1;
+ }
+ attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, weights_0, true);
+ attributes["WEIGHTS_1"] = _encode_accessor_as_weights(state, weights_1, true);
+ }
+ }
+ {
+ Vector<int32_t> mesh_indices = array[Mesh::ARRAY_INDEX];
+ if (mesh_indices.size()) {
+ if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) {
+ //swap around indices, convert ccw to cw for front face
+ const int is = mesh_indices.size();
+ for (int k = 0; k < is; k += 3) {
+ SWAP(mesh_indices.write[k + 0], mesh_indices.write[k + 2]);
+ }
+ }
+ primitive["indices"] = _encode_accessor_as_ints(state, mesh_indices, true);
+ } else {
+ if (primitive_type == Mesh::PRIMITIVE_TRIANGLES) {
+ //generate indices because they need to be swapped for CW/CCW
+ const Vector<Vector3> &vertices = array[Mesh::ARRAY_VERTEX];
+ Ref<SurfaceTool> st;
+ st.instantiate();
+ st->create_from_triangle_arrays(array);
+ st->index();
+ Vector<int32_t> generated_indices = st->commit_to_arrays()[Mesh::ARRAY_INDEX];
+ const int vs = vertices.size();
+ generated_indices.resize(vs);
+ {
+ for (int k = 0; k < vs; k += 3) {
+ generated_indices.write[k] = k;
+ generated_indices.write[k + 1] = k + 2;
+ generated_indices.write[k + 2] = k + 1;
+ }
+ }
+ primitive["indices"] = _encode_accessor_as_ints(state, generated_indices, true);
+ }
+ }
+ }
+
+ primitive["attributes"] = attributes;
+
+ //blend shapes
+ print_verbose("glTF: Mesh has targets");
+ if (import_mesh->get_blend_shape_count()) {
+ 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);
+ Dictionary t;
+ Vector<Vector3> varr = array_morph[Mesh::ARRAY_VERTEX];
+ Array mesh_arrays = import_mesh->get_surface_arrays(surface_i);
+ if (varr.size()) {
+ Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX];
+ if (shape_mode == ArrayMesh::BlendShapeMode::BLEND_SHAPE_MODE_NORMALIZED) {
+ const int max_idx = src_varr.size();
+ for (int blend_i = 0; blend_i < max_idx; blend_i++) {
+ varr.write[blend_i] = Vector3(varr[blend_i]) - src_varr[blend_i];
+ }
+ }
+
+ t["POSITION"] = _encode_accessor_as_vec3(state, varr, true);
+ }
+
+ Vector<Vector3> narr = array_morph[Mesh::ARRAY_NORMAL];
+ 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<Vector3> attribs;
+ attribs.resize(ret_size);
+ for (int i = 0; i < ret_size; i++) {
+ 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_vec3(state, attribs, true);
+ }
+ targets.push_back(t);
+ }
+ }
+
+ 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) {
+ primitive["material"] = material_cache_i->get();
+ } else {
+ GLTFMaterialIndex mat_i = state->materials.size();
+ state->materials.push_back(mat);
+ primitive["material"] = mat_i;
+ state->material_cache.insert(mat, mat_i);
+ }
+ }
+
+ if (targets.size()) {
+ primitive["targets"] = targets;
+ }
+
+ primitives.push_back(primitive);
+ }
+
+ Dictionary e;
+ e["targetNames"] = target_names;
+
+ weights.resize(target_names.size());
+ for (int name_i = 0; name_i < target_names.size(); name_i++) {
+ real_t weight = 0.0;
+ 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[name_i] = weight;
+ }
+ if (weights.size()) {
+ gltf_mesh["weights"] = weights;
+ }
+
+ ERR_FAIL_COND_V(target_names.size() != weights.size(), FAILED);
+
+ gltf_mesh["extras"] = e;
+
+ gltf_mesh["primitives"] = primitives;
+
+ meshes.push_back(gltf_mesh);
+ }
+
+ if (!meshes.size()) {
+ return OK;
+ }
+ state->json["meshes"] = meshes;
+ print_verbose("glTF: Total meshes: " + itos(meshes.size()));
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
+ if (!state->json.has("meshes")) {
+ return OK;
+ }
+
+ Array meshes = state->json["meshes"];
+ for (GLTFMeshIndex i = 0; i < meshes.size(); i++) {
+ print_verbose("glTF: Parsing mesh: " + itos(i));
+ Dictionary d = meshes[i];
+
+ Ref<GLTFMesh> mesh;
+ mesh.instantiate();
+ bool has_vertex_color = false;
+
+ ERR_FAIL_COND_V(!d.has("primitives"), ERR_PARSE_ERROR);
+
+ Array primitives = d["primitives"];
+ const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary();
+ Ref<ImporterMesh> import_mesh;
+ import_mesh.instantiate();
+ String mesh_name = "mesh";
+ if (d.has("name") && !String(d["name"]).is_empty()) {
+ mesh_name = d["name"];
+ }
+ 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;
+ array.resize(Mesh::ARRAY_MAX);
+
+ ERR_FAIL_COND_V(!p.has("attributes"), ERR_PARSE_ERROR);
+
+ Dictionary a = p["attributes"];
+
+ Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES;
+ if (p.has("mode")) {
+ const int mode = p["mode"];
+ ERR_FAIL_INDEX_V(mode, 7, ERR_FILE_CORRUPT);
+ static const Mesh::PrimitiveType primitives2[7] = {
+ Mesh::PRIMITIVE_POINTS,
+ Mesh::PRIMITIVE_LINES,
+ Mesh::PRIMITIVE_LINES, //loop not supported, should ce converted
+ Mesh::PRIMITIVE_LINES,
+ Mesh::PRIMITIVE_TRIANGLES,
+ Mesh::PRIMITIVE_TRIANGLE_STRIP,
+ Mesh::PRIMITIVE_TRIANGLES, //fan not supported, should be converted
+#ifndef _MSC_VER
+#warning line loop and triangle fan are not supported and need to be converted to lines and triangles
+#endif
+
+ };
+
+ primitive = primitives2[mode];
+ }
+
+ ERR_FAIL_COND_V(!a.has("POSITION"), ERR_PARSE_ERROR);
+ int32_t vertex_num = 0;
+ if (a.has("POSITION")) {
+ 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);
+ }
+ if (a.has("TANGENT")) {
+ array[Mesh::ARRAY_TANGENT] = _decode_accessor_as_floats(state, a["TANGENT"], true);
+ }
+ if (a.has("TEXCOORD_0")) {
+ array[Mesh::ARRAY_TEX_UV] = _decode_accessor_as_vec2(state, a["TEXCOORD_0"], true);
+ }
+ 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;
+ }
+ if (a.has("JOINTS_0") && !a.has("JOINTS_1")) {
+ array[Mesh::ARRAY_BONES] = _decode_accessor_as_ints(state, a["JOINTS_0"], true);
+ } else if (a.has("JOINTS_0") && a.has("JOINTS_1")) {
+ PackedInt32Array joints_0 = _decode_accessor_as_ints(state, a["JOINTS_0"], true);
+ 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;
+ Vector<int> joints;
+ 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];
+ joints.write[vertex_i * weight_8_count + 3] = joints_0[vertex_i * JOINT_GROUP_SIZE + 3];
+ joints.write[vertex_i * weight_8_count + 4] = joints_1[vertex_i * JOINT_GROUP_SIZE + 0];
+ joints.write[vertex_i * weight_8_count + 5] = joints_1[vertex_i * JOINT_GROUP_SIZE + 1];
+ joints.write[vertex_i * weight_8_count + 6] = joints_1[vertex_i * JOINT_GROUP_SIZE + 2];
+ joints.write[vertex_i * weight_8_count + 7] = joints_1[vertex_i * JOINT_GROUP_SIZE + 3];
+ }
+ array[Mesh::ARRAY_BONES] = joints;
+ }
+ if (a.has("WEIGHTS_0") && !a.has("WEIGHTS_1")) {
+ Vector<float> weights = _decode_accessor_as_floats(state, a["WEIGHTS_0"], true);
+ { //gltf does not seem to normalize the weights for some reason..
+ int wc = weights.size();
+ float *w = weights.ptrw();
+
+ for (int k = 0; k < wc; k += 4) {
+ float total = 0.0;
+ total += w[k + 0];
+ total += w[k + 1];
+ total += w[k + 2];
+ total += w[k + 3];
+ if (total > 0.0) {
+ w[k + 0] /= total;
+ w[k + 1] /= total;
+ w[k + 2] /= total;
+ w[k + 3] /= total;
+ }
+ }
+ }
+ array[Mesh::ARRAY_WEIGHTS] = weights;
+ } else if (a.has("WEIGHTS_0") && a.has("WEIGHTS_1")) {
+ Vector<float> weights_0 = _decode_accessor_as_floats(state, a["WEIGHTS_0"], true);
+ Vector<float> weights_1 = _decode_accessor_as_floats(state, a["WEIGHTS_1"], true);
+ 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;
+ 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];
+ weights.write[vertex_i * weight_8_count + 3] = weights_0[vertex_i * JOINT_GROUP_SIZE + 3];
+ weights.write[vertex_i * weight_8_count + 4] = weights_1[vertex_i * JOINT_GROUP_SIZE + 0];
+ weights.write[vertex_i * weight_8_count + 5] = weights_1[vertex_i * JOINT_GROUP_SIZE + 1];
+ weights.write[vertex_i * weight_8_count + 6] = weights_1[vertex_i * JOINT_GROUP_SIZE + 2];
+ weights.write[vertex_i * weight_8_count + 7] = weights_1[vertex_i * JOINT_GROUP_SIZE + 3];
+ }
+ { //gltf does not seem to normalize the weights for some reason..
+ int wc = weights.size();
+ float *w = weights.ptrw();
+
+ for (int k = 0; k < wc; k += weight_8_count) {
+ float total = 0.0;
+ total += w[k + 0];
+ total += w[k + 1];
+ total += w[k + 2];
+ total += w[k + 3];
+ total += w[k + 4];
+ total += w[k + 5];
+ total += w[k + 6];
+ total += w[k + 7];
+ if (total > 0.0) {
+ w[k + 0] /= total;
+ w[k + 1] /= total;
+ w[k + 2] /= total;
+ w[k + 3] /= total;
+ w[k + 4] /= total;
+ w[k + 5] /= total;
+ w[k + 6] /= total;
+ w[k + 7] /= total;
+ }
+ }
+ }
+ array[Mesh::ARRAY_WEIGHTS] = weights;
+ }
+
+ if (p.has("indices")) {
+ Vector<int> indices = _decode_accessor_as_ints(state, p["indices"], false);
+
+ if (primitive == Mesh::PRIMITIVE_TRIANGLES) {
+ //swap around indices, convert ccw to cw for front face
+
+ const int is = indices.size();
+ int *w = indices.ptrw();
+ for (int k = 0; k < is; k += 3) {
+ SWAP(w[k + 1], w[k + 2]);
+ }
+ }
+ array[Mesh::ARRAY_INDEX] = indices;
+
+ } else if (primitive == Mesh::PRIMITIVE_TRIANGLES) {
+ //generate indices because they need to be swapped for CW/CCW
+ const Vector<Vector3> &vertices = array[Mesh::ARRAY_VERTEX];
+ ERR_FAIL_COND_V(vertices.size() == 0, ERR_PARSE_ERROR);
+ Vector<int> indices;
+ const int vs = vertices.size();
+ indices.resize(vs);
+ {
+ int *w = indices.ptrw();
+ for (int k = 0; k < vs; k += 3) {
+ w[k] = k;
+ w[k + 1] = k + 2;
+ w[k + 2] = k + 1;
+ }
+ }
+ array[Mesh::ARRAY_INDEX] = indices;
+ }
+
+ 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..
+ mesh_surface_tool->generate_tangents();
+ }
+ array = mesh_surface_tool->commit_to_arrays();
+
+ Array morphs;
+ //blend shapes
+ if (p.has("targets")) {
+ print_verbose("glTF: Mesh has targets");
+ const Array &targets = p["targets"];
+
+ //ideally BLEND_SHAPE_MODE_RELATIVE since gltf2 stores in displacement
+ //but it could require a larger refactor?
+ import_mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
+
+ if (j == 0) {
+ const Array &target_names = extras.has("targetNames") ? (Array)extras["targetNames"] : Array();
+ for (int k = 0; k < targets.size(); k++) {
+ const String name = k < target_names.size() ? (String)target_names[k] : String("morph_") + itos(k);
+ import_mesh->add_blend_shape(name);
+ }
+ }
+
+ for (int k = 0; k < targets.size(); k++) {
+ const Dictionary &t = targets[k];
+
+ Array array_copy;
+ array_copy.resize(Mesh::ARRAY_MAX);
+
+ for (int l = 0; l < Mesh::ARRAY_MAX; l++) {
+ array_copy[l] = array[l];
+ }
+
+ if (t.has("POSITION")) {
+ Vector<Vector3> varr = _decode_accessor_as_vec3(state, t["POSITION"], true);
+ const Vector<Vector3> src_varr = array[Mesh::ARRAY_VERTEX];
+ const int size = src_varr.size();
+ ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR);
+ {
+ const int max_idx = varr.size();
+ varr.resize(size);
+
+ Vector3 *w_varr = varr.ptrw();
+ const Vector3 *r_varr = varr.ptr();
+ const Vector3 *r_src_varr = src_varr.ptr();
+ for (int l = 0; l < size; l++) {
+ if (l < max_idx) {
+ w_varr[l] = r_varr[l] + r_src_varr[l];
+ } else {
+ w_varr[l] = r_src_varr[l];
+ }
+ }
+ }
+ array_copy[Mesh::ARRAY_VERTEX] = varr;
+ }
+ if (t.has("NORMAL")) {
+ Vector<Vector3> narr = _decode_accessor_as_vec3(state, t["NORMAL"], true);
+ const Vector<Vector3> src_narr = array[Mesh::ARRAY_NORMAL];
+ int size = src_narr.size();
+ ERR_FAIL_COND_V(size == 0, ERR_PARSE_ERROR);
+ {
+ int max_idx = narr.size();
+ narr.resize(size);
+
+ Vector3 *w_narr = narr.ptrw();
+ const Vector3 *r_narr = narr.ptr();
+ const Vector3 *r_src_narr = src_narr.ptr();
+ for (int l = 0; l < size; l++) {
+ if (l < max_idx) {
+ w_narr[l] = r_narr[l] + r_src_narr[l];
+ } else {
+ w_narr[l] = r_src_narr[l];
+ }
+ }
+ }
+ array_copy[Mesh::ARRAY_NORMAL] = narr;
+ }
+ if (t.has("TANGENT")) {
+ const Vector<Vector3> tangents_v3 = _decode_accessor_as_vec3(state, t["TANGENT"], true);
+ const Vector<float> src_tangents = array[Mesh::ARRAY_TANGENT];
+ ERR_FAIL_COND_V(src_tangents.size() == 0, ERR_PARSE_ERROR);
+
+ Vector<float> tangents_v4;
+
+ {
+ int max_idx = tangents_v3.size();
+
+ int size4 = src_tangents.size();
+ tangents_v4.resize(size4);
+ float *w4 = tangents_v4.ptrw();
+
+ const Vector3 *r3 = tangents_v3.ptr();
+ const float *r4 = src_tangents.ptr();
+
+ for (int l = 0; l < size4 / 4; l++) {
+ if (l < max_idx) {
+ w4[l * 4 + 0] = r3[l].x + r4[l * 4 + 0];
+ w4[l * 4 + 1] = r3[l].y + r4[l * 4 + 1];
+ w4[l * 4 + 2] = r3[l].z + r4[l * 4 + 2];
+ } else {
+ w4[l * 4 + 0] = r4[l * 4 + 0];
+ w4[l * 4 + 1] = r4[l * 4 + 1];
+ w4[l * 4 + 2] = r4[l * 4 + 2];
+ }
+ w4[l * 4 + 3] = r4[l * 4 + 3]; //copy flip value
+ }
+ }
+
+ 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) {
+ blend_surface_tool->generate_tangents();
+ }
+ array_copy = blend_surface_tool->commit_to_arrays();
+
+ morphs.push_back(array_copy);
+ }
+ }
+
+ //just add it
+
+ Ref<BaseMaterial3D> mat;
+ if (p.has("material")) {
+ 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 {
+ Ref<StandardMaterial3D> mat3d;
+ mat3d.instantiate();
+ if (has_vertex_color) {
+ mat3d->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ }
+ mat = mat3d;
+ }
+ 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;
+ blend_weights.resize(import_mesh->get_blend_shape_count());
+ for (int32_t weight_i = 0; weight_i < blend_weights.size(); weight_i++) {
+ blend_weights.write[weight_i] = 0.0f;
+ }
+
+ if (d.has("weights")) {
+ const Array &weights = d["weights"];
+ for (int j = 0; j < weights.size(); j++) {
+ if (j >= blend_weights.size()) {
+ break;
+ }
+ blend_weights.write[j] = weights[j];
+ }
+ }
+ mesh->set_blend_weights(blend_weights);
+ mesh->set_mesh(import_mesh);
+
+ state->meshes.push_back(mesh);
+ }
+
+ print_verbose("glTF: Total meshes: " + itos(state->meshes.size()));
+
+ return OK;
+}
+
+Error GLTFDocument::_serialize_images(Ref<GLTFState> state, const String &p_path) {
+ Array images;
+ for (int i = 0; i < state->images.size(); i++) {
+ Dictionary d;
+
+ ERR_CONTINUE(state->images[i].is_null());
+
+ Ref<Image> image = state->images[i]->get_image();
+ ERR_CONTINUE(image.is_null());
+
+ if (p_path.to_lower().ends_with("glb")) {
+ GLTFBufferViewIndex bvi;
+
+ Ref<GLTFBufferView> bv;
+ bv.instantiate();
+
+ const GLTFBufferIndex bi = 0;
+ bv->buffer = bi;
+ bv->byte_offset = state->buffers[bi].size();
+ ERR_FAIL_INDEX_V(bi, state->buffers.size(), ERR_PARAMETER_RANGE_ERROR);
+
+ Vector<uint8_t> buffer;
+ Ref<ImageTexture> img_tex = image;
+ if (img_tex.is_valid()) {
+ image = img_tex->get_image();
+ }
+ Error err = PNGDriverCommon::image_to_png(image, buffer);
+ ERR_FAIL_COND_V_MSG(err, err, "Can't convert image to PNG.");
+
+ bv->byte_length = buffer.size();
+ state->buffers.write[bi].resize(state->buffers[bi].size() + bv->byte_length);
+ memcpy(&state->buffers.write[bi].write[bv->byte_offset], buffer.ptr(), buffer.size());
+ ERR_FAIL_COND_V(bv->byte_offset + bv->byte_length > state->buffers[bi].size(), ERR_FILE_CORRUPT);
+
+ state->buffer_views.push_back(bv);
+ bvi = state->buffer_views.size() - 1;
+ d["bufferView"] = bvi;
+ d["mimeType"] = "image/png";
+ } else {
+ String name = state->images[i]->get_name();
+ if (name.is_empty()) {
+ name = itos(i);
+ }
+ name = _gen_unique_name(state, name);
+ name = name.pad_zeros(3) + ".png";
+ String texture_dir = "textures";
+ String new_texture_dir = p_path.get_base_dir() + "/" + texture_dir;
+ DirAccessRef da = DirAccess::open(p_path.get_base_dir());
+ if (!da->dir_exists(new_texture_dir)) {
+ da->make_dir(new_texture_dir);
+ }
+ image->save_png(new_texture_dir.plus_file(name));
+ d["uri"] = texture_dir.plus_file(name);
+ }
+ images.push_back(d);
+ }
+
+ print_verbose("Total images: " + itos(state->images.size()));
+
+ if (!images.size()) {
+ return OK;
+ }
+ state->json["images"] = images;
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_images(Ref<GLTFState> state, const String &p_base_path) {
+ if (!state->json.has("images")) {
+ return OK;
+ }
+
+ // Ref: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#images
+
+ const Array &images = state->json["images"];
+ for (int i = 0; i < images.size(); i++) {
+ const Dictionary &d = images[i];
+
+ // glTF 2.0 supports PNG and JPEG types, which can be specified as (from spec):
+ // "- a URI to an external file in one of the supported images formats, or
+ // - a URI with embedded base64-encoded data, or
+ // - a reference to a bufferView; in that case mimeType must be defined."
+ // Since mimeType is optional for external files and base64 data, we'll have to
+ // fall back on letting Godot parse the data to figure out if it's PNG or JPEG.
+
+ // We'll assume that we use either URI or bufferView, so let's warn the user
+ // if their image somehow uses both. And fail if it has neither.
+ ERR_CONTINUE_MSG(!d.has("uri") && !d.has("bufferView"), "Invalid image definition in glTF file, it should specific an 'uri' or 'bufferView'.");
+ if (d.has("uri") && d.has("bufferView")) {
+ WARN_PRINT("Invalid image definition in glTF file using both 'uri' and 'bufferView'. 'bufferView' will take precedence.");
+ }
+
+ String mimetype;
+ if (d.has("mimeType")) { // Should be "image/png" or "image/jpeg".
+ mimetype = d["mimeType"];
+ }
+
+ Vector<uint8_t> data;
+ const uint8_t *data_ptr = nullptr;
+ int data_size = 0;
+
+ if (d.has("uri")) {
+ // Handles the first two bullet points from the spec (embedded data, or external file).
+ String uri = d["uri"];
+
+ if (uri.begins_with("data:")) { // Embedded data using base64.
+ // Validate data MIME types and throw a warning if it's one we don't know/support.
+ if (!uri.begins_with("data:application/octet-stream;base64") &&
+ !uri.begins_with("data:application/gltf-buffer;base64") &&
+ !uri.begins_with("data:image/png;base64") &&
+ !uri.begins_with("data:image/jpeg;base64")) {
+ WARN_PRINT(vformat("glTF: Image index '%d' uses an unsupported URI data type: %s. Skipping it.", i, uri));
+ state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
+ continue;
+ }
+ data = _parse_base64_uri(uri);
+ data_ptr = data.ptr();
+ data_size = data.size();
+ // mimeType is optional, but if we have it defined in the URI, let's use it.
+ if (mimetype.is_empty()) {
+ if (uri.begins_with("data:image/png;base64")) {
+ mimetype = "image/png";
+ } else if (uri.begins_with("data:image/jpeg;base64")) {
+ mimetype = "image/jpeg";
+ }
+ }
+ } 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.
+ // there could be a `.png` image which is actually JPEG), but there's no easy
+ // API for that in Godot, so we'd have to load as a buffer (i.e. embedded in
+ // the material), so we do this only as fallback.
+ Ref<Texture2D> texture = ResourceLoader::load(uri);
+ if (texture.is_valid()) {
+ state->images.push_back(texture);
+ continue;
+ } else if (mimetype == "image/png" || mimetype == "image/jpeg") {
+ // Fallback to loading as byte array.
+ // This enables us to support the spec's requirement that we honor mimetype
+ // regardless of file URI.
+ data = FileAccess::get_file_as_array(uri);
+ if (data.size() == 0) {
+ WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded as a buffer of MIME type '%s' from URI: %s. Skipping it.", i, mimetype, uri));
+ state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
+ continue;
+ }
+ data_ptr = data.ptr();
+ data_size = data.size();
+ } else {
+ WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded from URI: %s. Skipping it.", i, uri));
+ state->images.push_back(Ref<Texture2D>()); // Placeholder to keep count.
+ continue;
+ }
+ }
+ } else if (d.has("bufferView")) {
+ // Handles the third bullet point from the spec (bufferView).
+ ERR_FAIL_COND_V_MSG(mimetype.is_empty(), ERR_FILE_CORRUPT,
+ vformat("glTF: Image index '%d' specifies 'bufferView' but no 'mimeType', which is invalid.", i));
+
+ const GLTFBufferViewIndex bvi = d["bufferView"];
+
+ ERR_FAIL_INDEX_V(bvi, state->buffer_views.size(), ERR_PARAMETER_RANGE_ERROR);
+
+ Ref<GLTFBufferView> bv = state->buffer_views[bvi];
+
+ const GLTFBufferIndex bi = bv->buffer;
+ ERR_FAIL_INDEX_V(bi, state->buffers.size(), ERR_PARAMETER_RANGE_ERROR);
+
+ ERR_FAIL_COND_V(bv->byte_offset + bv->byte_length > state->buffers[bi].size(), ERR_FILE_CORRUPT);
+
+ data_ptr = &state->buffers[bi][bv->byte_offset];
+ data_size = bv->byte_length;
+ }
+
+ Ref<Image> img;
+
+ // First we honor the mime types if they were defined.
+ if (mimetype == "image/png") { // Load buffer as PNG.
+ ERR_FAIL_COND_V(Image::_png_mem_loader_func == nullptr, ERR_UNAVAILABLE);
+ img = Image::_png_mem_loader_func(data_ptr, data_size);
+ } else if (mimetype == "image/jpeg") { // Loader buffer as JPEG.
+ ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == nullptr, ERR_UNAVAILABLE);
+ img = Image::_jpg_mem_loader_func(data_ptr, data_size);
+ }
+
+ // If we didn't pass the above tests, we attempt loading as PNG and then
+ // JPEG directly.
+ // This covers URIs with base64-encoded data with application/* type but
+ // no optional mimeType property, or bufferViews with a bogus mimeType
+ // (e.g. `image/jpeg` but the data is actually PNG).
+ // That's not *exactly* what the spec mandates but this lets us be
+ // lenient with bogus glb files which do exist in production.
+ if (img.is_null()) { // Try PNG first.
+ ERR_FAIL_COND_V(Image::_png_mem_loader_func == nullptr, ERR_UNAVAILABLE);
+ img = Image::_png_mem_loader_func(data_ptr, data_size);
+ }
+ if (img.is_null()) { // And then JPEG.
+ ERR_FAIL_COND_V(Image::_jpg_mem_loader_func == nullptr, ERR_UNAVAILABLE);
+ img = Image::_jpg_mem_loader_func(data_ptr, data_size);
+ }
+ // Now we've done our best, fix your scenes.
+ if (img.is_null()) {
+ ERR_PRINT(vformat("glTF: Couldn't load image index '%d' with its given mimetype: %s.", i, mimetype));
+ state->images.push_back(Ref<Texture2D>());
+ continue;
+ }
+
+ Ref<ImageTexture> t;
+ t.instantiate();
+ t->create_from_image(img);
+
+ state->images.push_back(t);
+ }
+
+ print_verbose("glTF: Total images: " + itos(state->images.size()));
+
+ return OK;
+}
+
+Error GLTFDocument::_serialize_textures(Ref<GLTFState> state) {
+ if (!state->textures.size()) {
+ return OK;
+ }
+
+ Array textures;
+ for (int32_t i = 0; i < state->textures.size(); i++) {
+ Dictionary d;
+ Ref<GLTFTexture> t = state->textures[i];
+ ERR_CONTINUE(t->get_src_image() == -1);
+ d["source"] = t->get_src_image();
+ textures.push_back(d);
+ }
+ state->json["textures"] = textures;
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_textures(Ref<GLTFState> state) {
+ if (!state->json.has("textures")) {
+ return OK;
+ }
+
+ const Array &textures = state->json["textures"];
+ for (GLTFTextureIndex i = 0; i < textures.size(); i++) {
+ const Dictionary &d = textures[i];
+
+ ERR_FAIL_COND_V(!d.has("source"), ERR_PARSE_ERROR);
+
+ Ref<GLTFTexture> t;
+ t.instantiate();
+ t->set_src_image(d["source"]);
+ state->textures.push_back(t);
+ }
+
+ return OK;
+}
+
+GLTFTextureIndex GLTFDocument::_set_texture(Ref<GLTFState> state, Ref<Texture2D> p_texture) {
+ ERR_FAIL_COND_V(p_texture.is_null(), -1);
+ Ref<GLTFTexture> gltf_texture;
+ gltf_texture.instantiate();
+ ERR_FAIL_COND_V(p_texture->get_image().is_null(), -1);
+ GLTFImageIndex gltf_src_image_i = state->images.size();
+ state->images.push_back(p_texture);
+ gltf_texture->set_src_image(gltf_src_image_i);
+ GLTFTextureIndex gltf_texture_i = state->textures.size();
+ state->textures.push_back(gltf_texture);
+ return gltf_texture_i;
+}
+
+Ref<Texture2D> GLTFDocument::_get_texture(Ref<GLTFState> state, const GLTFTextureIndex p_texture) {
+ ERR_FAIL_INDEX_V(p_texture, state->textures.size(), Ref<Texture2D>());
+ const GLTFImageIndex image = state->textures[p_texture]->get_src_image();
+
+ ERR_FAIL_INDEX_V(image, state->images.size(), Ref<Texture2D>());
+
+ return state->images[image];
+}
+
+Error GLTFDocument::_serialize_materials(Ref<GLTFState> state) {
+ Array materials;
+ for (int32_t i = 0; i < state->materials.size(); i++) {
+ Dictionary d;
+
+ Ref<BaseMaterial3D> material = state->materials[i];
+ if (material.is_null()) {
+ materials.push_back(d);
+ continue;
+ }
+ if (!material->get_name().is_empty()) {
+ d["name"] = _gen_unique_name(state, material->get_name());
+ }
+ {
+ Dictionary mr;
+ {
+ Array arr;
+ const Color c = material->get_albedo().to_linear();
+ arr.push_back(c.r);
+ arr.push_back(c.g);
+ arr.push_back(c.b);
+ arr.push_back(c.a);
+ mr["baseColorFactor"] = arr;
+ }
+ {
+ Dictionary bct;
+ Ref<Texture2D> albedo_texture = material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
+ GLTFTextureIndex gltf_texture_index = -1;
+
+ if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) {
+ albedo_texture->set_name(material->get_name() + "_albedo");
+ gltf_texture_index = _set_texture(state, albedo_texture);
+ }
+ if (gltf_texture_index != -1) {
+ bct["index"] = gltf_texture_index;
+ bct["extensions"] = _serialize_texture_transform_uv1(material);
+ mr["baseColorTexture"] = bct;
+ }
+ }
+
+ mr["metallicFactor"] = material->get_metallic();
+ mr["roughnessFactor"] = material->get_roughness();
+ bool has_roughness = material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS).is_valid() && material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS)->get_image().is_valid();
+ bool has_ao = material->get_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION) && material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION).is_valid();
+ bool has_metalness = material->get_texture(BaseMaterial3D::TEXTURE_METALLIC).is_valid() && material->get_texture(BaseMaterial3D::TEXTURE_METALLIC)->get_image().is_valid();
+ if (has_ao || has_roughness || has_metalness) {
+ Dictionary mrt;
+ Ref<Texture2D> roughness_texture = material->get_texture(BaseMaterial3D::TEXTURE_ROUGHNESS);
+ BaseMaterial3D::TextureChannel roughness_channel = material->get_roughness_texture_channel();
+ Ref<Texture2D> metallic_texture = material->get_texture(BaseMaterial3D::TEXTURE_METALLIC);
+ BaseMaterial3D::TextureChannel metalness_channel = material->get_metallic_texture_channel();
+ Ref<Texture2D> ao_texture = material->get_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION);
+ BaseMaterial3D::TextureChannel ao_channel = material->get_ao_texture_channel();
+ Ref<ImageTexture> orm_texture;
+ orm_texture.instantiate();
+ Ref<Image> orm_image;
+ orm_image.instantiate();
+ int32_t height = 0;
+ int32_t width = 0;
+ Ref<Image> ao_image;
+ if (has_ao) {
+ height = ao_texture->get_height();
+ width = ao_texture->get_width();
+ ao_image = ao_texture->get_image();
+ Ref<ImageTexture> img_tex = ao_image;
+ if (img_tex.is_valid()) {
+ ao_image = img_tex->get_image();
+ }
+ if (ao_image->is_compressed()) {
+ ao_image->decompress();
+ }
+ }
+ Ref<Image> roughness_image;
+ if (has_roughness) {
+ height = roughness_texture->get_height();
+ width = roughness_texture->get_width();
+ roughness_image = roughness_texture->get_image();
+ Ref<ImageTexture> img_tex = roughness_image;
+ if (img_tex.is_valid()) {
+ roughness_image = img_tex->get_image();
+ }
+ if (roughness_image->is_compressed()) {
+ roughness_image->decompress();
+ }
+ }
+ Ref<Image> metallness_image;
+ if (has_metalness) {
+ height = metallic_texture->get_height();
+ width = metallic_texture->get_width();
+ metallness_image = metallic_texture->get_image();
+ Ref<ImageTexture> img_tex = metallness_image;
+ if (img_tex.is_valid()) {
+ metallness_image = img_tex->get_image();
+ }
+ if (metallness_image->is_compressed()) {
+ metallness_image->decompress();
+ }
+ }
+ Ref<Texture2D> albedo_texture = material->get_texture(BaseMaterial3D::TEXTURE_ALBEDO);
+ if (albedo_texture.is_valid() && albedo_texture->get_image().is_valid()) {
+ height = albedo_texture->get_height();
+ width = albedo_texture->get_width();
+ }
+ orm_image->create(width, height, false, Image::FORMAT_RGBA8);
+ if (ao_image.is_valid() && ao_image->get_size() != Vector2(width, height)) {
+ ao_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
+ }
+ if (roughness_image.is_valid() && roughness_image->get_size() != Vector2(width, height)) {
+ roughness_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
+ }
+ if (metallness_image.is_valid() && metallness_image->get_size() != Vector2(width, height)) {
+ metallness_image->resize(width, height, Image::INTERPOLATE_LANCZOS);
+ }
+ for (int32_t h = 0; h < height; h++) {
+ for (int32_t w = 0; w < width; w++) {
+ Color c = Color(1.0f, 1.0f, 1.0f);
+ if (has_ao) {
+ if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).r;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).g;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).b;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == ao_channel) {
+ c.r = ao_image->get_pixel(w, h).a;
+ }
+ }
+ if (has_roughness) {
+ if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).r;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).g;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).b;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == roughness_channel) {
+ c.g = roughness_image->get_pixel(w, h).a;
+ }
+ }
+ if (has_metalness) {
+ if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_RED == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).r;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_GREEN == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).g;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_BLUE == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).b;
+ } else if (BaseMaterial3D::TextureChannel::TEXTURE_CHANNEL_ALPHA == metalness_channel) {
+ c.b = metallness_image->get_pixel(w, h).a;
+ }
+ }
+ orm_image->set_pixel(w, h, c);
+ }
+ }
+ orm_image->generate_mipmaps();
+ orm_texture->create_from_image(orm_image);
+ GLTFTextureIndex orm_texture_index = -1;
+ if (has_ao || has_roughness || has_metalness) {
+ orm_texture->set_name(material->get_name() + "_orm");
+ orm_texture_index = _set_texture(state, orm_texture);
+ }
+ if (has_ao) {
+ Dictionary ot;
+ ot["index"] = orm_texture_index;
+ d["occlusionTexture"] = ot;
+ }
+ if (has_roughness || has_metalness) {
+ mrt["index"] = orm_texture_index;
+ mrt["extensions"] = _serialize_texture_transform_uv1(material);
+ mr["metallicRoughnessTexture"] = mrt;
+ }
+ }
+ d["pbrMetallicRoughness"] = mr;
+ }
+
+ if (material->get_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING)) {
+ Dictionary nt;
+ Ref<ImageTexture> tex;
+ tex.instantiate();
+ {
+ Ref<Texture2D> normal_texture = material->get_texture(BaseMaterial3D::TEXTURE_NORMAL);
+ // Code for uncompressing RG normal maps
+ Ref<Image> img = normal_texture->get_image();
+ Ref<ImageTexture> img_tex = img;
+ if (img_tex.is_valid()) {
+ img = img_tex->get_image();
+ }
+ img->decompress();
+ img->convert(Image::FORMAT_RGBA8);
+ img->convert_ra_rgba8_to_rg();
+ for (int32_t y = 0; y < img->get_height(); y++) {
+ for (int32_t x = 0; x < img->get_width(); x++) {
+ Color c = img->get_pixel(x, y);
+ Vector2 red_green = Vector2(c.r, c.g);
+ red_green = red_green * Vector2(2.0f, 2.0f) - Vector2(1.0f, 1.0f);
+ float blue = 1.0f - red_green.dot(red_green);
+ blue = MAX(0.0f, blue);
+ c.b = Math::sqrt(blue);
+ img->set_pixel(x, y, c);
+ }
+ }
+ tex->create_from_image(img);
+ }
+ Ref<Texture2D> normal_texture = material->get_texture(BaseMaterial3D::TEXTURE_NORMAL);
+ GLTFTextureIndex gltf_texture_index = -1;
+ if (tex.is_valid() && tex->get_image().is_valid()) {
+ tex->set_name(material->get_name() + "_normal");
+ gltf_texture_index = _set_texture(state, tex);
+ }
+ nt["scale"] = material->get_normal_scale();
+ if (gltf_texture_index != -1) {
+ nt["index"] = gltf_texture_index;
+ d["normalTexture"] = nt;
+ }
+ }
+
+ if (material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) {
+ const Color c = material->get_emission().to_srgb();
+ Array arr;
+ arr.push_back(c.r);
+ arr.push_back(c.g);
+ arr.push_back(c.b);
+ d["emissiveFactor"] = arr;
+ }
+ if (material->get_feature(BaseMaterial3D::FEATURE_EMISSION)) {
+ Dictionary et;
+ Ref<Texture2D> emission_texture = material->get_texture(BaseMaterial3D::TEXTURE_EMISSION);
+ GLTFTextureIndex gltf_texture_index = -1;
+ if (emission_texture.is_valid() && emission_texture->get_image().is_valid()) {
+ emission_texture->set_name(material->get_name() + "_emission");
+ gltf_texture_index = _set_texture(state, emission_texture);
+ }
+
+ if (gltf_texture_index != -1) {
+ et["index"] = gltf_texture_index;
+ d["emissiveTexture"] = et;
+ }
+ }
+ const bool ds = material->get_cull_mode() == BaseMaterial3D::CULL_DISABLED;
+ if (ds) {
+ d["doubleSided"] = ds;
+ }
+ if (material->get_transparency() == BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR) {
+ d["alphaMode"] = "MASK";
+ d["alphaCutoff"] = material->get_alpha_scissor_threshold();
+ } else if (material->get_transparency() != BaseMaterial3D::TRANSPARENCY_DISABLED) {
+ d["alphaMode"] = "BLEND";
+ }
+ materials.push_back(d);
+ }
+ if (!materials.size()) {
+ return OK;
+ }
+ state->json["materials"] = materials;
+ print_verbose("Total materials: " + itos(state->materials.size()));
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_materials(Ref<GLTFState> state) {
+ if (!state->json.has("materials")) {
+ return OK;
+ }
+
+ const Array &materials = state->json["materials"];
+ for (GLTFMaterialIndex i = 0; i < materials.size(); i++) {
+ const Dictionary &d = materials[i];
+
+ Ref<StandardMaterial3D> material;
+ material.instantiate();
+ if (d.has("name") && !String(d["name"]).is_empty()) {
+ material->set_name(d["name"]);
+ } else {
+ material->set_name(vformat("material_%s", itos(i)));
+ }
+ material->set_flag(BaseMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ Dictionary pbr_spec_gloss_extensions;
+ if (d.has("extensions")) {
+ pbr_spec_gloss_extensions = d["extensions"];
+ }
+ if (pbr_spec_gloss_extensions.has("KHR_materials_pbrSpecularGlossiness")) {
+ WARN_PRINT("Material uses a specular and glossiness workflow. Textures will be converted to roughness and metallic workflow, which may not be 100% accurate.");
+ Dictionary sgm = pbr_spec_gloss_extensions["KHR_materials_pbrSpecularGlossiness"];
+
+ Ref<GLTFSpecGloss> spec_gloss;
+ spec_gloss.instantiate();
+ if (sgm.has("diffuseTexture")) {
+ const Dictionary &diffuse_texture_dict = sgm["diffuseTexture"];
+ if (diffuse_texture_dict.has("index")) {
+ Ref<Texture2D> diffuse_texture = _get_texture(state, diffuse_texture_dict["index"]);
+ if (diffuse_texture.is_valid()) {
+ spec_gloss->diffuse_img = diffuse_texture->get_image();
+ material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, diffuse_texture);
+ }
+ }
+ }
+ if (sgm.has("diffuseFactor")) {
+ const Array &arr = sgm["diffuseFactor"];
+ ERR_FAIL_COND_V(arr.size() != 4, ERR_PARSE_ERROR);
+ const Color c = Color(arr[0], arr[1], arr[2], arr[3]).to_srgb();
+ spec_gloss->diffuse_factor = c;
+ material->set_albedo(spec_gloss->diffuse_factor);
+ }
+
+ if (sgm.has("specularFactor")) {
+ const Array &arr = sgm["specularFactor"];
+ ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR);
+ spec_gloss->specular_factor = Color(arr[0], arr[1], arr[2]);
+ }
+
+ if (sgm.has("glossinessFactor")) {
+ spec_gloss->gloss_factor = sgm["glossinessFactor"];
+ material->set_roughness(1.0f - CLAMP(spec_gloss->gloss_factor, 0.0f, 1.0f));
+ }
+ if (sgm.has("specularGlossinessTexture")) {
+ const Dictionary &spec_gloss_texture = sgm["specularGlossinessTexture"];
+ if (spec_gloss_texture.has("index")) {
+ const Ref<Texture2D> orig_texture = _get_texture(state, spec_gloss_texture["index"]);
+ if (orig_texture.is_valid()) {
+ spec_gloss->spec_gloss_img = orig_texture->get_image();
+ }
+ }
+ }
+ spec_gloss_to_rough_metal(spec_gloss, material);
+
+ } else if (d.has("pbrMetallicRoughness")) {
+ const Dictionary &mr = d["pbrMetallicRoughness"];
+ if (mr.has("baseColorFactor")) {
+ const Array &arr = mr["baseColorFactor"];
+ ERR_FAIL_COND_V(arr.size() != 4, ERR_PARSE_ERROR);
+ const Color c = Color(arr[0], arr[1], arr[2], arr[3]).to_srgb();
+ material->set_albedo(c);
+ }
+
+ if (mr.has("baseColorTexture")) {
+ const Dictionary &bct = mr["baseColorTexture"];
+ if (bct.has("index")) {
+ material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, _get_texture(state, bct["index"]));
+ }
+ if (!mr.has("baseColorFactor")) {
+ material->set_albedo(Color(1, 1, 1));
+ }
+ _set_texture_transform_uv1(bct, material);
+ }
+
+ if (mr.has("metallicFactor")) {
+ material->set_metallic(mr["metallicFactor"]);
+ } else {
+ material->set_metallic(1.0);
+ }
+
+ if (mr.has("roughnessFactor")) {
+ material->set_roughness(mr["roughnessFactor"]);
+ } else {
+ material->set_roughness(1.0);
+ }
+
+ if (mr.has("metallicRoughnessTexture")) {
+ const Dictionary &bct = mr["metallicRoughnessTexture"];
+ if (bct.has("index")) {
+ const Ref<Texture2D> t = _get_texture(state, bct["index"]);
+ material->set_texture(BaseMaterial3D::TEXTURE_METALLIC, t);
+ material->set_metallic_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_BLUE);
+ material->set_texture(BaseMaterial3D::TEXTURE_ROUGHNESS, t);
+ material->set_roughness_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_GREEN);
+ if (!mr.has("metallicFactor")) {
+ material->set_metallic(1);
+ }
+ if (!mr.has("roughnessFactor")) {
+ material->set_roughness(1);
+ }
+ }
+ }
+ }
+
+ if (d.has("normalTexture")) {
+ const Dictionary &bct = d["normalTexture"];
+ if (bct.has("index")) {
+ material->set_texture(BaseMaterial3D::TEXTURE_NORMAL, _get_texture(state, bct["index"]));
+ material->set_feature(BaseMaterial3D::FEATURE_NORMAL_MAPPING, true);
+ }
+ if (bct.has("scale")) {
+ material->set_normal_scale(bct["scale"]);
+ }
+ }
+ if (d.has("occlusionTexture")) {
+ const Dictionary &bct = d["occlusionTexture"];
+ if (bct.has("index")) {
+ material->set_texture(BaseMaterial3D::TEXTURE_AMBIENT_OCCLUSION, _get_texture(state, bct["index"]));
+ material->set_ao_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_RED);
+ material->set_feature(BaseMaterial3D::FEATURE_AMBIENT_OCCLUSION, true);
+ }
+ }
+
+ if (d.has("emissiveFactor")) {
+ const Array &arr = d["emissiveFactor"];
+ ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR);
+ const Color c = Color(arr[0], arr[1], arr[2]).to_srgb();
+ material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true);
+
+ material->set_emission(c);
+ }
+
+ if (d.has("emissiveTexture")) {
+ const Dictionary &bct = d["emissiveTexture"];
+ if (bct.has("index")) {
+ material->set_texture(BaseMaterial3D::TEXTURE_EMISSION, _get_texture(state, bct["index"]));
+ material->set_feature(BaseMaterial3D::FEATURE_EMISSION, true);
+ material->set_emission(Color(0, 0, 0));
+ }
+ }
+
+ if (d.has("doubleSided")) {
+ const bool ds = d["doubleSided"];
+ if (ds) {
+ material->set_cull_mode(BaseMaterial3D::CULL_DISABLED);
+ }
+ }
+ if (d.has("alphaMode")) {
+ const String &am = d["alphaMode"];
+ if (am == "BLEND") {
+ material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA_DEPTH_PRE_PASS);
+ } else if (am == "MASK") {
+ material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA_SCISSOR);
+ if (d.has("alphaCutoff")) {
+ material->set_alpha_scissor_threshold(d["alphaCutoff"]);
+ } else {
+ material->set_alpha_scissor_threshold(0.5f);
+ }
+ }
+ }
+ state->materials.push_back(material);
+ }
+
+ print_verbose("Total materials: " + itos(state->materials.size()));
+
+ return OK;
+}
+
+void GLTFDocument::_set_texture_transform_uv1(const Dictionary &d, Ref<BaseMaterial3D> material) {
+ if (d.has("extensions")) {
+ const Dictionary &extensions = d["extensions"];
+ if (extensions.has("KHR_texture_transform")) {
+ const Dictionary &texture_transform = extensions["KHR_texture_transform"];
+ const Array &offset_arr = texture_transform["offset"];
+ if (offset_arr.size() == 2) {
+ const Vector3 offset_vector3 = Vector3(offset_arr[0], offset_arr[1], 0.0f);
+ material->set_uv1_offset(offset_vector3);
+ }
+
+ const Array &scale_arr = texture_transform["scale"];
+ if (scale_arr.size() == 2) {
+ const Vector3 scale_vector3 = Vector3(scale_arr[0], scale_arr[1], 1.0f);
+ material->set_uv1_scale(scale_vector3);
+ }
+ }
+ }
+}
+
+void GLTFDocument::spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss, Ref<BaseMaterial3D> p_material) {
+ if (r_spec_gloss->spec_gloss_img.is_null()) {
+ return;
+ }
+ if (r_spec_gloss->diffuse_img.is_null()) {
+ return;
+ }
+ Ref<Image> rm_img;
+ rm_img.instantiate();
+ bool has_roughness = false;
+ bool has_metal = false;
+ p_material->set_roughness(1.0f);
+ p_material->set_metallic(1.0f);
+ rm_img->create(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), false, Image::FORMAT_RGBA8);
+ r_spec_gloss->spec_gloss_img->decompress();
+ if (r_spec_gloss->diffuse_img.is_valid()) {
+ r_spec_gloss->diffuse_img->decompress();
+ r_spec_gloss->diffuse_img->resize(r_spec_gloss->spec_gloss_img->get_width(), r_spec_gloss->spec_gloss_img->get_height(), Image::INTERPOLATE_LANCZOS);
+ r_spec_gloss->spec_gloss_img->resize(r_spec_gloss->diffuse_img->get_width(), r_spec_gloss->diffuse_img->get_height(), Image::INTERPOLATE_LANCZOS);
+ }
+ for (int32_t y = 0; y < r_spec_gloss->spec_gloss_img->get_height(); y++) {
+ for (int32_t x = 0; x < r_spec_gloss->spec_gloss_img->get_width(); x++) {
+ const Color specular_pixel = r_spec_gloss->spec_gloss_img->get_pixel(x, y).to_linear();
+ Color specular = Color(specular_pixel.r, specular_pixel.g, specular_pixel.b);
+ specular *= r_spec_gloss->specular_factor;
+ Color diffuse = Color(1.0f, 1.0f, 1.0f);
+ diffuse *= r_spec_gloss->diffuse_img->get_pixel(x, y).to_linear();
+ float metallic = 0.0f;
+ Color base_color;
+ spec_gloss_to_metal_base_color(specular, diffuse, base_color, metallic);
+ Color mr = Color(1.0f, 1.0f, 1.0f);
+ mr.g = specular_pixel.a;
+ mr.b = metallic;
+ if (!Math::is_equal_approx(mr.g, 1.0f)) {
+ has_roughness = true;
+ }
+ if (!Math::is_zero_approx(mr.b)) {
+ has_metal = true;
+ }
+ mr.g *= r_spec_gloss->gloss_factor;
+ mr.g = 1.0f - mr.g;
+ rm_img->set_pixel(x, y, mr);
+ if (r_spec_gloss->diffuse_img.is_valid()) {
+ r_spec_gloss->diffuse_img->set_pixel(x, y, base_color.to_srgb());
+ }
+ }
+ }
+ rm_img->generate_mipmaps();
+ r_spec_gloss->diffuse_img->generate_mipmaps();
+ Ref<ImageTexture> diffuse_image_texture;
+ diffuse_image_texture.instantiate();
+ diffuse_image_texture->create_from_image(r_spec_gloss->diffuse_img);
+ p_material->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, diffuse_image_texture);
+ Ref<ImageTexture> rm_image_texture;
+ rm_image_texture.instantiate();
+ rm_image_texture->create_from_image(rm_img);
+ if (has_roughness) {
+ p_material->set_texture(BaseMaterial3D::TEXTURE_ROUGHNESS, rm_image_texture);
+ p_material->set_roughness_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_GREEN);
+ }
+
+ if (has_metal) {
+ p_material->set_texture(BaseMaterial3D::TEXTURE_METALLIC, rm_image_texture);
+ p_material->set_metallic_texture_channel(BaseMaterial3D::TEXTURE_CHANNEL_BLUE);
+ }
+}
+
+void GLTFDocument::spec_gloss_to_metal_base_color(const Color &p_specular_factor, const Color &p_diffuse, Color &r_base_color, float &r_metallic) {
+ const Color DIELECTRIC_SPECULAR = Color(0.04f, 0.04f, 0.04f);
+ Color specular = Color(p_specular_factor.r, p_specular_factor.g, p_specular_factor.b);
+ const float one_minus_specular_strength = 1.0f - get_max_component(specular);
+ const float dielectric_specular_red = DIELECTRIC_SPECULAR.r;
+ float brightness_diffuse = get_perceived_brightness(p_diffuse);
+ const float brightness_specular = get_perceived_brightness(specular);
+ r_metallic = solve_metallic(dielectric_specular_red, brightness_diffuse, brightness_specular, one_minus_specular_strength);
+ const float one_minus_metallic = 1.0f - r_metallic;
+ const Color base_color_from_diffuse = p_diffuse * (one_minus_specular_strength / (1.0f - dielectric_specular_red) / MAX(one_minus_metallic, CMP_EPSILON));
+ const Color base_color_from_specular = (specular - (DIELECTRIC_SPECULAR * (one_minus_metallic))) * (1.0f / MAX(r_metallic, CMP_EPSILON));
+ r_base_color.r = Math::lerp(base_color_from_diffuse.r, base_color_from_specular.r, r_metallic * r_metallic);
+ 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_base_color.clamp();
+}
+
+GLTFNodeIndex GLTFDocument::_find_highest_node(Ref<GLTFState> state, const Vector<GLTFNodeIndex> &subset) {
+ int highest = -1;
+ GLTFNodeIndex best_node = -1;
+
+ for (int i = 0; i < subset.size(); ++i) {
+ const GLTFNodeIndex node_i = subset[i];
+ const Ref<GLTFNode> node = state->nodes[node_i];
+
+ if (highest == -1 || node->height < highest) {
+ highest = node->height;
+ best_node = node_i;
+ }
+ }
+
+ return best_node;
+}
+
+bool GLTFDocument::_capture_nodes_in_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin, const GLTFNodeIndex node_index) {
+ bool found_joint = false;
+
+ for (int i = 0; i < state->nodes[node_index]->children.size(); ++i) {
+ found_joint |= _capture_nodes_in_skin(state, skin, state->nodes[node_index]->children[i]);
+ }
+
+ if (found_joint) {
+ // Mark it if we happen to find another skins joint...
+ if (state->nodes[node_index]->joint && skin->joints.find(node_index) < 0) {
+ skin->joints.push_back(node_index);
+ } else if (skin->non_joints.find(node_index) < 0) {
+ skin->non_joints.push_back(node_index);
+ }
+ }
+
+ if (skin->joints.find(node_index) > 0) {
+ return true;
+ }
+
+ return false;
+}
+
+void GLTFDocument::_capture_nodes_for_multirooted_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
+ DisjointSet<GLTFNodeIndex> disjoint_set;
+
+ for (int i = 0; i < skin->joints.size(); ++i) {
+ const GLTFNodeIndex node_index = skin->joints[i];
+ const GLTFNodeIndex parent = state->nodes[node_index]->parent;
+ disjoint_set.insert(node_index);
+
+ if (skin->joints.find(parent) >= 0) {
+ disjoint_set.create_union(parent, node_index);
+ }
+ }
+
+ Vector<GLTFNodeIndex> roots;
+ disjoint_set.get_representatives(roots);
+
+ if (roots.size() <= 1) {
+ return;
+ }
+
+ int maxHeight = -1;
+
+ // Determine the max height rooted tree
+ for (int i = 0; i < roots.size(); ++i) {
+ const GLTFNodeIndex root = roots[i];
+
+ if (maxHeight == -1 || state->nodes[root]->height < maxHeight) {
+ maxHeight = state->nodes[root]->height;
+ }
+ }
+
+ // Go up the tree till all of the multiple roots of the skin are at the same hierarchy level.
+ // This sucks, but 99% of all game engines (not just Godot) would have this same issue.
+ for (int i = 0; i < roots.size(); ++i) {
+ GLTFNodeIndex current_node = roots[i];
+ while (state->nodes[current_node]->height > maxHeight) {
+ GLTFNodeIndex parent = state->nodes[current_node]->parent;
+
+ if (state->nodes[parent]->joint && skin->joints.find(parent) < 0) {
+ skin->joints.push_back(parent);
+ } else if (skin->non_joints.find(parent) < 0) {
+ skin->non_joints.push_back(parent);
+ }
+
+ current_node = parent;
+ }
+
+ // replace the roots
+ roots.write[i] = current_node;
+ }
+
+ // Climb up the tree until they all have the same parent
+ bool all_same;
+
+ do {
+ all_same = true;
+ const GLTFNodeIndex first_parent = state->nodes[roots[0]]->parent;
+
+ for (int i = 1; i < roots.size(); ++i) {
+ all_same &= (first_parent == state->nodes[roots[i]]->parent);
+ }
+
+ if (!all_same) {
+ for (int i = 0; i < roots.size(); ++i) {
+ const GLTFNodeIndex current_node = roots[i];
+ const GLTFNodeIndex parent = state->nodes[current_node]->parent;
+
+ if (state->nodes[parent]->joint && skin->joints.find(parent) < 0) {
+ skin->joints.push_back(parent);
+ } else if (skin->non_joints.find(parent) < 0) {
+ skin->non_joints.push_back(parent);
+ }
+
+ roots.write[i] = parent;
+ }
+ }
+
+ } while (!all_same);
+}
+
+Error GLTFDocument::_expand_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
+ _capture_nodes_for_multirooted_skin(state, skin);
+
+ // Grab all nodes that lay in between skin joints/nodes
+ DisjointSet<GLTFNodeIndex> disjoint_set;
+
+ Vector<GLTFNodeIndex> all_skin_nodes;
+ all_skin_nodes.append_array(skin->joints);
+ all_skin_nodes.append_array(skin->non_joints);
+
+ for (int i = 0; i < all_skin_nodes.size(); ++i) {
+ const GLTFNodeIndex node_index = all_skin_nodes[i];
+ const GLTFNodeIndex parent = state->nodes[node_index]->parent;
+ disjoint_set.insert(node_index);
+
+ if (all_skin_nodes.find(parent) >= 0) {
+ disjoint_set.create_union(parent, node_index);
+ }
+ }
+
+ Vector<GLTFNodeIndex> out_owners;
+ disjoint_set.get_representatives(out_owners);
+
+ Vector<GLTFNodeIndex> out_roots;
+
+ for (int i = 0; i < out_owners.size(); ++i) {
+ Vector<GLTFNodeIndex> set;
+ disjoint_set.get_members(set, out_owners[i]);
+
+ const GLTFNodeIndex root = _find_highest_node(state, set);
+ ERR_FAIL_COND_V(root < 0, FAILED);
+ out_roots.push_back(root);
+ }
+
+ out_roots.sort();
+
+ for (int i = 0; i < out_roots.size(); ++i) {
+ _capture_nodes_in_skin(state, skin, out_roots[i]);
+ }
+
+ skin->roots = out_roots;
+
+ return OK;
+}
+
+Error GLTFDocument::_verify_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin) {
+ // This may seem duplicated from expand_skins, but this is really a sanity check! (so it kinda is)
+ // In case additional interpolating logic is added to the skins, this will help ensure that you
+ // do not cause it to self implode into a fiery blaze
+
+ // We are going to re-calculate the root nodes and compare them to the ones saved in the skin,
+ // then ensure the multiple trees (if they exist) are on the same sublevel
+
+ // Grab all nodes that lay in between skin joints/nodes
+ DisjointSet<GLTFNodeIndex> disjoint_set;
+
+ Vector<GLTFNodeIndex> all_skin_nodes;
+ all_skin_nodes.append_array(skin->joints);
+ all_skin_nodes.append_array(skin->non_joints);
+
+ for (int i = 0; i < all_skin_nodes.size(); ++i) {
+ const GLTFNodeIndex node_index = all_skin_nodes[i];
+ const GLTFNodeIndex parent = state->nodes[node_index]->parent;
+ disjoint_set.insert(node_index);
+
+ if (all_skin_nodes.find(parent) >= 0) {
+ disjoint_set.create_union(parent, node_index);
+ }
+ }
+
+ Vector<GLTFNodeIndex> out_owners;
+ disjoint_set.get_representatives(out_owners);
+
+ Vector<GLTFNodeIndex> out_roots;
+
+ for (int i = 0; i < out_owners.size(); ++i) {
+ Vector<GLTFNodeIndex> set;
+ disjoint_set.get_members(set, out_owners[i]);
+
+ const GLTFNodeIndex root = _find_highest_node(state, set);
+ ERR_FAIL_COND_V(root < 0, FAILED);
+ out_roots.push_back(root);
+ }
+
+ out_roots.sort();
+
+ ERR_FAIL_COND_V(out_roots.size() == 0, FAILED);
+
+ // Make sure the roots are the exact same (they better be)
+ ERR_FAIL_COND_V(out_roots.size() != skin->roots.size(), FAILED);
+ for (int i = 0; i < out_roots.size(); ++i) {
+ ERR_FAIL_COND_V(out_roots[i] != skin->roots[i], FAILED);
+ }
+
+ // Single rooted skin? Perfectly ok!
+ if (out_roots.size() == 1) {
+ return OK;
+ }
+
+ // Make sure all parents of a multi-rooted skin are the SAME
+ const GLTFNodeIndex parent = state->nodes[out_roots[0]]->parent;
+ for (int i = 1; i < out_roots.size(); ++i) {
+ if (state->nodes[out_roots[i]]->parent != parent) {
+ return FAILED;
+ }
+ }
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_skins(Ref<GLTFState> state) {
+ if (!state->json.has("skins")) {
+ return OK;
+ }
+
+ const Array &skins = state->json["skins"];
+
+ // Create the base skins, and mark nodes that are joints
+ for (int i = 0; i < skins.size(); i++) {
+ const Dictionary &d = skins[i];
+
+ Ref<GLTFSkin> skin;
+ skin.instantiate();
+
+ ERR_FAIL_COND_V(!d.has("joints"), ERR_PARSE_ERROR);
+
+ const Array &joints = d["joints"];
+
+ if (d.has("inverseBindMatrices")) {
+ skin->inverse_binds = _decode_accessor_as_xform(state, d["inverseBindMatrices"], false);
+ ERR_FAIL_COND_V(skin->inverse_binds.size() != joints.size(), ERR_PARSE_ERROR);
+ }
+
+ for (int j = 0; j < joints.size(); j++) {
+ const GLTFNodeIndex node = joints[j];
+ ERR_FAIL_INDEX_V(node, state->nodes.size(), ERR_PARSE_ERROR);
+
+ skin->joints.push_back(node);
+ skin->joints_original.push_back(node);
+
+ state->nodes.write[node]->joint = true;
+ }
+
+ if (d.has("name") && !String(d["name"]).is_empty()) {
+ skin->set_name(d["name"]);
+ } else {
+ skin->set_name(vformat("skin_%s", itos(i)));
+ }
+
+ if (d.has("skeleton")) {
+ skin->skin_root = d["skeleton"];
+ }
+
+ state->skins.push_back(skin);
+ }
+
+ for (GLTFSkinIndex i = 0; i < state->skins.size(); ++i) {
+ Ref<GLTFSkin> skin = state->skins.write[i];
+
+ // Expand the skin to capture all the extra non-joints that lie in between the actual joints,
+ // and expand the hierarchy to ensure multi-rooted trees lie on the same height level
+ ERR_FAIL_COND_V(_expand_skin(state, skin), ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(_verify_skin(state, skin), ERR_PARSE_ERROR);
+ }
+
+ print_verbose("glTF: Total skins: " + itos(state->skins.size()));
+
+ return OK;
+}
+
+Error GLTFDocument::_determine_skeletons(Ref<GLTFState> state) {
+ // Using a disjoint set, we are going to potentially combine all skins that are actually branches
+ // of a main skeleton, or treat skins defining the same set of nodes as ONE skeleton.
+ // This is another unclear issue caused by the current glTF specification.
+
+ DisjointSet<GLTFNodeIndex> skeleton_sets;
+
+ for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
+ const Ref<GLTFSkin> skin = state->skins[skin_i];
+
+ Vector<GLTFNodeIndex> all_skin_nodes;
+ all_skin_nodes.append_array(skin->joints);
+ all_skin_nodes.append_array(skin->non_joints);
+
+ for (int i = 0; i < all_skin_nodes.size(); ++i) {
+ const GLTFNodeIndex node_index = all_skin_nodes[i];
+ const GLTFNodeIndex parent = state->nodes[node_index]->parent;
+ skeleton_sets.insert(node_index);
+
+ if (all_skin_nodes.find(parent) >= 0) {
+ skeleton_sets.create_union(parent, node_index);
+ }
+ }
+
+ // We are going to connect the separate skin subtrees in each skin together
+ // so that the final roots are entire sets of valid skin trees
+ for (int i = 1; i < skin->roots.size(); ++i) {
+ skeleton_sets.create_union(skin->roots[0], skin->roots[i]);
+ }
+ }
+
+ { // attempt to joint all touching subsets (siblings/parent are part of another skin)
+ Vector<GLTFNodeIndex> groups_representatives;
+ skeleton_sets.get_representatives(groups_representatives);
+
+ Vector<GLTFNodeIndex> highest_group_members;
+ Vector<Vector<GLTFNodeIndex>> groups;
+ for (int i = 0; i < groups_representatives.size(); ++i) {
+ Vector<GLTFNodeIndex> group;
+ skeleton_sets.get_members(group, groups_representatives[i]);
+ highest_group_members.push_back(_find_highest_node(state, group));
+ groups.push_back(group);
+ }
+
+ for (int i = 0; i < highest_group_members.size(); ++i) {
+ const GLTFNodeIndex node_i = highest_group_members[i];
+
+ // Attach any siblings together (this needs to be done n^2/2 times)
+ for (int j = i + 1; j < highest_group_members.size(); ++j) {
+ const GLTFNodeIndex node_j = highest_group_members[j];
+
+ // Even if they are siblings under the root! :)
+ if (state->nodes[node_i]->parent == state->nodes[node_j]->parent) {
+ skeleton_sets.create_union(node_i, node_j);
+ }
+ }
+
+ // Attach any parenting going on together (we need to do this n^2 times)
+ const GLTFNodeIndex node_i_parent = state->nodes[node_i]->parent;
+ if (node_i_parent >= 0) {
+ for (int j = 0; j < groups.size() && i != j; ++j) {
+ const Vector<GLTFNodeIndex> &group = groups[j];
+
+ if (group.find(node_i_parent) >= 0) {
+ const GLTFNodeIndex node_j = highest_group_members[j];
+ skeleton_sets.create_union(node_i, node_j);
+ }
+ }
+ }
+ }
+ }
+
+ // At this point, the skeleton groups should be finalized
+ Vector<GLTFNodeIndex> skeleton_owners;
+ skeleton_sets.get_representatives(skeleton_owners);
+
+ // Mark all the skins actual skeletons, after we have merged them
+ for (GLTFSkeletonIndex skel_i = 0; skel_i < skeleton_owners.size(); ++skel_i) {
+ const GLTFNodeIndex skeleton_owner = skeleton_owners[skel_i];
+ Ref<GLTFSkeleton> skeleton;
+ skeleton.instantiate();
+
+ Vector<GLTFNodeIndex> skeleton_nodes;
+ skeleton_sets.get_members(skeleton_nodes, skeleton_owner);
+
+ for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
+ Ref<GLTFSkin> skin = state->skins.write[skin_i];
+
+ // If any of the the skeletons nodes exist in a skin, that skin now maps to the skeleton
+ for (int i = 0; i < skeleton_nodes.size(); ++i) {
+ GLTFNodeIndex skel_node_i = skeleton_nodes[i];
+ if (skin->joints.find(skel_node_i) >= 0 || skin->non_joints.find(skel_node_i) >= 0) {
+ skin->skeleton = skel_i;
+ continue;
+ }
+ }
+ }
+
+ Vector<GLTFNodeIndex> non_joints;
+ for (int i = 0; i < skeleton_nodes.size(); ++i) {
+ const GLTFNodeIndex node_i = skeleton_nodes[i];
+
+ if (state->nodes[node_i]->joint) {
+ skeleton->joints.push_back(node_i);
+ } else {
+ non_joints.push_back(node_i);
+ }
+ }
+
+ state->skeletons.push_back(skeleton);
+
+ _reparent_non_joint_skeleton_subtrees(state, state->skeletons.write[skel_i], non_joints);
+ }
+
+ for (GLTFSkeletonIndex skel_i = 0; skel_i < state->skeletons.size(); ++skel_i) {
+ Ref<GLTFSkeleton> skeleton = state->skeletons.write[skel_i];
+
+ for (int i = 0; i < skeleton->joints.size(); ++i) {
+ const GLTFNodeIndex node_i = skeleton->joints[i];
+ Ref<GLTFNode> node = state->nodes[node_i];
+
+ ERR_FAIL_COND_V(!node->joint, ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(node->skeleton >= 0, ERR_PARSE_ERROR);
+ node->skeleton = skel_i;
+ }
+
+ ERR_FAIL_COND_V(_determine_skeleton_roots(state, skel_i), ERR_PARSE_ERROR);
+ }
+
+ return OK;
+}
+
+Error GLTFDocument::_reparent_non_joint_skeleton_subtrees(Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton, const Vector<GLTFNodeIndex> &non_joints) {
+ DisjointSet<GLTFNodeIndex> subtree_set;
+
+ // Populate the disjoint set with ONLY non joints that are in the skeleton hierarchy (non_joints vector)
+ // This way we can find any joints that lie in between joints, as the current glTF specification
+ // mentions nothing about non-joints being in between joints of the same skin. Hopefully one day we
+ // can remove this code.
+
+ // skinD depicted here explains this issue:
+ // https://github.com/KhronosGroup/glTF-Asset-Generator/blob/master/Output/Positive/Animation_Skin
+
+ for (int i = 0; i < non_joints.size(); ++i) {
+ const GLTFNodeIndex node_i = non_joints[i];
+
+ subtree_set.insert(node_i);
+
+ const GLTFNodeIndex parent_i = state->nodes[node_i]->parent;
+ if (parent_i >= 0 && non_joints.find(parent_i) >= 0 && !state->nodes[parent_i]->joint) {
+ subtree_set.create_union(parent_i, node_i);
+ }
+ }
+
+ // Find all the non joint subtrees and re-parent them to a new "fake" joint
+
+ Vector<GLTFNodeIndex> non_joint_subtree_roots;
+ subtree_set.get_representatives(non_joint_subtree_roots);
+
+ for (int root_i = 0; root_i < non_joint_subtree_roots.size(); ++root_i) {
+ const GLTFNodeIndex subtree_root = non_joint_subtree_roots[root_i];
+
+ Vector<GLTFNodeIndex> subtree_nodes;
+ subtree_set.get_members(subtree_nodes, subtree_root);
+
+ for (int subtree_i = 0; subtree_i < subtree_nodes.size(); ++subtree_i) {
+ Ref<GLTFNode> node = state->nodes[subtree_nodes[subtree_i]];
+ node->joint = true;
+ // Add the joint to the skeletons joints
+ skeleton->joints.push_back(subtree_nodes[subtree_i]);
+ }
+ }
+
+ return OK;
+}
+
+Error GLTFDocument::_determine_skeleton_roots(Ref<GLTFState> state, const GLTFSkeletonIndex skel_i) {
+ DisjointSet<GLTFNodeIndex> disjoint_set;
+
+ for (GLTFNodeIndex i = 0; i < state->nodes.size(); ++i) {
+ const Ref<GLTFNode> node = state->nodes[i];
+
+ if (node->skeleton != skel_i) {
+ continue;
+ }
+
+ disjoint_set.insert(i);
+
+ if (node->parent >= 0 && state->nodes[node->parent]->skeleton == skel_i) {
+ disjoint_set.create_union(node->parent, i);
+ }
+ }
+
+ Ref<GLTFSkeleton> skeleton = state->skeletons.write[skel_i];
+
+ Vector<GLTFNodeIndex> owners;
+ disjoint_set.get_representatives(owners);
+
+ Vector<GLTFNodeIndex> roots;
+
+ for (int i = 0; i < owners.size(); ++i) {
+ Vector<GLTFNodeIndex> set;
+ disjoint_set.get_members(set, owners[i]);
+ const GLTFNodeIndex root = _find_highest_node(state, set);
+ ERR_FAIL_COND_V(root < 0, FAILED);
+ roots.push_back(root);
+ }
+
+ roots.sort();
+
+ skeleton->roots = roots;
+
+ if (roots.size() == 0) {
+ return FAILED;
+ } else if (roots.size() == 1) {
+ return OK;
+ }
+
+ // Check that the subtrees have the same parent root
+ const GLTFNodeIndex parent = state->nodes[roots[0]]->parent;
+ for (int i = 1; i < roots.size(); ++i) {
+ if (state->nodes[roots[i]]->parent != parent) {
+ return FAILED;
+ }
+ }
+
+ return OK;
+}
+
+Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) {
+ for (GLTFSkeletonIndex skel_i = 0; skel_i < state->skeletons.size(); ++skel_i) {
+ Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_i];
+
+ 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"));
+
+ List<GLTFNodeIndex> bones;
+
+ for (int i = 0; i < gltf_skeleton->roots.size(); ++i) {
+ bones.push_back(gltf_skeleton->roots[i]);
+ }
+
+ // Make the skeleton creation deterministic by going through the roots in
+ // a sorted order, and DEPTH FIRST
+ bones.sort();
+
+ while (!bones.is_empty()) {
+ const GLTFNodeIndex node_i = bones.front()->get();
+ bones.pop_front();
+
+ Ref<GLTFNode> node = state->nodes[node_i];
+ ERR_FAIL_COND_V(node->skeleton != skel_i, FAILED);
+
+ { // Add all child nodes to the stack (deterministically)
+ Vector<GLTFNodeIndex> child_nodes;
+ for (int i = 0; i < node->children.size(); ++i) {
+ const GLTFNodeIndex child_i = node->children[i];
+ if (state->nodes[child_i]->skeleton == skel_i) {
+ child_nodes.push_back(child_i);
+ }
+ }
+
+ // Depth first insertion
+ child_nodes.sort();
+ for (int i = child_nodes.size() - 1; i >= 0; --i) {
+ bones.push_front(child_nodes[i]);
+ }
+ }
+
+ const int bone_index = skeleton->get_bone_count();
+
+ if (node->get_name().is_empty()) {
+ node->set_name("bone");
+ }
+
+ node->set_name(_gen_unique_bone_name(state, skel_i, node->get_name()));
+
+ 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());
+ ERR_FAIL_COND_V(bone_parent < 0, FAILED);
+ skeleton->set_bone_parent(bone_index, skeleton->find_bone(state->nodes[node->parent]->get_name()));
+ }
+
+ state->scene_nodes.insert(node_i, skeleton);
+ }
+ }
+
+ ERR_FAIL_COND_V(_map_skin_joints_indices_to_skeleton_bone_indices(state), ERR_PARSE_ERROR);
+
+ return OK;
+}
+
+Error GLTFDocument::_map_skin_joints_indices_to_skeleton_bone_indices(Ref<GLTFState> state) {
+ for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
+ Ref<GLTFSkin> skin = state->skins.write[skin_i];
+
+ Ref<GLTFSkeleton> skeleton = state->skeletons[skin->skeleton];
+
+ for (int joint_index = 0; joint_index < skin->joints_original.size(); ++joint_index) {
+ const GLTFNodeIndex node_i = skin->joints_original[joint_index];
+ const Ref<GLTFNode> node = state->nodes[node_i];
+
+ const int bone_index = skeleton->godot_skeleton->find_bone(node->get_name());
+ ERR_FAIL_COND_V(bone_index < 0, FAILED);
+
+ skin->joint_i_to_bone_i.insert(joint_index, bone_index);
+ }
+ }
+
+ return OK;
+}
+
+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;
+}
+
+Error GLTFDocument::_create_skins(Ref<GLTFState> state) {
+ for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
+ Ref<GLTFSkin> gltf_skin = state->skins.write[skin_i];
+
+ Ref<Skin> skin;
+ skin.instantiate();
+
+ // Some skins don't have IBM's! What absolute monsters!
+ const bool has_ibms = !gltf_skin->inverse_binds.is_empty();
+
+ for (int joint_i = 0; joint_i < gltf_skin->joints_original.size(); ++joint_i) {
+ GLTFNodeIndex node = gltf_skin->joints_original[joint_i];
+ String bone_name = state->nodes[node]->get_name();
+
+ Transform3D xform;
+ if (has_ibms) {
+ xform = gltf_skin->inverse_binds[joint_i];
+ }
+
+ if (state->use_named_skin_binds) {
+ skin->add_named_bind(bone_name, xform);
+ } else {
+ int32_t bone_i = gltf_skin->joint_i_to_bone_i[joint_i];
+ skin->add_bind(bone_i, xform);
+ }
+ }
+
+ gltf_skin->godot_skin = skin;
+ }
+
+ // Purge the duplicates!
+ _remove_duplicate_skins(state);
+
+ // Create unique names now, after removing duplicates
+ for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
+ Ref<Skin> skin = state->skins.write[skin_i]->godot_skin;
+ if (skin->get_name().is_empty()) {
+ // Make a unique name, no gltf node represents this skin
+ skin->set_name(_gen_unique_name(state, "Skin"));
+ }
+ }
+
+ return OK;
+}
+
+bool GLTFDocument::_skins_are_same(const Ref<Skin> skin_a, const Ref<Skin> skin_b) {
+ if (skin_a->get_bind_count() != skin_b->get_bind_count()) {
+ return false;
+ }
+
+ for (int i = 0; i < skin_a->get_bind_count(); ++i) {
+ if (skin_a->get_bind_bone(i) != skin_b->get_bind_bone(i)) {
+ return false;
+ }
+ if (skin_a->get_bind_name(i) != skin_b->get_bind_name(i)) {
+ return false;
+ }
+
+ Transform3D a_xform = skin_a->get_bind_pose(i);
+ Transform3D b_xform = skin_b->get_bind_pose(i);
+
+ if (a_xform != b_xform) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void GLTFDocument::_remove_duplicate_skins(Ref<GLTFState> state) {
+ for (int i = 0; i < state->skins.size(); ++i) {
+ for (int j = i + 1; j < state->skins.size(); ++j) {
+ const Ref<Skin> skin_i = state->skins[i]->godot_skin;
+ const Ref<Skin> skin_j = state->skins[j]->godot_skin;
+
+ if (_skins_are_same(skin_i, skin_j)) {
+ // replace it and delete the old
+ state->skins.write[j]->godot_skin = skin_i;
+ }
+ }
+ }
+}
+
+Error GLTFDocument::_serialize_lights(Ref<GLTFState> state) {
+ Array lights;
+ for (GLTFLightIndex i = 0; i < state->lights.size(); i++) {
+ Dictionary d;
+ Ref<GLTFLight> light = state->lights[i];
+ Array color;
+ color.resize(3);
+ color[0] = light->color.r;
+ color[1] = light->color.g;
+ color[2] = light->color.b;
+ d["color"] = color;
+ 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;
+ float outer_cone_angle = light->outer_cone_angle;
+ s["outerConeAngle"] = outer_cone_angle;
+ d["spot"] = s;
+ }
+ float intensity = light->intensity;
+ d["intensity"] = intensity;
+ float range = light->range;
+ d["range"] = range;
+ lights.push_back(d);
+ }
+
+ if (!state->lights.size()) {
+ return OK;
+ }
+
+ Dictionary extensions;
+ if (state->json.has("extensions")) {
+ extensions = state->json["extensions"];
+ } else {
+ state->json["extensions"] = extensions;
+ }
+ Dictionary lights_punctual;
+ extensions["KHR_lights_punctual"] = lights_punctual;
+ lights_punctual["lights"] = lights;
+
+ print_verbose("glTF: Total lights: " + itos(state->lights.size()));
+
+ return OK;
+}
+
+Error GLTFDocument::_serialize_cameras(Ref<GLTFState> state) {
+ Array cameras;
+ cameras.resize(state->cameras.size());
+ for (GLTFCameraIndex i = 0; i < state->cameras.size(); i++) {
+ Dictionary d;
+
+ Ref<GLTFCamera> camera = state->cameras[i];
+
+ if (camera->get_perspective() == false) {
+ Dictionary og;
+ og["ymag"] = Math::deg2rad(camera->get_fov_size());
+ og["xmag"] = Math::deg2rad(camera->get_fov_size());
+ 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_depth_far();
+ ppt["znear"] = camera->get_depth_near();
+ d["perspective"] = ppt;
+ d["type"] = "perspective";
+ }
+ cameras[i] = d;
+ }
+
+ if (!state->cameras.size()) {
+ return OK;
+ }
+
+ state->json["cameras"] = cameras;
+
+ print_verbose("glTF: Total cameras: " + itos(state->cameras.size()));
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_lights(Ref<GLTFState> state) {
+ if (!state->json.has("extensions")) {
+ return OK;
+ }
+ Dictionary extensions = state->json["extensions"];
+ if (!extensions.has("KHR_lights_punctual")) {
+ return OK;
+ }
+ Dictionary lights_punctual = extensions["KHR_lights_punctual"];
+ if (!lights_punctual.has("lights")) {
+ return OK;
+ }
+
+ const Array &lights = lights_punctual["lights"];
+
+ for (GLTFLightIndex light_i = 0; light_i < lights.size(); light_i++) {
+ const Dictionary &d = lights[light_i];
+
+ Ref<GLTFLight> light;
+ light.instantiate();
+ ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
+ const String &type = d["type"];
+ light->light_type = type;
+
+ if (d.has("color")) {
+ const Array &arr = d["color"];
+ ERR_FAIL_COND_V(arr.size() != 3, ERR_PARSE_ERROR);
+ const Color c = Color(arr[0], arr[1], arr[2]).to_srgb();
+ light->color = c;
+ }
+ if (d.has("intensity")) {
+ light->intensity = d["intensity"];
+ }
+ if (d.has("range")) {
+ light->range = d["range"];
+ }
+ if (type == "spot") {
+ const Dictionary &spot = d["spot"];
+ light->inner_cone_angle = spot["innerConeAngle"];
+ 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.");
+ }
+
+ state->lights.push_back(light);
+ }
+
+ print_verbose("glTF: Total lights: " + itos(state->lights.size()));
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_cameras(Ref<GLTFState> state) {
+ if (!state->json.has("cameras")) {
+ return OK;
+ }
+
+ const Array cameras = state->json["cameras"];
+
+ for (GLTFCameraIndex i = 0; i < cameras.size(); i++) {
+ const Dictionary &d = cameras[i];
+
+ Ref<GLTFCamera> camera;
+ camera.instantiate();
+ ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
+ const String &type = d["type"];
+ if (type == "orthographic") {
+ camera->set_perspective(false);
+ if (d.has("orthographic")) {
+ 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_depth_far(og["zfar"]);
+ camera->set_depth_near(og["znear"]);
+ } else {
+ camera->set_fov_size(10);
+ }
+ } else if (type == "perspective") {
+ camera->set_perspective(true);
+ if (d.has("perspective")) {
+ 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_depth_far(ppt["zfar"]);
+ camera->set_depth_near(ppt["znear"]);
+ } else {
+ camera->set_fov_size(10);
+ }
+ } else {
+ ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "Camera3D should be in 'orthographic' or 'perspective'");
+ }
+
+ state->cameras.push_back(camera);
+ }
+
+ print_verbose("glTF: Total cameras: " + itos(state->cameras.size()));
+
+ return OK;
+}
+
+String GLTFDocument::interpolation_to_string(const GLTFAnimation::Interpolation p_interp) {
+ String interp = "LINEAR";
+ if (p_interp == GLTFAnimation::INTERP_STEP) {
+ interp = "STEP";
+ } else if (p_interp == GLTFAnimation::INTERP_LINEAR) {
+ interp = "LINEAR";
+ } else if (p_interp == GLTFAnimation::INTERP_CATMULLROMSPLINE) {
+ interp = "CATMULLROMSPLINE";
+ } else if (p_interp == GLTFAnimation::INTERP_CUBIC_SPLINE) {
+ interp = "CUBICSPLINE";
+ }
+
+ return interp;
+}
+
+Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
+ if (!state->animation_players.size()) {
+ return OK;
+ }
+ for (int32_t player_i = 0; player_i < state->animation_players.size(); player_i++) {
+ List<StringName> animation_names;
+ AnimationPlayer *animation_player = state->animation_players[player_i];
+ animation_player->get_animation_list(&animation_names);
+ if (animation_names.size()) {
+ for (int animation_name_i = 0; animation_name_i < animation_names.size(); animation_name_i++) {
+ _convert_animation(state, animation_player, animation_names[animation_name_i]);
+ }
+ }
+ }
+ Array animations;
+ for (GLTFAnimationIndex animation_i = 0; animation_i < state->animations.size(); animation_i++) {
+ Dictionary d;
+ Ref<GLTFAnimation> gltf_animation = state->animations[animation_i];
+ if (!gltf_animation->get_tracks().size()) {
+ continue;
+ }
+
+ if (!gltf_animation->get_name().is_empty()) {
+ d["name"] = gltf_animation->get_name();
+ }
+ Array channels;
+ Array samplers;
+
+ 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.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.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;
+
+ t["target"] = target;
+ channels.push_back(t);
+ }
+ if (track.rotation_track.times.size()) {
+ Dictionary t;
+ t["sampler"] = samplers.size();
+ Dictionary s;
+
+ s["interpolation"] = interpolation_to_string(track.rotation_track.interpolation);
+ Vector<real_t> times = Variant(track.rotation_track.times);
+ s["input"] = _encode_accessor_as_floats(state, times, false);
+ Vector<Quaternion> values = track.rotation_track.values;
+ s["output"] = _encode_accessor_as_quaternions(state, values, false);
+
+ samplers.push_back(s);
+
+ Dictionary target;
+ target["path"] = "rotation";
+ target["node"] = track_i.key;
+
+ t["target"] = target;
+ channels.push_back(t);
+ }
+ if (track.scale_track.times.size()) {
+ Dictionary t;
+ t["sampler"] = samplers.size();
+ Dictionary s;
+
+ s["interpolation"] = interpolation_to_string(track.scale_track.interpolation);
+ Vector<real_t> times = Variant(track.scale_track.times);
+ s["input"] = _encode_accessor_as_floats(state, times, false);
+ Vector<Vector3> values = Variant(track.scale_track.values);
+ s["output"] = _encode_accessor_as_vec3(state, values, false);
+
+ samplers.push_back(s);
+
+ Dictionary target;
+ target["path"] = "scale";
+ 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;
+ 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 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<float>(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;
+ }
+
+ 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;
+ for (int l = 0; l < wdata.size(); 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, 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;
+
+ t["target"] = target;
+ channels.push_back(t);
+ }
+ }
+ if (channels.size() && samplers.size()) {
+ d["channels"] = channels;
+ d["samplers"] = samplers;
+ animations.push_back(d);
+ }
+ }
+
+ if (!animations.size()) {
+ return OK;
+ }
+ state->json["animations"] = animations;
+
+ print_verbose("glTF: Total animations '" + itos(state->animations.size()) + "'.");
+
+ return OK;
+}
+
+Error GLTFDocument::_parse_animations(Ref<GLTFState> state) {
+ if (!state->json.has("animations")) {
+ return OK;
+ }
+
+ const Array &animations = state->json["animations"];
+
+ for (GLTFAnimationIndex i = 0; i < animations.size(); i++) {
+ const Dictionary &d = animations[i];
+
+ Ref<GLTFAnimation> animation;
+ animation.instantiate();
+
+ if (!d.has("channels") || !d.has("samplers")) {
+ continue;
+ }
+
+ Array channels = d["channels"];
+ Array samplers = d["samplers"];
+
+ if (d.has("name")) {
+ const String name = d["name"];
+ if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
+ animation->set_loop(true);
+ }
+ animation->set_name(_gen_unique_animation_name(state, name));
+ }
+
+ for (int j = 0; j < channels.size(); j++) {
+ const Dictionary &c = channels[j];
+ if (!c.has("target")) {
+ continue;
+ }
+
+ const Dictionary &t = c["target"];
+ if (!t.has("node") || !t.has("path")) {
+ continue;
+ }
+
+ ERR_FAIL_COND_V(!c.has("sampler"), ERR_PARSE_ERROR);
+ const int sampler = c["sampler"];
+ ERR_FAIL_INDEX_V(sampler, samplers.size(), ERR_PARSE_ERROR);
+
+ GLTFNodeIndex node = t["node"];
+ String path = t["path"];
+
+ ERR_FAIL_INDEX_V(node, state->nodes.size(), ERR_PARSE_ERROR);
+
+ GLTFAnimation::Track *track = nullptr;
+
+ if (!animation->get_tracks().has(node)) {
+ animation->get_tracks()[node] = GLTFAnimation::Track();
+ }
+
+ track = &animation->get_tracks()[node];
+
+ const Dictionary &s = samplers[sampler];
+
+ ERR_FAIL_COND_V(!s.has("input"), ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(!s.has("output"), ERR_PARSE_ERROR);
+
+ const int input = s["input"];
+ const int output = s["output"];
+
+ GLTFAnimation::Interpolation interp = GLTFAnimation::INTERP_LINEAR;
+ int output_count = 1;
+ if (s.has("interpolation")) {
+ const String &in = s["interpolation"];
+ if (in == "STEP") {
+ interp = GLTFAnimation::INTERP_STEP;
+ } else if (in == "LINEAR") {
+ interp = GLTFAnimation::INTERP_LINEAR;
+ } else if (in == "CATMULLROMSPLINE") {
+ interp = GLTFAnimation::INTERP_CATMULLROMSPLINE;
+ output_count = 3;
+ } else if (in == "CUBICSPLINE") {
+ interp = GLTFAnimation::INTERP_CUBIC_SPLINE;
+ output_count = 3;
+ }
+ }
+
+ const Vector<float> times = _decode_accessor_as_floats(state, input, false);
+ if (path == "translation") {
+ 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;
+ track->rotation_track.times = Variant(times); //convert via variant
+ track->rotation_track.values = rotations;
+ } else if (path == "scale") {
+ const Vector<Vector3> scales = _decode_accessor_as_vec3(state, output, false);
+ track->scale_track.interpolation = interp;
+ track->scale_track.times = Variant(times); //convert via variant
+ track->scale_track.values = Variant(scales); //convert via variant
+ } else if (path == "weights") {
+ const Vector<float> weights = _decode_accessor_as_floats(state, output, false);
+
+ ERR_FAIL_INDEX_V(state->nodes[node]->mesh, state->meshes.size(), ERR_PARSE_ERROR);
+ Ref<GLTFMesh> mesh = state->meshes[state->nodes[node]->mesh];
+ ERR_CONTINUE(!mesh->get_blend_weights().size());
+ const int wc = mesh->get_blend_weights().size();
+
+ track->weight_tracks.resize(wc);
+
+ const int expected_value_count = times.size() * output_count * wc;
+ 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;
+ cf.interpolation = interp;
+ cf.times = Variant(times);
+ Vector<float> wdata;
+ wdata.resize(wlen);
+ for (int l = 0; l < wlen; l++) {
+ wdata.write[l] = weights[l * wc + k];
+ }
+
+ cf.values = wdata;
+ track->weight_tracks.write[k] = cf;
+ }
+ } else {
+ WARN_PRINT("Invalid path '" + path + "'.");
+ }
+ }
+
+ state->animations.push_back(animation);
+ }
+
+ print_verbose("glTF: Total animations '" + itos(state->animations.size()) + "'.");
+
+ return OK;
+}
+
+void GLTFDocument::_assign_scene_names(Ref<GLTFState> state) {
+ for (int i = 0; i < state->nodes.size(); i++) {
+ Ref<GLTFNode> n = state->nodes[i];
+
+ // Any joints get unique names generated when the skeleton is made, unique to the skeleton
+ if (n->skeleton >= 0) {
+ continue;
+ }
+
+ if (n->get_name().is_empty()) {
+ if (n->mesh >= 0) {
+ n->set_name(_gen_unique_name(state, "Mesh"));
+ } else if (n->camera >= 0) {
+ n->set_name(_gen_unique_name(state, "Camera3D"));
+ } else {
+ n->set_name(_gen_unique_name(state, "Node"));
+ }
+ }
+
+ n->set_name(_gen_unique_name(state, n->get_name()));
+ }
+}
+
+BoneAttachment3D *GLTFDocument::_generate_bone_attachment(Ref<GLTFState> state, Skeleton3D *skeleton, const GLTFNodeIndex node_index, const GLTFNodeIndex bone_index) {
+ Ref<GLTFNode> gltf_node = state->nodes[node_index];
+ Ref<GLTFNode> bone_node = state->nodes[bone_index];
+ BoneAttachment3D *bone_attachment = memnew(BoneAttachment3D);
+ print_verbose("glTF: Creating bone attachment for: " + gltf_node->get_name());
+
+ ERR_FAIL_COND_V(!bone_node->joint, nullptr);
+
+ bone_attachment->set_bone_name(bone_node->get_name());
+
+ return bone_attachment;
+}
+
+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<ImporterMesh> current_mesh;
+ current_mesh.instantiate();
+ Vector<float> blend_weights;
+ {
+ 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();
+ }
+ instance_materials.append(mat);
+ }
+ 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;
+}
+
+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);
+
+ 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<ImporterMesh> import_mesh = mesh->get_mesh();
+ if (import_mesh.is_null()) {
+ return mi;
+ }
+ mi->set_mesh(import_mesh);
+ return mi;
+}
+
+Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) {
+ Ref<GLTFNode> gltf_node = state->nodes[node_index];
+
+ ERR_FAIL_INDEX_V(gltf_node->light, state->lights.size(), nullptr);
+
+ print_verbose("glTF: Creating light for: " + gltf_node->get_name());
+
+ Ref<GLTFLight> l = state->lights[gltf_node->light];
+
+ float intensity = l->intensity;
+ if (intensity > 10) {
+ // GLTF spec has the default around 1, but Blender defaults lights to 100.
+ // The only sane way to handle this is to check where it came from and
+ // handle it accordingly. If it's over 10, it probably came from Blender.
+ intensity /= 100;
+ }
+
+ if (l->light_type == "directional") {
+ DirectionalLight3D *light = memnew(DirectionalLight3D);
+ light->set_param(Light3D::PARAM_ENERGY, intensity);
+ light->set_color(l->color);
+ return light;
+ }
+
+ const float range = CLAMP(l->range, 0, 4096);
+ // Doubling the range will double the effective brightness, so we need double attenuation (half brightness).
+ // We want to have double intensity give double brightness, so we need half the attenuation.
+ const float attenuation = range / (intensity * 2048);
+ if (l->light_type == "point") {
+ OmniLight3D *light = memnew(OmniLight3D);
+ light->set_param(OmniLight3D::PARAM_ATTENUATION, attenuation);
+ light->set_param(OmniLight3D::PARAM_RANGE, range);
+ light->set_color(l->color);
+ return light;
+ }
+ if (l->light_type == "spot") {
+ SpotLight3D *light = memnew(SpotLight3D);
+ light->set_param(SpotLight3D::PARAM_ATTENUATION, attenuation);
+ light->set_param(SpotLight3D::PARAM_RANGE, range);
+ light->set_param(SpotLight3D::PARAM_SPOT_ANGLE, Math::rad2deg(l->outer_cone_angle));
+ light->set_color(l->color);
+
+ // Line of best fit derived from guessing, see https://www.desmos.com/calculator/biiflubp8b
+ // The points in desmos are not exact, except for (1, infinity).
+ float angle_ratio = l->inner_cone_angle / l->outer_cone_angle;
+ float angle_attenuation = 0.2 / (1 - angle_ratio) - 0.1;
+ light->set_param(SpotLight3D::PARAM_SPOT_ATTENUATION, angle_attenuation);
+ return light;
+ }
+ return memnew(Node3D);
+}
+
+Camera3D *GLTFDocument::_generate_camera(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) {
+ Ref<GLTFNode> gltf_node = state->nodes[node_index];
+
+ ERR_FAIL_INDEX_V(gltf_node->camera, state->cameras.size(), nullptr);
+
+ Camera3D *camera = memnew(Camera3D);
+ print_verbose("glTF: Creating camera for: " + gltf_node->get_name());
+
+ Ref<GLTFCamera> c = state->cameras[gltf_node->camera];
+ if (c->get_perspective()) {
+ 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_depth_near(), c->get_depth_far());
+ }
+
+ return camera;
+}
+
+GLTFCameraIndex GLTFDocument::_convert_camera(Ref<GLTFState> state, Camera3D *p_camera) {
+ print_verbose("glTF: Converting camera: " + p_camera->get_name());
+
+ Ref<GLTFCamera> c;
+ c.instantiate();
+
+ if (p_camera->get_projection() == Camera3D::Projection::PROJECTION_PERSPECTIVE) {
+ c->set_perspective(true);
+ }
+ 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;
+}
+
+GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> state, Light3D *p_light) {
+ print_verbose("glTF: Converting light: " + p_light->get_name());
+
+ Ref<GLTFLight> l;
+ l.instantiate();
+ l->color = p_light->get_color();
+ if (cast_to<DirectionalLight3D>(p_light)) {
+ 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->light_type = "point";
+ OmniLight3D *light = cast_to<OmniLight3D>(p_light);
+ l->range = light->get_param(OmniLight3D::PARAM_RANGE);
+ float attenuation = p_light->get_param(OmniLight3D::PARAM_ATTENUATION);
+ l->intensity = l->range / (attenuation * 2048);
+ } else if (cast_to<SpotLight3D>(p_light)) {
+ l->light_type = "spot";
+ SpotLight3D *light = cast_to<SpotLight3D>(p_light);
+ l->range = light->get_param(SpotLight3D::PARAM_RANGE);
+ float attenuation = light->get_param(SpotLight3D::PARAM_ATTENUATION);
+ l->intensity = l->range / (attenuation * 2048);
+ l->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).
+ float angle_ratio = 1 - (0.2 / (0.1 + light->get_param(SpotLight3D::PARAM_SPOT_ATTENUATION)));
+ angle_ratio = MAX(0, angle_ratio);
+ l->inner_cone_angle = l->outer_cone_angle * angle_ratio;
+ }
+
+ GLTFLightIndex light_index = state->lights.size();
+ state->lights.push_back(l);
+ return light_index;
+}
+
+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->position = xform.origin;
+}
+
+Node3D *GLTFDocument::_generate_spatial(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) {
+ Ref<GLTFNode> gltf_node = state->nodes[node_index];
+
+ Node3D *spatial = memnew(Node3D);
+ print_verbose("glTF: Converting spatial: " + gltf_node->get_name());
+
+ return spatial;
+}
+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) {
+ return;
+ }
+ Ref<GLTFNode> gltf_node;
+ gltf_node.instantiate();
+ gltf_node->set_name(_gen_unique_name(state, p_current->get_name()));
+ if (cast_to<Node3D>(p_current)) {
+ Node3D *spatial = cast_to<Node3D>(p_current);
+ _convert_spatial(state, spatial, gltf_node);
+ }
+ if (cast_to<MeshInstance3D>(p_current)) {
+ MeshInstance3D *mi = cast_to<MeshInstance3D>(p_current);
+ _convert_mesh_instance_to_gltf(mi, state, gltf_node);
+ } else if (cast_to<BoneAttachment3D>(p_current)) {
+ 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)) {
+ 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)) {
+ 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)) {
+ 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)) {
+ 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, gltf_node);
+ } else if (cast_to<Light3D>(p_current)) {
+ Light3D *light = Object::cast_to<Light3D>(p_current);
+ _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);
+ }
+ GLTFNodeIndex current_node_i = state->nodes.size();
+ GLTFNodeIndex gltf_root = p_gltf_root;
+ if (gltf_root == -1) {
+ gltf_root = current_node_i;
+ Array scenes;
+ scenes.push_back(gltf_root);
+ state->json["scene"] = scenes;
+ }
+ _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), current_node_i, gltf_root);
+ }
+}
+
+#ifdef MODULE_CSG_ENABLED
+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) {
+ return;
+ }
+ Ref<Material> mat;
+ if (csg->get_material_override().is_valid()) {
+ mat = csg->get_material_override();
+ }
+ Ref<GLTFMesh> gltf_mesh;
+ gltf_mesh.instantiate();
+ 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;
+ gltf_node->xform = csg->get_meshes()[0];
+ gltf_node->set_name(_gen_unique_name(state, csg->get_name()));
+}
+#endif // MODULE_CSG_ENABLED
+
+void GLTFDocument::_create_gltf_node(Ref<GLTFState> state, Node *p_scene_parent, GLTFNodeIndex current_node_i,
+ 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);
+ 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, 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());
+}
+
+void GLTFDocument::_check_visibility(Node *p_node, bool &retflag) {
+ retflag = true;
+ Node3D *spatial = Object::cast_to<Node3D>(p_node);
+ Node2D *node_2d = Object::cast_to<Node2D>(p_node);
+ if (node_2d && !node_2d->is_visible()) {
+ return;
+ }
+ if (spatial && !spatial->is_visible()) {
+ return;
+ }
+ retflag = false;
+}
+
+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) {
+ gltf_node->camera = camera_index;
+ }
+}
+
+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) {
+ gltf_node->light = light_index;
+ }
+}
+
+#ifdef MODULE_GRIDMAP_ENABLED
+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 = p_grid_map->get_cell_item(
+ Vector3(cell_location.x, cell_location.y, cell_location.z));
+ 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(
+ p_grid_map->get_cell_item_orientation(
+ Vector3(cell_location.x, cell_location.y, cell_location.z)));
+ 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 * 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_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));
+ }
+ }
+ 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_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);
+ }
+ } 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);
+ }
+ }
+ // 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_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) {
+ Ref<GLTFNode> gltf_node = state->nodes[node_index];
+
+ if (gltf_node->skeleton >= 0) {
+ _generate_skeleton_bone_node(state, scene_parent, scene_root, node_index);
+ return;
+ }
+
+ Node3D *current_node = nullptr;
+
+ // Is our parent a skeleton
+ Skeleton3D *active_skeleton = Object::cast_to<Skeleton3D>(scene_parent);
+
+ const bool non_bone_parented_to_skeleton = active_skeleton;
+
+ // skinned meshes must not be placed in a bone attachment.
+ if (non_bone_parented_to_skeleton && gltf_node->skin < 0) {
+ // Bone Attachment - Parent Case
+ BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent);
+
+ 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
+ bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment3D"));
+
+ // We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
+ // and attach it to the bone_attachment
+ scene_parent = bone_attachment;
+ }
+ if (gltf_node->mesh >= 0) {
+ current_node = _generate_mesh_instance(state, scene_parent, node_index);
+ } else if (gltf_node->camera >= 0) {
+ current_node = _generate_camera(state, scene_parent, node_index);
+ } else if (gltf_node->light >= 0) {
+ current_node = _generate_light(state, scene_parent, node_index);
+ }
+
+ // We still have not managed to make a node.
+ if (!current_node) {
+ current_node = _generate_spatial(state, scene_parent, node_index);
+ }
+
+ scene_parent->add_child(current_node, true);
+ if (current_node != scene_root) {
+ current_node->set_owner(scene_root);
+ }
+ current_node->set_transform(gltf_node->xform);
+ current_node->set_name(gltf_node->get_name());
+
+ state->scene_nodes.insert(node_index, current_node);
+
+ for (int i = 0; i < gltf_node->children.size(); ++i) {
+ _generate_scene_node(state, current_node, scene_root, gltf_node->children[i]);
+ }
+}
+
+void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) {
+ Ref<GLTFNode> gltf_node = state->nodes[node_index];
+
+ Node3D *current_node = nullptr;
+
+ Skeleton3D *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton;
+ // In this case, this node is already a bone in skeleton.
+ const bool is_skinned_mesh = (gltf_node->skin >= 0 && gltf_node->mesh >= 0);
+ const bool requires_extra_node = (gltf_node->mesh >= 0 || gltf_node->camera >= 0 || gltf_node->light >= 0);
+
+ Skeleton3D *active_skeleton = Object::cast_to<Skeleton3D>(scene_parent);
+ if (active_skeleton != skeleton) {
+ if (active_skeleton) {
+ // 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, true);
+ bone_attachment->set_owner(scene_root);
+
+ // There is no gltf_node that represent this, so just directly create a unique name
+ bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment3D"));
+
+ // We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
+ // and attach it to the bone_attachment
+ scene_parent = bone_attachment;
+ WARN_PRINT(vformat("glTF: Generating scene detected direct parented Skeletons at node %d", node_index));
+ }
+
+ // Add it to the scene if it has not already been added
+ if (skeleton->get_parent() == nullptr) {
+ scene_parent->add_child(skeleton, true);
+ skeleton->set_owner(scene_root);
+ }
+ }
+
+ active_skeleton = skeleton;
+ current_node = skeleton;
+
+ if (requires_extra_node) {
+ // skinned meshes must not be placed in a bone attachment.
+ if (!is_skinned_mesh) {
+ // Bone Attachment - Same Node Case
+ BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, node_index);
+
+ 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
+ bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment3D"));
+
+ // We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
+ // and attach it to the bone_attachment
+ scene_parent = bone_attachment;
+ }
+
+ // We still have not managed to make a node
+ if (gltf_node->mesh >= 0) {
+ current_node = _generate_mesh_instance(state, scene_parent, node_index);
+ } else if (gltf_node->camera >= 0) {
+ current_node = _generate_camera(state, scene_parent, node_index);
+ } else if (gltf_node->light >= 0) {
+ current_node = _generate_light(state, scene_parent, node_index);
+ }
+
+ scene_parent->add_child(current_node, true);
+ if (current_node != scene_root) {
+ current_node->set_owner(scene_root);
+ }
+ // Do not set transform here. Transform is already applied to our bone.
+ current_node->set_name(gltf_node->get_name());
+ }
+
+ state->scene_nodes.insert(node_index, current_node);
+
+ for (int i = 0; i < gltf_node->children.size(); ++i) {
+ _generate_scene_node(state, active_skeleton, scene_root, gltf_node->children[i]);
+ }
+}
+
+template <class T>
+struct EditorSceneFormatImporterGLTFInterpolate {
+ T lerp(const T &a, const T &b, float c) const {
+ return a + (b - a) * c;
+ }
+
+ T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) {
+ const float t2 = t * t;
+ const float t3 = t2 * t;
+
+ return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4.0f * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
+ }
+
+ T bezier(T start, T control_1, T control_2, T end, float t) {
+ /* Formula from Wikipedia article on Bezier curves. */
+ const real_t omt = (1.0 - t);
+ const real_t omt2 = omt * omt;
+ const real_t omt3 = omt2 * omt;
+ const real_t t2 = t * t;
+ const real_t t3 = t2 * t;
+
+ return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
+ }
+};
+
+// thank you for existing, partial specialization
+template <>
+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.");
+
+ return a.slerp(b, c).normalized();
+ }
+
+ Quaternion catmull_rom(const Quaternion &p0, const Quaternion &p1, const Quaternion &p2, const Quaternion &p3, const float c) {
+ ERR_FAIL_COND_V_MSG(!p1.is_normalized(), Quaternion(), "The quaternion \"p1\" must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p2.is_normalized(), Quaternion(), "The quaternion \"p2\" must be normalized.");
+
+ return p1.slerp(p2, c).normalized();
+ }
+
+ Quaternion bezier(const Quaternion start, const Quaternion control_1, const Quaternion control_2, const Quaternion end, const float t) {
+ ERR_FAIL_COND_V_MSG(!start.is_normalized(), Quaternion(), "The start quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!end.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
+
+ return start.slerp(end, t).normalized();
+ }
+};
+
+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) {
+ ERR_FAIL_COND_V(!p_values.size(), T());
+ 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];
+ }
+ //could use binary search, worth it?
+ int idx = -1;
+ for (int i = 0; i < p_times.size(); i++) {
+ if (p_times[i] > p_time) {
+ break;
+ }
+ idx++;
+ }
+
+ EditorSceneFormatImporterGLTFInterpolate<T> interp;
+
+ switch (p_interp) {
+ case GLTFAnimation::INTERP_LINEAR: {
+ if (idx == -1) {
+ return p_values[0];
+ } else if (idx >= p_times.size() - 1) {
+ return p_values[p_times.size() - 1];
+ }
+
+ const float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
+
+ return interp.lerp(p_values[idx], p_values[idx + 1], c);
+ } break;
+ case GLTFAnimation::INTERP_STEP: {
+ if (idx == -1) {
+ return p_values[0];
+ } else if (idx >= p_times.size() - 1) {
+ return p_values[p_times.size() - 1];
+ }
+
+ return p_values[idx];
+ } break;
+ case GLTFAnimation::INTERP_CATMULLROMSPLINE: {
+ if (idx == -1) {
+ return p_values[1];
+ } else if (idx >= p_times.size() - 1) {
+ return p_values[1 + p_times.size() - 1];
+ }
+
+ const float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
+
+ return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c);
+ } break;
+ case GLTFAnimation::INTERP_CUBIC_SPLINE: {
+ if (idx == -1) {
+ return p_values[1];
+ } else if (idx >= p_times.size() - 1) {
+ return p_values[(p_times.size() - 1) * 3 + 1];
+ }
+
+ const float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
+
+ const T from = p_values[idx * 3 + 1];
+ const T c1 = from + p_values[idx * 3 + 2];
+ const T to = p_values[idx * 3 + 4];
+ const T c2 = to + p_values[idx * 3 + 3];
+
+ return interp.bezier(from, c1, c2, to, c);
+ } break;
+ }
+
+ ERR_FAIL_V(p_values[0]);
+}
+
+void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, const GLTFAnimationIndex index, const int bake_fps) {
+ Ref<GLTFAnimation> anim = state->animations[index];
+
+ String name = anim->get_name();
+ if (name.is_empty()) {
+ // No node represent these, and they are not in the hierarchy, so just make a unique name
+ name = _gen_unique_name(state, "Animation");
+ }
+
+ Ref<Animation> animation;
+ animation.instantiate();
+ animation->set_name(name);
+
+ if (anim->get_loop()) {
+ animation->set_loop_mode(Animation::LOOP_LINEAR);
+ }
+
+ float length = 0.0;
+
+ 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;
+
+ const Ref<GLTFNode> gltf_node = state->nodes[track_i.key];
+
+ Node *root = ap->get_parent();
+ ERR_FAIL_COND(root == nullptr);
+ Map<GLTFNodeIndex, Node *>::Element *node_element = state->scene_nodes.find(node_index);
+ ERR_CONTINUE_MSG(node_element == nullptr, vformat("Unable to find node %d for animation", node_index));
+ node_path = root->get_path_to(node_element->get());
+
+ if (gltf_node->skeleton >= 0) {
+ const Skeleton3D *sk = state->skeletons[gltf_node->skeleton]->godot_skeleton;
+ ERR_FAIL_COND(sk == nullptr);
+
+ const String path = ap->get_parent()->get_path_to(sk);
+ const String bone = gltf_node->get_name();
+ transform_node_path = path + ":" + bone;
+ } else {
+ transform_node_path = node_path;
+ }
+
+ 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.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]);
+ }
+
+ for (int i = 0; i < track.weight_tracks.size(); i++) {
+ for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
+ length = MAX(length, track.weight_tracks[i].times[j]);
+ }
+ }
+
+ // 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.position_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) {
+ //make transform track
+ 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;
+ double time = 0.0;
+
+ Vector3 base_pos;
+ Quaternion base_rot;
+ Vector3 base_scale = Vector3(1, 1, 1);
+
+ if (rotation_idx == -1) {
+ base_rot = state->nodes[track_i.key]->rotation.normalized();
+ }
+
+ if (position_idx == -1) {
+ base_pos = state->nodes[track_i.key]->position;
+ }
+
+ if (scale_idx == -1) {
+ base_scale = state->nodes[track_i.key]->scale;
+ }
+
+ bool last = false;
+ while (true) {
+ Vector3 pos = base_pos;
+ Quaternion rot = base_rot;
+ Vector3 scale = base_scale;
+
+ 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 (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 (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 (last) {
+ break;
+ }
+ time += increment;
+ if (time >= length) {
+ last = true;
+ time = length;
+ }
+ }
+ }
+
+ for (int i = 0; i < track.weight_tracks.size(); i++) {
+ ERR_CONTINUE(gltf_node->mesh < 0 || gltf_node->mesh >= state->meshes.size());
+ Ref<GLTFMesh> mesh = state->meshes[gltf_node->mesh];
+ ERR_CONTINUE(mesh.is_null());
+ ERR_CONTINUE(mesh->get_mesh().is_null());
+ ERR_CONTINUE(mesh->get_mesh()->get_mesh().is_null());
+
+ 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_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,
+ // the other modes have to be baked.
+ GLTFAnimation::Interpolation gltf_interp = track.weight_tracks[i].interpolation;
+ if (gltf_interp == GLTFAnimation::INTERP_LINEAR || gltf_interp == GLTFAnimation::INTERP_STEP) {
+ animation->track_set_interpolation_type(track_idx, gltf_interp == GLTFAnimation::INTERP_STEP ? Animation::INTERPOLATION_NEAREST : Animation::INTERPOLATION_LINEAR);
+ 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->blend_shape_track_insert_key(track_idx, t, attribs);
+ }
+ } else {
+ // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
+ const double increment = 1.0 / bake_fps;
+ double time = 0.0;
+ bool last = false;
+ while (true) {
+ float blend = _interpolate_track<float>(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;
+ }
+ time += increment;
+ if (time >= length) {
+ last = true;
+ time = length;
+ }
+ }
+ }
+ }
+ }
+
+ animation->set_length(length);
+
+ ap->add_animation(name, animation);
+}
+
+void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
+ for (GLTFNodeIndex mi_node_i = 0; mi_node_i < state->nodes.size(); ++mi_node_i) {
+ Ref<GLTFNode> node = state->nodes[mi_node_i];
+
+ if (node->mesh < 0) {
+ continue;
+ }
+ Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(mi_node_i);
+ if (!mi_element) {
+ continue;
+ }
+ MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(mi_element->get());
+ ERR_CONTINUE(!mi);
+ Transform3D mi_xform = mi->get_transform();
+ node->scale = mi_xform.basis.get_scale();
+ node->rotation = mi_xform.basis.get_rotation_quaternion();
+ node->position = mi_xform.origin;
+
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(mi->get_node(mi->get_skeleton_path()));
+ if (!skeleton) {
+ continue;
+ }
+ if (!skeleton->get_bone_count()) {
+ continue;
+ }
+ Ref<Skin> skin = mi->get_skin();
+ Ref<GLTFSkin> gltf_skin;
+ gltf_skin.instantiate();
+ Array json_joints;
+
+ NodePath skeleton_path = mi->get_skeleton_path();
+ 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 = 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 {
+ 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;
+ }
+ }
+}
+
+float GLTFDocument::solve_metallic(float p_dielectric_specular, float diffuse, float specular, float p_one_minus_specular_strength) {
+ if (specular <= p_dielectric_specular) {
+ return 0.0f;
+ }
+
+ const float a = p_dielectric_specular;
+ const float b = diffuse * p_one_minus_specular_strength / (1.0f - p_dielectric_specular) + specular - 2.0f * p_dielectric_specular;
+ const float c = p_dielectric_specular - specular;
+ const float D = b * b - 4.0f * a * c;
+ return CLAMP((-b + Math::sqrt(D)) / (2.0f * a), 0.0f, 1.0f);
+}
+
+float GLTFDocument::get_perceived_brightness(const Color p_color) {
+ const Color coeff = Color(R_BRIGHTNESS_COEFF, G_BRIGHTNESS_COEFF, B_BRIGHTNESS_COEFF);
+ const Color value = coeff * (p_color * p_color);
+
+ const float r = value.r;
+ const float g = value.g;
+ const float b = value.b;
+
+ return Math::sqrt(r + g + b);
+}
+
+float GLTFDocument::get_max_component(const Color &p_color) {
+ const float r = p_color.r;
+ const float g = p_color.g;
+ const float b = p_color.b;
+
+ return MAX(MAX(r, g), b);
+}
+
+void GLTFDocument::_process_mesh_instances(Ref<GLTFState> state, Node *scene_root) {
+ for (GLTFNodeIndex node_i = 0; node_i < state->nodes.size(); ++node_i) {
+ Ref<GLTFNode> node = state->nodes[node_i];
+
+ if (node->skin >= 0 && node->mesh >= 0) {
+ const GLTFSkinIndex skin_i = node->skin;
+
+ Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(node_i);
+ ERR_CONTINUE_MSG(mi_element == nullptr, vformat("Unable to find node %d", node_i));
+
+ 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];
+ Skeleton3D *skeleton = gltf_skeleton->godot_skeleton;
+ 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, true);
+ mi->set_owner(skeleton->get_owner());
+
+ mi->set_skin(state->skins.write[skin_i]->godot_skin);
+ mi->set_skeleton_path(mi->get_path_to(skeleton));
+ mi->set_transform(Transform3D());
+ }
+ }
+}
+
+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;
+ 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 = p_animation->track_get_type(p_track_i);
+ int32_t key_count = p_animation->track_get_key_count(p_track_i);
+ Vector<float> 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);
+ }
+ 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);
+ 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++) {
+ Quaternion rotation;
+ Error err = p_animation->rotation_track_get_key(p_track_i, key_i, &rotation);
+ ERR_CONTINUE(err != OK);
+ p_track.rotation_track.values.write[key_i] = rotation;
+ }
+ } else if (path.find(":transform") != -1) {
+ 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;
+ p_track.scale_track.interpolation = gltf_interpolation;
+
+ p_track.scale_track.values.resize(key_count);
+ p_track.scale_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.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();
+ }
+ } else if (track_type == Animation::TYPE_VALUE) {
+ if (path.find("/rotation_quat") != -1) {
+ p_track.rotation_track.times = times;
+ p_track.rotation_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++) {
+ 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(":position") != -1) {
+ p_track.position_track.times = times;
+ p_track.position_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 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;
+ p_track.rotation_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++) {
+ Vector3 rotation_radian = p_animation->track_get_key_value(p_track_i, key_i);
+ p_track.rotation_track.values.write[key_i] = Quaternion(rotation_radian);
+ }
+ } else if (path.find(":scale") != -1) {
+ 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;
+
+ for (int32_t key_i = 0; key_i < key_count; key_i++) {
+ Vector3 scale_track = p_animation->track_get_key_value(p_track_i, key_i);
+ p_track.scale_track.values.write[key_i] = scale_track;
+ }
+ }
+ } else if (track_type == Animation::TYPE_BEZIER) {
+ 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;
+ 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.scale_track.times = new_times;
+ p_track.scale_track.interpolation = gltf_interpolation;
+
+ p_track.scale_track.values.resize(keys);
+
+ for (int32_t key_i = 0; key_i < keys; key_i++) {
+ p_track.scale_track.values.write[key_i] = Vector3(1.0f, 1.0f, 1.0f);
+ }
+ p_track.scale_track.interpolation = gltf_interpolation;
+ }
+
+ for (int32_t key_i = 0; key_i < keys; key_i++) {
+ 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);
+ } else if (path.find("/scale:y") != -1) {
+ bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ } else if (path.find("/scale:z") != -1) {
+ bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ }
+ p_track.scale_track.values.write[key_i] = bezier_track;
+ }
+ } 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.position_track.times.size()) {
+ Vector<float> 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.position_track.times = new_times;
+ p_track.position_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.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);
+ } else if (path.find("/position:y") != -1) {
+ bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ } else if (path.find("/position:z") != -1) {
+ bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
+ }
+ p_track.position_track.values.write[key_i] = bezier_track;
+ }
+ }
+ }
+ return p_track;
+}
+
+void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap, String p_animation_track_name) {
+ Ref<Animation> animation = ap->get_animation(p_animation_track_name);
+ Ref<GLTFAnimation> gltf_animation;
+ gltf_animation.instantiate();
+ gltf_animation->set_name(_gen_unique_name(state, p_animation_track_name));
+
+ for (int32_t track_i = 0; track_i < animation->get_track_count(); track_i++) {
+ if (!animation->track_is_enabled(track_i)) {
+ continue;
+ }
+ String orig_track_path = animation->track_get_path(track_i);
+ 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 (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 (position_track_i) {
+ track = position_track_i->get();
+ }
+ track = _convert_animation_track(state, track, animation, track_i, node_index);
+ gltf_animation->get_tracks().insert(node_index, track);
+ }
+ }
+ } else if (String(orig_track_path).find(":rotation_degrees") != -1) {
+ 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 (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, track_i, node_index);
+ gltf_animation->get_tracks().insert(node_index, track);
+ }
+ }
+ } else if (String(orig_track_path).find(":scale") != -1) {
+ 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 (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, track_i, node_index);
+ gltf_animation->get_tracks().insert(node_index, track);
+ }
+ }
+ } else if (String(orig_track_path).find(":transform") != -1) {
+ 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 (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, 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];
+ 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<float> 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;
+ }
+ 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;
+ }
+ int32_t key_count = animation->track_get_key_count(shape_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(shape_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(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(":");
+ const String node = node_suffix[0];
+ const NodePath node_path = node;
+ const String suffix = node_suffix[1];
+ Node *godot_node = ap->get_parent()->get_node_or_null(node_path);
+ Skeleton3D *skeleton = nullptr;
+ GLTFSkeletonIndex skeleton_gltf_i = -1;
+ for (GLTFSkeletonIndex skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) {
+ if (state->skeletons[skeleton_i]->godot_skeleton == cast_to<Skeleton3D>(godot_node)) {
+ skeleton = state->skeletons[skeleton_i]->godot_skeleton;
+ skeleton_gltf_i = skeleton_i;
+ ERR_CONTINUE(!skeleton);
+ Ref<GLTFSkeleton> skeleton_gltf = state->skeletons[skeleton_gltf_i];
+ int32_t bone = skeleton->find_bone(suffix);
+ ERR_CONTINUE(bone == -1);
+ if (!skeleton_gltf->godot_bone_node.has(bone)) {
+ continue;
+ }
+ GLTFNodeIndex node_i = skeleton_gltf->godot_bone_node[bone];
+ Map<int, GLTFAnimation::Track>::Element *property_track_i = gltf_animation->get_tracks().find(node_i);
+ GLTFAnimation::Track track;
+ if (property_track_i) {
+ track = property_track_i->get();
+ }
+ 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());
+ 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;
+ }
+ }
+ }
+ }
+ if (gltf_animation->get_tracks().size()) {
+ state->animations.push_back(gltf_animation);
+ }
+}
+
+Error GLTFDocument::parse(Ref<GLTFState> state, String p_path, bool p_read_binary) {
+ Error err;
+ FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
+ if (!f) {
+ return err;
+ }
+ uint32_t magic = f->get_32();
+ if (magic == 0x46546C67) {
+ //binary file
+ //text file
+ err = _parse_glb(p_path, state);
+ if (err) {
+ return FAILED;
+ }
+ } else {
+ //text file
+ err = _parse_json(p_path, state);
+ if (err) {
+ return FAILED;
+ }
+ }
+ f->close();
+
+ // get file's name, use for scene name if none
+ state->filename = p_path.get_file().get_slice(".", 0);
+
+ ERR_FAIL_COND_V(!state->json.has("asset"), Error::FAILED);
+
+ Dictionary asset = state->json["asset"];
+
+ ERR_FAIL_COND_V(!asset.has("version"), Error::FAILED);
+
+ String version = asset["version"];
+
+ state->major_version = version.get_slice(".", 0).to_int();
+ state->minor_version = version.get_slice(".", 1).to_int();
+
+ /* STEP 0 PARSE SCENE */
+ err = _parse_scenes(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 1 PARSE NODES */
+ err = _parse_nodes(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 2 PARSE BUFFERS */
+ err = _parse_buffers(state, p_path.get_base_dir());
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 3 PARSE BUFFER VIEWS */
+ err = _parse_buffer_views(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 4 PARSE ACCESSORS */
+ err = _parse_accessors(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 5 PARSE IMAGES */
+ err = _parse_images(state, p_path.get_base_dir());
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 6 PARSE TEXTURES */
+ err = _parse_textures(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 7 PARSE TEXTURES */
+ err = _parse_materials(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 9 PARSE SKINS */
+ err = _parse_skins(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 10 DETERMINE SKELETONS */
+ err = _determine_skeletons(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 11 CREATE SKELETONS */
+ err = _create_skeletons(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 12 CREATE SKINS */
+ err = _create_skins(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 13 PARSE MESHES (we have enough info now) */
+ err = _parse_meshes(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 14 PARSE LIGHTS */
+ err = _parse_lights(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 15 PARSE CAMERAS */
+ err = _parse_cameras(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 16 PARSE ANIMATIONS */
+ err = _parse_animations(state);
+ if (err != OK) {
+ return Error::FAILED;
+ }
+
+ /* STEP 17 ASSIGN SCENE NAMES */
+ _assign_scene_names(state);
+
+ return OK;
+}
+
+Dictionary GLTFDocument::_serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material) {
+ Dictionary extension;
+ Ref<BaseMaterial3D> mat = p_material;
+ if (mat.is_valid()) {
+ Dictionary texture_transform;
+ Array offset;
+ offset.resize(2);
+ offset[0] = mat->get_uv2_offset().x;
+ offset[1] = mat->get_uv2_offset().y;
+ texture_transform["offset"] = offset;
+ Array scale;
+ scale.resize(2);
+ scale[0] = mat->get_uv2_scale().x;
+ scale[1] = mat->get_uv2_scale().y;
+ texture_transform["scale"] = scale;
+ // Godot doesn't support texture rotation
+ extension["KHR_texture_transform"] = texture_transform;
+ }
+ return extension;
+}
+
+Dictionary GLTFDocument::_serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material) {
+ Dictionary extension;
+ if (p_material.is_valid()) {
+ Dictionary texture_transform;
+ Array offset;
+ offset.resize(2);
+ offset[0] = p_material->get_uv1_offset().x;
+ offset[1] = p_material->get_uv1_offset().y;
+ texture_transform["offset"] = offset;
+ Array scale;
+ scale.resize(2);
+ scale[0] = p_material->get_uv1_scale().x;
+ scale[1] = p_material->get_uv1_scale().y;
+ texture_transform["scale"] = scale;
+ // Godot doesn't support texture rotation
+ extension["KHR_texture_transform"] = texture_transform;
+ }
+ return extension;
+}
+
+Error GLTFDocument::_serialize_version(Ref<GLTFState> state) {
+ const String version = "2.0";
+ state->major_version = version.get_slice(".", 0).to_int();
+ state->minor_version = version.get_slice(".", 1).to_int();
+ Dictionary asset;
+ asset["version"] = version;
+
+ String hash = VERSION_HASH;
+ asset["generator"] = String(VERSION_FULL_NAME) + String("@") + (hash.length() == 0 ? String("unknown") : hash);
+ state->json["asset"] = asset;
+ ERR_FAIL_COND_V(!asset.has("version"), Error::FAILED);
+ ERR_FAIL_COND_V(!state->json.has("asset"), Error::FAILED);
+ return OK;
+}
+
+Error GLTFDocument::_serialize_file(Ref<GLTFState> state, const String p_path) {
+ Error err = FAILED;
+ if (p_path.to_lower().ends_with("glb")) {
+ err = _encode_buffer_glb(state, p_path);
+ ERR_FAIL_COND_V(err != OK, err);
+ FileAccessRef f = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V(!f, FAILED);
+
+ String json = Variant(state->json).to_json_string();
+
+ const uint32_t magic = 0x46546C67; // GLTF
+ const int32_t header_size = 12;
+ const int32_t chunk_header_size = 8;
+ CharString cs = json.utf8();
+ 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
+
+ uint32_t binary_data_length = 0;
+ if (state->buffers.size()) {
+ binary_data_length = state->buffers[0].size();
+ }
+ 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_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 {
+ err = _encode_buffer_bins(state, p_path);
+ ERR_FAIL_COND_V(err != OK, err);
+ FileAccessRef f = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V(!f, FAILED);
+
+ f->create(FileAccess::ACCESS_RESOURCES);
+ String json = Variant(state->json).to_json_string();
+ f->store_string(json);
+ f->close();
+ }
+ 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
new file mode 100644
index 0000000000..27a1f64bca
--- /dev/null
+++ b/modules/gltf/gltf_document.h
@@ -0,0 +1,454 @@
+/*************************************************************************/
+/* gltf_document.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 GLTF_DOCUMENT_H
+#define GLTF_DOCUMENT_H
+
+#include "gltf_animation.h"
+
+#include "core/variant/dictionary.h"
+#include "core/variant/variant.h"
+#include "gltf_document_extension_convert_importer_mesh.h"
+#include "scene/3d/bone_attachment_3d.h"
+#include "scene/3d/importer_mesh_instance_3d.h"
+#include "scene/3d/light_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/node_3d.h"
+#include "scene/3d/skeleton_3d.h"
+#include "scene/animation/animation_player.h"
+#include "scene/resources/material.h"
+#include "scene/resources/texture.h"
+
+#include "modules/modules_enabled.gen.h" // For csg, gridmap.
+
+#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;
+using GLTFBufferIndex = int;
+using GLTFBufferViewIndex = int;
+using GLTFCameraIndex = int;
+using GLTFImageIndex = int;
+using GLTFMaterialIndex = int;
+using GLTFMeshIndex = int;
+using GLTFLightIndex = int;
+using GLTFNodeIndex = int;
+using GLTFSkeletonIndex = int;
+using GLTFSkinIndex = int;
+using GLTFTextureIndex = int;
+
+class GLTFDocument : public Resource {
+ GDCLASS(GLTFDocument, 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,
+ TYPE_VEC2,
+ TYPE_VEC3,
+ TYPE_VEC4,
+ TYPE_MAT2,
+ TYPE_MAT3,
+ TYPE_MAT4,
+ };
+
+ enum {
+ ARRAY_BUFFER = 34962,
+ ELEMENT_ARRAY_BUFFER = 34963,
+
+ TYPE_BYTE = 5120,
+ TYPE_UNSIGNED_BYTE = 5121,
+ TYPE_SHORT = 5122,
+ TYPE_UNSIGNED_SHORT = 5123,
+ TYPE_UNSIGNED_INT = 5125,
+ TYPE_FLOAT = 5126,
+
+ COMPONENT_TYPE_BYTE = 5120,
+ COMPONENT_TYPE_UNSIGNED_BYTE = 5121,
+ COMPONENT_TYPE_SHORT = 5122,
+ COMPONENT_TYPE_UNSIGNED_SHORT = 5123,
+ COMPONENT_TYPE_INT = 5125,
+ 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) {
+ Array ret;
+ for (int i = 0; i < p_inp.size(); i++) {
+ ret.push_back(p_inp[i]);
+ }
+ return ret;
+ }
+
+ template <class T>
+ static Array to_array(const Set<T> &p_inp) {
+ Array ret;
+ typename Set<T>::Element *elem = p_inp.front();
+ while (elem) {
+ ret.push_back(elem->get());
+ elem = elem->next();
+ }
+ return ret;
+ }
+
+ template <class T>
+ static void set_from_array(Vector<T> &r_out, const Array &p_inp) {
+ r_out.clear();
+ for (int i = 0; i < p_inp.size(); i++) {
+ r_out.push_back(p_inp[i]);
+ }
+ }
+
+ template <class T>
+ static void set_from_array(Set<T> &r_out, const Array &p_inp) {
+ r_out.clear();
+ for (int i = 0; i < p_inp.size(); i++) {
+ r_out.insert(p_inp[i]);
+ }
+ }
+ template <class K, class V>
+ static Dictionary to_dict(const Map<K, V> &p_inp) {
+ Dictionary ret;
+ for (typename Map<K, V>::Element *E = p_inp.front(); E; E = E->next()) {
+ ret[E->key()] = E->value();
+ }
+ return ret;
+ }
+
+ template <class K, class V>
+ static void set_from_dict(Map<K, V> &r_out, const Dictionary &p_inp) {
+ r_out.clear();
+ Array keys = p_inp.keys();
+ for (int i = 0; i < keys.size(); i++) {
+ 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);
+ Error _parse_scenes(Ref<GLTFState> state);
+ Error _parse_nodes(Ref<GLTFState> state);
+ String _get_type_name(const GLTFType p_component);
+ String _get_accessor_type_name(const GLTFDocument::GLTFType p_type);
+ String _gen_unique_name(Ref<GLTFState> state, const String &p_name);
+ String _sanitize_animation_name(const String &name);
+ String _gen_unique_animation_name(Ref<GLTFState> state, const String &p_name);
+ String _sanitize_bone_name(const String &name);
+ String _gen_unique_bone_name(Ref<GLTFState> state,
+ const GLTFSkeletonIndex skel_i,
+ const String &p_name);
+ GLTFTextureIndex _set_texture(Ref<GLTFState> state, Ref<Texture2D> p_texture);
+ Ref<Texture2D> _get_texture(Ref<GLTFState> state,
+ const GLTFTextureIndex p_texture);
+ Error _parse_json(const String &p_path, Ref<GLTFState> state);
+ Error _parse_glb(const String &p_path, Ref<GLTFState> state);
+ void _compute_node_heights(Ref<GLTFState> state);
+ Error _parse_buffers(Ref<GLTFState> state, const String &p_base_path);
+ Error _parse_buffer_views(Ref<GLTFState> state);
+ GLTFType _get_type_from_str(const String &p_string);
+ Error _parse_accessors(Ref<GLTFState> state);
+ Error _decode_buffer_view(Ref<GLTFState> state, double *dst,
+ const GLTFBufferViewIndex p_buffer_view,
+ const int skip_every, const int skip_bytes,
+ const int element_size, const int count,
+ const GLTFType type, const int component_count,
+ const int component_type, const int component_size,
+ const bool normalized, const int byte_offset,
+ const bool for_vertex);
+ Vector<double> _decode_accessor(Ref<GLTFState> state,
+ const GLTFAccessorIndex p_accessor,
+ const bool p_for_vertex);
+ Vector<float> _decode_accessor_as_floats(Ref<GLTFState> state,
+ const GLTFAccessorIndex p_accessor,
+ const bool p_for_vertex);
+ Vector<int> _decode_accessor_as_ints(Ref<GLTFState> state,
+ const GLTFAccessorIndex p_accessor,
+ const bool p_for_vertex);
+ Vector<Vector2> _decode_accessor_as_vec2(Ref<GLTFState> state,
+ const GLTFAccessorIndex p_accessor,
+ const bool p_for_vertex);
+ Vector<Vector3> _decode_accessor_as_vec3(Ref<GLTFState> state,
+ const GLTFAccessorIndex p_accessor,
+ const bool p_for_vertex);
+ Vector<Color> _decode_accessor_as_color(Ref<GLTFState> state,
+ const GLTFAccessorIndex p_accessor,
+ const bool p_for_vertex);
+ Vector<Quaternion> _decode_accessor_as_quaternion(Ref<GLTFState> state,
+ const GLTFAccessorIndex p_accessor,
+ const bool p_for_vertex);
+ Vector<Transform2D> _decode_accessor_as_xform2d(Ref<GLTFState> state,
+ const GLTFAccessorIndex p_accessor,
+ const bool p_for_vertex);
+ Vector<Basis> _decode_accessor_as_basis(Ref<GLTFState> state,
+ const GLTFAccessorIndex p_accessor,
+ const bool p_for_vertex);
+ Vector<Transform3D> _decode_accessor_as_xform(Ref<GLTFState> state,
+ const GLTFAccessorIndex p_accessor,
+ const bool p_for_vertex);
+ Error _parse_meshes(Ref<GLTFState> state);
+ Error _serialize_textures(Ref<GLTFState> state);
+ Error _serialize_images(Ref<GLTFState> state, const String &p_path);
+ Error _serialize_lights(Ref<GLTFState> state);
+ Error _parse_images(Ref<GLTFState> state, const String &p_base_path);
+ Error _parse_textures(Ref<GLTFState> state);
+ Error _parse_materials(Ref<GLTFState> state);
+ void _set_texture_transform_uv1(const Dictionary &d, Ref<BaseMaterial3D> material);
+ void spec_gloss_to_rough_metal(Ref<GLTFSpecGloss> r_spec_gloss,
+ Ref<BaseMaterial3D> p_material);
+ static void spec_gloss_to_metal_base_color(const Color &p_specular_factor,
+ const Color &p_diffuse,
+ Color &r_base_color,
+ float &r_metallic);
+ GLTFNodeIndex _find_highest_node(Ref<GLTFState> state,
+ const Vector<GLTFNodeIndex> &subset);
+ bool _capture_nodes_in_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin,
+ const GLTFNodeIndex node_index);
+ void _capture_nodes_for_multirooted_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin);
+ Error _expand_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin);
+ Error _verify_skin(Ref<GLTFState> state, Ref<GLTFSkin> skin);
+ Error _parse_skins(Ref<GLTFState> state);
+ Error _determine_skeletons(Ref<GLTFState> state);
+ Error _reparent_non_joint_skeleton_subtrees(
+ Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton,
+ const Vector<GLTFNodeIndex> &non_joints);
+ Error _determine_skeleton_roots(Ref<GLTFState> state,
+ const GLTFSkeletonIndex skel_i);
+ Error _create_skeletons(Ref<GLTFState> state);
+ Error _map_skin_joints_indices_to_skeleton_bone_indices(Ref<GLTFState> state);
+ Error _serialize_skins(Ref<GLTFState> state);
+ Error _create_skins(Ref<GLTFState> state);
+ bool _skins_are_same(const Ref<Skin> skin_a, const Ref<Skin> skin_b);
+ void _remove_duplicate_skins(Ref<GLTFState> state);
+ Error _serialize_cameras(Ref<GLTFState> state);
+ Error _parse_cameras(Ref<GLTFState> state);
+ Error _parse_lights(Ref<GLTFState> state);
+ Error _parse_animations(Ref<GLTFState> state);
+ Error _serialize_animations(Ref<GLTFState> state);
+ BoneAttachment3D *_generate_bone_attachment(Ref<GLTFState> state,
+ Skeleton3D *skeleton,
+ const GLTFNodeIndex node_index,
+ const GLTFNodeIndex bone_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,
+ const float p_time,
+ const GLTFAnimation::Interpolation p_interp);
+ GLTFAccessorIndex _encode_accessor_as_quaternions(Ref<GLTFState> state,
+ const Vector<Quaternion> p_attribs,
+ const bool p_for_vertex);
+ GLTFAccessorIndex _encode_accessor_as_weights(Ref<GLTFState> state,
+ const Vector<Color> p_attribs,
+ const bool p_for_vertex);
+ GLTFAccessorIndex _encode_accessor_as_joints(Ref<GLTFState> state,
+ const Vector<Color> p_attribs,
+ const bool p_for_vertex);
+ GLTFAccessorIndex _encode_accessor_as_floats(Ref<GLTFState> state,
+ const Vector<real_t> p_attribs,
+ const bool p_for_vertex);
+ GLTFAccessorIndex _encode_accessor_as_vec2(Ref<GLTFState> state,
+ const Vector<Vector2> p_attribs,
+ const bool p_for_vertex);
+
+ void _calc_accessor_vec2_min_max(int i, const int element_count, Vector<double> &type_max, Vector2 attribs, Vector<double> &type_min) {
+ if (i == 0) {
+ for (int32_t type_i = 0; type_i < element_count; type_i++) {
+ type_max.write[type_i] = attribs[(i * element_count) + type_i];
+ type_min.write[type_i] = attribs[(i * element_count) + type_i];
+ }
+ }
+ for (int32_t type_i = 0; type_i < element_count; type_i++) {
+ type_max.write[type_i] = MAX(attribs[(i * element_count) + type_i], type_max[type_i]);
+ type_min.write[type_i] = MIN(attribs[(i * element_count) + type_i], type_min[type_i]);
+ type_max.write[type_i] = _filter_number(type_max.write[type_i]);
+ type_min.write[type_i] = _filter_number(type_min.write[type_i]);
+ }
+ }
+
+ GLTFAccessorIndex _encode_accessor_as_vec3(Ref<GLTFState> state,
+ const Vector<Vector3> p_attribs,
+ const bool p_for_vertex);
+ GLTFAccessorIndex _encode_accessor_as_color(Ref<GLTFState> state,
+ const Vector<Color> p_attribs,
+ const bool p_for_vertex);
+
+ void _calc_accessor_min_max(int p_i, const int p_element_count, Vector<double> &p_type_max, Vector<double> p_attribs, Vector<double> &p_type_min);
+
+ GLTFAccessorIndex _encode_accessor_as_ints(Ref<GLTFState> state,
+ const Vector<int32_t> p_attribs,
+ const bool p_for_vertex);
+ GLTFAccessorIndex _encode_accessor_as_xform(Ref<GLTFState> state,
+ const Vector<Transform3D> p_attribs,
+ const bool p_for_vertex);
+ Error _encode_buffer_view(Ref<GLTFState> state, const double *src,
+ const int count, const GLTFType type,
+ const int component_type, const bool normalized,
+ const int byte_offset, const bool for_vertex,
+ GLTFBufferViewIndex &r_accessor);
+ Error _encode_accessors(Ref<GLTFState> state);
+ Error _encode_buffer_views(Ref<GLTFState> state);
+ Error _serialize_materials(Ref<GLTFState> state);
+ Error _serialize_meshes(Ref<GLTFState> state);
+ Error _serialize_nodes(Ref<GLTFState> state);
+ Error _serialize_scenes(Ref<GLTFState> state);
+ 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,
+ 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);
+ Dictionary _serialize_texture_transform_uv1(Ref<BaseMaterial3D> p_material);
+ Dictionary _serialize_texture_transform_uv2(Ref<BaseMaterial3D> p_material);
+ Error _serialize_version(Ref<GLTFState> state);
+ Error _serialize_file(Ref<GLTFState> state, const String p_path);
+ Error _serialize_extensions(Ref<GLTFState> state) const;
+
+public:
+ // https://www.itu.int/rec/R-REC-BT.601
+ // https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.601-7-201103-I!!PDF-E.pdf
+ static constexpr float R_BRIGHTNESS_COEFF = 0.299f;
+ static constexpr float G_BRIGHTNESS_COEFF = 0.587f;
+ static constexpr float B_BRIGHTNESS_COEFF = 0.114f;
+
+private:
+ // https://github.com/microsoft/glTF-SDK/blob/master/GLTFSDK/Source/PBRUtils.cpp#L9
+ // https://bghgary.github.io/glTF/convert-between-workflows-bjs/js/babylon.pbrUtilities.js
+ static float solve_metallic(float p_dielectric_specular, float diffuse,
+ float specular,
+ float p_one_minus_specular_strength);
+ static float get_perceived_brightness(const Color p_color);
+ static float get_max_component(const Color &p_color);
+
+public:
+ void _process_mesh_instances(Ref<GLTFState> state, Node *scene_root);
+ void _generate_scene_node(Ref<GLTFState> state, Node *scene_parent,
+ Node3D *scene_root,
+ const GLTFNodeIndex node_index);
+ 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);
+ 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, Ref<GLTFNode> gltf_node);
+ GLTFLightIndex _convert_light(Ref<GLTFState> state, Light3D *p_light);
+ void _convert_spatial(Ref<GLTFState> state, Node3D *p_spatial, Ref<GLTFNode> p_node);
+ 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(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,
+ Node *p_scene_parent,
+ GLTFNodeIndex current_node_i,
+ GLTFNodeIndex p_parent_node_index,
+ GLTFNodeIndex p_root_gltf_node,
+ Ref<GLTFNode> gltf_node);
+ void _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);
+ void _check_visibility(Node *p_node, bool &retflag);
+ void _convert_camera_to_gltf(Camera3D *camera, Ref<GLTFState> state,
+ Ref<GLTFNode> gltf_node);
+#ifdef MODULE_GRIDMAP_ENABLED
+ void _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);
+#endif // MODULE_GRIDMAP_ENABLED
+ 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(
+ 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,
+ 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);
+ Error parse(Ref<GLTFState> state, String p_paths, bool p_read_binary = false);
+};
+
+#endif // GLTF_DOCUMENT_H
diff --git a/modules/gltf/gltf_document_extension.cpp b/modules/gltf/gltf_document_extension.cpp
new file mode 100644
index 0000000000..a423059a9c
--- /dev/null
+++ b/modules/gltf/gltf_document_extension.cpp
@@ -0,0 +1,88 @@
+/*************************************************************************/
+/* gltf_document_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 "gltf_document_extension.h"
+
+#include "gltf_document.h"
+
+void GLTFDocumentExtension::_bind_methods() {
+ // Import
+ ClassDB::bind_method(D_METHOD("get_import_setting_keys"),
+ &GLTFDocumentExtension::get_import_setting_keys);
+ ClassDB::bind_method(D_METHOD("import_preflight", "document"),
+ &GLTFDocumentExtension::import_preflight);
+ ClassDB::bind_method(D_METHOD("get_import_setting", "key"),
+ &GLTFDocumentExtension::get_import_setting);
+ ClassDB::bind_method(D_METHOD("set_import_setting", "key", "value"),
+ &GLTFDocumentExtension::set_import_setting);
+ ClassDB::bind_method(D_METHOD("import_post", "document", "node"),
+ &GLTFDocumentExtension::import_post);
+ // Export
+ ClassDB::bind_method(D_METHOD("get_export_setting_keys"),
+ &GLTFDocumentExtension::get_export_setting_keys);
+ ClassDB::bind_method(D_METHOD("get_export_setting", "key"),
+ &GLTFDocumentExtension::get_export_setting);
+ ClassDB::bind_method(D_METHOD("set_export_setting", "key", "value"),
+ &GLTFDocumentExtension::set_export_setting);
+ ClassDB::bind_method(D_METHOD("export_preflight", "document", "node"),
+ &GLTFDocumentExtension::export_preflight);
+ ClassDB::bind_method(D_METHOD("export_post", "document"),
+ &GLTFDocumentExtension::export_post);
+}
+
+Array GLTFDocumentExtension::get_import_setting_keys() const {
+ return import_settings.keys();
+}
+
+Variant GLTFDocumentExtension::get_import_setting(const StringName &p_key) const {
+ if (!import_settings.has(p_key)) {
+ return Variant();
+ }
+ return import_settings[p_key];
+}
+
+void GLTFDocumentExtension::set_import_setting(const StringName &p_key, Variant p_var) {
+ import_settings[p_key] = p_var;
+}
+
+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/gltf/gltf_document_extension.h b/modules/gltf/gltf_document_extension.h
new file mode 100644
index 0000000000..622a65708c
--- /dev/null
+++ b/modules/gltf/gltf_document_extension.h
@@ -0,0 +1,63 @@
+/*************************************************************************/
+/* gltf_document_extension.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 GLTF_DOCUMENT_EXTENSION_H
+#define GLTF_DOCUMENT_EXTENSION_H
+
+#include "core/io/resource.h"
+#include "core/variant/dictionary.h"
+#include "core/variant/typed_array.h"
+#include "core/variant/variant.h"
+class GLTFDocument;
+class GLTFDocumentExtension : public Resource {
+ GDCLASS(GLTFDocumentExtension, Resource);
+
+ Dictionary import_settings;
+ Dictionary export_settings;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual Array get_import_setting_keys() const;
+ virtual Variant get_import_setting(const StringName &p_key) const;
+ virtual void set_import_setting(const StringName &p_key, Variant p_var);
+ virtual Error import_preflight(Ref<GLTFDocument> p_document) { return OK; }
+ virtual Error import_post(Ref<GLTFDocument> p_document, Node *p_node) { return OK; }
+
+public:
+ virtual Array get_export_setting_keys() const;
+ virtual Variant get_export_setting(const StringName &p_key) const;
+ virtual void set_export_setting(const StringName &p_key, Variant p_var);
+ virtual Error export_preflight(Ref<GLTFDocument> p_document, Node *p_node) { return OK; }
+ virtual Error export_post(Ref<GLTFDocument> p_document) { return OK; }
+};
+
+#endif // GLTF_DOCUMENT_EXTENSION_H
diff --git a/modules/mono/editor/script_class_parser.h b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp
index d611e8fb74..56c8f5ca27 100644
--- a/modules/mono/editor/script_class_parser.h
+++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* script_class_parser.h */
+/* gltf_document_extension_convert_importer_mesh.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,81 +28,55 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SCRIPT_CLASS_PARSER_H
-#define SCRIPT_CLASS_PARSER_H
-
-#include "core/ustring.h"
-#include "core/variant.h"
-#include "core/vector.h"
-
-class ScriptClassParser {
-public:
- struct NameDecl {
- enum Type {
- NAMESPACE_DECL,
- CLASS_DECL,
- STRUCT_DECL
- };
-
- String name;
- Type type;
- };
-
- struct ClassDecl {
- String name;
- String namespace_;
- Vector<String> base;
- bool nested;
- };
-
-private:
- String code;
- int idx;
- int line;
- String error_str;
- bool error;
- Variant value;
-
- Vector<ClassDecl> classes;
-
- enum Token {
- TK_BRACKET_OPEN,
- TK_BRACKET_CLOSE,
- TK_CURLY_BRACKET_OPEN,
- TK_CURLY_BRACKET_CLOSE,
- TK_PERIOD,
- TK_COLON,
- TK_COMMA,
- TK_SYMBOL,
- TK_IDENTIFIER,
- TK_STRING,
- TK_NUMBER,
- TK_OP_LESS,
- TK_OP_GREATER,
- TK_EOF,
- TK_ERROR,
- TK_MAX
- };
-
- static const char *token_names[TK_MAX];
- static String get_token_name(Token p_token);
-
- Token get_token();
-
- Error _skip_generic_type_params();
-
- Error _parse_type_full_name(String &r_full_name);
- Error _parse_class_base(Vector<String> &r_base);
- Error _parse_type_constraints();
- Error _parse_namespace_name(String &r_name, int &r_curly_stack);
-
-public:
- Error parse(const String &p_code);
- Error parse_file(const String &p_filepath);
-
- String get_error();
-
- Vector<ClassDecl> get_classes();
-};
-
-#endif // SCRIPT_CLASS_PARSER_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"
+
+#include <cstddef>
+
+void GLTFDocumentExtensionConvertImporterMesh::_bind_methods() {
+}
+
+Error GLTFDocumentExtensionConvertImporterMesh::import_post(Ref<GLTFDocument> p_document, Node *p_node) {
+ ERR_FAIL_NULL_V(p_document, ERR_INVALID_PARAMETER);
+ ERR_FAIL_NULL_V(p_node, ERR_INVALID_PARAMETER);
+ 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/arkit/arkit_session_delegate.h b/modules/gltf/gltf_document_extension_convert_importer_mesh.h
index df98bf506e..85ddb4d250 100644
--- a/modules/arkit/arkit_session_delegate.h
+++ b/modules/gltf/gltf_document_extension_convert_importer_mesh.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* arkit_session_delegate.h */
+/* gltf_document_extension_convert_importer_mesh.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,23 +28,28 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef ARKIT_SESSION_DELEGATE_H
-#define ARKIT_SESSION_DELEGATE_H
+#ifndef GLTF_EXTENSION_EDITOR_H
+#define GLTF_EXTENSION_EDITOR_H
-#import <ARKit/ARKit.h>
-#import <UIKit/UIKit.h>
+#include "core/io/resource.h"
+#include "core/variant/dictionary.h"
-class ARKitInterface;
+#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"
-@interface ARKitSessionDelegate : NSObject <ARSessionDelegate> {
- ARKitInterface *arkit_interface;
-}
+class GLTFDocumentExtension;
+class GLTFDocument;
+class GLTFDocumentExtensionConvertImporterMesh : public GLTFDocumentExtension {
+ GDCLASS(GLTFDocumentExtensionConvertImporterMesh, GLTFDocumentExtension);
-@property(nonatomic) ARKitInterface *arkit_interface;
+protected:
+ static void _bind_methods();
-- (void)session:(ARSession *)session didAddAnchors:(NSArray<ARAnchor *> *)anchors API_AVAILABLE(ios(11.0));
-- (void)session:(ARSession *)session didRemoveAnchors:(NSArray<ARAnchor *> *)anchors API_AVAILABLE(ios(11.0));
-- (void)session:(ARSession *)session didUpdateAnchors:(NSArray<ARAnchor *> *)anchors API_AVAILABLE(ios(11.0));
-@end
-
-#endif /* !ARKIT_SESSION_DELEGATE_H */
+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
new file mode 100644
index 0000000000..c5aa8d5724
--- /dev/null
+++ b/modules/gltf/gltf_light.cpp
@@ -0,0 +1,101 @@
+/*************************************************************************/
+/* gltf_light.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 "gltf_light.h"
+
+void GLTFLight::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_color"), &GLTFLight::get_color);
+ 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_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);
+ ClassDB::bind_method(D_METHOD("set_inner_cone_angle", "inner_cone_angle"), &GLTFLight::set_inner_cone_angle);
+ ClassDB::bind_method(D_METHOD("get_outer_cone_angle"), &GLTFLight::get_outer_cone_angle);
+ ClassDB::bind_method(D_METHOD("set_outer_cone_angle", "outer_cone_angle"), &GLTFLight::set_outer_cone_angle);
+
+ 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, "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
+}
+
+Color GLTFLight::get_color() {
+ return color;
+}
+
+void GLTFLight::set_color(Color p_color) {
+ color = p_color;
+}
+
+float GLTFLight::get_intensity() {
+ return intensity;
+}
+
+void GLTFLight::set_intensity(float p_intensity) {
+ intensity = p_intensity;
+}
+
+String GLTFLight::get_light_type() {
+ return light_type;
+}
+
+void GLTFLight::set_light_type(String p_light_type) {
+ light_type = p_light_type;
+}
+
+float GLTFLight::get_range() {
+ return range;
+}
+
+void GLTFLight::set_range(float p_range) {
+ range = p_range;
+}
+
+float GLTFLight::get_inner_cone_angle() {
+ return inner_cone_angle;
+}
+
+void GLTFLight::set_inner_cone_angle(float p_inner_cone_angle) {
+ inner_cone_angle = p_inner_cone_angle;
+}
+
+float GLTFLight::get_outer_cone_angle() {
+ return outer_cone_angle;
+}
+
+void GLTFLight::set_outer_cone_angle(float p_outer_cone_angle) {
+ outer_cone_angle = p_outer_cone_angle;
+}
diff --git a/modules/gdnative/net/packet_peer_gdnative.h b/modules/gltf/gltf_light.h
index 00de8f7f4c..62a20d2f16 100644
--- a/modules/gdnative/net/packet_peer_gdnative.h
+++ b/modules/gltf/gltf_light.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* packet_peer_gdnative.h */
+/* gltf_light.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,32 +28,45 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef PACKET_PEER_GDNATIVE_H
-#define PACKET_PEER_GDNATIVE_H
+#ifndef GLTF_LIGHT_H
+#define GLTF_LIGHT_H
-#include "core/io/packet_peer.h"
-#include "modules/gdnative/gdnative.h"
-#include "modules/gdnative/include/net/godot_net.h"
+#include "core/config/engine.h"
+#include "core/io/resource.h"
-class PacketPeerGDNative : public PacketPeer {
- GDCLASS(PacketPeerGDNative, PacketPeer);
+class GLTFLight : public Resource {
+ GDCLASS(GLTFLight, Resource)
+ friend class GLTFDocument;
protected:
static void _bind_methods();
- const godot_net_packet_peer *interface;
+
+private:
+ 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 = Math_TAU / 8.0f;
public:
- PacketPeerGDNative();
- ~PacketPeerGDNative();
+ Color get_color();
+ void set_color(Color p_color);
+
+ float get_intensity();
+ void set_intensity(float p_intensity);
+
+ String get_light_type();
+ void set_light_type(String p_light_type);
+
+ float get_range();
+ void set_range(float p_range);
- /* Sets the interface implementation from GDNative */
- void set_native_packet_peer(const godot_net_packet_peer *p_impl);
+ float get_inner_cone_angle();
+ void set_inner_cone_angle(float p_inner_cone_angle);
- /* 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;
+ float get_outer_cone_angle();
+ void set_outer_cone_angle(float p_outer_cone_angle);
};
-#endif // PACKET_PEER_GDNATIVE_H
+#endif // GLTF_LIGHT_H
diff --git a/modules/gdnative/net/packet_peer_gdnative.cpp b/modules/gltf/gltf_mesh.cpp
index 6bb21cb48d..7134345b30 100644
--- a/modules/gdnative/net/packet_peer_gdnative.cpp
+++ b/modules/gltf/gltf_mesh.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* packet_peer_gdnative.cpp */
+/* gltf_mesh.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,45 +28,42 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "packet_peer_gdnative.h"
+#include "gltf_mesh.h"
+#include "scene/resources/importer_mesh.h"
-PacketPeerGDNative::PacketPeerGDNative() {
- interface = nullptr;
-}
-
-PacketPeerGDNative::~PacketPeerGDNative() {
-}
+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);
-void PacketPeerGDNative::set_native_packet_peer(const godot_net_packet_peer *p_impl) {
- interface = p_impl;
+ 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");
}
-void PacketPeerGDNative::_bind_methods() {
+Ref<ImporterMesh> GLTFMesh::get_mesh() {
+ return mesh;
}
-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 GLTFMesh::set_mesh(Ref<ImporterMesh> p_mesh) {
+ mesh = p_mesh;
}
-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);
+Array GLTFMesh::get_instance_materials() {
+ return instance_materials;
}
-int PacketPeerGDNative::get_max_packet_size() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_max_packet_size(interface->data);
+void GLTFMesh::set_instance_materials(Array p_instance_materials) {
+ instance_materials = p_instance_materials;
}
-int PacketPeerGDNative::get_available_packet_count() const {
- ERR_FAIL_COND_V(interface == nullptr, 0);
- return interface->get_available_packet_count(interface->data);
+Vector<float> GLTFMesh::get_blend_weights() {
+ return blend_weights;
}
-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);
-}
+void GLTFMesh::set_blend_weights(Vector<float> p_blend_weights) {
+ blend_weights = p_blend_weights;
}
diff --git a/modules/gltf/gltf_mesh.h b/modules/gltf/gltf_mesh.h
new file mode 100644
index 0000000000..cc2be93c09
--- /dev/null
+++ b/modules/gltf/gltf_mesh.h
@@ -0,0 +1,59 @@
+/*************************************************************************/
+/* gltf_mesh.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 GLTF_MESH_H
+#define GLTF_MESH_H
+
+#include "core/io/resource.h"
+#include "editor/import/resource_importer_scene.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<ImporterMesh> mesh;
+ Vector<float> blend_weights;
+ Array instance_materials;
+
+protected:
+ static void _bind_methods();
+
+public:
+ 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
new file mode 100644
index 0000000000..9f925c7bbc
--- /dev/null
+++ b/modules/gltf/gltf_node.cpp
@@ -0,0 +1,178 @@
+/*************************************************************************/
+/* gltf_node.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 "gltf_node.h"
+
+void GLTFNode::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_parent"), &GLTFNode::get_parent);
+ ClassDB::bind_method(D_METHOD("set_parent", "parent"), &GLTFNode::set_parent);
+ ClassDB::bind_method(D_METHOD("get_height"), &GLTFNode::get_height);
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &GLTFNode::set_height);
+ ClassDB::bind_method(D_METHOD("get_xform"), &GLTFNode::get_xform);
+ ClassDB::bind_method(D_METHOD("set_xform", "xform"), &GLTFNode::set_xform);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &GLTFNode::get_mesh);
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &GLTFNode::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_camera"), &GLTFNode::get_camera);
+ ClassDB::bind_method(D_METHOD("set_camera", "camera"), &GLTFNode::set_camera);
+ ClassDB::bind_method(D_METHOD("get_skin"), &GLTFNode::get_skin);
+ ClassDB::bind_method(D_METHOD("set_skin", "skin"), &GLTFNode::set_skin);
+ ClassDB::bind_method(D_METHOD("get_skeleton"), &GLTFNode::get_skeleton);
+ 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_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);
+ ClassDB::bind_method(D_METHOD("set_scale", "scale"), &GLTFNode::set_scale);
+ ClassDB::bind_method(D_METHOD("get_children"), &GLTFNode::get_children);
+ ClassDB::bind_method(D_METHOD("set_children", "children"), &GLTFNode::set_children);
+ ClassDB::bind_method(D_METHOD("get_light"), &GLTFNode::get_light);
+ ClassDB::bind_method(D_METHOD("set_light", "light"), &GLTFNode::set_light);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "parent"), "set_parent", "get_parent"); // GLTFNodeIndex
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "height"), "set_height", "get_height"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "xform"), "set_xform", "get_xform"); // Transform3D
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mesh"), "set_mesh", "get_mesh"); // GLTFMeshIndex
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "camera"), "set_camera", "get_camera"); // GLTFCameraIndex
+ 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, "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>
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "light"), "set_light", "get_light"); // GLTFLightIndex
+}
+
+GLTFNodeIndex GLTFNode::get_parent() {
+ return parent;
+}
+
+void GLTFNode::set_parent(GLTFNodeIndex p_parent) {
+ parent = p_parent;
+}
+
+int GLTFNode::get_height() {
+ return height;
+}
+
+void GLTFNode::set_height(int p_height) {
+ height = p_height;
+}
+
+Transform3D GLTFNode::get_xform() {
+ return xform;
+}
+
+void GLTFNode::set_xform(Transform3D p_xform) {
+ xform = p_xform;
+}
+
+GLTFMeshIndex GLTFNode::get_mesh() {
+ return mesh;
+}
+
+void GLTFNode::set_mesh(GLTFMeshIndex p_mesh) {
+ mesh = p_mesh;
+}
+
+GLTFCameraIndex GLTFNode::get_camera() {
+ return camera;
+}
+
+void GLTFNode::set_camera(GLTFCameraIndex p_camera) {
+ camera = p_camera;
+}
+
+GLTFSkinIndex GLTFNode::get_skin() {
+ return skin;
+}
+
+void GLTFNode::set_skin(GLTFSkinIndex p_skin) {
+ skin = p_skin;
+}
+
+GLTFSkeletonIndex GLTFNode::get_skeleton() {
+ return skeleton;
+}
+
+void GLTFNode::set_skeleton(GLTFSkeletonIndex p_skeleton) {
+ skeleton = p_skeleton;
+}
+
+bool GLTFNode::get_joint() {
+ return joint;
+}
+
+void GLTFNode::set_joint(bool p_joint) {
+ joint = p_joint;
+}
+
+Vector3 GLTFNode::get_position() {
+ return position;
+}
+
+void GLTFNode::set_position(Vector3 p_position) {
+ position = p_position;
+}
+
+Quaternion GLTFNode::get_rotation() {
+ return rotation;
+}
+
+void GLTFNode::set_rotation(Quaternion p_rotation) {
+ rotation = p_rotation;
+}
+
+Vector3 GLTFNode::get_scale() {
+ return scale;
+}
+
+void GLTFNode::set_scale(Vector3 p_scale) {
+ scale = p_scale;
+}
+
+Vector<int> GLTFNode::get_children() {
+ return children;
+}
+
+void GLTFNode::set_children(Vector<int> p_children) {
+ children = p_children;
+}
+
+GLTFLightIndex GLTFNode::get_light() {
+ return light;
+}
+
+void GLTFNode::set_light(GLTFLightIndex p_light) {
+ light = p_light;
+}
diff --git a/modules/gdnative/net/multiplayer_peer_gdnative.h b/modules/gltf/gltf_node.h
index 64d764029f..3b6e061449 100644
--- a/modules/gdnative/net/multiplayer_peer_gdnative.h
+++ b/modules/gltf/gltf_node.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* multiplayer_peer_gdnative.h */
+/* gltf_node.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,50 +28,73 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef MULTIPLAYER_PEER_GDNATIVE_H
-#define MULTIPLAYER_PEER_GDNATIVE_H
+#ifndef GLTF_NODE_H
+#define GLTF_NODE_H
-#include "core/io/networked_multiplayer_peer.h"
-#include "modules/gdnative/gdnative.h"
-#include "modules/gdnative/include/net/godot_net.h"
+#include "core/io/resource.h"
+#include "gltf_document.h"
-class MultiplayerPeerGDNative : public NetworkedMultiplayerPeer {
- GDCLASS(MultiplayerPeerGDNative, NetworkedMultiplayerPeer);
+class GLTFNode : public Resource {
+ GDCLASS(GLTFNode, Resource);
+ friend class GLTFDocument;
+
+private:
+ // matrices need to be transformed to this
+ GLTFNodeIndex parent = -1;
+ int height = -1;
+ Transform3D xform;
+ GLTFMeshIndex mesh = -1;
+ GLTFCameraIndex camera = -1;
+ GLTFSkinIndex skin = -1;
+ GLTFSkeletonIndex skeleton = -1;
+ bool joint = false;
+ Vector3 position;
+ Quaternion rotation;
+ Vector3 scale = Vector3(1, 1, 1);
+ Vector<int> children;
+ GLTFLightIndex light = -1;
protected:
static void _bind_methods();
- const godot_net_multiplayer_peer *interface;
public:
- MultiplayerPeerGDNative();
- ~MultiplayerPeerGDNative();
+ GLTFNodeIndex get_parent();
+ void set_parent(GLTFNodeIndex p_parent);
- /* Sets the interface implementation from GDNative */
- void set_native_multiplayer_peer(const godot_net_multiplayer_peer *p_impl);
+ int get_height();
+ void set_height(int p_height);
- /* 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;
+ Transform3D get_xform();
+ void set_xform(Transform3D p_xform);
- /* Specific to NetworkedMultiplayerPeer */
- 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;
+ GLTFMeshIndex get_mesh();
+ void set_mesh(GLTFMeshIndex p_mesh);
- virtual int get_packet_peer() const override;
+ GLTFCameraIndex get_camera();
+ void set_camera(GLTFCameraIndex p_camera);
- virtual bool is_server() const override;
+ GLTFSkinIndex get_skin();
+ void set_skin(GLTFSkinIndex p_skin);
- virtual void poll() override;
+ GLTFSkeletonIndex get_skeleton();
+ void set_skeleton(GLTFSkeletonIndex p_skeleton);
- virtual int get_unique_id() const override;
+ bool get_joint();
+ void set_joint(bool p_joint);
- virtual void set_refuse_new_connections(bool p_enable) override;
- virtual bool is_refusing_new_connections() const override;
+ Vector3 get_position();
+ void set_position(Vector3 p_position);
- virtual ConnectionStatus get_connection_status() const override;
-};
+ Quaternion get_rotation();
+ void set_rotation(Quaternion p_rotation);
+
+ Vector3 get_scale();
+ void set_scale(Vector3 p_scale);
-#endif // MULTIPLAYER_PEER_GDNATIVE_H
+ Vector<int> get_children();
+ void set_children(Vector<int> p_children);
+
+ GLTFLightIndex get_light();
+ void set_light(GLTFLightIndex p_light);
+};
+#endif // GLTF_NODE_H
diff --git a/modules/gltf/gltf_skeleton.cpp b/modules/gltf/gltf_skeleton.cpp
new file mode 100644
index 0000000000..d6c7a25eaf
--- /dev/null
+++ b/modules/gltf/gltf_skeleton.cpp
@@ -0,0 +1,95 @@
+/*************************************************************************/
+/* gltf_skeleton.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 "gltf_skeleton.h"
+
+void GLTFSkeleton::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_joints"), &GLTFSkeleton::get_joints);
+ ClassDB::bind_method(D_METHOD("set_joints", "joints"), &GLTFSkeleton::set_joints);
+ ClassDB::bind_method(D_METHOD("get_roots"), &GLTFSkeleton::get_roots);
+ ClassDB::bind_method(D_METHOD("set_roots", "roots"), &GLTFSkeleton::set_roots);
+ ClassDB::bind_method(D_METHOD("get_godot_skeleton"), &GLTFSkeleton::get_godot_skeleton);
+ ClassDB::bind_method(D_METHOD("get_unique_names"), &GLTFSkeleton::get_unique_names);
+ ClassDB::bind_method(D_METHOD("set_unique_names", "unique_names"), &GLTFSkeleton::set_unique_names);
+ ClassDB::bind_method(D_METHOD("get_godot_bone_node"), &GLTFSkeleton::get_godot_bone_node);
+ ClassDB::bind_method(D_METHOD("set_godot_bone_node", "godot_bone_node"), &GLTFSkeleton::set_godot_bone_node);
+ ClassDB::bind_method(D_METHOD("get_bone_attachment_count"), &GLTFSkeleton::get_bone_attachment_count);
+ ClassDB::bind_method(D_METHOD("get_bone_attachment", "idx"), &GLTFSkeleton::get_bone_attachment);
+
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "joints"), "set_joints", "get_joints"); // Vector<GLTFNodeIndex>
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "roots"), "set_roots", "get_roots"); // Vector<GLTFNodeIndex>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_names", "get_unique_names"); // Set<String>
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "godot_bone_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_godot_bone_node", "get_godot_bone_node"); // Map<int32_t,
+}
+
+Vector<GLTFNodeIndex> GLTFSkeleton::get_joints() {
+ return joints;
+}
+
+void GLTFSkeleton::set_joints(Vector<GLTFNodeIndex> p_joints) {
+ joints = p_joints;
+}
+
+Vector<GLTFNodeIndex> GLTFSkeleton::get_roots() {
+ return roots;
+}
+
+void GLTFSkeleton::set_roots(Vector<GLTFNodeIndex> p_roots) {
+ roots = p_roots;
+}
+
+Skeleton3D *GLTFSkeleton::get_godot_skeleton() {
+ return godot_skeleton;
+}
+
+Array GLTFSkeleton::get_unique_names() {
+ return GLTFDocument::to_array(unique_names);
+}
+
+void GLTFSkeleton::set_unique_names(Array p_unique_names) {
+ GLTFDocument::set_from_array(unique_names, p_unique_names);
+}
+
+Dictionary GLTFSkeleton::get_godot_bone_node() {
+ return GLTFDocument::to_dict(godot_bone_node);
+}
+
+void GLTFSkeleton::set_godot_bone_node(Dictionary p_indict) {
+ GLTFDocument::set_from_dict(godot_bone_node, p_indict);
+}
+
+BoneAttachment3D *GLTFSkeleton::get_bone_attachment(int idx) {
+ ERR_FAIL_INDEX_V(idx, bone_attachments.size(), nullptr);
+ return bone_attachments[idx];
+}
+
+int32_t GLTFSkeleton::get_bone_attachment_count() {
+ return bone_attachments.size();
+}
diff --git a/modules/gltf/gltf_skeleton.h b/modules/gltf/gltf_skeleton.h
new file mode 100644
index 0000000000..d6986eb35a
--- /dev/null
+++ b/modules/gltf/gltf_skeleton.h
@@ -0,0 +1,101 @@
+/*************************************************************************/
+/* gltf_skeleton.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 GLTF_SKELETON_H
+#define GLTF_SKELETON_H
+
+#include "core/io/resource.h"
+#include "gltf_document.h"
+
+class GLTFSkeleton : public Resource {
+ GDCLASS(GLTFSkeleton, Resource);
+ friend class GLTFDocument;
+
+private:
+ // The *synthesized* skeletons joints
+ Vector<GLTFNodeIndex> joints;
+
+ // The roots of the skeleton. If there are multiple, each root must have the
+ // same parent (ie roots are siblings)
+ Vector<GLTFNodeIndex> roots;
+
+ // The created Skeleton3D for the scene
+ Skeleton3D *godot_skeleton = nullptr;
+
+ // Set of unique bone names for the skeleton
+ Set<String> unique_names;
+
+ Map<int32_t, GLTFNodeIndex> godot_bone_node;
+
+ Vector<BoneAttachment3D *> bone_attachments;
+
+protected:
+ static void _bind_methods();
+
+public:
+ Vector<GLTFNodeIndex> get_joints();
+ void set_joints(Vector<GLTFNodeIndex> p_joints);
+
+ Vector<GLTFNodeIndex> get_roots();
+ void set_roots(Vector<GLTFNodeIndex> p_roots);
+
+ Skeleton3D *get_godot_skeleton();
+
+ // Skeleton *get_godot_skeleton() {
+ // return this->godot_skeleton;
+ // }
+ // void set_godot_skeleton(Skeleton p_*godot_skeleton) {
+ // this->godot_skeleton = p_godot_skeleton;
+ // }
+
+ Array get_unique_names();
+ void set_unique_names(Array p_unique_names);
+
+ //Map<int32_t, GLTFNodeIndex> get_godot_bone_node() {
+ // return this->godot_bone_node;
+ //}
+ //void set_godot_bone_node(Map<int32_t, GLTFNodeIndex> p_godot_bone_node) {
+ // this->godot_bone_node = p_godot_bone_node;
+ //}
+ Dictionary get_godot_bone_node();
+ void set_godot_bone_node(Dictionary p_indict);
+
+ //Dictionary get_godot_bone_node() {
+ // return VariantConversion::to_dict(this->godot_bone_node);
+ //}
+ //void set_godot_bone_node(Dictionary p_indict) {
+ // VariantConversion::set_from_dict(this->godot_bone_node, p_indict);
+ //}
+
+ BoneAttachment3D *get_bone_attachment(int idx);
+
+ int32_t get_bone_attachment_count();
+};
+#endif // GLTF_SKELETON_H
diff --git a/modules/gltf/gltf_skin.cpp b/modules/gltf/gltf_skin.cpp
new file mode 100644
index 0000000000..5cf17135ac
--- /dev/null
+++ b/modules/gltf/gltf_skin.cpp
@@ -0,0 +1,155 @@
+/*************************************************************************/
+/* gltf_skin.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 "gltf_skin.h"
+
+void GLTFSkin::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_skin_root"), &GLTFSkin::get_skin_root);
+ ClassDB::bind_method(D_METHOD("set_skin_root", "skin_root"), &GLTFSkin::set_skin_root);
+ ClassDB::bind_method(D_METHOD("get_joints_original"), &GLTFSkin::get_joints_original);
+ ClassDB::bind_method(D_METHOD("set_joints_original", "joints_original"), &GLTFSkin::set_joints_original);
+ ClassDB::bind_method(D_METHOD("get_inverse_binds"), &GLTFSkin::get_inverse_binds);
+ ClassDB::bind_method(D_METHOD("set_inverse_binds", "inverse_binds"), &GLTFSkin::set_inverse_binds);
+ ClassDB::bind_method(D_METHOD("get_joints"), &GLTFSkin::get_joints);
+ ClassDB::bind_method(D_METHOD("set_joints", "joints"), &GLTFSkin::set_joints);
+ ClassDB::bind_method(D_METHOD("get_non_joints"), &GLTFSkin::get_non_joints);
+ ClassDB::bind_method(D_METHOD("set_non_joints", "non_joints"), &GLTFSkin::set_non_joints);
+ ClassDB::bind_method(D_METHOD("get_roots"), &GLTFSkin::get_roots);
+ ClassDB::bind_method(D_METHOD("set_roots", "roots"), &GLTFSkin::set_roots);
+ ClassDB::bind_method(D_METHOD("get_skeleton"), &GLTFSkin::get_skeleton);
+ ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &GLTFSkin::set_skeleton);
+ ClassDB::bind_method(D_METHOD("get_joint_i_to_bone_i"), &GLTFSkin::get_joint_i_to_bone_i);
+ ClassDB::bind_method(D_METHOD("set_joint_i_to_bone_i", "joint_i_to_bone_i"), &GLTFSkin::set_joint_i_to_bone_i);
+ ClassDB::bind_method(D_METHOD("get_joint_i_to_name"), &GLTFSkin::get_joint_i_to_name);
+ ClassDB::bind_method(D_METHOD("set_joint_i_to_name", "joint_i_to_name"), &GLTFSkin::set_joint_i_to_name);
+ ClassDB::bind_method(D_METHOD("get_godot_skin"), &GLTFSkin::get_godot_skin);
+ ClassDB::bind_method(D_METHOD("set_godot_skin", "godot_skin"), &GLTFSkin::set_godot_skin);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "skin_root"), "set_skin_root", "get_skin_root"); // GLTFNodeIndex
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "joints_original"), "set_joints_original", "get_joints_original"); // Vector<GLTFNodeIndex>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "inverse_binds", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_inverse_binds", "get_inverse_binds"); // Vector<Transform3D>
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "joints"), "set_joints", "get_joints"); // Vector<GLTFNodeIndex>
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "non_joints"), "set_non_joints", "get_non_joints"); // Vector<GLTFNodeIndex>
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "roots"), "set_roots", "get_roots"); // Vector<GLTFNodeIndex>
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton"), "set_skeleton", "get_skeleton"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_bone_i", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_bone_i", "get_joint_i_to_bone_i"); // Map<int,
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "joint_i_to_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL), "set_joint_i_to_name", "get_joint_i_to_name"); // Map<int,
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "godot_skin"), "set_godot_skin", "get_godot_skin"); // Ref<Skin>
+}
+
+GLTFNodeIndex GLTFSkin::get_skin_root() {
+ return skin_root;
+}
+
+void GLTFSkin::set_skin_root(GLTFNodeIndex p_skin_root) {
+ skin_root = p_skin_root;
+}
+
+Vector<GLTFNodeIndex> GLTFSkin::get_joints_original() {
+ return joints_original;
+}
+
+void GLTFSkin::set_joints_original(Vector<GLTFNodeIndex> p_joints_original) {
+ joints_original = p_joints_original;
+}
+
+Array GLTFSkin::get_inverse_binds() {
+ return GLTFDocument::to_array(inverse_binds);
+}
+
+void GLTFSkin::set_inverse_binds(Array p_inverse_binds) {
+ GLTFDocument::set_from_array(inverse_binds, p_inverse_binds);
+}
+
+Vector<GLTFNodeIndex> GLTFSkin::get_joints() {
+ return joints;
+}
+
+void GLTFSkin::set_joints(Vector<GLTFNodeIndex> p_joints) {
+ joints = p_joints;
+}
+
+Vector<GLTFNodeIndex> GLTFSkin::get_non_joints() {
+ return non_joints;
+}
+
+void GLTFSkin::set_non_joints(Vector<GLTFNodeIndex> p_non_joints) {
+ non_joints = p_non_joints;
+}
+
+Vector<GLTFNodeIndex> GLTFSkin::get_roots() {
+ return roots;
+}
+
+void GLTFSkin::set_roots(Vector<GLTFNodeIndex> p_roots) {
+ roots = p_roots;
+}
+
+int GLTFSkin::get_skeleton() {
+ return skeleton;
+}
+
+void GLTFSkin::set_skeleton(int p_skeleton) {
+ skeleton = p_skeleton;
+}
+
+Dictionary GLTFSkin::get_joint_i_to_bone_i() {
+ return GLTFDocument::to_dict(joint_i_to_bone_i);
+}
+
+void GLTFSkin::set_joint_i_to_bone_i(Dictionary p_joint_i_to_bone_i) {
+ GLTFDocument::set_from_dict(joint_i_to_bone_i, p_joint_i_to_bone_i);
+}
+
+Dictionary GLTFSkin::get_joint_i_to_name() {
+ Dictionary ret;
+ Map<int, StringName>::Element *elem = joint_i_to_name.front();
+ while (elem) {
+ ret[elem->key()] = String(elem->value());
+ elem = elem->next();
+ }
+ return ret;
+}
+
+void GLTFSkin::set_joint_i_to_name(Dictionary p_joint_i_to_name) {
+ joint_i_to_name = Map<int, StringName>();
+ Array keys = p_joint_i_to_name.keys();
+ for (int i = 0; i < keys.size(); i++) {
+ joint_i_to_name[keys[i]] = p_joint_i_to_name[keys[i]];
+ }
+}
+
+Ref<Skin> GLTFSkin::get_godot_skin() {
+ return godot_skin;
+}
+
+void GLTFSkin::set_godot_skin(Ref<Skin> p_godot_skin) {
+ godot_skin = p_godot_skin;
+}
diff --git a/modules/gltf/gltf_skin.h b/modules/gltf/gltf_skin.h
new file mode 100644
index 0000000000..e32e2d397c
--- /dev/null
+++ b/modules/gltf/gltf_skin.h
@@ -0,0 +1,109 @@
+/*************************************************************************/
+/* gltf_skin.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 GLTF_SKIN_H
+#define GLTF_SKIN_H
+
+#include "core/io/resource.h"
+#include "gltf_document.h"
+
+class GLTFSkin : public Resource {
+ GDCLASS(GLTFSkin, Resource);
+ friend class GLTFDocument;
+
+private:
+ // The "skeleton" property defined in the gltf spec. -1 = Scene Root
+ GLTFNodeIndex skin_root = -1;
+
+ Vector<GLTFNodeIndex> joints_original;
+ Vector<Transform3D> inverse_binds;
+
+ // Note: joints + non_joints should form a complete subtree, or subtrees
+ // with a common parent
+
+ // All nodes that are skins that are caught in-between the original joints
+ // (inclusive of joints_original)
+ Vector<GLTFNodeIndex> joints;
+
+ // All Nodes that are caught in-between skin joint nodes, and are not
+ // defined as joints by any skin
+ Vector<GLTFNodeIndex> non_joints;
+
+ // The roots of the skin. In the case of multiple roots, their parent *must*
+ // be the same (the roots must be siblings)
+ Vector<GLTFNodeIndex> roots;
+
+ // The GLTF Skeleton this Skin points to (after we determine skeletons)
+ GLTFSkeletonIndex skeleton = -1;
+
+ // A mapping from the joint indices (in the order of joints_original) to the
+ // Godot Skeleton's bone_indices
+ Map<int, int> joint_i_to_bone_i;
+ Map<int, StringName> joint_i_to_name;
+
+ // The Actual Skin that will be created as a mapping between the IBM's of
+ // this skin to the generated skeleton for the mesh instances.
+ Ref<Skin> godot_skin;
+
+protected:
+ static void _bind_methods();
+
+public:
+ GLTFNodeIndex get_skin_root();
+ void set_skin_root(GLTFNodeIndex p_skin_root);
+
+ Vector<GLTFNodeIndex> get_joints_original();
+ void set_joints_original(Vector<GLTFNodeIndex> p_joints_original);
+
+ Array get_inverse_binds();
+ void set_inverse_binds(Array p_inverse_binds);
+
+ Vector<GLTFNodeIndex> get_joints();
+ void set_joints(Vector<GLTFNodeIndex> p_joints);
+
+ Vector<GLTFNodeIndex> get_non_joints();
+ void set_non_joints(Vector<GLTFNodeIndex> p_non_joints);
+
+ Vector<GLTFNodeIndex> get_roots();
+ void set_roots(Vector<GLTFNodeIndex> p_roots);
+
+ int get_skeleton();
+ void set_skeleton(int p_skeleton);
+
+ Dictionary get_joint_i_to_bone_i();
+ void set_joint_i_to_bone_i(Dictionary p_joint_i_to_bone_i);
+
+ Dictionary get_joint_i_to_name();
+ void set_joint_i_to_name(Dictionary p_joint_i_to_name);
+
+ Ref<Skin> get_godot_skin();
+ void set_godot_skin(Ref<Skin> p_godot_skin);
+};
+#endif // GLTF_SKIN_H
diff --git a/modules/gltf/gltf_spec_gloss.cpp b/modules/gltf/gltf_spec_gloss.cpp
new file mode 100644
index 0000000000..70b182da52
--- /dev/null
+++ b/modules/gltf/gltf_spec_gloss.cpp
@@ -0,0 +1,90 @@
+/*************************************************************************/
+/* gltf_spec_gloss.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 "gltf_spec_gloss.h"
+
+void GLTFSpecGloss::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_diffuse_img"), &GLTFSpecGloss::get_diffuse_img);
+ ClassDB::bind_method(D_METHOD("set_diffuse_img", "diffuse_img"), &GLTFSpecGloss::set_diffuse_img);
+ ClassDB::bind_method(D_METHOD("get_diffuse_factor"), &GLTFSpecGloss::get_diffuse_factor);
+ ClassDB::bind_method(D_METHOD("set_diffuse_factor", "diffuse_factor"), &GLTFSpecGloss::set_diffuse_factor);
+ ClassDB::bind_method(D_METHOD("get_gloss_factor"), &GLTFSpecGloss::get_gloss_factor);
+ ClassDB::bind_method(D_METHOD("set_gloss_factor", "gloss_factor"), &GLTFSpecGloss::set_gloss_factor);
+ ClassDB::bind_method(D_METHOD("get_specular_factor"), &GLTFSpecGloss::get_specular_factor);
+ ClassDB::bind_method(D_METHOD("set_specular_factor", "specular_factor"), &GLTFSpecGloss::set_specular_factor);
+ ClassDB::bind_method(D_METHOD("get_spec_gloss_img"), &GLTFSpecGloss::get_spec_gloss_img);
+ ClassDB::bind_method(D_METHOD("set_spec_gloss_img", "spec_gloss_img"), &GLTFSpecGloss::set_spec_gloss_img);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "diffuse_img"), "set_diffuse_img", "get_diffuse_img"); // Ref<Image>
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "diffuse_factor"), "set_diffuse_factor", "get_diffuse_factor"); // Color
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gloss_factor"), "set_gloss_factor", "get_gloss_factor"); // float
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "specular_factor"), "set_specular_factor", "get_specular_factor"); // Color
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "spec_gloss_img"), "set_spec_gloss_img", "get_spec_gloss_img"); // Ref<Image>
+}
+
+Ref<Image> GLTFSpecGloss::get_diffuse_img() {
+ return diffuse_img;
+}
+
+void GLTFSpecGloss::set_diffuse_img(Ref<Image> p_diffuse_img) {
+ diffuse_img = p_diffuse_img;
+}
+
+Color GLTFSpecGloss::get_diffuse_factor() {
+ return diffuse_factor;
+}
+
+void GLTFSpecGloss::set_diffuse_factor(Color p_diffuse_factor) {
+ diffuse_factor = p_diffuse_factor;
+}
+
+float GLTFSpecGloss::get_gloss_factor() {
+ return gloss_factor;
+}
+
+void GLTFSpecGloss::set_gloss_factor(float p_gloss_factor) {
+ gloss_factor = p_gloss_factor;
+}
+
+Color GLTFSpecGloss::get_specular_factor() {
+ return specular_factor;
+}
+
+void GLTFSpecGloss::set_specular_factor(Color p_specular_factor) {
+ specular_factor = p_specular_factor;
+}
+
+Ref<Image> GLTFSpecGloss::get_spec_gloss_img() {
+ return spec_gloss_img;
+}
+
+void GLTFSpecGloss::set_spec_gloss_img(Ref<Image> p_spec_gloss_img) {
+ spec_gloss_img = p_spec_gloss_img;
+}
diff --git a/modules/gdnative/net/stream_peer_gdnative.h b/modules/gltf/gltf_spec_gloss.h
index 302fb48012..3cc6fb09ed 100644
--- a/modules/gdnative/net/stream_peer_gdnative.h
+++ b/modules/gltf/gltf_spec_gloss.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* stream_peer_gdnative.h */
+/* gltf_spec_gloss.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,33 +28,40 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef STREAM_PEER_GDNATIVE_H
-#define STREAM_PEER_GDNATIVE_H
+#ifndef GLTF_SPEC_GLOSS_H
+#define GLTF_SPEC_GLOSS_H
-#include "core/io/stream_peer.h"
-#include "modules/gdnative/gdnative.h"
-#include "modules/gdnative/include/net/godot_net.h"
+#include "core/io/image.h"
+#include "core/io/resource.h"
-class StreamPeerGDNative : public StreamPeer {
- GDCLASS(StreamPeerGDNative, StreamPeer);
+class GLTFSpecGloss : public Resource {
+ GDCLASS(GLTFSpecGloss, Resource);
+ friend class GLTFDocument;
+
+private:
+ Ref<Image> diffuse_img = nullptr;
+ Color diffuse_factor = Color(1.0f, 1.0f, 1.0f);
+ float gloss_factor = 1.0f;
+ Color specular_factor = Color(1.0f, 1.0f, 1.0f);
+ Ref<Image> spec_gloss_img = nullptr;
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;
-};
+ Ref<Image> get_diffuse_img();
+ void set_diffuse_img(Ref<Image> p_diffuse_img);
+
+ Color get_diffuse_factor();
+ void set_diffuse_factor(Color p_diffuse_factor);
-#endif // STREAM_PEER_GDNATIVE_H
+ float get_gloss_factor();
+ void set_gloss_factor(float p_gloss_factor);
+
+ Color get_specular_factor();
+ void set_specular_factor(Color p_specular_factor);
+
+ Ref<Image> get_spec_gloss_img();
+ void set_spec_gloss_img(Ref<Image> p_spec_gloss_img);
+};
+#endif // GLTF_SPEC_GLOSS_H
diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp
new file mode 100644
index 0000000000..ff9778e7d8
--- /dev/null
+++ b/modules/gltf/gltf_state.cpp
@@ -0,0 +1,307 @@
+/*************************************************************************/
+/* gltf_state.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 "gltf_state.h"
+
+void GLTFState::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_json"), &GLTFState::get_json);
+ ClassDB::bind_method(D_METHOD("set_json", "json"), &GLTFState::set_json);
+ ClassDB::bind_method(D_METHOD("get_major_version"), &GLTFState::get_major_version);
+ ClassDB::bind_method(D_METHOD("set_major_version", "major_version"), &GLTFState::set_major_version);
+ ClassDB::bind_method(D_METHOD("get_minor_version"), &GLTFState::get_minor_version);
+ ClassDB::bind_method(D_METHOD("set_minor_version", "minor_version"), &GLTFState::set_minor_version);
+ ClassDB::bind_method(D_METHOD("get_glb_data"), &GLTFState::get_glb_data);
+ ClassDB::bind_method(D_METHOD("set_glb_data", "glb_data"), &GLTFState::set_glb_data);
+ ClassDB::bind_method(D_METHOD("get_use_named_skin_binds"), &GLTFState::get_use_named_skin_binds);
+ ClassDB::bind_method(D_METHOD("set_use_named_skin_binds", "use_named_skin_binds"), &GLTFState::set_use_named_skin_binds);
+ ClassDB::bind_method(D_METHOD("get_nodes"), &GLTFState::get_nodes);
+ ClassDB::bind_method(D_METHOD("set_nodes", "nodes"), &GLTFState::set_nodes);
+ ClassDB::bind_method(D_METHOD("get_buffers"), &GLTFState::get_buffers);
+ ClassDB::bind_method(D_METHOD("set_buffers", "buffers"), &GLTFState::set_buffers);
+ ClassDB::bind_method(D_METHOD("get_buffer_views"), &GLTFState::get_buffer_views);
+ ClassDB::bind_method(D_METHOD("set_buffer_views", "buffer_views"), &GLTFState::set_buffer_views);
+ ClassDB::bind_method(D_METHOD("get_accessors"), &GLTFState::get_accessors);
+ ClassDB::bind_method(D_METHOD("set_accessors", "accessors"), &GLTFState::set_accessors);
+ ClassDB::bind_method(D_METHOD("get_meshes"), &GLTFState::get_meshes);
+ ClassDB::bind_method(D_METHOD("set_meshes", "meshes"), &GLTFState::set_meshes);
+ ClassDB::bind_method(D_METHOD("get_animation_players_count", "idx"), &GLTFState::get_animation_players_count);
+ ClassDB::bind_method(D_METHOD("get_animation_player", "idx"), &GLTFState::get_animation_player);
+ ClassDB::bind_method(D_METHOD("get_materials"), &GLTFState::get_materials);
+ ClassDB::bind_method(D_METHOD("set_materials", "materials"), &GLTFState::set_materials);
+ ClassDB::bind_method(D_METHOD("get_scene_name"), &GLTFState::get_scene_name);
+ ClassDB::bind_method(D_METHOD("set_scene_name", "scene_name"), &GLTFState::set_scene_name);
+ ClassDB::bind_method(D_METHOD("get_root_nodes"), &GLTFState::get_root_nodes);
+ ClassDB::bind_method(D_METHOD("set_root_nodes", "root_nodes"), &GLTFState::set_root_nodes);
+ ClassDB::bind_method(D_METHOD("get_textures"), &GLTFState::get_textures);
+ ClassDB::bind_method(D_METHOD("set_textures", "textures"), &GLTFState::set_textures);
+ ClassDB::bind_method(D_METHOD("get_images"), &GLTFState::get_images);
+ ClassDB::bind_method(D_METHOD("set_images", "images"), &GLTFState::set_images);
+ ClassDB::bind_method(D_METHOD("get_skins"), &GLTFState::get_skins);
+ ClassDB::bind_method(D_METHOD("set_skins", "skins"), &GLTFState::set_skins);
+ ClassDB::bind_method(D_METHOD("get_cameras"), &GLTFState::get_cameras);
+ ClassDB::bind_method(D_METHOD("set_cameras", "cameras"), &GLTFState::set_cameras);
+ ClassDB::bind_method(D_METHOD("get_lights"), &GLTFState::get_lights);
+ ClassDB::bind_method(D_METHOD("set_lights", "lights"), &GLTFState::set_lights);
+ ClassDB::bind_method(D_METHOD("get_unique_names"), &GLTFState::get_unique_names);
+ ClassDB::bind_method(D_METHOD("set_unique_names", "unique_names"), &GLTFState::set_unique_names);
+ ClassDB::bind_method(D_METHOD("get_unique_animation_names"), &GLTFState::get_unique_animation_names);
+ ClassDB::bind_method(D_METHOD("set_unique_animation_names", "unique_animation_names"), &GLTFState::set_unique_animation_names);
+ ClassDB::bind_method(D_METHOD("get_skeletons"), &GLTFState::get_skeletons);
+ ClassDB::bind_method(D_METHOD("set_skeletons", "skeletons"), &GLTFState::set_skeletons);
+ ClassDB::bind_method(D_METHOD("get_skeleton_to_node"), &GLTFState::get_skeleton_to_node);
+ ClassDB::bind_method(D_METHOD("set_skeleton_to_node", "skeleton_to_node"), &GLTFState::set_skeleton_to_node);
+ ClassDB::bind_method(D_METHOD("get_animations"), &GLTFState::get_animations);
+ ClassDB::bind_method(D_METHOD("set_animations", "animations"), &GLTFState::set_animations);
+ ClassDB::bind_method(D_METHOD("get_scene_node", "idx"), &GLTFState::get_scene_node);
+
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "json"), "set_json", "get_json"); // Dictionary
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "major_version"), "set_major_version", "get_major_version"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "minor_version"), "set_minor_version", "get_minor_version"); // int
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "glb_data"), "set_glb_data", "get_glb_data"); // Vector<uint8_t>
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_named_skin_binds"), "set_use_named_skin_binds", "get_use_named_skin_binds"); // bool
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_nodes", "get_nodes"); // Vector<Ref<GLTFNode>>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "buffers"), "set_buffers", "get_buffers"); // Vector<Vector<uint8_t>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "buffer_views", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_buffer_views", "get_buffer_views"); // Vector<Ref<GLTFBufferView>>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "accessors", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_accessors", "get_accessors"); // Vector<Ref<GLTFAccessor>>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "meshes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_meshes", "get_meshes"); // Vector<Ref<GLTFMesh>>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "materials", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_materials", "get_materials"); // Vector<Ref<Material>
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "scene_name"), "set_scene_name", "get_scene_name"); // String
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "root_nodes"), "set_root_nodes", "get_root_nodes"); // Vector<int>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_textures", "get_textures"); // Vector<Ref<GLTFTexture>>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "images", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_images", "get_images"); // Vector<Ref<Texture>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skins", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skins", "get_skins"); // Vector<Ref<GLTFSkin>>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "cameras", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_cameras", "get_cameras"); // Vector<Ref<GLTFCamera>>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "lights", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_lights", "get_lights"); // Vector<Ref<GLTFLight>>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_names", "get_unique_names"); // Set<String>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "unique_animation_names", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_unique_animation_names", "get_unique_animation_names"); // Set<String>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "skeletons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeletons", "get_skeletons"); // Vector<Ref<GLTFSkeleton>>
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "skeleton_to_node", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_skeleton_to_node", "get_skeleton_to_node"); // Map<GLTFSkeletonIndex,
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>>
+}
+
+Dictionary GLTFState::get_json() {
+ return json;
+}
+
+void GLTFState::set_json(Dictionary p_json) {
+ json = p_json;
+}
+
+int GLTFState::get_major_version() {
+ return major_version;
+}
+
+void GLTFState::set_major_version(int p_major_version) {
+ major_version = p_major_version;
+}
+
+int GLTFState::get_minor_version() {
+ return minor_version;
+}
+
+void GLTFState::set_minor_version(int p_minor_version) {
+ minor_version = p_minor_version;
+}
+
+Vector<uint8_t> GLTFState::get_glb_data() {
+ return glb_data;
+}
+
+void GLTFState::set_glb_data(Vector<uint8_t> p_glb_data) {
+ glb_data = p_glb_data;
+}
+
+bool GLTFState::get_use_named_skin_binds() {
+ return use_named_skin_binds;
+}
+
+void GLTFState::set_use_named_skin_binds(bool p_use_named_skin_binds) {
+ use_named_skin_binds = p_use_named_skin_binds;
+}
+
+Array GLTFState::get_nodes() {
+ return GLTFDocument::to_array(nodes);
+}
+
+void GLTFState::set_nodes(Array p_nodes) {
+ GLTFDocument::set_from_array(nodes, p_nodes);
+}
+
+Array GLTFState::get_buffers() {
+ return GLTFDocument::to_array(buffers);
+}
+
+void GLTFState::set_buffers(Array p_buffers) {
+ GLTFDocument::set_from_array(buffers, p_buffers);
+}
+
+Array GLTFState::get_buffer_views() {
+ return GLTFDocument::to_array(buffer_views);
+}
+
+void GLTFState::set_buffer_views(Array p_buffer_views) {
+ GLTFDocument::set_from_array(buffer_views, p_buffer_views);
+}
+
+Array GLTFState::get_accessors() {
+ return GLTFDocument::to_array(accessors);
+}
+
+void GLTFState::set_accessors(Array p_accessors) {
+ GLTFDocument::set_from_array(accessors, p_accessors);
+}
+
+Array GLTFState::get_meshes() {
+ return GLTFDocument::to_array(meshes);
+}
+
+void GLTFState::set_meshes(Array p_meshes) {
+ GLTFDocument::set_from_array(meshes, p_meshes);
+}
+
+Array GLTFState::get_materials() {
+ return GLTFDocument::to_array(materials);
+}
+
+void GLTFState::set_materials(Array p_materials) {
+ GLTFDocument::set_from_array(materials, p_materials);
+}
+
+String GLTFState::get_scene_name() {
+ return scene_name;
+}
+
+void GLTFState::set_scene_name(String p_scene_name) {
+ scene_name = p_scene_name;
+}
+
+Array GLTFState::get_root_nodes() {
+ return GLTFDocument::to_array(root_nodes);
+}
+
+void GLTFState::set_root_nodes(Array p_root_nodes) {
+ GLTFDocument::set_from_array(root_nodes, p_root_nodes);
+}
+
+Array GLTFState::get_textures() {
+ return GLTFDocument::to_array(textures);
+}
+
+void GLTFState::set_textures(Array p_textures) {
+ GLTFDocument::set_from_array(textures, p_textures);
+}
+
+Array GLTFState::get_images() {
+ return GLTFDocument::to_array(images);
+}
+
+void GLTFState::set_images(Array p_images) {
+ GLTFDocument::set_from_array(images, p_images);
+}
+
+Array GLTFState::get_skins() {
+ return GLTFDocument::to_array(skins);
+}
+
+void GLTFState::set_skins(Array p_skins) {
+ GLTFDocument::set_from_array(skins, p_skins);
+}
+
+Array GLTFState::get_cameras() {
+ return GLTFDocument::to_array(cameras);
+}
+
+void GLTFState::set_cameras(Array p_cameras) {
+ GLTFDocument::set_from_array(cameras, p_cameras);
+}
+
+Array GLTFState::get_lights() {
+ return GLTFDocument::to_array(lights);
+}
+
+void GLTFState::set_lights(Array p_lights) {
+ GLTFDocument::set_from_array(lights, p_lights);
+}
+
+Array GLTFState::get_unique_names() {
+ return GLTFDocument::to_array(unique_names);
+}
+
+void GLTFState::set_unique_names(Array p_unique_names) {
+ GLTFDocument::set_from_array(unique_names, p_unique_names);
+}
+
+Array GLTFState::get_unique_animation_names() {
+ return GLTFDocument::to_array(unique_animation_names);
+}
+
+void GLTFState::set_unique_animation_names(Array p_unique_animation_names) {
+ GLTFDocument::set_from_array(unique_animation_names, p_unique_animation_names);
+}
+
+Array GLTFState::get_skeletons() {
+ return GLTFDocument::to_array(skeletons);
+}
+
+void GLTFState::set_skeletons(Array p_skeletons) {
+ GLTFDocument::set_from_array(skeletons, p_skeletons);
+}
+
+Dictionary GLTFState::get_skeleton_to_node() {
+ return GLTFDocument::to_dict(skeleton_to_node);
+}
+
+void GLTFState::set_skeleton_to_node(Dictionary p_skeleton_to_node) {
+ GLTFDocument::set_from_dict(skeleton_to_node, p_skeleton_to_node);
+}
+
+Array GLTFState::get_animations() {
+ return GLTFDocument::to_array(animations);
+}
+
+void GLTFState::set_animations(Array p_animations) {
+ GLTFDocument::set_from_array(animations, p_animations);
+}
+
+Node *GLTFState::get_scene_node(GLTFNodeIndex idx) {
+ if (!scene_nodes.has(idx)) {
+ return nullptr;
+ }
+ return scene_nodes[idx];
+}
+
+int GLTFState::get_animation_players_count(int idx) {
+ return animation_players.size();
+}
+
+AnimationPlayer *GLTFState::get_animation_player(int idx) {
+ ERR_FAIL_INDEX_V(idx, animation_players.size(), nullptr);
+ return animation_players[idx];
+}
diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h
new file mode 100644
index 0000000000..61faba0dc5
--- /dev/null
+++ b/modules/gltf/gltf_state.h
@@ -0,0 +1,190 @@
+/*************************************************************************/
+/* gltf_state.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 GLTF_STATE_H
+#define GLTF_STATE_H
+
+#include "gltf_accessor.h"
+#include "gltf_animation.h"
+#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"
+#include "gltf_skeleton.h"
+#include "gltf_skin.h"
+#include "gltf_texture.h"
+
+#include "core/io/resource.h"
+#include "core/templates/map.h"
+#include "core/templates/pair.h"
+#include "core/templates/vector.h"
+#include "scene/animation/animation_player.h"
+#include "scene/resources/texture.h"
+
+class GLTFState : public Resource {
+ GDCLASS(GLTFState, Resource);
+ friend class GLTFDocument;
+
+ String filename;
+ Dictionary json;
+ int major_version = 0;
+ int minor_version = 0;
+ Vector<uint8_t> glb_data;
+
+ bool use_named_skin_binds = false;
+
+ Vector<Ref<GLTFNode>> nodes;
+ Vector<Vector<uint8_t>> buffers;
+ Vector<Ref<GLTFBufferView>> buffer_views;
+ Vector<Ref<GLTFAccessor>> accessors;
+
+ Vector<Ref<GLTFMesh>> meshes; // meshes are loaded directly, no reason not to.
+
+ Vector<AnimationPlayer *> animation_players;
+ Map<Ref<BaseMaterial3D>, GLTFMaterialIndex> material_cache;
+ Vector<Ref<BaseMaterial3D>> materials;
+
+ String scene_name;
+ Vector<int> root_nodes;
+ Vector<Ref<GLTFTexture>> textures;
+ Vector<Ref<Texture2D>> images;
+
+ Vector<Ref<GLTFSkin>> skins;
+ Vector<Ref<GLTFCamera>> cameras;
+ Vector<Ref<GLTFLight>> lights;
+ Set<String> unique_names;
+ Set<String> unique_animation_names;
+
+ Vector<Ref<GLTFSkeleton>> skeletons;
+ Map<GLTFSkeletonIndex, GLTFNodeIndex> skeleton_to_node;
+ 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();
+
+public:
+ Dictionary get_json();
+ void set_json(Dictionary p_json);
+
+ int get_major_version();
+ void set_major_version(int p_major_version);
+
+ int get_minor_version();
+ void set_minor_version(int p_minor_version);
+
+ Vector<uint8_t> get_glb_data();
+ void set_glb_data(Vector<uint8_t> p_glb_data);
+
+ bool get_use_named_skin_binds();
+ void set_use_named_skin_binds(bool p_use_named_skin_binds);
+
+ Array get_nodes();
+ void set_nodes(Array p_nodes);
+
+ Array get_buffers();
+ void set_buffers(Array p_buffers);
+
+ Array get_buffer_views();
+ void set_buffer_views(Array p_buffer_views);
+
+ Array get_accessors();
+ void set_accessors(Array p_accessors);
+
+ Array get_meshes();
+ void set_meshes(Array p_meshes);
+
+ Array get_materials();
+ void set_materials(Array p_materials);
+
+ String get_scene_name();
+ void set_scene_name(String p_scene_name);
+
+ Array get_root_nodes();
+ void set_root_nodes(Array p_root_nodes);
+
+ Array get_textures();
+ void set_textures(Array p_textures);
+
+ Array get_images();
+ void set_images(Array p_images);
+
+ Array get_skins();
+ void set_skins(Array p_skins);
+
+ Array get_cameras();
+ void set_cameras(Array p_cameras);
+
+ Array get_lights();
+ void set_lights(Array p_lights);
+
+ Array get_unique_names();
+ void set_unique_names(Array p_unique_names);
+
+ Array get_unique_animation_names();
+ void set_unique_animation_names(Array p_unique_names);
+
+ Array get_skeletons();
+ void set_skeletons(Array p_skeletons);
+
+ Dictionary get_skeleton_to_node();
+ void set_skeleton_to_node(Dictionary p_skeleton_to_node);
+
+ Array get_animations();
+ void set_animations(Array p_animations);
+
+ Node *get_scene_node(GLTFNodeIndex idx);
+
+ int get_animation_players_count(int idx);
+
+ AnimationPlayer *get_animation_player(int idx);
+
+ //void set_scene_nodes(Map<GLTFNodeIndex, Node *> p_scene_nodes) {
+ // this->scene_nodes = p_scene_nodes;
+ //}
+
+ //void set_animation_players(Vector<AnimationPlayer *> p_animation_players) {
+ // this->animation_players = p_animation_players;
+ //}
+
+ //Map<Ref<Material>, GLTFMaterialIndex> get_material_cache() {
+ // return this->material_cache;
+ //}
+ //void set_material_cache(Map<Ref<Material>, GLTFMaterialIndex> p_material_cache) {
+ // this->material_cache = p_material_cache;
+ //}
+};
+#endif // GLTF_STATE_H
diff --git a/modules/gltf/gltf_texture.cpp b/modules/gltf/gltf_texture.cpp
new file mode 100644
index 0000000000..0482c1064e
--- /dev/null
+++ b/modules/gltf/gltf_texture.cpp
@@ -0,0 +1,46 @@
+/*************************************************************************/
+/* gltf_texture.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 "gltf_texture.h"
+
+void GLTFTexture::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_src_image"), &GLTFTexture::get_src_image);
+ ClassDB::bind_method(D_METHOD("set_src_image", "src_image"), &GLTFTexture::set_src_image);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "src_image"), "set_src_image", "get_src_image"); // int
+}
+
+GLTFImageIndex GLTFTexture::get_src_image() const {
+ return src_image;
+}
+
+void GLTFTexture::set_src_image(GLTFImageIndex val) {
+ src_image = val;
+}
diff --git a/modules/camera/camera_ios.h b/modules/gltf/gltf_texture.h
index 7da43e4851..4659725502 100644
--- a/modules/camera/camera_ios.h
+++ b/modules/gltf/gltf_texture.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* camera_ios.h */
+/* gltf_texture.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,18 +28,24 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef CAMERAIOS_H
-#define CAMERAIOS_H
+#ifndef GLTF_TEXTURE_H
+#define GLTF_TEXTURE_H
-#include "servers/camera_server.h"
+#include "core/io/resource.h"
+#include "gltf_document.h"
+
+class GLTFTexture : public Resource {
+ GDCLASS(GLTFTexture, Resource);
-class CameraIOS : public CameraServer {
private:
-public:
- CameraIOS();
- ~CameraIOS();
+ GLTFImageIndex src_image = 0;
- void update_feeds();
+protected:
+ static void _bind_methods();
+
+public:
+ GLTFImageIndex get_src_image() const;
+ void set_src_image(GLTFImageIndex val);
};
-#endif /* CAMERAIOS_H */
+#endif // GLTF_TEXTURE_H
diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp
new file mode 100644
index 0000000000..5a60c2d328
--- /dev/null
+++ b/modules/gltf/register_types.cpp
@@ -0,0 +1,91 @@
+/*************************************************************************/
+/* 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 "editor/editor_node.h"
+#include "editor_scene_exporter_gltf_plugin.h"
+#include "editor_scene_importer_gltf.h"
+#include "gltf_accessor.h"
+#include "gltf_animation.h"
+#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"
+#include "gltf_skeleton.h"
+#include "gltf_skin.h"
+#include "gltf_spec_gloss.h"
+#include "gltf_state.h"
+#include "gltf_texture.h"
+
+#ifndef _3D_DISABLED
+#ifdef TOOLS_ENABLED
+static void _editor_init() {
+ Ref<EditorSceneFormatImporterGLTF> import_gltf;
+ import_gltf.instantiate();
+ ResourceImporterScene::get_singleton()->add_importer(import_gltf);
+}
+#endif
+#endif
+
+void register_gltf_types() {
+#ifndef _3D_DISABLED
+#ifdef TOOLS_ENABLED
+ ClassDB::APIType prev_api = ClassDB::get_current_api();
+ ClassDB::set_current_api(ClassDB::API_EDITOR);
+ GDREGISTER_CLASS(EditorSceneFormatImporterGLTF);
+ GDREGISTER_CLASS(GLTFMesh);
+ EditorPlugins::add_by_type<SceneExporterGLTFPlugin>();
+ ClassDB::set_current_api(prev_api);
+ EditorNode::add_init_callback(_editor_init);
+#endif
+ GDREGISTER_CLASS(GLTFSpecGloss);
+ GDREGISTER_CLASS(GLTFNode);
+ GDREGISTER_CLASS(GLTFAnimation);
+ GDREGISTER_CLASS(GLTFBufferView);
+ GDREGISTER_CLASS(GLTFAccessor);
+ GDREGISTER_CLASS(GLTFTexture);
+ GDREGISTER_CLASS(GLTFSkeleton);
+ GDREGISTER_CLASS(GLTFSkin);
+ GDREGISTER_CLASS(GLTFCamera);
+ GDREGISTER_CLASS(GLTFLight);
+ GDREGISTER_CLASS(GLTFState);
+ GDREGISTER_CLASS(GLTFDocumentExtensionConvertImporterMesh);
+ GDREGISTER_CLASS(GLTFDocumentExtension);
+ GDREGISTER_CLASS(GLTFDocument);
+#endif
+}
+
+void unregister_gltf_types() {
+}
diff --git a/modules/gdnative/xr/register_types.h b/modules/gltf/register_types.h
index 2501d28651..fefacb1106 100644
--- a/modules/gdnative/xr/register_types.h
+++ b/modules/gltf/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,10 +28,5 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef XR_REGISTER_TYPES_H
-#define XR_REGISTER_TYPES_H
-
-void register_xr_types();
-void unregister_xr_types();
-
-#endif // XR_REGISTER_TYPES_H
+void register_gltf_types();
+void unregister_gltf_types();
diff --git a/modules/gridmap/config.py b/modules/gridmap/config.py
index a6319fe1ea..720401b92d 100644
--- a/modules/gridmap/config.py
+++ b/modules/gridmap/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return True
+ return not env["disable_3d"]
def configure(env):
diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index 4dccb03369..73315350ff 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -8,126 +8,102 @@
GridMaps use a [MeshLibrary] which contains a list of tiles. Each tile is a mesh with materials plus optional collision and navigation shapes.
A GridMap contains a collection of cells. Each grid cell refers to a tile in the [MeshLibrary]. All cells in the map have the same dimensions.
Internally, a GridMap is split into a sparse collection of octants for efficient rendering and physics processing. Every octant has the same dimensions and can contain several cells.
+ [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>
<methods>
<method name="clear">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Clear all cells.
</description>
</method>
<method name="clear_baked_meshes">
- <return type="void">
- </return>
+ <return type="void" />
<description>
</description>
</method>
<method name="get_bake_mesh_instance">
- <return type="RID">
- </return>
- <argument index="0" name="idx" type="int">
- </argument>
+ <return type="RID" />
+ <argument index="0" name="idx" type="int" />
<description>
</description>
</method>
<method name="get_bake_meshes">
- <return type="Array">
- </return>
+ <return type="Array" />
<description>
+ Returns an array of [ArrayMesh]es and [Transform3D] references of all bake meshes that exist within the current GridMap.
</description>
</method>
<method name="get_cell_item" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="position" type="Vector3i">
- </argument>
+ <return type="int" />
+ <argument index="0" name="position" type="Vector3i" />
<description>
The [MeshLibrary] item index located at the given grid coordinates. If the cell is empty, [constant INVALID_CELL_ITEM] will be returned.
</description>
</method>
<method name="get_cell_item_orientation" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="position" type="Vector3i">
- </argument>
+ <return type="int" />
+ <argument index="0" name="position" type="Vector3i" />
<description>
The orientation of the cell at the given grid coordinates. [code]-1[/code] is returned if the cell is empty.
</description>
</method>
- <method name="get_collision_layer_bit" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="bit" type="int">
- </argument>
+ <method name="get_collision_layer_value" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="layer_number" type="int" />
<description>
- Returns an individual bit on the [member collision_layer].
+ Returns whether or not the specified layer of the [member collision_layer] is enabled, given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
- <method name="get_collision_mask_bit" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="bit" type="int">
- </argument>
+ <method name="get_collision_mask_value" qualifiers="const">
+ <return type="bool" />
+ <argument index="0" name="layer_number" type="int" />
<description>
- Returns an individual bit on the [member collision_mask].
+ Returns whether or not the specified layer of the [member collision_mask] is enabled, given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
<method name="get_meshes">
- <return type="Array">
- </return>
+ <return type="Array" />
<description>
- Returns an array of [Transform] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space.
+ Returns an array of [Transform3D] and [Mesh] references corresponding to the non-empty cells in the grid. The transforms are specified in world space.
</description>
</method>
<method name="get_used_cells" qualifiers="const">
- <return type="Array">
- </return>
+ <return type="Array" />
<description>
Returns an array of [Vector3] with the non-empty cell coordinates in the grid map.
</description>
</method>
<method name="make_baked_meshes">
- <return type="void">
- </return>
- <argument index="0" name="gen_lightmap_uv" type="bool" default="false">
- </argument>
- <argument index="1" name="lightmap_uv_texel_size" type="float" default="0.1">
- </argument>
+ <return type="void" />
+ <argument index="0" name="gen_lightmap_uv" type="bool" default="false" />
+ <argument index="1" name="lightmap_uv_texel_size" type="float" default="0.1" />
<description>
</description>
</method>
<method name="map_to_world" qualifiers="const">
- <return type="Vector3">
- </return>
- <argument index="0" name="map_position" type="Vector3i">
- </argument>
+ <return type="Vector3" />
+ <argument index="0" name="map_position" type="Vector3i" />
<description>
Returns the position of a grid cell in the GridMap's local coordinate space.
</description>
</method>
<method name="resource_changed">
- <return type="void">
- </return>
- <argument index="0" name="resource" type="Resource">
- </argument>
+ <return type="void" />
+ <argument index="0" name="resource" type="Resource" />
<description>
</description>
</method>
<method name="set_cell_item">
- <return type="void">
- </return>
- <argument index="0" name="position" type="Vector3i">
- </argument>
- <argument index="1" name="item" type="int">
- </argument>
- <argument index="2" name="orientation" type="int" default="0">
- </argument>
+ <return type="void" />
+ <argument index="0" name="position" type="Vector3i" />
+ <argument index="1" name="item" type="int" />
+ <argument index="2" name="orientation" type="int" default="0" />
<description>
Sets the mesh index for the cell referenced by its grid coordinates.
A negative item index such as [constant INVALID_CELL_ITEM] will clear the cell.
@@ -135,46 +111,33 @@
</description>
</method>
<method name="set_clip">
- <return type="void">
- </return>
- <argument index="0" name="enabled" type="bool">
- </argument>
- <argument index="1" name="clipabove" type="bool" default="true">
- </argument>
- <argument index="2" name="floor" type="int" default="0">
- </argument>
- <argument index="3" name="axis" type="int" enum="Vector3.Axis" default="0">
- </argument>
+ <return type="void" />
+ <argument index="0" name="enabled" type="bool" />
+ <argument index="1" name="clipabove" type="bool" default="true" />
+ <argument index="2" name="floor" type="int" default="0" />
+ <argument index="3" name="axis" type="int" enum="Vector3.Axis" default="0" />
<description>
</description>
</method>
- <method name="set_collision_layer_bit">
- <return type="void">
- </return>
- <argument index="0" name="bit" type="int">
- </argument>
- <argument index="1" name="value" type="bool">
- </argument>
+ <method name="set_collision_layer_value">
+ <return type="void" />
+ <argument index="0" name="layer_number" type="int" />
+ <argument index="1" name="value" type="bool" />
<description>
- Sets an individual bit on the [member collision_layer].
+ Based on [code]value[/code], enables or disables the specified layer in the [member collision_layer], given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
- <method name="set_collision_mask_bit">
- <return type="void">
- </return>
- <argument index="0" name="bit" type="int">
- </argument>
- <argument index="1" name="value" type="bool">
- </argument>
+ <method name="set_collision_mask_value">
+ <return type="void" />
+ <argument index="0" name="layer_number" type="int" />
+ <argument index="1" name="value" type="bool" />
<description>
- Sets an individual bit on the [member collision_mask].
+ Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32.
</description>
</method>
<method name="world_to_map" qualifiers="const">
- <return type="Vector3i">
- </return>
- <argument index="0" name="world_position" type="Vector3">
- </argument>
+ <return type="Vector3i" />
+ <argument index="0" name="world_position" type="Vector3" />
<description>
Returns the coordinates of the grid cell containing the given point.
[code]pos[/code] should be in the GridMap's local coordinate space.
@@ -182,6 +145,9 @@
</method>
</methods>
<members>
+ <member name="bake_navigation" type="bool" setter="set_bake_navigation" getter="is_baking_navigation" default="false">
+ If [code]true[/code], this GridMap bakes a navigation region.
+ </member>
<member name="cell_center_x" type="bool" setter="set_center_x" getter="get_center_x" default="true">
If [code]true[/code], grid items are centered on the X axis.
</member>
@@ -198,7 +164,7 @@
The scale of the cell items.
This does not affect the size of the grid cells themselves, only the items in them. This can be used to make cell items overlap their neighbors.
</member>
- <member name="cell_size" type="Vector3" setter="set_cell_size" getter="get_cell_size" default="Vector3( 2, 2, 2 )">
+ <member name="cell_size" type="Vector3" setter="set_cell_size" getter="get_cell_size" default="Vector3(2, 2, 2)">
The dimensions of the grid's cells.
This does not affect the size of the meshes. See [member cell_scale].
</member>
@@ -207,16 +173,18 @@
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].
</member>
+ <member name="navigation_layers" type="int" setter="set_navigation_layers" getter="get_navigation_layers" default="1">
+ The navigation layers the GridMap generates its navigable regions in.
+ </member>
</members>
<signals>
<signal name="cell_size_changed">
- <argument index="0" name="cell_size" type="Vector3">
- </argument>
+ <argument index="0" name="cell_size" type="Vector3" />
<description>
Emitted when [member cell_size] changes.
</description>
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index ccf1e49693..c9d8f2b42b 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#include "grid_map.h"
#include "core/io/marshalls.h"
-#include "core/message_queue.h"
+#include "core/object/message_queue.h"
#include "scene/3d/light_3d.h"
#include "scene/resources/mesh_library.h"
#include "scene/resources/surface_tool.h"
@@ -151,32 +151,58 @@ uint32_t GridMap::get_collision_mask() const {
return collision_mask;
}
-void GridMap::set_collision_mask_bit(int p_bit, bool p_value) {
- uint32_t mask = get_collision_mask();
+void GridMap::set_collision_layer_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
+ uint32_t collision_layer = get_collision_layer();
if (p_value) {
- mask |= 1 << p_bit;
+ collision_layer |= 1 << (p_layer_number - 1);
} else {
- mask &= ~(1 << p_bit);
+ collision_layer &= ~(1 << (p_layer_number - 1));
}
- set_collision_mask(mask);
+ set_collision_layer(collision_layer);
}
-bool GridMap::get_collision_mask_bit(int p_bit) const {
- return get_collision_mask() & (1 << p_bit);
+bool GridMap::get_collision_layer_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
+ return get_collision_layer() & (1 << (p_layer_number - 1));
}
-void GridMap::set_collision_layer_bit(int p_bit, bool p_value) {
- uint32_t mask = get_collision_layer();
+void GridMap::set_collision_mask_value(int p_layer_number, bool p_value) {
+ ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive.");
+ uint32_t mask = get_collision_mask();
if (p_value) {
- mask |= 1 << p_bit;
+ mask |= 1 << (p_layer_number - 1);
} else {
- mask &= ~(1 << p_bit);
+ mask &= ~(1 << (p_layer_number - 1));
}
- set_collision_layer(mask);
+ set_collision_mask(mask);
+}
+
+bool GridMap::get_collision_mask_value(int p_layer_number) const {
+ ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive.");
+ ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive.");
+ return get_collision_mask() & (1 << (p_layer_number - 1));
+}
+
+void GridMap::set_bake_navigation(bool p_bake_navigation) {
+ bake_navigation = p_bake_navigation;
+ _recreate_octant_data();
+}
+
+bool GridMap::is_baking_navigation() {
+ return bake_navigation;
+}
+
+void GridMap::set_navigation_layers(uint32_t p_layers) {
+ navigation_layers = p_layers;
+ _recreate_octant_data();
}
-bool GridMap::get_collision_layer_bit(int p_bit) const {
- return get_collision_layer() & (1 << p_bit);
+uint32_t GridMap::get_navigation_layers() {
+ return navigation_layers;
}
void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) {
@@ -189,7 +215,6 @@ void GridMap::set_mesh_library(const Ref<MeshLibrary> &p_mesh_library) {
}
_recreate_octant_data();
- _change_notify("mesh_library");
}
Ref<MeshLibrary> GridMap::get_mesh_library() const {
@@ -200,7 +225,7 @@ void GridMap::set_cell_size(const Vector3 &p_size) {
ERR_FAIL_COND(p_size.x < 0.001 || p_size.y < 0.001 || p_size.z < 0.001);
cell_size = p_size;
_recreate_octant_data();
- emit_signal("cell_size_changed", cell_size);
+ emit_signal(SNAME("cell_size_changed"), cell_size);
}
Vector3 GridMap::get_cell_size() const {
@@ -286,7 +311,8 @@ void GridMap::set_cell_item(const Vector3i &p_position, int p_item, int p_rot) {
//create octant because it does not exist
Octant *g = memnew(Octant);
g->dirty = true;
- g->static_body = PhysicsServer3D::get_singleton()->body_create(PhysicsServer3D::BODY_MODE_STATIC);
+ g->static_body = PhysicsServer3D::get_singleton()->body_create();
+ PhysicsServer3D::get_singleton()->body_set_mode(g->static_body, PhysicsServer3D::BODY_MODE_STATIC);
PhysicsServer3D::get_singleton()->body_attach_object_instance_id(g->static_body, get_instance_id());
PhysicsServer3D::get_singleton()->body_set_collision_layer(g->static_body, collision_layer);
PhysicsServer3D::get_singleton()->body_set_collision_mask(g->static_body, collision_mask);
@@ -351,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);
}
@@ -397,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();
@@ -424,7 +447,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
* and set said multimesh bounding box to one containing all cells which have this item
*/
- Map<int, List<Pair<Transform, IndexKey>>> multimesh_items;
+ Map<int, List<Pair<Transform3D, IndexKey>>> multimesh_items;
for (Set<IndexKey>::Element *E = g.cells.front(); E; E = E->next()) {
ERR_CONTINUE(!cell_map.has(E->get()));
@@ -437,7 +460,7 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
Vector3 cellpos = Vector3(E->get().x, E->get().y, E->get().z);
Vector3 ofs = _get_offset();
- Transform xform;
+ Transform3D xform;
xform.basis.set_orthogonal_index(c.rot);
xform.set_origin(cellpos * cell_size + ofs);
@@ -445,11 +468,11 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
if (baked_meshes.size() == 0) {
if (mesh_library->get_item_mesh(c.item).is_valid()) {
if (!multimesh_items.has(c.item)) {
- multimesh_items[c.item] = List<Pair<Transform, IndexKey>>();
+ multimesh_items[c.item] = List<Pair<Transform3D, IndexKey>>();
}
- Pair<Transform, IndexKey> p;
- p.first = xform;
+ Pair<Transform3D, IndexKey> p;
+ p.first = xform * mesh_library->get_item_mesh_transform(c.item);
p.second = E->get();
multimesh_items[c.item].push_back(p);
}
@@ -474,35 +497,37 @@ bool GridMap::_octant_update(const OctantKey &p_key) {
Octant::NavMesh nm;
nm.xform = xform * mesh_library->get_item_navmesh_transform(c.item);
- if (navigation) {
+ if (bake_navigation) {
RID region = NavigationServer3D::get_singleton()->region_create();
+ NavigationServer3D::get_singleton()->region_set_layers(region, navigation_layers);
NavigationServer3D::get_singleton()->region_set_navmesh(region, navmesh);
- NavigationServer3D::get_singleton()->region_set_transform(region, navigation->get_global_transform() * nm.xform);
- NavigationServer3D::get_singleton()->region_set_map(region, navigation->get_rid());
+ NavigationServer3D::get_singleton()->region_set_transform(region, get_global_transform() * mesh_library->get_item_navmesh_transform(c.item));
+ NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map());
nm.region = region;
}
+
g.navmesh_ids[E->get()] = nm;
}
}
//update multimeshes, only if not baked
if (baked_meshes.size() == 0) {
- for (Map<int, List<Pair<Transform, 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(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 (List<Pair<Transform, IndexKey>>::Element *F = E->get().front(); F; F = F->next()) {
- RS::get_singleton()->multimesh_instance_set_transform(mm, idx, F->get().first);
+ for (const Pair<Transform3D, IndexKey> &F : E.value) {
+ RS::get_singleton()->multimesh_instance_set_transform(mm, idx, F.first);
#ifdef TOOLS_ENABLED
Octant::MultimeshInstance::Item it;
it.index = idx;
- it.transform = F->get().first;
- it.key = F->get().second;
+ it.transform = F.first;
+ it.key = F.second;
mmi.items.push_back(it);
#endif
@@ -542,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);
}
}
@@ -564,16 +589,18 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
RS::get_singleton()->instance_set_transform(g.multimesh_instances[i].instance, get_global_transform());
}
- if (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);
+ if (bake_navigation && mesh_library.is_valid()) {
+ 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, navigation->get_global_transform() * F->get().xform);
- NavigationServer3D::get_singleton()->region_set_map(region, navigation->get_rid());
- F->get().region = region;
+ 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.value.region = region;
}
}
}
@@ -594,12 +621,10 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {
RS::get_singleton()->instance_set_scenario(g.multimesh_instances[i].instance, RID());
}
- if (navigation) {
- 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();
}
}
}
@@ -618,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();
@@ -635,20 +660,10 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) {
void GridMap::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_WORLD: {
- Node3D *c = this;
- while (c) {
- navigation = Object::cast_to<Navigation3D>(c);
- if (navigation) {
- break;
- }
-
- c = Object::cast_to<Node3D>(c->get_parent());
- }
-
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++) {
@@ -658,13 +673,13 @@ void GridMap::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
- Transform new_xform = get_global_transform();
+ Transform3D new_xform = get_global_transform();
if (new_xform == last_transform) {
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;
@@ -672,15 +687,12 @@ void GridMap::_notification(int p_what) {
for (int i = 0; i < baked_meshes.size(); i++) {
RS::get_singleton()->instance_set_transform(baked_meshes[i].instance, get_global_transform());
}
-
} 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);
}
- navigation = nullptr;
-
//_queue_octants_dirty(MAP_DIRTY_INSTANCES|MAP_DIRTY_TRANSFORMS);
//_update_octants_callback();
//_update_area_instances();
@@ -700,15 +712,17 @@ void GridMap::_update_visibility() {
return;
}
- _change_notify("visible");
-
- 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());
+ RS::get_singleton()->instance_set_visible(mi.instance, is_visible_in_tree());
}
}
+
+ for (int i = 0; i < baked_meshes.size(); i++) {
+ RS::get_singleton()->instance_set_visible(baked_meshes[i].instance, is_visible_in_tree());
+ }
}
void GridMap::_queue_octants_dirty() {
@@ -724,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();
@@ -759,15 +773,15 @@ 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);
}
}
while (to_delete.front()) {
octant_map.erase(to_delete.front()->get());
- to_delete.pop_back();
+ to_delete.pop_front();
}
_update_visibility();
@@ -781,11 +795,17 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &GridMap::set_collision_mask);
ClassDB::bind_method(D_METHOD("get_collision_mask"), &GridMap::get_collision_mask);
- ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &GridMap::set_collision_mask_bit);
- ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &GridMap::get_collision_mask_bit);
+ ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &GridMap::set_collision_mask_value);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &GridMap::get_collision_mask_value);
+
+ ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &GridMap::set_collision_layer_value);
+ ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &GridMap::get_collision_layer_value);
- ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &GridMap::set_collision_layer_bit);
- ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &GridMap::get_collision_layer_bit);
+ ClassDB::bind_method(D_METHOD("set_bake_navigation", "bake_navigation"), &GridMap::set_bake_navigation);
+ ClassDB::bind_method(D_METHOD("is_baking_navigation"), &GridMap::is_baking_navigation);
+
+ ClassDB::bind_method(D_METHOD("set_navigation_layers", "layers"), &GridMap::set_navigation_layers);
+ ClassDB::bind_method(D_METHOD("get_navigation_layers"), &GridMap::get_navigation_layers);
ClassDB::bind_method(D_METHOD("set_mesh_library", "mesh_library"), &GridMap::set_mesh_library);
ClassDB::bind_method(D_METHOD("get_mesh_library"), &GridMap::get_mesh_library);
@@ -840,6 +860,9 @@ void GridMap::_bind_methods() {
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+ ADD_GROUP("Navigation", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bake_navigation"), "set_bake_navigation", "is_baking_navigation");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers");
BIND_CONSTANT(INVALID_CELL_ITEM);
@@ -860,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;
@@ -881,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;
}
@@ -897,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;
}
@@ -907,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);
- Transform xform;
+ 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));
@@ -949,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;
}
@@ -965,9 +988,9 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
Vector3 cellpos = Vector3(key.x, key.y, key.z);
Vector3 ofs = _get_offset();
- Transform xform;
+ 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));
@@ -990,7 +1013,7 @@ void GridMap::make_baked_meshes(bool p_gen_lightmap_uv, float p_lightmap_uv_texe
Ref<Material> surf_mat = mesh->surface_get_material(i);
if (!mat_map.has(surf_mat)) {
Ref<SurfaceTool> st;
- st.instance();
+ st.instantiate();
st->begin(Mesh::PRIMITIVE_TRIANGLES);
st->set_material(surf_mat);
mat_map[surf_mat] = st;
@@ -1000,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.instance();
- for (Map<Ref<Material>, Ref<SurfaceTool>>::Element *F = E->get().front(); F; F = F->next()) {
- F->get()->commit(mesh);
+ mesh.instantiate();
+ for (KeyValue<Ref<Material>, Ref<SurfaceTool>> &F : E.value) {
+ F.value->commit(mesh);
}
BakedMesh bm;
@@ -1034,7 +1057,7 @@ Array GridMap::get_bake_meshes() {
Array arr;
for (int i = 0; i < baked_meshes.size(); i++) {
arr.push_back(baked_meshes[i].mesh);
- arr.push_back(Transform());
+ arr.push_back(Transform3D());
}
return arr;
@@ -1046,26 +1069,7 @@ RID GridMap::get_bake_mesh_instance(int p_idx) {
}
GridMap::GridMap() {
- collision_layer = 1;
- collision_mask = 1;
-
- cell_size = Vector3(2, 2, 2);
- octant_size = 8;
- awaiting_update = false;
- _in_tree = false;
- center_x = true;
- center_y = true;
- center_z = true;
-
- clip = false;
- clip_floor = 0;
- clip_axis = Vector3::AXIS_Z;
- clip_above = true;
- cell_scale = 1.0;
-
- navigation = nullptr;
set_notify_transform(true);
- recreating_octants = false;
}
GridMap::~GridMap() {
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index ca7429ea26..879489fc70 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,6 @@
#ifndef GRID_MAP_H
#define GRID_MAP_H
-#include "scene/3d/navigation_3d.h"
#include "scene/3d/node_3d.h"
#include "scene/resources/mesh_library.h"
#include "scene/resources/multimesh.h"
@@ -53,7 +52,7 @@ class GridMap : public Node3D {
int16_t y;
int16_t z;
};
- uint64_t key;
+ uint64_t key = 0;
_FORCE_INLINE_ bool operator<(const IndexKey &p_key) const {
return key < p_key.key;
@@ -68,7 +67,7 @@ class GridMap : public Node3D {
y = (int16_t)p_vector.y;
z = (int16_t)p_vector.z;
}
- IndexKey() { key = 0; }
+ IndexKey() {}
};
/**
@@ -80,13 +79,7 @@ class GridMap : public Node3D {
unsigned int rot : 5;
unsigned int layer : 8;
};
- uint32_t cell;
-
- Cell() {
- item = 0;
- rot = 0;
- layer = 0;
- }
+ uint32_t cell = 0;
};
/**
@@ -96,15 +89,15 @@ class GridMap : public Node3D {
struct Octant {
struct NavMesh {
RID region;
- Transform xform;
+ Transform3D xform;
};
struct MultimeshInstance {
RID instance;
RID multimesh;
struct Item {
- int index;
- Transform transform;
+ int index = 0;
+ Transform3D transform;
IndexKey key;
};
@@ -116,7 +109,7 @@ class GridMap : public Node3D {
RID collision_debug;
RID collision_debug_instance;
- bool dirty;
+ bool dirty = false;
RID static_body;
Map<IndexKey, NavMesh> navmesh_ids;
};
@@ -129,35 +122,38 @@ class GridMap : public Node3D {
int16_t empty;
};
- uint64_t key;
+ uint64_t key = 0;
_FORCE_INLINE_ bool operator<(const OctantKey &p_key) const {
return key < p_key.key;
}
//OctantKey(const IndexKey& p_k, int p_item) { indexkey=p_k.key; item=p_item; }
- OctantKey() { key = 0; }
+ OctantKey() {}
};
- uint32_t collision_layer;
- uint32_t collision_mask;
+ uint32_t collision_layer = 1;
+ uint32_t collision_mask = 1;
+ bool bake_navigation = false;
+ uint32_t navigation_layers = 1;
- Transform last_transform;
+ Transform3D last_transform;
- bool _in_tree;
- Vector3 cell_size;
- int octant_size;
- bool center_x, center_y, center_z;
- float cell_scale;
- Navigation3D *navigation;
+ bool _in_tree = false;
+ Vector3 cell_size = Vector3(2, 2, 2);
+ int octant_size = 8;
+ bool center_x = true;
+ bool center_y = true;
+ bool center_z = true;
+ float cell_scale = 1.0;
- bool clip;
- bool clip_above;
- int clip_floor;
+ bool clip = false;
+ bool clip_above = true;
+ int clip_floor = 0;
- bool recreating_octants;
+ bool recreating_octants = false;
- Vector3::Axis clip_axis;
+ Vector3::Axis clip_axis = Vector3::AXIS_Z;
Ref<MeshLibrary> mesh_library;
@@ -167,10 +163,10 @@ class GridMap : public Node3D {
void _recreate_octant_data();
struct BakeLight {
- RS::LightType type;
+ RS::LightType type = RS::LightType::LIGHT_DIRECTIONAL;
Vector3 pos;
Vector3 dir;
- float param[RS::LIGHT_PARAM_MAX];
+ float param[RS::LIGHT_PARAM_MAX] = {};
};
_FORCE_INLINE_ Vector3 _octant_get_offset(const OctantKey &p_key) const {
@@ -183,7 +179,7 @@ class GridMap : public Node3D {
bool _octant_update(const OctantKey &p_key);
void _octant_clean_up(const OctantKey &p_key);
void _octant_transform(const OctantKey &p_key);
- bool awaiting_update;
+ bool awaiting_update = false;
void _queue_octants_dirty();
void _update_octants_callback();
@@ -221,11 +217,17 @@ public:
void set_collision_mask(uint32_t p_mask);
uint32_t get_collision_mask() const;
- void set_collision_layer_bit(int p_bit, bool p_value);
- bool get_collision_layer_bit(int p_bit) const;
+ void set_collision_layer_value(int p_layer_number, bool p_value);
+ bool get_collision_layer_value(int p_layer_number) const;
+
+ void set_collision_mask_value(int p_layer_number, bool p_value);
+ bool get_collision_mask_value(int p_layer_number) const;
+
+ void set_bake_navigation(bool p_bake_navigation);
+ bool is_baking_navigation();
- void set_collision_mask_bit(int p_bit, bool p_value);
- bool get_collision_mask_bit(int p_bit) const;
+ void set_navigation_layers(uint32_t p_layers);
+ uint32_t get_navigation_layers();
void set_mesh_library(const Ref<MeshLibrary> &p_mesh_library);
Ref<MeshLibrary> get_mesh_library() const;
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 0e6ec7f520..d827ce2fb0 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -62,12 +62,6 @@ void GridMapEditor::_menu_option(int p_option) {
floor->set_value(floor->get_value() + 1);
} break;
- case MENU_OPTION_LOCK_VIEW: {
- int index = options->get_popup()->get_item_index(MENU_OPTION_LOCK_VIEW);
- lock_view = !options->get_popup()->is_item_checked(index);
-
- options->get_popup()->set_item_checked(index, lock_view);
- } break;
case MENU_OPTION_CLIP_DISABLED:
case MENU_OPTION_CLIP_ABOVE:
case MENU_OPTION_CLIP_BELOW: {
@@ -255,12 +249,18 @@ void GridMapEditor::_menu_option(int p_option) {
}
void GridMapEditor::_update_cursor_transform() {
- cursor_transform = Transform();
+ cursor_transform = Transform3D();
cursor_transform.origin = cursor_origin;
cursor_transform.basis.set_orthogonal_index(cursor_rot);
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);
@@ -268,7 +268,7 @@ void GridMapEditor::_update_cursor_transform() {
}
void GridMapEditor::_update_selection_transform() {
- Transform xf_zero;
+ Transform3D xf_zero;
xf_zero.basis.set_zero();
if (!selection.active) {
@@ -279,7 +279,7 @@ void GridMapEditor::_update_selection_transform() {
return;
}
- Transform xf;
+ Transform3D xf;
xf.scale((Vector3(1, 1, 1) + (selection.end - selection.begin)) * node->get_cell_size());
xf.origin = selection.begin * node->get_cell_size();
@@ -297,7 +297,7 @@ void GridMapEditor::_update_selection_transform() {
scale *= node->get_cell_size();
position *= node->get_cell_size();
- Transform xf2;
+ Transform3D xf2;
xf2.basis.scale(scale);
xf2.origin = position;
@@ -362,7 +362,7 @@ bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, b
Camera3D *camera = p_camera;
Vector3 from = camera->project_ray_origin(p_point);
Vector3 normal = camera->project_ray_normal(p_point);
- Transform local_xform = node->get_global_transform().affine_inverse();
+ Transform3D local_xform = node->get_global_transform().affine_inverse();
Vector<Plane> planes = camera->get_frustum();
from = local_xform.xform(from);
normal = local_xform.basis.xform(normal).normalized();
@@ -386,13 +386,13 @@ bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, b
}
int cell[3];
- float cell_size[3] = { node->get_cell_size().x, node->get_cell_size().y, node->get_cell_size().z };
+ Vector3 cell_size = node->get_cell_size();
for (int i = 0; i < 3; i++) {
if (i == edit_axis) {
cell[i] = edit_floor[i];
} else {
- cell[i] = inters[i] / node->get_cell_size()[i];
+ cell[i] = inters[i] / cell_size[i];
if (inters[i] < 0) {
cell[i] -= 1; // Compensate negative.
}
@@ -436,6 +436,7 @@ bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, b
}
return true;
}
+
if (input_action == INPUT_PAINT) {
SetItem si;
si.position = Vector3i(cell[0], cell[1], cell[2]);
@@ -502,8 +503,8 @@ void GridMapEditor::_fill_selection() {
}
void GridMapEditor::_clear_clipboard_data() {
- for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) {
- RenderingServer::get_singleton()->free(E->get().instance);
+ for (const ClipboardItem &E : clipboard_items) {
+ RenderingServer::get_singleton()->free(E.instance);
}
clipboard_items.clear();
@@ -539,15 +540,15 @@ void GridMapEditor::_set_clipboard_data() {
void GridMapEditor::_update_paste_indicator() {
if (input_action != INPUT_PASTE) {
- Transform xf;
+ Transform3D xf;
xf.basis.set_zero();
RenderingServer::get_singleton()->instance_set_transform(paste_instance, xf);
return;
}
- Vector3 center = 0.5 * Vector3(float(node->get_center_x()), float(node->get_center_y()), float(node->get_center_z()));
+ Vector3 center = 0.5 * Vector3(real_t(node->get_center_x()), real_t(node->get_center_y()), real_t(node->get_center_z()));
Vector3 scale = (Vector3(1, 1, 1) + (paste_indicator.end - paste_indicator.begin)) * node->get_cell_size();
- Transform xf;
+ Transform3D xf;
xf.scale(scale);
xf.origin = (paste_indicator.begin + (paste_indicator.current - paste_indicator.click) + center) * node->get_cell_size();
Basis rot;
@@ -557,10 +558,8 @@ void GridMapEditor::_update_paste_indicator() {
RenderingServer::get_singleton()->instance_set_transform(paste_instance, node->get_global_transform() * xf);
- for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) {
- ClipboardItem &item = E->get();
-
- xf = Transform();
+ for (const ClipboardItem &item : clipboard_items) {
+ xf = Transform3D();
xf.origin = (paste_indicator.begin + (paste_indicator.current - paste_indicator.click) + center) * node->get_cell_size();
xf.basis = rot * xf.basis;
xf.translate(item.grid_offset * node->get_cell_size());
@@ -583,9 +582,7 @@ void GridMapEditor::_do_paste() {
Vector3 ofs = paste_indicator.current - paste_indicator.click;
undo_redo->create_action(TTR("GridMap Paste Selection"));
- for (List<ClipboardItem>::Element *E = clipboard_items.front(); E; E = E->next()) {
- ClipboardItem &item = E->get();
-
+ for (const ClipboardItem &item : clipboard_items) {
Vector3 position = rot.xform(item.grid_offset) + paste_indicator.begin + ofs;
Basis orm;
@@ -606,70 +603,72 @@ 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() == BUTTON_WHEEL_UP && (mb->get_command() || mb->get_shift())) {
+ 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() == BUTTON_WHEEL_DOWN && (mb->get_command() || mb->get_shift())) {
+ 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->get_alt()) {
+ 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() == 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();
input_action = INPUT_NONE;
_update_paste_indicator();
- } else if (mb->get_shift() && can_edit) {
+ } else if (mb->is_shift_pressed() && can_edit) {
input_action = INPUT_SELECT;
last_selection = selection;
- } else if (mb->get_command() && can_edit) {
+ } else if (mb->is_command_pressed() && can_edit) {
input_action = INPUT_PICK;
} else {
input_action = INPUT_PAINT;
set_items.clear();
}
- } else if (mb->get_button_index() == 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() == BUTTON_RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == 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 (List<SetItem>::Element *E = set_items.front(); E; E = E->next()) {
- const SetItem &si = E->get();
+ for (const SetItem &si : set_items) {
undo_redo->add_do_method(node, "set_cell_item", si.position, si.new_value, si.new_orientation);
}
for (List<SetItem>::Element *E = set_items.back(); E; E = E->prev()) {
@@ -681,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() == BUTTON_LEFT && input_action == INPUT_SELECT) {
- undo_redo->create_action("GridMap Selection");
+ 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() == 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() == 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;
}
}
}
@@ -706,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->unselect_all();
+ mesh_library_palette->deselect_all();
update_palette();
_update_cursor_instance();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
- if (k->get_shift() && 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->is_shift_pressed() && selection.active && input_action != INPUT_PASTE) {
+ 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;
}
}
}
@@ -748,28 +754,28 @@ bool GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<In
Ref<InputEventPanGesture> pan_gesture = p_event;
if (pan_gesture.is_valid()) {
- if (pan_gesture->get_alt() && (pan_gesture->get_command() || pan_gesture->get_shift())) {
+ if (pan_gesture->is_alt_pressed() && (pan_gesture->is_command_pressed() || pan_gesture->is_shift_pressed())) {
const real_t delta = pan_gesture->get_delta().y * 0.5;
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 {
String name;
- int id;
+ int id = 0;
_FORCE_INLINE_ bool operator<(const _CGMEItemSort &r_it) const { return name < r_it.name; }
};
@@ -798,9 +804,9 @@ 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->call("_gui_input", k);
+ mesh_library_palette->gui_input(k);
search_box->accept_event();
}
}
@@ -809,12 +815,12 @@ void GridMapEditor::_mesh_library_palette_input(const Ref<InputEvent> &p_ie) {
const Ref<InputEventMouseButton> mb = p_ie;
// Zoom in/out using Ctrl + mouse wheel
- if (mb.is_valid() && mb->is_pressed() && mb->get_command()) {
- if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP) {
+ if (mb.is_valid() && mb->is_pressed() && mb->is_command_pressed()) {
+ 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() == BUTTON_WHEEL_DOWN) {
+ if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) {
size_slider->set_value(size_slider->get_value() - 0.2);
}
}
@@ -874,8 +880,8 @@ void GridMapEditor::update_palette() {
int item = 0;
- for (List<_CGMEItemSort>::Element *E = il.front(); E; E = E->next()) {
- int id = E->get().id;
+ for (_CGMEItemSort &E : il) {
+ int id = E.id;
String name = mesh_library->get_item_name(id);
Ref<Texture2D> preview = mesh_library->get_item_preview(id);
@@ -1030,11 +1036,15 @@ void GridMapEditor::_notification(int p_what) {
for (int i = 0; i < 3; i++) {
grid[i] = RS::get_singleton()->mesh_create();
grid_instance[i] = RS::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world_3d()->get_scenario());
+ RenderingServer::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
selection_level_instance[i] = RenderingServer::get_singleton()->instance_create2(selection_level_mesh[i], get_tree()->get_root()->get_world_3d()->get_scenario());
+ RenderingServer::get_singleton()->instance_set_layer_mask(selection_level_instance[i], 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
}
selection_instance = RenderingServer::get_singleton()->instance_create2(selection_mesh, get_tree()->get_root()->get_world_3d()->get_scenario());
+ RenderingServer::get_singleton()->instance_set_layer_mask(selection_instance, 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
paste_instance = RenderingServer::get_singleton()->instance_create2(paste_mesh, get_tree()->get_root()->get_world_3d()->get_scenario());
+ RenderingServer::get_singleton()->instance_set_layer_mask(paste_instance, 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
_update_selection_transform();
_update_paste_indicator();
@@ -1063,7 +1073,7 @@ void GridMapEditor::_notification(int p_what) {
return;
}
- Transform xf = node->get_global_transform();
+ Transform3D xf = node->get_global_transform();
if (xf != grid_xform) {
for (int i = 0; i < 3; i++) {
@@ -1075,25 +1085,21 @@ void GridMapEditor::_notification(int p_what) {
if (cgmt.operator->() != last_mesh_library) {
update_palette();
}
-
- if (lock_view) {
- EditorNode *editor = Object::cast_to<EditorNode>(get_tree()->get_root()->get_child(0));
-
- Plane p;
- p.normal[edit_axis] = 1.0;
- p.d = edit_floor[edit_axis] * node->get_cell_size()[edit_axis];
- p = node->get_transform().xform(p); // plane to snap
-
- Node3DEditorPlugin *sep = Object::cast_to<Node3DEditorPlugin>(editor->get_editor_plugin_screen());
- if (sep) {
- sep->snap_cursor_to_plane(p);
- }
- }
} break;
case NOTIFICATION_THEME_CHANGED: {
- options->set_icon(get_theme_icon("GridMap", "EditorIcons"));
- search_box->set_right_icon(get_theme_icon("Search", "EditorIcons"));
+ options->set_icon(get_theme_icon(SNAME("GridMap"), SNAME("EditorIcons")));
+ search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")));
+ } break;
+
+ case NOTIFICATION_APPLICATION_FOCUS_OUT: {
+ if (input_action == INPUT_PAINT) {
+ // Simulate mouse released event to stop drawing when editor focus exists.
+ Ref<InputEventMouseButton> release;
+ release.instantiate();
+ release->set_button_index(MouseButton::LEFT);
+ forward_spatial_input_event(nullptr, release);
+ }
} break;
}
}
@@ -1147,7 +1153,6 @@ void GridMapEditor::_bind_methods() {
}
GridMapEditor::GridMapEditor(EditorNode *p_editor) {
- input_action = INPUT_NONE;
editor = p_editor;
undo_redo = p_editor->get_undo_redo();
@@ -1169,7 +1174,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
floor->set_min(-32767);
floor->set_max(32767);
floor->set_step(1);
- floor->get_line_edit()->add_theme_constant_override("minimum_spaces", 16);
+ floor->get_line_edit()->add_theme_constant_override("minimum_character_width", 16);
spatial_editor_hb->add_child(floor);
floor->connect("value_changed", callable_mp(this, &GridMapEditor::_floor_changed));
@@ -1183,35 +1188,33 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
spatial_editor_hb->hide();
options->set_text(TTR("Grid Map"));
- options->get_popup()->add_check_item(TTR("Snap View"), MENU_OPTION_LOCK_VIEW);
- options->get_popup()->add_separator();
- 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);
@@ -1230,7 +1233,6 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
settings_pick_distance->set_value(EDITOR_DEF("editors/grid_map/pick_distance", 5000.0));
settings_vbc->add_margin_child(TTR("Pick Distance:"), settings_pick_distance);
- clip_mode = CLIP_DISABLED;
options->get_popup()->connect("id_pressed", callable_mp(this, &GridMapEditor::_menu_option));
HBoxContainer *hb = memnew(HBoxContainer);
@@ -1248,7 +1250,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
mode_thumbnail->set_flat(true);
mode_thumbnail->set_toggle_mode(true);
mode_thumbnail->set_pressed(true);
- mode_thumbnail->set_icon(p_editor->get_gui_base()->get_theme_icon("FileThumbnail", "EditorIcons"));
+ mode_thumbnail->set_icon(p_editor->get_gui_base()->get_theme_icon(SNAME("FileThumbnail"), SNAME("EditorIcons")));
hb->add_child(mode_thumbnail);
mode_thumbnail->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode), varray(DISPLAY_THUMBNAIL));
@@ -1256,7 +1258,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
mode_list->set_flat(true);
mode_list->set_toggle_mode(true);
mode_list->set_pressed(false);
- mode_list->set_icon(p_editor->get_gui_base()->get_theme_icon("FileList", "EditorIcons"));
+ mode_list->set_icon(p_editor->get_gui_base()->get_theme_icon(SNAME("FileList"), SNAME("EditorIcons")));
hb->add_child(mode_list);
mode_list->connect("pressed", callable_mp(this, &GridMapEditor::_set_display_mode), varray(DISPLAY_LIST));
@@ -1271,8 +1273,6 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
EDITOR_DEF("editors/grid_map/preview_size", 64);
- display_mode = DISPLAY_THUMBNAIL;
-
mesh_library_palette = memnew(ItemList);
add_child(mesh_library_palette);
mesh_library_palette->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -1282,9 +1282,9 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
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_autowrap(true);
+ info_message->set_autowrap_mode(Label::AUTOWRAP_WORD_SMART);
info_message->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
- info_message->set_anchors_and_margins_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
+ info_message->set_anchors_and_offsets_preset(PRESET_WIDE, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE);
mesh_library_palette->add_child(info_message);
edit_axis = Vector3::AXIS_Y;
@@ -1292,11 +1292,6 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
edit_floor[1] = -1;
edit_floor[2] = -1;
- cursor_visible = false;
- selected_palette = -1;
- lock_view = false;
- cursor_rot = 0;
-
selection_mesh = RenderingServer::get_singleton()->mesh_create();
paste_mesh = RenderingServer::get_singleton()->mesh_create();
@@ -1372,7 +1367,7 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
Array d;
d.resize(RS::ARRAY_MAX);
- inner_mat.instance();
+ inner_mat.instantiate();
inner_mat->set_albedo(Color(0.7, 0.7, 1.0, 0.2));
inner_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
inner_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
@@ -1381,14 +1376,14 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
RenderingServer::get_singleton()->mesh_add_surface_from_arrays(selection_mesh, RS::PRIMITIVE_TRIANGLES, d);
RenderingServer::get_singleton()->mesh_surface_set_material(selection_mesh, 0, inner_mat->get_rid());
- outer_mat.instance();
+ outer_mat.instantiate();
outer_mat->set_albedo(Color(0.7, 0.7, 1.0, 0.8));
outer_mat->set_on_top_of_alpha();
outer_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
outer_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
- selection_floor_mat.instance();
+ selection_floor_mat.instantiate();
selection_floor_mat->set_albedo(Color(0.80, 0.80, 1.0, 1));
selection_floor_mat->set_on_top_of_alpha();
selection_floor_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
@@ -1414,10 +1409,8 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
}
_set_selection(false);
- updating = false;
- accumulated_floor_delta = 0.0;
- indicator_mat.instance();
+ indicator_mat.instantiate();
indicator_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
indicator_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
indicator_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
diff --git a/modules/gridmap/grid_map_editor_plugin.h b/modules/gridmap/grid_map_editor_plugin.h
index 7e136ff9bb..1a6b1310d8 100644
--- a/modules/gridmap/grid_map_editor_plugin.h
+++ b/modules/gridmap/grid_map_editor_plugin.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -41,12 +41,10 @@ class GridMapEditor : public VBoxContainer {
GDCLASS(GridMapEditor, VBoxContainer);
enum {
-
GRID_CURSOR_SIZE = 50
};
enum InputAction {
-
INPUT_NONE,
INPUT_PAINT,
INPUT_ERASE,
@@ -56,7 +54,6 @@ class GridMapEditor : public VBoxContainer {
};
enum ClipMode {
-
CLIP_DISABLED,
CLIP_ABOVE,
CLIP_BELOW
@@ -68,11 +65,11 @@ class GridMapEditor : public VBoxContainer {
};
UndoRedo *undo_redo;
- InputAction input_action;
+ InputAction input_action = INPUT_NONE;
Panel *panel;
MenuButton *options;
SpinBox *floor;
- double accumulated_floor_delta;
+ double accumulated_floor_delta = 0.0;
Button *mode_thumbnail;
Button *mode_list;
LineEdit *search_box;
@@ -85,21 +82,20 @@ class GridMapEditor : public VBoxContainer {
struct SetItem {
Vector3i position;
- int new_value;
- int new_orientation;
- int old_value;
- int old_orientation;
+ int new_value = 0;
+ int new_orientation = 0;
+ int old_value = 0;
+ int old_orientation = 0;
};
List<SetItem> set_items;
- GridMap *node;
+ GridMap *node = nullptr;
MeshLibrary *last_mesh_library;
- ClipMode clip_mode;
+ ClipMode clip_mode = CLIP_DISABLED;
- bool lock_view;
- Transform grid_xform;
- Transform edit_grid_xform;
+ Transform3D grid_xform;
+ Transform3D edit_grid_xform;
Vector3::Axis edit_axis;
int edit_floor[3];
Vector3 grid_ofs;
@@ -115,9 +111,9 @@ class GridMapEditor : public VBoxContainer {
RID paste_instance;
struct ClipboardItem {
- int cell_item;
+ int cell_item = 0;
Vector3 grid_offset;
- int orientation;
+ int orientation = 0;
RID instance;
};
@@ -128,14 +124,14 @@ class GridMapEditor : public VBoxContainer {
Ref<StandardMaterial3D> outer_mat;
Ref<StandardMaterial3D> selection_floor_mat;
- bool updating;
+ bool updating = false;
struct Selection {
Vector3 click;
Vector3 current;
Vector3 begin;
Vector3 end;
- bool active;
+ bool active = false;
} selection;
Selection last_selection;
@@ -144,21 +140,20 @@ class GridMapEditor : public VBoxContainer {
Vector3 current;
Vector3 begin;
Vector3 end;
- int orientation;
+ int orientation = 0;
};
PasteIndicator paste_indicator;
- bool cursor_visible;
- Transform cursor_transform;
+ bool cursor_visible = false;
+ Transform3D cursor_transform;
Vector3 cursor_origin;
- int display_mode;
- int selected_palette;
- int cursor_rot;
+ int display_mode = DISPLAY_THUMBNAIL;
+ int selected_palette = -1;
+ int cursor_rot = 0;
enum Menu {
-
MENU_OPTION_NEXT_LEVEL,
MENU_OPTION_PREV_LEVEL,
MENU_OPTION_LOCK_VIEW,
@@ -237,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() {}
@@ -255,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/gridmap/icons/GridMap.svg b/modules/gridmap/icons/GridMap.svg
index eafe1211f2..e208257855 100644
--- a/modules/gridmap/icons/GridMap.svg
+++ b/modules/gridmap/icons/GridMap.svg
@@ -1,5 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m8 1-6 3v8l6 3 6-3v-2l-2-1-4 2-2-1v-4l2-1v-2l2-1zm4 2-2 1v2l2 1 2-1v-2z" fill="#fc9c9c" fill-opacity=".99608"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1-6 3v8l6 3 6-3v-2l-2-1-4 2-2-1v-4l2-1v-2l2-1zm4 2-2 1v2l2 1 2-1v-2z" fill="#fc7f7f" fill-opacity=".99608"/></svg>
diff --git a/modules/gridmap/register_types.cpp b/modules/gridmap/register_types.cpp
index 906e506b62..85739d202e 100644
--- a/modules/gridmap/register_types.cpp
+++ b/modules/gridmap/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,14 +30,14 @@
#include "register_types.h"
#ifndef _3D_DISABLED
-#include "core/class_db.h"
+#include "core/object/class_db.h"
#include "grid_map.h"
#include "grid_map_editor_plugin.h"
#endif
void register_gridmap_types() {
#ifndef _3D_DISABLED
- ClassDB::register_class<GridMap>();
+ GDREGISTER_CLASS(GridMap);
#ifdef TOOLS_ENABLED
EditorPlugins::add_by_type<GridMapEditorPlugin>();
#endif
diff --git a/modules/gridmap/register_types.h b/modules/gridmap/register_types.h
index c0e3c39ca8..b977f4c5da 100644
--- a/modules/gridmap/register_types.h
+++ b/modules/gridmap/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/hdr/image_loader_hdr.cpp b/modules/hdr/image_loader_hdr.cpp
index 333b1cf377..32a31aa764 100644
--- a/modules/hdr/image_loader_hdr.cpp
+++ b/modules/hdr/image_loader_hdr.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#include "image_loader_hdr.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
Error ImageLoaderHDR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
String header = f->get_token();
@@ -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/hdr/image_loader_hdr.h b/modules/hdr/image_loader_hdr.h
index ef8e116616..33fcdd1245 100644
--- a/modules/hdr/image_loader_hdr.h
+++ b/modules/hdr/image_loader_hdr.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/hdr/register_types.cpp b/modules/hdr/register_types.cpp
index e749928f60..5a4a1993e0 100644
--- a/modules/hdr/register_types.cpp
+++ b/modules/hdr/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/hdr/register_types.h b/modules/hdr/register_types.h
index 02441516ec..c85bc84dce 100644
--- a/modules/hdr/register_types.h
+++ b/modules/hdr/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub
index 8ee8e6dd6e..7c6ceeea29 100644
--- a/modules/jpg/SCsub
+++ b/modules/jpg/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_jpg = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
# Not unbundled for now as they are not commonly available as shared library
thirdparty_dir = "#thirdparty/jpeg-compressor/"
thirdparty_sources = [
@@ -17,7 +20,15 @@ env_jpg.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_jpg.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_jpg.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Godot's own source files
-env_jpg.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index 9c7ace5cf2..b5e4753e8d 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#include "image_loader_jpegd.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include <jpgd.h>
#include <string.h>
@@ -105,7 +105,7 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
Error ImageLoaderJPG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
Vector<uint8_t> src_image;
- int src_image_len = f->get_len();
+ uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
src_image.resize(src_image_len);
@@ -127,7 +127,7 @@ void ImageLoaderJPG::get_recognized_extensions(List<String> *p_extensions) const
static Ref<Image> _jpegd_mem_loader_func(const uint8_t *p_png, int p_size) {
Ref<Image> img;
- img.instance();
+ img.instantiate();
Error err = jpeg_load_image_from_buffer(img.ptr(), p_png, p_size);
ERR_FAIL_COND_V(err, Ref<Image>());
return img;
diff --git a/modules/jpg/image_loader_jpegd.h b/modules/jpg/image_loader_jpegd.h
index 9aebaad9e3..be265b280c 100644
--- a/modules/jpg/image_loader_jpegd.h
+++ b/modules/jpg/image_loader_jpegd.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/jpg/register_types.cpp b/modules/jpg/register_types.cpp
index b31746f769..a6ae96635f 100644
--- a/modules/jpg/register_types.cpp
+++ b/modules/jpg/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/jpg/register_types.h b/modules/jpg/register_types.h
index 52cd378b3a..577e9b06f7 100644
--- a/modules/jpg/register_types.h
+++ b/modules/jpg/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/jsonrpc/jsonrpc.cpp b/modules/jsonrpc/jsonrpc.cpp
index 320da182f8..3d0759d83e 100644
--- a/modules/jsonrpc/jsonrpc.cpp
+++ b/modules/jsonrpc/jsonrpc.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "jsonrpc.h"
+
#include "core/io/json.h"
JSONRPC::JSONRPC() {
@@ -151,24 +152,22 @@ Variant JSONRPC::process_action(const Variant &p_action, bool p_process_arr_elem
}
String JSONRPC::process_string(const String &p_input) {
- if (p_input.empty()) {
+ if (p_input.is_empty()) {
return String();
}
Variant ret;
- Variant input;
- String err_message;
- int err_line;
- if (OK != JSON::parse(p_input, input, err_message, err_line)) {
- ret = make_response_error(JSONRPC::PARSE_ERROR, "Parse error");
+ JSON json;
+ if (json.parse(p_input) == OK) {
+ ret = process_action(json.get_data(), true);
} else {
- ret = process_action(input, true);
+ ret = make_response_error(JSONRPC::PARSE_ERROR, "Parse error");
}
if (ret.get_type() == Variant::NIL) {
return "";
}
- return JSON::print(ret);
+ return ret.to_json_string();
}
void JSONRPC::set_scope(const String &p_scope, Object *p_obj) {
diff --git a/modules/jsonrpc/jsonrpc.h b/modules/jsonrpc/jsonrpc.h
index e2b7ab0975..9fd016602d 100644
--- a/modules/jsonrpc/jsonrpc.h
+++ b/modules/jsonrpc/jsonrpc.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#ifndef GODOT_JSON_RPC_H
#define GODOT_JSON_RPC_H
-#include "core/object.h"
-#include "core/variant.h"
+#include "core/object/class_db.h"
+#include "core/variant/variant.h"
class JSONRPC : public Object {
GDCLASS(JSONRPC, Object)
diff --git a/modules/jsonrpc/register_types.cpp b/modules/jsonrpc/register_types.cpp
index 0d3e446e3e..8fdf6fe1aa 100644
--- a/modules/jsonrpc/register_types.cpp
+++ b/modules/jsonrpc/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,11 +29,11 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/class_db.h"
+#include "core/object/class_db.h"
#include "jsonrpc.h"
void register_jsonrpc_types() {
- ClassDB::register_class<JSONRPC>();
+ GDREGISTER_CLASS(JSONRPC);
}
void unregister_jsonrpc_types() {
diff --git a/modules/jsonrpc/register_types.h b/modules/jsonrpc/register_types.h
index 854d73a21f..6a21a12444 100644
--- a/modules/jsonrpc/register_types.h
+++ b/modules/jsonrpc/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
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 4de523baa0..37e969db4d 100644
--- a/modules/lightmapper_rd/lightmapper_rd.cpp
+++ b/modules/lightmapper_rd/lightmapper_rd.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,8 +29,8 @@
/*************************************************************************/
#include "lightmapper_rd.h"
+#include "core/config/project_settings.h"
#include "core/math/geometry_2d.h"
-#include "core/project_settings.h"
#include "lm_blendseams.glsl.gen.h"
#include "lm_compute.glsl.gen.h"
#include "lm_raster.glsl.gen.h"
@@ -40,8 +40,8 @@
//#define DEBUG_TEXTURES
void LightmapperRD::add_mesh(const MeshData &p_mesh) {
- ERR_FAIL_COND(p_mesh.albedo_on_uv2.is_null() || p_mesh.albedo_on_uv2->empty());
- ERR_FAIL_COND(p_mesh.emission_on_uv2.is_null() || p_mesh.emission_on_uv2->empty());
+ ERR_FAIL_COND(p_mesh.albedo_on_uv2.is_null() || p_mesh.albedo_on_uv2->is_empty());
+ ERR_FAIL_COND(p_mesh.emission_on_uv2.is_null() || p_mesh.emission_on_uv2->is_empty());
ERR_FAIL_COND(p_mesh.albedo_on_uv2->get_width() != p_mesh.emission_on_uv2->get_width());
ERR_FAIL_COND(p_mesh.albedo_on_uv2->get_height() != p_mesh.emission_on_uv2->get_height());
ERR_FAIL_COND(p_mesh.points.size() == 0);
@@ -93,8 +93,8 @@ void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, con
l.direction[2] = p_direction.z;
l.range = p_range;
l.attenuation = p_attenuation;
- l.spot_angle = Math::deg2rad(p_spot_angle);
- l.spot_attenuation = p_spot_attenuation;
+ l.cos_spot_angle = Math::cos(Math::deg2rad(p_spot_angle));
+ l.inv_spot_attenuation = 1.0f / p_spot_attenuation;
l.color[0] = p_color.r;
l.color[1] = p_color.g;
l.color[2] = p_color.b;
@@ -162,8 +162,8 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
MeshInstance &mi = mesh_instances.write[m_i];
Size2i s = Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height());
sizes.push_back(s);
- atlas_size.width = MAX(atlas_size.width, s.width);
- atlas_size.height = MAX(atlas_size.height, s.height);
+ atlas_size.width = MAX(atlas_size.width, s.width + 2);
+ atlas_size.height = MAX(atlas_size.height, s.height + 2);
}
int max = nearest_power_of_2_templated(atlas_size.width);
@@ -186,10 +186,12 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
//determine best texture array atlas size by bruteforce fitting
while (atlas_size.x <= p_max_texture_size && atlas_size.y <= p_max_texture_size) {
- Vector<Vector2i> source_sizes = sizes;
+ Vector<Vector2i> source_sizes;
Vector<int> source_indices;
- source_indices.resize(source_sizes.size());
+ source_sizes.resize(sizes.size());
+ source_indices.resize(sizes.size());
for (int i = 0; i < source_indices.size(); i++) {
+ source_sizes.write[i] = sizes[i] + Vector2i(2, 2); // Add padding between lightmaps
source_indices.write[i] = i;
}
Vector<Vector3i> atlas_offsets;
@@ -207,7 +209,7 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
if (ofs.z > 0) {
//valid
ofs.z = slices;
- atlas_offsets.write[sidx] = ofs;
+ atlas_offsets.write[sidx] = ofs + Vector3i(1, 1, 0); // Center lightmap in the reserved oversized region
} else {
new_indices.push_back(sidx);
new_sources.push_back(source_sizes[i]);
@@ -246,13 +248,13 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
for (int i = 0; i < atlas_slices; i++) {
Ref<Image> albedo;
- albedo.instance();
+ albedo.instantiate();
albedo->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBA8);
albedo->set_as_black();
albedo_images.write[i] = albedo;
Ref<Image> emission;
- emission.instance();
+ emission.instantiate();
emission->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH);
emission->set_as_black();
emission_images.write[i] = emission;
@@ -272,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 &grid_texture_sdf, 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);
@@ -385,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]++;
}
@@ -432,10 +430,10 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
triangle_indices.resize(triangle_sort.size());
Vector<uint32_t> grid_indices;
grid_indices.resize(grid_size * grid_size * grid_size * 2);
- zeromem(grid_indices.ptrw(), grid_indices.size() * sizeof(uint32_t));
+ memset(grid_indices.ptrw(), 0, grid_indices.size() * sizeof(uint32_t));
Vector<bool> solid;
solid.resize(grid_size * grid_size * grid_size);
- zeromem(solid.ptrw(), solid.size() * sizeof(bool));
+ memset(solid.ptrw(), 0, solid.size() * sizeof(bool));
{
uint32_t *tiw = triangle_indices.ptrw();
@@ -447,7 +445,6 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
if (cell != last_cell) {
//cell changed, update pointer to indices
giw[cell * 2 + 1] = i;
- last_cell = cell;
solidw[cell] = true;
}
tiw[i] = triangle_sort[i].triangle_index;
@@ -478,19 +475,11 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
}
Ref<Image> img;
- img.instance();
+ img.instantiate();
img->create(grid_size, grid_size, false, Image::FORMAT_L8, grid_usage);
img->save_png("res://grid_layer_" + itos(1000 + i).substr(1, 3) + ".png");
}
#endif
- if (p_step_function) {
- p_step_function(0.45, TTR("Generating Signed Distance Field"), p_bake_userdata, true);
- }
-
- //generate SDF for raytracing
- Vector<uint32_t> euclidean_pos = Geometry3D::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), false);
- Vector<uint32_t> euclidean_neg = Geometry3D::generate_edf(solid, Vector3i(grid_size, grid_size, grid_size), true);
- Vector<int8_t> sdf8 = Geometry3D::generate_sdf8(euclidean_pos, euclidean_neg);
/*****************************/
/*** CREATE GPU STRUCTURES ***/
@@ -512,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);
@@ -543,7 +529,7 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
tf.width = grid_size;
tf.height = grid_size;
tf.depth = grid_size;
- tf.type = RD::TEXTURE_TYPE_3D;
+ tf.texture_type = RD::TEXTURE_TYPE_3D;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
Vector<Vector<uint8_t>> texdata;
@@ -552,10 +538,6 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i
tf.format = RD::DATA_FORMAT_R32G32_UINT;
texdata.write[0] = grid_indices.to_byte_array();
grid_texture = rd->texture_create(tf, RD::TextureView(), texdata);
- //sdf
- tf.format = RD::DATA_FORMAT_R8_SNORM;
- texdata.write[0] = sdf8.to_byte_array();
- grid_texture_sdf = rd->texture_create(tf, RD::TextureView(), texdata);
}
}
@@ -629,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);
@@ -695,7 +732,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
tf.width = atlas_size.width;
tf.height = atlas_size.height;
tf.array_layers = atlas_slices;
- tf.type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT;
tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
@@ -736,7 +773,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
panorama_tex = p_environment_panorama;
panorama_tex->convert(Image::FORMAT_RGBAF);
} else {
- panorama_tex.instance();
+ panorama_tex.instantiate();
panorama_tex->create(8, 8, false, Image::FORMAT_RGBAF);
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
@@ -756,8 +793,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
light_environment_tex = rd->texture_create(tfp, RD::TextureView(), tdata);
#ifdef DEBUG_TEXTURES
- panorama_tex->convert(Image::FORMAT_RGB8);
- panorama_tex->save_png("res://0_panorama.png");
+ panorama_tex->save_exr("res://0_panorama.exr", false);
#endif
}
}
@@ -767,11 +803,9 @@ 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;
- RID grid_texture_sdf;
RID seams_buffer;
RID probe_positions_buffer;
@@ -780,15 +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(grid_texture_sdf); \
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, grid_texture_sdf, 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);
@@ -796,7 +828,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
//shaders
Ref<RDShaderFile> raster_shader;
- raster_shader.instance();
+ raster_shader.instantiate();
Error err = raster_shader->parse_versions_from_text(lm_raster_shader_glsl);
if (err != OK) {
raster_shader->print_errors("raster_shader");
@@ -808,7 +840,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
}
ERR_FAIL_COND_V(err != OK, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
- RID rasterize_shader = rd->shader_create_from_bytecode(raster_shader->get_bytecode());
+ RID rasterize_shader = rd->shader_create_from_spirv(raster_shader->get_spirv_stages());
ERR_FAIL_COND_V(rasterize_shader.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //this is a bug check, though, should not happen
@@ -826,85 +858,71 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 1;
u.ids.push_back(vertex_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 2;
u.ids.push_back(triangle_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ 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.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.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 5;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 4;
u.ids.push_back(lights_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 6;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 5;
u.ids.push_back(seams_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 7;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 6;
u.ids.push_back(probe_positions_buffer);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 8;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 7;
u.ids.push_back(grid_texture);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 9;
- u.ids.push_back(grid_texture_sdf);
- base_uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 10;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 8;
u.ids.push_back(albedo_array_tex);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 11;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 9;
u.ids.push_back(emission_array_tex);
base_uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 12;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 10;
u.ids.push_back(sampler);
base_uniforms.push_back(u);
}
@@ -917,7 +935,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
tf.width = atlas_size.width;
tf.height = atlas_size.height;
tf.depth = 1;
- tf.type = RD::TEXTURE_TYPE_2D;
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
tf.format = RD::DATA_FORMAT_D32_SFLOAT;
@@ -936,15 +954,13 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
for (int i = 0; i < atlas_slices; i++) {
Vector<uint8_t> s = rd->texture_get_data(position_tex, i);
Ref<Image> img;
- img.instance();
+ img.instantiate();
img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAF, s);
- img->convert(Image::FORMAT_RGBA8);
- img->save_png("res://1_position_" + itos(i) + ".png");
+ img->save_exr("res://1_position_" + itos(i) + ".exr", false);
s = rd->texture_get_data(normal_tex, i);
img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
- img->convert(Image::FORMAT_RGBA8);
- img->save_png("res://1_normal_" + itos(i) + ".png");
+ img->save_exr("res://1_normal_" + itos(i) + ".exr", false);
}
#endif
@@ -956,7 +972,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
/* Plot direct light */
Ref<RDShaderFile> compute_shader;
- compute_shader.instance();
+ compute_shader.instantiate();
err = compute_shader->parse_versions_from_text(lm_compute_shader_glsl, p_bake_sh ? "\n#define USE_SH_LIGHTMAPS\n" : "");
if (err != OK) {
FREE_TEXTURES
@@ -967,28 +983,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
}
ERR_FAIL_COND_V(err != OK, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
- //unoccluder
- RID compute_shader_unocclude = rd->shader_create_from_bytecode(compute_shader->get_bytecode("unocclude"));
+ // Unoccluder
+ RID compute_shader_unocclude = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("unocclude"));
ERR_FAIL_COND_V(compute_shader_unocclude.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); // internal check, should not happen
RID compute_shader_unocclude_pipeline = rd->compute_pipeline_create(compute_shader_unocclude);
- //direct light
- RID compute_shader_primary = rd->shader_create_from_bytecode(compute_shader->get_bytecode("primary"));
+ // Direct light
+ RID compute_shader_primary = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("primary"));
ERR_FAIL_COND_V(compute_shader_primary.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); // internal check, should not happen
RID compute_shader_primary_pipeline = rd->compute_pipeline_create(compute_shader_primary);
- //indirect light
- RID compute_shader_secondary = rd->shader_create_from_bytecode(compute_shader->get_bytecode("secondary"));
+ // Indirect light
+ RID compute_shader_secondary = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("secondary"));
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_bytecode(compute_shader->get_bytecode("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);
-
- //dilate
- RID compute_shader_light_probes = rd->shader_create_from_bytecode(compute_shader->get_bytecode("light_probes"));
+ // 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
RID compute_shader_light_probes_pipeline = rd->compute_pipeline_create(compute_shader_light_probes);
@@ -998,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;
@@ -1049,14 +1059,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 0;
u.ids.push_back(position_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 1;
u.ids.push_back(unocclude_tex); //will be unused
uniforms.push_back(u);
@@ -1089,42 +1099,42 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 0;
u.ids.push_back(light_source_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1;
u.ids.push_back(light_dest_tex); //will be unused
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 2;
u.ids.push_back(position_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 3;
u.ids.push_back(normal_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 4;
u.ids.push_back(light_accum_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 5;
u.ids.push_back(light_primary_dynamic_tex);
uniforms.push_back(u);
@@ -1152,10 +1162,9 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
for (int i = 0; i < atlas_slices; i++) {
Vector<uint8_t> s = rd->texture_get_data(light_source_tex, i);
Ref<Image> img;
- img.instance();
+ img.instantiate();
img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
- img->convert(Image::FORMAT_RGBA8);
- img->save_png("res://2_light_primary_" + itos(i) + ".png");
+ img->save_exr("res://2_light_primary_" + itos(i) + ".exr", false);
}
#endif
@@ -1169,51 +1178,51 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 0;
u.ids.push_back(light_dest_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1;
u.ids.push_back(light_source_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 2;
u.ids.push_back(position_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 3;
u.ids.push_back(normal_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 4;
u.ids.push_back(light_accum_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
u.binding = 5;
u.ids.push_back(unocclude_tex); //reuse unocclude tex
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 6;
- u.ids.push_back(light_environment_tex); //reuse unocclude tex
+ u.ids.push_back(light_environment_tex);
uniforms.push_back(u);
}
}
@@ -1226,23 +1235,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
switch (p_quality) {
case BAKE_QUALITY_LOW: {
- push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/low_quality_ray_count");
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/low_quality_ray_count");
} break;
case BAKE_QUALITY_MEDIUM: {
- push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/medium_quality_ray_count");
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/medium_quality_ray_count");
} break;
case BAKE_QUALITY_HIGH: {
- push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/high_quality_ray_count");
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/high_quality_ray_count");
} break;
case BAKE_QUALITY_ULTRA: {
- push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/ultra_quality_ray_count");
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/ultra_quality_ray_count");
} break;
}
push_constant.ray_count = CLAMP(push_constant.ray_count, 16, 8192);
- int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/gpu_lightmapper/performance/region_size")));
- int max_rays = GLOBAL_GET("rendering/gpu_lightmapper/performance/max_rays_per_pass");
+ int max_region_size = nearest_power_of_2_templated(int(GLOBAL_GET("rendering/lightmapping/bake_performance/region_size")));
+ int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_pass");
int x_regions = (atlas_size.width - 1) / max_region_size + 1;
int y_regions = (atlas_size.height - 1) / max_region_size + 1;
@@ -1299,10 +1308,18 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
}
}
}
+
+ if (b == 0) {
+ // This disables the environment for subsequent bounces
+ push_constant.environment_xform[3] = -99.0f;
+ }
}
+
+ // Restore the correct environment transform
+ push_constant.environment_xform[3] = 0.0f;
}
- /* LIGHPROBES */
+ /* LIGHTPROBES */
RID light_probe_buffer;
@@ -1317,28 +1334,28 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 0;
u.ids.push_back(light_probe_buffer);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 1;
u.ids.push_back(light_dest_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 2;
u.ids.push_back(light_primary_dynamic_tex);
uniforms.push_back(u);
}
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 3;
u.ids.push_back(light_environment_tex);
uniforms.push_back(u);
@@ -1348,23 +1365,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
switch (p_quality) {
case BAKE_QUALITY_LOW: {
- push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/low_quality_probe_ray_count");
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/low_quality_probe_ray_count");
} break;
case BAKE_QUALITY_MEDIUM: {
- push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/medium_quality_probe_ray_count");
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/medium_quality_probe_ray_count");
} break;
case BAKE_QUALITY_HIGH: {
- push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/high_quality_probe_ray_count");
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/high_quality_probe_ray_count");
} break;
case BAKE_QUALITY_ULTRA: {
- push_constant.ray_count = GLOBAL_GET("rendering/gpu_lightmapper/quality/ultra_quality_probe_ray_count");
+ push_constant.ray_count = GLOBAL_GET("rendering/lightmapping/bake_quality/ultra_quality_probe_ray_count");
} break;
}
push_constant.atlas_size[0] = probe_positions.size();
push_constant.ray_count = CLAMP(push_constant.ray_count, 16, 8192);
- int max_rays = GLOBAL_GET("rendering/gpu_lightmapper/performance/max_rays_per_probe_pass");
+ int max_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_rays_per_probe_pass");
int ray_iterations = (push_constant.ray_count - 1) / max_rays + 1;
for (int i = 0; i < ray_iterations; i++) {
@@ -1395,12 +1412,12 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
#if 0
for (int i = 0; i < probe_positions.size(); i++) {
Ref<Image> img;
- img.instance();
+ img.instantiate();
img->create(6, 4, false, Image::FORMAT_RGB8);
for (int j = 0; j < 6; j++) {
Vector<uint8_t> s = rd->texture_get_data(lightprobe_tex, i * 6 + j);
Ref<Image> img2;
- img2.instance();
+ img2.instantiate();
img2->create(2, 2, false, Image::FORMAT_RGBAF, s);
img2->convert(Image::FORMAT_RGB8);
img->blit_rect(img2, Rect2(0, 0, 2, 2), Point2((j % 3) * 2, (j / 3) * 2));
@@ -1409,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) {
@@ -1421,7 +1446,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
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.instance();
+ img.instantiate();
img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
Ref<Image> denoised = denoiser->denoise_image(img);
@@ -1437,63 +1462,18 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
dst[j + 3] = src[j + 3];
}
}
- rd->texture_update(light_accum_tex, i, ds, true);
+ rd->texture_update(light_accum_tex, i, ds);
}
}
}
- }
-
-#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.instance();
- img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
- img->convert(Image::FORMAT_RGBA8);
- img->save_png("res://4_light_secondary_" + itos(i) + ".png");
- }
-#endif
-
- /* DILATE LIGHTMAP */
- {
- SWAP(light_accum_tex, light_accum_tex2);
-
- Vector<RD::Uniform> uniforms;
{
- {
- RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 0;
- u.ids.push_back(light_accum_tex);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.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
@@ -1501,17 +1481,16 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
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.instance();
+ 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
/* BLEND SEAMS */
//shaders
Ref<RDShaderFile> blendseams_shader;
- blendseams_shader.instance();
+ blendseams_shader.instantiate();
err = blendseams_shader->parse_versions_from_text(lm_blendseams_shader_glsl);
if (err != OK) {
FREE_TEXTURES
@@ -1523,11 +1502,11 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
}
ERR_FAIL_COND_V(err != OK, BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
- RID blendseams_line_raster_shader = rd->shader_create_from_bytecode(blendseams_shader->get_bytecode("lines"));
+ RID blendseams_line_raster_shader = rd->shader_create_from_spirv(blendseams_shader->get_spirv_stages("lines"));
ERR_FAIL_COND_V(blendseams_line_raster_shader.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
- RID blendseams_triangle_raster_shader = rd->shader_create_from_bytecode(blendseams_shader->get_bytecode("triangles"));
+ RID blendseams_triangle_raster_shader = rd->shader_create_from_spirv(blendseams_shader->get_spirv_stages("triangles"));
ERR_FAIL_COND_V(blendseams_triangle_raster_shader.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
@@ -1538,7 +1517,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
//pre copy
for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
- rd->texture_copy(light_accum_tex, light_accum_tex2, Vector3(), Vector3(), Vector3(atlas_size.width, atlas_size.height, 1), 0, 0, i, i, true);
+ rd->texture_copy(light_accum_tex, light_accum_tex2, Vector3(), Vector3(), Vector3(atlas_size.width, atlas_size.height, 1), 0, 0, i, i);
}
Vector<RID> framebuffers;
@@ -1554,7 +1533,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
{
{
RD::Uniform u;
- u.type = RD::UNIFORM_TYPE_TEXTURE;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 0;
u.ids.push_back(light_accum_tex2);
uniforms.push_back(u);
@@ -1583,6 +1562,11 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
clear_colors.push_back(Color(0, 0, 0, 1));
for (int i = 0; i < atlas_slices; i++) {
int subslices = (p_bake_sh ? 4 : 1);
+
+ if (slice_seam_count[i] == 0) {
+ continue;
+ }
+
for (int k = 0; k < subslices; k++) {
RasterSeamsPushConstant seams_push_constant;
seams_push_constant.slice = uint32_t(i * subslices + k);
@@ -1653,10 +1637,9 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
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.instance();
+ 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_blendseams" + itos(i) + ".png");
+ img->save_exr("res://5_blendseams" + itos(i) + ".exr", false);
}
#endif
if (p_step_function) {
@@ -1666,7 +1649,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
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.instance();
+ img.instantiate();
img->create(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
img->convert(Image::FORMAT_RGBH); //remove alpha
bake_textures.push_back(img);
@@ -1675,15 +1658,15 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
if (probe_positions.size() > 0) {
probe_values.resize(probe_positions.size() * 9);
Vector<uint8_t> probe_data = rd->buffer_get_data(light_probe_buffer);
- copymem(probe_values.ptrw(), probe_data.ptr(), probe_data.size());
+ memcpy(probe_values.ptrw(), probe_data.ptr(), probe_data.size());
rd->free(light_probe_buffer);
#ifdef DEBUG_TEXTURES
{
Ref<Image> img2;
- img2.instance();
+ img2.instantiate();
img2->create(probe_values.size(), 1, false, Image::FORMAT_RGBAF, probe_data);
- img2->save_png("res://6_lightprobes.png");
+ img2->save_exr("res://6_lightprobes.exr", false);
}
#endif
}
@@ -1744,7 +1727,7 @@ Vector<Color> LightmapperRD::get_bake_probe_sh(int p_probe) const {
ERR_FAIL_INDEX_V(p_probe, probe_positions.size(), Vector<Color>());
Vector<Color> ret;
ret.resize(9);
- copymem(ret.ptrw(), &probe_values[p_probe * 9], sizeof(Color) * 9);
+ memcpy(ret.ptrw(), &probe_values[p_probe * 9], sizeof(Color) * 9);
return ret;
}
diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h
index cd000414cf..51ab60fc29 100644
--- a/modules/lightmapper_rd/lightmapper_rd.h
+++ b/modules/lightmapper_rd/lightmapper_rd.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,11 +31,12 @@
#ifndef LIGHTMAPPER_RD_H
#define LIGHTMAPPER_RD_H
-#include "core/local_vector.h"
+#include "core/templates/local_vector.h"
#include "scene/3d/lightmapper.h"
#include "scene/resources/mesh.h"
#include "servers/rendering/rendering_device.h"
+class RDShaderFile;
class LightmapperRD : public Lightmapper {
GDCLASS(LightmapperRD, Lightmapper)
@@ -46,18 +47,18 @@ class LightmapperRD : public Lightmapper {
};
struct Light {
- float position[3];
+ float position[3] = {};
uint32_t type = LIGHT_TYPE_DIRECTIONAL;
- float direction[3];
- float energy;
- float color[3];
- float size;
- float range;
- float attenuation;
- float spot_angle;
- float spot_attenuation;
- uint32_t static_bake;
- uint32_t pad[3];
+ float direction[3] = {};
+ float energy = 0.0;
+ float color[3] = {};
+ float size = 0.0;
+ float range = 0.0;
+ float attenuation = 0.0;
+ float cos_spot_angle = 0.0;
+ float inv_spot_attenuation = 0.0;
+ uint32_t static_bake = 0;
+ uint32_t pad[3] = {};
bool operator<(const Light &p_light) const {
return type < p_light.type;
@@ -65,20 +66,20 @@ class LightmapperRD : public Lightmapper {
};
struct Vertex {
- float position[3];
- float normal_z;
- float uv[2];
- float normal_xy[2];
+ float position[3] = {};
+ float normal_z = 0.0;
+ float uv[2] = {};
+ float normal_xy[2] = {};
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);
}
};
@@ -102,7 +103,7 @@ class LightmapperRD : public Lightmapper {
};
struct Probe {
- float position[4];
+ float position[4] = {};
};
Vector<Probe> probe_positions;
@@ -157,16 +158,13 @@ class LightmapperRD : public Lightmapper {
}
};
- struct Box {
- float min_bounds[3];
- float pad0;
- float max_bounds[3];
- float pad1;
- };
-
struct Triangle {
- uint32_t indices[3];
- uint32_t slice;
+ uint32_t indices[3] = {};
+ uint32_t slice = 0;
+ float min_bounds[3] = {};
+ float pad0 = 0.0;
+ float max_bounds[3] = {};
+ float pad1 = 0.0;
bool operator<(const Triangle &p_triangle) const {
return slice < p_triangle.slice;
}
@@ -177,8 +175,8 @@ class LightmapperRD : public Lightmapper {
Vector<Light> lights;
struct TriangleSort {
- uint32_t cell_index;
- uint32_t triangle_index;
+ uint32_t cell_index = 0;
+ uint32_t triangle_index = 0;
bool operator<(const TriangleSort &p_triangle_sort) const {
return cell_index < p_triangle_sort.cell_index; //sorting by triangle index in this case makes no sense
}
@@ -187,53 +185,55 @@ class LightmapperRD : public Lightmapper {
void _plot_triangle_into_triangle_index_list(int p_size, const Vector3i &p_ofs, const AABB &p_bounds, const Vector3 p_points[], uint32_t p_triangle_index, LocalVector<TriangleSort> &triangles, uint32_t p_grid_size);
struct RasterPushConstant {
- float atlas_size[2];
- float uv_offset[2];
- float to_cell_size[3];
- uint32_t base_triangle;
- float to_cell_offset[3];
- float bias;
- int32_t grid_size[3];
- uint32_t pad2;
+ float atlas_size[2] = {};
+ float uv_offset[2] = {};
+ float to_cell_size[3] = {};
+ uint32_t base_triangle = 0;
+ float to_cell_offset[3] = {};
+ float bias = 0.0;
+ int32_t grid_size[3] = {};
+ uint32_t pad2 = 0;
};
struct RasterSeamsPushConstant {
- uint32_t base_index;
- uint32_t slice;
- float uv_offset[2];
- uint32_t debug;
- float blend;
- uint32_t pad[2];
+ uint32_t base_index = 0;
+ uint32_t slice = 0;
+ float uv_offset[2] = {};
+ uint32_t debug = 0;
+ float blend = 0.0;
+ uint32_t pad[2] = {};
};
struct PushConstant {
- int32_t atlas_size[2];
- uint32_t ray_count;
- uint32_t ray_to;
+ int32_t atlas_size[2] = {};
+ uint32_t ray_count = 0;
+ uint32_t ray_to = 0;
- float world_size[3];
- float bias;
+ float world_size[3] = {};
+ float bias = 0.0;
- float to_cell_offset[3];
- uint32_t ray_from;
+ float to_cell_offset[3] = {};
+ uint32_t ray_from = 0;
- float to_cell_size[3];
- uint32_t light_count;
+ float to_cell_size[3] = {};
+ uint32_t light_count = 0;
- int32_t grid_size;
- int32_t atlas_slice;
- int32_t region_ofs[2];
+ int32_t grid_size = 0;
+ int32_t atlas_slice = 0;
+ int32_t region_ofs[2] = {};
- float environment_xform[12];
+ float environment_xform[12] = {};
};
Vector<Ref<Image>> bake_textures;
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 &grid_texture_sdf, 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_blendseams.glsl b/modules/lightmapper_rd/lm_blendseams.glsl
index e47e5fcc51..374c48082e 100644
--- a/modules/lightmapper_rd/lm_blendseams.glsl
+++ b/modules/lightmapper_rd/lm_blendseams.glsl
@@ -7,7 +7,7 @@ triangles = "#define MODE_TRIANGLES";
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#include "lm_common_inc.glsl"
@@ -74,7 +74,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#include "lm_common_inc.glsl"
diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl
index 0ff455936e..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;
@@ -56,14 +48,14 @@ struct Light {
float range;
float attenuation;
- float spot_angle;
- float spot_attenuation;
+ float cos_spot_angle;
+ float inv_spot_attenuation;
bool static_bake;
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,20 +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 = 9) uniform texture3D grid_sdf;
+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 = 10) uniform texture2DArray albedo_tex;
-layout(set = 0, binding = 11) uniform texture2DArray emission_tex;
+layout(set = 0, binding = 10) uniform sampler linear_sampler;
-layout(set = 0, binding = 12) 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 56976bd623..158cd960c4 100644
--- a/modules/lightmapper_rd/lm_compute.glsl
+++ b/modules/lightmapper_rd/lm_compute.glsl
@@ -10,7 +10,7 @@ light_probes = "#define MODE_LIGHT_PROBES";
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
// One 2D local group focusing in one layer at a time, though all
// in parallel (no barriers) makes more sense than a 3D local group
@@ -94,21 +94,34 @@ 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 triangleNormal = cross(e1, e0);
+ vec3 triangle_normal = cross(e1, e0);
- const vec3 e2 = (1.0 / dot(triangleNormal, dir)) * (p0 - from);
+ float n_dot_dir = dot(triangle_normal, dir);
+
+ if (abs(n_dot_dir) < EPSILON) {
+ return false;
+ }
+
+ const vec3 e2 = (p0 - from) / n_dot_dir;
const vec3 i = cross(dir, e2);
r_barycentric.y = dot(i, e1);
r_barycentric.z = dot(i, e0);
r_barycentric.x = 1.0 - (r_barycentric.z + r_barycentric.y);
- r_distance = dot(triangleNormal, e2);
+ r_distance = dot(triangle_normal, e2);
+
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
@@ -118,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;
@@ -135,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;
@@ -143,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
}
@@ -231,7 +232,7 @@ bool trace_ray(vec3 p_from, vec3 p_to
iters++;
}
- return false;
+ return RAY_MISS;
}
const float PI = 3.14159265f;
@@ -249,6 +250,15 @@ float quick_hash(vec2 pos) {
return fract(sin(dot(pos * 19.19, vec2(49.5791, 97.413))) * 49831.189237);
}
+float get_omni_attenuation(float distance, float inv_range, float decay) {
+ float nd = distance * inv_range;
+ nd *= nd;
+ nd *= nd; // nd^4
+ nd = max(1.0 - nd, 0.0);
+ nd *= nd; // nd^2
+ return nd * pow(max(distance, 0.0001), -decay);
+}
+
void main() {
#ifdef MODE_LIGHT_PROBES
int probe_index = int(gl_GlobalInvocationID.x);
@@ -298,19 +308,20 @@ void main() {
continue;
}
- d /= lights.data[i].range;
-
- attenuation = pow(max(1.0 - d, 0.0), lights.data[i].attenuation);
+ attenuation = get_omni_attenuation(d, 1.0 / lights.data[i].range, lights.data[i].attenuation);
if (lights.data[i].type == LIGHT_TYPE_SPOT) {
vec3 rel = normalize(position - light_pos);
- float angle = acos(dot(rel, lights.data[i].direction));
- if (angle > lights.data[i].spot_angle) {
+ float cos_spot_angle = lights.data[i].cos_spot_angle;
+ float cos_angle = dot(rel, lights.data[i].direction);
+
+ if (cos_angle < cos_spot_angle) {
continue; //invisible, dont try
}
- float d = clamp(angle / lights.data[i].spot_angle, 0, 1);
- attenuation *= pow(1.0 - d, lights.data[i].spot_attenuation);
+ float scos = max(cos_angle, cos_spot_angle);
+ float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - cos_spot_angle));
+ attenuation *= 1.0 - pow(spot_rim, lights.data[i].inv_spot_attenuation);
}
}
@@ -321,7 +332,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;
@@ -392,14 +403,16 @@ void main() {
vec4(0.0, 0.0, 0.0, 1.0));
#endif
vec3 light_average = vec3(0.0);
+ float active_rays = 0.0;
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)));
uint tidx;
vec3 barycentric;
- vec3 light;
- if (trace_ray(position + ray_dir * params.bias, position + ray_dir * length(params.world_size), tidx, barycentric)) {
+ vec3 light = vec3(0.0);
+ 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;
@@ -407,20 +420,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 {
- //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;
@@ -444,7 +461,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;
@@ -459,7 +478,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));
@@ -467,7 +488,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
@@ -500,7 +521,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;
@@ -540,7 +561,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;
@@ -548,7 +570,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 6c2904192b..a86968a4f3 100644
--- a/modules/lightmapper_rd/lm_raster.glsl
+++ b/modules/lightmapper_rd/lm_raster.glsl
@@ -2,7 +2,7 @@
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#include "lm_common_inc.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);
}
@@ -56,7 +65,7 @@ void main() {
#version 450
-VERSION_DEFINES
+#VERSION_DEFINES
#include "lm_common_inc.glsl"
@@ -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/lightmapper_rd/register_types.cpp b/modules/lightmapper_rd/register_types.cpp
index 0e6d7590cc..ae9c5fc390 100644
--- a/modules/lightmapper_rd/register_types.cpp
+++ b/modules/lightmapper_rd/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "lightmapper_rd.h"
#include "scene/3d/lightmapper.h"
@@ -41,20 +41,20 @@ static Lightmapper *create_lightmapper_rd() {
#endif
void register_lightmapper_rd_types() {
- GLOBAL_DEF("rendering/gpu_lightmapper/quality/low_quality_ray_count", 16);
- GLOBAL_DEF("rendering/gpu_lightmapper/quality/medium_quality_ray_count", 64);
- GLOBAL_DEF("rendering/gpu_lightmapper/quality/high_quality_ray_count", 256);
- GLOBAL_DEF("rendering/gpu_lightmapper/quality/ultra_quality_ray_count", 1024);
- GLOBAL_DEF("rendering/gpu_lightmapper/performance/max_rays_per_pass", 32);
- GLOBAL_DEF("rendering/gpu_lightmapper/performance/region_size", 512);
+ GLOBAL_DEF("rendering/lightmapping/bake_quality/low_quality_ray_count", 16);
+ GLOBAL_DEF("rendering/lightmapping/bake_quality/medium_quality_ray_count", 64);
+ GLOBAL_DEF("rendering/lightmapping/bake_quality/high_quality_ray_count", 256);
+ GLOBAL_DEF("rendering/lightmapping/bake_quality/ultra_quality_ray_count", 1024);
+ GLOBAL_DEF("rendering/lightmapping/bake_performance/max_rays_per_pass", 32);
+ GLOBAL_DEF("rendering/lightmapping/bake_performance/region_size", 512);
- GLOBAL_DEF("rendering/gpu_lightmapper/quality/low_quality_probe_ray_count", 64);
- GLOBAL_DEF("rendering/gpu_lightmapper/quality/medium_quality_probe_ray_count", 256);
- GLOBAL_DEF("rendering/gpu_lightmapper/quality/high_quality_probe_ray_count", 512);
- GLOBAL_DEF("rendering/gpu_lightmapper/quality/ultra_quality_probe_ray_count", 2048);
- GLOBAL_DEF("rendering/gpu_lightmapper/performance/max_rays_per_probe_pass", 64);
+ GLOBAL_DEF("rendering/lightmapping/bake_quality/low_quality_probe_ray_count", 64);
+ GLOBAL_DEF("rendering/lightmapping/bake_quality/medium_quality_probe_ray_count", 256);
+ GLOBAL_DEF("rendering/lightmapping/bake_quality/high_quality_probe_ray_count", 512);
+ GLOBAL_DEF("rendering/lightmapping/bake_quality/ultra_quality_probe_ray_count", 2048);
+ GLOBAL_DEF("rendering/lightmapping/bake_performance/max_rays_per_probe_pass", 64);
#ifndef _3D_DISABLED
- ClassDB::register_class<LightmapperRD>();
+ GDREGISTER_CLASS(LightmapperRD);
Lightmapper::create_gpu = create_lightmapper_rd;
#endif
}
diff --git a/modules/lightmapper_rd/register_types.h b/modules/lightmapper_rd/register_types.h
index b0e15a927f..622d6e37a7 100644
--- a/modules/lightmapper_rd/register_types.h
+++ b/modules/lightmapper_rd/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mbedtls/SCsub b/modules/mbedtls/SCsub
index 5f5d25a3ee..4fcbe8fb43 100755..100644
--- a/modules/mbedtls/SCsub
+++ b/modules/mbedtls/SCsub
@@ -5,8 +5,11 @@ Import("env_modules")
env_mbed_tls = env_modules.Clone()
+# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_mbedtls"]:
- # Thirdparty source files
thirdparty_sources = [
"aes.c",
"aesni.c",
@@ -96,7 +99,21 @@ if env["builtin_mbedtls"]:
env_thirdparty = env_mbed_tls.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
+
+module_obj = []
+
+env_mbed_tls.add_source_files(module_obj, "*.cpp")
+
+if env["tests"]:
+ env_mbed_tls.Append(CPPDEFINES=["TESTS_ENABLED"])
+ env_mbed_tls.add_source_files(module_obj, "./tests/*.cpp")
+
+env.modules_sources += module_obj
-# Module sources
-env_mbed_tls.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/mbedtls/config.py b/modules/mbedtls/config.py
index d22f9454ed..d22f9454ed 100755..100644
--- a/modules/mbedtls/config.py
+++ b/modules/mbedtls/config.py
diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp
index 12a982df6e..2522f1bb11 100644
--- a/modules/mbedtls/crypto_mbedtls.cpp
+++ b/modules/mbedtls/crypto_mbedtls.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,12 +30,12 @@
#include "crypto_mbedtls.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
#include "core/io/certs_compressed.gen.h"
#include "core/io/compression.h"
-#include "core/project_settings.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
@@ -44,6 +44,7 @@
#define PEM_END_CRT "-----END CERTIFICATE-----\n"
#include <mbedtls/debug.h>
+#include <mbedtls/md.h>
#include <mbedtls/pem.h>
CryptoKey *CryptoKeyMbedTLS::create() {
@@ -57,7 +58,7 @@ Error CryptoKeyMbedTLS::load(String p_path, bool p_public_only) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(!f, ERR_INVALID_PARAMETER, "Cannot open CryptoKeyMbedTLS file '" + p_path + "'.");
- int flen = f->get_len();
+ uint64_t flen = f->get_length();
out.resize(flen + 1);
f->get_buffer(out.ptrw(), flen);
out.write[flen] = 0; // string terminator
@@ -145,7 +146,7 @@ Error X509CertificateMbedTLS::load(String p_path) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(!f, ERR_INVALID_PARAMETER, "Cannot open X509CertificateMbedTLS file '" + p_path + "'.");
- int flen = f->get_len();
+ uint64_t flen = f->get_length();
out.resize(flen + 1);
f->get_buffer(out.ptrw(), flen);
out.write[flen] = 0; // string terminator
@@ -186,6 +187,75 @@ Error X509CertificateMbedTLS::save(String p_path) {
return OK;
}
+bool HMACContextMbedTLS::is_md_type_allowed(mbedtls_md_type_t p_md_type) {
+ switch (p_md_type) {
+ case MBEDTLS_MD_SHA1:
+ case MBEDTLS_MD_SHA256:
+ return true;
+ default:
+ return false;
+ }
+}
+
+HMACContext *HMACContextMbedTLS::create() {
+ return memnew(HMACContextMbedTLS);
+}
+
+Error HMACContextMbedTLS::start(HashingContext::HashType p_hash_type, PackedByteArray p_key) {
+ ERR_FAIL_COND_V_MSG(ctx != nullptr, ERR_FILE_ALREADY_IN_USE, "HMACContext already started.");
+
+ // HMAC keys can be any size.
+ ERR_FAIL_COND_V_MSG(p_key.is_empty(), ERR_INVALID_PARAMETER, "Key must not be empty.");
+
+ hash_type = p_hash_type;
+ mbedtls_md_type_t ht = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, hash_len);
+
+ bool allowed = HMACContextMbedTLS::is_md_type_allowed(ht);
+ ERR_FAIL_COND_V_MSG(!allowed, ERR_INVALID_PARAMETER, "Unsupported hash type.");
+
+ ctx = memalloc(sizeof(mbedtls_md_context_t));
+ mbedtls_md_init((mbedtls_md_context_t *)ctx);
+
+ mbedtls_md_setup((mbedtls_md_context_t *)ctx, mbedtls_md_info_from_type((mbedtls_md_type_t)ht), 1);
+ int ret = mbedtls_md_hmac_starts((mbedtls_md_context_t *)ctx, (const uint8_t *)p_key.ptr(), (size_t)p_key.size());
+ return ret ? FAILED : OK;
+}
+
+Error HMACContextMbedTLS::update(PackedByteArray p_data) {
+ ERR_FAIL_COND_V_MSG(ctx == nullptr, ERR_INVALID_DATA, "Start must be called before update.");
+
+ ERR_FAIL_COND_V_MSG(p_data.is_empty(), ERR_INVALID_PARAMETER, "Src must not be empty.");
+
+ int ret = mbedtls_md_hmac_update((mbedtls_md_context_t *)ctx, (const uint8_t *)p_data.ptr(), (size_t)p_data.size());
+ return ret ? FAILED : OK;
+}
+
+PackedByteArray HMACContextMbedTLS::finish() {
+ ERR_FAIL_COND_V_MSG(ctx == nullptr, PackedByteArray(), "Start must be called before finish.");
+ ERR_FAIL_COND_V_MSG(hash_len == 0, PackedByteArray(), "Unsupported hash type.");
+
+ PackedByteArray out;
+ out.resize(hash_len);
+
+ unsigned char *out_ptr = (unsigned char *)out.ptrw();
+ int ret = mbedtls_md_hmac_finish((mbedtls_md_context_t *)ctx, out_ptr);
+
+ mbedtls_md_free((mbedtls_md_context_t *)ctx);
+ memfree((mbedtls_md_context_t *)ctx);
+ ctx = nullptr;
+ hash_len = 0;
+
+ ERR_FAIL_COND_V_MSG(ret, PackedByteArray(), "Error received while finishing HMAC");
+ return out;
+}
+
+HMACContextMbedTLS::~HMACContextMbedTLS() {
+ if (ctx != nullptr) {
+ mbedtls_md_free((mbedtls_md_context_t *)ctx);
+ memfree((mbedtls_md_context_t *)ctx);
+ }
+}
+
Crypto *CryptoMbedTLS::create() {
return memnew(CryptoMbedTLS);
}
@@ -199,6 +269,7 @@ void CryptoMbedTLS::initialize_crypto() {
Crypto::_load_default_certificates = load_default_certificates;
X509CertificateMbedTLS::make_default();
CryptoKeyMbedTLS::make_default();
+ HMACContextMbedTLS::make_default();
}
void CryptoMbedTLS::finalize_crypto() {
@@ -210,6 +281,7 @@ void CryptoMbedTLS::finalize_crypto() {
}
X509CertificateMbedTLS::finalize();
CryptoKeyMbedTLS::finalize();
+ HMACContextMbedTLS::finalize();
}
CryptoMbedTLS::CryptoMbedTLS() {
@@ -259,7 +331,7 @@ void CryptoMbedTLS::load_default_certificates(String p_path) {
Ref<CryptoKey> CryptoMbedTLS::generate_rsa(int p_bytes) {
Ref<CryptoKeyMbedTLS> out;
- out.instance();
+ out.instantiate();
int ret = mbedtls_pk_setup(&(out->pkey), mbedtls_pk_info_from_type(MBEDTLS_PK_RSA));
ERR_FAIL_COND_V(ret != 0, nullptr);
ret = mbedtls_rsa_gen_key(mbedtls_pk_rsa(out->pkey), mbedtls_ctr_drbg_random, &ctr_drbg, p_bytes, 65537);
@@ -301,7 +373,7 @@ Ref<X509Certificate> CryptoMbedTLS::generate_self_signed_certificate(Ref<CryptoK
buf[4095] = '\0'; // Make sure strlen can't fail.
Ref<X509CertificateMbedTLS> out;
- out.instance();
+ out.instantiate();
out->load_from_memory(buf, strlen((char *)buf) + 1); // Use strlen to find correct output size.
return out;
}
@@ -313,7 +385,7 @@ PackedByteArray CryptoMbedTLS::generate_random_bytes(int p_bytes) {
return out;
}
-mbedtls_md_type_t CryptoMbedTLS::_md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size) {
+mbedtls_md_type_t CryptoMbedTLS::md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size) {
switch (p_hash_type) {
case HashingContext::HASH_MD5:
r_size = 16;
@@ -332,7 +404,7 @@ mbedtls_md_type_t CryptoMbedTLS::_md_type_from_hashtype(HashingContext::HashType
Vector<uint8_t> CryptoMbedTLS::sign(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Ref<CryptoKey> p_key) {
int size;
- mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size);
+ mbedtls_md_type_t type = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, size);
ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, Vector<uint8_t>(), "Invalid hash type.");
ERR_FAIL_COND_V_MSG(p_hash.size() != size, Vector<uint8_t>(), "Invalid hash provided. Size must be " + itos(size));
Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
@@ -344,13 +416,13 @@ Vector<uint8_t> CryptoMbedTLS::sign(HashingContext::HashType p_hash_type, Vector
int ret = mbedtls_pk_sign(&(key->pkey), type, p_hash.ptr(), size, buf, &sig_size, mbedtls_ctr_drbg_random, &ctr_drbg);
ERR_FAIL_COND_V_MSG(ret, out, "Error while signing: " + itos(ret));
out.resize(sig_size);
- copymem(out.ptrw(), buf, sig_size);
+ memcpy(out.ptrw(), buf, sig_size);
return out;
}
bool CryptoMbedTLS::verify(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Vector<uint8_t> p_signature, Ref<CryptoKey> p_key) {
int size;
- mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size);
+ mbedtls_md_type_t type = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, size);
ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, false, "Invalid hash type.");
ERR_FAIL_COND_V_MSG(p_hash.size() != size, false, "Invalid hash provided. Size must be " + itos(size));
Ref<CryptoKeyMbedTLS> key = static_cast<Ref<CryptoKeyMbedTLS>>(p_key);
@@ -367,7 +439,7 @@ Vector<uint8_t> CryptoMbedTLS::encrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_p
int ret = mbedtls_pk_encrypt(&(key->pkey), p_plaintext.ptr(), p_plaintext.size(), buf, &size, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg);
ERR_FAIL_COND_V_MSG(ret, out, "Error while encrypting: " + itos(ret));
out.resize(size);
- copymem(out.ptrw(), buf, size);
+ memcpy(out.ptrw(), buf, size);
return out;
}
@@ -381,6 +453,6 @@ Vector<uint8_t> CryptoMbedTLS::decrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_c
int ret = mbedtls_pk_decrypt(&(key->pkey), p_ciphertext.ptr(), p_ciphertext.size(), buf, &size, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg);
ERR_FAIL_COND_V_MSG(ret, out, "Error while decrypting: " + itos(ret));
out.resize(size);
- copymem(out.ptrw(), buf, size);
+ memcpy(out.ptrw(), buf, size);
return out;
}
diff --git a/modules/mbedtls/crypto_mbedtls.h b/modules/mbedtls/crypto_mbedtls.h
index 2a446f9d48..afa1ea7a64 100644
--- a/modules/mbedtls/crypto_mbedtls.h
+++ b/modules/mbedtls/crypto_mbedtls.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define CRYPTO_MBEDTLS_H
#include "core/crypto/crypto.h"
-#include "core/resource.h"
+#include "core/io/resource.h"
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
@@ -101,12 +101,32 @@ public:
friend class SSLContextMbedTLS;
};
+class HMACContextMbedTLS : public HMACContext {
+private:
+ HashingContext::HashType hash_type;
+ int hash_len = 0;
+ void *ctx = nullptr;
+
+public:
+ static HMACContext *create();
+ static void make_default() { HMACContext::_create = create; }
+ static void finalize() { HMACContext::_create = nullptr; }
+
+ static bool is_md_type_allowed(mbedtls_md_type_t p_md_type);
+
+ virtual Error start(HashingContext::HashType p_hash_type, PackedByteArray p_key);
+ virtual Error update(PackedByteArray p_data);
+ virtual PackedByteArray finish();
+
+ HMACContextMbedTLS() {}
+ ~HMACContextMbedTLS();
+};
+
class CryptoMbedTLS : public Crypto {
private:
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
static X509CertificateMbedTLS *default_certs;
- mbedtls_md_type_t _md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size);
public:
static Crypto *create();
@@ -114,6 +134,7 @@ public:
static void finalize_crypto();
static X509CertificateMbedTLS *get_default_certificates();
static void load_default_certificates(String p_path);
+ static mbedtls_md_type_t md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size);
virtual PackedByteArray generate_random_bytes(int p_bytes);
virtual Ref<CryptoKey> generate_rsa(int p_bytes);
diff --git a/modules/mbedtls/dtls_server_mbedtls.cpp b/modules/mbedtls/dtls_server_mbedtls.cpp
index d9961b026f..b1b6b3844b 100644
--- a/modules/mbedtls/dtls_server_mbedtls.cpp
+++ b/modules/mbedtls/dtls_server_mbedtls.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -45,7 +45,7 @@ void DTLSServerMbedTLS::stop() {
Ref<PacketPeerDTLS> DTLSServerMbedTLS::take_connection(Ref<PacketPeerUDP> p_udp_peer) {
Ref<PacketPeerMbedDTLS> out;
- out.instance();
+ out.instantiate();
ERR_FAIL_COND_V(!out.is_valid(), out);
ERR_FAIL_COND_V(!p_udp_peer.is_valid(), out);
@@ -68,7 +68,7 @@ void DTLSServerMbedTLS::finalize() {
}
DTLSServerMbedTLS::DTLSServerMbedTLS() {
- _cookies.instance();
+ _cookies.instantiate();
}
DTLSServerMbedTLS::~DTLSServerMbedTLS() {
diff --git a/modules/mbedtls/dtls_server_mbedtls.h b/modules/mbedtls/dtls_server_mbedtls.h
index d93553bf7f..9f0c9670e7 100644
--- a/modules/mbedtls/dtls_server_mbedtls.h
+++ b/modules/mbedtls/dtls_server_mbedtls.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mbedtls/packet_peer_mbed_dtls.cpp b/modules/mbedtls/packet_peer_mbed_dtls.cpp
index 8206d739ae..114bf49e9e 100644
--- a/modules/mbedtls/packet_peer_mbed_dtls.cpp
+++ b/modules/mbedtls/packet_peer_mbed_dtls.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#include "packet_peer_mbed_dtls.h"
#include "mbedtls/platform_util.h"
+#include "core/io/file_access.h"
#include "core/io/stream_peer_ssl.h"
-#include "core/os/file_access.h"
int PacketPeerMbedDTLS::bio_send(void *ctx, const unsigned char *buf, size_t len) {
if (buf == nullptr || len <= 0) {
@@ -74,7 +74,7 @@ int PacketPeerMbedDTLS::bio_recv(void *ctx, unsigned char *buf, size_t len) {
if (err != OK) {
return MBEDTLS_ERR_SSL_INTERNAL_ERROR;
}
- copymem(buf, buffer, buffer_size);
+ memcpy(buf, buffer, buffer_size);
return buffer_size;
}
@@ -87,10 +87,10 @@ void PacketPeerMbedDTLS::_cleanup() {
int PacketPeerMbedDTLS::_set_cookie() {
// Setup DTLS session cookie for this client
uint8_t client_id[18];
- IP_Address addr = base->get_packet_address();
+ IPAddress addr = base->get_packet_address();
uint16_t port = base->get_packet_port();
- copymem(client_id, addr.get_ipv6(), 16);
- copymem(&client_id[16], (uint8_t *)&port, 2);
+ memcpy(client_id, addr.get_ipv6(), 16);
+ memcpy(&client_id[16], (uint8_t *)&port, 2);
return mbedtls_ssl_set_client_transport_id(ssl_ctx->get_context(), client_id, 18);
}
@@ -245,8 +245,7 @@ int PacketPeerMbedDTLS::get_max_packet_size() const {
}
PacketPeerMbedDTLS::PacketPeerMbedDTLS() {
- ssl_ctx.instance();
- status = STATUS_DISCONNECTED;
+ ssl_ctx.instantiate();
}
PacketPeerMbedDTLS::~PacketPeerMbedDTLS() {
diff --git a/modules/mbedtls/packet_peer_mbed_dtls.h b/modules/mbedtls/packet_peer_mbed_dtls.h
index b958fa3b95..92e6ab88c4 100755..100644
--- a/modules/mbedtls/packet_peer_mbed_dtls.h
+++ b/modules/mbedtls/packet_peer_mbed_dtls.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,7 +44,7 @@ private:
uint8_t packet_buffer[PACKET_BUFFER_SIZE];
- Status status;
+ Status status = STATUS_DISCONNECTED;
String hostname;
Ref<PacketPeerUDP> base;
@@ -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/register_types.cpp b/modules/mbedtls/register_types.cpp
index 84a27c29bd..e483030b94 100644
--- a/modules/mbedtls/register_types.cpp
+++ b/modules/mbedtls/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,6 +35,10 @@
#include "packet_peer_mbed_dtls.h"
#include "stream_peer_mbedtls.h"
+#ifdef TESTS_ENABLED
+#include "tests/test_crypto_mbedtls.h"
+#endif
+
void register_mbedtls_types() {
CryptoMbedTLS::initialize_crypto();
StreamPeerMbedTLS::initialize_ssl();
diff --git a/modules/mbedtls/register_types.h b/modules/mbedtls/register_types.h
index 90c81b1682..46ffb8522b 100755..100644
--- a/modules/mbedtls/register_types.h
+++ b/modules/mbedtls/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mbedtls/ssl_context_mbedtls.cpp b/modules/mbedtls/ssl_context_mbedtls.cpp
index a2200e0644..cbb532587f 100644
--- a/modules/mbedtls/ssl_context_mbedtls.cpp
+++ b/modules/mbedtls/ssl_context_mbedtls.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -76,7 +76,6 @@ void CookieContextMbedTLS::clear() {
}
CookieContextMbedTLS::CookieContextMbedTLS() {
- inited = false;
}
CookieContextMbedTLS::~CookieContextMbedTLS() {
@@ -205,7 +204,6 @@ mbedtls_ssl_context *SSLContextMbedTLS::get_context() {
}
SSLContextMbedTLS::SSLContextMbedTLS() {
- inited = false;
}
SSLContextMbedTLS::~SSLContextMbedTLS() {
diff --git a/modules/mbedtls/ssl_context_mbedtls.h b/modules/mbedtls/ssl_context_mbedtls.h
index baaeb6eb85..5692dec1b6 100644
--- a/modules/mbedtls/ssl_context_mbedtls.h
+++ b/modules/mbedtls/ssl_context_mbedtls.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,9 +33,9 @@
#include "crypto_mbedtls.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
-#include "core/reference.h"
+#include "core/object/ref_counted.h"
#include <mbedtls/config.h>
#include <mbedtls/ctr_drbg.h>
@@ -46,11 +46,11 @@
class SSLContextMbedTLS;
-class CookieContextMbedTLS : public Reference {
+class CookieContextMbedTLS : public RefCounted {
friend class SSLContextMbedTLS;
protected:
- bool inited;
+ bool inited = false;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_ssl_cookie_ctx cookie_ctx;
@@ -63,11 +63,9 @@ public:
~CookieContextMbedTLS();
};
-class SSLContextMbedTLS : public Reference {
+class SSLContextMbedTLS : public RefCounted {
protected:
- bool inited;
-
- static PackedByteArray _read_file(String p_path);
+ bool inited = false;
public:
static void print_mbedtls_error(int p_ret);
diff --git a/modules/mbedtls/stream_peer_mbedtls.cpp b/modules/mbedtls/stream_peer_mbedtls.cpp
index e9a610b7ee..5727f5f82f 100644
--- a/modules/mbedtls/stream_peer_mbedtls.cpp
+++ b/modules/mbedtls/stream_peer_mbedtls.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,8 @@
#include "stream_peer_mbedtls.h"
+#include "core/io/file_access.h"
#include "core/io/stream_peer_tcp.h"
-#include "core/os/file_access.h"
int StreamPeerMbedTLS::bio_send(void *ctx, const unsigned char *buf, size_t len) {
if (buf == nullptr || len <= 0) {
@@ -242,7 +242,7 @@ void StreamPeerMbedTLS::poll() {
return;
}
- // We could pass NULL as second parameter, but some behaviour sanitizers doesn't seem to like that.
+ // We could pass nullptr as second parameter, but some behaviour sanitizers don't seem to like that.
// Passing a 1 byte buffer to workaround it.
uint8_t byte;
int ret = mbedtls_ssl_read(ssl_ctx->get_context(), &byte, 0);
@@ -273,8 +273,7 @@ int StreamPeerMbedTLS::get_available_bytes() const {
}
StreamPeerMbedTLS::StreamPeerMbedTLS() {
- ssl_ctx.instance();
- status = STATUS_DISCONNECTED;
+ ssl_ctx.instantiate();
}
StreamPeerMbedTLS::~StreamPeerMbedTLS() {
diff --git a/modules/mbedtls/stream_peer_mbedtls.h b/modules/mbedtls/stream_peer_mbedtls.h
index 68b03ae995..407479e3cc 100755..100644
--- a/modules/mbedtls/stream_peer_mbedtls.h
+++ b/modules/mbedtls/stream_peer_mbedtls.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,7 +36,7 @@
class StreamPeerMbedTLS : public StreamPeerSSL {
private:
- Status status;
+ Status status = STATUS_DISCONNECTED;
String hostname;
Ref<StreamPeer> base;
@@ -50,8 +50,6 @@ private:
protected:
Ref<SSLContextMbedTLS> ssl_ctx;
- static void _bind_methods();
-
Error _do_handshake();
public:
diff --git a/modules/mbedtls/tests/test_crypto_mbedtls.cpp b/modules/mbedtls/tests/test_crypto_mbedtls.cpp
new file mode 100644
index 0000000000..8762838883
--- /dev/null
+++ b/modules/mbedtls/tests/test_crypto_mbedtls.cpp
@@ -0,0 +1,62 @@
+/*************************************************************************/
+/* test_crypto_mbedtls.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 "test_crypto_mbedtls.h"
+
+#include "modules/mbedtls/crypto_mbedtls.h"
+#include "tests/test_macros.h"
+
+namespace TestCryptoMbedTLS {
+
+void hmac_digest_test(HashingContext::HashType ht, String expected_hex) {
+ CryptoMbedTLS crypto;
+ PackedByteArray key = String("supersecretkey").to_utf8_buffer();
+ PackedByteArray msg = String("Return of the MAC!").to_utf8_buffer();
+ PackedByteArray digest = crypto.hmac_digest(ht, key, msg);
+ String hex = String::hex_encode_buffer(digest.ptr(), digest.size());
+ CHECK(hex == expected_hex);
+}
+
+void hmac_context_digest_test(HashingContext::HashType ht, String expected_hex) {
+ HMACContextMbedTLS ctx;
+ PackedByteArray key = String("supersecretkey").to_utf8_buffer();
+ PackedByteArray msg1 = String("Return of ").to_utf8_buffer();
+ PackedByteArray msg2 = String("the MAC!").to_utf8_buffer();
+ Error err = ctx.start(ht, key);
+ CHECK(err == OK);
+ err = ctx.update(msg1);
+ CHECK(err == OK);
+ err = ctx.update(msg2);
+ CHECK(err == OK);
+ PackedByteArray digest = ctx.finish();
+ String hex = String::hex_encode_buffer(digest.ptr(), digest.size());
+ CHECK(hex == expected_hex);
+}
+} // namespace TestCryptoMbedTLS
diff --git a/modules/mbedtls/tests/test_crypto_mbedtls.h b/modules/mbedtls/tests/test_crypto_mbedtls.h
new file mode 100644
index 0000000000..b798717e52
--- /dev/null
+++ b/modules/mbedtls/tests/test_crypto_mbedtls.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* test_crypto_mbedtls.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 TEST_CRYPTO_MBEDTLS_H
+#define TEST_CRYPTO_MBEDTLS_H
+
+#include "core/crypto/hashing_context.h"
+
+#include "tests/test_macros.h"
+
+namespace TestCryptoMbedTLS {
+
+void hmac_digest_test(HashingContext::HashType ht, String expected_hex);
+
+TEST_CASE("[CryptoMbedTLS] HMAC digest") {
+ // SHA-256
+ hmac_digest_test(HashingContext::HashType::HASH_SHA256, "fe442023f8a7d36a810e1e7cd8a8e2816457f350a008fbf638296afa12085e59");
+
+ // SHA-1
+ hmac_digest_test(HashingContext::HashType::HASH_SHA1, "a0ac4cd68a2f4812c355983d94e8d025afe7dddf");
+}
+
+void hmac_context_digest_test(HashingContext::HashType ht, String expected_hex);
+
+TEST_CASE("[HMACContext] HMAC digest") {
+ // SHA-256
+ hmac_context_digest_test(HashingContext::HashType::HASH_SHA256, "fe442023f8a7d36a810e1e7cd8a8e2816457f350a008fbf638296afa12085e59");
+
+ // SHA-1
+ hmac_context_digest_test(HashingContext::HashType::HASH_SHA1, "a0ac4cd68a2f4812c355983d94e8d025afe7dddf");
+}
+} // namespace TestCryptoMbedTLS
+
+#endif // TEST_CRYPTO_MBEDTLS_H
diff --git a/modules/meshoptimizer/SCsub b/modules/meshoptimizer/SCsub
new file mode 100644
index 0000000000..3f86bb4f00
--- /dev/null
+++ b/modules/meshoptimizer/SCsub
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_meshoptimizer = env_modules.Clone()
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+thirdparty_dir = "#thirdparty/meshoptimizer/"
+thirdparty_sources = [
+ "allocator.cpp",
+ "clusterizer.cpp",
+ "indexcodec.cpp",
+ "indexgenerator.cpp",
+ "overdrawanalyzer.cpp",
+ "overdrawoptimizer.cpp",
+ "simplifier.cpp",
+ "spatialorder.cpp",
+ "stripifier.cpp",
+ "vcacheanalyzer.cpp",
+ "vcacheoptimizer.cpp",
+ "vertexcodec.cpp",
+ "vertexfilter.cpp",
+ "vfetchanalyzer.cpp",
+ "vfetchoptimizer.cpp",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_thirdparty = env_meshoptimizer.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_meshoptimizer.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/meshoptimizer/config.py b/modules/meshoptimizer/config.py
new file mode 100644
index 0000000000..b7e69569b9
--- /dev/null
+++ b/modules/meshoptimizer/config.py
@@ -0,0 +1,7 @@
+def can_build(env, platform):
+ # Having this on release by default, it's small and a lot of users like to do procedural stuff
+ return not env["disable_3d"]
+
+
+def configure(env):
+ pass
diff --git a/modules/meshoptimizer/register_types.cpp b/modules/meshoptimizer/register_types.cpp
new file mode 100644
index 0000000000..77cc82a4e2
--- /dev/null
+++ b/modules/meshoptimizer/register_types.cpp
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* 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 "scene/resources/surface_tool.h"
+#include "thirdparty/meshoptimizer/meshoptimizer.h"
+
+void register_meshoptimizer_types() {
+ SurfaceTool::optimize_vertex_cache_func = meshopt_optimizeVertexCache;
+ SurfaceTool::simplify_func = meshopt_simplify;
+ SurfaceTool::simplify_with_attrib_func = meshopt_simplifyWithAttributes;
+ SurfaceTool::simplify_scale_func = meshopt_simplifyScale;
+ SurfaceTool::simplify_sloppy_func = meshopt_simplifySloppy;
+}
+
+void unregister_meshoptimizer_types() {
+ SurfaceTool::optimize_vertex_cache_func = nullptr;
+ SurfaceTool::simplify_func = nullptr;
+ SurfaceTool::simplify_scale_func = nullptr;
+ SurfaceTool::simplify_sloppy_func = nullptr;
+}
diff --git a/modules/meshoptimizer/register_types.h b/modules/meshoptimizer/register_types.h
new file mode 100644
index 0000000000..5b15503acd
--- /dev/null
+++ b/modules/meshoptimizer/register_types.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* 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 MESHOPTIMIZER_REGISTER_TYPES_H
+#define MESHOPTIMIZER_REGISTER_TYPES_H
+
+void register_meshoptimizer_types();
+void unregister_meshoptimizer_types();
+
+#endif // PVR_REGISTER_TYPES_H
diff --git a/modules/minimp3/SCsub b/modules/minimp3/SCsub
new file mode 100644
index 0000000000..f4d1605d55
--- /dev/null
+++ b/modules/minimp3/SCsub
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_minimp3 = env_modules.Clone()
+
+thirdparty_dir = "#thirdparty/minimp3/"
+
+# Treat minimp3 headers as system headers to avoid raising warnings. Not supported on MSVC.
+if not env.msvc:
+ env_minimp3.Append(CPPFLAGS=["-isystem", Dir(thirdparty_dir).path])
+else:
+ env_minimp3.Prepend(CPPPATH=[thirdparty_dir])
+
+# Godot's own source files
+env_minimp3.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
new file mode 100644
index 0000000000..49e9f5f97e
--- /dev/null
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -0,0 +1,241 @@
+/*************************************************************************/
+/* audio_stream_mp3.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. */
+/*************************************************************************/
+
+#define MINIMP3_ONLY_MP3
+#define MINIMP3_FLOAT_OUTPUT
+#define MINIMP3_IMPLEMENTATION
+#define MINIMP3_NO_STDIO
+
+#include "audio_stream_mp3.h"
+
+#include "core/io/file_access.h"
+
+int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
+ ERR_FAIL_COND_V(!active, 0);
+
+ int todo = p_frames;
+
+ int frames_mixed_this_step = p_frames;
+
+ while (todo && active) {
+ mp3dec_frame_info_t frame_info;
+ mp3d_sample_t *buf_frame = nullptr;
+
+ int samples_mixed = mp3dec_ex_read_frame(mp3d, &buf_frame, &frame_info, mp3_stream->channels);
+
+ if (samples_mixed) {
+ p_buffer[p_frames - todo] = AudioFrame(buf_frame[0], buf_frame[samples_mixed - 1]);
+ --todo;
+ ++frames_mixed;
+ }
+
+ else {
+ //EOF
+ if (mp3_stream->loop) {
+ seek(mp3_stream->loop_offset);
+ loops++;
+ } else {
+ frames_mixed_this_step = p_frames - todo;
+ //fill remainder with silence
+ 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 AudioStreamPlaybackMP3::get_stream_sampling_rate() {
+ return mp3_stream->sample_rate;
+}
+
+void AudioStreamPlaybackMP3::start(float p_from_pos) {
+ active = true;
+ seek(p_from_pos);
+ loops = 0;
+ _begin_resample();
+}
+
+void AudioStreamPlaybackMP3::stop() {
+ active = false;
+}
+
+bool AudioStreamPlaybackMP3::is_playing() const {
+ return active;
+}
+
+int AudioStreamPlaybackMP3::get_loop_count() const {
+ return loops;
+}
+
+float AudioStreamPlaybackMP3::get_playback_position() const {
+ return float(frames_mixed) / mp3_stream->sample_rate;
+}
+
+void AudioStreamPlaybackMP3::seek(float p_time) {
+ if (!active) {
+ return;
+ }
+
+ if (p_time >= mp3_stream->get_length()) {
+ p_time = 0;
+ }
+
+ frames_mixed = uint32_t(mp3_stream->sample_rate * p_time);
+ mp3dec_ex_seek(mp3d, (uint64_t)frames_mixed * mp3_stream->channels);
+}
+
+AudioStreamPlaybackMP3::~AudioStreamPlaybackMP3() {
+ if (mp3d) {
+ mp3dec_ex_close(mp3d);
+ memfree(mp3d);
+ }
+}
+
+Ref<AudioStreamPlayback> AudioStreamMP3::instance_playback() {
+ Ref<AudioStreamPlaybackMP3> mp3s;
+
+ ERR_FAIL_COND_V_MSG(data == nullptr, 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.");
+
+ mp3s.instantiate();
+ 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);
+
+ mp3s->frames_mixed = 0;
+ mp3s->active = false;
+ mp3s->loops = 0;
+
+ if (errorcode) {
+ ERR_FAIL_COND_V(errorcode, Ref<AudioStreamPlaybackMP3>());
+ }
+
+ return mp3s;
+}
+
+String AudioStreamMP3::get_stream_name() const {
+ return ""; //return stream_name;
+}
+
+void AudioStreamMP3::clear_data() {
+ if (data) {
+ memfree(data);
+ data = nullptr;
+ data_len = 0;
+ }
+}
+
+void AudioStreamMP3::set_data(const Vector<uint8_t> &p_data) {
+ int src_data_len = p_data.size();
+ const uint8_t *src_datar = p_data.ptr();
+
+ 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);
+
+ channels = mp3d.info.channels;
+ sample_rate = mp3d.info.hz;
+ length = float(mp3d.samples) / (sample_rate * float(channels));
+
+ mp3dec_ex_close(&mp3d);
+
+ clear_data();
+
+ data = memalloc(src_data_len);
+ memcpy(data, 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;
+}
+
+void AudioStreamMP3::set_loop(bool p_enable) {
+ loop = p_enable;
+}
+
+bool AudioStreamMP3::has_loop() const {
+ return loop;
+}
+
+void AudioStreamMP3::set_loop_offset(float p_seconds) {
+ loop_offset = p_seconds;
+}
+
+float AudioStreamMP3::get_loop_offset() const {
+ return loop_offset;
+}
+
+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);
+
+ ClassDB::bind_method(D_METHOD("set_loop", "enable"), &AudioStreamMP3::set_loop);
+ ClassDB::bind_method(D_METHOD("has_loop"), &AudioStreamMP3::has_loop);
+
+ 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_NO_EDITOR), "set_data", "get_data");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_loop", "has_loop");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "loop_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_loop_offset", "get_loop_offset");
+}
+
+AudioStreamMP3::AudioStreamMP3() {
+}
+
+AudioStreamMP3::~AudioStreamMP3() {
+ clear_data();
+}
diff --git a/modules/minimp3/audio_stream_mp3.h b/modules/minimp3/audio_stream_mp3.h
new file mode 100644
index 0000000000..3c8bdd8c53
--- /dev/null
+++ b/modules/minimp3/audio_stream_mp3.h
@@ -0,0 +1,112 @@
+/*************************************************************************/
+/* audio_stream_mp3.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 AUDIO_STREAM_MP3_H
+#define AUDIO_STREAM_MP3_H
+
+#include "core/io/resource_loader.h"
+#include "servers/audio/audio_stream.h"
+
+#include "minimp3_ex.h"
+
+class AudioStreamMP3;
+
+class AudioStreamPlaybackMP3 : public AudioStreamPlaybackResampled {
+ GDCLASS(AudioStreamPlaybackMP3, AudioStreamPlaybackResampled);
+
+ mp3dec_ex_t *mp3d = nullptr;
+ uint32_t frames_mixed = 0;
+ bool active = false;
+ int loops = 0;
+
+ friend class AudioStreamMP3;
+
+ Ref<AudioStreamMP3> mp3_stream;
+
+protected:
+ virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override;
+ virtual float get_stream_sampling_rate() override;
+
+public:
+ virtual void start(float p_from_pos = 0.0) override;
+ virtual void stop() override;
+ virtual bool is_playing() const override;
+
+ virtual int get_loop_count() const override; //times it looped
+
+ virtual float get_playback_position() const override;
+ virtual void seek(float p_time) override;
+
+ AudioStreamPlaybackMP3() {}
+ ~AudioStreamPlaybackMP3();
+};
+
+class AudioStreamMP3 : public AudioStream {
+ GDCLASS(AudioStreamMP3, AudioStream);
+ OBJ_SAVE_TYPE(AudioStream) //children are all saved as AudioStream, so they can be exchanged
+ RES_BASE_EXTENSION("mp3str");
+
+ friend class AudioStreamPlaybackMP3;
+
+ void *data = nullptr;
+ uint32_t data_len = 0;
+
+ float sample_rate = 1.0;
+ int channels = 1;
+ float length = 0.0;
+ bool loop = false;
+ float loop_offset = 0.0;
+ void clear_data();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_loop(bool p_enable);
+ bool has_loop() const;
+
+ void set_loop_offset(float p_seconds);
+ float get_loop_offset() const;
+
+ 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;
+
+ virtual float get_length() const override;
+
+ virtual bool is_monophonic() const override;
+
+ AudioStreamMP3();
+ virtual ~AudioStreamMP3();
+};
+
+#endif // AUDIO_STREAM_MP3_H
diff --git a/modules/stb_vorbis/config.py b/modules/minimp3/config.py
index 1eb0a8cf33..bd35d099b9 100644
--- a/modules/stb_vorbis/config.py
+++ b/modules/minimp3/config.py
@@ -8,7 +8,7 @@ def configure(env):
def get_doc_classes():
return [
- "AudioStreamOGGVorbis",
+ "AudioStreamMP3",
]
diff --git a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/minimp3/doc_classes/AudioStreamMP3.xml
index 8a1bb62e24..e4f56614ee 100644
--- a/modules/stb_vorbis/doc_classes/AudioStreamOGGVorbis.xml
+++ b/modules/minimp3/doc_classes/AudioStreamMP3.xml
@@ -1,17 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="AudioStreamOGGVorbis" inherits="AudioStream" version="4.0">
+<class name="AudioStreamMP3" inherits="AudioStream" version="4.0">
<brief_description>
- OGG Vorbis audio stream driver.
+ MP3 audio stream driver.
</brief_description>
<description>
- OGG Vorbis audio stream driver.
+ MP3 audio stream driver.
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
- <member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray( )">
+ <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">
@@ -21,6 +19,4 @@
Time in seconds at which the stream starts after being looped.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/stb_vorbis/register_types.cpp b/modules/minimp3/register_types.cpp
index 6669d30278..63f2589f42 100644
--- a/modules/stb_vorbis/register_types.cpp
+++ b/modules/minimp3/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,23 +30,23 @@
#include "register_types.h"
-#include "audio_stream_ogg_vorbis.h"
+#include "audio_stream_mp3.h"
#ifdef TOOLS_ENABLED
-#include "core/engine.h"
-#include "resource_importer_ogg_vorbis.h"
+#include "core/config/engine.h"
+#include "resource_importer_mp3.h"
#endif
-void register_stb_vorbis_types() {
+void register_minimp3_types() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- Ref<ResourceImporterOGGVorbis> ogg_import;
- ogg_import.instance();
- ResourceFormatImporter::get_singleton()->add_importer(ogg_import);
+ Ref<ResourceImporterMP3> mp3_import;
+ mp3_import.instantiate();
+ ResourceFormatImporter::get_singleton()->add_importer(mp3_import);
}
#endif
- ClassDB::register_class<AudioStreamOGGVorbis>();
+ GDREGISTER_CLASS(AudioStreamMP3);
}
-void unregister_stb_vorbis_types() {
+void unregister_minimp3_types() {
}
diff --git a/modules/minimp3/register_types.h b/modules/minimp3/register_types.h
new file mode 100644
index 0000000000..96227c272e
--- /dev/null
+++ b/modules/minimp3/register_types.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* 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 MINIMP3_REGISTER_TYPES_H
+#define MINIMP3_REGISTER_TYPES_H
+
+void register_minimp3_types();
+void unregister_minimp3_types();
+
+#endif // MINIMP3_REGISTER_TYPES_H
diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp b/modules/minimp3/resource_importer_mp3.cpp
index d68d050d34..b2a755e23b 100644
--- a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp
+++ b/modules/minimp3/resource_importer_mp3.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* resource_importer_ogg_vorbis.cpp */
+/* resource_importer_mp3.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,58 +28,58 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "resource_importer_ogg_vorbis.h"
+#include "resource_importer_mp3.h"
+#include "core/io/file_access.h"
#include "core/io/resource_saver.h"
-#include "core/os/file_access.h"
#include "scene/resources/texture.h"
-String ResourceImporterOGGVorbis::get_importer_name() const {
- return "ogg_vorbis";
+String ResourceImporterMP3::get_importer_name() const {
+ return "mp3";
}
-String ResourceImporterOGGVorbis::get_visible_name() const {
- return "OGGVorbis";
+String ResourceImporterMP3::get_visible_name() const {
+ return "MP3";
}
-void ResourceImporterOGGVorbis::get_recognized_extensions(List<String> *p_extensions) const {
- p_extensions->push_back("ogg");
+void ResourceImporterMP3::get_recognized_extensions(List<String> *p_extensions) const {
+ p_extensions->push_back("mp3");
}
-String ResourceImporterOGGVorbis::get_save_extension() const {
- return "oggstr";
+String ResourceImporterMP3::get_save_extension() const {
+ return "mp3str";
}
-String ResourceImporterOGGVorbis::get_resource_type() const {
- return "AudioStreamOGGVorbis";
+String ResourceImporterMP3::get_resource_type() const {
+ return "AudioStreamMP3";
}
-bool ResourceImporterOGGVorbis::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;
}
-int ResourceImporterOGGVorbis::get_preset_count() const {
+int ResourceImporterMP3::get_preset_count() const {
return 0;
}
-String ResourceImporterOGGVorbis::get_preset_name(int p_idx) const {
+String ResourceImporterMP3::get_preset_name(int p_idx) const {
return String();
}
-void ResourceImporterOGGVorbis::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));
}
-Error ResourceImporterOGGVorbis::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const Map<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
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 + "'.");
+ ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
- size_t len = f->get_len();
+ uint64_t len = f->get_length();
Vector<uint8_t> data;
data.resize(len);
@@ -89,16 +89,16 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
memdelete(f);
- Ref<AudioStreamOGGVorbis> ogg_stream;
- ogg_stream.instance();
+ Ref<AudioStreamMP3> mp3_stream;
+ mp3_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);
+ mp3_stream->set_data(data);
+ ERR_FAIL_COND_V(!mp3_stream->get_data().size(), ERR_FILE_CORRUPT);
+ mp3_stream->set_loop(loop);
+ mp3_stream->set_loop_offset(loop_offset);
- return ResourceSaver::save(p_save_path + ".oggstr", ogg_stream);
+ return ResourceSaver::save(p_save_path + ".mp3str", mp3_stream);
}
-ResourceImporterOGGVorbis::ResourceImporterOGGVorbis() {
+ResourceImporterMP3::ResourceImporterMP3() {
}
diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h
new file mode 100644
index 0000000000..356ec77d22
--- /dev/null
+++ b/modules/minimp3/resource_importer_mp3.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* resource_importer_mp3.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 RESOURCE_IMPORTER_MP3_H
+#define RESOURCE_IMPORTER_MP3_H
+
+#include "audio_stream_mp3.h"
+#include "core/io/resource_importer.h"
+
+class ResourceImporterMP3 : public ResourceImporter {
+ GDCLASS(ResourceImporterMP3, ResourceImporter);
+
+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 int get_preset_count() const override;
+ virtual String get_preset_name(int p_idx) const override;
+
+ virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
+ virtual bool get_option_visibility(const String &p_path, const String &p_option, const Map<StringName, Variant> &p_options) const override;
+
+ virtual 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;
+
+ ResourceImporterMP3();
+};
+
+#endif // RESOURCE_IMPORTER_MP3_H
diff --git a/modules/mobile_vr/config.py b/modules/mobile_vr/config.py
index ee401c1a2a..f6b64fb690 100644
--- a/modules/mobile_vr/config.py
+++ b/modules/mobile_vr/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return True
+ return not env["disable_3d"]
def configure(env):
diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml
index 120535bd41..18a77c8b8d 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" override="true" 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 a2fb443ef0..ba7353ace2 100644
--- a/modules/mobile_vr/mobile_vr_interface.cpp
+++ b/modules/mobile_vr/mobile_vr_interface.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,7 +39,7 @@ StringName MobileVRInterface::get_name() const {
return "Native mobile";
};
-int MobileVRInterface::get_capabilities() const {
+uint32_t MobileVRInterface::get_capabilities() const {
return XRInterface::XR_STEREO;
};
@@ -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;
@@ -153,8 +155,8 @@ void MobileVRInterface::set_position_from_sensors() {
last_magnetometer_data = magneto;
if (grav.length() < 0.1) {
- // not ideal but use our accelerometer, this will contain shakey shakey user behaviour
- // maybe look into some math but I'm guessing that if this isn't available, its because we lack the gyro sensor to actually work out
+ // not ideal but use our accelerometer, this will contain shaky user behaviour
+ // maybe look into some math but I'm guessing that if this isn't available, it's because we lack the gyro sensor to actually work out
// what a stable gravity vector is
grav = acc;
if (grav.length() > 0.1) {
@@ -181,12 +183,12 @@ void MobileVRInterface::set_position_from_sensors() {
tracking_state = XRInterface::XR_NORMAL_TRACKING;
};
- ///@TODO improve this, the magnetometer is very fidgity sometimes flipping the axis for no apparent reason (probably a bug on my part)
- // if you have a gyro + accelerometer that combo tends to be better then combining all three but without a gyro you need the magnetometer..
+ ///@TODO improve this, the magnetometer is very fidgety sometimes flipping the axis for no apparent reason (probably a bug on my part)
+ // if you have a gyro + accelerometer that combo tends to be better than combining all three but without a gyro you need the magnetometer..
if (has_magneto && has_grav && !has_gyro) {
// convert to quaternions, easier to smooth those out
- Quat transform_quat(orientation);
- Quat acc_mag_quat(combine_acc_mag(grav, magneto));
+ Quaternion transform_quat(orientation);
+ Quaternion acc_mag_quat(combine_acc_mag(grav, magneto));
transform_quat = transform_quat.slerp(acc_mag_quat, 0.1);
orientation = Basis(transform_quat);
@@ -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,67 +246,71 @@ 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;
};
-bool MobileVRInterface::is_stereo() {
+uint32_t MobileVRInterface::get_view_count() {
// needs stereo...
- return true;
+ return 2;
};
+XRInterface::TrackingStatus MobileVRInterface::get_tracking_status() const {
+ return tracking_state;
+}
+
bool MobileVRInterface::is_initialized() const {
return (initialized);
};
@@ -314,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;
@@ -322,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);
@@ -339,17 +351,39 @@ bool MobileVRInterface::initialize() {
void MobileVRInterface::uninitialize() {
if (initialized) {
+ // do any cleanup here...
XRServer *xr_server = XRServer::get_singleton();
if (xr_server != nullptr) {
- // no longer our primary interface
- xr_server->clear_primary_interface_if(this);
+ 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;
};
};
-Size2 MobileVRInterface::get_render_targetsize() {
+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_
// we use half our window size
@@ -361,10 +395,10 @@ Size2 MobileVRInterface::get_render_targetsize() {
return target_size;
};
-Transform MobileVRInterface::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) {
+Transform3D MobileVRInterface::get_camera_transform() {
_THREAD_SAFE_METHOD_
- Transform transform_for_eye;
+ Transform3D transform_for_eye;
XRServer *xr_server = XRServer::get_singleton();
ERR_FAIL_NULL_V(xr_server, transform_for_eye);
@@ -372,22 +406,42 @@ Transform MobileVRInterface::get_transform_for_eye(XRInterface::Eyes p_eye, cons
if (initialized) {
float world_scale = xr_server->get_world_scale();
- // we don't need to check for the existence of our HMD, doesn't effect our values...
+ // just scale our origin point of our transform
+ Transform3D _head_transform = head_transform;
+ _head_transform.origin *= world_scale;
+
+ transform_for_eye = (xr_server->get_reference_frame()) * _head_transform;
+ }
+
+ return transform_for_eye;
+};
+
+Transform3D MobileVRInterface::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
+ _THREAD_SAFE_METHOD_
+
+ Transform3D transform_for_eye;
+
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL_V(xr_server, transform_for_eye);
+
+ if (initialized) {
+ float world_scale = xr_server->get_world_scale();
+
+ // we don't need to check for the existence of our HMD, doesn't affect our values...
// note * 0.01 to convert cm to m and * 0.5 as we're moving half in each direction...
- if (p_eye == XRInterface::EYE_LEFT) {
+ if (p_view == 0) {
transform_for_eye.origin.x = -(intraocular_dist * 0.01 * 0.5 * world_scale);
- } else if (p_eye == XRInterface::EYE_RIGHT) {
+ } else if (p_view == 1) {
transform_for_eye.origin.x = intraocular_dist * 0.01 * 0.5 * world_scale;
} else {
- // for mono we don't reposition, we want our center position.
+ // should not have any other values..
};
// just scale our origin point of our transform
- Transform 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;
@@ -396,71 +450,74 @@ Transform MobileVRInterface::get_transform_for_eye(XRInterface::Eyes p_eye, cons
return transform_for_eye;
};
-CameraMatrix MobileVRInterface::get_projection_for_eye(XRInterface::Eyes p_eye, 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;
- if (p_eye == XRInterface::EYE_MONO) {
- ///@TODO for now hardcode some of this, what is really needed here is that this needs to be in sync with the real cameras properties
- // which probably means implementing a specific class for iOS and Android. For now this is purely here as an example.
- // Note also that if you use a normal viewport with AR/VR turned off you can still use the tracker output of this interface
- // to position a stock standard Godot camera and have control over this.
- // This will make more sense when we implement ARkit on iOS (probably a separate interface).
- eye.set_perspective(60.0, p_aspect, p_z_near, p_z_far, false);
- } else {
- eye.set_for_hmd(p_eye == XRInterface::EYE_LEFT ? 1 : 2, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far);
- };
+ aspect = p_aspect;
+ eye.set_for_hmd(p_view + 1, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far);
return eye;
};
-void MobileVRInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) {
+Vector<BlitToScreen> MobileVRInterface::commit_views(RID p_render_target, const Rect2 &p_screen_rect) {
_THREAD_SAFE_METHOD_
+ Vector<BlitToScreen> blit_to_screen;
+
// We must have a valid render target
- ERR_FAIL_COND(!p_render_target.is_valid());
+ ERR_FAIL_COND_V(!p_render_target.is_valid(), blit_to_screen);
// Because we are rendering to our device we must use our main viewport!
- ERR_FAIL_COND(p_screen_rect == Rect2());
-
- Rect2 dest = p_screen_rect;
- Vector2 eye_center;
-
- // we output half a screen
- dest.size.x *= 0.5;
-
- if (p_eye == XRInterface::EYE_LEFT) {
- eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0);
- } else if (p_eye == XRInterface::EYE_RIGHT) {
- dest.position.x = dest.size.x;
- eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0);
- }
- // we don't offset the eye center vertically (yet)
- eye_center.y = 0.0;
+ ERR_FAIL_COND_V(p_screen_rect == Rect2(), blit_to_screen);
+
+ // and add our blits
+ BlitToScreen blit;
+ blit.render_target = p_render_target;
+ blit.multi_view.use_layer = true;
+ blit.lens_distortion.apply = true;
+ blit.lens_distortion.k1 = k1;
+ blit.lens_distortion.k2 = k2;
+ blit.lens_distortion.upscale = oversample;
+ blit.lens_distortion.aspect_ratio = aspect;
+
+ // left eye
+ 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.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);
+
+ return blit_to_screen;
}
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());
+ }
};
};
-MobileVRInterface::MobileVRInterface() {
- initialized = false;
-
- // 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
- eye_height = 1.85;
- intraocular_dist = 6.0;
- display_width = 14.5;
- display_to_lens = 4.0;
- oversample = 1.5;
- k1 = 0.215;
- k2 = 0.215;
- last_ticks = 0;
-};
+MobileVRInterface::MobileVRInterface() {}
MobileVRInterface::~MobileVRInterface() {
// and make sure we cleanup if we haven't already
diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h
index 9b03fff777..b5bf966247 100644
--- a/modules/mobile_vr/mobile_vr_interface.h
+++ b/modules/mobile_vr/mobile_vr_interface.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -51,19 +51,25 @@ class MobileVRInterface : public XRInterface {
GDCLASS(MobileVRInterface, XRInterface);
private:
- bool initialized;
- Basis orientation;
- float eye_height;
- uint64_t last_ticks;
+ bool initialized = false;
+ XRInterface::TrackingStatus tracking_state;
- real_t intraocular_dist;
- real_t display_width;
- real_t display_to_lens;
- real_t oversample;
+ // 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
+ double eye_height = 1.85;
+ uint64_t last_ticks = 0;
- //@TODO not yet used, these are needed in our distortion shader...
- real_t k1;
- real_t k2;
+ double intraocular_dist = 6.0;
+ double display_width = 14.5;
+ double display_to_lens = 4.0;
+ double oversample = 1.5;
+
+ 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
@@ -73,9 +79,9 @@ private:
Vector3 scale_magneto(const Vector3 &p_magnetometer);
Basis combine_acc_mag(const Vector3 &p_grav, const Vector3 &p_magneto);
- int mag_count;
- bool has_gyro;
- bool sensor_first;
+ int mag_count = 0;
+ bool has_gyro = false;
+ bool sensor_first = false;
Vector3 last_accerometer_data;
Vector3 last_magnetometer_data;
Vector3 mag_current_min;
@@ -84,20 +90,20 @@ private:
Vector3 mag_next_max;
///@TODO a few support functions for trackers, most are math related and should likely be moved elsewhere
- float floor_decimals(float p_value, float p_decimals) {
+ float floor_decimals(const float p_value, const float p_decimals) {
float power_of_10 = pow(10.0f, p_decimals);
return floor(p_value * power_of_10) / power_of_10;
};
- Vector3 floor_decimals(const Vector3 &p_vector, float p_decimals) {
+ Vector3 floor_decimals(const Vector3 &p_vector, const float p_decimals) {
return Vector3(floor_decimals(p_vector.x, p_decimals), floor_decimals(p_vector.y, p_decimals), floor_decimals(p_vector.z, p_decimals));
};
- Vector3 low_pass(const Vector3 &p_vector, const Vector3 &p_last_vector, float p_factor) {
+ Vector3 low_pass(const Vector3 &p_vector, const Vector3 &p_last_vector, const float p_factor) {
return p_vector + (p_factor * (p_last_vector - p_vector));
};
- Vector3 scrub(const Vector3 &p_vector, const Vector3 &p_last_vector, float p_decimals, float p_factor) {
+ Vector3 scrub(const Vector3 &p_vector, const Vector3 &p_last_vector, const float p_decimals, const float p_factor) {
return low_pass(floor_decimals(p_vector, p_decimals), p_last_vector, p_factor);
};
@@ -107,42 +113,48 @@ 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 int get_capabilities() const override;
+ virtual uint32_t get_capabilities() const override;
+
+ virtual TrackingStatus get_tracking_status() const override;
virtual bool is_initialized() const override;
virtual bool initialize() override;
virtual void uninitialize() override;
- virtual Size2 get_render_targetsize() override;
- virtual bool is_stereo() override;
- virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) override;
- virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) override;
- virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) 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, 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;
- virtual void notification(int p_what) override {}
MobileVRInterface();
~MobileVRInterface();
diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp
index 0bb555e780..233c16531a 100644
--- a/modules/mobile_vr/register_types.cpp
+++ b/modules/mobile_vr/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,15 +32,30 @@
#include "mobile_vr_interface.h"
+Ref<MobileVRInterface> mobile_vr;
+
void register_mobile_vr_types() {
- ClassDB::register_class<MobileVRInterface>();
+ GDREGISTER_CLASS(MobileVRInterface);
if (XRServer::get_singleton()) {
- Ref<MobileVRInterface> mobile_vr;
- mobile_vr.instance();
+ 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/mobile_vr/register_types.h b/modules/mobile_vr/register_types.h
index 33f608b6ed..9f20f252a4 100644
--- a/modules/mobile_vr/register_types.h
+++ b/modules/mobile_vr/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/.editorconfig b/modules/mono/.editorconfig
new file mode 100644
index 0000000000..c9dcd7724e
--- /dev/null
+++ b/modules/mono/.editorconfig
@@ -0,0 +1,14 @@
+[*.sln]
+indent_style = tab
+
+[*.{csproj,props,targets,nuspec,resx}]
+indent_style = space
+indent_size = 2
+
+[*.cs]
+indent_style = space
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+max_line_length = 120
+csharp_indent_case_contents_when_block = false
diff --git a/modules/mono/Directory.Build.props b/modules/mono/Directory.Build.props
new file mode 100644
index 0000000000..fbf864b11b
--- /dev/null
+++ b/modules/mono/Directory.Build.props
@@ -0,0 +1,3 @@
+<Project>
+ <Import Project="$(MSBuildThisFileDirectory)\SdkPackageVersions.props" />
+</Project>
diff --git a/modules/mono/SCsub b/modules/mono/SCsub
index e8f3174a0a..95c959235c 100644
--- a/modules/mono/SCsub
+++ b/modules/mono/SCsub
@@ -40,10 +40,16 @@ if env_mono["tools"] and env_mono["mono_glue"] and env_mono["build_cil"]:
godot_tools_build.build(env_mono, api_sln_cmd)
+ # Build Godot.NET.Sdk
+ import build_scripts.godot_net_sdk_build as godot_net_sdk_build
+
+ godot_net_sdk_build.build(env_mono)
+
# Add sources
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")
@@ -52,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
new file mode 100644
index 0000000000..df3ebe581c
--- /dev/null
+++ b/modules/mono/SdkPackageVersions.props
@@ -0,0 +1,6 @@
+<Project>
+ <PropertyGroup>
+ <PackageVersion_Godot_NET_Sdk>4.0.0-dev5</PackageVersion_Godot_NET_Sdk>
+ <PackageVersion_Godot_SourceGenerators>4.0.0-dev2</PackageVersion_Godot_SourceGenerators>
+ </PropertyGroup>
+</Project>
diff --git a/modules/mono/build_scripts/godot_net_sdk_build.py b/modules/mono/build_scripts/godot_net_sdk_build.py
new file mode 100644
index 0000000000..8c5a60d2db
--- /dev/null
+++ b/modules/mono/build_scripts/godot_net_sdk_build.py
@@ -0,0 +1,55 @@
+# Build Godot.NET.Sdk solution
+
+import os
+
+from SCons.Script import Dir
+
+
+def build_godot_net_sdk(source, target, env):
+ # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
+
+ module_dir = env["module_dir"]
+
+ solution_path = os.path.join(module_dir, "editor/Godot.NET.Sdk/Godot.NET.Sdk.sln")
+ build_config = "Release"
+
+ from .solution_builder import build_solution
+
+ extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]]
+
+ build_solution(env, solution_path, build_config, extra_msbuild_args)
+ # No need to copy targets. The Godot.NET.Sdk csproj takes care of copying them.
+
+
+def get_nupkgs_versions(props_file):
+ import xml.etree.ElementTree as ET
+
+ tree = ET.parse(props_file)
+ root = tree.getroot()
+
+ return {
+ "Godot.NET.Sdk": root.find("./PropertyGroup/PackageVersion_Godot_NET_Sdk").text.strip(),
+ "Godot.SourceGenerators": root.find("./PropertyGroup/PackageVersion_Godot_SourceGenerators").text.strip(),
+ }
+
+
+def build(env_mono):
+ assert env_mono["tools"]
+
+ output_dir = Dir("#bin").abspath
+ editor_tools_dir = os.path.join(output_dir, "GodotSharp", "Tools")
+ nupkgs_dir = os.path.join(editor_tools_dir, "nupkgs")
+
+ module_dir = os.getcwd()
+
+ nupkgs_versions = get_nupkgs_versions(os.path.join(module_dir, "SdkPackageVersions.props"))
+
+ target_filenames = [
+ "Godot.NET.Sdk.%s.nupkg" % nupkgs_versions["Godot.NET.Sdk"],
+ "Godot.SourceGenerators.%s.nupkg" % nupkgs_versions["Godot.SourceGenerators"],
+ ]
+
+ targets = [os.path.join(nupkgs_dir, filename) for filename in target_filenames]
+
+ cmd = env_mono.CommandNoCache(targets, [], build_godot_net_sdk, module_dir=module_dir)
+ env_mono.AlwaysBuild(cmd)
diff --git a/modules/mono/build_scripts/make_android_mono_config.py b/modules/mono/build_scripts/make_android_mono_config.py
index d276d7d886..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 = ""
@@ -32,17 +34,16 @@ namespace {
static const int config_compressed_size = %d;
static const int config_uncompressed_size = %d;
static const unsigned char config_compressed_data[] = { %s };
-
} // namespace
String get_godot_android_mono_config() {
Vector<uint8_t> data;
data.resize(config_uncompressed_size);
uint8_t* w = data.ptrw();
- Compression::decompress(w.ptr(), config_uncompressed_size, config_compressed_data,
+ Compression::decompress(w, config_uncompressed_size, config_compressed_data,
config_compressed_size, Compression::MODE_DEFLATE);
String s;
- if (s.parse_utf8((const char *)w.ptr(), data.size())) {
+ if (s.parse_utf8((const char *)w, data.size())) {
ERR_FAIL_V(String());
}
return s;
diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py
index 6057004166..8e441e7e07 100644
--- a/modules/mono/build_scripts/mono_configure.py
+++ b/modules/mono/build_scripts/mono_configure.py
@@ -97,15 +97,10 @@ def configure(env, env_mono):
copy_mono_root = env["copy_mono_root"]
mono_prefix = env["mono_prefix"]
+ mono_bcl = env["mono_bcl"]
mono_lib_names = ["mono-2.0-sgen", "monosgen-2.0"]
- is_travis = os.environ.get("TRAVIS") == "true"
-
- if is_travis:
- # Travis CI may have a Mono version lower than 5.12
- env_mono.Append(CPPDEFINES=["NO_PENDING_EXCEPTIONS"])
-
if is_android and not env["android_arch"] in android_arch_dirs:
raise RuntimeError("This module does not support the specified 'android_arch': " + env["android_arch"])
@@ -262,7 +257,8 @@ def configure(env, env_mono):
env_mono.Append(CPPDEFINES=["_REENTRANT"])
if mono_static:
- env.Append(LINKFLAGS=["-rdynamic"])
+ if not is_javascript:
+ env.Append(LINKFLAGS=["-rdynamic"])
mono_lib_file = os.path.join(mono_lib_path, "lib" + mono_lib + ".a")
@@ -397,7 +393,7 @@ def configure(env, env_mono):
if tools_enabled:
# Only supported for editor builds.
- copy_mono_root_files(env, mono_root)
+ copy_mono_root_files(env, mono_root, mono_bcl)
def make_template_dir(env, mono_root):
@@ -430,7 +426,7 @@ def make_template_dir(env, mono_root):
copy_mono_shared_libs(env, mono_root, template_mono_root_dir)
-def copy_mono_root_files(env, mono_root):
+def copy_mono_root_files(env, mono_root, mono_bcl):
from glob import glob
from shutil import copy
from shutil import rmtree
@@ -455,7 +451,7 @@ def copy_mono_root_files(env, mono_root):
# Copy framework assemblies
- mono_framework_dir = os.path.join(mono_root, "lib", "mono", "4.5")
+ mono_framework_dir = mono_bcl or os.path.join(mono_root, "lib", "mono", "4.5")
mono_framework_facades_dir = os.path.join(mono_framework_dir, "Facades")
editor_mono_framework_dir = os.path.join(editor_mono_root_dir, "lib", "mono", "4.5")
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/class_db_api_json.cpp b/modules/mono/class_db_api_json.cpp
index d7b2028204..0da06131af 100644
--- a/modules/mono/class_db_api_json.cpp
+++ b/modules/mono/class_db_api_json.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,9 +32,9 @@
#ifdef DEBUG_METHODS_ENABLED
+#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
#include "core/io/json.h"
-#include "core/os/file_access.h"
-#include "core/project_settings.h"
#include "core/version.h"
void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
@@ -50,8 +50,8 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
//must be alphabetically sorted for hash to compute
names.sort_custom<StringName::AlphCompare>();
- for (List<StringName>::Element *E = names.front(); E; E = E->next()) {
- ClassDB::ClassInfo *t = ClassDB::classes.getptr(E->get());
+ for (const StringName &E : names) {
+ ClassDB::ClassInfo *t = ClassDB::classes.getptr(E);
ERR_FAIL_COND(!t);
if (t->api != p_api || !t->exposed) {
continue;
@@ -71,7 +71,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
while ((k = t->method_map.next(k))) {
String name = k->operator String();
- ERR_CONTINUE(name.empty());
+ ERR_CONTINUE(name.is_empty());
if (name[0] == '_') {
continue; // Ignore non-virtual methods that start with an underscore
@@ -84,11 +84,11 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
Array methods;
- for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
+ for (const StringName &F : snames) {
Dictionary method_dict;
methods.push_back(method_dict);
- MethodBind *mb = t->method_map[F->get()];
+ MethodBind *mb = t->method_map[F];
method_dict["name"] = mb->get_name();
method_dict["argument_count"] = mb->get_argument_count();
method_dict["return_type"] = mb->get_argument_type(-1);
@@ -122,7 +122,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
method_dict["hint_flags"] = mb->get_hint_flags();
}
- if (!methods.empty()) {
+ if (!methods.is_empty()) {
class_dict["methods"] = methods;
}
}
@@ -141,15 +141,15 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
Array constants;
- for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
+ for (const StringName &F : snames) {
Dictionary constant_dict;
constants.push_back(constant_dict);
- constant_dict["name"] = F->get();
- constant_dict["value"] = t->constant_map[F->get()];
+ constant_dict["name"] = F;
+ constant_dict["value"] = t->constant_map[F];
}
- if (!constants.empty()) {
+ if (!constants.is_empty()) {
class_dict["constants"] = constants;
}
}
@@ -168,12 +168,12 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
Array signals;
- for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
+ for (const StringName &F : snames) {
Dictionary signal_dict;
signals.push_back(signal_dict);
- MethodInfo &mi = t->signal_map[F->get()];
- signal_dict["name"] = F->get();
+ MethodInfo &mi = t->signal_map[F];
+ signal_dict["name"] = F;
Array arguments;
signal_dict["arguments"] = arguments;
@@ -184,7 +184,7 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
}
}
- if (!signals.empty()) {
+ if (!signals.is_empty()) {
class_dict["signals"] = signals;
}
}
@@ -203,18 +203,18 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
Array properties;
- for (List<StringName>::Element *F = snames.front(); F; F = F->next()) {
+ for (const StringName &F : snames) {
Dictionary property_dict;
properties.push_back(property_dict);
- ClassDB::PropertySetGet *psg = t->property_setget.getptr(F->get());
+ ClassDB::PropertySetGet *psg = t->property_setget.getptr(F);
- property_dict["name"] = F->get();
+ property_dict["name"] = F;
property_dict["setter"] = psg->setter;
property_dict["getter"] = psg->getter;
}
- if (!properties.empty()) {
+ if (!properties.is_empty()) {
class_dict["property_setget"] = properties;
}
}
@@ -222,25 +222,26 @@ void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api) {
Array property_list;
//property list
- for (List<PropertyInfo>::Element *F = t->property_list.front(); F; F = F->next()) {
+ for (const PropertyInfo &F : t->property_list) {
Dictionary property_dict;
property_list.push_back(property_dict);
- property_dict["name"] = F->get().name;
- property_dict["type"] = F->get().type;
- property_dict["hint"] = F->get().hint;
- property_dict["hint_string"] = F->get().hint_string;
- property_dict["usage"] = F->get().usage;
+ property_dict["name"] = F.name;
+ property_dict["type"] = F.type;
+ property_dict["hint"] = F.hint;
+ property_dict["hint_string"] = F.hint_string;
+ property_dict["usage"] = F.usage;
}
- if (!property_list.empty()) {
+ if (!property_list.is_empty()) {
class_dict["property_list"] = property_list;
}
}
FileAccessRef f = FileAccess::open(p_output_file, FileAccess::WRITE);
ERR_FAIL_COND_MSG(!f, "Cannot open file '" + p_output_file + "'.");
- f->store_string(JSON::print(classes_dict, /*indent: */ "\t"));
+ JSON json;
+ f->store_string(json.stringify(classes_dict, "\t"));
f->close();
print_line(String() + "ClassDB API JSON written to: " + ProjectSettings::get_singleton()->globalize_path(p_output_file));
diff --git a/modules/mono/class_db_api_json.h b/modules/mono/class_db_api_json.h
index 7f016ac3d6..6698a6260f 100644
--- a/modules/mono/class_db_api_json.h
+++ b/modules/mono/class_db_api_json.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,13 +31,13 @@
#ifndef CLASS_DB_API_JSON_H
#define CLASS_DB_API_JSON_H
-// 'core/method_bind.h' defines DEBUG_METHODS_ENABLED, but it looks like we
-// cannot include it here. That's why we include it through 'core/class_db.h'.
-#include "core/class_db.h"
+// 'core/object/method_bind.h' defines DEBUG_METHODS_ENABLED, but it looks like we
+// cannot include it here. That's why we include it through 'core/object/class_db.h'.
+#include "core/object/class_db.h"
#ifdef DEBUG_METHODS_ENABLED
-#include "core/ustring.h"
+#include "core/string/ustring.h"
void class_db_api_to_json(const String &p_output_file, ClassDB::APIType p_api);
diff --git a/modules/mono/config.py b/modules/mono/config.py
index d060ae9b28..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):
@@ -11,7 +11,6 @@ def configure(env):
if platform not in supported_platforms:
raise RuntimeError("This module does not currently support building for this platform")
- env.use_ptrcall = True
env.add_module_version_string("mono")
from SCons.Script import BoolVariable, PathVariable, Variables, Help
@@ -28,6 +27,14 @@ def configure(env):
PathVariable.PathAccept,
)
)
+ envvars.Add(
+ PathVariable(
+ "mono_bcl",
+ "Path to a custom Mono BCL (Base Class Library) directory for the target platform",
+ "",
+ PathVariable.PathAccept,
+ )
+ )
envvars.Add(BoolVariable("mono_static", "Statically link Mono", default_mono_static))
envvars.Add(BoolVariable("mono_glue", "Build with the Mono glue sources", True))
envvars.Add(BoolVariable("build_cil", "Build C# solutions", True))
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index a05b38b8bf..544f2a7584 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,20 +31,22 @@
#include "csharp_script.h"
#include <mono/metadata/threads.h>
+#include <mono/metadata/tokentype.h>
#include <stdint.h>
+#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
-#include "core/io/json.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include "core/os/mutex.h"
#include "core/os/os.h"
#include "core/os/thread.h"
-#include "core/project_settings.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
@@ -82,6 +84,12 @@ static bool _create_project_solution_if_needed() {
CSharpLanguage *CSharpLanguage::singleton = nullptr;
+GDNativeInstanceBindingCallbacks CSharpLanguage::_instance_binding_callbacks = {
+ &_instance_binding_create_callback,
+ &_instance_binding_free_callback,
+ &_instance_binding_reference_callback
+};
+
String CSharpLanguage::get_name() const {
return "C#";
}
@@ -145,8 +153,8 @@ void CSharpLanguage::finalize() {
finalizing = true;
// Make sure all script binding gchandles are released before finalizing GDMono
- for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
- CSharpScriptBinding &script_binding = E->value();
+ for (KeyValue<Object *, CSharpScriptBinding> &E : script_bindings) {
+ CSharpScriptBinding &script_binding = E.value;
if (!script_binding.gchandle.is_released()) {
script_binding.gchandle.release();
@@ -163,8 +171,8 @@ void CSharpLanguage::finalize() {
script_bindings.clear();
#ifdef DEBUG_ENABLED
- for (Map<ObjectID, int>::Element *E = unsafe_object_references.front(); E; E = E->next()) {
- const ObjectID &id = E->key();
+ for (const KeyValue<ObjectID, int> &E : unsafe_object_references) {
+ const ObjectID &id = E.key;
Object *obj = ObjectDB::get_instance(id);
if (obj) {
@@ -303,6 +311,26 @@ 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";
+}
+
void CSharpLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
p_delimiters->push_back("//"); // single-line comment
p_delimiters->push_back("/* */"); // delimited comment
@@ -311,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.
}
@@ -327,7 +355,7 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin
String script_template = "using " BINDINGS_NAMESPACE ";\n"
"using System;\n"
"\n"
- "public class %CLASS% : %BASE%\n"
+ "public partial class %CLASS% : %BASE%\n"
"{\n"
" // Declare member variables here. Examples:\n"
" // private int a = 2;\n"
@@ -346,14 +374,18 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin
"// }\n"
"}\n";
- String base_class_name = get_base_class_name(p_base_class_name, p_class_name);
+ // Replaces all spaces in p_class_name with underscores to prevent
+ // invalid C# Script templates from being generated when the object name
+ // has spaces in it.
+ String class_name_no_spaces = p_class_name.replace(" ", "_");
+ String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces);
script_template = script_template.replace("%BASE%", base_class_name)
- .replace("%CLASS%", p_class_name);
+ .replace("%CLASS%", class_name_no_spaces);
Ref<CSharpScript> script;
- script.instance();
+ script.instantiate();
script->set_source_code(script_template);
- script->set_name(p_class_name);
+ script->set_name(class_name_no_spaces);
return script;
}
@@ -364,9 +396,10 @@ bool CSharpLanguage::is_using_templates() {
void CSharpLanguage::make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) {
String src = p_script->get_source_code();
- String base_class_name = get_base_class_name(p_base_class_name, p_class_name);
+ String class_name_no_spaces = p_class_name.replace(" ", "_");
+ String base_class_name = get_base_class_name(p_base_class_name, class_name_no_spaces);
src = src.replace("%BASE%", base_class_name)
- .replace("%CLASS%", p_class_name)
+ .replace("%CLASS%", class_name_no_spaces)
.replace("%TS%", _get_indentation());
p_script->set_source_code(src);
}
@@ -395,7 +428,7 @@ bool CSharpLanguage::supports_builtin_mode() const {
#ifdef TOOLS_ENABLED
static String variant_type_to_managed_name(const String &p_var_type_name) {
- if (p_var_type_name.empty()) {
+ if (p_var_type_name.is_empty()) {
return "object";
}
@@ -470,14 +503,14 @@ static String variant_type_to_managed_name(const String &p_var_type_name) {
Variant::VECTOR3I,
Variant::TRANSFORM2D,
Variant::PLANE,
- Variant::QUAT,
+ Variant::QUATERNION,
Variant::AABB,
Variant::BASIS,
- Variant::TRANSFORM,
+ Variant::TRANSFORM3D,
Variant::COLOR,
Variant::STRING_NAME,
Variant::NODE_PATH,
- Variant::_RID,
+ Variant::RID,
Variant::CALLABLE
};
@@ -517,10 +550,10 @@ String CSharpLanguage::make_function(const String &, const String &, const Packe
String CSharpLanguage::_get_indentation() const {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
- bool use_space_indentation = EDITOR_DEF("text_editor/indent/type", 0);
+ bool use_space_indentation = EDITOR_DEF("text_editor/behavior/indent/type", 0);
if (use_space_indentation) {
- int indent_size = EDITOR_DEF("text_editor/indent/size", 4);
+ int indent_size = EDITOR_DEF("text_editor/behavior/indent/size", 4);
String space_indent = "";
for (int i = 0; i < indent_size; i++) {
@@ -757,7 +790,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
String appname = ProjectSettings::get_singleton()->get("application/config/name");
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
- if (appname_safe.empty()) {
+ if (appname_safe.is_empty()) {
appname_safe = "UnnamedProject";
}
@@ -837,24 +870,29 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
List<Ref<CSharpScript>> to_reload;
// We need to keep reference instances alive during reloading
- List<Ref<Reference>> ref_instances;
+ List<Ref<RefCounted>> rc_instances;
- for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
- CSharpScriptBinding &script_binding = E->value();
- Reference *ref = Object::cast_to<Reference>(script_binding.owner);
- if (ref) {
- ref_instances.push_back(Ref<Reference>(ref));
+ for (const KeyValue<Object *, CSharpScriptBinding> &E : script_bindings) {
+ const CSharpScriptBinding &script_binding = E.value;
+ RefCounted *rc = Object::cast_to<RefCounted>(script_binding.owner);
+ if (rc) {
+ rc_instances.push_back(Ref<RefCounted>(rc));
}
}
// As scripts are going to be reloaded, must proceed without locking here
- for (List<Ref<CSharpScript>>::Element *E = scripts.front(); E; E = E->next()) {
- Ref<CSharpScript> &script = E->get();
+ for (Ref<CSharpScript> &script : scripts) {
+ // If someone removes a script from a node, deletes the script, builds, adds a script to the
+ // same node, then builds again, the script might have no path and also no script_class. In
+ // that case, we can't (and don't need to) reload it.
+ if (script->get_path().is_empty() && !script->script_class) {
+ continue;
+ }
to_reload.push_back(script);
- if (script->get_path().empty()) {
+ if (script->get_path().is_empty()) {
script->tied_class_name_for_reload = script->script_class->get_name_for_lookup();
script->tied_class_namespace_for_reload = script->script_class->get_namespace();
}
@@ -862,24 +900,23 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Script::instances are deleted during managed object disposal, which happens on domain finalize.
// Only placeholders are kept. Therefore we need to keep a copy before that happens.
- for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) {
- Object *obj = F->get();
+ for (Object *&obj : script->instances) {
script->pending_reload_instances.insert(obj->get_instance_id());
- Reference *ref = Object::cast_to<Reference>(obj);
- if (ref) {
- ref_instances.push_back(Ref<Reference>(ref));
+ RefCounted *rc = Object::cast_to<RefCounted>(obj);
+ if (rc) {
+ rc_instances.push_back(Ref<RefCounted>(rc));
}
}
#ifdef TOOLS_ENABLED
- for (Set<PlaceHolderScriptInstance *>::Element *F = script->placeholders.front(); F; F = F->next()) {
- Object *obj = F->get()->get_owner();
+ for (PlaceHolderScriptInstance *&script_instance : script->placeholders) {
+ Object *obj = script_instance->get_owner();
script->pending_reload_instances.insert(obj->get_instance_id());
- Reference *ref = Object::cast_to<Reference>(obj);
- if (ref) {
- ref_instances.push_back(Ref<Reference>(ref));
+ RefCounted *rc = Object::cast_to<RefCounted>(obj);
+ if (rc) {
+ rc_instances.push_back(Ref<RefCounted>(rc));
}
}
#endif
@@ -887,9 +924,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Save state and remove script from instances
Map<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
- for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) {
- Object *obj = F->get();
-
+ for (Object *&obj : script->instances) {
ERR_CONTINUE(!obj->get_script_instance());
CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance());
@@ -911,9 +946,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
// After the state of all instances is saved, clear scripts and script instances
- for (List<Ref<CSharpScript>>::Element *E = scripts.front(); E; E = E->next()) {
- Ref<CSharpScript> &script = E->get();
-
+ for (Ref<CSharpScript> &script : scripts) {
while (script->instances.front()) {
Object *obj = script->instances.front()->get();
obj->set_script(REF()); // Remove script and existing script instances (placeholder are not removed before domain reload)
@@ -926,11 +959,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
if (gdmono->reload_scripts_domain() != OK) {
// Failed to reload the scripts domain
// Make sure to add the scripts back to their owners before returning
- for (List<Ref<CSharpScript>>::Element *E = to_reload.front(); E; E = E->next()) {
- Ref<CSharpScript> scr = E->get();
-
- for (const Map<ObjectID, CSharpScript::StateBackup>::Element *F = scr->pending_reload_state.front(); F; F = F->next()) {
- Object *obj = ObjectDB::get_instance(F->key());
+ for (Ref<CSharpScript> &scr : to_reload) {
+ for (const KeyValue<ObjectID, CSharpScript::StateBackup> &F : scr->pending_reload_state) {
+ Object *obj = ObjectDB::get_instance(F.key);
if (!obj) {
continue;
@@ -940,8 +971,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
// Use a placeholder for now to avoid losing the state when saving a scene
- obj->set_script(scr);
-
PlaceHolderScriptInstance *placeholder = scr->placeholder_instance_create(obj);
obj->set_script_instance(placeholder);
@@ -952,8 +981,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
#endif
// Restore Variant properties state, it will be kept by the placeholder until the next script reloading
- for (List<Pair<StringName, Variant>>::Element *G = scr->pending_reload_state[obj_id].properties.front(); G; G = G->next()) {
- placeholder->property_set_fallback(G->get().first, G->get().second, nullptr);
+ for (const Pair<StringName, Variant> &G : scr->pending_reload_state[obj_id].properties) {
+ placeholder->property_set_fallback(G.first, G.second, nullptr);
}
scr->pending_reload_state.erase(obj_id);
@@ -965,17 +994,14 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
List<Ref<CSharpScript>> to_reload_state;
- for (List<Ref<CSharpScript>>::Element *E = to_reload.front(); E; E = E->next()) {
- Ref<CSharpScript> script = E->get();
-
- if (!script->get_path().empty()) {
+ for (Ref<CSharpScript> &script : to_reload) {
#ifdef TOOLS_ENABLED
- script->exports_invalidated = true;
+ script->exports_invalidated = true;
#endif
- script->signals_invalidated = true;
+ script->signals_invalidated = true;
+ if (!script->get_path().is_empty()) {
script->reload(p_soft_reload);
- script->update_exports();
if (!script->valid) {
script->pending_reload_instances.clear();
@@ -1021,8 +1047,7 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
StringName native_name = NATIVE_GDMONOCLASS_NAME(script->native);
{
- for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
- ObjectID obj_id = F->get();
+ for (const ObjectID &obj_id : script->pending_reload_instances) {
Object *obj = ObjectDB::get_instance(obj_id);
if (!obj) {
@@ -1073,11 +1098,8 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
to_reload_state.push_back(script);
}
- for (List<Ref<CSharpScript>>::Element *E = to_reload_state.front(); E; E = E->next()) {
- Ref<CSharpScript> script = E->get();
-
- for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
- ObjectID obj_id = F->get();
+ for (Ref<CSharpScript> &script : to_reload_state) {
+ for (const ObjectID &obj_id : script->pending_reload_instances) {
Object *obj = ObjectDB::get_instance(obj_id);
if (!obj) {
@@ -1091,16 +1113,16 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
- for (List<Pair<StringName, Variant>>::Element *G = state_backup.properties.front(); G; G = G->next()) {
- obj->get_script_instance()->set(G->get().first, G->get().second);
+ for (const Pair<StringName, Variant> &G : state_backup.properties) {
+ obj->get_script_instance()->set(G.first, G.second);
}
CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance());
if (csi) {
- for (List<Pair<StringName, Array>>::Element *G = state_backup.event_signals.front(); G; G = G->next()) {
- const StringName &name = G->get().first;
- const Array &serialized_data = G->get().second;
+ for (const Pair<StringName, Array> &G : state_backup.event_signals) {
+ const StringName &name = G.first;
+ const Array &serialized_data = G.second;
Map<StringName, CSharpScript::EventSignal>::Element *match = script->event_signals.find(name);
@@ -1144,9 +1166,9 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
{
MutexLock lock(ManagedCallable::instances_mutex);
- for (Map<ManagedCallable *, Array>::Element *elem = ManagedCallable::instances_pending_reload.front(); elem; elem = elem->next()) {
- ManagedCallable *managed_callable = elem->key();
- const Array &serialized_data = elem->value();
+ for (const KeyValue<ManagedCallable *, Array> &elem : ManagedCallable::instances_pending_reload) {
+ ManagedCallable *managed_callable = elem.key;
+ const Array &serialized_data = elem.value;
MonoObject *managed_serialized_data = GDMonoMarshal::variant_to_mono_object(serialized_data);
MonoDelegate *delegate = nullptr;
@@ -1180,46 +1202,56 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
}
#endif
-void CSharpLanguage::_load_scripts_metadata() {
- scripts_metadata.clear();
+void CSharpLanguage::lookup_script_for_class(GDMonoClass *p_class) {
+ if (!p_class->has_attribute(CACHED_CLASS(ScriptPathAttribute))) {
+ return;
+ }
- String scripts_metadata_filename = "scripts_metadata.";
+ MonoObject *attr = p_class->get_attribute(CACHED_CLASS(ScriptPathAttribute));
+ String path = CACHED_FIELD(ScriptPathAttribute, path)->get_string_value(attr);
-#ifdef TOOLS_ENABLED
- scripts_metadata_filename += Engine::get_singleton()->is_editor_hint() ? "editor" : "editor_player";
-#else
-#ifdef DEBUG_ENABLED
- scripts_metadata_filename += "debug";
-#else
- scripts_metadata_filename += "release";
-#endif
-#endif
+ dotnet_script_lookup_map[path] = DotNetScriptLookupInfo(
+ p_class->get_namespace(), p_class->get_name(), p_class);
+}
- String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file(scripts_metadata_filename);
+void CSharpLanguage::lookup_scripts_in_assembly(GDMonoAssembly *p_assembly) {
+ if (p_assembly->has_attribute(CACHED_CLASS(AssemblyHasScriptsAttribute))) {
+ MonoObject *attr = p_assembly->get_attribute(CACHED_CLASS(AssemblyHasScriptsAttribute));
+ bool requires_lookup = CACHED_FIELD(AssemblyHasScriptsAttribute, requiresLookup)->get_bool_value(attr);
- if (FileAccess::exists(scripts_metadata_path)) {
- String old_json;
+ if (requires_lookup) {
+ // This is supported for scenarios where specifying all types would be cumbersome,
+ // such as when disabling C# source generators (for whatever reason) or when using a
+ // language other than C# that has nothing similar to source generators to automate it.
+ MonoImage *image = p_assembly->get_image();
- Error ferr = read_all_file_utf8(scripts_metadata_path, old_json);
+ int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF);
- ERR_FAIL_COND(ferr != OK);
+ for (int i = 1; i < rows; i++) {
+ // We don't search inner classes, only top-level.
+ MonoClass *mono_class = mono_class_get(image, (i + 1) | MONO_TOKEN_TYPE_DEF);
- Variant old_dict_var;
- String err_str;
- int err_line;
- Error json_err = JSON::parse(old_json, old_dict_var, err_str, err_line);
- if (json_err != OK) {
- ERR_PRINT("Failed to parse metadata file: '" + err_str + "' (" + String::num_int64(err_line) + ").");
- return;
- }
+ if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class)) {
+ continue;
+ }
- scripts_metadata = old_dict_var.operator Dictionary();
- scripts_metadata_invalidated = false;
+ GDMonoClass *current = p_assembly->get_class(mono_class);
+ if (current) {
+ lookup_script_for_class(current);
+ }
+ }
+ } else {
+ // This is the most likely scenario as we use C# source generators
+ MonoArray *script_types = (MonoArray *)CACHED_FIELD(AssemblyHasScriptsAttribute, scriptTypes)->get_value(attr);
- print_verbose("Successfully loaded scripts metadata");
- } else {
- if (!Engine::get_singleton()->is_editor_hint()) {
- ERR_PRINT("Missing scripts metadata file.");
+ int length = mono_array_length(script_types);
+
+ for (int i = 0; i < length; i++) {
+ MonoReflectionType *reftype = mono_array_get(script_types, MonoReflectionType *, i);
+ ManagedType type = ManagedType::from_reftype(reftype);
+ ERR_CONTINUE(!type.type_class);
+ lookup_script_for_class(type.type_class);
+ }
}
}
}
@@ -1280,8 +1312,8 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) {
}
void CSharpLanguage::_on_scripts_domain_unloaded() {
- for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
- CSharpScriptBinding &script_binding = E->value();
+ for (KeyValue<Object *, CSharpScriptBinding> &E : script_bindings) {
+ CSharpScriptBinding &script_binding = E.value;
script_binding.gchandle.release();
script_binding.inited = false;
}
@@ -1298,7 +1330,7 @@ void CSharpLanguage::_on_scripts_domain_unloaded() {
}
#endif
- scripts_metadata_invalidated = true;
+ dotnet_script_lookup_map.clear();
}
#ifdef TOOLS_ENABLED
@@ -1323,6 +1355,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;
@@ -1405,61 +1438,61 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
r_script_binding.owner = p_object;
// Tie managed to unmanaged
- Reference *ref = Object::cast_to<Reference>(p_object);
+ RefCounted *rc = Object::cast_to<RefCounted>(p_object);
- if (ref) {
+ if (rc) {
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
// but the managed instance is alive, the refcount will be 1 instead of 0.
- // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)
+ // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
- ref->reference();
- CSharpLanguage::get_singleton()->post_unsafe_reference(ref);
+ rc->reference();
+ CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
}
return true;
}
-void *CSharpLanguage::alloc_instance_binding_data(Object *p_object) {
- MutexLock lock(language_bind_mutex);
+Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) {
+ return script_bindings.insert(p_object, p_script_binding);
+}
+
+void *CSharpLanguage::_instance_binding_create_callback(void *, void *p_instance) {
+ CSharpLanguage *csharp_lang = CSharpLanguage::get_singleton();
- Map<Object *, CSharpScriptBinding>::Element *match = script_bindings.find(p_object);
+ MutexLock lock(csharp_lang->language_bind_mutex);
+
+ Map<Object *, CSharpScriptBinding>::Element *match = csharp_lang->script_bindings.find((Object *)p_instance);
if (match) {
return (void *)match;
}
CSharpScriptBinding script_binding;
- if (!setup_csharp_script_binding(script_binding, p_object)) {
- return nullptr;
- }
-
- return (void *)insert_script_binding(p_object, script_binding);
+ return (void *)csharp_lang->insert_script_binding((Object *)p_instance, script_binding);
}
-Map<Object *, CSharpScriptBinding>::Element *CSharpLanguage::insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding) {
- return script_bindings.insert(p_object, p_script_binding);
-}
+void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_binding) {
+ CSharpLanguage *csharp_lang = CSharpLanguage::get_singleton();
-void CSharpLanguage::free_instance_binding_data(void *p_data) {
if (GDMono::get_singleton() == nullptr) {
#ifdef DEBUG_ENABLED
- CRASH_COND(!script_bindings.empty());
+ CRASH_COND(!csharp_lang->script_bindings.is_empty());
#endif
// Mono runtime finalized, all the gchandle bindings were already released
return;
}
- if (finalizing) {
+ if (csharp_lang->finalizing) {
return; // inside CSharpLanguage::finish(), all the gchandle bindings are released there
}
GD_MONO_ASSERT_THREAD_ATTACHED;
{
- MutexLock lock(language_bind_mutex);
+ MutexLock lock(csharp_lang->language_bind_mutex);
- Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_data;
+ Map<Object *, CSharpScriptBinding>::Element *data = (Map<Object *, CSharpScriptBinding>::Element *)p_binding;
CSharpScriptBinding &script_binding = data->value();
@@ -1473,99 +1506,122 @@ void CSharpLanguage::free_instance_binding_data(void *p_data) {
script_binding.gchandle.release();
}
- script_bindings.erase(data);
+ csharp_lang->script_bindings.erase(data);
}
}
-void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) {
- Reference *ref_owner = Object::cast_to<Reference>(p_object);
+GDNativeBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference) {
+ CRASH_COND(!p_binding);
+
+ CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)p_binding)->get();
+
+ RefCounted *rc_owner = Object::cast_to<RefCounted>(script_binding.owner);
#ifdef DEBUG_ENABLED
- CRASH_COND(!ref_owner);
- CRASH_COND(!p_object->has_script_instance_binding(get_language_index()));
+ CRASH_COND(!rc_owner);
#endif
- void *data = p_object->get_script_instance_binding(get_language_index());
- CRASH_COND(!data);
-
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
MonoGCHandleData &gchandle = script_binding.gchandle;
+ int refcount = rc_owner->reference_get_count();
+
if (!script_binding.inited) {
- return;
+ return refcount == 0;
}
- if (ref_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
+ if (p_reference) {
+ // Refcount incremented
+ if (refcount > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ GD_MONO_SCOPE_THREAD_ATTACH;
- // The reference count was increased after the managed side was the only one referencing our owner.
- // This means the owner is being referenced again by the unmanaged side,
- // so the owner must hold the managed side alive again to avoid it from being GCed.
+ // The reference count was increased after the managed side was the only one referencing our owner.
+ // This means the owner is being referenced again by the unmanaged side,
+ // so the owner must hold the managed side alive again to avoid it from being GCed.
- MonoObject *target = gchandle.get_target();
- if (!target) {
- return; // Called after the managed side was collected, so nothing to do here
- }
+ MonoObject *target = gchandle.get_target();
+ if (!target) {
+ return false; // Called after the managed side was collected, so nothing to do here
+ }
- // Release the current weak handle and replace it with a strong handle.
- MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target);
- gchandle.release();
- gchandle = strong_gchandle;
- }
-}
+ // Release the current weak handle and replace it with a strong handle.
+ MonoGCHandleData strong_gchandle = MonoGCHandleData::new_strong_handle(target);
+ gchandle.release();
+ gchandle = strong_gchandle;
+ }
-bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
- Reference *ref_owner = Object::cast_to<Reference>(p_object);
+ return false;
+ } else {
+ // Refcount decremented
+ if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ GD_MONO_SCOPE_THREAD_ATTACH;
-#ifdef DEBUG_ENABLED
- CRASH_COND(!ref_owner);
- CRASH_COND(!p_object->has_script_instance_binding(get_language_index()));
-#endif
+ // If owner owner is no longer referenced by the unmanaged side,
+ // the managed instance takes responsibility of deleting the owner when GCed.
- void *data = p_object->get_script_instance_binding(get_language_index());
- CRASH_COND(!data);
+ MonoObject *target = gchandle.get_target();
+ if (!target) {
+ return refcount == 0; // Called after the managed side was collected, so nothing to do here
+ }
- CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
- MonoGCHandleData &gchandle = script_binding.gchandle;
+ // Release the current strong handle and replace it with a weak handle.
+ MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target);
+ gchandle.release();
+ gchandle = weak_gchandle;
- int refcount = ref_owner->reference_get_count();
+ return false;
+ }
- if (!script_binding.inited) {
return refcount == 0;
}
+}
- if (refcount == 1 && !gchandle.is_released() && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
- GD_MONO_SCOPE_THREAD_ATTACH;
+void *CSharpLanguage::get_instance_binding(Object *p_object) {
+ void *binding = p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks);
- // If owner owner is no longer referenced by the unmanaged side,
- // the managed instance takes responsibility of deleting the owner when GCed.
+ // Initially this was in `_instance_binding_create_callback`. However, after the new instance
+ // binding re-write it was resulting in a deadlock in `_instance_binding_reference`, as
+ // `setup_csharp_script_binding` may call `reference()`. It was moved here outside to fix that.
- MonoObject *target = gchandle.get_target();
- if (!target) {
- return refcount == 0; // Called after the managed side was collected, so nothing to do here
- }
+ if (binding) {
+ CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)binding)->value();
- // Release the current strong handle and replace it with a weak handle.
- MonoGCHandleData weak_gchandle = MonoGCHandleData::new_weak_handle(target);
- gchandle.release();
- gchandle = weak_gchandle;
+ if (!script_binding.inited) {
+ MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
- return false;
+ if (!script_binding.inited) { // Another thread may have set it up
+ CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, p_object);
+ }
+ }
}
- return refcount == 0;
+ return binding;
+}
+
+void *CSharpLanguage::get_existing_instance_binding(Object *p_object) {
+#ifdef DEBUG_ENABLED
+ CRASH_COND(p_object->has_instance_binding(p_object));
+#endif
+ return p_object->get_instance_binding(get_singleton(), &_instance_binding_callbacks);
+}
+
+void CSharpLanguage::set_instance_binding(Object *p_object, void *p_binding) {
+ p_object->set_instance_binding(get_singleton(), p_binding, &_instance_binding_callbacks);
+}
+
+bool CSharpLanguage::has_instance_binding(Object *p_object) {
+ return p_object->has_instance_binding(get_singleton());
}
CSharpInstance *CSharpInstance::create_for_managed_type(Object *p_owner, CSharpScript *p_script, const MonoGCHandleData &p_gchandle) {
CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(p_script)));
- Reference *ref = Object::cast_to<Reference>(p_owner);
+ RefCounted *rc = Object::cast_to<RefCounted>(p_owner);
- instance->base_ref = ref != nullptr;
+ instance->base_ref_counted = rc != nullptr;
instance->owner = p_owner;
instance->gchandle = p_gchandle;
- if (instance->base_ref) {
+ if (instance->base_ref_counted) {
instance->_reference_owner_unsafe();
}
@@ -1604,7 +1660,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;
}
@@ -1701,12 +1757,12 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
}
void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Variant>> &r_state) {
- List<PropertyInfo> pinfo;
- get_property_list(&pinfo);
+ List<PropertyInfo> property_list;
+ get_property_list(&property_list);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ for (const PropertyInfo &prop_info : property_list) {
Pair<StringName, Variant> state_pair;
- state_pair.first = E->get().name;
+ state_pair.first = prop_info.name;
ManagedType managedType;
@@ -1729,8 +1785,8 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName,
MonoObject *owner_managed = get_mono_object();
ERR_FAIL_NULL(owner_managed);
- for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
- const CSharpScript::EventSignal &event_signal = E->value();
+ for (const KeyValue<StringName, CSharpScript::EventSignal> &E : script->event_signals) {
+ const CSharpScript::EventSignal &event_signal = E.value;
MonoDelegate *delegate_field_value = (MonoDelegate *)event_signal.field->get_value(owner_managed);
if (!delegate_field_value) {
@@ -1757,8 +1813,9 @@ void CSharpInstance::get_event_signals_state_for_reloading(List<Pair<StringName,
}
void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
- for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) {
- 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
@@ -1781,9 +1838,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;
@@ -1791,6 +1847,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 {
@@ -1808,6 +1868,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;
@@ -1867,7 +1950,7 @@ Variant CSharpInstance::call(const StringName &p_method, const Variant **p_args,
bool CSharpInstance::_reference_owner_unsafe() {
#ifdef DEBUG_ENABLED
- CRASH_COND(!base_ref);
+ CRASH_COND(!base_ref_counted);
CRASH_COND(owner == nullptr);
CRASH_COND(unsafe_referenced); // already referenced
#endif
@@ -1878,7 +1961,7 @@ bool CSharpInstance::_reference_owner_unsafe() {
// See: _unreference_owner_unsafe()
// May not me referenced yet, so we must use init_ref() instead of reference()
- if (static_cast<Reference *>(owner)->init_ref()) {
+ if (static_cast<RefCounted *>(owner)->init_ref()) {
CSharpLanguage::get_singleton()->post_unsafe_reference(owner);
unsafe_referenced = true;
}
@@ -1888,7 +1971,7 @@ bool CSharpInstance::_reference_owner_unsafe() {
bool CSharpInstance::_unreference_owner_unsafe() {
#ifdef DEBUG_ENABLED
- CRASH_COND(!base_ref);
+ CRASH_COND(!base_ref_counted);
CRASH_COND(owner == nullptr);
#endif
@@ -1905,7 +1988,7 @@ bool CSharpInstance::_unreference_owner_unsafe() {
// Destroying the owner here means self destructing, so we defer the owner destruction to the caller.
CSharpLanguage::get_singleton()->pre_unsafe_unreference(owner);
- return static_cast<Reference *>(owner)->unreference();
+ return static_cast<RefCounted *>(owner)->unreference();
}
MonoObject *CSharpInstance::_internal_new_managed() {
@@ -1937,7 +2020,7 @@ MonoObject *CSharpInstance::_internal_new_managed() {
// Tie managed to unmanaged
gchandle = MonoGCHandleData::new_strong_handle(mono_object);
- if (base_ref) {
+ if (base_ref_counted) {
_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
}
@@ -1950,10 +2033,11 @@ MonoObject *CSharpInstance::_internal_new_managed() {
}
void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
+ // Must make sure event signals are not left dangling
disconnect_event_signals();
#ifdef DEBUG_ENABLED
- CRASH_COND(base_ref);
+ CRASH_COND(base_ref_counted);
CRASH_COND(gchandle.is_released());
#endif
CSharpLanguage::get_singleton()->release_script_gchandle(p_obj, gchandle);
@@ -1961,10 +2045,13 @@ void CSharpInstance::mono_object_disposed(MonoObject *p_obj) {
void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance) {
#ifdef DEBUG_ENABLED
- CRASH_COND(!base_ref);
+ CRASH_COND(!base_ref_counted);
CRASH_COND(gchandle.is_released());
#endif
+ // Must make sure event signals are not left dangling
+ disconnect_event_signals();
+
r_remove_script_instance = false;
if (_unreference_owner_unsafe()) {
@@ -1993,41 +2080,38 @@ void CSharpInstance::mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_f
}
void CSharpInstance::connect_event_signals() {
- for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
- const CSharpScript::EventSignal &event_signal = E->value();
+ for (const KeyValue<StringName, CSharpScript::EventSignal> &E : script->event_signals) {
+ const CSharpScript::EventSignal &event_signal = E.value;
StringName signal_name = event_signal.field->get_name();
// TODO: Use pooling for ManagedCallable instances.
- auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
+ EventSignalCallable *event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
- owner->connect(signal_name, Callable(event_signal_callable));
+ Callable callable(event_signal_callable);
+ connected_event_signals.push_back(callable);
+ owner->connect(signal_name, callable);
}
}
void CSharpInstance::disconnect_event_signals() {
- for (const Map<StringName, CSharpScript::EventSignal>::Element *E = script->event_signals.front(); E; E = E->next()) {
- const CSharpScript::EventSignal &event_signal = E->value();
-
- StringName signal_name = event_signal.field->get_name();
-
- // TODO: It would be great if we could store this EventSignalCallable on the stack.
- // The problem is that Callable memdeletes it when it's destructed...
- auto event_signal_callable = memnew(EventSignalCallable(owner, &event_signal));
-
- owner->disconnect(signal_name, Callable(event_signal_callable));
+ for (const Callable &callable : connected_event_signals) {
+ const EventSignalCallable *event_signal_callable = static_cast<const EventSignalCallable *>(callable.get_custom());
+ owner->disconnect(event_signal_callable->get_signal(), callable);
}
+
+ connected_event_signals.clear();
}
void CSharpInstance::refcount_incremented() {
#ifdef DEBUG_ENABLED
- CRASH_COND(!base_ref);
+ CRASH_COND(!base_ref_counted);
CRASH_COND(owner == nullptr);
#endif
- Reference *ref_owner = Object::cast_to<Reference>(owner);
+ RefCounted *rc_owner = Object::cast_to<RefCounted>(owner);
- if (ref_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
+ if (rc_owner->reference_get_count() > 1 && gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
GD_MONO_SCOPE_THREAD_ATTACH;
// The reference count was increased after the managed side was the only one referencing our owner.
@@ -2043,13 +2127,13 @@ void CSharpInstance::refcount_incremented() {
bool CSharpInstance::refcount_decremented() {
#ifdef DEBUG_ENABLED
- CRASH_COND(!base_ref);
+ CRASH_COND(!base_ref_counted);
CRASH_COND(owner == nullptr);
#endif
- Reference *ref_owner = Object::cast_to<Reference>(owner);
+ RefCounted *rc_owner = Object::cast_to<RefCounted>(owner);
- int refcount = ref_owner->reference_get_count();
+ int refcount = rc_owner->reference_get_count();
if (refcount == 1 && !gchandle.is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
GD_MONO_SCOPE_THREAD_ATTACH;
@@ -2070,46 +2154,10 @@ bool CSharpInstance::refcount_decremented() {
return ref_dying;
}
-Vector<ScriptNetData> CSharpInstance::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> CSharpInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
-uint16_t CSharpInstance::get_rpc_method_id(const StringName &p_method) const {
- return script->get_rpc_method_id(p_method);
-}
-
-StringName CSharpInstance::get_rpc_method(const uint16_t p_rpc_method_id) const {
- return script->get_rpc_method(p_rpc_method_id);
-}
-
-MultiplayerAPI::RPCMode CSharpInstance::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
- return script->get_rpc_mode_by_id(p_rpc_method_id);
-}
-
-MultiplayerAPI::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method) const {
- return script->get_rpc_mode(p_method);
-}
-
-Vector<ScriptNetData> CSharpInstance::get_rset_properties() const {
- return script->get_rset_properties();
-}
-
-uint16_t CSharpInstance::get_rset_property_id(const StringName &p_variable) const {
- return script->get_rset_property_id(p_variable);
-}
-
-StringName CSharpInstance::get_rset_property(const uint16_t p_rset_member_id) const {
- return script->get_rset_property(p_rset_member_id);
-}
-
-MultiplayerAPI::RPCMode CSharpInstance::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
- return script->get_rset_mode_by_id(p_rset_member_id);
-}
-
-MultiplayerAPI::RPCMode CSharpInstance::get_rset_mode(const StringName &p_variable) const {
- return script->get_rset_mode(p_variable);
-}
-
void CSharpInstance::notification(int p_notification) {
GD_MONO_SCOPE_THREAD_ATTACH;
@@ -2120,12 +2168,12 @@ void CSharpInstance::notification(int p_notification) {
predelete_notified = true;
- if (base_ref) {
- // It's not safe to proceed if the owner derives Reference and the refcount reached 0.
+ if (base_ref_counted) {
+ // It's not safe to proceed if the owner derives RefCounted and the refcount reached 0.
// At this point, Dispose() was already called (manually or from the finalizer) so
// that's not a problem. The refcount wouldn't have reached 0 otherwise, since the
// managed side references it and Dispose() needs to be called to release it.
- // However, this means C# Reference scripts can't receive NOTIFICATION_PREDELETE, but
+ // However, this means C# RefCounted scripts can't receive NOTIFICATION_PREDELETE, but
// this is likely the case with GDScript as well: https://github.com/godotengine/godot/issues/6784
return;
}
@@ -2224,6 +2272,9 @@ CSharpInstance::~CSharpInstance() {
destructing_script_instance = true;
+ // Must make sure event signals are not left dangling
+ disconnect_event_signals();
+
if (!gchandle.is_released()) {
if (!predelete_notified && !ref_dying) {
// This destructor is not called from the owners destructor.
@@ -2248,15 +2299,15 @@ CSharpInstance::~CSharpInstance() {
}
// If not being called from the owner's destructor, and we still hold a reference to the owner
- if (base_ref && !ref_dying && owner && unsafe_referenced) {
+ if (base_ref_counted && !ref_dying && owner && unsafe_referenced) {
// The owner's script or script instance is being replaced (or removed)
// Transfer ownership to an "instance binding"
- Reference *ref_owner = static_cast<Reference *>(owner);
+ RefCounted *rc_owner = static_cast<RefCounted *>(owner);
// We will unreference the owner before referencing it again, so we need to keep it alive
- Ref<Reference> scope_keep_owner_alive(ref_owner);
+ Ref<RefCounted> scope_keep_owner_alive(rc_owner);
(void)scope_keep_owner_alive;
// Unreference the owner here, before the new "instance binding" references it.
@@ -2264,24 +2315,14 @@ CSharpInstance::~CSharpInstance() {
bool die = _unreference_owner_unsafe();
CRASH_COND(die); // `owner_keep_alive` holds a reference, so it can't die
- void *data = owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
+ void *data = CSharpLanguage::get_instance_binding(owner);
CRASH_COND(data == nullptr);
-
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
-
- if (!script_binding.inited) {
- MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
-
- if (!script_binding.inited) { // Other thread may have set it up
- // Already had a binding that needs to be setup
- CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, owner);
- CRASH_COND(!script_binding.inited);
- }
- }
+ CRASH_COND(!script_binding.inited);
#ifdef DEBUG_ENABLED
// The "instance binding" holds a reference so the refcount should be at least 2 before `scope_keep_owner_alive` goes out of scope
- CRASH_COND(ref_owner->reference_get_count() <= 1);
+ CRASH_COND(rc_owner->reference_get_count() <= 1);
#endif
}
@@ -2311,12 +2352,12 @@ void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List
base_cache->_update_exports_values(values, propnames);
}
- for (Map<StringName, Variant>::Element *E = exported_members_defval_cache.front(); E; E = E->next()) {
- values[E->key()] = E->get();
+ for (const KeyValue<StringName, Variant> &E : exported_members_defval_cache) {
+ values[E.key] = E.value;
}
- for (List<PropertyInfo>::Element *E = exported_members_cache.front(); E; E = E->next()) {
- propnames.push_back(E->get());
+ for (const PropertyInfo &prop_info : exported_members_cache) {
+ propnames.push_back(prop_info);
}
}
@@ -2368,7 +2409,7 @@ void CSharpScript::_update_member_info_no_exports() {
}
#endif
-bool CSharpScript::_update_exports() {
+bool CSharpScript::_update_exports(PlaceHolderScriptInstance *p_instance_to_update) {
#ifdef TOOLS_ENABLED
bool is_editor = Engine::get_singleton()->is_editor_hint();
if (is_editor) {
@@ -2509,7 +2550,7 @@ bool CSharpScript::_update_exports() {
#ifdef TOOLS_ENABLED
if (is_editor) {
// Need to check this here, before disposal
- bool base_ref = Object::cast_to<Reference>(tmp_native) != nullptr;
+ bool base_ref_counted = Object::cast_to<RefCounted>(tmp_native) != nullptr;
// Dispose the temporary managed instance
@@ -2524,7 +2565,7 @@ bool CSharpScript::_update_exports() {
GDMonoUtils::free_gchandle(tmp_pinned_gchandle);
tmp_object = nullptr;
- if (tmp_native && !base_ref) {
+ if (tmp_native && !base_ref_counted) {
Node *node = Object::cast_to<Node>(tmp_native);
if (node && node->is_inside_tree()) {
ERR_PRINT("Temporary instance was added to the scene tree.");
@@ -2540,14 +2581,18 @@ bool CSharpScript::_update_exports() {
if (is_editor) {
placeholder_fallback_enabled = false;
- if (placeholders.size()) {
+ if ((changed || p_instance_to_update) && placeholders.size()) {
// Update placeholders if any
Map<StringName, Variant> values;
List<PropertyInfo> propnames;
_update_exports_values(values, propnames);
- for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
- E->get()->update(propnames, values);
+ if (changed) {
+ for (PlaceHolderScriptInstance *&script_instance : placeholders) {
+ script_instance->update(propnames, values);
+ }
+ } else {
+ p_instance_to_update->update(propnames, values);
}
}
}
@@ -2595,7 +2640,7 @@ void CSharpScript::load_script_signals(GDMonoClass *p_class, GDMonoClass *p_nati
MonoCustomAttrInfo *event_attrs = mono_custom_attrs_from_event(top->get_mono_ptr(), raw_event);
if (event_attrs) {
if (mono_custom_attrs_has_attr(event_attrs, CACHED_CLASS(SignalAttribute)->get_mono_ptr())) {
- const char *event_name = mono_event_get_name(raw_event);
+ String event_name = String::utf8(mono_event_get_name(raw_event));
found_event_signals.push_back(StringName(event_name));
}
@@ -2799,7 +2844,7 @@ int CSharpScript::_try_get_member_export_hint(IMonoClassMember *p_member, Manage
name_only_hint_string += ",";
}
- String enum_field_name = mono_field_get_name(field);
+ String enum_field_name = String::utf8(mono_field_get_name(field));
r_hint_string += enum_field_name;
name_only_hint_string += enum_field_name;
@@ -2851,12 +2896,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;
+ }
+ }
- ERR_FAIL_COND_V_MSG(hint_res == -1, -1, "Error while trying to determine information about the array element type.");
+ 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.");
+
+ // 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) {
@@ -2924,7 +2981,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() {
@@ -2955,13 +3012,24 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
CRASH_COND(p_script->native == nullptr);
+ p_script->valid = true;
+
+ update_script_class_info(p_script);
+
+#ifdef TOOLS_ENABLED
+ p_script->_update_member_info_no_exports();
+#endif
+}
+
+// Extract information about the script using the mono class.
+void CSharpScript::update_script_class_info(Ref<CSharpScript> p_script) {
GDMonoClass *base = p_script->script_class->get_parent_class();
+ // `base` should only be set if the script is a user defined type.
if (base != p_script->native) {
p_script->base = base;
}
- p_script->valid = true;
p_script->tool = p_script->script_class->has_attribute(CACHED_CLASS(ToolAttribute));
if (!p_script->tool) {
@@ -2969,7 +3037,7 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
p_script->tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute));
}
-#if TOOLS_ENABLED
+#ifdef TOOLS_ENABLED
if (!p_script->tool) {
p_script->tool = p_script->script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly();
}
@@ -2996,20 +3064,44 @@ void CSharpScript::initialize_for_managed_type(Ref<CSharpScript> p_script, GDMon
p_script->script_class->fetch_methods_with_godot_api_checks(p_script->native);
- // Need to fetch method from base classes as well
+ p_script->rpc_functions.clear();
+
GDMonoClass *top = p_script->script_class;
while (top && top != p_script->native) {
+ // Fetch methods from base classes as well
top->fetch_methods_with_godot_api_checks(p_script->native);
+
+ // Update RPC info
+ {
+ Vector<GDMonoMethod *> methods = top->get_all_methods();
+ for (int i = 0; i < methods.size(); i++) {
+ if (!methods[i]->is_static()) {
+ Multiplayer::RPCMode mode = p_script->_member_get_rpc_mode(methods[i]);
+ if (Multiplayer::RPC_MODE_DISABLED != mode) {
+ Multiplayer::RPCConfig nd;
+ nd.name = methods[i]->get_name();
+ nd.rpc_mode = mode;
+ // TODO Transfer mode, channel
+ nd.transfer_mode = Multiplayer::TRANSFER_MODE_RELIABLE;
+ nd.channel = 0;
+ if (-1 == p_script->rpc_functions.find(nd)) {
+ p_script->rpc_functions.push_back(nd);
+ }
+ }
+ }
+ }
+ }
+
top = top->get_parent_class();
}
+ // Sort so we are 100% that they are always the same.
+ p_script->rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
+
p_script->load_script_signals(p_script->script_class, p_script->native);
-#ifdef TOOLS_ENABLED
- p_script->_update_member_info_no_exports();
-#endif
}
-bool CSharpScript::can_instance() const {
+bool CSharpScript::can_instantiate() const {
#ifdef TOOLS_ENABLED
bool extra_cond = tool || ScriptServer::is_scripting_enabled();
#else
@@ -3040,7 +3132,7 @@ StringName CSharpScript::get_instance_base_type() const {
}
}
-CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error) {
+CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) {
GD_MONO_ASSERT_THREAD_ATTACHED;
/* STEP 1, CREATE */
@@ -3051,20 +3143,20 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
ERR_FAIL_COND_V_MSG(p_argcount == 0, nullptr,
"Cannot create script instance. The class '" + script_class->get_full_name() +
"' does not define a parameterless constructor." +
- (get_path().empty() ? String() : " Path: '" + get_path() + "'."));
+ (get_path().is_empty() ? String() : " Path: '" + get_path() + "'."));
ERR_FAIL_V_MSG(nullptr, "Constructor not found.");
}
- Ref<Reference> ref;
- if (p_isref) {
+ Ref<RefCounted> ref;
+ if (p_is_ref_counted) {
// Hold it alive. Important if we have to dispose a script instance binding before creating the CSharpInstance.
- ref = Ref<Reference>(static_cast<Reference *>(p_owner));
+ ref = Ref<RefCounted>(static_cast<RefCounted *>(p_owner));
}
// If the object had a script instance binding, dispose it before adding the CSharpInstance
- if (p_owner->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index())) {
- void *data = p_owner->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
+ if (CSharpLanguage::has_instance_binding(p_owner)) {
+ void *data = CSharpLanguage::get_existing_instance_binding(p_owner);
CRASH_COND(data == nullptr);
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
@@ -3085,7 +3177,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
}
CSharpInstance *instance = memnew(CSharpInstance(Ref<CSharpScript>(this)));
- instance->base_ref = p_isref;
+ instance->base_ref_counted = p_is_ref_counted;
instance->owner = p_owner;
instance->owner->set_script_instance(instance);
@@ -3110,7 +3202,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
// Tie managed to unmanaged
instance->gchandle = MonoGCHandleData::new_strong_handle(mono_object);
- if (instance->base_ref) {
+ if (instance->base_ref_counted) {
instance->_reference_owner_unsafe(); // Here, after assigning the gchandle (for the refcount_incremented callback)
}
@@ -3142,10 +3234,10 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
GD_MONO_SCOPE_THREAD_ATTACH;
- Object *owner = ClassDB::instance(NATIVE_GDMONOCLASS_NAME(native));
+ Object *owner = ClassDB::instantiate(NATIVE_GDMONOCLASS_NAME(native));
REF ref;
- Reference *r = Object::cast_to<Reference>(owner);
+ RefCounted *r = Object::cast_to<RefCounted>(owner);
if (r) {
ref = REF(r);
}
@@ -3153,7 +3245,7 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Callable::Cal
CSharpInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error);
if (!instance) {
if (ref.is_null()) {
- memdelete(owner); //no owner, sorry
+ memdelete(owner); // no owner, sorry
}
return Variant();
}
@@ -3176,24 +3268,23 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
if (EngineDebugger::is_active()) {
CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0,
"Script inherits from native type '" + String(native_name) +
- "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
+ "', 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 instanced 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() + "'.");
}
}
GD_MONO_SCOPE_THREAD_ATTACH;
Callable::CallError unchecked_error;
- return _create_instance(nullptr, 0, p_this, Object::cast_to<Reference>(p_this) != nullptr, unchecked_error);
+ return _create_instance(nullptr, 0, p_this, Object::cast_to<RefCounted>(p_this) != nullptr, unchecked_error);
}
PlaceHolderScriptInstance *CSharpScript::placeholder_instance_create(Object *p_this) {
#ifdef TOOLS_ENABLED
PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(CSharpLanguage::get_singleton(), Ref<Script>(this), p_this));
placeholders.insert(si);
- _update_exports();
+ _update_exports(si);
return si;
#else
return nullptr;
@@ -3206,7 +3297,7 @@ bool CSharpScript::instance_has(const Object *p_this) const {
}
bool CSharpScript::has_source_code() const {
- return !source.empty();
+ return !source.is_empty();
}
String CSharpScript::get_source_code() const {
@@ -3230,10 +3321,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();
}
}
@@ -3279,154 +3379,34 @@ Error CSharpScript::reload(bool p_keep_state) {
GD_MONO_SCOPE_THREAD_ATTACH;
- GDMonoAssembly *project_assembly = GDMono::get_singleton()->get_project_assembly();
+ const DotNetScriptLookupInfo *lookup_info =
+ CSharpLanguage::get_singleton()->lookup_dotnet_script(get_path());
- if (project_assembly) {
- const Variant *script_metadata_var = CSharpLanguage::get_singleton()->get_scripts_metadata().getptr(get_path());
- if (script_metadata_var) {
- Dictionary script_metadata = script_metadata_var->operator Dictionary()["class"];
- const Variant *namespace_ = script_metadata.getptr("namespace");
- const Variant *class_name = script_metadata.getptr("class_name");
- ERR_FAIL_NULL_V(namespace_, ERR_BUG);
- ERR_FAIL_NULL_V(class_name, ERR_BUG);
- GDMonoClass *klass = project_assembly->get_class(namespace_->operator String(), class_name->operator String());
- if (klass && CACHED_CLASS(GodotObject)->is_assignable_from(klass)) {
- script_class = klass;
- }
- } else {
- // Missing script metadata. Fallback to legacy method
- script_class = project_assembly->get_object_derived_class(name);
+ if (lookup_info) {
+ GDMonoClass *klass = lookup_info->script_class;
+ if (klass) {
+ ERR_FAIL_COND_V(!CACHED_CLASS(GodotObject)->is_assignable_from(klass), FAILED);
+ script_class = klass;
}
+ }
- valid = script_class != nullptr;
+ valid = script_class != nullptr;
- if (script_class) {
+ if (script_class) {
#ifdef DEBUG_ENABLED
- print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path());
-#endif
-
- tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute));
-
- if (!tool) {
- GDMonoClass *nesting_class = script_class->get_nesting_class();
- tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute));
- }
-
-#if TOOLS_ENABLED
- if (!tool) {
- tool = script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly();
- }
+ print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path());
#endif
- native = GDMonoUtils::get_class_native_base(script_class);
-
- CRASH_COND(native == nullptr);
-
- GDMonoClass *base_class = script_class->get_parent_class();
-
- if (base_class != native) {
- base = base_class;
- }
-
-#ifdef DEBUG_ENABLED
- // For debug builds, we must fetch from all native base methods as well.
- // Native base methods must be fetched before the current class.
- // Not needed if the script class itself is a native class.
+ native = GDMonoUtils::get_class_native_base(script_class);
- if (script_class != native) {
- GDMonoClass *native_top = native;
- while (native_top) {
- native_top->fetch_methods_with_godot_api_checks(native);
+ CRASH_COND(native == nullptr);
- if (native_top == CACHED_CLASS(GodotObject)) {
- break;
- }
+ update_script_class_info(this);
- native_top = native_top->get_parent_class();
- }
- }
-#endif
-
- script_class->fetch_methods_with_godot_api_checks(native);
-
- // Need to fetch method from base classes as well
- GDMonoClass *top = script_class;
- while (top && top != native) {
- top->fetch_methods_with_godot_api_checks(native);
- top = top->get_parent_class();
- }
-
- load_script_signals(script_class, native);
- _update_exports();
- }
-
- rpc_functions.clear();
- rpc_variables.clear();
-
- GDMonoClass *top = script_class;
- while (top && top != native) {
- {
- Vector<GDMonoMethod *> methods = top->get_all_methods();
- for (int i = 0; i < methods.size(); i++) {
- if (!methods[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(methods[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = methods[i]->get_name();
- nd.mode = mode;
- if (-1 == rpc_functions.find(nd)) {
- rpc_functions.push_back(nd);
- }
- }
- }
- }
- }
-
- {
- Vector<GDMonoField *> fields = top->get_all_fields();
- for (int i = 0; i < fields.size(); i++) {
- if (!fields[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(fields[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = fields[i]->get_name();
- nd.mode = mode;
- if (-1 == rpc_variables.find(nd)) {
- rpc_variables.push_back(nd);
- }
- }
- }
- }
- }
-
- {
- Vector<GDMonoProperty *> properties = top->get_all_properties();
- for (int i = 0; i < properties.size(); i++) {
- if (!properties[i]->is_static()) {
- MultiplayerAPI::RPCMode mode = _member_get_rpc_mode(properties[i]);
- if (MultiplayerAPI::RPC_MODE_DISABLED != mode) {
- ScriptNetData nd;
- nd.name = properties[i]->get_name();
- nd.mode = mode;
- if (-1 == rpc_variables.find(nd)) {
- rpc_variables.push_back(nd);
- }
- }
- }
- }
- }
-
- top = top->get_parent_class();
- }
-
- // Sort so we are 100% that they are always the same.
- rpc_functions.sort_custom<SortNetData>();
- rpc_variables.sort_custom<SortNetData>();
-
- return OK;
+ _update_exports();
}
- return ERR_FILE_MISSING_DEPENDENCIES;
+ return OK;
}
ScriptLanguage *CSharpScript::get_language() const {
@@ -3461,11 +3441,11 @@ bool CSharpScript::has_script_signal(const StringName &p_signal) const {
}
void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
- for (const Map<StringName, Vector<SignalParameter>>::Element *E = _signals.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, Vector<SignalParameter>> &E : _signals) {
MethodInfo mi;
- mi.name = E->key();
+ mi.name = E.key;
- const Vector<SignalParameter> &params = E->value();
+ const Vector<SignalParameter> &params = E.value;
for (int i = 0; i < params.size(); i++) {
const SignalParameter &param = params[i];
@@ -3480,11 +3460,11 @@ void CSharpScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
r_signals->push_back(mi);
}
- for (const Map<StringName, EventSignal>::Element *E = event_signals.front(); E; E = E->next()) {
+ for (const KeyValue<StringName, EventSignal> &E : event_signals) {
MethodInfo mi;
- mi.name = E->key();
+ mi.name = E.key;
- const EventSignal &event_signal = E->value();
+ const EventSignal &event_signal = E.value;
const Vector<SignalParameter> &params = event_signal.parameters;
for (int i = 0; i < params.size(); i++) {
const SignalParameter &param = params[i];
@@ -3523,9 +3503,15 @@ Ref<Script> CSharpScript::get_base_script() const {
return Ref<Script>();
}
-void CSharpScript::get_script_property_list(List<PropertyInfo> *p_list) const {
- for (Map<StringName, PropertyInfo>::Element *E = member_info.front(); E; E = E->next()) {
- 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);
}
}
@@ -3534,91 +3520,29 @@ 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_REMOTE;
- }
- if (p_member->has_attribute(CACHED_CLASS(MasterAttribute))) {
- return MultiplayerAPI::RPC_MODE_MASTER;
- }
- if (p_member->has_attribute(CACHED_CLASS(PuppetAttribute))) {
- return MultiplayerAPI::RPC_MODE_PUPPET;
+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(RemoteSyncAttribute))) {
- return MultiplayerAPI::RPC_MODE_REMOTESYNC;
- }
- if (p_member->has_attribute(CACHED_CLASS(MasterSyncAttribute))) {
- return MultiplayerAPI::RPC_MODE_MASTERSYNC;
- }
- if (p_member->has_attribute(CACHED_CLASS(PuppetSyncAttribute))) {
- return MultiplayerAPI::RPC_MODE_PUPPETSYNC;
+ if (p_member->has_attribute(CACHED_CLASS(AuthorityAttribute))) {
+ return Multiplayer::RPC_MODE_AUTHORITY;
}
- return MultiplayerAPI::RPC_MODE_DISABLED;
+ return Multiplayer::RPC_MODE_DISABLED;
}
-Vector<ScriptNetData> CSharpScript::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> CSharpScript::get_rpc_methods() const {
return rpc_functions;
}
-uint16_t CSharpScript::get_rpc_method_id(const StringName &p_method) const {
- for (int i = 0; i < rpc_functions.size(); i++) {
- if (rpc_functions[i].name == p_method) {
- return i;
- }
- }
- return UINT16_MAX;
-}
-
-StringName CSharpScript::get_rpc_method(const uint16_t p_rpc_method_id) const {
- ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), StringName());
- return rpc_functions[p_rpc_method_id].name;
-}
-
-MultiplayerAPI::RPCMode CSharpScript::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
- ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), MultiplayerAPI::RPC_MODE_DISABLED);
- return rpc_functions[p_rpc_method_id].mode;
-}
-
-MultiplayerAPI::RPCMode CSharpScript::get_rpc_mode(const StringName &p_method) const {
- return get_rpc_mode_by_id(get_rpc_method_id(p_method));
-}
-
-Vector<ScriptNetData> CSharpScript::get_rset_properties() const {
- return rpc_variables;
-}
-
-uint16_t CSharpScript::get_rset_property_id(const StringName &p_variable) const {
- for (int i = 0; i < rpc_variables.size(); i++) {
- if (rpc_variables[i].name == p_variable) {
- return i;
- }
- }
- return UINT16_MAX;
-}
-
-StringName CSharpScript::get_rset_property(const uint16_t p_rset_member_id) const {
- ERR_FAIL_COND_V(p_rset_member_id >= rpc_variables.size(), StringName());
- return rpc_variables[p_rset_member_id].name;
-}
-
-MultiplayerAPI::RPCMode CSharpScript::get_rset_mode_by_id(const uint16_t p_rset_member_id) const {
- ERR_FAIL_COND_V(p_rset_member_id >= rpc_functions.size(), MultiplayerAPI::RPC_MODE_DISABLED);
- return rpc_functions[p_rset_member_id].mode;
-}
-
-MultiplayerAPI::RPCMode CSharpScript::get_rset_mode(const StringName &p_variable) const {
- return get_rset_mode_by_id(get_rset_property_id(p_variable));
-}
-
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;
@@ -3630,7 +3554,7 @@ Error CSharpScript::load_source_code(const String &p_path) {
void CSharpScript::_update_name() {
String path = get_path();
- if (!path.empty()) {
+ if (!path.is_empty()) {
name = get_path().get_file().get_basename();
}
}
@@ -3667,8 +3591,8 @@ CSharpScript::~CSharpScript() {
void CSharpScript::get_members(Set<StringName> *p_members) {
#if defined(TOOLS_ENABLED) || defined(DEBUG_ENABLED)
if (p_members) {
- for (Set<StringName>::Element *E = exported_members_names.front(); E; E = E->next()) {
- p_members->insert(E->get());
+ for (const StringName &member_name : exported_members_names) {
+ p_members->insert(member_name);
}
}
#endif
@@ -3676,7 +3600,7 @@ void CSharpScript::get_members(Set<StringName> *p_members) {
/*************** RESOURCE ***************/
-RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderCSharpScript::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) {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index f0b43a40f9..c998d9c1e4 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,10 +31,11 @@
#ifndef CSHARP_SCRIPT_H
#define CSHARP_SCRIPT_H
+#include "core/doc_data.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/script_language.h"
-#include "core/self_list.h"
+#include "core/object/script_language.h"
+#include "core/templates/self_list.h"
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono.h"
@@ -65,6 +66,18 @@ TScriptInstance *cast_script_instance(ScriptInstance *p_inst) {
#define CAST_CSHARP_INSTANCE(m_inst) (cast_script_instance<CSharpInstance, CSharpLanguage>(m_inst))
+struct DotNetScriptLookupInfo {
+ String class_namespace;
+ String class_name;
+ GDMonoClass *script_class = nullptr;
+
+ DotNetScriptLookupInfo() {} // Required by HashMap...
+
+ DotNetScriptLookupInfo(const String &p_class_namespace, const String &p_class_name, GDMonoClass *p_script_class) :
+ class_namespace(p_class_namespace), class_name(p_class_name), script_class(p_script_class) {
+ }
+};
+
class CSharpScript : public Script {
GDCLASS(CSharpScript, Script);
@@ -123,8 +136,7 @@ private:
Map<StringName, EventSignal> event_signals;
bool signals_invalidated = true;
- Vector<ScriptNetData> rpc_functions;
- Vector<ScriptNetData> rpc_variables;
+ Vector<Multiplayer::RPCConfig> rpc_functions;
#ifdef TOOLS_ENABLED
List<PropertyInfo> exported_members_cache; // members_cache
@@ -142,7 +154,7 @@ private:
Set<StringName> exported_members_names;
#endif
- Map<StringName, PropertyInfo> member_info;
+ OrderedHashMap<StringName, PropertyInfo> member_info;
void _clear();
@@ -151,22 +163,23 @@ private:
void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
bool _get_signal(GDMonoClass *p_class, GDMonoMethod *p_delegate_invoke, Vector<SignalParameter> &params);
- bool _update_exports();
+ bool _update_exports(PlaceHolderScriptInstance *p_instance_to_update = nullptr);
bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported);
#ifdef TOOLS_ENABLED
static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
#endif
- CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error);
+ CSharpInstance *_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error);
Variant _new(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
// Do not use unless you know what you are doing
friend void GDMonoInternals::tie_managed_to_unmanaged(MonoObject *, Object *);
static Ref<CSharpScript> create_for_managed_type(GDMonoClass *p_class, GDMonoClass *p_native);
+ 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();
@@ -178,7 +191,7 @@ protected:
void _get_property_list(List<PropertyInfo> *p_properties) const;
public:
- bool can_instance() const override;
+ bool can_instantiate() const override;
StringName get_instance_base_type() const override;
ScriptInstance *instance_create(Object *p_this) override;
PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) override;
@@ -188,13 +201,21 @@ public:
String get_source_code() const override;
void set_source_code(const String &p_code) override;
+#ifdef TOOLS_ENABLED
+ virtual const Vector<DocData::ClassDoc> &get_documentation() const override {
+ // TODO
+ static Vector<DocData::ClassDoc> docs;
+ return docs;
+ }
+#endif // TOOLS_ENABLED
+
Error reload(bool p_keep_state = false) override;
bool has_script_signal(const StringName &p_signal) const override;
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;
@@ -213,17 +234,7 @@ public:
int get_member_line(const StringName &p_member) const override;
- Vector<ScriptNetData> get_rpc_methods() const override;
- uint16_t get_rpc_method_id(const StringName &p_method) const override;
- StringName get_rpc_method(const uint16_t p_rpc_method_id) const override;
- MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const override;
- MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
-
- Vector<ScriptNetData> get_rset_properties() const override;
- uint16_t get_rset_property_id(const StringName &p_variable) const override;
- StringName get_rset_property(const uint16_t p_variable_id) const override;
- MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const override;
- MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) 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; }
@@ -240,7 +251,7 @@ class CSharpInstance : public ScriptInstance {
friend class CSharpLanguage;
Object *owner = nullptr;
- bool base_ref = false;
+ bool base_ref_counted = false;
bool ref_dying = false;
bool unsafe_referenced = false;
bool predelete_notified = false;
@@ -249,6 +260,8 @@ class CSharpInstance : public ScriptInstance {
Ref<CSharpScript> script;
MonoGCHandleData gchandle;
+ List<Callable> connected_event_signals;
+
bool _reference_owner_unsafe();
/*
@@ -280,14 +293,14 @@ 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;
void mono_object_disposed(MonoObject *p_obj);
/*
- * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, ifevent_signal
+ * If 'r_delete_owner' is set to true, the caller must memdelete the script instance's owner. Otherwise, if
* 'r_remove_script_instance' is set to true, the caller must destroy the script instance by removing it from its owner.
*/
void mono_object_disposed_baseref(MonoObject *p_obj, bool p_is_finalizer, bool &r_delete_owner, bool &r_remove_script_instance);
@@ -298,17 +311,7 @@ public:
void refcount_incremented() override;
bool refcount_decremented() override;
- Vector<ScriptNetData> get_rpc_methods() const override;
- uint16_t get_rpc_method_id(const StringName &p_method) const override;
- StringName get_rpc_method(const uint16_t p_rpc_method_id) const override;
- MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const override;
- MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
-
- Vector<ScriptNetData> get_rset_properties() const override;
- uint16_t get_rset_property_id(const StringName &p_variable) const override;
- StringName get_rset_property(const uint16_t p_variable_id) const override;
- MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_variable_id) const override;
- MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const override;
+ const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
void notification(int p_notification) override;
void _call_notification(int p_notification);
@@ -380,16 +383,15 @@ class CSharpLanguage : public ScriptLanguage {
int lang_idx = -1;
- Dictionary scripts_metadata;
- bool scripts_metadata_invalidated = true;
+ HashMap<String, DotNetScriptLookupInfo> dotnet_script_lookup_map;
+
+ void lookup_script_for_class(GDMonoClass *p_class);
// For debug_break and debug_break_parse
int _debug_parse_err_line = -1;
String _debug_parse_err_file;
String _debug_error;
- void _load_scripts_metadata();
-
friend class GDMono;
void _on_scripts_domain_unloaded();
@@ -399,7 +401,18 @@ class CSharpLanguage : public ScriptLanguage {
static void _editor_init_callback();
#endif
+ static void *_instance_binding_create_callback(void *p_token, void *p_instance);
+ static void _instance_binding_free_callback(void *p_token, void *p_instance, void *p_binding);
+ static GDNativeBool _instance_binding_reference_callback(void *p_token, void *p_binding, GDNativeBool p_reference);
+
+ static GDNativeInstanceBindingCallbacks _instance_binding_callbacks;
+
public:
+ static void *get_instance_binding(Object *p_object);
+ static void *get_existing_instance_binding(Object *p_object);
+ static void set_instance_binding(Object *p_object, void *p_binding);
+ static bool has_instance_binding(Object *p_object);
+
StringNameCache string_names;
const Mutex &get_language_bind_mutex() { return language_bind_mutex; }
@@ -426,18 +439,13 @@ public:
void reload_assemblies(bool p_soft_reload);
#endif
- _FORCE_INLINE_ Dictionary get_scripts_metadata_or_nothing() {
- return scripts_metadata_invalidated ? Dictionary() : scripts_metadata;
- }
+ _FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; }
- _FORCE_INLINE_ const Dictionary &get_scripts_metadata() {
- if (scripts_metadata_invalidated) {
- _load_scripts_metadata();
- }
- return scripts_metadata;
- }
+ void lookup_scripts_in_assembly(GDMonoAssembly *p_assembly);
- _FORCE_INLINE_ ManagedCallableMiddleman *get_managed_callable_middleman() const { return managed_callable_middleman; }
+ const DotNetScriptLookupInfo *lookup_dotnet_script(const String &p_script_path) const {
+ return dotnet_script_lookup_map.getptr(p_script_path);
+ }
String get_name() const override;
@@ -452,14 +460,14 @@ public:
/* EDITOR FUNCTIONS */
void get_reserved_words(List<String> *p_words) const override;
+ bool is_control_flow_keyword(String p_keyword) const override;
void get_comment_delimiters(List<String> *p_delimiters) const override;
void get_string_delimiters(List<String> *p_delimiters) const override;
Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const override;
bool is_using_templates() override;
void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script) override;
- /* TODO */ bool validate(const String &p_script, int &r_line_error, int &r_col_error,
- String &r_test_error, const String &p_path, List<String> *r_functions,
- List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override {
+ /* TODO */ bool validate(const String &p_script, const String &p_path, List<String> *r_functions,
+ List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const override {
return true;
}
String validate_path(const String &p_path) const override;
@@ -510,12 +518,6 @@ public:
void thread_enter() override;
void thread_exit() override;
- // Don't use these. I'm watching you
- void *alloc_instance_binding_data(Object *p_object) override;
- void free_instance_binding_data(void *p_data) override;
- void refcount_incremented_instance_binding(Object *p_object) override;
- bool refcount_decremented_instance_binding(Object *p_object) override;
-
Map<Object *, CSharpScriptBinding>::Element *insert_script_binding(Object *p_object, const CSharpScriptBinding &p_script_binding);
bool setup_csharp_script_binding(CSharpScriptBinding &r_script_binding, Object *p_object);
@@ -532,7 +534,7 @@ public:
class ResourceFormatLoaderCSharpScript : public ResourceFormatLoader {
public:
- 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, bool p_no_cache = false) override;
+ 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) override;
void get_recognized_extensions(List<String> *p_extensions) const override;
bool handles_type(const String &p_type) const override;
String get_resource_type(const String &p_path) const override;
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.sln b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
index 56c0cb7703..d1868f52ef 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk.sln
@@ -2,6 +2,12 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.NET.Sdk", "Godot.NET.Sdk\Godot.NET.Sdk.csproj", "{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators", "Godot.SourceGenerators\Godot.SourceGenerators.csproj", "{32D31B23-2A45-4099-B4F5-95B4C8FF7D9F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Godot.SourceGenerators.Sample", "Godot.SourceGenerators.Sample\Godot.SourceGenerators.Sample.csproj", "{7297A614-8DF5-43DE-9EAD-99671B26BD1F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharp", "..\..\glue\GodotSharp\GodotSharp\GodotSharp.csproj", "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -12,5 +18,17 @@ Global
{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{31B00BFA-DEA1-42FA-A472-9E54A92A8A5F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {32D31B23-2A45-4099-B4F5-95B4C8FF7D9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {32D31B23-2A45-4099-B4F5-95B4C8FF7D9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {32D31B23-2A45-4099-B4F5-95B4C8FF7D9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {32D31B23-2A45-4099-B4F5-95B4C8FF7D9F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7297A614-8DF5-43DE-9EAD-99671B26BD1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7297A614-8DF5-43DE-9EAD-99671B26BD1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7297A614-8DF5-43DE-9EAD-99671B26BD1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7297A614-8DF5-43DE-9EAD-99671B26BD1F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AEBF0036-DA76-4341-B651-A3F2856AB2FA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
index 86a0a4393e..4e9e7184da 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
@@ -1,35 +1,41 @@
<Project Sdk="Microsoft.Build.NoTargets/2.0.1">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
+ <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Description>MSBuild .NET Sdk for Godot projects.</Description>
<Authors>Godot Engine contributors</Authors>
<PackageId>Godot.NET.Sdk</PackageId>
<Version>4.0.0</Version>
- <PackageVersion>4.0.0-dev2</PackageVersion>
- <PackageProjectUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</PackageProjectUrl>
+ <PackageVersion>$(PackageVersion_Godot_NET_Sdk)</PackageVersion>
+ <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</RepositoryUrl>
+ <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
<PackageType>MSBuildSdk</PackageType>
<PackageTags>MSBuildSdk</PackageTags>
+ <PackageLicenseExpression>MIT</PackageLicenseExpression>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
- </PropertyGroup>
- <PropertyGroup>
- <NuspecFile>Godot.NET.Sdk.nuspec</NuspecFile>
- <GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);SetNuSpecProperties</GenerateNuspecDependsOn>
+ <!-- Exclude target framework from the package dependencies as we don't include the build output -->
+ <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
+ <IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
- <Target Name="SetNuSpecProperties" Condition=" Exists('$(NuspecFile)') ">
+ <ItemGroup>
+ <!-- Package Sdk\Sdk.props and Sdk\Sdk.targets file -->
+ <None Include="Sdk\Sdk.props" Pack="true" PackagePath="Sdk" />
+ <None Include="Sdk\Sdk.targets" Pack="true" PackagePath="Sdk" />
+ <!-- SdkPackageVersions.props -->
+ <None Include="..\..\..\SdkPackageVersions.props" Pack="true" PackagePath="Sdk">
+ <Link>Sdk\SdkPackageVersions.props</Link>
+ </None>
+ </ItemGroup>
+
+ <Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack">
<PropertyGroup>
- <NuspecProperties>
- id=$(PackageId);
- description=$(Description);
- authors=$(Authors);
- version=$(PackageVersion);
- packagetype=$(PackageType);
- tags=$(PackageTags);
- projecturl=$(PackageProjectUrl)
- </NuspecProperties>
+ <GodotSourceRootPath>$(SolutionDir)\..\..\..\..\</GodotSourceRootPath>
+ <GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir>
</PropertyGroup>
+ <Copy SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg" DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
</Target>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec
deleted file mode 100644
index 5b5cefe80e..0000000000
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.nuspec
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8" ?>
-<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
- <metadata>
- <id>$id$</id>
- <version>$version$</version>
- <description>$description$</description>
- <authors>$authors$</authors>
- <owners>$authors$</owners>
- <projectUrl>$projecturl$</projectUrl>
- <requireLicenseAcceptance>false</requireLicenseAcceptance>
- <license type="expression">MIT</license>
- <licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
- <tags>$tags$</tags>
- <packageTypes>
- <packageType name="$packagetype$" />
- </packageTypes>
- <repository url="$projecturl$" />
- </metadata>
- <files>
- <file src="Sdk\**" target="Sdk" />\
- </files>
-</package>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
index 5febcf3175..0128f5c706 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
@@ -1,4 +1,6 @@
<Project>
+ <Import Project="$(MSBuildThisFileDirectory)\SdkPackageVersions.props" />
+
<PropertyGroup>
<!-- Determines if we should import Microsoft.NET.Sdk, if it wasn't already imported. -->
<GodotSdkImportsMicrosoftNetSdk Condition=" '$(UsingMicrosoftNETSdk)' != 'true' ">true</GodotSdkImportsMicrosoftNetSdk>
@@ -94,6 +96,7 @@
<DefineConstants>$(GodotDefineConstants);$(DefineConstants)</DefineConstants>
</PropertyGroup>
+ <!-- Godot API references -->
<ItemGroup>
<!--
TODO:
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 f5afd75505..92e299d2f3 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
@@ -14,4 +14,9 @@
-->
<DefineConstants Condition=" '$(GodotRealTIsDouble)' == 'true' ">GODOT_REAL_T_IS_DOUBLE;$(DefineConstants)</DefineConstants>
</PropertyGroup>
+
+ <!-- C# source generators -->
+ <ItemGroup Condition=" '$(DisableImplicitGodotGeneratorReferences)' != 'true' ">
+ <PackageReference Include="Godot.SourceGenerators" Version="$(PackageVersion_Godot_SourceGenerators)" />
+ </ItemGroup>
</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Bar.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Bar.cs
new file mode 100644
index 0000000000..5eaebc4474
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Bar.cs
@@ -0,0 +1,15 @@
+namespace Godot.SourceGenerators.Sample
+{
+ partial class Bar : Godot.Object
+ {
+ }
+
+ // Foo in another file
+ partial class Foo
+ {
+ }
+
+ partial class NotSameNameAsFile : Godot.Object
+ {
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Foo.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Foo.cs
new file mode 100644
index 0000000000..21a5bfe560
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Foo.cs
@@ -0,0 +1,11 @@
+namespace Godot.SourceGenerators.Sample
+{
+ partial class Foo : Godot.Object
+ {
+ }
+
+ // Foo again in the same file
+ partial class Foo
+ {
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
new file mode 100644
index 0000000000..24f7909861
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/Godot.SourceGenerators.Sample.csproj
@@ -0,0 +1,31 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>netstandard2.1</TargetFramework>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <!-- $(GodotProjectDir) would normally be defined by the Godot.NET.Sdk -->
+ <GodotProjectDir>$(MSBuildProjectDirectory)</GodotProjectDir>
+ </PropertyGroup>
+
+ <PropertyGroup>
+ <!-- The emitted files are not part of the compilation nor design.
+ They're only for peeking at the generated sources. Sometimes the
+ emitted files get corrupted, but that won't break anything. -->
+ <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
+ <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\glue\GodotSharp\GodotSharp\GodotSharp.csproj">
+ <Private>False</Private>
+ </ProjectReference>
+ <ProjectReference Include="..\Godot.SourceGenerators\Godot.SourceGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
+ </ItemGroup>
+
+ <!-- This file is imported automatically when using PackageReference to
+ reference Godot.SourceGenerators, but not when using ProjectReference -->
+ <Import Project="..\Godot.SourceGenerators\Godot.SourceGenerators.props" />
+
+</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
new file mode 100644
index 0000000000..4867c986e6
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
@@ -0,0 +1,33 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Godot.SourceGenerators
+{
+ public static class Common
+ {
+ public static void ReportNonPartialGodotScriptClass(
+ GeneratorExecutionContext context,
+ ClassDeclarationSyntax cds, INamedTypeSymbol symbol
+ )
+ {
+ string message =
+ "Missing partial modifier on declaration of type '" +
+ $"{symbol.FullQualifiedName()}' which is a subclass of '{GodotClasses.Object}'";
+
+ string description = $"{message}. Subclasses of '{GodotClasses.Object}' must be " +
+ "declared with the partial modifier or annotated with the " +
+ $"attribute '{GodotClasses.DisableGodotGeneratorsAttr}'.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GODOT-G0001",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ cds.GetLocation(),
+ cds.SyntaxTree.FilePath));
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
new file mode 100644
index 0000000000..e16f72f43a
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -0,0 +1,89 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace Godot.SourceGenerators
+{
+ static class ExtensionMethods
+ {
+ public static bool TryGetGlobalAnalyzerProperty(
+ this GeneratorExecutionContext context, string property, out string? value
+ ) => context.AnalyzerConfigOptions.GlobalOptions
+ .TryGetValue("build_property." + property, out value);
+
+ private static bool InheritsFrom(this INamedTypeSymbol? symbol, string baseName)
+ {
+ if (symbol == null)
+ return false;
+
+ while (true)
+ {
+ if (symbol.ToString() == baseName)
+ {
+ return true;
+ }
+
+ if (symbol.BaseType != null)
+ {
+ symbol = symbol.BaseType;
+ continue;
+ }
+
+ break;
+ }
+
+ return false;
+ }
+
+ private static bool IsGodotScriptClass(
+ this ClassDeclarationSyntax cds, Compilation compilation,
+ out INamedTypeSymbol? symbol
+ )
+ {
+ var sm = compilation.GetSemanticModel(cds.SyntaxTree);
+
+ var classTypeSymbol = sm.GetDeclaredSymbol(cds);
+
+ if (classTypeSymbol?.BaseType == null
+ || !classTypeSymbol.BaseType.InheritsFrom(GodotClasses.Object))
+ {
+ symbol = null;
+ return false;
+ }
+
+ symbol = classTypeSymbol;
+ return true;
+ }
+
+ public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectGodotScriptClasses(
+ this IEnumerable<ClassDeclarationSyntax> source,
+ Compilation compilation
+ )
+ {
+ foreach (var cds in source)
+ {
+ if (cds.IsGodotScriptClass(compilation, out var symbol))
+ yield return (cds, symbol!);
+ }
+ }
+
+ public static bool IsPartial(this ClassDeclarationSyntax cds)
+ => cds.Modifiers.Any(SyntaxKind.PartialKeyword);
+
+ public static bool HasDisableGeneratorsAttribute(this INamedTypeSymbol symbol)
+ => symbol.GetAttributes().Any(attr =>
+ attr.AttributeClass?.ToString() == GodotClasses.DisableGodotGeneratorsAttr);
+
+ private static SymbolDisplayFormat FullyQualifiedFormatOmitGlobal { get; } =
+ SymbolDisplayFormat.FullyQualifiedFormat
+ .WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted);
+
+ public static string FullQualifiedName(this INamedTypeSymbol symbol)
+ => symbol.ToDisplayString(NullableFlowState.NotNull, FullyQualifiedFormatOmitGlobal);
+
+ public static string FullQualifiedName(this INamespaceSymbol namespaceSymbol)
+ => namespaceSymbol.ToDisplayString(FullyQualifiedFormatOmitGlobal);
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
new file mode 100644
index 0000000000..11d8e0f72b
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
@@ -0,0 +1,41 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ <LangVersion>8.0</LangVersion>
+ <Nullable>enable</Nullable>
+ </PropertyGroup>
+ <PropertyGroup>
+ <Description>Core C# source generator for Godot projects.</Description>
+ <Authors>Godot Engine contributors</Authors>
+
+ <PackageId>Godot.SourceGenerators</PackageId>
+ <Version>4.0.0</Version>
+ <PackageVersion>$(PackageVersion_Godot_SourceGenerators)</PackageVersion>
+ <RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators</RepositoryUrl>
+ <PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
+ <PackageLicenseExpression>MIT</PackageLicenseExpression>
+
+ <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
+ <!-- Do not include the generator as a lib dependency -->
+ <IncludeBuildOutput>false</IncludeBuildOutput>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" PrivateAssets="all" />
+ <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.1" PrivateAssets="all" />
+ </ItemGroup>
+ <ItemGroup>
+ <!-- Package the generator in the analyzer directory of the nuget package -->
+ <None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
+
+ <!-- Package the props file -->
+ <None Include="Godot.SourceGenerators.props" Pack="true" PackagePath="build" Visible="false" />
+ </ItemGroup>
+
+ <Target Name="CopyNupkgToSConsOutputDir" AfterTargets="Pack">
+ <PropertyGroup>
+ <GodotSourceRootPath>$(SolutionDir)\..\..\..\..\</GodotSourceRootPath>
+ <GodotOutputDataDir>$(GodotSourceRootPath)\bin\GodotSharp\</GodotOutputDataDir>
+ </PropertyGroup>
+ <Copy SourceFiles="$(PackageOutputPath)$(PackageId).$(PackageVersion).nupkg" DestinationFolder="$(GodotOutputDataDir)Tools\nupkgs\" />
+ </Target>
+</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
new file mode 100644
index 0000000000..f9b47ad5b1
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.props
@@ -0,0 +1,7 @@
+<Project>
+ <ItemGroup>
+ <!-- $(GodotProjectDir) is defined by Godot.NET.Sdk -->
+ <CompilerVisibleProperty Include="GodotProjectDir" />
+ <CompilerVisibleProperty Include="GodotScriptPathAttributeGenerator" />
+ </ItemGroup>
+</Project>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
new file mode 100644
index 0000000000..29e41d155a
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotClasses.cs
@@ -0,0 +1,9 @@
+namespace Godot.SourceGenerators
+{
+ public static class GodotClasses
+ {
+ public const string Object = "Godot.Object";
+ public const string DisableGodotGeneratorsAttr = "Godot.DisableGodotGeneratorsAttribute";
+ public const string AssemblyHasScriptsAttr = "Godot.AssemblyHasScriptsAttribute";
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
new file mode 100644
index 0000000000..a51728e221
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPathAttributeGenerator.cs
@@ -0,0 +1,182 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Text;
+
+namespace Godot.SourceGenerators
+{
+ [Generator]
+ public class ScriptPathAttributeGenerator : ISourceGenerator
+ {
+ public void Execute(GeneratorExecutionContext context)
+ {
+ if (context.TryGetGlobalAnalyzerProperty("GodotScriptPathAttributeGenerator", out string? toggle)
+ && toggle == "disabled")
+ {
+ return;
+ }
+
+ // NOTE: IsNullOrEmpty doesn't work well with nullable checks
+ // ReSharper disable once ReplaceWithStringIsNullOrEmpty
+ if (!context.TryGetGlobalAnalyzerProperty("GodotProjectDir", out string? godotProjectDir)
+ || godotProjectDir!.Length == 0)
+ {
+ throw new InvalidOperationException("Property 'GodotProjectDir' is null or empty.");
+ }
+
+ var godotClasses = context.Compilation.SyntaxTrees
+ .SelectMany(tree =>
+ tree.GetRoot().DescendantNodes()
+ .OfType<ClassDeclarationSyntax>()
+ // Ignore inner classes
+ .Where(cds => !(cds.Parent is ClassDeclarationSyntax))
+ .SelectGodotScriptClasses(context.Compilation)
+ // Report and skip non-partial classes
+ .Where(x =>
+ {
+ if (x.cds.IsPartial() || x.symbol.HasDisableGeneratorsAttribute())
+ return true;
+ Common.ReportNonPartialGodotScriptClass(context, x.cds, x.symbol);
+ return false;
+ })
+ )
+ // Ignore classes whose name is not the same as the file name
+ .Where(x => Path.GetFileNameWithoutExtension(x.cds.SyntaxTree.FilePath) == x.symbol.Name)
+ .GroupBy(x => x.symbol)
+ .ToDictionary(g => g.Key, g => g.Select(x => x.cds));
+
+ foreach (var godotClass in godotClasses)
+ {
+ VisitGodotScriptClass(context, godotProjectDir,
+ symbol: godotClass.Key,
+ classDeclarations: godotClass.Value);
+ }
+
+ if (godotClasses.Count <= 0)
+ return;
+
+ AddScriptTypesAssemblyAttr(context, godotClasses);
+ }
+
+ private static void VisitGodotScriptClass(
+ GeneratorExecutionContext context,
+ string godotProjectDir,
+ INamedTypeSymbol symbol,
+ IEnumerable<ClassDeclarationSyntax> classDeclarations
+ )
+ {
+ var attributes = new StringBuilder();
+
+ // Remember syntax trees for which we already added an attribute, to prevent unnecessary duplicates.
+ var attributedTrees = new List<SyntaxTree>();
+
+ foreach (var cds in classDeclarations)
+ {
+ if (attributedTrees.Contains(cds.SyntaxTree))
+ continue;
+
+ attributedTrees.Add(cds.SyntaxTree);
+
+ if (attributes.Length != 0)
+ attributes.Append("\n");
+
+ attributes.Append(@"[ScriptPathAttribute(""res://");
+ attributes.Append(RelativeToDir(cds.SyntaxTree.FilePath, godotProjectDir));
+ attributes.Append(@""")]");
+ }
+
+ string className = symbol.Name;
+
+ INamespaceSymbol namespaceSymbol = symbol.ContainingNamespace;
+ string classNs = namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace ?
+ namespaceSymbol.FullQualifiedName() :
+ string.Empty;
+ bool hasNamespace = classNs.Length != 0;
+
+ string uniqueName = hasNamespace ?
+ classNs + "." + className + "_ScriptPath_Generated" :
+ className + "_ScriptPath_Generated";
+
+ var source = new StringBuilder();
+
+ // using Godot;
+ // namespace {classNs} {
+ // {attributesBuilder}
+ // partial class {className} { }
+ // }
+
+ source.Append("using Godot;\n");
+
+ if (hasNamespace)
+ {
+ source.Append("namespace ");
+ source.Append(classNs);
+ source.Append(" {\n\n");
+ }
+
+ source.Append(attributes);
+ source.Append("\n partial class ");
+ source.Append(className);
+ source.Append("\n{\n}\n");
+
+ if (hasNamespace)
+ {
+ source.Append("\n}\n");
+ }
+
+ context.AddSource(uniqueName, SourceText.From(source.ToString(), Encoding.UTF8));
+ }
+
+ private static void AddScriptTypesAssemblyAttr(GeneratorExecutionContext context,
+ Dictionary<INamedTypeSymbol, IEnumerable<ClassDeclarationSyntax>> godotClasses)
+ {
+ var sourceBuilder = new StringBuilder();
+
+ sourceBuilder.Append("[assembly:");
+ sourceBuilder.Append(GodotClasses.AssemblyHasScriptsAttr);
+ sourceBuilder.Append("(new System.Type[] {");
+
+ bool first = true;
+
+ foreach (var godotClass in godotClasses)
+ {
+ var qualifiedName = godotClass.Key.ToDisplayString(
+ NullableFlowState.NotNull, SymbolDisplayFormat.FullyQualifiedFormat);
+ if (!first)
+ sourceBuilder.Append(", ");
+ first = false;
+ sourceBuilder.Append("typeof(");
+ sourceBuilder.Append(qualifiedName);
+ sourceBuilder.Append(")");
+ }
+
+ sourceBuilder.Append("})]\n");
+
+ context.AddSource("AssemblyScriptTypes_Generated",
+ SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
+ }
+
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ }
+
+ private static string RelativeToDir(string path, string dir)
+ {
+ // Make sure the directory ends with a path separator
+ dir = Path.Combine(dir, " ").TrimEnd();
+
+ if (Path.DirectorySeparatorChar == '\\')
+ dir = dir.Replace("/", "\\") + "\\";
+
+ var fullPath = new Uri(Path.GetFullPath(path), UriKind.Absolute);
+ var relRoot = new Uri(Path.GetFullPath(dir), UriKind.Absolute);
+
+ // MakeRelativeUri converts spaces to %20, hence why we need UnescapeDataString
+ return Uri.UnescapeDataString(relRoot.MakeRelativeUri(fullPath).ToString());
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/.gitignore b/modules/mono/editor/GodotTools/.gitignore
index 48e2f914d8..a41d1c89b5 100644
--- a/modules/mono/editor/GodotTools/.gitignore
+++ b/modules/mono/editor/GodotTools/.gitignore
@@ -353,4 +353,3 @@ healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
-
diff --git a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
index 5edf72d63e..2bf1cb7a18 100644
--- a/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
@@ -12,17 +12,21 @@ 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 was not set.");
+ throw new LoggerException("Log directory parameter not specified.");
- var parameters = Parameters.Split(new[] { ';' });
+ string[] parameters = Parameters.Split(new[] { ';' });
string logDir = parameters[0];
if (string.IsNullOrEmpty(logDir))
- throw new LoggerException("Log directory was not set.");
+ throw new LoggerException("Log directory parameter is empty.");
if (parameters.Length > 1)
throw new LoggerException("Too many parameters passed.");
@@ -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)
{
@@ -51,22 +55,31 @@ namespace GodotTools.BuildLogger
{
throw new LoggerException("Failed to create log file: " + ex.Message);
}
- else
- {
- // Unexpected failure
- throw;
- }
+
+ // Unexpected failure
+ throw;
}
eventSource.ProjectStarted += eventSource_ProjectStarted;
- eventSource.TaskStarted += eventSource_TaskStarted;
+ eventSource.ProjectFinished += eventSource_ProjectFinished;
eventSource.MessageRaised += eventSource_MessageRaised;
eventSource.WarningRaised += eventSource_WarningRaised;
eventSource.ErrorRaised += eventSource_ErrorRaised;
- eventSource.ProjectFinished += eventSource_ProjectFinished;
}
- void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
+ private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
+ {
+ WriteLine(e.Message);
+ _indent++;
+ }
+
+ private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
+ {
+ _indent--;
+ WriteLine(e.Message);
+ }
+
+ private void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
{
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}";
@@ -78,10 +91,10 @@ 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);
}
- void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
+ private void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
{
string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): warning {e.Code}: {e.Message}";
@@ -93,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)
@@ -108,40 +121,6 @@ namespace GodotTools.BuildLogger
}
}
- private void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
- {
- // TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName
- // To keep this log clean, this logger will ignore these events.
- }
-
- private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
- {
- WriteLine(e.Message);
- indent++;
- }
-
- private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
- {
- indent--;
- WriteLine(e.Message);
- }
-
- /// <summary>
- /// Write a line to the log, adding the SenderName
- /// </summary>
- private void WriteLineWithSender(string line, BuildEventArgs e)
- {
- if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
- {
- // Well, if the sender name is MSBuild, let's leave it out for prettiness
- WriteLine(line);
- }
- else
- {
- WriteLine(e.SenderName + ": " + line);
- }
- }
-
/// <summary>
/// Write a line to the log, adding the SenderName and Message
/// (these parameters are on all MSBuild event argument objects)
@@ -161,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.CLI/Program.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs
index 4db71500da..450c4bf0cb 100644
--- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging.CLI/Program.cs
@@ -113,8 +113,7 @@ namespace GodotTools.IdeMessaging.CLI
}
}
- ExitMainLoop:
-
+ ExitMainLoop:
await forwarder.WriteLineToOutput("Event=Quit");
}
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 cc0da44a13..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;
@@ -150,8 +150,8 @@ EndProject";
{"Tools|Any CPU", "ExportRelease|Any CPU"}
};
- var regex = new Regex(string.Join("|",dict.Keys.Select(Regex.Escape)));
- var result = regex.Replace(input,m => dict[m.Value]);
+ var regex = new Regex(string.Join("|", dict.Keys.Select(Regex.Escape)));
+ string result = regex.Replace(input, m => dict[m.Value]);
if (result != input)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
index e4d6b2e010..37123ba2b2 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
@@ -10,6 +10,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" />
+ <ProjectReference Include="..\GodotTools.Shared\GodotTools.Shared.csproj" />
</ItemGroup>
<!--
The Microsoft.Build.Runtime package is too problematic so we create a MSBuild.exe stub. The workaround described
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.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
index 01d7c99662..7d49d251dd 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
@@ -3,14 +3,13 @@ using System.IO;
using System.Text;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
+using GodotTools.Shared;
namespace GodotTools.ProjectEditor
{
public static class ProjectGenerator
{
- public const string GodotSdkVersionToUse = "4.0.0-dev2";
-
- public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GodotSdkVersionToUse}";
+ public static string GodotSdkAttrValue => $"Godot.NET.Sdk/{GeneratedGodotNupkgsVersions.GodotNETSdk}";
public static ProjectRootElement GenGameProject(string name)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index 4e2c0f17cc..cdac9acb25 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -1,11 +1,5 @@
using System;
-using GodotTools.Core;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
using Microsoft.Build.Construction;
-using Microsoft.Build.Globbing;
namespace GodotTools.ProjectEditor
{
@@ -31,47 +25,6 @@ namespace GodotTools.ProjectEditor
return root != null ? new MSBuildProject(root) : null;
}
- private static List<string> GetAllFilesRecursive(string rootDirectory, string mask)
- {
- string[] files = Directory.GetFiles(rootDirectory, mask, SearchOption.AllDirectories);
-
- // We want relative paths
- for (int i = 0; i < files.Length; i++)
- {
- files[i] = files[i].RelativeToPath(rootDirectory);
- }
-
- return new List<string>(files);
- }
-
- // NOTE: Assumes auto-including items. Only used by the scripts metadata generator, which will be replaced with source generators in the future.
- public static IEnumerable<string> GetIncludeFiles(string projectPath, string itemType)
- {
- var excluded = new List<string>();
- var includedFiles = GetAllFilesRecursive(Path.GetDirectoryName(projectPath), "*.cs");
-
- var root = ProjectRootElement.Open(projectPath);
- Debug.Assert(root != null);
-
- foreach (var item in root.Items)
- {
- if (string.IsNullOrEmpty(item.Condition))
- continue;
-
- if (item.ItemType != itemType)
- continue;
-
- string normalizedRemove = item.Remove.NormalizePath();
-
- var glob = MSBuildGlob.Parse(normalizedRemove);
- excluded.AddRange(includedFiles.Where(includedFile => glob.IsMatch(includedFile)));
- }
-
- includedFiles.RemoveAll(f => excluded.Contains(f));
-
- return includedFiles;
- }
-
public static void MigrateToProjectSdksStyle(MSBuildProject project, string projectName)
{
var origRoot = project.Root;
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
new file mode 100644
index 0000000000..aab2d73bdd
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GenerateGodotNupkgsVersions.targets
@@ -0,0 +1,36 @@
+<Project>
+ <!-- Generate C# file with the version of all the nupkgs bundled with Godot -->
+
+ <Target Name="SetPropertiesForGenerateGodotNupkgsVersions">
+ <PropertyGroup>
+ <GeneratedGodotNupkgsVersionsFile>$(IntermediateOutputPath)GodotNupkgsVersions.g.cs</GeneratedGodotNupkgsVersionsFile>
+ </PropertyGroup>
+ </Target>
+
+ <Target Name="GenerateGodotNupkgsVersionsFile"
+ DependsOnTargets="PrepareForBuild;_GenerateGodotNupkgsVersionsFile"
+ BeforeTargets="BeforeCompile;CoreCompile">
+ <ItemGroup>
+ <Compile Include="$(GeneratedGodotNupkgsVersionsFile)" />
+ <FileWrites Include="$(GeneratedGodotNupkgsVersionsFile)" />
+ </ItemGroup>
+ </Target>
+ <Target Name="_GenerateGodotNupkgsVersionsFile"
+ DependsOnTargets="SetPropertiesForGenerateGodotNupkgsVersions"
+ Inputs="$(MSBuildProjectFile);$(MSBuildThisFileDirectory);$(MSBuildProjectFile)\..\..\..\SdkPackageVersions.props"
+ Outputs="$(GeneratedGodotNupkgsVersionsFile)">
+ <PropertyGroup>
+ <GenerateGodotNupkgsVersionsCode><![CDATA[
+namespace $(RootNamespace) {
+ public class GeneratedGodotNupkgsVersions {
+ public const string GodotNETSdk = "$(PackageVersion_Godot_NET_Sdk)"%3b
+ public const string GodotSourceGenerators = "$(PackageVersion_Godot_SourceGenerators)"%3b
+ }
+}
+]]></GenerateGodotNupkgsVersionsCode>
+ </PropertyGroup>
+ <WriteLinesToFile Lines="$(GenerateGodotNupkgsVersionsCode)"
+ File="$(GeneratedGodotNupkgsVersionsFile)"
+ Overwrite="True" WriteOnlyWhenDifferent="True" />
+ </Target>
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
new file mode 100644
index 0000000000..3bc1698c15
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools.Shared/GodotTools.Shared.csproj
@@ -0,0 +1,6 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ </PropertyGroup>
+ <Import Project="GenerateGodotNupkgsVersions.targets" />
+</Project>
diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln
index ba5379e562..d3107a69db 100644
--- a/modules/mono/editor/GodotTools/GodotTools.sln
+++ b/modules/mono/editor/GodotTools/GodotTools.sln
@@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.IdeMessaging", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio", "GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj", "{EAFFF236-FA96-4A4D-BD23-0E51EF988277}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.Shared", "GodotTools.Shared\GodotTools.Shared.csproj", "{2758FFAF-8237-4CF2-B569-66BF8B3587BB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -43,5 +45,9 @@ Global
{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2758FFAF-8237-4CF2-B569-66BF8B3587BB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs b/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
deleted file mode 100644
index 3ab669a9f3..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/BottomPanel.cs
+++ /dev/null
@@ -1,339 +0,0 @@
-using Godot;
-using System;
-using System.IO;
-using Godot.Collections;
-using GodotTools.Internals;
-using static GodotTools.Internals.Globals;
-using File = GodotTools.Utils.File;
-using Path = System.IO.Path;
-
-namespace GodotTools
-{
- public class BottomPanel : VBoxContainer
- {
- private EditorInterface editorInterface;
-
- private TabContainer panelTabs;
-
- private VBoxContainer panelBuildsTab;
-
- private ItemList buildTabsList;
- private TabContainer buildTabs;
-
- private Button warningsBtn;
- private Button errorsBtn;
- private Button viewLogBtn;
-
- private void _UpdateBuildTab(int index, int? currentTab)
- {
- var tab = (BuildTab)buildTabs.GetChild(index);
-
- string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution);
- itemName += " [" + tab.BuildInfo.Configuration + "]";
-
- buildTabsList.AddItem(itemName, tab.IconTexture);
-
- string itemTooltip = "Solution: " + tab.BuildInfo.Solution;
- itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration;
- itemTooltip += "\nStatus: ";
-
- if (tab.BuildExited)
- itemTooltip += tab.BuildResult == BuildTab.BuildResults.Success ? "Succeeded" : "Errored";
- else
- itemTooltip += "Running";
-
- if (!tab.BuildExited || tab.BuildResult == BuildTab.BuildResults.Error)
- itemTooltip += $"\nErrors: {tab.ErrorCount}";
-
- itemTooltip += $"\nWarnings: {tab.WarningCount}";
-
- buildTabsList.SetItemTooltip(index, itemTooltip);
-
- // If this tab was already selected before the changes or if no tab was selected
- if (currentTab == null || currentTab == index)
- {
- buildTabsList.Select(index);
- _BuildTabsItemSelected(index);
- }
- }
-
- private void _UpdateBuildTabsList()
- {
- buildTabsList.Clear();
-
- int? currentTab = buildTabs.CurrentTab;
-
- if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
- currentTab = null;
-
- for (int i = 0; i < buildTabs.GetChildCount(); i++)
- _UpdateBuildTab(i, currentTab);
- }
-
- public BuildTab GetBuildTabFor(BuildInfo buildInfo)
- {
- foreach (var buildTab in new Array<BuildTab>(buildTabs.GetChildren()))
- {
- if (buildTab.BuildInfo.Equals(buildInfo))
- return buildTab;
- }
-
- var newBuildTab = new BuildTab(buildInfo);
- AddBuildTab(newBuildTab);
-
- return newBuildTab;
- }
-
- private void _BuildTabsItemSelected(int idx)
- {
- if (idx < 0 || idx >= buildTabs.GetTabCount())
- throw new IndexOutOfRangeException();
-
- buildTabs.CurrentTab = idx;
- if (!buildTabs.Visible)
- buildTabs.Visible = true;
-
- warningsBtn.Visible = true;
- errorsBtn.Visible = true;
- viewLogBtn.Visible = true;
- }
-
- private void _BuildTabsNothingSelected()
- {
- if (buildTabs.GetTabCount() != 0)
- {
- // just in case
- buildTabs.Visible = false;
-
- // This callback is called when clicking on the empty space of the list.
- // ItemList won't deselect the items automatically, so we must do it ourselves.
- buildTabsList.UnselectAll();
- }
-
- warningsBtn.Visible = false;
- errorsBtn.Visible = false;
- viewLogBtn.Visible = false;
- }
-
- private void _WarningsToggled(bool pressed)
- {
- int currentTab = buildTabs.CurrentTab;
-
- if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
- throw new InvalidOperationException("No tab selected");
-
- var buildTab = (BuildTab)buildTabs.GetChild(currentTab);
- buildTab.WarningsVisible = pressed;
- buildTab.UpdateIssuesList();
- }
-
- private void _ErrorsToggled(bool pressed)
- {
- int currentTab = buildTabs.CurrentTab;
-
- if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
- throw new InvalidOperationException("No tab selected");
-
- var buildTab = (BuildTab)buildTabs.GetChild(currentTab);
- buildTab.ErrorsVisible = pressed;
- buildTab.UpdateIssuesList();
- }
-
- public void BuildProjectPressed()
- {
- if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
- return; // No solution to build
-
- string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor");
- string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player");
-
- CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath);
-
- if (File.Exists(editorScriptsMetadataPath))
- {
- try
- {
- File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
- }
- catch (IOException e)
- {
- GD.PushError($"Failed to copy scripts metadata file. Exception message: {e.Message}");
- return;
- }
- }
-
- bool buildSuccess = BuildManager.BuildProjectBlocking("Debug");
-
- if (!buildSuccess)
- return;
-
- // Notify running game for hot-reload
- Internal.EditorDebuggerNodeReloadScripts();
-
- // Hot-reload in the editor
- GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
-
- if (Internal.IsAssembliesReloadingNeeded())
- Internal.ReloadAssemblies(softReload: false);
- }
-
- private void _ViewLogPressed()
- {
- if (!buildTabsList.IsAnythingSelected())
- return;
-
- var selectedItems = buildTabsList.GetSelectedItems();
-
- if (selectedItems.Length != 1)
- throw new InvalidOperationException($"Expected 1 selected item, got {selectedItems.Length}");
-
- int selectedItem = selectedItems[0];
-
- var buildTab = (BuildTab)buildTabs.GetTabControl(selectedItem);
-
- OS.ShellOpen(Path.Combine(buildTab.BuildInfo.LogsDirPath, BuildManager.MsBuildLogFileName));
- }
-
- public override void _Notification(int what)
- {
- base._Notification(what);
-
- if (what == EditorSettings.NotificationEditorSettingsChanged)
- {
- var editorBaseControl = editorInterface.GetBaseControl();
- panelTabs.AddThemeStyleboxOverride("panel", editorBaseControl.GetThemeStylebox("DebuggerPanel", "EditorStyles"));
- panelTabs.AddThemeStyleboxOverride("tab_fg", editorBaseControl.GetThemeStylebox("DebuggerTabFG", "EditorStyles"));
- panelTabs.AddThemeStyleboxOverride("tab_bg", editorBaseControl.GetThemeStylebox("DebuggerTabBG", "EditorStyles"));
- }
- }
-
- public void AddBuildTab(BuildTab buildTab)
- {
- buildTabs.AddChild(buildTab);
- RaiseBuildTab(buildTab);
- }
-
- public void RaiseBuildTab(BuildTab buildTab)
- {
- if (buildTab.GetParent() != buildTabs)
- throw new InvalidOperationException("Build tab is not in the tabs list");
-
- buildTabs.MoveChild(buildTab, 0);
- _UpdateBuildTabsList();
- }
-
- public void ShowBuildTab()
- {
- for (int i = 0; i < panelTabs.GetTabCount(); i++)
- {
- if (panelTabs.GetTabControl(i) == panelBuildsTab)
- {
- panelTabs.CurrentTab = i;
- GodotSharpEditor.Instance.MakeBottomPanelItemVisible(this);
- return;
- }
- }
-
- GD.PushError("Builds tab not found");
- }
-
- public override void _Ready()
- {
- base._Ready();
-
- editorInterface = GodotSharpEditor.Instance.GetEditorInterface();
-
- var editorBaseControl = editorInterface.GetBaseControl();
-
- SizeFlagsVertical = (int)SizeFlags.ExpandFill;
- SetAnchorsAndMarginsPreset(LayoutPreset.Wide);
-
- panelTabs = new TabContainer
- {
- TabAlign = TabContainer.TabAlignEnum.Left,
- RectMinSize = new Vector2(0, 228) * EditorScale,
- SizeFlagsVertical = (int)SizeFlags.ExpandFill
- };
- panelTabs.AddThemeStyleboxOverride("panel", editorBaseControl.GetThemeStylebox("DebuggerPanel", "EditorStyles"));
- panelTabs.AddThemeStyleboxOverride("tab_fg", editorBaseControl.GetThemeStylebox("DebuggerTabFG", "EditorStyles"));
- panelTabs.AddThemeStyleboxOverride("tab_bg", editorBaseControl.GetThemeStylebox("DebuggerTabBG", "EditorStyles"));
- AddChild(panelTabs);
-
- {
- // Builds tab
- panelBuildsTab = new VBoxContainer
- {
- Name = "Builds".TTR(),
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill
- };
- panelTabs.AddChild(panelBuildsTab);
-
- var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
- panelBuildsTab.AddChild(toolBarHBox);
-
- var buildProjectBtn = new Button
- {
- Text = "Build Project".TTR(),
- FocusMode = FocusModeEnum.None
- };
- buildProjectBtn.PressedSignal += BuildProjectPressed;
- toolBarHBox.AddChild(buildProjectBtn);
-
- toolBarHBox.AddSpacer(begin: false);
-
- warningsBtn = new Button
- {
- Text = "Warnings".TTR(),
- ToggleMode = true,
- Pressed = true,
- Visible = false,
- FocusMode = FocusModeEnum.None
- };
- warningsBtn.Toggled += _WarningsToggled;
- toolBarHBox.AddChild(warningsBtn);
-
- errorsBtn = new Button
- {
- Text = "Errors".TTR(),
- ToggleMode = true,
- Pressed = true,
- Visible = false,
- FocusMode = FocusModeEnum.None
- };
- errorsBtn.Toggled += _ErrorsToggled;
- toolBarHBox.AddChild(errorsBtn);
-
- toolBarHBox.AddSpacer(begin: false);
-
- viewLogBtn = new Button
- {
- Text = "View log".TTR(),
- FocusMode = FocusModeEnum.None,
- Visible = false
- };
- viewLogBtn.PressedSignal += _ViewLogPressed;
- toolBarHBox.AddChild(viewLogBtn);
-
- var hsc = new HSplitContainer
- {
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill,
- SizeFlagsVertical = (int)SizeFlags.ExpandFill
- };
- panelBuildsTab.AddChild(hsc);
-
- buildTabsList = new ItemList {SizeFlagsHorizontal = (int)SizeFlags.ExpandFill};
- buildTabsList.ItemSelected += _BuildTabsItemSelected;
- buildTabsList.NothingSelected += _BuildTabsNothingSelected;
- hsc.AddChild(buildTabsList);
-
- buildTabs = new TabContainer
- {
- TabAlign = TabContainer.TabAlignEnum.Left,
- SizeFlagsHorizontal = (int)SizeFlags.ExpandFill,
- TabsVisible = false
- };
- hsc.AddChild(buildTabs);
- }
- }
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
index ab090c46e7..28bf57dc21 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildInfo.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildInfo.cs
@@ -4,23 +4,26 @@ using Godot.Collections;
using GodotTools.Internals;
using Path = System.IO.Path;
-namespace GodotTools
+namespace GodotTools.Build
{
[Serializable]
- public sealed class BuildInfo : Reference // TODO Remove Reference once we have proper serialization
+ public sealed class BuildInfo : RefCounted // TODO Remove RefCounted once we have proper serialization
{
public string Solution { get; }
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}");
public override bool Equals(object obj)
{
if (obj is BuildInfo other)
- return other.Solution == Solution && other.Configuration == Configuration;
+ return other.Solution == Solution && other.Targets == Targets &&
+ other.Configuration == Configuration && other.Restore == Restore &&
+ other.CustomProperties == CustomProperties && other.LogsDirPath == LogsDirPath;
return false;
}
@@ -30,8 +33,12 @@ namespace GodotTools
unchecked
{
int hash = 17;
- hash = hash * 29 + Solution.GetHashCode();
- hash = hash * 29 + Configuration.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/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
index ff7ce97c47..21bff70b15 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
@@ -1,20 +1,18 @@
using System;
-using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
-using GodotTools.Build;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
-using GodotTools.Utils;
using JetBrains.Annotations;
using static GodotTools.Internals.Globals;
using File = GodotTools.Utils.File;
+using OS = GodotTools.Utils.OS;
-namespace GodotTools
+namespace GodotTools.Build
{
public static class BuildManager
{
- private static readonly List<BuildInfo> BuildsInProgress = new List<BuildInfo>();
+ private static BuildInfo _buildInProgress;
public const string PropNameMSBuildMono = "MSBuild (Mono)";
public const string PropNameMSBuildVs = "MSBuild (VS Build Tools)";
@@ -24,9 +22,17 @@ namespace GodotTools
public const string MsBuildIssuesFileName = "msbuild_issues.csv";
public const string MsBuildLogFileName = "msbuild_log.txt";
+ public delegate void BuildLaunchFailedEventHandler(BuildInfo buildInfo, string reason);
+
+ public static event BuildLaunchFailedEventHandler BuildLaunchFailed;
+ public static event Action<BuildInfo> BuildStarted;
+ public static event Action<BuildResult> BuildFinished;
+ public static event Action<string> StdOutputReceived;
+ public static event Action<string> StdErrorReceived;
+
private static void RemoveOldIssuesFile(BuildInfo buildInfo)
{
- var issuesFile = GetIssuesFilePath(buildInfo);
+ string issuesFile = GetIssuesFilePath(buildInfo);
if (!File.Exists(issuesFile))
return;
@@ -36,12 +42,13 @@ namespace GodotTools
private static void ShowBuildErrorDialog(string message)
{
- GodotSharpEditor.Instance.ShowErrorDialog(message, "Build error");
- GodotSharpEditor.Instance.BottomPanel.ShowBuildTab();
+ var plugin = GodotSharpEditor.Instance;
+ plugin.ShowErrorDialog(message, "Build error");
+ plugin.MakeBottomPanelItemVisible(plugin.MSBuildPanel);
}
- public static void RestartBuild(BuildTab buildTab) => throw new NotImplementedException();
- public static void StopBuild(BuildTab buildTab) => throw new NotImplementedException();
+ public static void RestartBuild(BuildOutputView buildOutputView) => throw new NotImplementedException();
+ public static void StopBuild(BuildOutputView buildOutputView) => throw new NotImplementedException();
private static string GetLogFilePath(BuildInfo buildInfo)
{
@@ -61,15 +68,14 @@ namespace GodotTools
public static bool Build(BuildInfo buildInfo)
{
- if (BuildsInProgress.Contains(buildInfo))
+ if (_buildInProgress != null)
throw new InvalidOperationException("A build is already in progress");
- BuildsInProgress.Add(buildInfo);
+ _buildInProgress = buildInfo;
try
{
- BuildTab buildTab = GodotSharpEditor.Instance.BottomPanel.GetBuildTabFor(buildInfo);
- buildTab.OnBuildStart();
+ BuildStarted?.Invoke(buildInfo);
// Required in order to update the build tasks list
Internal.GodotMainIteration();
@@ -80,44 +86,44 @@ namespace GodotTools
}
catch (IOException e)
{
- buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
Console.Error.WriteLine(e);
}
try
{
- int exitCode = BuildSystem.Build(buildInfo);
+ int exitCode = BuildSystem.Build(buildInfo, StdOutputReceived, StdErrorReceived);
if (exitCode != 0)
PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
- buildTab.OnBuildExit(exitCode == 0 ? BuildTab.BuildResults.Success : BuildTab.BuildResults.Error);
+ BuildFinished?.Invoke(exitCode == 0 ? BuildResult.Success : BuildResult.Error);
return exitCode == 0;
}
catch (Exception e)
{
- buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
}
finally
{
- BuildsInProgress.Remove(buildInfo);
+ _buildInProgress = null;
}
}
public static async Task<bool> BuildAsync(BuildInfo buildInfo)
{
- if (BuildsInProgress.Contains(buildInfo))
+ if (_buildInProgress != null)
throw new InvalidOperationException("A build is already in progress");
- BuildsInProgress.Add(buildInfo);
+ _buildInProgress = buildInfo;
try
{
- BuildTab buildTab = GodotSharpEditor.Instance.BottomPanel.GetBuildTabFor(buildInfo);
+ BuildStarted?.Invoke(buildInfo);
try
{
@@ -125,43 +131,57 @@ namespace GodotTools
}
catch (IOException e)
{
- buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
Console.Error.WriteLine(e);
}
try
{
- int exitCode = await BuildSystem.BuildAsync(buildInfo);
+ int exitCode = await BuildSystem.BuildAsync(buildInfo, StdOutputReceived, StdErrorReceived);
if (exitCode != 0)
PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
- buildTab.OnBuildExit(exitCode == 0 ? BuildTab.BuildResults.Success : BuildTab.BuildResults.Error);
+ BuildFinished?.Invoke(exitCode == 0 ? BuildResult.Success : BuildResult.Error);
return exitCode == 0;
}
catch (Exception e)
{
- buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+ BuildLaunchFailed?.Invoke(buildInfo, $"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
Console.Error.WriteLine(e);
return false;
}
}
finally
{
- BuildsInProgress.Remove(buildInfo);
+ _buildInProgress = null;
}
}
- public static bool BuildProjectBlocking(string config, [CanBeNull] string platform = null)
+ public static bool BuildProjectBlocking(string config, [CanBeNull] string[] targets = null, [CanBeNull] string platform = null)
{
- if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets ?? new[] {"Build"}, config, restore: true);
+
+ // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
+ if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform))
+ buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
+
+ if (Internal.GodotIsRealTDouble())
+ buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
+
+ return BuildProjectBlocking(buildInfo);
+ }
+
+ private static bool BuildProjectBlocking(BuildInfo buildInfo)
+ {
+ if (!File.Exists(buildInfo.Solution))
return true; // No solution to build
// Make sure the API assemblies are up to date before building the project.
// We may not have had the chance to update the release API assemblies, and the debug ones
// may have been deleted by the user at some point after they were loaded by the Godot editor.
- string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(config == "ExportRelease" ? "Release" : "Debug");
+ string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(buildInfo.Configuration == "ExportRelease" ? "Release" : "Debug");
if (!string.IsNullOrEmpty(apiAssembliesUpdateError))
{
@@ -173,15 +193,6 @@ namespace GodotTools
{
pr.Step("Building project solution", 0);
- var buildInfo = new BuildInfo(GodotSharpDirs.ProjectSlnPath, targets: new[] {"Build"}, config, restore: true);
-
- // If a platform was not specified, try determining the current one. If that fails, let MSBuild auto-detect it.
- if (platform != null || OS.PlatformNameMap.TryGetValue(Godot.OS.GetName(), out platform))
- buildInfo.CustomProperties.Add($"GodotTargetPlatform={platform}");
-
- if (Internal.GodotIsRealTDouble())
- buildInfo.CustomProperties.Add("GodotRealTIsDouble=true");
-
if (!Build(buildInfo))
{
ShowBuildErrorDialog("Failed to build project solution");
@@ -197,13 +208,15 @@ namespace GodotTools
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return true; // No solution to build
- string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor");
- string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player");
-
- CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath);
-
- if (File.Exists(editorScriptsMetadataPath))
- File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
+ try
+ {
+ // Make sure our packages are added to the fallback folder
+ NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ Godot.GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
return true; // Requested play from an external editor/IDE which already built the project
@@ -254,8 +267,6 @@ namespace GodotTools
["hint"] = Godot.PropertyHint.Enum,
["hint_string"] = hintString
});
-
- EditorDef("mono/builds/print_build_output", false);
}
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
new file mode 100644
index 0000000000..b53347fc4c
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildOutputView.cs
@@ -0,0 +1,417 @@
+using Godot;
+using System;
+using Godot.Collections;
+using GodotTools.Internals;
+using JetBrains.Annotations;
+using File = GodotTools.Utils.File;
+using Path = System.IO.Path;
+
+namespace GodotTools.Build
+{
+ public class BuildOutputView : VBoxContainer, ISerializationListener
+ {
+ [Serializable]
+ private class BuildIssue : RefCounted // TODO Remove RefCounted once we have proper serialization
+ {
+ public bool Warning { get; set; }
+ public string File { get; set; }
+ public int Line { get; set; }
+ public int Column { get; set; }
+ public string Code { get; set; }
+ public string Message { get; set; }
+ public string ProjectFile { get; set; }
+ }
+
+ [Signal] public event Action BuildStateChanged;
+
+ public bool HasBuildExited { get; private set; } = false;
+
+ public BuildResult? BuildResult { get; private set; } = null;
+
+ public int ErrorCount { get; private set; } = 0;
+
+ public int WarningCount { get; private set; } = 0;
+
+ public bool ErrorsVisible { get; set; } = true;
+ public bool WarningsVisible { get; set; } = true;
+
+ public Texture2D BuildStateIcon
+ {
+ get
+ {
+ if (!HasBuildExited)
+ return GetThemeIcon("Stop", "EditorIcons");
+
+ if (BuildResult == Build.BuildResult.Error)
+ return GetThemeIcon("Error", "EditorIcons");
+
+ if (WarningCount > 1)
+ return GetThemeIcon("Warning", "EditorIcons");
+
+ return null;
+ }
+ }
+
+ public bool LogVisible
+ {
+ 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())
+ {
+ try
+ {
+ Error openError = file.Open(csvFile, Godot.File.ModeFlags.Read);
+
+ if (openError != Error.Ok)
+ return;
+
+ while (!file.EofReached())
+ {
+ string[] csvColumns = file.GetCsvLine();
+
+ if (csvColumns.Length == 1 && string.IsNullOrEmpty(csvColumns[0]))
+ return;
+
+ if (csvColumns.Length != 7)
+ {
+ GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
+ continue;
+ }
+
+ var issue = new BuildIssue
+ {
+ Warning = csvColumns[0] == "warning",
+ File = csvColumns[1],
+ Line = int.Parse(csvColumns[2]),
+ Column = int.Parse(csvColumns[3]),
+ Code = csvColumns[4],
+ Message = csvColumns[5],
+ ProjectFile = csvColumns[6]
+ };
+
+ if (issue.Warning)
+ WarningCount += 1;
+ else
+ ErrorCount += 1;
+
+ _issues.Add(issue);
+ }
+ }
+ finally
+ {
+ file.Close(); // Disposing it is not enough. We need to call Close()
+ }
+ }
+ }
+
+ private void IssueActivated(int idx)
+ {
+ if (idx < 0 || idx >= _issuesList.GetItemCount())
+ throw new IndexOutOfRangeException("Item list index out of range");
+
+ // Get correct issue idx from issue list
+ int issueIndex = (int)(long)_issuesList.GetItemMetadata(idx);
+
+ if (issueIndex < 0 || issueIndex >= _issues.Count)
+ throw new IndexOutOfRangeException("Issue index out of range");
+
+ 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 file = Path.Combine(projectDir.SimplifyGodotPath(), issue.File.SimplifyGodotPath());
+
+ if (!File.Exists(file))
+ return;
+
+ file = ProjectSettings.LocalizePath(file);
+
+ if (file.StartsWith("res://"))
+ {
+ var script = (Script)ResourceLoader.Load(file, typeHint: Internal.CSharpLanguageType);
+
+ if (script != null && Internal.ScriptEditorEdit(script, issue.Line, issue.Column))
+ Internal.EditorNodeShowScriptScreen();
+ }
+ }
+
+ public void UpdateIssuesList()
+ {
+ _issuesList.Clear();
+
+ using (var warningIcon = GetThemeIcon("Warning", "EditorIcons"))
+ using (var errorIcon = GetThemeIcon("Error", "EditorIcons"))
+ {
+ for (int i = 0; i < _issues.Count; i++)
+ {
+ BuildIssue issue = _issues[i];
+
+ if (!(issue.Warning ? WarningsVisible : ErrorsVisible))
+ continue;
+
+ string tooltip = string.Empty;
+ tooltip += $"Message: {issue.Message}";
+
+ if (!string.IsNullOrEmpty(issue.Code))
+ tooltip += $"\nCode: {issue.Code}";
+
+ tooltip += $"\nType: {(issue.Warning ? "warning" : "error")}";
+
+ string text = string.Empty;
+
+ if (!string.IsNullOrEmpty(issue.File))
+ {
+ text += $"{issue.File}({issue.Line},{issue.Column}): ";
+
+ tooltip += $"\nFile: {issue.File}";
+ tooltip += $"\nLine: {issue.Line}";
+ tooltip += $"\nColumn: {issue.Column}";
+ }
+
+ if (!string.IsNullOrEmpty(issue.ProjectFile))
+ tooltip += $"\nProject: {issue.ProjectFile}";
+
+ text += issue.Message;
+
+ int lineBreakIdx = text.IndexOf("\n", StringComparison.Ordinal);
+ string itemText = lineBreakIdx == -1 ? text : text.Substring(0, lineBreakIdx);
+ _issuesList.AddItem(itemText, issue.Warning ? warningIcon : errorIcon);
+
+ int index = _issuesList.GetItemCount() - 1;
+ _issuesList.SetItemTooltip(index, tooltip);
+ _issuesList.SetItemMetadata(index, i);
+ }
+ }
+ }
+
+ private void BuildLaunchFailed(BuildInfo buildInfo, string cause)
+ {
+ HasBuildExited = true;
+ BuildResult = Build.BuildResult.Error;
+
+ _issuesList.Clear();
+
+ var issue = new BuildIssue {Message = cause, Warning = false};
+
+ ErrorCount += 1;
+ _issues.Add(issue);
+
+ UpdateIssuesList();
+
+ EmitSignal(nameof(BuildStateChanged));
+ }
+
+ private void BuildStarted(BuildInfo buildInfo)
+ {
+ _buildInfo = buildInfo;
+ HasBuildExited = false;
+
+ _issues.Clear();
+ WarningCount = 0;
+ ErrorCount = 0;
+ _buildLog.Text = string.Empty;
+
+ UpdateIssuesList();
+
+ EmitSignal(nameof(BuildStateChanged));
+ }
+
+ private void BuildFinished(BuildResult result)
+ {
+ HasBuildExited = true;
+ BuildResult = result;
+
+ LoadIssuesFromFile(Path.Combine(_buildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName));
+
+ UpdateIssuesList();
+
+ EmitSignal(nameof(BuildStateChanged));
+ }
+
+ private void UpdateBuildLogText()
+ {
+ lock (_pendingBuildLogTextLock)
+ {
+ _buildLog.Text += _pendingBuildLogText;
+ _pendingBuildLogText = string.Empty;
+ ScrollToLastNonEmptyLogLine();
+ }
+ }
+
+ private void StdOutputReceived(string text)
+ {
+ lock (_pendingBuildLogTextLock)
+ {
+ if (_pendingBuildLogText.Length == 0)
+ CallDeferred(nameof(UpdateBuildLogText));
+ _pendingBuildLogText += text + "\n";
+ }
+ }
+
+ private void StdErrorReceived(string text)
+ {
+ lock (_pendingBuildLogTextLock)
+ {
+ if (_pendingBuildLogText.Length == 0)
+ CallDeferred(nameof(UpdateBuildLogText));
+ _pendingBuildLogText += text + "\n";
+ }
+ }
+
+ private void ScrollToLastNonEmptyLogLine()
+ {
+ int line;
+ for (line = _buildLog.GetLineCount(); line > 0; line--)
+ {
+ string lineText = _buildLog.GetLine(line);
+
+ if (!string.IsNullOrEmpty(lineText) || !string.IsNullOrEmpty(lineText?.Trim()))
+ break;
+ }
+
+ _buildLog.SetCaretLine(line);
+ }
+
+ public void RestartBuild()
+ {
+ if (!HasBuildExited)
+ throw new InvalidOperationException("Build already started");
+
+ BuildManager.RestartBuild(this);
+ }
+
+ public void StopBuild()
+ {
+ if (!HasBuildExited)
+ throw new InvalidOperationException("Build is not in progress");
+
+ BuildManager.StopBuild(this);
+ }
+
+ private enum IssuesContextMenuOption
+ {
+ Copy
+ }
+
+ private void IssuesListContextOptionPressed(int id)
+ {
+ switch ((IssuesContextMenuOption)id)
+ {
+ case IssuesContextMenuOption.Copy:
+ {
+ // We don't allow multi-selection but just in case that changes later...
+ string text = null;
+
+ foreach (int issueIndex in _issuesList.GetSelectedItems())
+ {
+ if (text != null)
+ text += "\n";
+ text += _issuesList.GetItemText(issueIndex);
+ }
+
+ if (text != null)
+ DisplayServer.ClipboardSet(text);
+ break;
+ }
+ default:
+ throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid issue context menu option");
+ }
+ }
+
+ private void IssuesListRmbSelected(int index, Vector2 atPosition)
+ {
+ _ = index; // Unused
+
+ _issuesListContextMenu.Clear();
+ _issuesListContextMenu.Size = new Vector2i(1, 1);
+
+ if (_issuesList.IsAnythingSelected())
+ {
+ // Add menu entries for the selected item
+ _issuesListContextMenu.AddIconItem(GetThemeIcon("ActionCopy", "EditorIcons"),
+ label: "Copy Error".TTR(), (int)IssuesContextMenuOption.Copy);
+ }
+
+ if (_issuesListContextMenu.GetItemCount() > 0)
+ {
+ _issuesListContextMenu.Position = (Vector2i)(_issuesList.RectGlobalPosition + atPosition);
+ _issuesListContextMenu.Popup();
+ }
+ }
+
+ public override void _Ready()
+ {
+ base._Ready();
+
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill;
+
+ var hsc = new HSplitContainer
+ {
+ SizeFlagsHorizontal = (int)SizeFlags.ExpandFill,
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill
+ };
+ AddChild(hsc);
+
+ _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);
+
+ _issuesListContextMenu = new PopupMenu();
+ _issuesListContextMenu.IdPressed += IssuesListContextOptionPressed;
+ _issuesList.AddChild(_issuesListContextMenu);
+
+ _buildLog = new TextEdit
+ {
+ Editable = false,
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill,
+ SizeFlagsHorizontal = (int)SizeFlags.ExpandFill // Avoid being squashed by the issues list
+ };
+ hsc.AddChild(_buildLog);
+
+ AddBuildEventListeners();
+ }
+
+ private void AddBuildEventListeners()
+ {
+ BuildManager.BuildLaunchFailed += BuildLaunchFailed;
+ BuildManager.BuildStarted += BuildStarted;
+ BuildManager.BuildFinished += BuildFinished;
+ // StdOutput/Error can be received from different threads, so we need to use CallDeferred
+ BuildManager.StdOutputReceived += StdOutputReceived;
+ BuildManager.StdErrorReceived += StdErrorReceived;
+ }
+
+ public void OnBeforeSerialize()
+ {
+ // In case it didn't update yet. We don't want to have to serialize any pending output.
+ UpdateBuildLogText();
+ }
+
+ public void OnAfterDeserialize()
+ {
+ AddBuildEventListeners(); // Re-add them
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs
new file mode 100644
index 0000000000..59055b60c3
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildResult.cs
@@ -0,0 +1,8 @@
+namespace GodotTools.Build
+{
+ public enum BuildResult
+ {
+ Error,
+ Success
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
index d9862ae361..bac7a2e6db 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
@@ -44,10 +44,7 @@ namespace GodotTools.Build
}
}
- private static bool PrintBuildOutput =>
- (bool)EditorSettings.GetSetting("mono/builds/print_build_output");
-
- private static Process LaunchBuild(BuildInfo buildInfo)
+ private static Process LaunchBuild(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
{
(string msbuildPath, BuildTool buildTool) = MsBuildFinder.FindMsBuild();
@@ -58,13 +55,13 @@ namespace GodotTools.Build
var startInfo = new ProcessStartInfo(msbuildPath, compilerArgs);
- bool redirectOutput = !IsDebugMsBuildRequested() && !PrintBuildOutput;
-
- if (!redirectOutput || Godot.OS.IsStdoutVerbose())
- Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}");
+ string launchMessage = $"Running: \"{startInfo.FileName}\" {startInfo.Arguments}";
+ stdOutHandler?.Invoke(launchMessage);
+ if (Godot.OS.IsStdoutVerbose())
+ Console.WriteLine(launchMessage);
- startInfo.RedirectStandardOutput = redirectOutput;
- startInfo.RedirectStandardError = redirectOutput;
+ startInfo.RedirectStandardOutput = true;
+ startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
if (UsingMonoMsBuildOnWindows)
@@ -82,20 +79,22 @@ namespace GodotTools.Build
var process = new Process {StartInfo = startInfo};
+ if (stdOutHandler != null)
+ process.OutputDataReceived += (s, e) => stdOutHandler.Invoke(e.Data);
+ if (stdErrHandler != null)
+ process.ErrorDataReceived += (s, e) => stdErrHandler.Invoke(e.Data);
+
process.Start();
- if (redirectOutput)
- {
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
- }
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
return process;
}
- public static int Build(BuildInfo buildInfo)
+ public static int Build(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
{
- using (var process = LaunchBuild(buildInfo))
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
{
process.WaitForExit();
@@ -103,9 +102,9 @@ namespace GodotTools.Build
}
}
- public static async Task<int> BuildAsync(BuildInfo buildInfo)
+ public static async Task<int> BuildAsync(BuildInfo buildInfo, Action<string> stdOutHandler, Action<string> stdErrHandler)
{
- using (var process = LaunchBuild(buildInfo))
+ using (var process = LaunchBuild(buildInfo, stdOutHandler, stdErrHandler))
{
await process.WaitForExitAsync();
@@ -152,10 +151,5 @@ namespace GodotTools.Build
foreach (string env in platformEnvironmentVariables)
environmentVariables.Remove(env);
}
-
- private static bool IsDebugMsBuildRequested()
- {
- return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1";
- }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
new file mode 100644
index 0000000000..e9cf7911be
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -0,0 +1,197 @@
+using System;
+using Godot;
+using GodotTools.Internals;
+using JetBrains.Annotations;
+using static GodotTools.Internals.Globals;
+using File = GodotTools.Utils.File;
+
+namespace GodotTools.Build
+{
+ public class MSBuildPanel : VBoxContainer
+ {
+ public BuildOutputView BuildOutputView { get; private set; }
+
+ private MenuButton _buildMenuBtn;
+ private Button _errorsBtn;
+ private Button _warningsBtn;
+ private Button _viewLogBtn;
+
+ private void WarningsToggled(bool pressed)
+ {
+ BuildOutputView.WarningsVisible = pressed;
+ BuildOutputView.UpdateIssuesList();
+ }
+
+ private void ErrorsToggled(bool pressed)
+ {
+ BuildOutputView.ErrorsVisible = pressed;
+ BuildOutputView.UpdateIssuesList();
+ }
+
+ [UsedImplicitly]
+ public void BuildSolution()
+ {
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ return; // No solution to build
+
+ try
+ {
+ // Make sure our packages are added to the fallback folder
+ NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
+
+ if (!BuildManager.BuildProjectBlocking("Debug"))
+ return; // Build failed
+
+ // Notify running game for hot-reload
+ Internal.EditorDebuggerNodeReloadScripts();
+
+ // Hot-reload in the editor
+ GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
+
+ if (Internal.IsAssembliesReloadingNeeded())
+ Internal.ReloadAssemblies(softReload: false);
+ }
+
+ [UsedImplicitly]
+ private void RebuildSolution()
+ {
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ return; // No solution to build
+
+ try
+ {
+ // Make sure our packages are added to the fallback folder
+ NuGetUtils.AddBundledPackagesToFallbackFolder(NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ GD.PushError("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
+
+ if (!BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Rebuild" }))
+ return; // Build failed
+
+ // Notify running game for hot-reload
+ Internal.EditorDebuggerNodeReloadScripts();
+
+ // Hot-reload in the editor
+ GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
+
+ if (Internal.IsAssembliesReloadingNeeded())
+ Internal.ReloadAssemblies(softReload: false);
+ }
+
+ [UsedImplicitly]
+ private void CleanSolution()
+ {
+ if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ return; // No solution to build
+
+ BuildManager.BuildProjectBlocking("Debug", targets: new[] { "Clean" });
+ }
+
+ private void ViewLogToggled(bool pressed) => BuildOutputView.LogVisible = pressed;
+
+ private void BuildMenuOptionPressed(int id)
+ {
+ switch ((BuildMenuOptions)id)
+ {
+ case BuildMenuOptions.BuildSolution:
+ BuildSolution();
+ break;
+ case BuildMenuOptions.RebuildSolution:
+ RebuildSolution();
+ break;
+ case BuildMenuOptions.CleanSolution:
+ CleanSolution();
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid build menu option");
+ }
+ }
+
+ private enum BuildMenuOptions
+ {
+ BuildSolution,
+ RebuildSolution,
+ CleanSolution
+ }
+
+ public override void _Ready()
+ {
+ base._Ready();
+
+ RectMinSize = new Vector2(0, 228) * EditorScale;
+ SizeFlagsVertical = (int)SizeFlags.ExpandFill;
+
+ var toolBarHBox = new HBoxContainer { SizeFlagsHorizontal = (int)SizeFlags.ExpandFill };
+ AddChild(toolBarHBox);
+
+ _buildMenuBtn = new MenuButton { Text = "Build", Icon = GetThemeIcon("Play", "EditorIcons") };
+ toolBarHBox.AddChild(_buildMenuBtn);
+
+ 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
+ {
+ HintTooltip = "Show Errors".TTR(),
+ Icon = GetThemeIcon("StatusError", "EditorIcons"),
+ ExpandIcon = false,
+ ToggleMode = true,
+ Pressed = true,
+ FocusMode = FocusModeEnum.None
+ };
+ _errorsBtn.Toggled += ErrorsToggled;
+ toolBarHBox.AddChild(_errorsBtn);
+
+ _warningsBtn = new Button
+ {
+ HintTooltip = "Show Warnings".TTR(),
+ Icon = GetThemeIcon("NodeWarning", "EditorIcons"),
+ ExpandIcon = false,
+ ToggleMode = true,
+ Pressed = true,
+ FocusMode = FocusModeEnum.None
+ };
+ _warningsBtn.Toggled += WarningsToggled;
+ toolBarHBox.AddChild(_warningsBtn);
+
+ _viewLogBtn = new Button
+ {
+ Text = "Show Output".TTR(),
+ ToggleMode = true,
+ Pressed = true,
+ FocusMode = FocusModeEnum.None
+ };
+ _viewLogBtn.Toggled += ViewLogToggled;
+ toolBarHBox.AddChild(_viewLogBtn);
+
+ BuildOutputView = new BuildOutputView();
+ AddChild(BuildOutputView);
+ }
+
+ public override void _Notification(int what)
+ {
+ base._Notification(what);
+
+ 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");
+ }
+ }
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
index 7bfba779fb..a859c6f717 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
@@ -31,7 +31,7 @@ namespace GodotTools.Build
string dotnetCliPath = OS.PathWhich("dotnet");
if (!string.IsNullOrEmpty(dotnetCliPath))
return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError("Cannot find dotnet CLI executable. Fallback to MSBuild from Visual Studio.");
+ GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Visual Studio.");
goto case BuildTool.MsBuildVs;
}
case BuildTool.MsBuildVs:
@@ -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}");
@@ -86,10 +86,10 @@ namespace GodotTools.Build
{
case BuildTool.DotnetCli:
{
- string dotnetCliPath = OS.PathWhich("dotnet");
+ string dotnetCliPath = FindBuildEngineOnUnix("dotnet");
if (!string.IsNullOrEmpty(dotnetCliPath))
return (dotnetCliPath, BuildTool.DotnetCli);
- GD.PushError("Cannot find dotnet CLI executable. Fallback to MSBuild from Mono.");
+ GD.PushError($"Cannot find executable for '{BuildManager.PropNameDotnetCli}'. Fallback to MSBuild from Mono.");
goto case BuildTool.MsBuildMono;
}
case BuildTool.MsBuildMono:
@@ -119,10 +119,14 @@ namespace GodotTools.Build
{
var result = new List<string>();
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
+ result.Add("/opt/local/bin/");
result.Add("/usr/local/var/homebrew/linked/mono/bin/");
+ result.Add("/usr/local/bin/");
+ result.Add("/usr/local/bin/dotnet/");
+ result.Add("/usr/local/share/dotnet/");
}
result.Add("/opt/novell/mono/bin/");
@@ -161,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)
@@ -181,7 +187,7 @@ namespace GodotTools.Build
var outputArray = new Godot.Collections.Array<string>();
int exitCode = Godot.OS.Execute(vsWherePath, vsWhereArgs,
- blocking: true, output: (Godot.Collections.Array)outputArray);
+ output: (Godot.Collections.Array)outputArray);
if (exitCode != 0)
return string.Empty;
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
new file mode 100644
index 0000000000..16dd1c8c6b
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/NuGetUtils.cs
@@ -0,0 +1,297 @@
+using System;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Text;
+using System.Xml;
+using Godot;
+using GodotTools.Internals;
+using GodotTools.Shared;
+using Directory = GodotTools.Utils.Directory;
+using Environment = System.Environment;
+using File = GodotTools.Utils.File;
+
+namespace GodotTools.Build
+{
+ public static class NuGetUtils
+ {
+ public const string GodotFallbackFolderName = "Godot Offline Packages";
+
+ public static string GodotFallbackFolderPath
+ => Path.Combine(GodotSharpDirs.MonoUserDir, "GodotNuGetFallbackFolder");
+
+ private static void AddFallbackFolderToNuGetConfig(string nuGetConfigPath, string name, string path)
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.Load(nuGetConfigPath);
+
+ const string nuGetConfigRootName = "configuration";
+
+ var rootNode = xmlDoc.DocumentElement;
+
+ if (rootNode == null)
+ {
+ // No root node, create it
+ rootNode = xmlDoc.CreateElement(nuGetConfigRootName);
+ xmlDoc.AppendChild(rootNode);
+
+ // Since this can be considered pretty much a new NuGet.Config, add the default nuget.org source as well
+ XmlElement nugetOrgSourceEntry = xmlDoc.CreateElement("add");
+ nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = "nuget.org";
+ nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = "https://api.nuget.org/v3/index.json";
+ nugetOrgSourceEntry.Attributes.Append(xmlDoc.CreateAttribute("protocolVersion")).Value = "3";
+ rootNode.AppendChild(xmlDoc.CreateElement("packageSources")).AppendChild(nugetOrgSourceEntry);
+ }
+ else
+ {
+ // Check that the root node is the expected one
+ if (rootNode.Name != nuGetConfigRootName)
+ throw new Exception("Invalid root Xml node for NuGet.Config. " +
+ $"Expected '{nuGetConfigRootName}' got '{rootNode.Name}'.");
+ }
+
+ var fallbackFoldersNode = rootNode["fallbackPackageFolders"] ??
+ rootNode.AppendChild(xmlDoc.CreateElement("fallbackPackageFolders"));
+
+ // Check if it already has our fallback package folder
+ for (var xmlNode = fallbackFoldersNode.FirstChild; xmlNode != null; xmlNode = xmlNode.NextSibling)
+ {
+ if (xmlNode.NodeType != XmlNodeType.Element)
+ continue;
+
+ var xmlElement = (XmlElement)xmlNode;
+ if (xmlElement.Name == "add" &&
+ xmlElement.Attributes["key"]?.Value == name &&
+ xmlElement.Attributes["value"]?.Value == path)
+ {
+ return;
+ }
+ }
+
+ XmlElement newEntry = xmlDoc.CreateElement("add");
+ newEntry.Attributes.Append(xmlDoc.CreateAttribute("key")).Value = name;
+ newEntry.Attributes.Append(xmlDoc.CreateAttribute("value")).Value = path;
+
+ fallbackFoldersNode.AppendChild(newEntry);
+
+ xmlDoc.Save(nuGetConfigPath);
+ }
+
+ /// <summary>
+ /// Returns all the paths where the user NuGet.Config files can be found.
+ /// Does not determine whether the returned files exist or not.
+ /// </summary>
+ private static string[] GetAllUserNuGetConfigFilePaths()
+ {
+ // Where to find 'NuGet/NuGet.Config':
+ //
+ // - Mono/.NETFramework (standalone NuGet):
+ // Uses Environment.SpecialFolder.ApplicationData
+ // - Windows: '%APPDATA%'
+ // - Linux/macOS: '$HOME/.config'
+ // - CoreCLR (dotnet CLI NuGet):
+ // - Windows: '%APPDATA%'
+ // - Linux/macOS: '$DOTNET_CLI_HOME/.nuget' otherwise '$HOME/.nuget'
+
+ string applicationData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+
+ if (Utils.OS.IsWindows)
+ {
+ // %APPDATA% for both
+ return new[] {Path.Combine(applicationData, "NuGet", "NuGet.Config")};
+ }
+
+ var paths = new string[2];
+
+ // CoreCLR (dotnet CLI NuGet)
+
+ string dotnetCliHome = Environment.GetEnvironmentVariable("DOTNET_CLI_HOME");
+ if (!string.IsNullOrEmpty(dotnetCliHome))
+ {
+ paths[0] = Path.Combine(dotnetCliHome, ".nuget", "NuGet", "NuGet.Config");
+ }
+ else
+ {
+ string home = Environment.GetEnvironmentVariable("HOME");
+ if (string.IsNullOrEmpty(home))
+ throw new InvalidOperationException("Required environment variable 'HOME' is not set.");
+ paths[0] = Path.Combine(home, ".nuget", "NuGet", "NuGet.Config");
+ }
+
+ // Mono/.NETFramework (standalone NuGet)
+
+ // ApplicationData is $HOME/.config on Linux/macOS
+ paths[1] = Path.Combine(applicationData, "NuGet", "NuGet.Config");
+
+ return paths;
+ }
+
+ // nupkg extraction
+ //
+ // Exclude: (NuGet.Client -> NuGet.Packaging.PackageHelper.ExcludePaths)
+ // package/
+ // _rels/
+ // [Content_Types].xml
+ //
+ // Don't ignore files that begin with a dot (.)
+ //
+ // The nuspec is not lower case inside the nupkg but must be made lower case when extracted.
+
+ /// <summary>
+ /// Adds the specified fallback folder to the user NuGet.Config files,
+ /// for both standalone NuGet (Mono/.NETFramework) and dotnet CLI NuGet.
+ /// </summary>
+ public static void AddFallbackFolderToUserNuGetConfigs(string name, string path)
+ {
+ foreach (string nuGetConfigPath in GetAllUserNuGetConfigFilePaths())
+ {
+ if (!System.IO.File.Exists(nuGetConfigPath))
+ {
+ // It doesn't exist, so we create a default one
+ const string defaultConfig = @"<?xml version=""1.0"" encoding=""utf-8""?>
+<configuration>
+ <packageSources>
+ <add key=""nuget.org"" value=""https://api.nuget.org/v3/index.json"" protocolVersion=""3"" />
+ </packageSources>
+</configuration>
+";
+ System.IO.File.WriteAllText(nuGetConfigPath, defaultConfig, Encoding.UTF8); // UTF-8 with BOM
+ }
+
+ AddFallbackFolderToNuGetConfig(nuGetConfigPath, name, path);
+ }
+ }
+
+ private static void AddPackageToFallbackFolder(string fallbackFolder,
+ string nupkgPath, string packageId, string packageVersion)
+ {
+ // dotnet CLI provides no command for this, but we can do it manually.
+ //
+ // - The expected structure is as follows:
+ // fallback_folder/
+ // <package.name>/<version>/
+ // <package.name>.<version>.nupkg
+ // <package.name>.<version>.nupkg.sha512
+ // <package.name>.nuspec
+ // ... extracted nupkg files (check code for excluded files) ...
+ //
+ // - <package.name> and <version> must be in lower case.
+ // - The sha512 of the nupkg is base64 encoded.
+ // - We can get the nuspec from the nupkg which is a Zip file.
+
+ string packageIdLower = packageId.ToLower();
+ string packageVersionLower = packageVersion.ToLower();
+
+ string destDir = Path.Combine(fallbackFolder, packageIdLower, packageVersionLower);
+ string nupkgDestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg");
+ string nupkgSha512DestPath = Path.Combine(destDir, $"{packageIdLower}.{packageVersionLower}.nupkg.sha512");
+
+ if (File.Exists(nupkgDestPath) && File.Exists(nupkgSha512DestPath))
+ return; // Already added (for speed we don't check if every file is properly extracted)
+
+ Directory.CreateDirectory(destDir);
+
+ // Generate .nupkg.sha512 file
+
+ using (var alg = SHA512.Create())
+ {
+ alg.ComputeHash(File.ReadAllBytes(nupkgPath));
+ string base64Hash = Convert.ToBase64String(alg.Hash);
+ File.WriteAllText(nupkgSha512DestPath, base64Hash);
+ }
+
+ // Extract nupkg
+ ExtractNupkg(destDir, nupkgPath, packageId, packageVersion);
+
+ // Copy .nupkg
+ File.Copy(nupkgPath, nupkgDestPath);
+ }
+
+ private static readonly string[] NupkgExcludePaths =
+ {
+ "_rels/",
+ "package/",
+ "[Content_Types].xml"
+ };
+
+ private static void ExtractNupkg(string destDir, string nupkgPath, string packageId, string packageVersion)
+ {
+ // NOTE: Must use SimplifyGodotPath to make sure we don't extract files outside the destination directory.
+
+ using (var archive = ZipFile.OpenRead(nupkgPath))
+ {
+ // Extract .nuspec manually as it needs to be in lower case
+
+ var nuspecEntry = archive.GetEntry(packageId + ".nuspec");
+
+ if (nuspecEntry == null)
+ throw new InvalidOperationException($"Failed to extract package {packageId}.{packageVersion}. Could not find the nuspec file.");
+
+ nuspecEntry.ExtractToFile(Path.Combine(destDir, nuspecEntry.Name.ToLower().SimplifyGodotPath()));
+
+ // Extract the other package files
+
+ foreach (var entry in archive.Entries)
+ {
+ // NOTE: SimplifyGodotPath() removes trailing slash and backslash,
+ // so we can't use the result to check if the entry is a directory.
+
+ string entryFullName = entry.FullName.Replace('\\', '/');
+
+ // Check if the file must be ignored
+ if ( // Excluded files.
+ NupkgExcludePaths.Any(e => entryFullName.StartsWith(e, StringComparison.OrdinalIgnoreCase)) ||
+ // Nupkg hash files and nupkg metadata files on all directory.
+ entryFullName.EndsWith(".nupkg.sha512", StringComparison.OrdinalIgnoreCase) ||
+ entryFullName.EndsWith(".nupkg.metadata", StringComparison.OrdinalIgnoreCase) ||
+ // Nuspec at root level. We already extracted it previously but in lower case.
+ entryFullName.IndexOf('/') == -1 && entryFullName.EndsWith(".nuspec"))
+ {
+ continue;
+ }
+
+ string entryFullNameSimplified = entryFullName.SimplifyGodotPath();
+ string destFilePath = Path.Combine(destDir, entryFullNameSimplified);
+ bool isDir = entryFullName.EndsWith("/");
+
+ if (isDir)
+ {
+ Directory.CreateDirectory(destFilePath);
+ }
+ else
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(destFilePath));
+ entry.ExtractToFile(destFilePath, overwrite: true);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Copies and extracts all the Godot bundled packages to the Godot NuGet fallback folder.
+ /// Does nothing if the packages were already copied.
+ /// </summary>
+ public static void AddBundledPackagesToFallbackFolder(string fallbackFolder)
+ {
+ GD.Print("Copying Godot Offline Packages...");
+
+ string nupkgsLocation = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "nupkgs");
+
+ void AddPackage(string packageId, string packageVersion)
+ {
+ string nupkgPath = Path.Combine(nupkgsLocation, $"{packageId}.{packageVersion}.nupkg");
+ AddPackageToFallbackFolder(fallbackFolder, nupkgPath, packageId, packageVersion);
+ }
+
+ foreach (var (packageId, packageVersion) in PackagesToAdd)
+ AddPackage(packageId, packageVersion);
+ }
+
+ private static readonly (string packageId, string packageVersion)[] PackagesToAdd =
+ {
+ ("Godot.NET.Sdk", GeneratedGodotNupkgsVersions.GodotNETSdk),
+ ("Godot.SourceGenerators", GeneratedGodotNupkgsVersions.GodotSourceGenerators),
+ };
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs b/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
deleted file mode 100644
index 8596cd24af..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/BuildTab.cs
+++ /dev/null
@@ -1,267 +0,0 @@
-using Godot;
-using System;
-using Godot.Collections;
-using GodotTools.Internals;
-using File = GodotTools.Utils.File;
-using Path = System.IO.Path;
-
-namespace GodotTools
-{
- public class BuildTab : VBoxContainer
- {
- public enum BuildResults
- {
- Error,
- Success
- }
-
- [Serializable]
- private class BuildIssue : Reference // TODO Remove Reference once we have proper serialization
- {
- public bool Warning { get; set; }
- public string File { get; set; }
- public int Line { get; set; }
- public int Column { get; set; }
- public string Code { get; set; }
- public string Message { get; set; }
- public string ProjectFile { get; set; }
- }
-
- private readonly Array<BuildIssue> issues = new Array<BuildIssue>(); // TODO Use List once we have proper serialization
- private ItemList issuesList;
-
- public bool BuildExited { get; private set; } = false;
-
- public BuildResults? BuildResult { get; private set; } = null;
-
- public int ErrorCount { get; private set; } = 0;
-
- public int WarningCount { get; private set; } = 0;
-
- public bool ErrorsVisible { get; set; } = true;
- public bool WarningsVisible { get; set; } = true;
-
- public Texture2D IconTexture
- {
- get
- {
- if (!BuildExited)
- return GetThemeIcon("Stop", "EditorIcons");
-
- if (BuildResult == BuildResults.Error)
- return GetThemeIcon("StatusError", "EditorIcons");
-
- return GetThemeIcon("StatusSuccess", "EditorIcons");
- }
- }
-
- public BuildInfo BuildInfo { get; private set; }
-
- private void _LoadIssuesFromFile(string csvFile)
- {
- using (var file = new Godot.File())
- {
- try
- {
- Error openError = file.Open(csvFile, Godot.File.ModeFlags.Read);
-
- if (openError != Error.Ok)
- return;
-
- while (!file.EofReached())
- {
- string[] csvColumns = file.GetCsvLine();
-
- if (csvColumns.Length == 1 && string.IsNullOrEmpty(csvColumns[0]))
- return;
-
- if (csvColumns.Length != 7)
- {
- GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
- continue;
- }
-
- var issue = new BuildIssue
- {
- Warning = csvColumns[0] == "warning",
- File = csvColumns[1],
- Line = int.Parse(csvColumns[2]),
- Column = int.Parse(csvColumns[3]),
- Code = csvColumns[4],
- Message = csvColumns[5],
- ProjectFile = csvColumns[6]
- };
-
- if (issue.Warning)
- WarningCount += 1;
- else
- ErrorCount += 1;
-
- issues.Add(issue);
- }
- }
- finally
- {
- file.Close(); // Disposing it is not enough. We need to call Close()
- }
- }
- }
-
- private void _IssueActivated(int idx)
- {
- if (idx < 0 || idx >= issuesList.GetItemCount())
- throw new IndexOutOfRangeException("Item list index out of range");
-
- // Get correct issue idx from issue list
- int issueIndex = (int)(long)issuesList.GetItemMetadata(idx);
-
- if (issueIndex < 0 || issueIndex >= issues.Count)
- throw new IndexOutOfRangeException("Issue index out of range");
-
- 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 file = Path.Combine(projectDir.SimplifyGodotPath(), issue.File.SimplifyGodotPath());
-
- if (!File.Exists(file))
- return;
-
- file = ProjectSettings.LocalizePath(file);
-
- if (file.StartsWith("res://"))
- {
- var script = (Script)ResourceLoader.Load(file, typeHint: Internal.CSharpLanguageType);
-
- if (script != null && Internal.ScriptEditorEdit(script, issue.Line, issue.Column))
- Internal.EditorNodeShowScriptScreen();
- }
- }
-
- public void UpdateIssuesList()
- {
- issuesList.Clear();
-
- using (var warningIcon = GetThemeIcon("Warning", "EditorIcons"))
- using (var errorIcon = GetThemeIcon("Error", "EditorIcons"))
- {
- for (int i = 0; i < issues.Count; i++)
- {
- BuildIssue issue = issues[i];
-
- if (!(issue.Warning ? WarningsVisible : ErrorsVisible))
- continue;
-
- string tooltip = string.Empty;
- tooltip += $"Message: {issue.Message}";
-
- if (!string.IsNullOrEmpty(issue.Code))
- tooltip += $"\nCode: {issue.Code}";
-
- tooltip += $"\nType: {(issue.Warning ? "warning" : "error")}";
-
- string text = string.Empty;
-
- if (!string.IsNullOrEmpty(issue.File))
- {
- text += $"{issue.File}({issue.Line},{issue.Column}): ";
-
- tooltip += $"\nFile: {issue.File}";
- tooltip += $"\nLine: {issue.Line}";
- tooltip += $"\nColumn: {issue.Column}";
- }
-
- if (!string.IsNullOrEmpty(issue.ProjectFile))
- tooltip += $"\nProject: {issue.ProjectFile}";
-
- text += issue.Message;
-
- int lineBreakIdx = text.IndexOf("\n", StringComparison.Ordinal);
- string itemText = lineBreakIdx == -1 ? text : text.Substring(0, lineBreakIdx);
- issuesList.AddItem(itemText, issue.Warning ? warningIcon : errorIcon);
-
- int index = issuesList.GetItemCount() - 1;
- issuesList.SetItemTooltip(index, tooltip);
- issuesList.SetItemMetadata(index, i);
- }
- }
- }
-
- public void OnBuildStart()
- {
- BuildExited = false;
-
- issues.Clear();
- WarningCount = 0;
- ErrorCount = 0;
- UpdateIssuesList();
-
- GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this);
- }
-
- public void OnBuildExit(BuildResults result)
- {
- BuildExited = true;
- BuildResult = result;
-
- _LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, BuildManager.MsBuildIssuesFileName));
- UpdateIssuesList();
-
- GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this);
- }
-
- public void OnBuildExecFailed(string cause)
- {
- BuildExited = true;
- BuildResult = BuildResults.Error;
-
- issuesList.Clear();
-
- var issue = new BuildIssue { Message = cause, Warning = false };
-
- ErrorCount += 1;
- issues.Add(issue);
-
- UpdateIssuesList();
-
- GodotSharpEditor.Instance.BottomPanel.RaiseBuildTab(this);
- }
-
- public void RestartBuild()
- {
- if (!BuildExited)
- throw new InvalidOperationException("Build already started");
-
- BuildManager.RestartBuild(this);
- }
-
- public void StopBuild()
- {
- if (!BuildExited)
- throw new InvalidOperationException("Build is not in progress");
-
- BuildManager.StopBuild(this);
- }
-
- public override void _Ready()
- {
- base._Ready();
-
- issuesList = new ItemList { SizeFlagsVertical = (int)SizeFlags.ExpandFill };
- issuesList.ItemActivated += _IssueActivated;
- AddChild(issuesList);
- }
-
- private BuildTab()
- {
- }
-
- public BuildTab(BuildInfo buildInfo)
- {
- BuildInfo = buildInfo;
- }
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
index 1d800b8151..e43f10804d 100644
--- a/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/CsProjOperations.cs
@@ -1,11 +1,6 @@
using Godot;
using System;
-using System.Linq;
-using Godot.Collections;
-using GodotTools.Internals;
using GodotTools.ProjectEditor;
-using File = GodotTools.Utils.File;
-using Directory = GodotTools.Utils.Directory;
namespace GodotTools
{
@@ -23,86 +18,5 @@ namespace GodotTools
return string.Empty;
}
}
-
- private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
-
- private static ulong ConvertToTimestamp(this DateTime value)
- {
- TimeSpan elapsedTime = value - Epoch;
- return (ulong)elapsedTime.TotalSeconds;
- }
-
- private static bool TryParseFileMetadata(string includeFile, ulong modifiedTime, out Dictionary fileMetadata)
- {
- fileMetadata = null;
-
- var parseError = ScriptClassParser.ParseFile(includeFile, out var classes, out string errorStr);
-
- if (parseError != Error.Ok)
- {
- GD.PushError($"Failed to determine namespace and class for script: {includeFile}. Parse error: {errorStr ?? parseError.ToString()}");
- return false;
- }
-
- string searchName = System.IO.Path.GetFileNameWithoutExtension(includeFile);
-
- var firstMatch = classes.FirstOrDefault(classDecl =>
- classDecl.BaseCount != 0 && // If it doesn't inherit anything, it can't be a Godot.Object.
- classDecl.SearchName == searchName // Filter by the name we're looking for
- );
-
- if (firstMatch == null)
- return false; // Not found
-
- fileMetadata = new Dictionary
- {
- ["modified_time"] = $"{modifiedTime}",
- ["class"] = new Dictionary
- {
- ["namespace"] = firstMatch.Namespace,
- ["class_name"] = firstMatch.Name,
- ["nested"] = firstMatch.Nested
- }
- };
-
- return true;
- }
-
- public static void GenerateScriptsMetadata(string projectPath, string outputPath)
- {
- var metadataDict = Internal.GetScriptsMetadataOrNothing().Duplicate();
-
- bool IsUpToDate(string includeFile, ulong modifiedTime)
- {
- return metadataDict.TryGetValue(includeFile, out var oldFileVar) &&
- ulong.TryParse(((Dictionary)oldFileVar)["modified_time"] as string,
- out ulong storedModifiedTime) && storedModifiedTime == modifiedTime;
- }
-
- var outdatedFiles = ProjectUtils.GetIncludeFiles(projectPath, "Compile")
- .Select(path => ("res://" + path).SimplifyGodotPath())
- .ToDictionary(path => path, path => File.GetLastWriteTime(path).ConvertToTimestamp())
- .Where(pair => !IsUpToDate(includeFile: pair.Key, modifiedTime: pair.Value))
- .ToArray();
-
- foreach (var pair in outdatedFiles)
- {
- metadataDict.Remove(pair.Key);
-
- string includeFile = pair.Key;
-
- if (TryParseFileMetadata(includeFile, modifiedTime: pair.Value, out var fileMetadata))
- metadataDict[includeFile] = fileMetadata;
- }
-
- string json = metadataDict.Count <= 0 ? "{}" : JSON.Print(metadataDict);
-
- string baseDir = outputPath.GetBaseDir();
-
- if (!Directory.Exists(baseDir))
- Directory.CreateDirectory(baseDir);
-
- File.WriteAllText(outputPath, json);
- }
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs b/modules/mono/editor/GodotTools/GodotTools/Export/AotBuilder.cs
index 42ede3f3f3..37e6a34977 100755..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
@@ -120,7 +120,7 @@ namespace GodotTools.Export
string assemblyPath = assembly.Value;
string outputFileExtension = platform == OS.Platforms.Windows ? ".dll" :
- platform == OS.Platforms.OSX ? ".dylib" :
+ platform == OS.Platforms.MacOS ? ".dylib" :
".so";
string outputFileName = assemblyName + ".dll" + outputFileExtension;
@@ -132,13 +132,14 @@ namespace GodotTools.Export
ExecuteCompiler(FindCrossCompiler(compilerDirPath), compilerArgs, bclDir);
- if (platform == OS.Platforms.OSX)
+ if (platform == OS.Platforms.MacOS)
{
exporter.AddSharedObject(tempOutputFilePath, tags: null);
}
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));
}
}
@@ -577,27 +578,27 @@ MONO_AOT_MODE_LAST = 1000,
{
case OS.Platforms.Windows:
case OS.Platforms.UWP:
- {
- string arch = bits == "64" ? "x86_64" : "i686";
- return $"windows-{arch}";
- }
- case OS.Platforms.OSX:
- {
- Debug.Assert(bits == null || bits == "64");
- string arch = "x86_64";
- return $"{platform}-{arch}";
- }
+ {
+ string arch = bits == "64" ? "x86_64" : "i686";
+ return $"windows-{arch}";
+ }
+ case OS.Platforms.MacOS:
+ {
+ Debug.Assert(bits == null || bits == "64");
+ string arch = "x86_64";
+ return $"{platform}-{arch}";
+ }
case OS.Platforms.LinuxBSD:
case OS.Platforms.Server:
- {
- string arch = bits == "64" ? "x86_64" : "i686";
- return $"linux-{arch}";
- }
+ {
+ string arch = bits == "64" ? "x86_64" : "i686";
+ return $"linux-{arch}";
+ }
case OS.Platforms.Haiku:
- {
- string arch = bits == "64" ? "x86_64" : "i686";
- return $"{platform}-{arch}";
- }
+ {
+ string arch = bits == "64" ? "x86_64" : "i686";
+ return $"{platform}-{arch}";
+ }
default:
throw new NotSupportedException($"Platform not supported: {platform}");
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index 599ca94699..3e46a89b7c 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -5,6 +5,7 @@ using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
+using GodotTools.Build;
using GodotTools.Core;
using GodotTools.Internals;
using JetBrains.Annotations;
@@ -19,7 +20,7 @@ namespace GodotTools.Export
public class ExportPlugin : EditorExportPlugin
{
[Flags]
- enum I18NCodesets : long
+ private enum I18NCodesets : long
{
None = 0,
CJK = 1,
@@ -30,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");
@@ -75,15 +78,13 @@ namespace GodotTools.Export
GlobalDef("mono/export/aot/use_interpreter", true);
// --aot or --aot=opt1,opt2 (use 'mono --aot=help AuxAssembly.dll' to list AOT options)
- GlobalDef("mono/export/aot/extra_aot_options", new string[] { });
+ GlobalDef("mono/export/aot/extra_aot_options", Array.Empty<string>());
// --optimize/-O=opt1,opt2 (use 'mono --list-opt'' to list optimize options)
- GlobalDef("mono/export/aot/extra_optimizer_options", new string[] { });
+ GlobalDef("mono/export/aot/extra_optimizer_options", Array.Empty<string>());
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
@@ -128,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.
}
@@ -143,6 +144,8 @@ namespace GodotTools.Export
private void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
{
+ _ = flags; // Unused
+
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return;
@@ -154,12 +157,7 @@ namespace GodotTools.Export
string buildConfig = isDebug ? "ExportDebug" : "ExportRelease";
- string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
- CsProjOperations.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);
-
- AddFile(scriptsMetadataPath, scriptsMetadataPath);
-
- if (!BuildManager.BuildProjectBlocking(buildConfig, platform))
+ if (!BuildManager.BuildProjectBlocking(buildConfig, platform: platform))
throw new Exception("Failed to build project");
// Add dependency assemblies
@@ -172,6 +170,8 @@ namespace GodotTools.Export
assemblies[projectDllName] = projectDllSrcPath;
+ string bclDir = DeterminePlatformBclDir(platform);
+
if (platform == OS.Platforms.Android)
{
string godotAndroidExtProfileDir = GetBclProfileDir("godot_android_ext");
@@ -182,8 +182,49 @@ namespace GodotTools.Export
assemblies["Mono.Android"] = monoAndroidAssemblyPath;
}
+ else if (platform == OS.Platforms.HTML5)
+ {
+ // Ideally these would be added automatically since they're referenced by the wasm BCL assemblies.
+ // However, at least in the case of 'WebAssembly.Net.Http' for some reason the BCL assemblies
+ // reference a different version even though the assembly is the same, for some weird reason.
- string bclDir = DeterminePlatformBclDir(platform);
+ var wasmFrameworkAssemblies = new[] { "WebAssembly.Bindings", "WebAssembly.Net.WebSockets" };
+
+ foreach (string thisWasmFrameworkAssemblyName in wasmFrameworkAssemblies)
+ {
+ string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName + ".dll");
+ if (!File.Exists(thisWasmFrameworkAssemblyPath))
+ throw new FileNotFoundException($"Assembly not found: '{thisWasmFrameworkAssemblyName}'", thisWasmFrameworkAssemblyPath);
+ assemblies[thisWasmFrameworkAssemblyName] = thisWasmFrameworkAssemblyPath;
+ }
+
+ // Assemblies that can have a different name in a newer version. Newer version must come first and it has priority.
+ (string newName, string oldName)[] wasmFrameworkAssembliesOneOf = new[]
+ {
+ ("System.Net.Http.WebAssemblyHttpHandler", "WebAssembly.Net.Http")
+ };
+
+ foreach (var thisWasmFrameworkAssemblyName in wasmFrameworkAssembliesOneOf)
+ {
+ string thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.newName + ".dll");
+ if (File.Exists(thisWasmFrameworkAssemblyPath))
+ {
+ assemblies[thisWasmFrameworkAssemblyName.newName] = thisWasmFrameworkAssemblyPath;
+ }
+ else
+ {
+ thisWasmFrameworkAssemblyPath = Path.Combine(bclDir, thisWasmFrameworkAssemblyName.oldName + ".dll");
+ if (!File.Exists(thisWasmFrameworkAssemblyPath))
+ {
+ throw new FileNotFoundException("Expected one of the following assemblies but none were found: " +
+ $"'{thisWasmFrameworkAssemblyName.newName}' / '{thisWasmFrameworkAssemblyName.oldName}'",
+ thisWasmFrameworkAssemblyPath);
+ }
+
+ assemblies[thisWasmFrameworkAssemblyName.oldName] = thisWasmFrameworkAssemblyPath;
+ }
+ }
+ }
var initialAssemblies = assemblies.Duplicate();
internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies);
@@ -257,8 +298,8 @@ namespace GodotTools.Export
LLVMOutputPath = "",
FullAot = platform == OS.Platforms.iOS || (bool)(ProjectSettings.GetSetting("mono/export/aot/full_aot") ?? false),
UseInterpreter = (bool)ProjectSettings.GetSetting("mono/export/aot/use_interpreter"),
- ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ?? new string[] { },
- ExtraOptimizerOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ?? new string[] { },
+ ExtraAotOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_aot_options") ?? Array.Empty<string>(),
+ ExtraOptimizerOptions = (string[])ProjectSettings.GetSetting("mono/export/aot/extra_optimizer_options") ?? Array.Empty<string>(),
ToolchainPath = aotToolchainPath
};
@@ -276,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");
}
@@ -340,7 +381,7 @@ namespace GodotTools.Export
private static bool PlatformHasTemplateDir(string platform)
{
// OSX export templates are contained in a zip, so we place our custom template inside it and let Godot do the rest.
- return !new[] {OS.Platforms.OSX, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5}.Contains(platform);
+ return !new[] { OS.Platforms.MacOS, OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform);
}
private static bool DeterminePlatformFromFeatures(IEnumerable<string> features, out string platform)
@@ -389,7 +430,7 @@ namespace GodotTools.Export
/// </summary>
private static bool PlatformRequiresCustomBcl(string platform)
{
- if (new[] {OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5}.Contains(platform))
+ if (new[] { OS.Platforms.Android, OS.Platforms.iOS, OS.Platforms.HTML5 }.Contains(platform))
return true;
// The 'net_4_x' BCL is not compatible between Windows and the other platforms.
@@ -411,7 +452,7 @@ namespace GodotTools.Export
case OS.Platforms.Windows:
case OS.Platforms.UWP:
return "net_4_x_win";
- case OS.Platforms.OSX:
+ case OS.Platforms.MacOS:
case OS.Platforms.LinuxBSD:
case OS.Platforms.Server:
case OS.Platforms.Haiku:
@@ -429,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/Export/XcodeHelper.cs b/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
index 219b7a698a..93ef837a83 100755..100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
@@ -27,7 +27,7 @@ namespace GodotTools.Export
{
var outputWrapper = new Godot.Collections.Array();
- int exitCode = Godot.OS.Execute("xcode-select", new string[] { "--print-path" }, blocking: true, output: outputWrapper);
+ int exitCode = Godot.OS.Execute("xcode-select", new string[] { "--print-path" }, output: outputWrapper);
if (exitCode == 0)
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 2a450c5b87..98c6881166 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -4,9 +4,9 @@ using GodotTools.Export;
using GodotTools.Utils;
using System;
using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
+using GodotTools.Build;
using GodotTools.Ides;
using GodotTools.Ides.Rider;
using GodotTools.Internals;
@@ -19,25 +19,23 @@ using Path = System.IO.Path;
namespace GodotTools
{
- [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public class GodotSharpEditor : EditorPlugin, ISerializationListener
{
- private EditorSettings editorSettings;
+ private EditorSettings _editorSettings;
- private PopupMenu menuPopup;
+ private PopupMenu _menuPopup;
- private AcceptDialog errorDialog;
- private AcceptDialog aboutDialog;
- private CheckBox aboutDialogCheckBox;
+ 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 BottomPanel BottomPanel { get; private set; }
+ public MSBuildPanel MSBuildPanel { get; private set; }
public bool SkipBuildBeforePlaying { get; set; } = false;
@@ -45,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";
@@ -126,16 +124,9 @@ namespace GodotTools
private void _RemoveCreateSlnMenuOption()
{
- menuPopup.RemoveItem(menuPopup.GetItemIndex((int)MenuOptions.CreateSln));
- bottomPanelBtn.Show();
- toolBarBuildButton.Show();
- }
-
- private void _ShowAboutDialog()
- {
- bool showOnStart = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
- aboutDialogCheckBox.Pressed = showOnStart;
- aboutDialog.PopupCentered();
+ _menuPopup.RemoveItem(_menuPopup.GetItemIndex((int)MenuOptions.CreateSln));
+ _bottomPanelBtn.Show();
+ _toolBarBuildButton.Show();
}
private void _MenuOptionPressed(int id)
@@ -145,15 +136,27 @@ namespace GodotTools
case MenuOptions.CreateSln:
CreateProjectSolution();
break;
- case MenuOptions.AboutCSharp:
- _ShowAboutDialog();
+ case MenuOptions.SetupGodotNugetFallbackFolder:
+ {
+ try
+ {
+ string fallbackFolder = NuGetUtils.GodotFallbackFolderPath;
+ NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, fallbackFolder);
+ NuGetUtils.AddBundledPackagesToFallbackFolder(fallbackFolder);
+ }
+ catch (Exception e)
+ {
+ ShowErrorDialog("Failed to setup Godot NuGet Offline Packages: " + e.Message);
+ }
+
break;
+ }
default:
throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid menu option");
}
}
- private void _BuildSolutionPressed()
+ private void BuildSolutionPressed()
{
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
{
@@ -161,37 +164,27 @@ namespace GodotTools
return; // Failed to create solution
}
- Instance.BottomPanel.BuildProjectPressed();
+ Instance.MSBuildPanel.BuildSolution();
}
- public override void _Notification(int what)
+ public override void _Ready()
{
- base._Notification(what);
+ base._Ready();
- if (what == NotificationReady)
- {
- bool showInfoDialog = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
- if (showInfoDialog)
- {
- aboutDialog.Exclusive = true;
- _ShowAboutDialog();
- // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on.
- aboutDialog.Exclusive = false;
- }
- }
+ MSBuildPanel.BuildOutputView.BuildStateChanged += BuildStateChanged;
}
private enum MenuOptions
{
CreateSln,
- AboutCSharp,
+ SetupGodotNugetFallbackFolder,
}
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;
@@ -204,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)
{
@@ -272,7 +265,7 @@ namespace GodotTools
bool osxAppBundleInstalled = false;
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
// The package path is '/Applications/Visual Studio Code.app'
const string vscodeBundleId = "com.microsoft.VSCode";
@@ -295,7 +288,7 @@ namespace GodotTools
}
}
- var resourcePath = ProjectSettings.GlobalizePath("res://");
+ string resourcePath = ProjectSettings.GlobalizePath("res://");
args.Add(resourcePath);
string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
@@ -312,7 +305,7 @@ namespace GodotTools
string command;
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
if (!osxAppBundleInstalled && string.IsNullOrEmpty(_vsCodePath))
{
@@ -354,10 +347,10 @@ 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()
+ public override bool _Build()
{
return BuildManager.EditorBuildCallback();
}
@@ -393,9 +386,15 @@ namespace GodotTools
}
}
- public override void EnablePlugin()
+ private void BuildStateChanged()
+ {
+ if (_bottomPanelBtn != null)
+ _bottomPanelBtn.Icon = MSBuildPanel.BuildOutputView.BuildStateIcon;
+ }
+
+ public override void _EnablePlugin()
{
- base.EnablePlugin();
+ base._EnablePlugin();
if (Instance != null)
throw new InvalidOperationException();
@@ -404,80 +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);
- BottomPanel = new BottomPanel();
-
- bottomPanelBtn = AddControlToBottomPanel(BottomPanel, "Mono".TTR());
+ MSBuildPanel = new MSBuildPanel();
+ _bottomPanelBtn = AddControlToBottomPanel(MSBuildPanel, "MSBuild".TTR());
AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
- menuPopup = new PopupMenu();
- menuPopup.Hide();
+ _menuPopup = new PopupMenu();
+ _menuPopup.Hide();
- AddToolSubmenuItem("Mono", menuPopup);
+ AddToolSubmenuItem("C#", _menuPopup);
- // TODO: Remove or edit this info dialog once Mono support is no longer in alpha
- {
- menuPopup.AddItem("About C# support".TTR(), (int)MenuOptions.AboutCSharp);
- aboutDialog = new AcceptDialog();
- editorBaseControl.AddChild(aboutDialog);
- aboutDialog.Title = "Important: C# support is not feature-complete";
-
- // We don't use DialogText as the default AcceptDialog Label doesn't play well with the TextureRect and CheckBox
- // we'll add. Instead we add containers and a new autowrapped Label inside.
-
- // Main VBoxContainer (icon + label on top, checkbox at bottom)
- var aboutVBox = new VBoxContainer();
- aboutDialog.AddChild(aboutVBox);
-
- // HBoxContainer for icon + label
- var aboutHBox = new HBoxContainer();
- aboutVBox.AddChild(aboutHBox);
-
- var aboutIcon = new TextureRect();
- aboutIcon.Texture = aboutIcon.GetThemeIcon("NodeWarning", "EditorIcons");
- aboutHBox.AddChild(aboutIcon);
-
- var aboutLabel = new Label();
- aboutHBox.AddChild(aboutLabel);
- aboutLabel.RectMinSize = new Vector2(600, 150) * EditorScale;
- aboutLabel.SizeFlagsVertical = (int)Control.SizeFlags.ExpandFill;
- aboutLabel.Autowrap = true;
- aboutLabel.Text =
- "C# support in Godot Engine is in late alpha stage and, while already usable, " +
- "it is not meant for use in production.\n\n" +
- "Projects can be exported to Linux, macOS, Windows, Android, iOS and HTML5, but not yet to UWP. " +
- "Bugs and usability issues will be addressed gradually over future releases, " +
- "potentially including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
- "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, MSBuild version, IDE, etc.:\n\n" +
- " https://github.com/godotengine/godot/issues\n\n" +
- "Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
-
- EditorDef("mono/editor/show_info_on_start", true);
-
- // CheckBox in main container
- aboutDialogCheckBox = new CheckBox {Text = "Show this warning when starting the editor"};
- aboutDialogCheckBox.Toggled += enabled =>
- {
- bool showOnStart = (bool)editorSettings.GetSetting("mono/editor/show_info_on_start");
- if (showOnStart != enabled)
- editorSettings.SetSetting("mono/editor/show_info_on_start", enabled);
- };
- aboutVBox.AddChild(aboutDialogCheckBox);
- }
+ 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))
{
@@ -485,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);
@@ -504,7 +456,7 @@ namespace GodotTools
$",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
$",JetBrains Rider:{(int)ExternalEditorId.Rider}";
}
- else if (OS.IsOSX)
+ else if (OS.IsMacOS)
{
settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudioForMac}" +
$",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
@@ -518,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",
@@ -530,7 +482,17 @@ namespace GodotTools
var exportPlugin = new ExportPlugin();
AddExportPlugin(exportPlugin);
exportPlugin.RegisterExportSettings();
- exportPluginWeak = WeakRef(exportPlugin);
+ _exportPluginWeak = WeakRef(exportPlugin);
+
+ try
+ {
+ // At startup we make sure NuGet.Config files have our Godot NuGet fallback folder included
+ NuGetUtils.AddFallbackFolderToUserNuGetConfigs(NuGetUtils.GodotFallbackFolderName, NuGetUtils.GodotFallbackFolderPath);
+ }
+ catch (Exception e)
+ {
+ GD.PushError("Failed to add Godot NuGet Offline Packages to NuGet.Config: " + e.Message);
+ }
BuildManager.Initialize();
RiderPathManager.Initialize();
@@ -543,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();
@@ -570,6 +532,7 @@ namespace GodotTools
public static GodotSharpEditor Instance { get; private set; }
+ [UsedImplicitly]
private GodotSharpEditor()
{
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index 3f14629b11..b9aa760f4d 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -3,7 +3,8 @@
<ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
<TargetFramework>net472</TargetFramework>
<LangVersion>7.2</LangVersion>
- <GodotApiConfiguration>Debug</GodotApiConfiguration> <!-- The Godot editor uses the Debug Godot API assemblies -->
+ <!-- The Godot editor uses the Debug Godot API assemblies -->
+ <GodotApiConfiguration>Debug</GodotApiConfiguration>
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
<GodotOutputDataDir>$(GodotSourceRootPath)/bin/GodotSharp</GodotOutputDataDir>
<GodotApiAssembliesDir>$(GodotOutputDataDir)/Api/$(GodotApiConfiguration)</GodotApiAssembliesDir>
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 e4932ca217..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();
}
}
@@ -111,16 +111,16 @@ namespace GodotTools.Ides
{
MonoDevelop.Instance GetMonoDevelopInstance(string solutionPath)
{
- if (Utils.OS.IsOSX && editorId == ExternalEditorId.VisualStudioForMac)
+ 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 d6fa2eeba7..3f1d5ac3ca 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MonoDevelop/Instance.cs
@@ -10,25 +10,25 @@ 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>();
string command;
- if (OS.IsOSX)
+ 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),
@@ -85,17 +85,17 @@ namespace GodotTools.Ides.MonoDevelop
public Instance(string solutionFile, EditorId editorId)
{
- if (editorId == EditorId.VisualStudioForMac && !OS.IsOSX)
+ 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;
@@ -103,7 +103,7 @@ namespace GodotTools.Ides.MonoDevelop
static Instance()
{
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
ExecutableNames = new Dictionary<EditorId, string>
{
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
index e22e9af919..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
@@ -32,7 +33,7 @@ namespace GodotTools.Ides.Rider
{
return CollectRiderInfosWindows();
}
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
return CollectRiderInfosMac();
}
@@ -47,16 +48,16 @@ namespace GodotTools.Ides.Rider
GD.PushWarning(e.Message);
}
- return new RiderInfo[0];
+ return Array.Empty<RiderInfo>();
}
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,11 +135,11 @@ namespace GodotTools.Ides.Rider
{
if (OS.IsWindows)
{
- var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return GetToolboxRiderRootPath(localAppData);
}
- if (OS.IsOSX)
+ if (OS.IsMacOS)
{
var home = Environment.GetEnvironmentVariable("HOME");
if (string.IsNullOrEmpty(home))
@@ -211,7 +212,7 @@ namespace GodotTools.Ides.Rider
{
if (OS.IsWindows || OS.IsUnixLike)
return "../../build.txt";
- if (OS.IsOSX)
+ if (OS.IsMacOS)
return "Contents/Resources/build.txt";
throw new Exception("Unknown OS.");
}
@@ -249,7 +250,7 @@ namespace GodotTools.Ides.Rider
bool isMac)
{
if (!Directory.Exists(toolboxRiderRootPath))
- return new string[0];
+ return Array.Empty<string>();
var channelDirs = Directory.GetDirectories(toolboxRiderRootPath);
var paths = channelDirs.SelectMany(channelDir =>
@@ -295,7 +296,7 @@ namespace GodotTools.Ides.Rider
Logger.Warn($"Failed to get RiderPath from {channelDir}", e);
}
- return new string[0];
+ return Array.Empty<string>();
})
.Where(c => !string.IsNullOrEmpty(c))
.ToArray();
@@ -306,7 +307,7 @@ namespace GodotTools.Ides.Rider
{
var folder = new DirectoryInfo(Path.Combine(buildDir, dirName));
if (!folder.Exists)
- return new string[0];
+ return Array.Empty<string>();
if (!isMac)
return new[] { Path.Combine(folder.FullName, searchPattern) }.Where(File.Exists).ToArray();
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
index 16f91a0925..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,15 +97,15 @@ 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);
if (line >= 0)
{
args.Add("--line");
- args.Add(line.ToString());
+ args.Add((line + 1).ToString()); // https://github.com/JetBrains/godot-support/issues/61
}
args.Add(scriptPath);
try
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/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 7e5049e4b7..77370090ec 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -1,7 +1,5 @@
-using System;
using System.Runtime.CompilerServices;
using Godot;
-using Godot.Collections;
using GodotTools.IdeMessaging.Requests;
namespace GodotTools.Internals
@@ -42,9 +40,6 @@ namespace GodotTools.Internals
public static void EditorNodeShowScriptScreen() => internal_EditorNodeShowScriptScreen();
- public static Dictionary<string, object> GetScriptsMetadataOrNothing() =>
- internal_GetScriptsMetadataOrNothing(typeof(Dictionary<string, object>));
-
public static string MonoWindowsInstallRoot => internal_MonoWindowsInstallRoot();
public static void EditorRunPlay() => internal_EditorRunPlay();
@@ -101,9 +96,6 @@ namespace GodotTools.Internals
private static extern void internal_EditorNodeShowScriptScreen();
[MethodImpl(MethodImplOptions.InternalCall)]
- private static extern Dictionary<string, object> internal_GetScriptsMetadataOrNothing(Type dictType);
-
- [MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_MonoWindowsInstallRoot();
[MethodImpl(MethodImplOptions.InternalCall)]
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
deleted file mode 100644
index c72a84c513..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
-using Godot;
-using Godot.Collections;
-
-namespace GodotTools.Internals
-{
- public static class ScriptClassParser
- {
- public class ClassDecl
- {
- public string Name { get; }
- public string Namespace { get; }
- public bool Nested { get; }
- public long BaseCount { get; }
-
- public string SearchName => Nested ?
- Name.Substring(Name.LastIndexOf(".", StringComparison.Ordinal) + 1) :
- Name;
-
- public ClassDecl(string name, string @namespace, bool nested, long baseCount)
- {
- Name = name;
- Namespace = @namespace;
- Nested = nested;
- BaseCount = baseCount;
- }
- }
-
- [MethodImpl(MethodImplOptions.InternalCall)]
- private static extern Error internal_ParseFile(string filePath, Array<Dictionary> classes, out string errorStr);
-
- public static Error ParseFile(string filePath, out IEnumerable<ClassDecl> classes, out string errorStr)
- {
- var classesArray = new Array<Dictionary>();
- var error = internal_ParseFile(filePath, classesArray, out errorStr);
- if (error != Error.Ok)
- {
- classes = null;
- return error;
- }
-
- var classesList = new List<ClassDecl>();
-
- foreach (var classDeclDict in classesArray)
- {
- classesList.Add(new ClassDecl(
- (string)classDeclDict["name"],
- (string)classDeclDict["namespace"],
- (bool)classDeclDict["nested"],
- (long)classDeclDict["base_count"]
- ));
- }
-
- classes = classesList;
-
- return Error.Ok;
- }
- }
-}
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 6c05891f2c..93a1360cb6 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
@@ -13,15 +13,15 @@ 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
{
public const string Windows = "Windows";
- public const string OSX = "OSX";
+ public const string MacOS = "macOS";
public const string Linux = "Linux";
public const string FreeBSD = "FreeBSD";
public const string NetBSD = "NetBSD";
@@ -37,7 +37,7 @@ namespace GodotTools.Utils
public static class Platforms
{
public const string Windows = "windows";
- public const string OSX = "osx";
+ public const string MacOS = "osx";
public const string LinuxBSD = "linuxbsd";
public const string Server = "server";
public const string UWP = "uwp";
@@ -50,7 +50,7 @@ namespace GodotTools.Utils
public static readonly Dictionary<string, string> PlatformNameMap = new Dictionary<string, string>
{
[Names.Windows] = Platforms.Windows,
- [Names.OSX] = Platforms.OSX,
+ [Names.MacOS] = Platforms.MacOS,
[Names.Linux] = Platforms.LinuxBSD,
[Names.FreeBSD] = Platforms.LinuxBSD,
[Names.NetBSD] = Platforms.LinuxBSD,
@@ -74,14 +74,14 @@ namespace GodotTools.Utils
}
private static readonly IEnumerable<string> LinuxBSDPlatforms =
- new[] {Names.Linux, Names.FreeBSD, Names.NetBSD, Names.BSD};
+ new[] { Names.Linux, Names.FreeBSD, Names.NetBSD, Names.BSD };
private static readonly IEnumerable<string> UnixLikePlatforms =
- new[] {Names.OSX, Names.Server, Names.Haiku, Names.Android, Names.iOS}
+ new[] { Names.MacOS, Names.Server, Names.Haiku, Names.Android, Names.iOS }
.Concat(LinuxBSDPlatforms).ToArray();
private static readonly Lazy<bool> _isWindows = new Lazy<bool>(() => IsOS(Names.Windows));
- private static readonly Lazy<bool> _isOSX = new Lazy<bool>(() => IsOS(Names.OSX));
+ private static readonly Lazy<bool> _isMacOS = new Lazy<bool>(() => IsOS(Names.MacOS));
private static readonly Lazy<bool> _isLinuxBSD = new Lazy<bool>(() => IsAnyOS(LinuxBSDPlatforms));
private static readonly Lazy<bool> _isServer = new Lazy<bool>(() => IsOS(Names.Server));
private static readonly Lazy<bool> _isUWP = new Lazy<bool>(() => IsOS(Names.UWP));
@@ -92,7 +92,7 @@ namespace GodotTools.Utils
private static readonly Lazy<bool> _isUnixLike = new Lazy<bool>(() => IsAnyOS(UnixLikePlatforms));
public static bool IsWindows => _isWindows.Value || IsUWP;
- public static bool IsOSX => _isOSX.Value;
+ public static bool IsMacOS => _isMacOS.Value;
public static bool IsLinuxBSD => _isLinuxBSD.Value;
public static bool IsServer => _isServer.Value;
public static bool IsUWP => _isUWP.Value;
@@ -106,21 +106,34 @@ 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)
{
- string[] windowsExts = Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? new string[] { };
+ string[] windowsExts = Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) ?? Array.Empty<string>();
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
+ char[] invalidPathChars = Path.GetInvalidPathChars();
var searchDirs = new List<string>();
if (pathDirs != null)
- searchDirs.AddRange(pathDirs);
+ {
+ foreach (var pathDir in pathDirs)
+ {
+ if (pathDir.IndexOfAny(invalidPathChars) != -1)
+ continue;
+
+ searchDirs.Add(pathDir);
+ }
+ }
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
@@ -128,20 +141,29 @@ namespace GodotTools.Utils
return searchDirs.Select(dir => Path.Combine(dir, name)).FirstOrDefault(File.Exists);
return (from dir in searchDirs
- select Path.Combine(dir, name)
+ select Path.Combine(dir, name)
into path
- from ext in windowsExts
- select path + ext).FirstOrDefault(File.Exists);
+ from ext in windowsExts
+ select path + ext).FirstOrDefault(File.Exists);
}
private static string PathWhichUnix([NotNull] string name)
{
string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
+ char[] invalidPathChars = Path.GetInvalidPathChars();
var searchDirs = new List<string>();
if (pathDirs != null)
- searchDirs.AddRange(pathDirs);
+ {
+ foreach (var pathDir in pathDirs)
+ {
+ if (pathDir.IndexOfAny(invalidPathChars) != -1)
+ continue;
+
+ searchDirs.Add(pathDir);
+ }
+ }
searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
@@ -196,7 +218,7 @@ namespace GodotTools.Utils
startInfo.UseShellExecute = false;
- using (var process = new Process {StartInfo = startInfo})
+ using (var process = new Process { StartInfo = startInfo })
{
process.Start();
process.WaitForExit();
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index a17c371117..148a6796d2 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,13 +32,14 @@
#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
-#include "core/engine.h"
-#include "core/global_constants.h"
+#include "core/config/engine.h"
+#include "core/core_constants.h"
#include "core/io/compression.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/os/os.h"
-#include "core/ucaps.h"
+#include "core/string/ucaps.h"
+#include "main/main.h"
#include "../glue/cs_glue_version.gen.h"
#include "../godotsharp_defs.h"
@@ -97,7 +98,7 @@
#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable"
#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info"
-#define BINDINGS_GENERATOR_VERSION UINT32_C(11)
+#define BINDINGS_GENERATOR_VERSION UINT32_C(13)
const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n");
@@ -181,11 +182,11 @@ static String snake_to_camel_case(const String &p_identifier, bool p_input_is_up
String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype) {
// Based on the version in EditorHelp
- if (p_bbcode.empty()) {
+ if (p_bbcode.is_empty()) {
return String();
}
- DocData *doc = EditorHelp::get_doc_data();
+ DocTools *doc = EditorHelp::get_doc_data();
String bbcode = p_bbcode;
@@ -364,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);
@@ -386,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) {
@@ -415,8 +415,8 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
// Try to find as global enum constant
const EnumInterface *target_ienum = nullptr;
- for (const List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) {
- target_ienum = &E->get();
+ for (const EnumInterface &ienum : global_enums) {
+ target_ienum = &ienum;
target_iconst = find_constant_by_name(target_name, target_ienum->constants);
if (target_iconst) {
break;
@@ -454,8 +454,8 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
// Try to find as enum constant in the current class
const EnumInterface *target_ienum = nullptr;
- for (const List<EnumInterface>::Element *E = target_itype->enums.front(); E; E = E->next()) {
- target_ienum = &E->get();
+ for (const EnumInterface &ienum : target_itype->enums) {
+ target_ienum = &ienum;
target_iconst = find_constant_by_name(target_name, target_ienum->constants);
if (target_iconst) {
break;
@@ -644,7 +644,7 @@ String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterf
}
int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
- CRASH_COND(p_ienum.constants.empty());
+ CRASH_COND(p_ienum.constants.is_empty());
const ConstantInterface &front_iconstant = p_ienum.constants.front()->get();
Vector<String> front_parts = front_iconstant.name.split("_", /* p_allow_empty: */ true);
@@ -654,9 +654,7 @@ int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
return 0;
}
- for (const List<ConstantInterface>::Element *E = p_ienum.constants.front()->next(); E; E = E->next()) {
- const ConstantInterface &iconstant = E->get();
-
+ for (const ConstantInterface &iconstant : p_ienum.constants) {
Vector<String> parts = iconstant.name.split("_", /* p_allow_empty: */ true);
int i;
@@ -681,12 +679,10 @@ int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) {
void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumInterface &p_ienum, int p_prefix_length) {
if (p_prefix_length > 0) {
- for (List<ConstantInterface>::Element *E = p_ienum.constants.front(); E; E = E->next()) {
+ for (ConstantInterface &iconstant : p_ienum.constants) {
int curr_prefix_length = p_prefix_length;
- ConstantInterface &curr_const = E->get();
-
- String constant_name = curr_const.name;
+ String constant_name = iconstant.name;
Vector<String> parts = constant_name.split("_", /* p_allow_empty: */ true);
@@ -712,15 +708,13 @@ void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumI
constant_name += parts[i];
}
- curr_const.proxy_name = snake_to_pascal_case(constant_name, true);
+ iconstant.proxy_name = snake_to_pascal_case(constant_name, true);
}
}
}
void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
- for (const List<MethodInterface>::Element *E = p_itype.methods.front(); E; E = E->next()) {
- const MethodInterface &imethod = E->get();
-
+ for (const MethodInterface &imethod : p_itype.methods) {
if (imethod.is_virtual) {
continue;
}
@@ -734,8 +728,8 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
// Get arguments information
int i = 0;
- for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
- const TypeInterface *arg_type = _get_type_or_placeholder(F->get().type);
+ for (const ArgumentInterface &iarg : imethod.arguments) {
+ const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
im_sig += ", ";
im_sig += arg_type->im_type_in;
@@ -775,14 +769,80 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
if (p_itype.api_type != ClassDB::API_EDITOR) {
match->get().editor_only = false;
}
- method_icalls_map.insert(&E->get(), &match->get());
+ method_icalls_map.insert(&imethod, &match->get());
} else {
List<InternalCall>::Element *added = method_icalls.push_back(im_icall);
- method_icalls_map.insert(&E->get(), &added->get());
+ method_icalls_map.insert(&imethod, &added->get());
}
}
}
+void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) {
+ p_output.append("using System;\n\n");
+ p_output.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
+ // The class where we put the extensions doesn't matter, so just use "GD".
+ p_output.append(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{");
+
+#define ARRAY_IS_EMPTY(m_type) \
+ p_output.append("\n" INDENT2 "/// <summary>\n"); \
+ p_output.append(INDENT2 "/// Returns true if this " #m_type " array is empty or doesn't exist.\n"); \
+ p_output.append(INDENT2 "/// </summary>\n"); \
+ p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array check.</param>\n"); \
+ p_output.append(INDENT2 "/// <returns>Whether or not the array is empty.</returns>\n"); \
+ p_output.append(INDENT2 "public static bool IsEmpty(this " #m_type "[] instance)\n"); \
+ p_output.append(INDENT2 OPEN_BLOCK); \
+ p_output.append(INDENT3 "return instance == null || instance.Length == 0;\n"); \
+ p_output.append(INDENT2 CLOSE_BLOCK);
+
+#define ARRAY_JOIN(m_type) \
+ p_output.append("\n" INDENT2 "/// <summary>\n"); \
+ p_output.append(INDENT2 "/// Converts this " #m_type " array to a string delimited by the given string.\n"); \
+ p_output.append(INDENT2 "/// </summary>\n"); \
+ p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
+ p_output.append(INDENT2 "/// <param name=\"delimiter\">The delimiter to use between items.</param>\n"); \
+ p_output.append(INDENT2 "/// <returns>A single string with all items.</returns>\n"); \
+ p_output.append(INDENT2 "public static string Join(this " #m_type "[] instance, string delimiter = \", \")\n"); \
+ p_output.append(INDENT2 OPEN_BLOCK); \
+ p_output.append(INDENT3 "return String.Join(delimiter, instance);\n"); \
+ p_output.append(INDENT2 CLOSE_BLOCK);
+
+#define ARRAY_STRINGIFY(m_type) \
+ p_output.append("\n" INDENT2 "/// <summary>\n"); \
+ p_output.append(INDENT2 "/// Converts this " #m_type " array to a string with brackets.\n"); \
+ p_output.append(INDENT2 "/// </summary>\n"); \
+ p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
+ p_output.append(INDENT2 "/// <returns>A single string with all items.</returns>\n"); \
+ p_output.append(INDENT2 "public static string Stringify(this " #m_type "[] instance)\n"); \
+ p_output.append(INDENT2 OPEN_BLOCK); \
+ p_output.append(INDENT3 "return \"[\" + instance.Join() + \"]\";\n"); \
+ p_output.append(INDENT2 CLOSE_BLOCK);
+
+#define ARRAY_ALL(m_type) \
+ ARRAY_IS_EMPTY(m_type) \
+ ARRAY_JOIN(m_type) \
+ ARRAY_STRINGIFY(m_type)
+
+ ARRAY_ALL(byte);
+ ARRAY_ALL(int);
+ ARRAY_ALL(long);
+ ARRAY_ALL(float);
+ ARRAY_ALL(double);
+ ARRAY_ALL(string);
+ ARRAY_ALL(Color);
+ ARRAY_ALL(Vector2);
+ ARRAY_ALL(Vector2i);
+ ARRAY_ALL(Vector3);
+ ARRAY_ALL(Vector3i);
+
+#undef ARRAY_ALL
+#undef ARRAY_IS_EMPTY
+#undef ARRAY_JOIN
+#undef ARRAY_STRINGIFY
+
+ p_output.append(INDENT1 CLOSE_BLOCK); // End of GD class.
+ p_output.append(CLOSE_BLOCK); // End of namespace.
+}
+
void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
// Constants (in partial GD class)
@@ -792,9 +852,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
p_output.append(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{");
- for (const List<ConstantInterface>::Element *E = global_constants.front(); E; E = E->next()) {
- const ConstantInterface &iconstant = E->get();
-
+ for (const ConstantInterface &iconstant : global_constants) {
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), nullptr);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
@@ -819,7 +877,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append(";");
}
- if (!global_constants.empty()) {
+ if (!global_constants.is_empty()) {
p_output.append("\n");
}
@@ -827,10 +885,8 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
// Enums
- for (List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) {
- const EnumInterface &ienum = E->get();
-
- CRASH_COND(ienum.constants.empty());
+ for (const EnumInterface &ienum : global_enums) {
+ CRASH_COND(ienum.constants.is_empty());
String enum_proxy_name = ienum.cname.operator String();
@@ -854,9 +910,8 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append(enum_proxy_name);
p_output.append("\n" INDENT1 OPEN_BLOCK);
- for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) {
- const ConstantInterface &iconstant = F->get();
-
+ const ConstantInterface &last = ienum.constants.back()->get();
+ for (const ConstantInterface &iconstant : ienum.constants) {
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), nullptr);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
@@ -878,7 +933,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append(iconstant.proxy_name);
p_output.append(" = ");
p_output.append(itos(iconstant.value));
- p_output.append(F != ienum.constants.back() ? ",\n" : "\n");
+ p_output.append(&iconstant != &last ? ",\n" : "\n");
}
p_output.append(INDENT1 CLOSE_BLOCK);
@@ -926,6 +981,19 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
compile_items.push_back(output_file);
}
+ // Generate source file for array extensions
+ {
+ StringBuilder extensions_source;
+ _generate_array_extensions(extensions_source);
+ String output_file = path::join(base_gen_dir, BINDINGS_GLOBAL_SCOPE_CLASS "_extensions.cs");
+ Error save_err = _save_file(output_file, extensions_source);
+ if (save_err != OK) {
+ return save_err;
+ }
+
+ compile_items.push_back(output_file);
+ }
+
for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
const TypeInterface &itype = E.get();
@@ -973,11 +1041,11 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
cs_icalls_content.append(m_icall.im_sig + ");\n"); \
}
- for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next()) {
- ADD_INTERNAL_CALL(E->get());
+ for (const InternalCall &internal_call : core_custom_icalls) {
+ ADD_INTERNAL_CALL(internal_call);
}
- for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) {
- ADD_INTERNAL_CALL(E->get());
+ for (const InternalCall &internal_call : method_icalls) {
+ ADD_INTERNAL_CALL(internal_call);
}
#undef ADD_INTERNAL_CALL
@@ -1081,11 +1149,11 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
cs_icalls_content.append(m_icall.im_sig + ");\n"); \
}
- for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next()) {
- ADD_INTERNAL_CALL(E->get());
+ for (const InternalCall &internal_call : editor_custom_icalls) {
+ ADD_INTERNAL_CALL(internal_call);
}
- for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) {
- ADD_INTERNAL_CALL(E->get());
+ for (const InternalCall &internal_call : method_icalls) {
+ ADD_INTERNAL_CALL(internal_call);
}
#undef ADD_INTERNAL_CALL
@@ -1180,7 +1248,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
CRASH_COND(itype.cname != name_cache.type_Object);
CRASH_COND(!itype.is_instantiable);
CRASH_COND(itype.api_type != ClassDB::API_CORE);
- CRASH_COND(itype.is_reference);
+ CRASH_COND(itype.is_ref_counted);
CRASH_COND(itype.is_singleton);
}
@@ -1247,9 +1315,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// Add constants
- for (const List<ConstantInterface>::Element *E = itype.constants.front(); E; E = E->next()) {
- const ConstantInterface &iconstant = E->get();
-
+ for (const ConstantInterface &iconstant : itype.constants) {
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
@@ -1280,18 +1346,15 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// Add enums
- for (const List<EnumInterface>::Element *E = itype.enums.front(); E; E = E->next()) {
- const EnumInterface &ienum = E->get();
-
- ERR_FAIL_COND_V(ienum.constants.empty(), ERR_BUG);
+ for (const EnumInterface &ienum : itype.enums) {
+ ERR_FAIL_COND_V(ienum.constants.is_empty(), ERR_BUG);
output.append(MEMBER_BEGIN "public enum ");
output.append(ienum.cname.operator String());
output.append(MEMBER_BEGIN OPEN_BLOCK);
- for (const List<ConstantInterface>::Element *F = ienum.constants.front(); F; F = F->next()) {
- const ConstantInterface &iconstant = F->get();
-
+ const ConstantInterface &last = ienum.constants.back()->get();
+ for (const ConstantInterface &iconstant : ienum.constants) {
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
@@ -1313,7 +1376,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append(iconstant.proxy_name);
output.append(" = ");
output.append(itos(iconstant.value));
- output.append(F != ienum.constants.back() ? ",\n" : "\n");
+ output.append(&iconstant != &last ? ",\n" : "\n");
}
output.append(INDENT2 CLOSE_BLOCK);
@@ -1321,8 +1384,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// Add properties
- for (const List<PropertyInterface>::Element *E = itype.properties.front(); E; E = E->next()) {
- const PropertyInterface &iprop = E->get();
+ for (const PropertyInterface &iprop : itype.properties) {
Error prop_err = _generate_cs_property(itype, iprop, output);
ERR_FAIL_COND_V_MSG(prop_err != OK, prop_err,
"Failed to generate property '" + iprop.cname.operator String() +
@@ -1368,7 +1430,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output.append(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
output.append("." + ctor_method);
- output.append("(this);\n" CLOSE_BLOCK_L2);
+ output.append("(this);\n" INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2);
} else {
// Hide the constructor
output.append(MEMBER_BEGIN "internal ");
@@ -1383,15 +1445,13 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
int method_bind_count = 0;
- for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) {
- const MethodInterface &imethod = E->get();
+ for (const MethodInterface &imethod : itype.methods) {
Error method_err = _generate_cs_method(itype, imethod, method_bind_count, output);
ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
}
- for (const List<SignalInterface>::Element *E = itype.signals_.front(); E; E = E->next()) {
- const SignalInterface &isignal = E->get();
+ for (const SignalInterface &isignal : itype.signals_) {
Error method_err = _generate_cs_signal(itype, isignal, output);
ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
"Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'.");
@@ -1463,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: '" +
@@ -1479,6 +1539,12 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
ERR_FAIL_COND_V_MSG(prop_itype->is_singleton, ERR_BUG,
"Property type is a singleton: '" + p_itype.name + "." + String(p_iprop.cname) + "'.");
+ if (p_itype.api_type == ClassDB::API_CORE) {
+ ERR_FAIL_COND_V_MSG(prop_itype->api_type == ClassDB::API_EDITOR, ERR_BUG,
+ "Property '" + p_itype.name + "." + String(p_iprop.cname) + "' has type '" + prop_itype->name +
+ "' from the editor API. Core API cannot have dependencies on the editor API.");
+ }
+
if (p_iprop.prop_doc && p_iprop.prop_doc->description.size()) {
String xml_summary = bbcode_to_xml(fix_doc_description(p_iprop.prop_doc->description), &p_itype);
Vector<String> summary_lines = xml_summary.length() ? xml_summary.split("\n") : Vector<String>();
@@ -1575,6 +1641,12 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
ERR_FAIL_COND_V_MSG(return_type->is_singleton, ERR_BUG,
"Method return type is a singleton: '" + p_itype.name + "." + p_imethod.name + "'.");
+ if (p_itype.api_type == ClassDB::API_CORE) {
+ ERR_FAIL_COND_V_MSG(return_type->api_type == ClassDB::API_EDITOR, ERR_BUG,
+ "Method '" + p_itype.name + "." + p_imethod.name + "' has return type '" + return_type->name +
+ "' from the editor API. Core API cannot have dependencies on the editor API.");
+ }
+
String method_bind_field = "__method_bind_" + itos(p_method_bind_count);
String arguments_sig;
@@ -1586,13 +1658,19 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
StringBuilder default_args_doc;
// Retrieve information from the arguments
- for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
- const ArgumentInterface &iarg = F->get();
+ const ArgumentInterface &first = p_imethod.arguments.front()->get();
+ for (const ArgumentInterface &iarg : p_imethod.arguments) {
const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG,
"Argument type is a singleton: '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'.");
+ if (p_itype.api_type == ClassDB::API_CORE) {
+ ERR_FAIL_COND_V_MSG(arg_type->api_type == ClassDB::API_EDITOR, ERR_BUG,
+ "Argument '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "' has type '" +
+ arg_type->name + "' from the editor API. Core API cannot have dependencies on the editor API.");
+ }
+
if (iarg.default_argument.size()) {
CRASH_COND_MSG(!_arg_default_value_is_assignable_to_type(iarg.def_param_value, *arg_type),
"Invalid default value for parameter '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'.");
@@ -1601,7 +1679,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Add the current arguments to the signature
// If the argument has a default value which is not a constant, we will make it Nullable
{
- if (F != p_imethod.arguments.front()) {
+ if (&iarg != &first) {
arguments_sig += ", ";
}
@@ -1656,19 +1734,26 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
cs_in_statements += " : ";
}
- String def_arg = sformat(iarg.default_argument, arg_type->cs_type);
+ String cs_type = arg_type->cs_type;
+ if (cs_type.ends_with("[]")) {
+ cs_type = cs_type.substr(0, cs_type.length() - 2);
+ }
+
+ String def_arg = sformat(iarg.default_argument, cs_type);
cs_in_statements += def_arg;
cs_in_statements += ";\n" INDENT3;
- icall_params += arg_type->cs_in.empty() ? arg_in : sformat(arg_type->cs_in, arg_in);
+ icall_params += arg_type->cs_in.is_empty() ? arg_in : sformat(arg_type->cs_in, arg_in);
// Apparently the name attribute must not include the @
String param_tag_name = iarg.name.begins_with("@") ? iarg.name.substr(1, iarg.name.length()) : iarg.name;
+ // Escape < and > in the attribute default value
+ String param_def_arg = def_arg.replacen("<", "&lt;").replacen(">", "&gt;");
- default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + def_arg + "</param>");
+ default_args_doc.append(MEMBER_BEGIN "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + param_def_arg + "</param>");
} else {
- icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
+ icall_params += arg_type->cs_in.is_empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
}
}
@@ -1714,7 +1799,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
}
if (p_imethod.is_deprecated) {
- if (p_imethod.deprecation_message.empty()) {
+ if (p_imethod.deprecation_message.is_empty()) {
WARN_PRINT("An empty deprecation message is discouraged. Method: '" + p_imethod.proxy_name + "'.");
}
@@ -1757,9 +1842,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append(p_imethod.name);
p_output.append("\"");
- for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
+ for (const ArgumentInterface &iarg : p_imethod.arguments) {
p_output.append(", ");
- p_output.append(F->get().name);
+ p_output.append(iarg.name);
}
p_output.append(");\n" CLOSE_BLOCK_L2);
@@ -1782,7 +1867,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
if (return_type->cname == name_cache.type_void) {
p_output.append(im_call + "(" + icall_params + ");\n");
- } else if (return_type->cs_out.empty()) {
+ } else if (return_type->cs_out.is_empty()) {
p_output.append("return " + im_call + "(" + icall_params + ");\n");
} else {
p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_type->cs_type, return_type->im_type_out));
@@ -1801,16 +1886,22 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
String arguments_sig;
// Retrieve information from the arguments
- for (const List<ArgumentInterface>::Element *F = p_isignal.arguments.front(); F; F = F->next()) {
- const ArgumentInterface &iarg = F->get();
+ const ArgumentInterface &first = p_isignal.arguments.front()->get();
+ for (const ArgumentInterface &iarg : p_isignal.arguments) {
const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG,
- "Argument type is a singleton: '" + iarg.name + "' of signal" + p_itype.name + "." + p_isignal.name + "'.");
+ "Argument type is a singleton: '" + iarg.name + "' of signal '" + p_itype.name + "." + p_isignal.name + "'.");
+
+ if (p_itype.api_type == ClassDB::API_CORE) {
+ ERR_FAIL_COND_V_MSG(arg_type->api_type == ClassDB::API_EDITOR, ERR_BUG,
+ "Argument '" + iarg.name + "' of signal '" + p_itype.name + "." + p_isignal.name + "' has type '" +
+ arg_type->name + "' from the editor API. Core API cannot have dependencies on the editor API.");
+ }
// Add the current arguments to the signature
- if (F != p_isignal.arguments.front()) {
+ if (&iarg != &first) {
arguments_sig += ", ";
}
@@ -1839,7 +1930,7 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
}
if (p_isignal.is_deprecated) {
- if (p_isignal.deprecation_message.empty()) {
+ if (p_isignal.deprecation_message.is_empty()) {
WARN_PRINT("An empty deprecation message is discouraged. Signal: '" + p_isignal.proxy_name + "'.");
}
@@ -1938,8 +2029,7 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
- for (const List<MethodInterface>::Element *E = itype.methods.front(); E; E = E->next()) {
- const MethodInterface &imethod = E->get();
+ for (const MethodInterface &imethod : itype.methods) {
Error method_err = _generate_glue_method(itype, imethod, output);
ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
"Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
@@ -1999,31 +2089,31 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
{ \
- output.append("\tmono_add_internal_call("); \
+ output.append("\tGDMonoUtils::add_internal_call("); \
output.append("\"" BINDINGS_NAMESPACE "."); \
output.append(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
output.append("::"); \
output.append(m_icall.name); \
- output.append("\", (void*)"); \
+ output.append("\", "); \
output.append(m_icall.name); \
output.append(");\n"); \
}
bool tools_sequence = false;
- for (const List<InternalCall>::Element *E = core_custom_icalls.front(); E; E = E->next()) {
+ for (const InternalCall &internal_call : core_custom_icalls) {
if (tools_sequence) {
- if (!E->get().editor_only) {
+ if (!internal_call.editor_only) {
tools_sequence = false;
output.append("#endif\n");
}
} else {
- if (E->get().editor_only) {
+ if (internal_call.editor_only) {
output.append("#ifdef TOOLS_ENABLED\n");
tools_sequence = true;
}
}
- ADD_INTERNAL_CALL_REGISTRATION(E->get());
+ ADD_INTERNAL_CALL_REGISTRATION(internal_call);
}
if (tools_sequence) {
@@ -2032,24 +2122,24 @@ Error BindingsGenerator::generate_glue(const String &p_output_dir) {
}
output.append("#ifdef TOOLS_ENABLED\n");
- for (const List<InternalCall>::Element *E = editor_custom_icalls.front(); E; E = E->next())
- ADD_INTERNAL_CALL_REGISTRATION(E->get());
+ for (const InternalCall &internal_call : editor_custom_icalls)
+ ADD_INTERNAL_CALL_REGISTRATION(internal_call);
output.append("#endif // TOOLS_ENABLED\n");
- for (const List<InternalCall>::Element *E = method_icalls.front(); E; E = E->next()) {
+ for (const InternalCall &internal_call : method_icalls) {
if (tools_sequence) {
- if (!E->get().editor_only) {
+ if (!internal_call.editor_only) {
tools_sequence = false;
output.append("#endif\n");
}
} else {
- if (E->get().editor_only) {
+ if (internal_call.editor_only) {
output.append("#ifdef TOOLS_ENABLED\n");
tools_sequence = true;
}
}
- ADD_INTERNAL_CALL_REGISTRATION(E->get());
+ ADD_INTERNAL_CALL_REGISTRATION(internal_call);
}
if (tools_sequence) {
@@ -2105,8 +2195,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
// Get arguments information
int i = 0;
- for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
- const ArgumentInterface &iarg = F->get();
+ for (const ArgumentInterface &iarg : p_imethod.arguments) {
const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
String c_param_name = "arg" + itos(i + 1);
@@ -2180,8 +2269,8 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
}
if (return_type->is_object_type) {
- ptrcall_return_type = return_type->is_reference ? "Ref<Reference>" : return_type->c_type;
- initialization = return_type->is_reference ? "" : " = nullptr";
+ ptrcall_return_type = return_type->is_ref_counted ? "Ref<RefCounted>" : return_type->c_type;
+ initialization = return_type->is_ref_counted ? "" : " = nullptr";
} else {
ptrcall_return_type = return_type->c_type;
}
@@ -2263,7 +2352,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
}
if (!ret_void) {
- if (return_type->c_out.empty()) {
+ if (return_type->c_out.is_empty()) {
p_output.append("\treturn " C_LOCAL_RET ";\n");
} else if (return_type->ret_as_byref_arg) {
p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name, "arg_ret"));
@@ -2391,42 +2480,42 @@ 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::TRANSFORM:
case Variant::TRANSFORM2D:
+ case Variant::TRANSFORM3D:
case Variant::BASIS:
- case Variant::QUAT:
+ case Variant::QUATERNION:
case Variant::PLANE:
case Variant::AABB:
case Variant::COLOR:
case Variant::VECTOR2:
case Variant::RECT2:
case Variant::VECTOR3:
- case Variant::_RID:
+ case Variant::RID:
case Variant::ARRAY:
case Variant::DICTIONARY:
case Variant::PACKED_BYTE_ARRAY:
@@ -2445,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;
@@ -2496,12 +2585,12 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
itype.base_name = ClassDB::get_parent_class(type_cname);
itype.is_singleton = Engine::get_singleton()->has_singleton(itype.proxy_name);
itype.is_instantiable = class_info->creation_func && !itype.is_singleton;
- itype.is_reference = ClassDB::is_parent_class(type_cname, name_cache.type_Reference);
- itype.memory_own = itype.is_reference;
+ itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted);
+ itype.memory_own = itype.is_ref_counted;
itype.c_out = "\treturn ";
itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED;
- itype.c_out += itype.is_reference ? "(%1.ptr());\n" : "(%1);\n";
+ itype.c_out += itype.is_ref_counted ? "(%1.ptr());\n" : "(%1);\n";
itype.cs_in = itype.is_singleton ? BINDINGS_PTR_FIELD : "Object." CS_SMETHOD_GETINSTANCE "(%0)";
@@ -2519,10 +2608,8 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
Map<StringName, StringName> accessor_methods;
- for (const List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
- const PropertyInfo &property = E->get();
-
- if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY) {
+ for (const PropertyInfo &property : property_list) {
+ if (property.usage & PROPERTY_USAGE_GROUP || property.usage & PROPERTY_USAGE_SUBGROUP || property.usage & PROPERTY_USAGE_CATEGORY || (property.type == Variant::NIL && property.usage & PROPERTY_USAGE_ARRAY)) {
continue;
}
@@ -2580,12 +2667,10 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
ClassDB::get_method_list(type_cname, &method_list, true);
method_list.sort();
- for (List<MethodInfo>::Element *E = method_list.front(); E; E = E->next()) {
- const MethodInfo &method_info = E->get();
-
+ for (const MethodInfo &method_info : method_list) {
int argc = method_info.arguments.size();
- if (method_info.name.empty()) {
+ if (method_info.name.is_empty()) {
continue;
}
@@ -2628,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;
@@ -2637,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_Reference);
+ 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 + "'.");
@@ -2736,9 +2821,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
// Classes starting with an underscore are ignored unless they're used as a property setter or getter
if (!imethod.is_virtual && imethod.name[0] == '_') {
- for (const List<PropertyInterface>::Element *F = itype.properties.front(); F; F = F->next()) {
- const PropertyInterface &iprop = F->get();
-
+ for (const PropertyInterface &iprop : itype.properties) {
if (iprop.setter == imethod.name || iprop.getter == imethod.name) {
imethod.is_internal = true;
itype.methods.push_back(imethod);
@@ -2848,8 +2931,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
}
EnumInterface ienum(enum_proxy_cname);
const List<StringName> &enum_constants = enum_map.get(*k);
- for (const List<StringName>::Element *E = enum_constants.front(); E; E = E->next()) {
- const StringName &constant_cname = E->get();
+ for (const StringName &constant_cname : enum_constants) {
String constant_name = constant_cname.operator String();
int *value = class_info->constant_map.getptr(constant_cname);
ERR_FAIL_NULL_V(value, false);
@@ -2885,9 +2967,8 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
enum_types.insert(enum_itype.cname, enum_itype);
}
- for (const List<String>::Element *E = constants.front(); E; E = E->next()) {
- const String &constant_name = E->get();
- int *value = class_info->constant_map.getptr(StringName(E->get()));
+ for (const String &constant_name : constants) {
+ int *value = class_info->constant_map.getptr(StringName(constant_name));
ERR_FAIL_NULL_V(value, false);
ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), *value);
@@ -2932,9 +3013,9 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
}
break;
case Variant::FLOAT:
-#ifndef REAL_T_IS_DOUBLE
- r_iarg.default_argument += "f";
-#endif
+ if (r_iarg.type.cname == name_cache.type_float) {
+ r_iarg.default_argument += "f";
+ }
break;
case Variant::STRING:
case Variant::STRING_NAME:
@@ -2947,23 +3028,29 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = "\"" + r_iarg.default_argument + "\"";
}
break;
- case Variant::TRANSFORM:
- if (p_val.operator Transform() == Transform()) {
- r_iarg.default_argument.clear();
- }
- r_iarg.default_argument = "new %s(" + r_iarg.default_argument + ")";
+ case Variant::PLANE: {
+ Plane plane = p_val.operator Plane();
+ r_iarg.default_argument = "new Plane(new Vector3" + plane.normal.operator String() + ", " + rtos(plane.d) + ")";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
- break;
- case Variant::PLANE:
- case Variant::AABB:
- case Variant::COLOR:
- r_iarg.default_argument = "new Color(1, 1, 1, 1)";
+ } break;
+ case Variant::AABB: {
+ AABB aabb = p_val.operator ::AABB();
+ r_iarg.default_argument = "new AABB(new Vector3" + aabb.position.operator String() + ", new Vector3" + aabb.size.operator String() + ")";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
- break;
+ } break;
+ case Variant::RECT2: {
+ Rect2 rect = p_val.operator Rect2();
+ r_iarg.default_argument = "new Rect2(new Vector2" + rect.position.operator String() + ", new Vector2" + rect.size.operator String() + ")";
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
+ case Variant::RECT2I: {
+ Rect2i rect = p_val.operator Rect2i();
+ r_iarg.default_argument = "new Rect2i(new Vector2i" + rect.position.operator String() + ", new Vector2i" + rect.size.operator String() + ")";
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
+ case Variant::COLOR:
case Variant::VECTOR2:
case Variant::VECTOR2I:
- case Variant::RECT2:
- case Variant::RECT2I:
case Variant::VECTOR3:
case Variant::VECTOR3I:
r_iarg.default_argument = "new %s" + r_iarg.default_argument;
@@ -2979,7 +3066,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = "new %s()";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
break;
- case Variant::_RID:
+ case Variant::RID:
ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_RID, false,
"Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_RID) + "'.");
@@ -2989,6 +3076,9 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = "null";
break;
case Variant::ARRAY:
+ r_iarg.default_argument = "new %s { }";
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
+ break;
case Variant::PACKED_BYTE_ARRAY:
case Variant::PACKED_INT32_ARRAY:
case Variant::PACKED_INT64_ARRAY:
@@ -2998,18 +3088,59 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
case Variant::PACKED_COLOR_ARRAY:
- r_iarg.default_argument = "new %s {}";
+ r_iarg.default_argument = "Array.Empty<%s>()";
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
break;
- case Variant::TRANSFORM2D:
- case Variant::BASIS:
- case Variant::QUAT:
- r_iarg.default_argument = Variant::get_type_name(p_val.get_type()) + ".Identity";
+ case Variant::TRANSFORM2D: {
+ Transform2D transform = p_val.operator Transform2D();
+ if (transform == Transform2D()) {
+ r_iarg.default_argument = "Transform2D.Identity";
+ } else {
+ r_iarg.default_argument = "new Transform2D(new Vector2" + transform.elements[0].operator String() + ", new Vector2" + transform.elements[1].operator String() + ", new Vector2" + transform.elements[2].operator String() + ")";
+ }
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
- break;
+ } break;
+ case Variant::TRANSFORM3D: {
+ Transform3D transform = p_val.operator Transform3D();
+ if (transform == Transform3D()) {
+ r_iarg.default_argument = "Transform3D.Identity";
+ } else {
+ Basis basis = transform.basis;
+ r_iarg.default_argument = "new Transform3D(new Vector3" + basis.get_column(0).operator String() + ", new Vector3" + basis.get_column(1).operator String() + ", new Vector3" + basis.get_column(2).operator String() + ", new Vector3" + transform.origin.operator String() + ")";
+ }
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
+ case Variant::BASIS: {
+ Basis basis = p_val.operator Basis();
+ if (basis == Basis()) {
+ r_iarg.default_argument = "Basis.Identity";
+ } else {
+ r_iarg.default_argument = "new Basis(new Vector3" + basis.get_column(0).operator String() + ", new Vector3" + basis.get_column(1).operator String() + ", new Vector3" + basis.get_column(2).operator String() + ")";
+ }
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
+ case Variant::QUATERNION: {
+ Quaternion quaternion = p_val.operator Quaternion();
+ if (quaternion == Quaternion()) {
+ r_iarg.default_argument = "Quaternion.Identity";
+ } else {
+ r_iarg.default_argument = "new Quaternion" + quaternion.operator String();
+ }
+ r_iarg.def_param_mode = ArgumentInterface::NULLABLE_VAL;
+ } break;
case Variant::CALLABLE:
+ ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_Callable, false,
+ "Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_Callable) + "'.");
+ ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false,
+ "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value.");
+ r_iarg.default_argument = "default";
+ break;
case Variant::SIGNAL:
- CRASH_NOW_MSG("Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value.");
+ ERR_FAIL_COND_V_MSG(r_iarg.type.cname != name_cache.type_Signal, false,
+ "Parameter of type '" + String(r_iarg.type.cname) + "' cannot have a default value of type '" + String(name_cache.type_Signal) + "'.");
+ ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false,
+ "Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value.");
+ r_iarg.default_argument = "default";
break;
default:
CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type()));
@@ -3052,8 +3183,8 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
INSERT_STRUCT_TYPE(Vector3)
INSERT_STRUCT_TYPE(Vector3i)
INSERT_STRUCT_TYPE(Basis)
- INSERT_STRUCT_TYPE(Quat)
- INSERT_STRUCT_TYPE(Transform)
+ INSERT_STRUCT_TYPE(Quaternion)
+ INSERT_STRUCT_TYPE(Transform3D)
INSERT_STRUCT_TYPE(AABB)
INSERT_STRUCT_TYPE(Color)
INSERT_STRUCT_TYPE(Plane)
@@ -3100,44 +3231,11 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
INSERT_INT_TYPE("sbyte", int8_t, int64_t);
INSERT_INT_TYPE("short", int16_t, int64_t);
INSERT_INT_TYPE("int", int32_t, int64_t);
+ INSERT_INT_TYPE("long", int64_t, int64_t);
INSERT_INT_TYPE("byte", uint8_t, int64_t);
INSERT_INT_TYPE("ushort", uint16_t, int64_t);
INSERT_INT_TYPE("uint", uint32_t, int64_t);
-
- itype = TypeInterface::create_value_type(String("long"));
- {
- itype.c_out = "\treturn (%0)%1;\n";
- itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
- itype.c_out = "\t*%3 = (%0)%1;\n";
- itype.c_type = "int64_t";
- itype.c_arg_in = "&%s_in";
- }
- itype.c_type_in = "int64_t*";
- itype.c_type_out = "int64_t";
- itype.im_type_in = "ref " + itype.name;
- itype.im_type_out = "out " + itype.name;
- itype.cs_in = "ref %0";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
- itype.ret_as_byref_arg = true;
- builtin_types.insert(itype.cname, itype);
-
- itype = TypeInterface::create_value_type(String("ulong"));
- {
- itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
- itype.c_out = "\t*%3 = (%0)%1;\n";
- itype.c_type = "int64_t";
- itype.c_arg_in = "&%s_in";
- }
- itype.c_type_in = "uint64_t*";
- itype.c_type_out = "uint64_t";
- itype.im_type_in = "ref " + itype.name;
- itype.im_type_out = "out " + itype.name;
- itype.cs_in = "ref %0";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
- itype.ret_as_byref_arg = true;
- builtin_types.insert(itype.cname, itype);
+ INSERT_INT_TYPE("ulong", uint64_t, int64_t);
}
// Floating point types
@@ -3149,20 +3247,16 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.proxy_name = "float";
{
// The expected type for 'float' in ptrcall is 'double'
- itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
- itype.c_out = "\t*%3 = (%0)%1;\n";
+ itype.c_in = "\t%0 %1_in = (%0)%1;\n";
+ itype.c_out = "\treturn (%0)%1;\n";
itype.c_type = "double";
- itype.c_type_in = "float*";
+ itype.c_type_in = "float";
itype.c_type_out = "float";
itype.c_arg_in = "&%s_in";
}
itype.cs_type = itype.proxy_name;
- itype.im_type_in = "ref " + itype.proxy_name;
- itype.im_type_out = "out " + itype.proxy_name;
- itype.cs_in = "ref %0";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
- itype.ret_as_byref_arg = true;
+ itype.im_type_in = itype.proxy_name;
+ itype.im_type_out = itype.proxy_name;
builtin_types.insert(itype.cname, itype);
// double
@@ -3171,20 +3265,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.cname = itype.name;
itype.proxy_name = "double";
{
- itype.c_in = "\t%0 %1_in = (%0)*%1;\n";
- itype.c_out = "\t*%3 = (%0)%1;\n";
itype.c_type = "double";
- itype.c_type_in = "double*";
+ itype.c_type_in = "double";
itype.c_type_out = "double";
- itype.c_arg_in = "&%s_in";
+ itype.c_arg_in = "&%s";
}
itype.cs_type = itype.proxy_name;
- itype.im_type_in = "ref " + itype.proxy_name;
- itype.im_type_out = "out " + itype.proxy_name;
- itype.cs_in = "ref %0";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
- itype.ret_as_byref_arg = true;
+ itype.im_type_in = itype.proxy_name;
+ itype.im_type_out = itype.proxy_name;
builtin_types.insert(itype.cname, itype);
}
@@ -3399,7 +3487,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
}
void BindingsGenerator::_populate_global_constants() {
- int global_constants_count = GlobalConstants::get_global_constant_count();
+ int global_constants_count = CoreConstants::get_global_constant_count();
if (global_constants_count > 0) {
Map<String, DocData::ClassDoc>::Element *match = EditorHelp::get_doc_data()->class_list.find("@GlobalScope");
@@ -3409,7 +3497,7 @@ void BindingsGenerator::_populate_global_constants() {
const DocData::ClassDoc &global_scope_doc = match->value();
for (int i = 0; i < global_constants_count; i++) {
- String constant_name = GlobalConstants::get_global_constant_name(i);
+ String constant_name = CoreConstants::get_global_constant_name(i);
const DocData::ConstantDoc *const_doc = nullptr;
for (int j = 0; j < global_scope_doc.constants.size(); j++) {
@@ -3421,8 +3509,8 @@ void BindingsGenerator::_populate_global_constants() {
}
}
- int constant_value = GlobalConstants::get_global_constant_value(i);
- StringName enum_name = GlobalConstants::get_global_constant_enum(i);
+ int constant_value = CoreConstants::get_global_constant_value(i);
+ StringName enum_name = CoreConstants::get_global_constant_enum(i);
ConstantInterface iconstant(constant_name, snake_to_pascal_case(constant_name, true), constant_value);
iconstant.const_doc = const_doc;
@@ -3441,9 +3529,7 @@ void BindingsGenerator::_populate_global_constants() {
}
}
- for (List<EnumInterface>::Element *E = global_enums.front(); E; E = E->next()) {
- EnumInterface &ienum = E->get();
-
+ for (EnumInterface &ienum : global_enums) {
TypeInterface enum_itype;
enum_itype.is_enum = true;
enum_itype.name = ienum.cname.operator String();
@@ -3473,13 +3559,13 @@ void BindingsGenerator::_populate_global_constants() {
hardcoded_enums.push_back("Vector2i.Axis");
hardcoded_enums.push_back("Vector3.Axis");
hardcoded_enums.push_back("Vector3i.Axis");
- for (List<StringName>::Element *E = hardcoded_enums.front(); E; E = E->next()) {
+ for (const StringName &enum_cname : hardcoded_enums) {
// These enums are not generated and must be written manually (e.g.: Vector3.Axis)
// Here, we assume core types do not begin with underscore
TypeInterface enum_itype;
enum_itype.is_enum = true;
- enum_itype.name = E->get().operator String();
- enum_itype.cname = E->get();
+ enum_itype.name = enum_cname.operator String();
+ enum_itype.cname = enum_cname;
enum_itype.proxy_name = enum_itype.name;
TypeInterface::postsetup_enum_type(enum_itype);
enum_types.insert(enum_itype.cname, enum_itype);
@@ -3530,11 +3616,44 @@ void BindingsGenerator::_initialize() {
initialized = true;
}
+static String generate_all_glue_option = "--generate-mono-glue";
+static String generate_cs_glue_option = "--generate-mono-cs-glue";
+static String generate_cpp_glue_option = "--generate-mono-cpp-glue";
+
+static void handle_cmdline_options(String glue_dir_path, String cs_dir_path, String cpp_dir_path) {
+ BindingsGenerator bindings_generator;
+ bindings_generator.set_log_print_enabled(true);
+
+ if (!bindings_generator.is_initialized()) {
+ ERR_PRINT("Failed to initialize the bindings generator");
+ return;
+ }
+
+ if (glue_dir_path.length()) {
+ if (bindings_generator.generate_glue(glue_dir_path) != OK) {
+ ERR_PRINT(generate_all_glue_option + ": Failed to generate the C++ glue.");
+ }
+
+ if (bindings_generator.generate_cs_api(glue_dir_path.plus_file(API_SOLUTION_NAME)) != OK) {
+ ERR_PRINT(generate_all_glue_option + ": Failed to generate the C# API.");
+ }
+ }
+
+ if (cs_dir_path.length()) {
+ if (bindings_generator.generate_cs_api(cs_dir_path) != OK) {
+ ERR_PRINT(generate_cs_glue_option + ": Failed to generate the C# API.");
+ }
+ }
+
+ if (cpp_dir_path.length()) {
+ if (bindings_generator.generate_glue(cpp_dir_path) != OK) {
+ ERR_PRINT(generate_cpp_glue_option + ": Failed to generate the C++ glue.");
+ }
+ }
+}
+
void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
const int NUM_OPTIONS = 2;
- String generate_all_glue_option = "--generate-mono-glue";
- String generate_cs_glue_option = "--generate-mono-cs-glue";
- String generate_cpp_glue_option = "--generate-mono-cpp-glue";
String glue_dir_path;
String cs_dir_path;
@@ -3542,6 +3661,8 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
int options_left = NUM_OPTIONS;
+ bool exit_godot = false;
+
const List<String>::Element *elem = p_cmdline_args.front();
while (elem && options_left) {
@@ -3553,6 +3674,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
elem = elem->next();
} else {
ERR_PRINT(generate_all_glue_option + ": No output directory specified (expected path to '{GODOT_ROOT}/modules/mono/glue').");
+ exit_godot = true;
}
--options_left;
@@ -3564,6 +3686,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
elem = elem->next();
} else {
ERR_PRINT(generate_cs_glue_option + ": No output directory specified.");
+ exit_godot = true;
}
--options_left;
@@ -3575,6 +3698,7 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
elem = elem->next();
} else {
ERR_PRINT(generate_cpp_glue_option + ": No output directory specified.");
+ exit_godot = true;
}
--options_left;
@@ -3584,37 +3708,13 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
}
if (glue_dir_path.length() || cs_dir_path.length() || cpp_dir_path.length()) {
- BindingsGenerator bindings_generator;
- bindings_generator.set_log_print_enabled(true);
-
- if (!bindings_generator.initialized) {
- ERR_PRINT("Failed to initialize the bindings generator");
- ::exit(0);
- }
-
- if (glue_dir_path.length()) {
- if (bindings_generator.generate_glue(glue_dir_path) != OK) {
- ERR_PRINT(generate_all_glue_option + ": Failed to generate the C++ glue.");
- }
-
- if (bindings_generator.generate_cs_api(glue_dir_path.plus_file(API_SOLUTION_NAME)) != OK) {
- ERR_PRINT(generate_all_glue_option + ": Failed to generate the C# API.");
- }
- }
-
- if (cs_dir_path.length()) {
- if (bindings_generator.generate_cs_api(cs_dir_path) != OK) {
- ERR_PRINT(generate_cs_glue_option + ": Failed to generate the C# API.");
- }
- }
-
- if (cpp_dir_path.length()) {
- if (bindings_generator.generate_glue(cpp_dir_path) != OK) {
- ERR_PRINT(generate_cpp_glue_option + ": Failed to generate the C++ glue.");
- }
- }
+ handle_cmdline_options(glue_dir_path, cs_dir_path, cpp_dir_path);
+ exit_godot = true;
+ }
+ if (exit_godot) {
// Exit once done
+ Main::cleanup(true);
::exit(0);
}
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index 90c1c9f3ee..51a27ee934 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,20 +31,21 @@
#ifndef BINDINGS_GENERATOR_H
#define BINDINGS_GENERATOR_H
-#include "core/class_db.h"
-#include "core/string_builder.h"
-#include "editor/doc_data.h"
+#include "core/doc_data.h"
+#include "core/object/class_db.h"
+#include "core/string/string_builder.h"
+#include "editor/doc_tools.h"
#include "editor/editor_help.h"
#if defined(DEBUG_METHODS_ENABLED) && defined(TOOLS_ENABLED)
-#include "core/ustring.h"
+#include "core/string/ustring.h"
class BindingsGenerator {
struct ConstantInterface {
String name;
String proxy_name;
- int value;
+ int value = 0;
const DocData::ConstantDoc *const_doc;
ConstantInterface() {}
@@ -74,7 +75,7 @@ class BindingsGenerator {
struct PropertyInterface {
StringName cname;
String proxy_name;
- int index;
+ int index = 0;
StringName setter;
StringName getter;
@@ -215,7 +216,7 @@ class BindingsGenerator {
bool is_enum = false;
bool is_object_type = false;
bool is_singleton = false;
- bool is_reference = false;
+ bool is_ref_counted = false;
/**
* Used only by Object-derived types.
@@ -227,7 +228,7 @@ class BindingsGenerator {
/**
* Used only by Object-derived types.
* Determines if the C# class owns the native handle and must free it somehow when disposed.
- * e.g.: Reference types must notify when the C# instance is disposed, for proper refcounting.
+ * e.g.: RefCounted types must notify when the C# instance is disposed, for proper refcounting.
*/
bool memory_own = false;
@@ -294,7 +295,7 @@ class BindingsGenerator {
* VarArg (fictitious type to represent variable arguments): Array
* float: double (because ptrcall only supports double)
* int: int64_t (because ptrcall only supports int64_t and uint64_t)
- * Reference types override this for the type of the return variable: Ref<Reference>
+ * RefCounted types override this for the type of the return variable: Ref<RefCounted>
*/
String c_type;
@@ -356,9 +357,9 @@ class BindingsGenerator {
List<SignalInterface> signals_;
const MethodInterface *find_method_by_name(const StringName &p_cname) const {
- for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().cname == p_cname) {
- return &E->get();
+ for (const MethodInterface &E : methods) {
+ if (E.cname == p_cname) {
+ return &E;
}
}
@@ -366,9 +367,9 @@ class BindingsGenerator {
}
const PropertyInterface *find_property_by_name(const StringName &p_cname) const {
- for (const List<PropertyInterface>::Element *E = properties.front(); E; E = E->next()) {
- if (E->get().cname == p_cname) {
- return &E->get();
+ for (const PropertyInterface &E : properties) {
+ if (E.cname == p_cname) {
+ return &E;
}
}
@@ -376,9 +377,9 @@ class BindingsGenerator {
}
const PropertyInterface *find_property_by_proxy_name(const String &p_proxy_name) const {
- for (const List<PropertyInterface>::Element *E = properties.front(); E; E = E->next()) {
- if (E->get().proxy_name == p_proxy_name) {
- return &E->get();
+ for (const PropertyInterface &E : properties) {
+ if (E.proxy_name == p_proxy_name) {
+ return &E;
}
}
@@ -386,9 +387,9 @@ class BindingsGenerator {
}
const MethodInterface *find_method_by_proxy_name(const String &p_proxy_name) const {
- for (const List<MethodInterface>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().proxy_name == p_proxy_name) {
- return &E->get();
+ for (const MethodInterface &E : methods) {
+ if (E.proxy_name == p_proxy_name) {
+ return &E;
}
}
@@ -479,7 +480,7 @@ class BindingsGenerator {
String im_type_out; // Return type for the C# method declaration. Also used as companion of [unique_siq]
String im_sig; // Signature for the C# method declaration
String unique_sig; // Unique signature to avoid duplicates in containers
- bool editor_only;
+ bool editor_only = false;
InternalCall() {}
@@ -533,8 +534,10 @@ class BindingsGenerator {
StringName type_Variant = StaticCString::create("Variant");
StringName type_VarArg = StaticCString::create("VarArg");
StringName type_Object = StaticCString::create("Object");
- StringName type_Reference = StaticCString::create("Reference");
+ StringName type_RefCounted = StaticCString::create("RefCounted");
StringName type_RID = StaticCString::create("RID");
+ StringName type_Callable = StaticCString::create("Callable");
+ StringName type_Signal = StaticCString::create("Signal");
StringName type_String = StaticCString::create("String");
StringName type_StringName = StaticCString::create("StringName");
StringName type_NodePath = StaticCString::create("NodePath");
@@ -572,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)),
@@ -612,9 +615,9 @@ class BindingsGenerator {
}
const ConstantInterface *find_constant_by_name(const String &p_name, const List<ConstantInterface> &p_constants) const {
- for (const List<ConstantInterface>::Element *E = p_constants.front(); E; E = E->next()) {
- if (E->get().name == p_name) {
- return &E->get();
+ for (const ConstantInterface &E : p_constants) {
+ if (E.name == p_name) {
+ return &E;
}
}
@@ -622,7 +625,7 @@ class BindingsGenerator {
}
inline String get_unique_sig(const TypeInterface &p_type) {
- if (p_type.is_reference) {
+ if (p_type.is_ref_counted) {
return "Ref";
} else if (p_type.is_object_type) {
return "Obj";
@@ -660,6 +663,7 @@ class BindingsGenerator {
Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output);
Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output);
+ void _generate_array_extensions(StringBuilder &p_output);
void _generate_global_constants(StringBuilder &p_output);
Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, StringBuilder &p_output);
diff --git a/modules/mono/editor/code_completion.cpp b/modules/mono/editor/code_completion.cpp
index 942c6d26a6..7433c865f5 100644
--- a/modules/mono/editor/code_completion.cpp
+++ b/modules/mono/editor/code_completion.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "code_completion.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "editor/editor_file_system.h"
#include "editor/editor_settings.h"
#include "scene/gui/control.h"
@@ -109,9 +109,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
List<PropertyInfo> project_props;
ProjectSettings::get_singleton()->get_property_list(&project_props);
- for (List<PropertyInfo>::Element *E = project_props.front(); E; E = E->next()) {
- const PropertyInfo &prop = E->get();
-
+ for (const PropertyInfo &prop : project_props) {
if (!prop.name.begins_with("input/")) {
continue;
}
@@ -123,10 +121,10 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
case CompletionKind::NODE_PATHS: {
{
// AutoLoads
- Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
+ OrderedHashMap<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list();
- for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) {
- 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)));
}
}
@@ -150,7 +148,7 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
List<String> directories;
directories.push_back(dir_access->get_current_dir());
- while (!directories.empty()) {
+ while (!directories.is_empty()) {
dir_access->change_dir(directories.back()->get());
directories.pop_back();
@@ -187,8 +185,8 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
ClassDB::get_signal_list(native, &signals, /* p_no_inheritance: */ false);
}
- for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) {
- const String &signal = E->get().name;
+ for (const MethodInfo &E : signals) {
+ const String &signal = E.name;
suggestions.push_back(quoted(signal));
}
} break;
@@ -199,8 +197,8 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
List<StringName> sn;
Theme::get_default()->get_color_list(base->get_class(), &sn);
- for (List<StringName>::Element *E = sn.front(); E; E = E->next()) {
- suggestions.push_back(quoted(E->get()));
+ for (const StringName &E : sn) {
+ suggestions.push_back(quoted(E));
}
}
} break;
@@ -211,8 +209,8 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
List<StringName> sn;
Theme::get_default()->get_constant_list(base->get_class(), &sn);
- for (List<StringName>::Element *E = sn.front(); E; E = E->next()) {
- suggestions.push_back(quoted(E->get()));
+ for (const StringName &E : sn) {
+ suggestions.push_back(quoted(E));
}
}
} break;
@@ -223,8 +221,20 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
List<StringName> sn;
Theme::get_default()->get_font_list(base->get_class(), &sn);
- for (List<StringName>::Element *E = sn.front(); E; E = E->next()) {
- suggestions.push_back(quoted(E->get()));
+ for (const StringName &E : sn) {
+ suggestions.push_back(quoted(E));
+ }
+ }
+ } break;
+ case CompletionKind::THEME_FONT_SIZES: {
+ Ref<Script> script = ResourceLoader::load(p_script_file.simplify_path());
+ Node *base = _try_find_owner_node_in_tree(script);
+ if (base && Object::cast_to<Control>(base)) {
+ List<StringName> sn;
+ Theme::get_default()->get_font_size_list(base->get_class(), &sn);
+
+ for (const StringName &E : sn) {
+ suggestions.push_back(quoted(E));
}
}
} break;
@@ -235,8 +245,8 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
List<StringName> sn;
Theme::get_default()->get_stylebox_list(base->get_class(), &sn);
- for (List<StringName>::Element *E = sn.front(); E; E = E->next()) {
- suggestions.push_back(quoted(E->get()));
+ for (const StringName &E : sn) {
+ suggestions.push_back(quoted(E));
}
}
} break;
@@ -246,5 +256,4 @@ PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_scr
return suggestions;
}
-
} // namespace gdmono
diff --git a/modules/mono/editor/code_completion.h b/modules/mono/editor/code_completion.h
index 77673b766f..7f7521672b 100644
--- a/modules/mono/editor/code_completion.h
+++ b/modules/mono/editor/code_completion.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#ifndef CODE_COMPLETION_H
#define CODE_COMPLETION_H
-#include "core/ustring.h"
-#include "core/variant.h"
+#include "core/string/ustring.h"
+#include "core/variant/variant.h"
namespace gdmono {
@@ -46,11 +46,11 @@ enum class CompletionKind {
THEME_COLORS,
THEME_CONSTANTS,
THEME_FONTS,
+ THEME_FONT_SIZES,
THEME_STYLES
};
PackedStringArray get_code_completion(CompletionKind p_kind, const String &p_script_file);
-
} // namespace gdmono
#endif // CODE_COMPLETION_H
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 68fc372959..9a61b63c12 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,10 +47,8 @@
#include "../godotsharp_dirs.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/osx_utils.h"
-#include "bindings_generator.h"
#include "code_completion.h"
#include "godotsharp_export.h"
-#include "script_class_parser.h"
MonoString *godot_icall_GodotSharpDirs_ResDataDir() {
return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_data_dir());
@@ -173,65 +171,6 @@ MonoBoolean godot_icall_EditorProgress_Step(MonoString *p_task, MonoString *p_st
return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh);
}
-BindingsGenerator *godot_icall_BindingsGenerator_Ctor() {
- return memnew(BindingsGenerator);
-}
-
-void godot_icall_BindingsGenerator_Dtor(BindingsGenerator *p_handle) {
- memdelete(p_handle);
-}
-
-MonoBoolean godot_icall_BindingsGenerator_LogPrintEnabled(BindingsGenerator *p_handle) {
- return p_handle->is_log_print_enabled();
-}
-
-void godot_icall_BindingsGenerator_SetLogPrintEnabled(BindingsGenerator p_handle, MonoBoolean p_enabled) {
- p_handle.set_log_print_enabled(p_enabled);
-}
-
-int32_t godot_icall_BindingsGenerator_GenerateCsApi(BindingsGenerator *p_handle, MonoString *p_output_dir) {
- String output_dir = GDMonoMarshal::mono_string_to_godot(p_output_dir);
- return p_handle->generate_cs_api(output_dir);
-}
-
-uint32_t godot_icall_BindingsGenerator_Version() {
- return BindingsGenerator::get_version();
-}
-
-uint32_t godot_icall_BindingsGenerator_CsGlueVersion() {
- return CS_GLUE_VERSION;
-}
-
-int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObject *p_classes, MonoString **r_error_str) {
- *r_error_str = nullptr;
-
- String filepath = GDMonoMarshal::mono_string_to_godot(p_filepath);
-
- ScriptClassParser scp;
- Error err = scp.parse_file(filepath);
- if (err == OK) {
- Array classes = GDMonoMarshal::mono_object_to_variant(p_classes);
- const Vector<ScriptClassParser::ClassDecl> &class_decls = scp.get_classes();
-
- for (int i = 0; i < class_decls.size(); i++) {
- const ScriptClassParser::ClassDecl &classDecl = class_decls[i];
-
- Dictionary classDeclDict;
- classDeclDict["name"] = classDecl.name;
- classDeclDict["namespace"] = classDecl.namespace_;
- classDeclDict["nested"] = classDecl.nested;
- classDeclDict["base_count"] = classDecl.base.size();
- classes.push_back(classDeclDict);
- }
- } else {
- String error_str = scp.get_error();
- if (!error_str.empty()) {
- *r_error_str = GDMonoMarshal::mono_string_from_godot(error_str);
- }
- }
- return err;
-}
-
uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_assemblies,
MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_assembly_dependencies) {
Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_assemblies);
@@ -302,7 +241,7 @@ MonoBoolean godot_icall_Internal_IsAssembliesReloadingNeeded() {
void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) {
#ifdef GD_MONO_HOT_RELOAD
- _GodotSharp::get_singleton()->call_deferred("_reload_assemblies", (bool)p_soft_reload);
+ mono_bind::GodotSharp::get_singleton()->call_deferred(SNAME("_reload_assemblies"), (bool)p_soft_reload);
#endif
}
@@ -319,18 +258,6 @@ void godot_icall_Internal_EditorNodeShowScriptScreen() {
EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT);
}
-MonoObject *godot_icall_Internal_GetScriptsMetadataOrNothing(MonoReflectionType *p_dict_reftype) {
- Dictionary maybe_metadata = CSharpLanguage::get_singleton()->get_scripts_metadata_or_nothing();
-
- MonoType *dict_type = mono_reflection_type_get_type(p_dict_reftype);
-
- int type_encoding = mono_type_get_type(dict_type);
- MonoClass *type_class_raw = mono_class_from_mono_type(dict_type);
- GDMonoClass *type_class = GDMono::get_singleton()->get_class(type_class_raw);
-
- return GDMonoMarshal::variant_to_mono_object(maybe_metadata, ManagedType(type_encoding, type_class));
-}
-
MonoString *godot_icall_Internal_MonoWindowsInstallRoot() {
#ifdef WINDOWS_ENABLED
String install_root_dir = GDMono::get_singleton()->get_mono_reg_info().install_root_dir;
@@ -379,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));
@@ -400,75 +333,63 @@ MonoBoolean godot_icall_Utils_OS_UnixFileHasExecutableAccess(MonoString *p_file_
void register_editor_internal_calls() {
// GodotSharpDirs
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", (void *)godot_icall_GodotSharpDirs_ResDataDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", (void *)godot_icall_GodotSharpDirs_ResMetadataDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesBaseDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", (void *)godot_icall_GodotSharpDirs_ResConfigDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", (void *)godot_icall_GodotSharpDirs_ResTempDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", (void *)godot_icall_GodotSharpDirs_MonoUserDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", (void *)godot_icall_GodotSharpDirs_MonoLogsDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", (void *)godot_icall_GodotSharpDirs_MonoSolutionsDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", (void *)godot_icall_GodotSharpDirs_BuildLogsDirs);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", (void *)godot_icall_GodotSharpDirs_ProjectSlnPath);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", (void *)godot_icall_GodotSharpDirs_ProjectCsProjPath);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", (void *)godot_icall_GodotSharpDirs_DataEditorToolsDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", (void *)godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", (void *)godot_icall_GodotSharpDirs_DataMonoEtcDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", (void *)godot_icall_GodotSharpDirs_DataMonoLibDir);
- mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", (void *)godot_icall_GodotSharpDirs_DataMonoBinDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", godot_icall_GodotSharpDirs_ResDataDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", godot_icall_GodotSharpDirs_ResMetadataDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", godot_icall_GodotSharpDirs_ResAssembliesBaseDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", godot_icall_GodotSharpDirs_ResAssembliesDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", godot_icall_GodotSharpDirs_ResConfigDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", godot_icall_GodotSharpDirs_ResTempDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", godot_icall_GodotSharpDirs_ResTempAssembliesDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", godot_icall_GodotSharpDirs_MonoUserDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", godot_icall_GodotSharpDirs_MonoLogsDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", godot_icall_GodotSharpDirs_MonoSolutionsDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", godot_icall_GodotSharpDirs_BuildLogsDirs);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", godot_icall_GodotSharpDirs_ProjectSlnPath);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", godot_icall_GodotSharpDirs_ProjectCsProjPath);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", godot_icall_GodotSharpDirs_DataEditorToolsDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", godot_icall_GodotSharpDirs_DataMonoEtcDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", godot_icall_GodotSharpDirs_DataMonoLibDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", godot_icall_GodotSharpDirs_DataMonoBinDir);
// EditorProgress
- mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", (void *)godot_icall_EditorProgress_Create);
- mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", (void *)godot_icall_EditorProgress_Dispose);
- mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", (void *)godot_icall_EditorProgress_Step);
-
- // BiningsGenerator
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Ctor", (void *)godot_icall_BindingsGenerator_Ctor);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Dtor", (void *)godot_icall_BindingsGenerator_Dtor);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_LogPrintEnabled", (void *)godot_icall_BindingsGenerator_LogPrintEnabled);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_SetLogPrintEnabled", (void *)godot_icall_BindingsGenerator_SetLogPrintEnabled);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_GenerateCsApi", (void *)godot_icall_BindingsGenerator_GenerateCsApi);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Version", (void *)godot_icall_BindingsGenerator_Version);
- mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_CsGlueVersion", (void *)godot_icall_BindingsGenerator_CsGlueVersion);
-
- // ScriptClassParser
- mono_add_internal_call("GodotTools.Internals.ScriptClassParser::internal_ParseFile", (void *)godot_icall_ScriptClassParser_ParseFile);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", godot_icall_EditorProgress_Create);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", godot_icall_EditorProgress_Dispose);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", godot_icall_EditorProgress_Step);
// ExportPlugin
- mono_add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", (void *)godot_icall_ExportPlugin_GetExportedAssemblyDependencies);
+ GDMonoUtils::add_internal_call("GodotTools.Export.ExportPlugin::internal_GetExportedAssemblyDependencies", godot_icall_ExportPlugin_GetExportedAssemblyDependencies);
// Internals
- mono_add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", (void *)godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", (void *)godot_icall_Internal_FullTemplatesDir);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", (void *)godot_icall_Internal_SimplifyGodotPath);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", (void *)godot_icall_Internal_IsOsxAppBundleInstalled);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", (void *)godot_icall_Internal_GodotIs32Bits);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", (void *)godot_icall_Internal_GodotIsRealTDouble);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", (void *)godot_icall_Internal_GodotMainIteration);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", (void *)godot_icall_Internal_GetCoreApiHash);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", (void *)godot_icall_Internal_GetEditorApiHash);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", (void *)godot_icall_Internal_IsAssembliesReloadingNeeded);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", (void *)godot_icall_Internal_ReloadAssemblies);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", (void *)godot_icall_Internal_EditorDebuggerNodeReloadScripts);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", (void *)godot_icall_Internal_ScriptEditorEdit);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", (void *)godot_icall_Internal_EditorNodeShowScriptScreen);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", (void *)godot_icall_Internal_GetScriptsMetadataOrNothing);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", (void *)godot_icall_Internal_MonoWindowsInstallRoot);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorRunPlay", (void *)godot_icall_Internal_EditorRunPlay);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorRunStop", (void *)godot_icall_Internal_EditorRunStop);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebugger_ReloadScripts", (void *)godot_icall_Internal_ScriptEditorDebugger_ReloadScripts);
- mono_add_internal_call("GodotTools.Internals.Internal::internal_CodeCompletionRequest", (void *)godot_icall_Internal_CodeCompletionRequest);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_UpdateApiAssembliesFromPrebuilt", godot_icall_Internal_UpdateApiAssembliesFromPrebuilt);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", godot_icall_Internal_FullTemplatesDir);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", godot_icall_Internal_SimplifyGodotPath);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", godot_icall_Internal_IsOsxAppBundleInstalled);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", godot_icall_Internal_GodotIs32Bits);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", godot_icall_Internal_GodotIsRealTDouble);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", godot_icall_Internal_GodotMainIteration);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", godot_icall_Internal_GetCoreApiHash);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", godot_icall_Internal_GetEditorApiHash);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", godot_icall_Internal_IsAssembliesReloadingNeeded);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", godot_icall_Internal_ReloadAssemblies);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorDebuggerNodeReloadScripts", godot_icall_Internal_EditorDebuggerNodeReloadScripts);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", godot_icall_Internal_ScriptEditorEdit);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", godot_icall_Internal_EditorNodeShowScriptScreen);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", godot_icall_Internal_MonoWindowsInstallRoot);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunPlay", godot_icall_Internal_EditorRunPlay);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_EditorRunStop", godot_icall_Internal_EditorRunStop);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebugger_ReloadScripts", godot_icall_Internal_ScriptEditorDebugger_ReloadScripts);
+ GDMonoUtils::add_internal_call("GodotTools.Internals.Internal::internal_CodeCompletionRequest", godot_icall_Internal_CodeCompletionRequest);
// Globals
- mono_add_internal_call("GodotTools.Internals.Globals::internal_EditorScale", (void *)godot_icall_Globals_EditorScale);
- mono_add_internal_call("GodotTools.Internals.Globals::internal_GlobalDef", (void *)godot_icall_Globals_GlobalDef);
- mono_add_internal_call("GodotTools.Internals.Globals::internal_EditorDef", (void *)godot_icall_Globals_EditorDef);
- mono_add_internal_call("GodotTools.Internals.Globals::internal_TTR", (void *)godot_icall_Globals_TTR);
+ 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
- mono_add_internal_call("GodotTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName);
- mono_add_internal_call("GodotTools.Utils.OS::UnixFileHasExecutableAccess", (void *)godot_icall_Utils_OS_UnixFileHasExecutableAccess);
+ GDMonoUtils::add_internal_call("GodotTools.Utils.OS::GetPlatformName", godot_icall_Utils_OS_GetPlatformName);
+ GDMonoUtils::add_internal_call("GodotTools.Utils.OS::UnixFileHasExecutableAccess", godot_icall_Utils_OS_UnixFileHasExecutableAccess);
}
diff --git a/modules/mono/editor/editor_internal_calls.h b/modules/mono/editor/editor_internal_calls.h
index ef4e639161..24080cd867 100644
--- a/modules/mono/editor/editor_internal_calls.h
+++ b/modules/mono/editor/editor_internal_calls.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index 2edd8c87dc..54dbaebf38 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,9 +32,9 @@
#include <mono/metadata/image.h>
+#include "core/config/project_settings.h"
#include "core/io/file_access_pack.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "../mono_gd/gd_mono.h"
#include "../mono_gd/gd_mono_assembly.h"
@@ -55,10 +55,10 @@ MonoAssemblyName *new_mono_assembly_name() {
struct AssemblyRefInfo {
String name;
- uint16_t major;
- uint16_t minor;
- uint16_t build;
- uint16_t revision;
+ uint16_t major = 0;
+ uint16_t minor = 0;
+ uint16_t build = 0;
+ uint16_t revision = 0;
};
AssemblyRefInfo get_assemblyref_name(MonoImage *p_image, int index) {
@@ -91,7 +91,7 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, MonoAssemblyName *re
mono_assembly_get_assemblyref(image, i, reusable_aname);
- GDMonoAssembly *ref_assembly = NULL;
+ GDMonoAssembly *ref_assembly = nullptr;
if (!GDMono::get_singleton()->load_assembly(ref_name, reusable_aname, &ref_assembly, /* refonly: */ true, p_search_dirs)) {
ERR_FAIL_V_MSG(ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
}
@@ -141,5 +141,4 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
return OK;
}
-
} // namespace GodotSharpExport
diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h
index 9ab57755de..0e9d689618 100644
--- a/modules/mono/editor/godotsharp_export.h
+++ b/modules/mono/editor/godotsharp_export.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,9 +31,9 @@
#ifndef GODOTSHARP_EXPORT_H
#define GODOTSHARP_EXPORT_H
-#include "core/dictionary.h"
-#include "core/error_list.h"
-#include "core/ustring.h"
+#include "core/error/error_list.h"
+#include "core/string/ustring.h"
+#include "core/variant/dictionary.h"
#include "../mono_gd/gd_mono_header.h"
@@ -43,7 +43,6 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String>
Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_assembly_dependencies);
-
} // namespace GodotSharpExport
#endif // GODOTSHARP_EXPORT_H
diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp
deleted file mode 100644
index f7d6e7e302..0000000000
--- a/modules/mono/editor/script_class_parser.cpp
+++ /dev/null
@@ -1,753 +0,0 @@
-/*************************************************************************/
-/* script_class_parser.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 "script_class_parser.h"
-
-#include "core/map.h"
-#include "core/os/os.h"
-
-#include "../utils/string_utils.h"
-
-const char *ScriptClassParser::token_names[ScriptClassParser::TK_MAX] = {
- "[",
- "]",
- "{",
- "}",
- ".",
- ":",
- ",",
- "Symbol",
- "Identifier",
- "String",
- "Number",
- "<",
- ">",
- "EOF",
- "Error"
-};
-
-String ScriptClassParser::get_token_name(ScriptClassParser::Token p_token) {
- ERR_FAIL_INDEX_V(p_token, TK_MAX, "<error>");
- return token_names[p_token];
-}
-
-ScriptClassParser::Token ScriptClassParser::get_token() {
- while (true) {
- switch (code[idx]) {
- case '\n': {
- line++;
- idx++;
- break;
- };
- case 0: {
- return TK_EOF;
- } break;
- case '{': {
- idx++;
- return TK_CURLY_BRACKET_OPEN;
- };
- case '}': {
- idx++;
- return TK_CURLY_BRACKET_CLOSE;
- };
- case '[': {
- idx++;
- return TK_BRACKET_OPEN;
- };
- case ']': {
- idx++;
- return TK_BRACKET_CLOSE;
- };
- case '<': {
- idx++;
- return TK_OP_LESS;
- };
- case '>': {
- idx++;
- return TK_OP_GREATER;
- };
- case ':': {
- idx++;
- return TK_COLON;
- };
- case ',': {
- idx++;
- return TK_COMMA;
- };
- case '.': {
- idx++;
- return TK_PERIOD;
- };
- case '#': {
- //compiler directive
- while (code[idx] != '\n' && code[idx] != 0) {
- idx++;
- }
- continue;
- } break;
- case '/': {
- switch (code[idx + 1]) {
- case '*': { // block comment
- idx += 2;
- while (true) {
- if (code[idx] == 0) {
- error_str = "Unterminated comment";
- error = true;
- return TK_ERROR;
- } else if (code[idx] == '*' && code[idx + 1] == '/') {
- idx += 2;
- break;
- } else if (code[idx] == '\n') {
- line++;
- }
-
- idx++;
- }
-
- } break;
- case '/': { // line comment skip
- while (code[idx] != '\n' && code[idx] != 0) {
- idx++;
- }
-
- } break;
- default: {
- value = "/";
- idx++;
- return TK_SYMBOL;
- }
- }
-
- continue; // a comment
- } break;
- case '\'':
- case '"': {
- bool verbatim = idx != 0 && code[idx - 1] == '@';
-
- char32_t begin_str = code[idx];
- idx++;
- String tk_string = String();
- while (true) {
- if (code[idx] == 0) {
- error_str = "Unterminated String";
- error = true;
- return TK_ERROR;
- } else if (code[idx] == begin_str) {
- if (verbatim && code[idx + 1] == '"') { // '""' is verbatim string's '\"'
- idx += 2; // skip next '"' as well
- continue;
- }
-
- idx += 1;
- break;
- } else if (code[idx] == '\\' && !verbatim) {
- //escaped characters...
- idx++;
- char32_t next = code[idx];
- if (next == 0) {
- error_str = "Unterminated String";
- error = true;
- return TK_ERROR;
- }
- char32_t res = 0;
-
- switch (next) {
- case 'b':
- res = 8;
- break;
- case 't':
- res = 9;
- break;
- case 'n':
- res = 10;
- break;
- case 'f':
- res = 12;
- break;
- case 'r':
- res = 13;
- break;
- case '\"':
- res = '\"';
- break;
- case '\\':
- res = '\\';
- break;
- default: {
- res = next;
- } break;
- }
-
- tk_string += res;
-
- } else {
- if (code[idx] == '\n') {
- line++;
- }
- tk_string += code[idx];
- }
- idx++;
- }
-
- value = tk_string;
-
- return TK_STRING;
- } break;
- default: {
- if (code[idx] <= 32) {
- idx++;
- break;
- }
-
- if ((code[idx] >= 33 && code[idx] <= 47) || (code[idx] >= 58 && code[idx] <= 63) || (code[idx] >= 91 && code[idx] <= 94) || code[idx] == 96 || (code[idx] >= 123 && code[idx] <= 127)) {
- value = String::chr(code[idx]);
- idx++;
- return TK_SYMBOL;
- }
-
- if (code[idx] == '-' || (code[idx] >= '0' && code[idx] <= '9')) {
- //a number
- const char32_t *rptr;
- double number = String::to_float(&code[idx], &rptr);
- idx += (rptr - &code[idx]);
- value = number;
- return TK_NUMBER;
-
- } else if ((code[idx] == '@' && code[idx + 1] != '"') || code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || code[idx] > 127) {
- String id;
-
- id += code[idx];
- idx++;
-
- while (code[idx] == '_' || (code[idx] >= 'A' && code[idx] <= 'Z') || (code[idx] >= 'a' && code[idx] <= 'z') || (code[idx] >= '0' && code[idx] <= '9') || code[idx] > 127) {
- id += code[idx];
- idx++;
- }
-
- value = id;
- return TK_IDENTIFIER;
- } else if (code[idx] == '@' && code[idx + 1] == '"') {
- // begin of verbatim string
- idx++;
- } else {
- error_str = "Unexpected character.";
- error = true;
- return TK_ERROR;
- }
- }
- }
- }
-}
-
-Error ScriptClassParser::_skip_generic_type_params() {
- Token tk;
-
- while (true) {
- tk = get_token();
-
- if (tk == TK_IDENTIFIER) {
- tk = get_token();
- // Type specifications can end with "?" to denote nullable types, such as IList<int?>
- if (tk == TK_SYMBOL) {
- tk = get_token();
- if (value.operator String() != "?") {
- error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found unexpected symbol '" + value + "'";
- error = true;
- return ERR_PARSE_ERROR;
- }
- if (tk != TK_OP_GREATER && tk != TK_COMMA) {
- error_str = "Nullable type symbol '?' is only allowed after an identifier, but found " + get_token_name(tk) + " next.";
- error = true;
- return ERR_PARSE_ERROR;
- }
- }
-
- if (tk == TK_PERIOD) {
- while (true) {
- tk = get_token();
-
- if (tk != TK_IDENTIFIER) {
- error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
-
- tk = get_token();
-
- if (tk != TK_PERIOD) {
- break;
- }
- }
- }
-
- if (tk == TK_OP_LESS) {
- Error err = _skip_generic_type_params();
- if (err) {
- return err;
- }
- tk = get_token();
- }
-
- if (tk == TK_OP_GREATER) {
- return OK;
- } else if (tk != TK_COMMA) {
- error_str = "Unexpected token: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
- } else if (tk == TK_OP_LESS) {
- error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found " + get_token_name(TK_OP_LESS);
- error = true;
- return ERR_PARSE_ERROR;
- } else if (tk == TK_OP_GREATER) {
- return OK;
- } else {
- error_str = "Unexpected token: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
- }
-}
-
-Error ScriptClassParser::_parse_type_full_name(String &r_full_name) {
- Token tk = get_token();
-
- if (tk != TK_IDENTIFIER) {
- error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
-
- r_full_name += String(value);
-
- if (code[idx] == '<') {
- idx++;
-
- // We don't mind if the base is generic, but we skip it any ways since this information is not needed
- Error err = _skip_generic_type_params();
- if (err) {
- return err;
- }
- }
-
- if (code[idx] != '.') { // We only want to take the next token if it's a period
- return OK;
- }
-
- tk = get_token();
-
- CRASH_COND(tk != TK_PERIOD); // Assertion
-
- r_full_name += ".";
-
- return _parse_type_full_name(r_full_name);
-}
-
-Error ScriptClassParser::_parse_class_base(Vector<String> &r_base) {
- String name;
-
- Error err = _parse_type_full_name(name);
- if (err) {
- return err;
- }
-
- Token tk = get_token();
-
- if (tk == TK_COMMA) {
- err = _parse_class_base(r_base);
- if (err) {
- return err;
- }
- } else if (tk == TK_IDENTIFIER && String(value) == "where") {
- err = _parse_type_constraints();
- if (err) {
- return err;
- }
-
- // An open curly bracket was parsed by _parse_type_constraints, so we can exit
- } else if (tk == TK_CURLY_BRACKET_OPEN) {
- // we are finished when we hit the open curly bracket
- } else {
- error_str = "Unexpected token: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
-
- r_base.push_back(name);
-
- return OK;
-}
-
-Error ScriptClassParser::_parse_type_constraints() {
- Token tk = get_token();
- if (tk != TK_IDENTIFIER) {
- error_str = "Unexpected token: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
-
- tk = get_token();
- if (tk != TK_COLON) {
- error_str = "Unexpected token: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
-
- while (true) {
- tk = get_token();
- if (tk == TK_IDENTIFIER) {
- if (String(value) == "where") {
- return _parse_type_constraints();
- }
-
- tk = get_token();
- if (tk == TK_PERIOD) {
- while (true) {
- tk = get_token();
-
- if (tk != TK_IDENTIFIER) {
- error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
-
- tk = get_token();
-
- if (tk != TK_PERIOD) {
- break;
- }
- }
- }
- }
-
- if (tk == TK_COMMA) {
- continue;
- } else if (tk == TK_IDENTIFIER && String(value) == "where") {
- return _parse_type_constraints();
- } else if (tk == TK_SYMBOL && String(value) == "(") {
- tk = get_token();
- if (tk != TK_SYMBOL || String(value) != ")") {
- error_str = "Unexpected token: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
- } else if (tk == TK_OP_LESS) {
- Error err = _skip_generic_type_params();
- if (err) {
- return err;
- }
- } else if (tk == TK_CURLY_BRACKET_OPEN) {
- return OK;
- } else {
- error_str = "Unexpected token: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
- }
-}
-
-Error ScriptClassParser::_parse_namespace_name(String &r_name, int &r_curly_stack) {
- Token tk = get_token();
-
- if (tk == TK_IDENTIFIER) {
- r_name += String(value);
- } else {
- error_str = "Unexpected token: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
-
- tk = get_token();
-
- if (tk == TK_PERIOD) {
- r_name += ".";
- return _parse_namespace_name(r_name, r_curly_stack);
- } else if (tk == TK_CURLY_BRACKET_OPEN) {
- r_curly_stack++;
- return OK;
- } else {
- error_str = "Unexpected token: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
-}
-
-Error ScriptClassParser::parse(const String &p_code) {
- code = p_code;
- idx = 0;
- line = 0;
- error_str = String();
- error = false;
- value = Variant();
- classes.clear();
-
- Token tk = get_token();
-
- Map<int, NameDecl> name_stack;
- int curly_stack = 0;
- int type_curly_stack = 0;
-
- while (!error && tk != TK_EOF) {
- String identifier = value;
- if (tk == TK_IDENTIFIER && (identifier == "class" || identifier == "struct")) {
- bool is_class = identifier == "class";
-
- tk = get_token();
-
- if (tk == TK_IDENTIFIER) {
- String name = value;
- int at_level = curly_stack;
-
- ClassDecl class_decl;
-
- for (Map<int, NameDecl>::Element *E = name_stack.front(); E; E = E->next()) {
- const NameDecl &name_decl = E->value();
-
- if (name_decl.type == NameDecl::NAMESPACE_DECL) {
- if (E != name_stack.front()) {
- class_decl.namespace_ += ".";
- }
- class_decl.namespace_ += name_decl.name;
- } else {
- class_decl.name += name_decl.name + ".";
- }
- }
-
- class_decl.name += name;
- class_decl.nested = type_curly_stack > 0;
-
- bool generic = false;
-
- while (true) {
- tk = get_token();
-
- if (tk == TK_COLON) {
- Error err = _parse_class_base(class_decl.base);
- if (err) {
- return err;
- }
-
- curly_stack++;
- type_curly_stack++;
-
- break;
- } else if (tk == TK_CURLY_BRACKET_OPEN) {
- curly_stack++;
- type_curly_stack++;
- break;
- } else if (tk == TK_OP_LESS && !generic) {
- generic = true;
-
- Error err = _skip_generic_type_params();
- if (err) {
- return err;
- }
- } else if (tk == TK_IDENTIFIER && String(value) == "where") {
- Error err = _parse_type_constraints();
- if (err) {
- return err;
- }
-
- // An open curly bracket was parsed by _parse_type_constraints, so we can exit
- curly_stack++;
- type_curly_stack++;
- break;
- } else {
- error_str = "Unexpected token: " + get_token_name(tk);
- error = true;
- return ERR_PARSE_ERROR;
- }
- }
-
- NameDecl name_decl;
- name_decl.name = name;
- name_decl.type = is_class ? NameDecl::CLASS_DECL : NameDecl::STRUCT_DECL;
- name_stack[at_level] = name_decl;
-
- if (is_class) {
- if (!generic) { // no generics, thanks
- classes.push_back(class_decl);
- } else if (OS::get_singleton()->is_stdout_verbose()) {
- String full_name = class_decl.namespace_;
- if (full_name.length()) {
- full_name += ".";
- }
- full_name += class_decl.name;
- OS::get_singleton()->print("Ignoring generic class declaration: %s\n", full_name.utf8().get_data());
- }
- }
- }
- } else if (tk == TK_IDENTIFIER && identifier == "namespace") {
- if (type_curly_stack > 0) {
- error_str = "Found namespace nested inside type.";
- error = true;
- return ERR_PARSE_ERROR;
- }
-
- String name;
- int at_level = curly_stack;
-
- Error err = _parse_namespace_name(name, curly_stack);
- if (err) {
- return err;
- }
-
- NameDecl name_decl;
- name_decl.name = name;
- name_decl.type = NameDecl::NAMESPACE_DECL;
- name_stack[at_level] = name_decl;
- } else if (tk == TK_CURLY_BRACKET_OPEN) {
- curly_stack++;
- } else if (tk == TK_CURLY_BRACKET_CLOSE) {
- curly_stack--;
- if (name_stack.has(curly_stack)) {
- if (name_stack[curly_stack].type != NameDecl::NAMESPACE_DECL) {
- type_curly_stack--;
- }
- name_stack.erase(curly_stack);
- }
- }
-
- tk = get_token();
- }
-
- if (!error && tk == TK_EOF && curly_stack > 0) {
- error_str = "Reached EOF with missing close curly brackets.";
- error = true;
- }
-
- if (error) {
- return ERR_PARSE_ERROR;
- }
-
- return OK;
-}
-
-static String get_preprocessor_directive(const String &p_line, int p_from) {
- CRASH_COND(p_line[p_from] != '#');
- p_from++;
- int i = p_from;
- while (i < p_line.length() && (p_line[i] == '_' || (p_line[i] >= 'A' && p_line[i] <= 'Z') ||
- (p_line[i] >= 'a' && p_line[i] <= 'z') || p_line[i] > 127)) {
- i++;
- }
- return p_line.substr(p_from, i - p_from);
-}
-
-static void run_dummy_preprocessor(String &r_source, const String &p_filepath) {
- Vector<String> lines = r_source.split("\n", /* p_allow_empty: */ true);
-
- bool *include_lines = memnew_arr(bool, lines.size());
-
- int if_level = -1;
- Vector<bool> is_branch_being_compiled;
-
- for (int i = 0; i < lines.size(); i++) {
- const String &line = lines[i];
-
- const int line_len = line.length();
-
- int j;
- for (j = 0; j < line_len; j++) {
- if (line[j] != ' ' && line[j] != '\t') {
- if (line[j] == '#') {
- // First non-whitespace char of the line is '#'
- include_lines[i] = false;
-
- String directive = get_preprocessor_directive(line, j);
-
- if (directive == "if") {
- if_level++;
- is_branch_being_compiled.push_back(if_level == 0 || is_branch_being_compiled[if_level - 1]);
- } else if (directive == "elif") {
- ERR_CONTINUE_MSG(if_level == -1, "Found unexpected '#elif' directive. File: '" + p_filepath + "'.");
- is_branch_being_compiled.write[if_level] = false;
- } else if (directive == "else") {
- ERR_CONTINUE_MSG(if_level == -1, "Found unexpected '#else' directive. File: '" + p_filepath + "'.");
- is_branch_being_compiled.write[if_level] = false;
- } else if (directive == "endif") {
- ERR_CONTINUE_MSG(if_level == -1, "Found unexpected '#endif' directive. File: '" + p_filepath + "'.");
- is_branch_being_compiled.remove(if_level);
- if_level--;
- }
-
- break;
- } else {
- // First non-whitespace char of the line is not '#'
- include_lines[i] = if_level == -1 || is_branch_being_compiled[if_level];
- break;
- }
- }
- }
-
- if (j == line_len) {
- // Loop ended without finding a non-whitespace character.
- // Either the line was empty or it only contained whitespaces.
- include_lines[i] = if_level == -1 || is_branch_being_compiled[if_level];
- }
- }
-
- r_source.clear();
-
- // Custom join ignoring lines removed by the preprocessor
- for (int i = 0; i < lines.size(); i++) {
- if (i > 0 && include_lines[i - 1]) {
- r_source += '\n';
- }
-
- if (include_lines[i]) {
- r_source += lines[i];
- }
- }
-}
-
-Error ScriptClassParser::parse_file(const String &p_filepath) {
- String source;
-
- Error ferr = read_all_file_utf8(p_filepath, source);
-
- ERR_FAIL_COND_V_MSG(ferr != OK, ferr,
- ferr == ERR_INVALID_DATA ?
- "File '" + p_filepath + "' 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_filepath + "'.");
-
- run_dummy_preprocessor(source, p_filepath);
-
- return parse(source);
-}
-
-String ScriptClassParser::get_error() {
- return error_str;
-}
-
-Vector<ScriptClassParser::ClassDecl> ScriptClassParser::get_classes() {
- return classes;
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
index 3aecce50f5..850ae7fc3b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/AABB.cs
@@ -1,16 +1,10 @@
-// file: core/math/aabb.h
-// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
-// file: core/math/aabb.cpp
-// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0
-// file: core/variant_call.cpp
-// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
-using System;
-using System.Runtime.InteropServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+using System;
+using System.Runtime.InteropServices;
namespace Godot
{
@@ -26,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
@@ -36,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>
@@ -50,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; }
@@ -58,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;
@@ -70,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>
+ /// <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 AABB that may be enclosed.</param>
- /// <returns>A bool for whether or not this AABB encloses `b`.</returns>
+ /// <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;
@@ -129,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()
@@ -138,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)
@@ -163,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);
}
@@ -191,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;
}
@@ -214,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);
}
@@ -254,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;
}
@@ -277,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>
@@ -301,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;
@@ -330,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)
@@ -371,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)
@@ -458,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 =
@@ -495,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;
@@ -555,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;
@@ -567,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>
@@ -593,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>
@@ -606,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>
@@ -619,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>
@@ -633,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)
@@ -653,43 +710,54 @@ 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 String.Format("{0} - {1}", new object[]
- {
- _position.ToString(),
- _size.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 String.Format("{0} - {1}", new object[]
- {
- _position.ToString(format),
- _size.ToString(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 f77d3052f4..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)
{
@@ -25,16 +25,30 @@ namespace Godot.Collections
}
}
+ /// <summary>
+ /// Wrapper around Godot's Array class, an array of Variant
+ /// typed elements allocated in the engine in C++. Useful when
+ /// interfacing with the engine. Otherwise prefer .NET collections
+ /// such as <see cref="System.Array"/> or <see cref="List{T}"/>.
+ /// </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>
+ /// Constructs a new <see cref="Array"/> from the given collection's elements.
+ /// </summary>
+ /// <param name="collection">The collection of elements to construct from.</param>
+ /// <returns>A new Godot Array.</returns>
public Array(IEnumerable collection) : this()
{
if (collection == null)
@@ -44,43 +58,72 @@ namespace Godot.Collections
Add(element);
}
+ /// <summary>
+ /// Constructs a new <see cref="Array"/> from the given objects.
+ /// </summary>
+ /// <param name="array">The objects to put in the new array.</param>
+ /// <returns>A new Godot Array.</returns>
public Array(params object[] array) : this()
{
if (array == null)
{
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 <see langword="true"/>, performs a deep copy.</param>
+ /// <returns>A new Godot Array.</returns>
public Array Duplicate(bool deep = false)
{
return new Array(godot_icall_Array_Duplicate(GetPtr(), deep));
}
+ /// <summary>
+ /// Resizes this <see cref="Array"/> to the given size.
+ /// </summary>
+ /// <param name="newSize">The new size of the array.</param>
+ /// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
public Error Resize(int newSize)
{
return godot_icall_Array_Resize(GetPtr(), newSize);
}
+ /// <summary>
+ /// Shuffles the contents of this <see cref="Array"/> into a random order.
+ /// </summary>
+ public void Shuffle()
+ {
+ godot_icall_Array_Shuffle(GetPtr());
+ }
+
+ /// <summary>
+ /// Concatenates these two <see cref="Array"/>s.
+ /// </summary>
+ /// <param name="left">The first array.</param>
+ /// <param name="right">The second array.</param>
+ /// <returns>A new Godot Array with the contents of both arrays.</returns>
public static Array operator +(Array left, Array right)
{
return new Array(godot_icall_Array_Concatenate(left.GetPtr(), right.GetPtr()));
@@ -88,54 +131,109 @@ namespace Godot.Collections
// IDisposable
+ /// <summary>
+ /// Disposes of this <see cref="Array"/>.
+ /// </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
- public bool IsReadOnly => false;
+ bool IList.IsReadOnly => false;
- public bool IsFixedSize => false;
+ bool IList.IsFixedSize => false;
+ /// <summary>
+ /// Returns the object at the given <paramref name="index"/>.
+ /// </summary>
+ /// <value>The object at the given <paramref name="index"/>.</value>
public object this[int index]
{
get => godot_icall_Array_At(GetPtr(), index);
set => godot_icall_Array_SetAt(GetPtr(), index, value);
}
+ /// <summary>
+ /// Adds an object to the end of this <see cref="Array"/>.
+ /// 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>
public int Add(object value) => godot_icall_Array_Add(GetPtr(), value);
+ /// <summary>
+ /// Checks if this <see cref="Array"/> contains the given object.
+ /// </summary>
+ /// <param name="value">The item to look for.</param>
+ /// <returns>Whether or not this array contains the given object.</returns>
public bool Contains(object value) => godot_icall_Array_Contains(GetPtr(), value);
+ /// <summary>
+ /// Erases all items from this <see cref="Array"/>.
+ /// </summary>
public void Clear() => godot_icall_Array_Clear(GetPtr());
+ /// <summary>
+ /// Searches this <see cref="Array"/> for an object
+ /// and returns its index or -1 if not found.
+ /// </summary>
+ /// <param name="value">The object to search for.</param>
+ /// <returns>The index of the object, or -1 if not found.</returns>
public int IndexOf(object value) => godot_icall_Array_IndexOf(GetPtr(), value);
+ /// <summary>
+ /// Inserts a new object at a given position in the array.
+ /// The position must be a valid position of an existing item,
+ /// or the position at the end of the array.
+ /// Existing items will be moved to the right.
+ /// </summary>
+ /// <param name="index">The index to insert at.</param>
+ /// <param name="value">The object to insert.</param>
public void Insert(int index, object value) => godot_icall_Array_Insert(GetPtr(), index, value);
+ /// <summary>
+ /// Removes the first occurrence of the specified <paramref name="value"/>
+ /// from this <see cref="Array"/>.
+ /// </summary>
+ /// <param name="value">The value to remove.</param>
public void Remove(object value) => godot_icall_Array_Remove(GetPtr(), value);
+ /// <summary>
+ /// Removes an element from this <see cref="Array"/> by index.
+ /// </summary>
+ /// <param name="index">The index of the element to remove.</param>
public void RemoveAt(int index) => godot_icall_Array_RemoveAt(GetPtr(), index);
// ICollection
+ /// <summary>
+ /// Returns the number of elements in this <see cref="Array"/>.
+ /// This is also known as the size or length of the array.
+ /// </summary>
+ /// <returns>The number of elements.</returns>
public int Count => godot_icall_Array_Count(GetPtr());
- public object SyncRoot => this;
+ object ICollection.SyncRoot => this;
- public bool IsSynchronized => false;
+ bool ICollection.IsSynchronized => false;
+ /// <summary>
+ /// Copies the elements of this <see cref="Array"/> to the given
+ /// untyped C# array, starting at the given index.
+ /// </summary>
+ /// <param name="array">The array to copy to.</param>
+ /// <param name="index">The index to start at.</param>
public void CopyTo(System.Array array, int index)
{
if (array == null)
@@ -150,6 +248,10 @@ namespace Godot.Collections
// IEnumerable
+ /// <summary>
+ /// Gets an enumerator for this <see cref="Array"/>.
+ /// </summary>
+ /// <returns>An enumerator.</returns>
public IEnumerator GetEnumerator()
{
int count = Count;
@@ -160,75 +262,89 @@ namespace Godot.Collections
}
}
+ /// <summary>
+ /// Converts this <see cref="Array"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this array.</returns>
public override string ToString()
{
return godot_icall_Array_ToString(GetPtr());
}
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Array_Ctor();
+ internal static extern IntPtr godot_icall_Array_Ctor();
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Array_Ctor_MonoArray(System.Array array);
+ internal static extern void godot_icall_Array_Dtor(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_Dtor(IntPtr ptr);
+ internal static extern object godot_icall_Array_At(IntPtr ptr, int index);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_Array_At(IntPtr ptr, int index);
+ internal static extern object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static object godot_icall_Array_At_Generic(IntPtr ptr, int index, int elemTypeEncoding, IntPtr elemTypeClass);
+ internal static extern void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_SetAt(IntPtr ptr, int index, object value);
+ internal static extern int godot_icall_Array_Count(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_Array_Count(IntPtr ptr);
+ internal static extern int godot_icall_Array_Add(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_Array_Add(IntPtr ptr, object item);
+ internal static extern void godot_icall_Array_Clear(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_Clear(IntPtr ptr);
+ internal static extern IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Array_Concatenate(IntPtr left, IntPtr right);
+ internal static extern bool godot_icall_Array_Contains(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Array_Contains(IntPtr ptr, object item);
+ internal static extern void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_CopyTo(IntPtr ptr, System.Array array, int arrayIndex);
+ internal static extern IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Array_Duplicate(IntPtr ptr, bool deep);
+ internal static extern int godot_icall_Array_IndexOf(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_Array_IndexOf(IntPtr ptr, object item);
+ internal static extern void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_Insert(IntPtr ptr, int index, object item);
+ internal static extern bool godot_icall_Array_Remove(IntPtr ptr, object item);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Array_Remove(IntPtr ptr, object item);
+ internal static extern void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Array_RemoveAt(IntPtr ptr, int index);
+ internal static extern Error godot_icall_Array_Resize(IntPtr ptr, int newSize);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static Error godot_icall_Array_Resize(IntPtr ptr, int newSize);
+ 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>
+ /// Typed wrapper around Godot's Array class, an array of Variant
+ /// typed elements allocated in the engine in C++. Useful when
+ /// interfacing with the engine. Otherwise prefer .NET collections
+ /// such as arrays or <see cref="List{T}"/>.
+ /// </summary>
+ /// <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;
@@ -238,118 +354,207 @@ namespace Godot.Collections
Array.godot_icall_Array_Generic_GetElementTypeInfo(typeof(T), out elemTypeEncoding, out elemTypeClass);
}
+ /// <summary>
+ /// Constructs a new empty <see cref="Array{T}"/>.
+ /// </summary>
public Array()
{
- objectArray = new Array();
+ _objectArray = new Array();
}
+ /// <summary>
+ /// Constructs a new <see cref="Array{T}"/> from the given collection's elements.
+ /// </summary>
+ /// <param name="collection">The collection of elements to construct from.</param>
+ /// <returns>A new Godot Array.</returns>
public Array(IEnumerable<T> collection)
{
if (collection == null)
throw new NullReferenceException($"Parameter '{nameof(collection)} cannot be null.'");
- objectArray = new Array(collection);
+ _objectArray = new Array(collection);
}
+ /// <summary>
+ /// Constructs a new <see cref="Array{T}"/> from the given items.
+ /// </summary>
+ /// <param name="array">The items to put in the new array.</param>
+ /// <returns>A new Godot Array.</returns>
public Array(params T[] array) : this()
{
if (array == null)
{
throw new NullReferenceException($"Parameter '{nameof(array)} cannot be null.'");
}
- objectArray = new Array(array);
+ _objectArray = new Array(array);
}
+ /// <summary>
+ /// Constructs a typed <see cref="Array{T}"/> from an untyped <see cref="Array"/>.
+ /// </summary>
+ /// <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>
+ /// Converts this typed <see cref="Array{T}"/> to an untyped <see cref="Array"/>.
+ /// </summary>
+ /// <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 <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>
+ /// Resizes this <see cref="Array{T}"/> to the given size.
+ /// </summary>
+ /// <param name="newSize">The new size of the array.</param>
+ /// <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>
+ /// Shuffles the contents of this <see cref="Array{T}"/> into a random order.
+ /// </summary>
+ public void Shuffle()
+ {
+ _objectArray.Shuffle();
+ }
+
+ /// <summary>
+ /// Concatenates these two <see cref="Array{T}"/>s.
+ /// </summary>
+ /// <param name="left">The first array.</param>
+ /// <param name="right">The second array.</param>
+ /// <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 <paramref name="index"/>.
+ /// </summary>
+ /// <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>
+ /// Searches this <see cref="Array{T}"/> for an item
+ /// and returns its index or -1 if not found.
+ /// </summary>
+ /// <param name="item">The item to search for.</param>
+ /// <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>
+ /// Inserts a new item at a given position in the <see cref="Array{T}"/>.
+ /// The position must be a valid position of an existing item,
+ /// or the position at the end of the array.
+ /// Existing items will be moved to the right.
+ /// </summary>
+ /// <param name="index">The index to insert at.</param>
+ /// <param name="item">The item to insert.</param>
public void Insert(int index, T item)
{
- objectArray.Insert(index, item);
+ _objectArray.Insert(index, item);
}
+ /// <summary>
+ /// Removes an element from this <see cref="Array{T}"/> by index.
+ /// </summary>
+ /// <param name="index">The index of the element to remove.</param>
public void RemoveAt(int index)
{
- objectArray.RemoveAt(index);
+ _objectArray.RemoveAt(index);
}
// ICollection<T>
+ /// <summary>
+ /// Returns the number of elements in this <see cref="Array{T}"/>.
+ /// This is also known as the size or length of the array.
+ /// </summary>
+ /// <returns>The number of elements.</returns>
public int Count
{
- get { return objectArray.Count; }
+ get { return _objectArray.Count; }
}
- public bool IsReadOnly
- {
- get { return objectArray.IsReadOnly; }
- }
+ bool ICollection<T>.IsReadOnly => false;
+ /// <summary>
+ /// Adds an item to the end of this <see cref="Array{T}"/>.
+ /// 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>
+ /// Erases all items from this <see cref="Array{T}"/>.
+ /// </summary>
public void Clear()
{
- objectArray.Clear();
+ _objectArray.Clear();
}
+ /// <summary>
+ /// Checks if this <see cref="Array{T}"/> contains the given item.
+ /// </summary>
+ /// <param name="item">The item to look for.</param>
+ /// <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>
+ /// Copies the elements of this <see cref="Array{T}"/> to the given
+ /// C# array, starting at the given index.
+ /// </summary>
+ /// <param name="array">The C# array to copy to.</param>
+ /// <param name="arrayIndex">The index to start at.</param>
public void CopyTo(T[] array, int arrayIndex)
{
if (array == null)
@@ -361,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.");
@@ -373,6 +578,12 @@ namespace Godot.Collections
}
}
+ /// <summary>
+ /// Removes the first occurrence of the specified value
+ /// from this <see cref="Array{T}"/>.
+ /// </summary>
+ /// <param name="item">The value to remove.</param>
+ /// <returns>A <see langword="bool"/> indicating success or failure.</returns>
public bool Remove(T item)
{
return Array.godot_icall_Array_Remove(GetPtr(), item);
@@ -380,9 +591,13 @@ namespace Godot.Collections
// IEnumerable<T>
+ /// <summary>
+ /// Gets an enumerator for this <see cref="Array{T}"/>.
+ /// </summary>
+ /// <returns>An enumerator.</returns>
public IEnumerator<T> GetEnumerator()
{
- int count = objectArray.Count;
+ int count = _objectArray.Count;
for (int i = 0; i < count; i++)
{
@@ -395,6 +610,10 @@ namespace Godot.Collections
return GetEnumerator();
}
- public override string ToString() => objectArray.ToString();
+ /// <summary>
+ /// Converts this <see cref="Array{T}"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this array.</returns>
+ public override string ToString() => _objectArray.ToString();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
new file mode 100644
index 0000000000..ef135da51a
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/AssemblyHasScriptsAttribute.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Godot
+{
+ [AttributeUsage(AttributeTargets.Assembly)]
+ public class AssemblyHasScriptsAttribute : Attribute
+ {
+ private readonly bool requiresLookup;
+ private readonly System.Type[] scriptTypes;
+
+ public AssemblyHasScriptsAttribute()
+ {
+ requiresLookup = true;
+ }
+
+ public AssemblyHasScriptsAttribute(System.Type[] scriptTypes)
+ {
+ requiresLookup = false;
+ this.scriptTypes = scriptTypes;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
new file mode 100644
index 0000000000..e93bc89811
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/DisableGodotGeneratorsAttribute.cs
@@ -0,0 +1,7 @@
+using System;
+
+namespace Godot
+{
+ [AttributeUsage(AttributeTargets.Class)]
+ 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 8fc430b6bc..b8b9bc660c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/RPCAttributes.cs
@@ -2,21 +2,9 @@ using System;
namespace Godot
{
- [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
- public class RemoteAttribute : Attribute {}
+ [AttributeUsage(AttributeTargets.Method)]
+ public class AnyPeerAttribute : Attribute { }
- [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
- public class MasterAttribute : Attribute {}
-
- [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
- public class PuppetAttribute : Attribute {}
-
- [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
- public class RemoteSyncAttribute : Attribute {}
-
- [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
- public class MasterSyncAttribute : Attribute {}
-
- [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
- public class PuppetSyncAttribute : Attribute {}
+ [AttributeUsage(AttributeTargets.Method)]
+ public class AuthorityAttribute : Attribute { }
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
new file mode 100644
index 0000000000..12eb1035c3
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Attributes/ScriptPathAttribute.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Godot
+{
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+ public class ScriptPathAttribute : Attribute
+ {
+ private string path;
+
+ public ScriptPathAttribute(string path)
+ {
+ this.path = path;
+ }
+ }
+}
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 3f1120575f..bfbf1a097e 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -1,10 +1,10 @@
-using System;
-using System.Runtime.InteropServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+using System;
+using System.Runtime.InteropServices;
namespace Godot
{
@@ -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,7 +209,14 @@ namespace Godot
}
}
- public Quat RotationQuat()
+ /// <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();
@@ -218,18 +227,18 @@ namespace Godot
orthonormalizedBasis = orthonormalizedBasis.Scaled(-Vector3.One);
}
- return orthonormalizedBasis.Quat();
+ return orthonormalizedBasis.Quaternion();
}
- internal void SetQuatScale(Quat quat, Vector3 scale)
+ internal void SetQuaternionScale(Quaternion quaternion, Vector3 scale)
{
SetDiagonal(scale);
- Rotate(quat);
+ Rotate(quaternion);
}
- private void Rotate(Quat quat)
+ private void Rotate(Quaternion quaternion)
{
- this *= new Basis(quat);
+ this *= new Basis(quaternion);
}
private void SetDiagonal(Vector3 diagonal)
@@ -263,10 +272,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.Quat()"/> method instead, which
- /// returns a <see cref="Godot.Quat"/> quaternion instead of Euler angles.
+ /// Consider using the <see cref="Quaternion()"/> method instead, which
+ /// returns a <see cref="Godot.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();
@@ -304,7 +313,10 @@ namespace Godot
/// 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 +338,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 +467,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>
@@ -486,8 +501,8 @@ namespace Godot
/// <returns>The resulting basis matrix of the interpolation.</returns>
public Basis Slerp(Basis target, real_t weight)
{
- Quat from = new Quat(this);
- Quat to = new Quat(target);
+ Quaternion from = new Quaternion(this);
+ Quaternion to = new Quaternion(target);
Basis b = new Basis(from.Slerp(to, weight));
b.Row0 *= Mathf.Lerp(Row0.Length(), target.Row0.Length(), weight);
@@ -504,7 +519,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 +529,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 +539,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 +548,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 +568,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,15 +587,16 @@ 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
);
}
@@ -588,8 +605,8 @@ namespace Godot
/// 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.Quat"/> representing the basis's rotation.</returns>
- public Quat Quat()
+ /// <returns>A <see cref="Godot.Quaternion"/> representing the basis's rotation.</returns>
+ public Quaternion Quaternion()
{
real_t trace = Row0[0] + Row1[1] + Row2[2];
@@ -597,7 +614,7 @@ namespace Godot
{
real_t s = Mathf.Sqrt(trace + 1.0f) * 2f;
real_t inv_s = 1f / s;
- return new Quat(
+ return new Quaternion(
(Row2[1] - Row1[2]) * inv_s,
(Row0[2] - Row2[0]) * inv_s,
(Row1[0] - Row0[1]) * inv_s,
@@ -609,7 +626,7 @@ namespace Godot
{
real_t s = Mathf.Sqrt(Row0[0] - Row1[1] - Row2[2] + 1.0f) * 2f;
real_t inv_s = 1f / s;
- return new Quat(
+ return new Quaternion(
s * 0.25f,
(Row0[1] + Row1[0]) * inv_s,
(Row0[2] + Row2[0]) * inv_s,
@@ -621,7 +638,7 @@ namespace Godot
{
real_t s = Mathf.Sqrt(-Row0[0] + Row1[1] - Row2[2] + 1.0f) * 2f;
real_t inv_s = 1f / s;
- return new Quat(
+ return new Quaternion(
(Row0[1] + Row1[0]) * inv_s,
s * 0.25f,
(Row1[2] + Row2[1]) * inv_s,
@@ -632,7 +649,7 @@ namespace Godot
{
real_t s = Mathf.Sqrt(-Row0[0] - Row1[1] + Row2[2] + 1.0f) * 2f;
real_t inv_s = 1f / s;
- return new Quat(
+ return new Quaternion(
(Row0[2] + Row2[0]) * inv_s,
(Row1[2] + Row2[1]) * inv_s,
s * 0.25f,
@@ -675,47 +692,47 @@ 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>
/// Constructs a pure rotation basis matrix from the given quaternion.
/// </summary>
- /// <param name="quat">The quaternion to create the basis from.</param>
- public Basis(Quat quat)
+ /// <param name="quaternion">The quaternion to create the basis from.</param>
+ public Basis(Quaternion quaternion)
{
- real_t s = 2.0f / quat.LengthSquared;
+ real_t s = 2.0f / quaternion.LengthSquared;
- real_t xs = quat.x * s;
- real_t ys = quat.y * s;
- real_t zs = quat.z * s;
- real_t wx = quat.w * xs;
- real_t wy = quat.w * ys;
- real_t wz = quat.w * zs;
- real_t xx = quat.x * xs;
- real_t xy = quat.x * ys;
- real_t xz = quat.x * zs;
- real_t yy = quat.y * ys;
- real_t yz = quat.y * zs;
- real_t zz = quat.z * zs;
+ real_t xs = quaternion.x * s;
+ real_t ys = quaternion.y * s;
+ real_t zs = quaternion.z * s;
+ real_t wx = quaternion.w * xs;
+ real_t wy = quaternion.w * ys;
+ real_t wz = quaternion.w * zs;
+ real_t xx = quaternion.x * xs;
+ real_t xy = quaternion.x * ys;
+ real_t xz = quaternion.x * zs;
+ real_t yy = quaternion.y * ys;
+ real_t yz = quaternion.y * zs;
+ real_t zz = quaternion.z * zs;
Row0 = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy);
Row1 = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx);
@@ -727,8 +744,8 @@ namespace Godot
/// (in the YXZ convention: when *composing*, first Y, then X, and Z last),
/// given in the vector format as (X angle, Y angle, Z angle).
///
- /// Consider using the <see cref="Basis(Quat)"/> constructor instead, which
- /// uses a <see cref="Godot.Quat"/> quaternion instead of Euler angles.
+ /// Consider using the <see cref="Basis(Quaternion)"/> constructor instead, which
+ /// uses a <see cref="Godot.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,45 +889,54 @@ 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 String.Format("({0}, {1}, {2})", new object[]
- {
- Row0.ToString(),
- Row1.ToString(),
- Row2.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 String.Format("({0}, {1}, {2})", new object[]
- {
- Row0.ToString(format),
- Row1.ToString(format),
- Row2.ToString(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 3700a6194f..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)]
@@ -104,7 +104,7 @@ namespace Godot
/// <summary>
/// The HSV hue of this color, on the range 0 to 1.
/// </summary>
- /// <value>Getting is a long process, refer to the source code for details. Setting uses <see cref="FromHsv"/>.</value>
+ /// <value>Getting is a long process, refer to the source code for details. Setting uses <see cref="FromHSV"/>.</value>
public float h
{
get
@@ -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;
@@ -145,14 +145,14 @@ namespace Godot
}
set
{
- this = FromHsv(value, s, v, a);
+ this = FromHSV(value, s, v, a);
}
}
/// <summary>
/// The HSV saturation of this color, on the range 0 to 1.
/// </summary>
- /// <value>Getting is equivalent to the ratio between the min and max RGB value. Setting uses <see cref="FromHsv"/>.</value>
+ /// <value>Getting is equivalent to the ratio between the min and max RGB value. Setting uses <see cref="FromHSV"/>.</value>
public float s
{
get
@@ -166,14 +166,14 @@ namespace Godot
}
set
{
- this = FromHsv(h, value, v, a);
+ this = FromHSV(h, value, v, a);
}
}
/// <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
@@ -182,14 +182,19 @@ namespace Godot
}
set
{
- this = FromHsv(h, s, value, a);
+ this = FromHSV(h, s, value, a);
}
}
/// <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,37 +241,44 @@ 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 the most contrasting color.
+ /// Returns a new color with all components clamped between the
+ /// components of <paramref name="min"/> and <paramref name="max"/>
+ /// using <see cref="Mathf.Clamp(float, float, float)"/>.
/// </summary>
- /// <returns>The most contrasting color</returns>
- public Color Contrasted()
+ /// <param name="min">The color with minimum allowed values.</param>
+ /// <param name="max">The color with maximum allowed values.</param>
+ /// <returns>The color with all components clamped.</returns>
+ public Color Clamp(Color? min = null, Color? max = null)
{
- return new Color(
- (r + 0.5f) % 1.0f,
- (g + 0.5f) % 1.0f,
- (b + 0.5f) % 1.0f,
- a
+ Color minimum = min ?? new Color(0, 0, 0, 0);
+ Color maximum = max ?? new Color(1, 1, 1, 1);
+ return new Color
+ (
+ (float)Mathf.Clamp(r, minimum.r, maximum.r),
+ (float)Mathf.Clamp(g, minimum.g, maximum.g),
+ (float)Mathf.Clamp(b, minimum.b, maximum.b),
+ (float)Mathf.Clamp(a, minimum.a, maximum.a)
);
}
@@ -279,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()
@@ -308,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>
@@ -334,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>
@@ -351,11 +363,11 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 32-bit integer in ABGR format
- /// (each byte represents a component of the ABGR profile).
+ /// Returns the color converted to an unsigned 32-bit integer in ABGR
+ /// 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);
@@ -370,11 +382,11 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 64-bit integer in ABGR format
- /// (each word represents a component of the ABGR profile).
+ /// Returns the color converted to an unsigned 64-bit integer in ABGR
+ /// 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);
@@ -389,11 +401,11 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 32-bit integer in ARGB format
- /// (each byte represents a component of the ARGB profile).
+ /// Returns the color converted to an unsigned 32-bit integer in ARGB
+ /// 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);
@@ -408,11 +420,11 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 64-bit integer in ARGB format
- /// (each word represents a component of the ARGB profile).
+ /// Returns the color converted to an unsigned 64-bit integer in ARGB
+ /// 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);
@@ -427,11 +439,11 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 32-bit integer in RGBA format
- /// (each byte represents a component of the RGBA profile).
+ /// Returns the color converted to an unsigned 32-bit integer in RGBA
+ /// 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);
@@ -446,11 +458,11 @@ namespace Godot
}
/// <summary>
- /// Returns the color's 64-bit integer in RGBA format
- /// (each word represents a component of the RGBA profile).
+ /// Returns the color converted to an unsigned 64-bit integer in RGBA
+ /// 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);
@@ -467,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)
+ public string ToHTML(bool includeAlpha = true)
{
- var txt = string.Empty;
+ string txt = string.Empty;
txt += ToHex32(r);
txt += ToHex32(g);
@@ -486,7 +500,7 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from RGBA values 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>
@@ -501,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>
@@ -514,10 +528,10 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from a 32-bit integer
- /// (each byte represents a component of the RGBA profile).
+ /// 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;
@@ -530,10 +544,10 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from a 64-bit integer
- /// (each word represents a component of the RGBA profile).
+ /// 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;
@@ -546,18 +560,53 @@ namespace Godot
}
/// <summary>
- /// Constructs a color from the HTML hexadecimal color string in RGBA format.
+ /// 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)
+ {
+ if (HtmlIsValid(code))
+ {
+ this = FromHTML(code);
+ }
+ else
+ {
+ this = Named(code);
+ }
+ }
+
+ /// <summary>
+ /// 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>
+ /// <param name="alpha">The alpha (transparency) value, typically on the range of 0 to 1.</param>
+ public Color(string code, float alpha)
+ {
+ this = new Color(code);
+ a = alpha;
+ }
+
+ /// <summary>
+ /// 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>
- public Color(string rgba)
+ /// <exception name="ArgumentOutOfRangeException">
+ /// Thrown when the given <paramref name="rgba"/> color code is invalid.
+ /// </exception>
+ private static Color FromHTML(string rgba)
{
+ Color c;
if (rgba.Length == 0)
{
- r = 0f;
- g = 0f;
- b = 0f;
- a = 1.0f;
- return;
+ c.r = 0f;
+ c.g = 0f;
+ c.b = 0f;
+ c.a = 1.0f;
+ return c;
}
if (rgba[0] == '#')
@@ -588,50 +637,52 @@ 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}");
}
- a = 1.0f;
+ c.a = 1.0f;
if (isShorthand)
{
- r = ParseCol4(rgba, 0) / 15f;
- g = ParseCol4(rgba, 1) / 15f;
- b = ParseCol4(rgba, 2) / 15f;
+ c.r = ParseCol4(rgba, 0) / 15f;
+ c.g = ParseCol4(rgba, 1) / 15f;
+ c.b = ParseCol4(rgba, 2) / 15f;
if (alpha)
{
- a = ParseCol4(rgba, 3) / 15f;
+ c.a = ParseCol4(rgba, 3) / 15f;
}
}
else
{
- r = ParseCol8(rgba, 0) / 255f;
- g = ParseCol8(rgba, 2) / 255f;
- b = ParseCol8(rgba, 4) / 255f;
+ c.r = ParseCol8(rgba, 0) / 255f;
+ c.g = ParseCol8(rgba, 2) / 255f;
+ c.b = ParseCol8(rgba, 4) / 255f;
if (alpha)
{
- a = ParseCol8(rgba, 6) / 255f;
+ c.a = ParseCol8(rgba, 6) / 255f;
}
}
- if (r < 0)
+ if (c.r < 0)
{
throw new ArgumentOutOfRangeException("Invalid color code. Red part is not valid hexadecimal: " + rgba);
}
- if (g < 0)
+ if (c.g < 0)
{
throw new ArgumentOutOfRangeException("Invalid color code. Green part is not valid hexadecimal: " + rgba);
}
- if (b < 0)
+ if (c.b < 0)
{
throw new ArgumentOutOfRangeException("Invalid color code. Blue part is not valid hexadecimal: " + rgba);
}
- if (a < 0)
+ if (c.a < 0)
{
throw new ArgumentOutOfRangeException("Invalid color code. Alpha part is not valid hexadecimal: " + rgba);
}
+ return c;
}
/// <summary>
@@ -654,42 +705,39 @@ namespace Godot
/// the constants defined in <see cref="Colors"/>.
/// </summary>
/// <param name="name">The name of the color.</param>
- /// <param name="alpha">The alpha (transparency) component represented on the range of 0 to 1. Default: 1.</param>
/// <returns>The constructed color.</returns>
- public static Color ColorN(string name, float alpha = 1f)
+ 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.ToLower();
+ 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))
{
throw new ArgumentOutOfRangeException($"Invalid Color Name: {name}");
}
- Color color = Colors.namedColors[name];
- color.a = alpha;
- return color;
+ return Colors.namedColors[name];
}
/// <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>
/// <param name="value">The HSV value (brightness), typically on the range of 0 to 1.</param>
/// <param name="alpha">The alpha (transparency) value, typically on the range of 0 to 1.</param>
/// <returns>The constructed color.</returns>
- public static Color FromHsv(float hue, float saturation, float value, float alpha = 1.0f)
+ public static Color FromHSV(float hue, float saturation, float value, float alpha = 1.0f)
{
if (saturation == 0)
{
- // acp_hromatic (grey)
+ // Achromatic (grey)
return new Color(value, value, value, alpha);
}
@@ -702,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)
{
@@ -724,12 +772,12 @@ 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>
/// <param name="value">Output parameter for the HSV value.</param>
- public void ToHsv(out float hue, out float saturation, out float value)
+ public void ToHSV(out float hue, out float saturation, out float value)
{
float max = (float)Mathf.Max(r, Mathf.Max(g, b));
float min = (float)Mathf.Min(r, Mathf.Min(g, b));
@@ -748,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;
}
@@ -791,31 +841,10 @@ namespace Godot
return ParseCol4(str, ofs) * 16 + ParseCol4(str, ofs + 1);
}
- private String ToHex32(float val)
+ private string ToHex32(float val)
{
- int v = Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255));
-
- var ret = string.Empty;
-
- for (int i = 0; i < 2; i++)
- {
- char c;
- int lv = v & 0xF;
-
- if (lv < 10)
- {
- c = (char)('0' + lv);
- }
- else
- {
- c = (char)('a' + lv - 10);
- }
-
- v >>= 4;
- ret = c + ret;
- }
-
- return ret;
+ byte b = (byte)Mathf.RoundToInt(Mathf.Clamp(val * 255, 0, 255));
+ return b.HexEncode();
}
internal static bool HtmlIsValid(string color)
@@ -838,7 +867,8 @@ namespace Godot
}
// Check if each hex digit is valid.
- for (int i = 0; i < len; i++) {
+ for (int i = 0; i < len; i++)
+ {
if (ParseCol4(color, i) == -1)
{
return false;
@@ -848,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;
@@ -857,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;
@@ -866,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;
@@ -880,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;
@@ -889,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;
@@ -898,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;
@@ -907,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;
@@ -916,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;
}
@@ -943,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;
}
@@ -960,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)
@@ -970,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>
@@ -986,19 +1182,31 @@ 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 String.Format("{0},{1},{2},{3}", r.ToString(), g.ToString(), b.ToString(), a.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 String.Format("{0},{1},{2},{3}", r.ToString(format), g.ToString(format), b.ToString(format), a.ToString(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 d05a0414aa..68c821b447 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Colors.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
namespace Godot
@@ -9,301 +8,303 @@ namespace Godot
/// </summary>
public static class Colors
{
- // Color names and values are derived from core/color_names.inc
+ // Color names and values are derived from core/math/color_names.inc
internal static readonly Dictionary<string, Color> namedColors = new Dictionary<string, Color> {
- {"aliceblue", new Color(0.94f, 0.97f, 1.00f)},
- {"antiquewhite", new Color(0.98f, 0.92f, 0.84f)},
- {"aqua", new Color(0.00f, 1.00f, 1.00f)},
- {"aquamarine", new Color(0.50f, 1.00f, 0.83f)},
- {"azure", new Color(0.94f, 1.00f, 1.00f)},
- {"beige", new Color(0.96f, 0.96f, 0.86f)},
- {"bisque", new Color(1.00f, 0.89f, 0.77f)},
- {"black", new Color(0.00f, 0.00f, 0.00f)},
- {"blanchedalmond", new Color(1.00f, 0.92f, 0.80f)},
- {"blue", new Color(0.00f, 0.00f, 1.00f)},
- {"blueviolet", new Color(0.54f, 0.17f, 0.89f)},
- {"brown", new Color(0.65f, 0.16f, 0.16f)},
- {"burlywood", new Color(0.87f, 0.72f, 0.53f)},
- {"cadetblue", new Color(0.37f, 0.62f, 0.63f)},
- {"chartreuse", new Color(0.50f, 1.00f, 0.00f)},
- {"chocolate", new Color(0.82f, 0.41f, 0.12f)},
- {"coral", new Color(1.00f, 0.50f, 0.31f)},
- {"cornflower", new Color(0.39f, 0.58f, 0.93f)},
- {"cornsilk", new Color(1.00f, 0.97f, 0.86f)},
- {"crimson", new Color(0.86f, 0.08f, 0.24f)},
- {"cyan", new Color(0.00f, 1.00f, 1.00f)},
- {"darkblue", new Color(0.00f, 0.00f, 0.55f)},
- {"darkcyan", new Color(0.00f, 0.55f, 0.55f)},
- {"darkgoldenrod", new Color(0.72f, 0.53f, 0.04f)},
- {"darkgray", new Color(0.66f, 0.66f, 0.66f)},
- {"darkgreen", new Color(0.00f, 0.39f, 0.00f)},
- {"darkkhaki", new Color(0.74f, 0.72f, 0.42f)},
- {"darkmagenta", new Color(0.55f, 0.00f, 0.55f)},
- {"darkolivegreen", new Color(0.33f, 0.42f, 0.18f)},
- {"darkorange", new Color(1.00f, 0.55f, 0.00f)},
- {"darkorchid", new Color(0.60f, 0.20f, 0.80f)},
- {"darkred", new Color(0.55f, 0.00f, 0.00f)},
- {"darksalmon", new Color(0.91f, 0.59f, 0.48f)},
- {"darkseagreen", new Color(0.56f, 0.74f, 0.56f)},
- {"darkslateblue", new Color(0.28f, 0.24f, 0.55f)},
- {"darkslategray", new Color(0.18f, 0.31f, 0.31f)},
- {"darkturquoise", new Color(0.00f, 0.81f, 0.82f)},
- {"darkviolet", new Color(0.58f, 0.00f, 0.83f)},
- {"deeppink", new Color(1.00f, 0.08f, 0.58f)},
- {"deepskyblue", new Color(0.00f, 0.75f, 1.00f)},
- {"dimgray", new Color(0.41f, 0.41f, 0.41f)},
- {"dodgerblue", new Color(0.12f, 0.56f, 1.00f)},
- {"firebrick", new Color(0.70f, 0.13f, 0.13f)},
- {"floralwhite", new Color(1.00f, 0.98f, 0.94f)},
- {"forestgreen", new Color(0.13f, 0.55f, 0.13f)},
- {"fuchsia", new Color(1.00f, 0.00f, 1.00f)},
- {"gainsboro", new Color(0.86f, 0.86f, 0.86f)},
- {"ghostwhite", new Color(0.97f, 0.97f, 1.00f)},
- {"gold", new Color(1.00f, 0.84f, 0.00f)},
- {"goldenrod", new Color(0.85f, 0.65f, 0.13f)},
- {"gray", new Color(0.75f, 0.75f, 0.75f)},
- {"green", new Color(0.00f, 1.00f, 0.00f)},
- {"greenyellow", new Color(0.68f, 1.00f, 0.18f)},
- {"honeydew", new Color(0.94f, 1.00f, 0.94f)},
- {"hotpink", new Color(1.00f, 0.41f, 0.71f)},
- {"indianred", new Color(0.80f, 0.36f, 0.36f)},
- {"indigo", new Color(0.29f, 0.00f, 0.51f)},
- {"ivory", new Color(1.00f, 1.00f, 0.94f)},
- {"khaki", new Color(0.94f, 0.90f, 0.55f)},
- {"lavender", new Color(0.90f, 0.90f, 0.98f)},
- {"lavenderblush", new Color(1.00f, 0.94f, 0.96f)},
- {"lawngreen", new Color(0.49f, 0.99f, 0.00f)},
- {"lemonchiffon", new Color(1.00f, 0.98f, 0.80f)},
- {"lightblue", new Color(0.68f, 0.85f, 0.90f)},
- {"lightcoral", new Color(0.94f, 0.50f, 0.50f)},
- {"lightcyan", new Color(0.88f, 1.00f, 1.00f)},
- {"lightgoldenrod", new Color(0.98f, 0.98f, 0.82f)},
- {"lightgray", new Color(0.83f, 0.83f, 0.83f)},
- {"lightgreen", new Color(0.56f, 0.93f, 0.56f)},
- {"lightpink", new Color(1.00f, 0.71f, 0.76f)},
- {"lightsalmon", new Color(1.00f, 0.63f, 0.48f)},
- {"lightseagreen", new Color(0.13f, 0.70f, 0.67f)},
- {"lightskyblue", new Color(0.53f, 0.81f, 0.98f)},
- {"lightslategray", new Color(0.47f, 0.53f, 0.60f)},
- {"lightsteelblue", new Color(0.69f, 0.77f, 0.87f)},
- {"lightyellow", new Color(1.00f, 1.00f, 0.88f)},
- {"lime", new Color(0.00f, 1.00f, 0.00f)},
- {"limegreen", new Color(0.20f, 0.80f, 0.20f)},
- {"linen", new Color(0.98f, 0.94f, 0.90f)},
- {"magenta", new Color(1.00f, 0.00f, 1.00f)},
- {"maroon", new Color(0.69f, 0.19f, 0.38f)},
- {"mediumaquamarine", new Color(0.40f, 0.80f, 0.67f)},
- {"mediumblue", new Color(0.00f, 0.00f, 0.80f)},
- {"mediumorchid", new Color(0.73f, 0.33f, 0.83f)},
- {"mediumpurple", new Color(0.58f, 0.44f, 0.86f)},
- {"mediumseagreen", new Color(0.24f, 0.70f, 0.44f)},
- {"mediumslateblue", new Color(0.48f, 0.41f, 0.93f)},
- {"mediumspringgreen", new Color(0.00f, 0.98f, 0.60f)},
- {"mediumturquoise", new Color(0.28f, 0.82f, 0.80f)},
- {"mediumvioletred", new Color(0.78f, 0.08f, 0.52f)},
- {"midnightblue", new Color(0.10f, 0.10f, 0.44f)},
- {"mintcream", new Color(0.96f, 1.00f, 0.98f)},
- {"mistyrose", new Color(1.00f, 0.89f, 0.88f)},
- {"moccasin", new Color(1.00f, 0.89f, 0.71f)},
- {"navajowhite", new Color(1.00f, 0.87f, 0.68f)},
- {"navyblue", new Color(0.00f, 0.00f, 0.50f)},
- {"oldlace", new Color(0.99f, 0.96f, 0.90f)},
- {"olive", new Color(0.50f, 0.50f, 0.00f)},
- {"olivedrab", new Color(0.42f, 0.56f, 0.14f)},
- {"orange", new Color(1.00f, 0.65f, 0.00f)},
- {"orangered", new Color(1.00f, 0.27f, 0.00f)},
- {"orchid", new Color(0.85f, 0.44f, 0.84f)},
- {"palegoldenrod", new Color(0.93f, 0.91f, 0.67f)},
- {"palegreen", new Color(0.60f, 0.98f, 0.60f)},
- {"paleturquoise", new Color(0.69f, 0.93f, 0.93f)},
- {"palevioletred", new Color(0.86f, 0.44f, 0.58f)},
- {"papayawhip", new Color(1.00f, 0.94f, 0.84f)},
- {"peachpuff", new Color(1.00f, 0.85f, 0.73f)},
- {"peru", new Color(0.80f, 0.52f, 0.25f)},
- {"pink", new Color(1.00f, 0.75f, 0.80f)},
- {"plum", new Color(0.87f, 0.63f, 0.87f)},
- {"powderblue", new Color(0.69f, 0.88f, 0.90f)},
- {"purple", new Color(0.63f, 0.13f, 0.94f)},
- {"rebeccapurple", new Color(0.40f, 0.20f, 0.60f)},
- {"red", new Color(1.00f, 0.00f, 0.00f)},
- {"rosybrown", new Color(0.74f, 0.56f, 0.56f)},
- {"royalblue", new Color(0.25f, 0.41f, 0.88f)},
- {"saddlebrown", new Color(0.55f, 0.27f, 0.07f)},
- {"salmon", new Color(0.98f, 0.50f, 0.45f)},
- {"sandybrown", new Color(0.96f, 0.64f, 0.38f)},
- {"seagreen", new Color(0.18f, 0.55f, 0.34f)},
- {"seashell", new Color(1.00f, 0.96f, 0.93f)},
- {"sienna", new Color(0.63f, 0.32f, 0.18f)},
- {"silver", new Color(0.75f, 0.75f, 0.75f)},
- {"skyblue", new Color(0.53f, 0.81f, 0.92f)},
- {"slateblue", new Color(0.42f, 0.35f, 0.80f)},
- {"slategray", new Color(0.44f, 0.50f, 0.56f)},
- {"snow", new Color(1.00f, 0.98f, 0.98f)},
- {"springgreen", new Color(0.00f, 1.00f, 0.50f)},
- {"steelblue", new Color(0.27f, 0.51f, 0.71f)},
- {"tan", new Color(0.82f, 0.71f, 0.55f)},
- {"teal", new Color(0.00f, 0.50f, 0.50f)},
- {"thistle", new Color(0.85f, 0.75f, 0.85f)},
- {"tomato", new Color(1.00f, 0.39f, 0.28f)},
- {"transparent", new Color(1.00f, 1.00f, 1.00f, 0.00f)},
- {"turquoise", new Color(0.25f, 0.88f, 0.82f)},
- {"violet", new Color(0.93f, 0.51f, 0.93f)},
- {"webgreen", new Color(0.00f, 0.50f, 0.00f)},
- {"webgray", new Color(0.50f, 0.50f, 0.50f)},
- {"webmaroon", new Color(0.50f, 0.00f, 0.00f)},
- {"webpurple", new Color(0.50f, 0.00f, 0.50f)},
- {"wheat", new Color(0.96f, 0.87f, 0.70f)},
- {"white", new Color(1.00f, 1.00f, 1.00f)},
- {"whitesmoke", new Color(0.96f, 0.96f, 0.96f)},
- {"yellow", new Color(1.00f, 1.00f, 0.00f)},
- {"yellowgreen", new Color(0.60f, 0.80f, 0.20f)},
+ {"ALICEBLUE", new Color(0.94f, 0.97f, 1.00f)},
+ {"ANTIQUEWHITE", new Color(0.98f, 0.92f, 0.84f)},
+ {"AQUA", new Color(0.00f, 1.00f, 1.00f)},
+ {"AQUAMARINE", new Color(0.50f, 1.00f, 0.83f)},
+ {"AZURE", new Color(0.94f, 1.00f, 1.00f)},
+ {"BEIGE", new Color(0.96f, 0.96f, 0.86f)},
+ {"BISQUE", new Color(1.00f, 0.89f, 0.77f)},
+ {"BLACK", new Color(0.00f, 0.00f, 0.00f)},
+ {"BLANCHEDALMOND", new Color(1.00f, 0.92f, 0.80f)},
+ {"BLUE", new Color(0.00f, 0.00f, 1.00f)},
+ {"BLUEVIOLET", new Color(0.54f, 0.17f, 0.89f)},
+ {"BROWN", new Color(0.65f, 0.16f, 0.16f)},
+ {"BURLYWOOD", new Color(0.87f, 0.72f, 0.53f)},
+ {"CADETBLUE", new Color(0.37f, 0.62f, 0.63f)},
+ {"CHARTREUSE", new Color(0.50f, 1.00f, 0.00f)},
+ {"CHOCOLATE", new Color(0.82f, 0.41f, 0.12f)},
+ {"CORAL", new Color(1.00f, 0.50f, 0.31f)},
+ {"CORNFLOWERBLUE", new Color(0.39f, 0.58f, 0.93f)},
+ {"CORNSILK", new Color(1.00f, 0.97f, 0.86f)},
+ {"CRIMSON", new Color(0.86f, 0.08f, 0.24f)},
+ {"CYAN", new Color(0.00f, 1.00f, 1.00f)},
+ {"DARKBLUE", new Color(0.00f, 0.00f, 0.55f)},
+ {"DARKCYAN", new Color(0.00f, 0.55f, 0.55f)},
+ {"DARKGOLDENROD", new Color(0.72f, 0.53f, 0.04f)},
+ {"DARKGRAY", new Color(0.66f, 0.66f, 0.66f)},
+ {"DARKGREEN", new Color(0.00f, 0.39f, 0.00f)},
+ {"DARKKHAKI", new Color(0.74f, 0.72f, 0.42f)},
+ {"DARKMAGENTA", new Color(0.55f, 0.00f, 0.55f)},
+ {"DARKOLIVEGREEN", new Color(0.33f, 0.42f, 0.18f)},
+ {"DARKORANGE", new Color(1.00f, 0.55f, 0.00f)},
+ {"DARKORCHID", new Color(0.60f, 0.20f, 0.80f)},
+ {"DARKRED", new Color(0.55f, 0.00f, 0.00f)},
+ {"DARKSALMON", new Color(0.91f, 0.59f, 0.48f)},
+ {"DARKSEAGREEN", new Color(0.56f, 0.74f, 0.56f)},
+ {"DARKSLATEBLUE", new Color(0.28f, 0.24f, 0.55f)},
+ {"DARKSLATEGRAY", new Color(0.18f, 0.31f, 0.31f)},
+ {"DARKTURQUOISE", new Color(0.00f, 0.81f, 0.82f)},
+ {"DARKVIOLET", new Color(0.58f, 0.00f, 0.83f)},
+ {"DEEPPINK", new Color(1.00f, 0.08f, 0.58f)},
+ {"DEEPSKYBLUE", new Color(0.00f, 0.75f, 1.00f)},
+ {"DIMGRAY", new Color(0.41f, 0.41f, 0.41f)},
+ {"DODGERBLUE", new Color(0.12f, 0.56f, 1.00f)},
+ {"FIREBRICK", new Color(0.70f, 0.13f, 0.13f)},
+ {"FLORALWHITE", new Color(1.00f, 0.98f, 0.94f)},
+ {"FORESTGREEN", new Color(0.13f, 0.55f, 0.13f)},
+ {"FUCHSIA", new Color(1.00f, 0.00f, 1.00f)},
+ {"GAINSBORO", new Color(0.86f, 0.86f, 0.86f)},
+ {"GHOSTWHITE", new Color(0.97f, 0.97f, 1.00f)},
+ {"GOLD", new Color(1.00f, 0.84f, 0.00f)},
+ {"GOLDENROD", new Color(0.85f, 0.65f, 0.13f)},
+ {"GRAY", new Color(0.75f, 0.75f, 0.75f)},
+ {"GREEN", new Color(0.00f, 1.00f, 0.00f)},
+ {"GREENYELLOW", new Color(0.68f, 1.00f, 0.18f)},
+ {"HONEYDEW", new Color(0.94f, 1.00f, 0.94f)},
+ {"HOTPINK", new Color(1.00f, 0.41f, 0.71f)},
+ {"INDIANRED", new Color(0.80f, 0.36f, 0.36f)},
+ {"INDIGO", new Color(0.29f, 0.00f, 0.51f)},
+ {"IVORY", new Color(1.00f, 1.00f, 0.94f)},
+ {"KHAKI", new Color(0.94f, 0.90f, 0.55f)},
+ {"LAVENDER", new Color(0.90f, 0.90f, 0.98f)},
+ {"LAVENDERBLUSH", new Color(1.00f, 0.94f, 0.96f)},
+ {"LAWNGREEN", new Color(0.49f, 0.99f, 0.00f)},
+ {"LEMONCHIFFON", new Color(1.00f, 0.98f, 0.80f)},
+ {"LIGHTBLUE", new Color(0.68f, 0.85f, 0.90f)},
+ {"LIGHTCORAL", new Color(0.94f, 0.50f, 0.50f)},
+ {"LIGHTCYAN", new Color(0.88f, 1.00f, 1.00f)},
+ {"LIGHTGOLDENROD", new Color(0.98f, 0.98f, 0.82f)},
+ {"LIGHTGRAY", new Color(0.83f, 0.83f, 0.83f)},
+ {"LIGHTGREEN", new Color(0.56f, 0.93f, 0.56f)},
+ {"LIGHTPINK", new Color(1.00f, 0.71f, 0.76f)},
+ {"LIGHTSALMON", new Color(1.00f, 0.63f, 0.48f)},
+ {"LIGHTSEAGREEN", new Color(0.13f, 0.70f, 0.67f)},
+ {"LIGHTSKYBLUE", new Color(0.53f, 0.81f, 0.98f)},
+ {"LIGHTSLATEGRAY", new Color(0.47f, 0.53f, 0.60f)},
+ {"LIGHTSTEELBLUE", new Color(0.69f, 0.77f, 0.87f)},
+ {"LIGHTYELLOW", new Color(1.00f, 1.00f, 0.88f)},
+ {"LIME", new Color(0.00f, 1.00f, 0.00f)},
+ {"LIMEGREEN", new Color(0.20f, 0.80f, 0.20f)},
+ {"LINEN", new Color(0.98f, 0.94f, 0.90f)},
+ {"MAGENTA", new Color(1.00f, 0.00f, 1.00f)},
+ {"MAROON", new Color(0.69f, 0.19f, 0.38f)},
+ {"MEDIUMAQUAMARINE", new Color(0.40f, 0.80f, 0.67f)},
+ {"MEDIUMBLUE", new Color(0.00f, 0.00f, 0.80f)},
+ {"MEDIUMORCHID", new Color(0.73f, 0.33f, 0.83f)},
+ {"MEDIUMPURPLE", new Color(0.58f, 0.44f, 0.86f)},
+ {"MEDIUMSEAGREEN", new Color(0.24f, 0.70f, 0.44f)},
+ {"MEDIUMSLATEBLUE", new Color(0.48f, 0.41f, 0.93f)},
+ {"MEDIUMSPRINGGREEN", new Color(0.00f, 0.98f, 0.60f)},
+ {"MEDIUMTURQUOISE", new Color(0.28f, 0.82f, 0.80f)},
+ {"MEDIUMVIOLETRED", new Color(0.78f, 0.08f, 0.52f)},
+ {"MIDNIGHTBLUE", new Color(0.10f, 0.10f, 0.44f)},
+ {"MINTCREAM", new Color(0.96f, 1.00f, 0.98f)},
+ {"MISTYROSE", new Color(1.00f, 0.89f, 0.88f)},
+ {"MOCCASIN", new Color(1.00f, 0.89f, 0.71f)},
+ {"NAVAJOWHITE", new Color(1.00f, 0.87f, 0.68f)},
+ {"NAVYBLUE", new Color(0.00f, 0.00f, 0.50f)},
+ {"OLDLACE", new Color(0.99f, 0.96f, 0.90f)},
+ {"OLIVE", new Color(0.50f, 0.50f, 0.00f)},
+ {"OLIVEDRAB", new Color(0.42f, 0.56f, 0.14f)},
+ {"ORANGE", new Color(1.00f, 0.65f, 0.00f)},
+ {"ORANGERED", new Color(1.00f, 0.27f, 0.00f)},
+ {"ORCHID", new Color(0.85f, 0.44f, 0.84f)},
+ {"PALEGOLDENROD", new Color(0.93f, 0.91f, 0.67f)},
+ {"PALEGREEN", new Color(0.60f, 0.98f, 0.60f)},
+ {"PALETURQUOISE", new Color(0.69f, 0.93f, 0.93f)},
+ {"PALEVIOLETRED", new Color(0.86f, 0.44f, 0.58f)},
+ {"PAPAYAWHIP", new Color(1.00f, 0.94f, 0.84f)},
+ {"PEACHPUFF", new Color(1.00f, 0.85f, 0.73f)},
+ {"PERU", new Color(0.80f, 0.52f, 0.25f)},
+ {"PINK", new Color(1.00f, 0.75f, 0.80f)},
+ {"PLUM", new Color(0.87f, 0.63f, 0.87f)},
+ {"POWDERBLUE", new Color(0.69f, 0.88f, 0.90f)},
+ {"PURPLE", new Color(0.63f, 0.13f, 0.94f)},
+ {"REBECCAPURPLE", new Color(0.40f, 0.20f, 0.60f)},
+ {"RED", new Color(1.00f, 0.00f, 0.00f)},
+ {"ROSYBROWN", new Color(0.74f, 0.56f, 0.56f)},
+ {"ROYALBLUE", new Color(0.25f, 0.41f, 0.88f)},
+ {"SADDLEBROWN", new Color(0.55f, 0.27f, 0.07f)},
+ {"SALMON", new Color(0.98f, 0.50f, 0.45f)},
+ {"SANDYBROWN", new Color(0.96f, 0.64f, 0.38f)},
+ {"SEAGREEN", new Color(0.18f, 0.55f, 0.34f)},
+ {"SEASHELL", new Color(1.00f, 0.96f, 0.93f)},
+ {"SIENNA", new Color(0.63f, 0.32f, 0.18f)},
+ {"SILVER", new Color(0.75f, 0.75f, 0.75f)},
+ {"SKYBLUE", new Color(0.53f, 0.81f, 0.92f)},
+ {"SLATEBLUE", new Color(0.42f, 0.35f, 0.80f)},
+ {"SLATEGRAY", new Color(0.44f, 0.50f, 0.56f)},
+ {"SNOW", new Color(1.00f, 0.98f, 0.98f)},
+ {"SPRINGGREEN", new Color(0.00f, 1.00f, 0.50f)},
+ {"STEELBLUE", new Color(0.27f, 0.51f, 0.71f)},
+ {"TAN", new Color(0.82f, 0.71f, 0.55f)},
+ {"TEAL", new Color(0.00f, 0.50f, 0.50f)},
+ {"THISTLE", new Color(0.85f, 0.75f, 0.85f)},
+ {"TOMATO", new Color(1.00f, 0.39f, 0.28f)},
+ {"TRANSPARENT", new Color(1.00f, 1.00f, 1.00f, 0.00f)},
+ {"TURQUOISE", new Color(0.25f, 0.88f, 0.82f)},
+ {"VIOLET", new Color(0.93f, 0.51f, 0.93f)},
+ {"WEBGRAY", new Color(0.50f, 0.50f, 0.50f)},
+ {"WEBGREEN", new Color(0.00f, 0.50f, 0.00f)},
+ {"WEBMAROON", new Color(0.50f, 0.00f, 0.00f)},
+ {"WEBPURPLE", new Color(0.50f, 0.00f, 0.50f)},
+ {"WHEAT", new Color(0.96f, 0.87f, 0.70f)},
+ {"WHITE", new Color(1.00f, 1.00f, 1.00f)},
+ {"WHITESMOKE", new Color(0.96f, 0.96f, 0.96f)},
+ {"YELLOW", new Color(1.00f, 1.00f, 0.00f)},
+ {"YELLOWGREEN", new Color(0.60f, 0.80f, 0.20f)},
};
- public static Color AliceBlue { get { return namedColors["aliceblue"]; } }
- public static Color AntiqueWhite { get { return namedColors["antiquewhite"]; } }
- public static Color Aqua { get { return namedColors["aqua"]; } }
- public static Color Aquamarine { get { return namedColors["aquamarine"]; } }
- public static Color Azure { get { return namedColors["azure"]; } }
- public static Color Beige { get { return namedColors["beige"]; } }
- public static Color Bisque { get { return namedColors["bisque"]; } }
- public static Color Black { get { return namedColors["black"]; } }
- public static Color BlanchedAlmond { get { return namedColors["blanchedalmond"]; } }
- public static Color Blue { get { return namedColors["blue"]; } }
- public static Color BlueViolet { get { return namedColors["blueviolet"]; } }
- public static Color Brown { get { return namedColors["brown"]; } }
- public static Color BurlyWood { get { return namedColors["burlywood"]; } }
- public static Color CadetBlue { get { return namedColors["cadetblue"]; } }
- public static Color Chartreuse { get { return namedColors["chartreuse"]; } }
- public static Color Chocolate { get { return namedColors["chocolate"]; } }
- public static Color Coral { get { return namedColors["coral"]; } }
- public static Color Cornflower { get { return namedColors["cornflower"]; } }
- public static Color Cornsilk { get { return namedColors["cornsilk"]; } }
- public static Color Crimson { get { return namedColors["crimson"]; } }
- public static Color Cyan { get { return namedColors["cyan"]; } }
- public static Color DarkBlue { get { return namedColors["darkblue"]; } }
- public static Color DarkCyan { get { return namedColors["darkcyan"]; } }
- public static Color DarkGoldenrod { get { return namedColors["darkgoldenrod"]; } }
- public static Color DarkGray { get { return namedColors["darkgray"]; } }
- public static Color DarkGreen { get { return namedColors["darkgreen"]; } }
- public static Color DarkKhaki { get { return namedColors["darkkhaki"]; } }
- public static Color DarkMagenta { get { return namedColors["darkmagenta"]; } }
- public static Color DarkOliveGreen { get { return namedColors["darkolivegreen"]; } }
- public static Color DarkOrange { get { return namedColors["darkorange"]; } }
- public static Color DarkOrchid { get { return namedColors["darkorchid"]; } }
- public static Color DarkRed { get { return namedColors["darkred"]; } }
- public static Color DarkSalmon { get { return namedColors["darksalmon"]; } }
- public static Color DarkSeaGreen { get { return namedColors["darkseagreen"]; } }
- public static Color DarkSlateBlue { get { return namedColors["darkslateblue"]; } }
- public static Color DarkSlateGray { get { return namedColors["darkslategray"]; } }
- public static Color DarkTurquoise { get { return namedColors["darkturquoise"]; } }
- public static Color DarkViolet { get { return namedColors["darkviolet"]; } }
- public static Color DeepPink { get { return namedColors["deeppink"]; } }
- public static Color DeepSkyBlue { get { return namedColors["deepskyblue"]; } }
- public static Color DimGray { get { return namedColors["dimgray"]; } }
- public static Color DodgerBlue { get { return namedColors["dodgerblue"]; } }
- public static Color Firebrick { get { return namedColors["firebrick"]; } }
- public static Color FloralWhite { get { return namedColors["floralwhite"]; } }
- public static Color ForestGreen { get { return namedColors["forestgreen"]; } }
- public static Color Fuchsia { get { return namedColors["fuchsia"]; } }
- public static Color Gainsboro { get { return namedColors["gainsboro"]; } }
- public static Color GhostWhite { get { return namedColors["ghostwhite"]; } }
- public static Color Gold { get { return namedColors["gold"]; } }
- public static Color Goldenrod { get { return namedColors["goldenrod"]; } }
- public static Color Gray { get { return namedColors["gray"]; } }
- public static Color Green { get { return namedColors["green"]; } }
- public static Color GreenYellow { get { return namedColors["greenyellow"]; } }
- public static Color Honeydew { get { return namedColors["honeydew"]; } }
- public static Color HotPink { get { return namedColors["hotpink"]; } }
- public static Color IndianRed { get { return namedColors["indianred"]; } }
- public static Color Indigo { get { return namedColors["indigo"]; } }
- public static Color Ivory { get { return namedColors["ivory"]; } }
- public static Color Khaki { get { return namedColors["khaki"]; } }
- public static Color Lavender { get { return namedColors["lavender"]; } }
- public static Color LavenderBlush { get { return namedColors["lavenderblush"]; } }
- public static Color LawnGreen { get { return namedColors["lawngreen"]; } }
- public static Color LemonChiffon { get { return namedColors["lemonchiffon"]; } }
- public static Color LightBlue { get { return namedColors["lightblue"]; } }
- public static Color LightCoral { get { return namedColors["lightcoral"]; } }
- public static Color LightCyan { get { return namedColors["lightcyan"]; } }
- public static Color LightGoldenrod { get { return namedColors["lightgoldenrod"]; } }
- public static Color LightGray { get { return namedColors["lightgray"]; } }
- public static Color LightGreen { get { return namedColors["lightgreen"]; } }
- public static Color LightPink { get { return namedColors["lightpink"]; } }
- public static Color LightSalmon { get { return namedColors["lightsalmon"]; } }
- public static Color LightSeaGreen { get { return namedColors["lightseagreen"]; } }
- public static Color LightSkyBlue { get { return namedColors["lightskyblue"]; } }
- public static Color LightSlateGray { get { return namedColors["lightslategray"]; } }
- public static Color LightSteelBlue { get { return namedColors["lightsteelblue"]; } }
- public static Color LightYellow { get { return namedColors["lightyellow"]; } }
- public static Color Lime { get { return namedColors["lime"]; } }
- public static Color Limegreen { get { return namedColors["limegreen"]; } }
- public static Color Linen { get { return namedColors["linen"]; } }
- public static Color Magenta { get { return namedColors["magenta"]; } }
- public static Color Maroon { get { return namedColors["maroon"]; } }
- public static Color MediumAquamarine { get { return namedColors["mediumaquamarine"]; } }
- public static Color MediumBlue { get { return namedColors["mediumblue"]; } }
- public static Color MediumOrchid { get { return namedColors["mediumorchid"]; } }
- public static Color MediumPurple { get { return namedColors["mediumpurple"]; } }
- public static Color MediumSeaGreen { get { return namedColors["mediumseagreen"]; } }
- public static Color MediumSlateBlue { get { return namedColors["mediumslateblue"]; } }
- public static Color MediumSpringGreen { get { return namedColors["mediumspringgreen"]; } }
- public static Color MediumTurquoise { get { return namedColors["mediumturquoise"]; } }
- public static Color MediumVioletRed { get { return namedColors["mediumvioletred"]; } }
- public static Color MidnightBlue { get { return namedColors["midnightblue"]; } }
- public static Color MintCream { get { return namedColors["mintcream"]; } }
- public static Color MistyRose { get { return namedColors["mistyrose"]; } }
- public static Color Moccasin { get { return namedColors["moccasin"]; } }
- public static Color NavajoWhite { get { return namedColors["navajowhite"]; } }
- public static Color NavyBlue { get { return namedColors["navyblue"]; } }
- public static Color OldLace { get { return namedColors["oldlace"]; } }
- public static Color Olive { get { return namedColors["olive"]; } }
- public static Color OliveDrab { get { return namedColors["olivedrab"]; } }
- public static Color Orange { get { return namedColors["orange"]; } }
- public static Color OrangeRed { get { return namedColors["orangered"]; } }
- public static Color Orchid { get { return namedColors["orchid"]; } }
- public static Color PaleGoldenrod { get { return namedColors["palegoldenrod"]; } }
- public static Color PaleGreen { get { return namedColors["palegreen"]; } }
- public static Color PaleTurquoise { get { return namedColors["paleturquoise"]; } }
- public static Color PaleVioletRed { get { return namedColors["palevioletred"]; } }
- public static Color PapayaWhip { get { return namedColors["papayawhip"]; } }
- public static Color PeachPuff { get { return namedColors["peachpuff"]; } }
- public static Color Peru { get { return namedColors["peru"]; } }
- public static Color Pink { get { return namedColors["pink"]; } }
- public static Color Plum { get { return namedColors["plum"]; } }
- public static Color PowderBlue { get { return namedColors["powderblue"]; } }
- public static Color Purple { get { return namedColors["purple"]; } }
- public static Color RebeccaPurple { get { return namedColors["rebeccapurple"]; } }
- public static Color Red { get { return namedColors["red"]; } }
- public static Color RosyBrown { get { return namedColors["rosybrown"]; } }
- public static Color RoyalBlue { get { return namedColors["royalblue"]; } }
- public static Color SaddleBrown { get { return namedColors["saddlebrown"]; } }
- public static Color Salmon { get { return namedColors["salmon"]; } }
- public static Color SandyBrown { get { return namedColors["sandybrown"]; } }
- public static Color SeaGreen { get { return namedColors["seagreen"]; } }
- public static Color SeaShell { get { return namedColors["seashell"]; } }
- public static Color Sienna { get { return namedColors["sienna"]; } }
- public static Color Silver { get { return namedColors["silver"]; } }
- public static Color SkyBlue { get { return namedColors["skyblue"]; } }
- public static Color SlateBlue { get { return namedColors["slateblue"]; } }
- public static Color SlateGray { get { return namedColors["slategray"]; } }
- public static Color Snow { get { return namedColors["snow"]; } }
- public static Color SpringGreen { get { return namedColors["springgreen"]; } }
- public static Color SteelBlue { get { return namedColors["steelblue"]; } }
- public static Color Tan { get { return namedColors["tan"]; } }
- public static Color Teal { get { return namedColors["teal"]; } }
- public static Color Thistle { get { return namedColors["thistle"]; } }
- public static Color Tomato { get { return namedColors["tomato"]; } }
- public static Color Transparent { get { return namedColors["transparent"]; } }
- public static Color Turquoise { get { return namedColors["turquoise"]; } }
- public static Color Violet { get { return namedColors["violet"]; } }
- public static Color WebGreen { get { return namedColors["webgreen"]; } }
- public static Color WebGray { get { return namedColors["webgray"]; } }
- public static Color WebMaroon { get { return namedColors["webmaroon"]; } }
- public static Color WebPurple { get { return namedColors["webpurple"]; } }
- public static Color Wheat { get { return namedColors["wheat"]; } }
- public static Color White { get { return namedColors["white"]; } }
- 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 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"]; } }
+ public static Color Aquamarine { get { return namedColors["AQUAMARINE"]; } }
+ public static Color Azure { get { return namedColors["AZURE"]; } }
+ public static Color Beige { get { return namedColors["BEIGE"]; } }
+ public static Color Bisque { get { return namedColors["BISQUE"]; } }
+ public static Color Black { get { return namedColors["BLACK"]; } }
+ public static Color BlanchedAlmond { get { return namedColors["BLANCHEDALMOND"]; } }
+ public static Color Blue { get { return namedColors["BLUE"]; } }
+ public static Color BlueViolet { get { return namedColors["BLUEVIOLET"]; } }
+ public static Color Brown { get { return namedColors["BROWN"]; } }
+ public static Color Burlywood { get { return namedColors["BURLYWOOD"]; } }
+ public static Color CadetBlue { get { return namedColors["CADETBLUE"]; } }
+ public static Color Chartreuse { get { return namedColors["CHARTREUSE"]; } }
+ public static Color Chocolate { get { return namedColors["CHOCOLATE"]; } }
+ public static Color Coral { get { return namedColors["CORAL"]; } }
+ public static Color CornflowerBlue { get { return namedColors["CORNFLOWERBLUE"]; } }
+ public static Color Cornsilk { get { return namedColors["CORNSILK"]; } }
+ public static Color Crimson { get { return namedColors["CRIMSON"]; } }
+ public static Color Cyan { get { return namedColors["CYAN"]; } }
+ public static Color DarkBlue { get { return namedColors["DARKBLUE"]; } }
+ public static Color DarkCyan { get { return namedColors["DARKCYAN"]; } }
+ public static Color DarkGoldenrod { get { return namedColors["DARKGOLDENROD"]; } }
+ public static Color DarkGray { get { return namedColors["DARKGRAY"]; } }
+ public static Color DarkGreen { get { return namedColors["DARKGREEN"]; } }
+ public static Color DarkKhaki { get { return namedColors["DARKKHAKI"]; } }
+ public static Color DarkMagenta { get { return namedColors["DARKMAGENTA"]; } }
+ public static Color DarkOliveGreen { get { return namedColors["DARKOLIVEGREEN"]; } }
+ public static Color DarkOrange { get { return namedColors["DARKORANGE"]; } }
+ public static Color DarkOrchid { get { return namedColors["DARKORCHID"]; } }
+ public static Color DarkRed { get { return namedColors["DARKRED"]; } }
+ public static Color DarkSalmon { get { return namedColors["DARKSALMON"]; } }
+ public static Color DarkSeaGreen { get { return namedColors["DARKSEAGREEN"]; } }
+ public static Color DarkSlateBlue { get { return namedColors["DARKSLATEBLUE"]; } }
+ public static Color DarkSlateGray { get { return namedColors["DARKSLATEGRAY"]; } }
+ public static Color DarkTurquoise { get { return namedColors["DARKTURQUOISE"]; } }
+ public static Color DarkViolet { get { return namedColors["DARKVIOLET"]; } }
+ public static Color DeepPink { get { return namedColors["DEEPPINK"]; } }
+ public static Color DeepSkyBlue { get { return namedColors["DEEPSKYBLUE"]; } }
+ public static Color DimGray { get { return namedColors["DIMGRAY"]; } }
+ public static Color DodgerBlue { get { return namedColors["DODGERBLUE"]; } }
+ public static Color Firebrick { get { return namedColors["FIREBRICK"]; } }
+ public static Color FloralWhite { get { return namedColors["FLORALWHITE"]; } }
+ public static Color ForestGreen { get { return namedColors["FORESTGREEN"]; } }
+ public static Color Fuchsia { get { return namedColors["FUCHSIA"]; } }
+ public static Color Gainsboro { get { return namedColors["GAINSBORO"]; } }
+ public static Color GhostWhite { get { return namedColors["GHOSTWHITE"]; } }
+ public static Color Gold { get { return namedColors["GOLD"]; } }
+ public static Color Goldenrod { get { return namedColors["GOLDENROD"]; } }
+ public static Color Gray { get { return namedColors["GRAY"]; } }
+ public static Color Green { get { return namedColors["GREEN"]; } }
+ public static Color GreenYellow { get { return namedColors["GREENYELLOW"]; } }
+ public static Color Honeydew { get { return namedColors["HONEYDEW"]; } }
+ public static Color HotPink { get { return namedColors["HOTPINK"]; } }
+ public static Color IndianRed { get { return namedColors["INDIANRED"]; } }
+ public static Color Indigo { get { return namedColors["INDIGO"]; } }
+ public static Color Ivory { get { return namedColors["IVORY"]; } }
+ public static Color Khaki { get { return namedColors["KHAKI"]; } }
+ public static Color Lavender { get { return namedColors["LAVENDER"]; } }
+ public static Color LavenderBlush { get { return namedColors["LAVENDERBLUSH"]; } }
+ public static Color LawnGreen { get { return namedColors["LAWNGREEN"]; } }
+ public static Color LemonChiffon { get { return namedColors["LEMONCHIFFON"]; } }
+ public static Color LightBlue { get { return namedColors["LIGHTBLUE"]; } }
+ public static Color LightCoral { get { return namedColors["LIGHTCORAL"]; } }
+ public static Color LightCyan { get { return namedColors["LIGHTCYAN"]; } }
+ public static Color LightGoldenrod { get { return namedColors["LIGHTGOLDENROD"]; } }
+ public static Color LightGray { get { return namedColors["LIGHTGRAY"]; } }
+ public static Color LightGreen { get { return namedColors["LIGHTGREEN"]; } }
+ public static Color LightPink { get { return namedColors["LIGHTPINK"]; } }
+ public static Color LightSalmon { get { return namedColors["LIGHTSALMON"]; } }
+ public static Color LightSeaGreen { get { return namedColors["LIGHTSEAGREEN"]; } }
+ public static Color LightSkyBlue { get { return namedColors["LIGHTSKYBLUE"]; } }
+ public static Color LightSlateGray { get { return namedColors["LIGHTSLATEGRAY"]; } }
+ public static Color LightSteelBlue { get { return namedColors["LIGHTSTEELBLUE"]; } }
+ public static Color LightYellow { get { return namedColors["LIGHTYELLOW"]; } }
+ public static Color Lime { get { return namedColors["LIME"]; } }
+ public static Color LimeGreen { get { return namedColors["LIMEGREEN"]; } }
+ public static Color Linen { get { return namedColors["LINEN"]; } }
+ public static Color Magenta { get { return namedColors["MAGENTA"]; } }
+ public static Color Maroon { get { return namedColors["MAROON"]; } }
+ public static Color MediumAquamarine { get { return namedColors["MEDIUMAQUAMARINE"]; } }
+ public static Color MediumBlue { get { return namedColors["MEDIUMBLUE"]; } }
+ public static Color MediumOrchid { get { return namedColors["MEDIUMORCHID"]; } }
+ public static Color MediumPurple { get { return namedColors["MEDIUMPURPLE"]; } }
+ public static Color MediumSeaGreen { get { return namedColors["MEDIUMSEAGREEN"]; } }
+ public static Color MediumSlateBlue { get { return namedColors["MEDIUMSLATEBLUE"]; } }
+ public static Color MediumSpringGreen { get { return namedColors["MEDIUMSPRINGGREEN"]; } }
+ public static Color MediumTurquoise { get { return namedColors["MEDIUMTURQUOISE"]; } }
+ public static Color MediumVioletRed { get { return namedColors["MEDIUMVIOLETRED"]; } }
+ public static Color MidnightBlue { get { return namedColors["MIDNIGHTBLUE"]; } }
+ public static Color MintCream { get { return namedColors["MINTCREAM"]; } }
+ public static Color MistyRose { get { return namedColors["MISTYROSE"]; } }
+ public static Color Moccasin { get { return namedColors["MOCCASIN"]; } }
+ public static Color NavajoWhite { get { return namedColors["NAVAJOWHITE"]; } }
+ public static Color NavyBlue { get { return namedColors["NAVYBLUE"]; } }
+ public static Color OldLace { get { return namedColors["OLDLACE"]; } }
+ public static Color Olive { get { return namedColors["OLIVE"]; } }
+ public static Color OliveDrab { get { return namedColors["OLIVEDRAB"]; } }
+ public static Color Orange { get { return namedColors["ORANGE"]; } }
+ public static Color OrangeRed { get { return namedColors["ORANGERED"]; } }
+ public static Color Orchid { get { return namedColors["ORCHID"]; } }
+ public static Color PaleGoldenrod { get { return namedColors["PALEGOLDENROD"]; } }
+ public static Color PaleGreen { get { return namedColors["PALEGREEN"]; } }
+ public static Color PaleTurquoise { get { return namedColors["PALETURQUOISE"]; } }
+ public static Color PaleVioletRed { get { return namedColors["PALEVIOLETRED"]; } }
+ public static Color PapayaWhip { get { return namedColors["PAPAYAWHIP"]; } }
+ public static Color PeachPuff { get { return namedColors["PEACHPUFF"]; } }
+ public static Color Peru { get { return namedColors["PERU"]; } }
+ public static Color Pink { get { return namedColors["PINK"]; } }
+ public static Color Plum { get { return namedColors["PLUM"]; } }
+ public static Color PowderBlue { get { return namedColors["POWDERBLUE"]; } }
+ public static Color Purple { get { return namedColors["PURPLE"]; } }
+ public static Color RebeccaPurple { get { return namedColors["REBECCAPURPLE"]; } }
+ public static Color Red { get { return namedColors["RED"]; } }
+ public static Color RosyBrown { get { return namedColors["ROSYBROWN"]; } }
+ public static Color RoyalBlue { get { return namedColors["ROYALBLUE"]; } }
+ public static Color SaddleBrown { get { return namedColors["SADDLEBROWN"]; } }
+ public static Color Salmon { get { return namedColors["SALMON"]; } }
+ public static Color SandyBrown { get { return namedColors["SANDYBROWN"]; } }
+ public static Color SeaGreen { get { return namedColors["SEAGREEN"]; } }
+ public static Color Seashell { get { return namedColors["SEASHELL"]; } }
+ public static Color Sienna { get { return namedColors["SIENNA"]; } }
+ public static Color Silver { get { return namedColors["SILVER"]; } }
+ public static Color SkyBlue { get { return namedColors["SKYBLUE"]; } }
+ public static Color SlateBlue { get { return namedColors["SLATEBLUE"]; } }
+ public static Color SlateGray { get { return namedColors["SLATEGRAY"]; } }
+ public static Color Snow { get { return namedColors["SNOW"]; } }
+ public static Color SpringGreen { get { return namedColors["SPRINGGREEN"]; } }
+ public static Color SteelBlue { get { return namedColors["STEELBLUE"]; } }
+ public static Color Tan { get { return namedColors["TAN"]; } }
+ public static Color Teal { get { return namedColors["TEAL"]; } }
+ public static Color Thistle { get { return namedColors["THISTLE"]; } }
+ public static Color Tomato { get { return namedColors["TOMATO"]; } }
+ public static Color Transparent { get { return namedColors["TRANSPARENT"]; } }
+ public static Color Turquoise { get { return namedColors["TURQUOISE"]; } }
+ public static Color Violet { get { return namedColors["VIOLET"]; } }
+ public static Color WebGray { get { return namedColors["WEBGRAY"]; } }
+ public static Color WebGreen { get { return namedColors["WEBGREEN"]; } }
+ public static Color WebMaroon { get { return namedColors["WEBMAROON"]; } }
+ public static Color WebPurple { get { return namedColors["WEBPURPLE"]; } }
+ public static Color Wheat { get { return namedColors["WHEAT"]; } }
+ public static Color White { get { return namedColors["WHITE"]; } }
+ 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/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index 785e87a043..1dca9e6ea7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -61,7 +61,7 @@ namespace Godot
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
- writer.Write((ulong) TargetKind.Static);
+ writer.Write((ulong)TargetKind.Static);
SerializeType(writer, @delegate.GetType());
@@ -77,8 +77,8 @@ namespace Godot
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
- writer.Write((ulong) TargetKind.GodotObject);
- writer.Write((ulong) godotObject.GetInstanceId());
+ writer.Write((ulong)TargetKind.GodotObject);
+ writer.Write((ulong)godotObject.GetInstanceId());
SerializeType(writer, @delegate.GetType());
@@ -100,7 +100,7 @@ namespace Godot
using (var stream = new MemoryStream())
using (var writer = new BinaryWriter(stream))
{
- writer.Write((ulong) TargetKind.CompilerGenerated);
+ writer.Write((ulong)TargetKind.CompilerGenerated);
SerializeType(writer, targetType);
SerializeType(writer, @delegate.GetType());
@@ -149,14 +149,14 @@ namespace Godot
int flags = 0;
if (methodInfo.IsPublic)
- flags |= (int) BindingFlags.Public;
+ flags |= (int)BindingFlags.Public;
else
- flags |= (int) BindingFlags.NonPublic;
+ flags |= (int)BindingFlags.NonPublic;
if (methodInfo.IsStatic)
- flags |= (int) BindingFlags.Static;
+ flags |= (int)BindingFlags.Static;
else
- flags |= (int) BindingFlags.Instance;
+ flags |= (int)BindingFlags.Instance;
writer.Write(flags);
@@ -238,7 +238,7 @@ namespace Godot
}
else
{
- if (TryDeserializeSingleDelegate((byte[]) elem, out Delegate oneDelegate))
+ if (TryDeserializeSingleDelegate((byte[])elem, out Delegate oneDelegate))
delegates.Add(oneDelegate);
}
}
@@ -257,7 +257,7 @@ namespace Godot
using (var stream = new MemoryStream(buffer, writable: false))
using (var reader = new BinaryReader(stream))
{
- var targetKind = (TargetKind) reader.ReadUInt64();
+ var targetKind = (TargetKind)reader.ReadUInt64();
switch (targetKind)
{
@@ -353,11 +353,11 @@ namespace Godot
parameterTypes[i] = parameterType;
}
- methodInfo = declaringType.GetMethod(methodName, (BindingFlags) flags, null, parameterTypes, null);
+ methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags, null, parameterTypes, null);
return methodInfo != null && methodInfo.ReturnType == returnType;
}
- methodInfo = declaringType.GetMethod(methodName, (BindingFlags) flags);
+ methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags);
return methodInfo != null && methodInfo.ReturnType == returnType;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
index 213fc181c1..75240b0c09 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs
@@ -3,10 +3,11 @@ using System.Collections.Generic;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Diagnostics.CodeAnalysis;
namespace Godot.Collections
{
- class DictionarySafeHandle : SafeHandle
+ internal class DictionarySafeHandle : SafeHandle
{
public DictionarySafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
{
@@ -25,18 +26,29 @@ namespace Godot.Collections
}
}
- public class Dictionary :
- IDictionary,
- IDisposable
+ /// <summary>
+ /// Wrapper around Godot's Dictionary class, a dictionary of Variant
+ /// typed elements allocated in the engine in C++. Useful when
+ /// interfacing with the engine.
+ /// </summary>
+ 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>
+ /// Constructs a new <see cref="Dictionary"/> from the given dictionary's elements.
+ /// </summary>
+ /// <param name="dictionary">The dictionary to construct from.</param>
+ /// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary dictionary) : this()
{
if (dictionary == null)
@@ -48,36 +60,44 @@ 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>
+ /// Disposes of this <see cref="Dictionary"/>.
+ /// </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>
+ /// Duplicates this <see cref="Dictionary"/>.
+ /// </summary>
+ /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
+ /// <returns>A new Godot Dictionary.</returns>
public Dictionary Duplicate(bool deep = false)
{
return new Dictionary(godot_icall_Dictionary_Duplicate(GetPtr(), deep));
@@ -85,6 +105,9 @@ namespace Godot.Collections
// IDictionary
+ /// <summary>
+ /// Gets the collection of keys in this <see cref="Dictionary"/>.
+ /// </summary>
public ICollection Keys
{
get
@@ -94,6 +117,9 @@ namespace Godot.Collections
}
}
+ /// <summary>
+ /// Gets the collection of elements in this <see cref="Dictionary"/>.
+ /// </summary>
public ICollection Values
{
get
@@ -103,47 +129,88 @@ namespace Godot.Collections
}
}
- public bool IsFixedSize => false;
+ private (Array keys, Array values, int count) GetKeyValuePairs()
+ {
+ int count = godot_icall_Dictionary_KeyValuePairs(GetPtr(), out IntPtr keysHandle, out IntPtr valuesHandle);
+ Array keys = new Array(new ArraySafeHandle(keysHandle));
+ Array values = new Array(new ArraySafeHandle(valuesHandle));
+ return (keys, values, count);
+ }
+
+ bool IDictionary.IsFixedSize => false;
- public bool IsReadOnly => false;
+ bool IDictionary.IsReadOnly => false;
+ /// <summary>
+ /// Returns the object at the given <paramref name="key"/>.
+ /// </summary>
+ /// <value>The object at the given <paramref name="key"/>.</value>
public object this[object key]
{
get => godot_icall_Dictionary_GetValue(GetPtr(), key);
set => godot_icall_Dictionary_SetValue(GetPtr(), key, value);
}
+ /// <summary>
+ /// Adds an object <paramref name="value"/> at key <paramref name="key"/>
+ /// to this <see cref="Dictionary"/>.
+ /// </summary>
+ /// <param name="key">The key at which to add the object.</param>
+ /// <param name="value">The object to add.</param>
public void Add(object key, object value) => godot_icall_Dictionary_Add(GetPtr(), key, value);
+ /// <summary>
+ /// Erases all items from this <see cref="Dictionary"/>.
+ /// </summary>
public void Clear() => godot_icall_Dictionary_Clear(GetPtr());
+ /// <summary>
+ /// Checks if this <see cref="Dictionary"/> contains the given key.
+ /// </summary>
+ /// <param name="key">The key to look for.</param>
+ /// <returns>Whether or not this dictionary contains the given key.</returns>
public bool Contains(object key) => godot_icall_Dictionary_ContainsKey(GetPtr(), key);
+ /// <summary>
+ /// Gets an enumerator for this <see cref="Dictionary"/>.
+ /// </summary>
+ /// <returns>An enumerator.</returns>
public IDictionaryEnumerator GetEnumerator() => new DictionaryEnumerator(this);
+ /// <summary>
+ /// Removes an element from this <see cref="Dictionary"/> by key.
+ /// </summary>
+ /// <param name="key">The key of the element to remove.</param>
public void Remove(object key) => godot_icall_Dictionary_RemoveKey(GetPtr(), key);
// ICollection
- public object SyncRoot => this;
+ object ICollection.SyncRoot => this;
- public bool IsSynchronized => false;
+ bool ICollection.IsSynchronized => false;
+ /// <summary>
+ /// Returns the number of elements in this <see cref="Dictionary"/>.
+ /// This is also known as the size or length of the dictionary.
+ /// </summary>
+ /// <returns>The number of elements.</returns>
public int Count => godot_icall_Dictionary_Count(GetPtr());
+ /// <summary>
+ /// Copies the elements of this <see cref="Dictionary"/> to the given
+ /// untyped C# array, starting at the given index.
+ /// </summary>
+ /// <param name="array">The array to copy to.</param>
+ /// <param name="index">The index to start at.</param>
public void CopyTo(System.Array array, int index)
{
- // TODO Can be done with single internal call
-
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), "Number was less than the array's lower bound in the first dimension.");
- Array keys = (Array)Keys;
- Array values = (Array)Values;
- int count = Count;
+ var (keys, values, count) = GetKeyValuePairs();
if (array.Length < (index + count))
throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
@@ -161,24 +228,39 @@ namespace Godot.Collections
private class DictionaryEnumerator : IDictionaryEnumerator
{
- Array keys;
- Array values;
- int count;
- int index = -1;
+ private readonly Dictionary _dictionary;
+ private readonly int _count;
+ private int _index = -1;
+ private bool _dirty = true;
+
+ private DictionaryEntry _entry;
public DictionaryEnumerator(Dictionary dictionary)
{
- // TODO 3 internal calls, can reduce to 1
- keys = (Array)dictionary.Keys;
- values = (Array)dictionary.Values;
- count = dictionary.Count;
+ _dictionary = dictionary;
+ _count = dictionary.Count;
}
public object Current => Entry;
- public DictionaryEntry Entry =>
- // TODO 2 internal calls, can reduce to 1
- new DictionaryEntry(keys[index], values[index]);
+ public DictionaryEntry Entry
+ {
+ get
+ {
+ if (_dirty)
+ {
+ UpdateEntry();
+ }
+ 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);
+ }
public object Key => Entry.Key;
@@ -186,83 +268,102 @@ namespace Godot.Collections
public bool MoveNext()
{
- index++;
- return index < count;
+ _index++;
+ _dirty = true;
+ return _index < _count;
}
public void Reset()
{
- index = -1;
+ _index = -1;
+ _dirty = true;
}
}
+ /// <summary>
+ /// Converts this <see cref="Dictionary"/> to a string.
+ /// </summary>
+ /// <returns>A string representation of this dictionary.</returns>
public override string ToString()
{
return godot_icall_Dictionary_ToString(GetPtr());
}
[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 void godot_icall_Dictionary_Add(IntPtr ptr, object key, object value);
+ 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_Clear(IntPtr ptr);
+ internal static extern void godot_icall_Dictionary_KeyValuePairAt(IntPtr ptr, int index, out object key, out object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Dictionary_Contains(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 bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
+ internal static extern void godot_icall_Dictionary_Clear(IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep);
+ internal static extern bool godot_icall_Dictionary_Contains(IntPtr ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Dictionary_RemoveKey(IntPtr ptr, object key);
+ internal static extern bool godot_icall_Dictionary_ContainsKey(IntPtr ptr, object key);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static bool godot_icall_Dictionary_Remove(IntPtr ptr, object key, object value);
+ internal static extern IntPtr godot_icall_Dictionary_Duplicate(IntPtr ptr, bool deep);
[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_RemoveKey(IntPtr ptr, object key);
[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_Remove(IntPtr ptr, object key, object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass);
+ internal static extern bool godot_icall_Dictionary_TryGetValue(IntPtr ptr, object key, out object value);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static string godot_icall_Dictionary_ToString(IntPtr ptr);
+ internal static extern bool godot_icall_Dictionary_TryGetValue_Generic(IntPtr ptr, object key, out object value, int valTypeEncoding, IntPtr valTypeClass);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern void godot_icall_Dictionary_Generic_GetValueTypeInfo(Type valueType, out int valTypeEncoding, out IntPtr valTypeClass);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern string godot_icall_Dictionary_ToString(IntPtr ptr);
}
- public class Dictionary<TKey, TValue> :
- IDictionary<TKey, TValue>
+ /// <summary>
+ /// Typed wrapper around Godot's Dictionary class, a dictionary of Variant
+ /// typed elements allocated in the engine in C++. Useful when
+ /// interfacing with the engine. Otherwise prefer .NET collections
+ /// such as <see cref="System.Collections.Generic.Dictionary{TKey, TValue}"/>.
+ /// </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>
{
- Dictionary objectDict;
+ private readonly Dictionary _objectDict;
internal static int valTypeEncoding;
internal static IntPtr valTypeClass;
@@ -272,14 +373,22 @@ namespace Godot.Collections
Dictionary.godot_icall_Dictionary_Generic_GetValueTypeInfo(typeof(TValue), out valTypeEncoding, out valTypeClass);
}
+ /// <summary>
+ /// Constructs a new empty <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
public Dictionary()
{
- objectDict = new Dictionary();
+ _objectDict = new Dictionary();
}
+ /// <summary>
+ /// Constructs a new <see cref="Dictionary{TKey, TValue}"/> from the given dictionary's elements.
+ /// </summary>
+ /// <param name="dictionary">The dictionary to construct from.</param>
+ /// <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.'");
@@ -294,112 +403,173 @@ namespace Godot.Collections
}
}
+ /// <summary>
+ /// Constructs a new <see cref="Dictionary{TKey, TValue}"/> from the given dictionary's elements.
+ /// </summary>
+ /// <param name="dictionary">The dictionary to construct from.</param>
+ /// <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>
+ /// Converts this typed <see cref="Dictionary{TKey, TValue}"/> to an untyped <see cref="Dictionary"/>.
+ /// </summary>
+ /// <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>
+ /// Duplicates this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
+ /// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
+ /// <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>
+ /// <summary>
+ /// Returns the value at the given <paramref name="key"/>.
+ /// </summary>
+ /// <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>
+ /// Gets the collection of keys in this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
public ICollection<TKey> Keys
{
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));
}
}
+ /// <summary>
+ /// Gets the collection of elements in this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
public ICollection<TValue> Values
{
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));
}
}
+ private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
+ {
+ Dictionary.godot_icall_Dictionary_KeyValuePairAt(GetPtr(), index, out object key, out object value);
+ return new KeyValuePair<TKey, TValue>((TKey)key, (TValue)value);
+ }
+
+ /// <summary>
+ /// Adds an object <paramref name="value"/> at key <paramref name="key"/>
+ /// to this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
+ /// <param name="key">The key at which to add the object.</param>
+ /// <param name="value">The object to add.</param>
public void Add(TKey key, TValue value)
{
- objectDict.Add(key, value);
+ _objectDict.Add(key, value);
}
+ /// <summary>
+ /// Checks if this <see cref="Dictionary{TKey, TValue}"/> contains the given key.
+ /// </summary>
+ /// <param name="key">The key to look for.</param>
+ /// <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>
+ /// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key.
+ /// </summary>
+ /// <param name="key">The key of the element to remove.</param>
public bool Remove(TKey key)
{
return Dictionary.godot_icall_Dictionary_RemoveKey(GetPtr(), key);
}
- public bool TryGetValue(TKey key, out TValue value)
+ /// <summary>
+ /// Gets the object at the given <paramref name="key"/>.
+ /// </summary>
+ /// <param name="key">The key of the element to get.</param>
+ /// <param name="value">The value at the given <paramref name="key"/>.</param>
+ /// <returns>If an object was found for the given <paramref name="key"/>.</returns>
+ public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
- object retValue;
- bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out retValue, valTypeEncoding, valTypeClass);
- value = found ? (TValue)retValue : default(TValue);
+ bool found = Dictionary.godot_icall_Dictionary_TryGetValue_Generic(GetPtr(), key, out object retValue, valTypeEncoding, valTypeClass);
+ value = found ? (TValue)retValue : default;
return found;
}
// ICollection<KeyValuePair<TKey, TValue>>
+ /// <summary>
+ /// Returns the number of elements in this <see cref="Dictionary{TKey, TValue}"/>.
+ /// This is also known as the size or length of the dictionary.
+ /// </summary>
+ /// <returns>The number of elements.</returns>
public int Count
{
- get { return objectDict.Count; }
+ get { return _objectDict.Count; }
}
- public bool IsReadOnly
- {
- get { return objectDict.IsReadOnly; }
- }
+ bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
- public void Add(KeyValuePair<TKey, TValue> item)
+ void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
- objectDict.Add(item.Key, item.Value);
+ _objectDict.Add(item.Key, item.Value);
}
+ /// <summary>
+ /// Erases all the items from this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
public void Clear()
{
- objectDict.Clear();
+ _objectDict.Clear();
}
- public bool Contains(KeyValuePair<TKey, TValue> item)
+ 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>
+ /// Copies the elements of this <see cref="Dictionary{TKey, TValue}"/> to the given
+ /// untyped C# array, starting at the given index.
+ /// </summary>
+ /// <param name="array">The array to copy to.</param>
+ /// <param name="arrayIndex">The index to start at.</param>
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
if (array == null)
@@ -408,9 +578,6 @@ namespace Godot.Collections
if (arrayIndex < 0)
throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Number was less than the array's lower bound in the first dimension.");
- // TODO 3 internal calls, can reduce to 1
- Array<TKey> keys = (Array<TKey>)Keys;
- Array<TValue> values = (Array<TValue>)Values;
int count = Count;
if (array.Length < (arrayIndex + count))
@@ -418,13 +585,12 @@ namespace Godot.Collections
for (int i = 0; i < count; i++)
{
- // TODO 2 internal calls, can reduce to 1
- array[arrayIndex] = new KeyValuePair<TKey, TValue>(keys[i], values[i]);
+ array[arrayIndex] = GetKeyValuePair(i);
arrayIndex++;
}
}
- public bool Remove(KeyValuePair<TKey, TValue> item)
+ bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return Dictionary.godot_icall_Dictionary_Remove(GetPtr(), item.Key, item.Value);
;
@@ -432,17 +598,15 @@ namespace Godot.Collections
// IEnumerable<KeyValuePair<TKey, TValue>>
+ /// <summary>
+ /// Gets an enumerator for this <see cref="Dictionary{TKey, TValue}"/>.
+ /// </summary>
+ /// <returns>An enumerator.</returns>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
- // TODO 3 internal calls, can reduce to 1
- Array<TKey> keys = (Array<TKey>)Keys;
- Array<TValue> values = (Array<TValue>)Values;
- int count = Count;
-
- for (int i = 0; i < count; i++)
+ for (int i = 0; i < Count; i++)
{
- // TODO 2 internal calls, can reduce to 1
- yield return new KeyValuePair<TKey, TValue>(keys[i], values[i]);
+ yield return GetKeyValuePair(i);
}
}
@@ -451,6 +615,10 @@ namespace Godot.Collections
return GetEnumerator();
}
- public override string ToString() => objectDict.ToString();
+ /// <summary>
+ /// 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();
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
index c4c911b863..26d5f9c796 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DynamicObject.cs
@@ -1,4 +1,3 @@
-
using System;
using System.Collections.Generic;
using System.Dynamic;
@@ -8,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);
@@ -31,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;
///
@@ -55,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)
@@ -122,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))
@@ -140,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)
@@ -153,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)
@@ -176,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
new file mode 100644
index 0000000000..435b59d5f3
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/PackedSceneExtensions.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Godot
+{
+ public partial class PackedScene
+ {
+ /// <summary>
+ /// Instantiates the scene's node hierarchy, erroring on failure.
+ /// Triggers child scene instantiation(s). Triggers a
+ /// <see cref="Node.NotificationInstanced"/> notification on the root node.
+ /// </summary>
+ /// <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 <see langword="null"/> on failure.
+ /// Triggers child scene instantiation(s). Triggers a
+ /// <see cref="Node.NotificationInstanced"/> notification on the root node.
+ /// </summary>
+ /// <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 5f64c09a89..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, bool noCache = false) 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 e050d1fdd1..a3afc83222 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GD.cs
@@ -1,152 +1,441 @@
-using System;
-using System.Collections.Generic;
-using System.Runtime.CompilerServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
-
#endif
-
-// TODO: Add comments describing what this class does. It is not obvious.
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
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);
+ return (real_t)Math.Exp(db * 0.11512925464970228420089957273422);
}
- public static real_t DecTime(real_t value, real_t amount, real_t step)
+ private static object[] GetPrintParams(object[] parameters)
{
- real_t sgn = Mathf.Sign(value);
- real_t val = Mathf.Abs(value);
- val -= amount * step;
- if (val < 0)
- val = 0;
- return val * sgn;
- }
+ if (parameters == null)
+ {
+ return new[] { "null" };
+ }
- public static FuncRef FuncRef(Object instance, StringName funcName)
- {
- var ret = new FuncRef();
- ret.SetInstance(instance);
- ret.SetFunction(funcName);
- return ret;
+ 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);
- }
-
+ 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(Array.ConvertAll(what ?? new object[]{"null"}, x => x != null ? x.ToString() : "null"));
+ 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(Array.ConvertAll(what ?? new object[]{"null"}, x => x != null ? x.ToString() : "null"));
- }
-
+ 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(Array.ConvertAll(what ?? new object[]{"null"}, x => x != null ? x.ToString() : "null"));
+ 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(Array.ConvertAll(what ?? new object[]{"null"}, x => x != null ? x.ToString() : "null"));
+ 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(Array.ConvertAll(what ?? new object[]{"null"}, x => x != null ? x.ToString() : "null"));
+ 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_rand_range(from, to);
+ return godot_icall_GD_randf_range(from, to);
}
- public static uint RandSeed(ulong seed, out ulong newSeed)
+ /// <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_rand_seed(seed, out newSeed);
+ 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)
@@ -167,107 +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 extern static object godot_icall_GD_convert(object what, Variant.Type type);
+ internal static extern object godot_icall_GD_convert(object what, Variant.Type type);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static int godot_icall_GD_hash(object var);
+ internal static extern int godot_icall_GD_hash(object var);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static Object godot_icall_GD_instance_from_id(ulong instanceId);
+ internal static extern Object godot_icall_GD_instance_from_id(ulong instanceId);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_print(object[] what);
+ internal static extern void godot_icall_GD_print(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_printerr(object[] what);
+ internal static extern void godot_icall_GD_printerr(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_printraw(object[] what);
+ internal static extern void godot_icall_GD_printraw(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_prints(object[] what);
+ internal static extern void godot_icall_GD_prints(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_printt(object[] what);
+ internal static extern void godot_icall_GD_printt(object[] what);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static float godot_icall_GD_randf();
+ internal static extern void godot_icall_GD_randomize();
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static uint godot_icall_GD_randi();
+ internal static extern uint godot_icall_GD_randi();
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static void godot_icall_GD_randomize();
+ internal static extern float godot_icall_GD_randf();
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern int godot_icall_GD_randi_range(int from, int to);
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ internal static extern double godot_icall_GD_randf_range(double from, double to);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal extern static double godot_icall_GD_rand_range(double from, double 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 f1a00ae0fa..9ccac1faaf 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs
@@ -1,4 +1,3 @@
-using System;
using System.Diagnostics;
namespace Godot
@@ -25,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/GodotUnhandledExceptionEvent.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs
new file mode 100644
index 0000000000..729529d093
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotUnhandledExceptionEvent.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace Godot
+{
+ public static partial class GD
+ {
+ /// <summary>
+ /// Fires when an unhandled exception occurs, regardless of project settings.
+ /// </summary>
+ public static event EventHandler<UnhandledExceptionArgs> UnhandledException;
+
+ private static void OnUnhandledException(Exception e)
+ {
+ UnhandledException?.Invoke(null, new UnhandledExceptionArgs(e));
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
index c59d083080..3051bcedc7 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MarshalUtils.cs
@@ -1,66 +1,137 @@
using System;
-using System.Collections;
using System.Collections.Generic;
namespace Godot
{
- using Array = Godot.Collections.Array;
- using Dictionary = Godot.Collections.Dictionary;
-
- 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<>);
- 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="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 TypeIsGenericIEnumerable(Type type) => type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
+ /// <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 TypeIsGenericICollection(Type type) => type.GetGenericTypeDefinition() == typeof(ICollection<>);
+ /// <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 TypeIsGenericIDictionary(Type type) => type.GetGenericTypeDefinition() == typeof(IDictionary<,>);
+ /// <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 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 6eecc262d6..fbc8ff64a6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs
@@ -1,12 +1,15 @@
-using System;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+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.
@@ -14,122 +17,125 @@ namespace Godot
/// <summary>
/// The circle constant, the circumference of the unit circle in radians.
/// </summary>
- public const real_t Tau = (real_t) 6.2831853071795864769252867666M; // 6.2831855f and 6.28318530717959
+ // 6.2831855f and 6.28318530717959
+ public const real_t Tau = (real_t)6.2831853071795864769252867666M;
/// <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>
- public const real_t Pi = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979
+ // 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;
- private const real_t Deg2RadConst = (real_t) 0.0174532925199432957692369077M; // 0.0174532924f and 0.0174532925199433
- private const real_t Rad2DegConst = (real_t) 57.295779513082320876798154814M; // 57.29578f and 57.2957795130823
+ // 0.0174532924f and 0.0174532925199433
+ private const real_t _deg2RadConst = (real_t)0.0174532925199432957692369077M;
+ // 57.29578f and 57.2957795130823
+ 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>
@@ -141,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>
@@ -153,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>
@@ -163,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>
@@ -179,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)
{
@@ -218,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;
@@ -226,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);
@@ -259,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.
@@ -282,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;
@@ -324,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>
@@ -341,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>
@@ -350,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);
@@ -401,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>
@@ -411,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>
@@ -432,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)
{
@@ -461,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)
{
@@ -477,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);
@@ -494,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>
@@ -509,29 +509,33 @@ 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) return 0;
+ if (s == 0)
+ return 0;
return s < 0 ? -1 : 1;
}
/// <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) return 0;
+ if (s == 0)
+ return 0;
return s < 0 ? -1 : 1;
}
/// <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>
@@ -541,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>
@@ -551,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>
@@ -566,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);
@@ -590,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,
@@ -601,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++)
{
@@ -614,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 stepify.</param>
+ /// <param name="s">The value to snap.</param>
/// <param name="step">The step size to snap to.</param>
/// <returns></returns>
- public static real_t Stepify(real_t s, real_t step)
+ 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>
@@ -642,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>
@@ -652,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>
@@ -663,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>
@@ -678,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 c2f4701b5f..9bb73ce7dd 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/MathfEx.cs
@@ -1,10 +1,9 @@
-using System;
-
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+using System;
namespace Godot
{
@@ -13,18 +12,18 @@ 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
+ public const real_t E = (real_t)2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045
/// <summary>
/// The square root of 2.
/// </summary>
- public const real_t Sqrt2 = (real_t) 1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095
+ public const real_t Sqrt2 = (real_t)1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095
/// <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.
@@ -45,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 cref="decimal"/> value.</param>
/// <returns>The amount of digits.</returns>
public static int DecimalCount(decimal s)
{
@@ -53,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 <c>int</c>.
/// </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 <c>int</c>.
/// </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 <c>int</c>.
/// </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..f53b5dc904 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"></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 42610c5ef7..746612477d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Object.base.cs
@@ -5,24 +5,26 @@ 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)
- {
ptr = godot_icall_Object_Ctor(this);
- }
- else
- {
- // This is called inside godot_icall_Object_Ctor, so we must call it as well in this case.
- godot_icall_Object_ConnectEventSignals(ptr);
- }
+ _InitializeGodotScriptInstanceInternals();
+ }
+
+ internal void _InitializeGodotScriptInstanceInternals()
+ {
+ godot_icall_Object_ConnectEventSignals(ptr);
}
internal Object(bool memoryOwn)
@@ -30,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; }
@@ -40,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;
@@ -51,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)
@@ -67,26 +78,30 @@ namespace Godot
if (memoryOwn)
{
memoryOwn = false;
- godot_icall_Reference_Disposed(this, ptr, !disposing);
+ godot_icall_RefCounted_Disposed(this, ptr, !disposing);
}
else
{
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">
@@ -102,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);
@@ -130,7 +149,7 @@ namespace Godot
internal static extern void godot_icall_Object_Disposed(Object obj, IntPtr ptr);
[MethodImpl(MethodImplOptions.InternalCall)]
- internal static extern void godot_icall_Reference_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
+ internal static extern void godot_icall_RefCounted_Disposed(Object obj, IntPtr ptr, bool isFinalizer);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void godot_icall_Object_ConnectEventSignals(IntPtr obj);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
index 2f8b5f297c..63af1c5892 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Plane.cs
@@ -1,10 +1,10 @@
-using System;
-using System.Runtime.InteropServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+using System;
+using System.Runtime.InteropServices;
namespace Godot
{
@@ -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);
@@ -177,17 +177,17 @@ namespace Godot
return null;
}
- return from + dir * -dist;
+ return from - (dir * dist);
}
/// <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;
@@ -206,14 +206,14 @@ namespace Godot
return null;
}
- return begin + segment * -dist;
+ return begin - (segment * dist);
}
/// <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,27 +387,31 @@ 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 String.Format("({0}, {1})", new object[]
- {
- _normal.ToString(),
- D.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 String.Format("({0}, {1})", new object[]
- {
- _normal.ToString(format),
- D.ToString(format)
- });
+ return $"{_normal.ToString(format)}, {D.ToString(format)}";
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs
deleted file mode 100644
index b33490f9cb..0000000000
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quat.cs
+++ /dev/null
@@ -1,541 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-#if REAL_T_IS_DOUBLE
-using real_t = System.Double;
-#else
-using real_t = System.Single;
-#endif
-
-namespace Godot
-{
- /// <summary>
- /// 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 Quat 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)
- /// are more efficient and robust against floating-point errors.
- /// </summary>
- [Serializable]
- [StructLayout(LayoutKind.Sequential)]
- public struct Quat : IEquatable<Quat>
- {
- /// <summary>
- /// X component of the quaternion (imaginary `i` 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).
- /// Quaternion components should usually not be manipulated directly.
- /// </summary>
- public real_t y;
-
- /// <summary>
- /// Z component of the quaternion (imaginary `k` axis part).
- /// Quaternion components should usually not be manipulated directly.
- /// </summary>
- public real_t z;
-
- /// <summary>
- /// W component of the quaternion (real part).
- /// Quaternion components should usually not be manipulated directly.
- /// </summary>
- public real_t w;
-
- /// <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>
- public real_t this[int index]
- {
- get
- {
- switch (index)
- {
- case 0:
- return x;
- case 1:
- return y;
- case 2:
- return z;
- case 3:
- return w;
- default:
- throw new IndexOutOfRangeException();
- }
- }
- set
- {
- switch (index)
- {
- case 0:
- x = value;
- break;
- case 1:
- y = value;
- break;
- case 2:
- z = value;
- break;
- case 3:
- w = value;
- break;
- default:
- throw new IndexOutOfRangeException();
- }
- }
- }
-
- /// <summary>
- /// Returns the length (magnitude) of the quaternion.
- /// </summary>
- /// <value>Equivalent to `Mathf.Sqrt(LengthSquared)`.</value>
- public real_t Length
- {
- get { return Mathf.Sqrt(LengthSquared); }
- }
-
- /// <summary>
- /// Returns the squared length (squared magnitude) of the quaternion.
- /// 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>
- public real_t LengthSquared
- {
- get { return Dot(this); }
- }
-
- /// <summary>
- /// Performs a cubic spherical interpolation between quaternions `preA`,
- /// this vector, `b`, and `postB`, by the given amount `t`.
- /// </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="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
- /// <returns>The interpolated quaternion.</returns>
- public Quat CubicSlerp(Quat b, Quat preA, Quat postB, real_t t)
- {
- real_t t2 = (1.0f - t) * t * 2f;
- Quat sp = Slerp(b, t);
- Quat sq = preA.Slerpni(postB, t);
- return sp.Slerpni(sq, t2);
- }
-
- /// <summary>
- /// Returns the dot product of two quaternions.
- /// </summary>
- /// <param name="b">The other quaternion.</param>
- /// <returns>The dot product.</returns>
- public real_t Dot(Quat b)
- {
- return x * b.x + y * b.y + z * b.z + w * b.w;
- }
-
- /// <summary>
- /// Returns Euler angles (in the YXZ convention: when decomposing,
- /// first Z, then X, and Y last) corresponding to the rotation
- /// represented by the unit quaternion. Returned vector contains
- /// the rotation angles in the format (X angle, Y angle, Z angle).
- /// </summary>
- /// <returns>The Euler angle representation of this quaternion.</returns>
- public Vector3 GetEuler()
- {
-#if DEBUG
- if (!IsNormalized())
- {
- throw new InvalidOperationException("Quat is not normalized");
- }
-#endif
- var basis = new Basis(this);
- return basis.GetEuler();
- }
-
- /// <summary>
- /// Returns the inverse of the quaternion.
- /// </summary>
- /// <returns>The inverse quaternion.</returns>
- public Quat Inverse()
- {
-#if DEBUG
- if (!IsNormalized())
- {
- throw new InvalidOperationException("Quat is not normalized");
- }
-#endif
- return new Quat(-x, -y, -z, w);
- }
-
- /// <summary>
- /// Returns whether the quaternion is normalized or not.
- /// </summary>
- /// <returns>A bool for whether the quaternion is normalized or not.</returns>
- public bool IsNormalized()
- {
- return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
- }
-
- /// <summary>
- /// Returns a copy of the quaternion, normalized to unit length.
- /// </summary>
- /// <returns>The normalized quaternion.</returns>
- public Quat Normalized()
- {
- return this / Length;
- }
-
- /// <summary>
- /// Returns the result of the spherical linear interpolation between
- /// this quaternion and `to` by amount `weight`.
- ///
- /// Note: Both quaternions must be normalized.
- /// </summary>
- /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
- /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
- /// <returns>The resulting quaternion of the interpolation.</returns>
- public Quat Slerp(Quat to, real_t weight)
- {
-#if DEBUG
- if (!IsNormalized())
- {
- throw new InvalidOperationException("Quat is not normalized");
- }
- if (!to.IsNormalized())
- {
- throw new ArgumentException("Argument is not normalized", nameof(to));
- }
-#endif
-
- // Calculate cosine.
- real_t cosom = x * to.x + y * to.y + z * to.z + w * to.w;
-
- var to1 = new Quat();
-
- // Adjust signs if necessary.
- if (cosom < 0.0)
- {
- cosom = -cosom;
- to1.x = -to.x;
- to1.y = -to.y;
- to1.z = -to.z;
- to1.w = -to.w;
- }
- else
- {
- to1.x = to.x;
- to1.y = to.y;
- to1.z = to.z;
- to1.w = to.w;
- }
-
- real_t sinom, scale0, scale1;
-
- // Calculate coefficients.
- if (1.0 - cosom > Mathf.Epsilon)
- {
- // Standard case (Slerp).
- real_t omega = Mathf.Acos(cosom);
- sinom = Mathf.Sin(omega);
- scale0 = Mathf.Sin((1.0f - weight) * omega) / sinom;
- scale1 = Mathf.Sin(weight * omega) / sinom;
- }
- else
- {
- // Quaternions are very close so we can do a linear interpolation.
- scale0 = 1.0f - weight;
- scale1 = weight;
- }
-
- // Calculate final values.
- return new Quat
- (
- 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
- /// checking if the rotation path is not bigger than 90 degrees.
- /// </summary>
- /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
- /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
- /// <returns>The resulting quaternion of the interpolation.</returns>
- public Quat Slerpni(Quat to, real_t weight)
- {
- real_t dot = Dot(to);
-
- if (Mathf.Abs(dot) > 0.9999f)
- {
- return this;
- }
-
- real_t theta = Mathf.Acos(dot);
- real_t sinT = 1.0f / Mathf.Sin(theta);
- real_t newFactor = Mathf.Sin(weight * theta) * sinT;
- real_t invFactor = Mathf.Sin((1.0f - weight) * theta) * sinT;
-
- return new Quat
- (
- invFactor * x + newFactor * to.x,
- invFactor * y + newFactor * to.y,
- invFactor * z + newFactor * to.z,
- invFactor * w + newFactor * to.w
- );
- }
-
- /// <summary>
- /// Returns a vector transformed (multiplied) by this quaternion.
- /// </summary>
- /// <param name="v">A vector to transform.</param>
- /// <returns>The transformed vector.</returns>
- public Vector3 Xform(Vector3 v)
- {
-#if DEBUG
- if (!IsNormalized())
- {
- throw new InvalidOperationException("Quat is not normalized");
- }
-#endif
- var u = new Vector3(x, y, z);
- Vector3 uv = u.Cross(v);
- return v + ((uv * w) + u.Cross(uv)) * 2;
- }
-
- // Constants
- private static readonly Quat _identity = new Quat(0, 0, 0, 1);
-
- /// <summary>
- /// The identity quaternion, representing no rotation.
- /// 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 Quat(0, 0, 0, 1)`.</value>
- public static Quat Identity { get { return _identity; } }
-
- /// <summary>
- /// Constructs a 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="w">W component of the quaternion (real part).</param>
- public Quat(real_t x, real_t y, real_t z, real_t w)
- {
- this.x = x;
- this.y = y;
- this.z = z;
- this.w = w;
- }
-
- /// <summary>
- /// Constructs a quaternion from the given quaternion.
- /// </summary>
- /// <param name="q">The existing quaternion.</param>
- public Quat(Quat q)
- {
- this = q;
- }
-
- /// <summary>
- /// Constructs a quaternion from the given <see cref="Basis"/>.
- /// </summary>
- /// <param name="basis">The basis to construct from.</param>
- public Quat(Basis basis)
- {
- this = basis.Quat();
- }
-
- /// <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),
- /// given in the vector format as (X angle, Y angle, Z angle).
- /// </summary>
- /// <param name="eulerYXZ"></param>
- public Quat(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;
-
- // 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);
-
- 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;
- }
-
- /// <summary>
- /// Constructs a 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>
- /// <param name="angle">The angle to rotate, in radians.</param>
- public Quat(Vector3 axis, real_t angle)
- {
-#if DEBUG
- if (!axis.IsNormalized())
- {
- throw new ArgumentException("Argument is not normalized", nameof(axis));
- }
-#endif
-
- real_t d = axis.Length();
-
- if (d == 0f)
- {
- x = 0f;
- y = 0f;
- z = 0f;
- w = 0f;
- }
- else
- {
- real_t sinAngle = Mathf.Sin(angle * 0.5f);
- real_t cosAngle = Mathf.Cos(angle * 0.5f);
- real_t s = sinAngle / d;
-
- x = axis.x * s;
- y = axis.y * s;
- z = axis.z * s;
- w = cosAngle;
- }
- }
-
- public static Quat operator *(Quat left, Quat right)
- {
- return new Quat
- (
- 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
- );
- }
-
- public static Quat operator +(Quat left, Quat right)
- {
- return new Quat(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w);
- }
-
- public static Quat operator -(Quat left, Quat right)
- {
- return new Quat(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w);
- }
-
- public static Quat operator -(Quat left)
- {
- return new Quat(-left.x, -left.y, -left.z, -left.w);
- }
-
- public static Quat operator *(Quat left, Vector3 right)
- {
- return new Quat
- (
- 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
- );
- }
-
- public static Quat operator *(Vector3 left, Quat right)
- {
- return new Quat
- (
- 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
- );
- }
-
- public static Quat operator *(Quat left, real_t right)
- {
- return new Quat(left.x * right, left.y * right, left.z * right, left.w * right);
- }
-
- public static Quat operator *(real_t left, Quat right)
- {
- return new Quat(right.x * left, right.y * left, right.z * left, right.w * left);
- }
-
- public static Quat operator /(Quat left, real_t right)
- {
- return left * (1.0f / right);
- }
-
- public static bool operator ==(Quat left, Quat right)
- {
- return left.Equals(right);
- }
-
- public static bool operator !=(Quat left, Quat right)
- {
- return !left.Equals(right);
- }
-
- public override bool Equals(object obj)
- {
- if (obj is Quat)
- {
- return Equals((Quat)obj);
- }
-
- return false;
- }
-
- public bool Equals(Quat 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.
- /// </summary>
- /// <param name="other">The other quaternion to compare.</param>
- /// <returns>Whether or not the quaternions are approximately equal.</returns>
- public bool IsEqualApprox(Quat other)
- {
- return Mathf.IsEqualApprox(x, other.x) && Mathf.IsEqualApprox(y, other.y) && Mathf.IsEqualApprox(z, other.z) && Mathf.IsEqualApprox(w, other.w);
- }
-
- public override int GetHashCode()
- {
- return y.GetHashCode() ^ x.GetHashCode() ^ z.GetHashCode() ^ w.GetHashCode();
- }
-
- public override string ToString()
- {
- return String.Format("({0}, {1}, {2}, {3})", x.ToString(), y.ToString(), z.ToString(), w.ToString());
- }
-
- public string ToString(string format)
- {
- return String.Format("({0}, {1}, {2}, {3})", x.ToString(format), y.ToString(format), z.ToString(format), w.ToString(format));
- }
- }
-}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
new file mode 100644
index 0000000000..dfb8e87bce
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs
@@ -0,0 +1,673 @@
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
+using System;
+using System.Runtime.InteropServices;
+
+namespace Godot
+{
+ /// <summary>
+ /// A unit quaternion used for representing 3D rotations.
+ /// Quaternions need to be normalized to be used for 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)
+ /// are more efficient and robust against floating-point errors.
+ /// </summary>
+ [Serializable]
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Quaternion : IEquatable<Quaternion>
+ {
+ /// <summary>
+ /// 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 <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 <c>k</c> axis part).
+ /// Quaternion components should usually not be manipulated directly.
+ /// </summary>
+ public real_t z;
+
+ /// <summary>
+ /// W component of the quaternion (real part).
+ /// Quaternion components should usually not be manipulated directly.
+ /// </summary>
+ public real_t w;
+
+ /// <summary>
+ /// Access quaternion components using their index.
+ /// </summary>
+ /// <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
+ {
+ switch (index)
+ {
+ case 0:
+ return x;
+ case 1:
+ return y;
+ case 2:
+ return z;
+ case 3:
+ return w;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ set
+ {
+ switch (index)
+ {
+ case 0:
+ x = value;
+ break;
+ case 1:
+ y = value;
+ break;
+ case 2:
+ z = value;
+ break;
+ case 3:
+ w = value;
+ break;
+ default:
+ throw new IndexOutOfRangeException();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Returns the length (magnitude) of the quaternion.
+ /// </summary>
+ /// <seealso cref="LengthSquared"/>
+ /// <value>Equivalent to <c>Mathf.Sqrt(LengthSquared)</c>.</value>
+ public real_t Length
+ {
+ get { return Mathf.Sqrt(LengthSquared); }
+ }
+
+ /// <summary>
+ /// Returns the squared length (squared magnitude) of the quaternion.
+ /// 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 <c>Dot(this)</c>.</value>
+ public real_t LengthSquared
+ {
+ get { return Dot(this); }
+ }
+
+ /// <summary>
+ /// 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.
+ ///
+ /// Note: This method has an abnormally high amount
+ /// of floating-point error, so methods such as
+ /// <see cref="Mathf.IsZeroApprox"/> will not work reliably.
+ /// </summary>
+ /// <param name="to">The other quaternion.</param>
+ /// <returns>The angle between the quaternions.</returns>
+ public real_t AngleTo(Quaternion to)
+ {
+ real_t dot = Dot(to);
+ return Mathf.Acos(Mathf.Clamp(dot * dot * 2 - 1, -1, 1));
+ }
+
+ /// <summary>
+ /// 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 <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)
+ {
+ real_t t2 = (1.0f - weight) * weight * 2f;
+ Quaternion sp = Slerp(b, weight);
+ Quaternion sq = preA.Slerpni(postB, weight);
+ return sp.Slerpni(sq, t2);
+ }
+
+ /// <summary>
+ /// Returns the dot product of two quaternions.
+ /// </summary>
+ /// <param name="b">The other quaternion.</param>
+ /// <returns>The dot product.</returns>
+ public real_t Dot(Quaternion b)
+ {
+ return (x * b.x) + (y * b.y) + (z * b.z) + (w * b.w);
+ }
+
+ /// <summary>
+ /// Returns Euler angles (in the YXZ convention: when decomposing,
+ /// first Z, then X, and Y last) corresponding to the rotation
+ /// represented by the unit quaternion. Returned vector contains
+ /// the rotation angles in the format (X angle, Y angle, Z angle).
+ /// </summary>
+ /// <returns>The Euler angle representation of this quaternion.</returns>
+ public Vector3 GetEuler()
+ {
+#if DEBUG
+ if (!IsNormalized())
+ {
+ throw new InvalidOperationException("Quaternion is not normalized");
+ }
+#endif
+ var basis = new Basis(this);
+ return basis.GetEuler();
+ }
+
+ /// <summary>
+ /// Returns the inverse of the quaternion.
+ /// </summary>
+ /// <returns>The inverse quaternion.</returns>
+ public Quaternion Inverse()
+ {
+#if DEBUG
+ if (!IsNormalized())
+ {
+ throw new InvalidOperationException("Quaternion is not normalized");
+ }
+#endif
+ return new Quaternion(-x, -y, -z, w);
+ }
+
+ /// <summary>
+ /// Returns whether the quaternion is normalized or not.
+ /// </summary>
+ /// <returns>A <see langword="bool"/> for whether the quaternion is normalized or not.</returns>
+ public bool IsNormalized()
+ {
+ return Mathf.Abs(LengthSquared - 1) <= Mathf.Epsilon;
+ }
+
+ /// <summary>
+ /// Returns a copy of the quaternion, normalized to unit length.
+ /// </summary>
+ /// <returns>The normalized quaternion.</returns>
+ public Quaternion Normalized()
+ {
+ return this / Length;
+ }
+
+ /// <summary>
+ /// Returns the result of the spherical linear interpolation between
+ /// this quaternion and <paramref name="to"/> by amount <paramref name="weight"/>.
+ ///
+ /// Note: Both quaternions must be normalized.
+ /// </summary>
+ /// <param name="to">The destination quaternion for interpolation. Must be normalized.</param>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting quaternion of the interpolation.</returns>
+ public Quaternion Slerp(Quaternion to, real_t weight)
+ {
+#if DEBUG
+ if (!IsNormalized())
+ {
+ throw new InvalidOperationException("Quaternion is not normalized");
+ }
+ if (!to.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(to));
+ }
+#endif
+
+ // Calculate cosine.
+ real_t cosom = x * to.x + y * to.y + z * to.z + w * to.w;
+
+ var to1 = new Quaternion();
+
+ // Adjust signs if necessary.
+ if (cosom < 0.0)
+ {
+ cosom = -cosom;
+ to1.x = -to.x;
+ to1.y = -to.y;
+ to1.z = -to.z;
+ to1.w = -to.w;
+ }
+ else
+ {
+ to1.x = to.x;
+ to1.y = to.y;
+ to1.z = to.z;
+ to1.w = to.w;
+ }
+
+ real_t sinom, scale0, scale1;
+
+ // Calculate coefficients.
+ if (1.0 - cosom > Mathf.Epsilon)
+ {
+ // Standard case (Slerp).
+ real_t omega = Mathf.Acos(cosom);
+ sinom = Mathf.Sin(omega);
+ scale0 = Mathf.Sin((1.0f - weight) * omega) / sinom;
+ scale1 = Mathf.Sin(weight * omega) / sinom;
+ }
+ else
+ {
+ // Quaternions are very close so we can do a linear interpolation.
+ scale0 = 1.0f - weight;
+ scale1 = weight;
+ }
+
+ // 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)
+ );
+ }
+
+ /// <summary>
+ /// Returns the result of the spherical linear interpolation between
+ /// 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>
+ /// <param name="weight">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</param>
+ /// <returns>The resulting quaternion of the interpolation.</returns>
+ public Quaternion Slerpni(Quaternion to, real_t weight)
+ {
+ real_t dot = Dot(to);
+
+ if (Mathf.Abs(dot) > 0.9999f)
+ {
+ return this;
+ }
+
+ real_t theta = Mathf.Acos(dot);
+ real_t sinT = 1.0f / Mathf.Sin(theta);
+ real_t newFactor = Mathf.Sin(weight * theta) * sinT;
+ real_t invFactor = Mathf.Sin((1.0f - weight) * theta) * sinT;
+
+ return new Quaternion
+ (
+ (invFactor * x) + (newFactor * to.x),
+ (invFactor * y) + (newFactor * to.y),
+ (invFactor * z) + (newFactor * to.z),
+ (invFactor * w) + (newFactor * to.w)
+ );
+ }
+
+ /// <summary>
+ /// Returns a vector transformed (multiplied) by this quaternion.
+ /// </summary>
+ /// <param name="v">A vector to transform.</param>
+ /// <returns>The transformed vector.</returns>
+ public Vector3 Xform(Vector3 v)
+ {
+#if DEBUG
+ if (!IsNormalized())
+ {
+ throw new InvalidOperationException("Quaternion is not normalized");
+ }
+#endif
+ var u = new Vector3(x, y, z);
+ Vector3 uv = u.Cross(v);
+ return v + (((uv * w) + u.Cross(uv)) * 2);
+ }
+
+ // Constants
+ private static readonly Quaternion _identity = new Quaternion(0, 0, 0, 1);
+
+ /// <summary>
+ /// The identity quaternion, representing no rotation.
+ /// 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 <c>new Quaternion(0, 0, 0, 1)</c>.</value>
+ public static Quaternion Identity { get { return _identity; } }
+
+ /// <summary>
+ /// Constructs a <see cref="Quaternion"/> defined by the given values.
+ /// </summary>
+ /// <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)
+ {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+
+ /// <summary>
+ /// Constructs a <see cref="Quaternion"/> from the given <see cref="Quaternion"/>.
+ /// </summary>
+ /// <param name="q">The existing quaternion.</param>
+ public Quaternion(Quaternion q)
+ {
+ this = q;
+ }
+
+ /// <summary>
+ /// Constructs a <see cref="Quaternion"/> from the given <see cref="Basis"/>.
+ /// </summary>
+ /// <param name="basis">The <see cref="Basis"/> to construct from.</param>
+ public Quaternion(Basis basis)
+ {
+ this = basis.Quaternion();
+ }
+
+ /// <summary>
+ /// 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">Euler angles that the quaternion will be rotated by.</param>
+ public Quaternion(Vector3 eulerYXZ)
+ {
+ 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 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 = (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 <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>
+ /// <param name="angle">The angle to rotate, in radians.</param>
+ public Quaternion(Vector3 axis, real_t angle)
+ {
+#if DEBUG
+ if (!axis.IsNormalized())
+ {
+ throw new ArgumentException("Argument is not normalized", nameof(axis));
+ }
+#endif
+
+ real_t d = axis.Length();
+
+ if (d == 0f)
+ {
+ x = 0f;
+ y = 0f;
+ z = 0f;
+ w = 0f;
+ }
+ else
+ {
+ real_t sinAngle = Mathf.Sin(angle * 0.5f);
+ real_t cosAngle = Mathf.Cos(angle * 0.5f);
+ real_t s = sinAngle / d;
+
+ x = axis.x * s;
+ y = axis.y * s;
+ z = axis.z * s;
+ w = cosAngle;
+ }
+ }
+
+ /// <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)
+ );
+ }
+
+ /// <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);
+ }
+
+ /// <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(-quat.x, -quat.y, -quat.z, -quat.w);
+ }
+
+ /// <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)
+ {
+#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);
+ }
+
+ /// <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 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)
+ {
+ return Equals((Quaternion)obj);
+ }
+
+ 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 <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>
+ public bool IsEqualApprox(Quaternion other)
+ {
+ 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 f7703c77cc..ec16920fed 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Rect2.cs
@@ -1,10 +1,10 @@
-using System;
-using System.Runtime.InteropServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+using System;
+using System.Runtime.InteropServices;
namespace Godot
{
@@ -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 rect.
+ /// 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 rect.</returns>
+ /// <returns>The modified <see cref="Rect2"/>.</returns>
public Rect2 Abs()
{
Vector2 end = End;
@@ -73,13 +76,17 @@ namespace Godot
}
/// <summary>
- /// Returns the intersection of this Rect2 and `b`.
+ /// 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 rect.</param>
- /// <returns>The clipped rect.</returns>
- public Rect2 Clip(Rect2 b)
+ /// <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))
{
@@ -99,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 rect that may be enclosed.</param>
- /// <returns>A bool for whether or not this rect 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 &&
@@ -111,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 rect.</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;
@@ -147,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()
@@ -156,13 +165,26 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2 grown a given amount of units towards all the 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 rect.</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;
@@ -173,16 +195,19 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2 grown a given amount of units towards each direction individually.
+ /// Returns a copy of the <see cref="Rect2"/> grown by the specified amount
+ /// on each side individually.
/// </summary>
- /// <param name="left">The amount to grow by on the left.</param>
- /// <param name="top">The amount to grow by on the top.</param>
- /// <param name="right">The amount to grow by on the right.</param>
- /// <param name="bottom">The amount to grow by on the bottom.</param>
- /// <returns>The grown rect.</returns>
+ /// <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 <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;
@@ -193,37 +218,46 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2 grown a given amount of units towards the <see cref="Margin"/> direction.
+ /// Returns a copy of the <see cref="Rect2"/> grown by the specified amount
+ /// on the specified <see cref="Side"/>.
/// </summary>
- /// <param name="margin">The direction to grow in.</param>
+ /// <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 rect.</returns>
- public Rect2 GrowMargin(Margin margin, real_t by)
+ /// <returns>The grown <see cref="Rect2"/>.</returns>
+ public Rect2 GrowSide(Side side, real_t by)
{
- var g = this;
+ Rect2 g = this;
- g = g.GrowIndividual(Margin.Left == margin ? by : 0,
- Margin.Top == margin ? by : 0,
- Margin.Right == margin ? by : 0,
- Margin.Bottom == margin ? by : 0);
+ g = g.GrowIndividual(Side.Left == side ? by : 0,
+ Side.Top == side ? by : 0,
+ Side.Right == side ? by : 0,
+ Side.Bottom == side ? by : 0);
return g;
}
/// <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 rect 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 rect 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)
@@ -240,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 rect 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)
@@ -294,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 rect.</param>
- /// <returns>The merged rect.</returns>
+ /// <param name="b">The other <see cref="Rect2"/>.</param>
+ /// <returns>The merged <see cref="Rect2"/>.</returns>
public Rect2 Merge(Rect2 b)
{
Rect2 newRect;
@@ -314,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>
@@ -325,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>
@@ -337,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>
@@ -349,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>
@@ -361,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)
@@ -381,14 +439,19 @@ 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 rect 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 rect to compare.</param>
/// <returns>Whether or not the rects are approximately equal.</returns>
@@ -397,27 +460,31 @@ namespace Godot
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 String.Format("({0}, {1})", new object[]
- {
- _position.ToString(),
- _size.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 String.Format("({0}, {1})", new object[]
- {
- _position.ToString(format),
- _size.ToString(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 8f71c00d76..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 rect.
+ /// 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 rect.</returns>
+ /// <returns>The modified <see cref="Rect2i"/>.</returns>
public Rect2i Abs()
{
Vector2i end = End;
@@ -68,13 +71,17 @@ namespace Godot
}
/// <summary>
- /// Returns the intersection of this Rect2i and `b`.
+ /// 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 rect.</param>
- /// <returns>The clipped rect.</returns>
- public Rect2i Clip(Rect2i b)
+ /// <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))
{
@@ -94,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 rect that may be enclosed.</param>
- /// <returns>A bool for whether or not this rect 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 &&
@@ -106,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 rect.</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;
@@ -142,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()
@@ -151,13 +160,28 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2i grown a given amount of units towards all the 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 rect.</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;
@@ -168,16 +192,19 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2i grown a given amount of units towards each direction individually.
+ /// Returns a copy of the <see cref="Rect2i"/> grown by the specified amount
+ /// on each side individually.
/// </summary>
- /// <param name="left">The amount to grow by on the left.</param>
- /// <param name="top">The amount to grow by on the top.</param>
- /// <param name="right">The amount to grow by on the right.</param>
- /// <param name="bottom">The amount to grow by on the bottom.</param>
- /// <returns>The grown rect.</returns>
+ /// <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 <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;
@@ -188,37 +215,46 @@ namespace Godot
}
/// <summary>
- /// Returns a copy of the Rect2i grown a given amount of units towards the <see cref="Margin"/> direction.
+ /// Returns a copy of the <see cref="Rect2i"/> grown by the specified amount
+ /// on the specified <see cref="Side"/>.
/// </summary>
- /// <param name="margin">The direction to grow in.</param>
+ /// <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 rect.</returns>
- public Rect2i GrowMargin(Margin margin, int by)
+ /// <returns>The grown <see cref="Rect2i"/>.</returns>
+ public Rect2i GrowSide(Side side, int by)
{
- var g = this;
+ Rect2i g = this;
- g = g.GrowIndividual(Margin.Left == margin ? by : 0,
- Margin.Top == margin ? by : 0,
- Margin.Right == margin ? by : 0,
- Margin.Bottom == margin ? by : 0);
+ g = g.GrowIndividual(Side.Left == side ? by : 0,
+ Side.Top == side ? by : 0,
+ Side.Right == side ? by : 0,
+ Side.Bottom == side ? by : 0);
return g;
}
/// <summary>
- /// Returns true if the Rect2 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 rect 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 Rect2 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 rect 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)
@@ -235,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 rect 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)
@@ -273,10 +310,10 @@ namespace Godot
}
/// <summary>
- /// Returns a larger Rect2i that contains this Rect2 and `b`.
+ /// Returns a larger <see cref="Rect2i"/> that contains this <see cref="Rect2i"/> and <paramref name="b"/>.
/// </summary>
- /// <param name="b">The other rect.</param>
- /// <returns>The merged rect.</returns>
+ /// <param name="b">The other <see cref="Rect2i"/>.</param>
+ /// <returns>The merged <see cref="Rect2i"/>.</returns>
public Rect2i Merge(Rect2i b)
{
Rect2i newRect;
@@ -293,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>
@@ -304,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>
@@ -316,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>
@@ -328,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>
@@ -340,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)
@@ -370,32 +434,41 @@ 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 String.Format("{0}, {1}", new object[]
- {
- _position.ToString(),
- _size.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 String.Format("{0}, {1}", new object[]
- {
- _position.ToString(format),
- _size.ToString(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 bd1dbc1229..d9ee684c5b 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)
@@ -61,10 +64,15 @@ namespace Godot
return string.Empty;
}
- // <summary>
- // If the string is a path to a file, return the path to the file without the extension.
- // </summary>
- public static string BaseName(this string instance)
+ /// <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('.');
@@ -74,20 +82,26 @@ namespace Godot
return instance;
}
- // <summary>
- // Return true if the strings begins with the given string.
- // </summary>
+ /// <summary>
+ /// 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.
- // </summary>
+ /// <summary>
+ /// 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++)
{
@@ -97,9 +111,45 @@ namespace Godot
return b;
}
- // <summary>
- // Return the amount of substrings in string.
- // </summary>
+ /// <summary>
+ /// Converts a string containing a binary number into an integer.
+ /// 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>
+ public static int BinToInt(this string instance)
+ {
+ if (instance.Length == 0)
+ {
+ return 0;
+ }
+
+ int sign = 1;
+
+ if (instance[0] == '-')
+ {
+ sign = -1;
+ instance = instance.Substring(1);
+ }
+
+ if (instance.StartsWith("0b"))
+ {
+ instance = instance.Substring(2);
+ }
+
+ return sign * Convert.ToInt32(instance, 2);
+ }
+
+ /// <summary>
+ /// 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)
@@ -157,9 +207,11 @@ namespace Godot
return c;
}
- // <summary>
- // Return a copy of the string with special characters escaped using the C language standard.
- // </summary>
+ /// <summary>
+ /// 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));
@@ -179,9 +231,12 @@ namespace Godot
return sb.ToString();
}
- // <summary>
- // Return a copy of the string with escaped characters replaced by their meanings according to the C language standard.
- // </summary>
+ /// <summary>
+ /// 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));
@@ -201,13 +256,18 @@ namespace Godot
return sb.ToString();
}
- // <summary>
- // Change 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 [code]capitalize camelCase mixed_with_underscores[/code] it will return [code]Capitalize Camelcase Mixed With Underscores[/code].
- // </summary>
+ /// <summary>
+ /// 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++)
{
@@ -224,17 +284,28 @@ namespace Godot
return cap;
}
- // <summary>
- // Perform a case-sensitive comparison to another string, return -1 if less, 0 if equal and +1 if greater.
- // </summary>
+ /// <summary>
+ /// 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.
- // </summary>
+ /// <summary>
+ /// 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))
@@ -286,26 +357,52 @@ namespace Godot
}
}
- // <summary>
- // Return true if the strings ends with the given string.
- // </summary>
+ /// <summary>
+ /// 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);
}
- // <summary>
- // Erase [code]chars[/code] characters from the string starting from [code]pos[/code].
- // </summary>
+ /// <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>
- public static string Extension(this string instance)
+ /// <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(".");
@@ -315,14 +412,46 @@ namespace Godot
return instance.Substring(pos + 1);
}
- /// <summary>Find the first occurrence of a substring. Optionally, the search starting position can be passed.</summary>
+ /// <summary>
+ /// Find the first occurrence of a substring. Optionally, the search starting position can be passed.
+ /// </summary>
+ /// <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)
{
return instance.IndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
}
+ /// <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)
+ {
+ // TODO: Could be more efficient if we get a char version of `IndexOf`.
+ // See https://github.com/dotnet/runtime/issues/44116
+ return instance.IndexOf(what.ToString(), from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
+ }
+
/// <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)
{
@@ -330,41 +459,64 @@ 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)
{
return instance.LastIndexOf(what, from, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
}
- /// <summary>Find the first occurrence of a substring but search as case-insensitive. Optionally, the search starting position can be passed.</summary>
+ /// <summary>
+ /// 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)
{
return instance.IndexOf(what, from, StringComparison.OrdinalIgnoreCase);
}
- // <summary>
- // If the string is a path to a file, return the base directory.
- // </summary>
+ /// <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
{
@@ -375,14 +527,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>
+ /// <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("\\"));
@@ -393,26 +550,113 @@ namespace Godot
return instance.Substring(sep + 1);
}
- // <summary>
- // Hash the string and return a 32 bits integer.
- // </summary>
- public static int Hash(this string instance)
+ /// <summary>
+ /// Converts the given byte array of ASCII encoded text to a string.
+ /// Faster alternative to <see cref="GetStringFromUTF8"/> if the
+ /// content is ASCII-only. Unlike the UTF-8 function this function
+ /// maps every byte to a character in the array. Multibyte sequences
+ /// will not be interpreted correctly. For parsing user input always
+ /// use <see cref="GetStringFromUTF8"/>.
+ /// </summary>
+ /// <param name="bytes">A byte array of ASCII characters (on the range of 0-127).</param>
+ /// <returns>A string created from the bytes.</returns>
+ public static string GetStringFromASCII(this byte[] bytes)
{
- int index = 0;
- int hashv = 5381;
- int c;
+ return Encoding.ASCII.GetString(bytes);
+ }
- while ((c = instance[index++]) != 0)
- hashv = (hashv << 5) + hashv + c; // hash * 33 + c
+ /// <summary>
+ /// Converts the given byte array of UTF-8 encoded text to a string.
+ /// Slower than <see cref="GetStringFromASCII"/> but supports UTF-8
+ /// encoded data. Use this function if you are unsure about the
+ /// source of the data. For user input this function
+ /// should always be preferred.
+ /// </summary>
+ /// <param name="bytes">A byte array of UTF-8 characters (a character may take up multiple bytes).</param>
+ /// <returns>A string created from the bytes.</returns>
+ public static string GetStringFromUTF8(this byte[] bytes)
+ {
+ return Encoding.UTF8.GetString(bytes);
+ }
+
+ /// <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;
+
+ foreach (uint c in instance)
+ {
+ hash = (hash << 5) + hash + c; // hash * 33 + c
+ }
- return hashv;
+ return hash;
}
- // <summary>
- // Convert a string containing an hexadecimal number into an int.
- // </summary>
+ /// <summary>
+ /// Returns a hexadecimal representation of this byte as a string.
+ /// </summary>
+ /// <param name="b">The byte to encode.</param>
+ /// <returns>The hexadecimal representation of this byte.</returns>
+ internal static string HexEncode(this byte b)
+ {
+ string ret = string.Empty;
+
+ for (int i = 0; i < 2; i++)
+ {
+ char c;
+ int lv = b & 0xF;
+
+ if (lv < 10)
+ {
+ c = (char)('0' + lv);
+ }
+ else
+ {
+ c = (char)('a' + lv - 10);
+ }
+
+ b >>= 4;
+ ret = c + ret;
+ }
+
+ return ret;
+ }
+
+ /// <summary>
+ /// Returns a hexadecimal representation of this byte array as a string.
+ /// </summary>
+ /// <param name="bytes">The byte array to encode.</param>
+ /// <returns>The hexadecimal representation of this byte array.</returns>
+ public static string HexEncode(this byte[] bytes)
+ {
+ string ret = string.Empty;
+
+ foreach (byte b in bytes)
+ {
+ ret += b.HexEncode();
+ }
+
+ return ret;
+ }
+
+ /// <summary>
+ /// Converts a string containing a hexadecimal number into an integer.
+ /// 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>
public static int HexToInt(this string instance)
{
+ if (instance.Length == 0)
+ {
+ return 0;
+ }
+
int sign = 1;
if (instance[0] == '-')
@@ -421,24 +665,38 @@ namespace Godot
instance = instance.Substring(1);
}
- if (!instance.StartsWith("0x"))
- return 0;
+ if (instance.StartsWith("0x"))
+ {
+ instance = instance.Substring(2);
+ }
- return sign * int.Parse(instance.Substring(2), NumberStyles.HexNumber);
+ return sign * int.Parse(instance, NumberStyles.HexNumber);
}
- // <summary>
- // Insert a substring at a given position.
- // </summary>
+ /// <summary>
+ /// 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 true if the path is absolute.
- // </summary>
- public static bool IsAbsPath(this string instance)
+ /// <summary>
+ /// 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))
return false;
@@ -448,17 +706,28 @@ namespace Godot
return instance[0] == '/' || instance[0] == '\\';
}
- // <summary>
- // If the string is a path to a file or directory, return true if the path is relative.
- // </summary>
- public static bool IsRelPath(this string instance)
+ /// <summary>
+ /// 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>
+ /// <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 !IsAbsPath(instance);
+ return !IsAbsolutePath(instance);
}
- // <summary>
- // Check whether this string is a subsequence of the given string.
- // </summary>
+ /// <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;
@@ -499,34 +768,46 @@ namespace Godot
return false;
}
- // <summary>
- // Check whether this string is a subsequence of the given string, ignoring case differences.
- // </summary>
+ /// <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);
}
- // <summary>
- // Check whether the string contains a valid float.
- // </summary>
+ /// <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;
return float.TryParse(instance, out f);
}
- // <summary>
- // Check whether the string contains a valid color in HTML notation.
- // </summary>
+ /// <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);
}
- // <summary>
- // Check whether the string is a valid identifier. As is common in programming languages, a valid identifier may contain only letters, digits and underscores (_) and the first character may not be a digit.
- // </summary>
+ /// <summary>
+ /// Check whether the string is a valid identifier. As is common in
+ /// 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;
@@ -534,18 +815,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;
@@ -554,18 +832,22 @@ namespace Godot
return true;
}
- // <summary>
- // Check whether the string contains a valid integer.
- // </summary>
+ /// <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;
return int.TryParse(instance, out f);
}
- // <summary>
- // Check whether the string contains a valid IP address.
- // </summary>
+ /// <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
@@ -588,9 +870,11 @@ namespace Godot
return true;
}
- // <summary>
- // Return a copy of the string with special characters escaped using the JSON standard.
- // </summary>
+ /// <summary>
+ /// 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));
@@ -607,9 +891,13 @@ namespace Godot
return sb.ToString();
}
- // <summary>
- // Return an amount of characters from the left of the string.
- // </summary>
+ /// <summary>
+ /// 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)
@@ -622,16 +910,53 @@ 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;
}
/// <summary>
- /// Do a simple expression match, where '*' matches zero or more arbitrary characters and '?' matches any single character except '.'.
+ /// 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>
+ public static string LStrip(this string instance, string chars)
+ {
+ int len = instance.Length;
+ int beg;
+
+ for (beg = 0; beg < len; beg++)
+ {
+ if (chars.Find(instance[beg]) == -1)
+ {
+ break;
+ }
+ }
+
+ if (beg == 0)
+ {
+ return instance;
+ }
+
+ return instance.Substr(beg, len - beg);
+ }
+
+ /// <summary>
+ /// 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':
@@ -645,14 +970,25 @@ namespace Godot
case '?':
return instance.Length > 0 && instance[0] != '.' && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
default:
- if (instance.Length == 0) return false;
- return (caseSensitive ? instance[0] == expr[0] : char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
+ if (instance.Length == 0)
+ return false;
+ if (caseSensitive)
+ return instance[0] == expr[0];
+ return (char.ToUpper(instance[0]) == char.ToUpper(expr[0])) && ExprMatch(instance.Substring(1), expr.Substring(1), caseSensitive);
}
}
/// <summary>
- /// Do a simple case sensitive expression match, using ? and * wildcards (see [method expr_match]).
+ /// 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)
@@ -662,8 +998,13 @@ namespace Godot
}
/// <summary>
- /// Do a simple case insensitive expression match, using ? and * wildcards (see [method expr_match]).
+ /// 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)
@@ -672,47 +1013,66 @@ namespace Godot
return instance.ExprMatch(expr, caseSensitive: false);
}
- // <summary>
- // Return the MD5 hash of the string as an array of bytes.
- // </summary>
+ /// <summary>
+ /// 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.
- // </summary>
+ /// <summary>
+ /// 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>
+ /// <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 [code]at[/code].
- // </summary>
+ /// <summary>
+ /// 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 [code]digits[/code] after the decimal point.
- // </summary>
+ /// <summary>
+ /// 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(".");
@@ -746,9 +1106,14 @@ namespace Godot
return instance;
}
- // <summary>
- // Format a number to have an exact number of [code]digits[/code] before the decimal point.
- // </summary>
+ /// <summary>
+ /// 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;
@@ -779,25 +1144,14 @@ namespace Godot
return s;
}
- // <summary>
- // Decode a percent-encoded string. See [method percent_encode].
- // </summary>
- public static string PercentDecode(this string instance)
- {
- return Uri.UnescapeDataString(instance);
- }
-
- // <summary>
- // Percent-encode a string. This is meant to encode parameters in a URL when sending a HTTP GET request and bodies of form-urlencoded POST request.
- // </summary>
- public static string PercentEncode(this string instance)
- {
- return Uri.EscapeDataString(instance);
- }
-
- // <summary>
- // If the string is a path, this concatenates [code]file[/code] at the end of the string as a subpath. E.g. [code]"this/is".plus_file("path") == "this/is/path"[/code].
- // </summary>
+ /// <summary>
+ /// 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] == '/')
@@ -805,47 +1159,72 @@ namespace Godot
return instance + "/" + file;
}
- // <summary>
- // Replace occurrences of a substring for different ones inside the string.
- // </summary>
+ /// <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);
}
- // <summary>
- // Replace occurrences of a substring for different ones inside the string, but search case-insensitive.
- // </summary>
+ /// <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);
}
- // <summary>
- // Perform a search for a substring, but start from the end of the string instead of the beginning.
- // </summary>
+ /// <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>
+ /// <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.
- // </summary>
+ /// <summary>
+ /// 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)
@@ -857,28 +1236,69 @@ namespace Godot
return instance.Substring(pos, instance.Length - pos);
}
+ /// <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>
+ public static string RStrip(this string instance, string chars)
+ {
+ int len = instance.Length;
+ int end;
+
+ for (end = len - 1; end >= 0; end--)
+ {
+ if (chars.Find(instance[end]) == -1)
+ {
+ break;
+ }
+ }
+
+ if (end == len - 1)
+ {
+ return instance;
+ }
+
+ 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.
- // </summary>
+ /// <summary>
+ /// 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. 1 means totally similar and 0 means totally dissimilar.
- // </summary>
+ /// <summary>
+ /// 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)
@@ -916,17 +1336,44 @@ namespace Godot
return 2.0f * inter / sum;
}
- // <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>
+ /// <summary>
+ /// Returns a simplified canonical path.
+ /// </summary>
+ public static string SimplifyPath(this string instance)
+ {
+ return godot_icall_String_simplify_path(instance);
+ }
+
+ [MethodImpl(MethodImplOptions.InternalCall)]
+ 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 }, StringSplitOptions.RemoveEmptyEntries);
+ return instance.Split(new[] { divisor }, allowEmpty ? StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries);
}
- // <summary>
- // 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>
+ /// <summary>
+ /// 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>();
@@ -949,7 +1396,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,
@@ -958,9 +1406,14 @@ namespace Godot
(char)30, (char)31, (char)32
};
- // <summary>
- // Return 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>
+ /// <summary>
+ /// 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)
@@ -973,74 +1426,136 @@ namespace Godot
return instance.TrimEnd(_nonPrintable);
}
- // <summary>
- // Return part of the string from the position [code]from[/code], with length [code]len[/code].
- // </summary>
+ /// <summary>
+ /// 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;
return instance.Substring(from, len > max ? max : len);
}
- // <summary>
- // Convert the String (which is a character array) to PackedByteArray (which is an array of bytes). The conversion is speeded up in comparison to to_utf8() with the assumption that all the characters the String contains are only ASCII characters.
- // </summary>
+ /// <summary>
+ /// 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 [code]float[/code].
- // </summary>
+ /// <summary>
+ /// 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 [code]int[/code].
- // </summary>
+ /// <summary>
+ /// 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.
- // </summary>
+ /// <summary>
+ /// 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.
- // </summary>
+ /// <summary>
+ /// 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). The conversion is a bit slower than to_ascii(), but supports all UTF-8 characters. Therefore, you should prefer this function over to_ascii().
- // </summary>
+ /// <summary>
+ /// 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);
}
- // <summary>
- // Return a copy of the string with special characters escaped using the XML standard.
- // </summary>
+ /// <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 <see cref="Uri.UnescapeDataString"/>,
+ /// but also handles <c>+</c>.
+ /// See <see cref="URIEncode"/> for encoding.
+ /// </summary>
+ /// <param name="instance">The string to decode.</param>
+ /// <returns>The unescaped string.</returns>
+ public static string URIDecode(this string instance)
+ {
+ return Uri.UnescapeDataString(instance.Replace("+", "%20"));
+ }
+
+ /// <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 <see cref="Uri.EscapeDataString"/>.
+ /// See <see cref="URIDecode"/> for decoding.
+ /// </summary>
+ /// <param name="instance">The string to encode.</param>
+ /// <returns>The escaped string.</returns>
+ public static string URIEncode(this string instance)
+ {
+ return Uri.EscapeDataString(instance);
+ }
+
+ /// <summary>
+ /// 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 according to the XML standard.
- // </summary>
+ /// <summary>
+ /// 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 06bbe98497..6f1d9574a8 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs
@@ -1,10 +1,10 @@
-using System;
-using System.Runtime.InteropServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+using System;
+using System.Runtime.InteropServices;
namespace Godot
{
@@ -21,18 +21,18 @@ 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 +77,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 +117,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 +140,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 +149,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 +176,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 +193,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 +207,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>
@@ -221,8 +226,7 @@ namespace Godot
real_t dot = v1.Dot(v2);
- // Clamp dot to [-1, 1]
- dot = dot < -1.0f ? -1.0f : (dot > 1.0f ? 1.0f : dot);
+ dot = Mathf.Clamp(dot, -1.0f, 1.0f);
Vector2 v;
@@ -234,8 +238,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
@@ -259,7 +263,7 @@ namespace Godot
/// <returns>The inverse matrix.</returns>
public Transform2D Inverse()
{
- var inv = this;
+ Transform2D inv = this;
// Swap
real_t temp = inv.x.y;
@@ -278,13 +282,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;
@@ -294,7 +298,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>
@@ -310,7 +314,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;
@@ -327,16 +331,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"/>,
@@ -346,7 +350,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;
}
@@ -354,8 +358,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;
@@ -364,8 +370,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;
@@ -379,20 +387,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>
@@ -412,12 +420,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);
@@ -426,21 +434,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);
@@ -455,29 +472,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>
@@ -486,29 +624,31 @@ 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 String.Format("({0}, {1}, {2})", new object[]
- {
- x.ToString(),
- y.ToString(),
- origin.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 String.Format("({0}, {1}, {2})", new object[]
- {
- x.ToString(format),
- y.ToString(format),
- origin.ToString(format)
- });
+ return $"[X: {x.ToString(format)}, Y: {y.ToString(format)}, O: {origin.ToString(format)}]";
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index ac47f6029f..4bb8308c12 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -1,10 +1,10 @@
-using System;
-using System.Runtime.InteropServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+using System;
+using System.Runtime.InteropServices;
namespace Godot
{
@@ -19,7 +19,7 @@ namespace Godot
/// </summary>
[Serializable]
[StructLayout(LayoutKind.Sequential)]
- public struct Transform : IEquatable<Transform>
+ public struct Transform3D : IEquatable<Transform3D>
{
/// <summary>
/// The <see cref="Basis"/> of this transform. Contains the X, Y, and Z basis
@@ -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,33 +108,36 @@ 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 Transform AffineInverse()
+ public Transform3D AffineInverse()
{
Basis basisInv = basis.Inverse();
- return new Transform(basisInv, basisInv.Xform(-origin));
+ return new Transform3D(basisInv, basisInv.Xform(-origin));
}
/// <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>
/// <returns>The interpolated transform.</returns>
- public Transform InterpolateWith(Transform transform, real_t weight)
+ public Transform3D InterpolateWith(Transform3D transform, real_t weight)
{
/* not sure if very "efficient" but good enough? */
Vector3 sourceScale = basis.Scale;
- Quat sourceRotation = basis.RotationQuat();
+ Quaternion sourceRotation = basis.GetRotationQuaternion();
Vector3 sourceLocation = origin;
Vector3 destinationScale = transform.basis.Scale;
- Quat destinationRotation = transform.basis.RotationQuat();
+ Quaternion destinationRotation = transform.basis.GetRotationQuaternion();
Vector3 destinationLocation = transform.origin;
- var interpolated = new Transform();
- interpolated.basis.SetQuatScale(sourceRotation.Slerp(destinationRotation, weight).Normalized(), sourceScale.Lerp(destinationScale, weight));
+ var interpolated = new Transform3D();
+ 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;
@@ -144,28 +149,28 @@ namespace Godot
/// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling).
/// </summary>
/// <returns>The inverse matrix.</returns>
- public Transform Inverse()
+ public Transform3D Inverse()
{
Basis basisTr = basis.Transposed();
- return new Transform(basisTr, basisTr.Xform(-origin));
+ return new Transform3D(basisTr, basisTr.Xform(-origin));
}
/// <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>
/// <returns>The resulting transform.</returns>
- public Transform LookingAt(Vector3 target, Vector3 up)
+ public Transform3D LookingAt(Vector3 target, Vector3 up)
{
- var t = this;
+ Transform3D t = this;
t.SetLookAt(origin, target, up);
return t;
}
@@ -175,21 +180,21 @@ namespace Godot
/// and normalized axis vectors (scale of 1 or -1).
/// </summary>
/// <returns>The orthonormalized transform.</returns>
- public Transform Orthonormalized()
+ public Transform3D Orthonormalized()
{
- return new Transform(basis.Orthonormalized(), origin);
+ return new Transform3D(basis.Orthonormalized(), origin);
}
/// <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>
/// <param name="phi">The angle to rotate, in radians.</param>
/// <returns>The rotated transformation matrix.</returns>
- public Transform Rotated(Vector3 axis, real_t phi)
+ public Transform3D Rotated(Vector3 axis, real_t phi)
{
- return new Transform(new Basis(axis, phi), new Vector3()) * this;
+ return new Transform3D(new Basis(axis, phi), new Vector3()) * this;
}
/// <summary>
@@ -197,9 +202,9 @@ namespace Godot
/// </summary>
/// <param name="scale">The scale to introduce.</param>
/// <returns>The scaled transformation matrix.</returns>
- public Transform Scaled(Vector3 scale)
+ public Transform3D Scaled(Vector3 scale)
{
- return new Transform(basis.Scaled(scale), origin * scale);
+ return new Transform3D(basis.Scaled(scale), origin * scale);
}
private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
@@ -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"/>,
@@ -234,9 +239,9 @@ namespace Godot
/// </summary>
/// <param name="offset">The offset to translate by.</param>
/// <returns>The translated matrix.</returns>
- public Transform Translated(Vector3 offset)
+ public Transform3D Translated(Vector3 offset)
{
- return new Transform(basis, new Vector3
+ return new Transform3D(basis, new Vector3
(
origin[0] += basis.Row0.Dot(offset),
origin[1] += basis.Row1.Dot(offset),
@@ -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,40 +280,40 @@ 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)
);
}
// Constants
- private static readonly Transform _identity = new Transform(Basis.Identity, Vector3.Zero);
- private static readonly Transform _flipX = new Transform(new Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1), Vector3.Zero);
- private static readonly Transform _flipY = new Transform(new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1), Vector3.Zero);
- private static readonly Transform _flipZ = new Transform(new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1), Vector3.Zero);
+ private static readonly Transform3D _identity = new Transform3D(Basis.Identity, Vector3.Zero);
+ private static readonly Transform3D _flipX = new Transform3D(new Basis(-1, 0, 0, 0, 1, 0, 0, 0, 1), Vector3.Zero);
+ private static readonly Transform3D _flipY = new Transform3D(new Basis(1, 0, 0, 0, -1, 0, 0, 0, 1), Vector3.Zero);
+ private static readonly Transform3D _flipZ = new Transform3D(new Basis(1, 0, 0, 0, 1, 0, 0, 0, -1), Vector3.Zero);
/// <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>
- public static Transform Identity { get { return _identity; } }
+ /// <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>
- public static Transform FlipX { get { return _flipX; } }
+ /// <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>
- public static Transform FlipY { get { return _flipY; } }
+ /// <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>
- public static Transform FlipZ { get { return _flipZ; } }
+ /// <value>Equivalent to <c>new Transform(Vector3.Right, Vector3.Up, Vector3.Forward, Vector3.Zero)</c>.</value>
+ public static Transform3D FlipZ { get { return _flipZ; } }
/// <summary>
/// Constructs a transformation matrix from 4 vectors (matrix columns).
@@ -315,98 +322,143 @@ namespace Godot
/// <param name="column1">The Y vector, or column index 1.</param>
/// <param name="column2">The Z vector, or column index 2.</param>
/// <param name="origin">The origin vector, or column index 3.</param>
- public Transform(Vector3 column0, Vector3 column1, Vector3 column2, Vector3 origin)
+ public Transform3D(Vector3 column0, Vector3 column1, Vector3 column2, Vector3 origin)
{
basis = new Basis(column0, column1, column2);
this.origin = origin;
}
/// <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="quat">The <see cref="Godot.Quat"/> 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 Transform(Quat quat, Vector3 origin)
+ public Transform3D(Quaternion quaternion, Vector3 origin)
{
- basis = new Basis(quat);
+ basis = new Basis(quaternion);
this.origin = origin;
}
/// <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 Transform(Basis basis, Vector3 origin)
+ public Transform3D(Basis basis, Vector3 origin)
{
this.basis = basis;
this.origin = origin;
}
- public static Transform operator *(Transform left, Transform right)
+ /// <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);
left.basis *= right.basis;
return left;
}
- public static bool operator ==(Transform left, Transform right)
+ /// <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);
}
- public static bool operator !=(Transform left, Transform 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 Transform)
+ if (obj is Transform3D)
{
- return Equals((Transform)obj);
+ return Equals((Transform3D)obj);
}
return false;
}
- public bool Equals(Transform other)
+ /// <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>
- public bool IsEqualApprox(Transform other)
+ public bool IsEqualApprox(Transform3D other)
{
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 String.Format("{0} - {1}", new object[]
- {
- basis.ToString(),
- origin.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 String.Format("{0} - {1}", new object[]
- {
- basis.ToString(format),
- origin.ToString(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
new file mode 100644
index 0000000000..eae8927ceb
--- /dev/null
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/UnhandledExceptionArgs.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Godot
+{
+ /// <summary>
+ /// Event arguments for when unhandled exceptions occur.
+ /// </summary>
+ public class UnhandledExceptionArgs
+ {
+ /// <summary>
+ /// Exception object.
+ /// </summary>
+ public Exception Exception { get; private set; }
+
+ internal UnhandledExceptionArgs(Exception exception)
+ {
+ Exception = exception;
+ }
+ }
+}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
index d536b14eac..0c3331900a 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs
@@ -1,16 +1,10 @@
-// file: core/math/math_2d.h
-// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
-// file: core/math/math_2d.cpp
-// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
-// file: core/variant_call.cpp
-// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
-using System;
-using System.Runtime.InteropServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+using System;
+using System.Runtime.InteropServices;
namespace Godot
{
@@ -27,23 +21,36 @@ namespace Godot
/// </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
@@ -103,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()
@@ -132,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;
@@ -160,70 +167,72 @@ namespace Godot
}
/// <summary>
- /// Returns the vector with a maximum length by limiting its length to `length`.
+ /// Returns a new vector with all components clamped between the
+ /// components of <paramref name="min"/> and <paramref name="max"/> using
+ /// <see cref="Mathf.Clamp(real_t, real_t, real_t)"/>.
/// </summary>
- /// <param name="length">The length to limit to.</param>
- /// <returns>The vector with its length limited.</returns>
- public Vector2 Clamped(real_t length)
+ /// <param name="min">The vector with minimum allowed values.</param>
+ /// <param name="max">The vector with maximum allowed values.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public Vector2 Clamp(Vector2 min, Vector2 max)
{
- var v = this;
- real_t l = Length();
-
- if (l > 0 && length < l)
- {
- v /= l;
- v *= length;
- }
-
- return v;
+ return new Vector2
+ (
+ Mathf.Clamp(x, min.x, max.x),
+ Mathf.Clamp(y, min.y, max.y)
+ );
}
/// <summary>
- /// Returns the cross product of this vector and `b`.
+ /// Returns the cross product of this vector and <paramref name="b"/>.
/// </summary>
/// <param name="b">The other vector.</param>
/// <returns>The cross product value.</returns>
public real_t Cross(Vector2 b)
{
- return x * b.y - y * b.x;
+ return (x * b.y) - (y * b.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="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</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 t)
+ public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t weight)
{
Vector2 p0 = preA;
Vector2 p1 = this;
Vector2 p2 = b;
Vector2 p3 = postB;
+ real_t t = weight;
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="b"/>.
/// </summary>
/// <param name="b">The other vector to point towards.</param>
- /// <returns>The direction from this vector to `b`.</returns>
+ /// <returns>The direction from this vector to <paramref name="b"/>.</returns>
public Vector2 DirectionTo(Vector2 b)
{
return new Vector2(b.x - x, b.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>
@@ -235,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>
@@ -245,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>
@@ -264,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()
@@ -273,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;
@@ -284,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>
@@ -298,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>
@@ -319,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)
{
@@ -334,6 +346,25 @@ namespace Godot
}
/// <summary>
+ /// 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>
+ public Vector2 LimitLength(real_t length = 1.0f)
+ {
+ Vector2 v = this;
+ real_t l = Length();
+
+ if (l > 0 && length < l)
+ {
+ v /= l;
+ v *= length;
+ }
+
+ return v;
+ }
+
+ /// <summary>
/// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
/// If both components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
@@ -354,35 +385,41 @@ namespace Godot
}
/// <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;
@@ -392,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;
@@ -405,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>
@@ -415,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>
@@ -424,14 +464,14 @@ namespace Godot
#if DEBUG
if (!normal.IsNormalized())
{
- throw new ArgumentException("Argument is not normalized", nameof(normal));
+ 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>
@@ -459,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;
@@ -470,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>
@@ -486,31 +526,31 @@ 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>
/// <returns>The snapped vector.</returns>
public Vector2 Snapped(Vector2 step)
{
- return new Vector2(Mathf.Stepify(x, step.x), Mathf.Stepify(y, step.y));
+ return new Vector2(Mathf.Snapped(x, step.x), Mathf.Snapped(y, step.y));
}
/// <summary>
@@ -518,7 +558,7 @@ namespace Godot
/// compared to the original, with the same length.
/// </summary>
/// <returns>The perpendicular vector.</returns>
- public Vector2 Perpendicular()
+ public Vector2 Orthogonal()
{
return new Vector2(y, -x);
}
@@ -534,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>
@@ -591,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;
@@ -598,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;
@@ -605,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;
@@ -612,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;
@@ -619,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;
@@ -626,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;
@@ -633,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;
@@ -640,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;
@@ -647,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;
@@ -654,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;
@@ -661,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)
@@ -680,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)
@@ -689,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)
@@ -716,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>
@@ -732,27 +948,31 @@ 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 String.Format("({0}, {1})", new object[]
- {
- x.ToString(),
- y.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 String.Format("({0}, {1})", new object[]
- {
- x.ToString(format),
- y.ToString(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 8dd9ab2f0d..6cac16d53b 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2i.cs
@@ -1,11 +1,10 @@
-using System;
-using System.Runtime.InteropServices;
-
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+using System;
+using System.Runtime.InteropServices;
namespace Godot
{
@@ -22,23 +21,36 @@ namespace Godot
/// </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
@@ -82,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()
@@ -111,16 +123,33 @@ 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;
}
/// <summary>
- /// Returns the cross product of this vector and `b`.
+ /// Returns a new vector with all components clamped between the
+ /// 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>
+ /// <param name="max">The vector with maximum allowed values.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public Vector2i Clamp(Vector2i min, Vector2i max)
+ {
+ return new Vector2i
+ (
+ Mathf.Clamp(x, min.x, max.x),
+ Mathf.Clamp(y, min.y, max.y)
+ );
+ }
+
+ /// <summary>
+ /// Returns the cross product of this vector and <paramref name="b"/>.
/// </summary>
/// <param name="b">The other vector.</param>
/// <returns>The cross product vector.</returns>
@@ -130,7 +159,7 @@ namespace Godot
}
/// <summary>
- /// Returns the squared distance between this vector and `b`.
+ /// Returns the squared distance between this vector and <paramref name="b"/>.
/// 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>
@@ -142,7 +171,7 @@ namespace Godot
}
/// <summary>
- /// Returns the distance between this vector and `b`.
+ /// Returns the distance between this vector and <paramref name="b"/>.
/// </summary>
/// <param name="b">The other vector to use.</param>
/// <returns>The distance between the two vectors.</returns>
@@ -152,7 +181,7 @@ namespace Godot
}
/// <summary>
- /// Returns the dot product of this vector and `b`.
+ /// Returns the dot product of this vector and <paramref name="b"/>.
/// </summary>
/// <param name="b">The other vector to use.</param>
/// <returns>The dot product of the two vectors.</returns>
@@ -164,6 +193,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()
{
@@ -208,10 +238,13 @@ namespace Godot
}
/// <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;
@@ -221,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;
@@ -238,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;
@@ -248,13 +284,13 @@ namespace Godot
}
/// <summary>
- /// Returns a vector rotated 90 degrees counter-clockwise
+ /// Returns a perpendicular vector rotated 90 degrees counter-clockwise
/// compared to the original, with the same length.
/// </summary>
/// <returns>The perpendicular vector.</returns>
- public Vector2 Perpendicular()
+ public Vector2i Orthogonal()
{
- return new Vector2(y, -x);
+ return new Vector2i(y, -x);
}
// Constants
@@ -267,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>
@@ -330,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;
@@ -337,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;
@@ -344,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;
@@ -351,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;
@@ -358,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;
@@ -365,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;
@@ -372,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;
@@ -379,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;
@@ -386,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;
@@ -393,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;
@@ -400,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;
@@ -407,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;
@@ -414,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)
@@ -480,32 +689,41 @@ 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 String.Format("({0}, {1})", new object[]
- {
- this.x.ToString(),
- this.y.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 String.Format("({0}, {1})", new object[]
- {
- this.x.ToString(format),
- this.y.ToString(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 4a4a2a43cd..63d9be0a6d 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -1,16 +1,10 @@
-// file: core/math/vector3.h
-// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0
-// file: core/math/vector3.cpp
-// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
-// file: core/variant_call.cpp
-// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
-using System;
-using System.Runtime.InteropServices;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+using System;
+using System.Runtime.InteropServices;
namespace Godot
{
@@ -27,28 +21,46 @@ namespace Godot
/// </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
@@ -111,10 +123,10 @@ namespace Godot
}
/// <summary>
- /// Returns the minimum angle to the given vector, in radians.
+ /// Returns the unsigned minimum angle to the given vector, in radians.
/// </summary>
/// <param name="to">The other vector to compare this vector to.</param>
- /// <returns>The angle between the two vectors, in radians.</returns>
+ /// <returns>The unsigned angle between the two vectors, in radians.</returns>
public real_t AngleTo(Vector3 to)
{
return Mathf.Atan2(Cross(to).Length(), Dot(to));
@@ -140,7 +152,25 @@ namespace Godot
}
/// <summary>
- /// Returns the cross product of this vector and `b`.
+ /// Returns a new vector with all components clamped between the
+ /// 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>
+ /// <param name="max">The vector with maximum allowed values.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public Vector3 Clamp(Vector3 min, Vector3 max)
+ {
+ return new Vector3
+ (
+ Mathf.Clamp(x, min.x, max.x),
+ Mathf.Clamp(y, min.y, max.y),
+ Mathf.Clamp(z, min.z, max.z)
+ );
+ }
+
+ /// <summary>
+ /// Returns the cross product of this vector and <paramref name="b"/>.
/// </summary>
/// <param name="b">The other vector.</param>
/// <returns>The cross product vector.</returns>
@@ -148,50 +178,51 @@ namespace Godot
{
return new Vector3
(
- y * b.z - z * b.y,
- z * b.x - x * b.z,
- x * b.y - y * b.x
+ (y * b.z) - (z * b.y),
+ (z * b.x) - (x * b.z),
+ (x * b.y) - (y * b.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="t">A value on the range of 0.0 to 1.0, representing the amount of interpolation.</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 t)
+ public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t weight)
{
Vector3 p0 = preA;
Vector3 p1 = this;
Vector3 p2 = b;
Vector3 p3 = postB;
+ real_t t = weight;
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 + 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="b"/>.
/// </summary>
/// <param name="b">The other vector to point towards.</param>
- /// <returns>The direction from this vector to `b`.</returns>
+ /// <returns>The direction from this vector to <paramref name="b"/>.</returns>
public Vector3 DirectionTo(Vector3 b)
{
return new Vector3(b.x - x, b.y - y, b.z - z).Normalized();
}
/// <summary>
- /// Returns the squared distance between this vector and `b`.
+ /// Returns the squared distance between this vector and <paramref name="b"/>.
/// 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>
@@ -203,8 +234,9 @@ namespace Godot
}
/// <summary>
- /// Returns the distance between this vector and `b`.
+ /// Returns the distance between this vector and <paramref name="b"/>.
/// </summary>
+ /// <seealso cref="DistanceSquaredTo(Vector3)"/>
/// <param name="b">The other vector to use.</param>
/// <returns>The distance between the two vectors.</returns>
public real_t DistanceTo(Vector3 b)
@@ -213,13 +245,13 @@ namespace Godot
}
/// <summary>
- /// Returns the dot product of this vector and `b`.
+ /// Returns the dot product of this vector and <paramref name="b"/>.
/// </summary>
/// <param name="b">The other vector to use.</param>
/// <returns>The dot product of the two vectors.</returns>
public real_t Dot(Vector3 b)
{
- return x * b.x + y * b.y + z * b.z;
+ return (x * b.x) + (y * b.y) + (z * b.z);
}
/// <summary>
@@ -232,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()
@@ -241,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;
@@ -252,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()
{
@@ -279,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>
@@ -296,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>
@@ -312,6 +345,25 @@ namespace Godot
}
/// <summary>
+ /// 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>
+ public Vector3 LimitLength(real_t length = 1.0f)
+ {
+ Vector3 v = this;
+ real_t l = Length();
+
+ if (l > 0 && length < l)
+ {
+ v /= l;
+ v *= length;
+ }
+
+ return v;
+ }
+
+ /// <summary>
/// Returns the axis of the vector's largest value. See <see cref="Axis"/>.
/// If all components are equal, this method returns <see cref="Axis.X"/>.
/// </summary>
@@ -332,32 +384,35 @@ namespace Godot
}
/// <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="b"/>.
/// </summary>
/// <param name="b">The other vector.</param>
/// <returns>A <see cref="Basis"/> representing the outer product matrix.</returns>
@@ -371,10 +426,13 @@ namespace Godot
}
/// <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;
@@ -385,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;
@@ -399,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>
@@ -409,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>
@@ -418,15 +479,15 @@ namespace Godot
#if DEBUG
if (!normal.IsNormalized())
{
- throw new ArgumentException("Argument is not normalized", nameof(normal));
+ 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>
@@ -436,7 +497,7 @@ namespace Godot
#if DEBUG
if (!axis.IsNormalized())
{
- throw new ArgumentException("Argument is not normalized", nameof(axis));
+ throw new ArgumentException("Argument is not normalized", nameof(axis));
}
#endif
return new Basis(axis, phi).Xform(this);
@@ -457,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;
@@ -468,8 +529,25 @@ namespace Godot
}
/// <summary>
+ /// 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 <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>
+ /// <returns>The signed angle between the two vectors, in radians.</returns>
+ public real_t SignedAngleTo(Vector3 to, Vector3 axis)
+ {
+ Vector3 crossTo = Cross(to);
+ real_t unsignedAngle = Mathf.Atan2(crossTo.Length(), Dot(to));
+ real_t sign = crossTo.Dot(axis);
+ return (sign < 0) ? -unsignedAngle : unsignedAngle;
+ }
+
+ /// <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>
@@ -485,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);
@@ -493,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>
@@ -512,19 +590,19 @@ namespace Godot
{
return new Vector3
(
- Mathf.Stepify(x, step.x),
- Mathf.Stepify(y, step.y),
- Mathf.Stepify(z, step.z)
+ Mathf.Snapped(x, step.x),
+ Mathf.Snapped(y, step.y),
+ Mathf.Snapped(z, step.z)
);
}
/// <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(
@@ -547,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>
@@ -621,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;
@@ -629,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;
@@ -637,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;
@@ -645,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;
@@ -653,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;
@@ -661,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;
@@ -669,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;
@@ -677,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;
@@ -685,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;
@@ -693,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;
@@ -701,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)
@@ -724,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)
@@ -737,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)
@@ -750,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)
@@ -763,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)
@@ -773,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>
@@ -789,29 +1032,31 @@ 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 String.Format("({0}, {1}, {2})", new object[]
- {
- x.ToString(),
- y.ToString(),
- z.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 String.Format("({0}, {1}, {2})", new object[]
- {
- x.ToString(format),
- y.ToString(format),
- z.ToString(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 bf25ba9cb3..474876fc91 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3i.cs
@@ -1,11 +1,10 @@
-using System;
-using System.Runtime.InteropServices;
-
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
using real_t = System.Single;
#endif
+using System;
+using System.Runtime.InteropServices;
namespace Godot
{
@@ -22,28 +21,46 @@ namespace Godot
/// </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 +106,25 @@ namespace Godot
}
/// <summary>
- /// Returns the squared distance between this vector and `b`.
+ /// Returns a new vector with all components clamped between the
+ /// 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>
+ /// <param name="max">The vector with maximum allowed values.</param>
+ /// <returns>The vector with all components clamped.</returns>
+ public Vector3i Clamp(Vector3i min, Vector3i max)
+ {
+ return new Vector3i
+ (
+ Mathf.Clamp(x, min.x, max.x),
+ Mathf.Clamp(y, min.y, max.y),
+ Mathf.Clamp(z, min.z, max.z)
+ );
+ }
+
+ /// <summary>
+ /// Returns the squared distance between this vector and <paramref name="b"/>.
/// 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>
@@ -101,8 +136,9 @@ namespace Godot
}
/// <summary>
- /// Returns the distance between this vector and `b`.
+ /// Returns the distance between this vector and <paramref name="b"/>.
/// </summary>
+ /// <seealso cref="DistanceSquaredTo(Vector3i)"/>
/// <param name="b">The other vector to use.</param>
/// <returns>The distance between the two vectors.</returns>
public real_t DistanceTo(Vector3i b)
@@ -111,7 +147,7 @@ namespace Godot
}
/// <summary>
- /// Returns the dot product of this vector and `b`.
+ /// Returns the dot product of this vector and <paramref name="b"/>.
/// </summary>
/// <param name="b">The other vector to use.</param>
/// <returns>The dot product of the two vectors.</returns>
@@ -123,6 +159,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()
{
@@ -169,10 +206,13 @@ namespace Godot
}
/// <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;
@@ -183,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;
@@ -201,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;
@@ -223,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>
@@ -304,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;
@@ -312,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;
@@ -320,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;
@@ -328,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;
@@ -336,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;
@@ -344,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;
@@ -352,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;
@@ -360,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;
@@ -368,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;
@@ -376,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;
@@ -384,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;
@@ -392,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;
@@ -400,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)
@@ -482,34 +698,41 @@ 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 String.Format("({0}, {1}, {2})", new object[]
- {
- this.x.ToString(),
- this.y.ToString(),
- this.z.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 String.Format("({0}, {1}, {2})", new object[]
- {
- this.x.ToString(format),
- this.y.ToString(format),
- this.z.ToString(format)
- });
+ return $"({x.ToString(format)}, {y.ToString(format)}, {z.ToString(format)})";
}
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index 86a16c17f1..1fcfe74c86 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -14,9 +14,12 @@
<ItemGroup>
<Compile Include="Core\AABB.cs" />
<Compile Include="Core\Array.cs" />
+ <Compile Include="Core\Attributes\AssemblyHasScriptsAttribute.cs" />
+ <Compile Include="Core\Attributes\DisableGodotGeneratorsAttribute.cs" />
<Compile Include="Core\Attributes\ExportAttribute.cs" />
<Compile Include="Core\Attributes\GodotMethodAttribute.cs" />
<Compile Include="Core\Attributes\RPCAttributes.cs" />
+ <Compile Include="Core\Attributes\ScriptPathAttribute.cs" />
<Compile Include="Core\Attributes\SignalAttribute.cs" />
<Compile Include="Core\Attributes\ToolAttribute.cs" />
<Compile Include="Core\Basis.cs" />
@@ -30,12 +33,14 @@
<Compile Include="Core\DynamicObject.cs" />
<Compile Include="Core\Extensions\NodeExtensions.cs" />
<Compile Include="Core\Extensions\ObjectExtensions.cs" />
+ <Compile Include="Core\Extensions\PackedSceneExtensions.cs" />
<Compile Include="Core\Extensions\ResourceLoaderExtensions.cs" />
<Compile Include="Core\Extensions\SceneTreeExtensions.cs" />
<Compile Include="Core\GD.cs" />
<Compile Include="Core\GodotSynchronizationContext.cs" />
<Compile Include="Core\GodotTaskScheduler.cs" />
<Compile Include="Core\GodotTraceListener.cs" />
+ <Compile Include="Core\GodotUnhandledExceptionEvent.cs" />
<Compile Include="Core\Interfaces\IAwaitable.cs" />
<Compile Include="Core\Interfaces\IAwaiter.cs" />
<Compile Include="Core\Interfaces\ISerializationListener.cs" />
@@ -45,7 +50,7 @@
<Compile Include="Core\NodePath.cs" />
<Compile Include="Core\Object.base.cs" />
<Compile Include="Core\Plane.cs" />
- <Compile Include="Core\Quat.cs" />
+ <Compile Include="Core\Quaternion.cs" />
<Compile Include="Core\Rect2.cs" />
<Compile Include="Core\Rect2i.cs" />
<Compile Include="Core\RID.cs" />
@@ -53,8 +58,9 @@
<Compile Include="Core\SignalAwaiter.cs" />
<Compile Include="Core\StringExtensions.cs" />
<Compile Include="Core\StringName.cs" />
- <Compile Include="Core\Transform.cs" />
<Compile Include="Core\Transform2D.cs" />
+ <Compile Include="Core\Transform3D.cs" />
+ <Compile Include="Core\UnhandledExceptionArgs.cs" />
<Compile Include="Core\Vector2.cs" />
<Compile Include="Core\Vector2i.cs" />
<Compile Include="Core\Vector3.cs" />
diff --git a/modules/mono/glue/arguments_vector.h b/modules/mono/glue/arguments_vector.h
index ab48904571..9ba6a05ac6 100644
--- a/modules/mono/glue/arguments_vector.h
+++ b/modules/mono/glue/arguments_vector.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/glue/base_object_glue.cpp b/modules/mono/glue/base_object_glue.cpp
index ebcd6d5e9c..6c5503a3bd 100644
--- a/modules/mono/glue/base_object_glue.cpp
+++ b/modules/mono/glue/base_object_glue.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,10 +30,9 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/class_db.h"
-#include "core/object.h"
-#include "core/reference.h"
-#include "core/string_name.h"
+#include "core/object/class_db.h"
+#include "core/object/ref_counted.h"
+#include "core/string/string_name.h"
#include "../csharp_script.h"
#include "../mono_gd/gd_mono_cache.h"
@@ -66,7 +65,7 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
}
}
- void *data = p_ptr->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
+ void *data = CSharpLanguage::get_existing_instance_binding(p_ptr);
if (data) {
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
@@ -79,17 +78,17 @@ void godot_icall_Object_Disposed(MonoObject *p_obj, Object *p_ptr) {
}
}
-void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) {
+void godot_icall_RefCounted_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolean p_is_finalizer) {
#ifdef DEBUG_ENABLED
CRASH_COND(p_ptr == nullptr);
- // This is only called with Reference derived classes
- CRASH_COND(!Object::cast_to<Reference>(p_ptr));
+ // This is only called with RefCounted derived classes
+ CRASH_COND(!Object::cast_to<RefCounted>(p_ptr));
#endif
- Reference *ref = static_cast<Reference *>(p_ptr);
+ RefCounted *rc = static_cast<RefCounted *>(p_ptr);
- if (ref->get_script_instance()) {
- CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(ref->get_script_instance());
+ if (rc->get_script_instance()) {
+ CSharpInstance *cs_instance = CAST_CSHARP_INSTANCE(rc->get_script_instance());
if (cs_instance) {
if (!cs_instance->is_destructing_script_instance()) {
bool delete_owner;
@@ -98,9 +97,9 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolea
cs_instance->mono_object_disposed_baseref(p_obj, p_is_finalizer, delete_owner, remove_script_instance);
if (delete_owner) {
- memdelete(ref);
+ memdelete(rc);
} else if (remove_script_instance) {
- ref->set_script_instance(nullptr);
+ rc->set_script_instance(nullptr);
}
}
return;
@@ -109,11 +108,11 @@ void godot_icall_Reference_Disposed(MonoObject *p_obj, Object *p_ptr, MonoBoolea
// Unsafe refcount decrement. The managed instance also counts as a reference.
// See: CSharpLanguage::alloc_instance_binding_data(Object *p_object)
- CSharpLanguage::get_singleton()->pre_unsafe_unreference(ref);
- if (ref->unreference()) {
- memdelete(ref);
+ CSharpLanguage::get_singleton()->pre_unsafe_unreference(rc);
+ if (rc->unreference()) {
+ memdelete(rc);
} else {
- void *data = ref->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
+ void *data = CSharpLanguage::get_existing_instance_binding(rc);
if (data) {
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
@@ -146,27 +145,27 @@ MonoObject *godot_icall_Object_weakref(Object *p_ptr) {
}
Ref<WeakRef> wref;
- Reference *ref = Object::cast_to<Reference>(p_ptr);
+ RefCounted *rc = Object::cast_to<RefCounted>(p_ptr);
- if (ref) {
- REF r = ref;
+ if (rc) {
+ REF r = rc;
if (!r.is_valid()) {
return nullptr;
}
- wref.instance();
+ wref.instantiate();
wref->set_ref(r);
} else {
- wref.instance();
+ wref.instantiate();
wref->set_obj(p_ptr);
}
return GDMonoUtils::unmanaged_get_managed(wref.ptr());
}
-Error godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) {
+int32_t godot_icall_SignalAwaiter_connect(Object *p_source, StringName *p_signal, Object *p_target, MonoObject *p_awaiter) {
StringName signal = p_signal ? *p_signal : StringName();
- return gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
+ return (int32_t)gd_mono_connect_signal_awaiter(p_source, signal, p_target, p_awaiter);
}
MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
@@ -176,8 +175,8 @@ MonoArray *godot_icall_DynamicGodotObject_SetMemberList(Object *p_ptr) {
MonoArray *result = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), property_list.size());
int i = 0;
- for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
- MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get().name);
+ for (const PropertyInfo &E : property_list) {
+ MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E.name);
mono_array_setref(result, i, boxed);
i++;
}
@@ -241,18 +240,18 @@ MonoString *godot_icall_Object_ToString(Object *p_ptr) {
}
void godot_register_object_icalls() {
- mono_add_internal_call("Godot.Object::godot_icall_Object_Ctor", (void *)godot_icall_Object_Ctor);
- mono_add_internal_call("Godot.Object::godot_icall_Object_Disposed", (void *)godot_icall_Object_Disposed);
- mono_add_internal_call("Godot.Object::godot_icall_Reference_Disposed", (void *)godot_icall_Reference_Disposed);
- mono_add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", (void *)godot_icall_Object_ConnectEventSignals);
- mono_add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", (void *)godot_icall_Object_ClassDB_get_method);
- mono_add_internal_call("Godot.Object::godot_icall_Object_ToString", (void *)godot_icall_Object_ToString);
- mono_add_internal_call("Godot.Object::godot_icall_Object_weakref", (void *)godot_icall_Object_weakref);
- mono_add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", (void *)godot_icall_SignalAwaiter_connect);
- mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", (void *)godot_icall_DynamicGodotObject_SetMemberList);
- mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", (void *)godot_icall_DynamicGodotObject_InvokeMember);
- mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", (void *)godot_icall_DynamicGodotObject_GetMember);
- mono_add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", (void *)godot_icall_DynamicGodotObject_SetMember);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Ctor", godot_icall_Object_Ctor);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_Disposed", godot_icall_Object_Disposed);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_RefCounted_Disposed", godot_icall_RefCounted_Disposed);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ConnectEventSignals", godot_icall_Object_ConnectEventSignals);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ClassDB_get_method", godot_icall_Object_ClassDB_get_method);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_ToString", godot_icall_Object_ToString);
+ GDMonoUtils::add_internal_call("Godot.Object::godot_icall_Object_weakref", godot_icall_Object_weakref);
+ GDMonoUtils::add_internal_call("Godot.SignalAwaiter::godot_icall_SignalAwaiter_connect", godot_icall_SignalAwaiter_connect);
+ GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMemberList", godot_icall_DynamicGodotObject_SetMemberList);
+ GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_InvokeMember", godot_icall_DynamicGodotObject_InvokeMember);
+ GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_GetMember", godot_icall_DynamicGodotObject_GetMember);
+ GDMonoUtils::add_internal_call("Godot.DynamicGodotObject::godot_icall_DynamicGodotObject_SetMember", godot_icall_DynamicGodotObject_SetMember);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/callable_glue.cpp b/modules/mono/glue/callable_glue.cpp
new file mode 100644
index 0000000000..54b65fdb94
--- /dev/null
+++ b/modules/mono/glue/callable_glue.cpp
@@ -0,0 +1,79 @@
+/*************************************************************************/
+/* callable_glue.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 MONO_GLUE_ENABLED
+
+#include "../mono_gd/gd_mono_marshal.h"
+#include "arguments_vector.h"
+
+MonoObject *godot_icall_Callable_Call(GDMonoMarshal::M_Callable *p_callable, MonoArray *p_args) {
+ Callable callable = GDMonoMarshal::managed_to_callable(*p_callable);
+
+ int argc = mono_array_length(p_args);
+
+ ArgumentsVector<Variant> arg_store(argc);
+ ArgumentsVector<const Variant *> args(argc);
+
+ 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));
+ }
+
+ Variant result;
+ Callable::CallError error;
+ callable.call(args.ptr(), argc, result, error);
+
+ return GDMonoMarshal::variant_to_mono_object(result);
+}
+
+void godot_icall_Callable_CallDeferred(GDMonoMarshal::M_Callable *p_callable, MonoArray *p_args) {
+ Callable callable = GDMonoMarshal::managed_to_callable(*p_callable);
+
+ int argc = mono_array_length(p_args);
+
+ ArgumentsVector<Variant> arg_store(argc);
+ ArgumentsVector<const Variant *> args(argc);
+
+ 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));
+ }
+
+ 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 3313e8cb77..86976de244 100644
--- a/modules/mono/glue/collections_glue.cpp
+++ b/modules/mono/glue/collections_glue.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#include <mono/metadata/exception.h>
-#include "core/array.h"
+#include "core/variant/array.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_class.h"
@@ -47,7 +47,7 @@ void godot_icall_Array_Dtor(Array *ptr) {
memdelete(ptr);
}
-MonoObject *godot_icall_Array_At(Array *ptr, int index) {
+MonoObject *godot_icall_Array_At(Array *ptr, int32_t index) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return nullptr;
@@ -55,7 +55,7 @@ MonoObject *godot_icall_Array_At(Array *ptr, int index) {
return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index));
}
-MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_encoding, GDMonoClass *type_class) {
+MonoObject *godot_icall_Array_At_Generic(Array *ptr, int32_t index, uint32_t type_encoding, GDMonoClass *type_class) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return nullptr;
@@ -63,7 +63,7 @@ MonoObject *godot_icall_Array_At_Generic(Array *ptr, int index, uint32_t type_en
return GDMonoMarshal::variant_to_mono_object(ptr->operator[](index), ManagedType(type_encoding, type_class));
}
-void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) {
+void godot_icall_Array_SetAt(Array *ptr, int32_t index, MonoObject *value) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return;
@@ -71,11 +71,11 @@ void godot_icall_Array_SetAt(Array *ptr, int index, MonoObject *value) {
ptr->operator[](index) = GDMonoMarshal::mono_object_to_variant(value);
}
-int godot_icall_Array_Count(Array *ptr) {
+int32_t godot_icall_Array_Count(Array *ptr) {
return ptr->size();
}
-int godot_icall_Array_Add(Array *ptr, MonoObject *item) {
+int32_t godot_icall_Array_Add(Array *ptr, MonoObject *item) {
ptr->append(GDMonoMarshal::mono_object_to_variant(item));
return ptr->size();
}
@@ -88,7 +88,7 @@ MonoBoolean godot_icall_Array_Contains(Array *ptr, MonoObject *item) {
return ptr->find(GDMonoMarshal::mono_object_to_variant(item)) != -1;
}
-void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int array_index) {
+void godot_icall_Array_CopyTo(Array *ptr, MonoArray *array, int32_t array_index) {
unsigned int count = ptr->size();
if (mono_array_length(array) < (array_index + count)) {
@@ -129,11 +129,11 @@ Array *godot_icall_Array_Concatenate(Array *left, Array *right) {
return new_array;
}
-int godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
+int32_t godot_icall_Array_IndexOf(Array *ptr, MonoObject *item) {
return ptr->find(GDMonoMarshal::mono_object_to_variant(item));
}
-void godot_icall_Array_Insert(Array *ptr, int index, MonoObject *item) {
+void godot_icall_Array_Insert(Array *ptr, int32_t index, MonoObject *item) {
if (index < 0 || index > ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return;
@@ -150,7 +150,7 @@ MonoBoolean godot_icall_Array_Remove(Array *ptr, MonoObject *item) {
return false;
}
-void godot_icall_Array_RemoveAt(Array *ptr, int index) {
+void godot_icall_Array_RemoveAt(Array *ptr, int32_t index) {
if (index < 0 || index >= ptr->size()) {
GDMonoUtils::set_pending_exception(mono_get_exception_index_out_of_range());
return;
@@ -158,8 +158,12 @@ void godot_icall_Array_RemoveAt(Array *ptr, int index) {
ptr->remove(index);
}
-Error godot_icall_Array_Resize(Array *ptr, int new_size) {
- return ptr->resize(new_size);
+int32_t godot_icall_Array_Resize(Array *ptr, int32_t new_size) {
+ return (int32_t)ptr->resize(new_size);
+}
+
+void godot_icall_Array_Shuffle(Array *ptr) {
+ ptr->shuffle();
}
void godot_icall_Array_Generic_GetElementTypeInfo(MonoReflectionType *refltype, uint32_t *type_encoding, GDMonoClass **type_class) {
@@ -222,10 +226,23 @@ Array *godot_icall_Dictionary_Values(Dictionary *ptr) {
return memnew(Array(ptr->values()));
}
-int godot_icall_Dictionary_Count(Dictionary *ptr) {
+int32_t godot_icall_Dictionary_Count(Dictionary *ptr) {
return ptr->size();
}
+int32_t godot_icall_Dictionary_KeyValuePairs(Dictionary *ptr, Array **keys, Array **values) {
+ *keys = godot_icall_Dictionary_Keys(ptr);
+ *values = godot_icall_Dictionary_Values(ptr);
+ return godot_icall_Dictionary_Count(ptr);
+}
+
+void godot_icall_Dictionary_KeyValuePairAt(Dictionary *ptr, int index, MonoObject **key, MonoObject **value) {
+ Array *keys = godot_icall_Dictionary_Keys(ptr);
+ Array *values = godot_icall_Dictionary_Values(ptr);
+ *key = GDMonoMarshal::variant_to_mono_object(keys->get(index));
+ *value = GDMonoMarshal::variant_to_mono_object(values->get(index));
+}
+
void godot_icall_Dictionary_Add(Dictionary *ptr, MonoObject *key, MonoObject *value) {
Variant varKey = GDMonoMarshal::mono_object_to_variant(key);
Variant *ret = ptr->getptr(varKey);
@@ -304,46 +321,49 @@ MonoString *godot_icall_Dictionary_ToString(Dictionary *ptr) {
}
void godot_register_collections_icalls() {
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", (void *)godot_icall_Array_Ctor);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", (void *)godot_icall_Array_Ctor_MonoArray);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", (void *)godot_icall_Array_Dtor);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At", (void *)godot_icall_Array_At);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", (void *)godot_icall_Array_At_Generic);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", (void *)godot_icall_Array_SetAt);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", (void *)godot_icall_Array_Count);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", (void *)godot_icall_Array_Add);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", (void *)godot_icall_Array_Clear);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", (void *)godot_icall_Array_Concatenate);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", (void *)godot_icall_Array_Contains);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", (void *)godot_icall_Array_CopyTo);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", (void *)godot_icall_Array_Duplicate);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", (void *)godot_icall_Array_IndexOf);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", (void *)godot_icall_Array_Insert);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", (void *)godot_icall_Array_Remove);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", (void *)godot_icall_Array_RemoveAt);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", (void *)godot_icall_Array_Resize);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", (void *)godot_icall_Array_Generic_GetElementTypeInfo);
- mono_add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", (void *)godot_icall_Array_ToString);
-
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", (void *)godot_icall_Dictionary_Ctor);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", (void *)godot_icall_Dictionary_Dtor);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", (void *)godot_icall_Dictionary_GetValue);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue_Generic", (void *)godot_icall_Dictionary_GetValue_Generic);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", (void *)godot_icall_Dictionary_SetValue);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", (void *)godot_icall_Dictionary_Keys);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", (void *)godot_icall_Dictionary_Values);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", (void *)godot_icall_Dictionary_Count);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", (void *)godot_icall_Dictionary_Add);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", (void *)godot_icall_Dictionary_Clear);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", (void *)godot_icall_Dictionary_Contains);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", (void *)godot_icall_Dictionary_ContainsKey);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", (void *)godot_icall_Dictionary_Duplicate);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", (void *)godot_icall_Dictionary_RemoveKey);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", (void *)godot_icall_Dictionary_Remove);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", (void *)godot_icall_Dictionary_TryGetValue);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", (void *)godot_icall_Dictionary_TryGetValue_Generic);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", (void *)godot_icall_Dictionary_Generic_GetValueTypeInfo);
- mono_add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", (void *)godot_icall_Dictionary_ToString);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor", godot_icall_Array_Ctor);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Ctor_MonoArray", godot_icall_Array_Ctor_MonoArray);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Dtor", godot_icall_Array_Dtor);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At", godot_icall_Array_At);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_At_Generic", godot_icall_Array_At_Generic);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_SetAt", godot_icall_Array_SetAt);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Count", godot_icall_Array_Count);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Add", godot_icall_Array_Add);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Clear", godot_icall_Array_Clear);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Concatenate", godot_icall_Array_Concatenate);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Contains", godot_icall_Array_Contains);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_CopyTo", godot_icall_Array_CopyTo);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Duplicate", godot_icall_Array_Duplicate);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_IndexOf", godot_icall_Array_IndexOf);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Insert", godot_icall_Array_Insert);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Remove", godot_icall_Array_Remove);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_RemoveAt", godot_icall_Array_RemoveAt);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Resize", godot_icall_Array_Resize);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Shuffle", godot_icall_Array_Shuffle);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_Generic_GetElementTypeInfo", godot_icall_Array_Generic_GetElementTypeInfo);
+ GDMonoUtils::add_internal_call("Godot.Collections.Array::godot_icall_Array_ToString", godot_icall_Array_ToString);
+
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Ctor", godot_icall_Dictionary_Ctor);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Dtor", godot_icall_Dictionary_Dtor);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue", godot_icall_Dictionary_GetValue);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_GetValue_Generic", godot_icall_Dictionary_GetValue_Generic);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_SetValue", godot_icall_Dictionary_SetValue);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Keys", godot_icall_Dictionary_Keys);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Values", godot_icall_Dictionary_Values);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Count", godot_icall_Dictionary_Count);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairs", godot_icall_Dictionary_KeyValuePairs);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_KeyValuePairAt", godot_icall_Dictionary_KeyValuePairAt);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Add", godot_icall_Dictionary_Add);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Clear", godot_icall_Dictionary_Clear);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Contains", godot_icall_Dictionary_Contains);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ContainsKey", godot_icall_Dictionary_ContainsKey);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Duplicate", godot_icall_Dictionary_Duplicate);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_RemoveKey", godot_icall_Dictionary_RemoveKey);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Remove", godot_icall_Dictionary_Remove);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue", godot_icall_Dictionary_TryGetValue);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_TryGetValue_Generic", godot_icall_Dictionary_TryGetValue_Generic);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_Generic_GetValueTypeInfo", godot_icall_Dictionary_Generic_GetValueTypeInfo);
+ GDMonoUtils::add_internal_call("Godot.Collections.Dictionary::godot_icall_Dictionary_ToString", godot_icall_Dictionary_ToString);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/gd_glue.cpp b/modules/mono/glue/gd_glue.cpp
index 5e892b616b..07ddf5d945 100644
--- a/modules/mono/glue/gd_glue.cpp
+++ b/modules/mono/glue/gd_glue.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,12 +30,12 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/array.h"
#include "core/io/marshalls.h"
#include "core/os/os.h"
-#include "core/ustring.h"
-#include "core/variant.h"
-#include "core/variant_parser.h"
+#include "core/string/ustring.h"
+#include "core/variant/array.h"
+#include "core/variant/variant.h"
+#include "core/variant/variant_parser.h"
#include "../mono_gd/gd_mono_cache.h"
#include "../mono_gd/gd_mono_marshal.h"
@@ -55,7 +55,8 @@ MonoObject *godot_icall_GD_convert(MonoObject *p_what, int32_t p_type) {
Variant what = GDMonoMarshal::mono_object_to_variant(p_what);
const Variant *args[1] = { &what };
Callable::CallError ce;
- Variant ret = Variant::construct(Variant::Type(p_type), args, 1, ce);
+ Variant ret;
+ Variant::construct(Variant::Type(p_type), ret, args, 1, ce);
ERR_FAIL_COND_V(ce.error != Callable::CallError::CALL_OK, nullptr);
return GDMonoMarshal::variant_to_mono_object(ret);
}
@@ -181,22 +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_rand_range(double from, double to) {
+int32_t godot_icall_GD_randi_range(int32_t from, int32_t to) {
return Math::random(from, 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;
@@ -284,32 +293,34 @@ MonoObject *godot_icall_DefaultGodotTaskScheduler() {
}
void godot_register_gd_icalls() {
- mono_add_internal_call("Godot.GD::godot_icall_GD_bytes2var", (void *)godot_icall_GD_bytes2var);
- mono_add_internal_call("Godot.GD::godot_icall_GD_convert", (void *)godot_icall_GD_convert);
- mono_add_internal_call("Godot.GD::godot_icall_GD_hash", (void *)godot_icall_GD_hash);
- mono_add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", (void *)godot_icall_GD_instance_from_id);
- mono_add_internal_call("Godot.GD::godot_icall_GD_pusherror", (void *)godot_icall_GD_pusherror);
- mono_add_internal_call("Godot.GD::godot_icall_GD_pushwarning", (void *)godot_icall_GD_pushwarning);
- mono_add_internal_call("Godot.GD::godot_icall_GD_print", (void *)godot_icall_GD_print);
- mono_add_internal_call("Godot.GD::godot_icall_GD_printerr", (void *)godot_icall_GD_printerr);
- mono_add_internal_call("Godot.GD::godot_icall_GD_printraw", (void *)godot_icall_GD_printraw);
- mono_add_internal_call("Godot.GD::godot_icall_GD_prints", (void *)godot_icall_GD_prints);
- mono_add_internal_call("Godot.GD::godot_icall_GD_printt", (void *)godot_icall_GD_printt);
- mono_add_internal_call("Godot.GD::godot_icall_GD_randf", (void *)godot_icall_GD_randf);
- mono_add_internal_call("Godot.GD::godot_icall_GD_randi", (void *)godot_icall_GD_randi);
- mono_add_internal_call("Godot.GD::godot_icall_GD_randomize", (void *)godot_icall_GD_randomize);
- mono_add_internal_call("Godot.GD::godot_icall_GD_rand_range", (void *)godot_icall_GD_rand_range);
- mono_add_internal_call("Godot.GD::godot_icall_GD_rand_seed", (void *)godot_icall_GD_rand_seed);
- mono_add_internal_call("Godot.GD::godot_icall_GD_seed", (void *)godot_icall_GD_seed);
- mono_add_internal_call("Godot.GD::godot_icall_GD_str", (void *)godot_icall_GD_str);
- mono_add_internal_call("Godot.GD::godot_icall_GD_str2var", (void *)godot_icall_GD_str2var);
- mono_add_internal_call("Godot.GD::godot_icall_GD_type_exists", (void *)godot_icall_GD_type_exists);
- mono_add_internal_call("Godot.GD::godot_icall_GD_var2bytes", (void *)godot_icall_GD_var2bytes);
- mono_add_internal_call("Godot.GD::godot_icall_GD_var2str", (void *)godot_icall_GD_var2str);
- mono_add_internal_call("Godot.GD::godot_icall_TypeToVariantType", (void *)godot_icall_TypeToVariantType);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_bytes2var", godot_icall_GD_bytes2var);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_convert", godot_icall_GD_convert);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_hash", godot_icall_GD_hash);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_instance_from_id", godot_icall_GD_instance_from_id);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pusherror", godot_icall_GD_pusherror);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_pushwarning", godot_icall_GD_pushwarning);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_print", godot_icall_GD_print);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printerr", godot_icall_GD_printerr);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printraw", godot_icall_GD_printraw);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_prints", godot_icall_GD_prints);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_printt", godot_icall_GD_printt);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_randomize", godot_icall_GD_randomize);
+ 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);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_str2var", godot_icall_GD_str2var);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_type_exists", godot_icall_GD_type_exists);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2bytes", godot_icall_GD_var2bytes);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_GD_var2str", godot_icall_GD_var2str);
+ GDMonoUtils::add_internal_call("Godot.GD::godot_icall_TypeToVariantType", godot_icall_TypeToVariantType);
// Dispatcher
- mono_add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", (void *)godot_icall_DefaultGodotTaskScheduler);
+ GDMonoUtils::add_internal_call("Godot.Dispatcher::godot_icall_DefaultGodotTaskScheduler", godot_icall_DefaultGodotTaskScheduler);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/glue_header.h b/modules/mono/glue/glue_header.h
index c1f1936711..074220bb9b 100644
--- a/modules/mono/glue/glue_header.h
+++ b/modules/mono/glue/glue_header.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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();
@@ -58,16 +60,15 @@ void godot_register_glue_header_icalls() {
// Used by the generated glue
-#include "core/array.h"
-#include "core/class_db.h"
-#include "core/dictionary.h"
-#include "core/engine.h"
-#include "core/method_bind.h"
-#include "core/node_path.h"
-#include "core/object.h"
-#include "core/reference.h"
+#include "core/config/engine.h"
+#include "core/object/class_db.h"
+#include "core/object/method_bind.h"
+#include "core/object/ref_counted.h"
+#include "core/string/node_path.h"
+#include "core/string/ustring.h"
#include "core/typedefs.h"
-#include "core/ustring.h"
+#include "core/variant/array.h"
+#include "core/variant/dictionary.h"
#include "../mono_gd/gd_mono_class.h"
#include "../mono_gd/gd_mono_internals.h"
diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/nodepath_glue.cpp
index 2aa75dd309..4ddb94e1a8 100644
--- a/modules/mono/glue/nodepath_glue.cpp
+++ b/modules/mono/glue/nodepath_glue.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,8 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/node_path.h"
-#include "core/ustring.h"
+#include "core/string/node_path.h"
+#include "core/string/ustring.h"
#include "../mono_gd/gd_mono_marshal.h"
@@ -81,17 +81,17 @@ MonoBoolean godot_icall_NodePath_is_empty(NodePath *p_ptr) {
}
void godot_register_nodepath_icalls() {
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_Ctor", (void *)godot_icall_NodePath_Ctor);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", (void *)godot_icall_NodePath_Dtor);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", (void *)godot_icall_NodePath_operator_String);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", (void *)godot_icall_NodePath_get_as_property_path);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", (void *)godot_icall_NodePath_get_concatenated_subnames);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", (void *)godot_icall_NodePath_get_name);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", (void *)godot_icall_NodePath_get_name_count);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", (void *)godot_icall_NodePath_get_subname);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", (void *)godot_icall_NodePath_get_subname_count);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", (void *)godot_icall_NodePath_is_absolute);
- mono_add_internal_call("Godot.NodePath::godot_icall_NodePath_is_empty", (void *)godot_icall_NodePath_is_empty);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Ctor", godot_icall_NodePath_Ctor);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", godot_icall_NodePath_Dtor);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", godot_icall_NodePath_operator_String);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", godot_icall_NodePath_get_as_property_path);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", godot_icall_NodePath_get_concatenated_subnames);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", godot_icall_NodePath_get_name);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", godot_icall_NodePath_get_name_count);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname", godot_icall_NodePath_get_subname);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_subname_count", godot_icall_NodePath_get_subname_count);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_absolute", godot_icall_NodePath_is_absolute);
+ GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_is_empty", godot_icall_NodePath_is_empty);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/rid_glue.cpp b/modules/mono/glue/rid_glue.cpp
index 6d2e6b559f..f464e63a81 100644
--- a/modules/mono/glue/rid_glue.cpp
+++ b/modules/mono/glue/rid_glue.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,9 +30,9 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/object.h"
-#include "core/resource.h"
-#include "core/rid.h"
+#include "core/io/resource.h"
+#include "core/object/class_db.h"
+#include "core/templates/rid.h"
#include "../mono_gd/gd_mono_marshal.h"
@@ -56,9 +56,9 @@ uint32_t godot_icall_RID_get_id(RID *p_ptr) {
}
void godot_register_rid_icalls() {
- mono_add_internal_call("Godot.RID::godot_icall_RID_Ctor", (void *)godot_icall_RID_Ctor);
- mono_add_internal_call("Godot.RID::godot_icall_RID_Dtor", (void *)godot_icall_RID_Dtor);
- mono_add_internal_call("Godot.RID::godot_icall_RID_get_id", (void *)godot_icall_RID_get_id);
+ GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Ctor", godot_icall_RID_Ctor);
+ GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_Dtor", godot_icall_RID_Dtor);
+ GDMonoUtils::add_internal_call("Godot.RID::godot_icall_RID_get_id", godot_icall_RID_get_id);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/scene_tree_glue.cpp b/modules/mono/glue/scene_tree_glue.cpp
index b43daccc1b..5a6fd69db8 100644
--- a/modules/mono/glue/scene_tree_glue.cpp
+++ b/modules/mono/glue/scene_tree_glue.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,9 +30,9 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/array.h"
-#include "core/class_db.h"
-#include "core/string_name.h"
+#include "core/object/class_db.h"
+#include "core/string/string_name.h"
+#include "core/variant/array.h"
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
@@ -48,7 +48,7 @@ Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringNa
ptr->get_nodes_in_group(*group, &nodes);
// No need to bother if the group is empty
- if (!nodes.empty()) {
+ if (!nodes.is_empty()) {
MonoType *elem_type = mono_reflection_type_get_type(refltype);
MonoClass *mono_class = mono_class_from_mono_type(elem_type);
GDMonoClass *klass = GDMono::get_singleton()->get_class(mono_class);
@@ -80,7 +80,7 @@ Array *godot_icall_SceneTree_get_nodes_in_group_Generic(SceneTree *ptr, StringNa
}
void godot_register_scene_tree_icalls() {
- mono_add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", (void *)godot_icall_SceneTree_get_nodes_in_group_Generic);
+ GDMonoUtils::add_internal_call("Godot.SceneTree::godot_icall_SceneTree_get_nodes_in_group_Generic", godot_icall_SceneTree_get_nodes_in_group_Generic);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/string_glue.cpp b/modules/mono/glue/string_glue.cpp
index 595b8d71f1..bb80a836ad 100644
--- a/modules/mono/glue/string_glue.cpp
+++ b/modules/mono/glue/string_glue.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,9 +30,9 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/ustring.h"
-#include "core/variant.h"
-#include "core/vector.h"
+#include "core/string/ustring.h"
+#include "core/templates/vector.h"
+#include "core/variant/variant.h"
#include "../mono_gd/gd_mono_marshal.h"
@@ -67,13 +67,19 @@ MonoString *godot_icall_String_sha256_text(MonoString *p_str) {
return GDMonoMarshal::mono_string_from_godot(ret);
}
+MonoString *godot_icall_String_simplify_path(MonoString *p_str) {
+ String ret = GDMonoMarshal::mono_string_to_godot(p_str).simplify_path();
+ return GDMonoMarshal::mono_string_from_godot(ret);
+}
+
void godot_register_string_icalls() {
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", (void *)godot_icall_String_md5_buffer);
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", (void *)godot_icall_String_md5_text);
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", (void *)godot_icall_String_rfind);
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", (void *)godot_icall_String_rfindn);
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", (void *)godot_icall_String_sha256_buffer);
- mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", (void *)godot_icall_String_sha256_text);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", godot_icall_String_md5_buffer);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", godot_icall_String_md5_text);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", godot_icall_String_rfind);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", godot_icall_String_rfindn);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", godot_icall_String_sha256_buffer);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", godot_icall_String_sha256_text);
+ GDMonoUtils::add_internal_call("Godot.StringExtensions::godot_icall_String_simplify_path", godot_icall_String_simplify_path);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/glue/string_name_glue.cpp b/modules/mono/glue/string_name_glue.cpp
index 4b2e88569b..f537896559 100644
--- a/modules/mono/glue/string_name_glue.cpp
+++ b/modules/mono/glue/string_name_glue.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,8 @@
#ifdef MONO_GLUE_ENABLED
-#include "core/string_name.h"
-#include "core/ustring.h"
+#include "core/string/string_name.h"
+#include "core/string/ustring.h"
#include "../mono_gd/gd_mono_marshal.h"
@@ -53,10 +53,10 @@ MonoBoolean godot_icall_StringName_is_empty(StringName *p_ptr) {
}
void godot_register_string_name_icalls() {
- mono_add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", (void *)godot_icall_StringName_Ctor);
- mono_add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", (void *)godot_icall_StringName_Dtor);
- mono_add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", (void *)godot_icall_StringName_operator_String);
- mono_add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", (void *)godot_icall_StringName_is_empty);
+ GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Ctor", godot_icall_StringName_Ctor);
+ GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_Dtor", godot_icall_StringName_Dtor);
+ GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_operator_String", godot_icall_StringName_operator_String);
+ GDMonoUtils::add_internal_call("Godot.StringName::godot_icall_StringName_is_empty", godot_icall_StringName_is_empty);
}
#endif // MONO_GLUE_ENABLED
diff --git a/modules/mono/godotsharp_defs.h b/modules/mono/godotsharp_defs.h
index 7d57d0fac3..273dba52f9 100644
--- a/modules/mono/godotsharp_defs.h
+++ b/modules/mono/godotsharp_defs.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index df31823deb..24bd1ed492 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,9 +30,9 @@
#include "godotsharp_dirs.h"
-#include "core/os/dir_access.h"
+#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#ifdef TOOLS_ENABLED
#include "core/version.h"
@@ -63,8 +63,8 @@ String _get_expected_build_config() {
String _get_mono_user_dir() {
#ifdef TOOLS_ENABLED
- if (EditorSettings::get_singleton()) {
- return EditorSettings::get_singleton()->get_data_dir().plus_file("mono");
+ if (EditorPaths::get_singleton()) {
+ return EditorPaths::get_singleton()->get_data_dir().plus_file("mono");
} else {
String settings_path;
@@ -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());
@@ -146,7 +146,7 @@ private:
String appname = ProjectSettings::get_singleton()->get("application/config/name");
String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
- if (appname_safe.empty()) {
+ if (appname_safe.is_empty()) {
appname_safe = "UnnamedProject";
}
@@ -179,16 +179,16 @@ private:
#ifdef OSX_ENABLED
if (!DirAccess::exists(data_editor_tools_dir)) {
- data_editor_tools_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Tools");
+ data_editor_tools_dir = exe_dir.plus_file("../Resources/GodotSharp/Tools");
}
if (!DirAccess::exists(data_editor_prebuilt_api_dir)) {
- data_editor_prebuilt_api_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Api");
+ data_editor_prebuilt_api_dir = exe_dir.plus_file("../Resources/GodotSharp/Api");
}
if (!DirAccess::exists(data_mono_root_dir)) {
data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
- data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib");
+ data_mono_lib_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/lib");
}
#endif
@@ -218,11 +218,11 @@ private:
#ifdef OSX_ENABLED
if (!DirAccess::exists(data_mono_root_dir)) {
data_mono_etc_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/etc");
- data_mono_lib_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Mono/lib");
+ data_mono_lib_dir = exe_dir.plus_file("../Resources/GodotSharp/Mono/lib");
}
if (!DirAccess::exists(data_game_assemblies_dir)) {
- data_game_assemblies_dir = exe_dir.plus_file("../Frameworks/GodotSharp/Assemblies");
+ data_game_assemblies_dir = exe_dir.plus_file("../Resources/GodotSharp/Assemblies");
}
#endif
@@ -322,5 +322,4 @@ String get_data_mono_bin_dir() {
return _GodotSharpDirs::get_singleton().data_mono_bin_dir;
}
#endif
-
} // namespace GodotSharpDirs
diff --git a/modules/mono/godotsharp_dirs.h b/modules/mono/godotsharp_dirs.h
index 2ab4b0e309..3a3c6f980e 100644
--- a/modules/mono/godotsharp_dirs.h
+++ b/modules/mono/godotsharp_dirs.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef GODOTSHARP_DIRS_H
#define GODOTSHARP_DIRS_H
-#include "core/ustring.h"
+#include "core/string/ustring.h"
namespace GodotSharpDirs {
@@ -66,7 +66,6 @@ String get_data_mono_lib_dir();
#ifdef WINDOWS_ENABLED
String get_data_mono_bin_dir();
#endif
-
} // namespace GodotSharpDirs
#endif // GODOTSHARP_DIRS_H
diff --git a/modules/mono/icons/CSharpScript.svg b/modules/mono/icons/CSharpScript.svg
index 69664ca553..0b2cc840f8 100644
--- a/modules/mono/icons/CSharpScript.svg
+++ b/modules/mono/icons/CSharpScript.svg
@@ -1,5 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path d="m6 1046.4c-1.6569 0-3 1.3431-3 3s1.3431 3 3 3h1v-2h-1c-0.55228 0-1-0.4478-1-1 0-0.5523 0.44772-1 1-1h1v-2zm1-9-0.56445 2.2578c-0.23643 0.076-0.46689 0.1692-0.68945 0.2793l-1.9883-1.1933-1.4141 1.414 1.1953 1.9942c-0.11191 0.2211-0.20723 0.4502-0.28516 0.6855l-2.2539 0.5625v2h5.2715c-0.17677-0.3037-0.27041-0.6486-0.27148-1 9.6e-6 -1.1046 0.89543-2 2-2s2 0.8954 2 2c-4.817e-4 0.3512-0.093442 0.6961-0.26953 1h5.2695v-2l-2.2578-0.5645c-0.07594-0.2357-0.1693-0.4655-0.2793-0.6875l1.1934-1.9902-1.4141-1.414-1.9941 1.1953c-0.22113-0.1119-0.45028-0.2073-0.68555-0.2852l-0.5625-2.2539zm4 9c-0.71466-1e-4 -1.3751 0.3811-1.7324 1-0.35727 0.6188-0.35727 1.3812 0 2 0.35733 0.6189 1.0178 1.0001 1.7324 1h-2v2h2c0.71466 1e-4 1.3751-0.3811 1.7324-1 0.35727-0.6188 0.35727-1.3812 0-2-0.35733-0.6189-1.0178-1.0001-1.7324-1h2v-2z" fill="#e0e0e0"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6 1046.4c-1.6569 0-3 1.3431-3 3s1.3431 3 3 3h1v-2h-1c-.55228 0-1-.4478-1-1 0-.5523.44772-1 1-1h1v-2zm1-9-.56445 2.2578c-.23643.076-.46689.1692-.68945.2793l-1.9883-1.1933-1.4141 1.414 1.1953 1.9942c-.11191.2211-.20723.4502-.28516.6855l-2.2539.5625v2h5.2715c-.17677-.3037-.27041-.6486-.27148-1 .0000096-1.1046.89543-2 2-2s2 .8954 2 2c-.0004817.3512-.093442.6961-.26953 1h5.2695v-2l-2.2578-.5645c-.07594-.2357-.1693-.4655-.2793-.6875l1.1934-1.9902-1.4141-1.414-1.9941 1.1953c-.22113-.1119-.45028-.2073-.68555-.2852l-.5625-2.2539zm4 9c-.71466-.0001-1.3751.3811-1.7324 1-.35727.6188-.35727 1.3812 0 2 .35733.6189 1.0178 1.0001 1.7324 1h-2v2h2c.71466.0001 1.3751-.3811 1.7324-1 .35727-.6188.35727-1.3812 0-2-.35733-.6189-1.0178-1.0001-1.7324-1h2v-2z" fill="#e0e0e0" transform="translate(0 -1036.4)"/></svg>
diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp
index dbe9c7fc5d..6d868b527c 100644
--- a/modules/mono/managed_callable.cpp
+++ b/modules/mono/managed_callable.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h
index 4f71e14a2f..c620eee60d 100644
--- a/modules/mono/managed_callable.h
+++ b/modules/mono/managed_callable.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,9 +33,9 @@
#include <mono/metadata/object.h>
-#include "core/callable.h"
#include "core/os/mutex.h"
-#include "core/self_list.h"
+#include "core/templates/self_list.h"
+#include "core/variant/callable.h"
#include "mono_gc_handle.h"
#include "mono_gd/gd_mono_method.h"
diff --git a/modules/mono/mono_gc_handle.cpp b/modules/mono/mono_gc_handle.cpp
index 16a6875406..8583065016 100644
--- a/modules/mono/mono_gc_handle.cpp
+++ b/modules/mono/mono_gc_handle.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/mono_gc_handle.h b/modules/mono/mono_gc_handle.h
index 13cfad4654..d0e51d159f 100644
--- a/modules/mono/mono_gc_handle.h
+++ b/modules/mono/mono_gc_handle.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#include <mono/jit/jit.h>
-#include "core/reference.h"
+#include "core/object/ref_counted.h"
namespace gdmono {
@@ -42,7 +42,6 @@ enum class GCHandleType : char {
STRONG_HANDLE,
WEAK_HANDLE
};
-
}
// Manual release of the GC handle must be done when using this struct
@@ -80,8 +79,8 @@ struct MonoGCHandleData {
static MonoGCHandleData new_weak_handle(MonoObject *p_object);
};
-class MonoGCHandleRef : public Reference {
- GDCLASS(MonoGCHandleRef, Reference);
+class MonoGCHandleRef : public RefCounted {
+ GDCLASS(MonoGCHandleRef, RefCounted);
MonoGCHandleData data;
diff --git a/modules/mono/mono_gd/android_mono_config.h b/modules/mono/mono_gd/android_mono_config.h
index 93f708bba0..9d7cfe1b7c 100644
--- a/modules/mono/mono_gd/android_mono_config.h
+++ b/modules/mono/mono_gd/android_mono_config.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#ifdef ANDROID_ENABLED
-#include "core/ustring.h"
+#include "core/string/ustring.h"
// This function is defined in an auto-generated source file
String get_godot_android_mono_config();
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index cf5ac33d20..52447bc59b 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -37,12 +37,12 @@
#include <mono/metadata/mono-gc.h>
#include <mono/metadata/profiler.h>
+#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/os/os.h"
#include "core/os/thread.h"
-#include "core/project_settings.h"
#include "../csharp_script.h"
#include "../godotsharp_dirs.h"
@@ -73,7 +73,7 @@
#endif
// TODO:
-// This has turn into a gigantic mess. There's too much going on here. Too much #ifdef as well.
+// This has turned into a gigantic mess. There's too much going on here. Too much #ifdef as well.
// It's just painful to read... It needs to be re-structured. Please, clean this up, future me.
GDMono *GDMono::singleton = nullptr;
@@ -100,8 +100,8 @@ void gd_mono_setup_runtime_main_args() {
main_args.write[0] = execpath.ptrw();
int i = 1;
- for (List<String>::Element *E = cmdline_args.front(); E; E = E->next()) {
- CharString &stored = cmdline_args_utf8.push_back(E->get().utf8())->get();
+ for (const String &E : cmdline_args) {
+ CharString &stored = cmdline_args_utf8.push_back(E.utf8())->get();
main_args.write[i] = stored.ptrw();
i++;
}
@@ -119,11 +119,11 @@ void gd_mono_profiler_init() {
const String env_var_name = "MONO_ENV_OPTIONS";
if (OS::get_singleton()->has_environment(env_var_name)) {
- const auto mono_env_ops = OS::get_singleton()->get_environment(env_var_name);
+ const String mono_env_ops = OS::get_singleton()->get_environment(env_var_name);
// Usually MONO_ENV_OPTIONS looks like: --profile=jb:prof=timeline,ctl=remote,host=127.0.0.1:55467
const String prefix = "--profile=";
if (mono_env_ops.begins_with(prefix)) {
- const auto ops = mono_env_ops.substr(prefix.length(), mono_env_ops.length());
+ const String ops = mono_env_ops.substr(prefix.length(), mono_env_ops.length());
mono_profiler_load(ops.utf8());
}
}
@@ -142,7 +142,7 @@ void gd_mono_debug_init() {
int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
if (Engine::get_singleton()->is_editor_hint() ||
- ProjectSettings::get_singleton()->get_resource_path().empty() ||
+ ProjectSettings::get_singleton()->get_resource_path().is_empty() ||
Main::is_project_manager()) {
if (da_args.size() == 0) {
return;
@@ -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
@@ -201,7 +201,6 @@ MonoDomain *gd_initialize_mono_runtime() {
return mono_jit_init_version("GodotEngine.RootDomain", runtime_version);
}
#endif
-
} // namespace
void GDMono::add_mono_shared_libs_dir_to_path() {
@@ -298,7 +297,7 @@ void GDMono::determine_mono_dirs(String &r_assembly_rootdir, String &r_config_di
}
#ifdef WINDOWS_ENABLED
- if (r_assembly_rootdir.empty() || r_config_dir.empty()) {
+ if (r_assembly_rootdir.is_empty() || r_config_dir.is_empty()) {
ERR_PRINT("Cannot find Mono in the registry.");
// Assertion: if they are not set, then they weren't found in the registry
CRASH_COND(mono_reg_info.assembly_dir.length() > 0 || mono_reg_info.config_dir.length() > 0);
@@ -361,7 +360,7 @@ void GDMono::initialize() {
#ifndef TOOLS_ENABLED
// Exported games that don't use C# must still work. They likely don't ship with mscorlib.
// We only initialize the Mono runtime if we can find mscorlib. Otherwise it would crash.
- if (GDMonoAssembly::find_assembly("mscorlib.dll").empty()) {
+ if (GDMonoAssembly::find_assembly("mscorlib.dll").is_empty()) {
print_verbose("Mono: Skipping runtime initialization because 'mscorlib.dll' could not be found");
return;
}
@@ -505,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);
@@ -593,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);
@@ -692,7 +691,7 @@ static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool
}
Ref<ConfigFile> cfg;
- cfg.instance();
+ cfg.instantiate();
Error cfg_err = cfg->load(cached_api_hash_path);
ERR_FAIL_COND_V(cfg_err != OK, false);
@@ -703,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;
}
@@ -718,7 +717,7 @@ static void create_cached_api_hash_for(const String &p_api_assemblies_dir) {
String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg");
Ref<ConfigFile> cfg;
- cfg.instance();
+ cfg.instantiate();
cfg->set_value("core", "modified_time", FileAccess::get_modified_time(core_api_assembly_path));
cfg->set_value("editor", "modified_time", FileAccess::get_modified_time(editor_api_assembly_path));
@@ -755,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);
@@ -820,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
@@ -835,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;
}
@@ -853,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;
}
@@ -945,7 +940,7 @@ void GDMono::_load_api_assemblies() {
// 2. Update the API assemblies
String update_error = update_api_assemblies_from_prebuilt("Debug", &core_api_assembly.out_of_sync, &editor_api_assembly.out_of_sync);
- CRASH_COND_MSG(!update_error.empty(), update_error);
+ CRASH_COND_MSG(!update_error.is_empty(), update_error);
// 3. Load the scripts domain again
Error domain_load_err = _load_scripts_domain();
@@ -986,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;
}
@@ -999,7 +994,7 @@ bool GDMono::_load_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.empty()) {
+ if (appname_safe.is_empty()) {
appname_safe = "UnnamedProject";
}
@@ -1007,6 +1002,7 @@ bool GDMono::_load_project_assembly() {
if (success) {
mono_assembly_set_main(project_assembly->get_assembly());
+ CSharpLanguage::get_singleton()->lookup_scripts_in_assembly(project_assembly);
}
return success;
@@ -1335,23 +1331,25 @@ GDMono::~GDMono() {
singleton = nullptr;
}
-_GodotSharp *_GodotSharp::singleton = nullptr;
+namespace mono_bind {
+
+GodotSharp *GodotSharp::singleton = nullptr;
-void _GodotSharp::attach_thread() {
+void GodotSharp::attach_thread() {
GDMonoUtils::attach_current_thread();
}
-void _GodotSharp::detach_thread() {
+void GodotSharp::detach_thread() {
GDMonoUtils::detach_current_thread();
}
-int32_t _GodotSharp::get_domain_id() {
+int32_t GodotSharp::get_domain_id() {
MonoDomain *domain = mono_domain_get();
ERR_FAIL_NULL_V(domain, -1);
return mono_domain_get_id(domain);
}
-int32_t _GodotSharp::get_scripts_domain_id() {
+int32_t GodotSharp::get_scripts_domain_id() {
ERR_FAIL_NULL_V_MSG(GDMono::get_singleton(),
-1, "The Mono runtime is not initialized");
MonoDomain *domain = GDMono::get_singleton()->get_scripts_domain();
@@ -1359,21 +1357,21 @@ int32_t _GodotSharp::get_scripts_domain_id() {
return mono_domain_get_id(domain);
}
-bool _GodotSharp::is_scripts_domain_loaded() {
+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) {
+bool GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) {
return is_domain_finalizing_for_unload(p_domain_id);
}
-bool _GodotSharp::is_domain_finalizing_for_unload(int32_t p_domain_id) {
+bool GodotSharp::is_domain_finalizing_for_unload(int32_t p_domain_id) {
return is_domain_finalizing_for_unload(mono_domain_get_by_id(p_domain_id));
}
-bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) {
+bool GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) {
GDMono *gd_mono = GDMono::get_singleton();
ERR_FAIL_COND_V_MSG(!gd_mono || !gd_mono->is_runtime_initialized(),
@@ -1388,15 +1386,15 @@ bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) {
return mono_domain_is_unloading(p_domain);
}
-bool _GodotSharp::is_runtime_shutting_down() {
+bool GodotSharp::is_runtime_shutting_down() {
return mono_runtime_is_shutting_down();
}
-bool _GodotSharp::is_runtime_initialized() {
+bool GodotSharp::is_runtime_initialized() {
return GDMono::get_singleton() != nullptr && GDMono::get_singleton()->is_runtime_initialized();
}
-void _GodotSharp::_reload_assemblies(bool p_soft_reload) {
+void GodotSharp::_reload_assemblies(bool p_soft_reload) {
#ifdef GD_MONO_HOT_RELOAD
CRASH_COND(CSharpLanguage::get_singleton() == nullptr);
// This method may be called more than once with `call_deferred`, so we need to check
@@ -1407,24 +1405,26 @@ void _GodotSharp::_reload_assemblies(bool p_soft_reload) {
#endif
}
-void _GodotSharp::_bind_methods() {
- ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread);
- ClassDB::bind_method(D_METHOD("detach_thread"), &_GodotSharp::detach_thread);
+void GodotSharp::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("attach_thread"), &GodotSharp::attach_thread);
+ ClassDB::bind_method(D_METHOD("detach_thread"), &GodotSharp::detach_thread);
- ClassDB::bind_method(D_METHOD("get_domain_id"), &_GodotSharp::get_domain_id);
- ClassDB::bind_method(D_METHOD("get_scripts_domain_id"), &_GodotSharp::get_scripts_domain_id);
- ClassDB::bind_method(D_METHOD("is_scripts_domain_loaded"), &_GodotSharp::is_scripts_domain_loaded);
- ClassDB::bind_method(D_METHOD("is_domain_finalizing_for_unload", "domain_id"), &_GodotSharp::_is_domain_finalizing_for_unload);
+ ClassDB::bind_method(D_METHOD("get_domain_id"), &GodotSharp::get_domain_id);
+ ClassDB::bind_method(D_METHOD("get_scripts_domain_id"), &GodotSharp::get_scripts_domain_id);
+ ClassDB::bind_method(D_METHOD("is_scripts_domain_loaded"), &GodotSharp::is_scripts_domain_loaded);
+ ClassDB::bind_method(D_METHOD("is_domain_finalizing_for_unload", "domain_id"), &GodotSharp::_is_domain_finalizing_for_unload);
- ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &_GodotSharp::is_runtime_shutting_down);
- ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &_GodotSharp::is_runtime_initialized);
- ClassDB::bind_method(D_METHOD("_reload_assemblies"), &_GodotSharp::_reload_assemblies);
+ ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &GodotSharp::is_runtime_shutting_down);
+ ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &GodotSharp::is_runtime_initialized);
+ ClassDB::bind_method(D_METHOD("_reload_assemblies"), &GodotSharp::_reload_assemblies);
}
-_GodotSharp::_GodotSharp() {
+GodotSharp::GodotSharp() {
singleton = this;
}
-_GodotSharp::~_GodotSharp() {
+GodotSharp::~GodotSharp() {
singleton = nullptr;
}
+
+} // namespace mono_bind
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 18f7418049..a18fa6c6b4 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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() {}
@@ -283,7 +283,6 @@ public:
}
}
};
-
} // namespace gdmono
#define _GDMONO_SCOPE_DOMAIN_(m_mono_domain) \
@@ -294,8 +293,10 @@ public:
gdmono::ScopeExitDomainUnload __gdmono__scope__exit__domain__unload__(m_mono_domain); \
(void)__gdmono__scope__exit__domain__unload__;
-class _GodotSharp : public Object {
- GDCLASS(_GodotSharp, Object);
+namespace mono_bind {
+
+class GodotSharp : public Object {
+ GDCLASS(GodotSharp, Object);
friend class GDMono;
@@ -304,11 +305,11 @@ class _GodotSharp : public Object {
void _reload_assemblies(bool p_soft_reload);
protected:
- static _GodotSharp *singleton;
+ static GodotSharp *singleton;
static void _bind_methods();
public:
- static _GodotSharp *get_singleton() { return singleton; }
+ static GodotSharp *get_singleton() { return singleton; }
void attach_thread();
void detach_thread();
@@ -324,8 +325,10 @@ public:
bool is_runtime_shutting_down();
bool is_runtime_initialized();
- _GodotSharp();
- ~_GodotSharp();
+ GodotSharp();
+ ~GodotSharp();
};
+} // namespace mono_bind
+
#endif // GD_MONO_H
diff --git a/modules/mono/mono_gd/gd_mono_assembly.cpp b/modules/mono/mono_gd/gd_mono_assembly.cpp
index 6e351001d4..67f38bf127 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.cpp
+++ b/modules/mono/mono_gd/gd_mono_assembly.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,11 +33,11 @@
#include <mono/metadata/mono-debug.h>
#include <mono/metadata/tokentype.h>
+#include "core/config/project_settings.h"
+#include "core/io/file_access.h"
#include "core/io/file_access_pack.h"
-#include "core/list.h"
-#include "core/os/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
+#include "core/templates/list.h"
#include "../godotsharp_dirs.h"
#include "gd_mono_cache.h"
@@ -48,20 +48,20 @@ Vector<String> GDMonoAssembly::search_dirs;
void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) {
String framework_dir;
- if (!p_custom_bcl_dir.empty()) {
+ if (!p_custom_bcl_dir.is_empty()) {
framework_dir = p_custom_bcl_dir;
} else if (mono_assembly_getrootdir()) {
framework_dir = String::utf8(mono_assembly_getrootdir()).plus_file("mono").plus_file("4.5");
}
- if (!framework_dir.empty()) {
+ if (!framework_dir.is_empty()) {
r_search_dirs.push_back(framework_dir);
r_search_dirs.push_back(framework_dir.plus_file("Facades"));
}
#if !defined(TOOLS_ENABLED)
String data_game_assemblies_dir = GodotSharpDirs::get_data_game_assemblies_dir();
- if (!data_game_assemblies_dir.empty()) {
+ if (!data_game_assemblies_dir.is_empty()) {
r_search_dirs.push_back(data_game_assemblies_dir);
}
#endif
@@ -72,7 +72,7 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin
r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
}
- if (p_custom_config.empty()) {
+ if (p_custom_config.is_empty()) {
r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
} else {
String api_config = p_custom_config == "ExportRelease" ? "Release" : "Debug";
@@ -230,7 +230,7 @@ void GDMonoAssembly::initialize() {
MonoAssembly *GDMonoAssembly::_real_load_assembly_from(const String &p_path, bool p_refonly, MonoAssemblyName *p_aname) {
Vector<uint8_t> data = FileAccess::get_file_as_array(p_path);
- ERR_FAIL_COND_V_MSG(data.empty(), nullptr, "Could read the assembly in the specified location");
+ ERR_FAIL_COND_V_MSG(data.is_empty(), nullptr, "Could read the assembly in the specified location");
String image_filename;
@@ -330,8 +330,8 @@ no_pdb:
void GDMonoAssembly::unload() {
ERR_FAIL_NULL(image); // Should not be called if already unloaded
- for (Map<MonoClass *, GDMonoClass *>::Element *E = cached_raw.front(); E; E = E->next()) {
- memdelete(E->value());
+ for (const KeyValue<MonoClass *, GDMonoClass *> &E : cached_raw) {
+ memdelete(E.value);
}
cached_classes.clear();
@@ -345,6 +345,45 @@ String GDMonoAssembly::get_path() const {
return String::utf8(mono_image_get_filename(image));
}
+bool GDMonoAssembly::has_attribute(GDMonoClass *p_attr_class) {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_NULL_V(p_attr_class, false);
+#endif
+
+ if (!attrs_fetched) {
+ fetch_attributes();
+ }
+
+ if (!attributes) {
+ return false;
+ }
+
+ return mono_custom_attrs_has_attr(attributes, p_attr_class->get_mono_ptr());
+}
+
+MonoObject *GDMonoAssembly::get_attribute(GDMonoClass *p_attr_class) {
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_NULL_V(p_attr_class, nullptr);
+#endif
+
+ if (!attrs_fetched) {
+ fetch_attributes();
+ }
+
+ if (!attributes) {
+ return nullptr;
+ }
+
+ return mono_custom_attrs_get_attr(attributes, p_attr_class->get_mono_ptr());
+}
+
+void GDMonoAssembly::fetch_attributes() {
+ ERR_FAIL_COND(attributes != nullptr);
+
+ attributes = mono_custom_attrs_from_assembly(assembly);
+ attrs_fetched = true;
+}
+
GDMonoClass *GDMonoAssembly::get_class(const StringName &p_namespace, const StringName &p_name) {
ERR_FAIL_NULL_V(image, nullptr);
@@ -379,8 +418,8 @@ GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
return match->value();
}
- StringName namespace_name = mono_class_get_namespace(p_mono_class);
- StringName class_name = mono_class_get_name(p_mono_class);
+ StringName namespace_name = String::utf8(mono_class_get_namespace(p_mono_class));
+ StringName class_name = String::utf8(mono_class_get_name(p_mono_class));
GDMonoClass *wrapped_class = memnew(GDMonoClass(namespace_name, class_name, p_mono_class, this));
@@ -390,70 +429,6 @@ GDMonoClass *GDMonoAssembly::get_class(MonoClass *p_mono_class) {
return wrapped_class;
}
-GDMonoClass *GDMonoAssembly::get_object_derived_class(const StringName &p_class) {
- GDMonoClass *match = nullptr;
-
- if (gdobject_class_cache_updated) {
- Map<StringName, GDMonoClass *>::Element *result = gdobject_class_cache.find(p_class);
-
- if (result) {
- match = result->get();
- }
- } else {
- List<GDMonoClass *> nested_classes;
-
- int rows = mono_image_get_table_rows(image, MONO_TABLE_TYPEDEF);
-
- for (int i = 1; i < rows; i++) {
- MonoClass *mono_class = mono_class_get(image, (i + 1) | MONO_TOKEN_TYPE_DEF);
-
- if (!mono_class_is_assignable_from(CACHED_CLASS_RAW(GodotObject), mono_class)) {
- continue;
- }
-
- GDMonoClass *current = get_class(mono_class);
-
- if (!current) {
- continue;
- }
-
- nested_classes.push_back(current);
-
- if (!match && current->get_name() == p_class) {
- match = current;
- }
-
- while (!nested_classes.empty()) {
- GDMonoClass *current_nested = nested_classes.front()->get();
- nested_classes.pop_front();
-
- void *iter = nullptr;
-
- while (true) {
- MonoClass *raw_nested = mono_class_get_nested_types(current_nested->get_mono_ptr(), &iter);
-
- if (!raw_nested) {
- break;
- }
-
- GDMonoClass *nested_class = get_class(raw_nested);
-
- if (nested_class) {
- gdobject_class_cache.insert(nested_class->get_name(), nested_class);
- nested_classes.push_back(nested_class);
- }
- }
- }
-
- gdobject_class_cache.insert(current->get_name(), current);
- }
-
- gdobject_class_cache_updated = true;
- }
-
- return match;
-}
-
GDMonoAssembly *GDMonoAssembly::load(const String &p_name, MonoAssemblyName *p_aname, bool p_refonly, const Vector<String> &p_search_dirs) {
if (GDMono::get_singleton()->get_corlib_assembly() && (p_name == "mscorlib" || p_name == "mscorlib.dll")) {
return GDMono::get_singleton()->get_corlib_assembly();
diff --git a/modules/mono/mono_gd/gd_mono_assembly.h b/modules/mono/mono_gd/gd_mono_assembly.h
index 63899dc9be..6191c491f4 100644
--- a/modules/mono/mono_gd/gd_mono_assembly.h
+++ b/modules/mono/mono_gd/gd_mono_assembly.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,9 +34,9 @@
#include <mono/jit/jit.h>
#include <mono/metadata/assembly.h>
-#include "core/hash_map.h"
-#include "core/map.h"
-#include "core/ustring.h"
+#include "core/string/ustring.h"
+#include "core/templates/hash_map.h"
+#include "core/templates/map.h"
#include "gd_mono_utils.h"
class GDMonoAssembly {
@@ -71,13 +71,13 @@ class GDMonoAssembly {
MonoImage *image;
MonoAssembly *assembly;
+ bool attrs_fetched = false;
+ MonoCustomAttrInfo *attributes = nullptr;
+
#ifdef GD_MONO_HOT_RELOAD
uint64_t modified_time = 0;
#endif
- bool gdobject_class_cache_updated = false;
- Map<StringName, GDMonoClass *> gdobject_class_cache;
-
HashMap<ClassKey, GDMonoClass *, ClassKey::Hasher> cached_classes;
Map<MonoClass *, GDMonoClass *> cached_raw;
@@ -111,11 +111,14 @@ public:
String get_path() const;
+ bool has_attribute(GDMonoClass *p_attr_class);
+ MonoObject *get_attribute(GDMonoClass *p_attr_class);
+
+ void fetch_attributes();
+
GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
GDMonoClass *get_class(MonoClass *p_mono_class);
- GDMonoClass *get_object_derived_class(const StringName &p_class);
-
static String find_assembly(const String &p_name);
static void fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config = String(), const String &p_custom_bcl_dir = String());
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index 29aef6e609..2bf55493e0 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -109,8 +109,8 @@ void CachedData::clear_godot_api_cache() {
class_Vector3 = nullptr;
class_Vector3i = nullptr;
class_Basis = nullptr;
- class_Quat = nullptr;
- class_Transform = nullptr;
+ class_Quaternion = nullptr;
+ class_Transform3D = nullptr;
class_AABB = nullptr;
class_Color = nullptr;
class_Plane = nullptr;
@@ -140,14 +140,15 @@ void CachedData::clear_godot_api_cache() {
field_ExportAttribute_hintString = nullptr;
class_SignalAttribute = nullptr;
class_ToolAttribute = nullptr;
- class_RemoteAttribute = nullptr;
- class_MasterAttribute = nullptr;
- class_PuppetAttribute = nullptr;
- class_RemoteSyncAttribute = nullptr;
- class_MasterSyncAttribute = nullptr;
- class_PuppetSyncAttribute = nullptr;
+ class_AnyPeerAttribute = nullptr;
+ class_AuthorityAttribute = nullptr;
class_GodotMethodAttribute = nullptr;
field_GodotMethodAttribute_methodName = nullptr;
+ class_ScriptPathAttribute = nullptr;
+ field_ScriptPathAttribute_path = nullptr;
+ class_AssemblyHasScriptsAttribute = nullptr;
+ field_AssemblyHasScriptsAttribute_requiresLookup = nullptr;
+ field_AssemblyHasScriptsAttribute_scriptTypes = nullptr;
field_GodotObject_ptr = nullptr;
field_StringName_ptr = nullptr;
@@ -233,8 +234,8 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(Vector3, GODOT_API_CLASS(Vector3));
CACHE_CLASS_AND_CHECK(Vector3i, GODOT_API_CLASS(Vector3i));
CACHE_CLASS_AND_CHECK(Basis, GODOT_API_CLASS(Basis));
- CACHE_CLASS_AND_CHECK(Quat, GODOT_API_CLASS(Quat));
- CACHE_CLASS_AND_CHECK(Transform, GODOT_API_CLASS(Transform));
+ CACHE_CLASS_AND_CHECK(Quaternion, GODOT_API_CLASS(Quaternion));
+ CACHE_CLASS_AND_CHECK(Transform3D, GODOT_API_CLASS(Transform3D));
CACHE_CLASS_AND_CHECK(AABB, GODOT_API_CLASS(AABB));
CACHE_CLASS_AND_CHECK(Color, GODOT_API_CLASS(Color));
CACHE_CLASS_AND_CHECK(Plane, GODOT_API_CLASS(Plane));
@@ -264,14 +265,15 @@ 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(MasterAttribute, GODOT_API_CLASS(MasterAttribute));
- CACHE_CLASS_AND_CHECK(PuppetAttribute, GODOT_API_CLASS(PuppetAttribute));
- CACHE_CLASS_AND_CHECK(RemoteSyncAttribute, GODOT_API_CLASS(RemoteSyncAttribute));
- CACHE_CLASS_AND_CHECK(MasterSyncAttribute, GODOT_API_CLASS(MasterSyncAttribute));
- CACHE_CLASS_AND_CHECK(PuppetSyncAttribute, GODOT_API_CLASS(PuppetSyncAttribute));
+ 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));
+ CACHE_FIELD_AND_CHECK(ScriptPathAttribute, path, CACHED_CLASS(ScriptPathAttribute)->get_field("path"));
+ CACHE_CLASS_AND_CHECK(AssemblyHasScriptsAttribute, GODOT_API_CLASS(AssemblyHasScriptsAttribute));
+ CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, requiresLookup, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("requiresLookup"));
+ CACHE_FIELD_AND_CHECK(AssemblyHasScriptsAttribute, scriptTypes, CACHED_CLASS(AssemblyHasScriptsAttribute)->get_field("scriptTypes"));
CACHE_FIELD_AND_CHECK(GodotObject, ptr, CACHED_CLASS(GodotObject)->get_field(BINDINGS_PTR_FIELD));
CACHE_FIELD_AND_CHECK(StringName, ptr, CACHED_CLASS(StringName)->get_field(BINDINGS_PTR_FIELD));
@@ -316,5 +318,4 @@ void update_godot_api_cache() {
cached_data.godot_api_cache_updated = true;
}
-
} // namespace GDMonoCache
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index a7bbc763a7..4b4688b4d9 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -80,8 +80,8 @@ struct CachedData {
GDMonoClass *class_Vector3;
GDMonoClass *class_Vector3i;
GDMonoClass *class_Basis;
- GDMonoClass *class_Quat;
- GDMonoClass *class_Transform;
+ GDMonoClass *class_Quaternion;
+ GDMonoClass *class_Transform3D;
GDMonoClass *class_AABB;
GDMonoClass *class_Color;
GDMonoClass *class_Plane;
@@ -111,14 +111,15 @@ struct CachedData {
GDMonoField *field_ExportAttribute_hintString;
GDMonoClass *class_SignalAttribute;
GDMonoClass *class_ToolAttribute;
- GDMonoClass *class_RemoteAttribute;
- GDMonoClass *class_MasterAttribute;
- GDMonoClass *class_PuppetAttribute;
- GDMonoClass *class_RemoteSyncAttribute;
- GDMonoClass *class_MasterSyncAttribute;
- GDMonoClass *class_PuppetSyncAttribute;
+ GDMonoClass *class_AnyPeerAttribute;
+ GDMonoClass *class_AuthorityAttribute;
GDMonoClass *class_GodotMethodAttribute;
GDMonoField *field_GodotMethodAttribute_methodName;
+ GDMonoClass *class_ScriptPathAttribute;
+ GDMonoField *field_ScriptPathAttribute_path;
+ GDMonoClass *class_AssemblyHasScriptsAttribute;
+ GDMonoField *field_AssemblyHasScriptsAttribute_requiresLookup;
+ GDMonoField *field_AssemblyHasScriptsAttribute_scriptTypes;
GDMonoField *field_GodotObject_ptr;
GDMonoField *field_StringName_ptr;
@@ -181,7 +182,6 @@ inline void clear_corlib_cache() {
inline void clear_godot_api_cache() {
cached_data.clear_godot_api_cache();
}
-
} // namespace GDMonoCache
#define CACHED_CLASS(m_class) (GDMonoCache::cached_data.class_##m_class)
diff --git a/modules/mono/mono_gd/gd_mono_class.cpp b/modules/mono/mono_gd/gd_mono_class.cpp
index 6575cbc1c8..4f4480fa49 100644
--- a/modules/mono/mono_gd/gd_mono_class.cpp
+++ b/modules/mono/mono_gd/gd_mono_class.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -177,7 +177,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
void *iter = nullptr;
MonoMethod *raw_method = nullptr;
while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) {
- StringName name = mono_method_get_name(raw_method);
+ StringName name = String::utf8(mono_method_get_name(raw_method));
// get_method implicitly fetches methods and adds them to this->methods
GDMonoMethod *method = get_method(raw_method, name);
@@ -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;
}
@@ -290,7 +290,7 @@ bool GDMonoClass::has_public_parameterless_ctor() {
return ctor && ctor->get_visibility() == IMonoClassMember::PUBLIC;
}
-GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) {
+GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, uint16_t p_params_count) {
MethodKey key = MethodKey(p_name, p_params_count);
GDMonoMethod **match = methods.getptr(key);
@@ -319,7 +319,7 @@ GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method) {
MonoMethodSignature *sig = mono_method_signature(p_raw_method);
int params_count = mono_signature_get_param_count(sig);
- StringName method_name = mono_method_get_name(p_raw_method);
+ StringName method_name = String::utf8(mono_method_get_name(p_raw_method));
return get_method(p_raw_method, method_name, params_count);
}
@@ -330,7 +330,7 @@ GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName
return get_method(p_raw_method, p_name, params_count);
}
-GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count) {
+GDMonoMethod *GDMonoClass::get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count) {
ERR_FAIL_NULL_V(p_raw_method, nullptr);
MethodKey key = MethodKey(p_name, p_params_count);
@@ -392,7 +392,7 @@ const Vector<GDMonoField *> &GDMonoClass::get_all_fields() {
void *iter = nullptr;
MonoClassField *raw_field = nullptr;
while ((raw_field = mono_class_get_fields(mono_class, &iter)) != nullptr) {
- StringName name = mono_field_get_name(raw_field);
+ StringName name = String::utf8(mono_field_get_name(raw_field));
Map<StringName, GDMonoField *>::Element *match = fields.find(name);
@@ -441,7 +441,7 @@ const Vector<GDMonoProperty *> &GDMonoClass::get_all_properties() {
void *iter = nullptr;
MonoProperty *raw_property = nullptr;
while ((raw_property = mono_class_get_properties(mono_class, &iter)) != nullptr) {
- StringName name = mono_property_get_name(raw_property);
+ StringName name = String::utf8(mono_property_get_name(raw_property));
Map<StringName, GDMonoProperty *>::Element *match = properties.find(name);
@@ -468,14 +468,14 @@ const Vector<GDMonoClass *> &GDMonoClass::get_all_delegates() {
MonoClass *raw_class = nullptr;
while ((raw_class = mono_class_get_nested_types(mono_class, &iter)) != nullptr) {
if (mono_class_is_delegate(raw_class)) {
- StringName name = mono_class_get_name(raw_class);
+ StringName name = String::utf8(mono_class_get_name(raw_class));
Map<StringName, GDMonoClass *>::Element *match = delegates.find(name);
if (match) {
delegates_list.push_back(match->get());
} else {
- GDMonoClass *delegate = memnew(GDMonoClass(mono_class_get_namespace(raw_class), mono_class_get_name(raw_class), raw_class, assembly));
+ GDMonoClass *delegate = memnew(GDMonoClass(String::utf8(mono_class_get_namespace(raw_class)), String::utf8(mono_class_get_name(raw_class)), raw_class, assembly));
delegates.insert(name, delegate);
delegates_list.push_back(delegate);
}
@@ -492,7 +492,7 @@ const Vector<GDMonoMethod *> &GDMonoClass::get_all_methods() {
void *iter = nullptr;
MonoMethod *raw_method = nullptr;
while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != nullptr) {
- method_list.push_back(memnew(GDMonoMethod(mono_method_get_name(raw_method), raw_method)));
+ method_list.push_back(memnew(GDMonoMethod(String::utf8(mono_method_get_name(raw_method)), raw_method)));
}
method_list_fetched = true;
@@ -522,12 +522,12 @@ GDMonoClass::~GDMonoClass() {
mono_custom_attrs_free(attributes);
}
- for (Map<StringName, GDMonoField *>::Element *E = fields.front(); E; E = E->next()) {
- memdelete(E->value());
+ for (const KeyValue<StringName, GDMonoField *> &E : fields) {
+ memdelete(E.value);
}
- for (Map<StringName, GDMonoProperty *>::Element *E = properties.front(); E; E = E->next()) {
- memdelete(E->value());
+ for (const KeyValue<StringName, GDMonoProperty *> &E : properties) {
+ memdelete(E.value);
}
{
diff --git a/modules/mono/mono_gd/gd_mono_class.h b/modules/mono/mono_gd/gd_mono_class.h
index 44b146b87c..daea75bae8 100644
--- a/modules/mono/mono_gd/gd_mono_class.h
+++ b/modules/mono/mono_gd/gd_mono_class.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#ifndef GD_MONO_CLASS_H
#define GD_MONO_CLASS_H
-#include "core/map.h"
-#include "core/ustring.h"
+#include "core/string/ustring.h"
+#include "core/templates/map.h"
#include "gd_mono_field.h"
#include "gd_mono_header.h"
@@ -59,13 +59,12 @@ class GDMonoClass {
MethodKey() {}
- MethodKey(const StringName &p_name, int p_params_count) {
- name = p_name;
- params_count = p_params_count;
+ MethodKey(const StringName &p_name, uint16_t p_params_count) :
+ name(p_name), params_count(p_params_count) {
}
StringName name;
- int params_count;
+ uint16_t params_count = 0;
};
StringName namespace_name;
@@ -139,10 +138,10 @@ public:
bool implements_interface(GDMonoClass *p_interface);
bool has_public_parameterless_ctor();
- GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0);
+ GDMonoMethod *get_method(const StringName &p_name, uint16_t p_params_count = 0);
GDMonoMethod *get_method(MonoMethod *p_raw_method);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name);
- GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count);
+ GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, uint16_t p_params_count);
GDMonoMethod *get_method_with_desc(const String &p_description, bool p_include_namespace);
GDMonoField *get_field(const StringName &p_name);
diff --git a/modules/mono/mono_gd/gd_mono_field.cpp b/modules/mono/mono_gd/gd_mono_field.cpp
index 563c45e71f..111eaa0bbf 100644
--- a/modules/mono/mono_gd/gd_mono_field.cpp
+++ b/modules/mono/mono_gd/gd_mono_field.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -46,29 +46,15 @@ void GDMonoField::set_value_raw(MonoObject *p_object, void *p_ptr) {
}
void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_value) {
-#define SET_FROM_STRUCT(m_type) \
- { \
- GDMonoMarshal::M_##m_type from = MARSHALLED_OUT(m_type, p_value.operator ::m_type()); \
- mono_field_set_value(p_object, mono_field, &from); \
- }
-
-#define SET_FROM_ARRAY(m_type) \
- { \
- MonoArray *managed = GDMonoMarshal::m_type##_to_mono_array(p_value.operator ::m_type()); \
- mono_field_set_value(p_object, mono_field, managed); \
- }
-
switch (type.type_encoding) {
case MONO_TYPE_BOOLEAN: {
MonoBoolean val = p_value.operator bool();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
case MONO_TYPE_CHAR: {
int16_t val = p_value.operator unsigned short();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
case MONO_TYPE_I1: {
int8_t val = p_value.operator signed char();
mono_field_set_value(p_object, mono_field, &val);
@@ -85,7 +71,6 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
int64_t val = p_value.operator int64_t();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
case MONO_TYPE_U1: {
uint8_t val = p_value.operator unsigned char();
mono_field_set_value(p_object, mono_field, &val);
@@ -102,93 +87,92 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
uint64_t val = p_value.operator uint64_t();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
case MONO_TYPE_R4: {
float val = p_value.operator float();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
case MONO_TYPE_R8: {
double val = p_value.operator double();
mono_field_set_value(p_object, mono_field, &val);
} break;
-
- case MONO_TYPE_STRING: {
- if (p_value.get_type() == Variant::NIL) {
- // Otherwise, Variant -> String would return the string "Null"
- MonoString *mono_string = nullptr;
- mono_field_set_value(p_object, mono_field, mono_string);
- } else {
- MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
- mono_field_set_value(p_object, mono_field, mono_string);
- }
- } break;
-
case MONO_TYPE_VALUETYPE: {
GDMonoClass *tclass = type.type_class;
if (tclass == CACHED_CLASS(Vector2)) {
- SET_FROM_STRUCT(Vector2);
+ GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Vector2i)) {
- SET_FROM_STRUCT(Vector2i);
+ GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Rect2)) {
- SET_FROM_STRUCT(Rect2);
+ GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Rect2i)) {
- SET_FROM_STRUCT(Rect2i);
+ GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Transform2D)) {
- SET_FROM_STRUCT(Transform2D);
+ GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Vector3)) {
- SET_FROM_STRUCT(Vector3);
+ GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Vector3i)) {
- SET_FROM_STRUCT(Vector3i);
+ GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Basis)) {
- SET_FROM_STRUCT(Basis);
+ GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
- if (tclass == CACHED_CLASS(Quat)) {
- SET_FROM_STRUCT(Quat);
+ if (tclass == CACHED_CLASS(Quaternion)) {
+ GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_value.operator ::Quaternion());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
- if (tclass == CACHED_CLASS(Transform)) {
- SET_FROM_STRUCT(Transform);
+ if (tclass == CACHED_CLASS(Transform3D)) {
+ GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_value.operator ::Transform3D());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(AABB)) {
- SET_FROM_STRUCT(AABB);
+ GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Color)) {
- SET_FROM_STRUCT(Color);
+ GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
if (tclass == CACHED_CLASS(Plane)) {
- SET_FROM_STRUCT(Plane);
+ GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane());
+ mono_field_set_value(p_object, mono_field, &from);
break;
}
@@ -267,118 +251,35 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + tclass->get_name() + "'.");
} break;
-
+ case MONO_TYPE_STRING: {
+ if (p_value.get_type() == Variant::NIL) {
+ // Otherwise, Variant -> String would return the string "Null"
+ MonoString *mono_string = nullptr;
+ mono_field_set_value(p_object, mono_field, mono_string);
+ } else {
+ MonoString *mono_string = GDMonoMarshal::mono_string_from_godot(p_value);
+ mono_field_set_value(p_object, mono_field, mono_string);
+ }
+ } break;
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(type.type_class->get_mono_type());
-
- if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
- SET_FROM_ARRAY(Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
- SET_FROM_ARRAY(PackedByteArray);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
- SET_FROM_ARRAY(PackedInt32Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
- SET_FROM_ARRAY(PackedInt64Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(float)) {
- SET_FROM_ARRAY(PackedFloat32Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(double)) {
- SET_FROM_ARRAY(PackedFloat64Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(String)) {
- SET_FROM_ARRAY(PackedStringArray);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
- SET_FROM_ARRAY(PackedVector2Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
- SET_FROM_ARRAY(PackedVector3Array);
- break;
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
- SET_FROM_ARRAY(PackedColorArray);
- break;
- }
-
- GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
- if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
- MonoArray *managed = GDMonoMarshal::Array_to_mono_array(p_value.operator ::Array(), array_type_class);
+ MonoArray *managed = GDMonoMarshal::variant_to_mono_array(p_value, type.type_class);
+ if (likely(managed != nullptr)) {
mono_field_set_value(p_object, mono_field, managed);
- break;
}
-
- ERR_FAIL_MSG("Attempted to convert Variant to a managed array of unmarshallable element type.");
} break;
-
case MONO_TYPE_CLASS: {
- GDMonoClass *type_class = type.type_class;
-
- // GodotObject
- if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
- MonoObject *managed = GDMonoUtils::unmanaged_get_managed(p_value.operator Object *());
+ MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_class(p_value, type.type_class);
+ if (likely(managed != nullptr)) {
mono_field_set_value(p_object, mono_field, managed);
- break;
}
-
- if (CACHED_CLASS(StringName) == type_class) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName());
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- if (CACHED_CLASS(NodePath) == type_class) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- if (CACHED_CLASS(RID) == type_class) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator RID());
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // Godot.Collections.Dictionary or IDictionary
- if (CACHED_CLASS(Dictionary) == type_class || type_class == CACHED_CLASS(System_Collections_IDictionary)) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), CACHED_CLASS(Dictionary));
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // Godot.Collections.Array or ICollection or IEnumerable
- if (CACHED_CLASS(Array) == type_class ||
- type_class == CACHED_CLASS(System_Collections_ICollection) ||
- type_class == CACHED_CLASS(System_Collections_IEnumerable)) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
+ } break;
+ case MONO_TYPE_GENERICINST: {
+ MonoObject *managed = GDMonoMarshal::variant_to_mono_object_of_genericinst(p_value, type.type_class);
+ if (likely(managed != nullptr)) {
mono_field_set_value(p_object, mono_field, managed);
- break;
}
-
- ERR_FAIL_MSG("Attempted to set the value of a field of unmarshallable type: '" + type_class->get_name() + "'.");
} break;
-
case MONO_TYPE_OBJECT: {
// Variant
switch (p_value.get_type()) {
@@ -404,43 +305,56 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
mono_field_set_value(p_object, mono_field, mono_string);
} break;
case Variant::VECTOR2: {
- SET_FROM_STRUCT(Vector2);
+ GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_value.operator ::Vector2());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::VECTOR2I: {
- SET_FROM_STRUCT(Vector2i);
+ GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_value.operator ::Vector2i());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::RECT2: {
- SET_FROM_STRUCT(Rect2);
+ GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_value.operator ::Rect2());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::RECT2I: {
- SET_FROM_STRUCT(Rect2i);
+ GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_value.operator ::Rect2i());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::VECTOR3: {
- SET_FROM_STRUCT(Vector3);
+ GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_value.operator ::Vector3());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::VECTOR3I: {
- SET_FROM_STRUCT(Vector3i);
+ GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_value.operator ::Vector3i());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::TRANSFORM2D: {
- SET_FROM_STRUCT(Transform2D);
+ GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_value.operator ::Transform2D());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::PLANE: {
- SET_FROM_STRUCT(Plane);
+ GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_value.operator ::Plane());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
- case Variant::QUAT: {
- SET_FROM_STRUCT(Quat);
+ case Variant::QUATERNION: {
+ GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_value.operator ::Quaternion());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::AABB: {
- SET_FROM_STRUCT(AABB);
+ GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_value.operator ::AABB());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::BASIS: {
- SET_FROM_STRUCT(Basis);
+ GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_value.operator ::Basis());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
- case Variant::TRANSFORM: {
- SET_FROM_STRUCT(Transform);
+ case Variant::TRANSFORM3D: {
+ GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_value.operator ::Transform3D());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::COLOR: {
- SET_FROM_STRUCT(Color);
+ GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_value.operator ::Color());
+ mono_field_set_value(p_object, mono_field, &from);
} break;
case Variant::STRING_NAME: {
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator StringName());
@@ -450,8 +364,8 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator NodePath());
mono_field_set_value(p_object, mono_field, managed);
} break;
- case Variant::_RID: {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator RID());
+ case Variant::RID: {
+ MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator ::RID());
mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::OBJECT: {
@@ -475,106 +389,49 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_BYTE_ARRAY: {
- SET_FROM_ARRAY(PackedByteArray);
+ MonoArray *managed = GDMonoMarshal::PackedByteArray_to_mono_array(p_value.operator ::PackedByteArray());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_INT32_ARRAY: {
- SET_FROM_ARRAY(PackedInt32Array);
+ MonoArray *managed = GDMonoMarshal::PackedInt32Array_to_mono_array(p_value.operator ::PackedInt32Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_INT64_ARRAY: {
- SET_FROM_ARRAY(PackedInt64Array);
+ MonoArray *managed = GDMonoMarshal::PackedInt64Array_to_mono_array(p_value.operator ::PackedInt64Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_FLOAT32_ARRAY: {
- SET_FROM_ARRAY(PackedFloat32Array);
+ MonoArray *managed = GDMonoMarshal::PackedFloat32Array_to_mono_array(p_value.operator ::PackedFloat32Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_FLOAT64_ARRAY: {
- SET_FROM_ARRAY(PackedFloat64Array);
+ MonoArray *managed = GDMonoMarshal::PackedFloat64Array_to_mono_array(p_value.operator ::PackedFloat64Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_STRING_ARRAY: {
- SET_FROM_ARRAY(PackedStringArray);
+ MonoArray *managed = GDMonoMarshal::PackedStringArray_to_mono_array(p_value.operator ::PackedStringArray());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_VECTOR2_ARRAY: {
- SET_FROM_ARRAY(PackedVector2Array);
+ MonoArray *managed = GDMonoMarshal::PackedVector2Array_to_mono_array(p_value.operator ::PackedVector2Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_VECTOR3_ARRAY: {
- SET_FROM_ARRAY(PackedVector3Array);
+ MonoArray *managed = GDMonoMarshal::PackedVector3Array_to_mono_array(p_value.operator ::PackedVector3Array());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
case Variant::PACKED_COLOR_ARRAY: {
- SET_FROM_ARRAY(PackedColorArray);
+ MonoArray *managed = GDMonoMarshal::PackedColorArray_to_mono_array(p_value.operator ::PackedColorArray());
+ mono_field_set_value(p_object, mono_field, managed);
} break;
default:
break;
}
} break;
-
- case MONO_TYPE_GENERICINST: {
- MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type());
-
- // Godot.Collections.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // Godot.Collections.Array<T>
- if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), type.type_class);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // System.Collections.Generic.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
- MonoReflectionType *key_reftype = nullptr;
- MonoReflectionType *value_reftype = nullptr;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- MonoObject *managed = GDMonoMarshal::Dictionary_to_system_generic_dict(p_value.operator Dictionary(),
- type.type_class, key_reftype, value_reftype);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // System.Collections.Generic.List<T>
- if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
- MonoReflectionType *elem_reftype = nullptr;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- MonoObject *managed = GDMonoMarshal::Array_to_system_generic_list(p_value.operator Array(),
- type.type_class, elem_reftype);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // IDictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
- MonoReflectionType *key_reftype;
- MonoReflectionType *value_reftype;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype);
-
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), godot_dict_class);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
-
- // ICollection<T> or IEnumerable<T>
- if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
- MonoReflectionType *elem_reftype;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype);
-
- MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), godot_array_class);
- mono_field_set_value(p_object, mono_field, managed);
- break;
- }
- } break;
-
default: {
ERR_PRINT("Attempted to set the value of a field of unexpected type encoding: " + itos(type.type_encoding) + ".");
} break;
}
-
-#undef SET_FROM_ARRAY_AND_BREAK
-#undef SET_FROM_STRUCT_AND_BREAK
}
MonoObject *GDMonoField::get_value(MonoObject *p_object) {
@@ -652,7 +509,7 @@ IMonoClassMember::Visibility GDMonoField::get_visibility() {
GDMonoField::GDMonoField(MonoClassField *p_mono_field, GDMonoClass *p_owner) {
owner = p_owner;
mono_field = p_mono_field;
- name = mono_field_get_name(mono_field);
+ name = String::utf8(mono_field_get_name(mono_field));
MonoType *field_type = mono_field_get_type(mono_field);
type.type_encoding = mono_type_get_type(field_type);
MonoClass *field_type_class = mono_class_from_mono_type(field_type);
diff --git a/modules/mono/mono_gd/gd_mono_field.h b/modules/mono/mono_gd/gd_mono_field.h
index 5b40b439f9..ed5078c673 100644
--- a/modules/mono/mono_gd/gd_mono_field.h
+++ b/modules/mono/mono_gd/gd_mono_field.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/mono_gd/gd_mono_header.h b/modules/mono/mono_gd/gd_mono_header.h
index 0f4f888546..483030610f 100644
--- a/modules/mono/mono_gd/gd_mono_header.h
+++ b/modules/mono/mono_gd/gd_mono_header.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef GD_MONO_HEADER_H
#define GD_MONO_HEADER_H
-#include "core/int_types.h"
+#include <cstdint>
#ifdef WIN32
#define GD_MONO_STDCALL __stdcall
diff --git a/modules/mono/mono_gd/gd_mono_internals.cpp b/modules/mono/mono_gd/gd_mono_internals.cpp
index fe1c2d28dd..cf76c23926 100644
--- a/modules/mono/mono_gd/gd_mono_internals.cpp
+++ b/modules/mono/mono_gd/gd_mono_internals.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -43,7 +43,6 @@
#include <mono/metadata/exception.h>
namespace GDMonoInternals {
-
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
// This method should not fail
@@ -52,7 +51,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
// All mono objects created from the managed world (e.g.: 'new Player()')
// need to have a CSharpScript in order for their methods to be callable from the unmanaged side
- Reference *ref = Object::cast_to<Reference>(unmanaged);
+ RefCounted *rc = Object::cast_to<RefCounted>(unmanaged);
GDMonoClass *klass = GDMonoUtils::get_object_class(managed);
@@ -74,33 +73,37 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
script_binding.inited = true;
script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass);
script_binding.wrapper_class = klass;
- script_binding.gchandle = ref ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
+ script_binding.gchandle = rc ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
script_binding.owner = unmanaged;
- if (ref) {
+ if (rc) {
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
// but the managed instance is alive, the refcount will be 1 instead of 0.
- // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)
+ // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
// May not me referenced yet, so we must use init_ref() instead of reference()
- if (ref->init_ref()) {
- CSharpLanguage::get_singleton()->post_unsafe_reference(ref);
+ if (rc->init_ref()) {
+ CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
}
}
// The object was just created, no script instance binding should have been attached
- CRASH_COND(unmanaged->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()));
+ CRASH_COND(CSharpLanguage::has_instance_binding(unmanaged));
- void *data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding);
+ void *data;
+ {
+ MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
+ data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding);
+ }
// Should be thread safe because the object was just created and nothing else should be referencing it
- unmanaged->set_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index(), data);
+ CSharpLanguage::set_instance_binding(unmanaged, data);
return;
}
- MonoGCHandleData gchandle = ref ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
+ MonoGCHandleData gchandle = rc ? MonoGCHandleData::new_weak_handle(managed) : MonoGCHandleData::new_strong_handle(managed);
Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native);
@@ -109,15 +112,15 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
CSharpInstance *csharp_instance = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);
unmanaged->set_script_and_instance(script, csharp_instance);
-
- csharp_instance->connect_event_signals();
}
void unhandled_exception(MonoException *p_exc) {
mono_print_unhandled_exception((MonoObject *)p_exc);
+ gd_unhandled_exception_event(p_exc);
if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) {
// Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
+ mono_unhandled_exception((MonoObject *)p_exc);
GDMono::unhandled_exception_hook((MonoObject *)p_exc, nullptr);
GD_UNREACHABLE();
} else {
@@ -130,4 +133,13 @@ void unhandled_exception(MonoException *p_exc) {
}
}
+void gd_unhandled_exception_event(MonoException *p_exc) {
+ MonoImage *mono_image = GDMono::get_singleton()->get_core_api_assembly()->get_image();
+
+ MonoClass *gd_klass = mono_class_from_name(mono_image, "Godot", "GD");
+ MonoMethod *unhandled_exception_method = mono_class_get_method_from_name(gd_klass, "OnUnhandledException", -1);
+ void *args[1];
+ args[0] = p_exc;
+ mono_runtime_invoke(unhandled_exception_method, nullptr, (void **)args, nullptr);
+}
} // namespace GDMonoInternals
diff --git a/modules/mono/mono_gd/gd_mono_internals.h b/modules/mono/mono_gd/gd_mono_internals.h
index 038d17f782..26eb270eee 100644
--- a/modules/mono/mono_gd/gd_mono_internals.h
+++ b/modules/mono/mono_gd/gd_mono_internals.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,10 +35,9 @@
#include "../utils/macros.h"
-#include "core/object.h"
+#include "core/object/class_db.h"
namespace GDMonoInternals {
-
void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
/**
@@ -47,6 +46,7 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged);
*/
void unhandled_exception(MonoException *p_exc);
+void gd_unhandled_exception_event(MonoException *p_exc);
} // namespace GDMonoInternals
#endif // GD_MONO_INTERNALS_H
diff --git a/modules/mono/mono_gd/gd_mono_log.cpp b/modules/mono/mono_gd/gd_mono_log.cpp
index b8ee0204c4..179bbfb40c 100644
--- a/modules/mono/mono_gd/gd_mono_log.cpp
+++ b/modules/mono/mono_gd/gd_mono_log.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#include <stdlib.h> // abort
-#include "core/os/dir_access.h"
+#include "core/io/dir_access.h"
#include "core/os/os.h"
#include "../godotsharp_dirs.h"
@@ -160,13 +160,13 @@ void GDMonoLog::initialize() {
OS::Date date_now = OS::get_singleton()->get_date();
OS::Time time_now = OS::get_singleton()->get_time();
- String log_file_name = str_format("%d_%02d_%02d %02d.%02d.%02d",
- date_now.year, date_now.month, date_now.day,
- time_now.hour, time_now.min, time_now.sec);
+ String log_file_name = str_format("%04d-%02d-%02d_%02d.%02d.%02d",
+ (int)date_now.year, (int)date_now.month, (int)date_now.day,
+ (int)time_now.hour, (int)time_now.minute, (int)time_now.second);
- log_file_name += str_format(" (%d)", OS::get_singleton()->get_process_id());
+ log_file_name += str_format("_%d", OS::get_singleton()->get_process_id());
- log_file_name += ".txt";
+ log_file_name += ".log";
log_file_path = logs_dir.plus_file(log_file_name);
@@ -189,8 +189,6 @@ void GDMonoLog::initialize() {
GDMonoLog::GDMonoLog() {
singleton = this;
-
- log_level_id = -1;
}
GDMonoLog::~GDMonoLog() {
diff --git a/modules/mono/mono_gd/gd_mono_log.h b/modules/mono/mono_gd/gd_mono_log.h
index 3a52316060..9ddbd251ac 100644
--- a/modules/mono/mono_gd/gd_mono_log.h
+++ b/modules/mono/mono_gd/gd_mono_log.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -41,14 +41,14 @@
#endif
#ifdef GD_MONO_LOG_ENABLED
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#endif
class GDMonoLog {
#ifdef GD_MONO_LOG_ENABLED
- int log_level_id;
+ int log_level_id = -1;
- FileAccess *log_file;
+ FileAccess *log_file = nullptr;
String log_file_path;
bool _try_create_logs_dir(const String &p_logs_dir);
diff --git a/modules/mono/mono_gd/gd_mono_marshal.cpp b/modules/mono/mono_gd/gd_mono_marshal.cpp
index c460e283ea..6b395303dd 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.cpp
+++ b/modules/mono/mono_gd/gd_mono_marshal.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -104,12 +104,12 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
return Variant::BASIS;
}
- if (vtclass == CACHED_CLASS(Quat)) {
- return Variant::QUAT;
+ if (vtclass == CACHED_CLASS(Quaternion)) {
+ return Variant::QUATERNION;
}
- if (vtclass == CACHED_CLASS(Transform)) {
- return Variant::TRANSFORM;
+ if (vtclass == CACHED_CLASS(Transform3D)) {
+ return Variant::TRANSFORM3D;
}
if (vtclass == CACHED_CLASS(AABB)) {
@@ -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;
}
@@ -204,7 +220,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type, bool *r_nil_is_
}
if (CACHED_CLASS(RID) == type_class) {
- return Variant::_RID;
+ return Variant::RID;
}
if (CACHED_CLASS(Dictionary) == type_class) {
@@ -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: {
@@ -311,152 +332,601 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_
return false;
}
-MonoObject *variant_to_mono_object(const Variant *p_var) {
- ManagedType type;
+MonoString *variant_to_mono_string(const Variant &p_var) {
+ if (p_var.get_type() == Variant::NIL) {
+ return nullptr; // Otherwise, Variant -> String would return the string "Null"
+ }
+ return mono_string_from_godot(p_var.operator String());
+}
+
+MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class) {
+ MonoArrayType *array_type = mono_type_get_array_type(p_type_class->get_mono_type());
+
+ if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
+ return Array_to_mono_array(p_var.operator Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
+ return PackedByteArray_to_mono_array(p_var.operator PackedByteArray());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
+ return PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
+ return PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(float)) {
+ return PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(double)) {
+ return PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(String)) {
+ return PackedStringArray_to_mono_array(p_var.operator PackedStringArray());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
+ return PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
+ return PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array());
+ }
+
+ if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
+ 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) + "'.");
+}
+
+MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class) {
+ // GodotObject
+ if (CACHED_CLASS(GodotObject)->is_assignable_from(p_type_class)) {
+ return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *());
+ }
+
+ if (CACHED_CLASS(StringName) == p_type_class) {
+ return GDMonoUtils::create_managed_from(p_var.operator StringName());
+ }
- type.type_encoding = MONO_TYPE_OBJECT;
- // type.type_class is not needed when we specify the MONO_TYPE_OBJECT encoding
+ if (CACHED_CLASS(NodePath) == p_type_class) {
+ return GDMonoUtils::create_managed_from(p_var.operator NodePath());
+ }
+
+ if (CACHED_CLASS(RID) == p_type_class) {
+ return GDMonoUtils::create_managed_from(p_var.operator ::RID());
+ }
- return variant_to_mono_object(p_var, type);
+ // Godot.Collections.Dictionary or IDictionary
+ if (CACHED_CLASS(Dictionary) == p_type_class || CACHED_CLASS(System_Collections_IDictionary) == p_type_class) {
+ return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary));
+ }
+
+ // Godot.Collections.Array or ICollection or IEnumerable
+ if (CACHED_CLASS(Array) == p_type_class ||
+ CACHED_CLASS(System_Collections_ICollection) == p_type_class ||
+ CACHED_CLASS(System_Collections_IEnumerable) == p_type_class) {
+ 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() + "'.");
}
-MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) {
+MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class) {
+ MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type_class->get_mono_type());
+
+ // Godot.Collections.Dictionary<TKey, TValue>
+ if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
+ return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), p_type_class);
+ }
+
+ // Godot.Collections.Array<T>
+ if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
+ return GDMonoUtils::create_managed_from(p_var.operator Array(), p_type_class);
+ }
+
+ // System.Collections.Generic.Dictionary<TKey, TValue>
+ if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
+ MonoReflectionType *key_reftype = nullptr;
+ MonoReflectionType *value_reftype = nullptr;
+ GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
+ return Dictionary_to_system_generic_dict(p_var.operator Dictionary(), p_type_class, key_reftype, value_reftype);
+ }
+
+ // System.Collections.Generic.List<T>
+ if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
+ MonoReflectionType *elem_reftype = nullptr;
+ GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
+ return Array_to_system_generic_list(p_var.operator Array(), p_type_class, elem_reftype);
+ }
+
+ // IDictionary<TKey, TValue>
+ if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
+ MonoReflectionType *key_reftype;
+ MonoReflectionType *value_reftype;
+ GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
+ GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype);
+
+ return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), godot_dict_class);
+ }
+
+ // ICollection<T> or IEnumerable<T>
+ if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
+ MonoReflectionType *elem_reftype;
+ GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
+ GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype);
+
+ return GDMonoUtils::create_managed_from(p_var.operator Array(), godot_array_class);
+ }
+
+ // 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) {
+ // Variant
+ switch (p_var.get_type()) {
+ case Variant::BOOL: {
+ MonoBoolean val = p_var.operator bool();
+ return BOX_BOOLEAN(val);
+ }
+ case Variant::INT: {
+ int64_t val = p_var.operator int64_t();
+ return BOX_INT64(val);
+ }
+ case Variant::FLOAT: {
+#ifdef REAL_T_IS_DOUBLE
+ double val = p_var.operator double();
+ return BOX_DOUBLE(val);
+#else
+ float val = p_var.operator float();
+ return BOX_FLOAT(val);
+#endif
+ }
+ case Variant::STRING:
+ return (MonoObject *)mono_string_from_godot(p_var.operator String());
+ case Variant::VECTOR2: {
+ GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var.operator ::Vector2());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
+ }
+ case Variant::VECTOR2I: {
+ GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var.operator ::Vector2i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from);
+ }
+ case Variant::RECT2: {
+ GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var.operator ::Rect2());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
+ }
+ case Variant::RECT2I: {
+ GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var.operator ::Rect2i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from);
+ }
+ case Variant::VECTOR3: {
+ GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var.operator ::Vector3());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
+ }
+ case Variant::VECTOR3I: {
+ GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var.operator ::Vector3i());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from);
+ }
+ case Variant::TRANSFORM2D: {
+ GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var.operator ::Transform2D());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
+ }
+ case Variant::PLANE: {
+ GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var.operator ::Plane());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
+ }
+ case Variant::QUATERNION: {
+ GDMonoMarshal::M_Quaternion from = MARSHALLED_OUT(Quaternion, p_var.operator ::Quaternion());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quaternion), &from);
+ }
+ case Variant::AABB: {
+ GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var.operator ::AABB());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from);
+ }
+ case Variant::BASIS: {
+ GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var.operator ::Basis());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from);
+ }
+ case Variant::TRANSFORM3D: {
+ GDMonoMarshal::M_Transform3D from = MARSHALLED_OUT(Transform3D, p_var.operator ::Transform3D());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform3D), &from);
+ }
+ case Variant::COLOR: {
+ GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var.operator ::Color());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
+ }
+ case Variant::STRING_NAME:
+ return GDMonoUtils::create_managed_from(p_var.operator StringName());
+ case Variant::NODE_PATH:
+ return GDMonoUtils::create_managed_from(p_var.operator NodePath());
+ case Variant::RID:
+ return GDMonoUtils::create_managed_from(p_var.operator ::RID());
+ case Variant::OBJECT:
+ return GDMonoUtils::unmanaged_get_managed(p_var.operator Object *());
+ case Variant::CALLABLE: {
+ GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
+ }
+ case Variant::SIGNAL: {
+ GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
+ }
+ case Variant::DICTIONARY:
+ return GDMonoUtils::create_managed_from(p_var.operator Dictionary(), CACHED_CLASS(Dictionary));
+ case Variant::ARRAY:
+ return GDMonoUtils::create_managed_from(p_var.operator Array(), CACHED_CLASS(Array));
+ case Variant::PACKED_BYTE_ARRAY:
+ return (MonoObject *)PackedByteArray_to_mono_array(p_var.operator PackedByteArray());
+ case Variant::PACKED_INT32_ARRAY:
+ return (MonoObject *)PackedInt32Array_to_mono_array(p_var.operator PackedInt32Array());
+ case Variant::PACKED_INT64_ARRAY:
+ return (MonoObject *)PackedInt64Array_to_mono_array(p_var.operator PackedInt64Array());
+ case Variant::PACKED_FLOAT32_ARRAY:
+ return (MonoObject *)PackedFloat32Array_to_mono_array(p_var.operator PackedFloat32Array());
+ case Variant::PACKED_FLOAT64_ARRAY:
+ return (MonoObject *)PackedFloat64Array_to_mono_array(p_var.operator PackedFloat64Array());
+ case Variant::PACKED_STRING_ARRAY:
+ return (MonoObject *)PackedStringArray_to_mono_array(p_var.operator PackedStringArray());
+ case Variant::PACKED_VECTOR2_ARRAY:
+ return (MonoObject *)PackedVector2Array_to_mono_array(p_var.operator PackedVector2Array());
+ case Variant::PACKED_VECTOR3_ARRAY:
+ return (MonoObject *)PackedVector3Array_to_mono_array(p_var.operator PackedVector3Array());
+ case Variant::PACKED_COLOR_ARRAY:
+ return (MonoObject *)PackedColorArray_to_mono_array(p_var.operator PackedColorArray());
+ default:
+ return nullptr;
+ }
+}
+
+size_t variant_get_managed_unboxed_size(const ManagedType &p_type) {
+ // This method prints no errors for unsupported types. It's called on all methods, not only
+ // those that end up being invoked with Variant parameters.
+
+ // For MonoObject* we return 0, as it doesn't need to be stored.
+ constexpr size_t zero_for_mono_object = 0;
+
+ switch (p_type.type_encoding) {
+ case MONO_TYPE_BOOLEAN:
+ return sizeof(MonoBoolean);
+ case MONO_TYPE_CHAR:
+ return sizeof(uint16_t);
+ case MONO_TYPE_I1:
+ return sizeof(int8_t);
+ case MONO_TYPE_I2:
+ return sizeof(int16_t);
+ case MONO_TYPE_I4:
+ return sizeof(int32_t);
+ case MONO_TYPE_I8:
+ return sizeof(int64_t);
+ case MONO_TYPE_U1:
+ return sizeof(uint8_t);
+ case MONO_TYPE_U2:
+ return sizeof(uint16_t);
+ case MONO_TYPE_U4:
+ return sizeof(uint32_t);
+ case MONO_TYPE_U8:
+ return sizeof(uint64_t);
+ case MONO_TYPE_R4:
+ return sizeof(float);
+ case MONO_TYPE_R8:
+ return sizeof(double);
+ case MONO_TYPE_VALUETYPE: {
+ GDMonoClass *vtclass = p_type.type_class;
+
+#define RETURN_CHECK_FOR_STRUCT(m_struct) \
+ if (vtclass == CACHED_CLASS(m_struct)) { \
+ return sizeof(M_##m_struct); \
+ }
+
+ RETURN_CHECK_FOR_STRUCT(Vector2);
+ RETURN_CHECK_FOR_STRUCT(Vector2i);
+ RETURN_CHECK_FOR_STRUCT(Rect2);
+ RETURN_CHECK_FOR_STRUCT(Rect2i);
+ RETURN_CHECK_FOR_STRUCT(Transform2D);
+ RETURN_CHECK_FOR_STRUCT(Vector3);
+ RETURN_CHECK_FOR_STRUCT(Vector3i);
+ RETURN_CHECK_FOR_STRUCT(Basis);
+ RETURN_CHECK_FOR_STRUCT(Quaternion);
+ RETURN_CHECK_FOR_STRUCT(Transform3D);
+ RETURN_CHECK_FOR_STRUCT(AABB);
+ RETURN_CHECK_FOR_STRUCT(Color);
+ RETURN_CHECK_FOR_STRUCT(Plane);
+ RETURN_CHECK_FOR_STRUCT(Callable);
+ RETURN_CHECK_FOR_STRUCT(SignalInfo);
+
+#undef RETURN_CHECK_FOR_STRUCT
+
+ if (mono_class_is_enum(vtclass->get_mono_ptr())) {
+ MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
+ switch (mono_type_get_type(enum_basetype)) {
+ case MONO_TYPE_BOOLEAN:
+ return sizeof(MonoBoolean);
+ case MONO_TYPE_CHAR:
+ return sizeof(uint16_t);
+ case MONO_TYPE_I1:
+ return sizeof(int8_t);
+ case MONO_TYPE_I2:
+ return sizeof(int16_t);
+ case MONO_TYPE_I4:
+ return sizeof(int32_t);
+ case MONO_TYPE_I8:
+ return sizeof(int64_t);
+ case MONO_TYPE_U1:
+ return sizeof(uint8_t);
+ case MONO_TYPE_U2:
+ return sizeof(uint16_t);
+ case MONO_TYPE_U4:
+ return sizeof(uint32_t);
+ case MONO_TYPE_U8:
+ return sizeof(uint64_t);
+ default: {
+ // Enum with unsupported base type. We return nullptr MonoObject* on error.
+ return zero_for_mono_object;
+ }
+ }
+ }
+
+ // Enum with unsupported value type. We return nullptr MonoObject* on error.
+ } break;
+ case MONO_TYPE_STRING:
+ return zero_for_mono_object;
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ case MONO_TYPE_CLASS:
+ case MONO_TYPE_GENERICINST:
+ return zero_for_mono_object;
+ case MONO_TYPE_OBJECT:
+ return zero_for_mono_object;
+ }
+
+ // Unsupported type encoding. We return nullptr MonoObject* on error.
+ return zero_for_mono_object;
+}
+
+void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) {
+#define RETURN_TYPE_VAL(m_type, m_val) \
+ *reinterpret_cast<m_type *>(r_buffer) = m_val; \
+ r_offset += sizeof(m_type); \
+ return r_buffer;
+
+ switch (p_type.type_encoding) {
+ case MONO_TYPE_BOOLEAN:
+ RETURN_TYPE_VAL(MonoBoolean, (MonoBoolean)p_var.operator bool());
+ case MONO_TYPE_CHAR:
+ RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short());
+ case MONO_TYPE_I1:
+ RETURN_TYPE_VAL(int8_t, p_var.operator signed char());
+ case MONO_TYPE_I2:
+ RETURN_TYPE_VAL(int16_t, p_var.operator signed short());
+ case MONO_TYPE_I4:
+ RETURN_TYPE_VAL(int32_t, p_var.operator signed int());
+ case MONO_TYPE_I8:
+ RETURN_TYPE_VAL(int64_t, p_var.operator int64_t());
+ case MONO_TYPE_U1:
+ RETURN_TYPE_VAL(uint8_t, p_var.operator unsigned char());
+ case MONO_TYPE_U2:
+ RETURN_TYPE_VAL(uint16_t, p_var.operator unsigned short());
+ case MONO_TYPE_U4:
+ RETURN_TYPE_VAL(uint32_t, p_var.operator unsigned int());
+ case MONO_TYPE_U8:
+ RETURN_TYPE_VAL(uint64_t, p_var.operator uint64_t());
+ case MONO_TYPE_R4:
+ RETURN_TYPE_VAL(float, p_var.operator float());
+ case MONO_TYPE_R8:
+ RETURN_TYPE_VAL(double, p_var.operator double());
+ case MONO_TYPE_VALUETYPE: {
+ GDMonoClass *vtclass = p_type.type_class;
+
+#define RETURN_CHECK_FOR_STRUCT(m_struct) \
+ if (vtclass == CACHED_CLASS(m_struct)) { \
+ GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \
+ RETURN_TYPE_VAL(M_##m_struct, from); \
+ }
+
+ RETURN_CHECK_FOR_STRUCT(Vector2);
+ RETURN_CHECK_FOR_STRUCT(Vector2i);
+ RETURN_CHECK_FOR_STRUCT(Rect2);
+ RETURN_CHECK_FOR_STRUCT(Rect2i);
+ RETURN_CHECK_FOR_STRUCT(Transform2D);
+ RETURN_CHECK_FOR_STRUCT(Vector3);
+ RETURN_CHECK_FOR_STRUCT(Vector3i);
+ RETURN_CHECK_FOR_STRUCT(Basis);
+ RETURN_CHECK_FOR_STRUCT(Quaternion);
+ RETURN_CHECK_FOR_STRUCT(Transform3D);
+ RETURN_CHECK_FOR_STRUCT(AABB);
+ RETURN_CHECK_FOR_STRUCT(Color);
+ RETURN_CHECK_FOR_STRUCT(Plane);
+
+#undef RETURN_CHECK_FOR_STRUCT
+
+ if (vtclass == CACHED_CLASS(Callable)) {
+ GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
+ RETURN_TYPE_VAL(M_Callable, from);
+ }
+
+ if (vtclass == CACHED_CLASS(SignalInfo)) {
+ GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
+ RETURN_TYPE_VAL(M_SignalInfo, from);
+ }
+
+ if (mono_class_is_enum(vtclass->get_mono_ptr())) {
+ MonoType *enum_basetype = mono_class_enum_basetype(vtclass->get_mono_ptr());
+ switch (mono_type_get_type(enum_basetype)) {
+ case MONO_TYPE_BOOLEAN: {
+ MonoBoolean val = p_var.operator bool();
+ RETURN_TYPE_VAL(MonoBoolean, val);
+ }
+ case MONO_TYPE_CHAR: {
+ uint16_t val = p_var.operator unsigned short();
+ RETURN_TYPE_VAL(uint16_t, val);
+ }
+ case MONO_TYPE_I1: {
+ int8_t val = p_var.operator signed char();
+ RETURN_TYPE_VAL(int8_t, val);
+ }
+ case MONO_TYPE_I2: {
+ int16_t val = p_var.operator signed short();
+ RETURN_TYPE_VAL(int16_t, val);
+ }
+ case MONO_TYPE_I4: {
+ int32_t val = p_var.operator signed int();
+ RETURN_TYPE_VAL(int32_t, val);
+ }
+ case MONO_TYPE_I8: {
+ int64_t val = p_var.operator int64_t();
+ RETURN_TYPE_VAL(int64_t, val);
+ }
+ case MONO_TYPE_U1: {
+ uint8_t val = p_var.operator unsigned char();
+ RETURN_TYPE_VAL(uint8_t, val);
+ }
+ case MONO_TYPE_U2: {
+ uint16_t val = p_var.operator unsigned short();
+ RETURN_TYPE_VAL(uint16_t, val);
+ }
+ case MONO_TYPE_U4: {
+ uint32_t val = p_var.operator unsigned int();
+ RETURN_TYPE_VAL(uint32_t, val);
+ }
+ case MONO_TYPE_U8: {
+ uint64_t val = p_var.operator uint64_t();
+ 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 unsupported value type: '" + p_type.type_class->get_full_name() + "'.");
+ } break;
+#undef RETURN_TYPE_VAL
+ case MONO_TYPE_STRING:
+ return variant_to_mono_string(p_var);
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ return variant_to_mono_array(p_var, p_type.type_class);
+ case MONO_TYPE_CLASS:
+ return variant_to_mono_object_of_class(p_var, p_type.type_class);
+ case MONO_TYPE_GENERICINST:
+ return variant_to_mono_object_of_genericinst(p_var, p_type.type_class);
+ case MONO_TYPE_OBJECT:
+ 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) + ".");
+}
+
+MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) {
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_var->operator bool();
+ MonoBoolean val = p_var.operator bool();
return BOX_BOOLEAN(val);
}
-
case MONO_TYPE_CHAR: {
- uint16_t val = p_var->operator unsigned short();
+ uint16_t val = p_var.operator unsigned short();
return BOX_UINT16(val);
}
-
case MONO_TYPE_I1: {
- int8_t val = p_var->operator signed char();
+ int8_t val = p_var.operator signed char();
return BOX_INT8(val);
}
case MONO_TYPE_I2: {
- int16_t val = p_var->operator signed short();
+ int16_t val = p_var.operator signed short();
return BOX_INT16(val);
}
case MONO_TYPE_I4: {
- int32_t val = p_var->operator signed int();
+ int32_t val = p_var.operator signed int();
return BOX_INT32(val);
}
case MONO_TYPE_I8: {
- int64_t val = p_var->operator int64_t();
+ int64_t val = p_var.operator int64_t();
return BOX_INT64(val);
}
-
case MONO_TYPE_U1: {
- uint8_t val = p_var->operator unsigned char();
+ uint8_t val = p_var.operator unsigned char();
return BOX_UINT8(val);
}
case MONO_TYPE_U2: {
- uint16_t val = p_var->operator unsigned short();
+ uint16_t val = p_var.operator unsigned short();
return BOX_UINT16(val);
}
case MONO_TYPE_U4: {
- uint32_t val = p_var->operator unsigned int();
+ uint32_t val = p_var.operator unsigned int();
return BOX_UINT32(val);
}
case MONO_TYPE_U8: {
- uint64_t val = p_var->operator uint64_t();
+ uint64_t val = p_var.operator uint64_t();
return BOX_UINT64(val);
}
-
case MONO_TYPE_R4: {
- float val = p_var->operator float();
+ float val = p_var.operator float();
return BOX_FLOAT(val);
}
case MONO_TYPE_R8: {
- double val = p_var->operator double();
+ double val = p_var.operator double();
return BOX_DOUBLE(val);
}
-
- case MONO_TYPE_STRING: {
- if (p_var->get_type() == Variant::NIL) {
- return nullptr; // Otherwise, Variant -> String would return the string "Null"
- }
- return (MonoObject *)mono_string_from_godot(p_var->operator String());
- } break;
-
case MONO_TYPE_VALUETYPE: {
GDMonoClass *vtclass = p_type.type_class;
- if (vtclass == CACHED_CLASS(Vector2)) {
- GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
- }
-
- if (vtclass == CACHED_CLASS(Vector2i)) {
- GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var->operator ::Vector2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from);
- }
-
- if (vtclass == CACHED_CLASS(Rect2)) {
- GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
- }
-
- if (vtclass == CACHED_CLASS(Rect2i)) {
- GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var->operator ::Rect2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from);
- }
-
- if (vtclass == CACHED_CLASS(Transform2D)) {
- GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
- }
-
- if (vtclass == CACHED_CLASS(Vector3)) {
- GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
- }
-
- if (vtclass == CACHED_CLASS(Vector3i)) {
- GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var->operator ::Vector3i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from);
- }
-
- if (vtclass == CACHED_CLASS(Basis)) {
- GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from);
- }
-
- if (vtclass == CACHED_CLASS(Quat)) {
- GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from);
- }
-
- if (vtclass == CACHED_CLASS(Transform)) {
- GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from);
- }
-
- if (vtclass == CACHED_CLASS(AABB)) {
- GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from);
- }
+#define RETURN_CHECK_FOR_STRUCT(m_struct) \
+ if (vtclass == CACHED_CLASS(m_struct)) { \
+ GDMonoMarshal::M_##m_struct from = MARSHALLED_OUT(m_struct, p_var.operator ::m_struct()); \
+ return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(m_struct), &from); \
+ }
- if (vtclass == CACHED_CLASS(Color)) {
- GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
- }
-
- if (vtclass == CACHED_CLASS(Plane)) {
- GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
- }
+ RETURN_CHECK_FOR_STRUCT(Vector2);
+ RETURN_CHECK_FOR_STRUCT(Vector2i);
+ RETURN_CHECK_FOR_STRUCT(Rect2);
+ RETURN_CHECK_FOR_STRUCT(Rect2i);
+ RETURN_CHECK_FOR_STRUCT(Transform2D);
+ RETURN_CHECK_FOR_STRUCT(Vector3);
+ RETURN_CHECK_FOR_STRUCT(Vector3i);
+ RETURN_CHECK_FOR_STRUCT(Basis);
+ RETURN_CHECK_FOR_STRUCT(Quaternion);
+ RETURN_CHECK_FOR_STRUCT(Transform3D);
+ RETURN_CHECK_FOR_STRUCT(AABB);
+ RETURN_CHECK_FOR_STRUCT(Color);
+ RETURN_CHECK_FOR_STRUCT(Plane);
+
+#undef RETURN_CHECK_FOR_STRUCT
if (vtclass == CACHED_CLASS(Callable)) {
- GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable());
+ GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var.operator Callable());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
}
if (vtclass == CACHED_CLASS(SignalInfo)) {
- GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal());
+ GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var.operator Signal());
return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
}
@@ -465,316 +935,81 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
MonoClass *enum_baseclass = mono_class_from_mono_type(enum_basetype);
switch (mono_type_get_type(enum_basetype)) {
case MONO_TYPE_BOOLEAN: {
- MonoBoolean val = p_var->operator bool();
+ MonoBoolean val = p_var.operator bool();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_CHAR: {
- uint16_t val = p_var->operator unsigned short();
+ uint16_t val = p_var.operator unsigned short();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_I1: {
- int8_t val = p_var->operator signed char();
+ int8_t val = p_var.operator signed char();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_I2: {
- int16_t val = p_var->operator signed short();
+ int16_t val = p_var.operator signed short();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_I4: {
- int32_t val = p_var->operator signed int();
+ int32_t val = p_var.operator signed int();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_I8: {
- int64_t val = p_var->operator int64_t();
+ int64_t val = p_var.operator int64_t();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_U1: {
- uint8_t val = p_var->operator unsigned char();
+ uint8_t val = p_var.operator unsigned char();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_U2: {
- uint16_t val = p_var->operator unsigned short();
+ uint16_t val = p_var.operator unsigned short();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_U4: {
- uint32_t val = p_var->operator unsigned int();
+ uint32_t val = p_var.operator unsigned int();
return BOX_ENUM(enum_baseclass, val);
}
case MONO_TYPE_U8: {
- uint64_t val = p_var->operator uint64_t();
+ uint64_t val = p_var.operator uint64_t();
return BOX_ENUM(enum_baseclass, val);
}
default: {
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to a managed enum value of unmarshallable base type.");
+ ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to enum value of unsupported base type: '" + GDMonoClass::get_full_name(enum_baseclass) + "'.");
}
}
}
- } break;
-
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_SZARRAY: {
- MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
-
- if (array_type->eklass == CACHED_CLASS_RAW(MonoObject)) {
- return (MonoObject *)Array_to_mono_array(p_var->operator Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(uint8_t)) {
- return (MonoObject *)PackedByteArray_to_mono_array(p_var->operator PackedByteArray());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int32_t)) {
- return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(int64_t)) {
- return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(float)) {
- return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(double)) {
- return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(String)) {
- return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector2)) {
- return (MonoObject *)PackedVector2Array_to_mono_array(p_var->operator PackedVector2Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Vector3)) {
- return (MonoObject *)PackedVector3Array_to_mono_array(p_var->operator PackedVector3Array());
- }
-
- if (array_type->eklass == CACHED_CLASS_RAW(Color)) {
- return (MonoObject *)PackedColorArray_to_mono_array(p_var->operator PackedColorArray());
- }
-
- GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
- if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
- return (MonoObject *)Array_to_mono_array(p_var->operator Array(), array_type_class);
- }
-
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to a managed array of unmarshallable element type.");
- } break;
-
- case MONO_TYPE_CLASS: {
- GDMonoClass *type_class = p_type.type_class;
-
- // GodotObject
- if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
- return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
- }
-
- if (CACHED_CLASS(StringName) == type_class) {
- return GDMonoUtils::create_managed_from(p_var->operator StringName());
- }
-
- if (CACHED_CLASS(NodePath) == type_class) {
- return GDMonoUtils::create_managed_from(p_var->operator NodePath());
- }
-
- if (CACHED_CLASS(RID) == type_class) {
- return GDMonoUtils::create_managed_from(p_var->operator RID());
- }
-
- // Godot.Collections.Dictionary or IDictionary
- if (CACHED_CLASS(Dictionary) == type_class || CACHED_CLASS(System_Collections_IDictionary) == type_class) {
- return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
- }
- // Godot.Collections.Array or ICollection or IEnumerable
- if (CACHED_CLASS(Array) == type_class ||
- CACHED_CLASS(System_Collections_ICollection) == type_class ||
- CACHED_CLASS(System_Collections_IEnumerable) == type_class) {
- return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
- }
- } break;
- case MONO_TYPE_OBJECT: {
- // Variant
- switch (p_var->get_type()) {
- case Variant::BOOL: {
- MonoBoolean val = p_var->operator bool();
- return BOX_BOOLEAN(val);
- }
- case Variant::INT: {
- int64_t val = p_var->operator int64_t();
- return BOX_INT64(val);
- }
- case Variant::FLOAT: {
-#ifdef REAL_T_IS_DOUBLE
- double val = p_var->operator double();
- return BOX_DOUBLE(val);
-#else
- float val = p_var->operator float();
- return BOX_FLOAT(val);
-#endif
- }
- case Variant::STRING:
- return (MonoObject *)mono_string_from_godot(p_var->operator String());
- case Variant::VECTOR2: {
- GDMonoMarshal::M_Vector2 from = MARSHALLED_OUT(Vector2, p_var->operator ::Vector2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2), &from);
- }
- case Variant::VECTOR2I: {
- GDMonoMarshal::M_Vector2i from = MARSHALLED_OUT(Vector2i, p_var->operator ::Vector2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector2i), &from);
- }
- case Variant::RECT2: {
- GDMonoMarshal::M_Rect2 from = MARSHALLED_OUT(Rect2, p_var->operator ::Rect2());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2), &from);
- }
- case Variant::RECT2I: {
- GDMonoMarshal::M_Rect2i from = MARSHALLED_OUT(Rect2i, p_var->operator ::Rect2i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Rect2i), &from);
- }
- case Variant::VECTOR3: {
- GDMonoMarshal::M_Vector3 from = MARSHALLED_OUT(Vector3, p_var->operator ::Vector3());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3), &from);
- }
- case Variant::VECTOR3I: {
- GDMonoMarshal::M_Vector3i from = MARSHALLED_OUT(Vector3i, p_var->operator ::Vector3i());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Vector3i), &from);
- }
- case Variant::TRANSFORM2D: {
- GDMonoMarshal::M_Transform2D from = MARSHALLED_OUT(Transform2D, p_var->operator ::Transform2D());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform2D), &from);
- }
- case Variant::PLANE: {
- GDMonoMarshal::M_Plane from = MARSHALLED_OUT(Plane, p_var->operator ::Plane());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Plane), &from);
- }
- case Variant::QUAT: {
- GDMonoMarshal::M_Quat from = MARSHALLED_OUT(Quat, p_var->operator ::Quat());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Quat), &from);
- }
- case Variant::AABB: {
- GDMonoMarshal::M_AABB from = MARSHALLED_OUT(AABB, p_var->operator ::AABB());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(AABB), &from);
- }
- case Variant::BASIS: {
- GDMonoMarshal::M_Basis from = MARSHALLED_OUT(Basis, p_var->operator ::Basis());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Basis), &from);
- }
- case Variant::TRANSFORM: {
- GDMonoMarshal::M_Transform from = MARSHALLED_OUT(Transform, p_var->operator ::Transform());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Transform), &from);
- }
- case Variant::COLOR: {
- GDMonoMarshal::M_Color from = MARSHALLED_OUT(Color, p_var->operator ::Color());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Color), &from);
- }
- case Variant::STRING_NAME:
- return GDMonoUtils::create_managed_from(p_var->operator StringName());
- case Variant::NODE_PATH:
- return GDMonoUtils::create_managed_from(p_var->operator NodePath());
- case Variant::_RID:
- return GDMonoUtils::create_managed_from(p_var->operator RID());
- case Variant::OBJECT:
- return GDMonoUtils::unmanaged_get_managed(p_var->operator Object *());
- case Variant::CALLABLE: {
- GDMonoMarshal::M_Callable from = GDMonoMarshal::callable_to_managed(p_var->operator Callable());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(Callable), &from);
- }
- case Variant::SIGNAL: {
- GDMonoMarshal::M_SignalInfo from = GDMonoMarshal::signal_info_to_managed(p_var->operator Signal());
- return mono_value_box(mono_domain_get(), CACHED_CLASS_RAW(SignalInfo), &from);
- }
- case Variant::DICTIONARY:
- return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), CACHED_CLASS(Dictionary));
- case Variant::ARRAY:
- return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
- case Variant::PACKED_BYTE_ARRAY:
- return (MonoObject *)PackedByteArray_to_mono_array(p_var->operator PackedByteArray());
- case Variant::PACKED_INT32_ARRAY:
- return (MonoObject *)PackedInt32Array_to_mono_array(p_var->operator PackedInt32Array());
- case Variant::PACKED_INT64_ARRAY:
- return (MonoObject *)PackedInt64Array_to_mono_array(p_var->operator PackedInt64Array());
- case Variant::PACKED_FLOAT32_ARRAY:
- return (MonoObject *)PackedFloat32Array_to_mono_array(p_var->operator PackedFloat32Array());
- case Variant::PACKED_FLOAT64_ARRAY:
- return (MonoObject *)PackedFloat64Array_to_mono_array(p_var->operator PackedFloat64Array());
- case Variant::PACKED_STRING_ARRAY:
- return (MonoObject *)PackedStringArray_to_mono_array(p_var->operator PackedStringArray());
- case Variant::PACKED_VECTOR2_ARRAY:
- return (MonoObject *)PackedVector2Array_to_mono_array(p_var->operator PackedVector2Array());
- case Variant::PACKED_VECTOR3_ARRAY:
- return (MonoObject *)PackedVector3Array_to_mono_array(p_var->operator PackedVector3Array());
- case Variant::PACKED_COLOR_ARRAY:
- return (MonoObject *)PackedColorArray_to_mono_array(p_var->operator PackedColorArray());
- default:
- return nullptr;
- }
- break;
- case MONO_TYPE_GENERICINST: {
- MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
-
- // Godot.Collections.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
- return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
- }
-
- // Godot.Collections.Array<T>
- if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
- return GDMonoUtils::create_managed_from(p_var->operator Array(), p_type.type_class);
- }
-
- // System.Collections.Generic.Dictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_system_generic_dictionary(reftype)) {
- MonoReflectionType *key_reftype = nullptr;
- MonoReflectionType *value_reftype = nullptr;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- return Dictionary_to_system_generic_dict(p_var->operator Dictionary(), p_type.type_class, key_reftype, value_reftype);
- }
-
- // System.Collections.Generic.List<T>
- if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
- MonoReflectionType *elem_reftype = nullptr;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- return Array_to_system_generic_list(p_var->operator Array(), p_type.type_class, elem_reftype);
- }
-
- // IDictionary<TKey, TValue>
- if (GDMonoUtils::Marshal::type_is_generic_idictionary(reftype)) {
- MonoReflectionType *key_reftype;
- MonoReflectionType *value_reftype;
- GDMonoUtils::Marshal::dictionary_get_key_value_types(reftype, &key_reftype, &value_reftype);
- GDMonoClass *godot_dict_class = GDMonoUtils::Marshal::make_generic_dictionary_type(key_reftype, value_reftype);
-
- return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), godot_dict_class);
- }
-
- // ICollection<T> or IEnumerable<T>
- if (GDMonoUtils::Marshal::type_is_generic_icollection(reftype) || GDMonoUtils::Marshal::type_is_generic_ienumerable(reftype)) {
- MonoReflectionType *elem_reftype;
- GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- GDMonoClass *godot_array_class = GDMonoUtils::Marshal::make_generic_array_type(elem_reftype);
-
- return GDMonoUtils::create_managed_from(p_var->operator Array(), godot_array_class);
- }
- } break;
+ 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);
+ case MONO_TYPE_ARRAY:
+ case MONO_TYPE_SZARRAY:
+ return (MonoObject *)variant_to_mono_array(p_var, p_type.type_class);
+ case MONO_TYPE_CLASS:
+ return variant_to_mono_object_of_class(p_var, p_type.type_class);
+ case MONO_TYPE_GENERICINST:
+ return variant_to_mono_object_of_genericinst(p_var, p_type.type_class);
+ case MONO_TYPE_OBJECT:
+ return variant_to_mono_object(p_var);
}
- ERR_FAIL_V_MSG(nullptr, "Attempted to convert Variant to an unmarshallable managed type. Name: '" +
- p_type.type_class->get_name() + "' 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) {
ERR_FAIL_COND_V(!p_type.type_class, Variant());
+#ifdef DEBUG_ENABLED
+ CRASH_COND_MSG(p_type.type_encoding == MONO_TYPE_OBJECT, "Type of object should be known.");
+#endif
+
switch (p_type.type_encoding) {
case MONO_TYPE_BOOLEAN:
return (bool)unbox<MonoBoolean>(p_obj);
-
case MONO_TYPE_CHAR:
return unbox<uint16_t>(p_obj);
-
case MONO_TYPE_I1:
return unbox<int8_t>(p_obj);
case MONO_TYPE_I2:
@@ -783,7 +1018,6 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return unbox<int32_t>(p_obj);
case MONO_TYPE_I8:
return unbox<int64_t>(p_obj);
-
case MONO_TYPE_U1:
return unbox<uint8_t>(p_obj);
case MONO_TYPE_U2:
@@ -792,19 +1026,10 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return unbox<uint32_t>(p_obj);
case MONO_TYPE_U8:
return unbox<uint64_t>(p_obj);
-
case MONO_TYPE_R4:
return unbox<float>(p_obj);
case MONO_TYPE_R8:
return unbox<double>(p_obj);
-
- case MONO_TYPE_STRING: {
- if (p_obj == nullptr) {
- return Variant(); // NIL
- }
- return mono_string_to_godot_not_null((MonoString *)p_obj);
- } break;
-
case MONO_TYPE_VALUETYPE: {
GDMonoClass *vtclass = p_type.type_class;
@@ -840,12 +1065,12 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return MARSHALLED_IN(Basis, unbox_addr<GDMonoMarshal::M_Basis>(p_obj));
}
- if (vtclass == CACHED_CLASS(Quat)) {
- return MARSHALLED_IN(Quat, unbox_addr<GDMonoMarshal::M_Quat>(p_obj));
+ if (vtclass == CACHED_CLASS(Quaternion)) {
+ return MARSHALLED_IN(Quaternion, unbox_addr<GDMonoMarshal::M_Quaternion>(p_obj));
}
- if (vtclass == CACHED_CLASS(Transform)) {
- return MARSHALLED_IN(Transform, unbox_addr<GDMonoMarshal::M_Transform>(p_obj));
+ if (vtclass == CACHED_CLASS(Transform3D)) {
+ return MARSHALLED_IN(Transform3D, unbox_addr<GDMonoMarshal::M_Transform3D>(p_obj));
}
if (vtclass == CACHED_CLASS(AABB)) {
@@ -872,7 +1097,12 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return unbox<int32_t>(p_obj);
}
} break;
-
+ case MONO_TYPE_STRING: {
+ if (p_obj == nullptr) {
+ return Variant(); // NIL
+ }
+ return mono_string_to_godot_not_null((MonoString *)p_obj);
+ } break;
case MONO_TYPE_ARRAY:
case MONO_TYPE_SZARRAY: {
MonoArrayType *array_type = mono_type_get_array_type(p_type.type_class->get_mono_type());
@@ -917,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);
@@ -928,7 +1170,6 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return Variant();
}
} break;
-
case MONO_TYPE_CLASS: {
GDMonoClass *type_class = p_type.type_class;
@@ -936,8 +1177,8 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
if (CACHED_CLASS(GodotObject)->is_assignable_from(type_class)) {
Object *ptr = unbox<Object *>(CACHED_FIELD(GodotObject, ptr)->get_value(p_obj));
if (ptr != nullptr) {
- Reference *ref = Object::cast_to<Reference>(ptr);
- return ref ? Variant(Ref<Reference>(ref)) : Variant(ptr);
+ RefCounted *rc = Object::cast_to<RefCounted>(ptr);
+ return rc ? Variant(Ref<RefCounted>(rc)) : Variant(ptr);
}
return Variant();
}
@@ -973,7 +1214,6 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
return ptr ? Variant(*ptr) : Variant();
}
} break;
-
case MONO_TYPE_GENERICINST: {
MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
@@ -1005,14 +1245,24 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
if (GDMonoUtils::Marshal::type_is_system_generic_list(reftype)) {
MonoReflectionType *elem_reftype = nullptr;
GDMonoUtils::Marshal::array_get_element_type(reftype, &elem_reftype);
- return system_generic_list_to_Array(p_obj, p_type.type_class, 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();
}
@@ -1052,7 +1302,7 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc) {
ManagedType type = ManagedType::from_class(mono_object_get_class(p_obj));
Variant var = GDMonoMarshal::mono_object_to_variant_no_err(p_obj, type);
- if (var.get_type() == Variant::NIL && p_obj != nullptr) {
+ if (var.get_type() == Variant::NIL) { // `&& p_obj != nullptr` but omitted because always true
// Cannot convert MonoObject* to Variant; fallback to 'ToString()'.
MonoException *exc = nullptr;
MonoString *mono_str = GDMonoUtils::object_to_string(p_obj, &exc);
@@ -1072,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);
@@ -1094,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);
@@ -1115,16 +1365,19 @@ 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) {
- GDMonoClass *elem_class = ManagedType::from_reftype(p_elem_reftype).type_class;
+ MonoType *elem_type = mono_reflection_type_get_type(p_elem_reftype);
- String ctor_desc = ":.ctor(System.Collections.Generic.IEnumerable`1<" + elem_class->get_type_desc() + ">)";
+ 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);
CRASH_COND(ctor == nullptr);
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);
@@ -1133,15 +1386,22 @@ MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_cl
return mono_object;
}
-Array system_generic_list_to_Array(MonoObject *p_obj, GDMonoClass *p_class, [[maybe_unused]] MonoReflectionType *p_elem_reftype) {
+Variant system_generic_list_to_Array_variant(MonoObject *p_obj, GDMonoClass *p_class, [[maybe_unused]] MonoReflectionType *p_elem_reftype) {
GDMonoMethod *to_array = p_class->get_method("ToArray", 0);
CRASH_COND(to_array == nullptr);
MonoException *exc = nullptr;
- MonoArray *mono_array = (MonoArray *)to_array->invoke_raw(p_obj, nullptr, &exc);
+ MonoObject *array = to_array->invoke_raw(p_obj, nullptr, &exc);
UNHANDLED_EXCEPTION(exc);
- return mono_array_to_Array(mono_array);
+ ERR_FAIL_NULL_V(array, Variant());
+
+ ManagedType type = ManagedType::from_class(mono_object_get_class(array));
+
+ bool result_is_array = type.type_encoding != MONO_TYPE_SZARRAY && type.type_encoding != MONO_TYPE_ARRAY;
+ ERR_FAIL_COND_V(result_is_array, Variant());
+
+ return mono_object_to_variant(array, type);
}
MonoArray *Array_to_mono_array(const Array &p_array) {
@@ -1156,9 +1416,9 @@ MonoArray *Array_to_mono_array(const Array &p_array) {
return ret;
}
-MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_class) {
+MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class) {
int length = p_array.size();
- MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class->get_mono_ptr(), length);
+ MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class, length);
for (int i = 0; i < length; i++) {
MonoObject *boxed = variant_to_mono_object(p_array[i]);
@@ -1476,10 +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 = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_callable.method_string_name));
+ 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);
}
@@ -1522,10 +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 = unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(p_managed_signal.name_string_name));
+ 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);
}
@@ -1536,5 +1800,4 @@ M_SignalInfo signal_info_to_managed(const Signal &p_signal) {
MonoObject *name_string_name_managed = GDMonoUtils::create_managed_from(p_signal.get_name());
return { owner_managed, name_string_name_managed };
}
-
} // namespace GDMonoMarshal
diff --git a/modules/mono/mono_gd/gd_mono_marshal.h b/modules/mono/mono_gd/gd_mono_marshal.h
index a1fd975916..2f4b619b61 100644
--- a/modules/mono/mono_gd/gd_mono_marshal.h
+++ b/modules/mono/mono_gd/gd_mono_marshal.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef GDMONOMARSHAL_H
#define GDMONOMARSHAL_H
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "../managed_callable.h"
#include "gd_mono.h"
@@ -90,15 +90,40 @@ _FORCE_INLINE_ MonoString *mono_string_from_godot(const String &p_string) {
// Variant
-MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type);
-MonoObject *variant_to_mono_object(const Variant *p_var);
+size_t variant_get_managed_unboxed_size(const ManagedType &p_type);
+void *variant_to_managed_unboxed(const Variant &p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset);
+MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type);
-_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant &p_var) {
- return variant_to_mono_object(&p_var);
-}
+MonoObject *variant_to_mono_object(const Variant &p_var);
+MonoArray *variant_to_mono_array(const Variant &p_var, GDMonoClass *p_type_class);
+MonoObject *variant_to_mono_object_of_class(const Variant &p_var, GDMonoClass *p_type_class);
+MonoObject *variant_to_mono_object_of_genericinst(const Variant &p_var, GDMonoClass *p_type_class);
+MonoString *variant_to_mono_string(const Variant &p_var);
+
+// These overloads were added to avoid passing a `const Variant *` to the `const Variant &`
+// parameter. That would result in the `Variant(bool)` copy constructor being called as
+// pointers are implicitly converted to bool. Implicit conversions are f-ing evil.
-_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant &p_var, const ManagedType &p_type) {
- return variant_to_mono_object(&p_var, p_type);
+_FORCE_INLINE_ void *variant_to_managed_unboxed(const Variant *p_var, const ManagedType &p_type, void *r_buffer, unsigned int &r_offset) {
+ return variant_to_managed_unboxed(*p_var, p_type, r_buffer, r_offset);
+}
+_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_type) {
+ return variant_to_mono_object(*p_var, p_type);
+}
+_FORCE_INLINE_ MonoObject *variant_to_mono_object(const Variant *p_var) {
+ return variant_to_mono_object(*p_var);
+}
+_FORCE_INLINE_ MonoArray *variant_to_mono_array(const Variant *p_var, GDMonoClass *p_type_class) {
+ return variant_to_mono_array(*p_var, p_type_class);
+}
+_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_class(const Variant *p_var, GDMonoClass *p_type_class) {
+ return variant_to_mono_object_of_class(*p_var, p_type_class);
+}
+_FORCE_INLINE_ MonoObject *variant_to_mono_object_of_genericinst(const Variant *p_var, GDMonoClass *p_type_class) {
+ return variant_to_mono_object_of_genericinst(*p_var, p_type_class);
+}
+_FORCE_INLINE_ MonoString *variant_to_mono_string(const Variant *p_var) {
+ return variant_to_mono_string(*p_var);
}
Variant mono_object_to_variant(MonoObject *p_obj);
@@ -115,12 +140,12 @@ MonoObject *Dictionary_to_system_generic_dict(const Dictionary &p_dict, GDMonoCl
Dictionary system_generic_dict_to_Dictionary(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
MonoObject *Array_to_system_generic_list(const Array &p_array, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype);
-Array system_generic_list_to_Array(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype);
+Variant system_generic_list_to_Array_variant(MonoObject *p_obj, GDMonoClass *p_class, MonoReflectionType *p_elem_reftype);
// Array
MonoArray *Array_to_mono_array(const Array &p_array);
-MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_class);
+MonoArray *Array_to_mono_array(const Array &p_array, MonoClass *p_array_type_class);
Array mono_array_to_Array(MonoArray *p_array);
// PackedInt32Array
@@ -209,69 +234,68 @@ 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_Quat = (MATCHES_real_t && (sizeof(Quat) == (sizeof(real_t) * 4)) &&
- offsetof(Quat, x) == (sizeof(real_t) * 0) &&
- offsetof(Quat, y) == (sizeof(real_t) * 1) &&
- offsetof(Quat, z) == (sizeof(real_t) * 2) &&
- offsetof(Quat, w) == (sizeof(real_t) * 3)),
+ 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)),
- MATCHES_Transform = (MATCHES_Basis && MATCHES_Vector3 && (sizeof(Transform) == (sizeof(Basis) + sizeof(Vector3))) &&
- offsetof(Transform, basis) == 0 &&
- offsetof(Transform, origin) == sizeof(Basis)),
+ MATCHES_Transform3D = (MATCHES_Basis && MATCHES_Vector3 && (sizeof(Transform3D) == (sizeof(Basis) + sizeof(Vector3))) &&
+ 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
#ifdef GD_MONO_FORCE_INTEROP_STRUCT_COPY
/* clang-format off */
static_assert(MATCHES_Vector2 && MATCHES_Rect2 && MATCHES_Transform2D && MATCHES_Vector3 &&
- MATCHES_Basis && MATCHES_Quat && MATCHES_Transform && MATCHES_AABB && MATCHES_Color &&
+ MATCHES_Basis && MATCHES_Quaternion && MATCHES_Transform3D && MATCHES_AABB && MATCHES_Color &&
MATCHES_Plane && MATCHES_Vector2i && MATCHES_Rect2i && MATCHES_Vector3i);
/* clang-format on */
#endif
-
} // namespace InteropLayout
#pragma pack(push, 1)
@@ -396,29 +420,29 @@ struct M_Basis {
}
};
-struct M_Quat {
+struct M_Quaternion {
real_t x, y, z, w;
- static _FORCE_INLINE_ Quat convert_to(const M_Quat &p_from) {
- return Quat(p_from.x, p_from.y, p_from.z, p_from.w);
+ static _FORCE_INLINE_ Quaternion convert_to(const M_Quaternion &p_from) {
+ return Quaternion(p_from.x, p_from.y, p_from.z, p_from.w);
}
- static _FORCE_INLINE_ M_Quat convert_from(const Quat &p_from) {
- M_Quat ret = { p_from.x, p_from.y, p_from.z, p_from.w };
+ static _FORCE_INLINE_ M_Quaternion convert_from(const Quaternion &p_from) {
+ M_Quaternion ret = { p_from.x, p_from.y, p_from.z, p_from.w };
return ret;
}
};
-struct M_Transform {
+struct M_Transform3D {
M_Basis basis;
M_Vector3 origin;
- static _FORCE_INLINE_ Transform convert_to(const M_Transform &p_from) {
- return Transform(M_Basis::convert_to(p_from.basis), M_Vector3::convert_to(p_from.origin));
+ static _FORCE_INLINE_ Transform3D convert_to(const M_Transform3D &p_from) {
+ return Transform3D(M_Basis::convert_to(p_from.basis), M_Vector3::convert_to(p_from.origin));
}
- static _FORCE_INLINE_ M_Transform convert_from(const Transform &p_from) {
- M_Transform ret = { M_Basis::convert_from(p_from.basis), M_Vector3::convert_from(p_from.origin) };
+ static _FORCE_INLINE_ M_Transform3D convert_from(const Transform3D &p_from) {
+ M_Transform3D ret = { M_Basis::convert_from(p_from.basis), M_Vector3::convert_from(p_from.origin) };
return ret;
}
};
@@ -509,15 +533,14 @@ DECL_TYPE_MARSHAL_TEMPLATES(Transform2D)
DECL_TYPE_MARSHAL_TEMPLATES(Vector3)
DECL_TYPE_MARSHAL_TEMPLATES(Vector3i)
DECL_TYPE_MARSHAL_TEMPLATES(Basis)
-DECL_TYPE_MARSHAL_TEMPLATES(Quat)
-DECL_TYPE_MARSHAL_TEMPLATES(Transform)
+DECL_TYPE_MARSHAL_TEMPLATES(Quaternion)
+DECL_TYPE_MARSHAL_TEMPLATES(Transform3D)
DECL_TYPE_MARSHAL_TEMPLATES(AABB)
DECL_TYPE_MARSHAL_TEMPLATES(Color)
DECL_TYPE_MARSHAL_TEMPLATES(Plane)
#define MARSHALLED_IN(m_type, m_from_ptr) (GDMonoMarshal::marshalled_in_##m_type(m_from_ptr))
#define MARSHALLED_OUT(m_type, m_from) (GDMonoMarshal::marshalled_out_##m_type(m_from))
-
} // namespace GDMonoMarshal
#endif // GDMONOMARSHAL_H
diff --git a/modules/mono/mono_gd/gd_mono_method.cpp b/modules/mono/mono_gd/gd_mono_method.cpp
index 04f3b25a70..67aabcde10 100644
--- a/modules/mono/mono_gd/gd_mono_method.cpp
+++ b/modules/mono/mono_gd/gd_mono_method.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -75,6 +75,10 @@ void GDMonoMethod::_update_signature(MonoMethodSignature *p_method_sig) {
// clear the cache
method_info_fetched = false;
method_info = MethodInfo();
+
+ for (int i = 0; i < params_count; i++) {
+ params_buffer_size += GDMonoMarshal::variant_get_managed_unboxed_size(param_types[i]);
+ }
}
GDMonoClass *GDMonoMethod::get_enclosing_class() const {
@@ -107,14 +111,15 @@ MonoObject *GDMonoMethod::invoke(MonoObject *p_object, const Variant **p_params,
MonoObject *ret;
if (params_count > 0) {
- MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), params_count);
+ void **params = (void **)alloca(params_count * sizeof(void *));
+ uint8_t *buffer = (uint8_t *)alloca(params_buffer_size);
+ unsigned int offset = 0;
for (int i = 0; i < params_count; i++) {
- MonoObject *boxed_param = GDMonoMarshal::variant_to_mono_object(p_params[i], param_types[i]);
- mono_array_setref(params, i, boxed_param);
+ params[i] = GDMonoMarshal::variant_to_managed_unboxed(p_params[i], param_types[i], buffer + offset, offset);
}
- ret = GDMonoUtils::runtime_invoke_array(mono_method, p_object, params, &exc);
+ ret = GDMonoUtils::runtime_invoke(mono_method, p_object, params, &exc);
} else {
ret = GDMonoUtils::runtime_invoke(mono_method, p_object, nullptr, &exc);
}
@@ -279,16 +284,8 @@ const MethodInfo &GDMonoMethod::get_method_info() {
return method_info;
}
-GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) {
- name = p_name;
-
- mono_method = p_method;
-
- method_info_fetched = false;
-
- attrs_fetched = false;
- attributes = nullptr;
-
+GDMonoMethod::GDMonoMethod(StringName p_name, MonoMethod *p_method) :
+ name(p_name), mono_method(p_method) {
_update_signature();
}
diff --git a/modules/mono/mono_gd/gd_mono_method.h b/modules/mono/mono_gd/gd_mono_method.h
index f78f57dca0..c08ffe904b 100644
--- a/modules/mono/mono_gd/gd_mono_method.h
+++ b/modules/mono/mono_gd/gd_mono_method.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -38,15 +38,16 @@
class GDMonoMethod : public IMonoClassMember {
StringName name;
- int params_count;
+ uint16_t params_count;
+ unsigned int params_buffer_size = 0;
ManagedType return_type;
Vector<ManagedType> param_types;
- bool method_info_fetched;
+ bool method_info_fetched = false;
MethodInfo method_info;
- bool attrs_fetched;
- MonoCustomAttrInfo *attributes;
+ bool attrs_fetched = false;
+ MonoCustomAttrInfo *attributes = nullptr;
void _update_signature();
void _update_signature(MonoMethodSignature *p_method_sig);
@@ -72,7 +73,7 @@ public:
_FORCE_INLINE_ MonoMethod *get_mono_ptr() const { return mono_method; }
- _FORCE_INLINE_ int get_parameters_count() const { return params_count; }
+ _FORCE_INLINE_ uint16_t get_parameters_count() const { return params_count; }
_FORCE_INLINE_ ManagedType get_return_type() const { return return_type; }
MonoObject *invoke(MonoObject *p_object, const Variant **p_params, MonoException **r_exc = nullptr) const;
diff --git a/modules/mono/mono_gd/gd_mono_method_thunk.h b/modules/mono/mono_gd/gd_mono_method_thunk.h
index 01f3ae342a..091d26df1d 100644
--- a/modules/mono/mono_gd/gd_mono_method_thunk.h
+++ b/modules/mono/mono_gd/gd_mono_method_thunk.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/mono_gd/gd_mono_property.cpp b/modules/mono/mono_gd/gd_mono_property.cpp
index bc3be97102..5c7cf29e88 100644
--- a/modules/mono/mono_gd/gd_mono_property.cpp
+++ b/modules/mono/mono_gd/gd_mono_property.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,7 +40,7 @@
GDMonoProperty::GDMonoProperty(MonoProperty *p_mono_property, GDMonoClass *p_owner) {
owner = p_owner;
mono_property = p_mono_property;
- name = mono_property_get_name(mono_property);
+ name = String::utf8(mono_property_get_name(mono_property));
MonoMethod *prop_method = mono_property_get_get_method(mono_property);
@@ -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,25 +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);
- MonoArray *params = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), 1);
- mono_array_setref(params, 0, p_value);
- MonoException *exc = nullptr;
- GDMonoUtils::runtime_invoke_array(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 611ac293e4..9bb1caa759 100644
--- a/modules/mono/mono_gd/gd_mono_property.h
+++ b/modules/mono/mono_gd/gd_mono_property.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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 3f1155f430..09aa9ad948 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,10 +35,10 @@
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
-#include "core/os/dir_access.h"
+#include "core/io/dir_access.h"
+#include "core/object/ref_counted.h"
#include "core/os/mutex.h"
#include "core/os/os.h"
-#include "core/reference.h"
#ifdef TOOLS_ENABLED
#include "editor/debugger/editor_debugger_node.h"
@@ -68,22 +68,10 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
// If the owner does not have a CSharpInstance...
- void *data = unmanaged->get_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index());
-
+ void *data = CSharpLanguage::get_instance_binding(unmanaged);
ERR_FAIL_NULL_V(data, nullptr);
-
CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->value();
-
- if (!script_binding.inited) {
- MutexLock lock(CSharpLanguage::get_singleton()->get_language_bind_mutex());
-
- if (!script_binding.inited) { // Other thread may have set it up
- // Already had a binding that needs to be setup
- CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, unmanaged);
-
- ERR_FAIL_COND_V(!script_binding.inited, nullptr);
- }
- }
+ ERR_FAIL_COND_V(!script_binding.inited, nullptr);
MonoGCHandleData &gchandle = script_binding.gchandle;
@@ -108,15 +96,15 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
gchandle = MonoGCHandleData::new_strong_handle(mono_object);
// Tie managed to unmanaged
- Reference *ref = Object::cast_to<Reference>(unmanaged);
+ RefCounted *rc = Object::cast_to<RefCounted>(unmanaged);
- if (ref) {
+ if (rc) {
// Unsafe refcount increment. The managed instance also counts as a reference.
// This way if the unmanaged world has no references to our owner
// but the managed instance is alive, the refcount will be 1 instead of 0.
- // See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)
- ref->reference();
- CSharpLanguage::get_singleton()->post_unsafe_reference(ref);
+ // See: godot_icall_RefCounted_Dtor(MonoObject *p_obj, Object *p_ptr)
+ rc->reference();
+ CSharpLanguage::get_singleton()->post_unsafe_reference(rc);
}
return mono_object;
@@ -238,7 +226,7 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) {
bool parent_is_object_class = ClassDB::is_parent_class(p_object->get_class_name(), p_native);
ERR_FAIL_COND_V_MSG(!parent_is_object_class, nullptr,
- "Type inherits from native type '" + p_native + "', so it can't be instanced in object of type: '" + p_object->get_class() + "'.");
+ "Type inherits from native type '" + p_native + "', so it can't be instantiated in object of type: '" + p_object->get_class() + "'.");
MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
ERR_FAIL_NULL_V(mono_object, nullptr);
@@ -462,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
}
@@ -480,6 +468,7 @@ void set_pending_exception(MonoException *p_exc) {
#else
if (get_runtime_invoke_count() == 0) {
debug_unhandled_exception(p_exc);
+ return;
}
if (!mono_runtime_set_pending_exception(p_exc, false)) {
@@ -498,13 +487,6 @@ MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, M
return ret;
}
-MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc) {
- GD_MONO_BEGIN_RUNTIME_INVOKE;
- MonoObject *ret = mono_runtime_invoke_array(p_method, p_obj, p_params, (MonoObject **)r_exc);
- GD_MONO_END_RUNTIME_INVOKE;
- return ret;
-}
-
MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc) {
GD_MONO_BEGIN_RUNTIME_INVOKE;
MonoString *ret = mono_object_to_string(p_obj, (MonoObject **)r_exc);
@@ -659,7 +641,6 @@ GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, Mon
UNHANDLED_EXCEPTION(exc);
return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
}
-
} // namespace Marshal
ScopeThreadAttach::ScopeThreadAttach() {
@@ -679,5 +660,4 @@ StringName get_native_godot_class_name(GDMonoClass *p_class) {
StringName *ptr = GDMonoMarshal::unbox<StringName *>(CACHED_FIELD(StringName, ptr)->get_value(native_name_obj));
return ptr ? *ptr : StringName();
}
-
} // namespace GDMonoUtils
diff --git a/modules/mono/mono_gd/gd_mono_utils.h b/modules/mono/mono_gd/gd_mono_utils.h
index 5958bf3cc1..773501e93d 100644
--- a/modules/mono/mono_gd/gd_mono_utils.h
+++ b/modules/mono/mono_gd/gd_mono_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,9 +36,12 @@
#include "../mono_gc_handle.h"
#include "../utils/macros.h"
#include "gd_mono_header.h"
+#ifdef JAVASCRIPT_ENABLED
+#include "gd_mono_wasm_m2n.h"
+#endif
-#include "core/object.h"
-#include "core/reference.h"
+#include "core/object/class_db.h"
+#include "core/object/ref_counted.h"
#define UNHANDLED_EXCEPTION(m_exc) \
if (unlikely(m_exc != nullptr)) { \
@@ -64,7 +67,6 @@ void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoRefl
GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype);
GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
-
} // namespace Marshal
_FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) {
@@ -136,7 +138,6 @@ _FORCE_INLINE_ int &get_runtime_invoke_count_ref() {
}
MonoObject *runtime_invoke(MonoMethod *p_method, void *p_obj, void **p_params, MonoException **r_exc);
-MonoObject *runtime_invoke_array(MonoMethod *p_method, void *p_obj, MonoArray *p_params, MonoException **r_exc);
MonoString *object_to_string(MonoObject *p_obj, MonoException **r_exc);
@@ -157,6 +158,21 @@ private:
StringName get_native_godot_class_name(GDMonoClass *p_class);
+template <typename... P>
+void add_internal_call(const char *p_name, void (*p_func)(P...)) {
+#ifdef JAVASCRIPT_ENABLED
+ GDMonoWasmM2n::ICallTrampolines<P...>::add();
+#endif
+ mono_add_internal_call(p_name, (void *)p_func);
+}
+
+template <typename R, typename... P>
+void add_internal_call(const char *p_name, R (*p_func)(P...)) {
+#ifdef JAVASCRIPT_ENABLED
+ GDMonoWasmM2n::ICallTrampolinesR<R, P...>::add();
+#endif
+ mono_add_internal_call(p_name, (void *)p_func);
+}
} // namespace GDMonoUtils
#define NATIVE_GDMONOCLASS_NAME(m_class) (GDMonoUtils::get_native_godot_class_name(m_class))
diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp b/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp
new file mode 100644
index 0000000000..a477c55456
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_wasm_m2n.cpp
@@ -0,0 +1,79 @@
+/*************************************************************************/
+/* gd_mono_wasm_m2n.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 "gd_mono_wasm_m2n.h"
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "core/templates/oa_hash_map.h"
+
+typedef mono_bool (*GodotMonoM2nIcallTrampolineDispatch)(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs);
+
+// This extern function is implemented in our patched version of Mono
+MONO_API void godot_mono_register_m2n_icall_trampoline_dispatch_hook(GodotMonoM2nIcallTrampolineDispatch hook);
+
+namespace GDMonoWasmM2n {
+
+struct HashMapCookieComparator {
+ static bool compare(const char *p_lhs, const char *p_rhs) {
+ return strcmp(p_lhs, p_rhs) == 0;
+ }
+};
+
+// The default hasher supports 'const char *' C Strings, but we need a custom comparator
+OAHashMap<const char *, TrampolineFunc, HashMapHasherDefault, HashMapCookieComparator> trampolines;
+
+void set_trampoline(const char *cookies, GDMonoWasmM2n::TrampolineFunc trampoline_func) {
+ trampolines.set(cookies, trampoline_func);
+}
+
+mono_bool trampoline_dispatch_hook(const char *cookie, void *target_func, Mono_InterpMethodArguments *margs) {
+ TrampolineFunc *trampoline_func = trampolines.lookup_ptr(cookie);
+
+ if (!trampoline_func) {
+ return false;
+ }
+
+ (*trampoline_func)(target_func, margs);
+ return true;
+}
+
+bool initialized = false;
+
+void lazy_initialize() {
+ // Doesn't need to be thread safe
+ if (!initialized) {
+ initialized = true;
+ godot_mono_register_m2n_icall_trampoline_dispatch_hook(&trampoline_dispatch_hook);
+ }
+}
+} // namespace GDMonoWasmM2n
+
+#endif
diff --git a/modules/mono/mono_gd/gd_mono_wasm_m2n.h b/modules/mono/mono_gd/gd_mono_wasm_m2n.h
new file mode 100644
index 0000000000..c49a62a632
--- /dev/null
+++ b/modules/mono/mono_gd/gd_mono_wasm_m2n.h
@@ -0,0 +1,263 @@
+/*************************************************************************/
+/* gd_mono_wasm_m2n.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 GD_MONO_WASM_M2N_H
+#define GD_MONO_WASM_M2N_H
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "core/string/ustring.h"
+#include "core/typedefs.h"
+
+#include <mono/metadata/loader.h>
+#include <mono/utils/mono-publib.h>
+#include <stdexcept>
+#include <type_traits>
+
+extern "C" {
+
+struct Mono_InterpMethodArguments {
+ size_t ilen;
+ void **iargs;
+ size_t flen;
+ double *fargs;
+ void **retval;
+ size_t is_float_ret;
+ //#ifdef TARGET_WASM
+ void *sig;
+ //#endif
+};
+} // extern "C"
+
+namespace GDMonoWasmM2n {
+
+template <typename T, size_t Size>
+struct array {
+ T elems[Size];
+};
+
+template <typename T>
+constexpr char get_m2n_cookie_impl() {
+#define M2N_REG_COOKIE(m_type, m_cookie) \
+ if constexpr (std::is_same_v<m_type, T>) { \
+ return m_cookie; \
+ }
+
+ M2N_REG_COOKIE(MonoBoolean, 'I');
+ M2N_REG_COOKIE(int8_t, 'I');
+ M2N_REG_COOKIE(uint8_t, 'I');
+ M2N_REG_COOKIE(int16_t, 'I');
+ M2N_REG_COOKIE(uint16_t, 'I');
+ M2N_REG_COOKIE(int32_t, 'I');
+ M2N_REG_COOKIE(uint32_t, 'I');
+ M2N_REG_COOKIE(int64_t, 'L');
+ M2N_REG_COOKIE(uint64_t, 'L');
+ M2N_REG_COOKIE(float, 'F');
+ M2N_REG_COOKIE(double, 'D');
+
+ if constexpr (std::is_pointer_v<T>) {
+ if constexpr (sizeof(void *) == 4) {
+ return 'I';
+ } else {
+ return 'L';
+ }
+ }
+
+ if constexpr (std::is_void_v<T>) {
+ return 'V';
+ }
+
+ return 'X';
+
+#undef M2N_REG_COOKIE
+}
+
+template <typename T>
+constexpr char get_m2n_cookie() {
+ constexpr char cookie = get_m2n_cookie_impl<T>();
+ static_assert(cookie != 'X', "Type not supported in internal call signature.");
+ return cookie;
+}
+
+template <typename... T>
+constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies() {
+ return array<const char, sizeof...(T) + 2>{ 'V', get_m2n_cookie<T>()..., '\0' };
+}
+
+template <typename R, typename... T>
+constexpr array<const char, sizeof...(T) + 2> get_m2n_cookies_r() {
+ return array<const char, sizeof...(T) + 2>{ get_m2n_cookie<R>(), get_m2n_cookie<T>()..., '\0' };
+}
+
+template <typename T>
+constexpr size_t calc_m2n_index(size_t &r_int_idx, size_t &r_float_idx) {
+ constexpr char cookie = get_m2n_cookie<T>();
+
+ static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D');
+
+ if constexpr (cookie == 'I' || cookie == 'L') {
+ size_t ret = r_int_idx;
+ r_int_idx += cookie == 'I' ? 1 : 2;
+ return ret;
+ } else {
+ size_t ret = r_float_idx;
+ r_float_idx += cookie == 'F' ? 1 : 2;
+ return ret;
+ }
+}
+
+template <typename... P>
+constexpr array<size_t, sizeof...(P)> get_indices_for_type() {
+ size_t int_idx = 0;
+ size_t float_idx = 0;
+ return array<size_t, sizeof...(P)>{ calc_m2n_index<P>(int_idx, float_idx)... };
+}
+
+constexpr size_t fidx(size_t p_x) {
+ if constexpr (sizeof(void *) == 4) {
+ return p_x * 2;
+ } else {
+ return p_x;
+ }
+}
+
+template <typename T>
+T m2n_arg_cast(Mono_InterpMethodArguments *p_margs, size_t p_idx) {
+ constexpr char cookie = get_m2n_cookie<T>();
+
+ static_assert(cookie == 'I' || cookie == 'L' || cookie == 'F' || cookie == 'D');
+
+ if constexpr (cookie == 'I') {
+ 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>),
+ "Invalid type for cookie 'L'.");
+
+ union {
+ T l;
+ struct {
+ int32_t lo;
+ int32_t hi;
+ } pair;
+ } p;
+
+ p.pair.lo = (int32_t)(size_t)p_margs->iargs[p_idx];
+ p.pair.hi = (int32_t)(size_t)p_margs->iargs[p_idx + 1];
+
+ return p.l;
+ } else if constexpr (cookie == 'F') {
+ return *reinterpret_cast<float *>(&p_margs->fargs[fidx(p_idx)]);
+ } else if constexpr (cookie == 'D') {
+ return (T)p_margs->fargs[p_idx];
+ }
+}
+
+template <typename... P, size_t... Is>
+void m2n_trampoline_with_idx_seq(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) {
+ constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>();
+ typedef void (*Func)(P...);
+ Func func = (Func)p_target_func;
+ func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...);
+}
+
+template <typename R, typename... P, size_t... Is>
+void m2n_trampoline_with_idx_seq_r(void *p_target_func, Mono_InterpMethodArguments *p_margs, IndexSequence<Is...>) {
+ constexpr array<size_t, sizeof...(P)> indices = get_indices_for_type<P...>();
+ typedef R (*Func)(P...);
+ Func func = (Func)p_target_func;
+ R res = func(m2n_arg_cast<P>(p_margs, indices.elems[Is])...);
+ *reinterpret_cast<R *>(p_margs->retval) = res;
+}
+
+inline void m2n_trampoline_with_idx_seq_0(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
+ typedef void (*Func)();
+ Func func = (Func)p_target_func;
+ func();
+}
+
+template <typename R>
+void m2n_trampoline_with_idx_seq_r0(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
+ typedef R (*Func)();
+ Func func = (Func)p_target_func;
+ R res = func();
+ *reinterpret_cast<R *>(p_margs->retval) = res;
+}
+
+template <typename... P>
+void m2n_trampoline(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
+ if constexpr (sizeof...(P) == 0) {
+ m2n_trampoline_with_idx_seq_0(p_target_func, p_margs);
+ } else {
+ m2n_trampoline_with_idx_seq<P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{});
+ }
+}
+
+template <typename R, typename... P>
+void m2n_trampoline_r(void *p_target_func, Mono_InterpMethodArguments *p_margs) {
+ if constexpr (sizeof...(P) == 0) {
+ m2n_trampoline_with_idx_seq_r0<R>(p_target_func, p_margs);
+ } else {
+ m2n_trampoline_with_idx_seq_r<R, P...>(p_target_func, p_margs, BuildIndexSequence<sizeof...(P)>{});
+ }
+}
+
+typedef void (*TrampolineFunc)(void *p_target_func, Mono_InterpMethodArguments *p_margs);
+
+void set_trampoline(const char *cookies, TrampolineFunc trampoline_func);
+
+void lazy_initialize();
+
+template <typename... P>
+struct ICallTrampolines {
+ static constexpr auto cookies = get_m2n_cookies<P...>();
+
+ static void add() {
+ lazy_initialize();
+ set_trampoline(cookies.elems, &m2n_trampoline<P...>);
+ }
+};
+
+template <typename R, typename... P>
+struct ICallTrampolinesR {
+ static constexpr auto cookies = get_m2n_cookies_r<R, P...>();
+
+ static void add() {
+ lazy_initialize();
+ set_trampoline(cookies.elems, &m2n_trampoline_r<R, P...>);
+ }
+};
+
+void initialize();
+} // namespace GDMonoWasmM2n
+
+#endif
+
+#endif // GD_MONO_WASM_M2N_H
diff --git a/modules/mono/mono_gd/i_mono_class_member.h b/modules/mono/mono_gd/i_mono_class_member.h
index 2e8e01c80e..36e14ba27c 100644
--- a/modules/mono/mono_gd/i_mono_class_member.h
+++ b/modules/mono/mono_gd/i_mono_class_member.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/mono_gd/managed_type.cpp b/modules/mono/mono_gd/managed_type.cpp
index 3e971efece..0acfafe841 100644
--- a/modules/mono/mono_gd/managed_type.cpp
+++ b/modules/mono/mono_gd/managed_type.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/mono_gd/managed_type.h b/modules/mono/mono_gd/managed_type.h
index 491a2f3d20..0456a9a864 100644
--- a/modules/mono/mono_gd/managed_type.h
+++ b/modules/mono/mono_gd/managed_type.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/mono_gd/support/android_support.cpp b/modules/mono/mono_gd/support/android_support.cpp
index 8bcdeec9dd..c65353dfd1 100644
--- a/modules/mono/mono_gd/support/android_support.cpp
+++ b/modules/mono/mono_gd/support/android_support.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -44,7 +44,7 @@
#endif
#include "core/os/os.h"
-#include "core/ustring.h"
+#include "core/string/ustring.h"
#include "platform/android/java_godot_wrapper.h"
#include "platform/android/os_android.h"
#include "platform/android/thread_jandroid.h"
@@ -109,7 +109,7 @@ bool jni_exception_check(JNIEnv *p_env) {
String app_native_lib_dir_cache;
String determine_app_native_lib_dir() {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
ScopedLocalRef<jclass> activityThreadClass(env, env->FindClass("android/app/ActivityThread"));
jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
@@ -134,7 +134,7 @@ String determine_app_native_lib_dir() {
}
String get_app_native_lib_dir() {
- if (app_native_lib_dir_cache.empty())
+ if (app_native_lib_dir_cache.is_empty())
app_native_lib_dir_cache = determine_app_native_lib_dir();
return app_native_lib_dir_cache;
}
@@ -253,7 +253,7 @@ int32_t get_build_version_sdk_int() {
// android.os.Build.VERSION.SDK_INT
if (build_version_sdk_int == 0) {
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jclass versionClass = env->FindClass("android/os/Build$VERSION");
ERR_FAIL_NULL_V(versionClass, 0);
@@ -281,7 +281,7 @@ MonoBoolean _gd_mono_init_cert_store() {
// return false;
// }
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
@@ -322,7 +322,7 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
return nullptr;
}
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
ScopedLocalRef<jstring> js_alias(env, env->NewStringUTF(alias_utf8));
mono_free(alias_utf8);
@@ -355,8 +355,8 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
}
void register_internal_calls() {
- mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", (void *)_gd_mono_init_cert_store);
- mono_add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", (void *)_gd_mono_android_cert_store_lookup);
+ GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_init_cert_store", _gd_mono_init_cert_store);
+ GDMonoUtils::add_internal_call("Android.Runtime.AndroidEnvironment::_gd_mono_android_cert_store_lookup", _gd_mono_android_cert_store_lookup);
}
void initialize() {
@@ -380,14 +380,13 @@ void cleanup() {
if (godot_dl_handle)
gd_mono_android_dlclose(godot_dl_handle, nullptr);
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
if (certStore) {
env->DeleteGlobalRef(certStore);
certStore = nullptr;
}
}
-
} // namespace support
} // namespace android
} // namespace gdmono
@@ -416,8 +415,7 @@ GD_PINVOKE_EXPORT int32_t monodroid_get_system_property(const char *p_name, char
if (r_value) {
if (len >= 0) {
*r_value = (char *)malloc(len + 1);
- if (!*r_value)
- return -1;
+ ERR_FAIL_NULL_V_MSG(*r_value, -1, "Out of memory.");
memcpy(*r_value, prop_value_str, len);
(*r_value)[len] = '\0';
} else {
@@ -438,7 +436,7 @@ GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_up_state(const char
*r_is_up = 0;
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
ERR_FAIL_NULL_V(networkInterfaceClass, 0);
@@ -470,7 +468,7 @@ GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_supports_multicast(
*r_supports_multicast = 0;
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
ERR_FAIL_NULL_V(networkInterfaceClass, 0);
@@ -508,7 +506,7 @@ static void interop_get_active_network_dns_servers(char **r_dns_servers, int *dn
CRASH_COND(get_build_version_sdk_int() < 23);
#endif
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
GodotJavaWrapper *godot_java = ((OS_Android *)OS::get_singleton())->get_godot_java();
jobject activity = godot_java->get_activity();
@@ -638,6 +636,7 @@ GD_PINVOKE_EXPORT int32_t _monodroid_get_dns_servers(void **r_dns_servers_array)
if (dns_servers_count > 0) {
size_t ret_size = sizeof(char *) * (size_t)dns_servers_count;
*r_dns_servers_array = malloc(ret_size); // freed by the BCL
+ ERR_FAIL_NULL_V_MSG(*r_dns_servers_array, -1, "Out of memory.");
memcpy(*r_dns_servers_array, dns_servers, ret_size);
}
@@ -649,7 +648,7 @@ GD_PINVOKE_EXPORT const char *_monodroid_timezone_get_default_id() {
//
// TimeZone.getDefault().getID()
- JNIEnv *env = ThreadAndroid::get_env();
+ JNIEnv *env = get_jni_env();
ScopedLocalRef<jclass> timeZoneClass(env, env->FindClass("java/util/TimeZone"));
ERR_FAIL_NULL_V(timeZoneClass, nullptr);
diff --git a/modules/mono/mono_gd/support/android_support.h b/modules/mono/mono_gd/support/android_support.h
index dc2e6c95ed..0c5dd2764c 100755..100644
--- a/modules/mono/mono_gd/support/android_support.h
+++ b/modules/mono/mono_gd/support/android_support.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#if defined(ANDROID_ENABLED)
-#include "core/ustring.h"
+#include "core/string/ustring.h"
namespace gdmono {
namespace android {
@@ -45,7 +45,6 @@ void initialize();
void cleanup();
void register_internal_calls();
-
} // namespace support
} // namespace android
} // namespace gdmono
diff --git a/modules/mono/mono_gd/support/ios_support.h b/modules/mono/mono_gd/support/ios_support.h
index e28af120e3..28a8806d0e 100755..100644
--- a/modules/mono/mono_gd/support/ios_support.h
+++ b/modules/mono/mono_gd/support/ios_support.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#if defined(IPHONE_ENABLED)
-#include "core/ustring.h"
+#include "core/string/ustring.h"
namespace gdmono {
namespace ios {
@@ -41,7 +41,6 @@ namespace support {
void initialize();
void cleanup();
-
} // namespace support
} // namespace ios
} // namespace gdmono
diff --git a/modules/mono/mono_gd/support/ios_support.mm b/modules/mono/mono_gd/support/ios_support.mm
index dc23c06eba..23424fbaf9 100644
--- a/modules/mono/mono_gd/support/ios_support.mm
+++ b/modules/mono/mono_gd/support/ios_support.mm
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -57,9 +57,9 @@ void ios_mono_log_callback(const char *log_domain, const char *log_level, const
}
void initialize() {
- mono_dllmap_insert(NULL, "System.Native", NULL, "__Internal", NULL);
- mono_dllmap_insert(NULL, "System.IO.Compression.Native", NULL, "__Internal", NULL);
- mono_dllmap_insert(NULL, "System.Security.Cryptography.Native.Apple", NULL, "__Internal", NULL);
+ mono_dllmap_insert(nullptr, "System.Native", nullptr, "__Internal", nullptr);
+ mono_dllmap_insert(nullptr, "System.IO.Compression.Native", nullptr, "__Internal", nullptr);
+ mono_dllmap_insert(nullptr, "System.Security.Cryptography.Native.Apple", nullptr, "__Internal", nullptr);
#ifdef IOS_DEVICE
// This function is defined in an auto-generated source file
@@ -72,7 +72,6 @@ void initialize() {
void cleanup() {
}
-
} // namespace support
} // namespace ios
} // namespace gdmono
@@ -86,7 +85,7 @@ void cleanup() {
GD_PINVOKE_EXPORT const char *xamarin_get_locale_country_code() {
NSLocale *locale = [NSLocale currentLocale];
NSString *countryCode = [locale objectForKey:NSLocaleCountryCode];
- if (countryCode == NULL) {
+ if (countryCode == nullptr) {
return strdup("US");
}
return strdup([countryCode UTF8String]);
diff --git a/modules/mono/register_types.cpp b/modules/mono/register_types.cpp
index 98c3ba1324..98e83335e9 100644
--- a/modules/mono/register_types.cpp
+++ b/modules/mono/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "register_types.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "csharp_script.h"
@@ -38,24 +38,24 @@ CSharpLanguage *script_language_cs = nullptr;
Ref<ResourceFormatLoaderCSharpScript> resource_loader_cs;
Ref<ResourceFormatSaverCSharpScript> resource_saver_cs;
-_GodotSharp *_godotsharp = nullptr;
+mono_bind::GodotSharp *_godotsharp = nullptr;
void register_mono_types() {
- ClassDB::register_class<CSharpScript>();
+ GDREGISTER_CLASS(CSharpScript);
- _godotsharp = memnew(_GodotSharp);
+ _godotsharp = memnew(mono_bind::GodotSharp);
- ClassDB::register_class<_GodotSharp>();
- Engine::get_singleton()->add_singleton(Engine::Singleton("GodotSharp", _GodotSharp::get_singleton()));
+ GDREGISTER_CLASS(mono_bind::GodotSharp);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("GodotSharp", mono_bind::GodotSharp::get_singleton()));
script_language_cs = memnew(CSharpLanguage);
script_language_cs->set_language_index(ScriptServer::get_language_count());
ScriptServer::register_language(script_language_cs);
- resource_loader_cs.instance();
+ resource_loader_cs.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_cs);
- resource_saver_cs.instance();
+ resource_saver_cs.instantiate();
ResourceSaver::add_resource_format_saver(resource_saver_cs);
}
diff --git a/modules/mono/register_types.h b/modules/mono/register_types.h
index e30d9a8abd..1a2ff004b5 100644
--- a/modules/mono/register_types.h
+++ b/modules/mono/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/mono/signal_awaiter_utils.cpp b/modules/mono/signal_awaiter_utils.cpp
index bd67b03c8e..3aaf726fc8 100644
--- a/modules/mono/signal_awaiter_utils.cpp
+++ b/modules/mono/signal_awaiter_utils.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -48,18 +48,10 @@ Error gd_mono_connect_signal_awaiter(Object *p_source, const StringName &p_signa
}
bool SignalAwaiterCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ // Only called if both instances are of type SignalAwaiterCallable. Static cast is safe.
const SignalAwaiterCallable *a = static_cast<const SignalAwaiterCallable *>(p_a);
const SignalAwaiterCallable *b = static_cast<const SignalAwaiterCallable *>(p_b);
-
- if (a->target_id != b->target_id) {
- return false;
- }
-
- if (a->signal != b->signal) {
- return false;
- }
-
- return true;
+ return a->awaiter_handle.handle == b->awaiter_handle.handle;
}
bool SignalAwaiterCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -109,11 +101,15 @@ void SignalAwaiterCallable::call(const Variant **p_arguments, int p_argcount, Va
"Resumed after await, but class instance is gone.");
#endif
- MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount);
+ MonoArray *signal_args = nullptr;
- for (int i = 0; i < p_argcount; i++) {
- MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]);
- mono_array_setref(signal_args, i, boxed);
+ if (p_argcount > 0) {
+ signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), p_argcount);
+
+ for (int i = 0; i < p_argcount; i++) {
+ MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_arguments[i]);
+ mono_array_setref(signal_args, i, boxed);
+ }
}
MonoObject *awaiter = awaiter_handle.get_target();
diff --git a/modules/mono/signal_awaiter_utils.h b/modules/mono/signal_awaiter_utils.h
index c550315a23..e12ea45b3f 100644
--- a/modules/mono/signal_awaiter_utils.h
+++ b/modules/mono/signal_awaiter_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef SIGNAL_AWAITER_UTILS_H
#define SIGNAL_AWAITER_UTILS_H
-#include "core/reference.h"
+#include "core/object/ref_counted.h"
#include "csharp_script.h"
#include "mono_gc_handle.h"
diff --git a/modules/mono/utils/macros.h b/modules/mono/utils/macros.h
index c76619cca4..4a220d89c8 100644
--- a/modules/mono/utils/macros.h
+++ b/modules/mono/utils/macros.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -64,7 +64,6 @@ public:
template <typename F>
ScopeExit<F> operator+(F p_exit_func) { return ScopeExit<F>(p_exit_func); }
};
-
} // namespace gdmono
#define SCOPE_EXIT \
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index a619f0b975..d0a27b27c1 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,13 +29,13 @@
/*************************************************************************/
#include "mono_reg_utils.h"
-#include "core/os/dir_access.h"
+#include "core/io/dir_access.h"
#ifdef WINDOWS_ENABLED
#include "core/os/os.h"
-// Here, after os/os.h
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
namespace MonoRegUtils {
@@ -173,7 +173,7 @@ String find_msbuild_tools_path() {
String output;
int exit_code;
- OS::get_singleton()->execute(vswhere_path, vswhere_args, true, nullptr, &output, &exit_code);
+ OS::get_singleton()->execute(vswhere_path, vswhere_args, &output, &exit_code);
if (exit_code == 0) {
Vector<String> lines = output.split("\n");
@@ -188,7 +188,7 @@ String find_msbuild_tools_path() {
if (key == "installationPath") {
String val = line.substr(sep_idx + 1, line.length()).strip_edges();
- ERR_BREAK(val.empty());
+ ERR_BREAK(val.is_empty());
if (!val.ends_with("\\")) {
val += "\\";
@@ -225,7 +225,6 @@ cleanup:
return msbuild_tools_path;
}
-
} // namespace MonoRegUtils
#endif // WINDOWS_ENABLED
diff --git a/modules/mono/utils/mono_reg_utils.h b/modules/mono/utils/mono_reg_utils.h
index 4ef876f2b6..0e617761ea 100644
--- a/modules/mono/utils/mono_reg_utils.h
+++ b/modules/mono/utils/mono_reg_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#ifdef WINDOWS_ENABLED
-#include "core/ustring.h"
+#include "core/string/ustring.h"
struct MonoRegInfo {
String version;
diff --git a/modules/mono/utils/osx_utils.cpp b/modules/mono/utils/osx_utils.cpp
index e68466b1cf..f4216c8129 100644
--- a/modules/mono/utils/osx_utils.cpp
+++ b/modules/mono/utils/osx_utils.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#ifdef OSX_ENABLED
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
diff --git a/modules/mono/utils/osx_utils.h b/modules/mono/utils/osx_utils.h
index 55002702f8..6704f19077 100644
--- a/modules/mono/utils/osx_utils.h
+++ b/modules/mono/utils/osx_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "core/ustring.h"
+#include "core/string/ustring.h"
#ifndef OSX_UTILS_H
#define OSX_UTILS_H
diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp
index 5d1abd0c09..64aec5d359 100644
--- a/modules/mono/utils/path_utils.cpp
+++ b/modules/mono/utils/path_utils.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,12 +30,13 @@
#include "path_utils.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
+#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#ifdef WINDOWS_ENABLED
+#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define ENV_PATH_SEP ";"
@@ -80,7 +81,7 @@ String cwd() {
}
String abspath(const String &p_path) {
- if (p_path.is_abs_path()) {
+ if (p_path.is_absolute_path()) {
return p_path.simplify_path();
} else {
return path::join(path::cwd(), p_path).simplify_path();
@@ -136,7 +137,7 @@ String realpath(const String &p_path) {
}
String join(const String &p_a, const String &p_b) {
- if (p_a.empty()) {
+ if (p_a.is_empty()) {
return p_b;
}
@@ -165,7 +166,7 @@ String relative_to_impl(const String &p_path, const String &p_relative_to) {
} else {
String base_dir = p_relative_to.get_base_dir();
- if (base_dir.length() <= 2 && (base_dir.empty() || base_dir.ends_with(":"))) {
+ if (base_dir.length() <= 2 && (base_dir.is_empty() || base_dir.ends_with(":"))) {
return p_path;
}
@@ -194,5 +195,4 @@ String relative_to(const String &p_path, const String &p_relative_to) {
return relative_to_impl(path_abs_norm, relative_to_abs_norm);
}
-
} // namespace path
diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h
index bcd8af8bb9..82b8f95f49 100644
--- a/modules/mono/utils/path_utils.h
+++ b/modules/mono/utils/path_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#ifndef PATH_UTILS_H
#define PATH_UTILS_H
-#include "core/string_builder.h"
-#include "core/ustring.h"
+#include "core/string/string_builder.h"
+#include "core/string/ustring.h"
namespace path {
@@ -56,7 +56,6 @@ String abspath(const String &p_path);
String realpath(const String &p_path);
String relative_to(const String &p_path, const String &p_relative_to);
-
} // namespace path
#endif // PATH_UTILS_H
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index 65da4328f6..74f5e6d18a 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "string_utils.h"
-#include "core/os/file_access.h"
+#include "core/io/file_access.h"
#include <stdio.h>
#include <stdlib.h>
@@ -84,7 +84,6 @@ int sfind(const String &p_text, int p_from) {
return -1;
}
-
} // namespace
String sformat(const String &p_text, const Variant &p1, const Variant &p2, const Variant &p3, const Variant &p4, const Variant &p5) {
@@ -140,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) {
@@ -171,10 +170,10 @@ Error read_all_file_utf8(const String &p_path, String &r_content) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'.");
- int len = f->get_len();
+ uint64_t len = f->get_length();
sourcef.resize(len + 1);
uint8_t *w = sourcef.ptrw();
- int r = f->get_buffer(w, len);
+ uint64_t r = f->get_buffer(w, len);
f->close();
memdelete(f);
ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN);
@@ -201,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) {
@@ -232,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'
@@ -240,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/mono/utils/string_utils.h b/modules/mono/utils/string_utils.h
index 0318fec592..3290cb38b9 100644
--- a/modules/mono/utils/string_utils.h
+++ b/modules/mono/utils/string_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#ifndef STRING_FORMAT_H
#define STRING_FORMAT_H
-#include "core/ustring.h"
-#include "core/variant.h"
+#include "core/string/ustring.h"
+#include "core/variant/variant.h"
#include <stdarg.h>
diff --git a/modules/msdfgen/SCsub b/modules/msdfgen/SCsub
new file mode 100644
index 0000000000..227369f4bd
--- /dev/null
+++ b/modules/msdfgen/SCsub
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_msdfgen = env_modules.Clone()
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+if env["builtin_msdfgen"]:
+ env_msdfgen.disable_warnings()
+
+ thirdparty_dir = "#thirdparty/msdfgen/"
+ thirdparty_sources = [
+ "core/Contour.cpp",
+ "core/EdgeHolder.cpp",
+ "core/MSDFErrorCorrection.cpp",
+ "core/Projection.cpp",
+ "core/Scanline.cpp",
+ "core/Shape.cpp",
+ "core/SignedDistance.cpp",
+ "core/Vector2.cpp",
+ "core/contour-combiners.cpp",
+ "core/edge-coloring.cpp",
+ "core/edge-segments.cpp",
+ "core/edge-selectors.cpp",
+ "core/equation-solver.cpp",
+ "core/msdf-error-correction.cpp",
+ "core/msdfgen.cpp",
+ "core/rasterization.cpp",
+ "core/render-sdf.cpp",
+ "core/sdf-error-estimation.cpp",
+ "core/shape-description.cpp",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_msdfgen.Append(CPPPATH=["#thirdparty/freetype/include", "#thirdparty/msdfgen", "#thirdparty/nanosvg"])
+
+ lib = env_msdfgen.add_library("msdfgen_builtin", thirdparty_sources)
+ thirdparty_obj += lib
+
+ # Needs to be appended to arrive after libscene in the linker call,
+ # but we don't want it to arrive *after* system libs, so manual hack
+ # LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
+ # and then plain strings for system library. We insert between the two.
+ inserted = False
+ for idx, linklib in enumerate(env["LIBS"]):
+ if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object
+ env["LIBS"].insert(idx, lib)
+ inserted = True
+ break
+ if not inserted:
+ env.Append(LIBS=[lib])
+
+
+# Godot source files
+
+module_obj = []
+
+env_msdfgen.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/msdfgen/config.py b/modules/msdfgen/config.py
new file mode 100644
index 0000000000..653e466a74
--- /dev/null
+++ b/modules/msdfgen/config.py
@@ -0,0 +1,6 @@
+def can_build(env, platform):
+ return env.module_check_dependencies("msdfgen", ["freetype"])
+
+
+def configure(env):
+ pass
diff --git a/modules/opus/register_types.cpp b/modules/msdfgen/register_types.cpp
index a4329e142c..ad60a66d2d 100644
--- a/modules/opus/register_types.cpp
+++ b/modules/msdfgen/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,6 @@
#include "register_types.h"
-// Dummy module as libvorbis is needed by other modules (theora ...)
+void register_msdfgen_types() {}
-void register_opus_types() {}
-
-void unregister_opus_types() {}
+void unregister_msdfgen_types() {}
diff --git a/modules/msdfgen/register_types.h b/modules/msdfgen/register_types.h
new file mode 100644
index 0000000000..fb776c14ed
--- /dev/null
+++ b/modules/msdfgen/register_types.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* 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 MSDFGEN_REGISTER_TYPES_H
+#define MSDFGEN_REGISTER_TYPES_H
+
+void register_msdfgen_types();
+void unregister_msdfgen_types();
+
+#endif // MSDFGEN_REGISTER_TYPES_H
diff --git a/modules/gdnavigation/SCsub b/modules/navigation/SCsub
index 877d601c6a..22b5509b32 100644
--- a/modules/gdnavigation/SCsub
+++ b/modules/navigation/SCsub
@@ -5,6 +5,10 @@ Import("env_modules")
env_navigation = env_modules.Clone()
+# Thirdparty source files
+
+thirdparty_obj = []
+
# Recast Thirdparty source files
if env["builtin_recast"]:
thirdparty_dir = "#thirdparty/recastnavigation/Recast/"
@@ -23,28 +27,37 @@ if env["builtin_recast"]:
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_navigation.Prepend(CPPPATH=[thirdparty_dir + "/Include"])
+ env_navigation.Prepend(CPPPATH=[thirdparty_dir + "Include"])
env_thirdparty = env_navigation.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
-
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
# RVO Thirdparty source files
if env["builtin_rvo2"]:
- thirdparty_dir = "#thirdparty/rvo2"
+ thirdparty_dir = "#thirdparty/rvo2/"
thirdparty_sources = [
- "/src/Agent.cpp",
- "/src/KdTree.cpp",
+ "Agent.cpp",
+ "KdTree.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
- env_navigation.Prepend(CPPPATH=[thirdparty_dir + "/src"])
+ env_navigation.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_navigation.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+
+
+env.modules_sources += thirdparty_obj
# Godot source files
-env_navigation.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_navigation.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/gdnative/xr/config.py b/modules/navigation/config.py
index d22f9454ed..d22f9454ed 100644
--- a/modules/gdnative/xr/config.py
+++ b/modules/navigation/config.py
diff --git a/modules/gdnavigation/gd_navigation_server.cpp b/modules/navigation/godot_navigation_server.cpp
index c80cdcfeab..f600f07c87 100644
--- a/modules/gdnavigation/gd_navigation_server.cpp
+++ b/modules/navigation/godot_navigation_server.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* gd_navigation_server.cpp */
+/* godot_navigation_server.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "gd_navigation_server.h"
+#include "godot_navigation_server.h"
#include "core/os/mutex.h"
@@ -44,208 +44,212 @@
/// an instance of that struct with the submitted parameters.
/// Then, that struct is stored in an array; the `sync` function consume that array.
-#define COMMAND_1(F_NAME, T_0, D_0) \
- struct MERGE(F_NAME, _command) : public SetCommand { \
- T_0 d_0; \
- MERGE(F_NAME, _command) \
- (T_0 p_d_0) : \
- d_0(p_d_0) {} \
- virtual void exec(GdNavigationServer *server) { \
- server->MERGE(_cmd_, F_NAME)(d_0); \
- } \
- }; \
- void GdNavigationServer::F_NAME(T_0 D_0) const { \
- auto cmd = memnew(MERGE(F_NAME, _command)( \
- D_0)); \
- add_command(cmd); \
- } \
- void GdNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0)
-
-#define COMMAND_2(F_NAME, T_0, D_0, T_1, D_1) \
- struct MERGE(F_NAME, _command) : public SetCommand { \
- T_0 d_0; \
- T_1 d_1; \
- MERGE(F_NAME, _command) \
- ( \
- T_0 p_d_0, \
- T_1 p_d_1) : \
- d_0(p_d_0), \
- d_1(p_d_1) {} \
- virtual void exec(GdNavigationServer *server) { \
- server->MERGE(_cmd_, F_NAME)(d_0, d_1); \
- } \
- }; \
- void GdNavigationServer::F_NAME(T_0 D_0, T_1 D_1) const { \
- auto cmd = memnew(MERGE(F_NAME, _command)( \
- D_0, \
- D_1)); \
- add_command(cmd); \
- } \
- void GdNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
-
-#define COMMAND_4(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3) \
- struct MERGE(F_NAME, _command) : public SetCommand { \
- T_0 d_0; \
- T_1 d_1; \
- T_2 d_2; \
- T_3 d_3; \
- MERGE(F_NAME, _command) \
- ( \
- T_0 p_d_0, \
- T_1 p_d_1, \
- T_2 p_d_2, \
- T_3 p_d_3) : \
- d_0(p_d_0), \
- d_1(p_d_1), \
- d_2(p_d_2), \
- d_3(p_d_3) {} \
- virtual void exec(GdNavigationServer *server) { \
- server->MERGE(_cmd_, F_NAME)(d_0, d_1, d_2, d_3); \
- } \
- }; \
- void GdNavigationServer::F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) const { \
- auto cmd = memnew(MERGE(F_NAME, _command)( \
- D_0, \
- D_1, \
- D_2, \
- D_3)); \
- add_command(cmd); \
- } \
- void GdNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
-
-GdNavigationServer::GdNavigationServer() :
+#define COMMAND_1(F_NAME, T_0, D_0) \
+ struct MERGE(F_NAME, _command) : public SetCommand { \
+ T_0 d_0; \
+ MERGE(F_NAME, _command) \
+ (T_0 p_d_0) : \
+ d_0(p_d_0) {} \
+ virtual void exec(GodotNavigationServer *server) { \
+ server->MERGE(_cmd_, F_NAME)(d_0); \
+ } \
+ }; \
+ void GodotNavigationServer::F_NAME(T_0 D_0) const { \
+ auto cmd = memnew(MERGE(F_NAME, _command)( \
+ D_0)); \
+ add_command(cmd); \
+ } \
+ void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0)
+
+#define COMMAND_2(F_NAME, T_0, D_0, T_1, D_1) \
+ struct MERGE(F_NAME, _command) : public SetCommand { \
+ T_0 d_0; \
+ T_1 d_1; \
+ MERGE(F_NAME, _command) \
+ ( \
+ T_0 p_d_0, \
+ T_1 p_d_1) : \
+ d_0(p_d_0), \
+ d_1(p_d_1) {} \
+ virtual void exec(GodotNavigationServer *server) { \
+ server->MERGE(_cmd_, F_NAME)(d_0, d_1); \
+ } \
+ }; \
+ void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1) const { \
+ auto cmd = memnew(MERGE(F_NAME, _command)( \
+ D_0, \
+ D_1)); \
+ add_command(cmd); \
+ } \
+ void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1)
+
+#define COMMAND_4(F_NAME, T_0, D_0, T_1, D_1, T_2, D_2, T_3, D_3) \
+ struct MERGE(F_NAME, _command) : public SetCommand { \
+ T_0 d_0; \
+ T_1 d_1; \
+ T_2 d_2; \
+ T_3 d_3; \
+ MERGE(F_NAME, _command) \
+ ( \
+ T_0 p_d_0, \
+ T_1 p_d_1, \
+ T_2 p_d_2, \
+ T_3 p_d_3) : \
+ d_0(p_d_0), \
+ d_1(p_d_1), \
+ d_2(p_d_2), \
+ d_3(p_d_3) {} \
+ virtual void exec(GodotNavigationServer *server) { \
+ server->MERGE(_cmd_, F_NAME)(d_0, d_1, d_2, d_3); \
+ } \
+ }; \
+ void GodotNavigationServer::F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3) const { \
+ auto cmd = memnew(MERGE(F_NAME, _command)( \
+ D_0, \
+ D_1, \
+ D_2, \
+ D_3)); \
+ add_command(cmd); \
+ } \
+ void GodotNavigationServer::MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
+
+GodotNavigationServer::GodotNavigationServer() :
NavigationServer3D() {
}
-GdNavigationServer::~GdNavigationServer() {
+GodotNavigationServer::~GodotNavigationServer() {
flush_queries();
}
-void GdNavigationServer::add_command(SetCommand *command) const {
- auto mut_this = const_cast<GdNavigationServer *>(this);
+void GodotNavigationServer::add_command(SetCommand *command) const {
+ GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
{
MutexLock lock(commands_mutex);
mut_this->commands.push_back(command);
}
}
-RID GdNavigationServer::map_create() const {
- auto mut_this = const_cast<GdNavigationServer *>(this);
+RID GodotNavigationServer::map_create() const {
+ GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
- NavMap *space = memnew(NavMap);
- RID rid = map_owner.make_rid(space);
+ RID rid = map_owner.make_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) {
if (!map_is_active(p_map)) {
active_maps.push_back(map);
+ active_maps_update_id.push_back(map->get_map_update_id());
}
} else {
- active_maps.erase(map);
+ 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);
}
}
-bool GdNavigationServer::map_is_active(RID p_map) const {
- NavMap *map = map_owner.getornull(p_map);
+bool GodotNavigationServer::map_is_active(RID p_map) const {
+ 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 GdNavigationServer::map_get_up(RID p_map) const {
- const NavMap *map = map_owner.getornull(p_map);
+Vector3 GodotNavigationServer::map_get_up(RID p_map) const {
+ 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 GdNavigationServer::map_get_cell_size(RID p_map) const {
- const NavMap *map = map_owner.getornull(p_map);
+real_t GodotNavigationServer::map_get_cell_size(RID p_map) const {
+ 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 GdNavigationServer::map_get_edge_connection_margin(RID p_map) const {
- const NavMap *map = map_owner.getornull(p_map);
+real_t GodotNavigationServer::map_get_edge_connection_margin(RID p_map) const {
+ 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> GdNavigationServer::map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize) const {
- const NavMap *map = map_owner.getornull(p_map);
+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.get_or_null(p_map);
ERR_FAIL_COND_V(map == nullptr, Vector<Vector3>());
- return map->get_path(p_origin, p_destination, p_optimize);
+ return map->get_path(p_origin, p_destination, p_optimize, p_layers);
}
-Vector3 GdNavigationServer::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);
+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.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 GdNavigationServer::map_get_closest_point(RID p_map, const Vector3 &p_point) const {
- const NavMap *map = map_owner.getornull(p_map);
+Vector3 GodotNavigationServer::map_get_closest_point(RID p_map, const Vector3 &p_point) const {
+ 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 GdNavigationServer::map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const {
- const NavMap *map = map_owner.getornull(p_map);
+Vector3 GodotNavigationServer::map_get_closest_point_normal(RID p_map, const Vector3 &p_point) const {
+ 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 GdNavigationServer::map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const {
- const NavMap *map = map_owner.getornull(p_map);
+RID GodotNavigationServer::map_get_closest_point_owner(RID p_map, const Vector3 &p_point) const {
+ 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);
}
-RID GdNavigationServer::region_create() const {
- auto mut_this = const_cast<GdNavigationServer *>(this);
+RID GodotNavigationServer::region_create() const {
+ GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
- NavRegion *reg = memnew(NavRegion);
- RID rid = region_owner.make_rid(reg);
+ RID rid = region_owner.make_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) {
@@ -258,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);
@@ -266,21 +270,35 @@ COMMAND_2(region_set_map, RID, p_region, RID, p_map) {
}
}
-COMMAND_2(region_set_transform, RID, p_region, Transform, p_transform) {
- NavRegion *region = region_owner.getornull(p_region);
+COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform) {
+ 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.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.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);
}
-void GdNavigationServer::region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const {
+void GodotNavigationServer::region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const {
ERR_FAIL_COND(r_mesh.is_null());
ERR_FAIL_COND(p_node == nullptr);
@@ -290,17 +308,38 @@ void GdNavigationServer::region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p
#endif
}
-RID GdNavigationServer::agent_create() const {
- auto mut_this = const_cast<GdNavigationServer *>(this);
+int GodotNavigationServer::region_get_connections_count(RID p_region) const {
+ 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.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.get_or_null(p_region);
+ ERR_FAIL_COND_V(!region, Vector3());
+
+ return region->get_connection_pathway_end(p_connection_id);
+}
+
+RID GodotNavigationServer::agent_create() const {
+ GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
- RvoAgent *agent = memnew(RvoAgent());
- RID rid = agent_owner.make_rid(agent);
+ RID rid = agent_owner.make_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()) {
@@ -314,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);
@@ -327,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 GdNavigationServer::agent_is_map_changed(RID p_agent) const {
- RvoAgent *agent = agent_owner.getornull(p_agent);
+bool GodotNavigationServer::agent_is_map_changed(RID p_agent) const {
+ 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);
@@ -413,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();
@@ -429,12 +468,13 @@ COMMAND_1(free, RID, p_object) {
agents[i]->set_map(nullptr);
}
- active_maps.erase(map);
+ int map_index = active_maps.find(map);
+ active_maps.remove(map_index);
+ active_maps_update_id.remove(map_index);
map_owner.free(p_object);
- memdelete(map);
} 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) {
@@ -443,10 +483,9 @@ COMMAND_1(free, RID, p_object) {
}
region_owner.free(p_object);
- memdelete(region);
} 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) {
@@ -455,20 +494,19 @@ COMMAND_1(free, RID, p_object) {
}
agent_owner.free(p_object);
- memdelete(agent);
} else {
ERR_FAIL_COND("Invalid ID.");
}
}
-void GdNavigationServer::set_active(bool p_active) const {
- auto mut_this = const_cast<GdNavigationServer *>(this);
+void GodotNavigationServer::set_active(bool p_active) const {
+ GodotNavigationServer *mut_this = const_cast<GodotNavigationServer *>(this);
MutexLock lock(mut_this->operations_mutex);
mut_this->active = p_active;
}
-void GdNavigationServer::flush_queries() {
+void GodotNavigationServer::flush_queries() {
// In c++ we can't be sure that this is performed in the main thread
// even with mutable functions.
MutexLock lock(commands_mutex);
@@ -480,7 +518,7 @@ void GdNavigationServer::flush_queries() {
commands.clear();
}
-void GdNavigationServer::process(real_t p_delta_time) {
+void GodotNavigationServer::process(real_t p_delta_time) {
flush_queries();
if (!active) {
@@ -490,10 +528,17 @@ void GdNavigationServer::process(real_t p_delta_time) {
// In c++ we can't be sure that this is performed in the main thread
// even with mutable functions.
MutexLock lock(operations_mutex);
- for (int i(0); i < active_maps.size(); i++) {
+ for (uint32_t i(0); i < active_maps.size(); i++) {
active_maps[i]->sync();
active_maps[i]->step(p_delta_time);
active_maps[i]->dispatch_callbacks();
+
+ // Emit a signal if a map changed.
+ const uint32_t new_map_update_id = active_maps[i]->get_map_update_id();
+ if (new_map_update_id != active_maps_update_id[i]) {
+ emit_signal(SNAME("map_changed"), active_maps[i]->get_self());
+ active_maps_update_id[i] = new_map_update_id;
+ }
}
}
diff --git a/modules/gdnavigation/gd_navigation_server.h b/modules/navigation/godot_navigation_server.h
index e3e02f3d7c..65224493fd 100644
--- a/modules/gdnavigation/gd_navigation_server.h
+++ b/modules/navigation/godot_navigation_server.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* gd_navigation_server.h */
+/* godot_navigation_server.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,11 +28,12 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef GD_NAVIGATION_SERVER_H
-#define GD_NAVIGATION_SERVER_H
+#ifndef GODOT_NAVIGATION_SERVER_H
+#define GODOT_NAVIGATION_SERVER_H
-#include "core/rid.h"
-#include "core/rid_owner.h"
+#include "core/templates/local_vector.h"
+#include "core/templates/rid.h"
+#include "core/templates/rid_owner.h"
#include "servers/navigation_server_3d.h"
#include "nav_map.h"
@@ -60,30 +61,31 @@
virtual void F_NAME(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3 = D_3_DEF) const; \
void MERGE(_cmd_, F_NAME)(T_0 D_0, T_1 D_1, T_2 D_2, T_3 D_3)
-class GdNavigationServer;
+class GodotNavigationServer;
struct SetCommand {
virtual ~SetCommand() {}
- virtual void exec(GdNavigationServer *server) = 0;
+ virtual void exec(GodotNavigationServer *server) = 0;
};
-class GdNavigationServer : public NavigationServer3D {
+class GodotNavigationServer : public NavigationServer3D {
Mutex commands_mutex;
/// Mutex used to make any operation threadsafe.
Mutex operations_mutex;
std::vector<SetCommand *> commands;
- mutable RID_PtrOwner<NavMap> map_owner;
- mutable RID_PtrOwner<NavRegion> region_owner;
- mutable RID_PtrOwner<RvoAgent> agent_owner;
+ mutable RID_Owner<NavMap> map_owner;
+ mutable RID_Owner<NavRegion> region_owner;
+ mutable RID_Owner<RvoAgent> agent_owner;
bool active = true;
- Vector<NavMap *> active_maps;
+ LocalVector<NavMap *> active_maps;
+ LocalVector<uint32_t> active_maps_update_id;
public:
- GdNavigationServer();
- virtual ~GdNavigationServer();
+ GodotNavigationServer();
+ virtual ~GodotNavigationServer();
void add_command(SetCommand *command) const;
@@ -100,7 +102,7 @@ public:
COMMAND_2(map_set_edge_connection_margin, RID, p_map, real_t, p_connection_margin);
virtual real_t map_get_edge_connection_margin(RID p_map) const;
- virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize) const;
+ virtual Vector<Vector3> map_get_path(RID p_map, Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers = 1) const;
virtual Vector3 map_get_closest_point_to_segment(RID p_map, const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision = false) const;
virtual Vector3 map_get_closest_point(RID p_map, const Vector3 &p_point) const;
@@ -109,9 +111,14 @@ public:
virtual RID region_create() const;
COMMAND_2(region_set_map, RID, p_region, RID, p_map);
- COMMAND_2(region_set_transform, RID, p_region, Transform, p_transform);
+ COMMAND_2(region_set_layers, RID, p_region, uint32_t, p_layers);
+ virtual uint32_t region_get_layers(RID p_region) const;
+ COMMAND_2(region_set_transform, RID, p_region, Transform3D, p_transform);
COMMAND_2(region_set_navmesh, RID, p_region, Ref<NavigationMesh>, p_nav_mesh);
virtual void region_bake_navmesh(Ref<NavigationMesh> r_mesh, Node *p_node) const;
+ virtual int region_get_connections_count(RID p_region) const;
+ virtual Vector3 region_get_connection_pathway_start(RID p_region, int p_connection_id) const;
+ virtual Vector3 region_get_connection_pathway_end(RID p_region, int p_connection_id) const;
virtual RID agent_create() const;
COMMAND_2(agent_set_map, RID, p_agent, RID, p_map);
@@ -139,4 +146,4 @@ public:
#undef COMMAND_2
#undef COMMAND_4_DEF
-#endif // GD_NAVIGATION_SERVER_H
+#endif // GODOT_NAVIGATION_SERVER_H
diff --git a/modules/gdnavigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 7919e6a01f..0c8f0ed8c9 100644
--- a/modules/gdnavigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,7 +40,7 @@
@author AndreaCatania
*/
-#define USE_ENTRY_POINT
+#define THREE_POINTS_CROSS_PRODUCT(m_a, m_b, m_c) (((m_c) - (m_a)).cross((m_b) - (m_a)))
void NavMap::set_up(Vector3 p_up) {
up = p_up;
@@ -70,44 +70,52 @@ gd::PointKey NavMap::get_point_key(const Vector3 &p_pos) const {
return p;
}
-Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize) const {
+Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers) const {
+ // Find the start poly and the end poly on this map.
const gd::Polygon *begin_poly = nullptr;
const gd::Polygon *end_poly = nullptr;
Vector3 begin_point;
Vector3 end_point;
float begin_d = 1e20;
float end_d = 1e20;
-
// Find the initial poly and the end poly on this map.
for (size_t i(0); i < polygons.size(); i++) {
const gd::Polygon &p = polygons[i];
+ // Only consider the polygon if it in a region with compatible layers.
+ if ((p_layers & p.owner->get_layers()) == 0) {
+ continue;
+ }
+
// For each point cast a face and check the distance between the origin/destination
- for (size_t point_id = 2; point_id < p.points.size(); point_id++) {
- Face3 f(p.points[point_id - 2].pos, p.points[point_id - 1].pos, p.points[point_id].pos);
- Vector3 spoint = f.get_closest_point_to(p_origin);
- float dpoint = spoint.distance_to(p_origin);
- if (dpoint < begin_d) {
- begin_d = dpoint;
+ for (size_t point_id = 0; point_id < p.points.size(); point_id++) {
+ const Vector3 p1 = p.points[point_id].pos;
+ const Vector3 p2 = p.points[(point_id + 1) % p.points.size()].pos;
+ const Vector3 p3 = p.points[(point_id + 2) % p.points.size()].pos;
+ const Face3 face(p1, p2, p3);
+
+ Vector3 point = face.get_closest_point_to(p_origin);
+ float distance_to_point = point.distance_to(p_origin);
+ if (distance_to_point < begin_d) {
+ begin_d = distance_to_point;
begin_poly = &p;
- begin_point = spoint;
+ begin_point = point;
}
- spoint = f.get_closest_point_to(p_destination);
- dpoint = spoint.distance_to(p_destination);
- if (dpoint < end_d) {
- end_d = dpoint;
+ point = face.get_closest_point_to(p_destination);
+ distance_to_point = point.distance_to(p_destination);
+ if (distance_to_point < end_d) {
+ end_d = distance_to_point;
end_poly = &p;
- end_point = spoint;
+ end_point = point;
}
}
}
+ // Check for trivial cases
if (!begin_poly || !end_poly) {
- // No path
return Vector<Vector3>();
}
-
if (begin_poly == end_poly) {
Vector<Vector3> path;
path.resize(2);
@@ -116,90 +124,89 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
return path;
}
+ // List of all reachable navigation polys.
std::vector<gd::NavigationPoly> navigation_polys;
navigation_polys.reserve(polygons.size() * 0.75);
- // The elements indices in the `navigation_polys`.
- int least_cost_id(-1);
- List<uint32_t> open_list;
- bool found_route = false;
+ // Add the start polygon to the reachable navigation polygons.
+ gd::NavigationPoly begin_navigation_poly = gd::NavigationPoly(begin_poly);
+ begin_navigation_poly.self_id = 0;
+ begin_navigation_poly.entry = begin_point;
+ begin_navigation_poly.back_navigation_edge_pathway_start = begin_point;
+ begin_navigation_poly.back_navigation_edge_pathway_end = begin_point;
+ navigation_polys.push_back(begin_navigation_poly);
- navigation_polys.push_back(gd::NavigationPoly(begin_poly));
- {
- least_cost_id = 0;
- gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id];
- least_cost_poly->self_id = least_cost_id;
- least_cost_poly->entry = begin_point;
- }
+ // List of polygon IDs to visit.
+ List<uint32_t> to_visit;
+ to_visit.push_back(0);
- open_list.push_back(0);
+ // This is an implementation of the A* algorithm.
+ int least_cost_id = 0;
+ bool found_route = false;
const gd::Polygon *reachable_end = nullptr;
float reachable_d = 1e30;
bool is_reachable = true;
- while (found_route == false) {
- {
- // Takes the current least_cost_poly neighbors and compute the traveled_distance of each
- for (size_t i = 0; i < navigation_polys[least_cost_id].poly->edges.size(); i++) {
- gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id];
+ while (true) {
+ gd::NavigationPoly *least_cost_poly = &navigation_polys[least_cost_id];
+
+ // Takes the current least_cost_poly neighbors (iterating over its edges) and compute the traveled_distance.
+ for (size_t i = 0; i < least_cost_poly->poly->edges.size(); i++) {
+ const gd::Edge &edge = least_cost_poly->poly->edges[i];
+
+ // Iterate over connections in this edge, then compute the new optimized travel distance assigned to this polygon.
+ for (int connection_index = 0; connection_index < edge.connections.size(); connection_index++) {
+ const gd::Edge::Connection &connection = edge.connections[connection_index];
- const gd::Edge &edge = least_cost_poly->poly->edges[i];
- if (!edge.other_polygon) {
+ // Only consider the connection to another polygon if this polygon is in a region with compatible layers.
+ if ((p_layers & connection.polygon->owner->get_layers()) == 0) {
continue;
}
-#ifdef USE_ENTRY_POINT
- Vector3 edge_line[2] = {
- least_cost_poly->poly->points[i].pos,
- least_cost_poly->poly->points[(i + 1) % least_cost_poly->poly->points.size()].pos
- };
-
- const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly->entry, edge_line);
+ Vector3 pathway[2] = { connection.pathway_start, connection.pathway_end };
+ const Vector3 new_entry = Geometry3D::get_closest_point_to_segment(least_cost_poly->entry, pathway);
const float new_distance = least_cost_poly->entry.distance_to(new_entry) + least_cost_poly->traveled_distance;
-#else
- const float new_distance = least_cost_poly->poly->center.distance_to(edge.other_polygon->center) + least_cost_poly->traveled_distance;
-#endif
- auto it = std::find(
+ const std::vector<gd::NavigationPoly>::iterator it = std::find(
navigation_polys.begin(),
navigation_polys.end(),
- gd::NavigationPoly(edge.other_polygon));
+ gd::NavigationPoly(connection.polygon));
if (it != navigation_polys.end()) {
- // Oh this was visited already, can we win the cost?
- if (it->traveled_distance > new_distance) {
- it->prev_navigation_poly_id = least_cost_id;
- it->back_navigation_edge = edge.other_edge;
+ // Polygon already visited, check if we can reduce the travel cost.
+ if (new_distance < it->traveled_distance) {
+ it->back_navigation_poly_id = least_cost_id;
+ it->back_navigation_edge = connection.edge;
+ it->back_navigation_edge_pathway_start = connection.pathway_start;
+ it->back_navigation_edge_pathway_end = connection.pathway_end;
it->traveled_distance = new_distance;
-#ifdef USE_ENTRY_POINT
it->entry = new_entry;
-#endif
}
} else {
- // Add to open neighbours
-
- navigation_polys.push_back(gd::NavigationPoly(edge.other_polygon));
- gd::NavigationPoly *np = &navigation_polys[navigation_polys.size() - 1];
-
- np->self_id = navigation_polys.size() - 1;
- np->prev_navigation_poly_id = least_cost_id;
- np->back_navigation_edge = edge.other_edge;
- np->traveled_distance = new_distance;
-#ifdef USE_ENTRY_POINT
- np->entry = new_entry;
-#endif
- open_list.push_back(navigation_polys.size() - 1);
+ // Add the neighbour polygon to the reachable ones.
+ gd::NavigationPoly new_navigation_poly = gd::NavigationPoly(connection.polygon);
+ new_navigation_poly.self_id = navigation_polys.size();
+ new_navigation_poly.back_navigation_poly_id = least_cost_id;
+ new_navigation_poly.back_navigation_edge = connection.edge;
+ new_navigation_poly.back_navigation_edge_pathway_start = connection.pathway_start;
+ new_navigation_poly.back_navigation_edge_pathway_end = connection.pathway_end;
+ new_navigation_poly.traveled_distance = new_distance;
+ new_navigation_poly.entry = new_entry;
+ navigation_polys.push_back(new_navigation_poly);
+
+ // Add the neighbour polygon to the polygons to visit.
+ to_visit.push_back(navigation_polys.size() - 1);
}
}
}
- // Removes the least cost polygon from the open list so we can advance.
- open_list.erase(least_cost_id);
+ // Removes the least cost polygon from the list of polygons to visit so we can advance.
+ to_visit.erase(least_cost_id);
- if (open_list.size() == 0) {
- // When the open list is empty at this point the End Polygon is not reachable
- // so use the further reachable polygon
+ // When the list of polygons to visit is empty at this point it means the End Polygon is not reachable
+ if (to_visit.size() == 0) {
+ // Thus use the further reachable polygon
ERR_BREAK_MSG(is_reachable == false, "It's not expect to not find the most reachable polygons");
is_reachable = false;
if (reachable_end == nullptr) {
@@ -224,26 +231,21 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
gd::NavigationPoly np = navigation_polys[0];
navigation_polys.clear();
navigation_polys.push_back(np);
- open_list.clear();
- open_list.push_back(0);
+ to_visit.clear();
+ to_visit.push_back(0);
reachable_end = nullptr;
continue;
}
- // Now take the new least_cost_poly from the open list.
+ // Find the polygon with the minimum cost from the list of polygons to visit.
least_cost_id = -1;
float least_cost = 1e30;
-
- for (auto element = open_list.front(); element != nullptr; element = element->next()) {
+ for (List<uint32_t>::Element *element = to_visit.front(); element != nullptr; element = element->next()) {
gd::NavigationPoly *np = &navigation_polys[element->get()];
float cost = np->traveled_distance;
-#ifdef USE_ENTRY_POINT
cost += np->entry.distance_to(end_point);
-#else
- cost += np->poly->center.distance_to(end_point);
-#endif
if (cost < least_cost) {
least_cost_id = np->self_id;
least_cost = cost;
@@ -263,124 +265,108 @@ Vector<Vector3> NavMap::get_path(Vector3 p_origin, Vector3 p_destination, bool p
// Check if we reached the end
if (navigation_polys[least_cost_id].poly == end_poly) {
- // Yep, done!!
found_route = true;
break;
}
}
- if (found_route) {
- Vector<Vector3> path;
- if (p_optimize) {
- // String pulling
-
- gd::NavigationPoly *apex_poly = &navigation_polys[least_cost_id];
- Vector3 apex_point = end_point;
- Vector3 portal_left = apex_point;
- Vector3 portal_right = apex_point;
- gd::NavigationPoly *left_poly = apex_poly;
- gd::NavigationPoly *right_poly = apex_poly;
- gd::NavigationPoly *p = apex_poly;
-
- path.push_back(end_point);
+ // If we did not find a route, return an empty path.
+ if (!found_route) {
+ return Vector<Vector3>();
+ }
- while (p) {
- Vector3 left;
- Vector3 right;
+ Vector<Vector3> path;
+ // Optimize the path.
+ if (p_optimize) {
+ // Set the apex poly/point to the end point
+ gd::NavigationPoly *apex_poly = &navigation_polys[least_cost_id];
+ Vector3 apex_point = end_point;
-#define CLOCK_TANGENT(m_a, m_b, m_c) (((m_a) - (m_c)).cross((m_a) - (m_b)))
+ gd::NavigationPoly *left_poly = apex_poly;
+ Vector3 left_portal = apex_point;
+ gd::NavigationPoly *right_poly = apex_poly;
+ Vector3 right_portal = apex_point;
- if (p->poly == begin_poly) {
- left = begin_point;
- right = begin_point;
- } else {
- int prev = p->back_navigation_edge;
- int prev_n = (p->back_navigation_edge + 1) % p->poly->points.size();
- left = p->poly->points[prev].pos;
- right = p->poly->points[prev_n].pos;
+ gd::NavigationPoly *p = apex_poly;
- if (p->poly->clockwise) {
- SWAP(left, right);
- }
- }
+ path.push_back(end_point);
- bool skip = false;
-
- if (CLOCK_TANGENT(apex_point, portal_left, left).dot(up) >= 0) {
- //process
- if (portal_left == apex_point || CLOCK_TANGENT(apex_point, left, portal_right).dot(up) > 0) {
- left_poly = p;
- portal_left = left;
- } else {
- clip_path(navigation_polys, path, apex_poly, portal_right, right_poly);
-
- apex_point = portal_right;
- p = right_poly;
- left_poly = p;
- apex_poly = p;
- portal_left = apex_point;
- portal_right = apex_point;
- path.push_back(apex_point);
- skip = true;
- }
- }
+ while (p) {
+ // Set left and right points of the pathway between polygons.
+ Vector3 left = p->back_navigation_edge_pathway_start;
+ Vector3 right = p->back_navigation_edge_pathway_end;
+ if (THREE_POINTS_CROSS_PRODUCT(apex_point, left, right).dot(up) < 0) {
+ SWAP(left, right);
+ }
- if (!skip && CLOCK_TANGENT(apex_point, portal_right, right).dot(up) <= 0) {
- //process
- if (portal_right == apex_point || CLOCK_TANGENT(apex_point, right, portal_left).dot(up) < 0) {
- right_poly = p;
- portal_right = right;
- } else {
- clip_path(navigation_polys, path, apex_poly, portal_left, left_poly);
-
- apex_point = portal_left;
- p = left_poly;
- right_poly = p;
- apex_poly = p;
- portal_right = apex_point;
- portal_left = apex_point;
- path.push_back(apex_point);
- }
+ bool skip = false;
+ if (THREE_POINTS_CROSS_PRODUCT(apex_point, left_portal, left).dot(up) >= 0) {
+ //process
+ if (left_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, left, right_portal).dot(up) > 0) {
+ left_poly = p;
+ left_portal = left;
+ } else {
+ clip_path(navigation_polys, path, apex_poly, right_portal, right_poly);
+
+ apex_point = right_portal;
+ p = right_poly;
+ left_poly = p;
+ apex_poly = p;
+ left_portal = apex_point;
+ right_portal = apex_point;
+ path.push_back(apex_point);
+ skip = true;
}
+ }
- if (p->prev_navigation_poly_id != -1) {
- p = &navigation_polys[p->prev_navigation_poly_id];
+ if (!skip && THREE_POINTS_CROSS_PRODUCT(apex_point, right_portal, right).dot(up) <= 0) {
+ //process
+ if (right_portal == apex_point || THREE_POINTS_CROSS_PRODUCT(apex_point, right, left_portal).dot(up) < 0) {
+ right_poly = p;
+ right_portal = right;
} else {
- // The end
- p = nullptr;
+ clip_path(navigation_polys, path, apex_poly, left_portal, left_poly);
+
+ apex_point = left_portal;
+ p = left_poly;
+ right_poly = p;
+ apex_poly = p;
+ right_portal = apex_point;
+ left_portal = apex_point;
+ path.push_back(apex_point);
}
}
- if (path[path.size() - 1] != begin_point) {
- path.push_back(begin_point);
+ // Go to the previous polygon.
+ if (p->back_navigation_poly_id != -1) {
+ p = &navigation_polys[p->back_navigation_poly_id];
+ } else {
+ // The end
+ p = nullptr;
}
+ }
- path.invert();
-
- } else {
- path.push_back(end_point);
+ // If the last point is not the begin point, add it to the list.
+ if (path[path.size() - 1] != begin_point) {
+ path.push_back(begin_point);
+ }
- // Add mid points
- int np_id = least_cost_id;
- while (np_id != -1) {
-#ifdef USE_ENTRY_POINT
- Vector3 point = navigation_polys[np_id].entry;
-#else
- int prev = navigation_polys[np_id].back_navigation_edge;
- int prev_n = (navigation_polys[np_id].back_navigation_edge + 1) % navigation_polys[np_id].poly->points.size();
- Vector3 point = (navigation_polys[np_id].poly->points[prev].pos + navigation_polys[np_id].poly->points[prev_n].pos) * 0.5;
-#endif
+ path.reverse();
- path.push_back(point);
- np_id = navigation_polys[np_id].prev_navigation_poly_id;
- }
+ } else {
+ path.push_back(end_point);
- path.invert();
+ // Add mid points
+ int np_id = least_cost_id;
+ while (np_id != -1) {
+ path.push_back(navigation_polys[np_id].entry);
+ np_id = navigation_polys[np_id].back_navigation_poly_id;
}
- return path;
+ path.reverse();
}
- return Vector<Vector3>();
+
+ return path;
}
Vector3 NavMap::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const {
@@ -518,7 +504,7 @@ void NavMap::add_region(NavRegion *p_region) {
}
void NavMap::remove_region(NavRegion *p_region) {
- std::vector<NavRegion *>::iterator it = std::find(regions.begin(), regions.end(), p_region);
+ const std::vector<NavRegion *>::iterator it = std::find(regions.begin(), regions.end(), p_region);
if (it != regions.end()) {
regions.erase(it);
regenerate_links = true;
@@ -538,7 +524,7 @@ void NavMap::add_agent(RvoAgent *agent) {
void NavMap::remove_agent(RvoAgent *agent) {
remove_agent_as_controlled(agent);
- auto it = std::find(agents.begin(), agents.end(), agent);
+ const std::vector<RvoAgent *>::iterator it = std::find(agents.begin(), agents.end(), agent);
if (it != agents.end()) {
agents.erase(it);
agents_dirty = true;
@@ -554,13 +540,14 @@ void NavMap::set_agent_as_controlled(RvoAgent *agent) {
}
void NavMap::remove_agent_as_controlled(RvoAgent *agent) {
- auto it = std::find(controlled_agents.begin(), controlled_agents.end(), agent);
+ const std::vector<RvoAgent *>::iterator it = std::find(controlled_agents.begin(), controlled_agents.end(), agent);
if (it != controlled_agents.end()) {
controlled_agents.erase(it);
}
}
void NavMap::sync() {
+ // Check if we need to update the links.
if (regenerate_polygons) {
for (size_t r(0); r < regions.size(); r++) {
regions[r]->scratch_polygons();
@@ -575,27 +562,30 @@ void NavMap::sync() {
}
if (regenerate_links) {
- // Copy all region polygons in the map.
+ // Remove regions connections.
+ for (size_t r(0); r < regions.size(); r++) {
+ regions[r]->get_connections().clear();
+ }
+
+ // Resize the polygon count.
int count = 0;
for (size_t r(0); r < regions.size(); r++) {
count += regions[r]->get_polygons().size();
}
-
polygons.resize(count);
- count = 0;
+ // Copy all region polygons in the map.
+ count = 0;
for (size_t r(0); r < regions.size(); r++) {
std::copy(
regions[r]->get_polygons().data(),
regions[r]->get_polygons().data() + regions[r]->get_polygons().size(),
polygons.begin() + count);
-
count += regions[r]->get_polygons().size();
}
- // Connects the `Edges` of all the `Polygons` of all `Regions` each other.
- Map<gd::EdgeKey, gd::Connection> connections;
-
+ // Group all edges per key.
+ Map<gd::EdgeKey, Vector<gd::Edge::Connection>> connections;
for (size_t poly_id(0); poly_id < polygons.size(); poly_id++) {
gd::Polygon &poly(polygons[poly_id]);
@@ -603,69 +593,40 @@ void NavMap::sync() {
int next_point = (p + 1) % poly.points.size();
gd::EdgeKey ek(poly.points[p].key, poly.points[next_point].key);
- Map<gd::EdgeKey, gd::Connection>::Element *connection = connections.find(ek);
+ Map<gd::EdgeKey, Vector<gd::Edge::Connection>>::Element *connection = connections.find(ek);
if (!connection) {
- // Nothing yet
- gd::Connection c;
- c.A = &poly;
- c.A_edge = p;
- c.B = nullptr;
- c.B_edge = -1;
- connections[ek] = c;
-
- } else if (connection->get().B == nullptr) {
- CRASH_COND(connection->get().A == nullptr); // Unreachable
-
- // Connect the two Polygons by this edge
- connection->get().B = &poly;
- connection->get().B_edge = p;
-
- connection->get().A->edges[connection->get().A_edge].this_edge = connection->get().A_edge;
- connection->get().A->edges[connection->get().A_edge].other_polygon = connection->get().B;
- connection->get().A->edges[connection->get().A_edge].other_edge = connection->get().B_edge;
-
- connection->get().B->edges[connection->get().B_edge].this_edge = connection->get().B_edge;
- connection->get().B->edges[connection->get().B_edge].other_polygon = connection->get().A;
- connection->get().B->edges[connection->get().B_edge].other_edge = connection->get().A_edge;
+ connections[ek] = Vector<gd::Edge::Connection>();
+ }
+ if (connections[ek].size() <= 1) {
+ // Add the polygon/edge tuple to this key.
+ gd::Edge::Connection new_connection;
+ new_connection.polygon = &poly;
+ new_connection.edge = p;
+ new_connection.pathway_start = poly.points[p].pos;
+ new_connection.pathway_end = poly.points[next_point].pos;
+ connections[ek].push_back(new_connection);
} else {
// The edge is already connected with another edge, skip.
- ERR_PRINT("Attempted to merge a navigation mesh triangle edge with another already-merged edge. This happens when the Navigation3D's `cell_size` is different from the one used to generate the navigation mesh. This will cause navigation problem.");
+ ERR_PRINT("Attempted to merge a navigation mesh triangle edge with another already-merged edge. This happens when the current `cell_size` is different from the one used to generate the navigation mesh. This will cause navigation problem.");
}
}
}
- // Takes all the free edges.
- std::vector<gd::FreeEdge> free_edges;
- free_edges.reserve(connections.size());
-
- for (auto connection_element = connections.front(); connection_element; connection_element = connection_element->next()) {
- if (connection_element->get().B == nullptr) {
- CRASH_COND(connection_element->get().A == nullptr); // Unreachable
- CRASH_COND(connection_element->get().A_edge < 0); // Unreachable
-
- // This is a free edge
- uint32_t id(free_edges.size());
- free_edges.push_back(gd::FreeEdge());
- free_edges[id].is_free = true;
- free_edges[id].poly = connection_element->get().A;
- free_edges[id].edge_id = connection_element->get().A_edge;
- uint32_t point_0(free_edges[id].edge_id);
- uint32_t point_1((free_edges[id].edge_id + 1) % free_edges[id].poly->points.size());
- Vector3 pos_0 = free_edges[id].poly->points[point_0].pos;
- Vector3 pos_1 = free_edges[id].poly->points[point_1].pos;
- Vector3 relative = pos_1 - pos_0;
- free_edges[id].edge_center = (pos_0 + pos_1) / 2.0;
- free_edges[id].edge_dir = relative.normalized();
- free_edges[id].edge_len_squared = relative.length_squared();
+ Vector<gd::Edge::Connection> free_edges;
+ 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.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.value.size() != 1, vformat("Number of connection != 1. Found: %d", E.value.size()));
+ free_edges.push_back(E.value[0]);
}
}
- const float ecm_squared(edge_connection_margin * edge_connection_margin);
-#define LEN_TOLLERANCE 0.1
-#define DIR_TOLLERANCE 0.9
- // In front of tolerance
-#define IFO_TOLLERANCE 0.5
-
// Find the compatible near edges.
//
// Note:
@@ -673,43 +634,67 @@ void NavMap::sync() {
// to be connected, create new polygons to remove that small gap is
// not really useful and would result in wasteful computation during
// connection, integration and path finding.
- for (size_t i(0); i < free_edges.size(); i++) {
- if (!free_edges[i].is_free) {
- continue;
- }
- gd::FreeEdge &edge = free_edges[i];
- for (size_t y(0); y < free_edges.size(); y++) {
- gd::FreeEdge &other_edge = free_edges[y];
- if (i == y || !other_edge.is_free || edge.poly->owner == other_edge.poly->owner) {
+ for (int i = 0; i < free_edges.size(); i++) {
+ const gd::Edge::Connection &free_edge = free_edges[i];
+ Vector3 edge_p1 = free_edge.polygon->points[free_edge.edge].pos;
+ Vector3 edge_p2 = free_edge.polygon->points[(free_edge.edge + 1) % free_edge.polygon->points.size()].pos;
+
+ for (int j = 0; j < free_edges.size(); j++) {
+ const gd::Edge::Connection &other_edge = free_edges[j];
+ if (i == j || free_edge.polygon->owner == other_edge.polygon->owner) {
continue;
}
- Vector3 rel_centers = other_edge.edge_center - edge.edge_center;
- if (ecm_squared > rel_centers.length_squared() // Are enough closer?
- && ABS(edge.edge_len_squared - other_edge.edge_len_squared) < LEN_TOLLERANCE // Are the same length?
- && ABS(edge.edge_dir.dot(other_edge.edge_dir)) > DIR_TOLLERANCE // Are aligned?
- && ABS(rel_centers.normalized().dot(edge.edge_dir)) < IFO_TOLLERANCE // Are one in front the other?
- ) {
- // The edges can be connected
- edge.is_free = false;
- other_edge.is_free = false;
-
- edge.poly->edges[edge.edge_id].this_edge = edge.edge_id;
- edge.poly->edges[edge.edge_id].other_edge = other_edge.edge_id;
- edge.poly->edges[edge.edge_id].other_polygon = other_edge.poly;
-
- other_edge.poly->edges[other_edge.edge_id].this_edge = other_edge.edge_id;
- other_edge.poly->edges[other_edge.edge_id].other_edge = edge.edge_id;
- other_edge.poly->edges[other_edge.edge_id].other_polygon = edge.poly;
+ Vector3 other_edge_p1 = other_edge.polygon->points[other_edge.edge].pos;
+ Vector3 other_edge_p2 = other_edge.polygon->points[(other_edge.edge + 1) % other_edge.polygon->points.size()].pos;
+
+ // Compute the projection of the opposite edge on the current one
+ Vector3 edge_vector = edge_p2 - edge_p1;
+ float projected_p1_ratio = edge_vector.dot(other_edge_p1 - edge_p1) / (edge_vector.length_squared());
+ float projected_p2_ratio = edge_vector.dot(other_edge_p2 - edge_p1) / (edge_vector.length_squared());
+ if ((projected_p1_ratio < 0.0 && projected_p2_ratio < 0.0) || (projected_p1_ratio > 1.0 && projected_p2_ratio > 1.0)) {
+ continue;
+ }
+
+ // Check if the two edges are close to each other enough and compute a pathway between the two regions.
+ Vector3 self1 = edge_vector * CLAMP(projected_p1_ratio, 0.0, 1.0) + edge_p1;
+ Vector3 other1;
+ if (projected_p1_ratio >= 0.0 && projected_p1_ratio <= 1.0) {
+ other1 = other_edge_p1;
+ } else {
+ other1 = other_edge_p1.lerp(other_edge_p2, (1.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));
}
+ if (other1.distance_to(self1) > edge_connection_margin) {
+ continue;
+ }
+
+ Vector3 self2 = edge_vector * CLAMP(projected_p2_ratio, 0.0, 1.0) + edge_p1;
+ Vector3 other2;
+ if (projected_p2_ratio >= 0.0 && projected_p2_ratio <= 1.0) {
+ other2 = other_edge_p2;
+ } else {
+ other2 = other_edge_p1.lerp(other_edge_p2, (0.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));
+ }
+ if (other2.distance_to(self2) > edge_connection_margin) {
+ continue;
+ }
+
+ // The edges can now be connected.
+ gd::Edge::Connection new_connection = other_edge;
+ new_connection.pathway_start = (self1 + other1) / 2.0;
+ new_connection.pathway_end = (self2 + other2) / 2.0;
+ free_edge.polygon->edges[free_edge.edge].connections.push_back(new_connection);
+
+ // Add the connection to the region_connection map.
+ free_edge.polygon->owner->get_connections().push_back(new_connection);
}
}
- }
- if (regenerate_links) {
- map_update_id = map_update_id + 1 % 9999999;
+ // Update the update ID.
+ map_update_id = (map_update_id + 1) % 9999999;
}
+ // Update agents tree.
if (agents_dirty) {
std::vector<RVO::Agent *> raw_agents;
raw_agents.reserve(agents.size());
@@ -749,7 +734,7 @@ void NavMap::dispatch_callbacks() {
void NavMap::clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const {
Vector3 from = path[path.size() - 1];
- if (from.distance_to(p_to_point) < CMP_EPSILON) {
+ if (from.is_equal_approx(p_to_point)) {
return;
}
Plane cut_plane;
@@ -761,17 +746,16 @@ void NavMap::clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys
cut_plane.d = cut_plane.normal.dot(from);
while (from_poly != p_to_poly) {
- int back_nav_edge = from_poly->back_navigation_edge;
- Vector3 a = from_poly->poly->points[back_nav_edge].pos;
- Vector3 b = from_poly->poly->points[(back_nav_edge + 1) % from_poly->poly->points.size()].pos;
+ Vector3 pathway_start = from_poly->back_navigation_edge_pathway_start;
+ Vector3 pathway_end = from_poly->back_navigation_edge_pathway_end;
- ERR_FAIL_COND(from_poly->prev_navigation_poly_id == -1);
- from_poly = &p_navigation_polys[from_poly->prev_navigation_poly_id];
+ ERR_FAIL_COND(from_poly->back_navigation_poly_id == -1);
+ from_poly = &p_navigation_polys[from_poly->back_navigation_poly_id];
- if (a.distance_to(b) > CMP_EPSILON) {
+ if (!pathway_start.is_equal_approx(pathway_end)) {
Vector3 inters;
- if (cut_plane.intersects_segment(a, b, &inters)) {
- if (inters.distance_to(p_to_point) > CMP_EPSILON && inters.distance_to(path[path.size() - 1]) > CMP_EPSILON) {
+ if (cut_plane.intersects_segment(pathway_start, pathway_end, &inters)) {
+ if (!inters.is_equal_approx(p_to_point) && !inters.is_equal_approx(path[path.size() - 1])) {
path.push_back(inters);
}
}
diff --git a/modules/gdnavigation/nav_map.h b/modules/navigation/nav_map.h
index 892755f3f9..8e013a72eb 100644
--- a/modules/gdnavigation/nav_map.h
+++ b/modules/navigation/nav_map.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,6 +34,7 @@
#include "nav_rid.h"
#include "core/math/math_defs.h"
+#include "core/templates/map.h"
#include "nav_utils.h"
#include <KdTree.h>
@@ -102,7 +103,7 @@ public:
gd::PointKey get_point_key(const Vector3 &p_pos) const;
- Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize) const;
+ Vector<Vector3> get_path(Vector3 p_origin, Vector3 p_destination, bool p_optimize, uint32_t p_layers = 1) const;
Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, const bool p_use_collision) const;
Vector3 get_closest_point(const Vector3 &p_point) const;
Vector3 get_closest_point_normal(const Vector3 &p_point) const;
diff --git a/modules/gdnavigation/nav_region.cpp b/modules/navigation/nav_region.cpp
index 51fba67cc3..81b15a49f5 100644
--- a/modules/gdnavigation/nav_region.cpp
+++ b/modules/navigation/nav_region.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,9 +39,20 @@
void NavRegion::set_map(NavMap *p_map) {
map = p_map;
polygons_dirty = true;
+ if (!map) {
+ connections.clear();
+ }
+}
+
+void NavRegion::set_layers(uint32_t p_layers) {
+ layers = p_layers;
}
-void NavRegion::set_transform(Transform p_transform) {
+uint32_t NavRegion::get_layers() const {
+ return layers;
+}
+
+void NavRegion::set_transform(Transform3D p_transform) {
transform = p_transform;
polygons_dirty = true;
}
@@ -51,6 +62,25 @@ void NavRegion::set_mesh(Ref<NavigationMesh> p_mesh) {
polygons_dirty = true;
}
+int NavRegion::get_connections_count() const {
+ if (!map) {
+ return 0;
+ }
+ return connections.size();
+}
+
+Vector3 NavRegion::get_connection_pathway_start(int p_connection_id) const {
+ ERR_FAIL_COND_V(!map, Vector3());
+ ERR_FAIL_INDEX_V(p_connection_id, connections.size(), Vector3());
+ return connections[p_connection_id].pathway_start;
+}
+
+Vector3 NavRegion::get_connection_pathway_end(int p_connection_id) const {
+ ERR_FAIL_COND_V(!map, Vector3());
+ ERR_FAIL_INDEX_V(p_connection_id, connections.size(), Vector3());
+ return connections[p_connection_id].pathway_end;
+}
+
bool NavRegion::sync() {
bool something_changed = polygons_dirty /* || something_dirty? */;
diff --git a/modules/gdnavigation/nav_region.h b/modules/navigation/nav_region.h
index 731855bfb5..f8b067e638 100644
--- a/modules/gdnavigation/nav_region.h
+++ b/modules/navigation/nav_region.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,10 +31,10 @@
#ifndef NAV_REGION_H
#define NAV_REGION_H
-#include "nav_rid.h"
+#include "scene/resources/navigation_mesh.h"
+#include "nav_rid.h"
#include "nav_utils.h"
-#include "scene/3d/navigation_3d.h"
#include <vector>
/**
@@ -46,8 +46,10 @@ class NavRegion;
class NavRegion : public NavRid {
NavMap *map = nullptr;
- Transform transform;
+ Transform3D transform;
Ref<NavigationMesh> mesh;
+ uint32_t layers = 1;
+ Vector<gd::Edge::Connection> connections;
bool polygons_dirty = true;
@@ -66,8 +68,11 @@ public:
return map;
}
- void set_transform(Transform transform);
- const Transform &get_transform() const {
+ void set_layers(uint32_t p_layers);
+ uint32_t get_layers() const;
+
+ void set_transform(Transform3D transform);
+ const Transform3D &get_transform() const {
return transform;
}
@@ -76,6 +81,13 @@ public:
return mesh;
}
+ Vector<gd::Edge::Connection> &get_connections() {
+ return connections;
+ }
+ int get_connections_count() const;
+ Vector3 get_connection_pathway_start(int p_connection_id) const;
+ Vector3 get_connection_pathway_end(int p_connection_id) const;
+
std::vector<gd::Polygon> const &get_polygons() const {
return polygons;
}
diff --git a/modules/gdnavigation/nav_rid.h b/modules/navigation/nav_rid.h
index c119ecc5e0..a0a60a3643 100644
--- a/modules/gdnavigation/nav_rid.h
+++ b/modules/navigation/nav_rid.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef NAV_RID_H
#define NAV_RID_H
-#include "core/rid.h"
+#include "core/templates/rid.h"
/**
@author AndreaCatania
diff --git a/modules/gdnavigation/nav_utils.h b/modules/navigation/nav_utils.h
index 40e54df553..35da391eea 100644
--- a/modules/gdnavigation/nav_utils.h
+++ b/modules/navigation/nav_utils.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -51,7 +51,7 @@ union PointKey {
int64_t z : 21;
};
- uint64_t key;
+ uint64_t key = 0;
bool operator<(const PointKey &p_key) const { return key < p_key.key; }
};
@@ -81,13 +81,14 @@ struct Edge {
/// This edge ID
int this_edge = -1;
- /// Other Polygon
- Polygon *other_polygon = nullptr;
-
- /// The other `Polygon` at this edge id has this `Polygon`.
- int other_edge = -1;
-
- Edge() {}
+ /// The gateway in the edge, as, in some case, the whole edge might not be navigable.
+ struct Connection {
+ Polygon *polygon = nullptr;
+ int edge = -1;
+ Vector3 pathway_start;
+ Vector3 pathway_end;
+ };
+ Vector<Connection> connections;
};
struct Polygon {
@@ -106,23 +107,17 @@ struct Polygon {
Vector3 center;
};
-struct Connection {
- Polygon *A = nullptr;
- int A_edge = -1;
- Polygon *B = nullptr;
- int B_edge = -1;
-
- Connection() {}
-};
-
struct NavigationPoly {
uint32_t self_id = 0;
/// This poly.
const Polygon *poly;
- /// The previous navigation poly (id in the `navigation_poly` array).
- int prev_navigation_poly_id = -1;
- /// The edge id in this `Poly` to reach the `prev_navigation_poly_id`.
- uint32_t back_navigation_edge = 0;
+
+ /// Those 4 variables are used to travel the path backwards.
+ int back_navigation_poly_id = -1;
+ uint32_t back_navigation_edge = UINT32_MAX;
+ Vector3 back_navigation_edge_pathway_start;
+ Vector3 back_navigation_edge_pathway_end;
+
/// The entry location of this poly.
Vector3 entry;
/// The distance to the destination.
@@ -140,14 +135,6 @@ struct NavigationPoly {
}
};
-struct FreeEdge {
- bool is_free;
- Polygon *poly;
- uint32_t edge_id;
- Vector3 edge_center;
- Vector3 edge_dir;
- float edge_len_squared;
-};
} // namespace gd
#endif // NAV_UTILS_H
diff --git a/modules/gdnavigation/navigation_mesh_editor_plugin.cpp b/modules/navigation/navigation_mesh_editor_plugin.cpp
index 648f4f7cdd..587e56f593 100644
--- a/modules/gdnavigation/navigation_mesh_editor_plugin.cpp
+++ b/modules/navigation/navigation_mesh_editor_plugin.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,8 +47,8 @@ void NavigationMeshEditor::_node_removed(Node *p_node) {
void NavigationMeshEditor::_notification(int p_option) {
if (p_option == NOTIFICATION_ENTER_TREE) {
- button_bake->set_icon(get_theme_icon("Bake", "EditorIcons"));
- button_reset->set_icon(get_theme_icon("Reload", "EditorIcons"));
+ button_bake->set_icon(get_theme_icon(SNAME("Bake"), SNAME("EditorIcons")));
+ button_reset->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
}
}
@@ -65,7 +65,7 @@ void NavigationMeshEditor::_bake_pressed() {
NavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());
NavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node);
- node->update_gizmo();
+ node->update_gizmos();
}
void NavigationMeshEditor::_clear_pressed() {
@@ -77,7 +77,7 @@ void NavigationMeshEditor::_clear_pressed() {
bake_info->set_text("");
if (node) {
- node->update_gizmo();
+ node->update_gizmos();
}
}
@@ -142,7 +142,7 @@ void NavigationMeshEditorPlugin::make_visible(bool p_visible) {
NavigationMeshEditorPlugin::NavigationMeshEditorPlugin(EditorNode *p_node) {
editor = p_node;
navigation_mesh_editor = memnew(NavigationMeshEditor);
- editor->get_viewport()->add_child(navigation_mesh_editor);
+ editor->get_main_control()->add_child(navigation_mesh_editor);
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, navigation_mesh_editor->bake_hbox);
navigation_mesh_editor->hide();
navigation_mesh_editor->bake_hbox->hide();
diff --git a/modules/gdnavigation/navigation_mesh_editor_plugin.h b/modules/navigation/navigation_mesh_editor_plugin.h
index f09182fff4..c39269865b 100644
--- a/modules/gdnavigation/navigation_mesh_editor_plugin.h
+++ b/modules/navigation/navigation_mesh_editor_plugin.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdnavigation/navigation_mesh_generator.cpp b/modules/navigation/navigation_mesh_generator.cpp
index 5329600e39..05e040b518 100644
--- a/modules/gdnavigation/navigation_mesh_generator.cpp
+++ b/modules/navigation/navigation_mesh_generator.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#include "navigation_mesh_generator.h"
-#include "core/math/quick_hull.h"
+#include "core/math/convex_hull.h"
#include "core/os/thread.h"
#include "scene/3d/collision_shape_3d.h"
#include "scene/3d/mesh_instance_3d.h"
@@ -45,14 +45,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"
-#include "modules/modules_enabled.gen.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#endif
+#include "modules/modules_enabled.gen.h" // For csg, gridmap.
+
#ifdef MODULE_CSG_ENABLED
#include "modules/csg/csg_shape.h"
#endif
@@ -68,7 +69,7 @@ void NavigationMeshGenerator::_add_vertex(const Vector3 &p_vec3, Vector<float> &
p_verticies.push_back(p_vec3.z);
}
-void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform &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_verticies, Vector<int> &p_indices) {
int current_vertex_count;
for (int i = 0; i < p_mesh->get_surface_count(); i++) {
@@ -123,7 +124,7 @@ void NavigationMeshGenerator::_add_mesh(const Ref<Mesh> &p_mesh, const Transform
}
}
-void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, const Transform &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_verticies, Vector<int> &p_indices) {
int face_count = p_faces.size() / 3;
int current_vertex_count = p_verticies.size() / 3;
@@ -138,7 +139,7 @@ void NavigationMeshGenerator::_add_faces(const PackedVector3Array &p_faces, cons
}
}
-void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int 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_verticies, 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();
@@ -151,7 +152,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform,
if (Object::cast_to<CSGShape3D>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_node);
Array meshes = csg_shape->get_meshes();
- if (!meshes.empty()) {
+ 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);
@@ -169,32 +170,32 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform,
if (Object::cast_to<CollisionShape3D>(child)) {
CollisionShape3D *col_shape = Object::cast_to<CollisionShape3D>(child);
- Transform transform = p_accumulated_transform * static_body->get_transform() * col_shape->get_transform();
+ Transform3D transform = p_accumulated_transform * static_body->get_transform() * col_shape->get_transform();
Ref<Mesh> mesh;
Ref<Shape3D> s = col_shape->get_shape();
BoxShape3D *box = Object::cast_to<BoxShape3D>(*s);
if (box) {
- Ref<CubeMesh> cube_mesh;
- cube_mesh.instance();
- cube_mesh->set_size(box->get_extents() * 2.0);
- mesh = cube_mesh;
+ Ref<BoxMesh> box_mesh;
+ box_mesh.instantiate();
+ box_mesh->set_size(box->get_size());
+ mesh = box_mesh;
}
CapsuleShape3D *capsule = Object::cast_to<CapsuleShape3D>(*s);
if (capsule) {
Ref<CapsuleMesh> capsule_mesh;
- capsule_mesh.instance();
+ capsule_mesh.instantiate();
capsule_mesh->set_radius(capsule->get_radius());
- capsule_mesh->set_mid_height(capsule->get_height() / 2.0);
+ capsule_mesh->set_height(capsule->get_height());
mesh = capsule_mesh;
}
CylinderShape3D *cylinder = Object::cast_to<CylinderShape3D>(*s);
if (cylinder) {
Ref<CylinderMesh> cylinder_mesh;
- cylinder_mesh.instance();
+ cylinder_mesh.instantiate();
cylinder_mesh->set_height(cylinder->get_height());
cylinder_mesh->set_bottom_radius(cylinder->get_radius());
cylinder_mesh->set_top_radius(cylinder->get_radius());
@@ -204,7 +205,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform,
SphereShape3D *sphere = Object::cast_to<SphereShape3D>(*s);
if (sphere) {
Ref<SphereMesh> sphere_mesh;
- sphere_mesh.instance();
+ sphere_mesh.instantiate();
sphere_mesh->set_radius(sphere->get_radius());
sphere_mesh->set_height(sphere->get_radius() * 2.0);
mesh = sphere_mesh;
@@ -220,7 +221,7 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform,
Vector<Vector3> varr = Variant(convex_polygon->get_points());
Geometry3D::MeshData md;
- Error err = QuickHull::build(varr, md);
+ Error err = ConvexHullComputer::convex_hull(varr, md);
if (err == OK) {
PackedVector3Array faces;
@@ -251,11 +252,11 @@ void NavigationMeshGenerator::_parse_geometry(Transform p_accumulated_transform,
if (Object::cast_to<GridMap>(p_node) && p_generate_from != NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS) {
GridMap *gridmap_instance = Object::cast_to<GridMap>(p_node);
Array meshes = gridmap_instance->get_meshes();
- Transform xform = gridmap_instance->get_transform();
+ Transform3D xform = gridmap_instance->get_transform();
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 * meshes[i], p_verticies, p_indices);
+ _add_mesh(mesh, p_accumulated_transform * xform * (Transform3D)meshes[i], p_verticies, p_indices);
}
}
}
@@ -489,7 +490,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);
@@ -513,12 +514,12 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
p_node->get_tree()->get_nodes_in_group(p_nav_mesh->get_source_group_name(), &parse_nodes);
}
- Transform navmesh_xform = Object::cast_to<Node3D>(p_node)->get_transform().affine_inverse();
- for (const List<Node *>::Element *E = parse_nodes.front(); E; E = E->next()) {
- int geometry_type = p_nav_mesh->get_parsed_geometry_type();
+ Transform3D navmesh_xform = Object::cast_to<Node3D>(p_node)->get_transform().affine_inverse();
+ for (Node *E : parse_nodes) {
+ NavigationMesh::ParsedGeometryType geometry_type = p_nav_mesh->get_parsed_geometry_type();
uint32_t collision_mask = p_nav_mesh->get_collision_mask();
bool recurse_children = p_nav_mesh->get_source_geometry_mode() != NavigationMesh::SOURCE_GEOMETRY_GROUPS_EXPLICIT;
- _parse_geometry(navmesh_xform, E->get(), vertices, indices, geometry_type, collision_mask, recurse_children);
+ _parse_geometry(navmesh_xform, E, vertices, indices, geometry_type, collision_mask, recurse_children);
}
if (vertices.size() > 0 && indices.size() > 0) {
diff --git a/modules/gdnavigation/navigation_mesh_generator.h b/modules/navigation/navigation_mesh_generator.h
index c5f7b2ab81..78f1329e3f 100644
--- a/modules/gdnavigation/navigation_mesh_generator.h
+++ b/modules/navigation/navigation_mesh_generator.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -50,9 +50,9 @@ 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 Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
- static void _add_faces(const PackedVector3Array &p_faces, const Transform &p_xform, Vector<float> &p_verticies, Vector<int> &p_indices);
- static void _parse_geometry(Transform p_accumulated_transform, Node *p_node, Vector<float> &p_verticies, Vector<int> &p_indices, int p_generate_from, uint32_t p_collision_mask, bool p_recurse_children);
+ 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 _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/gdnavigation/register_types.cpp b/modules/navigation/register_types.cpp
index 088b26bf17..97c01d42ab 100644
--- a/modules/gdnavigation/register_types.cpp
+++ b/modules/navigation/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,10 +30,11 @@
#include "register_types.h"
-#include "core/engine.h"
-#include "gd_navigation_server.h"
+#include "core/config/engine.h"
#include "servers/navigation_server_3d.h"
+#include "godot_navigation_server.h"
+
#ifndef _3D_DISABLED
#include "navigation_mesh_generator.h"
#endif
@@ -42,38 +43,29 @@
#include "navigation_mesh_editor_plugin.h"
#endif
-/**
- @author AndreaCatania
-*/
-
#ifndef _3D_DISABLED
NavigationMeshGenerator *_nav_mesh_generator = nullptr;
#endif
NavigationServer3D *new_server() {
- return memnew(GdNavigationServer);
+ return memnew(GodotNavigationServer);
}
-void register_gdnavigation_types() {
+void register_navigation_types() {
NavigationServer3DManager::set_default_server(new_server);
#ifndef _3D_DISABLED
_nav_mesh_generator = memnew(NavigationMeshGenerator);
- ClassDB::register_class<NavigationMeshGenerator>();
+ GDREGISTER_CLASS(NavigationMeshGenerator);
Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationMeshGenerator", NavigationMeshGenerator::get_singleton()));
#endif
#ifdef TOOLS_ENABLED
EditorPlugins::add_by_type<NavigationMeshEditorPlugin>();
-
- ClassDB::APIType prev_api = ClassDB::get_current_api();
- ClassDB::set_current_api(ClassDB::API_EDITOR);
-
- ClassDB::set_current_api(prev_api);
#endif
}
-void unregister_gdnavigation_types() {
+void unregister_navigation_types() {
#ifndef _3D_DISABLED
if (_nav_mesh_generator) {
memdelete(_nav_mesh_generator);
diff --git a/modules/navigation/register_types.h b/modules/navigation/register_types.h
new file mode 100644
index 0000000000..4737c818eb
--- /dev/null
+++ b/modules/navigation/register_types.h
@@ -0,0 +1,37 @@
+/*************************************************************************/
+/* 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 NAVIGATION_REGISTER_TYPES_H
+#define NAVIGATION_REGISTER_TYPES_H
+
+void register_navigation_types();
+void unregister_navigation_types();
+
+#endif // NAVIGATION_REGISTER_TYPES_H
diff --git a/modules/gdnavigation/rvo_agent.cpp b/modules/navigation/rvo_agent.cpp
index 1e1bdbd07d..21e43d08c1 100644
--- a/modules/gdnavigation/rvo_agent.cpp
+++ b/modules/navigation/rvo_agent.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/gdnavigation/rvo_agent.h b/modules/navigation/rvo_agent.h
index f5c579ba84..369cb1f9a3 100644
--- a/modules/gdnavigation/rvo_agent.h
+++ b/modules/navigation/rvo_agent.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,9 @@
#ifndef RVO_AGENT_H
#define RVO_AGENT_H
-#include "core/object.h"
+#include "core/object/class_db.h"
#include "nav_rid.h"
+
#include <Agent.h>
/**
@@ -52,7 +53,7 @@ class RvoAgent : public NavRid {
NavMap *map = nullptr;
RVO::Agent agent;
AvoidanceComputedCallback callback;
- uint32_t map_update_id;
+ uint32_t map_update_id = 0;
public:
RvoAgent();
diff --git a/modules/ogg/SCsub b/modules/ogg/SCsub
index e768fb4ae8..f15525648f 100644
--- a/modules/ogg/SCsub
+++ b/modules/ogg/SCsub
@@ -3,12 +3,12 @@
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
+
+thirdparty_obj = []
+
if env["builtin_libogg"]:
thirdparty_dir = "#thirdparty/libogg/"
thirdparty_sources = [
@@ -21,7 +21,16 @@ if env["builtin_libogg"]:
env_thirdparty = env_ogg.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_ogg.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_ogg.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/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 73c691397c..3448e7063a 100644
--- a/modules/ogg/register_types.cpp
+++ b/modules/ogg/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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/ogg/register_types.h b/modules/ogg/register_types.h
index 849d27bb06..49d5ed9c80 100644
--- a/modules/ogg/register_types.h
+++ b/modules/ogg/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/opensimplex/SCsub b/modules/opensimplex/SCsub
index 52d8b145ef..86d77c3dfb 100644
--- a/modules/opensimplex/SCsub
+++ b/modules/opensimplex/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_opensimplex = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
thirdparty_dir = "#thirdparty/misc/"
thirdparty_sources = [
"open-simplex-noise.c",
@@ -16,7 +19,15 @@ env_opensimplex.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_opensimplex.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_opensimplex.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Godot's own source files
-env_opensimplex.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/opensimplex/doc_classes/NoiseTexture.xml b/modules/opensimplex/doc_classes/NoiseTexture.xml
index c06f3096de..16fea228b1 100644
--- a/modules/opensimplex/doc_classes/NoiseTexture.xml
+++ b/modules/opensimplex/doc_classes/NoiseTexture.xml
@@ -5,20 +5,20 @@
</brief_description>
<description>
Uses an [OpenSimplexNoise] to fill the texture data. You can specify the texture size but keep in mind that larger textures will take longer to generate and seamless noise only works with square sized textures.
- NoiseTexture can also generate normalmap textures.
- The class uses [Thread]s to generate the texture data internally, so [method Texture2D.get_data] 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 data:
+ 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 image = texture.get_data()
+ 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_normalmap" type="bool" setter="set_as_normalmap" getter="is_normalmap" default="false">
+ <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.
</member>
<member name="bump_strength" type="float" setter="set_bump_strength" getter="get_bump_strength" default="8.0">
@@ -30,13 +30,15 @@
<member name="noise" type="OpenSimplexNoise" setter="set_noise" getter="get_noise">
The [OpenSimplexNoise] instance used to generate the noise.
</member>
+ <member name="noise_offset" type="Vector2" setter="set_noise_offset" getter="get_noise_offset" default="Vector2(0, 0)">
+ An offset used to specify the noise space coordinate of the top left corner of the generated noise. This value is ignored if [member seamless] is enabled.
+ </member>
<member name="seamless" type="bool" setter="set_seamless" getter="get_seamless" default="false">
Whether the texture can be tiled without visible seams or not. Seamless textures take longer to generate.
+ [b]Note:[/b] Seamless noise has a lower contrast compared to non-seamless noise. This is due to the way noise uses higher dimensions for generating seamless noise.
</member>
<member name="width" type="int" setter="set_width" getter="get_width" default="512">
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 d89828037f..604b07b645 100644
--- a/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
+++ b/modules/opensimplex/doc_classes/OpenSimplexNoise.xml
@@ -24,91 +24,70 @@
<tutorials>
</tutorials>
<methods>
- <method name="get_image">
- <return type="Image">
- </return>
- <argument index="0" name="width" type="int">
- </argument>
- <argument index="1" name="height" type="int">
- </argument>
+ <method name="get_image" qualifiers="const">
+ <return type="Image" />
+ <argument index="0" name="width" type="int" />
+ <argument index="1" name="height" type="int" />
+ <argument index="2" name="noise_offset" type="Vector2" default="Vector2(0, 0)" />
<description>
- Generate a noise image with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters.
+ Generate a noise image in [constant Image.FORMAT_L8] format with the requested [code]width[/code] and [code]height[/code], based on the current noise parameters. If [code]noise_offset[/code] is specified, then the offset value is used as the coordinates of the top-left corner of the generated noise.
</description>
</method>
- <method name="get_noise_1d">
- <return type="float">
- </return>
- <argument index="0" name="x" type="float">
- </argument>
+ <method name="get_noise_1d" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="x" type="float" />
<description>
Returns the 1D noise value [code][-1,1][/code] at the given x-coordinate.
[b]Note:[/b] This method actually returns the 2D noise value [code][-1,1][/code] with fixed y-coordinate value 0.0.
</description>
</method>
- <method name="get_noise_2d">
- <return type="float">
- </return>
- <argument index="0" name="x" type="float">
- </argument>
- <argument index="1" name="y" type="float">
- </argument>
+ <method name="get_noise_2d" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="x" type="float" />
+ <argument index="1" name="y" type="float" />
<description>
Returns the 2D noise value [code][-1,1][/code] at the given position.
</description>
</method>
- <method name="get_noise_2dv">
- <return type="float">
- </return>
- <argument index="0" name="pos" type="Vector2">
- </argument>
+ <method name="get_noise_2dv" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="pos" type="Vector2" />
<description>
Returns the 2D noise value [code][-1,1][/code] at the given position.
</description>
</method>
- <method name="get_noise_3d">
- <return type="float">
- </return>
- <argument index="0" name="x" type="float">
- </argument>
- <argument index="1" name="y" type="float">
- </argument>
- <argument index="2" name="z" type="float">
- </argument>
+ <method name="get_noise_3d" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="x" type="float" />
+ <argument index="1" name="y" type="float" />
+ <argument index="2" name="z" type="float" />
<description>
Returns the 3D noise value [code][-1,1][/code] at the given position.
</description>
</method>
- <method name="get_noise_3dv">
- <return type="float">
- </return>
- <argument index="0" name="pos" type="Vector3">
- </argument>
+ <method name="get_noise_3dv" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="pos" type="Vector3" />
<description>
Returns the 3D noise value [code][-1,1][/code] at the given position.
</description>
</method>
- <method name="get_noise_4d">
- <return type="float">
- </return>
- <argument index="0" name="x" type="float">
- </argument>
- <argument index="1" name="y" type="float">
- </argument>
- <argument index="2" name="z" type="float">
- </argument>
- <argument index="3" name="w" type="float">
- </argument>
+ <method name="get_noise_4d" qualifiers="const">
+ <return type="float" />
+ <argument index="0" name="x" type="float" />
+ <argument index="1" name="y" type="float" />
+ <argument index="2" name="z" type="float" />
+ <argument index="3" name="w" type="float" />
<description>
Returns the 4D noise value [code][-1,1][/code] at the given position.
</description>
</method>
- <method name="get_seamless_image">
- <return type="Image">
- </return>
- <argument index="0" name="size" type="int">
- </argument>
+ <method name="get_seamless_image" qualifiers="const">
+ <return type="Image" />
+ <argument index="0" name="size" type="int" />
<description>
- Generate a tileable noise image, based on the current noise parameters. Generated seamless images are always square ([code]size[/code] × [code]size[/code]).
+ Generate a tileable noise image in [constant Image.FORMAT_L8] format, based on the current noise parameters. Generated seamless images are always square ([code]size[/code] × [code]size[/code]).
+ [b]Note:[/b] Seamless noise has a lower contrast compared to non-seamless noise. This is due to the way noise uses higher dimensions for generating seamless noise.
</description>
</method>
</methods>
@@ -130,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/icons/NoiseTexture.svg b/modules/opensimplex/icons/NoiseTexture.svg
index 5908c2b2d4..479684cde2 100644
--- a/modules/opensimplex/icons/NoiseTexture.svg
+++ b/modules/opensimplex/icons/NoiseTexture.svg
@@ -1,3 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<path d="m2 1c-0.55228 0-1 0.44772-1 1v12c0 0.55228 0.44772 1 1 1h12c0.55228 0 1-0.44772 1-1v-12c0-0.55228-0.44772-1-1-1zm1 2h10v8h-10zm3 1v2h2v-2zm2 2v2h2v2h2v-6h-2v2zm0 2h-2v-2h-2v4h4z" fill="#e0e0e0" fill-opacity=".99608"/>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 1c-.55228 0-1 .44772-1 1v12c0 .55228.44772 1 1 1h12c.55228 0 1-.44772 1-1v-12c0-.55228-.44772-1-1-1zm1 2h10v8h-10zm3 1v2h2v-2zm2 2v2h2v2h2v-6h-2v2zm0 2h-2v-2h-2v4h4z" fill="#e0e0e0" fill-opacity=".99608"/></svg>
diff --git a/modules/opensimplex/noise_texture.cpp b/modules/opensimplex/noise_texture.cpp
index 1181e69cd3..e36dcfcea5 100644
--- a/modules/opensimplex/noise_texture.cpp
+++ b/modules/opensimplex/noise_texture.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,16 +33,6 @@
#include "core/core_string_names.h"
NoiseTexture::NoiseTexture() {
- update_queued = false;
- noise_thread = nullptr;
- regen_queued = false;
- first_time = true;
-
- size = Vector2i(512, 512);
- seamless = false;
- as_normalmap = false;
- bump_strength = 8.0;
-
noise = Ref<OpenSimplexNoise>();
_queue_update();
@@ -52,10 +42,7 @@ NoiseTexture::~NoiseTexture() {
if (texture.is_valid()) {
RS::get_singleton()->free(texture);
}
- if (noise_thread) {
- Thread::wait_to_finish(noise_thread);
- memdelete(noise_thread);
- }
+ noise_thread.wait_to_finish();
}
void NoiseTexture::_bind_methods() {
@@ -65,11 +52,14 @@ void NoiseTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_noise", "noise"), &NoiseTexture::set_noise);
ClassDB::bind_method(D_METHOD("get_noise"), &NoiseTexture::get_noise);
+ ClassDB::bind_method(D_METHOD("set_noise_offset", "noise_offset"), &NoiseTexture::set_noise_offset);
+ ClassDB::bind_method(D_METHOD("get_noise_offset"), &NoiseTexture::get_noise_offset);
+
ClassDB::bind_method(D_METHOD("set_seamless", "seamless"), &NoiseTexture::set_seamless);
ClassDB::bind_method(D_METHOD("get_seamless"), &NoiseTexture::get_seamless);
- ClassDB::bind_method(D_METHOD("set_as_normalmap", "as_normalmap"), &NoiseTexture::set_as_normalmap);
- ClassDB::bind_method(D_METHOD("is_normalmap"), &NoiseTexture::is_normalmap);
+ ClassDB::bind_method(D_METHOD("set_as_normal_map", "as_normal_map"), &NoiseTexture::set_as_normal_map);
+ ClassDB::bind_method(D_METHOD("is_normal_map"), &NoiseTexture::is_normal_map);
ClassDB::bind_method(D_METHOD("set_bump_strength", "bump_strength"), &NoiseTexture::set_bump_strength);
ClassDB::bind_method(D_METHOD("get_bump_strength"), &NoiseTexture::get_bump_strength);
@@ -81,22 +71,23 @@ void NoiseTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "width", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_width", "get_width");
ADD_PROPERTY(PropertyInfo(Variant::INT, "height", PROPERTY_HINT_RANGE, "1,2048,1,or_greater"), "set_height", "get_height");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "seamless"), "set_seamless", "get_seamless");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normalmap"), "set_as_normalmap", "is_normalmap");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "as_normal_map"), "set_as_normal_map", "is_normal_map");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bump_strength", PROPERTY_HINT_RANGE, "0,32,0.1,or_greater"), "set_bump_strength", "get_bump_strength");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "noise", PROPERTY_HINT_RESOURCE_TYPE, "OpenSimplexNoise"), "set_noise", "get_noise");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "noise_offset"), "set_noise_offset", "get_noise_offset");
}
void NoiseTexture::_validate_property(PropertyInfo &property) const {
if (property.name == "bump_strength") {
- if (!as_normalmap) {
- property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
+ if (!as_normal_map) {
+ property.usage = PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL;
}
}
}
-void NoiseTexture::_set_texture_data(const Ref<Image> &p_image) {
- data = p_image;
- if (data.is_valid()) {
+void NoiseTexture::_set_texture_image(const Ref<Image> &p_image) {
+ image = p_image;
+ if (image.is_valid()) {
if (texture.is_valid()) {
RID new_texture = RS::get_singleton()->texture_2d_create(p_image);
RS::get_singleton()->texture_replace(texture, new_texture);
@@ -108,19 +99,17 @@ void NoiseTexture::_set_texture_data(const Ref<Image> &p_image) {
}
void NoiseTexture::_thread_done(const Ref<Image> &p_image) {
- _set_texture_data(p_image);
- Thread::wait_to_finish(noise_thread);
- memdelete(noise_thread);
- noise_thread = nullptr;
+ _set_texture_image(p_image);
+ noise_thread.wait_to_finish();
if (regen_queued) {
- noise_thread = Thread::create(_thread_function, this);
+ noise_thread.start(_thread_function, this);
regen_queued = false;
}
}
void NoiseTexture::_thread_function(void *p_ud) {
NoiseTexture *tex = (NoiseTexture *)p_ud;
- tex->call_deferred("_thread_done", tex->_generate_texture());
+ tex->call_deferred(SNAME("_thread_done"), tex->_generate_texture());
}
void NoiseTexture::_queue_update() {
@@ -129,7 +118,7 @@ void NoiseTexture::_queue_update() {
}
update_queued = true;
- call_deferred("_update_texture");
+ call_deferred(SNAME("_update_texture"));
}
Ref<Image> NoiseTexture::_generate_texture() {
@@ -145,11 +134,11 @@ Ref<Image> NoiseTexture::_generate_texture() {
if (seamless) {
image = ref_noise->get_seamless_image(size.x);
} else {
- image = ref_noise->get_image(size.x, size.y);
+ image = ref_noise->get_image(size.x, size.y, noise_offset);
}
- if (as_normalmap) {
- image->bumpmap_to_normalmap(bump_strength);
+ if (as_normal_map) {
+ image->bump_map_to_normal_map(bump_strength);
}
return image;
@@ -165,8 +154,8 @@ void NoiseTexture::_update_texture() {
use_thread = false;
#endif
if (use_thread) {
- if (!noise_thread) {
- noise_thread = Thread::create(_thread_function, this);
+ if (!noise_thread.is_started()) {
+ noise_thread.start(_thread_function, this);
regen_queued = false;
} else {
regen_queued = true;
@@ -174,7 +163,7 @@ void NoiseTexture::_update_texture() {
} else {
Ref<Image> image = _generate_texture();
- _set_texture_data(image);
+ _set_texture_image(image);
}
update_queued = false;
}
@@ -198,6 +187,7 @@ Ref<OpenSimplexNoise> NoiseTexture::get_noise() {
}
void NoiseTexture::set_width(int p_width) {
+ ERR_FAIL_COND(p_width <= 0);
if (p_width == size.x) {
return;
}
@@ -206,6 +196,7 @@ void NoiseTexture::set_width(int p_width) {
}
void NoiseTexture::set_height(int p_height) {
+ ERR_FAIL_COND(p_height <= 0);
if (p_height == size.y) {
return;
}
@@ -213,6 +204,14 @@ void NoiseTexture::set_height(int p_height) {
_queue_update();
}
+void NoiseTexture::set_noise_offset(Vector2 p_noise_offset) {
+ if (noise_offset == p_noise_offset) {
+ return;
+ }
+ noise_offset = p_noise_offset;
+ _queue_update();
+}
+
void NoiseTexture::set_seamless(bool p_seamless) {
if (p_seamless == seamless) {
return;
@@ -225,17 +224,17 @@ bool NoiseTexture::get_seamless() {
return seamless;
}
-void NoiseTexture::set_as_normalmap(bool p_as_normalmap) {
- if (p_as_normalmap == as_normalmap) {
+void NoiseTexture::set_as_normal_map(bool p_as_normal_map) {
+ if (p_as_normal_map == as_normal_map) {
return;
}
- as_normalmap = p_as_normalmap;
+ as_normal_map = p_as_normal_map;
_queue_update();
- _change_notify();
+ notify_property_list_changed();
}
-bool NoiseTexture::is_normalmap() {
- return as_normalmap;
+bool NoiseTexture::is_normal_map() {
+ return as_normal_map;
}
void NoiseTexture::set_bump_strength(float p_bump_strength) {
@@ -243,7 +242,7 @@ void NoiseTexture::set_bump_strength(float p_bump_strength) {
return;
}
bump_strength = p_bump_strength;
- if (as_normalmap) {
+ if (as_normal_map) {
_queue_update();
}
}
@@ -260,6 +259,10 @@ int NoiseTexture::get_height() const {
return size.y;
}
+Vector2 NoiseTexture::get_noise_offset() const {
+ return noise_offset;
+}
+
RID NoiseTexture::get_rid() const {
if (!texture.is_valid()) {
texture = RS::get_singleton()->texture_2d_placeholder_create();
@@ -268,6 +271,6 @@ RID NoiseTexture::get_rid() const {
return texture;
}
-Ref<Image> NoiseTexture::get_data() const {
- return data;
+Ref<Image> NoiseTexture::get_image() const {
+ return image;
}
diff --git a/modules/opensimplex/noise_texture.h b/modules/opensimplex/noise_texture.h
index 7357e54e35..a0b2a86c41 100644
--- a/modules/opensimplex/noise_texture.h
+++ b/modules/opensimplex/noise_texture.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,32 +33,30 @@
#include "open_simplex_noise.h"
-#include "core/image.h"
-#include "core/reference.h"
-#include "editor/editor_node.h"
-#include "editor/editor_plugin.h"
-#include "editor/property_editor.h"
+#include "core/io/image.h"
+#include "core/object/ref_counted.h"
class NoiseTexture : public Texture2D {
GDCLASS(NoiseTexture, Texture2D);
private:
- Ref<Image> data;
+ Ref<Image> image;
- Thread *noise_thread;
+ Thread noise_thread;
- bool first_time;
- bool update_queued;
- bool regen_queued;
+ bool first_time = true;
+ bool update_queued = false;
+ bool regen_queued = false;
mutable RID texture;
- uint32_t flags;
+ uint32_t flags = 0;
Ref<OpenSimplexNoise> noise;
- Vector2i size;
- bool seamless;
- bool as_normalmap;
- float bump_strength;
+ Vector2i size = Vector2i(512, 512);
+ Vector2 noise_offset;
+ bool seamless = false;
+ bool as_normal_map = false;
+ float bump_strength = 8.0;
void _thread_done(const Ref<Image> &p_image);
static void _thread_function(void *p_ud);
@@ -66,7 +64,7 @@ private:
void _queue_update();
Ref<Image> _generate_texture();
void _update_texture();
- void _set_texture_data(const Ref<Image> &p_image);
+ void _set_texture_image(const Ref<Image> &p_image);
protected:
static void _bind_methods();
@@ -79,11 +77,14 @@ public:
void set_width(int p_width);
void set_height(int p_height);
+ void set_noise_offset(Vector2 p_noise_offset);
+ Vector2 get_noise_offset() const;
+
void set_seamless(bool p_seamless);
bool get_seamless();
- void set_as_normalmap(bool p_as_normalmap);
- bool is_normalmap();
+ void set_as_normal_map(bool p_as_normal_map);
+ bool is_normal_map();
void set_bump_strength(float p_bump_strength);
float get_bump_strength();
@@ -94,7 +95,7 @@ public:
virtual RID get_rid() const override;
virtual bool has_alpha() const override { return false; }
- virtual Ref<Image> get_data() const override;
+ virtual Ref<Image> get_image() const override;
NoiseTexture();
virtual ~NoiseTexture();
diff --git a/modules/opensimplex/open_simplex_noise.cpp b/modules/opensimplex/open_simplex_noise.cpp
index b08219d258..f0a8867284 100644
--- a/modules/opensimplex/open_simplex_noise.cpp
+++ b/modules/opensimplex/open_simplex_noise.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,12 +33,6 @@
#include "core/core_string_names.h"
OpenSimplexNoise::OpenSimplexNoise() {
- seed = 0;
- persistence = 0.5;
- octaves = 3;
- period = 64;
- lacunarity = 2.0;
-
_init_seeds();
}
@@ -63,7 +57,7 @@ void OpenSimplexNoise::set_seed(int p_seed) {
emit_changed();
}
-int OpenSimplexNoise::get_seed() {
+int OpenSimplexNoise::get_seed() const {
return seed;
}
@@ -102,31 +96,27 @@ void OpenSimplexNoise::set_lacunarity(float p_lacunarity) {
emit_changed();
}
-Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) {
+Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height, const Vector2 &p_noise_offset) const {
Vector<uint8_t> data;
- data.resize(p_width * p_height * 4);
+ data.resize(p_width * p_height);
uint8_t *wd8 = data.ptrw();
for (int i = 0; i < p_height; i++) {
for (int j = 0; j < p_width; j++) {
- float v = get_noise_2d(j, i);
+ float v = get_noise_2d(float(j) + p_noise_offset.x, float(i) + p_noise_offset.y);
v = v * 0.5 + 0.5; // Normalize [0..1]
- uint8_t value = uint8_t(CLAMP(v * 255.0, 0, 255));
- wd8[(i * p_width + j) * 4 + 0] = value;
- wd8[(i * p_width + j) * 4 + 1] = value;
- wd8[(i * p_width + j) * 4 + 2] = value;
- wd8[(i * p_width + j) * 4 + 3] = 255;
+ wd8[(i * p_width + j)] = uint8_t(CLAMP(v * 255.0, 0, 255));
}
}
- Ref<Image> image = memnew(Image(p_width, p_height, false, Image::FORMAT_RGBA8, data));
+ Ref<Image> image = memnew(Image(p_width, p_height, false, Image::FORMAT_L8, data));
return image;
}
-Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) {
+Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) const {
Vector<uint8_t> data;
- data.resize(p_size * p_size * 4);
+ data.resize(p_size * p_size);
uint8_t *wd8 = data.ptrw();
@@ -135,10 +125,10 @@ Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) {
float ii = (float)i / (float)p_size;
float jj = (float)j / (float)p_size;
- ii *= 2.0 * Math_PI;
- jj *= 2.0 * Math_PI;
+ ii *= Math_TAU;
+ jj *= Math_TAU;
- float radius = p_size / (2.0 * Math_PI);
+ float radius = p_size / Math_TAU;
float x = radius * Math::sin(jj);
float y = radius * Math::cos(jj);
@@ -147,15 +137,11 @@ Ref<Image> OpenSimplexNoise::get_seamless_image(int p_size) {
float v = get_noise_4d(x, y, z, w);
v = v * 0.5 + 0.5; // Normalize [0..1]
- uint8_t value = uint8_t(CLAMP(v * 255.0, 0, 255));
- wd8[(i * p_size + j) * 4 + 0] = value;
- wd8[(i * p_size + j) * 4 + 1] = value;
- wd8[(i * p_size + j) * 4 + 2] = value;
- wd8[(i * p_size + j) * 4 + 3] = 255;
+ wd8[(i * p_size + j)] = uint8_t(CLAMP(v * 255.0, 0, 255));
}
}
- Ref<Image> image = memnew(Image(p_size, p_size, false, Image::FORMAT_RGBA8, data));
+ Ref<Image> image = memnew(Image(p_size, p_size, false, Image::FORMAT_L8, data));
return image;
}
@@ -175,7 +161,7 @@ void OpenSimplexNoise::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_lacunarity", "lacunarity"), &OpenSimplexNoise::set_lacunarity);
ClassDB::bind_method(D_METHOD("get_lacunarity"), &OpenSimplexNoise::get_lacunarity);
- ClassDB::bind_method(D_METHOD("get_image", "width", "height"), &OpenSimplexNoise::get_image);
+ ClassDB::bind_method(D_METHOD("get_image", "width", "height", "noise_offset"), &OpenSimplexNoise::get_image, DEFVAL(Vector2()));
ClassDB::bind_method(D_METHOD("get_seamless_image", "size"), &OpenSimplexNoise::get_seamless_image);
ClassDB::bind_method(D_METHOD("get_noise_1d", "x"), &OpenSimplexNoise::get_noise_1d);
@@ -193,11 +179,11 @@ void OpenSimplexNoise::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lacunarity", PROPERTY_HINT_RANGE, "0.1,4.0,0.01"), "set_lacunarity", "get_lacunarity");
}
-float OpenSimplexNoise::get_noise_1d(float x) {
+float OpenSimplexNoise::get_noise_1d(float x) const {
return get_noise_2d(x, 1.0);
}
-float OpenSimplexNoise::get_noise_2d(float x, float y) {
+float OpenSimplexNoise::get_noise_2d(float x, float y) const {
x /= period;
y /= period;
@@ -217,7 +203,7 @@ float OpenSimplexNoise::get_noise_2d(float x, float y) {
return sum / max;
}
-float OpenSimplexNoise::get_noise_3d(float x, float y, float z) {
+float OpenSimplexNoise::get_noise_3d(float x, float y, float z) const {
x /= period;
y /= period;
z /= period;
@@ -239,7 +225,7 @@ float OpenSimplexNoise::get_noise_3d(float x, float y, float z) {
return sum / max;
}
-float OpenSimplexNoise::get_noise_4d(float x, float y, float z, float w) {
+float OpenSimplexNoise::get_noise_4d(float x, float y, float z, float w) const {
x /= period;
y /= period;
z /= period;
diff --git a/modules/opensimplex/open_simplex_noise.h b/modules/opensimplex/open_simplex_noise.h
index dce62bc1f9..dcf922a8bf 100644
--- a/modules/opensimplex/open_simplex_noise.h
+++ b/modules/opensimplex/open_simplex_noise.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,28 +31,28 @@
#ifndef OPEN_SIMPLEX_NOISE_H
#define OPEN_SIMPLEX_NOISE_H
-#include "core/image.h"
-#include "core/reference.h"
+#include "core/io/image.h"
+#include "core/object/ref_counted.h"
#include "scene/resources/texture.h"
#include "thirdparty/misc/open-simplex-noise.h"
-// The maximum number of octaves allowed. Note that these are statically allocated.
-// Higher values become exponentially slower, so this shouldn't be set too high
-// to avoid freezing the editor for long periods of time.
-#define MAX_OCTAVES 9
-
class OpenSimplexNoise : public Resource {
GDCLASS(OpenSimplexNoise, Resource);
OBJ_SAVE_TYPE(OpenSimplexNoise);
+ // The maximum number of octaves allowed. Note that these are statically allocated.
+ // Higher values become exponentially slower, so this shouldn't be set too high
+ // to avoid freezing the editor for long periods of time.
+ static const int MAX_OCTAVES = 9;
+
osn_context contexts[MAX_OCTAVES];
- int seed;
- float persistence; // Controls details, value in [0,1]. Higher increases grain, lower increases smoothness.
- int octaves; // Number of noise layers
- float period; // Distance above which we start to see similarities. The higher, the longer "hills" will be on a terrain.
- float lacunarity; // Controls period change across octaves. 2 is usually a good value to address all detail levels.
+ int seed = 0;
+ float persistence = 0.5; // Controls details, value in [0,1]. Higher increases grain, lower increases smoothness.
+ int octaves = 3; // Number of noise layers
+ float period = 64.0; // Distance above which we start to see similarities. The higher, the longer "hills" will be on a terrain.
+ float lacunarity = 2.0; // Controls period change across octaves. 2 is usually a good value to address all detail levels.
public:
OpenSimplexNoise();
@@ -61,7 +61,7 @@ public:
void _init_seeds();
void set_seed(int seed);
- int get_seed();
+ int get_seed() const;
void set_octaves(int p_octaves);
int get_octaves() const { return octaves; }
@@ -75,22 +75,22 @@ public:
void set_lacunarity(float p_lacunarity);
float get_lacunarity() const { return lacunarity; }
- Ref<Image> get_image(int p_width, int p_height);
- Ref<Image> get_seamless_image(int p_size);
+ Ref<Image> get_image(int p_width, int p_height, const Vector2 &p_noise_offset = Vector2()) const;
+ Ref<Image> get_seamless_image(int p_size) const;
- float get_noise_1d(float x);
- float get_noise_2d(float x, float y);
- float get_noise_3d(float x, float y, float z);
- float get_noise_4d(float x, float y, float z, float w);
+ float get_noise_1d(float x) const;
+ float get_noise_2d(float x, float y) const;
+ float get_noise_3d(float x, float y, float z) const;
+ float get_noise_4d(float x, float y, float z, float w) const;
- _FORCE_INLINE_ float _get_octave_noise_2d(int octave, float x, float y) { return open_simplex_noise2(&(contexts[octave]), x, y); }
- _FORCE_INLINE_ float _get_octave_noise_3d(int octave, float x, float y, float z) { return open_simplex_noise3(&(contexts[octave]), x, y, z); }
- _FORCE_INLINE_ float _get_octave_noise_4d(int octave, float x, float y, float z, float w) { return open_simplex_noise4(&(contexts[octave]), x, y, z, w); }
+ _FORCE_INLINE_ float _get_octave_noise_2d(int octave, float x, float y) const { return open_simplex_noise2(&(contexts[octave]), x, y); }
+ _FORCE_INLINE_ float _get_octave_noise_3d(int octave, float x, float y, float z) const { return open_simplex_noise3(&(contexts[octave]), x, y, z); }
+ _FORCE_INLINE_ float _get_octave_noise_4d(int octave, float x, float y, float z, float w) const { return open_simplex_noise4(&(contexts[octave]), x, y, z, w); }
// Convenience
- _FORCE_INLINE_ float get_noise_2dv(Vector2 v) { return get_noise_2d(v.x, v.y); }
- _FORCE_INLINE_ float get_noise_3dv(Vector3 v) { return get_noise_3d(v.x, v.y, v.z); }
+ _FORCE_INLINE_ float get_noise_2dv(const Vector2 &v) const { return get_noise_2d(v.x, v.y); }
+ _FORCE_INLINE_ float get_noise_3dv(const Vector3 &v) const { return get_noise_3d(v.x, v.y, v.z); }
protected:
static void _bind_methods();
diff --git a/modules/opensimplex/register_types.cpp b/modules/opensimplex/register_types.cpp
index fef90cdce3..d6f9f3436d 100644
--- a/modules/opensimplex/register_types.cpp
+++ b/modules/opensimplex/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,8 +33,8 @@
#include "open_simplex_noise.h"
void register_opensimplex_types() {
- ClassDB::register_class<OpenSimplexNoise>();
- ClassDB::register_class<NoiseTexture>();
+ GDREGISTER_CLASS(OpenSimplexNoise);
+ GDREGISTER_CLASS(NoiseTexture);
}
void unregister_opensimplex_types() {
diff --git a/modules/opensimplex/register_types.h b/modules/opensimplex/register_types.h
index 51c6815eae..d72e37e3a3 100644
--- a/modules/opensimplex/register_types.h
+++ b/modules/opensimplex/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/opus/SCsub b/modules/opus/SCsub
deleted file mode 100644
index 52c61fa708..0000000000
--- a/modules/opus/SCsub
+++ /dev/null
@@ -1,239 +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
-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(env.modules_sources, thirdparty_sources)
-
-# Module files
-env_opus.add_source_files(env.modules_sources, "register_types.cpp")
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.h b/modules/opus/register_types.h
deleted file mode 100644
index ad6e083c82..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-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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/SCsub b/modules/pvr/SCsub
index e0baf851f1..36052cffed 100644
--- a/modules/pvr/SCsub
+++ b/modules/pvr/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_pvr = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
# Not unbundled so far since not widespread as shared library
thirdparty_dir = "#thirdparty/pvrtccompressor/"
thirdparty_sources = [
@@ -21,7 +24,15 @@ env_pvr.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_pvr.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
# Godot source files
-env_pvr.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_pvr.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/pvr/image_compress_pvrtc.cpp b/modules/pvr/image_compress_pvrtc.cpp
new file mode 100644
index 0000000000..980cac17d3
--- /dev/null
+++ b/modules/pvr/image_compress_pvrtc.cpp
@@ -0,0 +1,88 @@
+/*************************************************************************/
+/* image_compress_pvrtc.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 "image_compress_pvrtc.h"
+
+#include "core/io/image.h"
+#include "core/object/ref_counted.h"
+
+#include <PvrTcEncoder.h>
+#include <RgbaBitmap.h>
+
+static void _compress_pvrtc1_4bpp(Image *p_img) {
+ Ref<Image> img = p_img->duplicate();
+
+ bool make_mipmaps = false;
+ if (!img->is_size_po2() || img->get_width() != img->get_height()) {
+ make_mipmaps = img->has_mipmaps();
+ img->resize_to_po2(true);
+ // Resizing can fail for some formats
+ if (!img->is_size_po2() || img->get_width() != img->get_height()) {
+ ERR_FAIL_MSG("Failed to resize the image for compression.");
+ }
+ }
+ img->convert(Image::FORMAT_RGBA8);
+ if (!img->has_mipmaps() && make_mipmaps) {
+ img->generate_mipmaps();
+ }
+
+ bool use_alpha = img->detect_alpha();
+
+ Ref<Image> new_img;
+ new_img.instantiate();
+ new_img->create(img->get_width(), img->get_height(), img->has_mipmaps(), use_alpha ? Image::FORMAT_PVRTC1_4A : Image::FORMAT_PVRTC1_4);
+
+ Vector<uint8_t> data = new_img->get_data();
+ {
+ uint8_t *wr = data.ptrw();
+ const uint8_t *r = img->get_data().ptr();
+
+ for (int i = 0; i <= new_img->get_mipmap_count(); i++) {
+ int ofs, size, w, h;
+ img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
+ Javelin::RgbaBitmap bm(w, h);
+ void *dst = (void *)bm.GetData();
+ memcpy(dst, &r[ofs], size);
+ Javelin::ColorRgba<unsigned char> *dp = bm.GetData();
+ for (int j = 0; j < size / 4; j++) {
+ // Red and blue colors are swapped.
+ SWAP(dp[j].r, dp[j].b);
+ }
+ new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
+ Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm);
+ }
+ }
+
+ p_img->create(new_img->get_width(), new_img->get_height(), new_img->has_mipmaps(), new_img->get_format(), data);
+}
+
+void _register_pvrtc_compress_func() {
+ Image::_image_compress_pvrtc1_4bpp_func = _compress_pvrtc1_4bpp;
+}
diff --git a/modules/etc/image_etc.h b/modules/pvr/image_compress_pvrtc.h
index 7b4f26e127..985076ce4d 100644
--- a/modules/etc/image_etc.h
+++ b/modules/pvr/image_compress_pvrtc.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* image_etc.h */
+/* image_compress_pvrtc.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,9 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef IMAGE_ETC1_H
-#define IMAGE_ETC1_H
+#ifndef IMAGE_COMPRESS_PVRTC_H
+#define IMAGE_COMPRESS_PVRTC_H
-void _register_etc_compress_func();
+void _register_pvrtc_compress_func();
-#endif // IMAGE_ETC_H
+#endif // IMAGE_COMPRESS_PVRTC_H
diff --git a/modules/pvr/register_types.cpp b/modules/pvr/register_types.cpp
index 1eb697bba3..ef72087d25 100644
--- a/modules/pvr/register_types.cpp
+++ b/modules/pvr/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,13 +30,16 @@
#include "register_types.h"
+#include "image_compress_pvrtc.h"
#include "texture_loader_pvr.h"
static Ref<ResourceFormatPVR> resource_loader_pvr;
void register_pvr_types() {
- resource_loader_pvr.instance();
+ resource_loader_pvr.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_pvr);
+
+ _register_pvrtc_compress_func();
}
void unregister_pvr_types() {
diff --git a/modules/pvr/register_types.h b/modules/pvr/register_types.h
index 8318996a46..74fcfe2ce4 100644
--- a/modules/pvr/register_types.h
+++ b/modules/pvr/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
index 050dce1aab..ffa900ef99 100644
--- a/modules/pvr/texture_loader_pvr.cpp
+++ b/modules/pvr/texture_loader_pvr.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,16 +29,12 @@
/*************************************************************************/
#include "texture_loader_pvr.h"
-#include "PvrTcEncoder.h"
-#include "RgbaBitmap.h"
-#include "core/os/file_access.h"
-#include <string.h>
-#include <new>
+
+#include "core/io/file_access.h"
static void _pvrtc_decompress(Image *p_img);
enum PVRFLags {
-
PVR_HAS_MIPMAPS = 0x00000100,
PVR_TWIDDLED = 0x00000200,
PVR_NORMAL_MAP = 0x00000400,
@@ -48,10 +44,9 @@ enum PVRFLags {
PVR_VOLUME_TEXTURES = 0x00004000,
PVR_HAS_ALPHA = 0x00008000,
PVR_VFLIP = 0x00010000
-
};
-RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatPVR::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) {
if (r_error) {
*r_error = ERR_CANT_OPEN;
}
@@ -113,11 +108,11 @@ RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path,
switch (flags & 0xFF) {
case 0x18:
case 0xC:
- format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC2A : Image::FORMAT_PVRTC2;
+ format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC1_2A : Image::FORMAT_PVRTC1_2;
break;
case 0x19:
case 0xD:
- format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC4A : Image::FORMAT_PVRTC4;
+ format = (flags & PVR_HAS_ALPHA) ? Image::FORMAT_PVRTC1_4A : Image::FORMAT_PVRTC1_4;
break;
case 0x16:
format = Image::FORMAT_L8;
@@ -158,7 +153,7 @@ RES ResourceFormatPVR::load(const String &p_path, const String &p_original_path,
}
Ref<Image> image = memnew(Image(width, height, mipmaps, format, data));
- ERR_FAIL_COND_V(image->empty(), RES());
+ ERR_FAIL_COND_V(image->is_empty(), RES());
Ref<ImageTexture> texture = memnew(ImageTexture);
texture->create_from_image(image);
@@ -185,53 +180,8 @@ String ResourceFormatPVR::get_resource_type(const String &p_path) const {
return "";
}
-static void _compress_pvrtc4(Image *p_img) {
- Ref<Image> img = p_img->duplicate();
-
- bool make_mipmaps = false;
- if (!img->is_size_po2() || img->get_width() != img->get_height()) {
- make_mipmaps = img->has_mipmaps();
- img->resize_to_po2(true);
- }
- img->convert(Image::FORMAT_RGBA8);
- if (!img->has_mipmaps() && make_mipmaps) {
- img->generate_mipmaps();
- }
-
- bool use_alpha = img->detect_alpha();
-
- Ref<Image> new_img;
- new_img.instance();
- new_img->create(img->get_width(), img->get_height(), img->has_mipmaps(), use_alpha ? Image::FORMAT_PVRTC4A : Image::FORMAT_PVRTC4);
-
- Vector<uint8_t> data = new_img->get_data();
- {
- uint8_t *wr = data.ptrw();
- const uint8_t *r = img->get_data().ptr();
-
- for (int i = 0; i <= new_img->get_mipmap_count(); i++) {
- int ofs, size, w, h;
- img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
- Javelin::RgbaBitmap bm(w, h);
- void *dst = (void *)bm.GetData();
- copymem(dst, &r[ofs], size);
- Javelin::ColorRgba<unsigned char> *dp = bm.GetData();
- for (int j = 0; j < size / 4; j++) {
- /* red and blue colors are swapped. */
- SWAP(dp[j].r, dp[j].b);
- }
- new_img->get_mipmap_offset_size_and_dimensions(i, ofs, size, w, h);
- Javelin::PvrTcEncoder::EncodeRgba4Bpp(&wr[ofs], bm);
- }
- }
-
- p_img->create(new_img->get_width(), new_img->get_height(), new_img->has_mipmaps(), new_img->get_format(), data);
-}
-
ResourceFormatPVR::ResourceFormatPVR() {
Image::_image_decompress_pvrtc = _pvrtc_decompress;
- Image::_image_compress_pvrtc4_func = _compress_pvrtc4;
- Image::_image_compress_pvrtc2_func = _compress_pvrtc4;
}
/////////////////////////////////////////////////////////
@@ -257,7 +207,7 @@ ResourceFormatPVR::ResourceFormatPVR() {
struct PVRTCBlock {
//blocks are 64 bits
- uint32_t data[2];
+ uint32_t data[2] = {};
};
_FORCE_INLINE_ bool is_po2(uint32_t p_input) {
@@ -440,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]];
@@ -637,9 +587,9 @@ static void decompress_pvrtc(PVRTCBlock *p_comp_img, const int p_2bit, const int
}
static void _pvrtc_decompress(Image *p_img) {
- ERR_FAIL_COND(p_img->get_format() != Image::FORMAT_PVRTC2 && p_img->get_format() != Image::FORMAT_PVRTC2A && p_img->get_format() != Image::FORMAT_PVRTC4 && p_img->get_format() != Image::FORMAT_PVRTC4A);
+ ERR_FAIL_COND(p_img->get_format() != Image::FORMAT_PVRTC1_2 && p_img->get_format() != Image::FORMAT_PVRTC1_2A && p_img->get_format() != Image::FORMAT_PVRTC1_4 && p_img->get_format() != Image::FORMAT_PVRTC1_4A);
- bool _2bit = (p_img->get_format() == Image::FORMAT_PVRTC2 || p_img->get_format() == Image::FORMAT_PVRTC2A);
+ bool _2bit = (p_img->get_format() == Image::FORMAT_PVRTC1_2 || p_img->get_format() == Image::FORMAT_PVRTC1_2A);
Vector<uint8_t> data = p_img->get_data();
const uint8_t *r = data.ptr();
diff --git a/modules/pvr/texture_loader_pvr.h b/modules/pvr/texture_loader_pvr.h
index 07ef129689..26071ce30f 100644
--- a/modules/pvr/texture_loader_pvr.h
+++ b/modules/pvr/texture_loader_pvr.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,7 +36,7 @@
class ResourceFormatPVR : 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, bool p_no_cache = false);
+ 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;
diff --git a/modules/raycast/SCsub b/modules/raycast/SCsub
new file mode 100644
index 0000000000..ef4c598194
--- /dev/null
+++ b/modules/raycast/SCsub
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_raycast = env_modules.Clone()
+
+# Thirdparty source files
+
+thirdparty_obj = []
+
+if env["builtin_embree"]:
+ thirdparty_dir = "#thirdparty/embree/"
+
+ embree_src = [
+ "common/sys/sysinfo.cpp",
+ "common/sys/alloc.cpp",
+ "common/sys/filename.cpp",
+ "common/sys/library.cpp",
+ "common/sys/thread.cpp",
+ "common/sys/string.cpp",
+ "common/sys/regression.cpp",
+ "common/sys/mutex.cpp",
+ "common/sys/condition.cpp",
+ "common/sys/barrier.cpp",
+ "common/math/constants.cpp",
+ "common/simd/sse.cpp",
+ "common/lexers/stringstream.cpp",
+ "common/lexers/tokenstream.cpp",
+ "common/tasking/taskschedulerinternal.cpp",
+ "kernels/common/device.cpp",
+ "kernels/common/stat.cpp",
+ "kernels/common/acceln.cpp",
+ "kernels/common/accelset.cpp",
+ "kernels/common/state.cpp",
+ "kernels/common/rtcore.cpp",
+ "kernels/common/rtcore_builder.cpp",
+ "kernels/common/scene.cpp",
+ "kernels/common/alloc.cpp",
+ "kernels/common/geometry.cpp",
+ "kernels/common/scene_triangle_mesh.cpp",
+ "kernels/geometry/primitive4.cpp",
+ "kernels/builders/primrefgen.cpp",
+ "kernels/bvh/bvh.cpp",
+ "kernels/bvh/bvh_statistics.cpp",
+ "kernels/bvh/bvh4_factory.cpp",
+ "kernels/bvh/bvh8_factory.cpp",
+ "kernels/bvh/bvh_collider.cpp",
+ "kernels/bvh/bvh_rotate.cpp",
+ "kernels/bvh/bvh_refit.cpp",
+ "kernels/bvh/bvh_builder.cpp",
+ "kernels/bvh/bvh_builder_morton.cpp",
+ "kernels/bvh/bvh_builder_sah.cpp",
+ "kernels/bvh/bvh_builder_sah_spatial.cpp",
+ "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]
+
+ env_raycast.Prepend(CPPPATH=[thirdparty_dir, thirdparty_dir + "include"])
+ env_raycast.Append(CPPDEFINES=["EMBREE_TARGET_SSE2", "EMBREE_LOWEST_ISA", "TASKING_INTERNAL", "NDEBUG"])
+
+ if not env.msvc:
+ if env["arch"] in ["x86", "x86_64"]:
+ env_raycast.Append(CPPFLAGS=["-msse2", "-mxsave"])
+
+ if env["platform"] == "windows":
+ env_raycast.Append(CPPFLAGS=["-mstackrealign"])
+
+ if env["platform"] == "windows":
+ if env.msvc:
+ env.Append(LINKFLAGS=["psapi.lib"])
+ else:
+ 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)
+
+ if not env["arch"] in ["x86", "x86_64"] or env.msvc:
+ # 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
+
+
+# Godot source files
+
+module_obj = []
+
+env_raycast.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/raycast/config.py b/modules/raycast/config.py
new file mode 100644
index 0000000000..7e8b3e9840
--- /dev/null
+++ b/modules/raycast/config.py
@@ -0,0 +1,19 @@
+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"]
+
+ if platform == "javascript":
+ return False # No SIMD support yet
+
+ if env["bits"] == "32":
+ return False
+
+ return True
+
+
+def configure(env):
+ pass
diff --git a/modules/raycast/godot_update_embree.py b/modules/raycast/godot_update_embree.py
new file mode 100644
index 0000000000..e31d88b741
--- /dev/null
+++ b/modules/raycast/godot_update_embree.py
@@ -0,0 +1,261 @@
+import glob, os, shutil, subprocess, re
+
+include_dirs = [
+ "common/tasking",
+ "kernels/bvh",
+ "kernels/builders",
+ "common/sys",
+ "kernels",
+ "kernels/common",
+ "common/math",
+ "common/algorithms",
+ "common/lexers",
+ "common/simd",
+ "common/simd/arm",
+ "include/embree3",
+ "kernels/subdiv",
+ "kernels/geometry",
+]
+
+cpp_files = [
+ "common/sys/sysinfo.cpp",
+ "common/sys/alloc.cpp",
+ "common/sys/filename.cpp",
+ "common/sys/library.cpp",
+ "common/sys/thread.cpp",
+ "common/sys/string.cpp",
+ "common/sys/regression.cpp",
+ "common/sys/mutex.cpp",
+ "common/sys/condition.cpp",
+ "common/sys/barrier.cpp",
+ "common/math/constants.cpp",
+ "common/simd/sse.cpp",
+ "common/lexers/stringstream.cpp",
+ "common/lexers/tokenstream.cpp",
+ "common/tasking/taskschedulerinternal.cpp",
+ "kernels/common/device.cpp",
+ "kernels/common/stat.cpp",
+ "kernels/common/acceln.cpp",
+ "kernels/common/accelset.cpp",
+ "kernels/common/state.cpp",
+ "kernels/common/rtcore.cpp",
+ "kernels/common/rtcore_builder.cpp",
+ "kernels/common/scene.cpp",
+ "kernels/common/alloc.cpp",
+ "kernels/common/geometry.cpp",
+ "kernels/common/scene_triangle_mesh.cpp",
+ "kernels/geometry/primitive4.cpp",
+ "kernels/builders/primrefgen.cpp",
+ "kernels/bvh/bvh.cpp",
+ "kernels/bvh/bvh_statistics.cpp",
+ "kernels/bvh/bvh4_factory.cpp",
+ "kernels/bvh/bvh8_factory.cpp",
+ "kernels/bvh/bvh_collider.cpp",
+ "kernels/bvh/bvh_rotate.cpp",
+ "kernels/bvh/bvh_refit.cpp",
+ "kernels/bvh/bvh_builder.cpp",
+ "kernels/bvh/bvh_builder_morton.cpp",
+ "kernels/bvh/bvh_builder_sah.cpp",
+ "kernels/bvh/bvh_builder_sah_spatial.cpp",
+ "kernels/bvh/bvh_builder_sah_mb.cpp",
+ "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")
+
+dir_name = "embree"
+if os.path.exists(dir_name):
+ shutil.rmtree(dir_name)
+
+subprocess.run(["git", "clone", "https://github.com/embree/embree.git", "embree-tmp"])
+os.chdir("embree-tmp")
+
+commit_hash = str(subprocess.check_output(["git", "rev-parse", "HEAD"], universal_newlines=True)).strip()
+
+all_files = set(cpp_files)
+
+dest_dir = os.path.join("..", dir_name)
+for include_dir in include_dirs:
+ headers = glob.iglob(os.path.join(include_dir, "*.h"))
+ all_files.update(headers)
+
+for f in all_files:
+ d = os.path.join(dest_dir, os.path.dirname(f))
+ if not os.path.exists(d):
+ os.makedirs(d)
+ shutil.copy2(f, d)
+
+with open(os.path.join(dest_dir, "kernels/hash.h"), "w") as hash_file:
+ hash_file.write(
+ f"""
+// Copyright 2009-2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#define RTC_HASH "{commit_hash}"
+"""
+ )
+
+with open(os.path.join(dest_dir, "kernels/config.h"), "w") as config_file:
+ config_file.write(
+ """
+// Copyright 2009-2020 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+/* #undef EMBREE_RAY_MASK */
+/* #undef EMBREE_STAT_COUNTERS */
+/* #undef EMBREE_BACKFACE_CULLING */
+/* #undef EMBREE_BACKFACE_CULLING_CURVES */
+#define EMBREE_FILTER_FUNCTION
+/* #undef EMBREE_IGNORE_INVALID_RAYS */
+#define EMBREE_GEOMETRY_TRIANGLE
+/* #undef EMBREE_GEOMETRY_QUAD */
+/* #undef EMBREE_GEOMETRY_CURVE */
+/* #undef EMBREE_GEOMETRY_SUBDIVISION */
+/* #undef EMBREE_GEOMETRY_USER */
+/* #undef EMBREE_GEOMETRY_INSTANCE */
+/* #undef EMBREE_GEOMETRY_GRID */
+/* #undef EMBREE_GEOMETRY_POINT */
+#define EMBREE_RAY_PACKETS
+/* #undef EMBREE_COMPACT_POLYS */
+
+#define EMBREE_CURVE_SELF_INTERSECTION_AVOIDANCE_FACTOR 2.0
+
+#if defined(EMBREE_GEOMETRY_TRIANGLE)
+ #define IF_ENABLED_TRIS(x) x
+#else
+ #define IF_ENABLED_TRIS(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_QUAD)
+ #define IF_ENABLED_QUADS(x) x
+#else
+ #define IF_ENABLED_QUADS(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_CURVE) || defined(EMBREE_GEOMETRY_POINT)
+ #define IF_ENABLED_CURVES_OR_POINTS(x) x
+#else
+ #define IF_ENABLED_CURVES_OR_POINTS(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_CURVE)
+ #define IF_ENABLED_CURVES(x) x
+#else
+ #define IF_ENABLED_CURVES(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_POINT)
+ #define IF_ENABLED_POINTS(x) x
+#else
+ #define IF_ENABLED_POINTS(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_SUBDIVISION)
+ #define IF_ENABLED_SUBDIV(x) x
+#else
+ #define IF_ENABLED_SUBDIV(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_USER)
+ #define IF_ENABLED_USER(x) x
+#else
+ #define IF_ENABLED_USER(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_INSTANCE)
+ #define IF_ENABLED_INSTANCE(x) x
+#else
+ #define IF_ENABLED_INSTANCE(x)
+#endif
+
+#if defined(EMBREE_GEOMETRY_GRID)
+ #define IF_ENABLED_GRIDS(x) x
+#else
+ #define IF_ENABLED_GRIDS(x)
+#endif
+"""
+ )
+
+
+with open("CMakeLists.txt", "r") as cmake_file:
+ cmake_content = cmake_file.read()
+ major_version = int(re.compile(r"EMBREE_VERSION_MAJOR\s(\d+)").findall(cmake_content)[0])
+ minor_version = int(re.compile(r"EMBREE_VERSION_MINOR\s(\d+)").findall(cmake_content)[0])
+ patch_version = int(re.compile(r"EMBREE_VERSION_PATCH\s(\d+)").findall(cmake_content)[0])
+
+with open(os.path.join(dest_dir, "include/embree3/rtcore_config.h"), "w") as config_file:
+ config_file.write(
+ f"""
+// Copyright 2009-2021 Intel Corporation
+// SPDX-License-Identifier: Apache-2.0
+
+#pragma once
+
+#define RTC_VERSION_MAJOR {major_version}
+#define RTC_VERSION_MINOR {minor_version}
+#define RTC_VERSION_PATCH {patch_version}
+#define RTC_VERSION {major_version}{minor_version:02d}{patch_version:02d}
+#define RTC_VERSION_STRING "{major_version}.{minor_version}.{patch_version}"
+
+#define RTC_MAX_INSTANCE_LEVEL_COUNT 1
+
+#define EMBREE_MIN_WIDTH 0
+#define RTC_MIN_WIDTH EMBREE_MIN_WIDTH
+
+#define EMBREE_STATIC_LIB
+/* #undef EMBREE_API_NAMESPACE */
+
+#if defined(EMBREE_API_NAMESPACE)
+# define RTC_NAMESPACE
+# define RTC_NAMESPACE_BEGIN namespace {{
+# define RTC_NAMESPACE_END }}
+# define RTC_NAMESPACE_USE using namespace ;
+# define RTC_API_EXTERN_C
+# undef EMBREE_API_NAMESPACE
+#else
+# define RTC_NAMESPACE_BEGIN
+# define RTC_NAMESPACE_END
+# define RTC_NAMESPACE_USE
+# if defined(__cplusplus)
+# define RTC_API_EXTERN_C extern "C"
+# else
+# define RTC_API_EXTERN_C
+# endif
+#endif
+
+#if defined(ISPC)
+# define RTC_API_IMPORT extern "C" unmasked
+# define RTC_API_EXPORT extern "C" unmasked
+#elif defined(EMBREE_STATIC_LIB)
+# define RTC_API_IMPORT RTC_API_EXTERN_C
+# define RTC_API_EXPORT RTC_API_EXTERN_C
+#elif defined(_WIN32)
+# define RTC_API_IMPORT RTC_API_EXTERN_C __declspec(dllimport)
+# define RTC_API_EXPORT RTC_API_EXTERN_C __declspec(dllexport)
+#else
+# define RTC_API_IMPORT RTC_API_EXTERN_C
+# define RTC_API_EXPORT RTC_API_EXTERN_C __attribute__ ((visibility ("default")))
+#endif
+
+#if defined(RTC_EXPORT_API)
+# define RTC_API RTC_API_EXPORT
+#else
+# define RTC_API RTC_API_IMPORT
+#endif
+"""
+ )
+
+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
new file mode 100644
index 0000000000..fdcf509da8
--- /dev/null
+++ b/modules/raycast/lightmap_raycaster.cpp
@@ -0,0 +1,196 @@
+/*************************************************************************/
+/* lightmap_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 "lightmap_raycaster.h"
+
+#ifdef __SSE2__
+#include <pmmintrin.h>
+#endif
+
+LightmapRaycaster *LightmapRaycasterEmbree::create_embree_raycaster() {
+ return memnew(LightmapRaycasterEmbree);
+}
+
+void LightmapRaycasterEmbree::make_default_raycaster() {
+ create_function = create_embree_raycaster;
+}
+
+void LightmapRaycasterEmbree::filter_function(const struct RTCFilterFunctionNArguments *p_args) {
+ RTCHit *hit = (RTCHit *)p_args->hit;
+
+ unsigned int geomID = hit->geomID;
+ float u = hit->u;
+ float v = hit->v;
+
+ LightmapRaycasterEmbree *scene = (LightmapRaycasterEmbree *)p_args->geometryUserPtr;
+ RTCGeometry geom = rtcGetGeometry(scene->embree_scene, geomID);
+
+ rtcInterpolate0(geom, hit->primID, hit->u, hit->v, RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE, 0, &hit->u, 2);
+
+ if (scene->alpha_textures.has(geomID)) {
+ const AlphaTextureData &alpha_texture = scene->alpha_textures[geomID];
+
+ if (alpha_texture.sample(hit->u, hit->v) < 128) {
+ p_args->valid[0] = 0;
+ return;
+ }
+ }
+
+ rtcInterpolate0(geom, hit->primID, u, v, RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE, 1, &hit->Ng_x, 3);
+}
+
+bool LightmapRaycasterEmbree::intersect(Ray &r_ray) {
+ RTCIntersectContext context;
+
+ rtcInitIntersectContext(&context);
+
+ rtcIntersect1(embree_scene, &context, (RTCRayHit *)&r_ray);
+ return r_ray.geomID != RTC_INVALID_GEOMETRY_ID;
+}
+
+void LightmapRaycasterEmbree::intersect(Vector<Ray> &r_rays) {
+ Ray *rays = r_rays.ptrw();
+ for (int i = 0; i < r_rays.size(); ++i) {
+ intersect(rays[i]);
+ }
+}
+
+void LightmapRaycasterEmbree::set_mesh_alpha_texture(Ref<Image> p_alpha_texture, unsigned int p_id) {
+ if (p_alpha_texture.is_valid() && p_alpha_texture->get_size() != Vector2i()) {
+ AlphaTextureData tex;
+ tex.size = p_alpha_texture->get_size();
+ tex.data = p_alpha_texture->get_data();
+ alpha_textures.insert(p_id, tex);
+ }
+}
+
+float blerp(float c00, float c10, float c01, float c11, float tx, float ty) {
+ return Math::lerp(Math::lerp(c00, c10, tx), Math::lerp(c01, c11, tx), ty);
+}
+
+uint8_t LightmapRaycasterEmbree::AlphaTextureData::sample(float u, float v) const {
+ float x = u * size.x;
+ float y = v * size.y;
+ int xi = (int)x;
+ int yi = (int)y;
+
+ uint8_t texels[4];
+
+ for (int i = 0; i < 4; ++i) {
+ int sample_x = CLAMP(xi + i % 2, 0, size.x - 1);
+ int sample_y = CLAMP(yi + i / 2, 0, size.y - 1);
+ texels[i] = data[sample_y * size.x + sample_x];
+ }
+
+ return Math::round(blerp(texels[0], texels[1], texels[2], texels[3], x - xi, y - yi));
+}
+
+void LightmapRaycasterEmbree::add_mesh(const Vector<Vector3> &p_vertices, const Vector<Vector3> &p_normals, const Vector<Vector2> &p_uv2s, unsigned int p_id) {
+ RTCGeometry embree_mesh = rtcNewGeometry(embree_device, RTC_GEOMETRY_TYPE_TRIANGLE);
+
+ rtcSetGeometryVertexAttributeCount(embree_mesh, 2);
+
+ int vertex_count = p_vertices.size();
+
+ ERR_FAIL_COND(vertex_count % 3 != 0);
+ ERR_FAIL_COND(vertex_count != p_uv2s.size());
+ ERR_FAIL_COND(!p_normals.is_empty() && vertex_count != p_normals.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);
+
+ Vector2 *embree_light_uvs = (Vector2 *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE, 0, RTC_FORMAT_FLOAT2, sizeof(Vector2), vertex_count);
+ memcpy(embree_light_uvs, p_uv2s.ptr(), sizeof(Vector2) * vertex_count);
+
+ 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;
+ }
+
+ if (!p_normals.is_empty()) {
+ Vector3 *embree_normals = (Vector3 *)rtcSetNewGeometryBuffer(embree_mesh, RTC_BUFFER_TYPE_VERTEX_ATTRIBUTE, 1, RTC_FORMAT_FLOAT3, sizeof(Vector3), vertex_count);
+ memcpy(embree_normals, p_normals.ptr(), sizeof(Vector3) * vertex_count);
+ }
+
+ rtcCommitGeometry(embree_mesh);
+ rtcSetGeometryIntersectFilterFunction(embree_mesh, filter_function);
+ rtcSetGeometryUserData(embree_mesh, this);
+ rtcAttachGeometryByID(embree_scene, embree_mesh, p_id);
+ rtcReleaseGeometry(embree_mesh);
+}
+
+void LightmapRaycasterEmbree::commit() {
+ rtcCommitScene(embree_scene);
+}
+
+void LightmapRaycasterEmbree::set_mesh_filter(const Set<int> &p_mesh_ids) {
+ for (Set<int>::Element *E = p_mesh_ids.front(); E; E = E->next()) {
+ rtcDisableGeometry(rtcGetGeometry(embree_scene, E->get()));
+ }
+ rtcCommitScene(embree_scene);
+ filter_meshes = p_mesh_ids;
+}
+
+void LightmapRaycasterEmbree::clear_mesh_filter() {
+ for (Set<int>::Element *E = filter_meshes.front(); E; E = E->next()) {
+ rtcEnableGeometry(rtcGetGeometry(embree_scene, E->get()));
+ }
+ rtcCommitScene(embree_scene);
+ filter_meshes.clear();
+}
+
+void embree_lm_error_handler(void *p_user_data, RTCError p_code, const char *p_str) {
+ print_error("Embree error: " + String(p_str));
+}
+
+LightmapRaycasterEmbree::LightmapRaycasterEmbree() {
+#ifdef __SSE2__
+ _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
+ _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
+#endif
+
+ embree_device = rtcNewDevice(nullptr);
+ rtcSetDeviceErrorFunction(embree_device, &embree_lm_error_handler, nullptr);
+ embree_scene = rtcNewScene(embree_device);
+}
+
+LightmapRaycasterEmbree::~LightmapRaycasterEmbree() {
+ if (embree_scene != nullptr) {
+ rtcReleaseScene(embree_scene);
+ }
+
+ if (embree_device != nullptr) {
+ rtcReleaseDevice(embree_device);
+ }
+}
+
+#endif
diff --git a/modules/raycast/lightmap_raycaster.h b/modules/raycast/lightmap_raycaster.h
new file mode 100644
index 0000000000..290b0a1cf3
--- /dev/null
+++ b/modules/raycast/lightmap_raycaster.h
@@ -0,0 +1,77 @@
+/*************************************************************************/
+/* lightmap_raycaster.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. */
+/*************************************************************************/
+
+#ifdef TOOLS_ENABLED
+
+#include "core/io/image.h"
+#include "core/object/object.h"
+#include "scene/3d/lightmapper.h"
+
+#include <embree3/rtcore.h>
+
+class LightmapRaycasterEmbree : public LightmapRaycaster {
+ GDCLASS(LightmapRaycasterEmbree, LightmapRaycaster);
+
+private:
+ struct AlphaTextureData {
+ Vector<uint8_t> data;
+ Vector2i size;
+
+ uint8_t sample(float u, float v) const;
+ };
+
+ RTCDevice embree_device;
+ RTCScene embree_scene;
+
+ static void filter_function(const struct RTCFilterFunctionNArguments *p_args);
+
+ Map<unsigned int, AlphaTextureData> alpha_textures;
+ Set<int> filter_meshes;
+
+public:
+ virtual bool intersect(Ray &p_ray) override;
+
+ virtual void intersect(Vector<Ray> &r_rays) override;
+
+ virtual void add_mesh(const Vector<Vector3> &p_vertices, const Vector<Vector3> &p_normals, const Vector<Vector2> &p_uv2s, unsigned int p_id) override;
+ virtual void set_mesh_alpha_texture(Ref<Image> p_alpha_texture, unsigned int p_id) override;
+ virtual void commit() override;
+
+ virtual void set_mesh_filter(const Set<int> &p_mesh_ids) override;
+ virtual void clear_mesh_filter() override;
+
+ static LightmapRaycaster *create_embree_raycaster();
+ static void make_default_raycaster();
+
+ LightmapRaycasterEmbree();
+ ~LightmapRaycasterEmbree();
+};
+
+#endif
diff --git a/modules/raycast/raycast_occlusion_cull.cpp b/modules/raycast/raycast_occlusion_cull.cpp
new file mode 100644
index 0000000000..75491c98e5
--- /dev/null
+++ b/modules/raycast/raycast_occlusion_cull.cpp
@@ -0,0 +1,618 @@
+/*************************************************************************/
+/* raycast_occlusion_cull.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 "raycast_occlusion_cull.h"
+#include "core/config/project_settings.h"
+#include "core/templates/local_vector.h"
+
+#ifdef __SSE2__
+#include <pmmintrin.h>
+#endif
+
+RaycastOcclusionCull *RaycastOcclusionCull::raycast_singleton = nullptr;
+
+void RaycastOcclusionCull::RaycastHZBuffer::clear() {
+ HZBuffer::clear();
+
+ if (camera_rays_unaligned_buffer) {
+ memfree(camera_rays_unaligned_buffer);
+ camera_rays_unaligned_buffer = nullptr;
+ camera_rays = nullptr;
+ }
+ camera_ray_masks.clear();
+ camera_rays_tile_count = 0;
+ tile_grid_size = Size2i();
+}
+
+void RaycastOcclusionCull::RaycastHZBuffer::resize(const Size2i &p_size) {
+ if (p_size == Size2i()) {
+ clear();
+ return;
+ }
+
+ if (!sizes.is_empty() && p_size == sizes[0]) {
+ return; // Size didn't change
+ }
+
+ HZBuffer::resize(p_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.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, 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 * 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 CameraRayThreadData *p_data, int p_from, int p_to) {
+ const Size2i &buffer_size = sizes[0];
+
+ for (int i = p_from; i < p_to; i++) {
+ 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++) {
+ int x = tile_x + j % TILE_SIZE;
+ int y = tile_y + j / TILE_SIZE;
+
+ 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;
+
+ tile.ray.tnear[j] = p_data->z_near;
+
+ 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);
+ }
+
+ tile.ray.dir_x[j] = dir.x;
+ tile.ray.dir_y[j] = dir.y;
+ tile.ray.dir_z[j] = dir.z;
+
+ tile.ray.tfar[j] = p_data->z_far;
+ tile.ray.time[j] = 0.0f;
+
+ tile.ray.flags[j] = 0;
+ tile.ray.mask[j] = ~0U;
+ tile.hit.geomID[j] = RTC_INVALID_GEOMETRY_ID;
+ }
+ }
+}
+
+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 < 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;
+ int y = i * TILE_SIZE + tile_i;
+ if (x >= buffer_size.x || y >= buffer_size.y) {
+ continue;
+ }
+ int k = tile_i * TILE_SIZE + tile_j;
+ 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) {
+ return occluder_owner.owns(p_rid);
+}
+
+RID RaycastOcclusionCull::occluder_allocate() {
+ return occluder_owner.allocate_rid();
+}
+
+void RaycastOcclusionCull::occluder_initialize(RID p_occluder) {
+ Occluder *occluder = memnew(Occluder);
+ occluder_owner.initialize_rid(p_occluder, occluder);
+}
+
+void RaycastOcclusionCull::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) {
+ Occluder *occluder = occluder_owner.get_or_null(p_occluder);
+ ERR_FAIL_COND(!occluder);
+
+ occluder->vertices = p_vertices;
+ occluder->indices = p_indices;
+
+ for (Set<InstanceID>::Element *E = occluder->users.front(); E; E = E->next()) {
+ RID scenario_rid = E->get().scenario;
+ RID instance_rid = E->get().instance;
+ ERR_CONTINUE(!scenarios.has(scenario_rid));
+ Scenario &scenario = scenarios[scenario_rid];
+ ERR_CONTINUE(!scenario.instances.has(instance_rid));
+
+ if (!scenario.dirty_instances.has(instance_rid)) {
+ scenario.dirty_instances.insert(instance_rid);
+ scenario.dirty_instances_array.push_back(instance_rid);
+ }
+ }
+}
+
+void RaycastOcclusionCull::free_occluder(RID p_occluder) {
+ Occluder *occluder = occluder_owner.get_or_null(p_occluder);
+ ERR_FAIL_COND(!occluder);
+ memdelete(occluder);
+ occluder_owner.free(p_occluder);
+}
+
+////////////////////////////////////////////////////////
+
+void RaycastOcclusionCull::add_scenario(RID p_scenario) {
+ if (scenarios.has(p_scenario)) {
+ scenarios[p_scenario].removed = false;
+ } else {
+ scenarios[p_scenario] = Scenario();
+ }
+}
+
+void RaycastOcclusionCull::remove_scenario(RID p_scenario) {
+ ERR_FAIL_COND(!scenarios.has(p_scenario));
+ Scenario &scenario = scenarios[p_scenario];
+ scenario.removed = true;
+}
+
+void RaycastOcclusionCull::scenario_set_instance(RID p_scenario, RID p_instance, RID p_occluder, const Transform3D &p_xform, bool p_enabled) {
+ ERR_FAIL_COND(!scenarios.has(p_scenario));
+ Scenario &scenario = scenarios[p_scenario];
+
+ if (!scenario.instances.has(p_instance)) {
+ scenario.instances[p_instance] = OccluderInstance();
+ }
+
+ OccluderInstance &instance = scenario.instances[p_instance];
+
+ if (instance.removed) {
+ instance.removed = false;
+ scenario.removed_instances.erase(p_instance);
+ }
+
+ bool changed = false;
+
+ if (instance.occluder != p_occluder) {
+ Occluder *old_occluder = occluder_owner.get_or_null(instance.occluder);
+ if (old_occluder) {
+ old_occluder->users.erase(InstanceID(p_scenario, p_instance));
+ }
+
+ instance.occluder = p_occluder;
+
+ if (p_occluder.is_valid()) {
+ Occluder *occluder = occluder_owner.get_or_null(p_occluder);
+ ERR_FAIL_COND(!occluder);
+ occluder->users.insert(InstanceID(p_scenario, p_instance));
+ }
+ changed = true;
+ }
+
+ if (instance.xform != p_xform) {
+ scenario.instances[p_instance].xform = p_xform;
+ changed = true;
+ }
+
+ if (instance.enabled != p_enabled) {
+ instance.enabled = p_enabled;
+ scenario.dirty = true; // The scenario needs a scene re-build, but the instance doesn't need update
+ }
+
+ if (changed && !scenario.dirty_instances.has(p_instance)) {
+ scenario.dirty_instances.insert(p_instance);
+ scenario.dirty_instances_array.push_back(p_instance);
+ scenario.dirty = true;
+ }
+}
+
+void RaycastOcclusionCull::scenario_remove_instance(RID p_scenario, RID p_instance) {
+ ERR_FAIL_COND(!scenarios.has(p_scenario));
+ Scenario &scenario = scenarios[p_scenario];
+
+ if (scenario.instances.has(p_instance)) {
+ OccluderInstance &instance = scenario.instances[p_instance];
+
+ if (!instance.removed) {
+ Occluder *occluder = occluder_owner.get_or_null(instance.occluder);
+ if (occluder) {
+ occluder->users.erase(InstanceID(p_scenario, p_instance));
+ }
+
+ scenario.removed_instances.push_back(p_instance);
+ instance.removed = true;
+ }
+ }
+}
+
+void RaycastOcclusionCull::Scenario::_update_dirty_instance_thread(int p_idx, RID *p_instances) {
+ _update_dirty_instance(p_idx, p_instances, nullptr);
+}
+
+void RaycastOcclusionCull::Scenario::_update_dirty_instance(int p_idx, RID *p_instances, ThreadWorkPool *p_thread_pool) {
+ OccluderInstance *occ_inst = instances.getptr(p_instances[p_idx]);
+
+ if (!occ_inst) {
+ return;
+ }
+
+ Occluder *occ = raycast_singleton->occluder_owner.get_or_null(occ_inst->occluder);
+
+ if (!occ) {
+ return;
+ }
+
+ int vertices_size = occ->vertices.size();
+
+ // Embree requires the last element to be readable by a 16-byte SSE load instruction, so we add padding to be safe.
+ occ_inst->xformed_vertices.resize(vertices_size + 1);
+
+ const Vector3 *read_ptr = occ->vertices.ptr();
+ Vector3 *write_ptr = occ_inst->xformed_vertices.ptr();
+
+ if (p_thread_pool && vertices_size > 1024) {
+ TransformThreadData td;
+ td.xform = occ_inst->xform;
+ td.read = read_ptr;
+ td.write = write_ptr;
+ td.vertex_count = vertices_size;
+ td.thread_count = p_thread_pool->get_thread_count();
+ p_thread_pool->do_work(td.thread_count, this, &Scenario::_transform_vertices_thread, &td);
+ } else {
+ _transform_vertices_range(read_ptr, write_ptr, occ_inst->xform, 0, vertices_size);
+ }
+
+ occ_inst->indices.resize(occ->indices.size());
+ memcpy(occ_inst->indices.ptr(), occ->indices.ptr(), occ->indices.size() * sizeof(int32_t));
+}
+
+void RaycastOcclusionCull::Scenario::_transform_vertices_thread(uint32_t p_thread, TransformThreadData *p_data) {
+ uint32_t vertex_total = p_data->vertex_count;
+ uint32_t total_threads = p_data->thread_count;
+ uint32_t from = p_thread * vertex_total / total_threads;
+ uint32_t to = (p_thread + 1 == total_threads) ? vertex_total : ((p_thread + 1) * vertex_total / total_threads);
+ _transform_vertices_range(p_data->read, p_data->write, p_data->xform, from, to);
+}
+
+void RaycastOcclusionCull::Scenario::_transform_vertices_range(const Vector3 *p_read, Vector3 *p_write, const Transform3D &p_xform, int p_from, int p_to) {
+ for (int i = p_from; i < p_to; i++) {
+ p_write[i] = p_xform.xform(p_read[i]);
+ }
+}
+
+void RaycastOcclusionCull::Scenario::_commit_scene(void *p_ud) {
+ Scenario *scenario = (Scenario *)p_ud;
+ int commit_idx = 1 - (scenario->current_scene_idx);
+ rtcCommitScene(scenario->ebr_scene[commit_idx]);
+ scenario->commit_done = true;
+}
+
+bool RaycastOcclusionCull::Scenario::update(ThreadWorkPool &p_thread_pool) {
+ ERR_FAIL_COND_V(singleton == nullptr, false);
+
+ if (commit_thread == nullptr) {
+ commit_thread = memnew(Thread);
+ }
+
+ if (commit_thread->is_started()) {
+ if (commit_done) {
+ commit_thread->wait_to_finish();
+ current_scene_idx = 1 - current_scene_idx;
+ } else {
+ return false;
+ }
+ }
+
+ if (removed) {
+ if (ebr_scene[0]) {
+ rtcReleaseScene(ebr_scene[0]);
+ }
+ if (ebr_scene[1]) {
+ rtcReleaseScene(ebr_scene[1]);
+ }
+ return true;
+ }
+
+ if (!dirty && removed_instances.is_empty() && dirty_instances_array.is_empty()) {
+ return false;
+ }
+
+ for (unsigned int i = 0; i < removed_instances.size(); i++) {
+ instances.erase(removed_instances[i]);
+ }
+
+ if (dirty_instances_array.size() / p_thread_pool.get_thread_count() > 128) {
+ // Lots of instances, use per-instance threading
+ p_thread_pool.do_work(dirty_instances_array.size(), this, &Scenario::_update_dirty_instance_thread, dirty_instances_array.ptr());
+ } else {
+ // Few instances, use threading on the vertex transforms
+ for (unsigned int i = 0; i < dirty_instances_array.size(); i++) {
+ _update_dirty_instance(i, dirty_instances_array.ptr(), &p_thread_pool);
+ }
+ }
+
+ dirty_instances.clear();
+ dirty_instances_array.clear();
+ removed_instances.clear();
+
+ if (raycast_singleton->ebr_device == nullptr) {
+ raycast_singleton->_init_embree();
+ }
+
+ int next_scene_idx = 1 - current_scene_idx;
+ RTCScene &next_scene = ebr_scene[next_scene_idx];
+
+ if (next_scene) {
+ rtcReleaseScene(next_scene);
+ }
+
+ next_scene = rtcNewScene(raycast_singleton->ebr_device);
+ rtcSetSceneBuildQuality(next_scene, RTCBuildQuality(raycast_singleton->build_quality));
+
+ 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.get_or_null(occ_inst->occluder);
+
+ if (!occ || !occ_inst->enabled) {
+ continue;
+ }
+
+ RTCGeometry geom = rtcNewGeometry(raycast_singleton->ebr_device, RTC_GEOMETRY_TYPE_TRIANGLE);
+ rtcSetSharedGeometryBuffer(geom, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, occ_inst->xformed_vertices.ptr(), 0, sizeof(Vector3), occ_inst->xformed_vertices.size());
+ rtcSetSharedGeometryBuffer(geom, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, occ_inst->indices.ptr(), 0, sizeof(uint32_t) * 3, occ_inst->indices.size() / 3);
+ rtcCommitGeometry(geom);
+ rtcAttachGeometry(next_scene, geom);
+ rtcReleaseGeometry(geom);
+ }
+
+ dirty = false;
+ commit_done = false;
+ commit_thread->start(&Scenario::_commit_scene, this);
+ return false;
+}
+
+void RaycastOcclusionCull::Scenario::_raycast(uint32_t p_idx, const RaycastThreadData *p_raycast_data) const {
+ RTCIntersectContext ctx;
+ rtcInitIntersectContext(&ctx);
+ ctx.flags = RTC_INTERSECT_CONTEXT_FLAG_COHERENT;
+
+ 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(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.
+ }
+
+ if (ebr_scene[current_scene_idx] == nullptr) {
+ return;
+ }
+
+ RaycastThreadData td;
+ td.rays = r_rays;
+ td.masks = p_valid_masks;
+
+ p_thread_pool.do_work(p_tile_count, this, &Scenario::_raycast, &td);
+}
+
+////////////////////////////////////////////////////////
+
+void RaycastOcclusionCull::add_buffer(RID p_buffer) {
+ ERR_FAIL_COND(buffers.has(p_buffer));
+ buffers[p_buffer] = RaycastHZBuffer();
+}
+
+void RaycastOcclusionCull::remove_buffer(RID p_buffer) {
+ ERR_FAIL_COND(!buffers.has(p_buffer));
+ buffers.erase(p_buffer);
+}
+
+void RaycastOcclusionCull::buffer_set_scenario(RID p_buffer, RID p_scenario) {
+ ERR_FAIL_COND(!buffers.has(p_buffer));
+ ERR_FAIL_COND(p_scenario.is_valid() && !scenarios.has(p_scenario));
+ buffers[p_buffer].scenario_rid = p_scenario;
+}
+
+void RaycastOcclusionCull::buffer_set_size(RID p_buffer, const Vector2i &p_size) {
+ ERR_FAIL_COND(!buffers.has(p_buffer));
+ buffers[p_buffer].resize(p_size);
+}
+
+void RaycastOcclusionCull::buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_pool) {
+ if (!buffers.has(p_buffer)) {
+ return;
+ }
+
+ RaycastHZBuffer &buffer = buffers[p_buffer];
+
+ if (buffer.is_empty() || !scenarios.has(buffer.scenario_rid)) {
+ return;
+ }
+
+ Scenario &scenario = scenarios[buffer.scenario_rid];
+
+ bool removed = scenario.update(p_thread_pool);
+
+ if (removed) {
+ scenarios.erase(buffer.scenario_rid);
+ return;
+ }
+
+ 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.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();
+}
+
+RaycastOcclusionCull::HZBuffer *RaycastOcclusionCull::buffer_get_ptr(RID p_buffer) {
+ if (!buffers.has(p_buffer)) {
+ return nullptr;
+ }
+ return &buffers[p_buffer];
+}
+
+RID RaycastOcclusionCull::buffer_get_debug_texture(RID p_buffer) {
+ ERR_FAIL_COND_V(!buffers.has(p_buffer), RID());
+ return buffers[p_buffer].get_debug_texture();
+}
+
+////////////////////////////////////////////////////////
+
+void RaycastOcclusionCull::set_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality) {
+ if (build_quality == p_quality) {
+ return;
+ }
+
+ build_quality = p_quality;
+
+ const RID *scenario_rid = nullptr;
+ while ((scenario_rid = scenarios.next(scenario_rid))) {
+ scenarios[*scenario_rid].dirty = true;
+ }
+}
+
+void RaycastOcclusionCull::_init_embree() {
+#ifdef __SSE2__
+ _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
+ _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
+#endif
+
+ String settings = vformat("threads=%d", MAX(1, OS::get_singleton()->get_processor_count() - 2));
+ ebr_device = rtcNewDevice(settings.utf8().ptr());
+}
+
+RaycastOcclusionCull::RaycastOcclusionCull() {
+ raycast_singleton = this;
+ int default_quality = GLOBAL_GET("rendering/occlusion_culling/bvh_build_quality");
+ build_quality = RS::ViewportOcclusionCullingBuildQuality(default_quality);
+}
+
+RaycastOcclusionCull::~RaycastOcclusionCull() {
+ const RID *scenario_rid = nullptr;
+ while ((scenario_rid = scenarios.next(scenario_rid))) {
+ Scenario &scenario = scenarios[*scenario_rid];
+ if (scenario.commit_thread) {
+ 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) {
+ rtcReleaseDevice(ebr_device);
+ }
+
+ raycast_singleton = nullptr;
+}
diff --git a/modules/raycast/raycast_occlusion_cull.h b/modules/raycast/raycast_occlusion_cull.h
new file mode 100644
index 0000000000..ea96df5ff6
--- /dev/null
+++ b/modules/raycast/raycast_occlusion_cull.h
@@ -0,0 +1,193 @@
+/*************************************************************************/
+/* raycast_occlusion_cull.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 OCCLUSION_CULL_RAYCASTER_H
+#define OCCLUSION_CULL_RAYCASTER_H
+
+#include "core/io/image.h"
+#include "core/math/camera_matrix.h"
+#include "core/object/object.h"
+#include "core/object/ref_counted.h"
+#include "core/templates/local_vector.h"
+#include "core/templates/rid_owner.h"
+#include "scene/resources/mesh.h"
+#include "servers/rendering/renderer_scene_occlusion_cull.h"
+
+#include <embree3/rtcore.h>
+
+class RaycastOcclusionCull : public RendererSceneOcclusionCull {
+ typedef RTCRayHit16 CameraRayTile;
+
+public:
+ class RaycastHZBuffer : public HZBuffer {
+ private:
+ Size2i tile_grid_size;
+
+ struct CameraRayThreadData {
+ 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, const CameraRayThreadData *p_data);
+ void _generate_camera_rays(const CameraRayThreadData *p_data, int p_from, int p_to);
+
+ public:
+ 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(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:
+ struct InstanceID {
+ RID scenario;
+ RID instance;
+
+ bool operator<(const InstanceID &rhs) const {
+ if (instance == rhs.instance) {
+ return rhs.scenario < scenario;
+ }
+ return instance < rhs.instance;
+ }
+
+ InstanceID() {}
+ InstanceID(RID s, RID i) :
+ scenario(s), instance(i) {}
+ };
+
+ struct Occluder {
+ PackedVector3Array vertices;
+ PackedInt32Array indices;
+ Set<InstanceID> users;
+ };
+
+ struct OccluderInstance {
+ RID occluder;
+ LocalVector<uint32_t> indices;
+ LocalVector<Vector3> xformed_vertices;
+ Transform3D xform;
+ bool enabled = true;
+ bool removed = false;
+ };
+
+ struct Scenario {
+ struct RaycastThreadData {
+ CameraRayTile *rays;
+ const uint32_t *masks;
+ };
+
+ struct TransformThreadData {
+ uint32_t thread_count;
+ uint32_t vertex_count;
+ Transform3D xform;
+ const Vector3 *read;
+ Vector3 *write;
+ };
+
+ Thread *commit_thread = nullptr;
+ bool commit_done = true;
+ bool dirty = false;
+ bool removed = false;
+
+ RTCScene ebr_scene[2] = { nullptr, nullptr };
+ int current_scene_idx = 0;
+
+ HashMap<RID, OccluderInstance> instances;
+ Set<RID> dirty_instances; // To avoid duplicates
+ LocalVector<RID> dirty_instances_array; // To iterate and split into threads
+ LocalVector<RID> removed_instances;
+
+ void _update_dirty_instance_thread(int p_idx, RID *p_instances);
+ void _update_dirty_instance(int p_idx, RID *p_instances, ThreadWorkPool *p_thread_pool);
+ void _transform_vertices_thread(uint32_t p_thread, TransformThreadData *p_data);
+ void _transform_vertices_range(const Vector3 *p_read, Vector3 *p_write, const Transform3D &p_xform, int p_from, int p_to);
+ static void _commit_scene(void *p_ud);
+ bool update(ThreadWorkPool &p_thread_pool);
+
+ void _raycast(uint32_t p_thread, const RaycastThreadData *p_raycast_data) 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;
+
+ static const int TILE_SIZE = 4;
+ static const int TILE_RAYS = TILE_SIZE * TILE_SIZE;
+
+ RTCDevice ebr_device = nullptr;
+ RID_PtrOwner<Occluder> occluder_owner;
+ HashMap<RID, Scenario> scenarios;
+ HashMap<RID, RaycastHZBuffer> buffers;
+ RS::ViewportOcclusionCullingBuildQuality build_quality;
+
+ void _init_embree();
+
+public:
+ virtual bool is_occluder(RID p_rid) override;
+ virtual RID occluder_allocate() override;
+ virtual void occluder_initialize(RID p_occluder) override;
+ virtual void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) override;
+ virtual void free_occluder(RID p_occluder) override;
+
+ virtual void add_scenario(RID p_scenario) override;
+ virtual void remove_scenario(RID p_scenario) override;
+ virtual void scenario_set_instance(RID p_scenario, RID p_instance, RID p_occluder, const Transform3D &p_xform, bool p_enabled) override;
+ virtual void scenario_remove_instance(RID p_scenario, RID p_instance) override;
+
+ virtual void add_buffer(RID p_buffer) override;
+ virtual void remove_buffer(RID p_buffer) override;
+ virtual HZBuffer *buffer_get_ptr(RID p_buffer) override;
+ virtual void buffer_set_scenario(RID p_buffer, RID p_scenario) override;
+ virtual void buffer_set_size(RID p_buffer, const Vector2i &p_size) override;
+ virtual void buffer_update(RID p_buffer, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, ThreadWorkPool &p_thread_pool) override;
+ virtual RID buffer_get_debug_texture(RID p_buffer) override;
+
+ virtual void set_build_quality(RS::ViewportOcclusionCullingBuildQuality p_quality) override;
+
+ RaycastOcclusionCull();
+ ~RaycastOcclusionCull();
+};
+
+#endif // OCCLUSION_CULL_RAYCASTER_H
diff --git a/modules/webm/register_types.cpp b/modules/raycast/register_types.cpp
index 6248787879..ed99e635e1 100644
--- a/modules/webm/register_types.cpp
+++ b/modules/raycast/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,18 +30,25 @@
#include "register_types.h"
-#include "video_stream_webm.h"
+#include "lightmap_raycaster.h"
+#include "raycast_occlusion_cull.h"
+#include "static_raycaster.h"
-static Ref<ResourceFormatLoaderWebm> resource_loader_webm;
+RaycastOcclusionCull *raycast_occlusion_cull = nullptr;
-void register_webm_types() {
- resource_loader_webm.instance();
- ResourceLoader::add_resource_format_loader(resource_loader_webm, true);
-
- ClassDB::register_class<VideoStreamWebm>();
+void register_raycast_types() {
+#ifdef TOOLS_ENABLED
+ LightmapRaycasterEmbree::make_default_raycaster();
+ StaticRaycasterEmbree::make_default_raycaster();
+#endif
+ raycast_occlusion_cull = memnew(RaycastOcclusionCull);
}
-void unregister_webm_types() {
- ResourceLoader::remove_resource_format_loader(resource_loader_webm);
- resource_loader_webm.unref();
+void unregister_raycast_types() {
+ if (raycast_occlusion_cull) {
+ memdelete(raycast_occlusion_cull);
+ }
+#ifdef TOOLS_ENABLED
+ StaticRaycasterEmbree::free();
+#endif
}
diff --git a/modules/etc/register_types.h b/modules/raycast/register_types.h
index 247c7213af..789604a491 100644
--- a/modules/etc/register_types.h
+++ b/modules/raycast/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,10 +28,5 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef ETC_REGISTER_TYPES_H
-#define ETC_REGISTER_TYPES_H
-
-void register_etc_types();
-void unregister_etc_types();
-
-#endif // ETC_REGISTER_TYPES_H
+void register_raycast_types();
+void unregister_raycast_types();
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/raycast/static_raycaster.h b/modules/raycast/static_raycaster.h
new file mode 100644
index 0000000000..6b13ecf690
--- /dev/null
+++ b/modules/raycast/static_raycaster.h
@@ -0,0 +1,64 @@
+/*************************************************************************/
+/* static_raycaster.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. */
+/*************************************************************************/
+
+#ifdef TOOLS_ENABLED
+
+#include "core/math/static_raycaster.h"
+
+#include <embree3/rtcore.h>
+
+class StaticRaycasterEmbree : public StaticRaycaster {
+ GDCLASS(StaticRaycasterEmbree, StaticRaycaster);
+
+private:
+ static RTCDevice embree_device;
+ RTCScene embree_scene;
+
+ Set<int> filter_meshes;
+
+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/SCsub b/modules/regex/SCsub
index 2afacc1d9c..deb9db7591 100644
--- a/modules/regex/SCsub
+++ b/modules/regex/SCsub
@@ -5,6 +5,10 @@ Import("env_modules")
env_regex = env_modules.Clone()
+# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_pcre2"]:
thirdparty_dir = "#thirdparty/pcre2/src/"
thirdparty_flags = ["PCRE2_STATIC", "HAVE_CONFIG_H", "SUPPORT_UNICODE"]
@@ -52,11 +56,21 @@ if env["builtin_pcre2"]:
env_pcre2 = env_regex.Clone()
env_pcre2.disable_warnings()
env_pcre2["OBJSUFFIX"] = "_" + width + env_pcre2["OBJSUFFIX"]
- env_pcre2.add_source_files(env.modules_sources, thirdparty_sources)
env_pcre2.Append(CPPDEFINES=[("PCRE2_CODE_UNIT_WIDTH", width)])
+ env_pcre2.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
pcre2_builtin("16")
pcre2_builtin("32")
+
+# Godot source files
+
+module_obj = []
+
env_regex.Append(CPPDEFINES=[("PCRE2_CODE_UNIT_WIDTH", 0)])
-env_regex.add_source_files(env.modules_sources, "*.cpp")
+env_regex.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/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 312275842a..2ae2e53b02 100644
--- a/modules/regex/doc_classes/RegEx.xml
+++ b/modules/regex/doc_classes/RegEx.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="RegEx" inherits="Reference" version="4.0">
+<class name="RegEx" inherits="RefCounted" version="4.0">
<brief_description>
Class for searching text for patterns using regular expressions.
</brief_description>
@@ -39,8 +39,8 @@
var regex = RegEx.new()
regex.compile("\\S+") # Negated whitespace character class.
var results = []
- for match in regex.search_all("One Two \n\tThree"):
- results.push_back(match.get_string())
+ for result in regex.search_all("One Two \n\tThree"):
+ results.push_back(result.get_string())
# The `results` array now contains "One", "Two", "Three".
[/codeblock]
[b]Note:[/b] Godot's regex implementation is based on the [url=https://www.pcre.org/]PCRE2[/url] library. You can view the full pattern reference [url=https://www.pcre.org/current/doc/html/pcre2pattern.html]here[/url].
@@ -50,93 +50,70 @@
</tutorials>
<methods>
<method name="clear">
- <return type="void">
- </return>
+ <return type="void" />
<description>
This method resets the state of the object, as if it was freshly created. Namely, it unassigns the regular expression of this object.
</description>
</method>
<method name="compile">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="pattern" type="String">
- </argument>
+ <return type="int" enum="Error" />
+ <argument index="0" name="pattern" type="String" />
<description>
Compiles and assign the search pattern to use. Returns [constant OK] if the compilation is successful. If an error is encountered, details are printed to standard output and an error is returned.
</description>
</method>
<method name="get_group_count" qualifiers="const">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Returns the number of capturing groups in compiled pattern.
</description>
</method>
<method name="get_names" qualifiers="const">
- <return type="Array">
- </return>
+ <return type="Array" />
<description>
Returns an array of names of named capturing groups in the compiled pattern. They are ordered by appearance.
</description>
</method>
<method name="get_pattern" qualifiers="const">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Returns the original search pattern that was compiled.
</description>
</method>
<method name="is_valid" qualifiers="const">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns whether this object has a valid search pattern assigned.
</description>
</method>
<method name="search" qualifiers="const">
- <return type="RegExMatch">
- </return>
- <argument index="0" name="subject" type="String">
- </argument>
- <argument index="1" name="offset" type="int" default="0">
- </argument>
- <argument index="2" name="end" type="int" default="-1">
- </argument>
+ <return type="RegExMatch" />
+ <argument index="0" name="subject" type="String" />
+ <argument index="1" name="offset" type="int" default="0" />
+ <argument index="2" name="end" type="int" default="-1" />
<description>
Searches the text for the compiled pattern. Returns a [RegExMatch] container of the first matching result if found, otherwise [code]null[/code]. The region to search within can be specified without modifying where the start and end anchor would be.
</description>
</method>
<method name="search_all" qualifiers="const">
- <return type="Array">
- </return>
- <argument index="0" name="subject" type="String">
- </argument>
- <argument index="1" name="offset" type="int" default="0">
- </argument>
- <argument index="2" name="end" type="int" default="-1">
- </argument>
+ <return type="Array" />
+ <argument index="0" name="subject" type="String" />
+ <argument index="1" name="offset" type="int" default="0" />
+ <argument index="2" name="end" type="int" default="-1" />
<description>
Searches the text for the compiled pattern. Returns an array of [RegExMatch] containers for each non-overlapping result. If no results were found, an empty array is returned instead. The region to search within can be specified without modifying where the start and end anchor would be.
</description>
</method>
<method name="sub" qualifiers="const">
- <return type="String">
- </return>
- <argument index="0" name="subject" type="String">
- </argument>
- <argument index="1" name="replacement" type="String">
- </argument>
- <argument index="2" name="all" type="bool" default="false">
- </argument>
- <argument index="3" name="offset" type="int" default="0">
- </argument>
- <argument index="4" name="end" type="int" default="-1">
- </argument>
+ <return type="String" />
+ <argument index="0" name="subject" type="String" />
+ <argument index="1" name="replacement" type="String" />
+ <argument index="2" name="all" type="bool" default="false" />
+ <argument index="3" name="offset" type="int" default="0" />
+ <argument index="4" name="end" type="int" default="-1" />
<description>
Searches the text for the compiled pattern and replaces it with the specified string. Escapes and backreferences such as [code]$1[/code] and [code]$name[/code] are expanded and resolved. By default, only the first instance is replaced, but it can be changed for all instances (global replacement). The region to search within can be specified without modifying where the start and end anchor would be.
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/regex/doc_classes/RegExMatch.xml b/modules/regex/doc_classes/RegExMatch.xml
index a45de60aef..20680b41fd 100644
--- a/modules/regex/doc_classes/RegExMatch.xml
+++ b/modules/regex/doc_classes/RegExMatch.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="RegExMatch" inherits="Reference" version="4.0">
+<class name="RegExMatch" inherits="RefCounted" version="4.0">
<brief_description>
Contains the results of a [RegEx] search.
</brief_description>
@@ -10,37 +10,30 @@
</tutorials>
<methods>
<method name="get_end" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="name" type="Variant" default="0">
- </argument>
+ <return type="int" />
+ <argument index="0" name="name" type="Variant" default="0" />
<description>
Returns the end position of the match within the source string. The end position of capturing groups can be retrieved by providing its group number as an integer or its string name (if it's a named group). The default value of 0 refers to the whole pattern.
Returns -1 if the group did not match or doesn't exist.
</description>
</method>
<method name="get_group_count" qualifiers="const">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Returns the number of capturing groups.
</description>
</method>
<method name="get_start" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="name" type="Variant" default="0">
- </argument>
+ <return type="int" />
+ <argument index="0" name="name" type="Variant" default="0" />
<description>
Returns the starting position of the match within the source string. The starting position of capturing groups can be retrieved by providing its group number as an integer or its string name (if it's a named group). The default value of 0 refers to the whole pattern.
Returns -1 if the group did not match or doesn't exist.
</description>
</method>
<method name="get_string" qualifiers="const">
- <return type="String">
- </return>
- <argument index="0" name="name" type="Variant" default="0">
- </argument>
+ <return type="String" />
+ <argument index="0" name="name" type="Variant" default="0" />
<description>
Returns the substring of the match from the source string. Capturing groups can be retrieved by providing its group number as an integer or its string name (if it's a named group). The default value of 0 refers to the whole pattern.
Returns an empty string if the group did not match or doesn't exist.
@@ -51,13 +44,11 @@
<member name="names" type="Dictionary" setter="" getter="get_names" default="{}">
A dictionary of named groups and its corresponding group number. Only groups that were matched are included. If multiple groups have the same name, that name would refer to the first matching one.
</member>
- <member name="strings" type="Array" setter="" getter="get_strings" default="[ ]">
+ <member name="strings" type="Array" setter="" getter="get_strings" default="[]">
An [Array] of the match and its capturing groups.
</member>
<member name="subject" type="String" setter="" getter="get_subject" default="&quot;&quot;">
The source string used with the search pattern to find this matching result.
</member>
</members>
- <constants>
- </constants>
</class>
diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp
index c10a276eae..6bae12e7e6 100644
--- a/modules/regex/regex.cpp
+++ b/modules/regex/regex.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -365,12 +365,10 @@ Array RegEx::get_names() const {
RegEx::RegEx() {
general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr);
- code = nullptr;
}
RegEx::RegEx(const String &p_pattern) {
general_ctx = pcre2_general_context_create_32(&_regex_malloc, &_regex_free, nullptr);
- code = nullptr;
compile(p_pattern);
}
diff --git a/modules/regex/regex.h b/modules/regex/regex.h
index 52b49c783e..68fe2bc6e0 100644
--- a/modules/regex/regex.h
+++ b/modules/regex/regex.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,19 +31,19 @@
#ifndef REGEX_H
#define REGEX_H
-#include "core/array.h"
-#include "core/dictionary.h"
-#include "core/map.h"
-#include "core/reference.h"
-#include "core/ustring.h"
-#include "core/vector.h"
+#include "core/object/ref_counted.h"
+#include "core/string/ustring.h"
+#include "core/templates/map.h"
+#include "core/templates/vector.h"
+#include "core/variant/array.h"
+#include "core/variant/dictionary.h"
-class RegExMatch : public Reference {
- GDCLASS(RegExMatch, Reference);
+class RegExMatch : public RefCounted {
+ GDCLASS(RegExMatch, RefCounted);
struct Range {
- int start;
- int end;
+ int start = 0;
+ int end = 0;
};
String subject;
@@ -68,11 +68,11 @@ public:
int get_end(const Variant &p_name) const;
};
-class RegEx : public Reference {
- GDCLASS(RegEx, Reference);
+class RegEx : public RefCounted {
+ GDCLASS(RegEx, RefCounted);
void *general_ctx;
- void *code;
+ void *code = nullptr;
String pattern;
void _pattern_info(uint32_t what, void *where) const;
@@ -83,7 +83,6 @@ protected:
public:
void clear();
Error compile(const String &p_pattern);
- void _init(const String &p_pattern = "");
Ref<RegExMatch> search(const String &p_subject, int p_offset = 0, int p_end = -1) const;
Array search_all(const String &p_subject, int p_offset = 0, int p_end = -1) const;
diff --git a/modules/regex/register_types.cpp b/modules/regex/register_types.cpp
index 5d4aeba2d7..03957f88cf 100644
--- a/modules/regex/register_types.cpp
+++ b/modules/regex/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,12 +29,12 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/class_db.h"
+#include "core/object/class_db.h"
#include "regex.h"
void register_regex_types() {
- ClassDB::register_class<RegExMatch>();
- ClassDB::register_class<RegEx>();
+ GDREGISTER_CLASS(RegExMatch);
+ GDREGISTER_CLASS(RegEx);
}
void unregister_regex_types() {
diff --git a/modules/regex/register_types.h b/modules/regex/register_types.h
index cf377cdf5f..fe94cde954 100644
--- a/modules/regex/register_types.h
+++ b/modules/regex/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/regex/tests/test_regex.h b/modules/regex/tests/test_regex.h
new file mode 100644
index 0000000000..c2d303b435
--- /dev/null
+++ b/modules/regex/tests/test_regex.h
@@ -0,0 +1,164 @@
+/*************************************************************************/
+/* test_regex.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 TEST_REGEX_H
+#define TEST_REGEX_H
+
+#include "core/string/ustring.h"
+#include "modules/regex/regex.h"
+
+#include "tests/test_macros.h"
+
+namespace TestRegEx {
+
+TEST_CASE("[RegEx] Initialization") {
+ const String pattern = "(?<vowel>[aeiou])";
+
+ RegEx re1(pattern);
+ CHECK(re1.is_valid());
+ CHECK(re1.get_pattern() == pattern);
+ CHECK(re1.get_group_count() == 1);
+
+ Array names = re1.get_names();
+ CHECK(names.size() == 1);
+ CHECK(names[0] == "vowel");
+
+ RegEx re2;
+ CHECK(re2.is_valid() == false);
+ CHECK(re2.compile(pattern) == OK);
+ CHECK(re2.is_valid());
+
+ CHECK(re1.get_pattern() == re2.get_pattern());
+ CHECK(re1.get_group_count() == re2.get_group_count());
+
+ names = re2.get_names();
+ CHECK(names.size() == 1);
+ CHECK(names[0] == "vowel");
+}
+
+TEST_CASE("[RegEx] Clearing") {
+ RegEx re("Godot");
+ REQUIRE(re.is_valid());
+ re.clear();
+ CHECK(re.is_valid() == false);
+}
+
+TEST_CASE("[RegEx] Searching") {
+ const String s = "Searching";
+ const String vowels = "[aeiou]{1,2}";
+ const String numerics = "\\d";
+
+ RegEx re(vowels);
+ REQUIRE(re.is_valid());
+
+ Ref<RegExMatch> match = re.search(s);
+ REQUIRE(match != nullptr);
+ CHECK(match->get_string(0) == "ea");
+
+ match = re.search(s, 2, 4);
+ REQUIRE(match != nullptr);
+ CHECK(match->get_string(0) == "a");
+
+ const Array all_results = re.search_all(s);
+ CHECK(all_results.size() == 2);
+ match = all_results[0];
+ REQUIRE(match != nullptr);
+ CHECK(match->get_string(0) == "ea");
+ match = all_results[1];
+ REQUIRE(match != nullptr);
+ CHECK(match->get_string(0) == "i");
+
+ CHECK(re.compile(numerics) == OK);
+ CHECK(re.is_valid());
+ CHECK(re.search(s) == nullptr);
+ CHECK(re.search_all(s).size() == 0);
+}
+
+TEST_CASE("[RegEx] Substitution") {
+ String s = "Double all the vowels.";
+
+ RegEx re("(?<vowel>[aeiou])");
+ REQUIRE(re.is_valid());
+ CHECK(re.sub(s, "$0$vowel", true) == "Doouublee aall thee vooweels.");
+}
+
+TEST_CASE("[RegEx] Uninitialized use") {
+ const String s = "Godot";
+
+ RegEx re;
+ ERR_PRINT_OFF;
+ CHECK(re.search(s) == nullptr);
+ CHECK(re.search_all(s).size() == 0);
+ CHECK(re.sub(s, "") == "");
+ CHECK(re.get_group_count() == 0);
+ CHECK(re.get_names().size() == 0);
+ ERR_PRINT_ON
+}
+
+TEST_CASE("[RegEx] Empty Pattern") {
+ const String s = "Godot";
+
+ RegEx re;
+ CHECK(re.compile("") == OK);
+ CHECK(re.is_valid());
+}
+
+TEST_CASE("[RegEx] Invalid offset") {
+ const String s = "Godot";
+
+ RegEx re("o");
+ REQUIRE(re.is_valid());
+ CHECK(re.search(s, -1) == nullptr);
+ CHECK(re.search_all(s, -1).size() == 0);
+ CHECK(re.sub(s, "", true, -1) == "");
+}
+
+TEST_CASE("[RegEx] Invalid end position") {
+ const String s = "Godot";
+
+ RegEx re("o");
+ REQUIRE(re.is_valid());
+ Ref<RegExMatch> match = re.search(s, 0, 10);
+ CHECK(match->get_string(0) == "o");
+
+ const Array all_results = re.search_all(s, 0, 10);
+ CHECK(all_results.size() == 2);
+ match = all_results[0];
+ REQUIRE(match != nullptr);
+ CHECK(match->get_string(0) == String("o"));
+ match = all_results[1];
+ REQUIRE(match != nullptr);
+ CHECK(match->get_string(0) == String("o"));
+
+ CHECK(re.sub(s, "", true, 0, 10) == "Gdt");
+}
+} // namespace TestRegEx
+
+#endif // TEST_REGEX_H
diff --git a/modules/register_module_types.h b/modules/register_module_types.h
index acd9fc7c97..2cff8c54c4 100644
--- a/modules/register_module_types.h
+++ b/modules/register_module_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/squish/SCsub b/modules/squish/SCsub
index b31032403f..c9e29911d8 100644
--- a/modules/squish/SCsub
+++ b/modules/squish/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_squish = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_squish"]:
thirdparty_dir = "#thirdparty/squish/"
thirdparty_sources = [
@@ -26,7 +29,16 @@ if env["builtin_squish"]:
env_thirdparty = env_squish.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_squish.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_squish.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/squish/image_compress_squish.h b/modules/squish/image_compress_squish.h
deleted file mode 100644
index 19e6d57474..0000000000
--- a/modules/squish/image_compress_squish.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*************************************************************************/
-/* image_compress_squish.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 IMAGE_COMPRESS_SQUISH_H
-#define IMAGE_COMPRESS_SQUISH_H
-
-#include "core/image.h"
-
-void image_compress_squish(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels);
-void image_decompress_squish(Image *p_image);
-
-#endif // IMAGE_COMPRESS_SQUISH_H
diff --git a/modules/squish/image_compress_squish.cpp b/modules/squish/image_decompress_squish.cpp
index c510779317..1450b0fe88 100644
--- a/modules/squish/image_compress_squish.cpp
+++ b/modules/squish/image_decompress_squish.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* image_compress_squish.cpp */
+/* image_decompress_squish.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "image_compress_squish.h"
+#include "image_decompress_squish.h"
#include <squish.h>
@@ -76,83 +76,3 @@ void image_decompress_squish(Image *p_image) {
p_image->convert_ra_rgba8_to_rg();
}
}
-
-void image_compress_squish(Image *p_image, float p_lossy_quality, Image::UsedChannels p_channels) {
- if (p_image->get_format() >= Image::FORMAT_DXT1) {
- return; //do not compress, already compressed
- }
-
- int w = p_image->get_width();
- int h = p_image->get_height();
-
- if (p_image->get_format() <= Image::FORMAT_RGBA8) {
- int squish_comp = squish::kColourRangeFit;
-
- if (p_lossy_quality > 0.85) {
- squish_comp = squish::kColourIterativeClusterFit;
- } else if (p_lossy_quality > 0.75) {
- squish_comp = squish::kColourClusterFit;
- }
-
- Image::Format target_format = Image::FORMAT_RGBA8;
-
- p_image->convert(Image::FORMAT_RGBA8); //still uses RGBA to convert
-
- switch (p_channels) {
- case Image::USED_CHANNELS_L: {
- target_format = Image::FORMAT_DXT1;
- squish_comp |= squish::kDxt1;
- } break;
- case Image::USED_CHANNELS_LA: {
- target_format = Image::FORMAT_DXT5;
- squish_comp |= squish::kDxt5;
- } break;
- case Image::USED_CHANNELS_R: {
- target_format = Image::FORMAT_RGTC_R;
- squish_comp |= squish::kBc4;
- } break;
- case Image::USED_CHANNELS_RG: {
- target_format = Image::FORMAT_RGTC_RG;
- squish_comp |= squish::kBc5;
- } break;
- case Image::USED_CHANNELS_RGB: {
- target_format = Image::FORMAT_DXT1;
- squish_comp |= squish::kDxt1;
- } break;
- case Image::USED_CHANNELS_RGBA: {
- //TODO, should convert both, then measure which one does a better job
- target_format = Image::FORMAT_DXT5;
- squish_comp |= squish::kDxt5;
-
- } break;
- default: {
- ERR_PRINT("Unknown image format, defaulting to RGBA8");
- break;
- }
- }
-
- Vector<uint8_t> data;
- int target_size = Image::get_image_data_size(w, h, target_format, p_image->has_mipmaps());
- int mm_count = p_image->has_mipmaps() ? Image::get_image_required_mipmaps(w, h, target_format) : 0;
- data.resize(target_size);
- int shift = Image::get_format_pixel_rshift(target_format);
-
- const uint8_t *rb = p_image->get_data().ptr();
- uint8_t *wb = data.ptrw();
-
- int dst_ofs = 0;
-
- for (int i = 0; i <= mm_count; i++) {
- int bw = w % 4 != 0 ? w + (4 - w % 4) : w;
- int bh = h % 4 != 0 ? h + (4 - h % 4) : h;
-
- int src_ofs = p_image->get_mipmap_offset(i);
- squish::CompressImage(&rb[src_ofs], w, h, &wb[dst_ofs], squish_comp);
- dst_ofs += (MAX(4, bw) * MAX(4, bh)) >> shift;
- w = MAX(w / 2, 1);
- h = MAX(h / 2, 1);
- }
-
- p_image->create(p_image->get_width(), p_image->get_height(), p_image->has_mipmaps(), target_format, data);
- }
-}
diff --git a/modules/squish/image_decompress_squish.h b/modules/squish/image_decompress_squish.h
new file mode 100644
index 0000000000..fff5839ac4
--- /dev/null
+++ b/modules/squish/image_decompress_squish.h
@@ -0,0 +1,38 @@
+/*************************************************************************/
+/* image_decompress_squish.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 IMAGE_DECOMPRESS_SQUISH_H
+#define IMAGE_DECOMPRESS_SQUISH_H
+
+#include "core/io/image.h"
+
+void image_decompress_squish(Image *p_image);
+
+#endif // IMAGE_DECOMPRESS_SQUISH_H
diff --git a/modules/squish/register_types.cpp b/modules/squish/register_types.cpp
index ad28aff058..51aab040e7 100644
--- a/modules/squish/register_types.cpp
+++ b/modules/squish/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,10 +29,10 @@
/*************************************************************************/
#include "register_types.h"
-#include "image_compress_squish.h"
+
+#include "image_decompress_squish.h"
void register_squish_types() {
- Image::set_compress_bc_func(image_compress_squish);
Image::_image_decompress_bc = image_decompress_squish;
}
diff --git a/modules/squish/register_types.h b/modules/squish/register_types.h
index ab56c54d4a..0f87d64333 100644
--- a/modules/squish/register_types.h
+++ b/modules/squish/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/stb_vorbis/SCsub b/modules/stb_vorbis/SCsub
deleted file mode 100644
index 266c87c802..0000000000
--- a/modules/stb_vorbis/SCsub
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-env_stb_vorbis = env_modules.Clone()
-
-# Thirdparty source files
-thirdparty_sources = ["#thirdparty/misc/stb_vorbis.c"]
-
-env_thirdparty = env_stb_vorbis.Clone()
-env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
-
-# Godot's own source files
-env_stb_vorbis.add_source_files(env.modules_sources, "*.cpp")
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 346833ab9c..0000000000
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
-/*************************************************************************/
-/* audio_stream_ogg_vorbis.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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/os/file_access.h"
-
-void AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
- ERR_FAIL_COND(!active);
-
- int todo = p_frames;
-
- int start_buffer = 0;
-
- 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 < 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 {
- for (int i = p_frames - todo; i < p_frames; i++) {
- p_buffer[i] = AudioFrame(0, 0);
- }
- active = false;
- todo = 0;
- }
- }
- }
-}
-
-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(data == nullptr, ovs);
-
- ovs.instance();
- 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);
- copymem(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();
- copymem(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() {
- data = nullptr;
- data_len = 0;
- length = 0;
- sample_rate = 1;
- channels = 1;
- loop_offset = 0;
- decode_mem_size = 0;
- loop = false;
-}
-
-AudioStreamOGGVorbis::~AudioStreamOGGVorbis() {
- clear_data();
-}
diff --git a/modules/stb_vorbis/register_types.h b/modules/stb_vorbis/register_types.h
deleted file mode 100644
index f5a1dd31bc..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-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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/svg/SCsub b/modules/svg/SCsub
index 0bfba34fe5..c7228a8d0b 100644
--- a/modules/svg/SCsub
+++ b/modules/svg/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_svg = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
thirdparty_dir = "#thirdparty/nanosvg/"
thirdparty_sources = [
"nanosvg.cc",
@@ -16,7 +19,15 @@ env_svg.Prepend(CPPPATH=[thirdparty_dir])
env_thirdparty = env_svg.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_svg.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Godot's own source files
-env_svg.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index 8ca4452ac9..4911346b96 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -140,7 +140,7 @@ Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, const char *p
}
Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
- uint32_t size = f->get_len();
+ uint64_t size = f->get_length();
Vector<uint8_t> src_image;
src_image.resize(size + 1);
uint8_t *src_w = src_image.ptrw();
diff --git a/modules/svg/image_loader_svg.h b/modules/svg/image_loader_svg.h
index ecaba5052b..e64175b172 100644
--- a/modules/svg/image_loader_svg.h
+++ b/modules/svg/image_loader_svg.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define IMAGE_LOADER_SVG_H
#include "core/io/image_loader.h"
-#include "core/ustring.h"
+#include "core/string/ustring.h"
/**
@author Daniel Ramirez <djrmuv@gmail.com>
diff --git a/modules/svg/register_types.cpp b/modules/svg/register_types.cpp
index 9fbefd2cfe..1a611184d2 100644
--- a/modules/svg/register_types.cpp
+++ b/modules/svg/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/svg/register_types.h b/modules/svg/register_types.h
index a3d914e0cb..106ac9056f 100644
--- a/modules/svg/register_types.h
+++ b/modules/svg/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
new file mode 100644
index 0000000000..d6a96282f3
--- /dev/null
+++ b/modules/text_server_adv/SCsub
@@ -0,0 +1,533 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_text_server_adv = env_modules.Clone()
+
+
+def make_icu_data(target, source, env):
+
+ dst = target[0].srcnode().abspath
+
+ g = open(dst, "w", encoding="utf-8")
+
+ g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n")
+ g.write("/* (C) 2016 and later: Unicode, Inc. and others. */\n")
+ g.write("/* License & terms of use: https://www.unicode.org/copyright.html */\n")
+ g.write("#ifndef _ICU_DATA_H\n")
+ g.write("#define _ICU_DATA_H\n")
+ g.write('#include "unicode/utypes.h"\n')
+ g.write('#include "unicode/udata.h"\n')
+ g.write('#include "unicode/uversion.h"\n')
+
+ f = open(source[0].srcnode().abspath, "rb")
+ buf = f.read()
+
+ 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')
+ for i in range(len(buf)):
+ g.write("\t" + str(buf[i]) + ",\n")
+
+ g.write("};\n")
+ g.write("#endif")
+
+
+# Thirdparty source files
+
+thirdparty_obj = []
+freetype_enabled = env.module_check_dependencies("text_server_adv", ["freetype"], True)
+msdngen_enabled = env.module_check_dependencies("text_server_adv", ["msdfgen"], True)
+
+if env["builtin_harfbuzz"]:
+ env_harfbuzz = env_modules.Clone()
+ env_harfbuzz.disable_warnings()
+
+ thirdparty_dir = "#thirdparty/harfbuzz/"
+ thirdparty_sources = [
+ "src/hb-aat-layout.cc",
+ "src/hb-aat-map.cc",
+ "src/hb-blob.cc",
+ "src/hb-buffer-serialize.cc",
+ "src/hb-buffer.cc",
+ "src/hb-common.cc",
+ #'src/hb-coretext.cc',
+ #'src/hb-directwrite.cc',
+ "src/hb-draw.cc",
+ "src/hb-face.cc",
+ "src/hb-fallback-shape.cc",
+ "src/hb-font.cc",
+ #'src/hb-gdi.cc',
+ #'src/hb-glib.cc',
+ #'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",
+ "src/hb-ot-color.cc",
+ "src/hb-ot-face.cc",
+ "src/hb-ot-font.cc",
+ "src/hb-ot-layout.cc",
+ "src/hb-ot-map.cc",
+ "src/hb-ot-math.cc",
+ "src/hb-ot-meta.cc",
+ "src/hb-ot-metrics.cc",
+ "src/hb-ot-name.cc",
+ "src/hb-ot-shape-complex-arabic.cc",
+ "src/hb-ot-shape-complex-default.cc",
+ "src/hb-ot-shape-complex-hangul.cc",
+ "src/hb-ot-shape-complex-hebrew.cc",
+ "src/hb-ot-shape-complex-indic-table.cc",
+ "src/hb-ot-shape-complex-indic.cc",
+ "src/hb-ot-shape-complex-khmer.cc",
+ "src/hb-ot-shape-complex-myanmar.cc",
+ "src/hb-ot-shape-complex-syllabic.cc",
+ "src/hb-ot-shape-complex-thai.cc",
+ "src/hb-ot-shape-complex-use.cc",
+ "src/hb-ot-shape-complex-vowel-constraints.cc",
+ "src/hb-ot-shape-fallback.cc",
+ "src/hb-ot-shape-normalize.cc",
+ "src/hb-ot-shape.cc",
+ "src/hb-ot-tag.cc",
+ "src/hb-ot-var.cc",
+ "src/hb-set.cc",
+ "src/hb-shape-plan.cc",
+ "src/hb-shape.cc",
+ "src/hb-shaper.cc",
+ "src/hb-static.cc",
+ "src/hb-style.cc",
+ "src/hb-subset-cff-common.cc",
+ "src/hb-subset-cff1.cc",
+ "src/hb-subset-cff2.cc",
+ "src/hb-subset-input.cc",
+ "src/hb-subset-plan.cc",
+ "src/hb-subset.cc",
+ "src/hb-ucd.cc",
+ "src/hb-unicode.cc",
+ #'src/hb-uniscribe.cc'
+ ]
+
+ if freetype_enabled:
+ thirdparty_sources += [
+ "src/hb-ft.cc",
+ "src/hb-graphite2.cc",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_harfbuzz.Append(
+ CPPPATH=[
+ "#thirdparty/harfbuzz/src",
+ "#thirdparty/icu4c/common/",
+ ]
+ )
+
+ if freetype_enabled:
+ env_harfbuzz.Append(
+ CPPPATH=[
+ "#thirdparty/freetype/include",
+ "#thirdparty/graphite/include",
+ ]
+ )
+
+ if env["platform"] == "android" or env["platform"] == "linuxbsd":
+ env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"])
+
+ if env["platform"] == "javascript":
+ if env["threads_enabled"]:
+ env_harfbuzz.Append(CCFLAGS=["-DHAVE_PTHREAD"])
+ else:
+ env_harfbuzz.Append(CCFLAGS=["-DHB_NO_MT"])
+
+ env_harfbuzz.Append(
+ CCFLAGS=[
+ "-DHAVE_ICU_BUILTIN",
+ "-DHAVE_ICU",
+ ]
+ )
+
+ if freetype_enabled:
+ env_harfbuzz.Append(
+ CCFLAGS=[
+ "-DHAVE_FREETYPE",
+ "-DHAVE_GRAPHITE2",
+ "-DGRAPHITE2_STATIC",
+ ]
+ )
+
+ lib = env_harfbuzz.add_library("harfbuzz_builtin", thirdparty_sources)
+ thirdparty_obj += lib
+
+ # Needs to be appended to arrive after libscene in the linker call,
+ # but we don't want it to arrive *after* system libs, so manual hack
+ # LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
+ # and then plain strings for system library. We insert between the two.
+ inserted = False
+ for idx, linklib in enumerate(env["LIBS"]):
+ if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object
+ env["LIBS"].insert(idx, lib)
+ inserted = True
+ break
+ if not inserted:
+ env.Append(LIBS=[lib])
+
+
+if env["builtin_graphite"] and freetype_enabled:
+ env_graphite = env_modules.Clone()
+ env_graphite.disable_warnings()
+
+ thirdparty_dir = "#thirdparty/graphite/"
+ thirdparty_sources = [
+ "src/gr_char_info.cpp",
+ "src/gr_face.cpp",
+ "src/gr_features.cpp",
+ "src/gr_font.cpp",
+ "src/gr_logging.cpp",
+ "src/gr_segment.cpp",
+ "src/gr_slot.cpp",
+ "src/CmapCache.cpp",
+ "src/Code.cpp",
+ "src/Collider.cpp",
+ "src/Decompressor.cpp",
+ "src/Face.cpp",
+ #'src/FileFace.cpp',
+ "src/FeatureMap.cpp",
+ "src/Font.cpp",
+ "src/GlyphCache.cpp",
+ "src/GlyphFace.cpp",
+ "src/Intervals.cpp",
+ "src/Justifier.cpp",
+ "src/NameTable.cpp",
+ "src/Pass.cpp",
+ "src/Position.cpp",
+ "src/Segment.cpp",
+ "src/Silf.cpp",
+ "src/Slot.cpp",
+ "src/Sparse.cpp",
+ "src/TtfUtil.cpp",
+ "src/UtfCodec.cpp",
+ "src/FileFace.cpp",
+ "src/json.cpp",
+ ]
+ if not env_graphite.msvc:
+ thirdparty_sources += ["src/direct_machine.cpp"]
+ else:
+ thirdparty_sources += ["src/call_machine.cpp"]
+
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ env_graphite.Append(CPPPATH=["#thirdparty/graphite/src", "#thirdparty/graphite/include"])
+ env_graphite.Append(
+ CCFLAGS=[
+ "-DGRAPHITE2_STATIC",
+ "-DGRAPHITE2_NTRACING",
+ "-DGRAPHITE2_NFILEFACE",
+ ]
+ )
+
+ lib = env_graphite.add_library("graphite_builtin", thirdparty_sources)
+ thirdparty_obj += lib
+
+ # Needs to be appended to arrive after libscene in the linker call,
+ # but we don't want it to arrive *after* system libs, so manual hack
+ # LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
+ # and then plain strings for system library. We insert between the two.
+ inserted = False
+ for idx, linklib in enumerate(env["LIBS"]):
+ if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object
+ env["LIBS"].insert(idx, lib)
+ inserted = True
+ break
+ if not inserted:
+ env.Append(LIBS=[lib])
+
+
+if env["builtin_icu"]:
+ env_icu = env_modules.Clone()
+ env_icu.disable_warnings()
+
+ thirdparty_dir = "#thirdparty/icu4c/"
+ thirdparty_sources = [
+ "common/appendable.cpp",
+ "common/bmpset.cpp",
+ "common/brkeng.cpp",
+ "common/brkiter.cpp",
+ "common/bytesinkutil.cpp",
+ "common/bytestream.cpp",
+ "common/bytestrie.cpp",
+ "common/bytestriebuilder.cpp",
+ "common/bytestrieiterator.cpp",
+ "common/caniter.cpp",
+ "common/characterproperties.cpp",
+ "common/chariter.cpp",
+ "common/charstr.cpp",
+ "common/cmemory.cpp",
+ "common/cstr.cpp",
+ "common/cstring.cpp",
+ "common/cwchar.cpp",
+ "common/dictbe.cpp",
+ "common/dictionarydata.cpp",
+ "common/dtintrv.cpp",
+ "common/edits.cpp",
+ "common/emojiprops.cpp",
+ "common/errorcode.cpp",
+ "common/filteredbrk.cpp",
+ "common/filterednormalizer2.cpp",
+ "common/icudataver.cpp",
+ "common/icuplug.cpp",
+ "common/loadednormalizer2impl.cpp",
+ "common/localebuilder.cpp",
+ "common/localematcher.cpp",
+ "common/localeprioritylist.cpp",
+ "common/locavailable.cpp",
+ "common/locbased.cpp",
+ "common/locdispnames.cpp",
+ "common/locdistance.cpp",
+ "common/locdspnm.cpp",
+ "common/locid.cpp",
+ "common/loclikely.cpp",
+ "common/loclikelysubtags.cpp",
+ "common/locmap.cpp",
+ "common/locresdata.cpp",
+ "common/locutil.cpp",
+ "common/lsr.cpp",
+ "common/lstmbe.cpp",
+ "common/messagepattern.cpp",
+ "common/normalizer2.cpp",
+ "common/normalizer2impl.cpp",
+ "common/normlzr.cpp",
+ "common/parsepos.cpp",
+ "common/patternprops.cpp",
+ "common/pluralmap.cpp",
+ "common/propname.cpp",
+ "common/propsvec.cpp",
+ "common/punycode.cpp",
+ "common/putil.cpp",
+ "common/rbbi.cpp",
+ "common/rbbi_cache.cpp",
+ "common/rbbidata.cpp",
+ "common/rbbinode.cpp",
+ "common/rbbirb.cpp",
+ "common/rbbiscan.cpp",
+ "common/rbbisetb.cpp",
+ "common/rbbistbl.cpp",
+ "common/rbbitblb.cpp",
+ "common/resbund.cpp",
+ "common/resbund_cnv.cpp",
+ "common/resource.cpp",
+ "common/restrace.cpp",
+ "common/ruleiter.cpp",
+ "common/schriter.cpp",
+ "common/serv.cpp",
+ "common/servlk.cpp",
+ "common/servlkf.cpp",
+ "common/servls.cpp",
+ "common/servnotf.cpp",
+ "common/servrbf.cpp",
+ "common/servslkf.cpp",
+ "common/sharedobject.cpp",
+ "common/simpleformatter.cpp",
+ "common/static_unicode_sets.cpp",
+ "common/stringpiece.cpp",
+ "common/stringtriebuilder.cpp",
+ "common/uarrsort.cpp",
+ "common/ubidi.cpp",
+ "common/ubidi_props.cpp",
+ "common/ubidiln.cpp",
+ "common/ubiditransform.cpp",
+ "common/ubidiwrt.cpp",
+ "common/ubrk.cpp",
+ "common/ucase.cpp",
+ "common/ucasemap.cpp",
+ "common/ucasemap_titlecase_brkiter.cpp",
+ "common/ucat.cpp",
+ "common/uchar.cpp",
+ "common/ucharstrie.cpp",
+ "common/ucharstriebuilder.cpp",
+ "common/ucharstrieiterator.cpp",
+ "common/uchriter.cpp",
+ "common/ucln_cmn.cpp",
+ "common/ucmndata.cpp",
+ "common/ucnv.cpp",
+ "common/ucnv2022.cpp",
+ "common/ucnv_bld.cpp",
+ "common/ucnv_cb.cpp",
+ "common/ucnv_cnv.cpp",
+ "common/ucnv_ct.cpp",
+ "common/ucnv_err.cpp",
+ "common/ucnv_ext.cpp",
+ "common/ucnv_io.cpp",
+ "common/ucnv_lmb.cpp",
+ "common/ucnv_set.cpp",
+ "common/ucnv_u16.cpp",
+ "common/ucnv_u32.cpp",
+ "common/ucnv_u7.cpp",
+ "common/ucnv_u8.cpp",
+ "common/ucnvbocu.cpp",
+ "common/ucnvdisp.cpp",
+ "common/ucnvhz.cpp",
+ "common/ucnvisci.cpp",
+ "common/ucnvlat1.cpp",
+ "common/ucnvmbcs.cpp",
+ "common/ucnvscsu.cpp",
+ "common/ucnvsel.cpp",
+ "common/ucol_swp.cpp",
+ "common/ucptrie.cpp",
+ "common/ucurr.cpp",
+ "common/udata.cpp",
+ "common/udatamem.cpp",
+ "common/udataswp.cpp",
+ "common/uenum.cpp",
+ "common/uhash.cpp",
+ "common/uhash_us.cpp",
+ "common/uidna.cpp",
+ "common/uinit.cpp",
+ "common/uinvchar.cpp",
+ "common/uiter.cpp",
+ "common/ulist.cpp",
+ "common/uloc.cpp",
+ "common/uloc_keytype.cpp",
+ "common/uloc_tag.cpp",
+ "common/umapfile.cpp",
+ "common/umath.cpp",
+ "common/umutablecptrie.cpp",
+ "common/umutex.cpp",
+ "common/unames.cpp",
+ "common/unifiedcache.cpp",
+ "common/unifilt.cpp",
+ "common/unifunct.cpp",
+ "common/uniset.cpp",
+ "common/uniset_closure.cpp",
+ "common/uniset_props.cpp",
+ "common/unisetspan.cpp",
+ "common/unistr.cpp",
+ "common/unistr_case.cpp",
+ "common/unistr_case_locale.cpp",
+ "common/unistr_cnv.cpp",
+ "common/unistr_props.cpp",
+ "common/unistr_titlecase_brkiter.cpp",
+ "common/unorm.cpp",
+ "common/unormcmp.cpp",
+ "common/uobject.cpp",
+ "common/uprops.cpp",
+ "common/ures_cnv.cpp",
+ "common/uresbund.cpp",
+ "common/uresdata.cpp",
+ "common/usc_impl.cpp",
+ "common/uscript.cpp",
+ "common/uscript_props.cpp",
+ "common/uset.cpp",
+ "common/uset_props.cpp",
+ "common/usetiter.cpp",
+ # "common/ushape.cpp",
+ "common/usprep.cpp",
+ "common/ustack.cpp",
+ "common/ustr_cnv.cpp",
+ "common/ustr_titlecase_brkiter.cpp",
+ "common/ustr_wcs.cpp",
+ "common/ustrcase.cpp",
+ "common/ustrcase_locale.cpp",
+ "common/ustrenum.cpp",
+ "common/ustrfmt.cpp",
+ "common/ustring.cpp",
+ "common/ustrtrns.cpp",
+ "common/utext.cpp",
+ "common/utf_impl.cpp",
+ "common/util.cpp",
+ "common/util_props.cpp",
+ "common/utrace.cpp",
+ "common/utrie.cpp",
+ "common/utrie2.cpp",
+ "common/utrie2_builder.cpp",
+ "common/utrie_swap.cpp",
+ "common/uts46.cpp",
+ "common/utypes.cpp",
+ "common/uvector.cpp",
+ "common/uvectr32.cpp",
+ "common/uvectr64.cpp",
+ "common/wintz.cpp",
+ ]
+ thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+ icu_data_name = "icudt70l.dat"
+
+ if env_icu["tools"]:
+ env_icu.Depends("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name)
+ env_icu.Command("#thirdparty/icu4c/icudata.gen.h", "#thirdparty/icu4c/" + icu_data_name, make_icu_data)
+ else:
+ thirdparty_sources += ["icu_data/icudata_stub.cpp"]
+
+ env_icu.Append(CPPPATH=["#thirdparty/icu4c/common/"])
+ env_icu.Append(
+ CXXFLAGS=[
+ "-DU_STATIC_IMPLEMENTATION",
+ "-DU_COMMON_IMPLEMENTATION",
+ "-DUCONFIG_NO_COLLATION",
+ "-DUCONFIG_NO_CONVERSION",
+ "-DUCONFIG_NO_FORMATTING",
+ "-DUCONFIG_NO_SERVICE",
+ "-DUCONFIG_NO_IDNA",
+ "-DUCONFIG_NO_FILE_IO",
+ "-DUCONFIG_NO_TRANSLITERATION",
+ "-DPKGDATA_MODE=static",
+ "-DICU_DATA_NAME=" + icu_data_name,
+ ]
+ )
+ env_text_server_adv.Append(
+ CXXFLAGS=[
+ "-DICU_DATA_NAME=" + icu_data_name,
+ ]
+ )
+
+ lib = env_icu.add_library("icu_builtin", thirdparty_sources)
+ thirdparty_obj += lib
+
+ # Needs to be appended to arrive after libscene in the linker call,
+ # but we don't want it to arrive *after* system libs, so manual hack
+ # LIBS contains first SCons Library objects ("SCons.Node.FS.File object")
+ # and then plain strings for system library. We insert between the two.
+ inserted = False
+ for idx, linklib in enumerate(env["LIBS"]):
+ if isinstance(linklib, (str, bytes)): # first system lib such as "X11", otherwise SCons lib object
+ env["LIBS"].insert(idx, lib)
+ inserted = True
+ break
+ if not inserted:
+ env.Append(LIBS=[lib])
+
+
+# Godot source files
+
+module_obj = []
+
+if env_text_server_adv["tools"]:
+ env_text_server_adv.Append(CXXFLAGS=["-DICU_STATIC_DATA"])
+
+env_text_server_adv.Append(
+ CPPPATH=[
+ "#thirdparty/harfbuzz/src",
+ "#thirdparty/icu4c/common/",
+ ]
+)
+
+if msdngen_enabled:
+ env_text_server_adv.Append(
+ CPPPATH=[
+ "#thirdparty/msdfgen",
+ ]
+ )
+
+if freetype_enabled:
+ env_text_server_adv.Append(
+ CPPPATH=[
+ "#thirdparty/freetype/include",
+ "#thirdparty/graphite/include",
+ ]
+ )
+
+env_text_server_adv.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/text_server_adv/config.py b/modules/text_server_adv/config.py
new file mode 100644
index 0000000000..8c8df9b05e
--- /dev/null
+++ b/modules/text_server_adv/config.py
@@ -0,0 +1,16 @@
+def can_build(env, platform):
+ return True
+
+
+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/icu_data/icudata_stub.cpp b/modules/text_server_adv/icu_data/icudata_stub.cpp
new file mode 100644
index 0000000000..187001f33a
--- /dev/null
+++ b/modules/text_server_adv/icu_data/icudata_stub.cpp
@@ -0,0 +1,63 @@
+/*************************************************************************/
+/* icudata_stub.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 "unicode/udata.h"
+#include "unicode/utypes.h"
+#include "unicode/uversion.h"
+
+typedef struct {
+ uint16_t header_size;
+ uint8_t magic_1, magic_2;
+ UDataInfo info;
+ char padding[8];
+ uint32_t count, reserved;
+ int fake_name_and_data[4];
+} ICU_data_header;
+
+extern "C" U_EXPORT const ICU_data_header U_ICUDATA_ENTRY_POINT = {
+ 32,
+ 0xDA, 0x27,
+ { sizeof(UDataInfo),
+ 0,
+#if U_IS_BIG_ENDIAN
+ 1,
+#else
+ 0,
+#endif
+ U_CHARSET_FAMILY,
+ sizeof(UChar),
+ 0,
+ { 0x54, 0x6F, 0x43, 0x50 },
+ { 1, 0, 0, 0 },
+ { 0, 0, 0, 0 } },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ 0, 0,
+ { 0, 0, 0, 0 }
+};
diff --git a/modules/gdnative/net/register_types.cpp b/modules/text_server_adv/register_types.cpp
index 4e48a43210..b711d1561f 100644
--- a/modules/gdnative/net/register_types.cpp
+++ b/modules/text_server_adv/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,15 +29,20 @@
/*************************************************************************/
#include "register_types.h"
-#include "multiplayer_peer_gdnative.h"
-#include "packet_peer_gdnative.h"
-#include "stream_peer_gdnative.h"
-void register_net_types() {
- ClassDB::register_class<MultiplayerPeerGDNative>();
- ClassDB::register_class<PacketPeerGDNative>();
- ClassDB::register_class<StreamPeerGDNative>();
+#include "text_server_adv.h"
+
+void preregister_text_server_adv_types() {
+ GDREGISTER_CLASS(TextServerAdvanced);
+ if (TextServerManager::get_singleton()) {
+ Ref<TextServerAdvanced> ts;
+ ts.instantiate();
+ TextServerManager::get_singleton()->add_interface(ts);
+ }
+}
+
+void register_text_server_adv_types() {
}
-void unregister_net_types() {
+void unregister_text_server_adv_types() {
}
diff --git a/modules/text_server_adv/register_types.h b/modules/text_server_adv/register_types.h
new file mode 100644
index 0000000000..ddd1190f40
--- /dev/null
+++ b/modules/text_server_adv/register_types.h
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* 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_SERVER_ADV_REGISTER_TYPES_H
+#define TEXT_SERVER_ADV_REGISTER_TYPES_H
+
+#define MODULE_TEXT_SERVER_ADV_HAS_PREREGISTER
+
+void preregister_text_server_adv_types();
+void register_text_server_adv_types();
+void unregister_text_server_adv_types();
+
+#endif // TEXT_SERVER_ADV_REGISTER_TYPES_H
diff --git a/modules/text_server_adv/script_iterator.cpp b/modules/text_server_adv/script_iterator.cpp
new file mode 100644
index 0000000000..d1e849def8
--- /dev/null
+++ b/modules/text_server_adv/script_iterator.cpp
@@ -0,0 +1,128 @@
+/*************************************************************************/
+/* script_iterator.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 "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;
+}
+
+ScriptIterator::ScriptIterator(const String &p_string, int p_start, int p_length) {
+ struct ParenStackEntry {
+ int pair_index;
+ UScriptCode script_code;
+ };
+
+ if (p_start >= p_length) {
+ p_start = p_length - 1;
+ }
+
+ if (p_start < 0) {
+ p_start = 0;
+ }
+
+ int paren_size = PAREN_STACK_DEPTH;
+ ParenStackEntry *paren_stack = (ParenStackEntry *)memalloc(paren_size * sizeof(ParenStackEntry));
+
+ int script_start;
+ int script_end = p_start;
+ UScriptCode script_code;
+ int paren_sp = -1;
+ int start_sp = paren_sp;
+ UErrorCode err = U_ZERO_ERROR;
+ const char32_t *str = p_string.ptr();
+
+ do {
+ script_code = USCRIPT_COMMON;
+ for (script_start = script_end; script_end < p_length; script_end++) {
+ 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) {
+ // 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;
+ }
+ if (paren_sp < start_sp) {
+ start_sp = paren_sp;
+ }
+ if (paren_sp >= 0) {
+ sc = paren_stack[paren_sp].script_code;
+ }
+ }
+ }
+
+ 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;
+ }
+ }
+ } else {
+ break;
+ }
+ }
+
+ ScriptRange rng;
+ rng.script = hb_icu_script_to_script(script_code);
+ rng.start = script_start;
+ rng.end = script_end;
+
+ 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
new file mode 100644
index 0000000000..5efd40f7c4
--- /dev/null
+++ b/modules/text_server_adv/script_iterator.h
@@ -0,0 +1,63 @@
+/*************************************************************************/
+/* script_iterator.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 SCRIPT_ITERATOR_H
+#define SCRIPT_ITERATOR_H
+
+#include "servers/text_server.h"
+
+#include <unicode/uchar.h>
+#include <unicode/uloc.h>
+#include <unicode/uscript.h>
+#include <unicode/ustring.h>
+#include <unicode/utypes.h>
+
+#include <hb-icu.h>
+#include <hb.h>
+
+class ScriptIterator {
+ static const int PAREN_STACK_DEPTH = 128;
+
+public:
+ struct ScriptRange {
+ int start = 0;
+ int end = 0;
+ hb_script_t script = HB_SCRIPT_COMMON;
+ };
+ Vector<ScriptRange> script_ranges;
+
+private:
+ static bool same_script(int32_t p_script_one, int32_t p_script_two);
+
+public:
+ ScriptIterator(const String &p_string, int p_start, int p_length);
+};
+
+#endif //SCRIPT_ITERATOR_H
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
new file mode 100644
index 0000000000..1adaef4d84
--- /dev/null
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -0,0 +1,5091 @@
+/*************************************************************************/
+/* text_server_adv.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_adv.h"
+
+#include "core/error/error_macros.h"
+#include "core/string/print_string.h"
+#include "core/string/translation.h"
+
+#ifdef ICU_STATIC_DATA
+#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"
+#include "core/edge-selectors.h"
+#include "msdfgen.h"
+#endif
+
+/*************************************************************************/
+/* bmp_font_t HarfBuzz Bitmap font interface */
+/*************************************************************************/
+
+hb_font_funcs_t *TextServerAdvanced::funcs = nullptr;
+
+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;
+ }
+
+ bm_font->face = p_face;
+ bm_font->unref = p_unref;
+
+ return bm_font;
+}
+
+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::_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;
+ }
+
+ if (!bm_font->face->glyph_map.has(p_unicode)) {
+ if (bm_font->face->glyph_map.has(0xF000u + p_unicode)) {
+ *r_glyph = 0xF000u + p_unicode;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ *r_glyph = p_unicode;
+ return true;
+}
+
+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;
+ }
+
+ if (!bm_font->face->glyph_map.has(p_glyph)) {
+ return 0;
+ }
+
+ return bm_font->face->glyph_map[p_glyph].advance.x * 64;
+}
+
+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;
+ }
+
+ if (!bm_font->face->glyph_map.has(p_glyph)) {
+ return 0;
+ }
+
+ return -bm_font->face->glyph_map[p_glyph].advance.y * 64;
+}
+
+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;
+ }
+
+ 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)].x * 64;
+}
+
+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;
+ }
+
+ if (!bm_font->face->glyph_map.has(p_glyph)) {
+ return false;
+ }
+
+ *r_x = bm_font->face->glyph_map[p_glyph].advance.x * 32;
+ *r_y = -bm_font->face->ascent * 64;
+
+ return true;
+}
+
+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;
+ }
+
+ if (!bm_font->face->glyph_map.has(p_glyph)) {
+ return false;
+ }
+
+ r_extents->x_bearing = 0;
+ r_extents->y_bearing = 0;
+ r_extents->width = bm_font->face->glyph_map[p_glyph].rect.size.x * 64;
+ r_extents->height = bm_font->face->glyph_map[p_glyph].rect.size.y * 64;
+
+ return true;
+}
+
+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;
+ }
+
+ r_metrics->ascender = bm_font->face->ascent;
+ r_metrics->descender = bm_font->face->descent;
+ r_metrics->line_gap = 0;
+
+ return true;
+}
+
+void TextServerAdvanced::_bmp_create_font_funcs() {
+ if (funcs == nullptr) {
+ funcs = hb_font_funcs_create();
+
+ 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::_bmp_free_font_funcs() {
+ if (funcs != nullptr) {
+ hb_font_funcs_destroy(funcs);
+ funcs = nullptr;
+ }
+}
+
+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::_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);
+ _bmp_font_set_funcs(font, p_face, false);
+ return font;
+}
+
+/*************************************************************************/
+/* Character properties. */
+/*************************************************************************/
+
+_FORCE_INLINE_ bool is_ain(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_AIN;
+}
+
+_FORCE_INLINE_ bool is_alef(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_ALEF;
+}
+
+_FORCE_INLINE_ bool is_beh(char32_t p_chr) {
+ int32_t prop = u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP);
+ return (prop == U_JG_BEH) || (prop == U_JG_NOON) || (prop == U_JG_AFRICAN_NOON) || (prop == U_JG_NYA) || (prop == U_JG_YEH) || (prop == U_JG_FARSI_YEH);
+}
+
+_FORCE_INLINE_ bool is_dal(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_DAL;
+}
+
+_FORCE_INLINE_ bool is_feh(char32_t p_chr) {
+ return (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_FEH) || (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_AFRICAN_FEH);
+}
+
+_FORCE_INLINE_ bool is_gaf(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_GAF;
+}
+
+_FORCE_INLINE_ bool is_heh(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_HEH;
+}
+
+_FORCE_INLINE_ bool is_kaf(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_KAF;
+}
+
+_FORCE_INLINE_ bool is_lam(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_LAM;
+}
+
+_FORCE_INLINE_ bool is_qaf(char32_t p_chr) {
+ return (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_QAF) || (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_AFRICAN_QAF);
+}
+
+_FORCE_INLINE_ bool is_reh(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_REH;
+}
+
+_FORCE_INLINE_ bool is_seen_sad(char32_t p_chr) {
+ return (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_SAD) || (u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_SEEN);
+}
+
+_FORCE_INLINE_ bool is_tah(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_TAH;
+}
+
+_FORCE_INLINE_ bool is_teh_marbuta(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_TEH_MARBUTA;
+}
+
+_FORCE_INLINE_ bool is_yeh(char32_t p_chr) {
+ int32_t prop = u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP);
+ return (prop == U_JG_YEH) || (prop == U_JG_FARSI_YEH) || (prop == U_JG_YEH_BARREE) || (prop == U_JG_BURUSHASKI_YEH_BARREE) || (prop == U_JG_YEH_WITH_TAIL);
+}
+
+_FORCE_INLINE_ bool is_waw(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_GROUP) == U_JG_WAW;
+}
+
+_FORCE_INLINE_ bool is_transparent(char32_t p_chr) {
+ return u_getIntPropertyValue(p_chr, UCHAR_JOINING_TYPE) == U_JT_TRANSPARENT;
+}
+
+_FORCE_INLINE_ bool is_ligature(char32_t p_chr, char32_t p_nchr) {
+ return (is_lam(p_chr) && is_alef(p_nchr));
+}
+
+_FORCE_INLINE_ bool is_connected_to_prev(char32_t p_chr, char32_t p_pchr) {
+ int32_t prop = u_getIntPropertyValue(p_pchr, UCHAR_JOINING_TYPE);
+ return (prop != U_JT_RIGHT_JOINING) && (prop != U_JT_NON_JOINING) ? !is_ligature(p_pchr, p_chr) : false;
+}
+
+_FORCE_INLINE_ bool is_control(char32_t p_char) {
+ return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F);
+}
+
+_FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
+ return (p_char == 0x0020) || (p_char == 0x00A0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
+}
+
+_FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
+ return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
+}
+
+_FORCE_INLINE_ bool is_underscore(char32_t p_char) {
+ return (p_char == 0x005F);
+}
+
+/*************************************************************************/
+
+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) const {
+ return (interface_features & p_feature) == p_feature;
+}
+
+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.get_or_null(p_rid);
+ font_owner.free(p_rid);
+ memdelete(fd);
+ } else if (shaped_owner.owns(p_rid)) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_rid);
+ shaped_owner.free(p_rid);
+ memdelete(sd);
+ }
+}
+
+bool TextServerAdvanced::has(RID p_rid) {
+ _THREAD_SAFE_METHOD_
+ return font_owner.owns(p_rid) || shaped_owner.owns(p_rid);
+}
+
+bool TextServerAdvanced::load_support_data(const String &p_filename) {
+ _THREAD_SAFE_METHOD_
+
+#ifdef ICU_STATIC_DATA
+ if (icu_data == nullptr) {
+ UErrorCode err = U_ZERO_ERROR;
+ u_init(&err); // Do not check for errors, since we only load part of the data.
+ icu_data = (uint8_t *)&U_ICUDATA_ENTRY_POINT;
+ }
+#else
+ if (icu_data == nullptr) {
+ String filename = (p_filename.is_empty()) ? String("res://") + _MKSTR(ICU_DATA_NAME) : p_filename;
+
+ FileAccess *f = FileAccess::open(filename, FileAccess::READ);
+ if (!f) {
+ return false;
+ }
+
+ UErrorCode err = U_ZERO_ERROR;
+
+ // ICU data found.
+ uint64_t len = f->get_length();
+ icu_data = (uint8_t *)memalloc(len);
+ f->get_buffer(icu_data, len);
+ f->close();
+ memdelete(f);
+
+ udata_setCommonData(icu_data, &err);
+ if (U_FAILURE(err)) {
+ memfree(icu_data);
+ icu_data = nullptr;
+ ERR_FAIL_V_MSG(false, u_errorName(err));
+ }
+
+ err = U_ZERO_ERROR;
+ u_init(&err);
+ if (U_FAILURE(err)) {
+ memfree(icu_data);
+ icu_data = nullptr;
+ ERR_FAIL_V_MSG(false, u_errorName(err));
+ }
+ }
+#endif
+ return true;
+}
+
+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) const {
+ _THREAD_SAFE_METHOD_
+#ifdef ICU_STATIC_DATA
+
+ // Store data to the res file if it's available.
+ FileAccess *f = FileAccess::open(p_filename, FileAccess::WRITE);
+ if (!f) {
+ return false;
+ }
+ f->store_buffer(U_ICUDATA_ENTRY_POINT, U_ICUDATA_SIZE);
+ f->close();
+ memdelete(f);
+ return true;
+
+#else
+ return false;
+#endif
+}
+
+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;
+ } else {
+ return false;
+ }
+}
+
+void TextServerAdvanced::_insert_feature_sets() {
+ // Registered OpenType feature tags.
+ 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 {
+ if (feature_sets.has(p_name)) {
+ return feature_sets[p_name];
+ }
+
+ // No readable name, use tag string.
+ return hb_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1);
+}
+
+String TextServerAdvanced::tag_to_name(int32_t p_tag) const {
+ for (const KeyValue<StringName, int32_t> &E : feature_sets) {
+ if (E.value == p_tag) {
+ return E.key;
+ }
+ }
+
+ // No readable name, use tag string.
+ char name[5];
+ memset(name, 0, 5);
+ hb_tag_to_string(p_tag, name);
+ return String("custom_") + String(name);
+}
+
+/*************************************************************************/
+/* Font Glyph Rendering */
+/*************************************************************************/
+
+_FORCE_INLINE_ TextServerAdvanced::FontTexturePosition TextServerAdvanced::find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const {
+ FontTexturePosition ret;
+ ret.index = -1;
+
+ int mw = p_width;
+ int mh = p_height;
+
+ for (int i = 0; i < p_data->textures.size(); i++) {
+ const FontTexture &ct = p_data->textures[i];
+
+ if (RenderingServer::get_singleton() != nullptr) {
+ if (ct.texture->get_format() != p_image_format) {
+ continue;
+ }
+ }
+
+ if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture.
+ continue;
+ }
+
+ if (ct.offsets.size() < ct.texture_w) {
+ continue;
+ }
+
+ ret.y = 0x7FFFFFFF;
+ ret.x = 0;
+
+ for (int j = 0; j < ct.texture_w - mw; j++) {
+ int max_y = 0;
+
+ for (int k = j; k < j + mw; k++) {
+ int y = ct.offsets[k];
+ if (y > max_y) {
+ max_y = y;
+ }
+ }
+
+ if (max_y < ret.y) {
+ ret.y = max_y;
+ ret.x = j;
+ }
+ }
+
+ if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_h) {
+ continue; // Fail, could not fit it here.
+ }
+
+ ret.index = i;
+ break;
+ }
+
+ if (ret.index == -1) {
+ // Could not find texture to fit, create one.
+ ret.x = 0;
+ ret.y = 0;
+
+ int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256);
+ if (mw > texsize) {
+ texsize = mw; // Special case, adapt to it?
+ }
+ if (mh > texsize) {
+ texsize = mh; // Special case, adapt to it?
+ }
+
+ texsize = next_power_of_2(texsize);
+
+ texsize = MIN(texsize, 4096);
+
+ FontTexture tex;
+ tex.texture_w = texsize;
+ tex.texture_h = texsize;
+ tex.format = p_image_format;
+ tex.imgdata.resize(texsize * texsize * p_color_size);
+
+ {
+ // Zero texture.
+ uint8_t *w = tex.imgdata.ptrw();
+ ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
+ // Initialize the texture to all-white pixels to prevent artifacts when the
+ // font is displayed at a non-default scale with filtering enabled.
+ if (p_color_size == 2) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8, BW font.
+ w[i + 0] = 255;
+ w[i + 1] = 0;
+ }
+ } else if (p_color_size == 4) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8, Color font, Multichannel(+True) SDF.
+ w[i + 0] = 255;
+ w[i + 1] = 255;
+ w[i + 2] = 255;
+ w[i + 3] = 0;
+ }
+ } else {
+ ERR_FAIL_V(ret);
+ }
+ }
+ tex.offsets.resize(texsize);
+ for (int i = 0; i < texsize; i++) { // Zero offsets.
+ tex.offsets.write[i] = 0;
+ }
+
+ p_data->textures.push_back(tex);
+ ret.index = p_data->textures.size() - 1;
+ }
+
+ return ret;
+}
+
+#ifdef MODULE_MSDFGEN_ENABLED
+
+struct MSContext {
+ msdfgen::Point2 position;
+ msdfgen::Shape *shape;
+ msdfgen::Contour *contour;
+};
+
+class DistancePixelConversion {
+ double invRange;
+
+public:
+ _FORCE_INLINE_ explicit DistancePixelConversion(double range) :
+ invRange(1 / range) {}
+ _FORCE_INLINE_ void operator()(float *pixels, const msdfgen::MultiAndTrueDistance &distance) const {
+ pixels[0] = float(invRange * distance.r + .5);
+ pixels[1] = float(invRange * distance.g + .5);
+ pixels[2] = float(invRange * distance.b + .5);
+ pixels[3] = float(invRange * distance.a + .5);
+ }
+};
+
+struct MSDFThreadData {
+ msdfgen::Bitmap<float, 4> *output;
+ msdfgen::Shape *shape;
+ msdfgen::Projection *projection;
+ DistancePixelConversion *distancePixelConversion;
+};
+
+static msdfgen::Point2 ft_point2(const FT_Vector &vector) {
+ return msdfgen::Point2(vector.x / 60.0f, vector.y / 60.0f);
+}
+
+static int ft_move_to(const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ if (!(context->contour && context->contour->edges.empty())) {
+ context->contour = &context->shape->addContour();
+ }
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+static int ft_line_to(const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ msdfgen::Point2 endpoint = ft_point2(*to);
+ if (endpoint != context->position) {
+ context->contour->addEdge(new msdfgen::LinearSegment(context->position, endpoint));
+ context->position = endpoint;
+ }
+ return 0;
+}
+
+static int ft_conic_to(const FT_Vector *control, const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ context->contour->addEdge(new msdfgen::QuadraticSegment(context->position, ft_point2(*control), ft_point2(*to)));
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ context->contour->addEdge(new msdfgen::CubicSegment(context->position, ft_point2(*control1), ft_point2(*control2), ft_point2(*to)));
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+void TextServerAdvanced::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
+ MSDFThreadData *td = (MSDFThreadData *)p_td;
+
+ msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape);
+ int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y;
+ for (int col = 0; col < td->output->width(); ++col) {
+ int x = (y % 2) ? td->output->width() - col - 1 : col;
+ msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5));
+ msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p);
+ td->distancePixelConversion->operator()(td->output->operator()(x, row), distance);
+ }
+}
+
+_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
+ msdfgen::Shape shape;
+
+ shape.contours.clear();
+ shape.inverseYAxis = false;
+
+ MSContext context = {};
+ context.shape = &shape;
+ FT_Outline_Funcs ft_functions;
+ ft_functions.move_to = &ft_move_to;
+ ft_functions.line_to = &ft_line_to;
+ ft_functions.conic_to = &ft_conic_to;
+ ft_functions.cubic_to = &ft_cubic_to;
+ ft_functions.shift = 0;
+ ft_functions.delta = 0;
+
+ int error = FT_Outline_Decompose(outline, &ft_functions, &context);
+ ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'.");
+ if (!shape.contours.empty() && shape.contours.back().edges.empty()) {
+ shape.contours.pop_back();
+ }
+
+ if (FT_Outline_Get_Orientation(outline) == 1) {
+ for (int i = 0; i < (int)shape.contours.size(); ++i) {
+ shape.contours[i].reverse();
+ }
+ }
+
+ shape.inverseYAxis = true;
+ shape.normalize();
+
+ msdfgen::Shape::Bounds bounds = shape.getBounds(p_pixel_range);
+
+ FontGlyph chr;
+ chr.found = true;
+ chr.advance = advance.round();
+
+ if (shape.validate() && shape.contours.size() > 0) {
+ int w = (bounds.r - bounds.l);
+ int h = (bounds.t - bounds.b);
+
+ int mw = w + p_rect_margin * 2;
+ int mh = h + p_rect_margin * 2;
+
+ ERR_FAIL_COND_V(mw > 4096, FontGlyph());
+ ERR_FAIL_COND_V(mh > 4096, FontGlyph());
+
+ FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh);
+ ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
+ FontTexture &tex = p_data->textures.write[tex_pos.index];
+
+ edgeColoringSimple(shape, 3.0); // Max. angle.
+ 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));
+ msdfgen::MSDFGeneratorConfig config(true, msdfgen::ErrorCorrectionConfig());
+
+ MSDFThreadData td;
+ td.output = &image;
+ td.shape = &shape;
+ td.projection = &projection;
+ td.distancePixelConversion = &distancePixelConversion;
+
+ if (p_font_data->work_pool.get_thread_count() == 0) {
+ p_font_data->work_pool.init();
+ }
+ p_font_data->work_pool.do_work(h, this, &TextServerAdvanced::_generateMTSDF_threaded, &td);
+
+ msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);
+
+ {
+ uint8_t *wr = tex.imgdata.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * 4;
+ ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph());
+ wr[ofs + 0] = (uint8_t)(CLAMP(image(j, i)[0] * 256.f, 0.f, 255.f));
+ 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));
+ }
+ }
+ }
+
+ // Blit to image and texture.
+ {
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, Image::FORMAT_RGBA8, tex.imgdata));
+ if (tex.texture.is_null()) {
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
+ } else {
+ tex.texture->update(img);
+ }
+ }
+ }
+
+ // Update height array.
+ for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
+ tex.offsets.write[k] = tex_pos.y + mh;
+ }
+
+ chr.texture_idx = tex_pos.index;
+
+ chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h);
+ chr.rect.position = Vector2(bounds.l, -bounds.t);
+ chr.rect.size = chr.uv_rect.size;
+ }
+ return chr;
+}
+#endif
+
+#ifdef MODULE_FREETYPE_ENABLED
+_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
+ int w = bitmap.width;
+ int h = bitmap.rows;
+
+ int mw = w + p_rect_margin * 2;
+ int mh = h + p_rect_margin * 2;
+
+ ERR_FAIL_COND_V(mw > 4096, FontGlyph());
+ ERR_FAIL_COND_V(mh > 4096, FontGlyph());
+
+ int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
+ Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
+
+ FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh);
+ ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
+
+ // Fit character in char texture.
+
+ FontTexture &tex = p_data->textures.write[tex_pos.index];
+
+ {
+ uint8_t *wr = tex.imgdata.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * color_size;
+ ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph());
+ switch (bitmap.pixel_mode) {
+ 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 + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
+ } break;
+ case FT_PIXEL_MODE_GRAY:
+ wr[ofs + 0] = 255; // grayscale as 1
+ wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
+ break;
+ case FT_PIXEL_MODE_BGRA: {
+ int ofs_color = i * bitmap.pitch + (j << 2);
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
+ wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
+ } break;
+ default:
+ ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + ".");
+ break;
+ }
+ }
+ }
+ }
+
+ // Blit to image and texture.
+ {
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, require_format, tex.imgdata));
+
+ if (tex.texture.is_null()) {
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
+ } else {
+ tex.texture->update(img);
+ }
+ }
+ }
+
+ // Update height array.
+ for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
+ tex.offsets.write[k] = tex_pos.y + mh;
+ }
+
+ FontGlyph chr;
+ chr.advance = (advance * p_data->scale / p_data->oversampling).round();
+ 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.size = chr.uv_rect.size * p_data->scale / p_data->oversampling;
+ return chr;
+}
+#endif
+
+/*************************************************************************/
+/* Font Cache */
+/*************************************************************************/
+
+_FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
+
+ FontDataForSizeAdvanced *fd = p_font_data->cache[p_size];
+ if (fd->glyph_map.has(p_glyph)) {
+ return fd->glyph_map[p_glyph].found;
+ }
+
+ if (p_glyph == 0) { // Non graphical or invalid glyph, do not render.
+ fd->glyph_map[p_glyph] = FontGlyph();
+ return true;
+ }
+
+#ifdef MODULE_FREETYPE_ENABLED
+ FontGlyph gl;
+ if (fd->face) {
+ FT_Int32 flags = FT_LOAD_DEFAULT;
+
+ bool outline = p_size.y > 0;
+ switch (p_font_data->hinting) {
+ case TextServer::HINTING_NONE:
+ flags |= FT_LOAD_NO_HINTING;
+ break;
+ case TextServer::HINTING_LIGHT:
+ flags |= FT_LOAD_TARGET_LIGHT;
+ break;
+ default:
+ flags |= FT_LOAD_TARGET_NORMAL;
+ break;
+ }
+ if (p_font_data->force_autohinter) {
+ flags |= FT_LOAD_FORCE_AUTOHINT;
+ }
+ if (outline) {
+ flags |= FT_LOAD_NO_BITMAP;
+ } else if (FT_HAS_COLOR(fd->face)) {
+ flags |= FT_LOAD_COLOR;
+ }
+
+ 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);
+
+ int error = FT_Load_Glyph(fd->face, p_glyph, flags);
+ if (error) {
+ fd->glyph_map[p_glyph] = FontGlyph();
+ return false;
+ }
+
+ if (!outline) {
+ if (!p_font_data->msdf) {
+ error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
+ }
+ FT_GlyphSlot slot = fd->face->glyph;
+ if (!error) {
+ if (p_font_data->msdf) {
+#ifdef MODULE_MSDFGEN_ENABLED
+ gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
+#else
+ fd->glyph_map[p_glyph] = FontGlyph();
+ ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!");
+#endif
+ } else {
+ gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
+ }
+ }
+ } else {
+ FT_Stroker stroker;
+ if (FT_Stroker_New(library, &stroker) != 0) {
+ fd->glyph_map[p_glyph] = FontGlyph();
+ ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph stroker.");
+ }
+
+ FT_Stroker_Set(stroker, (int)(fd->size.y * fd->oversampling * 16.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
+ FT_Glyph glyph;
+ FT_BitmapGlyph glyph_bitmap;
+
+ if (FT_Get_Glyph(fd->face->glyph, &glyph) != 0) {
+ goto cleanup_stroker;
+ }
+ if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {
+ goto cleanup_glyph;
+ }
+ if (FT_Glyph_To_Bitmap(&glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) {
+ goto cleanup_glyph;
+ }
+ glyph_bitmap = (FT_BitmapGlyph)glyph;
+ gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2());
+
+ cleanup_glyph:
+ FT_Done_Glyph(glyph);
+ cleanup_stroker:
+ FT_Stroker_Done(stroker);
+ }
+ fd->glyph_map[p_glyph] = gl;
+ return gl.found;
+ }
+#endif
+ fd->glyph_map[p_glyph] = FontGlyph();
+ return false;
+}
+
+_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 && (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, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
+
+ memset(&fd->stream, 0, sizeof(FT_StreamRec));
+ fd->stream.base = (unsigned char *)p_font_data->data_ptr;
+ fd->stream.size = p_font_data->data_size;
+ fd->stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
+ fargs.memory_size = p_font_data->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &fd->stream;
+ error = FT_Open_Face(library, &fargs, 0, &fd->face);
+ if (error) {
+ FT_Done_Face(fd->face);
+ fd->face = nullptr;
+ 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 = font_get_global_oversampling();
+ } else {
+ fd->oversampling = p_font_data->oversampling;
+ }
+
+ 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 = 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 = 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);
+ }
+
+ 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);
+ if (count != 0) {
+ hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, script_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ p_font_data->supported_scripts.insert(script_tags[i]);
+ }
+ memfree(script_tags);
+ }
+ count = hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr);
+ if (count != 0) {
+ hb_tag_t *script_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_script_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, script_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ p_font_data->supported_scripts.insert(script_tags[i]);
+ }
+ memfree(script_tags);
+ }
+
+ // Get supported scripts from OS2 table.
+ TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(fd->face, FT_SFNT_OS2);
+ if (os2) {
+ if ((os2->ulUnicodeRange1 & 1L << 4) || (os2->ulUnicodeRange1 & 1L << 5) || (os2->ulUnicodeRange1 & 1L << 6) || (os2->ulUnicodeRange1 & 1L << 31) || (os2->ulUnicodeRange2 & 1L << 0) || (os2->ulUnicodeRange2 & 1L << 1) || (os2->ulUnicodeRange2 & 1L << 2) || (os2->ulUnicodeRange2 & 1L << 3) || (os2->ulUnicodeRange2 & 1L << 4) || (os2->ulUnicodeRange2 & 1L << 5) || (os2->ulUnicodeRange2 & 1L << 6) || (os2->ulUnicodeRange2 & 1L << 7) || (os2->ulUnicodeRange2 & 1L << 8) || (os2->ulUnicodeRange2 & 1L << 9) || (os2->ulUnicodeRange2 & 1L << 10) || (os2->ulUnicodeRange2 & 1L << 11) || (os2->ulUnicodeRange2 & 1L << 12) || (os2->ulUnicodeRange2 & 1L << 13) || (os2->ulUnicodeRange2 & 1L << 14) || (os2->ulUnicodeRange2 & 1L << 15) || (os2->ulUnicodeRange2 & 1L << 30) || (os2->ulUnicodeRange3 & 1L << 0) || (os2->ulUnicodeRange3 & 1L << 1) || (os2->ulUnicodeRange3 & 1L << 2) || (os2->ulUnicodeRange3 & 1L << 4) || (os2->ulUnicodeRange3 & 1L << 5) || (os2->ulUnicodeRange3 & 1L << 18) || (os2->ulUnicodeRange3 & 1L << 24) || (os2->ulUnicodeRange3 & 1L << 25) || (os2->ulUnicodeRange3 & 1L << 26) || (os2->ulUnicodeRange3 & 1L << 27) || (os2->ulUnicodeRange3 & 1L << 28) || (os2->ulUnicodeRange4 & 1L << 3) || (os2->ulUnicodeRange4 & 1L << 6) || (os2->ulUnicodeRange4 & 1L << 15) || (os2->ulUnicodeRange4 & 1L << 23) || (os2->ulUnicodeRange4 & 1L << 24) || (os2->ulUnicodeRange4 & 1L << 26)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_COMMON);
+ }
+ if ((os2->ulUnicodeRange1 & 1L << 0) || (os2->ulUnicodeRange1 & 1L << 1) || (os2->ulUnicodeRange1 & 1L << 2) || (os2->ulUnicodeRange1 & 1L << 3) || (os2->ulUnicodeRange1 & 1L << 29)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_LATIN);
+ }
+ if ((os2->ulUnicodeRange1 & 1L << 7) || (os2->ulUnicodeRange1 & 1L << 30)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GREEK);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 8) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_COPTIC);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 9) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CYRILLIC);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 10) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_ARMENIAN);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 11) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_HEBREW);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 12) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_VAI);
+ }
+ if ((os2->ulUnicodeRange1 & 1L << 13) || (os2->ulUnicodeRange2 & 1L << 31) || (os2->ulUnicodeRange3 & 1L << 3)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_ARABIC);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 14) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_NKO);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 15) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_DEVANAGARI);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 16) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_BENGALI);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 17) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GURMUKHI);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 18) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GUJARATI);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 19) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_ORIYA);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 20) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TAMIL);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 21) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TELUGU);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 22) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_KANNADA);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 23) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_MALAYALAM);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 24) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_THAI);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 25) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_LAO);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 26) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GEORGIAN);
+ }
+ if (os2->ulUnicodeRange1 & 1L << 27) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_BALINESE);
+ }
+ if ((os2->ulUnicodeRange1 & 1L << 28) || (os2->ulUnicodeRange2 & 1L << 20) || (os2->ulUnicodeRange2 & 1L << 24)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_HANGUL);
+ }
+ if ((os2->ulUnicodeRange2 & 1L << 21) || (os2->ulUnicodeRange2 & 1L << 22) || (os2->ulUnicodeRange2 & 1L << 23) || (os2->ulUnicodeRange2 & 1L << 26) || (os2->ulUnicodeRange2 & 1L << 27) || (os2->ulUnicodeRange2 & 1L << 29)) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_HAN);
+ }
+ if (os2->ulUnicodeRange2 & 1L << 17) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_HIRAGANA);
+ }
+ if (os2->ulUnicodeRange2 & 1L << 18) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_KATAKANA);
+ }
+ if (os2->ulUnicodeRange2 & 1L << 19) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_BOPOMOFO);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 6) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TIBETAN);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 7) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SYRIAC);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 8) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_THAANA);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 9) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SINHALA);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 10) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_MYANMAR);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 11) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_ETHIOPIC);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 12) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CHEROKEE);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 13) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CANADIAN_SYLLABICS);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 14) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_OGHAM);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 15) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_RUNIC);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 16) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_KHMER);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 17) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_MONGOLIAN);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 19) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_YI);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 20) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_HANUNOO);
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TAGBANWA);
+ p_font_data->supported_scripts.insert(HB_SCRIPT_BUHID);
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TAGALOG);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 21) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_OLD_ITALIC);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 22) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GOTHIC);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 23) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_DESERET);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 29) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_LIMBU);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 30) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TAI_LE);
+ }
+ if (os2->ulUnicodeRange3 & 1L << 31) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_NEW_TAI_LUE);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 0) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_BUGINESE);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 1) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_GLAGOLITIC);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 2) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TIFINAGH);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 4) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SYLOTI_NAGRI);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 5) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_LINEAR_B);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 7) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_UGARITIC);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 8) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_OLD_PERSIAN);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 9) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SHAVIAN);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 10) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_OSMANYA);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 11) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CYPRIOT);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 12) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_KHAROSHTHI);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 13) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_TAI_VIET);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 14) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CUNEIFORM);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 16) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SUNDANESE);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 17) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_LEPCHA);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 18) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_OL_CHIKI);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 19) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_SAURASHTRA);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 20) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_KAYAH_LI);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 21) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_REJANG);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 22) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_CHAM);
+ }
+ if (os2->ulUnicodeRange4 & 1L << 25) {
+ p_font_data->supported_scripts.insert(HB_SCRIPT_ANATOLIAN_HIEROGLYPHS);
+ }
+ }
+
+ // Read OpenType feature tags.
+ p_font_data->supported_features.clear();
+ count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, nullptr, nullptr);
+ if (count != 0) {
+ hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GSUB, 0, &count, feature_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ p_font_data->supported_features[feature_tags[i]] = 1;
+ }
+ memfree(feature_tags);
+ }
+ count = hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, nullptr, nullptr);
+ if (count != 0) {
+ hb_tag_t *feature_tags = (hb_tag_t *)memalloc(count * sizeof(hb_tag_t));
+ hb_ot_layout_table_get_feature_tags(hb_font_get_face(fd->hb_handle), HB_OT_TAG_GPOS, 0, &count, feature_tags);
+ for (unsigned int i = 0; i < count; i++) {
+ p_font_data->supported_features[feature_tags[i]] = 1;
+ }
+ memfree(feature_tags);
+ }
+
+ // Read OpenType variations.
+ p_font_data->supported_varaitions.clear();
+ if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
+ FT_MM_Var *amaster;
+ FT_Get_MM_Var(fd->face, &amaster);
+ for (FT_UInt i = 0; i < amaster->num_axis; i++) {
+ p_font_data->supported_varaitions[(int32_t)amaster->axis[i].tag] = Vector3i(amaster->axis[i].minimum / 65536, amaster->axis[i].maximum / 65536, amaster->axis[i].def / 65536);
+ }
+ FT_Done_MM_Var(library, amaster);
+ }
+ p_font_data->face_init = true;
+ }
+
+ // Write variations.
+ if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
+ FT_MM_Var *amaster;
+
+ FT_Get_MM_Var(fd->face, &amaster);
+
+ Vector<hb_variation_t> hb_vars;
+ Vector<FT_Fixed> coords;
+ coords.resize(amaster->num_axis);
+
+ FT_Get_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw());
+
+ for (FT_UInt i = 0; i < amaster->num_axis; i++) {
+ hb_variation_t var;
+
+ // Reset to default.
+ var.tag = amaster->axis[i].tag;
+ 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)) {
+ var.value = p_font_data->variation_coordinates[var.tag];
+ coords.write[i] = CLAMP(var.value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum);
+ }
+
+ if (p_font_data->variation_coordinates.has(tag_to_name(var.tag))) {
+ var.value = p_font_data->variation_coordinates[tag_to_name(var.tag)];
+ coords.write[i] = CLAMP(var.value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum);
+ }
+
+ hb_vars.push_back(var);
+ }
+
+ FT_Set_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw());
+ hb_font_set_variations(fd->hb_handle, hb_vars.is_empty() ? nullptr : &hb_vars[0], hb_vars.size());
+ FT_Done_MM_Var(library, amaster);
+ }
+#else
+ 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 = _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 KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : p_font_data->cache) {
+ memdelete(E.value);
+ }
+ p_font_data->cache.clear();
+ p_font_data->face_init = false;
+ p_font_data->supported_features.clear();
+ p_font_data->supported_varaitions.clear();
+ p_font_data->supported_scripts.clear();
+}
+
+hb_font_t *TextServerAdvanced::_font_get_hb_handle(RID p_font_rid, int p_size) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, nullptr);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), nullptr);
+
+ return fd->cache[size]->hb_handle;
+}
+
+RID TextServerAdvanced::create_font() {
+ FontDataAdvanced *fd = memnew(FontDataAdvanced);
+
+ return font_owner.make_rid(fd);
+}
+
+void TextServerAdvanced::font_set_data(RID p_font_rid, const PackedByteArray &p_data) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ _font_clear_cache(fd);
+ fd->data = p_data;
+ fd->data_ptr = fd->data.ptr();
+ fd->data_size = fd->data.size();
+}
+
+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.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ _font_clear_cache(fd);
+ fd->data.clear();
+ fd->data_ptr = p_data_ptr;
+ fd->data_size = p_data_size;
+}
+
+void TextServerAdvanced::font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_flags = p_style;
+}
+
+uint32_t /*FontStyle*/ TextServerAdvanced::font_get_style(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+ return fd->style_flags;
+}
+
+void TextServerAdvanced::font_set_style_name(RID p_font_rid, const String &p_name) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_name = p_name;
+}
+
+String TextServerAdvanced::font_get_style_name(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->style_name;
+}
+
+void TextServerAdvanced::font_set_name(RID p_font_rid, const String &p_name) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->font_name = p_name;
+}
+
+String TextServerAdvanced::font_get_name(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->font_name;
+}
+
+void TextServerAdvanced::font_set_antialiased(RID p_font_rid, bool p_antialiased) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->antialiased != p_antialiased) {
+ _font_clear_cache(fd);
+ fd->antialiased = p_antialiased;
+ }
+}
+
+bool TextServerAdvanced::font_is_antialiased(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->antialiased;
+}
+
+void TextServerAdvanced::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf != p_msdf) {
+ _font_clear_cache(fd);
+ fd->msdf = p_msdf;
+ }
+}
+
+bool TextServerAdvanced::font_is_multichannel_signed_distance_field(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf;
+}
+
+void TextServerAdvanced::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf_range != p_msdf_pixel_range) {
+ _font_clear_cache(fd);
+ fd->msdf_range = p_msdf_pixel_range;
+ }
+}
+
+int TextServerAdvanced::font_get_msdf_pixel_range(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf_range;
+}
+
+void TextServerAdvanced::font_set_msdf_size(RID p_font_rid, int p_msdf_size) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf_source_size != p_msdf_size) {
+ _font_clear_cache(fd);
+ fd->msdf_source_size = p_msdf_size;
+ }
+}
+
+int TextServerAdvanced::font_get_msdf_size(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf_source_size;
+}
+
+void TextServerAdvanced::font_set_fixed_size(RID p_font_rid, int p_fixed_size) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->fixed_size != p_fixed_size) {
+ fd->fixed_size = p_fixed_size;
+ }
+}
+
+int TextServerAdvanced::font_get_fixed_size(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->fixed_size;
+}
+
+void TextServerAdvanced::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->force_autohinter != p_force_autohinter) {
+ _font_clear_cache(fd);
+ fd->force_autohinter = p_force_autohinter;
+ }
+}
+
+bool TextServerAdvanced::font_is_force_autohinter(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->force_autohinter;
+}
+
+void TextServerAdvanced::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->hinting != p_hinting) {
+ _font_clear_cache(fd);
+ fd->hinting = p_hinting;
+ }
+}
+
+TextServer::Hinting TextServerAdvanced::font_get_hinting(RID p_font_rid) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, HINTING_NONE);
+
+ MutexLock lock(fd->mutex);
+ return fd->hinting;
+}
+
+void TextServerAdvanced::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->variation_coordinates != p_variation_coordinates) {
+ _font_clear_cache(fd);
+ fd->variation_coordinates = p_variation_coordinates;
+ }
+}
+
+Dictionary TextServerAdvanced::font_get_variation_coordinates(RID p_font_rid) const {
+ 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, float p_oversampling) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->oversampling != p_oversampling) {
+ _font_clear_cache(fd);
+ fd->oversampling = p_oversampling;
+ }
+}
+
+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);
+ return fd->oversampling;
+}
+
+Array TextServerAdvanced::font_get_size_cache_list(RID p_font_rid) const {
+ FontDataAdvanced *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, FontDataForSizeAdvanced *>::Element *E = fd->cache.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+void TextServerAdvanced::font_clear_size_cache(RID p_font_rid) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ for (const KeyValue<Vector2i, FontDataForSizeAdvanced *> &E : fd->cache) {
+ memdelete(E.value);
+ }
+ fd->cache.clear();
+}
+
+void TextServerAdvanced::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.has(p_size)) {
+ memdelete(fd->cache[p_size]);
+ fd->cache.erase(p_size);
+ }
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->ascent = p_ascent;
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ 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, float p_descent) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->descent = p_descent;
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ 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, float p_underline_position) {
+ 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));
+ fd->cache[size]->underline_position = p_underline_position;
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ 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, float p_underline_thickness) {
+ 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));
+ fd->cache[size]->underline_thickness = p_underline_thickness;
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ 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, float p_scale) {
+ 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));
+ fd->cache[size]->scale = p_scale;
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ 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.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ switch (p_spacing) {
+ case TextServer::SPACING_GLYPH: {
+ fd->cache[size]->spacing_glyph = p_value;
+ } break;
+ case TextServer::SPACING_SPACE: {
+ fd->cache[size]->spacing_space = p_value;
+ } break;
+ default: {
+ ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing));
+ } break;
+ }
+}
+
+int TextServerAdvanced::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+
+ switch (p_spacing) {
+ case TextServer::SPACING_GLYPH: {
+ if (fd->msdf) {
+ return fd->cache[size]->spacing_glyph * (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 * (float)p_size / (float)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->spacing_space;
+ }
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing));
+ } break;
+ }
+ return 0;
+}
+
+int TextServerAdvanced::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) 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_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+
+ return fd->cache[size]->textures.size();
+}
+
+void TextServerAdvanced::font_clear_textures(RID p_font_rid, const Vector2i &p_size) {
+ 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));
+ fd->cache[size]->textures.clear();
+}
+
+void TextServerAdvanced::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) {
+ 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_INDEX(p_texture_index, fd->cache[size]->textures.size());
+
+ fd->cache[size]->textures.remove(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.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+ ERR_FAIL_COND(p_image.is_null());
+
+ 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);
+ }
+
+ FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+
+ tex.imgdata = p_image->get_data();
+ tex.texture_w = p_image->get_width();
+ tex.texture_h = p_image->get_height();
+ tex.format = p_image->get_format();
+
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata));
+ tex.texture = Ref<ImageTexture>();
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
+}
+
+Ref<Image> TextServerAdvanced::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Ref<Image>());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>());
+ ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>());
+
+ const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata));
+
+ return img;
+}
+
+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.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);
+ }
+
+ FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ tex.offsets = p_offset;
+}
+
+PackedInt32Array TextServerAdvanced::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, PackedInt32Array());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array());
+ ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array());
+
+ const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ return tex.offsets;
+}
+
+Array TextServerAdvanced::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Array());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
+
+ Array ret;
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+ const int32_t *E = nullptr;
+ while ((E = gl.next(E))) {
+ ret.push_back(*E);
+ }
+ return ret;
+}
+
+void TextServerAdvanced::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) {
+ 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));
+
+ fd->cache[size]->glyph_map.clear();
+}
+
+void TextServerAdvanced::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) {
+ 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));
+
+ fd->cache[size]->glyph_map.erase(p_glyph);
+}
+
+Vector2 TextServerAdvanced::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
+ if (!_ensure_glyph(fd, size, p_glyph)) {
+ return Vector2(); // Invalid or non graphicl glyph, do not display errors.
+ }
+
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ if (fd->msdf) {
+ 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.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));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].advance = p_advance;
+ gl[p_glyph].found = true;
+}
+
+Vector2 TextServerAdvanced::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
+ if (!_ensure_glyph(fd, size, p_glyph)) {
+ return Vector2(); // Invalid or non graphicl glyph, do not display errors.
+ }
+
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ if (fd->msdf) {
+ 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.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));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].rect.position = p_offset;
+ gl[p_glyph].found = true;
+}
+
+Vector2 TextServerAdvanced::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
+ if (!_ensure_glyph(fd, size, p_glyph)) {
+ return Vector2(); // Invalid or non graphicl glyph, do not display errors.
+ }
+
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ if (fd->msdf) {
+ 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.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));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].rect.size = p_gl_size;
+ gl[p_glyph].found = true;
+}
+
+Rect2 TextServerAdvanced::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Rect2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2());
+ if (!_ensure_glyph(fd, size, p_glyph)) {
+ return Rect2(); // Invalid or non graphicl glyph, do not display errors.
+ }
+
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+ return gl[p_glyph].uv_rect;
+}
+
+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.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));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].uv_rect = p_uv_rect;
+ gl[p_glyph].found = true;
+}
+
+int TextServerAdvanced::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, -1);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1);
+ if (!_ensure_glyph(fd, size, p_glyph)) {
+ return -1; // Invalid or non graphicl glyph, do not display errors.
+ }
+
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+ return gl[p_glyph].texture_idx;
+}
+
+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.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));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].texture_idx = p_texture_idx;
+ gl[p_glyph].found = true;
+}
+
+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), 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, Dictionary());
+
+ points.clear();
+ contours.clear();
+
+ 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 * (float)p_size / (float)fd->msdf_source_size;
+ }
+ for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
+ points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
+ }
+ for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) {
+ contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
+ }
+ orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);
+#else
+ return Dictionary();
+#endif
+
+ 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.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Array());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ 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());
+ }
+ return ret;
+}
+
+void TextServerAdvanced::font_clear_kerning_map(RID p_font_rid, int p_size) {
+ 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));
+ fd->cache[size]->kerning_map.clear();
+}
+
+void TextServerAdvanced::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) {
+ 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));
+ fd->cache[size]->kerning_map.erase(p_glyph_pair);
+}
+
+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.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));
+ fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning;
+}
+
+Vector2 TextServerAdvanced::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
+
+ const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
+
+ if (kern.has(p_glyph_pair)) {
+ if (fd->msdf) {
+ return kern[p_glyph_pair] * (float)p_size / (float)fd->msdf_source_size;
+ } else {
+ return kern[p_glyph_pair];
+ }
+ } else {
+#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);
+ if (fd->msdf) {
+ return Vector2(delta.x, delta.y) * (float)p_size / (float)fd->msdf_source_size;
+ } else {
+ return Vector2(delta.x, delta.y);
+ }
+ }
+#endif
+ }
+ return Vector2();
+}
+
+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.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);
+ 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
+ return (int32_t)p_char;
+#endif
+}
+
+bool TextServerAdvanced::font_has_char(RID p_font_rid, char32_t p_char) const {
+ 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()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false);
+ }
+ FontDataForSizeAdvanced *at_size = fd->cache.front()->get();
+
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ return FT_Get_Char_Index(at_size->face, p_char) != 0;
+ }
+#endif
+ return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false;
+}
+
+String TextServerAdvanced::font_get_supported_chars(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);
+ if (fd->cache.is_empty()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String());
+ }
+ FontDataForSizeAdvanced *at_size = fd->cache.front()->get();
+
+ String chars;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ FT_UInt gindex;
+ FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex);
+ while (gindex != 0) {
+ if (charcode != 0) {
+ chars += char32_t(charcode);
+ }
+ charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex);
+ }
+ return chars;
+ }
+#endif
+ if (at_size) {
+ const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
+ const int32_t *E = nullptr;
+ while ((E = gl.next(E))) {
+ chars += char32_t(*E);
+ }
+ }
+ return chars;
+}
+
+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.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 TextServerAdvanced::font_render_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_index) {
+ 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(!_ensure_glyph(fd, size, p_index));
+}
+
+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.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.
+ }
+
+ const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+ if (gl.found) {
+ ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
+
+ if (gl.texture_idx != -1) {
+ Color modulate = p_color;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+#endif
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
+ if (fd->msdf) {
+ Point2 cpos = p_pos;
+ 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;
+ cpos += gl.rect.position;
+ Size2i 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);
+ }
+ }
+ }
+ }
+}
+
+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.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.
+ }
+
+ const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+ if (gl.found) {
+ ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
+
+ if (gl.texture_idx != -1) {
+ Color modulate = p_color;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+#endif
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
+ if (fd->msdf) {
+ Point2 cpos = p_pos;
+ 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;
+ cpos += gl.rect.position;
+ Size2i 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);
+ }
+ }
+ }
+ }
+}
+
+bool TextServerAdvanced::font_is_language_supported(RID p_font_rid, const String &p_language) const {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ if (fd->language_support_overrides.has(p_language)) {
+ return fd->language_support_overrides[p_language];
+ } else {
+ return true;
+ }
+}
+
+void TextServerAdvanced::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ fd->language_support_overrides[p_language] = p_supported;
+}
+
+bool TextServerAdvanced::font_get_language_support_override(RID p_font_rid, const String &p_language) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->language_support_overrides[p_language];
+}
+
+void TextServerAdvanced::font_remove_language_support_override(RID p_font_rid, const String &p_language) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ fd->language_support_overrides.erase(p_language);
+}
+
+Vector<String> TextServerAdvanced::font_get_language_support_overrides(RID 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 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.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ if (fd->script_support_overrides.has(p_script)) {
+ return fd->script_support_overrides[p_script];
+ } else {
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), false);
+ 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.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ fd->script_support_overrides[p_script] = p_supported;
+}
+
+bool TextServerAdvanced::font_get_script_support_override(RID p_font_rid, const String &p_script) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->script_support_overrides[p_script];
+}
+
+void TextServerAdvanced::font_remove_script_support_override(RID p_font_rid, const String &p_script) {
+ FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ fd->script_support_overrides.erase(p_script);
+}
+
+Vector<String> TextServerAdvanced::font_get_script_support_overrides(RID 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->script_support_overrides.front(); E; E = E->next()) {
+ out.push_back(E->key());
+ }
+ return out;
+}
+
+Dictionary TextServerAdvanced::font_supported_feature_list(RID p_font_rid) 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, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary());
+ return fd->supported_features;
+}
+
+Dictionary TextServerAdvanced::font_supported_variation_list(RID p_font_rid) 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, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary());
+ return fd->supported_varaitions;
+}
+
+float TextServerAdvanced::font_get_global_oversampling() const {
+ return oversampling;
+}
+
+void TextServerAdvanced::font_set_global_oversampling(float p_oversampling) {
+ _THREAD_SAFE_METHOD_
+ if (oversampling != p_oversampling) {
+ oversampling = p_oversampling;
+ List<RID> fonts;
+ font_owner.get_owned_list(&fonts);
+ bool font_cleared = false;
+ for (const RID &E : fonts) {
+ if (!font_is_multichannel_signed_distance_field(E) && font_get_oversampling(E) <= 0) {
+ font_clear_size_cache(E);
+ font_cleared = true;
+ }
+ }
+
+ if (font_cleared) {
+ List<RID> text_bufs;
+ shaped_owner.get_owned_list(&text_bufs);
+ for (const RID &E : text_bufs) {
+ invalidate(shaped_owner.get_or_null(E));
+ }
+ }
+ }
+}
+
+/*************************************************************************/
+/* Shaped text buffer interface */
+/*************************************************************************/
+
+int TextServerAdvanced::_convert_pos(const ShapedTextDataAdvanced *p_sd, int p_pos) const {
+ int32_t limit = p_pos;
+ if (p_sd->text.length() != p_sd->utf16.length()) {
+ const UChar *data = p_sd->utf16.ptr();
+ for (int i = 0; i < p_pos; i++) {
+ if (U16_IS_LEAD(data[i])) {
+ limit--;
+ }
+ }
+ }
+ return limit;
+}
+
+int TextServerAdvanced::_convert_pos_inv(const ShapedTextDataAdvanced *p_sd, int p_pos) const {
+ int32_t limit = p_pos;
+ if (p_sd->text.length() != p_sd->utf16.length()) {
+ for (int i = 0; i < p_pos; i++) {
+ if (p_sd->text[i] > 0xFFFF) {
+ limit++;
+ }
+ }
+ }
+ return limit;
+}
+
+void TextServerAdvanced::invalidate(TextServerAdvanced::ShapedTextDataAdvanced *p_shaped) {
+ p_shaped->valid = false;
+ p_shaped->sort_valid = false;
+ p_shaped->line_breaks_valid = false;
+ p_shaped->justification_ops_valid = false;
+ p_shaped->text_trimmed = false;
+ p_shaped->ascent = 0.f;
+ p_shaped->descent = 0.f;
+ p_shaped->width = 0.f;
+ p_shaped->upos = 0.f;
+ p_shaped->uthk = 0.f;
+ p_shaped->glyphs.clear();
+ p_shaped->glyphs_logical.clear();
+ p_shaped->overrun_trim_data = TrimData();
+ p_shaped->utf16 = Char16String();
+ if (p_shaped->script_iter != nullptr) {
+ memdelete(p_shaped->script_iter);
+ p_shaped->script_iter = nullptr;
+ }
+ for (int i = 0; i < p_shaped->bidi_iter.size(); i++) {
+ ubidi_close(p_shaped->bidi_iter[i]);
+ }
+ p_shaped->bidi_iter.clear();
+}
+
+void TextServerAdvanced::full_copy(ShapedTextDataAdvanced *p_shaped) {
+ ShapedTextDataAdvanced *parent = shaped_owner.get_or_null(p_shaped->parent);
+
+ 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;
+ }
+ }
+
+ for (int k = 0; k < parent->spans.size(); k++) {
+ ShapedTextDataAdvanced::Span span = parent->spans[k];
+ if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
+ continue;
+ }
+ span.start = MAX(p_shaped->start, span.start);
+ span.end = MIN(p_shaped->end, span.end);
+ p_shaped->spans.push_back(span);
+ }
+
+ p_shaped->parent = RID();
+}
+
+RID TextServerAdvanced::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextDataAdvanced *sd = memnew(ShapedTextDataAdvanced);
+ sd->hb_buffer = hb_buffer_create();
+ sd->direction = p_direction;
+ sd->orientation = p_orientation;
+ return shaped_owner.make_rid(sd);
+}
+
+void TextServerAdvanced::shaped_text_clear(RID p_shaped) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ sd->parent = RID();
+ sd->start = 0;
+ sd->end = 0;
+ sd->text = String();
+ sd->spans.clear();
+ sd->objects.clear();
+ sd->bidi_override.clear();
+ invalidate(sd);
+}
+
+void TextServerAdvanced::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ if (sd->direction != p_direction) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->direction = p_direction;
+ invalidate(sd);
+ }
+}
+
+TextServer::Direction TextServerAdvanced::shaped_text_get_direction(RID p_shaped) const {
+ 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_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.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.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ if (sd->orientation != p_orientation) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->orientation = p_orientation;
+ invalidate(sd);
+ }
+}
+
+void TextServerAdvanced::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ ERR_FAIL_COND(sd->parent != RID());
+ if (sd->preserve_invalid != p_enabled) {
+ sd->preserve_invalid = p_enabled;
+ invalidate(sd);
+ }
+}
+
+bool TextServerAdvanced::shaped_text_get_preserve_invalid(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ return sd->preserve_invalid;
+}
+
+void TextServerAdvanced::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ if (sd->preserve_control != p_enabled) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->preserve_control = p_enabled;
+ invalidate(sd);
+ }
+}
+
+bool TextServerAdvanced::shaped_text_get_preserve_control(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ return sd->preserve_control;
+}
+
+TextServer::Orientation TextServerAdvanced::shaped_text_get_orientation(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
+
+ MutexLock lock(sd->mutex);
+ return sd->orientation;
+}
+
+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.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.get_or_null(p_fonts[i]), false);
+ }
+
+ if (p_text.is_empty()) {
+ return true;
+ }
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+
+ ShapedTextDataAdvanced::Span span;
+ span.start = sd->text.length();
+ span.end = span.start + p_text.length();
+ span.fonts = p_fonts; // Do not pre-sort, spans will be divided to subruns later.
+ span.font_size = p_size;
+ span.language = p_language;
+ span.features = p_opentype_features;
+
+ sd->spans.push_back(span);
+ sd->text += p_text;
+ sd->end += p_text.length();
+ invalidate(sd);
+
+ 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) {
+ _THREAD_SAFE_METHOD_
+ 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);
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+
+ ShapedTextDataAdvanced::Span span;
+ span.start = sd->text.length();
+ span.end = span.start + p_length;
+ span.embedded_key = p_key;
+
+ ShapedTextDataAdvanced::EmbeddedObject obj;
+ obj.inline_align = p_inline_align;
+ obj.rect.size = p_size;
+ obj.pos = span.start;
+
+ sd->spans.push_back(span);
+ sd->text += String::chr(0xfffc).repeat(p_length);
+ sd->end += p_length;
+ sd->objects[p_key] = obj;
+ invalidate(sd);
+
+ 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.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ ERR_FAIL_COND_V(!sd->objects.has(p_key), false);
+ sd->objects[p_key].rect.size = p_size;
+ sd->objects[p_key].inline_align = p_inline_align;
+ if (sd->valid) {
+ // Recalc string metrics.
+ sd->ascent = 0;
+ sd->descent = 0;
+ sd->width = 0;
+ sd->upos = 0;
+ sd->uthk = 0;
+ int sd_size = sd->glyphs.size();
+
+ for (int i = 0; i < sd_size; i++) {
+ Glyph gl = sd->glyphs[i];
+ Variant key;
+ if (gl.count == 1) {
+ for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ if (E.value.pos == gl.start) {
+ key = E.key;
+ break;
+ }
+ }
+ }
+ if (key != Variant()) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->objects[key].rect.position.x = sd->width;
+ sd->width += sd->objects[key].rect.size.x;
+ sd->glyphs.write[i].advance = sd->objects[key].rect.size.x;
+ } else {
+ sd->objects[key].rect.position.y = sd->width;
+ sd->width += sd->objects[key].rect.size.y;
+ sd->glyphs.write[i].advance = sd->objects[key].rect.size.y;
+ }
+ } else {
+ if (gl.font_rid.is_valid()) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->ascent = MAX(sd->ascent, MAX(font_get_ascent(gl.font_rid, gl.font_size), -gl.y_off));
+ sd->descent = MAX(sd->descent, MAX(font_get_descent(gl.font_rid, gl.font_size), gl.y_off));
+ } else {
+ sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ }
+ sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size));
+ sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size));
+ } 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, 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));
+ }
+ }
+ sd->width += gl.advance * gl.repeat;
+ }
+ }
+
+ // Align embedded objects to baseline.
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.y = -sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.y = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.y = sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.x = -sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.x = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.x = sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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;
+ sd->descent = full_descent;
+ }
+ return true;
+}
+
+RID TextServerAdvanced::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, RID());
+
+ MutexLock lock(sd->mutex);
+ if (sd->parent != RID()) {
+ return shaped_text_substr(sd->parent, p_start, p_length);
+ }
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ ERR_FAIL_COND_V(p_start < 0 || p_length < 0, RID());
+ ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID());
+ 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 (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()));
+
+ int sd_size = sd->glyphs.size();
+ const Glyph *sd_glyphs = sd->glyphs.ptr();
+ for (int ov = 0; ov < 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) {
+ 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);
+
+ ERR_FAIL_COND_V_MSG((start < 0 || end - start > new_sd->utf16.length()), RID(), "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);
+ if (U_FAILURE(err)) {
+ ubidi_close(bidi_iter);
+ ERR_FAIL_V_MSG(RID(), u_errorName(err));
+ }
+ 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));
+ 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);
+
+ for (int j = 0; j < sd_size; j++) {
+ if ((sd_glyphs[j].start >= bidi_run_start) && (sd_glyphs[j].end <= bidi_run_end)) {
+ // Copy glyphs.
+ Glyph gl = sd_glyphs[j];
+ Variant key;
+ bool find_embedded = false;
+ if (gl.count == 1) {
+ 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.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;
+ } else {
+ new_sd->objects[key].rect.position.y = new_sd->width;
+ new_sd->width += 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));
+ } 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));
+ }
+ } 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, 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));
+ }
+ }
+ new_sd->width += gl.advance * gl.repeat;
+ }
+ new_sd->glyphs.push_back(gl);
+ }
+ }
+ }
+ }
+
+ // Align embedded objects to baseline.
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.y = -new_sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.y = (-new_sd->ascent + new_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.y = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.y = new_sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.x = -new_sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.x = (-new_sd->ascent + new_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.x = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.x = new_sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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;
+ }
+ new_sd->valid = true;
+
+ return shaped_owner.make_rid(new_sd);
+}
+
+RID TextServerAdvanced::shaped_text_get_parent(RID p_shaped) const {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, RID());
+
+ MutexLock lock(sd->mutex);
+ return sd->parent;
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (!sd->justification_ops_valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_update_justification_ops(p_shaped);
+ }
+
+ sd->fit_width_minimum_reached = false;
+ int start_pos = 0;
+ int end_pos = sd->glyphs.size() - 1;
+
+ if ((p_jst_flags & JUSTIFICATION_AFTER_LAST_TAB) == JUSTIFICATION_AFTER_LAST_TAB) {
+ int start, end, delta;
+ if (sd->para_direction == DIRECTION_LTR) {
+ start = sd->glyphs.size() - 1;
+ end = -1;
+ delta = -1;
+ } else {
+ start = 0;
+ end = sd->glyphs.size();
+ delta = +1;
+ }
+
+ for (int i = start; i != end; i += delta) {
+ if ((sd->glyphs[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
+ if (sd->para_direction == DIRECTION_LTR) {
+ start_pos = i;
+ break;
+ } else {
+ end_pos = i;
+ break;
+ }
+ }
+ }
+ }
+
+ 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;
+ justification_width = sd->width_trimmed;
+ } else {
+ return sd->width;
+ }
+ } else {
+ justification_width = sd->width;
+ }
+
+ if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
+ while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
+ sd->glyphs.write[start_pos].advance = 0;
+ start_pos += sd->glyphs[start_pos].count;
+ }
+ while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ justification_width -= sd->glyphs[end_pos].advance * sd->glyphs[end_pos].repeat;
+ sd->glyphs.write[end_pos].advance = 0;
+ end_pos -= sd->glyphs[end_pos].count;
+ }
+ }
+
+ int space_count = 0;
+ int elongation_count = 0;
+ for (int i = start_pos; i <= end_pos; i++) {
+ const Glyph &gl = sd->glyphs[i];
+ if (gl.count > 0) {
+ if ((gl.flags & GRAPHEME_IS_ELONGATION) == GRAPHEME_IS_ELONGATION) {
+ elongation_count++;
+ }
+ if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
+ space_count++;
+ }
+ }
+ }
+
+ if ((elongation_count > 0) && ((p_jst_flags & JUSTIFICATION_KASHIDA) == JUSTIFICATION_KASHIDA)) {
+ 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) {
+ if (((gl.flags & GRAPHEME_IS_ELONGATION) == GRAPHEME_IS_ELONGATION) && (gl.advance > 0)) {
+ int count = delta_width_per_kashida / gl.advance;
+ int prev_count = gl.repeat;
+ if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
+ gl.repeat = MAX(count, 0);
+ }
+ justification_width += (gl.repeat - prev_count) * gl.advance;
+ }
+ }
+ }
+ }
+ float adv_remain = 0;
+ if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
+ 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) {
+ 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 {
+ new_advance = MAX(gl.advance + delta_width_per_space, 0.1 * gl.font_size);
+ }
+ gl.advance = new_advance;
+ adv_remain += (new_advance - gl.advance);
+ if (adv_remain >= 1.0) {
+ gl.advance++;
+ adv_remain -= 1.0;
+ } else if (adv_remain <= -1.0) {
+ gl.advance = MAX(gl.advance - 1, 0);
+ adv_remain -= 1.0;
+ }
+ justification_width += (gl.advance - old_adv);
+ }
+ }
+ }
+ }
+
+ if (Math::floor(p_width) < Math::floor(justification_width)) {
+ sd->fit_width_minimum_reached = true;
+ }
+
+ if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) != JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
+ sd->width = justification_width;
+ }
+
+ return sd->width;
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (!sd->line_breaks_valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_update_breaks(p_shaped);
+ }
+
+ int tab_index = 0;
+ float off = 0.f;
+
+ int start, end, delta;
+ if (sd->para_direction == DIRECTION_LTR) {
+ start = 0;
+ end = sd->glyphs.size();
+ delta = +1;
+ } else {
+ start = sd->glyphs.size() - 1;
+ end = -1;
+ delta = -1;
+ }
+
+ Glyph *gl = sd->glyphs.ptrw();
+
+ for (int i = start; i != end; i += delta) {
+ if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
+ float tab_off = 0.f;
+ while (tab_off <= off) {
+ tab_off += p_tab_stops[tab_index];
+ tab_index++;
+ if (tab_index >= p_tab_stops.size()) {
+ tab_index = 0;
+ }
+ }
+ float old_adv = gl[i].advance;
+ gl[i].advance = tab_off - off;
+ sd->width += gl[i].advance - old_adv;
+ off = 0;
+ continue;
+ }
+ off += gl[i].advance * gl[i].repeat;
+ }
+
+ return 0.f;
+}
+
+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);
+ if (!sd->valid) {
+ 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;
+ bool cut_per_word = (p_trim_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY;
+ bool enforce_ellipsis = (p_trim_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS;
+ bool justification_aware = (p_trim_flags & OVERRUN_JUSTIFICATION_AWARE) == OVERRUN_JUSTIFICATION_AWARE;
+
+ Glyph *sd_glyphs = sd->glyphs.ptrw();
+
+ if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
+ sd->overrun_trim_data.trim_pos = -1;
+ sd->overrun_trim_data.ellipsis_pos = -1;
+ return;
+ }
+
+ if (justification_aware && !sd->fit_width_minimum_reached) {
+ return;
+ }
+
+ int sd_size = sd->glyphs.size();
+ RID last_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
+ int last_gl_font_size = sd_glyphs[sd_size - 1].font_size;
+ int32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, last_gl_font_size, '.');
+ Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, dot_gl_idx);
+ int32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, last_gl_font_size, ' ');
+ Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, whitespace_gl_idx);
+
+ int ellipsis_width = 0;
+ if (add_ellipsis) {
+ ellipsis_width = 3 * dot_adv.x + font_get_spacing(last_gl_font_rid, last_gl_font_size, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
+ }
+
+ int ell_min_characters = 6;
+ float width = sd->width;
+
+ bool is_rtl = sd->direction == DIRECTION_RTL || (sd->direction == DIRECTION_AUTO && sd->para_direction == DIRECTION_RTL);
+
+ int trim_pos = (is_rtl) ? sd_size : 0;
+ int ellipsis_pos = (enforce_ellipsis) ? 0 : -1;
+
+ int last_valid_cut = 0;
+ bool found = false;
+
+ int glyphs_from = (is_rtl) ? 0 : sd_size - 1;
+ int glyphs_to = (is_rtl) ? sd_size - 1 : -1;
+ int glyphs_delta = (is_rtl) ? +1 : -1;
+
+ for (int i = glyphs_from; i != glyphs_to; i += glyphs_delta) {
+ if (!is_rtl) {
+ width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
+ }
+ if (sd_glyphs[i].count > 0) {
+ bool above_min_char_threshold = ((is_rtl) ? sd_size - 1 - i : i) >= ell_min_characters;
+
+ 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;
+ }
+ } else {
+ last_valid_cut = i;
+ found = true;
+ }
+ if (found) {
+ trim_pos = last_valid_cut;
+
+ if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {
+ ellipsis_pos = trim_pos;
+ }
+ break;
+ }
+ }
+ }
+ if (is_rtl) {
+ width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
+ }
+ }
+
+ sd->overrun_trim_data.trim_pos = trim_pos;
+ sd->overrun_trim_data.ellipsis_pos = ellipsis_pos;
+ if (trim_pos == 0 && enforce_ellipsis && add_ellipsis) {
+ sd->overrun_trim_data.ellipsis_pos = 0;
+ }
+
+ if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis) {
+ 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)) {
+ Glyph gl;
+ gl.count = 1;
+ gl.advance = whitespace_adv.x;
+ gl.index = whitespace_gl_idx;
+ gl.font_rid = last_gl_font_rid;
+ gl.font_size = last_gl_font_size;
+ gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0);
+
+ sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
+ }
+ // Add ellipsis dots.
+ Glyph gl;
+ gl.count = 1;
+ gl.repeat = 3;
+ gl.advance = dot_adv.x;
+ gl.index = dot_gl_idx;
+ gl.font_rid = last_gl_font_rid;
+ gl.font_size = last_gl_font_size;
+ gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0);
+
+ sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
+ }
+
+ sd->text_trimmed = true;
+ sd->width_trimmed = width + ((ellipsis_pos != -1) ? ellipsis_width : 0);
+ }
+}
+
+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.ellipsis_glyph_buf.size();
+}
+
+bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ if (!sd->valid) {
+ shaped_text_shape(p_shaped);
+ }
+
+ if (sd->line_breaks_valid) {
+ return true; // Nothing to do.
+ }
+
+ const UChar *data = sd->utf16.ptr();
+
+ HashMap<int, bool> breaks;
+ UErrorCode err = U_ZERO_ERROR;
+ int i = 0;
+ while (i < sd->spans.size()) {
+ String language = sd->spans[i].language;
+ int r_start = sd->spans[i].start;
+ while (i + 1 < sd->spans.size() && language == sd->spans[i + 1].language) {
+ i++;
+ }
+ int r_end = sd->spans[i].end;
+ UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
+ if (U_FAILURE(err)) {
+ // No data loaded - use fallback.
+ for (int j = r_start; j < r_end; j++) {
+ char32_t c = sd->text[j - sd->start];
+ if (is_whitespace(c)) {
+ breaks[j] = false;
+ }
+ if (is_linebreak(c)) {
+ breaks[j] = true;
+ }
+ }
+ } else {
+ while (ubrk_next(bi) != UBRK_DONE) {
+ int pos = _convert_pos(sd, ubrk_current(bi)) + r_start - 1;
+ if (pos != r_end) {
+ if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
+ breaks[pos] = true;
+ } else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
+ breaks[pos] = false;
+ }
+ }
+ }
+ }
+ ubrk_close(bi);
+ i++;
+ }
+
+ sd->sort_valid = false;
+ sd->glyphs_logical.clear();
+ int sd_size = sd->glyphs.size();
+ 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];
+ if (c == 0xfffc) {
+ continue;
+ }
+ if (c == 0x0009 || c == 0x000b) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_TAB;
+ }
+ 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 (breaks.has(sd->glyphs[i].start)) {
+ if (breaks[sd->glyphs[i].start]) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
+ } else {
+ if (is_whitespace(c)) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
+ } else {
+ 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;
+ 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();
+ sd_glyphs = sd->glyphs.ptrw();
+
+ i += sd_glyphs[i].count;
+ continue;
+ }
+ }
+ }
+
+ i += (sd_glyphs[i].count - 1);
+ }
+ }
+
+ sd->line_breaks_valid = true;
+
+ return sd->line_breaks_valid;
+}
+
+_FORCE_INLINE_ int _generate_kashida_justification_opportunies(const String &p_data, int p_start, int p_end) {
+ int kashida_pos = -1;
+ int8_t priority = 100;
+ int i = p_start;
+
+ char32_t pc = 0;
+
+ while ((p_end > p_start) && is_transparent(p_data[p_end - 1])) {
+ p_end--;
+ }
+
+ while (i < p_end) {
+ uint32_t c = p_data[i];
+
+ if (c == 0x0640) {
+ kashida_pos = i;
+ priority = 0;
+ }
+ if (priority >= 1 && i < p_end - 1) {
+ if (is_seen_sad(c) && (p_data[i + 1] != 0x200C)) {
+ kashida_pos = i;
+ priority = 1;
+ }
+ }
+ if (priority >= 2 && i > p_start) {
+ if (is_teh_marbuta(c) || is_dal(c) || (is_heh(c) && i == p_end - 1)) {
+ if (is_connected_to_prev(c, pc)) {
+ kashida_pos = i - 1;
+ priority = 2;
+ }
+ }
+ }
+ if (priority >= 3 && i > p_start) {
+ if (is_alef(c) || ((is_lam(c) || is_tah(c) || is_kaf(c) || is_gaf(c)) && i == p_end - 1)) {
+ if (is_connected_to_prev(c, pc)) {
+ kashida_pos = i - 1;
+ priority = 3;
+ }
+ }
+ }
+ if (priority >= 4 && i > p_start && i < p_end - 1) {
+ if (is_beh(c)) {
+ if (is_reh(p_data[i + 1]) || is_yeh(p_data[i + 1])) {
+ if (is_connected_to_prev(c, pc)) {
+ kashida_pos = i - 1;
+ priority = 4;
+ }
+ }
+ }
+ }
+ if (priority >= 5 && i > p_start) {
+ if (is_waw(c) || ((is_ain(c) || is_qaf(c) || is_feh(c)) && i == p_end - 1)) {
+ if (is_connected_to_prev(c, pc)) {
+ kashida_pos = i - 1;
+ priority = 5;
+ }
+ }
+ }
+ if (priority >= 6 && i > p_start) {
+ if (is_reh(c)) {
+ if (is_connected_to_prev(c, pc)) {
+ kashida_pos = i - 1;
+ priority = 6;
+ }
+ }
+ }
+ if (!is_transparent(c)) {
+ pc = c;
+ }
+ i++;
+ }
+
+ return kashida_pos;
+}
+
+bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ if (!sd->valid) {
+ shaped_text_shape(p_shaped);
+ }
+ if (!sd->line_breaks_valid) {
+ shaped_text_update_breaks(p_shaped);
+ }
+
+ if (sd->justification_ops_valid) {
+ return true; // Nothing to do.
+ }
+
+ const UChar *data = sd->utf16.ptr();
+ int32_t data_size = sd->utf16.length();
+
+ Map<int, bool> jstops;
+
+ // Use ICU word iterator and custom kashida detection.
+ UErrorCode err = U_ZERO_ERROR;
+ UBreakIterator *bi = ubrk_open(UBRK_WORD, "", data, data_size, &err);
+ if (U_FAILURE(err)) {
+ // No data - use fallback.
+ int limit = 0;
+ for (int i = 0; i < sd->text.length(); i++) {
+ if (is_whitespace(data[i])) {
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, i) + sd->start;
+ if (ks != -1) {
+ jstops[ks] = true;
+ }
+ limit = i + 1;
+ }
+ }
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, sd->text.length()) + sd->start;
+ if (ks != -1) {
+ jstops[ks] = true;
+ }
+ } else {
+ int limit = 0;
+ while (ubrk_next(bi) != UBRK_DONE) {
+ if (ubrk_getRuleStatus(bi) != UBRK_WORD_NONE) {
+ int i = _convert_pos(sd, ubrk_current(bi));
+ jstops[i + sd->start] = false;
+ int ks = _generate_kashida_justification_opportunies(sd->text, limit, i);
+ if (ks != -1) {
+ jstops[ks + sd->start] = true;
+ }
+ limit = i;
+ }
+ }
+ ubrk_close(bi);
+ }
+
+ sd->sort_valid = false;
+ sd->glyphs_logical.clear();
+ int sd_size = sd->glyphs.size();
+
+ if (jstops.size() > 0) {
+ for (int i = 0; i < sd_size; i++) {
+ if (sd->glyphs[i].count > 0) {
+ if (jstops.has(sd->glyphs[i].start)) {
+ char32_t c = sd->text[sd->glyphs[i].start - sd->start];
+ if (c == 0xfffc) {
+ continue;
+ }
+ if (jstops[sd->glyphs[i].start]) {
+ if (c == 0x0640) {
+ sd->glyphs.write[i].flags |= GRAPHEME_IS_ELONGATION;
+ } else {
+ if (sd->glyphs[i].font_rid != RID()) {
+ 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;
+ gl.repeat = 0;
+ gl.count = 1;
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.y_off = sd->glyphs[i].y_off;
+ } else {
+ gl.x_off = sd->glyphs[i].x_off;
+ }
+ gl.flags |= GRAPHEME_IS_ELONGATION | GRAPHEME_IS_VIRTUAL;
+ sd->glyphs.insert(i, gl);
+ i++;
+ }
+ }
+ }
+ } else if (!is_whitespace(c)) {
+ 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;
+ 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;
+ }
+ }
+ }
+ }
+ }
+
+ sd->justification_ops_valid = true;
+ return sd->justification_ops_valid;
+}
+
+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, Glyph());
+
+ hb_buffer_clear_contents(p_sd->hb_buffer);
+ hb_buffer_set_direction(p_sd->hb_buffer, p_direction);
+ hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)(HB_BUFFER_FLAG_DEFAULT));
+ hb_buffer_set_script(p_sd->hb_buffer, p_script);
+ hb_buffer_add_utf32(p_sd->hb_buffer, (const uint32_t *)&p_char, 1, 0, 1);
+
+ hb_shape(hb_font, p_sd->hb_buffer, nullptr, 0);
+
+ unsigned int glyph_count = 0;
+ hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(p_sd->hb_buffer, &glyph_count);
+ hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(p_sd->hb_buffer, &glyph_count);
+
+ // Process glyphs.
+ Glyph gl;
+
+ if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
+ gl.flags |= TextServer::GRAPHEME_IS_RTL;
+ }
+
+ gl.font_rid = p_font;
+ gl.font_size = p_font_size;
+
+ if (glyph_count > 0) {
+ 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 {
+ gl.advance = -Math::round(glyph_pos[0].y_advance / (64.0 / scale));
+ }
+ gl.count = 1;
+
+ gl.index = glyph_info[0].codepoint;
+ gl.x_off = Math::round(glyph_pos[0].x_offset / (64.0 / scale));
+ gl.y_off = -Math::round(glyph_pos[0].y_offset / (64.0 / scale));
+
+ if ((glyph_info[0].codepoint != 0) || !u_isgraph(p_char)) {
+ gl.flags |= GRAPHEME_IS_VALID;
+ }
+ }
+ return gl;
+}
+
+void TextServerAdvanced::_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) {
+ int fs = p_sd->spans[p_span].font_size;
+ if (p_fb_index >= p_fonts.size()) {
+ // 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]))) {
+ Glyph gl;
+ gl.start = i;
+ gl.end = i + 1;
+ gl.count = 1;
+ gl.index = p_sd->text[i];
+ gl.font_size = fs;
+ gl.font_rid = RID();
+ if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
+ gl.flags |= TextServer::GRAPHEME_IS_RTL;
+ }
+ if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.advance = get_hex_code_box_size(fs, gl.index).x;
+ 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));
+ p_sd->descent = MAX(p_sd->descent, Math::round(get_hex_code_box_size(fs, gl.index).x * 0.5f));
+ }
+ p_sd->width += gl.advance;
+
+ p_sd->glyphs.push_back(gl);
+ }
+ }
+ return;
+ }
+
+ RID f = p_fonts[p_fb_index];
+ hb_font_t *hb_font = _font_get_hb_handle(f, fs);
+ ERR_FAIL_COND(hb_font == nullptr);
+
+ hb_buffer_clear_contents(p_sd->hb_buffer);
+ hb_buffer_set_direction(p_sd->hb_buffer, p_direction);
+ if (p_sd->preserve_control) {
+ hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)(HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES | (p_start == 0 ? HB_BUFFER_FLAG_BOT : 0) | (p_end == p_sd->text.length() ? HB_BUFFER_FLAG_EOT : 0)));
+ } else {
+ hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)(HB_BUFFER_FLAG_DEFAULT | (p_start == 0 ? HB_BUFFER_FLAG_BOT : 0) | (p_end == p_sd->text.length() ? HB_BUFFER_FLAG_EOT : 0)));
+ }
+ hb_buffer_set_script(p_sd->hb_buffer, p_script);
+
+ if (p_sd->spans[p_span].language != String()) {
+ 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);
+ }
+
+ hb_buffer_add_utf32(p_sd->hb_buffer, (const uint32_t *)p_sd->text.ptr(), p_sd->text.length(), p_start, p_end - p_start);
+
+ Vector<hb_feature_t> ftrs;
+ for (const Variant *ftr = p_sd->spans[p_span].features.next(nullptr); ftr != nullptr; ftr = p_sd->spans[p_span].features.next(ftr)) {
+ double values = p_sd->spans[p_span].features[*ftr];
+ if (values >= 0) {
+ hb_feature_t feature;
+ feature.tag = *ftr;
+ feature.value = values;
+ feature.start = 0;
+ feature.end = -1;
+ ftrs.push_back(feature);
+ }
+ }
+ hb_shape(hb_font, p_sd->hb_buffer, ftrs.is_empty() ? nullptr : &ftrs[0], ftrs.size());
+
+ unsigned int glyph_count = 0;
+ hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(p_sd->hb_buffer, &glyph_count);
+ hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(p_sd->hb_buffer, &glyph_count);
+
+ // Process glyphs.
+ if (glyph_count > 0) {
+ 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;
+ unsigned int last_cluster_index = 0;
+ bool last_cluster_valid = true;
+
+ for (unsigned int i = 0; i < glyph_count; i++) {
+ if ((i > 0) && (last_cluster_id != glyph_info[i].cluster)) {
+ if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
+ end = w[last_cluster_index].start;
+ } else {
+ for (unsigned int j = last_cluster_index; j < i; j++) {
+ w[j].end = glyph_info[i].cluster;
+ }
+ }
+ if (p_direction == HB_DIRECTION_RTL || p_direction == HB_DIRECTION_BTT) {
+ w[last_cluster_index].flags |= GRAPHEME_IS_RTL;
+ }
+ if (last_cluster_valid) {
+ w[last_cluster_index].flags |= GRAPHEME_IS_VALID;
+ }
+ w[last_cluster_index].count = i - last_cluster_index;
+ last_cluster_index = i;
+ last_cluster_valid = true;
+ }
+
+ last_cluster_id = glyph_info[i].cluster;
+
+ Glyph &gl = w[i];
+ gl = Glyph();
+
+ gl.start = glyph_info[i].cluster;
+ gl.end = end;
+ gl.count = 0;
+
+ 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) {
+ float 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 {
+ gl.advance = -Math::round(glyph_pos[i].y_advance / (64.0 / scale));
+ }
+ 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);
+ } else {
+ gl.advance += font_get_spacing(f, fs, SPACING_GLYPH);
+ }
+
+ if (p_sd->preserve_control) {
+ 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) || (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) {
+ for (unsigned int j = last_cluster_index; j < glyph_count; j++) {
+ w[j].end = p_end;
+ }
+ }
+ 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 |= GRAPHEME_IS_RTL;
+ }
+ if (last_cluster_valid) {
+ w[last_cluster_index].flags |= GRAPHEME_IS_VALID;
+ }
+
+ // Fallback.
+ int failed_subrun_start = p_end + 1;
+ int failed_subrun_end = p_start;
+
+ for (unsigned int i = 0; i < glyph_count; i++) {
+ if ((w[i].flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) {
+ if (failed_subrun_start != p_end + 1) {
+ _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1);
+ failed_subrun_start = p_end + 1;
+ failed_subrun_end = p_start;
+ }
+ for (int j = 0; j < w[i].count; j++) {
+ if (p_sd->orientation == ORIENTATION_HORIZONTAL) {
+ 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));
+ }
+ p_sd->width += w[i + j].advance;
+ p_sd->glyphs.push_back(w[i + j]);
+ }
+ } else {
+ if (failed_subrun_start >= w[i].start) {
+ failed_subrun_start = w[i].start;
+ }
+ if (failed_subrun_end <= w[i].end) {
+ failed_subrun_end = w[i].end;
+ }
+ }
+ i += w[i].count - 1;
+ }
+ memfree(w);
+ if (failed_subrun_start != p_end + 1) {
+ _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1);
+ }
+ p_sd->ascent = MAX(p_sd->ascent, font_get_ascent(f, fs));
+ p_sd->descent = MAX(p_sd->descent, font_get_descent(f, fs));
+ p_sd->upos = MAX(p_sd->upos, font_get_underline_position(f, fs));
+ p_sd->uthk = MAX(p_sd->uthk, font_get_underline_thickness(f, fs));
+ }
+}
+
+bool TextServerAdvanced::shaped_text_shape(RID p_shaped) {
+ ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ if (sd->valid) {
+ return true;
+ }
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ invalidate(sd);
+
+ if (sd->text.length() == 0) {
+ sd->valid = true;
+ return true;
+ }
+
+ sd->utf16 = sd->text.utf16();
+ const UChar *data = sd->utf16.ptr();
+
+ // Create script iterator.
+ if (sd->script_iter == nullptr) {
+ sd->script_iter = memnew(ScriptIterator(sd->text, 0, sd->text.length()));
+ }
+
+ if (sd->bidi_override.is_empty()) {
+ sd->bidi_override.push_back(Vector2i(0, 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);
+
+ ERR_FAIL_COND_V_MSG((start < 0 || end - start > sd->utf16.length()), false, "Invalid BiDi override range.");
+
+ UErrorCode err = U_ZERO_ERROR;
+ UBiDi *bidi_iter = ubidi_openSized(end, 0, &err);
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err));
+
+ switch (sd->direction) {
+ case DIRECTION_LTR: {
+ ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err);
+ sd->para_direction = DIRECTION_LTR;
+ } break;
+ case DIRECTION_RTL: {
+ ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_RTL, nullptr, &err);
+ sd->para_direction = DIRECTION_RTL;
+ } break;
+ case DIRECTION_AUTO: {
+ UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start);
+ if (direction != UBIDI_NEUTRAL) {
+ ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
+ sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR;
+ } else {
+ ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_DEFAULT_LTR, nullptr, &err);
+ sd->para_direction = DIRECTION_LTR;
+ }
+ } break;
+ }
+ ERR_FAIL_COND_V_MSG(U_FAILURE(err), false, u_errorName(err));
+ 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), 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;
+ hb_direction_t bidi_run_direction = HB_DIRECTION_INVALID;
+ bool is_rtl = (ubidi_getVisualRun(bidi_iter, i, &_bidi_run_start, &_bidi_run_length) == UBIDI_LTR);
+ switch (sd->orientation) {
+ case ORIENTATION_HORIZONTAL: {
+ if (is_rtl) {
+ bidi_run_direction = HB_DIRECTION_LTR;
+ } else {
+ bidi_run_direction = HB_DIRECTION_RTL;
+ }
+ } break;
+ case ORIENTATION_VERTICAL: {
+ if (is_rtl) {
+ bidi_run_direction = HB_DIRECTION_TTB;
+ } else {
+ bidi_run_direction = HB_DIRECTION_BTT;
+ }
+ }
+ }
+
+ 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);
+
+ // Shape runs.
+
+ int scr_from = (is_rtl) ? 0 : sd->script_iter->script_ranges.size() - 1;
+ int scr_to = (is_rtl) ? sd->script_iter->script_ranges.size() : -1;
+ int scr_delta = (is_rtl) ? +1 : -1;
+
+ for (int j = scr_from; j != scr_to; j += scr_delta) {
+ if ((sd->script_iter->script_ranges[j].start < bidi_run_end) && (sd->script_iter->script_ranges[j].end > bidi_run_start)) {
+ int32_t script_run_start = MAX(sd->script_iter->script_ranges[j].start, bidi_run_start);
+ int32_t script_run_end = MIN(sd->script_iter->script_ranges[j].end, bidi_run_end);
+ char scr_buffer[5] = { 0, 0, 0, 0, 0 };
+ hb_tag_to_string(hb_script_to_iso15924_tag(sd->script_iter->script_ranges[j].script), scr_buffer);
+ String script = String(scr_buffer);
+
+ int spn_from = (is_rtl) ? 0 : sd->spans.size() - 1;
+ int spn_to = (is_rtl) ? sd->spans.size() : -1;
+ int spn_delta = (is_rtl) ? +1 : -1;
+
+ 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) {
+ continue;
+ }
+ if (span.embedded_key != Variant()) {
+ // Embedded object.
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->objects[span.embedded_key].rect.position.x = sd->width;
+ sd->width += sd->objects[span.embedded_key].rect.size.x;
+ } else {
+ sd->objects[span.embedded_key].rect.position.y = sd->width;
+ sd->width += sd->objects[span.embedded_key].rect.size.y;
+ }
+ Glyph gl;
+ gl.start = span.start;
+ gl.end = span.end;
+ gl.count = 1;
+ gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_VIRTUAL;
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.advance = sd->objects[span.embedded_key].rect.size.x;
+ } else {
+ gl.advance = sd->objects[span.embedded_key].rect.size.y;
+ }
+ sd->glyphs.push_back(gl);
+ } else {
+ Vector<RID> fonts;
+ Vector<RID> fonts_scr_only;
+ Vector<RID> fonts_no_match;
+ int font_count = span.fonts.size();
+ for (int l = 0; l < font_count; l++) {
+ if (font_is_script_supported(span.fonts[l], script)) {
+ if (font_is_language_supported(span.fonts[l], span.language)) {
+ fonts.push_back(sd->spans[k].fonts[l]);
+ } else {
+ fonts_scr_only.push_back(sd->spans[k].fonts[l]);
+ }
+ } else {
+ fonts_no_match.push_back(sd->spans[k].fonts[l]);
+ }
+ }
+ 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);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Align embedded objects to baseline.
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.y = -sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.y = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.y = sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.x = -sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.x = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.x = sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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;
+ sd->descent = full_descent;
+ sd->valid = true;
+ return sd->valid;
+}
+
+bool TextServerAdvanced::shaped_text_is_ready(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ return sd->valid;
+}
+
+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.ptr();
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->glyphs.size();
+}
+
+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) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+
+ if (!sd->sort_valid) {
+ sd->glyphs_logical = sd->glyphs;
+ sd->glyphs_logical.sort_custom<GlyphCompare>();
+ sd->sort_valid = true;
+ }
+
+ 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.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, ret);
+
+ MutexLock lock(sd->mutex);
+ 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.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, Rect2());
+
+ MutexLock lock(sd->mutex);
+ ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2());
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->objects[p_key].rect;
+}
+
+Size2 TextServerAdvanced::shaped_text_get_size(RID p_shaped) const {
+ const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, Size2());
+
+ MutexLock lock(sd->mutex);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
+ return Size2((sd->text_trimmed ? sd->width_trimmed : sd->width), sd->ascent + sd->descent);
+ } else {
+ return Size2(sd->ascent + sd->descent, (sd->text_trimmed ? sd->width_trimmed : sd->width));
+ }
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->ascent;
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->descent;
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+ return (sd->text_trimmed ? sd->width_trimmed : sd->width);
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+
+ return sd->upos;
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerAdvanced *>(this)->shaped_text_shape(p_shaped);
+ }
+
+ return sd->uthk;
+}
+
+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);
+ }
+
+ // 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 {
+ const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+
+ String res = p_string;
+ for (int i = 0; i < num_systems.size(); i++) {
+ if (num_systems[i].lang.has(lang)) {
+ if (num_systems[i].digits == String()) {
+ return p_string;
+ }
+ res.replace("e", num_systems[i].exp);
+ res.replace("E", num_systems[i].exp);
+ char32_t *data = res.ptrw();
+ for (int j = 0; j < res.size(); j++) {
+ if (data[j] >= 0x30 && data[j] <= 0x39) {
+ data[j] = num_systems[i].digits[data[j] - 0x30];
+ } else if (data[j] == '.' || data[j] == ',') {
+ data[j] = num_systems[i].digits[10];
+ }
+ }
+ break;
+ }
+ }
+ return res;
+}
+
+String TextServerAdvanced::parse_number(const String &p_string, const String &p_language) const {
+ const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+
+ String res = p_string;
+ for (int i = 0; i < num_systems.size(); i++) {
+ if (num_systems[i].lang.has(lang)) {
+ if (num_systems[i].digits == String()) {
+ return p_string;
+ }
+ res.replace(num_systems[i].exp, "e");
+ char32_t *data = res.ptrw();
+ for (int j = 0; j < res.size(); j++) {
+ if (data[j] == num_systems[i].digits[10]) {
+ data[j] = '.';
+ } else {
+ for (int k = 0; k < 10; k++) {
+ if (data[j] == num_systems[i].digits[k]) {
+ data[j] = 0x30 + k;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ return res;
+}
+
+String TextServerAdvanced::percent_sign(const String &p_language) const {
+ const StringName lang = (p_language == "") ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
+
+ for (int i = 0; i < num_systems.size(); i++) {
+ if (num_systems[i].lang.has(lang)) {
+ if (num_systems[i].percent_sign == String()) {
+ return "%";
+ }
+ return num_systems[i].percent_sign;
+ }
+ }
+ return "%";
+}
+
+String TextServerAdvanced::strip_diacritics(const String &p_string) const {
+ UErrorCode err = U_ZERO_ERROR;
+
+ // 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() {
+ _insert_num_systems_lang();
+ _insert_feature_sets();
+ _bmp_create_font_funcs();
+}
+
+TextServerAdvanced::~TextServerAdvanced() {
+ _bmp_free_font_funcs();
+ if (library != nullptr) {
+ FT_Done_FreeType(library);
+ }
+ u_cleanup();
+#ifndef ICU_STATIC_DATA
+ if (icu_data != nullptr) {
+ memfree(icu_data);
+ icu_data = nullptr;
+ }
+#endif
+}
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
new file mode 100644
index 0000000000..5eaff67a6e
--- /dev/null
+++ b/modules/text_server_adv/text_server_adv.h
@@ -0,0 +1,527 @@
+/*************************************************************************/
+/* text_server_adv.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_ADV_H
+#define TEXT_SERVER_ADV_H
+
+/*************************************************************************/
+/* ICU/HarfBuzz/Graphite backed Text Server implementation with BiDi, */
+/* shaping and advanced font features support. */
+/*************************************************************************/
+
+#include "servers/text_server.h"
+
+#include "core/templates/rid_owner.h"
+#include "core/templates/thread_work_pool.h"
+#include "scene/resources/texture.h"
+#include "script_iterator.h"
+
+#include <unicode/ubidi.h>
+#include <unicode/ubrk.h>
+#include <unicode/uchar.h>
+#include <unicode/uclean.h>
+#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" // For freetype, msdfgen.
+
+#ifdef MODULE_FREETYPE_ENABLED
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_STROKER_H
+#include FT_ADVANCES_H
+#include FT_MULTIPLE_MASTERS_H
+#include FT_BBOX_H
+
+#include <hb-ft.h>
+#include <hb-ot.h>
+#endif
+
+#include <hb-icu.h>
+#include <hb.h>
+
+class TextServerAdvanced : public TextServer {
+ GDCLASS(TextServerAdvanced, TextServer);
+ _THREAD_SAFE_CLASS_
+
+ 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;
+
+ // Font cache data.
+
+#ifdef MODULE_FREETYPE_ENABLED
+ mutable FT_Library library = nullptr;
+#endif
+
+ const int rect_range = 2;
+
+ struct FontTexture {
+ Image::Format format;
+ PackedByteArray imgdata;
+ int texture_w = 0;
+ int texture_h = 0;
+ PackedInt32Array offsets;
+ Ref<ImageTexture> texture;
+ };
+
+ struct FontTexturePosition {
+ int index = 0;
+ int x = 0;
+ int y = 0;
+ };
+
+ struct FontGlyph {
+ bool found = false;
+ int texture_idx = -1;
+ Rect2 rect;
+ Rect2 uv_rect;
+ Vector2 advance;
+ };
+
+ struct FontDataForSizeAdvanced {
+ 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;
+
+ Vector2i size;
+
+ Vector<FontTexture> textures;
+ HashMap<int32_t, FontGlyph> glyph_map;
+ Map<Vector2i, Vector2> kerning_map;
+
+ hb_font_t *hb_handle = nullptr;
+
+#ifdef MODULE_FREETYPE_ENABLED
+ FT_Face face = nullptr;
+ FT_StreamRec stream;
+#endif
+
+ ~FontDataForSizeAdvanced() {
+ if (hb_handle != nullptr) {
+ hb_font_destroy(hb_handle);
+ }
+#ifdef MODULE_FREETYPE_ENABLED
+ if (face != nullptr) {
+ FT_Done_Face(face);
+ }
+#endif
+ }
+ };
+
+ struct FontDataAdvanced {
+ Mutex mutex;
+
+ bool antialiased = true;
+ bool msdf = false;
+ int msdf_range = 14;
+ int msdf_source_size = 48;
+ int fixed_size = 0;
+ bool force_autohinter = false;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ Dictionary variation_coordinates;
+ float oversampling = 0.f;
+
+ uint32_t style_flags = 0;
+ String font_name;
+ String style_name;
+
+ Map<Vector2i, FontDataForSizeAdvanced *> cache;
+
+ bool face_init = false;
+ Set<uint32_t> supported_scripts;
+ Dictionary supported_features;
+ Dictionary supported_varaitions;
+
+ // Language/script support override.
+ Map<String, bool> language_support_overrides;
+ Map<String, bool> script_support_overrides;
+
+ PackedByteArray data;
+ const uint8_t *data_ptr;
+ size_t data_size;
+ mutable ThreadWorkPool work_pool;
+
+ ~FontDataAdvanced() {
+ work_pool.finish();
+ for (const Map<Vector2i, FontDataForSizeAdvanced *>::Element *E = cache.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ cache.clear();
+ }
+ };
+
+ _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeAdvanced *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const;
+#ifdef MODULE_MSDFGEN_ENABLED
+ _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataAdvanced *p_font_data, FontDataForSizeAdvanced *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const;
+#endif
+#ifdef MODULE_FREETYPE_ENABLED
+ _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontDataForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
+#endif
+ _FORCE_INLINE_ bool _ensure_glyph(FontDataAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
+ _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataAdvanced *p_font_data, const Vector2i &p_size) const;
+ _FORCE_INLINE_ void _font_clear_cache(FontDataAdvanced *p_font_data);
+ void _generateMTSDF_threaded(uint32_t y, void *p_td) const;
+
+ _FORCE_INLINE_ Vector2i _get_size(const FontDataAdvanced *p_font_data, int p_size) const {
+ if (p_font_data->msdf) {
+ return Vector2i(p_font_data->msdf_source_size, 0);
+ } else if (p_font_data->fixed_size > 0) {
+ return Vector2i(p_font_data->fixed_size, 0);
+ } else {
+ return Vector2i(p_size, 0);
+ }
+ }
+
+ _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataAdvanced *p_font_data, const Vector2i &p_size) const {
+ if (p_font_data->msdf) {
+ return Vector2i(p_font_data->msdf_source_size, 0);
+ } else if (p_font_data->fixed_size > 0) {
+ return Vector2i(p_font_data->fixed_size, MIN(p_size.y, 1));
+ } else {
+ return p_size;
+ }
+ }
+
+ // Shaped text cache data.
+
+ struct ShapedTextDataAdvanced : public ShapedTextData {
+ /* Intermediate data */
+ Char16String utf16;
+ Vector<UBiDi *> bidi_iter;
+ Vector<Vector2i> bidi_override;
+ ScriptIterator *script_iter = nullptr;
+ hb_buffer_t *hb_buffer = nullptr;
+
+ ~ShapedTextDataAdvanced() {
+ for (int i = 0; i < bidi_iter.size(); i++) {
+ ubidi_close(bidi_iter[i]);
+ }
+ if (script_iter) {
+ memdelete(script_iter);
+ }
+ if (hb_buffer) {
+ hb_buffer_destroy(hb_buffer);
+ }
+ }
+ };
+
+ // Common data.
+
+ 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;
+ 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);
+ 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 bmp_font_t {
+ TextServerAdvanced::FontDataForSizeAdvanced *face = nullptr;
+ bool unref = false; /* Whether to destroy bm_face when done. */
+ };
+
+ static bmp_font_t *_bmp_font_create(TextServerAdvanced::FontDataForSizeAdvanced *p_face, bool p_unref);
+ static 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(){};
+
+ void full_copy(ShapedTextDataAdvanced *p_shaped);
+ void invalidate(ShapedTextDataAdvanced *p_shaped);
+
+public:
+ 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;
+
+ 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) 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;
+
+ /* 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_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;
+
+ 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, 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;
+
+ 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, 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, 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, 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, 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, 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;
+
+ 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 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;
+ 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 float font_get_global_oversampling() const override;
+ virtual void font_set_global_oversampling(float 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 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;
+
+ 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 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 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 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 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 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;
+
+ virtual String strip_diacritics(const String &p_string) const override;
+
+ TextServerAdvanced();
+ ~TextServerAdvanced();
+};
+
+#endif // TEXT_SERVER_ADV_H
diff --git a/modules/text_server_fb/SCsub b/modules/text_server_fb/SCsub
new file mode 100644
index 0000000000..31d1db6167
--- /dev/null
+++ b/modules/text_server_fb/SCsub
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+freetype_enabled = env.module_check_dependencies("text_server_fb", ["freetype"], True)
+msdngen_enabled = env.module_check_dependencies("text_server_fb", ["msdfgen"], True)
+
+env_text_server_fb = env_modules.Clone()
+
+if msdngen_enabled:
+ env_text_server_fb.Append(
+ CPPPATH=[
+ "#thirdparty/msdfgen",
+ ]
+ )
+
+if freetype_enabled:
+ env_text_server_fb.Append(
+ CPPPATH=[
+ "#thirdparty/freetype/include",
+ ]
+ )
+
+env_text_server_fb.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/text_server_fb/config.py b/modules/text_server_fb/config.py
new file mode 100644
index 0000000000..275c2b4d53
--- /dev/null
+++ b/modules/text_server_fb/config.py
@@ -0,0 +1,21 @@
+def can_build(env, platform):
+ return True
+
+
+def configure(env):
+ pass
+
+
+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/arkit/register_types.cpp b/modules/text_server_fb/register_types.cpp
index 91069ab364..0b59040ce8 100644
--- a/modules/arkit/register_types.cpp
+++ b/modules/text_server_fb/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,16 +30,19 @@
#include "register_types.h"
-#include "arkit_interface.h"
+#include "text_server_fb.h"
-void register_arkit_types() {
- // does it make sense to register the class?
+void preregister_text_server_fb_types() {
+ GDREGISTER_CLASS(TextServerFallback);
+ if (TextServerManager::get_singleton()) {
+ Ref<TextServerFallback> ts;
+ ts.instantiate();
+ TextServerManager::get_singleton()->add_interface(ts);
+ }
+}
- Ref<ARKitInterface> arkit_interface;
- arkit_interface.instance();
- XRServer::get_singleton()->add_interface(arkit_interface);
+void register_text_server_fb_types() {
}
-void unregister_arkit_types() {
- // should clean itself up nicely :)
+void unregister_text_server_fb_types() {
}
diff --git a/modules/gdnavigation/register_types.h b/modules/text_server_fb/register_types.h
index cdbff1b937..c854db92e5 100644
--- a/modules/gdnavigation/register_types.h
+++ b/modules/text_server_fb/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,14 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-/**
- @author AndreaCatania
-*/
+#ifndef TEXT_SERVER_FB_REGISTER_TYPES_H
+#define TEXT_SERVER_FB_REGISTER_TYPES_H
-#ifndef GDNAVIGATION_REGISTER_TYPES_H
-#define GDNAVIGATION_REGISTER_TYPES_H
+#define MODULE_TEXT_SERVER_FB_HAS_PREREGISTER
-void register_gdnavigation_types();
-void unregister_gdnavigation_types();
+void preregister_text_server_fb_types();
+void register_text_server_fb_types();
+void unregister_text_server_fb_types();
-#endif // GDNAVIGATION_REGISTER_TYPES_H
+#endif // TEXT_SERVER_FB_REGISTER_TYPES_H
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
new file mode 100644
index 0000000000..80ae10c005
--- /dev/null
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -0,0 +1,3275 @@
+/*************************************************************************/
+/* text_server_fb.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_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"
+#include "core/edge-selectors.h"
+#include "msdfgen.h"
+#endif
+
+/*************************************************************************/
+/* Character properties. */
+/*************************************************************************/
+
+_FORCE_INLINE_ bool is_control(char32_t p_char) {
+ return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009F);
+}
+
+_FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
+ return (p_char == 0x0020) || (p_char == 0x00A0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
+}
+
+_FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
+ return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
+}
+
+_FORCE_INLINE_ bool is_punct(char32_t p_char) {
+ return (p_char >= 0x0020 && p_char <= 0x002F) || (p_char >= 0x003A && p_char <= 0x0040) || (p_char >= 0x005B && p_char <= 0x005E) || (p_char == 0x0060) || (p_char >= 0x007B && p_char <= 0x007E) || (p_char >= 0x2000 && p_char <= 0x206F) || (p_char >= 0x3000 && p_char <= 0x303F);
+}
+
+_FORCE_INLINE_ bool is_underscore(char32_t p_char) {
+ return (p_char == 0x005F);
+}
+
+/*************************************************************************/
+
+String TextServerFallback::interface_name = "Fallback";
+uint32_t TextServerFallback::interface_features = 0; // Nothing is supported.
+
+bool TextServerFallback::has_feature(Feature p_feature) const {
+ return (interface_features & p_feature) == p_feature;
+}
+
+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.get_or_null(p_rid);
+ font_owner.free(p_rid);
+ memdelete(fd);
+ } else if (shaped_owner.owns(p_rid)) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_rid);
+ shaped_owner.free(p_rid);
+ memdelete(sd);
+ }
+}
+
+bool TextServerFallback::has(RID p_rid) {
+ _THREAD_SAFE_METHOD_
+ return font_owner.owns(p_rid) || shaped_owner.owns(p_rid);
+}
+
+bool TextServerFallback::load_support_data(const String &p_filename) {
+ return false; // No extra data used.
+}
+
+bool TextServerFallback::save_support_data(const String &p_filename) const {
+ return false; // No extra data used.
+}
+
+bool TextServerFallback::is_locale_right_to_left(const String &p_locale) const {
+ return false; // No RTL support.
+}
+
+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];
+ uint32_t i;
+
+ if (!p_str || !p_len || !*p_str)
+ return OT_TAG(0, 0, 0, 0);
+
+ if (p_len < 0 || p_len > 4) {
+ p_len = 4;
+ }
+ for (i = 0; i < (uint32_t)p_len && p_str[i]; i++) {
+ tag[i] = p_str[i];
+ }
+
+ for (; i < 4; i++) {
+ tag[i] = ' ';
+ }
+
+ return OT_TAG(tag[0], tag[1], tag[2], tag[3]);
+}
+
+int32_t TextServerFallback::name_to_tag(const String &p_name) const {
+ if (feature_sets.has(p_name)) {
+ return feature_sets[p_name];
+ }
+
+ // No readable name, use tag string.
+ return ot_tag_from_string(p_name.replace("custom_", "").ascii().get_data(), -1);
+}
+
+_FORCE_INLINE_ void ot_tag_to_string(int32_t p_tag, char *p_buf) {
+ p_buf[0] = (char)(uint8_t)(p_tag >> 24);
+ p_buf[1] = (char)(uint8_t)(p_tag >> 16);
+ p_buf[2] = (char)(uint8_t)(p_tag >> 8);
+ p_buf[3] = (char)(uint8_t)(p_tag >> 0);
+}
+
+String TextServerFallback::tag_to_name(int32_t p_tag) const {
+ for (const KeyValue<StringName, int32_t> &E : feature_sets) {
+ if (E.value == p_tag) {
+ return E.key;
+ }
+ }
+
+ // No readable name, use tag string.
+ char name[5];
+ memset(name, 0, 5);
+ ot_tag_to_string(p_tag, name);
+ return String("custom_") + String(name);
+}
+
+/*************************************************************************/
+/* Font Glyph Rendering */
+/*************************************************************************/
+
+_FORCE_INLINE_ TextServerFallback::FontTexturePosition TextServerFallback::find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const {
+ FontTexturePosition ret;
+ ret.index = -1;
+
+ int mw = p_width;
+ int mh = p_height;
+
+ for (int i = 0; i < p_data->textures.size(); i++) {
+ const FontTexture &ct = p_data->textures[i];
+
+ if (RenderingServer::get_singleton() != nullptr) {
+ if (ct.texture->get_format() != p_image_format) {
+ continue;
+ }
+ }
+
+ if (mw > ct.texture_w || mh > ct.texture_h) { // Too big for this texture.
+ continue;
+ }
+
+ if (ct.offsets.size() < ct.texture_w) {
+ continue;
+ }
+
+ ret.y = 0x7FFFFFFF;
+ ret.x = 0;
+
+ for (int j = 0; j < ct.texture_w - mw; j++) {
+ int max_y = 0;
+
+ for (int k = j; k < j + mw; k++) {
+ int y = ct.offsets[k];
+ if (y > max_y) {
+ max_y = y;
+ }
+ }
+
+ if (max_y < ret.y) {
+ ret.y = max_y;
+ ret.x = j;
+ }
+ }
+
+ if (ret.y == 0x7FFFFFFF || ret.y + mh > ct.texture_h) {
+ continue; // Fail, could not fit it here.
+ }
+
+ ret.index = i;
+ break;
+ }
+
+ if (ret.index == -1) {
+ // Could not find texture to fit, create one.
+ ret.x = 0;
+ ret.y = 0;
+
+ int texsize = MAX(p_data->size.x * p_data->oversampling * 8, 256);
+ if (mw > texsize) {
+ texsize = mw; // Special case, adapt to it?
+ }
+ if (mh > texsize) {
+ texsize = mh; // Special case, adapt to it?
+ }
+
+ texsize = next_power_of_2(texsize);
+
+ texsize = MIN(texsize, 4096);
+
+ FontTexture tex;
+ tex.texture_w = texsize;
+ tex.texture_h = texsize;
+ tex.format = p_image_format;
+ tex.imgdata.resize(texsize * texsize * p_color_size);
+
+ {
+ // Zero texture.
+ uint8_t *w = tex.imgdata.ptrw();
+ ERR_FAIL_COND_V(texsize * texsize * p_color_size > tex.imgdata.size(), ret);
+ // Initialize the texture to all-white pixels to prevent artifacts when the
+ // font is displayed at a non-default scale with filtering enabled.
+ if (p_color_size == 2) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 2) { // FORMAT_LA8, BW font.
+ w[i + 0] = 255;
+ w[i + 1] = 0;
+ }
+ } else if (p_color_size == 4) {
+ for (int i = 0; i < texsize * texsize * p_color_size; i += 4) { // FORMAT_RGBA8, Color font, Multichannel(+True) SDF.
+ w[i + 0] = 255;
+ w[i + 1] = 255;
+ w[i + 2] = 255;
+ w[i + 3] = 0;
+ }
+ } else {
+ ERR_FAIL_V(ret);
+ }
+ }
+ tex.offsets.resize(texsize);
+ for (int i = 0; i < texsize; i++) { // Zero offsets.
+ tex.offsets.write[i] = 0;
+ }
+
+ p_data->textures.push_back(tex);
+ ret.index = p_data->textures.size() - 1;
+ }
+
+ return ret;
+}
+
+#ifdef MODULE_MSDFGEN_ENABLED
+
+struct MSContext {
+ msdfgen::Point2 position;
+ msdfgen::Shape *shape;
+ msdfgen::Contour *contour;
+};
+
+class DistancePixelConversion {
+ double invRange;
+
+public:
+ _FORCE_INLINE_ explicit DistancePixelConversion(double range) :
+ invRange(1 / range) {}
+ _FORCE_INLINE_ void operator()(float *pixels, const msdfgen::MultiAndTrueDistance &distance) const {
+ pixels[0] = float(invRange * distance.r + .5);
+ pixels[1] = float(invRange * distance.g + .5);
+ pixels[2] = float(invRange * distance.b + .5);
+ pixels[3] = float(invRange * distance.a + .5);
+ }
+};
+
+struct MSDFThreadData {
+ msdfgen::Bitmap<float, 4> *output;
+ msdfgen::Shape *shape;
+ msdfgen::Projection *projection;
+ DistancePixelConversion *distancePixelConversion;
+};
+
+static msdfgen::Point2 ft_point2(const FT_Vector &vector) {
+ return msdfgen::Point2(vector.x / 60.0f, vector.y / 60.0f);
+}
+
+static int ft_move_to(const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ if (!(context->contour && context->contour->edges.empty())) {
+ context->contour = &context->shape->addContour();
+ }
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+static int ft_line_to(const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ msdfgen::Point2 endpoint = ft_point2(*to);
+ if (endpoint != context->position) {
+ context->contour->addEdge(new msdfgen::LinearSegment(context->position, endpoint));
+ context->position = endpoint;
+ }
+ return 0;
+}
+
+static int ft_conic_to(const FT_Vector *control, const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ context->contour->addEdge(new msdfgen::QuadraticSegment(context->position, ft_point2(*control), ft_point2(*to)));
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+static int ft_cubic_to(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user) {
+ MSContext *context = reinterpret_cast<MSContext *>(user);
+ context->contour->addEdge(new msdfgen::CubicSegment(context->position, ft_point2(*control1), ft_point2(*control2), ft_point2(*to)));
+ context->position = ft_point2(*to);
+ return 0;
+}
+
+void TextServerFallback::_generateMTSDF_threaded(uint32_t y, void *p_td) const {
+ MSDFThreadData *td = (MSDFThreadData *)p_td;
+
+ msdfgen::ShapeDistanceFinder<msdfgen::OverlappingContourCombiner<msdfgen::MultiAndTrueDistanceSelector>> distanceFinder(*td->shape);
+ int row = td->shape->inverseYAxis ? td->output->height() - y - 1 : y;
+ for (int col = 0; col < td->output->width(); ++col) {
+ int x = (y % 2) ? td->output->width() - col - 1 : col;
+ msdfgen::Point2 p = td->projection->unproject(msdfgen::Point2(x + .5, y + .5));
+ msdfgen::MultiAndTrueDistance distance = distanceFinder.distance(p);
+ td->distancePixelConversion->operator()(td->output->operator()(x, row), distance);
+ }
+}
+
+_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const {
+ msdfgen::Shape shape;
+
+ shape.contours.clear();
+ shape.inverseYAxis = false;
+
+ MSContext context = {};
+ context.shape = &shape;
+ FT_Outline_Funcs ft_functions;
+ ft_functions.move_to = &ft_move_to;
+ ft_functions.line_to = &ft_line_to;
+ ft_functions.conic_to = &ft_conic_to;
+ ft_functions.cubic_to = &ft_cubic_to;
+ ft_functions.shift = 0;
+ ft_functions.delta = 0;
+
+ int error = FT_Outline_Decompose(outline, &ft_functions, &context);
+ ERR_FAIL_COND_V_MSG(error, FontGlyph(), "FreeType: Outline decomposition error: '" + String(FT_Error_String(error)) + "'.");
+ if (!shape.contours.empty() && shape.contours.back().edges.empty()) {
+ shape.contours.pop_back();
+ }
+
+ if (FT_Outline_Get_Orientation(outline) == 1) {
+ for (int i = 0; i < (int)shape.contours.size(); ++i) {
+ shape.contours[i].reverse();
+ }
+ }
+
+ shape.inverseYAxis = true;
+ shape.normalize();
+
+ msdfgen::Shape::Bounds bounds = shape.getBounds(p_pixel_range);
+
+ FontGlyph chr;
+ chr.found = true;
+ chr.advance = advance.round();
+
+ if (shape.validate() && shape.contours.size() > 0) {
+ int w = (bounds.r - bounds.l);
+ int h = (bounds.t - bounds.b);
+
+ int mw = w + p_rect_margin * 2;
+ int mh = h + p_rect_margin * 2;
+
+ ERR_FAIL_COND_V(mw > 4096, FontGlyph());
+ ERR_FAIL_COND_V(mh > 4096, FontGlyph());
+
+ FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, 4, Image::FORMAT_RGBA8, mw, mh);
+ ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
+ FontTexture &tex = p_data->textures.write[tex_pos.index];
+
+ edgeColoringSimple(shape, 3.0); // Max. angle.
+ 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));
+ msdfgen::MSDFGeneratorConfig config(true, msdfgen::ErrorCorrectionConfig());
+
+ MSDFThreadData td;
+ td.output = &image;
+ td.shape = &shape;
+ td.projection = &projection;
+ td.distancePixelConversion = &distancePixelConversion;
+
+ if (p_font_data->work_pool.get_thread_count() == 0) {
+ p_font_data->work_pool.init();
+ }
+ p_font_data->work_pool.do_work(h, this, &TextServerFallback::_generateMTSDF_threaded, &td);
+
+ msdfgen::msdfErrorCorrection(image, shape, projection, p_pixel_range, config);
+
+ {
+ uint8_t *wr = tex.imgdata.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * 4;
+ ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph());
+ wr[ofs + 0] = (uint8_t)(CLAMP(image(j, i)[0] * 256.f, 0.f, 255.f));
+ 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));
+ }
+ }
+ }
+
+ // Blit to image and texture.
+ {
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, Image::FORMAT_RGBA8, tex.imgdata));
+ if (tex.texture.is_null()) {
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
+ } else {
+ tex.texture->update(img);
+ }
+ }
+ }
+
+ // Update height array.
+ for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
+ tex.offsets.write[k] = tex_pos.y + mh;
+ }
+
+ chr.texture_idx = tex_pos.index;
+
+ chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w, h);
+ chr.rect.position = Vector2(bounds.l, -bounds.t);
+ chr.rect.size = chr.uv_rect.size;
+ }
+ return chr;
+}
+#endif
+
+#ifdef MODULE_FREETYPE_ENABLED
+_FORCE_INLINE_ TextServerFallback::FontGlyph TextServerFallback::rasterize_bitmap(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const {
+ int w = bitmap.width;
+ int h = bitmap.rows;
+
+ int mw = w + p_rect_margin * 2;
+ int mh = h + p_rect_margin * 2;
+
+ ERR_FAIL_COND_V(mw > 4096, FontGlyph());
+ ERR_FAIL_COND_V(mh > 4096, FontGlyph());
+
+ int color_size = bitmap.pixel_mode == FT_PIXEL_MODE_BGRA ? 4 : 2;
+ Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8;
+
+ FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh);
+ ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph());
+
+ // Fit character in char texture.
+
+ FontTexture &tex = p_data->textures.write[tex_pos.index];
+
+ {
+ uint8_t *wr = tex.imgdata.ptrw();
+
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ int ofs = ((i + tex_pos.y + p_rect_margin) * tex.texture_w + j + tex_pos.x + p_rect_margin) * color_size;
+ ERR_FAIL_COND_V(ofs >= tex.imgdata.size(), FontGlyph());
+ switch (bitmap.pixel_mode) {
+ 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 + 1] = (bitmap.buffer[byte] & bit) ? 255 : 0;
+ } break;
+ case FT_PIXEL_MODE_GRAY:
+ wr[ofs + 0] = 255; // grayscale as 1
+ wr[ofs + 1] = bitmap.buffer[i * bitmap.pitch + j];
+ break;
+ case FT_PIXEL_MODE_BGRA: {
+ int ofs_color = i * bitmap.pitch + (j << 2);
+ wr[ofs + 2] = bitmap.buffer[ofs_color + 0];
+ wr[ofs + 1] = bitmap.buffer[ofs_color + 1];
+ wr[ofs + 0] = bitmap.buffer[ofs_color + 2];
+ wr[ofs + 3] = bitmap.buffer[ofs_color + 3];
+ } break;
+ default:
+ ERR_FAIL_V_MSG(FontGlyph(), "Font uses unsupported pixel format: " + itos(bitmap.pixel_mode) + ".");
+ break;
+ }
+ }
+ }
+ }
+
+ // Blit to image and texture.
+ {
+ if (RenderingServer::get_singleton() != nullptr) {
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, require_format, tex.imgdata));
+
+ if (tex.texture.is_null()) {
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
+ } else {
+ tex.texture->update(img);
+ }
+ }
+ }
+
+ // Update height array.
+ for (int k = tex_pos.x; k < tex_pos.x + mw; k++) {
+ tex.offsets.write[k] = tex_pos.y + mh;
+ }
+
+ FontGlyph chr;
+ chr.advance = (advance * p_data->scale / p_data->oversampling).round();
+ 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.size = chr.uv_rect.size * p_data->scale / p_data->oversampling;
+ return chr;
+}
+#endif
+
+/*************************************************************************/
+/* Font Cache */
+/*************************************************************************/
+
+_FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(p_font_data, p_size), false);
+
+ FontDataForSizeFallback *fd = p_font_data->cache[p_size];
+ if (fd->glyph_map.has(p_glyph)) {
+ return fd->glyph_map[p_glyph].found;
+ }
+
+ if (p_glyph == 0) { // Non graphical or invalid glyph, do not render.
+ fd->glyph_map[p_glyph] = FontGlyph();
+ return true;
+ }
+
+#ifdef MODULE_FREETYPE_ENABLED
+ FontGlyph gl;
+ if (fd->face) {
+ FT_Int32 flags = FT_LOAD_DEFAULT;
+
+ bool outline = p_size.y > 0;
+ switch (p_font_data->hinting) {
+ case TextServer::HINTING_NONE:
+ flags |= FT_LOAD_NO_HINTING;
+ break;
+ case TextServer::HINTING_LIGHT:
+ flags |= FT_LOAD_TARGET_LIGHT;
+ break;
+ default:
+ flags |= FT_LOAD_TARGET_NORMAL;
+ break;
+ }
+ if (p_font_data->force_autohinter) {
+ flags |= FT_LOAD_FORCE_AUTOHINT;
+ }
+ if (outline) {
+ flags |= FT_LOAD_NO_BITMAP;
+ } else if (FT_HAS_COLOR(fd->face)) {
+ 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, glyph_index, flags, &h);
+ FT_Get_Advance(fd->face, glyph_index, flags | FT_LOAD_VERTICAL_LAYOUT, &v);
+
+ int error = FT_Load_Glyph(fd->face, glyph_index, flags);
+ if (error) {
+ fd->glyph_map[p_glyph] = FontGlyph();
+ return false;
+ }
+
+ if (!outline) {
+ if (!p_font_data->msdf) {
+ error = FT_Render_Glyph(fd->face->glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
+ }
+ FT_GlyphSlot slot = fd->face->glyph;
+ if (!error) {
+ if (p_font_data->msdf) {
+#ifdef MODULE_MSDFGEN_ENABLED
+ gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
+#else
+ fd->glyph_map[p_glyph] = FontGlyph();
+ ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!");
+#endif
+ } else {
+ gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0);
+ }
+ }
+ } else {
+ FT_Stroker stroker;
+ if (FT_Stroker_New(library, &stroker) != 0) {
+ fd->glyph_map[p_glyph] = FontGlyph();
+ ERR_FAIL_V_MSG(false, "FreeType: Failed to load glyph stroker.");
+ }
+
+ FT_Stroker_Set(stroker, (int)(fd->size.y * fd->oversampling * 16.0), FT_STROKER_LINECAP_BUTT, FT_STROKER_LINEJOIN_ROUND, 0);
+ FT_Glyph glyph;
+ FT_BitmapGlyph glyph_bitmap;
+
+ if (FT_Get_Glyph(fd->face->glyph, &glyph) != 0) {
+ goto cleanup_stroker;
+ }
+ if (FT_Glyph_Stroke(&glyph, stroker, 1) != 0) {
+ goto cleanup_glyph;
+ }
+ if (FT_Glyph_To_Bitmap(&glyph, p_font_data->antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, nullptr, 1) != 0) {
+ goto cleanup_glyph;
+ }
+ glyph_bitmap = (FT_BitmapGlyph)glyph;
+ gl = rasterize_bitmap(fd, rect_range, glyph_bitmap->bitmap, glyph_bitmap->top, glyph_bitmap->left, Vector2());
+
+ cleanup_glyph:
+ FT_Done_Glyph(glyph);
+ cleanup_stroker:
+ FT_Stroker_Done(stroker);
+ }
+ fd->glyph_map[p_glyph] = gl;
+ return gl.found;
+ }
+#endif
+ fd->glyph_map[p_glyph] = FontGlyph();
+ return false;
+}
+
+_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 && (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, "FreeType: Error initializing library: '" + String(FT_Error_String(error)) + "'.");
+ }
+
+ memset(&fd->stream, 0, sizeof(FT_StreamRec));
+ fd->stream.base = (unsigned char *)p_font_data->data_ptr;
+ fd->stream.size = p_font_data->data_size;
+ fd->stream.pos = 0;
+
+ FT_Open_Args fargs;
+ memset(&fargs, 0, sizeof(FT_Open_Args));
+ fargs.memory_base = (unsigned char *)p_font_data->data_ptr;
+ fargs.memory_size = p_font_data->data_size;
+ fargs.flags = FT_OPEN_MEMORY;
+ fargs.stream = &fd->stream;
+ error = FT_Open_Face(library, &fargs, 0, &fd->face);
+ if (error) {
+ FT_Done_Face(fd->face);
+ fd->face = nullptr;
+ 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 = font_get_global_oversampling();
+ } else {
+ fd->oversampling = p_font_data->oversampling;
+ }
+
+ 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 = 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 = 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);
+ }
+
+ 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;
+ }
+ // Read OpenType variations.
+ p_font_data->supported_varaitions.clear();
+ if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
+ FT_MM_Var *amaster;
+ FT_Get_MM_Var(fd->face, &amaster);
+ for (FT_UInt i = 0; i < amaster->num_axis; i++) {
+ p_font_data->supported_varaitions[(int32_t)amaster->axis[i].tag] = Vector3i(amaster->axis[i].minimum / 65536, amaster->axis[i].maximum / 65536, amaster->axis[i].def / 65536);
+ }
+ FT_Done_MM_Var(library, amaster);
+ }
+ p_font_data->face_init = true;
+ }
+
+ // Write variations.
+ if (fd->face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
+ FT_MM_Var *amaster;
+
+ FT_Get_MM_Var(fd->face, &amaster);
+
+ Vector<FT_Fixed> coords;
+ coords.resize(amaster->num_axis);
+
+ FT_Get_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw());
+
+ for (FT_UInt i = 0; i < amaster->num_axis; i++) {
+ // Reset to default.
+ int32_t var_tag = amaster->axis[i].tag;
+ 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)) {
+ var_value = p_font_data->variation_coordinates[var_tag];
+ coords.write[i] = CLAMP(var_value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum);
+ }
+
+ if (p_font_data->variation_coordinates.has(tag_to_name(var_tag))) {
+ var_value = p_font_data->variation_coordinates[tag_to_name(var_tag)];
+ coords.write[i] = CLAMP(var_value * 65536.f, amaster->axis[i].minimum, amaster->axis[i].maximum);
+ }
+ }
+
+ FT_Set_Var_Design_Coordinates(fd->face, coords.size(), coords.ptrw());
+ FT_Done_MM_Var(library, amaster);
+ }
+#else
+ 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;
+ return true;
+}
+
+_FORCE_INLINE_ void TextServerFallback::_font_clear_cache(FontDataFallback *p_font_data) {
+ for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : p_font_data->cache) {
+ memdelete(E.value);
+ }
+
+ p_font_data->cache.clear();
+ p_font_data->face_init = false;
+ p_font_data->supported_varaitions.clear();
+}
+
+RID TextServerFallback::create_font() {
+ FontDataFallback *fd = memnew(FontDataFallback);
+
+ return font_owner.make_rid(fd);
+}
+
+void TextServerFallback::font_set_data(RID p_font_rid, const PackedByteArray &p_data) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ _font_clear_cache(fd);
+ fd->data = p_data;
+ fd->data_ptr = fd->data.ptr();
+ fd->data_size = fd->data.size();
+}
+
+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.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ _font_clear_cache(fd);
+ fd->data.clear();
+ fd->data_ptr = p_data_ptr;
+ fd->data_size = p_data_size;
+}
+
+void TextServerFallback::font_set_style(RID p_font_rid, uint32_t /*FontStyle*/ p_style) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_flags = p_style;
+}
+
+uint32_t /*FontStyle*/ TextServerFallback::font_get_style(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+ return fd->style_flags;
+}
+
+void TextServerFallback::font_set_style_name(RID p_font_rid, const String &p_name) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->style_name = p_name;
+}
+
+String TextServerFallback::font_get_style_name(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->style_name;
+}
+
+void TextServerFallback::font_set_name(RID p_font_rid, const String &p_name) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->font_name = p_name;
+}
+
+String TextServerFallback::font_get_name(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, String());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), String());
+ return fd->font_name;
+}
+
+void TextServerFallback::font_set_antialiased(RID p_font_rid, bool p_antialiased) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->antialiased != p_antialiased) {
+ _font_clear_cache(fd);
+ fd->antialiased = p_antialiased;
+ }
+}
+
+bool TextServerFallback::font_is_antialiased(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->antialiased;
+}
+
+void TextServerFallback::font_set_multichannel_signed_distance_field(RID p_font_rid, bool p_msdf) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf != p_msdf) {
+ _font_clear_cache(fd);
+ fd->msdf = p_msdf;
+ }
+}
+
+bool TextServerFallback::font_is_multichannel_signed_distance_field(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf;
+}
+
+void TextServerFallback::font_set_msdf_pixel_range(RID p_font_rid, int p_msdf_pixel_range) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf_range != p_msdf_pixel_range) {
+ _font_clear_cache(fd);
+ fd->msdf_range = p_msdf_pixel_range;
+ }
+}
+
+int TextServerFallback::font_get_msdf_pixel_range(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf_range;
+}
+
+void TextServerFallback::font_set_msdf_size(RID p_font_rid, int p_msdf_size) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->msdf_source_size != p_msdf_size) {
+ _font_clear_cache(fd);
+ fd->msdf_source_size = p_msdf_size;
+ }
+}
+
+int TextServerFallback::font_get_msdf_size(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->msdf_source_size;
+}
+
+void TextServerFallback::font_set_fixed_size(RID p_font_rid, int p_fixed_size) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->fixed_size != p_fixed_size) {
+ fd->fixed_size = p_fixed_size;
+ }
+}
+
+int TextServerFallback::font_get_fixed_size(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->fixed_size;
+}
+
+void TextServerFallback::font_set_force_autohinter(RID p_font_rid, bool p_force_autohinter) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->force_autohinter != p_force_autohinter) {
+ _font_clear_cache(fd);
+ fd->force_autohinter = p_force_autohinter;
+ }
+}
+
+bool TextServerFallback::font_is_force_autohinter(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->force_autohinter;
+}
+
+void TextServerFallback::font_set_hinting(RID p_font_rid, TextServer::Hinting p_hinting) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->hinting != p_hinting) {
+ _font_clear_cache(fd);
+ fd->hinting = p_hinting;
+ }
+}
+
+TextServer::Hinting TextServerFallback::font_get_hinting(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, HINTING_NONE);
+
+ MutexLock lock(fd->mutex);
+ return fd->hinting;
+}
+
+void TextServerFallback::font_set_variation_coordinates(RID p_font_rid, const Dictionary &p_variation_coordinates) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->variation_coordinates != p_variation_coordinates) {
+ _font_clear_cache(fd);
+ fd->variation_coordinates = p_variation_coordinates;
+ }
+}
+
+Dictionary TextServerFallback::font_get_variation_coordinates(RID p_font_rid) const {
+ 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, float p_oversampling) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->oversampling != p_oversampling) {
+ _font_clear_cache(fd);
+ fd->oversampling = p_oversampling;
+ }
+}
+
+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);
+ return fd->oversampling;
+}
+
+Array TextServerFallback::font_get_size_cache_list(RID p_font_rid) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Array());
+
+ MutexLock lock(fd->mutex);
+ Array ret;
+ for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : fd->cache) {
+ ret.push_back(E.key);
+ }
+ return ret;
+}
+
+void TextServerFallback::font_clear_size_cache(RID p_font_rid) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ for (const KeyValue<Vector2i, FontDataForSizeFallback *> &E : fd->cache) {
+ memdelete(E.value);
+ }
+ fd->cache.clear();
+}
+
+void TextServerFallback::font_remove_size_cache(RID p_font_rid, const Vector2i &p_size) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.has(p_size)) {
+ memdelete(fd->cache[p_size]);
+ fd->cache.erase(p_size);
+ }
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->ascent = p_ascent;
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ 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, float p_descent) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ fd->cache[size]->descent = p_descent;
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ 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, float p_underline_position) {
+ 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));
+ fd->cache[size]->underline_position = p_underline_position;
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ 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, float p_underline_thickness) {
+ 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));
+ fd->cache[size]->underline_thickness = p_underline_thickness;
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ 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, float p_scale) {
+ 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));
+ fd->cache[size]->scale = p_scale;
+}
+
+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);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0.f);
+
+ if (fd->msdf) {
+ 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.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
+ switch (p_spacing) {
+ case TextServer::SPACING_GLYPH: {
+ fd->cache[size]->spacing_glyph = p_value;
+ } break;
+ case TextServer::SPACING_SPACE: {
+ fd->cache[size]->spacing_space = p_value;
+ } break;
+ default: {
+ ERR_FAIL_MSG("Invalid spacing type: " + itos(p_spacing));
+ } break;
+ }
+}
+
+int TextServerFallback::font_get_spacing(RID p_font_rid, int p_size, TextServer::SpacingType p_spacing) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, 0);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+
+ switch (p_spacing) {
+ case TextServer::SPACING_GLYPH: {
+ if (fd->msdf) {
+ return fd->cache[size]->spacing_glyph * (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 * (float)p_size / (float)fd->msdf_source_size;
+ } else {
+ return fd->cache[size]->spacing_space;
+ }
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(0, "Invalid spacing type: " + itos(p_spacing));
+ } break;
+ }
+ return 0;
+}
+
+int TextServerFallback::font_get_texture_count(RID p_font_rid, const Vector2i &p_size) 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_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), 0);
+
+ return fd->cache[size]->textures.size();
+}
+
+void TextServerFallback::font_clear_textures(RID p_font_rid, const Vector2i &p_size) {
+ 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));
+ fd->cache[size]->textures.clear();
+}
+
+void TextServerFallback::font_remove_texture(RID p_font_rid, const Vector2i &p_size, int p_texture_index) {
+ 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_INDEX(p_texture_index, fd->cache[size]->textures.size());
+
+ fd->cache[size]->textures.remove(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.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+ ERR_FAIL_COND(p_image.is_null());
+
+ 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);
+ }
+
+ FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+
+ tex.imgdata = p_image->get_data();
+ tex.texture_w = p_image->get_width();
+ tex.texture_h = p_image->get_height();
+ tex.format = p_image->get_format();
+
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata));
+ tex.texture = Ref<ImageTexture>();
+ tex.texture.instantiate();
+ tex.texture->create_from_image(img);
+}
+
+Ref<Image> TextServerFallback::font_get_texture_image(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Ref<Image>());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Ref<Image>());
+ ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), Ref<Image>());
+
+ const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ Ref<Image> img = memnew(Image(tex.texture_w, tex.texture_h, 0, tex.format, tex.imgdata));
+
+ return img;
+}
+
+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.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);
+ }
+
+ FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ tex.offsets = p_offset;
+}
+
+PackedInt32Array TextServerFallback::font_get_texture_offsets(RID p_font_rid, const Vector2i &p_size, int p_texture_index) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, PackedInt32Array());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), PackedInt32Array());
+ ERR_FAIL_INDEX_V(p_texture_index, fd->cache[size]->textures.size(), PackedInt32Array());
+
+ const FontTexture &tex = fd->cache[size]->textures.write[p_texture_index];
+ return tex.offsets;
+}
+
+Array TextServerFallback::font_get_glyph_list(RID p_font_rid, const Vector2i &p_size) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Array());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
+
+ Array ret;
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+ const int32_t *E = nullptr;
+ while ((E = gl.next(E))) {
+ ret.push_back(*E);
+ }
+ return ret;
+}
+
+void TextServerFallback::font_clear_glyphs(RID p_font_rid, const Vector2i &p_size) {
+ 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));
+
+ fd->cache[size]->glyph_map.clear();
+}
+
+void TextServerFallback::font_remove_glyph(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) {
+ 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));
+
+ fd->cache[size]->glyph_map.erase(p_glyph);
+}
+
+Vector2 TextServerFallback::font_get_glyph_advance(RID p_font_rid, int p_size, int32_t p_glyph) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
+ if (!_ensure_glyph(fd, size, p_glyph)) {
+ return Vector2(); // Invalid or non graphicl glyph, do not display errors.
+ }
+
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ if (fd->msdf) {
+ 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.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));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].advance = p_advance;
+ gl[p_glyph].found = true;
+}
+
+Vector2 TextServerFallback::font_get_glyph_offset(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
+ if (!_ensure_glyph(fd, size, p_glyph)) {
+ return Vector2(); // Invalid or non graphicl glyph, do not display errors.
+ }
+
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ if (fd->msdf) {
+ 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.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));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].rect.position = p_offset;
+ gl[p_glyph].found = true;
+}
+
+Vector2 TextServerFallback::font_get_glyph_size(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
+ if (!_ensure_glyph(fd, size, p_glyph)) {
+ return Vector2(); // Invalid or non graphicl glyph, do not display errors.
+ }
+
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ if (fd->msdf) {
+ 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.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));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].rect.size = p_gl_size;
+ gl[p_glyph].found = true;
+}
+
+Rect2 TextServerFallback::font_get_glyph_uv_rect(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Rect2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Rect2());
+ if (!_ensure_glyph(fd, size, p_glyph)) {
+ return Rect2(); // Invalid or non graphicl glyph, do not display errors.
+ }
+
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+ return gl[p_glyph].uv_rect;
+}
+
+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.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));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].uv_rect = p_uv_rect;
+ gl[p_glyph].found = true;
+}
+
+int TextServerFallback::font_get_glyph_texture_idx(RID p_font_rid, const Vector2i &p_size, int32_t p_glyph) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, -1);
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size_outline(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), -1);
+ if (!_ensure_glyph(fd, size, p_glyph)) {
+ return -1; // Invalid or non graphicl glyph, do not display errors.
+ }
+
+ const HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+ return gl[p_glyph].texture_idx;
+}
+
+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.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));
+
+ HashMap<int32_t, FontGlyph> &gl = fd->cache[size]->glyph_map;
+
+ gl[p_glyph].texture_idx = p_texture_idx;
+ gl[p_glyph].found = true;
+}
+
+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), Dictionary());
+
+ Vector<Vector3> points;
+ Vector<int32_t> contours;
+ bool orientation;
+#ifdef MODULE_FREETYPE_ENABLED
+ 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());
+
+ points.clear();
+ contours.clear();
+
+ 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 * (float)p_size / (float)fd->msdf_source_size;
+ }
+ for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_points; i++) {
+ points.push_back(Vector3(fd->cache[size]->face->glyph->outline.points[i].x * scale, h - fd->cache[size]->face->glyph->outline.points[i].y * scale, FT_CURVE_TAG(fd->cache[size]->face->glyph->outline.tags[i])));
+ }
+ for (short i = 0; i < fd->cache[size]->face->glyph->outline.n_contours; i++) {
+ contours.push_back(fd->cache[size]->face->glyph->outline.contours[i]);
+ }
+ orientation = (FT_Outline_Get_Orientation(&fd->cache[size]->face->glyph->outline) == FT_ORIENTATION_FILL_RIGHT);
+#else
+ return Dictionary();
+#endif
+
+ 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.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Array());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Array());
+
+ Array ret;
+ 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.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));
+ fd->cache[size]->kerning_map.clear();
+}
+
+void TextServerFallback::font_remove_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) {
+ 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));
+ fd->cache[size]->kerning_map.erase(p_glyph_pair);
+}
+
+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.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));
+ fd->cache[size]->kerning_map[p_glyph_pair] = p_kerning;
+}
+
+Vector2 TextServerFallback::font_get_kerning(RID p_font_rid, int p_size, const Vector2i &p_glyph_pair) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, Vector2());
+
+ MutexLock lock(fd->mutex);
+ Vector2i size = _get_size(fd, p_size);
+
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Vector2());
+
+ const Map<Vector2i, Vector2> &kern = fd->cache[size]->kerning_map;
+
+ if (kern.has(p_glyph_pair)) {
+ if (fd->msdf) {
+ return kern[p_glyph_pair] * (float)p_size / (float)fd->msdf_source_size;
+ } else {
+ return kern[p_glyph_pair];
+ }
+ } else {
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face) {
+ FT_Vector 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) * (float)p_size / (float)fd->msdf_source_size;
+ } else {
+ return Vector2(delta.x, delta.y);
+ }
+ }
+#endif
+ }
+ return Vector2();
+}
+
+int32_t TextServerFallback::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_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;
+}
+
+bool TextServerFallback::font_has_char(RID p_font_rid, char32_t p_char) const {
+ 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()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), false);
+ }
+ FontDataForSizeFallback *at_size = fd->cache.front()->get();
+
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ return FT_Get_Char_Index(at_size->face, p_char) != 0;
+ }
+#endif
+ return (at_size) ? at_size->glyph_map.has((int32_t)p_char) : false;
+}
+
+String TextServerFallback::font_get_supported_chars(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);
+ if (fd->cache.is_empty()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), String());
+ }
+ FontDataForSizeFallback *at_size = fd->cache.front()->get();
+
+ String chars;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ FT_UInt gindex;
+ FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex);
+ while (gindex != 0) {
+ if (charcode != 0) {
+ chars += char32_t(charcode);
+ }
+ charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex);
+ }
+ return chars;
+ }
+#endif
+ if (at_size) {
+ const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
+ const int32_t *E = nullptr;
+ while ((E = gl.next(E))) {
+ chars += char32_t(*E);
+ }
+ }
+ return chars;
+}
+
+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.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++) {
+ _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.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(!_ensure_glyph(fd, size, p_index));
+}
+
+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.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.
+ }
+
+ const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+ if (gl.found) {
+ ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
+
+ if (gl.texture_idx != -1) {
+ Color modulate = p_color;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+#endif
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
+ if (fd->msdf) {
+ Point2 cpos = p_pos;
+ 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;
+ cpos += gl.rect.position;
+ Size2i 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);
+ }
+ }
+ }
+ }
+}
+
+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.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.
+ }
+
+ const FontGlyph &gl = fd->cache[size]->glyph_map[p_index];
+ if (gl.found) {
+ ERR_FAIL_COND(gl.texture_idx < -1 || gl.texture_idx >= fd->cache[size]->textures.size());
+
+ if (gl.texture_idx != -1) {
+ Color modulate = p_color;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (fd->cache[size]->face && FT_HAS_COLOR(fd->cache[size]->face)) {
+ modulate.r = modulate.g = modulate.b = 1.0;
+ }
+#endif
+ if (RenderingServer::get_singleton() != nullptr) {
+ RID texture = fd->cache[size]->textures[gl.texture_idx].texture->get_rid();
+ if (fd->msdf) {
+ Point2 cpos = p_pos;
+ 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;
+ cpos += gl.rect.position;
+ Size2i 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);
+ }
+ }
+ }
+ }
+}
+
+bool TextServerFallback::font_is_language_supported(RID p_font_rid, const String &p_language) const {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ if (fd->language_support_overrides.has(p_language)) {
+ return fd->language_support_overrides[p_language];
+ } else {
+ return true;
+ }
+}
+
+void TextServerFallback::font_set_language_support_override(RID p_font_rid, const String &p_language, bool p_supported) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ fd->language_support_overrides[p_language] = p_supported;
+}
+
+bool TextServerFallback::font_get_language_support_override(RID p_font_rid, const String &p_language) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->language_support_overrides[p_language];
+}
+
+void TextServerFallback::font_remove_language_support_override(RID p_font_rid, const String &p_language) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ fd->language_support_overrides.erase(p_language);
+}
+
+Vector<String> TextServerFallback::font_get_language_support_overrides(RID 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 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.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ if (fd->script_support_overrides.has(p_script)) {
+ return fd->script_support_overrides[p_script];
+ } else {
+ return true;
+ }
+}
+
+void TextServerFallback::font_set_script_support_override(RID p_font_rid, const String &p_script, bool p_supported) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND(!fd);
+
+ MutexLock lock(fd->mutex);
+ fd->script_support_overrides[p_script] = p_supported;
+}
+
+bool TextServerFallback::font_get_script_support_override(RID p_font_rid, const String &p_script) {
+ FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
+ ERR_FAIL_COND_V(!fd, false);
+
+ MutexLock lock(fd->mutex);
+ return fd->script_support_overrides[p_script];
+}
+
+void TextServerFallback::font_remove_script_support_override(RID p_font_rid, const String &p_script) {
+ 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->script_support_overrides.erase(p_script);
+}
+
+Vector<String> TextServerFallback::font_get_script_support_overrides(RID 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 KeyValue<String, bool> &E : fd->script_support_overrides) {
+ out.push_back(E.key);
+ }
+ return out;
+}
+
+Dictionary TextServerFallback::font_supported_feature_list(RID p_font_rid) const {
+ return Dictionary();
+}
+
+Dictionary TextServerFallback::font_supported_variation_list(RID p_font_rid) 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, 16);
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size), Dictionary());
+ return fd->supported_varaitions;
+}
+
+float TextServerFallback::font_get_global_oversampling() const {
+ return oversampling;
+}
+
+void TextServerFallback::font_set_global_oversampling(float p_oversampling) {
+ _THREAD_SAFE_METHOD_
+ if (oversampling != p_oversampling) {
+ oversampling = p_oversampling;
+ List<RID> fonts;
+ font_owner.get_owned_list(&fonts);
+ bool font_cleared = false;
+ for (const RID &E : fonts) {
+ if (!font_is_multichannel_signed_distance_field(E) && font_get_oversampling(E) <= 0) {
+ font_clear_size_cache(E);
+ font_cleared = true;
+ }
+ }
+
+ if (font_cleared) {
+ List<RID> text_bufs;
+ shaped_owner.get_owned_list(&text_bufs);
+ for (const RID &E : text_bufs) {
+ invalidate(shaped_owner.get_or_null(E));
+ }
+ }
+ }
+}
+
+/*************************************************************************/
+/* Shaped text buffer interface */
+/*************************************************************************/
+
+void TextServerFallback::invalidate(ShapedTextData *p_shaped) {
+ p_shaped->valid = false;
+ p_shaped->sort_valid = false;
+ p_shaped->line_breaks_valid = false;
+ p_shaped->justification_ops_valid = false;
+ p_shaped->ascent = 0.f;
+ p_shaped->descent = 0.f;
+ p_shaped->width = 0.f;
+ p_shaped->upos = 0.f;
+ p_shaped->uthk = 0.f;
+ p_shaped->glyphs.clear();
+ p_shaped->glyphs_logical.clear();
+}
+
+void TextServerFallback::full_copy(ShapedTextData *p_shaped) {
+ ShapedTextData *parent = shaped_owner.get_or_null(p_shaped->parent);
+
+ 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;
+ }
+ }
+
+ for (int k = 0; k < parent->spans.size(); k++) {
+ ShapedTextData::Span span = parent->spans[k];
+ if (span.start >= p_shaped->end || span.end <= p_shaped->start) {
+ continue;
+ }
+ span.start = MAX(p_shaped->start, span.start);
+ span.end = MIN(p_shaped->end, span.end);
+ p_shaped->spans.push_back(span);
+ }
+
+ p_shaped->parent = RID();
+}
+
+RID TextServerFallback::create_shaped_text(TextServer::Direction p_direction, TextServer::Orientation p_orientation) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = memnew(ShapedTextData);
+ sd->direction = p_direction;
+ sd->orientation = p_orientation;
+
+ return shaped_owner.make_rid(sd);
+}
+
+void TextServerFallback::shaped_text_clear(RID p_shaped) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ sd->parent = RID();
+ sd->start = 0;
+ sd->end = 0;
+ sd->text = String();
+ sd->spans.clear();
+ sd->objects.clear();
+ invalidate(sd);
+}
+
+void TextServerFallback::shaped_text_set_direction(RID p_shaped, TextServer::Direction p_direction) {
+ if (p_direction == DIRECTION_RTL) {
+ ERR_PRINT_ONCE("Right-to-left layout is not supported by this text server.");
+ }
+}
+
+TextServer::Direction TextServerFallback::shaped_text_get_direction(RID p_shaped) const {
+ return TextServer::DIRECTION_LTR;
+}
+
+void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
+ _THREAD_SAFE_METHOD_
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ if (sd->custom_punct != p_punct) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->custom_punct = p_punct;
+ invalidate(sd);
+ }
+}
+
+String TextServerFallback::shaped_text_get_custom_punctuation(RID p_shaped) const {
+ _THREAD_SAFE_METHOD_
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, String());
+ return sd->custom_punct;
+}
+
+void TextServerFallback::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ if (sd->orientation != p_orientation) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->orientation = p_orientation;
+ invalidate(sd);
+ }
+}
+
+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.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, TextServer::ORIENTATION_HORIZONTAL);
+
+ MutexLock lock(sd->mutex);
+ return sd->orientation;
+}
+
+void TextServerFallback::shaped_text_set_preserve_invalid(RID p_shaped, bool p_enabled) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+
+ MutexLock lock(sd->mutex);
+ ERR_FAIL_COND(!sd);
+ if (sd->preserve_invalid != p_enabled) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->preserve_invalid = p_enabled;
+ invalidate(sd);
+ }
+}
+
+bool TextServerFallback::shaped_text_get_preserve_invalid(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ return sd->preserve_invalid;
+}
+
+void TextServerFallback::shaped_text_set_preserve_control(RID p_shaped, bool p_enabled) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND(!sd);
+
+ MutexLock lock(sd->mutex);
+ if (sd->preserve_control != p_enabled) {
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+ sd->preserve_control = p_enabled;
+ invalidate(sd);
+ }
+}
+
+bool TextServerFallback::shaped_text_get_preserve_control(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ return sd->preserve_control;
+}
+
+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.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.get_or_null(p_fonts[i]), false);
+ }
+
+ if (p_text.is_empty()) {
+ return true;
+ }
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+
+ ShapedTextData::Span span;
+ span.start = sd->text.length();
+ span.end = span.start + p_text.length();
+
+ // Pre-sort fonts, push fonts with the language support first.
+ Vector<RID> fonts_no_match;
+ int font_count = p_fonts.size();
+ for (int i = 0; i < font_count; i++) {
+ if (font_is_language_supported(p_fonts[i], p_language)) {
+ span.fonts.push_back(p_fonts[i]);
+ } else {
+ fonts_no_match.push_back(p_fonts[i]);
+ }
+ }
+ span.fonts.append_array(fonts_no_match);
+
+ ERR_FAIL_COND_V(span.fonts.is_empty(), false);
+ span.font_size = p_size;
+ span.language = p_language;
+
+ sd->spans.push_back(span);
+ sd->text += p_text;
+ sd->end += p_text.length();
+ invalidate(sd);
+
+ 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.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ ERR_FAIL_COND_V(p_key == Variant(), false);
+ ERR_FAIL_COND_V(sd->objects.has(p_key), false);
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+
+ ShapedTextData::Span span;
+ span.start = sd->text.length();
+ span.end = span.start + p_length;
+ span.embedded_key = p_key;
+
+ ShapedTextData::EmbeddedObject obj;
+ obj.inline_align = p_inline_align;
+ obj.rect.size = p_size;
+ obj.pos = span.start;
+
+ sd->spans.push_back(span);
+ sd->text += String::chr(0xfffc).repeat(p_length);
+ sd->end += p_length;
+ sd->objects[p_key] = obj;
+ invalidate(sd);
+
+ 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.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ ERR_FAIL_COND_V(!sd->objects.has(p_key), false);
+ sd->objects[p_key].rect.size = p_size;
+ sd->objects[p_key].inline_align = p_inline_align;
+ if (sd->valid) {
+ // Recalc string metrics.
+ sd->ascent = 0;
+ sd->descent = 0;
+ sd->width = 0;
+ sd->upos = 0;
+ sd->uthk = 0;
+ int sd_size = sd->glyphs.size();
+
+ for (int i = 0; i < sd_size; i++) {
+ Glyph gl = sd->glyphs[i];
+ Variant key;
+ if (gl.count == 1) {
+ for (const KeyValue<Variant, ShapedTextData::EmbeddedObject> &E : sd->objects) {
+ if (E.value.pos == gl.start) {
+ key = E.key;
+ break;
+ }
+ }
+ }
+ if (key != Variant()) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->objects[key].rect.position.x = sd->width;
+ sd->width += sd->objects[key].rect.size.x;
+ sd->glyphs.write[i].advance = sd->objects[key].rect.size.x;
+ } else {
+ sd->objects[key].rect.position.y = sd->width;
+ sd->width += sd->objects[key].rect.size.y;
+ sd->glyphs.write[i].advance = sd->objects[key].rect.size.y;
+ }
+ } else {
+ if (gl.font_rid.is_valid()) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size));
+ sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, gl.font_size));
+ } else {
+ sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ sd->descent = MAX(sd->descent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ }
+ sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size));
+ sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size));
+ } 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, 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));
+ }
+ }
+ sd->width += gl.advance * gl.repeat;
+ }
+ }
+
+ // Align embedded objects to baseline.
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.y = -sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.y = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.y = sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.x = -sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.x = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.x = sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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;
+ sd->descent = full_descent;
+ }
+ return true;
+}
+
+RID TextServerFallback::shaped_text_substr(RID p_shaped, int p_start, int p_length) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, RID());
+
+ MutexLock lock(sd->mutex);
+ if (sd->parent != RID()) {
+ return shaped_text_substr(sd->parent, p_start, p_length);
+ }
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ ERR_FAIL_COND_V(p_start < 0 || p_length < 0, RID());
+ ERR_FAIL_COND_V(sd->start > p_start || sd->end < p_start, RID());
+ ERR_FAIL_COND_V(sd->end < p_start + p_length, RID());
+
+ ShapedTextData *new_sd = memnew(ShapedTextData);
+ 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 (p_length > 0) {
+ new_sd->text = sd->text.substr(p_start, p_length);
+ int sd_size = sd->glyphs.size();
+ const Glyph *sd_glyphs = sd->glyphs.ptr();
+
+ for (int i = 0; i < sd_size; i++) {
+ if ((sd_glyphs[i].start >= new_sd->start) && (sd_glyphs[i].end <= new_sd->end)) {
+ Glyph gl = sd_glyphs[i];
+ Variant key;
+ bool find_embedded = false;
+ if (gl.count == 1) {
+ 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.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;
+ } else {
+ new_sd->objects[key].rect.position.y = new_sd->width;
+ new_sd->width += 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, font_get_ascent(gl.font_rid, gl.font_size));
+ new_sd->descent = MAX(new_sd->descent, font_get_descent(gl.font_rid, gl.font_size));
+ } 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));
+ }
+ } 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, 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));
+ }
+ }
+ new_sd->width += gl.advance * gl.repeat;
+ }
+ new_sd->glyphs.push_back(gl);
+ }
+ }
+
+ // Align embedded objects to baseline.
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.y = -new_sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.y = (-new_sd->ascent + new_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.y = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.y = new_sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.x = -new_sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.x = (-new_sd->ascent + new_sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.x = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.x = new_sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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;
+ }
+ new_sd->valid = true;
+
+ return shaped_owner.make_rid(new_sd);
+}
+
+RID TextServerFallback::shaped_text_get_parent(RID p_shaped) const {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, RID());
+
+ MutexLock lock(sd->mutex);
+ return sd->parent;
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (!sd->justification_ops_valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_update_justification_ops(p_shaped);
+ }
+
+ int start_pos = 0;
+ int end_pos = sd->glyphs.size() - 1;
+
+ if ((p_jst_flags & JUSTIFICATION_AFTER_LAST_TAB) == JUSTIFICATION_AFTER_LAST_TAB) {
+ int start, end, delta;
+ if (sd->para_direction == DIRECTION_LTR) {
+ start = sd->glyphs.size() - 1;
+ end = -1;
+ delta = -1;
+ } else {
+ start = 0;
+ end = sd->glyphs.size();
+ delta = +1;
+ }
+
+ for (int i = start; i != end; i += delta) {
+ if ((sd->glyphs[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
+ if (sd->para_direction == DIRECTION_LTR) {
+ start_pos = i;
+ break;
+ } else {
+ end_pos = i;
+ break;
+ }
+ }
+ }
+ }
+
+ if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
+ while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ sd->width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
+ sd->glyphs.write[start_pos].advance = 0;
+ start_pos += sd->glyphs[start_pos].count;
+ }
+ while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
+ sd->width -= sd->glyphs[end_pos].advance * sd->glyphs[end_pos].repeat;
+ sd->glyphs.write[end_pos].advance = 0;
+ end_pos -= sd->glyphs[end_pos].count;
+ }
+ }
+
+ int space_count = 0;
+ for (int i = start_pos; i <= end_pos; i++) {
+ const Glyph &gl = sd->glyphs[i];
+ if (gl.count > 0) {
+ if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
+ space_count++;
+ }
+ }
+ }
+
+ if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
+ 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) {
+ 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);
+ }
+ }
+ }
+ }
+
+ return sd->width;
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (!sd->line_breaks_valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_update_breaks(p_shaped);
+ }
+
+ int tab_index = 0;
+ float off = 0.f;
+
+ int start, end, delta;
+ if (sd->para_direction == DIRECTION_LTR) {
+ start = 0;
+ end = sd->glyphs.size();
+ delta = +1;
+ } else {
+ start = sd->glyphs.size() - 1;
+ end = -1;
+ delta = -1;
+ }
+
+ Glyph *gl = sd->glyphs.ptrw();
+
+ for (int i = start; i != end; i += delta) {
+ if ((gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB) {
+ float tab_off = 0.f;
+ while (tab_off <= off) {
+ tab_off += p_tab_stops[tab_index];
+ tab_index++;
+ if (tab_index >= p_tab_stops.size()) {
+ tab_index = 0;
+ }
+ }
+ float old_adv = gl[i].advance;
+ gl[i].advance = tab_off - off;
+ sd->width += gl[i].advance - old_adv;
+ off = 0;
+ continue;
+ }
+ off += gl[i].advance * gl[i].repeat;
+ }
+
+ return 0.f;
+}
+
+bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ if (!sd->valid) {
+ shaped_text_shape(p_shaped);
+ }
+
+ if (sd->line_breaks_valid) {
+ return true; // Nothing to do.
+ }
+
+ 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 (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[i].flags |= GRAPHEME_IS_SPACE;
+ sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
+ }
+ if (is_linebreak(c)) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
+ }
+ if (c == 0x0009 || c == 0x000b) {
+ sd_glyphs[i].flags |= GRAPHEME_IS_TAB;
+ }
+
+ i += (sd_glyphs[i].count - 1);
+ }
+ }
+ sd->line_breaks_valid = true;
+ return sd->line_breaks_valid;
+}
+
+bool TextServerFallback::shaped_text_update_justification_ops(RID p_shaped) {
+ ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ if (!sd->valid) {
+ shaped_text_shape(p_shaped);
+ }
+ if (!sd->line_breaks_valid) {
+ shaped_text_update_breaks(p_shaped);
+ }
+
+ sd->justification_ops_valid = true; // Not supported by fallback server.
+ return true;
+}
+
+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);
+ if (!sd->valid) {
+ 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;
+ bool cut_per_word = (p_trim_flags & OVERRUN_TRIM_WORD_ONLY) == OVERRUN_TRIM_WORD_ONLY;
+ bool enforce_ellipsis = (p_trim_flags & OVERRUN_ENFORCE_ELLIPSIS) == OVERRUN_ENFORCE_ELLIPSIS;
+ bool justification_aware = (p_trim_flags & OVERRUN_JUSTIFICATION_AWARE) == OVERRUN_JUSTIFICATION_AWARE;
+
+ Glyph *sd_glyphs = sd->glyphs.ptrw();
+
+ if ((p_trim_flags & OVERRUN_TRIM) == OVERRUN_NO_TRIMMING || sd_glyphs == nullptr || p_width <= 0 || !(sd->width > p_width || enforce_ellipsis)) {
+ sd->overrun_trim_data.trim_pos = -1;
+ sd->overrun_trim_data.ellipsis_pos = -1;
+ return;
+ }
+
+ if (justification_aware && !sd->fit_width_minimum_reached) {
+ return;
+ }
+
+ int sd_size = sd->glyphs.size();
+ RID last_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
+ int last_gl_font_size = sd_glyphs[sd_size - 1].font_size;
+ int32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.', 0);
+ Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, dot_gl_idx);
+ int32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' ', 0);
+ Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, whitespace_gl_idx);
+
+ int ellipsis_width = 0;
+ if (add_ellipsis) {
+ ellipsis_width = 3 * dot_adv.x + font_get_spacing(last_gl_font_rid, last_gl_font_size, TextServer::SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
+ }
+
+ int ell_min_characters = 6;
+ float width = sd->width;
+
+ int trim_pos = 0;
+ int ellipsis_pos = (enforce_ellipsis) ? 0 : -1;
+
+ int last_valid_cut = 0;
+ bool found = false;
+
+ for (int i = sd_size - 1; i != -1; i--) {
+ width -= sd_glyphs[i].advance * sd_glyphs[i].repeat;
+
+ if (sd_glyphs[i].count > 0) {
+ bool above_min_char_threshold = (i >= ell_min_characters);
+
+ 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;
+ }
+ } else {
+ last_valid_cut = i;
+ found = true;
+ }
+ if (found) {
+ trim_pos = last_valid_cut;
+
+ if (add_ellipsis && (above_min_char_threshold || enforce_ellipsis) && width - ellipsis_width <= p_width) {
+ ellipsis_pos = trim_pos;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ sd->overrun_trim_data.trim_pos = trim_pos;
+ sd->overrun_trim_data.ellipsis_pos = ellipsis_pos;
+ if (trim_pos == 0 && enforce_ellipsis && add_ellipsis) {
+ sd->overrun_trim_data.ellipsis_pos = 0;
+ }
+
+ if ((trim_pos >= 0 && sd->width > p_width) || enforce_ellipsis) {
+ 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)) {
+ Glyph gl;
+ gl.count = 1;
+ gl.advance = whitespace_adv.x;
+ gl.index = whitespace_gl_idx;
+ gl.font_rid = last_gl_font_rid;
+ gl.font_size = last_gl_font_size;
+ gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL;
+
+ sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
+ }
+ // Add ellipsis dots.
+ Glyph gl;
+ gl.count = 1;
+ gl.repeat = 3;
+ gl.advance = dot_adv.x;
+ gl.index = dot_gl_idx;
+ gl.font_rid = last_gl_font_rid;
+ gl.font_size = last_gl_font_size;
+ gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL;
+
+ sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
+ }
+
+ sd->text_trimmed = true;
+ sd->width_trimmed = width + ((ellipsis_pos != -1) ? ellipsis_width : 0);
+ }
+}
+
+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.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.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ if (sd->valid) {
+ return true;
+ }
+
+ if (sd->parent != RID()) {
+ full_copy(sd);
+ }
+
+ // Cleanup.
+ sd->justification_ops_valid = false;
+ sd->line_breaks_valid = false;
+ sd->ascent = 0.f;
+ sd->descent = 0.f;
+ sd->width = 0.f;
+ sd->glyphs.clear();
+
+ if (sd->text.length() == 0) {
+ sd->valid = true;
+ return true;
+ }
+
+ // "Shape" string.
+ for (int i = 0; i < sd->spans.size(); i++) {
+ const ShapedTextData::Span &span = sd->spans[i];
+ if (span.embedded_key != Variant()) {
+ // Embedded object.
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ sd->objects[span.embedded_key].rect.position.x = sd->width;
+ sd->width += sd->objects[span.embedded_key].rect.size.x;
+ } else {
+ sd->objects[span.embedded_key].rect.position.y = sd->width;
+ sd->width += sd->objects[span.embedded_key].rect.size.y;
+ }
+ Glyph gl;
+ gl.start = span.start;
+ gl.end = span.end;
+ gl.count = 1;
+ gl.index = 0;
+ gl.flags = GRAPHEME_IS_VALID | GRAPHEME_IS_VIRTUAL;
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.advance = sd->objects[span.embedded_key].rect.size.x;
+ } else {
+ gl.advance = sd->objects[span.embedded_key].rect.size.y;
+ }
+ sd->glyphs.push_back(gl);
+ } else {
+ // Text span.
+ for (int j = span.start; j < span.end; j++) {
+ Glyph gl;
+ gl.start = j;
+ gl.end = j + 1;
+ gl.count = 1;
+ gl.font_size = span.font_size;
+ gl.index = (int32_t)sd->text[j]; // Use codepoint.
+ if (gl.index == 0x0009 || gl.index == 0x000b) {
+ gl.index = 0x0020;
+ }
+ if (!sd->preserve_control && is_control(gl.index)) {
+ gl.index = 0x0020;
+ }
+ // Select first font which has character (font are already sorted by span language).
+ for (int k = 0; k < span.fonts.size(); k++) {
+ if (font_has_char(span.fonts[k], gl.index)) {
+ gl.font_rid = span.fonts[k];
+ break;
+ }
+ }
+
+ if (gl.font_rid.is_valid()) {
+ if (sd->text[j] != 0 && !is_linebreak(sd->text[j])) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x;
+ gl.x_off = 0;
+ gl.y_off = 0;
+ sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size));
+ sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, gl.font_size));
+ } else {
+ gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).y;
+ gl.x_off = -Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5);
+ gl.y_off = font_get_ascent(gl.font_rid, gl.font_size);
+ sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
+ 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])) {
+ 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);
+ }
+ sd->upos = MAX(sd->upos, font_get_underline_position(gl.font_rid, gl.font_size));
+ sd->uthk = MAX(sd->uthk, font_get_underline_thickness(gl.font_rid, gl.font_size));
+
+ // Add kerning to previous glyph.
+ if (sd->glyphs.size() > 0) {
+ Glyph &prev_gl = sd->glyphs.write[sd->glyphs.size() - 1];
+ if (prev_gl.font_rid == gl.font_rid && prev_gl.font_size == gl.font_size) {
+ if (sd->orientation == ORIENTATION_HORIZONTAL) {
+ prev_gl.advance += font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).x;
+ } else {
+ prev_gl.advance += font_get_kerning(gl.font_rid, gl.font_size, Vector2i(prev_gl.index, gl.index)).y;
+ }
+ }
+ }
+ } 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) {
+ gl.advance = get_hex_code_box_size(gl.font_size, gl.index).x;
+ 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));
+ sd->descent = MAX(sd->descent, Math::round(get_hex_code_box_size(gl.font_size, gl.index).x * 0.5f));
+ }
+ }
+ sd->width += gl.advance;
+ sd->glyphs.push_back(gl);
+ }
+ }
+ }
+
+ // Align embedded objects to baseline.
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.y = -sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.y = (-sd->ascent + sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.y = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.y = sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.y -= E.value.rect.size.y / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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.value.inline_align & INLINE_ALIGN_TEXT_MASK) {
+ case INLINE_ALIGN_TO_TOP: {
+ E.value.rect.position.x = -sd->ascent;
+ } break;
+ case INLINE_ALIGN_TO_CENTER: {
+ E.value.rect.position.x = (-sd->ascent + sd->descent) / 2;
+ } break;
+ case INLINE_ALIGN_TO_BASELINE: {
+ E.value.rect.position.x = 0;
+ } break;
+ case INLINE_ALIGN_TO_BOTTOM: {
+ E.value.rect.position.x = sd->descent;
+ } break;
+ }
+ switch (E.value.inline_align & INLINE_ALIGN_IMAGE_MASK) {
+ case INLINE_ALIGN_BOTTOM_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x;
+ } break;
+ case INLINE_ALIGN_CENTER_TO: {
+ E.value.rect.position.x -= E.value.rect.size.x / 2;
+ } break;
+ case INLINE_ALIGN_TOP_TO: {
+ // NOP
+ } break;
+ }
+ 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;
+ sd->descent = full_descent;
+ sd->valid = true;
+ return sd->valid;
+}
+
+bool TextServerFallback::shaped_text_is_ready(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, false);
+
+ MutexLock lock(sd->mutex);
+ return sd->valid;
+}
+
+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.ptr();
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->glyphs.size();
+}
+
+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.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.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, ret);
+
+ MutexLock lock(sd->mutex);
+ 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.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, Rect2());
+
+ MutexLock lock(sd->mutex);
+ ERR_FAIL_COND_V(!sd->objects.has(p_key), Rect2());
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->objects[p_key].rect;
+}
+
+Size2 TextServerFallback::shaped_text_get_size(RID p_shaped) const {
+ const ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
+ ERR_FAIL_COND_V(!sd, Size2());
+
+ MutexLock lock(sd->mutex);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ if (sd->orientation == TextServer::ORIENTATION_HORIZONTAL) {
+ return Size2(sd->width, sd->ascent + sd->descent);
+ } else {
+ return Size2(sd->ascent + sd->descent, sd->width);
+ }
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->ascent;
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->descent;
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+ return sd->width;
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+
+ return sd->upos;
+}
+
+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);
+ if (!sd->valid) {
+ const_cast<TextServerFallback *>(this)->shaped_text_shape(p_shaped);
+ }
+
+ return sd->uthk;
+}
+
+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
new file mode 100644
index 0000000000..67b08d1eac
--- /dev/null
+++ b/modules/text_server_fb/text_server_fb.h
@@ -0,0 +1,432 @@
+/*************************************************************************/
+/* text_server_fb.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_FALLBACK_H
+#define TEXT_SERVER_FALLBACK_H
+
+/*************************************************************************/
+/* Fallback Text Server provides simplified TS functionality, without */
+/* BiDi, shaping and advanced font features support. */
+/*************************************************************************/
+
+#include "servers/text_server.h"
+
+#include "core/templates/rid_owner.h"
+#include "core/templates/thread_work_pool.h"
+#include "scene/resources/texture.h"
+
+#include "modules/modules_enabled.gen.h" // For freetype, msdfgen.
+
+#ifdef MODULE_FREETYPE_ENABLED
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_STROKER_H
+#include FT_ADVANCES_H
+#include FT_MULTIPLE_MASTERS_H
+#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_
+
+ 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
+ mutable FT_Library library = nullptr;
+#endif
+
+ const int rect_range = 2;
+
+ struct FontTexture {
+ Image::Format format;
+ PackedByteArray imgdata;
+ int texture_w = 0;
+ int texture_h = 0;
+ PackedInt32Array offsets;
+ Ref<ImageTexture> texture;
+ };
+
+ struct FontTexturePosition {
+ int index = 0;
+ int x = 0;
+ int y = 0;
+ };
+
+ struct FontGlyph {
+ bool found = false;
+ int texture_idx = -1;
+ Rect2 rect;
+ Rect2 uv_rect;
+ Vector2 advance;
+ };
+
+ struct FontDataForSizeFallback {
+ 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;
+
+ Vector2i size;
+
+ Vector<FontTexture> textures;
+ HashMap<int32_t, FontGlyph> glyph_map;
+ Map<Vector2i, Vector2> kerning_map;
+
+#ifdef MODULE_FREETYPE_ENABLED
+ FT_Face face = nullptr;
+ FT_StreamRec stream;
+#endif
+
+ ~FontDataForSizeFallback() {
+#ifdef MODULE_FREETYPE_ENABLED
+ if (face != nullptr) {
+ FT_Done_Face(face);
+ }
+#endif
+ }
+ };
+
+ struct FontDataFallback {
+ Mutex mutex;
+
+ bool antialiased = true;
+ bool msdf = false;
+ int msdf_range = 14;
+ int msdf_source_size = 48;
+ int fixed_size = 0;
+ bool force_autohinter = false;
+ TextServer::Hinting hinting = TextServer::HINTING_LIGHT;
+ Dictionary variation_coordinates;
+ float oversampling = 0.f;
+
+ uint32_t style_flags = 0;
+ String font_name;
+ String style_name;
+
+ Map<Vector2i, FontDataForSizeFallback *> cache;
+
+ bool face_init = false;
+ Dictionary supported_varaitions;
+
+ // Language/script support override.
+ Map<String, bool> language_support_overrides;
+ Map<String, bool> script_support_overrides;
+
+ PackedByteArray data;
+ const uint8_t *data_ptr;
+ size_t data_size;
+
+ mutable ThreadWorkPool work_pool;
+
+ ~FontDataFallback() {
+ work_pool.finish();
+ for (const Map<Vector2i, FontDataForSizeFallback *>::Element *E = cache.front(); E; E = E->next()) {
+ memdelete(E->get());
+ }
+ cache.clear();
+ }
+ };
+
+ _FORCE_INLINE_ FontTexturePosition find_texture_pos_for_glyph(FontDataForSizeFallback *p_data, int p_color_size, Image::Format p_image_format, int p_width, int p_height) const;
+#ifdef MODULE_MSDFGEN_ENABLED
+ _FORCE_INLINE_ FontGlyph rasterize_msdf(FontDataFallback *p_font_data, FontDataForSizeFallback *p_data, int p_pixel_range, int p_rect_margin, FT_Outline *outline, const Vector2 &advance) const;
+#endif
+#ifdef MODULE_FREETYPE_ENABLED
+ _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontDataForSizeFallback *p_data, int p_rect_margin, FT_Bitmap bitmap, int yofs, int xofs, const Vector2 &advance) const;
+#endif
+ _FORCE_INLINE_ bool _ensure_glyph(FontDataFallback *p_font_data, const Vector2i &p_size, int32_t p_glyph) const;
+ _FORCE_INLINE_ bool _ensure_cache_for_size(FontDataFallback *p_font_data, const Vector2i &p_size) const;
+ _FORCE_INLINE_ void _font_clear_cache(FontDataFallback *p_font_data);
+ void _generateMTSDF_threaded(uint32_t y, void *p_td) const;
+
+ _FORCE_INLINE_ Vector2i _get_size(const FontDataFallback *p_font_data, int p_size) const {
+ if (p_font_data->msdf) {
+ return Vector2i(p_font_data->msdf_source_size, 0);
+ } else if (p_font_data->fixed_size > 0) {
+ return Vector2i(p_font_data->fixed_size, 0);
+ } else {
+ return Vector2i(p_size, 0);
+ }
+ }
+
+ _FORCE_INLINE_ Vector2i _get_size_outline(const FontDataFallback *p_font_data, const Vector2i &p_size) const {
+ if (p_font_data->msdf) {
+ return Vector2i(p_font_data->msdf_source_size, 0);
+ } else if (p_font_data->fixed_size > 0) {
+ return Vector2i(p_font_data->fixed_size, MIN(p_size.y, 1));
+ } else {
+ return p_size;
+ }
+ }
+
+ // Common data.
+
+ float oversampling = 1.f;
+ mutable RID_PtrOwner<FontDataFallback> font_owner;
+ mutable RID_PtrOwner<ShapedTextData> shaped_owner;
+
+protected:
+ static void _bind_methods(){};
+
+ void full_copy(ShapedTextData *p_shaped);
+ void invalidate(ShapedTextData *p_shaped);
+
+public:
+ 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;
+
+ 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) 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;
+
+ /* 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_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;
+
+ 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, 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, 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, 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, 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, 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, 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;
+
+ 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 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;
+ 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 float font_get_global_oversampling() const override;
+ virtual void font_set_global_oversampling(float 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 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;
+
+ 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 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 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 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 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 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();
+};
+
+#endif // TEXT_SERVER_FALLBACK_H
diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp
index 1475d24792..f0d7c335bd 100644
--- a/modules/tga/image_loader_tga.cpp
+++ b/modules/tga/image_loader_tga.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,12 +30,12 @@
#include "image_loader_tga.h"
-#include "core/error_macros.h"
+#include "core/error/error_macros.h"
#include "core/io/file_access_memory.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
-Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size) {
+Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size, size_t p_input_size) {
Error error;
Vector<uint8_t> pixels;
@@ -56,7 +56,14 @@ Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t
compressed_pos += 1;
count = (c & 0x7f) + 1;
+ if (output_pos + count * p_pixel_size > p_output_size) {
+ return ERR_PARSE_ERROR;
+ }
+
if (c & 0x80) {
+ if (compressed_pos + p_pixel_size > p_input_size) {
+ return ERR_PARSE_ERROR;
+ }
for (size_t i = 0; i < p_pixel_size; i++) {
pixels_w[i] = p_compressed_buffer[compressed_pos];
compressed_pos += 1;
@@ -68,6 +75,9 @@ Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t
output_pos += p_pixel_size;
}
} else {
+ if (compressed_pos + count * p_pixel_size > p_input_size) {
+ return ERR_PARSE_ERROR;
+ }
count *= p_pixel_size;
for (size_t i = 0; i < count; i++) {
p_uncompressed_buffer[output_pos] = p_compressed_buffer[compressed_pos];
@@ -79,7 +89,7 @@ Error ImageLoaderTGA::decode_tga_rle(const uint8_t *p_compressed_buffer, size_t
return OK;
}
-Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome) {
+Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_input_size) {
#define TGA_PUT_PIXEL(r, g, b, a) \
int image_data_ofs = ((y * width) + x); \
image_data_w[image_data_ofs * 4 + 0] = r; \
@@ -130,6 +140,9 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
if (p_is_monochrome) {
while (y != y_end) {
while (x != x_end) {
+ if (i >= p_input_size) {
+ return ERR_PARSE_ERROR;
+ }
uint8_t shade = p_buffer[i];
TGA_PUT_PIXEL(shade, shade, shade, 0xff)
@@ -143,6 +156,9 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
} else {
while (y != y_end) {
while (x != x_end) {
+ if (i >= p_input_size) {
+ return ERR_PARSE_ERROR;
+ }
uint8_t index = p_buffer[i];
uint8_t r = 0x00;
uint8_t g = 0x00;
@@ -171,6 +187,10 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
} else if (p_header.pixel_depth == 24) {
while (y != y_end) {
while (x != x_end) {
+ if (i + 2 >= p_input_size) {
+ return ERR_PARSE_ERROR;
+ }
+
uint8_t r = p_buffer[i + 2];
uint8_t g = p_buffer[i + 1];
uint8_t b = p_buffer[i + 0];
@@ -186,6 +206,10 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
} else if (p_header.pixel_depth == 32) {
while (y != y_end) {
while (x != x_end) {
+ if (i + 3 >= p_input_size) {
+ return ERR_PARSE_ERROR;
+ }
+
uint8_t a = p_buffer[i + 3];
uint8_t r = p_buffer[i + 2];
uint8_t g = p_buffer[i + 1];
@@ -208,9 +232,9 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
Vector<uint8_t> src_image;
- int src_image_len = f->get_len();
+ uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
- ERR_FAIL_COND_V(src_image_len < (int)sizeof(tga_header_s), ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V(src_image_len < (int64_t)sizeof(tga_header_s), ERR_FILE_CORRUPT);
src_image.resize(src_image_len);
Error err = OK;
@@ -279,7 +303,7 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
const uint8_t *src_image_r = src_image.ptr();
const size_t pixel_size = tga_header.pixel_depth >> 3;
- const size_t buffer_size = (tga_header.image_width * tga_header.image_height) * pixel_size;
+ size_t buffer_size = (tga_header.image_width * tga_header.image_height) * pixel_size;
Vector<uint8_t> uncompressed_buffer;
uncompressed_buffer.resize(buffer_size);
@@ -289,7 +313,7 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
const uint8_t *buffer = nullptr;
if (is_encoded) {
- err = decode_tga_rle(src_image_r, pixel_size, uncompressed_buffer_w, buffer_size);
+ err = decode_tga_rle(src_image_r, pixel_size, uncompressed_buffer_w, buffer_size, src_image_len);
if (err == OK) {
uncompressed_buffer_r = uncompressed_buffer.ptr();
@@ -297,11 +321,12 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
}
} else {
buffer = src_image_r;
+ buffer_size = src_image_len;
};
if (err == OK) {
const uint8_t *palette_r = palette.ptr();
- err = convert_to_image(p_image, buffer, tga_header, palette_r, is_monochrome);
+ err = convert_to_image(p_image, buffer, tga_header, palette_r, is_monochrome, buffer_size);
}
}
@@ -313,12 +338,12 @@ void ImageLoaderTGA::get_recognized_extensions(List<String> *p_extensions) const
p_extensions->push_back("tga");
}
-static Ref<Image> _tga_mem_loader_func(const uint8_t *p_png, int p_size) {
+static Ref<Image> _tga_mem_loader_func(const uint8_t *p_tga, int p_size) {
FileAccessMemory memfile;
- Error open_memfile_error = memfile.open_custom(p_png, p_size);
+ Error open_memfile_error = memfile.open_custom(p_tga, p_size);
ERR_FAIL_COND_V_MSG(open_memfile_error, Ref<Image>(), "Could not create memfile for TGA image buffer.");
Ref<Image> img;
- img.instance();
+ img.instantiate();
Error load_error = ImageLoaderTGA().load_image(img, &memfile, false, 1.0f);
ERR_FAIL_COND_V_MSG(load_error, Ref<Image>(), "Failed to load TGA image.");
return img;
diff --git a/modules/tga/image_loader_tga.h b/modules/tga/image_loader_tga.h
index 6b3d33e7ef..e4463a322f 100644
--- a/modules/tga/image_loader_tga.h
+++ b/modules/tga/image_loader_tga.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -57,23 +57,23 @@ class ImageLoaderTGA : public ImageFormatLoader {
};
struct tga_header_s {
- uint8_t id_length;
- uint8_t color_map_type;
+ uint8_t id_length = 0;
+ uint8_t color_map_type = 0;
tga_type_e image_type;
- uint16_t first_color_entry;
- uint16_t color_map_length;
- uint8_t color_map_depth;
+ uint16_t first_color_entry = 0;
+ uint16_t color_map_length = 0;
+ uint8_t color_map_depth = 0;
- uint16_t x_origin;
- uint16_t y_origin;
- uint16_t image_width;
- uint16_t image_height;
- uint8_t pixel_depth;
- uint8_t image_descriptor;
+ uint16_t x_origin = 0;
+ uint16_t y_origin = 0;
+ uint16_t image_width = 0;
+ uint16_t image_height = 0;
+ uint8_t pixel_depth = 0;
+ uint8_t image_descriptor = 0;
};
- static Error decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size);
- static Error convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome);
+ static Error decode_tga_rle(const uint8_t *p_compressed_buffer, size_t p_pixel_size, uint8_t *p_uncompressed_buffer, size_t p_output_size, size_t p_input_size);
+ static Error convert_to_image(Ref<Image> p_image, const uint8_t *p_buffer, const tga_header_s &p_header, const uint8_t *p_palette, const bool p_is_monochrome, size_t p_input_size);
public:
virtual Error load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale);
diff --git a/modules/tga/register_types.cpp b/modules/tga/register_types.cpp
index 320f748083..9e5fe124ef 100644
--- a/modules/tga/register_types.cpp
+++ b/modules/tga/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/tga/register_types.h b/modules/tga/register_types.h
index 94a77d295e..0dcd750250 100644
--- a/modules/tga/register_types.h
+++ b/modules/tga/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/theora/SCsub b/modules/theora/SCsub
index a01e65b4b0..6038ea086a 100644
--- a/modules/theora/SCsub
+++ b/modules/theora/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_theora = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_libtheora"]:
thirdparty_dir = "#thirdparty/libtheora/"
thirdparty_sources = [
@@ -80,7 +83,16 @@ if env["builtin_libtheora"]:
env_thirdparty = env_theora.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_theora.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_theora.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/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 cb8852d5ef..725f87b046 100644
--- a/modules/theora/doc_classes/VideoStreamTheora.xml
+++ b/modules/theora/doc_classes/VideoStreamTheora.xml
@@ -4,29 +4,24 @@
[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>
</tutorials>
<methods>
<method name="get_file">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Returns the Ogg Theora video file handled by this [VideoStreamTheora].
</description>
</method>
<method name="set_file">
- <return type="void">
- </return>
- <argument index="0" name="file" type="String">
- </argument>
+ <return type="void" />
+ <argument index="0" name="file" type="String" />
<description>
Sets the Ogg Theora video file that this [VideoStreamTheora] resource handles. The [code]file[/code] name should have the [code].ogv[/code] extension.
</description>
</method>
</methods>
- <constants>
- </constants>
</class>
diff --git a/modules/theora/register_types.cpp b/modules/theora/register_types.cpp
index 0676cab5c5..55148a6b87 100644
--- a/modules/theora/register_types.cpp
+++ b/modules/theora/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,10 +35,10 @@
static Ref<ResourceFormatLoaderTheora> resource_loader_theora;
void register_theora_types() {
- resource_loader_theora.instance();
+ resource_loader_theora.instantiate();
ResourceLoader::add_resource_format_loader(resource_loader_theora, true);
- ClassDB::register_class<VideoStreamTheora>();
+ GDREGISTER_CLASS(VideoStreamTheora);
}
void unregister_theora_types() {
diff --git a/modules/theora/register_types.h b/modules/theora/register_types.h
index 4f0670b2c7..654d70e417 100644
--- a/modules/theora/register_types.h
+++ b/modules/theora/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 498391e44a..4f5ae4afb0 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,8 +30,8 @@
#include "video_stream_theora.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "thirdparty/misc/yuv2rgb.h"
@@ -58,7 +58,7 @@ int VideoStreamPlaybackTheora::buffer_data() {
#else
- int bytes = file->get_buffer((uint8_t *)buffer, 4096);
+ uint64_t bytes = file->get_buffer((uint8_t *)buffer, 4096);
ogg_sync_wrote(&oy, bytes);
return (bytes);
@@ -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, true); //zero copy send to visual server
+ texture->update(img); //zero copy send to rendering server
frames_pending = 1;
}
@@ -140,9 +140,7 @@ void VideoStreamPlaybackTheora::clear() {
#ifdef THEORA_USE_THREAD_STREAMING
thread_exit = true;
thread_sem->post(); //just in case
- Thread::wait_to_finish(thread);
- memdelete(thread);
- thread = nullptr;
+ thread.wait_to_finish();
ring_buffer.clear();
#endif
@@ -178,10 +176,10 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
thread_eof = false;
//pre-fill buffer
int to_read = ring_buffer.space_left();
- int read = file->get_buffer(read_buffer.ptr(), to_read);
+ uint64_t read = file->get_buffer(read_buffer.ptr(), to_read);
ring_buffer.write(read_buffer.ptr(), read);
- thread = Thread::create(_streaming_thread, this);
+ thread.start(_streaming_thread, this);
#endif
@@ -227,7 +225,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
/* identify the codec: try theora */
if (!theora_p && th_decode_headerin(&ti, &tc, &ts, &op) >= 0) {
/* it is theora */
- copymem(&to, &test, sizeof(test));
+ memcpy(&to, &test, sizeof(test));
theora_p = 1;
} else if (!vorbis_p && vorbis_synthesis_headerin(&vi, &vc, &op) >= 0) {
/* it is vorbis */
@@ -240,7 +238,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
audio_track_skip--;
} else {
- copymem(&vo, &test, sizeof(test));
+ memcpy(&vo, &test, sizeof(test));
vorbis_p = 1;
}
} else {
@@ -304,7 +302,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
}
}
- /* and now we have it all. initialize decoders */
+ /* And now we have it all. Initialize decoders. */
if (theora_p) {
td = th_decode_alloc(&ti, ts);
px_fmt = ti.pixel_fmt;
@@ -337,7 +335,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
size.y = h;
Ref<Image> img;
- img.instance();
+ img.instantiate();
img->create(w, h, false, Image::FORMAT_RGBA8);
} else {
@@ -486,10 +484,10 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
//printf("frame time %f, play time %f, ready %i\n", (float)videobuf_time, get_time(), videobuf_ready);
- /* is it already too old to be useful? This is only actually
- useful cosmetically after a SIGSTOP. Note that we have to
+ /* is it already too old to be useful? This is only actually
+ useful cosmetically after a SIGSTOP. Note that we have to
decode the frame even if we don't show it (for now) due to
- keyframing. Soon enough libtheora will be able to deal
+ keyframing. Soon enough libtheora will be able to deal
with non-keyframe seeks. */
if (videobuf_time >= get_time()) {
@@ -556,7 +554,7 @@ void VideoStreamPlaybackTheora::play() {
}
playing = true;
- delay_compensation = ProjectSettings::get_singleton()->get("audio/video_delay_compensation_ms");
+ delay_compensation = ProjectSettings::get_singleton()->get("audio/video/video_delay_compensation_ms");
delay_compensation /= 1000.0;
};
@@ -605,6 +603,7 @@ float VideoStreamPlaybackTheora::get_playback_position() const {
};
void VideoStreamPlaybackTheora::seek(float p_time) {
+ 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) {
@@ -633,8 +632,8 @@ void VideoStreamPlaybackTheora::_streaming_thread(void *ud) {
//just fill back the buffer
if (!vs->thread_eof) {
int to_read = vs->ring_buffer.space_left();
- if (to_read) {
- int read = vs->file->get_buffer(vs->read_buffer.ptr(), to_read);
+ if (to_read > 0) {
+ uint64_t read = vs->file->get_buffer(vs->read_buffer.ptr(), to_read);
vs->ring_buffer.write(vs->read_buffer.ptr(), read);
vs->thread_eof = vs->file->eof_reached();
}
@@ -647,31 +646,13 @@ void VideoStreamPlaybackTheora::_streaming_thread(void *ud) {
#endif
VideoStreamPlaybackTheora::VideoStreamPlaybackTheora() {
- file = nullptr;
- theora_p = 0;
- vorbis_p = 0;
- videobuf_ready = 0;
- playing = false;
- frames_pending = 0;
- videobuf_time = 0;
- paused = false;
-
- buffering = false;
texture = Ref<ImageTexture>(memnew(ImageTexture));
- mix_callback = nullptr;
- mix_udata = nullptr;
- audio_track = 0;
- delay_compensation = 0;
- audio_frames_wrote = 0;
#ifdef THEORA_USE_THREAD_STREAMING
int rb_power = nearest_shift(RB_SIZE_KB * 1024);
ring_buffer.resize(rb_power);
read_buffer.resize(RB_SIZE_KB * 1024);
thread_sem = Semaphore::create();
- thread = nullptr;
- thread_exit = false;
- thread_eof = false;
#endif
};
@@ -692,12 +673,12 @@ 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");
}
////////////
-RES ResourceFormatLoaderTheora::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) {
+RES ResourceFormatLoaderTheora::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) {
diff --git a/modules/theora/video_stream_theora.h b/modules/theora/video_stream_theora.h
index 84f816acf8..760173d0df 100644
--- a/modules/theora/video_stream_theora.h
+++ b/modules/theora/video_stream_theora.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,11 +31,12 @@
#ifndef VIDEO_STREAM_THEORA_H
#define VIDEO_STREAM_THEORA_H
+#include "core/io/file_access.h"
#include "core/io/resource_loader.h"
-#include "core/os/file_access.h"
#include "core/os/semaphore.h"
#include "core/os/thread.h"
-#include "core/ring_buffer.h"
+#include "core/templates/ring_buffer.h"
+#include "core/templates/safe_refcount.h"
#include "scene/resources/video_stream.h"
#include "servers/audio_server.h"
@@ -52,12 +53,12 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback {
};
//Image frames[MAX_FRAMES];
- Image::Format format;
+ Image::Format format = Image::Format::FORMAT_L8;
Vector<uint8_t> frame_data;
- int frames_pending;
- FileAccess *file;
+ int frames_pending = 0;
+ FileAccess *file = nullptr;
String file_name;
- int audio_frames_wrote;
+ int audio_frames_wrote = 0;
Point2i size;
int buffer_data();
@@ -65,8 +66,8 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback {
void video_write();
float get_time() const;
- bool theora_eos;
- bool vorbis_eos;
+ bool theora_eos = false;
+ bool vorbis_eos = false;
ogg_sync_state oy;
ogg_page og;
@@ -74,33 +75,33 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback {
ogg_stream_state to;
th_info ti;
th_comment tc;
- th_dec_ctx *td;
+ th_dec_ctx *td = nullptr;
vorbis_info vi;
vorbis_dsp_state vd;
vorbis_block vb;
vorbis_comment vc;
th_pixel_fmt px_fmt;
- double videobuf_time;
- int pp_inc;
+ double videobuf_time = 0;
+ int pp_inc = 0;
- int theora_p;
- int vorbis_p;
- int pp_level_max;
- int pp_level;
- int videobuf_ready;
+ int theora_p = 0;
+ int vorbis_p = 0;
+ int pp_level_max = 0;
+ int pp_level = 0;
+ int videobuf_ready = 0;
- bool playing;
- bool buffering;
+ bool playing = false;
+ bool buffering = false;
- double last_update_time;
- double time;
- double delay_compensation;
+ double last_update_time = 0;
+ double time = 0;
+ double delay_compensation = 0;
Ref<ImageTexture> texture;
AudioMixCallback mix_callback;
- void *mix_udata;
- bool paused;
+ void *mix_udata = nullptr;
+ bool paused = false;
#ifdef THEORA_USE_THREAD_STREAMING
@@ -110,16 +111,16 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback {
RingBuffer<uint8_t> ring_buffer;
Vector<uint8_t> read_buffer;
- bool thread_eof;
+ bool thread_eof = false;
Semaphore *thread_sem;
- Thread *thread;
- volatile bool thread_exit;
+ Thread thread;
+ SafeFlag thread_exit;
static void _streaming_thread(void *ud);
#endif
- int audio_track;
+ int audio_track = 0;
protected:
void clear();
@@ -185,7 +186,7 @@ public:
class ResourceFormatLoaderTheora : 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, bool p_no_cache = false);
+ 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;
diff --git a/modules/tinyexr/SCsub b/modules/tinyexr/SCsub
index 84b3b4015b..30bde96fb4 100644
--- a/modules/tinyexr/SCsub
+++ b/modules/tinyexr/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_tinyexr = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
# Not unbundled for now as they are not commonly available as shared library
thirdparty_dir = "#thirdparty/tinyexr/"
thirdparty_sources = [
@@ -20,7 +23,15 @@ env_tinyexr.Append(CPPDEFINES=["TINYEXR_USE_THREAD"])
env_thirdparty = env_tinyexr.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_tinyexr.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Godot's own source files
-env_tinyexr.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp
index 5bdcb84244..eb7a8597e6 100644
--- a/modules/tinyexr/image_loader_tinyexr.cpp
+++ b/modules/tinyexr/image_loader_tinyexr.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,13 +31,13 @@
#include "image_loader_tinyexr.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include "thirdparty/tinyexr/tinyexr.h"
Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
Vector<uint8_t> src_image;
- int src_image_len = f->get_len();
+ uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
src_image.resize(src_image_len);
diff --git a/modules/tinyexr/image_loader_tinyexr.h b/modules/tinyexr/image_loader_tinyexr.h
index ff040b0915..34390fccb0 100644
--- a/modules/tinyexr/image_loader_tinyexr.h
+++ b/modules/tinyexr/image_loader_tinyexr.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/tinyexr/image_saver_tinyexr.cpp b/modules/tinyexr/image_saver_tinyexr.cpp
index 420619bd5f..6a2fb0f666 100644
--- a/modules/tinyexr/image_saver_tinyexr.cpp
+++ b/modules/tinyexr/image_saver_tinyexr.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -169,7 +169,7 @@ Error save_exr(const String &p_path, const Ref<Image> &p_img, bool p_grayscale)
{ 0 }, // R
{ 1, 0 }, // GR
{ 2, 1, 0 }, // BGR
- { 2, 1, 0, 3 } // BGRA
+ { 3, 2, 1, 0 } // ABGR
};
int channel_count = get_channel_count(format);
diff --git a/modules/tinyexr/image_saver_tinyexr.h b/modules/tinyexr/image_saver_tinyexr.h
index c7154bcfc7..e5060ef11c 100644
--- a/modules/tinyexr/image_saver_tinyexr.h
+++ b/modules/tinyexr/image_saver_tinyexr.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/tinyexr/register_types.cpp b/modules/tinyexr/register_types.cpp
index 9d0fb8729e..ecbabc4951 100644
--- a/modules/tinyexr/register_types.cpp
+++ b/modules/tinyexr/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/tinyexr/register_types.h b/modules/tinyexr/register_types.h
index 9739488312..e401f37066 100644
--- a/modules/tinyexr/register_types.h
+++ b/modules/tinyexr/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/upnp/SCsub b/modules/upnp/SCsub
index 2e129e15ca..b2fed0cb23 100644
--- a/modules/upnp/SCsub
+++ b/modules/upnp/SCsub
@@ -7,21 +7,24 @@ env_upnp = env_modules.Clone()
# Thirdparty source files
+thirdparty_obj = []
+
if env["builtin_miniupnpc"]:
thirdparty_dir = "#thirdparty/miniupnpc/"
thirdparty_sources = [
+ "igd_desc_parse.c",
"miniupnpc.c",
- "upnpcommands.c",
+ "minixml.c",
+ "minisoap.c",
+ "minissdpc.c",
"miniwget.c",
+ "upnpcommands.c",
"upnpdev.c",
- "igd_desc_parse.c",
- "minissdpc.c",
- "minisoap.c",
- "minixml.c",
+ "upnpreplyparse.c",
"connecthostport.c",
- "receivedata.c",
"portlistingparse.c",
- "upnpreplyparse.c",
+ "receivedata.c",
+ "addr_is_reserved.c",
]
thirdparty_sources = [thirdparty_dir + "miniupnpc/" + file for file in thirdparty_sources]
@@ -31,7 +34,16 @@ if env["builtin_miniupnpc"]:
env_thirdparty = env_upnp.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_upnp.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_upnp.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/upnp/doc_classes/UPNP.xml b/modules/upnp/doc_classes/UPNP.xml
index 785c8dad50..2cd0b8843a 100644
--- a/modules/upnp/doc_classes/UPNP.xml
+++ b/modules/upnp/doc_classes/UPNP.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="UPNP" inherits="Reference" version="4.0">
+<class name="UPNP" inherits="RefCounted" version="4.0">
<brief_description>
UPNP network functions.
</brief_description>
@@ -16,32 +16,56 @@
[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>
<methods>
<method name="add_device">
- <return type="void">
- </return>
- <argument index="0" name="device" type="UPNPDevice">
- </argument>
+ <return type="void" />
+ <argument index="0" name="device" type="UPNPDevice" />
<description>
Adds the given [UPNPDevice] to the list of discovered devices.
</description>
</method>
<method name="add_port_mapping" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="port" type="int">
- </argument>
- <argument index="1" name="port_internal" type="int" default="0">
- </argument>
- <argument index="2" name="desc" type="String" default="&quot;&quot;">
- </argument>
- <argument index="3" name="proto" type="String" default="&quot;UDP&quot;">
- </argument>
- <argument index="4" name="duration" type="int" default="0">
- </argument>
+ <return type="int" />
+ <argument index="0" name="port" type="int" />
+ <argument index="1" name="port_internal" type="int" default="0" />
+ <argument index="2" name="desc" type="String" default="&quot;&quot;" />
+ <argument index="3" name="proto" type="String" default="&quot;UDP&quot;" />
+ <argument index="4" name="duration" type="int" default="0" />
<description>
Adds a mapping to forward the external [code]port[/code] (between 1 and 65535) on the default gateway (see [method get_gateway]) to the [code]internal_port[/code] on the local machine for the given protocol [code]proto[/code] (either [code]TCP[/code] or [code]UDP[/code], with UDP being the default). If a port mapping for the given port and protocol combination already exists on that gateway device, this method tries to overwrite it. If that is not desired, you can retrieve the gateway manually with [method get_gateway] and call [method add_port_mapping] on it, if any.
If [code]internal_port[/code] is [code]0[/code] (the default), the same port number is used for both the external and the internal port (the [code]port[/code] value).
@@ -50,32 +74,24 @@
</description>
</method>
<method name="clear_devices">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Clears the list of discovered devices.
</description>
</method>
<method name="delete_port_mapping" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="port" type="int">
- </argument>
- <argument index="1" name="proto" type="String" default="&quot;UDP&quot;">
- </argument>
+ <return type="int" />
+ <argument index="0" name="port" type="int" />
+ <argument index="1" name="proto" type="String" default="&quot;UDP&quot;" />
<description>
Deletes the port mapping for the given port and protocol combination on the default gateway (see [method get_gateway]) if one exists. [code]port[/code] must be a valid port between 1 and 65535, [code]proto[/code] can be either [code]TCP[/code] or [code]UDP[/code]. See [enum UPNPResult] for possible return values.
</description>
</method>
<method name="discover">
- <return type="int">
- </return>
- <argument index="0" name="timeout" type="int" default="2000">
- </argument>
- <argument index="1" name="ttl" type="int" default="2">
- </argument>
- <argument index="2" name="device_filter" type="String" default="&quot;InternetGatewayDevice&quot;">
- </argument>
+ <return type="int" />
+ <argument index="0" name="timeout" type="int" default="2000" />
+ <argument index="1" name="ttl" type="int" default="2" />
+ <argument index="2" name="device_filter" type="String" default="&quot;InternetGatewayDevice&quot;" />
<description>
Discovers local [UPNPDevice]s. Clears the list of previously discovered devices.
Filters for IGD (InternetGatewayDevice) type devices by default, as those manage port forwarding. [code]timeout[/code] is the time to wait for responses in milliseconds. [code]ttl[/code] is the time-to-live; only touch this if you know what you're doing.
@@ -83,51 +99,41 @@
</description>
</method>
<method name="get_device" qualifiers="const">
- <return type="UPNPDevice">
- </return>
- <argument index="0" name="index" type="int">
- </argument>
+ <return type="UPNPDevice" />
+ <argument index="0" name="index" type="int" />
<description>
Returns the [UPNPDevice] at the given [code]index[/code].
</description>
</method>
<method name="get_device_count" qualifiers="const">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Returns the number of discovered [UPNPDevice]s.
</description>
</method>
<method name="get_gateway" qualifiers="const">
- <return type="UPNPDevice">
- </return>
+ <return type="UPNPDevice" />
<description>
Returns the default gateway. That is the first discovered [UPNPDevice] that is also a valid IGD (InternetGatewayDevice).
</description>
</method>
<method name="query_external_address" qualifiers="const">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Returns the external [IP] address of the default gateway (see [method get_gateway]) as string. Returns an empty string on error.
</description>
</method>
<method name="remove_device">
- <return type="void">
- </return>
- <argument index="0" name="index" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
<description>
Removes the device at [code]index[/code] from the list of discovered devices.
</description>
</method>
<method name="set_device">
- <return type="void">
- </return>
- <argument index="0" name="index" type="int">
- </argument>
- <argument index="1" name="device" type="UPNPDevice">
- </argument>
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
+ <argument index="1" name="device" type="UPNPDevice" />
<description>
Sets the device at [code]index[/code] from the list of discovered devices to [code]device[/code].
</description>
diff --git a/modules/upnp/doc_classes/UPNPDevice.xml b/modules/upnp/doc_classes/UPNPDevice.xml
index f3b96bb89d..b7c2ff7dd7 100644
--- a/modules/upnp/doc_classes/UPNPDevice.xml
+++ b/modules/upnp/doc_classes/UPNPDevice.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="UPNPDevice" inherits="Reference" version="4.0">
+<class name="UPNPDevice" inherits="RefCounted" version="4.0">
<brief_description>
UPNP device.
</brief_description>
@@ -10,43 +10,32 @@
</tutorials>
<methods>
<method name="add_port_mapping" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="port" type="int">
- </argument>
- <argument index="1" name="port_internal" type="int" default="0">
- </argument>
- <argument index="2" name="desc" type="String" default="&quot;&quot;">
- </argument>
- <argument index="3" name="proto" type="String" default="&quot;UDP&quot;">
- </argument>
- <argument index="4" name="duration" type="int" default="0">
- </argument>
+ <return type="int" />
+ <argument index="0" name="port" type="int" />
+ <argument index="1" name="port_internal" type="int" default="0" />
+ <argument index="2" name="desc" type="String" default="&quot;&quot;" />
+ <argument index="3" name="proto" type="String" default="&quot;UDP&quot;" />
+ <argument index="4" name="duration" type="int" default="0" />
<description>
Adds a port mapping to forward the given external port on this [UPNPDevice] for the given protocol to the local machine. See [method UPNP.add_port_mapping].
</description>
</method>
<method name="delete_port_mapping" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="port" type="int">
- </argument>
- <argument index="1" name="proto" type="String" default="&quot;UDP&quot;">
- </argument>
+ <return type="int" />
+ <argument index="0" name="port" type="int" />
+ <argument index="1" name="proto" type="String" default="&quot;UDP&quot;" />
<description>
Deletes the port mapping identified by the given port and protocol combination on this device. See [method UPNP.delete_port_mapping].
</description>
</method>
<method name="is_valid_gateway" qualifiers="const">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if this is a valid IGD (InternetGatewayDevice) which potentially supports port forwarding.
</description>
</method>
<method name="query_external_address" qualifiers="const">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Returns the external IP address of this [UPNPDevice] or an empty string.
</description>
diff --git a/modules/upnp/register_types.cpp b/modules/upnp/register_types.cpp
index 34900206de..1e5edd3602 100644
--- a/modules/upnp/register_types.cpp
+++ b/modules/upnp/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,14 +30,14 @@
#include "register_types.h"
-#include "core/error_macros.h"
+#include "core/error/error_macros.h"
#include "upnp.h"
#include "upnp_device.h"
void register_upnp_types() {
- ClassDB::register_class<UPNP>();
- ClassDB::register_class<UPNPDevice>();
+ GDREGISTER_CLASS(UPNP);
+ GDREGISTER_CLASS(UPNPDevice);
}
void unregister_upnp_types() {
diff --git a/modules/upnp/register_types.h b/modules/upnp/register_types.h
index 0c71db9ffa..768031c4d9 100644
--- a/modules/upnp/register_types.h
+++ b/modules/upnp/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/upnp/upnp.cpp b/modules/upnp/upnp.cpp
index 988bf3017d..0e51822b01 100644
--- a/modules/upnp/upnp.cpp
+++ b/modules/upnp/upnp.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -36,11 +36,11 @@
#include <stdlib.h>
bool UPNP::is_common_device(const String &dev) const {
- return dev.empty() ||
- dev.find("InternetGatewayDevice") >= 0 ||
- dev.find("WANIPConnection") >= 0 ||
- dev.find("WANPPPConnection") >= 0 ||
- dev.find("rootdevice") >= 0;
+ return dev.is_empty() ||
+ 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) {
@@ -52,10 +52,12 @@ int UPNP::discover(int timeout, int ttl, const String &device_filter) {
int error = 0;
struct UPNPDev *devlist;
+ CharString cs = discover_multicast_if.utf8();
+ const char *m_if = cs.length() ? cs.get_data() : nullptr;
if (is_common_device(device_filter)) {
- devlist = upnpDiscover(timeout, discover_multicast_if.utf8().get_data(), nullptr, discover_local_port, discover_ipv6, ttl, &error);
+ devlist = upnpDiscover(timeout, m_if, nullptr, discover_local_port, discover_ipv6, ttl, &error);
} else {
- devlist = upnpDiscoverAll(timeout, discover_multicast_if.utf8().get_data(), nullptr, discover_local_port, discover_ipv6, ttl, &error);
+ devlist = upnpDiscoverAll(timeout, m_if, nullptr, discover_local_port, discover_ipv6, ttl, &error);
}
if (error != UPNPDISCOVER_SUCCESS) {
@@ -76,7 +78,7 @@ int UPNP::discover(int timeout, int ttl, const String &device_filter) {
struct UPNPDev *dev = devlist;
while (dev) {
- if (device_filter.empty() || strstr(dev->st, device_filter.utf8().get_data())) {
+ if (device_filter.is_empty() || strstr(dev->st, device_filter.utf8().get_data())) {
add_device_to_list(dev, devlist);
}
@@ -90,7 +92,7 @@ int UPNP::discover(int timeout, int ttl, const String &device_filter) {
void UPNP::add_device_to_list(UPNPDev *dev, UPNPDev *devlist) {
Ref<UPNPDevice> new_device;
- new_device.instance();
+ new_device.instantiate();
new_device->set_description_url(dev->descURL);
new_device->set_service_type(dev->st);
@@ -393,9 +395,6 @@ void UPNP::_bind_methods() {
}
UPNP::UPNP() {
- discover_multicast_if = "";
- discover_local_port = 0;
- discover_ipv6 = false;
}
UPNP::~UPNP() {
diff --git a/modules/upnp/upnp.h b/modules/upnp/upnp.h
index 1c4b5549f4..b961a9667f 100644
--- a/modules/upnp/upnp.h
+++ b/modules/upnp/upnp.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,19 +31,19 @@
#ifndef GODOT_UPNP_H
#define GODOT_UPNP_H
-#include "core/reference.h"
+#include "core/object/ref_counted.h"
#include "upnp_device.h"
#include <miniupnpc/miniupnpc.h>
-class UPNP : public Reference {
- GDCLASS(UPNP, Reference);
+class UPNP : public RefCounted {
+ GDCLASS(UPNP, RefCounted);
private:
- String discover_multicast_if;
- int discover_local_port;
- bool discover_ipv6;
+ String discover_multicast_if = "";
+ int discover_local_port = 0;
+ bool discover_ipv6 = false;
Vector<Ref<UPNPDevice>> devices;
@@ -57,7 +57,6 @@ protected:
public:
enum UPNPResult {
-
UPNP_RESULT_SUCCESS,
UPNP_RESULT_NOT_AUTHORIZED,
UPNP_RESULT_PORT_MAPPING_NOT_FOUND,
diff --git a/modules/upnp/upnp_device.cpp b/modules/upnp/upnp_device.cpp
index 40eb6106a0..ddc66d593c 100644
--- a/modules/upnp/upnp_device.cpp
+++ b/modules/upnp/upnp_device.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -65,7 +65,7 @@ int UPNPDevice::add_port_mapping(int port, int port_internal, String desc, Strin
itos(port).utf8().get_data(),
itos(port_internal).utf8().get_data(),
igd_our_addr.utf8().get_data(),
- desc.empty() ? nullptr : desc.utf8().get_data(),
+ desc.is_empty() ? nullptr : desc.utf8().get_data(),
proto.utf8().get_data(),
nullptr, // Remote host, always nullptr as IGDs don't support it
duration > 0 ? itos(duration).utf8().get_data() : nullptr);
diff --git a/modules/upnp/upnp_device.h b/modules/upnp/upnp_device.h
index 4b519fce32..0a66c36ab9 100644
--- a/modules/upnp/upnp_device.h
+++ b/modules/upnp/upnp_device.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,14 +31,13 @@
#ifndef GODOT_UPNP_DEVICE_H
#define GODOT_UPNP_DEVICE_H
-#include "core/reference.h"
+#include "core/object/ref_counted.h"
-class UPNPDevice : public Reference {
- GDCLASS(UPNPDevice, Reference);
+class UPNPDevice : public RefCounted {
+ GDCLASS(UPNPDevice, RefCounted);
public:
enum IGDStatus {
-
IGD_STATUS_OK,
IGD_STATUS_HTTP_ERROR,
IGD_STATUS_HTTP_EMPTY,
diff --git a/modules/vhacd/SCsub b/modules/vhacd/SCsub
index ecd432b275..1ff4122114 100644
--- a/modules/vhacd/SCsub
+++ b/modules/vhacd/SCsub
@@ -7,6 +7,8 @@ env_vhacd = env_modules.Clone()
# Thirdparty source files
+thirdparty_obj = []
+
thirdparty_dir = "#thirdparty/vhacd/"
thirdparty_sources = [
@@ -24,10 +26,19 @@ thirdparty_sources = [
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
-env_vhacd.Prepend(CPPPATH=[thirdparty_dir + "/inc"])
+env_vhacd.Prepend(CPPPATH=[thirdparty_dir + "inc"])
env_thirdparty = env_vhacd.Clone()
env_thirdparty.disable_warnings()
-env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+
+module_obj = []
+
+env_vhacd.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-env_vhacd.add_source_files(env.modules_sources, "*.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/vhacd/config.py b/modules/vhacd/config.py
index d22f9454ed..a42f27fbe1 100644
--- a/modules/vhacd/config.py
+++ b/modules/vhacd/config.py
@@ -1,5 +1,5 @@
def can_build(env, platform):
- return True
+ return not env["disable_3d"]
def configure(env):
diff --git a/modules/vhacd/register_types.cpp b/modules/vhacd/register_types.cpp
index 40c5e47440..54240e66fc 100644
--- a/modules/vhacd/register_types.cpp
+++ b/modules/vhacd/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,44 +32,55 @@
#include "scene/resources/mesh.h"
#include "thirdparty/vhacd/public/VHACD.h"
-static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces) {
- Vector<float> 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;
+ 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();
- VHACD::IVHACD::Parameters params;
- 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();
@@ -79,9 +90,9 @@ static Vector<Vector<Face3>> convex_decompose(const Vector<Face3> &p_faces) {
}
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/vhacd/register_types.h b/modules/vhacd/register_types.h
index d02a990901..24ad9378f4 100644
--- a/modules/vhacd/register_types.h
+++ b/modules/vhacd/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
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 088d84d2ec..a452974014 100644
--- a/modules/visual_script/doc_classes/VisualScript.xml
+++ b/modules/visual_script/doc_classes/VisualScript.xml
@@ -4,491 +4,343 @@
A script implemented in the Visual Script programming environment.
</brief_description>
<description>
- A script implemented in the Visual Script programming environment. The script extends the functionality of all objects that instance it.
+ A script implemented in the Visual Script programming environment. The script extends the functionality of all objects that instance it.
[method Object.set_script] extends an existing object, if that object's class matches one of the script's base classes.
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">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
<description>
Add a custom signal with the specified name to the VisualScript.
</description>
</method>
<method name="add_function">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="func_node_id" type="int" />
<description>
- Add a function with the specified name to the VisualScript.
+ Add a function with the specified name to the VisualScript, and assign the root [VisualScriptFunction] node's id as [code]func_node_id[/code].
</description>
</method>
<method name="add_node">
- <return type="void">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="id" type="int">
- </argument>
- <argument index="2" name="node" type="VisualScriptNode">
- </argument>
- <argument index="3" name="position" type="Vector2" default="Vector2( 0, 0 )">
- </argument>
+ <return type="void" />
+ <argument index="0" name="id" type="int" />
+ <argument index="1" name="node" type="VisualScriptNode" />
+ <argument index="2" name="position" type="Vector2" default="Vector2(0, 0)" />
<description>
- Add a node to a function of the VisualScript.
+ Add a node to the VisualScript.
</description>
</method>
<method name="add_variable">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="default_value" type="Variant" default="null">
- </argument>
- <argument index="2" name="export" type="bool" default="false">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="default_value" type="Variant" default="null" />
+ <argument index="2" name="export" type="bool" default="false" />
<description>
Add a variable to the VisualScript, optionally giving it a default value or marking it as exported.
</description>
</method>
<method name="custom_signal_add_argument">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="type" type="int" enum="Variant.Type">
- </argument>
- <argument index="2" name="argname" type="String">
- </argument>
- <argument index="3" name="index" type="int" default="-1">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="type" type="int" enum="Variant.Type" />
+ <argument index="2" name="argname" type="String" />
+ <argument index="3" name="index" type="int" default="-1" />
<description>
Add an argument to a custom signal added with [method add_custom_signal].
</description>
</method>
<method name="custom_signal_get_argument_count" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="int" />
+ <argument index="0" name="name" type="StringName" />
<description>
Get the count of a custom signal's arguments.
</description>
</method>
<method name="custom_signal_get_argument_name" qualifiers="const">
- <return type="String">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="argidx" type="int">
- </argument>
+ <return type="String" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="argidx" type="int" />
<description>
Get the name of a custom signal's argument.
</description>
</method>
<method name="custom_signal_get_argument_type" qualifiers="const">
- <return type="int" enum="Variant.Type">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="argidx" type="int">
- </argument>
+ <return type="int" enum="Variant.Type" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="argidx" type="int" />
<description>
Get the type of a custom signal's argument.
</description>
</method>
<method name="custom_signal_remove_argument">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="argidx" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="argidx" type="int" />
<description>
Remove a specific custom signal's argument.
</description>
</method>
<method name="custom_signal_set_argument_name">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="argidx" type="int">
- </argument>
- <argument index="2" name="argname" type="String">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="argidx" type="int" />
+ <argument index="2" name="argname" type="String" />
<description>
Rename a custom signal's argument.
</description>
</method>
<method name="custom_signal_set_argument_type">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="argidx" type="int">
- </argument>
- <argument index="2" name="type" type="int" enum="Variant.Type">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="argidx" type="int" />
+ <argument index="2" name="type" type="int" enum="Variant.Type" />
<description>
Change the type of a custom signal's argument.
</description>
</method>
<method name="custom_signal_swap_argument">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="argidx" type="int">
- </argument>
- <argument index="2" name="withidx" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="argidx" type="int" />
+ <argument index="2" name="withidx" type="int" />
<description>
Swap two of the arguments of a custom signal.
</description>
</method>
<method name="data_connect">
- <return type="void">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="from_node" type="int">
- </argument>
- <argument index="2" name="from_port" type="int">
- </argument>
- <argument index="3" name="to_node" type="int">
- </argument>
- <argument index="4" name="to_port" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="from_node" type="int" />
+ <argument index="1" name="from_port" type="int" />
+ <argument index="2" name="to_node" type="int" />
+ <argument index="3" name="to_port" type="int" />
<description>
Connect two data ports. The value of [code]from_node[/code]'s [code]from_port[/code] would be fed into [code]to_node[/code]'s [code]to_port[/code].
</description>
</method>
<method name="data_disconnect">
- <return type="void">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="from_node" type="int">
- </argument>
- <argument index="2" name="from_port" type="int">
- </argument>
- <argument index="3" name="to_node" type="int">
- </argument>
- <argument index="4" name="to_port" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="from_node" type="int" />
+ <argument index="1" name="from_port" type="int" />
+ <argument index="2" name="to_node" type="int" />
+ <argument index="3" name="to_port" type="int" />
<description>
Disconnect two data ports previously connected with [method data_connect].
</description>
</method>
<method name="get_function_node_id" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="int" />
+ <argument index="0" name="name" type="StringName" />
<description>
Returns the id of a function's entry point node.
</description>
</method>
- <method name="get_function_scroll" qualifiers="const">
- <return type="Vector2">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <description>
- Returns the position of the center of the screen for a given function.
- </description>
- </method>
<method name="get_node" qualifiers="const">
- <return type="VisualScriptNode">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="id" type="int">
- </argument>
+ <return type="VisualScriptNode" />
+ <argument index="0" name="id" type="int" />
<description>
- Returns a node given its id and its function.
+ Returns a node given its id.
</description>
</method>
<method name="get_node_position" qualifiers="const">
- <return type="Vector2">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="id" type="int">
- </argument>
+ <return type="Vector2" />
+ <argument index="0" name="id" type="int" />
<description>
Returns a node's position in pixels.
</description>
</method>
+ <method name="get_scroll" qualifiers="const">
+ <return type="Vector2" />
+ <description>
+ Returns the current position of the center of the screen.
+ </description>
+ </method>
<method name="get_variable_default_value" qualifiers="const">
- <return type="Variant">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="Variant" />
+ <argument index="0" name="name" type="StringName" />
<description>
Returns the default (initial) value of a variable.
</description>
</method>
<method name="get_variable_export" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="name" type="StringName" />
<description>
Returns whether a variable is exported.
</description>
</method>
<method name="get_variable_info" qualifiers="const">
- <return type="Dictionary">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="Dictionary" />
+ <argument index="0" name="name" type="StringName" />
<description>
Returns the information for a given variable as a dictionary. The information includes its name, type, hint and usage.
</description>
</method>
<method name="has_custom_signal" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="name" type="StringName" />
<description>
Returns whether a signal exists with the specified name.
</description>
</method>
<method name="has_data_connection" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="from_node" type="int">
- </argument>
- <argument index="2" name="from_port" type="int">
- </argument>
- <argument index="3" name="to_node" type="int">
- </argument>
- <argument index="4" name="to_port" type="int">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="from_node" type="int" />
+ <argument index="1" name="from_port" type="int" />
+ <argument index="2" name="to_node" type="int" />
+ <argument index="3" name="to_port" type="int" />
<description>
Returns whether the specified data ports are connected.
</description>
</method>
<method name="has_function" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="name" type="StringName" />
<description>
Returns whether a function exists with the specified name.
</description>
</method>
<method name="has_node" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="id" type="int">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="id" type="int" />
<description>
Returns whether a node exists with the given id.
</description>
</method>
<method name="has_sequence_connection" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="from_node" type="int">
- </argument>
- <argument index="2" name="from_output" type="int">
- </argument>
- <argument index="3" name="to_node" type="int">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="from_node" type="int" />
+ <argument index="1" name="from_output" type="int" />
+ <argument index="2" name="to_node" type="int" />
<description>
Returns whether the specified sequence ports are connected.
</description>
</method>
<method name="has_variable" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="name" type="StringName" />
<description>
Returns whether a variable exists with the specified name.
</description>
</method>
<method name="remove_custom_signal">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
<description>
Remove a custom signal with the given name.
</description>
</method>
<method name="remove_function">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
<description>
Remove a specific function and its nodes from the script.
</description>
</method>
<method name="remove_node">
- <return type="void">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="id" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="id" type="int" />
<description>
- Remove a specific node.
+ Remove the node with the specified id.
</description>
</method>
<method name="remove_variable">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
<description>
Remove a variable with the given name.
</description>
</method>
<method name="rename_custom_signal">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="new_name" type="StringName">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="new_name" type="StringName" />
<description>
Change the name of a custom signal.
</description>
</method>
<method name="rename_function">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="new_name" type="StringName">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="new_name" type="StringName" />
<description>
Change the name of a function.
</description>
</method>
<method name="rename_variable">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="new_name" type="StringName">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="new_name" type="StringName" />
<description>
Change the name of a variable.
</description>
</method>
<method name="sequence_connect">
- <return type="void">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="from_node" type="int">
- </argument>
- <argument index="2" name="from_output" type="int">
- </argument>
- <argument index="3" name="to_node" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="from_node" type="int" />
+ <argument index="1" name="from_output" type="int" />
+ <argument index="2" name="to_node" type="int" />
<description>
Connect two sequence ports. The execution will flow from of [code]from_node[/code]'s [code]from_output[/code] into [code]to_node[/code].
Unlike [method data_connect], there isn't a [code]to_port[/code], since the target node can have only one sequence port.
</description>
</method>
<method name="sequence_disconnect">
- <return type="void">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="from_node" type="int">
- </argument>
- <argument index="2" name="from_output" type="int">
- </argument>
- <argument index="3" name="to_node" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="from_node" type="int" />
+ <argument index="1" name="from_output" type="int" />
+ <argument index="2" name="to_node" type="int" />
<description>
Disconnect two sequence ports previously connected with [method sequence_connect].
</description>
</method>
- <method name="set_function_scroll">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="ofs" type="Vector2">
- </argument>
- <description>
- Position the center of the screen for a function.
- </description>
- </method>
<method name="set_instance_base_type">
- <return type="void">
- </return>
- <argument index="0" name="type" type="StringName">
- </argument>
+ <return type="void" />
+ <argument index="0" name="type" type="StringName" />
<description>
Set the base type of the script.
</description>
</method>
<method name="set_node_position">
- <return type="void">
- </return>
- <argument index="0" name="func" type="StringName">
- </argument>
- <argument index="1" name="id" type="int">
- </argument>
- <argument index="2" name="position" type="Vector2">
- </argument>
+ <return type="void" />
+ <argument index="0" name="id" type="int" />
+ <argument index="1" name="position" type="Vector2" />
+ <description>
+ Set the node position in the VisualScript graph.
+ </description>
+ </method>
+ <method name="set_scroll">
+ <return type="void" />
+ <argument index="0" name="ofs" type="Vector2" />
<description>
- Position a node on the screen.
+ Set the screen center to the given position.
</description>
</method>
<method name="set_variable_default_value">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="value" type="Variant">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="value" type="Variant" />
<description>
Change the default (initial) value of a variable.
</description>
</method>
<method name="set_variable_export">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="enable" type="bool">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="enable" type="bool" />
<description>
Change whether a variable is exported.
</description>
</method>
<method name="set_variable_info">
- <return type="void">
- </return>
- <argument index="0" name="name" type="StringName">
- </argument>
- <argument index="1" name="value" type="Dictionary">
- </argument>
+ <return type="void" />
+ <argument index="0" name="name" type="StringName" />
+ <argument index="1" name="value" type="Dictionary" />
<description>
Set a variable's info, using the same format as [method get_variable_info].
</description>
@@ -496,15 +348,10 @@
</methods>
<signals>
<signal name="node_ports_changed">
- <argument index="0" name="function" type="String">
- </argument>
- <argument index="1" name="id" type="int">
- </argument>
+ <argument index="0" name="id" type="int" />
<description>
Emitted when the ports of a node are changed.
</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 ef4183e6f6..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.
@@ -92,7 +90,7 @@
<constant name="MATH_STEP_DECIMALS" value="24" enum="BuiltinFunc">
Return the number of digit places after the decimal that the first non-zero digit occurs.
</constant>
- <constant name="MATH_STEPIFY" value="25" enum="BuiltinFunc">
+ <constant name="MATH_SNAPPED" value="25" enum="BuiltinFunc">
Return the input snapped to a given step.
</constant>
<constant name="MATH_LERP" value="26" enum="BuiltinFunc">
@@ -105,48 +103,48 @@
<constant name="MATH_MOVE_TOWARD" value="29" enum="BuiltinFunc">
Moves the number toward a value, based on the third input.
</constant>
- <constant name="MATH_DECTIME" value="30" enum="BuiltinFunc">
- Return the result of [code]value[/code] decreased by [code]step[/code] * [code]amount[/code].
- </constant>
- <constant name="MATH_RANDOMIZE" value="31" enum="BuiltinFunc">
+ <constant name="MATH_RANDOMIZE" value="30" enum="BuiltinFunc">
Randomize the seed (or the internal state) of the random number generator. Current implementation reseeds using a number based on time.
</constant>
- <constant name="MATH_RAND" value="32" enum="BuiltinFunc">
+ <constant name="MATH_RANDI" value="31" enum="BuiltinFunc">
Return a random 32 bits integer value. To obtain a random value between 0 to N (where N is smaller than 2^32 - 1), you can use it with the remainder function.
</constant>
- <constant name="MATH_RANDF" value="33" enum="BuiltinFunc">
+ <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_RANDOM" value="34" 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_SEED" value="35" enum="BuiltinFunc">
+ <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="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>
- <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_WRAP" value="42" enum="BuiltinFunc">
</constant>
- <constant name="MATH_WRAP" value="43" enum="BuiltinFunc">
+ <constant name="MATH_WRAPF" 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.
@@ -163,33 +161,32 @@
<constant name="OBJ_WEAKREF" value="49" enum="BuiltinFunc">
Create a [WeakRef] from the input.
</constant>
- <constant name="FUNC_FUNCREF" value="50" enum="BuiltinFunc">
- Create a [FuncRef] from the input.
- </constant>
- <constant name="TYPE_CONVERT" value="51" enum="BuiltinFunc">
+ <constant name="TYPE_CONVERT" value="50" enum="BuiltinFunc">
Convert between types.
</constant>
- <constant name="TYPE_OF" value="52" enum="BuiltinFunc">
+ <constant name="TYPE_OF" value="51" enum="BuiltinFunc">
Return the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned.
</constant>
- <constant name="TYPE_EXISTS" value="53" enum="BuiltinFunc">
+ <constant name="TYPE_EXISTS" value="52" enum="BuiltinFunc">
Checks if a type is registered in the [ClassDB].
</constant>
- <constant name="TEXT_CHAR" value="54" enum="BuiltinFunc">
+ <constant name="TEXT_CHAR" value="53" enum="BuiltinFunc">
Return a character with the given ascii value.
</constant>
- <constant name="TEXT_STR" value="55" enum="BuiltinFunc">
+ <constant name="TEXT_STR" value="54" enum="BuiltinFunc">
Convert the input to a string.
</constant>
- <constant name="TEXT_PRINT" value="56" enum="BuiltinFunc">
+ <constant name="TEXT_PRINT" value="55" enum="BuiltinFunc">
Print the given string to the output window.
</constant>
- <constant name="TEXT_PRINTERR" value="57" enum="BuiltinFunc">
+ <constant name="TEXT_PRINTERR" value="56" enum="BuiltinFunc">
Print the given string to the standard error output.
</constant>
- <constant name="TEXT_PRINTRAW" value="58" enum="BuiltinFunc">
+ <constant name="TEXT_PRINTRAW" value="57" enum="BuiltinFunc">
Print the given string to the standard output, without adding a newline.
</constant>
+ <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>
@@ -202,24 +199,20 @@
<constant name="BYTES_TO_VAR" value="62" enum="BuiltinFunc">
Deserialize a [Variant] from a [PackedByteArray] serialized using [constant VAR_TO_BYTES].
</constant>
- <constant name="COLORN" value="63" enum="BuiltinFunc">
- Return the [Color] with the given name and alpha ranging from 0 to 1.
- [b]Note:[/b] Names are defined in [code]color_names.inc[/code].
- </constant>
- <constant name="MATH_SMOOTHSTEP" value="64" 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="65" enum="BuiltinFunc">
+ <constant name="MATH_POSMOD" value="64" enum="BuiltinFunc">
</constant>
- <constant name="MATH_LERP_ANGLE" value="66" enum="BuiltinFunc">
+ <constant name="MATH_LERP_ANGLE" value="65" enum="BuiltinFunc">
</constant>
- <constant name="TEXT_ORD" value="67" enum="BuiltinFunc">
+ <constant name="TEXT_ORD" value="66" enum="BuiltinFunc">
</constant>
- <constant name="FUNC_MAX" value="68" 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 de5d731cc0..ae32500d2f 100644
--- a/modules/visual_script/doc_classes/VisualScriptClassConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptClassConstant.xml
@@ -12,16 +12,12 @@
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="@&quot;Object&quot;">
+ <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="&amp;&quot;Object&quot;">
The constant's parent class.
</member>
- <member name="constant" type="StringName" setter="set_class_constant" getter="get_class_constant" default="@&quot;&quot;">
+ <member name="constant" type="StringName" setter="set_class_constant" getter="get_class_constant" default="&amp;&quot;&quot;">
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 243338ea52..5024aae384 100644
--- a/modules/visual_script/doc_classes/VisualScriptComment.xml
+++ b/modules/visual_script/doc_classes/VisualScriptComment.xml
@@ -9,19 +9,15 @@
</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.
</member>
- <member name="size" type="Vector2" setter="set_size" getter="get_size" default="Vector2( 150, 150 )">
+ <member name="size" type="Vector2" setter="set_size" getter="get_size" default="Vector2(150, 150)">
The comment node's size (in pixels).
</member>
<member name="title" type="String" setter="set_title" getter="get_title" default="&quot;Comment&quot;">
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 2f162e78b6..4a3d10aa8e 100644
--- a/modules/visual_script/doc_classes/VisualScriptConstructor.xml
+++ b/modules/visual_script/doc_classes/VisualScriptConstructor.xml
@@ -10,34 +10,26 @@
</tutorials>
<methods>
<method name="get_constructor" qualifiers="const">
- <return type="Dictionary">
- </return>
+ <return type="Dictionary" />
<description>
</description>
</method>
<method name="get_constructor_type" qualifiers="const">
- <return type="int" enum="Variant.Type">
- </return>
+ <return type="int" enum="Variant.Type" />
<description>
</description>
</method>
<method name="set_constructor">
- <return type="void">
- </return>
- <argument index="0" name="constructor" type="Dictionary">
- </argument>
+ <return type="void" />
+ <argument index="0" name="constructor" type="Dictionary" />
<description>
</description>
</method>
<method name="set_constructor_type">
- <return type="void">
- </return>
- <argument index="0" name="type" type="int" enum="Variant.Type">
- </argument>
+ <return type="void" />
+ <argument index="0" name="type" type="int" enum="Variant.Type" />
<description>
</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 1c23b58507..2c6313c80a 100644
--- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
+++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml
@@ -9,124 +9,129 @@
<tutorials>
</tutorials>
<methods>
- <method name="_get_caption" qualifiers="virtual">
- <return type="String">
- </return>
+ <method name="_get_caption" qualifiers="virtual const">
+ <return type="String" />
<description>
Return the node's title.
</description>
</method>
- <method name="_get_category" qualifiers="virtual">
- <return type="String">
- </return>
+ <method name="_get_category" qualifiers="virtual const">
+ <return type="String" />
<description>
Return the node's category.
</description>
</method>
- <method name="_get_input_value_port_count" qualifiers="virtual">
- <return type="int">
- </return>
+ <method name="_get_input_value_port_count" qualifiers="virtual const">
+ <return type="int" />
<description>
Return the count of input value ports.
</description>
</method>
- <method name="_get_input_value_port_name" qualifiers="virtual">
- <return type="String">
- </return>
- <argument index="0" name="idx" type="int">
- </argument>
+ <method name="_get_input_value_port_hint" qualifiers="virtual const">
+ <return type="int" />
+ <argument index="0" name="input_idx" type="int" />
+ <description>
+ Return the specified input port's hint. See the [enum @GlobalScope.PropertyHint] hints.
+ </description>
+ </method>
+ <method name="_get_input_value_port_hint_string" qualifiers="virtual const">
+ <return type="String" />
+ <argument index="0" name="input_idx" type="int" />
+ <description>
+ Return the specified input port's hint string.
+ </description>
+ </method>
+ <method name="_get_input_value_port_name" qualifiers="virtual const">
+ <return type="String" />
+ <argument index="0" name="input_idx" type="int" />
<description>
Return the specified input port's name.
</description>
</method>
- <method name="_get_input_value_port_type" qualifiers="virtual">
- <return type="int">
- </return>
- <argument index="0" name="idx" type="int">
- </argument>
+ <method name="_get_input_value_port_type" qualifiers="virtual const">
+ <return type="int" />
+ <argument index="0" name="input_idx" type="int" />
<description>
Return the specified input port's type. See the [enum Variant.Type] values.
</description>
</method>
- <method name="_get_output_sequence_port_count" qualifiers="virtual">
- <return type="int">
- </return>
+ <method name="_get_output_sequence_port_count" qualifiers="virtual const">
+ <return type="int" />
<description>
Return the amount of output [b]sequence[/b] ports.
</description>
</method>
- <method name="_get_output_sequence_port_text" qualifiers="virtual">
- <return type="String">
- </return>
- <argument index="0" name="idx" type="int">
- </argument>
+ <method name="_get_output_sequence_port_text" qualifiers="virtual const">
+ <return type="String" />
+ <argument index="0" name="seq_idx" type="int" />
<description>
Return the specified [b]sequence[/b] output's name.
</description>
</method>
- <method name="_get_output_value_port_count" qualifiers="virtual">
- <return type="int">
- </return>
+ <method name="_get_output_value_port_count" qualifiers="virtual const">
+ <return type="int" />
<description>
Return the amount of output value ports.
</description>
</method>
- <method name="_get_output_value_port_name" qualifiers="virtual">
- <return type="String">
- </return>
- <argument index="0" name="idx" type="int">
- </argument>
+ <method name="_get_output_value_port_hint" qualifiers="virtual const">
+ <return type="int" />
+ <argument index="0" name="output_idx" type="int" />
+ <description>
+ Return the specified output port's hint. See the [enum @GlobalScope.PropertyHint] hints.
+ </description>
+ </method>
+ <method name="_get_output_value_port_hint_string" qualifiers="virtual const">
+ <return type="String" />
+ <argument index="0" name="output_idx" type="int" />
+ <description>
+ Return the specified output port's hint string.
+ </description>
+ </method>
+ <method name="_get_output_value_port_name" qualifiers="virtual const">
+ <return type="String" />
+ <argument index="0" name="output_idx" type="int" />
<description>
- Return the specified output's name.
+ Return the specified output port's name.
</description>
</method>
- <method name="_get_output_value_port_type" qualifiers="virtual">
- <return type="int">
- </return>
- <argument index="0" name="idx" type="int">
- </argument>
+ <method name="_get_output_value_port_type" qualifiers="virtual const">
+ <return type="int" />
+ <argument index="0" name="output_idx" type="int" />
<description>
- Return the specified output's type. See the [enum Variant.Type] values.
+ Return the specified output port's type. See the [enum Variant.Type] values.
</description>
</method>
- <method name="_get_text" qualifiers="virtual">
- <return type="String">
- </return>
+ <method name="_get_text" qualifiers="virtual const">
+ <return type="String" />
<description>
Return the custom node's text, which is shown right next to the input [b]sequence[/b] port (if there is none, on the place that is usually taken by it).
</description>
</method>
- <method name="_get_working_memory_size" qualifiers="virtual">
- <return type="int">
- </return>
+ <method name="_get_working_memory_size" qualifiers="virtual const">
+ <return type="int" />
<description>
Return the size of the custom node's working memory. See [method _step] for more details.
</description>
</method>
- <method name="_has_input_sequence_port" qualifiers="virtual">
- <return type="bool">
- </return>
+ <method name="_has_input_sequence_port" qualifiers="virtual const">
+ <return type="bool" />
<description>
Return whether the custom node has an input [b]sequence[/b] port.
</description>
</method>
- <method name="_step" qualifiers="virtual">
- <return type="Variant">
- </return>
- <argument index="0" name="inputs" type="Array">
- </argument>
- <argument index="1" name="outputs" type="Array">
- </argument>
- <argument index="2" name="start_mode" type="int">
- </argument>
- <argument index="3" name="working_mem" type="Array">
- </argument>
+ <method name="_step" qualifiers="virtual const">
+ <return type="Variant" />
+ <argument index="0" name="inputs" type="Array" />
+ <argument index="1" name="outputs" type="Array" />
+ <argument index="2" name="start_mode" type="int" />
+ <argument index="3" name="working_mem" type="Array" />
<description>
Execute the custom node's logic, returning the index of the output sequence port to use or a [String] when there is an error.
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/VisualScriptEditor.xml b/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml
index 186cd21239..1681da7653 100644
--- a/modules/visual_script/doc_classes/VisualScriptEditor.xml
+++ b/modules/visual_script/doc_classes/VisualScriptCustomNodes.xml
@@ -1,32 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptEditor" inherits="Object" version="4.0">
+<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">
- </return>
- <argument index="0" name="name" type="String">
- </argument>
- <argument index="1" name="category" type="String">
- </argument>
- <argument index="2" name="script" type="Script">
- </argument>
+ <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">
- </return>
- <argument index="0" name="name" type="String">
- </argument>
- <argument index="1" name="category" type="String">
- </argument>
+ <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>
@@ -39,6 +34,4 @@
</description>
</signal>
</signals>
- <constants>
- </constants>
</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 5c9c8d3eca..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="@&quot;&quot;">
+ <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 5253f7bc7d..15e16a15f0 100644
--- a/modules/visual_script/doc_classes/VisualScriptExpression.xml
+++ b/modules/visual_script/doc_classes/VisualScriptExpression.xml
@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptExpression" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node that can execute a custom expression.
</brief_description>
<description>
+ A Visual Script node that can execute a custom expression. Values can be provided for the input and the expression result can be retrieved from the output.
</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 873d26a5be..e0ca9eb280 100644
--- a/modules/visual_script/doc_classes/VisualScriptFunction.xml
+++ b/modules/visual_script/doc_classes/VisualScriptFunction.xml
@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptFunction" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node representing a function.
</brief_description>
<description>
+ [VisualScriptFunction] represents a function header. It is the starting point for the function body and can be used to tweak the function's properties (e.g. RPC mode).
</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 2d0fac1fa0..a98cb79106 100644
--- a/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml
+++ b/modules/visual_script/doc_classes/VisualScriptFunctionCall.xml
@@ -1,55 +1,75 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptFunctionCall" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node for calling a function.
</brief_description>
<description>
+ [VisualScriptFunctionCall] is created when you add or drag and drop a function onto the Visual Script graph. It allows to tweak parameters of the call, e.g. what object the function is called on.
</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].
</member>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="@&quot;Object&quot;">
+ <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].
</member>
<member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type">
+ The type to be used when [member call_mode] is set to [constant CALL_MODE_BASIC_TYPE].
</member>
<member name="call_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptFunctionCall.CallMode" default="0">
+ [code]call_mode[/code] determines the target object on which the method will be called. See [enum CallMode] for options.
</member>
- <member name="function" type="StringName" setter="set_function" getter="get_function" default="@&quot;&quot;">
+ <member name="function" type="StringName" setter="set_function" getter="get_function" default="&amp;&quot;&quot;">
+ The name of the function to be called.
</member>
<member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path">
+ The node path to use when [member call_mode] is set to [constant CALL_MODE_NODE_PATH].
</member>
<member name="rpc_call_mode" type="int" setter="set_rpc_call_mode" getter="get_rpc_call_mode" enum="VisualScriptFunctionCall.RPCCallMode" default="0">
+ The mode for RPC calls. See [method Node.rpc] for more details and [enum RPCCallMode] for available options.
</member>
<member name="singleton" type="StringName" setter="set_singleton" getter="get_singleton">
+ The singleton to call the method on. Used when [member call_mode] is set to [constant CALL_MODE_SINGLETON].
</member>
<member name="use_default_args" type="int" setter="set_use_default_args" getter="get_use_default_args">
+ Number of default arguments that will be used when calling the function. Can't be higher than the number of available default arguments in the method's declaration.
</member>
<member name="validate" type="bool" setter="set_validate" getter="get_validate" default="true">
+ If [code]false[/code], call errors (e.g. wrong number of arguments) will be ignored.
</member>
</members>
<constants>
<constant name="CALL_MODE_SELF" value="0" enum="CallMode">
+ The method will be called on this [Object].
</constant>
<constant name="CALL_MODE_NODE_PATH" value="1" enum="CallMode">
+ The method will be called on the given [Node] in the scene tree.
</constant>
<constant name="CALL_MODE_INSTANCE" value="2" enum="CallMode">
+ The method will be called on an instanced node with the given type and script.
</constant>
<constant name="CALL_MODE_BASIC_TYPE" value="3" enum="CallMode">
+ The method will be called on a GDScript basic type (e.g. [Vector2]).
</constant>
<constant name="CALL_MODE_SINGLETON" value="4" enum="CallMode">
+ The method will be called on a singleton.
</constant>
<constant name="RPC_DISABLED" value="0" enum="RPCCallMode">
+ The method will be called locally.
</constant>
<constant name="RPC_RELIABLE" value="1" enum="RPCCallMode">
+ The method will be called remotely.
</constant>
<constant name="RPC_UNRELIABLE" value="2" enum="RPCCallMode">
+ The method will be called remotely using an unreliable protocol.
</constant>
<constant name="RPC_RELIABLE_TO_ID" value="3" enum="RPCCallMode">
+ The method will be called remotely for the given peer.
</constant>
<constant name="RPC_UNRELIABLE_TO_ID" value="4" enum="RPCCallMode">
+ The method will be called remotely for the given peer, using an unreliable protocol.
</constant>
</constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
index 68083614a6..0d7833446d 100644
--- a/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
+++ b/modules/visual_script/doc_classes/VisualScriptFunctionState.xml
@@ -1,39 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="VisualScriptFunctionState" inherits="Reference" version="4.0">
+<class name="VisualScriptFunctionState" inherits="RefCounted" version="4.0">
<brief_description>
+ A Visual Script node representing a function state.
</brief_description>
<description>
+ [VisualScriptFunctionState] is returned from [VisualScriptYield] and can be used to resume a paused function call.
</description>
<tutorials>
</tutorials>
<methods>
<method name="connect_to_signal">
- <return type="void">
- </return>
- <argument index="0" name="obj" type="Object">
- </argument>
- <argument index="1" name="signals" type="String">
- </argument>
- <argument index="2" name="args" type="Array">
- </argument>
+ <return type="void" />
+ <argument index="0" name="obj" type="Object" />
+ <argument index="1" name="signals" type="String" />
+ <argument index="2" name="args" type="Array" />
<description>
+ Connects this [VisualScriptFunctionState] to a signal in the given object to automatically resume when it's emitted.
</description>
</method>
<method name="is_valid" qualifiers="const">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
+ Returns whether the function state is valid.
</description>
</method>
<method name="resume">
- <return type="Variant">
- </return>
- <argument index="0" name="args" type="Array" default="null">
- </argument>
+ <return type="Variant" />
+ <argument index="0" name="args" type="Array" default="[]" />
<description>
+ Resumes the function to run from the point it was yielded.
</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 ef17bd8a28..c6b5b22590 100644
--- a/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptGlobalConstant.xml
@@ -1,17 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptGlobalConstant" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node returning a constant from [@GlobalScope].
</brief_description>
<description>
+ A Visual Script node returning a constant from [@GlobalScope].
</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 bb1618a655..78fd17c5fc 100644
--- a/modules/visual_script/doc_classes/VisualScriptIndexGet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptIndexGet.xml
@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptIndexGet" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node for getting a value from an array or a dictionary.
</brief_description>
<description>
+ [VisualScriptIndexGet] will return the value stored in an array or a dictionary under the given index.
</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 4ff96f7211..0e5e832c65 100644
--- a/modules/visual_script/doc_classes/VisualScriptIndexSet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptIndexSet.xml
@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptIndexSet" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node for setting a value in an array or a dictionary.
</brief_description>
<description>
+ [VisualScriptIndexSet] will set the value stored in an array or a dictionary under the given index to the provided new value.
</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 6c296fdb4b..eb06d52314 100644
--- a/modules/visual_script/doc_classes/VisualScriptInputAction.xml
+++ b/modules/visual_script/doc_classes/VisualScriptInputAction.xml
@@ -1,27 +1,33 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptInputAction" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node returning a state of an action.
</brief_description>
<description>
+ [VisualScriptInputAction] can be used to check if an action is pressed or released.
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
- <member name="action" type="StringName" setter="set_action_name" getter="get_action_name" default="@&quot;&quot;">
+ <member name="action" type="StringName" setter="set_action_name" getter="get_action_name" default="&amp;&quot;&quot;">
+ Name of the action.
</member>
<member name="mode" type="int" setter="set_action_mode" getter="get_action_mode" enum="VisualScriptInputAction.Mode" default="0">
+ State of the action to check. See [enum Mode] for options.
</member>
</members>
<constants>
<constant name="MODE_PRESSED" value="0" enum="Mode">
+ [code]True[/code] if action is pressed.
</constant>
<constant name="MODE_RELEASED" value="1" enum="Mode">
+ [code]True[/code] if action is released (i.e. not pressed).
</constant>
<constant name="MODE_JUST_PRESSED" value="2" enum="Mode">
+ [code]True[/code] on the frame the action was pressed.
</constant>
<constant name="MODE_JUST_RELEASED" value="3" enum="Mode">
+ [code]True[/code] on the frame the action was released.
</constant>
</constants>
</class>
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 5b64d8438b..373e3c7191 100644
--- a/modules/visual_script/doc_classes/VisualScriptLists.xml
+++ b/modules/visual_script/doc_classes/VisualScriptLists.xml
@@ -4,92 +4,74 @@
A Visual Script virtual class for in-graph editable nodes.
</brief_description>
<description>
- A Visual Script virtual class that defines the shape and the default behaviour of the nodes that have to be in-graph editable nodes.
+ A Visual Script virtual class that defines the shape and the default behavior of the nodes that have to be in-graph editable nodes.
</description>
<tutorials>
</tutorials>
<methods>
<method name="add_input_data_port">
- <return type="void">
- </return>
- <argument index="0" name="type" type="int" enum="Variant.Type">
- </argument>
- <argument index="1" name="name" type="String">
- </argument>
- <argument index="2" name="index" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="type" type="int" enum="Variant.Type" />
+ <argument index="1" name="name" type="String" />
+ <argument index="2" name="index" type="int" />
<description>
+ Adds an input port to the Visual Script node.
</description>
</method>
<method name="add_output_data_port">
- <return type="void">
- </return>
- <argument index="0" name="type" type="int" enum="Variant.Type">
- </argument>
- <argument index="1" name="name" type="String">
- </argument>
- <argument index="2" name="index" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="type" type="int" enum="Variant.Type" />
+ <argument index="1" name="name" type="String" />
+ <argument index="2" name="index" type="int" />
<description>
+ Adds an output port to the Visual Script node.
</description>
</method>
<method name="remove_input_data_port">
- <return type="void">
- </return>
- <argument index="0" name="index" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
<description>
+ Removes an input port from the Visual Script node.
</description>
</method>
<method name="remove_output_data_port">
- <return type="void">
- </return>
- <argument index="0" name="index" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
<description>
+ Removes an output port from the Visual Script node.
</description>
</method>
<method name="set_input_data_port_name">
- <return type="void">
- </return>
- <argument index="0" name="index" type="int">
- </argument>
- <argument index="1" name="name" type="String">
- </argument>
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
+ <argument index="1" name="name" type="String" />
<description>
+ Sets the name of an input port.
</description>
</method>
<method name="set_input_data_port_type">
- <return type="void">
- </return>
- <argument index="0" name="index" type="int">
- </argument>
- <argument index="1" name="type" type="int" enum="Variant.Type">
- </argument>
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
+ <argument index="1" name="type" type="int" enum="Variant.Type" />
<description>
+ Sets the type of an input port.
</description>
</method>
<method name="set_output_data_port_name">
- <return type="void">
- </return>
- <argument index="0" name="index" type="int">
- </argument>
- <argument index="1" name="name" type="String">
- </argument>
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
+ <argument index="1" name="name" type="String" />
<description>
+ Sets the name of an output port.
</description>
</method>
<method name="set_output_data_port_type">
- <return type="void">
- </return>
- <argument index="0" name="index" type="int">
- </argument>
- <argument index="1" name="type" type="int" enum="Variant.Type">
- </argument>
+ <return type="void" />
+ <argument index="0" name="index" type="int" />
+ <argument index="1" name="type" type="int" enum="Variant.Type" />
<description>
+ Sets the type of an output port.
</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 c3741eea89..29dbddcdf4 100644
--- a/modules/visual_script/doc_classes/VisualScriptLocalVar.xml
+++ b/modules/visual_script/doc_classes/VisualScriptLocalVar.xml
@@ -12,16 +12,12 @@
</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.
</member>
- <member name="var_name" type="StringName" setter="set_var_name" getter="get_var_name" default="@&quot;new_local&quot;">
+ <member name="var_name" type="StringName" setter="set_var_name" getter="get_var_name" default="&amp;&quot;new_local&quot;">
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 619bbb90ca..96de8ebfdd 100644
--- a/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptLocalVarSet.xml
@@ -14,16 +14,12 @@
</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.
</member>
- <member name="var_name" type="StringName" setter="set_var_name" getter="get_var_name" default="@&quot;new_local&quot;">
+ <member name="var_name" type="StringName" setter="set_var_name" getter="get_var_name" default="&amp;&quot;new_local&quot;">
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 82a023f3e4..d080d9eac1 100644
--- a/modules/visual_script/doc_classes/VisualScriptNode.xml
+++ b/modules/visual_script/doc_classes/VisualScriptNode.xml
@@ -10,35 +10,28 @@
</tutorials>
<methods>
<method name="get_default_input_value" qualifiers="const">
- <return type="Variant">
- </return>
- <argument index="0" name="port_idx" type="int">
- </argument>
+ <return type="Variant" />
+ <argument index="0" name="port_idx" type="int" />
<description>
Returns the default value of a given port. The default value is used when nothing is connected to the port.
</description>
</method>
<method name="get_visual_script" qualifiers="const">
- <return type="VisualScript">
- </return>
+ <return type="VisualScript" />
<description>
Returns the [VisualScript] instance the node is bound to.
</description>
</method>
<method name="ports_changed_notify">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Notify that the node's ports have changed. Usually used in conjunction with [VisualScriptCustomNode] .
</description>
</method>
<method name="set_default_input_value">
- <return type="void">
- </return>
- <argument index="0" name="port_idx" type="int">
- </argument>
- <argument index="1" name="value" type="Variant">
- </argument>
+ <return type="void" />
+ <argument index="0" name="port_idx" type="int" />
+ <argument index="1" name="value" type="Variant" />
<description>
Change the default value of a given port.
</description>
@@ -51,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 c8ce0f2732..73d28899f6 100644
--- a/modules/visual_script/doc_classes/VisualScriptOperator.xml
+++ b/modules/visual_script/doc_classes/VisualScriptOperator.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptOperator" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node that performs an operation on two values.
</brief_description>
<description>
[b]Input Ports:[/b]
@@ -11,14 +12,12 @@
</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.
</member>
<member name="type" type="int" setter="set_typed" getter="get_typed" enum="Variant.Type" default="0">
+ 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 f13d449064..e9f30cb605 100644
--- a/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptPropertyGet.xml
@@ -1,35 +1,48 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptPropertyGet" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node returning a value of a property from an [Object].
</brief_description>
<description>
+ [VisualScriptPropertyGet] can return a value of any property from the current object or other objects.
</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].
</member>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="@&quot;Object&quot;">
+ <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 set_mode] is set to [constant CALL_MODE_INSTANCE].
</member>
<member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type">
+ The type to be used when [member set_mode] is set to [constant CALL_MODE_BASIC_TYPE].
</member>
<member name="index" type="StringName" setter="set_index" getter="get_index">
+ The indexed name of the property to retrieve. See [method Object.get_indexed] for details.
</member>
<member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path">
+ The node path to use when [member set_mode] is set to [constant CALL_MODE_NODE_PATH].
</member>
- <member name="property" type="StringName" setter="set_property" getter="get_property" default="@&quot;&quot;">
+ <member name="property" type="StringName" setter="set_property" getter="get_property" default="&amp;&quot;&quot;">
+ The name of the property to retrieve. Changing this will clear [member index].
</member>
<member name="set_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptPropertyGet.CallMode" default="0">
+ [code]set_mode[/code] determines the target object from which the property will be retrieved. See [enum CallMode] for options.
</member>
</members>
<constants>
<constant name="CALL_MODE_SELF" value="0" enum="CallMode">
+ The property will be retrieved from this [Object].
</constant>
<constant name="CALL_MODE_NODE_PATH" value="1" enum="CallMode">
+ The property will be retrieved from the given [Node] in the scene tree.
</constant>
<constant name="CALL_MODE_INSTANCE" value="2" enum="CallMode">
+ The property will be retrieved from an instanced node with the given type and script.
+ </constant>
+ <constant name="CALL_MODE_BASIC_TYPE" value="3" enum="CallMode">
+ The property will be retrieved from a GDScript basic type (e.g. [Vector2]).
</constant>
</constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml
index 629576e261..96261d2c5e 100644
--- a/modules/visual_script/doc_classes/VisualScriptPropertySet.xml
+++ b/modules/visual_script/doc_classes/VisualScriptPropertySet.xml
@@ -1,61 +1,84 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptPropertySet" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node that sets a property of an [Object].
</brief_description>
<description>
+ [VisualScriptPropertySet] can set the value of any property from the current object or other objects.
</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.
</member>
<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].
</member>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="@&quot;Object&quot;">
+ <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 set_mode] is set to [constant CALL_MODE_INSTANCE].
</member>
<member name="basic_type" type="int" setter="set_basic_type" getter="get_basic_type" enum="Variant.Type">
+ The type to be used when [member set_mode] is set to [constant CALL_MODE_BASIC_TYPE].
</member>
<member name="index" type="StringName" setter="set_index" getter="get_index">
+ The indexed name of the property to set. See [method Object.set_indexed] for details.
</member>
<member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path">
+ The node path to use when [member set_mode] is set to [constant CALL_MODE_NODE_PATH].
</member>
- <member name="property" type="StringName" setter="set_property" getter="get_property" default="@&quot;&quot;">
+ <member name="property" type="StringName" setter="set_property" getter="get_property" default="&amp;&quot;&quot;">
+ The name of the property to set. Changing this will clear [member index].
</member>
<member name="set_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptPropertySet.CallMode" default="0">
+ [code]set_mode[/code] determines the target object on which the property will be set. See [enum CallMode] for options.
</member>
</members>
<constants>
<constant name="CALL_MODE_SELF" value="0" enum="CallMode">
+ The property will be set on this [Object].
</constant>
<constant name="CALL_MODE_NODE_PATH" value="1" enum="CallMode">
+ The property will be set on the given [Node] in the scene tree.
</constant>
<constant name="CALL_MODE_INSTANCE" value="2" enum="CallMode">
+ The property will be set on an instanced node with the given type and script.
</constant>
<constant name="CALL_MODE_BASIC_TYPE" value="3" enum="CallMode">
+ The property will be set on a GDScript basic type (e.g. [Vector2]).
</constant>
<constant name="ASSIGN_OP_NONE" value="0" enum="AssignOp">
+ The property will be assigned regularly.
</constant>
<constant name="ASSIGN_OP_ADD" value="1" enum="AssignOp">
+ The value will be added to the property. Equivalent of doing [code]+=[/code].
</constant>
<constant name="ASSIGN_OP_SUB" value="2" enum="AssignOp">
+ The value will be subtracted from the property. Equivalent of doing [code]-=[/code].
</constant>
<constant name="ASSIGN_OP_MUL" value="3" enum="AssignOp">
+ The property will be multiplied by the value. Equivalent of doing [code]*=[/code].
</constant>
<constant name="ASSIGN_OP_DIV" value="4" enum="AssignOp">
+ The property will be divided by the value. Equivalent of doing [code]/=[/code].
</constant>
<constant name="ASSIGN_OP_MOD" value="5" enum="AssignOp">
+ A modulo operation will be performed on the property and the value. Equivalent of doing [code]%=[/code].
</constant>
<constant name="ASSIGN_OP_SHIFT_LEFT" value="6" enum="AssignOp">
+ The property will be binarly shifted to the left by the given value. Equivalent of doing [code]&lt;&lt;[/code].
</constant>
<constant name="ASSIGN_OP_SHIFT_RIGHT" value="7" enum="AssignOp">
+ The property will be binarly shifted to the right by the given value. Equivalent of doing [code]&gt;&gt;[/code].
</constant>
<constant name="ASSIGN_OP_BIT_AND" value="8" enum="AssignOp">
+ A binary [code]AND[/code] operation will be performed on the property. Equivalent of doing [code]&amp;=[/code].
</constant>
<constant name="ASSIGN_OP_BIT_OR" value="9" enum="AssignOp">
+ A binary [code]OR[/code] operation will be performed on the property. Equivalent of doing [code]|=[/code].
</constant>
<constant name="ASSIGN_OP_BIT_XOR" value="10" enum="AssignOp">
+ A binary [code]XOR[/code] operation will be performed on the property. Equivalent of doing [code]^=[/code].
</constant>
</constants>
</class>
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 191d4b6977..fc383593c5 100644
--- a/modules/visual_script/doc_classes/VisualScriptSceneTree.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSceneTree.xml
@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptSceneTree" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node for accessing [SceneTree] methods.
</brief_description>
<description>
+ A Visual Script node for accessing [SceneTree] methods.
</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 cb3b04b583..fdf0e24d3e 100644
--- a/modules/visual_script/doc_classes/VisualScriptSubCall.xml
+++ b/modules/visual_script/doc_classes/VisualScriptSubCall.xml
@@ -1,21 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptSubCall" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ Calls a method called [code]_subcall[/code] in this object.
</brief_description>
<description>
+ [VisualScriptSubCall] will call method named [code]_subcall[/code] in the current script. It will fail if the method doesn't exist or the provided arguments are wrong.
</description>
<tutorials>
</tutorials>
- <methods>
- <method name="_subcall" qualifiers="virtual">
- <return type="Variant">
- </return>
- <argument index="0" name="arguments" type="Variant">
- </argument>
- <description>
- </description>
- </method>
- </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 80a8d31041..ee8e2ad31e 100644
--- a/modules/visual_script/doc_classes/VisualScriptTypeCast.xml
+++ b/modules/visual_script/doc_classes/VisualScriptTypeCast.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptTypeCast" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node that casts the given value to another type.
</brief_description>
<description>
+ [VisualScriptTypeCast] will perform a type conversion to an [Object]-derived type.
</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.
</member>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="@&quot;Object&quot;">
+ <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="&amp;&quot;Object&quot;">
+ 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 d182e14e4d..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="@&quot;&quot;">
+ <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 3bd392dd85..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="@&quot;&quot;">
+ <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 0a8d529a48..bb7fd8bfb5 100644
--- a/modules/visual_script/doc_classes/VisualScriptYield.xml
+++ b/modules/visual_script/doc_classes/VisualScriptYield.xml
@@ -1,25 +1,30 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptYield" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node used to pause a function execution.
</brief_description>
<description>
+ [VisualScriptYield] will pause the function call and return [VisualScriptFunctionState], which can be used to resume the function.
</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.
</member>
<member name="wait_time" type="float" setter="set_wait_time" getter="get_wait_time">
+ The time to wait when [member mode] is set to [constant YIELD_WAIT].
</member>
</members>
<constants>
<constant name="YIELD_FRAME" value="1" enum="YieldMode">
+ Yields during an idle frame.
</constant>
<constant name="YIELD_PHYSICS_FRAME" value="2" enum="YieldMode">
+ Yields during a physics frame.
</constant>
<constant name="YIELD_WAIT" value="3" enum="YieldMode">
+ Yields a function and waits the given time.
</constant>
</constants>
</class>
diff --git a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml
index 483cdfeaf8..ad6a7fb4e2 100644
--- a/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml
+++ b/modules/visual_script/doc_classes/VisualScriptYieldSignal.xml
@@ -1,29 +1,36 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="VisualScriptYieldSignal" inherits="VisualScriptNode" version="4.0">
<brief_description>
+ A Visual Script node yielding for a signal.
</brief_description>
<description>
+ [VisualScriptYieldSignal] will pause the function execution until the provided signal is emitted.
</description>
<tutorials>
</tutorials>
- <methods>
- </methods>
<members>
- <member name="base_type" type="StringName" setter="set_base_type" getter="get_base_type" default="@&quot;Object&quot;">
+ <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].
</member>
<member name="call_mode" type="int" setter="set_call_mode" getter="get_call_mode" enum="VisualScriptYieldSignal.CallMode" default="0">
+ [code]call_mode[/code] determines the target object to wait for the signal emission. See [enum CallMode] for options.
</member>
<member name="node_path" type="NodePath" setter="set_base_path" getter="get_base_path">
+ The node path to use when [member call_mode] is set to [constant CALL_MODE_NODE_PATH].
</member>
- <member name="signal" type="StringName" setter="set_signal" getter="get_signal" default="@&quot;&quot;">
+ <member name="signal" type="StringName" setter="set_signal" getter="get_signal" default="&amp;&quot;&quot;">
+ The signal name to be waited for.
</member>
</members>
<constants>
<constant name="CALL_MODE_SELF" value="0" enum="CallMode">
+ A signal from this [Object] will be used.
</constant>
<constant name="CALL_MODE_NODE_PATH" value="1" enum="CallMode">
+ A signal from the given [Node] in the scene tree will be used.
</constant>
<constant name="CALL_MODE_INSTANCE" value="2" enum="CallMode">
+ A signal from an instanced node with the given type will be used.
</constant>
</constants>
</class>
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/editor/visual_script_editor.cpp
index b1d8c05d87..2096487235 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/editor/visual_script_editor.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,19 +30,19 @@
#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.h"
+#include "core/object/class_db.h"
+#include "core/object/script_language.h"
#include "core/os/keyboard.h"
-#include "core/script_language.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
#include "editor/editor_node.h"
#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 {
@@ -61,8 +61,8 @@ protected:
}
void _sig_changed() {
- _change_notify();
- emit_signal("changed");
+ notify_property_list_changed();
+ emit_signal(SNAME("changed"));
}
bool _set(const StringName &p_name, const Variant &p_value) {
@@ -172,7 +172,7 @@ protected:
public:
void edit(const StringName &p_sig) {
sig = p_sig;
- _change_notify();
+ notify_property_list_changed();
}
VisualScriptEditorSignalEdit() { undo_redo = nullptr; }
@@ -195,12 +195,11 @@ protected:
}
void _var_changed() {
- _change_notify();
- emit_signal("changed");
+ notify_property_list_changed();
+ emit_signal(SNAME("changed"));
}
void _var_value_changed() {
- _change_notify("value"); //so the whole tree is not redrawn, makes editing smoother in general
- emit_signal("changed");
+ emit_signal(SNAME("changed"));
}
bool _set(const StringName &p_name, const Variant &p_value) {
@@ -227,6 +226,19 @@ protected:
undo_redo->create_action(TTR("Set Variable Type"));
undo_redo->add_do_method(script.ptr(), "set_variable_info", var, dc);
undo_redo->add_undo_method(script.ptr(), "set_variable_info", var, d);
+
+ // Setting the default value.
+ Variant::Type type = (Variant::Type)(int)p_value;
+ if (type != Variant::NIL) {
+ Variant default_value;
+ Callable::CallError ce;
+ Variant::construct(type, default_value, nullptr, 0, ce);
+ if (ce.error == Callable::CallError::CALL_OK) {
+ undo_redo->add_do_method(script.ptr(), "set_variable_default_value", var, default_value);
+ undo_redo->add_undo_method(script.ptr(), "set_variable_default_value", var, dc["value"]);
+ }
+ }
+
undo_redo->add_do_method(this, "_var_changed");
undo_redo->add_undo_method(this, "_var_changed");
undo_redo->commit_action();
@@ -309,7 +321,7 @@ protected:
}
p_list->push_back(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, argt));
p_list->push_back(PropertyInfo(script->get_variable_info(var).type, "value", script->get_variable_info(var).hint, script->get_variable_info(var).hint_string, PROPERTY_USAGE_DEFAULT));
- // Update this when PropertyHint changes
+ // Update this when PropertyHint changes.
p_list->push_back(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,ExpRange,Enum,ExpEasing,Length,SpriteFrame,KeyAccel,Flags,Layers2dRender,Layers2dPhysics,Layer3dRender,Layer3dPhysics,File,Dir,GlobalFile,GlobalDir,ResourceType,MultilineText,PlaceholderText,ColorNoAlpha,ImageCompressLossy,ImageCompressLossLess,ObjectId,String,NodePathToEditedNode,MethodOfVariantType,MethodOfBaseType,MethodOfInstance,MethodOfScript,PropertyOfVariantType,PropertyOfBaseType,PropertyOfInstance,PropertyOfScript,ObjectTooBig,NodePathValidTypes"));
p_list->push_back(PropertyInfo(Variant::STRING, "hint_string"));
p_list->push_back(PropertyInfo(Variant::BOOL, "export"));
@@ -318,7 +330,7 @@ protected:
public:
void edit(const StringName &p_var) {
var = p_var;
- _change_notify();
+ notify_property_list_changed();
}
VisualScriptEditorVariableEdit() { undo_redo = nullptr; }
@@ -369,7 +381,7 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
case Variant::PLANE:
color = Color(0.97, 0.44, 0.44);
break;
- case Variant::QUAT:
+ case Variant::QUATERNION:
color = Color(0.93, 0.41, 0.64);
break;
case Variant::AABB:
@@ -378,7 +390,7 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
case Variant::BASIS:
color = Color(0.89, 0.93, 0.41);
break;
- case Variant::TRANSFORM:
+ case Variant::TRANSFORM3D:
color = Color(0.96, 0.66, 0.43);
break;
@@ -388,7 +400,7 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
case Variant::NODE_PATH:
color = Color(0.41, 0.58, 0.93);
break;
- case Variant::_RID:
+ case Variant::RID:
color = Color(0.41, 0.93, 0.6);
break;
case Variant::OBJECT:
@@ -475,7 +487,7 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
case Variant::PLANE:
color = Color(0.97, 0.44, 0.44);
break;
- case Variant::QUAT:
+ case Variant::QUATERNION:
color = Color(0.93, 0.41, 0.64);
break;
case Variant::AABB:
@@ -484,7 +496,7 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
case Variant::BASIS:
color = Color(0.7, 0.73, 0.1);
break;
- case Variant::TRANSFORM:
+ case Variant::TRANSFORM3D:
color = Color(0.96, 0.56, 0.28);
break;
@@ -494,7 +506,7 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
case Variant::NODE_PATH:
color = Color(0.41, 0.58, 0.93);
break;
- case Variant::_RID:
+ case Variant::RID:
color = Color(0.17, 0.9, 0.45);
break;
case Variant::OBJECT:
@@ -546,39 +558,27 @@ static Color _color_from_type(Variant::Type p_type, bool dark_theme = true) {
void VisualScriptEditor::_update_graph_connections() {
graph->clear_connections();
- List<StringName> funcs;
- script->get_function_list(&funcs);
+ List<VisualScript::SequenceConnection> sequence_conns;
+ script->get_sequence_connection_list(&sequence_conns);
- if (funcs.size() <= 0) {
- updating_graph = false;
- return;
+ for (const VisualScript::SequenceConnection &E : sequence_conns) {
+ graph->connect_node(itos(E.from_node), E.from_output, itos(E.to_node), 0);
}
- for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) {
- List<VisualScript::SequenceConnection> sequence_conns;
- script->get_sequence_connection_list(F->get(), &sequence_conns);
-
- for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) {
- graph->connect_node(itos(E->get().from_node), E->get().from_output, itos(E->get().to_node), 0);
- }
-
- List<VisualScript::DataConnection> data_conns;
- script->get_data_connection_list(F->get(), &data_conns);
-
- for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) {
- VisualScript::DataConnection dc = E->get();
+ List<VisualScript::DataConnection> data_conns;
+ script->get_data_connection_list(&data_conns);
- Ref<VisualScriptNode> from_node = script->get_node(F->get(), E->get().from_node);
- Ref<VisualScriptNode> to_node = script->get_node(F->get(), E->get().to_node);
+ for (VisualScript::DataConnection &dc : data_conns) {
+ Ref<VisualScriptNode> from_node = script->get_node(dc.from_node);
+ Ref<VisualScriptNode> to_node = script->get_node(dc.to_node);
- if (to_node->has_input_sequence_port()) {
- dc.to_port++;
- }
+ if (to_node->has_input_sequence_port()) {
+ dc.to_port++;
+ }
- dc.from_port += from_node->get_output_sequence_port_count();
+ dc.from_port += from_node->get_output_sequence_port_count();
- graph->connect_node(itos(E->get().from_node), dc.from_port, itos(E->get().to_node), dc.to_port);
- }
+ graph->connect_node(itos(dc.from_node), dc.from_port, itos(dc.to_node), dc.to_port);
}
}
@@ -605,402 +605,386 @@ void VisualScriptEditor::_update_graph(int p_only_id) {
}
}
}
-
- List<StringName> funcs;
- script->get_function_list(&funcs);
-
- if (funcs.size() <= 0) {
- graph->hide();
- select_func_text->show();
- updating_graph = false;
- return;
- }
-
graph->show();
select_func_text->hide();
Ref<Texture2D> type_icons[Variant::VARIANT_MAX] = {
- Control::get_theme_icon("Variant", "EditorIcons"),
- Control::get_theme_icon("bool", "EditorIcons"),
- Control::get_theme_icon("int", "EditorIcons"),
- Control::get_theme_icon("float", "EditorIcons"),
- Control::get_theme_icon("String", "EditorIcons"),
- Control::get_theme_icon("Vector2", "EditorIcons"),
- Control::get_theme_icon("Vector2i", "EditorIcons"),
- Control::get_theme_icon("Rect2", "EditorIcons"),
- Control::get_theme_icon("Rect2i", "EditorIcons"),
- Control::get_theme_icon("Vector3", "EditorIcons"),
- Control::get_theme_icon("Vector3i", "EditorIcons"),
- Control::get_theme_icon("Transform2D", "EditorIcons"),
- Control::get_theme_icon("Plane", "EditorIcons"),
- Control::get_theme_icon("Quat", "EditorIcons"),
- Control::get_theme_icon("AABB", "EditorIcons"),
- Control::get_theme_icon("Basis", "EditorIcons"),
- Control::get_theme_icon("Transform", "EditorIcons"),
- Control::get_theme_icon("Color", "EditorIcons"),
- Control::get_theme_icon("NodePath", "EditorIcons"),
- Control::get_theme_icon("RID", "EditorIcons"),
- Control::get_theme_icon("MiniObject", "EditorIcons"),
- Control::get_theme_icon("Callable", "EditorIcons"),
- Control::get_theme_icon("Signal", "EditorIcons"),
- Control::get_theme_icon("Dictionary", "EditorIcons"),
- Control::get_theme_icon("Array", "EditorIcons"),
- Control::get_theme_icon("PackedByteArray", "EditorIcons"),
- Control::get_theme_icon("PackedInt32Array", "EditorIcons"),
- Control::get_theme_icon("PackedFloat32Array", "EditorIcons"),
- Control::get_theme_icon("PackedStringArray", "EditorIcons"),
- Control::get_theme_icon("PackedVector2Array", "EditorIcons"),
- Control::get_theme_icon("PackedVector3Array", "EditorIcons"),
- Control::get_theme_icon("PackedColorArray", "EditorIcons")
+ Control::get_theme_icon(SNAME("Variant"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("bool"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("int"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("float"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("String"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Vector2i"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Rect2"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Rect2i"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Vector3i"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Transform2D"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Plane"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Quaternion"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("AABB"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Basis"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Color"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("StringName"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("NodePath"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("RID"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("MiniObject"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Callable"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Signal"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Dictionary"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedByteArray"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedInt32Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedInt64Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedFloat32Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedFloat64Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedStringArray"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedVector2Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedVector3Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedColorArray"), SNAME("EditorIcons"))
};
- Ref<Texture2D> seq_port = Control::get_theme_icon("VisualShaderPort", "EditorIcons");
-
- for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { // loop through all the functions
+ Ref<Texture2D> seq_port = Control::get_theme_icon(SNAME("VisualShaderPort"), SNAME("EditorIcons"));
+ List<int> node_ids;
+ script->get_node_list(&node_ids);
- List<int> ids;
- script->get_node_list(F->get(), &ids);
- StringName editor_icons = "EditorIcons";
+ List<int> ids;
+ script->get_node_list(&ids);
+ StringName editor_icons = "EditorIcons";
- for (List<int>::Element *E = ids.front(); E; E = E->next()) {
- if (p_only_id >= 0 && p_only_id != E->get()) {
- continue;
- }
+ for (int &E : ids) {
+ if (p_only_id >= 0 && p_only_id != E) {
+ continue;
+ }
- Ref<VisualScriptNode> node = script->get_node(F->get(), E->get());
- Vector2 pos = script->get_node_position(F->get(), E->get());
+ Ref<VisualScriptNode> node = script->get_node(E);
+ Vector2 pos = script->get_node_position(E);
- GraphNode *gnode = memnew(GraphNode);
- gnode->set_title(node->get_caption());
- gnode->set_offset(pos * EDSCALE);
- if (error_line == E->get()) {
- gnode->set_overlay(GraphNode::OVERLAY_POSITION);
- } else if (node->is_breakpoint()) {
- gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT);
- }
+ GraphNode *gnode = memnew(GraphNode);
+ gnode->set_title(node->get_caption());
+ gnode->set_position_offset(pos * EDSCALE);
+ if (error_line == E) {
+ gnode->set_overlay(GraphNode::OVERLAY_POSITION);
+ } else if (node->is_breakpoint()) {
+ gnode->set_overlay(GraphNode::OVERLAY_BREAKPOINT);
+ }
- gnode->set_meta("__vnode", node);
- gnode->set_name(itos(E->get()));
- gnode->connect("dragged", callable_mp(this, &VisualScriptEditor::_node_moved), varray(E->get()));
- gnode->connect("close_request", callable_mp(this, &VisualScriptEditor::_remove_node), varray(E->get()), CONNECT_DEFERRED);
+ gnode->set_meta("__vnode", node);
+ gnode->set_name(itos(E));
+ gnode->connect("dragged", callable_mp(this, &VisualScriptEditor::_node_moved), varray(E));
+ gnode->connect("close_request", callable_mp(this, &VisualScriptEditor::_remove_node), varray(E), CONNECT_DEFERRED);
- if (E->get() != script->get_function_node_id(F->get())) {
- //function can't be erased
+ {
+ Ref<VisualScriptFunction> v = node;
+ if (!v.is_valid()) {
gnode->set_show_close_button(true);
}
+ }
- bool has_gnode_text = false;
+ bool has_gnode_text = false;
- Ref<VisualScriptLists> nd_list = node;
- bool is_vslist = nd_list.is_valid();
- if (is_vslist) {
- HBoxContainer *hbnc = memnew(HBoxContainer);
+ Ref<VisualScriptLists> nd_list = node;
+ bool is_vslist = nd_list.is_valid();
+ if (is_vslist) {
+ HBoxContainer *hbnc = memnew(HBoxContainer);
+ if (nd_list->is_input_port_editable()) {
+ has_gnode_text = true;
+ Button *btn = memnew(Button);
+ btn->set_text(TTR("Add Input Port"));
+ hbnc->add_child(btn);
+ btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_input_port), varray(E), CONNECT_DEFERRED);
+ }
+ if (nd_list->is_output_port_editable()) {
if (nd_list->is_input_port_editable()) {
- has_gnode_text = true;
- Button *btn = memnew(Button);
- btn->set_text(TTR("Add Input Port"));
- hbnc->add_child(btn);
- btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_input_port), varray(E->get()), CONNECT_DEFERRED);
- }
- if (nd_list->is_output_port_editable()) {
- if (nd_list->is_input_port_editable()) {
- hbnc->add_spacer();
- }
- has_gnode_text = true;
- Button *btn = memnew(Button);
- btn->set_text(TTR("Add Output Port"));
- hbnc->add_child(btn);
- btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_output_port), varray(E->get()), CONNECT_DEFERRED);
+ hbnc->add_spacer();
}
- gnode->add_child(hbnc);
- } else if (Object::cast_to<VisualScriptExpression>(node.ptr())) {
has_gnode_text = true;
- LineEdit *line_edit = memnew(LineEdit);
- line_edit->set_text(node->get_text());
- line_edit->set_expand_to_text_length(true);
- line_edit->add_theme_font_override("font", get_theme_font("source", "EditorFonts"));
- gnode->add_child(line_edit);
- line_edit->connect("text_changed", callable_mp(this, &VisualScriptEditor::_expression_text_changed), varray(E->get()));
- } else {
- String text = node->get_text();
- if (!text.empty()) {
- has_gnode_text = true;
- Label *label = memnew(Label);
- label->set_text(text);
- gnode->add_child(label);
- }
+ Button *btn = memnew(Button);
+ btn->set_text(TTR("Add Output Port"));
+ hbnc->add_child(btn);
+ btn->connect("pressed", callable_mp(this, &VisualScriptEditor::_add_output_port), varray(E), CONNECT_DEFERRED);
}
-
- if (Object::cast_to<VisualScriptComment>(node.ptr())) {
- Ref<VisualScriptComment> vsc = node;
- gnode->set_comment(true);
- gnode->set_resizable(true);
- gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE);
- gnode->connect("resize_request", callable_mp(this, &VisualScriptEditor::_comment_node_resized), varray(E->get()));
+ gnode->add_child(hbnc);
+ } else if (Object::cast_to<VisualScriptExpression>(node.ptr())) {
+ has_gnode_text = true;
+ LineEdit *line_edit = memnew(LineEdit);
+ line_edit->set_text(node->get_text());
+ line_edit->set_expand_to_text_length_enabled(true);
+ line_edit->add_theme_font_override("font", get_theme_font(SNAME("source"), SNAME("EditorFonts")));
+ gnode->add_child(line_edit);
+ line_edit->connect("text_changed", callable_mp(this, &VisualScriptEditor::_expression_text_changed), varray(E));
+ } else {
+ String text = node->get_text();
+ if (!text.is_empty()) {
+ has_gnode_text = true;
+ Label *label = memnew(Label);
+ label->set_text(text);
+ gnode->add_child(label);
}
+ }
- if (node_styles.has(node->get_category())) {
- Ref<StyleBoxFlat> sbf = node_styles[node->get_category()];
- if (gnode->is_comment()) {
- sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox("comment", "GraphNode");
- }
+ if (Object::cast_to<VisualScriptComment>(node.ptr())) {
+ Ref<VisualScriptComment> vsc = node;
+ gnode->set_comment(true);
+ gnode->set_resizable(true);
+ gnode->set_custom_minimum_size(vsc->get_size() * EDSCALE);
+ gnode->connect("resize_request", callable_mp(this, &VisualScriptEditor::_comment_node_resized), varray(E));
+ }
- Color c = sbf->get_border_color();
- Color ic = c;
- c.a = 1;
- if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) {
- Color mono_color;
- if (((c.r + c.g + c.b) / 3) < 0.7) {
- mono_color = Color(1.0, 1.0, 1.0);
- ic = Color(0.0, 0.0, 0.0, 0.7);
- } else {
- mono_color = Color(0.0, 0.0, 0.0);
- ic = Color(1.0, 1.0, 1.0, 0.7);
- }
- mono_color.a = 0.85;
- c = mono_color;
- }
- gnode->add_theme_color_override("title_color", c);
- c.a = 0.7;
- gnode->add_theme_color_override("close_color", c);
- gnode->add_theme_color_override("resizer_color", ic);
- gnode->add_theme_style_override("frame", sbf);
+ if (node_styles.has(node->get_category())) {
+ Ref<StyleBoxFlat> sbf = node_styles[node->get_category()];
+ if (gnode->is_comment()) {
+ sbf = EditorNode::get_singleton()->get_theme_base()->get_theme()->get_stylebox(SNAME("comment"), SNAME("GraphNode"));
}
- const Color mono_color = get_theme_color("mono_color", "Editor");
+ Color c = sbf->get_border_color();
+ c = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0, 0.85) : Color(0.0, 0.0, 0.0, 0.85);
+ Color ic = c;
+ gnode->add_theme_color_override("title_color", c);
+ c.a = 1;
+ gnode->add_theme_color_override("close_color", c);
+ gnode->add_theme_color_override("resizer_color", ic);
+ gnode->add_theme_style_override("frame", sbf);
+ }
+
+ const Color mono_color = get_theme_color(SNAME("mono_color"), SNAME("Editor"));
- int slot_idx = 0;
+ int slot_idx = 0;
- bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String();
- if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) {
- // IF has_gnode_text is true BUT we have no sequence ports to draw (in here),
- // we still draw the disabled default ones to shift up the slots by one,
- // so the slots DON'T start with the content text.
+ bool single_seq_output = node->get_output_sequence_port_count() == 1 && node->get_output_sequence_port_text(0) == String();
+ if ((node->has_input_sequence_port() || single_seq_output) || has_gnode_text) {
+ // IF has_gnode_text is true BUT we have no sequence ports to draw (in here),
+ // we still draw the disabled default ones to shift up the slots by one,
+ // so the slots DON'T start with the content text.
- // IF has_gnode_text is false, but we DO want to draw default sequence ports,
- // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly.
- if (!has_gnode_text) {
- Label *dummy = memnew(Label);
- dummy->set_text(" ");
- gnode->add_child(dummy);
- }
- gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
- slot_idx++;
+ // IF has_gnode_text is false, but we DO want to draw default sequence ports,
+ // we draw a dummy text to take up the position of the sequence nodes, so all the other ports are still aligned correctly.
+ if (!has_gnode_text) {
+ Label *dummy = memnew(Label);
+ dummy->set_text(" ");
+ gnode->add_child(dummy);
}
+ gnode->set_slot(0, node->has_input_sequence_port(), TYPE_SEQUENCE, mono_color, single_seq_output, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
+ slot_idx++;
+ }
- int mixed_seq_ports = 0;
+ int mixed_seq_ports = 0;
- if (!single_seq_output) {
- if (node->has_mixed_input_and_sequence_ports()) {
- mixed_seq_ports = node->get_output_sequence_port_count();
- } else {
- 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);
- gnode->add_child(text2);
- gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
- slot_idx++;
- }
+ if (!single_seq_output) {
+ if (node->has_mixed_input_and_sequence_ports()) {
+ mixed_seq_ports = node->get_output_sequence_port_count();
+ } else {
+ 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);
+ gnode->add_child(text2);
+ gnode->set_slot(slot_idx, false, 0, Color(), true, TYPE_SEQUENCE, mono_color, seq_port, seq_port);
+ slot_idx++;
}
}
+ }
- for (int i = 0; i < MAX(node->get_output_value_port_count(), MAX(mixed_seq_ports, node->get_input_value_port_count())); i++) {
- bool left_ok = false;
- Variant::Type left_type = Variant::NIL;
- String left_name;
+ for (int i = 0; i < MAX(node->get_output_value_port_count(), MAX(mixed_seq_ports, node->get_input_value_port_count())); i++) {
+ bool left_ok = false;
+ Variant::Type left_type = Variant::NIL;
+ String left_name;
- if (i < node->get_input_value_port_count()) {
- PropertyInfo pi = node->get_input_value_port_info(i);
- left_ok = true;
- left_type = pi.type;
- left_name = pi.name;
- }
+ if (i < node->get_input_value_port_count()) {
+ PropertyInfo pi = node->get_input_value_port_info(i);
+ left_ok = true;
+ left_type = pi.type;
+ left_name = pi.name;
+ }
- bool right_ok = false;
- Variant::Type right_type = Variant::NIL;
- String right_name;
+ bool right_ok = false;
+ Variant::Type right_type = Variant::NIL;
+ String right_name;
- if (i >= mixed_seq_ports && i < node->get_output_value_port_count() + mixed_seq_ports) {
- PropertyInfo pi = node->get_output_value_port_info(i - mixed_seq_ports);
- right_ok = true;
- right_type = pi.type;
- right_name = pi.name;
+ if (i >= mixed_seq_ports && i < node->get_output_value_port_count() + mixed_seq_ports) {
+ PropertyInfo pi = node->get_output_value_port_info(i - mixed_seq_ports);
+ right_ok = true;
+ right_type = pi.type;
+ right_name = pi.name;
+ }
+ VBoxContainer *vbc = memnew(VBoxContainer);
+ HBoxContainer *hbc = memnew(HBoxContainer);
+ HBoxContainer *hbc2 = memnew(HBoxContainer);
+ vbc->add_child(hbc);
+ vbc->add_child(hbc2);
+ if (left_ok) {
+ Ref<Texture2D> t;
+ if (left_type >= 0 && left_type < Variant::VARIANT_MAX) {
+ t = type_icons[left_type];
+ }
+ if (t.is_valid()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(t);
+ tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
+ hbc->add_child(tf);
}
- VBoxContainer *vbc = memnew(VBoxContainer);
- HBoxContainer *hbc = memnew(HBoxContainer);
- HBoxContainer *hbc2 = memnew(HBoxContainer);
- vbc->add_child(hbc);
- vbc->add_child(hbc2);
- if (left_ok) {
- Ref<Texture2D> t;
- if (left_type >= 0 && left_type < Variant::VARIANT_MAX) {
- t = type_icons[left_type];
- }
- if (t.is_valid()) {
- TextureRect *tf = memnew(TextureRect);
- tf->set_texture(t);
- tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
- hbc->add_child(tf);
- }
-
- if (is_vslist) {
- if (nd_list->is_input_port_name_editable()) {
- LineEdit *name_box = memnew(LineEdit);
- hbc->add_child(name_box);
- name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
- name_box->set_text(left_name);
- name_box->set_expand_to_text_length(true);
- name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get()));
- name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, true));
- } else {
- hbc->add_child(memnew(Label(left_name)));
- }
-
- if (nd_list->is_input_port_type_editable()) {
- OptionButton *opbtn = memnew(OptionButton);
- for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) {
- opbtn->add_item(Variant::get_type_name(Variant::Type(j)));
- }
- opbtn->select(left_type);
- opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
- hbc->add_child(opbtn);
- opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E->get(), i, true), CONNECT_DEFERRED);
- }
- Button *rmbtn = memnew(Button);
- rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Remove", "EditorIcons"));
- hbc->add_child(rmbtn);
- rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_input_port), varray(E->get(), i), CONNECT_DEFERRED);
+ if (is_vslist) {
+ if (nd_list->is_input_port_name_editable()) {
+ LineEdit *name_box = memnew(LineEdit);
+ hbc->add_child(name_box);
+ name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
+ name_box->set_text(left_name);
+ name_box->set_expand_to_text_length_enabled(true);
+ name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E));
+ name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E, i, true));
} else {
hbc->add_child(memnew(Label(left_name)));
}
- if (left_type != Variant::NIL && !script->is_input_value_port_connected(F->get(), E->get(), i)) {
- PropertyInfo pi = node->get_input_value_port_info(i);
- Button *button = memnew(Button);
- Variant value = node->get_default_input_value(i);
- if (value.get_type() != left_type) {
- //different type? for now convert
- //not the same, reconvert
- Callable::CallError ce;
- const Variant *existingp = &value;
- value = Variant::construct(left_type, &existingp, 1, ce, false);
+ if (nd_list->is_input_port_type_editable()) {
+ OptionButton *opbtn = memnew(OptionButton);
+ for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) {
+ opbtn->add_item(Variant::get_type_name(Variant::Type(j)));
}
-
- if (left_type == Variant::COLOR) {
- button->set_custom_minimum_size(Size2(30, 0) * EDSCALE);
- button->connect("draw", callable_mp(this, &VisualScriptEditor::_draw_color_over_button), varray(button, value));
- } else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) {
- Ref<Resource> res = value;
- Array arr;
- arr.push_back(button->get_instance_id());
- arr.push_back(String(value));
- EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr);
-
- } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) {
- button->set_text(pi.hint_string.get_slice(",", value));
- } else {
- button->set_text(value);
- }
- button->connect("pressed", callable_mp(this, &VisualScriptEditor::_default_value_edited), varray(button, E->get(), i));
- hbc2->add_child(button);
+ opbtn->select(left_type);
+ opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ hbc->add_child(opbtn);
+ opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E, i, true), CONNECT_DEFERRED);
}
+
+ Button *rmbtn = memnew(Button);
+ rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
+ hbc->add_child(rmbtn);
+ rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_input_port), varray(E, i), CONNECT_DEFERRED);
} else {
- Control *c = memnew(Control);
- c->set_custom_minimum_size(Size2(10, 0) * EDSCALE);
- hbc->add_child(c);
+ hbc->add_child(memnew(Label(left_name)));
}
- hbc->add_spacer();
- hbc2->add_spacer();
+ if (left_type != Variant::NIL && !script->is_input_value_port_connected(E, i)) {
+ PropertyInfo pi = node->get_input_value_port_info(i);
+ Button *button = memnew(Button);
+ Variant value = node->get_default_input_value(i);
+ if (value.get_type() != left_type) {
+ //different type? for now convert
+ //not the same, reconvert
+ Callable::CallError ce;
+ const Variant *existingp = &value;
+ Variant::construct(left_type, value, &existingp, 1, ce);
+ }
- 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);
- hbc->add_child(text2);
+ if (left_type == Variant::COLOR) {
+ button->set_custom_minimum_size(Size2(30, 0) * EDSCALE);
+ button->connect("draw", callable_mp(this, &VisualScriptEditor::_draw_color_over_button), varray(button, value));
+ } else if (left_type == Variant::OBJECT && Ref<Resource>(value).is_valid()) {
+ Ref<Resource> res = value;
+ Array arr;
+ arr.push_back(button->get_instance_id());
+ arr.push_back(String(value));
+ EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_button_resource_previewed", arr);
+
+ } else if (pi.type == Variant::INT && pi.hint == PROPERTY_HINT_ENUM) {
+ button->set_text(pi.hint_string.get_slice(",", value));
+ } else {
+ button->set_text(value);
+ }
+ button->connect("pressed", callable_mp(this, &VisualScriptEditor::_default_value_edited), varray(button, E, i));
+ hbc2->add_child(button);
}
+ } else {
+ Control *c = memnew(Control);
+ c->set_custom_minimum_size(Size2(10, 0) * EDSCALE);
+ hbc->add_child(c);
+ }
- if (right_ok) {
- if (is_vslist) {
- Button *rmbtn = memnew(Button);
- rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Remove", "EditorIcons"));
- hbc->add_child(rmbtn);
- rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_output_port), varray(E->get(), i), CONNECT_DEFERRED);
-
- if (nd_list->is_output_port_type_editable()) {
- OptionButton *opbtn = memnew(OptionButton);
- for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) {
- opbtn->add_item(Variant::get_type_name(Variant::Type(j)));
- }
- opbtn->select(right_type);
- opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
- hbc->add_child(opbtn);
- opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E->get(), i, false), CONNECT_DEFERRED);
- }
+ hbc->add_spacer();
+ hbc2->add_spacer();
- if (nd_list->is_output_port_name_editable()) {
- LineEdit *name_box = memnew(LineEdit);
- hbc->add_child(name_box);
- name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
- name_box->set_text(right_name);
- name_box->set_expand_to_text_length(true);
- name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E->get()));
- name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E->get(), i, false));
- } else {
- hbc->add_child(memnew(Label(right_name)));
+ 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);
+ hbc->add_child(text2);
+ }
+
+ if (right_ok) {
+ if (is_vslist) {
+ Button *rmbtn = memnew(Button);
+ rmbtn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
+ hbc->add_child(rmbtn);
+ rmbtn->connect("pressed", callable_mp(this, &VisualScriptEditor::_remove_output_port), varray(E, i), CONNECT_DEFERRED);
+
+ if (nd_list->is_output_port_type_editable()) {
+ OptionButton *opbtn = memnew(OptionButton);
+ for (int j = Variant::NIL; j < Variant::VARIANT_MAX; j++) {
+ opbtn->add_item(Variant::get_type_name(Variant::Type(j)));
}
- } else {
- hbc->add_child(memnew(Label(right_name)));
+ opbtn->select(right_type);
+ opbtn->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
+ hbc->add_child(opbtn);
+ opbtn->connect("item_selected", callable_mp(this, &VisualScriptEditor::_change_port_type), varray(E, i, false), CONNECT_DEFERRED);
}
- Ref<Texture2D> t;
- if (right_type >= 0 && right_type < Variant::VARIANT_MAX) {
- t = type_icons[right_type];
- }
- if (t.is_valid()) {
- TextureRect *tf = memnew(TextureRect);
- tf->set_texture(t);
- tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
- hbc->add_child(tf);
+ if (nd_list->is_output_port_name_editable()) {
+ LineEdit *name_box = memnew(LineEdit);
+ hbc->add_child(name_box);
+ name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0));
+ name_box->set_text(right_name);
+ name_box->set_expand_to_text_length_enabled(true);
+ name_box->connect("resized", callable_mp(this, &VisualScriptEditor::_update_node_size), varray(E));
+ name_box->connect("focus_exited", callable_mp(this, &VisualScriptEditor::_port_name_focus_out), varray(name_box, E, i, false));
+ } else {
+ hbc->add_child(memnew(Label(right_name)));
}
- }
-
- gnode->add_child(vbc);
-
- bool dark_theme = get_theme_constant("dark_theme", "Editor");
- if (i < mixed_seq_ports) {
- gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), true, TYPE_SEQUENCE, mono_color, Ref<Texture2D>(), seq_port);
} else {
- gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), right_ok, right_type, _color_from_type(right_type, dark_theme));
+ hbc->add_child(memnew(Label(right_name)));
}
- slot_idx++;
+ Ref<Texture2D> t;
+ if (right_type >= 0 && right_type < Variant::VARIANT_MAX) {
+ t = type_icons[right_type];
+ }
+ if (t.is_valid()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(t);
+ tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
+ hbc->add_child(tf);
+ }
}
- graph->add_child(gnode);
+ gnode->add_child(vbc);
- if (gnode->is_comment()) {
- graph->move_child(gnode, 0);
+ bool dark_theme = get_theme_constant(SNAME("dark_theme"), SNAME("Editor"));
+ if (i < mixed_seq_ports) {
+ gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), true, TYPE_SEQUENCE, mono_color, Ref<Texture2D>(), seq_port);
+ } else {
+ gnode->set_slot(slot_idx, left_ok, left_type, _color_from_type(left_type, dark_theme), right_ok, right_type, _color_from_type(right_type, dark_theme));
}
+
+ slot_idx++;
+ }
+
+ graph->add_child(gnode);
+
+ if (gnode->is_comment()) {
+ graph->move_child(gnode, 0);
}
}
+
_update_graph_connections();
- // use default_func instead of default_func for now I think that should be good stop gap solution to ensure not breaking anything
- graph->call_deferred("set_scroll_ofs", script->get_function_scroll(default_func) * EDSCALE);
+
+ float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity");
+ graph->set_minimap_opacity(graph_minimap_opacity);
+
+ // Use default_func instead of default_func for now I think that should be good stop gap solution to ensure not breaking anything.
+ graph->call_deferred(SNAME("set_scroll_ofs"), script->get_scroll() * EDSCALE);
updating_graph = false;
}
void VisualScriptEditor::_change_port_type(int p_select, int p_id, int p_port, bool is_input) {
- StringName func = _get_function_of_node(p_id);
-
- Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
+ Ref<VisualScriptLists> vsn = script->get_node(p_id);
if (!vsn.is_valid()) {
return;
}
- undo_redo->create_action("Change Port Type");
+ undo_redo->create_action(TTR("Change Port Type"));
if (is_input) {
undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_type", p_port, Variant::Type(p_select));
undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_type", p_port, vsn->get_input_value_port_info(p_port).type);
@@ -1014,14 +998,12 @@ 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)->set_size(Vector2(1, 1)); // Shrink if text is smaller.
}
}
void VisualScriptEditor::_port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input) {
- StringName func = _get_function_of_node(p_id);
-
- Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
+ Ref<VisualScriptLists> vsn = script->get_node(p_id);
if (!vsn.is_valid()) {
return;
}
@@ -1034,7 +1016,7 @@ void VisualScriptEditor::_port_name_focus_out(const Node *p_name_box, int p_id,
return;
}
- undo_redo->create_action("Change Port Name");
+ undo_redo->create_action(TTR("Change Port Name"));
if (is_input) {
undo_redo->add_do_method(vsn.ptr(), "set_input_data_port_name", p_port, text);
undo_redo->add_undo_method(vsn.ptr(), "set_input_data_port_name", p_port, vsn->get_input_value_port_info(p_port).name);
@@ -1056,23 +1038,20 @@ void VisualScriptEditor::_update_members() {
TreeItem *functions = members->create_item(root);
functions->set_selectable(0, false);
functions->set_text(0, TTR("Functions:"));
- functions->add_button(0, Control::get_theme_icon("Override", "EditorIcons"), 1, false, TTR("Override an existing built-in function."));
- functions->add_button(0, Control::get_theme_icon("Add", "EditorIcons"), 0, false, TTR("Create a new function."));
- functions->set_custom_color(0, Control::get_theme_color("mono_color", "Editor"));
+ functions->add_button(0, Control::get_theme_icon(SNAME("Override"), SNAME("EditorIcons")), 1, false, TTR("Override an existing built-in function."));
+ functions->add_button(0, Control::get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), 0, false, TTR("Create a new function."));
+ functions->set_custom_color(0, Control::get_theme_color(SNAME("mono_color"), SNAME("Editor")));
List<StringName> func_names;
script->get_function_list(&func_names);
- for (List<StringName>::Element *E = func_names.front(); E; E = E->next()) {
- if (E->get() == default_func) {
- continue;
- }
-
+ func_names.sort_custom<StringName::AlphCompare>();
+ for (const StringName &E : func_names) {
TreeItem *ti = members->create_item(functions);
- ti->set_text(0, E->get());
+ ti->set_text(0, E);
ti->set_selectable(0, true);
- ti->set_metadata(0, E->get());
- ti->add_button(0, Control::get_theme_icon("Edit", "EditorIcons"), 0);
- if (selected == E->get()) {
+ ti->set_metadata(0, E);
+ ti->add_button(0, Control::get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")), 0);
+ if (selected == E) {
ti->select(0);
}
}
@@ -1080,58 +1059,58 @@ void VisualScriptEditor::_update_members() {
TreeItem *variables = members->create_item(root);
variables->set_selectable(0, false);
variables->set_text(0, TTR("Variables:"));
- variables->add_button(0, Control::get_theme_icon("Add", "EditorIcons"), -1, false, TTR("Create a new variable."));
- variables->set_custom_color(0, Control::get_theme_color("mono_color", "Editor"));
+ variables->add_button(0, Control::get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), -1, false, TTR("Create a new variable."));
+ variables->set_custom_color(0, Control::get_theme_color(SNAME("mono_color"), SNAME("Editor")));
Ref<Texture2D> type_icons[Variant::VARIANT_MAX] = {
- Control::get_theme_icon("Variant", "EditorIcons"),
- Control::get_theme_icon("bool", "EditorIcons"),
- Control::get_theme_icon("int", "EditorIcons"),
- Control::get_theme_icon("float", "EditorIcons"),
- Control::get_theme_icon("String", "EditorIcons"),
- Control::get_theme_icon("Vector2", "EditorIcons"),
- Control::get_theme_icon("Vector2i", "EditorIcons"),
- Control::get_theme_icon("Rect2", "EditorIcons"),
- Control::get_theme_icon("Rect2i", "EditorIcons"),
- Control::get_theme_icon("Vector3", "EditorIcons"),
- Control::get_theme_icon("Vector3i", "EditorIcons"),
- Control::get_theme_icon("Transform2D", "EditorIcons"),
- Control::get_theme_icon("Plane", "EditorIcons"),
- Control::get_theme_icon("Quat", "EditorIcons"),
- Control::get_theme_icon("AABB", "EditorIcons"),
- Control::get_theme_icon("Basis", "EditorIcons"),
- Control::get_theme_icon("Transform", "EditorIcons"),
- Control::get_theme_icon("Color", "EditorIcons"),
- Control::get_theme_icon("NodePath", "EditorIcons"),
- Control::get_theme_icon("RID", "EditorIcons"),
- Control::get_theme_icon("MiniObject", "EditorIcons"),
- Control::get_theme_icon("Callable", "EditorIcons"),
- Control::get_theme_icon("Signal", "EditorIcons"),
- Control::get_theme_icon("Dictionary", "EditorIcons"),
- Control::get_theme_icon("Array", "EditorIcons"),
- Control::get_theme_icon("PackedByteArray", "EditorIcons"),
- Control::get_theme_icon("PackedInt32Array", "EditorIcons"),
- Control::get_theme_icon("PackedFloat32Array", "EditorIcons"),
- Control::get_theme_icon("PackedStringArray", "EditorIcons"),
- Control::get_theme_icon("PackedVector2Array", "EditorIcons"),
- Control::get_theme_icon("PackedVector3Array", "EditorIcons"),
- Control::get_theme_icon("PackedColorArray", "EditorIcons")
+ Control::get_theme_icon(SNAME("Variant"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("bool"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("int"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("float"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("String"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Vector2i"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Rect2"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Rect2i"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Vector3i"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Transform2D"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Plane"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Quaternion"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("AABB"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Basis"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Color"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("NodePath"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("RID"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("MiniObject"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Callable"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Signal"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Dictionary"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedByteArray"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedInt32Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedFloat32Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedStringArray"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedVector2Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedVector3Array"), SNAME("EditorIcons")),
+ Control::get_theme_icon(SNAME("PackedColorArray"), SNAME("EditorIcons"))
};
List<StringName> var_names;
script->get_variable_list(&var_names);
- for (List<StringName>::Element *E = var_names.front(); E; E = E->next()) {
+ for (const StringName &E : var_names) {
TreeItem *ti = members->create_item(variables);
- ti->set_text(0, E->get());
+ ti->set_text(0, E);
- ti->set_suffix(0, "= " + _sanitized_variant_text(E->get()));
- ti->set_icon(0, type_icons[script->get_variable_info(E->get()).type]);
+ ti->set_suffix(0, "= " + _sanitized_variant_text(E));
+ ti->set_icon(0, type_icons[script->get_variable_info(E).type]);
ti->set_selectable(0, true);
ti->set_editable(0, true);
- ti->set_metadata(0, E->get());
- if (selected == E->get()) {
+ ti->set_metadata(0, E);
+ if (selected == E) {
ti->select(0);
}
}
@@ -1139,30 +1118,30 @@ void VisualScriptEditor::_update_members() {
TreeItem *_signals = members->create_item(root);
_signals->set_selectable(0, false);
_signals->set_text(0, TTR("Signals:"));
- _signals->add_button(0, Control::get_theme_icon("Add", "EditorIcons"), -1, false, TTR("Create a new signal."));
- _signals->set_custom_color(0, Control::get_theme_color("mono_color", "Editor"));
+ _signals->add_button(0, Control::get_theme_icon(SNAME("Add"), SNAME("EditorIcons")), -1, false, TTR("Create a new signal."));
+ _signals->set_custom_color(0, Control::get_theme_color(SNAME("mono_color"), SNAME("Editor")));
List<StringName> signal_names;
script->get_custom_signal_list(&signal_names);
- for (List<StringName>::Element *E = signal_names.front(); E; E = E->next()) {
+ for (const StringName &E : signal_names) {
TreeItem *ti = members->create_item(_signals);
- ti->set_text(0, E->get());
+ ti->set_text(0, E);
ti->set_selectable(0, true);
ti->set_editable(0, true);
- ti->set_metadata(0, E->get());
- if (selected == E->get()) {
+ ti->set_metadata(0, E);
+ if (selected == E) {
ti->select(0);
}
}
String base_type = script->get_instance_base_type();
String icon_type = base_type;
- if (!Control::has_theme_icon(base_type, "EditorIcons")) {
+ if (!Control::has_theme_icon(base_type, SNAME("EditorIcons"))) {
icon_type = "Object";
}
base_type_select->set_text(base_type);
- base_type_select->set_icon(Control::get_theme_icon(icon_type, "EditorIcons"));
+ base_type_select->set_icon(Control::get_theme_icon(icon_type, SNAME("EditorIcons")));
updating_members = false;
}
@@ -1173,7 +1152,9 @@ String VisualScriptEditor::_sanitized_variant_text(const StringName &property_na
if (script->get_variable_info(property_name).type != Variant::NIL) {
Callable::CallError ce;
const Variant *converted = &var;
- var = Variant::construct(script->get_variable_info(property_name).type, &converted, 1, ce, false);
+ Variant n;
+ Variant::construct(script->get_variable_info(property_name).type, n, &converted, 1, ce);
+ var = n;
}
return String(var);
@@ -1189,15 +1170,15 @@ void VisualScriptEditor::_member_selected() {
selected = ti->get_metadata(0);
- if (ti->get_parent() == members->get_root()->get_children()) {
+ 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_CONTROL);
+ bool held_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
#endif
if (held_ctrl) {
ERR_FAIL_COND(!script->has_function(selected));
- _center_on_node(selected, script->get_function_node_id(selected));
+ _center_on_node(script->get_function_node_id(selected));
}
}
}
@@ -1235,13 +1216,13 @@ void VisualScriptEditor::_member_edited() {
TreeItem *root = members->get_root();
- if (ti->get_parent() == root->get_children()) {
+ if (ti->get_parent() == root->get_first_child()) {
selected = new_name;
int node_id = script->get_function_node_id(name);
Ref<VisualScriptFunction> func;
- if (script->has_node(name, node_id)) {
- func = script->get_node(name, node_id);
+ if (script->has_node(node_id)) {
+ func = script->get_node(node_id);
}
undo_redo->create_action(TTR("Rename Function"));
undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name);
@@ -1251,21 +1232,17 @@ void VisualScriptEditor::_member_edited() {
undo_redo->add_undo_method(func.ptr(), "set_name", name);
}
- // also fix all function calls
- List<StringName> flst;
- script->get_function_list(&flst);
- for (List<StringName>::Element *E = flst.front(); E; E = E->next()) {
- List<int> lst;
- script->get_node_list(E->get(), &lst);
- for (List<int>::Element *F = lst.front(); F; F = F->next()) {
- Ref<VisualScriptFunctionCall> fncall = script->get_node(E->get(), F->get());
- if (!fncall.is_valid()) {
- continue;
- }
- if (fncall->get_function() == name) {
- undo_redo->add_do_method(fncall.ptr(), "set_function", new_name);
- undo_redo->add_undo_method(fncall.ptr(), "set_function", name);
- }
+ // Also fix all function calls.
+ List<int> lst;
+ script->get_node_list(&lst);
+ for (int &F : lst) {
+ Ref<VisualScriptFunctionCall> fncall = script->get_node(F);
+ if (!fncall.is_valid()) {
+ continue;
+ }
+ if (fncall->get_function() == name) {
+ undo_redo->add_do_method(fncall.ptr(), "set_function", new_name);
+ undo_redo->add_undo_method(fncall.ptr(), "set_function", name);
}
}
@@ -1277,10 +1254,10 @@ void VisualScriptEditor::_member_edited() {
undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
undo_redo->commit_action();
- return; //or crash because it will become invalid
+ return; // Or crash because it will become invalid.
}
- if (ti->get_parent() == root->get_children()->get_next()) {
+ if (ti->get_parent() == root->get_first_child()->get_next()) {
selected = new_name;
undo_redo->create_action(TTR("Rename Variable"));
undo_redo->add_do_method(script.ptr(), "rename_variable", name, new_name);
@@ -1293,10 +1270,10 @@ void VisualScriptEditor::_member_edited() {
undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
undo_redo->commit_action();
- return; //or crash because it will become invalid
+ return; // Or crash because it will become invalid.
}
- if (ti->get_parent() == root->get_children()->get_next()->get_next()) {
+ if (ti->get_parent() == root->get_first_child()->get_next()->get_next()) {
selected = new_name;
undo_redo->create_action(TTR("Rename Signal"));
undo_redo->add_do_method(script.ptr(), "rename_custom_signal", name, new_name);
@@ -1307,7 +1284,7 @@ void VisualScriptEditor::_member_edited() {
undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
undo_redo->commit_action();
- return; //or crash because it will become invalid
+ return; // Or crash because it will become invalid.
}
}
@@ -1324,10 +1301,10 @@ 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());
selected = name;
- Vector2 ofs = _get_available_pos();
+ Vector2 pos = _get_available_pos();
Ref<VisualScriptFunction> func_node;
- func_node.instance();
+ func_node.instantiate();
func_node->set_name(name);
for (int i = 0; i < func_input_vbox->get_child_count(); i++) {
@@ -1341,10 +1318,13 @@ void VisualScriptEditor::_create_function() {
func_node->add_argument(arg_type, arg_name);
}
+ int func_node_id = script->get_available_id();
+
undo_redo->create_action(TTR("Add Function"));
- undo_redo->add_do_method(script.ptr(), "add_function", name);
- undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs);
+ undo_redo->add_do_method(script.ptr(), "add_function", name, func_node_id);
undo_redo->add_undo_method(script.ptr(), "remove_function", name);
+ undo_redo->add_do_method(script.ptr(), "add_node", func_node_id, func_node, pos);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", func_node_id);
undo_redo->add_do_method(this, "_update_members");
undo_redo->add_undo_method(this, "_update_members");
undo_redo->add_do_method(this, "_update_graph");
@@ -1387,7 +1367,7 @@ void VisualScriptEditor::_add_func_input() {
hbox->add_child(type_box);
Button *delete_button = memnew(Button);
- delete_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon("Remove", "EditorIcons"));
+ delete_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
delete_button->set_tooltip(vformat(TTR("Delete input port")));
hbox->add_child(delete_button);
@@ -1427,26 +1407,29 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt
if (ti->get_parent() == root) {
//main buttons
- if (ti == root->get_children()) {
- //add function, this one uses menu
+ if (ti == root->get_first_child()) {
+ // Add function, this one uses menu.
if (p_button == 1) {
+ // Ensure script base exists otherwise use custom base type.
+ ERR_FAIL_COND(script.is_null());
new_virtual_method_select->select_method_from_base_type(script->get_instance_base_type(), String(), true);
-
return;
} else if (p_button == 0) {
String name = _validate_name("new_function");
selected = name;
- Vector2 ofs = _get_available_pos();
+ Vector2 pos = _get_available_pos();
Ref<VisualScriptFunction> func_node;
- func_node.instance();
+ func_node.instantiate();
func_node->set_name(name);
+ int fn_id = script->get_available_id();
undo_redo->create_action(TTR("Add Function"));
- undo_redo->add_do_method(script.ptr(), "add_function", name);
- undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs);
+ undo_redo->add_do_method(script.ptr(), "add_function", name, fn_id);
+ undo_redo->add_do_method(script.ptr(), "add_node", fn_id, func_node, pos);
undo_redo->add_undo_method(script.ptr(), "remove_function", name);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", fn_id);
undo_redo->add_do_method(this, "_update_members");
undo_redo->add_undo_method(this, "_update_members");
undo_redo->add_do_method(this, "_update_graph");
@@ -1458,11 +1441,11 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt
_update_graph();
}
- return; //or crash because it will become invalid
+ return; // Or crash because it will become invalid.
}
- if (ti == root->get_children()->get_next()) {
- //add variable
+ if (ti == root->get_first_child()->get_next()) {
+ // Add variable.
String name = _validate_name("new_variable");
selected = name;
@@ -1474,11 +1457,11 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt
undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
undo_redo->commit_action();
- return; //or crash because it will become invalid
+ return; // Or crash because it will become invalid.
}
- if (ti == root->get_children()->get_next()->get_next()) {
- //add variable
+ if (ti == root->get_first_child()->get_next()->get_next()) {
+ // Add variable.
String name = _validate_name("new_signal");
selected = name;
@@ -1490,9 +1473,9 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt
undo_redo->add_do_method(this, "emit_signal", "edited_script_changed");
undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
undo_redo->commit_action();
- return; //or crash because it will become invalid
+ return; // Or crash because it will become invalid.
}
- } else if (ti->get_parent() == root->get_children()) {
+ } 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->popup();
@@ -1502,9 +1485,7 @@ void VisualScriptEditor::_member_button(Object *p_item, int p_column, int p_butt
}
void VisualScriptEditor::_add_input_port(int p_id) {
- StringName func = _get_function_of_node(p_id);
-
- Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
+ Ref<VisualScriptLists> vsn = script->get_node(p_id);
if (!vsn.is_valid()) {
return;
}
@@ -1524,9 +1505,7 @@ void VisualScriptEditor::_add_input_port(int p_id) {
}
void VisualScriptEditor::_add_output_port(int p_id) {
- StringName func = _get_function_of_node(p_id);
-
- Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
+ Ref<VisualScriptLists> vsn = script->get_node(p_id);
if (!vsn.is_valid()) {
return;
}
@@ -1546,9 +1525,7 @@ void VisualScriptEditor::_add_output_port(int p_id) {
}
void VisualScriptEditor::_remove_input_port(int p_id, int p_port) {
- StringName func = _get_function_of_node(p_id);
-
- Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
+ Ref<VisualScriptLists> vsn = script->get_node(p_id);
if (!vsn.is_valid()) {
return;
}
@@ -1558,17 +1535,17 @@ void VisualScriptEditor::_remove_input_port(int p_id, int p_port) {
undo_redo->create_action(TTR("Remove Input Port"), UndoRedo::MERGE_ENDS);
int conn_from = -1, conn_port = -1;
- script->get_input_value_port_connection_source(func, p_id, p_port, &conn_from, &conn_port);
+ script->get_input_value_port_connection_source(p_id, p_port, &conn_from, &conn_port);
if (conn_from != -1) {
- undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_id, p_port);
+ undo_redo->add_do_method(script.ptr(), "data_disconnect", conn_from, conn_port, p_id, p_port);
}
undo_redo->add_do_method(vsn.ptr(), "remove_input_data_port", p_port);
undo_redo->add_do_method(this, "_update_graph", p_id);
if (conn_from != -1) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_id, p_port);
+ undo_redo->add_undo_method(script.ptr(), "data_connect", conn_from, conn_port, p_id, p_port);
}
undo_redo->add_undo_method(vsn.ptr(), "add_input_data_port", vsn->get_input_value_port_info(p_port).type, vsn->get_input_value_port_info(p_port).name, p_port);
@@ -1580,9 +1557,7 @@ void VisualScriptEditor::_remove_input_port(int p_id, int p_port) {
}
void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
- StringName func = _get_function_of_node(p_id);
-
- Ref<VisualScriptLists> vsn = script->get_node(func, p_id);
+ Ref<VisualScriptLists> vsn = script->get_node(p_id);
if (!vsn.is_valid()) {
return;
}
@@ -1592,16 +1567,16 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
undo_redo->create_action(TTR("Remove Output Port"), UndoRedo::MERGE_ENDS);
List<VisualScript::DataConnection> data_connections;
- script->get_data_connection_list(func, &data_connections);
+ script->get_data_connection_list(&data_connections);
HashMap<int, Set<int>> conn_map;
- for (const List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- if (E->get().from_node == p_id && E->get().from_port == p_port) {
- // push into the connections map
- if (!conn_map.has(E->get().to_node)) {
- conn_map.set(E->get().to_node, Set<int>());
+ for (const VisualScript::DataConnection &E : data_connections) {
+ if (E.from_node == p_id && E.from_port == p_port) {
+ // Push into the connections map.
+ if (!conn_map.has(E.to_node)) {
+ conn_map.set(E.to_node, Set<int>());
}
- conn_map[E->get().to_node].insert(E->get().to_port);
+ conn_map[E.to_node].insert(E.to_port);
}
}
@@ -1610,9 +1585,9 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
List<int> keys;
conn_map.get_key_list(&keys);
- for (const List<int>::Element *E = keys.front(); E; E = E->next()) {
- for (const Set<int>::Element *F = conn_map[E->get()].front(); F; F = F->next()) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", func, p_id, p_port, E->get(), F->get());
+ for (const int &E : keys) {
+ for (const Set<int>::Element *F = conn_map[E].front(); F; F = F->next()) {
+ undo_redo->add_undo_method(script.ptr(), "data_connect", p_id, p_port, E, F);
}
}
@@ -1625,9 +1600,7 @@ void VisualScriptEditor::_remove_output_port(int p_id, int p_port) {
}
void VisualScriptEditor::_expression_text_changed(const String &p_text, int p_id) {
- StringName func = _get_function_of_node(p_id);
-
- Ref<VisualScriptExpression> vse = script->get_node(func, p_id);
+ Ref<VisualScriptExpression> vse = script->get_node(p_id);
if (!vse.is_valid()) {
return;
}
@@ -1643,39 +1616,36 @@ 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)->set_size(Vector2(1, 1)); // Shrink if text is smaller.
}
updating_graph = false;
}
-Vector2 VisualScriptEditor::_get_available_pos(bool centered, Vector2 ofs) const {
- if (centered) {
- ofs = graph->get_scroll_ofs() + graph->get_size() * 0.5;
- }
-
+Vector2 VisualScriptEditor::_get_pos_in_graph(Vector2 p_point) const {
+ Vector2 pos = (graph->get_scroll_ofs() + p_point) / (graph->get_zoom() * EDSCALE);
if (graph->is_using_snap()) {
int snap = graph->get_snap();
- ofs = ofs.snapped(Vector2(snap, snap));
+ pos = pos.snapped(Vector2(snap, snap));
}
+ return pos;
+}
- ofs /= EDSCALE;
+Vector2 VisualScriptEditor::_get_available_pos(bool p_centered, Vector2 p_pos) const {
+ if (p_centered) {
+ p_pos = _get_pos_in_graph(graph->get_size() * 0.5);
+ }
while (true) {
bool exists = false;
- List<StringName> all_fn;
- script->get_function_list(&all_fn);
- for (List<StringName>::Element *F = all_fn.front(); F; F = F->next()) {
- StringName curr_fn = F->get();
- List<int> existing;
- script->get_node_list(curr_fn, &existing);
- for (List<int>::Element *E = existing.front(); E; E = E->next()) {
- Point2 pos = script->get_node_position(curr_fn, E->get());
- if (pos.distance_to(ofs) < 50) {
- ofs += Vector2(graph->get_snap(), graph->get_snap());
- exists = true;
- break;
- }
+ List<int> existing;
+ script->get_node_list(&existing);
+ for (int &E : existing) {
+ Point2 pos = script->get_node_position(E);
+ if (pos.distance_to(p_pos) < 50) {
+ p_pos += Vector2(graph->get_snap(), graph->get_snap());
+ exists = true;
+ break;
}
}
if (exists) {
@@ -1684,7 +1654,7 @@ Vector2 VisualScriptEditor::_get_available_pos(bool centered, Vector2 ofs) const
break;
}
- return ofs;
+ return p_pos;
}
String VisualScriptEditor::_validate_name(const String &p_name) const {
@@ -1706,8 +1676,130 @@ 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
+ // Delete all the selected nodes.
List<int> to_erase;
@@ -1720,35 +1812,33 @@ void VisualScriptEditor::_on_nodes_delete() {
}
}
- if (to_erase.empty()) {
+ if (to_erase.is_empty()) {
return;
}
undo_redo->create_action(TTR("Remove VisualScript Nodes"));
- for (List<int>::Element *F = to_erase.front(); F; F = F->next()) {
- int cr_node = F->get();
-
- StringName func = _get_function_of_node(cr_node);
+ for (int &F : to_erase) {
+ int cr_node = F;
- undo_redo->add_do_method(script.ptr(), "remove_node", func, cr_node);
- undo_redo->add_undo_method(script.ptr(), "add_node", func, cr_node, script->get_node(func, cr_node), script->get_node_position(func, cr_node));
+ undo_redo->add_do_method(script.ptr(), "remove_node", cr_node);
+ undo_redo->add_undo_method(script.ptr(), "add_node", cr_node, script->get_node(cr_node), script->get_node_position(cr_node));
List<VisualScript::SequenceConnection> sequence_conns;
- script->get_sequence_connection_list(func, &sequence_conns);
+ script->get_sequence_connection_list(&sequence_conns);
- for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) {
- if (E->get().from_node == cr_node || E->get().to_node == cr_node) {
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, E->get().from_node, E->get().from_output, E->get().to_node);
+ for (const VisualScript::SequenceConnection &E : sequence_conns) {
+ if (E.from_node == cr_node || E.to_node == cr_node) {
+ undo_redo->add_undo_method(script.ptr(), "sequence_connect", E.from_node, E.from_output, E.to_node);
}
}
List<VisualScript::DataConnection> data_conns;
- script->get_data_connection_list(func, &data_conns);
+ script->get_data_connection_list(&data_conns);
- for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) {
- if (E->get().from_node == F->get() || E->get().to_node == F->get()) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ for (const VisualScript::DataConnection &E : data_conns) {
+ if (E.from_node == F || E.to_node == F) {
+ undo_redo->add_undo_method(script.ptr(), "data_connect", E.from_node, E.from_port, E.to_node, E.to_port);
}
}
}
@@ -1760,7 +1850,6 @@ void VisualScriptEditor::_on_nodes_delete() {
void VisualScriptEditor::_on_nodes_duplicate() {
Set<int> to_duplicate;
- List<StringName> funcs;
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
@@ -1768,12 +1857,11 @@ void VisualScriptEditor::_on_nodes_duplicate() {
if (gn->is_selected() && gn->is_close_button_visible()) {
int id = gn->get_name().operator String().to_int();
to_duplicate.insert(id);
- funcs.push_back(_get_function_of_node(id));
}
}
}
- if (to_duplicate.empty()) {
+ if (to_duplicate.is_empty()) {
return;
}
@@ -1784,9 +1872,8 @@ void VisualScriptEditor::_on_nodes_duplicate() {
HashMap<int, int> remap;
for (Set<int>::Element *F = to_duplicate.front(); F; F = F->next()) {
- // duplicate from the specific function but place it into the default func as it would lack the connections
- StringName func = _get_function_of_node(F->get());
- Ref<VisualScriptNode> node = script->get_node(func, F->get());
+ // Duplicate from the specific function but place it into the default func as it would lack the connections.
+ Ref<VisualScriptNode> node = script->get_node(F->get());
Ref<VisualScriptNode> dupe = node->duplicate(true);
@@ -1794,25 +1881,23 @@ void VisualScriptEditor::_on_nodes_duplicate() {
remap.set(F->get(), new_id);
to_select.insert(new_id);
- undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, dupe, script->get_node_position(func, F->get()) + Vector2(20, 20));
- undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, dupe, script->get_node_position(F->get()) + Vector2(20, 20));
+ undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
}
- for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) {
- List<VisualScript::SequenceConnection> seqs;
- script->get_sequence_connection_list(F->get(), &seqs);
- for (List<VisualScript::SequenceConnection>::Element *E = seqs.front(); E; E = E->next()) {
- if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", default_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]);
- }
+ List<VisualScript::SequenceConnection> seqs;
+ script->get_sequence_connection_list(&seqs);
+ for (const VisualScript::SequenceConnection &E : seqs) {
+ if (to_duplicate.has(E.from_node) && to_duplicate.has(E.to_node)) {
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", remap[E.from_node], E.from_output, remap[E.to_node]);
}
+ }
- List<VisualScript::DataConnection> data;
- script->get_data_connection_list(F->get(), &data);
- for (List<VisualScript::DataConnection>::Element *E = data.front(); E; E = E->next()) {
- if (to_duplicate.has(E->get().from_node) && to_duplicate.has(E->get().to_node)) {
- undo_redo->add_do_method(script.ptr(), "data_connect", default_func, remap[E->get().from_node], E->get().from_port, remap[E->get().to_node], E->get().to_port);
- }
+ List<VisualScript::DataConnection> data;
+ script->get_data_connection_list(&data);
+ for (const VisualScript::DataConnection &E : data) {
+ if (to_duplicate.has(E.from_node) && to_duplicate.has(E.to_node)) {
+ undo_redo->add_do_method(script.ptr(), "data_connect", remap[E.from_node], E.from_port, remap[E.to_node], E.to_port);
}
}
@@ -1830,7 +1915,7 @@ void VisualScriptEditor::_on_nodes_duplicate() {
}
if (to_select.size()) {
- EditorNode::get_singleton()->push_item(script->get_node(default_func, to_select.front()->get()).ptr());
+ EditorNode::get_singleton()->push_item(script->get_node(to_select.front()->get()).ptr());
}
}
@@ -1843,7 +1928,7 @@ void VisualScriptEditor::_generic_search(String p_base_type, Vector2 pos, bool n
new_connect_node_select->select_from_visual_script(p_base_type, false, false); // neither connecting nor reset text
- // ensure that the dialog fits inside the graph
+ // Ensure that the dialog fits inside the graph.
Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size();
pos.x = pos.x > bounds.x ? bounds.x : pos.x;
pos.y = pos.y > bounds.y ? bounds.y : pos.y;
@@ -1853,7 +1938,9 @@ void VisualScriptEditor::_generic_search(String p_base_type, Vector2 pos, bool n
}
}
-void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) {
+void VisualScriptEditor::input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(p_event.is_null());
+
// GUI input for VS Editor Plugin
Ref<InputEventMouseButton> key = p_event;
@@ -1865,7 +1952,7 @@ void VisualScriptEditor::_input(const Ref<InputEvent> &p_event) {
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() == 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();
@@ -1880,18 +1967,18 @@ void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) {
TreeItem *ti = members->get_selected();
if (ti) {
TreeItem *root = members->get_root();
- if (ti->get_parent() == root->get_children()) {
+ if (ti->get_parent() == root->get_first_child()) {
member_type = MEMBER_FUNCTION;
}
- if (ti->get_parent() == root->get_children()->get_next()) {
+ if (ti->get_parent() == root->get_first_child()->get_next()) {
member_type = MEMBER_VARIABLE;
}
- if (ti->get_parent() == root->get_children()->get_next()->get_next()) {
+ if (ti->get_parent() == root->get_first_child()->get_next()->get_next()) {
member_type = MEMBER_SIGNAL;
}
member_name = ti->get_text(0);
}
- if (ED_IS_SHORTCUT("visual_script_editor/delete_selected", p_event)) {
+ if (ED_IS_SHORTCUT("ui_graph_delete", p_event)) {
_member_option(MEMBER_REMOVE);
}
if (ED_IS_SHORTCUT("visual_script_editor/edit_member", p_event)) {
@@ -1901,10 +1988,10 @@ void VisualScriptEditor::_members_gui_input(const Ref<InputEvent> &p_event) {
}
Ref<InputEventMouseButton> btn = p_event;
- if (btn.is_valid() && btn->is_doubleclick()) {
+ if (btn.is_valid() && btn->is_double_click()) {
TreeItem *ti = members->get_selected();
- if (ti && ti->get_parent() == members->get_root()->get_children()) { // to check if it's a function
- _center_on_node(ti->get_metadata(0), script->get_function_node_id(ti->get_metadata(0)));
+ if (ti && ti->get_parent() == members->get_root()->get_first_child()) { // to check if it's a function
+ _center_on_node(script->get_function_node_id(ti->get_metadata(0)));
}
}
}
@@ -1922,8 +2009,8 @@ void VisualScriptEditor::_rename_function(const String &name, const String &new_
int node_id = script->get_function_node_id(name);
Ref<VisualScriptFunction> func;
- if (script->has_node(name, node_id)) {
- func = script->get_node(name, node_id);
+ if (script->has_node(node_id)) {
+ func = script->get_node(node_id);
}
undo_redo->create_action(TTR("Rename Function"));
undo_redo->add_do_method(script.ptr(), "rename_function", name, new_name);
@@ -1933,21 +2020,17 @@ void VisualScriptEditor::_rename_function(const String &name, const String &new_
undo_redo->add_undo_method(func.ptr(), "set_name", name);
}
- // also fix all function calls
- List<StringName> flst;
- script->get_function_list(&flst);
- for (List<StringName>::Element *E = flst.front(); E; E = E->next()) {
- List<int> lst;
- script->get_node_list(E->get(), &lst);
- for (List<int>::Element *F = lst.front(); F; F = F->next()) {
- Ref<VisualScriptFunctionCall> fncall = script->get_node(E->get(), F->get());
- if (!fncall.is_valid()) {
- continue;
- }
- if (fncall->get_function() == name) {
- undo_redo->add_do_method(fncall.ptr(), "set_function", new_name);
- undo_redo->add_undo_method(fncall.ptr(), "set_function", name);
- }
+ // Also fix all function calls.
+ List<int> lst;
+ script->get_node_list(&lst);
+ for (int &F : lst) {
+ Ref<VisualScriptFunctionCall> fncall = script->get_node(F);
+ if (!fncall.is_valid()) {
+ continue;
+ }
+ if (fncall->get_function() == name) {
+ undo_redo->add_do_method(fncall.ptr(), "set_function", new_name);
+ undo_redo->add_undo_method(fncall.ptr(), "set_function", name);
}
}
@@ -1966,7 +2049,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();
@@ -1989,13 +2072,13 @@ Variant VisualScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_f
Dictionary dd;
TreeItem *root = members->get_root();
- if (it->get_parent() == root->get_children()) {
+ if (it->get_parent() == root->get_first_child()) {
dd["type"] = "visual_script_function_drag";
dd["function"] = type;
- } else if (it->get_parent() == root->get_children()->get_next()) {
+ } else if (it->get_parent() == root->get_first_child()->get_next()) {
dd["type"] = "visual_script_variable_drag";
dd["variable"] = type;
- } else if (it->get_parent() == root->get_children()->get_next()->get_next()) {
+ } else if (it->get_parent() == root->get_first_child()->get_next()->get_next()) {
dd["type"] = "visual_script_signal_drag";
dd["signal"] = type;
@@ -2025,7 +2108,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
@@ -2033,7 +2116,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
@@ -2041,7 +2124,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
@@ -2091,16 +2174,9 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
return;
}
- Vector2 ofs = graph->get_scroll_ofs() + p_point;
+ Vector2 pos = _get_pos_in_graph(p_point);
- if (graph->is_using_snap()) {
- int snap = graph->get_snap();
- ofs = ofs.snapped(Vector2(snap, snap));
- }
-
- ofs /= EDSCALE;
-
- int new_id = _create_new_node_from_name(d["node_type"], ofs, default_func);
+ int new_id = _create_new_node_from_name(d["node_type"], pos);
Node *node = graph->get_node(itos(new_id));
if (node) {
@@ -2111,36 +2187,30 @@ 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_CONTROL);
+ bool use_set = Input::get_singleton()->is_key_pressed(Key::CTRL);
#endif
- Vector2 ofs = graph->get_scroll_ofs() + p_point;
- if (graph->is_using_snap()) {
- int snap = graph->get_snap();
- ofs = ofs.snapped(Vector2(snap, snap));
- }
-
- ofs /= EDSCALE;
+ Vector2 pos = _get_pos_in_graph(p_point);
Ref<VisualScriptNode> vnode;
if (use_set) {
- Ref<VisualScriptVariableSet> vnodes;
- vnodes.instance();
- vnodes->set_variable(d["variable"]);
- vnode = vnodes;
+ Ref<VisualScriptPropertySet> pset;
+ pset.instantiate();
+ vnode = pset;
} else {
- Ref<VisualScriptVariableGet> vnodeg;
- vnodeg.instance();
- 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(script.ptr(), "add_node", default_func, new_id, vnode, ofs);
- undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
+ 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");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
@@ -2153,26 +2223,20 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
if (String(d["type"]) == "visual_script_function_drag") {
- Vector2 ofs = graph->get_scroll_ofs() + p_point;
- if (graph->is_using_snap()) {
- int snap = graph->get_snap();
- ofs = ofs.snapped(Vector2(snap, snap));
- }
-
- ofs /= EDSCALE;
+ Vector2 pos = _get_pos_in_graph(p_point);
Ref<VisualScriptFunctionCall> vnode;
- vnode.instance();
+ vnode.instantiate();
vnode->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SELF);
int new_id = script->get_available_id();
undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs);
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, pos);
undo_redo->add_do_method(vnode.ptr(), "set_base_type", script->get_instance_base_type());
undo_redo->add_do_method(vnode.ptr(), "set_function", d["function"]);
- undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
@@ -2185,23 +2249,17 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
if (String(d["type"]) == "visual_script_signal_drag") {
- Vector2 ofs = graph->get_scroll_ofs() + p_point;
- if (graph->is_using_snap()) {
- int snap = graph->get_snap();
- ofs = ofs.snapped(Vector2(snap, snap));
- }
-
- ofs /= EDSCALE;
+ Vector2 pos = _get_pos_in_graph(p_point);
Ref<VisualScriptEmitSignal> vnode;
- vnode.instance();
+ vnode.instantiate();
vnode->set_signal(d["signal"]);
int new_id = script->get_available_id();
undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, vnode, ofs);
- undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
+ 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");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
@@ -2214,23 +2272,17 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
if (String(d["type"]) == "resource") {
- Vector2 ofs = graph->get_scroll_ofs() + p_point;
- if (graph->is_using_snap()) {
- int snap = graph->get_snap();
- ofs = ofs.snapped(Vector2(snap, snap));
- }
-
- ofs /= EDSCALE;
+ Vector2 pos = _get_pos_in_graph(p_point);
Ref<VisualScriptPreload> prnode;
- prnode.instance();
+ prnode.instantiate();
prnode->set_preload(d["resource"]);
int new_id = script->get_available_id();
undo_redo->create_action(TTR("Add Preload Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, prnode, ofs);
- undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, prnode, pos);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
@@ -2243,13 +2295,12 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
if (String(d["type"]) == "files") {
- Vector2 ofs = graph->get_scroll_ofs() + p_point;
- if (graph->is_using_snap()) {
- int snap = graph->get_snap();
- ofs = ofs.snapped(Vector2(snap, snap));
- }
-
- ofs /= EDSCALE;
+#ifdef OSX_ENABLED
+ bool use_preload = Input::get_singleton()->is_key_pressed(Key::META);
+#else
+ bool use_preload = Input::get_singleton()->is_key_pressed(Key::CTRL);
+#endif
+ Vector2 pos = _get_pos_in_graph(p_point);
Array files = d["files"];
@@ -2257,23 +2308,32 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
int new_id = script->get_available_id();
if (files.size()) {
- undo_redo->create_action(TTR("Add Preload Node"));
+ undo_redo->create_action(TTR("Add Node(s)"));
for (int i = 0; i < files.size(); i++) {
Ref<Resource> res = ResourceLoader::load(files[i]);
if (!res.is_valid()) {
continue;
}
+ Ref<Script> drop_script = ResourceLoader::load(files[i]);
+ if (drop_script.is_valid() && drop_script->is_tool() && drop_script->get_instance_base_type() == "VisualScriptCustomNode" && !use_preload) {
+ Ref<VisualScriptCustomNode> vscn;
+ vscn.instantiate();
+ vscn->set_script(drop_script);
+
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, vscn, pos);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
+ } else {
+ Ref<VisualScriptPreload> prnode;
+ prnode.instantiate();
+ prnode->set_preload(res);
- Ref<VisualScriptPreload> prnode;
- prnode.instance();
- prnode->set_preload(res);
-
- undo_redo->add_do_method(script.ptr(), "add_node", default_func, new_id, prnode, ofs);
- undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, new_id);
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, prnode, pos);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
+ }
new_ids.push_back(new_id);
new_id++;
- ofs += Vector2(20, 20) * EDSCALE;
+ pos += Vector2(20, 20);
}
undo_redo->add_do_method(this, "_update_graph");
@@ -2281,8 +2341,8 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
undo_redo->commit_action();
}
- for (List<int>::Element *E = new_ids.front(); E; E = E->next()) {
- Node *node = graph->get_node(itos(E->get()));
+ for (int &E : new_ids) {
+ Node *node = graph->get_node(itos(E));
if (node) {
graph->set_selected(node);
_node_selected(node);
@@ -2299,59 +2359,47 @@ 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_CONTROL);
+ bool use_node = Input::get_singleton()->is_key_pressed(Key::CTRL);
#endif
Array nodes = d["nodes"];
- Vector2 ofs = graph->get_scroll_ofs() + p_point;
-
- if (graph->is_using_snap()) {
- int snap = graph->get_snap();
- ofs = ofs.snapped(Vector2(snap, snap));
- }
- ofs /= EDSCALE;
+ Vector2 pos = _get_pos_in_graph(p_point);
undo_redo->create_action(TTR("Add Node(s) From Tree"));
int base_id = script->get_available_id();
- if (nodes.size() > 1) {
- use_node = true;
- }
-
- for (int i = 0; i < nodes.size(); i++) {
- NodePath np = nodes[i];
- Node *node = get_node(np);
- if (!node) {
- continue;
- }
+ if (use_node || nodes.size() > 1) {
+ for (int i = 0; i < nodes.size(); i++) {
+ NodePath np = nodes[i];
+ Node *node = get_node(np);
+ if (!node) {
+ continue;
+ }
- Ref<VisualScriptNode> n;
+ Ref<VisualScriptNode> n;
- if (use_node) {
Ref<VisualScriptSceneNode> scene_node;
- scene_node.instance();
+ scene_node.instantiate();
scene_node->set_node_path(sn->get_path_to(node));
n = scene_node;
- } else {
- // ! Doesn't work properly
- Ref<VisualScriptFunctionCall> call;
- call.instance();
- call->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH);
- call->set_base_path(sn->get_path_to(node));
- call->set_base_type(node->get_class());
- n = call;
- method_select->select_from_instance(node, "", true, node->get_class());
- selecting_method_id = base_id;
- }
- undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, n, ofs);
- undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, base_id);
+ undo_redo->add_do_method(script.ptr(), "add_node", base_id, n, pos);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", base_id);
- base_id++;
- ofs += Vector2(25, 25);
+ base_id++;
+ pos += Vector2(25, 25);
+ }
+
+ } else {
+ NodePath np = nodes[0];
+ Node *node = get_node(np);
+ drop_position = pos;
+ drop_node = node;
+ drop_path = sn->get_path_to(node);
+ new_connect_node_select->select_from_instance(node, "", false, node->get_class());
}
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
@@ -2361,7 +2409,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;
}
@@ -2373,21 +2421,15 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
Node *node = Object::cast_to<Node>(obj);
- Vector2 ofs = graph->get_scroll_ofs() + p_point;
-
- if (graph->is_using_snap()) {
- int snap = graph->get_snap();
- ofs = ofs.snapped(Vector2(snap, snap));
- }
+ Vector2 pos = _get_pos_in_graph(p_point);
- ofs /= EDSCALE;
#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_CONTROL);
+ 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 {
@@ -2400,30 +2442,28 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
if (!use_get) {
Ref<VisualScriptPropertySet> pset;
- pset.instance();
+ pset.instantiate();
pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_INSTANCE);
pset->set_base_type(obj->get_class());
- /*if (use_value) {
- pset->set_use_builtin_value(true);
- pset->set_builtin_value(d["value"]);
- }*/
vnode = pset;
} else {
Ref<VisualScriptPropertyGet> pget;
- pget.instance();
+ 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", default_func, base_id, vnode, ofs);
+ 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", default_func, base_id);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", base_id);
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
@@ -2442,18 +2482,17 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
if (!use_get) {
Ref<VisualScriptPropertySet> pset;
- pset.instance();
+ pset.instantiate();
if (sn == node) {
pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF);
} else {
pset->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH);
pset->set_base_path(sn->get_path_to(node));
}
-
vnode = pset;
} else {
Ref<VisualScriptPropertyGet> pget;
- pget.instance();
+ pget.instantiate();
if (sn == node) {
pget->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF);
} else {
@@ -2462,12 +2501,16 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
vnode = pget;
}
- undo_redo->add_do_method(script.ptr(), "add_node", default_func, base_id, vnode, ofs);
+ 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", default_func, base_id);
+
+ undo_redo->add_undo_method(script.ptr(), "remove_node", base_id);
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
@@ -2476,21 +2519,13 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
}
}
-void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type, const bool p_connecting) {
- Ref<VisualScriptFunctionCall> vsfc = script->get_node(default_func, selecting_method_id);
- if (!vsfc.is_valid()) {
- return;
- }
- vsfc->set_function(p_method);
-}
-
void VisualScriptEditor::_draw_color_over_button(Object *obj, Color p_color) {
Button *button = Object::cast_to<Button>(obj);
if (!button) {
return;
}
- Ref<StyleBox> normal = get_theme_stylebox("normal", "Button");
+ Ref<StyleBox> normal = get_theme_stylebox(SNAME("normal"), SNAME("Button"));
button->draw_rect(Rect2(normal->get_offset(), button->get_size() - normal->get_minimum_size()), p_color);
}
@@ -2535,16 +2570,8 @@ void VisualScriptEditor::set_edited_resource(const RES &p_res) {
script->connect("node_ports_changed", callable_mp(this, &VisualScriptEditor::_node_ports_changed));
- default_func = script->get_default_func();
-
- if (!script->has_function(default_func)) // this is the supposed default function
- {
- script->add_function(default_func);
- script->set_edited(true); //so that if a function was added it's saved
- }
-
_update_graph();
- _update_members();
+ call_deferred(SNAME("_update_members"));
}
void VisualScriptEditor::enable_editor() {
@@ -2560,38 +2587,40 @@ 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().empty()) {
- name = TTR("[unsaved]");
- }
- name += "(*)";
+ name = script->get_path().get_file();
+ if (name.is_empty()) {
+ // This appears for newly created built-in scripts before saving the scene.
+ name = TTR("[unsaved]");
+ } else if (script->is_built_in()) {
+ const String &script_name = script->get_name();
+ if (script_name != "") {
+ // If the built-in script has a custom resource name defined,
+ // display the built-in script name as follows: `ResourceName (scene_file.tscn)`
+ name = vformat("%s (%s)", script_name, name.get_slice("::", 0));
}
- } else if (script->get_name() != "") {
- name = script->get_name();
- } else {
- name = script->get_class() + "(" + itos(script->get_instance_id()) + ")";
+ }
+
+ if (is_unsaved()) {
+ name += "(*)";
}
return name;
}
Ref<Texture2D> VisualScriptEditor::get_theme_icon() {
- return Control::get_theme_icon("VisualScript", "EditorIcons");
+ return Control::get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons"));
}
bool VisualScriptEditor::is_unsaved() {
bool unsaved =
script->is_edited() ||
script->are_subnodes_edited() ||
- script->get_path().empty(); // In memory.
+ script->get_path().is_empty(); // In memory.
return unsaved;
}
Variant VisualScriptEditor::get_edit_state() {
Dictionary d;
- d["function"] = default_func;
d["scroll"] = graph->get_scroll_ofs();
d["zoom"] = graph->get_zoom();
d["using_snap"] = graph->is_using_snap();
@@ -2601,9 +2630,6 @@ Variant VisualScriptEditor::get_edit_state() {
void VisualScriptEditor::set_edit_state(const Variant &p_state) {
Dictionary d = p_state;
- if (d.has("function")) {
- selected = default_func;
- }
_update_graph();
_update_members();
@@ -2622,11 +2648,11 @@ void VisualScriptEditor::set_edit_state(const Variant &p_state) {
}
}
-void VisualScriptEditor::_center_on_node(const StringName &p_func, int p_id) {
+void VisualScriptEditor::_center_on_node(int p_id) {
Node *n = graph->get_node(itos(p_id));
GraphNode *gn = Object::cast_to<GraphNode>(n);
- // clear selection
+ // Clear selection.
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gnd = Object::cast_to<GraphNode>(graph->get_child(i));
if (gnd) {
@@ -2636,30 +2662,25 @@ void VisualScriptEditor::_center_on_node(const StringName &p_func, int p_id) {
if (gn) {
gn->set_selected(true);
- Vector2 new_scroll = gn->get_offset() - graph->get_size() * 0.5 + gn->get_size() * 0.5;
+ Vector2 new_scroll = gn->get_position_offset() - graph->get_size() * 0.5 + gn->get_size() * 0.5;
graph->set_scroll_ofs(new_scroll);
- script->set_function_scroll(p_func, new_scroll / EDSCALE);
+ script->set_scroll(new_scroll / EDSCALE);
script->set_edited(true);
}
}
void VisualScriptEditor::goto_line(int p_line, bool p_with_error) {
- p_line += 1; //add one because script lines begin from 0.
+ p_line += 1; // Add one because script lines begin from 0.
if (p_with_error) {
error_line = p_line;
}
- List<StringName> functions;
- script->get_function_list(&functions);
- for (List<StringName>::Element *E = functions.front(); E; E = E->next()) {
- if (script->has_node(E->get(), p_line)) {
- _update_graph();
- _update_members();
+ if (script->has_node(p_line)) {
+ _update_graph();
+ _update_members();
- call_deferred("call_deferred", "_center_on_node", E->get(), 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.
}
}
@@ -2698,13 +2719,13 @@ Array VisualScriptEditor::get_breakpoints() {
Array breakpoints;
List<StringName> functions;
script->get_function_list(&functions);
- for (List<StringName>::Element *E = functions.front(); E; E = E->next()) {
+ for (int i = 0; i < functions.size(); i++) {
List<int> nodes;
- script->get_node_list(E->get(), &nodes);
- for (List<int>::Element *F = nodes.front(); F; F = F->next()) {
- Ref<VisualScriptNode> vsn = script->get_node(E->get(), F->get());
+ script->get_node_list(&nodes);
+ for (int &F : nodes) {
+ Ref<VisualScriptNode> vsn = script->get_node(F);
if (vsn->is_breakpoint()) {
- breakpoints.push_back(F->get() - 1); //subtract 1 because breakpoints in text start from zero
+ breakpoints.push_back(F - 1); // Subtract 1 because breakpoints in text start from zero.
}
}
}
@@ -2715,12 +2736,12 @@ void VisualScriptEditor::add_callback(const String &p_function, PackedStringArra
if (script->has_function(p_function)) {
_update_members();
_update_graph();
- _center_on_node(p_function, script->get_function_node_id(p_function));
+ _center_on_node(script->get_function_node_id(p_function));
return;
}
Ref<VisualScriptFunction> func;
- func.instance();
+ func.instantiate();
for (int i = 0; i < p_args.size(); i++) {
String name = p_args[i];
Variant::Type type = Variant::NIL;
@@ -2739,15 +2760,15 @@ void VisualScriptEditor::add_callback(const String &p_function, PackedStringArra
func->add_argument(type, name);
}
-
+ int fn_id = script->get_available_id();
func->set_name(p_function);
- script->add_function(p_function);
- script->add_node(p_function, script->get_available_id(), func);
+ script->add_function(p_function, fn_id);
+ script->add_node(fn_id, func);
_update_members();
_update_graph();
- _center_on_node(p_function, script->get_function_node_id(p_function));
+ _center_on_node(script->get_function_node_id(p_function));
}
bool VisualScriptEditor::show_members_overview() {
@@ -2765,6 +2786,10 @@ void VisualScriptEditor::set_debugger_active(bool p_active) {
}
}
+Control *VisualScriptEditor::get_base_editor() const {
+ return graph;
+}
+
void VisualScriptEditor::set_tooltip_request_func(String p_method, Object *p_obj) {
}
@@ -2841,62 +2866,46 @@ void VisualScriptEditor::_end_node_move() {
undo_redo->commit_action();
}
-void VisualScriptEditor::_move_node(const StringName &p_func, int p_id, const Vector2 &p_to) {
- if (!script->has_function(p_func)) {
+void VisualScriptEditor::_move_node(int p_id, const Vector2 &p_to) {
+ if (!script->has_node(p_id)) {
return;
}
Node *node = graph->get_node(itos(p_id));
if (Object::cast_to<GraphNode>(node)) {
- Object::cast_to<GraphNode>(node)->set_offset(p_to);
+ Object::cast_to<GraphNode>(node)->set_position_offset(p_to);
}
- script->set_node_position(p_func, p_id, p_to / EDSCALE);
-}
-
-StringName VisualScriptEditor::_get_function_of_node(int p_id) const {
- List<StringName> funcs;
- script->get_function_list(&funcs);
- for (List<StringName>::Element *E = funcs.front(); E; E = E->next()) {
- if (script->has_node(E->get(), p_id)) {
- return E->get();
- }
- }
-
- return ""; // this is passed to avoid crash and is tested against later
+ script->set_node_position(p_id, p_to / EDSCALE);
}
void VisualScriptEditor::_node_moved(Vector2 p_from, Vector2 p_to, int p_id) {
- StringName func = _get_function_of_node(p_id);
-
- undo_redo->add_do_method(this, "_move_node", func, p_id, p_to);
- undo_redo->add_undo_method(this, "_move_node", func, p_id, p_from);
+ undo_redo->add_do_method(this, "_move_node", p_id, p_to);
+ undo_redo->add_undo_method(this, "_move_node", p_id, p_from);
}
void VisualScriptEditor::_remove_node(int p_id) {
undo_redo->create_action(TTR("Remove VisualScript Node"));
- StringName func = _get_function_of_node(p_id);
-
- undo_redo->add_do_method(script.ptr(), "remove_node", func, p_id);
- undo_redo->add_undo_method(script.ptr(), "add_node", func, p_id, script->get_node(func, p_id), script->get_node_position(func, p_id));
+ undo_redo->add_do_method(script.ptr(), "remove_node", p_id);
+ undo_redo->add_undo_method(script.ptr(), "add_node", p_id, script->get_node(p_id), script->get_node_position(p_id));
List<VisualScript::SequenceConnection> sequence_conns;
- script->get_sequence_connection_list(func, &sequence_conns);
+ script->get_sequence_connection_list(&sequence_conns);
- for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) {
- if (E->get().from_node == p_id || E->get().to_node == p_id) {
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, E->get().from_node, E->get().from_output, E->get().to_node);
+ for (const VisualScript::SequenceConnection &E : sequence_conns) {
+ if (E.from_node == p_id || E.to_node == p_id) {
+ undo_redo->add_undo_method(script.ptr(), "sequence_connect", E.from_node, E.from_output, E.to_node);
}
}
List<VisualScript::DataConnection> data_conns;
- script->get_data_connection_list(func, &data_conns);
+ script->get_data_connection_list(&data_conns);
- for (List<VisualScript::DataConnection>::Element *E = data_conns.front(); E; E = E->next()) {
- if (E->get().from_node == p_id || E->get().to_node == p_id) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", func, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ for (const VisualScript::DataConnection &E : data_conns) {
+ if (E.from_node == p_id || E.to_node == p_id) {
+ undo_redo->add_undo_method(script.ptr(), "data_connect", E.from_node, E.from_port, E.to_node, E.to_port);
}
}
@@ -2906,17 +2915,17 @@ void VisualScriptEditor::_remove_node(int p_id) {
undo_redo->commit_action();
}
-void VisualScriptEditor::_node_ports_changed(const String &p_func, int p_id) {
+void VisualScriptEditor::_node_ports_changed(int p_id) {
_update_graph(p_id);
}
-bool VisualScriptEditor::node_has_sequence_connections(const StringName &p_func, int p_id) {
+bool VisualScriptEditor::node_has_sequence_connections(int p_id) {
List<VisualScript::SequenceConnection> sequence_conns;
- script->get_sequence_connection_list(p_func, &sequence_conns);
+ script->get_sequence_connection_list(&sequence_conns);
- for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) {
- int from = E->get().from_node;
- int to = E->get().to_node;
+ for (const VisualScript::SequenceConnection &E : sequence_conns) {
+ int from = E.from_node;
+ int to = E.to_node;
if (to == p_id || from == p_id) {
return true;
@@ -2927,9 +2936,7 @@ bool VisualScriptEditor::node_has_sequence_connections(const StringName &p_func,
}
void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) {
- StringName from_func = _get_function_of_node(p_from.to_int());
-
- Ref<VisualScriptNode> from_node = script->get_node(from_func, p_from.to_int());
+ Ref<VisualScriptNode> from_node = script->get_node(p_from.to_int());
ERR_FAIL_COND(!from_node.is_valid());
bool from_seq;
@@ -2939,9 +2946,7 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot,
return; //can't connect this, it's invalid
}
- StringName to_func = _get_function_of_node(p_to.to_int());
-
- Ref<VisualScriptNode> to_node = script->get_node(to_func, p_to.to_int());
+ Ref<VisualScriptNode> to_node = script->get_node(p_to.to_int());
ERR_FAIL_COND(!to_node.is_valid());
bool to_seq;
@@ -2953,56 +2958,37 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot,
ERR_FAIL_COND(from_seq != to_seq);
- // Do all the checks here
- StringName func; // this the func where we store the one the nodes at the end of the resolution on having multiple nodes
-
- undo_redo->create_action(TTR("Connect Nodes"));
-
- if (from_func == to_func) {
- func = to_func;
- } else if (from_seq) {
- // this is a sequence connection
- _move_nodes_with_rescan(to_func, from_func, p_to.to_int()); // this function moves the nodes from func1 to func2
- func = from_func;
- } else {
- if (node_has_sequence_connections(to_func, p_to.to_int())) {
- if (node_has_sequence_connections(from_func, p_from.to_int())) {
- ERR_PRINT("Trying to connect between different sequence node trees");
- return;
- } else {
- _move_nodes_with_rescan(from_func, to_func, p_from.to_int());
- func = to_func;
- }
- } else if (node_has_sequence_connections(from_func, p_from.to_int())) {
- if (from_func == default_func) {
- _move_nodes_with_rescan(from_func, to_func, p_from.to_int());
- func = to_func;
- } else {
- _move_nodes_with_rescan(to_func, from_func, p_to.to_int());
- func = from_func;
- }
- } else {
- if (to_func == default_func) {
- _move_nodes_with_rescan(to_func, from_func, p_to.to_int());
- func = from_func;
- } else {
- _move_nodes_with_rescan(from_func, to_func, p_from.to_int());
- func = to_func;
- }
+ // Checking to prevent warnings.
+ if (from_seq) {
+ if (script->has_sequence_connection(p_from.to_int(), from_port, p_to.to_int())) {
+ return;
}
+ } else if (script->has_data_connection(p_from.to_int(), from_port, p_to.to_int(), to_port)) {
+ return;
}
+ // Preventing connection to itself.
+ if (p_from.to_int() == p_to.to_int()) {
+ return;
+ }
+
+ // Do all the checks here.
+ StringName func; // This the func where we store the one the nodes at the end of the resolution on having multiple nodes.
+
+ undo_redo->create_action(TTR("Connect Nodes"));
+
if (from_seq) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", func, p_from.to_int(), from_port, p_to.to_int());
- // this undo error on undo after move can't be removed without painful gymnastics
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, p_from.to_int(), from_port, p_to.to_int());
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", p_from.to_int(), from_port, p_to.to_int());
+ // This undo error on undo after move can't be removed without painful gymnastics
+ undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", p_from.to_int(), from_port, p_to.to_int());
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
} else {
bool converted = false;
- int conv_node = -1;
Ref<VisualScriptOperator> oper = to_node;
if (oper.is_valid() && oper->get_typed() == Variant::NIL) {
- // it's an operator Node and if the type is already nil
+ // It's an operator Node and if the type is already nil
if (from_node->get_output_value_port_info(from_port).type != Variant::NIL) {
oper->set_typed(from_node->get_output_value_port_info(from_port).type);
}
@@ -3010,107 +2996,36 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot,
Ref<VisualScriptOperator> operf = from_node;
if (operf.is_valid() && operf->get_typed() == Variant::NIL) {
- // it's an operator Node and if the type is already nil
+ // It's an operator Node and if the type is already nil
if (to_node->get_input_value_port_info(to_port).type != Variant::NIL) {
operf->set_typed(to_node->get_input_value_port_info(to_port).type);
}
}
- Variant::Type to_type = to_node->get_input_value_port_info(to_port).type;
- Variant::Type from_type = from_node->get_output_value_port_info(from_port).type;
-
- if (to_type != Variant::NIL && from_type != Variant::NIL && to_type != from_type) {
- // add a constructor node between the ports
- bool exceptions = false; // true if there are any exceptions
- exceptions = exceptions || (to_type == Variant::INT && from_type == Variant::FLOAT);
- exceptions = exceptions || (to_type == Variant::FLOAT && from_type == Variant::INT);
- if (Variant::can_convert(from_type, to_type) && !exceptions) {
- MethodInfo mi;
- mi.name = Variant::get_type_name(to_type);
- PropertyInfo pi;
- pi.name = "from";
- pi.type = from_type;
- mi.arguments.push_back(pi);
- mi.return_val.type = to_type;
- // we know that this is allowed so create a new constructor node
- Ref<VisualScriptConstructor> constructor;
- constructor.instance();
- constructor->set_constructor_type(to_type);
- constructor->set_constructor(mi);
- // add the new constructor node
-
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(p_from));
- GraphNode *gn2 = Object::cast_to<GraphNode>(graph->get_node(p_to));
- if (gn && gn2) {
- Vector2 from_node_size = gn->get_rect().get_size();
- Vector2 to_node_size = gn2->get_rect().get_size();
- Vector2 to_node_pos = script->get_node_position(func, p_to.to_int());
- Vector2 from_node_pos = script->get_node_position(func, p_from.to_int());
- Vector2 new_to_node_pos = from_node_pos;
- Vector2 constructor_pos;
- if ((to_node_pos.x - from_node_pos.x) < 0) {
- // to is behind from node
- if (to_node_pos.x > (from_node_pos.x - to_node_size.x - 240)) {
- new_to_node_pos.x = from_node_pos.x - to_node_size.x - 240; // approx size of constructor node + padding
- } else {
- new_to_node_pos.x = to_node_pos.x;
- }
- new_to_node_pos.y = to_node_pos.y;
- constructor_pos.x = from_node_pos.x - 210;
- constructor_pos.y = to_node_pos.y;
- } else {
- // to is ahead of from node
- if (to_node_pos.x < (from_node_size.x + from_node_pos.x + 240)) {
- new_to_node_pos.x = from_node_size.x + from_node_pos.x + 240; // approx size of constructor node + padding
- } else {
- new_to_node_pos.x = to_node_pos.x;
- }
- new_to_node_pos.y = to_node_pos.y;
- constructor_pos.x = from_node_size.x + from_node_pos.x + 10;
- constructor_pos.y = to_node_pos.y;
- }
- undo_redo->add_do_method(this, "_move_node", func, p_to.to_int(), new_to_node_pos);
- undo_redo->add_undo_method(this, "_move_node", func, p_to.to_int(), to_node_pos);
- conv_node = script->get_available_id();
- undo_redo->add_do_method(script.ptr(), "add_node", func, conv_node, constructor, _get_available_pos(false, constructor_pos));
- undo_redo->add_undo_method(script.ptr(), "remove_node", func, conv_node);
- converted = true;
- }
- }
- }
-
- // disconnect current, and connect the new one
- if (script->is_input_value_port_connected(func, p_to.to_int(), to_port)) {
+ // Disconnect current, and connect the new one
+ if (script->is_input_value_port_connected(p_to.to_int(), to_port)) {
if (can_swap && data_disconnect_node == p_to.to_int()) {
int conn_from;
int conn_port;
- script->get_input_value_port_connection_source(func, p_to.to_int(), to_port, &conn_from, &conn_port);
- undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_to.to_int(), to_port);
- undo_redo->add_do_method(script.ptr(), "data_connect", func, conn_from, conn_port, data_disconnect_node, data_disconnect_port);
- undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, data_disconnect_node, data_disconnect_port);
- undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_to.to_int(), to_port);
+ script->get_input_value_port_connection_source(p_to.to_int(), to_port, &conn_from, &conn_port);
+ undo_redo->add_do_method(script.ptr(), "data_disconnect", conn_from, conn_port, p_to.to_int(), to_port);
+ undo_redo->add_do_method(script.ptr(), "data_connect", conn_from, conn_port, data_disconnect_node, data_disconnect_port);
+ undo_redo->add_undo_method(script.ptr(), "data_disconnect", conn_from, conn_port, data_disconnect_node, data_disconnect_port);
+ undo_redo->add_undo_method(script.ptr(), "data_connect", conn_from, conn_port, p_to.to_int(), to_port);
can_swap = false; // swapped
} else {
int conn_from;
int conn_port;
- script->get_input_value_port_connection_source(func, p_to.to_int(), to_port, &conn_from, &conn_port);
- undo_redo->add_do_method(script.ptr(), "data_disconnect", func, conn_from, conn_port, p_to.to_int(), to_port);
- undo_redo->add_undo_method(script.ptr(), "data_connect", func, conn_from, conn_port, p_to.to_int(), to_port);
+ script->get_input_value_port_connection_source(p_to.to_int(), to_port, &conn_from, &conn_port);
+ undo_redo->add_do_method(script.ptr(), "data_disconnect", conn_from, conn_port, p_to.to_int(), to_port);
+ undo_redo->add_undo_method(script.ptr(), "data_connect", conn_from, conn_port, p_to.to_int(), to_port);
}
}
if (!converted) {
- undo_redo->add_do_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, p_to.to_int(), to_port);
- undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, p_to.to_int(), to_port);
- } else {
- // this is noice
- undo_redo->add_do_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, conv_node, 0);
- undo_redo->add_do_method(script.ptr(), "data_connect", func, conv_node, 0, p_to.to_int(), to_port);
- // I don't think this is needed but gonna leave it here for now... until I need to finalise it all
- undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, conv_node, 0);
- undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, conv_node, 0, p_to.to_int(), to_port);
- }
- //update nodes in graph
- if (!converted) {
+ undo_redo->add_do_method(script.ptr(), "data_connect", p_from.to_int(), from_port, p_to.to_int(), to_port);
+ undo_redo->add_undo_method(script.ptr(), "data_disconnect", p_from.to_int(), from_port, p_to.to_int(), to_port);
+
+ // Update nodes in graph
undo_redo->add_do_method(this, "_update_graph", p_from.to_int());
undo_redo->add_do_method(this, "_update_graph", p_to.to_int());
undo_redo->add_undo_method(this, "_update_graph", p_from.to_int());
@@ -3121,34 +3036,28 @@ void VisualScriptEditor::_graph_connected(const String &p_from, int p_from_slot,
}
}
- undo_redo->add_do_method(this, "_update_graph_connections");
- undo_redo->add_undo_method(this, "_update_graph_connections");
-
undo_redo->commit_action();
}
void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot) {
- StringName func = _get_function_of_node(p_from.to_int());
- ERR_FAIL_COND(func != _get_function_of_node(p_to.to_int()));
-
- Ref<VisualScriptNode> from_node = script->get_node(func, p_from.to_int());
+ Ref<VisualScriptNode> from_node = script->get_node(p_from.to_int());
ERR_FAIL_COND(!from_node.is_valid());
bool from_seq;
int from_port;
if (!_get_out_slot(from_node, p_from_slot, from_port, from_seq)) {
- return; //can't connect this, it's invalid
+ return; // Can't connect this, it's invalid.
}
- Ref<VisualScriptNode> to_node = script->get_node(func, p_to.to_int());
+ Ref<VisualScriptNode> to_node = script->get_node(p_to.to_int());
ERR_FAIL_COND(!to_node.is_valid());
bool to_seq;
int to_port;
if (!_get_in_slot(to_node, p_to_slot, to_port, to_seq)) {
- return; //can't connect this, it's invalid
+ return; // Can't connect this, it's invalid.
}
ERR_FAIL_COND(from_seq != to_seq);
@@ -3156,248 +3065,27 @@ void VisualScriptEditor::_graph_disconnected(const String &p_from, int p_from_sl
undo_redo->create_action(TTR("Disconnect Nodes"));
if (from_seq) {
- undo_redo->add_do_method(script.ptr(), "sequence_disconnect", func, p_from.to_int(), from_port, p_to.to_int());
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", func, p_from.to_int(), from_port, p_to.to_int());
+ undo_redo->add_do_method(script.ptr(), "sequence_disconnect", p_from.to_int(), from_port, p_to.to_int());
+ undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_from.to_int(), from_port, p_to.to_int());
+ undo_redo->add_do_method(this, "_update_graph");
+ undo_redo->add_undo_method(this, "_update_graph");
} else {
can_swap = true;
data_disconnect_node = p_to.to_int();
data_disconnect_port = to_port;
- undo_redo->add_do_method(script.ptr(), "data_disconnect", func, p_from.to_int(), from_port, p_to.to_int(), to_port);
- undo_redo->add_undo_method(script.ptr(), "data_connect", func, p_from.to_int(), from_port, p_to.to_int(), to_port);
- //update relevant nodes in the graph
+ undo_redo->add_do_method(script.ptr(), "data_disconnect", p_from.to_int(), from_port, p_to.to_int(), to_port);
+ undo_redo->add_undo_method(script.ptr(), "data_connect", p_from.to_int(), from_port, p_to.to_int(), to_port);
+ // Update relevant nodes in the graph.
undo_redo->add_do_method(this, "_update_graph", p_from.to_int());
undo_redo->add_do_method(this, "_update_graph", p_to.to_int());
undo_redo->add_undo_method(this, "_update_graph", p_from.to_int());
undo_redo->add_undo_method(this, "_update_graph", p_to.to_int());
}
- undo_redo->add_do_method(this, "_update_graph_connections");
- undo_redo->add_undo_method(this, "_update_graph_connections");
undo_redo->commit_action();
}
-void VisualScriptEditor::_move_nodes_with_rescan(const StringName &p_func_from, const StringName &p_func_to, int p_id) {
- Set<int> nodes_to_move;
- HashMap<int, Map<int, int>> seqconns_to_move; // from => List(outp, to)
- HashMap<int, Map<int, Pair<int, int>>> dataconns_to_move; // to => List(inp_p => from, outp)
-
- nodes_to_move.insert(p_id);
- Set<int> sequence_connections;
- {
- List<VisualScript::SequenceConnection> sequence_conns;
- script->get_sequence_connection_list(p_func_from, &sequence_conns);
-
- HashMap<int, Map<int, int>> seqcons; // from => List(out_p => to)
-
- for (List<VisualScript::SequenceConnection>::Element *E = sequence_conns.front(); E; E = E->next()) {
- int from = E->get().from_node;
- int to = E->get().to_node;
- int out_p = E->get().from_output;
- if (!seqcons.has(from)) {
- seqcons.set(from, Map<int, int>());
- }
- seqcons[from].insert(out_p, to);
- sequence_connections.insert(to);
- sequence_connections.insert(from);
- }
-
- int conn = p_id;
- List<int> stack;
- HashMap<int, Set<int>> seen; // from, outp
- while (seqcons.has(conn)) {
- for (auto E = seqcons[conn].front(); E; E = E->next()) {
- if (seen.has(conn) && seen[conn].has(E->key())) {
- if (!E->next()) {
- if (stack.size() > 0) {
- conn = stack.back()->get();
- stack.pop_back();
- break;
- }
- conn = -101;
- break;
- }
- continue;
- }
- if (!seen.has(conn)) {
- seen.set(conn, Set<int>());
- }
- seen[conn].insert(E->key());
- stack.push_back(conn);
- if (!seqconns_to_move.has(conn)) {
- seqconns_to_move.set(conn, Map<int, int>());
- }
- seqconns_to_move[conn].insert(E->key(), E->get());
- conn = E->get();
- nodes_to_move.insert(conn);
- break;
- }
- if (!seqcons.has(conn) && stack.size() > 0) {
- conn = stack.back()->get();
- stack.pop_back();
- }
- }
- }
-
- {
- List<VisualScript::DataConnection> data_connections;
- script->get_data_connection_list(p_func_from, &data_connections);
- int func_from_node_id = script->get_function_node_id(p_func_from);
-
- HashMap<int, Map<int, Pair<int, int>>> connections;
-
- for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- int from = E->get().from_node;
- int to = E->get().to_node;
- int out_p = E->get().from_port;
- int in_p = E->get().to_port;
-
- // skip if the from_node is a function node
- if (from == func_from_node_id) {
- continue;
- }
-
- if (!connections.has(to)) {
- connections.set(to, Map<int, Pair<int, int>>());
- }
- connections[to].insert(in_p, Pair<int, int>(from, out_p));
- }
-
- // go through the HashMap and do all sorts of crazy ass stuff now...
- Set<int> nodes_to_be_added;
- for (Set<int>::Element *F = nodes_to_move.front(); F; F = F->next()) {
- HashMap<int, Set<int>> seen;
- List<int> stack;
- int id = F->get();
- while (connections.has(id)) {
- for (auto E = connections[id].front(); E; E = E->next()) {
- if (seen.has(id) && seen[id].has(E->key())) {
- if (!E->next()) {
- if (stack.size() > 0) {
- id = stack.back()->get();
- stack.pop_back();
- break;
- }
- id = -11; // I assume ids can't be negative should confirm it...
- break;
- }
- continue;
- }
-
- if (sequence_connections.has(E->get().first)) {
- if (!nodes_to_move.has(E->get().first)) {
- if (stack.size() > 0) {
- id = stack.back()->get();
- stack.pop_back();
- break;
- }
- id = -11; // I assume ids can't be negative should confirm it...
- break;
- }
- }
-
- if (!seen.has(id)) {
- seen.set(id, Set<int>());
- }
- seen[id].insert(E->key());
- stack.push_back(id);
- if (!dataconns_to_move.has(id)) {
- dataconns_to_move.set(id, Map<int, Pair<int, int>>());
- }
- dataconns_to_move[id].insert(E->key(), Pair<int, int>(E->get().first, E->get().second));
- id = E->get().first;
- nodes_to_be_added.insert(id);
- break;
- }
- if (!connections.has(id) && stack.size() > 0) {
- id = stack.back()->get();
- stack.pop_back();
- }
- }
- }
- for (Set<int>::Element *E = nodes_to_be_added.front(); E; E = E->next()) {
- nodes_to_move.insert(E->get());
- }
- }
-
- // * this is primarily for the sake of the having proper undo
- List<VisualScript::SequenceConnection> seqext;
- List<VisualScript::DataConnection> dataext;
-
- List<VisualScript::SequenceConnection> seq_connections;
- script->get_sequence_connection_list(p_func_from, &seq_connections);
-
- for (List<VisualScript::SequenceConnection>::Element *E = seq_connections.front(); E; E = E->next()) {
- if (!nodes_to_move.has(E->get().from_node) && nodes_to_move.has(E->get().to_node)) {
- seqext.push_back(E->get());
- } else if (nodes_to_move.has(E->get().from_node) && !nodes_to_move.has(E->get().to_node)) {
- seqext.push_back(E->get());
- }
- }
-
- List<VisualScript::DataConnection> data_connections;
- script->get_data_connection_list(p_func_from, &data_connections);
-
- for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- if (!nodes_to_move.has(E->get().from_node) && nodes_to_move.has(E->get().to_node)) {
- dataext.push_back(E->get());
- } else if (nodes_to_move.has(E->get().from_node) && !nodes_to_move.has(E->get().to_node)) {
- dataext.push_back(E->get());
- }
- }
-
- // undo_redo->create_action("Rescan Functions");
-
- for (Set<int>::Element *E = nodes_to_move.front(); E; E = E->next()) {
- int id = E->get();
-
- undo_redo->add_do_method(script.ptr(), "remove_node", p_func_from, id);
- undo_redo->add_do_method(script.ptr(), "add_node", p_func_to, id, script->get_node(p_func_from, id), script->get_node_position(p_func_from, id));
-
- undo_redo->add_undo_method(script.ptr(), "remove_node", p_func_to, id);
- undo_redo->add_undo_method(script.ptr(), "add_node", p_func_from, id, script->get_node(p_func_from, id), script->get_node_position(p_func_from, id));
- }
-
- List<int> skeys;
- seqconns_to_move.get_key_list(&skeys);
- for (List<int>::Element *E = skeys.front(); E; E = E->next()) {
- int from_node = E->get();
- for (Map<int, int>::Element *F = seqconns_to_move[from_node].front(); F; F = F->next()) {
- int from_port = F->key();
- int to_node = F->get();
- undo_redo->add_do_method(script.ptr(), "sequence_connect", p_func_to, from_node, from_port, to_node);
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_func_from, from_node, from_port, to_node);
- }
- }
-
- List<int> keys;
- dataconns_to_move.get_key_list(&keys);
- for (List<int>::Element *E = keys.front(); E; E = E->next()) {
- int to_node = E->get(); // to_node
- for (Map<int, Pair<int, int>>::Element *F = dataconns_to_move[E->get()].front(); F; F = F->next()) {
- int inp_p = F->key();
- Pair<int, int> fro = F->get();
-
- undo_redo->add_do_method(script.ptr(), "data_connect", p_func_to, fro.first, fro.second, to_node, inp_p);
- undo_redo->add_undo_method(script.ptr(), "data_connect", p_func_from, fro.first, fro.second, to_node, inp_p);
- }
- }
-
- // this to have proper undo operations
- for (List<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) {
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", p_func_from, E->get().from_node, E->get().from_output, E->get().to_node);
- }
- for (List<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", p_func_from, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
- }
- // this doesn't need do methods as they are handled by the subsequent do calls implicitly
-
- undo_redo->add_do_method(this, "_update_graph");
- undo_redo->add_undo_method(this, "_update_graph");
-
- // undo_redo->commit_action();
-}
-
void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_pos) {
Node *node = graph->get_node(p_from);
GraphNode *gn = Object::cast_to<GraphNode>(node);
@@ -3405,23 +3093,22 @@ void VisualScriptEditor::_graph_connect_to_empty(const String &p_from, int p_fro
return;
}
- StringName func = _get_function_of_node(p_from.to_int());
-
- Ref<VisualScriptNode> vsn = script->get_node(func, p_from.to_int());
+ Ref<VisualScriptNode> vsn = script->get_node(p_from.to_int());
if (!vsn.is_valid()) {
return;
}
-
- port_action_pos = p_release_pos;
+ if (vsn->get_output_value_port_count() || vsn->get_output_sequence_port_count()) {
+ port_action_pos = p_release_pos;
+ }
if (p_from_slot < vsn->get_output_sequence_port_count()) {
port_action_node = p_from.to_int();
port_action_output = p_from_slot;
- _port_action_menu(CREATE_ACTION, func);
+ _port_action_menu(CREATE_ACTION);
} else {
port_action_output = p_from_slot - vsn->get_output_sequence_port_count();
port_action_node = p_from.to_int();
- _port_action_menu(CREATE_CALL_SET_GET, func);
+ _port_action_menu(CREATE_CALL_SET_GET);
}
}
@@ -3435,11 +3122,9 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac
visited_nodes.insert(p_port_action_node);
- StringName func = _get_function_of_node(p_port_action_node);
-
- Ref<VisualScriptNode> node = script->get_node(func, p_port_action_node);
+ Ref<VisualScriptNode> node = script->get_node(p_port_action_node);
- if (!node.is_valid()) {
+ if (!node.is_valid() || node->get_output_value_port_count() <= p_port_action_output) {
return tg;
}
@@ -3451,11 +3136,11 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac
g.type = pi.type;
if (g.type == Variant::NIL || g.type == Variant::OBJECT) {
- //any or object input, must further guess what this is
+ // Any or object input, must further guess what this is.
int from_node;
int from_port;
- if (script->get_input_value_port_connection_source(func, p_port_action_node, i, &from_node, &from_port)) {
+ if (script->get_input_value_port_connection_source(p_port_action_node, i, &from_node, &from_port)) {
g = _guess_output_type(from_node, from_port, visited_nodes);
} else {
Variant defval = node->get_default_input_value(i);
@@ -3477,20 +3162,13 @@ VisualScriptNode::TypeGuess VisualScriptEditor::_guess_output_type(int p_port_ac
return node->guess_output_type(in_guesses.ptrw(), p_port_action_output);
}
-void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func) {
- Vector2 ofs = graph->get_scroll_ofs() + port_action_pos;
- if (graph->is_using_snap()) {
- int snap = graph->get_snap();
- ofs = ofs.snapped(Vector2(snap, snap));
- }
- ofs /= EDSCALE;
-
+void VisualScriptEditor::_port_action_menu(int p_option) {
Set<int> vn;
switch (p_option) {
case CREATE_CALL_SET_GET: {
Ref<VisualScriptFunctionCall> n;
- n.instance();
+ n.instantiate();
VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
@@ -3500,8 +3178,8 @@ void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func)
n->set_base_type("Object");
}
String type_string;
- if (script->get_node(func, port_action_node)->get_output_value_port_count() > 0) {
- type_string = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ if (script->get_node(port_action_node)->get_output_value_port_count() > 0) {
+ type_string = script->get_node(port_action_node)->get_output_value_port_info(port_action_output).hint_string;
}
if (tg.type == Variant::OBJECT) {
if (tg.script.is_valid()) {
@@ -3516,7 +3194,7 @@ void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func)
} else {
new_connect_node_select->select_from_basic_type(tg.type);
}
- // ensure that the dialog fits inside the graph
+ // Ensure that the dialog fits inside the graph.
Vector2 pos = mouse_up_position;
Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size();
pos.x = pos.x > bounds.x ? bounds.x : pos.x;
@@ -3526,8 +3204,8 @@ void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func)
case CREATE_ACTION: {
VisualScriptNode::TypeGuess tg = _guess_output_type(port_action_node, port_action_output, vn);
PropertyInfo property_info;
- if (script->get_node(func, port_action_node)->get_output_value_port_count() > 0) {
- property_info = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output);
+ if (script->get_node(port_action_node)->get_output_value_port_count() > 0) {
+ 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()) {
@@ -3540,7 +3218,7 @@ void VisualScriptEditor::_port_action_menu(int p_option, const StringName &func)
} else {
new_connect_node_select->select_from_action(Variant::get_type_name(tg.type));
}
- // ensure that the dialog fits inside the graph
+ // Ensure that the dialog fits inside the graph.
Vector2 pos = mouse_up_position;
Size2 bounds = graph->get_global_position() + graph->get_size() - new_connect_node_select->get_size();
pos.x = pos.x > bounds.x ? bounds.x : pos.x;
@@ -3569,36 +3247,33 @@ void VisualScriptEditor::connect_data(Ref<VisualScriptNode> vnode_old, Ref<Visua
if (port >= value_count) {
port = 0;
}
- StringName func = _get_function_of_node(port_action_node);
- undo_redo->add_do_method(script.ptr(), "data_connect", func, port_action_node, port, new_id, 0);
- undo_redo->add_undo_method(script.ptr(), "data_disconnect", func, port_action_node, port, new_id, 0);
+ undo_redo->add_do_method(script.ptr(), "data_connect", port_action_node, port, new_id, 0);
+ undo_redo->add_undo_method(script.ptr(), "data_disconnect", port_action_node, port, new_id, 0);
undo_redo->commit_action();
}
void VisualScriptEditor::_selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting) {
- Vector2 ofs = graph->get_scroll_ofs() + port_action_pos;
- if (graph->is_using_snap()) {
- int snap = graph->get_snap();
- ofs = ofs.snapped(Vector2(snap, snap));
- }
- ofs /= EDSCALE;
- ofs /= graph->get_zoom();
+ Vector2 pos = _get_pos_in_graph(port_action_pos);
Set<int> vn;
+ if (drop_position != Vector2()) {
+ pos = drop_position;
+ }
+ drop_position = Vector2();
+
bool port_node_exists = true;
- StringName func = _get_function_of_node(port_action_node);
- if (func == StringName()) {
- func = default_func;
- port_node_exists = false;
- }
+ // if (func == StringName()) {
+ // func = default_func;
+ // port_node_exists = false;
+ // }
if (p_category == "visualscript") {
Ref<VisualScriptNode> vnode_new = VisualScriptLanguage::singleton->create_node_from_name(p_text);
Ref<VisualScriptNode> vnode_old;
- if (port_node_exists) {
- vnode_old = script->get_node(func, port_action_node);
+ if (port_node_exists && p_connecting) {
+ vnode_old = script->get_node(port_action_node);
}
int new_id = script->get_available_id();
@@ -3621,13 +3296,13 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
}
undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode_new, ofs);
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode_new, pos);
if (vnode_old.is_valid() && p_connecting) {
connect_seq(vnode_old, vnode_new, new_id);
connect_data(vnode_old, vnode_new, new_id);
}
- undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
@@ -3639,53 +3314,97 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
if (p_category == String("method")) {
Ref<VisualScriptFunctionCall> n;
- n.instance();
+ n.instantiate();
+ if (!drop_path.is_empty()) {
+ if (drop_path == ".") {
+ n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SELF);
+ } else {
+ n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH);
+ n->set_base_path(drop_path);
+ }
+ }
+ if (drop_node) {
+ n->set_base_type(drop_node->get_class());
+ if (drop_node->get_script_instance()) {
+ n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
+ }
+ }
vnode = n;
} else if (p_category == String("set")) {
Ref<VisualScriptPropertySet> n;
- n.instance();
+ n.instantiate();
+ if (!drop_path.is_empty()) {
+ if (drop_path == ".") {
+ n->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF);
+ } else {
+ n->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH);
+ n->set_base_path(drop_path);
+ }
+ }
+ if (drop_node) {
+ n->set_base_type(drop_node->get_class());
+ if (drop_node->get_script_instance()) {
+ n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
+ }
+ }
vnode = n;
script_prop_set = n;
} else if (p_category == String("get")) {
Ref<VisualScriptPropertyGet> n;
- n.instance();
+ n.instantiate();
n->set_property(p_text);
+ if (!drop_path.is_empty()) {
+ if (drop_path == ".") {
+ n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF);
+ } else {
+ n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH);
+ n->set_base_path(drop_path);
+ }
+ }
+ if (drop_node) {
+ n->set_base_type(drop_node->get_class());
+ if (drop_node->get_script_instance()) {
+ n->set_base_script(drop_node->get_script_instance()->get_script()->get_path());
+ }
+ }
vnode = n;
}
+ drop_path = String();
+ drop_node = nullptr;
if (p_category == String("action")) {
if (p_text == "VisualScriptCondition") {
Ref<VisualScriptCondition> n;
- n.instance();
+ n.instantiate();
vnode = n;
}
if (p_text == "VisualScriptSwitch") {
Ref<VisualScriptSwitch> n;
- n.instance();
+ n.instantiate();
vnode = n;
} else if (p_text == "VisualScriptSequence") {
Ref<VisualScriptSequence> n;
- n.instance();
+ n.instantiate();
vnode = n;
} else if (p_text == "VisualScriptIterator") {
Ref<VisualScriptIterator> n;
- n.instance();
+ n.instantiate();
vnode = n;
} else if (p_text == "VisualScriptWhile") {
Ref<VisualScriptWhile> n;
- n.instance();
+ n.instantiate();
vnode = n;
} else if (p_text == "VisualScriptReturn") {
Ref<VisualScriptReturn> n;
- n.instance();
+ n.instantiate();
vnode = n;
}
}
int new_id = script->get_available_id();
undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode, ofs);
- undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id);
+ 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", new_id);
undo_redo->add_undo_method(this, "_update_graph", new_id);
undo_redo->commit_action();
@@ -3696,7 +3415,7 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
port_action_new_node = new_id;
- Ref<VisualScriptNode> vsn = script->get_node(func, port_action_new_node);
+ Ref<VisualScriptNode> vsn = script->get_node(port_action_new_node);
if (Object::cast_to<VisualScriptFunctionCall>(vsn.ptr())) {
Ref<VisualScriptFunctionCall> vsfc = vsn;
@@ -3710,10 +3429,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
vsfc->set_base_type(String(""));
if (tg.gdclass != StringName()) {
vsfc->set_base_type(tg.gdclass);
-
- } else if (script->get_node(func, port_action_node).is_valid()) {
- PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint;
- String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ } 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) {
vsfc->set_base_type(base_type);
@@ -3746,9 +3464,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
if (tg.gdclass != StringName()) {
vsp->set_base_type(tg.gdclass);
- } else if (script->get_node(func, port_action_node).is_valid()) {
- PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint;
- String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ } 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) {
vsp->set_base_type(base_type);
@@ -3776,9 +3494,9 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
if (tg.gdclass != StringName()) {
vsp->set_base_type(tg.gdclass);
- } else if (script->get_node(func, port_action_node).is_valid()) {
- PropertyHint hint = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint;
- String base_type = script->get_node(func, port_action_node)->get_output_value_port_info(port_action_output).hint_string;
+ } 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) {
vsp->set_base_type(base_type);
}
@@ -3796,16 +3514,13 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri
}
}
if (port_node_exists) {
- Ref<VisualScriptNode> vnode_old = script->get_node(func, port_action_node);
+ Ref<VisualScriptNode> vnode_old = script->get_node(port_action_node);
if (vnode_old.is_valid() && p_connecting) {
connect_seq(vnode_old, vnode, port_action_new_node);
connect_data(vnode_old, vnode, port_action_new_node);
}
}
_update_graph(port_action_new_node);
- if (port_node_exists) {
- _update_graph_connections();
- }
}
void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id) {
@@ -3824,29 +3539,27 @@ void VisualScriptEditor::connect_seq(Ref<VisualScriptNode> vnode_old, Ref<Visual
return;
}
- StringName func = _get_function_of_node(port_action_node);
-
undo_redo->create_action(TTR("Connect Node Sequence"));
int pass_port = -vnode_old->get_output_sequence_port_count() + 1;
int return_port = port_action_output - 1;
if (vnode_old->get_output_value_port_info(port_action_output).name == String("pass") &&
- !script->get_output_sequence_ports_connected(func, port_action_node).has(pass_port)) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, pass_port, new_id);
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, pass_port, new_id);
+ !script->get_output_sequence_ports_connected(port_action_node).has(pass_port)) {
+ 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(func, port_action_node).has(return_port)) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, return_port, new_id);
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, return_port, new_id);
+ !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 {
for (int port = 0; port < vnode_old->get_output_sequence_port_count(); port++) {
int count = vnode_old->get_output_sequence_port_count();
- if (port_action_output < count && !script->get_output_sequence_ports_connected(func, port_action_node).has(port_action_output)) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, port_action_output, new_id);
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, port_action_output, new_id);
+ if (port_action_output < count && !script->get_output_sequence_ports_connected(port_action_node).has(port_action_output)) {
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, port_action_output, new_id);
+ undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, port_action_output, new_id);
break;
- } else if (!script->get_output_sequence_ports_connected(func, port_action_node).has(port)) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", func, port_action_node, port, new_id);
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", func, port_action_node, port, new_id);
+ } else if (!script->get_output_sequence_ports_connected(port_action_node).has(port)) {
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", port_action_node, port, new_id);
+ undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", port_action_node, port, new_id);
break;
}
}
@@ -3867,9 +3580,9 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons
List<MethodInfo> methods;
bool found = false;
ClassDB::get_virtual_methods(script->get_instance_base_type(), &methods);
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name == name) {
- minfo = E->get();
+ for (const MethodInfo &E : methods) {
+ if (E.name == name) {
+ minfo = E;
found = true;
}
}
@@ -3879,26 +3592,29 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons
selected = name;
Ref<VisualScriptFunction> func_node;
- func_node.instance();
+ func_node.instantiate();
func_node->set_name(name);
-
+ int fn_id = script->get_available_id();
undo_redo->create_action(TTR("Add Function"));
- undo_redo->add_do_method(script.ptr(), "add_function", name);
+ undo_redo->add_do_method(script.ptr(), "add_function", name, fn_id);
for (int i = 0; i < minfo.arguments.size(); i++) {
func_node->add_argument(minfo.arguments[i].type, minfo.arguments[i].name, -1, minfo.arguments[i].hint, minfo.arguments[i].hint_string);
}
- Vector2 ofs = _get_available_pos();
+ Vector2 pos = _get_available_pos();
- undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id(), func_node, ofs);
+ undo_redo->add_do_method(script.ptr(), "add_node", fn_id, func_node, pos);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", fn_id);
if (minfo.return_val.type != Variant::NIL || minfo.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
Ref<VisualScriptReturn> ret_node;
- ret_node.instance();
+ ret_node.instantiate();
ret_node->set_return_type(minfo.return_val.type);
ret_node->set_enable_return_value(true);
ret_node->set_name(name);
- undo_redo->add_do_method(script.ptr(), "add_node", name, script->get_available_id() + 1, ret_node, _get_available_pos(false, ofs + Vector2(500, 0)));
+ int nid = script->get_available_id() + 1;
+ undo_redo->add_do_method(script.ptr(), "add_node", nid, ret_node, _get_available_pos(false, pos + Vector2(500, 0)));
+ undo_redo->add_undo_method(script.ptr(), "remove_node", nid);
}
undo_redo->add_undo_method(script.ptr(), "remove_function", name);
@@ -3913,21 +3629,16 @@ void VisualScriptEditor::_selected_new_virtual_method(const String &p_text, cons
}
void VisualScriptEditor::_cancel_connect_node() {
- // ensure the cancel is done
+ // Ensure the cancel is done.
port_action_new_node = -1;
}
-int VisualScriptEditor::_create_new_node_from_name(const String &p_text, const Vector2 &p_point, const StringName &p_func) {
- StringName func = default_func;
- if (p_func != StringName()) {
- func = p_func;
- }
-
+int VisualScriptEditor::_create_new_node_from_name(const String &p_text, const Vector2 &p_point) {
Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(p_text);
int new_id = script->get_available_id();
undo_redo->create_action(TTR("Add Node"));
- undo_redo->add_do_method(script.ptr(), "add_node", func, new_id, vnode, p_point);
- undo_redo->add_undo_method(script.ptr(), "remove_node", func, new_id);
+ undo_redo->add_do_method(script.ptr(), "add_node", new_id, vnode, p_point);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", new_id);
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
@@ -3935,7 +3646,7 @@ int VisualScriptEditor::_create_new_node_from_name(const String &p_text, const V
}
void VisualScriptEditor::_default_value_changed() {
- Ref<VisualScriptNode> vsn = script->get_node(_get_function_of_node(editing_id), editing_id);
+ Ref<VisualScriptNode> vsn = script->get_node(editing_id);
if (vsn.is_null()) {
return;
}
@@ -3950,7 +3661,7 @@ void VisualScriptEditor::_default_value_changed() {
}
void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_input_port) {
- Ref<VisualScriptNode> vsn = script->get_node(_get_function_of_node(p_id), p_id);
+ Ref<VisualScriptNode> vsn = script->get_node(p_id);
if (vsn.is_null()) {
return;
}
@@ -3959,8 +3670,9 @@ void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_i
Variant existing = vsn->get_default_input_value(p_input_port);
if (pinfo.type != Variant::NIL && existing.get_type() != pinfo.type) {
Callable::CallError ce;
- const Variant *existingp = &existing;
- existing = Variant::construct(pinfo.type, &existingp, 1, ce, false);
+ Variant e = existing;
+ const Variant *existingp = &e;
+ 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));
@@ -3968,15 +3680,15 @@ void VisualScriptEditor::_default_value_edited(Node *p_button, int p_id, int p_i
if (pinfo.type == Variant::NODE_PATH) {
Node *edited_scene = get_tree()->get_edited_scene_root();
- if (edited_scene) { // Fixing an old crash bug ( Visual Script Crashes on editing NodePath with an empty scene open)
+ if (edited_scene) { // Fixing an old crash bug ( Visual Script Crashes on editing NodePath with an empty scene open).
Node *script_node = _find_script_node(edited_scene, edited_scene, script);
if (script_node) {
- //pick a node relative to the script, IF the script exists
+ // Pick a node relative to the script, IF the script exists.
pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE;
pinfo.hint_string = script_node->get_path();
} else {
- //pick a path relative to edited scene
+ // Pick a path relative to edited scene.
pinfo.hint = PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE;
pinfo.hint_string = get_tree()->get_edited_scene_root()->get_path();
}
@@ -4005,6 +3717,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: {
@@ -4019,40 +3736,43 @@ void VisualScriptEditor::_notification(int p_what) {
return;
}
- edit_variable_edit->add_theme_style_override("bg", get_theme_stylebox("bg", "Tree"));
- edit_signal_edit->add_theme_style_override("bg", get_theme_stylebox("bg", "Tree"));
- func_input_scroll->add_theme_style_override("bg", get_theme_stylebox("bg", "Tree"));
+ 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")));
Ref<Theme> tm = EditorNode::get_singleton()->get_theme_base()->get_theme();
bool dark_theme = tm->get_constant("dark_theme", "Editor");
- List<Pair<String, Color>> colors;
if (dark_theme) {
- colors.push_back(Pair<String, Color>("flow_control", Color(0.96, 0.96, 0.96)));
- colors.push_back(Pair<String, Color>("functions", Color(0.96, 0.52, 0.51)));
- colors.push_back(Pair<String, Color>("data", Color(0.5, 0.96, 0.81)));
- colors.push_back(Pair<String, Color>("operators", Color(0.67, 0.59, 0.87)));
- colors.push_back(Pair<String, Color>("custom", Color(0.5, 0.73, 0.96)));
- colors.push_back(Pair<String, Color>("constants", Color(0.96, 0.5, 0.69)));
+ node_colors["flow_control"] = Color(0.96, 0.96, 0.96);
+ node_colors["functions"] = Color(0.96, 0.52, 0.51);
+ node_colors["data"] = Color(0.5, 0.96, 0.81);
+ node_colors["operators"] = Color(0.67, 0.59, 0.87);
+ node_colors["custom"] = Color(0.5, 0.73, 0.96);
+ node_colors["constants"] = Color(0.96, 0.5, 0.69);
} else {
- colors.push_back(Pair<String, Color>("flow_control", Color(0.26, 0.26, 0.26)));
- colors.push_back(Pair<String, Color>("functions", Color(0.95, 0.4, 0.38)));
- colors.push_back(Pair<String, Color>("data", Color(0.07, 0.73, 0.51)));
- colors.push_back(Pair<String, Color>("operators", Color(0.51, 0.4, 0.82)));
- colors.push_back(Pair<String, Color>("custom", Color(0.31, 0.63, 0.95)));
- colors.push_back(Pair<String, Color>("constants", Color(0.94, 0.18, 0.49)));
+ node_colors["flow_control"] = Color(0.26, 0.26, 0.26);
+ node_colors["functions"] = Color(0.95, 0.4, 0.38);
+ node_colors["data"] = Color(0.07, 0.73, 0.51);
+ node_colors["operators"] = Color(0.51, 0.4, 0.82);
+ node_colors["custom"] = Color(0.31, 0.63, 0.95);
+ node_colors["constants"] = Color(0.94, 0.18, 0.49);
}
- for (List<Pair<String, Color>>::Element *E = colors.front(); E; E = E->next()) {
- Ref<StyleBoxFlat> sb = tm->get_stylebox("frame", "GraphNode");
+ 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();
- Color c = sb->get_border_color();
- Color cn = E->get().second;
- cn.a = c.a;
- frame_style->set_border_color(cn);
- node_styles[E->get().first] = frame_style;
+ // 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.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;
}
}
@@ -4062,6 +3782,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;
}
@@ -4074,11 +3795,8 @@ void VisualScriptEditor::_graph_ofs_changed(const Vector2 &p_ofs) {
updating_graph = true;
- // Just use the default func for all the properties that need to be handled for drawing rather than adding to the Visual Script Class
- if (script->has_function(default_func)) {
- script->set_function_scroll(default_func, graph->get_scroll_ofs() / EDSCALE);
- script->set_edited(true);
- }
+ script->set_scroll(graph->get_scroll_ofs() / EDSCALE);
+ script->set_edited(true);
updating_graph = false;
}
@@ -4086,10 +3804,7 @@ void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_
if (updating_graph) {
return;
}
-
- StringName func = _get_function_of_node(p_node);
-
- Ref<VisualScriptComment> vsc = script->get_node(func, p_node);
+ Ref<VisualScriptComment> vsc = script->get_node(p_node);
if (vsc.is_null()) {
return;
}
@@ -4100,16 +3815,23 @@ void VisualScriptEditor::_comment_node_resized(const Vector2 &p_new_size, int p_
return;
}
+ Vector2 new_size = p_new_size;
+ if (graph->is_using_snap()) {
+ Vector2 snap = Vector2(graph->get_snap(), graph->get_snap());
+ Vector2 min_size = (gn->get_minimum_size() + (snap * 0.5)).snapped(snap);
+ new_size = new_size.snapped(snap).max(min_size);
+ }
+
updating_graph = true;
graph->set_block_minimum_size_adjust(true); //faster resize
undo_redo->create_action(TTR("Resize Comment"), UndoRedo::MERGE_ENDS);
- undo_redo->add_do_method(vsc.ptr(), "set_size", p_new_size / EDSCALE);
+ undo_redo->add_do_method(vsc.ptr(), "set_size", new_size / EDSCALE);
undo_redo->add_undo_method(vsc.ptr(), "set_size", vsc->get_size());
undo_redo->commit_action();
- gn->set_custom_minimum_size(p_new_size);
+ gn->set_custom_minimum_size(new_size);
gn->set_size(Size2(1, 1));
graph->set_block_minimum_size_adjust(false);
updating_graph = false;
@@ -4127,8 +3849,7 @@ void VisualScriptEditor::_menu_option(int p_what) {
if (gn) {
if (gn->is_selected()) {
int id = String(gn->get_name()).to_int();
- StringName func = _get_function_of_node(id);
- Ref<VisualScriptNode> vsn = script->get_node(func, id);
+ Ref<VisualScriptNode> vsn = script->get_node(id);
if (vsn.is_valid()) {
vsn->set_breakpoint(!vsn->is_breakpoint());
reselect.push_back(gn->get_name());
@@ -4139,8 +3860,8 @@ void VisualScriptEditor::_menu_option(int p_what) {
_update_graph();
- for (List<String>::Element *E = reselect.front(); E; E = E->next()) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(E->get()));
+ for (const String &E : reselect) {
+ GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(E));
gn->set_selected(true);
}
@@ -4148,142 +3869,18 @@ 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: {
- if (!script->has_function(default_func)) {
- break;
- }
-
- clipboard->nodes.clear();
- clipboard->data_connections.clear();
- clipboard->sequence_connections.clear();
-
- Set<String> funcs;
- 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 = String(gn->get_name()).to_int();
- StringName func = _get_function_of_node(id);
- Ref<VisualScriptNode> node = script->get_node(func, 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(func, id);
- funcs.insert(String(func));
- }
- }
- }
- }
-
- if (clipboard->nodes.empty()) {
- break;
- }
-
- for (Set<String>::Element *F = funcs.front(); F; F = F->next()) {
- List<VisualScript::SequenceConnection> sequence_connections;
-
- script->get_sequence_connection_list(F->get(), &sequence_connections);
-
- for (List<VisualScript::SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) {
- if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) {
- clipboard->sequence_connections.insert(E->get());
- }
- }
-
- List<VisualScript::DataConnection> data_connections;
-
- script->get_data_connection_list(F->get(), &data_connections);
-
- for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- if (clipboard->nodes.has(E->get().from_node) && clipboard->nodes.has(E->get().to_node)) {
- clipboard->data_connections.insert(E->get());
- }
- }
- }
- 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 (!script->has_function(default_func)) {
- break;
- }
-
- if (clipboard->nodes.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<StringName> functions;
- script->get_function_list(&functions);
- for (List<StringName>::Element *F = functions.front(); F; F = F->next()) {
- List<int> nodes;
- script->get_node_list(F->get(), &nodes);
- for (List<int>::Element *E = nodes.front(); E; E = E->next()) {
- Vector2 pos = script->get_node_position(F->get(), E->get()).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", default_func, new_id, node, paste_pos);
- undo_redo->add_undo_method(script.ptr(), "remove_node", default_func, 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", default_func, remap[E->get().from_node], E->get().from_output, remap[E->get().to_node]);
- undo_redo->add_undo_method(script.ptr(), "sequence_disconnect", default_func, 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", default_func, 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", default_func, 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: {
- StringName function = "";
+ // Create Function.
Map<int, Ref<VisualScriptNode>> nodes;
Set<int> selections;
for (int i = 0; i < graph->get_child_count(); i++) {
@@ -4291,20 +3888,14 @@ void VisualScriptEditor::_menu_option(int p_what) {
if (gn) {
if (gn->is_selected()) {
int id = String(gn->get_name()).to_int();
- StringName func = _get_function_of_node(id);
- Ref<VisualScriptNode> node = script->get_node(func, id);
+ Ref<VisualScriptNode> node = script->get_node(id);
if (Object::cast_to<VisualScriptFunction>(*node)) {
EditorNode::get_singleton()->show_warning(TTR("Can't create function with a function node."));
return;
}
if (node.is_valid()) {
- if (func != function && function != StringName("")) {
- EditorNode::get_singleton()->show_warning(TTR("Can't create function of nodes from nodes of multiple functions."));
- return;
- }
nodes.insert(id, node);
selections.insert(id);
- function = func;
}
}
}
@@ -4323,7 +3914,7 @@ void VisualScriptEditor::_menu_option(int p_what) {
int start_node = -1;
Set<int> end_nodes;
if (nodes.size() == 1) {
- Ref<VisualScriptNode> nd = script->get_node(function, nodes.front()->key());
+ Ref<VisualScriptNode> nd = script->get_node(nodes.front()->key());
if (nd.is_valid() && nd->has_input_sequence_port()) {
start_node = nodes.front()->key();
} else {
@@ -4332,29 +3923,29 @@ void VisualScriptEditor::_menu_option(int p_what) {
}
} else {
List<VisualScript::SequenceConnection> seqs;
- script->get_sequence_connection_list(function, &seqs);
+ script->get_sequence_connection_list(&seqs);
if (seqs.size() == 0) {
- // in case there are no sequence connections
- // select the top most node cause that's probably how
- // the user wants to connect the nodes
+ // In case there are no sequence connections,
+ // select the top most node cause that's probably how,
+ // 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(function, 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 = script->get_node_position(function, top_nd);
+ top_nd = E.key;
+ top = script->get_node_position(top_nd);
}
- Vector2 pos = script->get_node_position(function, 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;
}
}
}
- Ref<VisualScriptNode> nd = script->get_node(function, top_nd);
+ Ref<VisualScriptNode> nd = script->get_node(top_nd);
if (nd.is_valid() && nd->has_input_sequence_port()) {
start_node = top_nd;
} else {
@@ -4362,34 +3953,34 @@ void VisualScriptEditor::_menu_option(int p_what) {
return;
}
} else {
- // pick the node with input sequence
+ // Pick the node with input sequence.
Set<int> nodes_from;
Set<int> nodes_to;
- for (List<VisualScript::SequenceConnection>::Element *E = seqs.front(); E; E = E->next()) {
- if (nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) {
- seqmove.insert(E->get());
- nodes_from.insert(E->get().from_node);
- } else if (nodes.has(E->get().from_node) && !nodes.has(E->get().to_node)) {
- seqext.insert(E->get());
- } else if (!nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) {
+ for (const VisualScript::SequenceConnection &E : seqs) {
+ if (nodes.has(E.from_node) && nodes.has(E.to_node)) {
+ seqmove.insert(E);
+ nodes_from.insert(E.from_node);
+ } else if (nodes.has(E.from_node) && !nodes.has(E.to_node)) {
+ seqext.insert(E);
+ } else if (!nodes.has(E.from_node) && nodes.has(E.to_node)) {
if (start_node == -1) {
- seqext.insert(E->get());
- start_node = E->get().to_node;
+ seqext.insert(E);
+ start_node = E.to_node;
} else {
EditorNode::get_singleton()->show_warning(TTR("Try to only have one sequence input in selection."));
return;
}
}
- nodes_to.insert(E->get().to_node);
+ nodes_to.insert(E.to_node);
}
- // to use to add return nodes
+ // To use to add return nodes.
_get_ends(start_node, seqs, selections, end_nodes);
if (start_node == -1) {
- // if we still don't have a start node then
- // run through the nodes and select the first tree node
- // ie node without any input sequence but output sequence
+ // If we still don't have a start node then,
+ // run through the nodes and select the first tree node,
+ // i.e. node without any input sequence but output sequence.
for (Set<int>::Element *E = nodes_from.front(); E; E = E->next()) {
if (!nodes_to.has(E->get())) {
start_node = E->get();
@@ -4400,121 +3991,108 @@ void VisualScriptEditor::_menu_option(int p_what) {
}
if (start_node == -1) {
- return; // this should not happen, but just in case something goes wrong
+ return; // This should not happen, but just in case something goes wrong.
}
List<Variant::Type> inputs; // input types
List<Pair<int, int>> input_connections;
{
List<VisualScript::DataConnection> dats;
- script->get_data_connection_list(function, &dats);
- for (List<VisualScript::DataConnection>::Element *E = dats.front(); E; E = E->next()) {
- if (nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) {
- datamove.insert(E->get());
- } else if (!nodes.has(E->get().from_node) && nodes.has(E->get().to_node)) {
- // add all these as inputs for the Function
- Ref<VisualScriptNode> node = script->get_node(function, E->get().to_node);
+ script->get_data_connection_list(&dats);
+ for (const VisualScript::DataConnection &E : dats) {
+ if (nodes.has(E.from_node) && nodes.has(E.to_node)) {
+ datamove.insert(E);
+ } else if (!nodes.has(E.from_node) && nodes.has(E.to_node)) {
+ // Add all these as inputs for the Function.
+ Ref<VisualScriptNode> node = script->get_node(E.to_node);
if (node.is_valid()) {
- dataext.insert(E->get());
- PropertyInfo pi = node->get_input_value_port_info(E->get().to_port);
+ dataext.insert(E);
+ PropertyInfo pi = node->get_input_value_port_info(E.to_port);
inputs.push_back(pi.type);
- input_connections.push_back(Pair<int, int>(E->get().to_node, E->get().to_port));
+ input_connections.push_back(Pair<int, int>(E.to_node, E.to_port));
}
- } else if (nodes.has(E->get().from_node) && !nodes.has(E->get().to_node)) {
- dataext.insert(E->get());
+ } else if (nodes.has(E.from_node) && !nodes.has(E.to_node)) {
+ dataext.insert(E);
}
}
}
-
- String new_fn = _validate_name("new_function");
-
- Vector2 ofs = _get_available_pos(false, script->get_node_position(function, start_node) - Vector2(80, 150));
-
- Ref<VisualScriptFunction> func_node;
- func_node.instance();
- func_node->set_name(new_fn);
-
- undo_redo->create_action(TTR("Create Function"));
-
- undo_redo->add_do_method(script.ptr(), "add_function", new_fn);
int fn_id = script->get_available_id();
- undo_redo->add_do_method(script.ptr(), "add_node", new_fn, fn_id, func_node, ofs);
- undo_redo->add_undo_method(script.ptr(), "remove_function", new_fn);
- 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");
- undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
-
- // Move the nodes
+ {
+ String new_fn = _validate_name("new_function");
- for (Map<int, Ref<VisualScriptNode>>::Element *E = nodes.front(); E; E = E->next()) {
- undo_redo->add_do_method(script.ptr(), "remove_node", function, E->key());
- undo_redo->add_do_method(script.ptr(), "add_node", new_fn, E->key(), E->get(), script->get_node_position(function, E->key()));
+ Vector2 pos = _get_available_pos(false, script->get_node_position(start_node) - Vector2(80, 150));
- // undo_redo->add_undo_method(script.ptr(), "remove_node", new_fn, E->key()); not needed cause we already remove the function :P
- undo_redo->add_undo_method(script.ptr(), "add_node", function, E->key(), E->get(), script->get_node_position(function, E->key()));
- }
+ Ref<VisualScriptFunction> func_node;
+ func_node.instantiate();
+ func_node->set_name(new_fn);
- for (Set<VisualScript::SequenceConnection>::Element *E = seqmove.front(); E; E = E->next()) {
- undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, E->get().from_node, E->get().from_output, E->get().to_node);
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", function, E->get().from_node, E->get().from_output, E->get().to_node);
- }
+ undo_redo->create_action(TTR("Create Function"));
- for (Set<VisualScript::DataConnection>::Element *E = datamove.front(); E; E = E->next()) {
- undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
- undo_redo->add_undo_method(script.ptr(), "data_connect", function, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ undo_redo->add_do_method(script.ptr(), "add_function", new_fn, fn_id);
+ undo_redo->add_do_method(script.ptr(), "add_node", fn_id, func_node, pos);
+ undo_redo->add_undo_method(script.ptr(), "remove_function", new_fn);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", fn_id);
+ 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");
+ undo_redo->add_undo_method(this, "emit_signal", "edited_script_changed");
+ // Might make the system more intelligent by checking port from info.
+ int i = 0;
+ List<Pair<int, int>>::Element *F = input_connections.front();
+ for (List<Variant::Type>::Element *E = inputs.front(); E && F; E = E->next(), F = F->next()) {
+ func_node->add_argument(E->get(), "arg_" + String::num_int64(i), i);
+ undo_redo->add_do_method(script.ptr(), "data_connect", fn_id, i, F->get().first, F->get().second);
+ i++; // increment i
+ }
+ // Ensure Preview Selection is of newly created function node.
+ if (selections.size()) {
+ EditorNode::get_singleton()->push_item(func_node.ptr());
+ }
}
+ // Move the nodes.
- // Add undo for external connections as well so that it's easier to revert back and forth
- // these didn't require do methods as it's already handled internally by other do calls
+ // Handles reconnection of sequence connections on undo, start here in case of issues.
for (Set<VisualScript::SequenceConnection>::Element *E = seqext.front(); E; E = E->next()) {
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", function, E->get().from_node, E->get().from_output, E->get().to_node);
+ undo_redo->add_do_method(script.ptr(), "sequence_disconnect", E->get().from_node, E->get().from_output, E->get().to_node);
+ undo_redo->add_undo_method(script.ptr(), "sequence_connect", E->get().from_node, E->get().from_output, E->get().to_node);
}
for (Set<VisualScript::DataConnection>::Element *E = dataext.front(); E; E = E->next()) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", function, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ undo_redo->add_do_method(script.ptr(), "data_disconnect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ undo_redo->add_undo_method(script.ptr(), "data_connect", E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
}
- // I don't really think we need support for non sequenced functions at this moment
- undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, fn_id, 0, start_node);
+ // I don't really think we need support for non sequenced functions at this moment.
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", fn_id, 0, start_node);
- // end nodes are mapped to the return nodes with data connections if possible
+ // Could fail with the new changes, start here when searching for bugs in create function shortcut.
int m = 1;
for (Set<int>::Element *G = end_nodes.front(); G; G = G->next()) {
Ref<VisualScriptReturn> ret_node;
- ret_node.instance();
+ ret_node.instantiate();
int ret_id = fn_id + (m++);
selections.insert(ret_id);
- Vector2 ofsi = _get_available_pos(false, script->get_node_position(function, G->get()) + Vector2(80, -100));
- undo_redo->add_do_method(script.ptr(), "add_node", new_fn, ret_id, ret_node, ofsi);
- undo_redo->add_undo_method(script.ptr(), "remove_node", new_fn, ret_id);
+ Vector2 posi = _get_available_pos(false, script->get_node_position(G->get()) + Vector2(80, -100));
+ undo_redo->add_do_method(script.ptr(), "add_node", ret_id, ret_node, posi);
+ undo_redo->add_undo_method(script.ptr(), "remove_node", ret_id);
- undo_redo->add_do_method(script.ptr(), "sequence_connect", new_fn, G->get(), 0, ret_id);
- // add data outputs from each of the end_nodes
- Ref<VisualScriptNode> vsn = script->get_node(function, G->get());
+ undo_redo->add_do_method(script.ptr(), "sequence_connect", G->get(), 0, ret_id);
+ // Add data outputs from each of the end_nodes.
+ Ref<VisualScriptNode> vsn = script->get_node(G->get());
if (vsn.is_valid() && vsn->get_output_value_port_count() > 0) {
ret_node->set_enable_return_value(true);
- // use the zeroth data port cause that's the likely one that is planned to be used
+ // Use the zeroth data port cause that's the likely one that is planned to be used.
ret_node->set_return_type(vsn->get_output_value_port_info(0).type);
- undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, G->get(), 0, ret_id, 0);
+ undo_redo->add_do_method(script.ptr(), "data_connect", G->get(), 0, ret_id, 0);
}
}
- // * might make the system more intelligent by checking port from info.
- int i = 0;
- List<Pair<int, int>>::Element *F = input_connections.front();
- for (List<Variant::Type>::Element *E = inputs.front(); E && F; E = E->next(), F = F->next()) {
- func_node->add_argument(E->get(), "arg_" + String::num_int64(i), i);
- undo_redo->add_do_method(script.ptr(), "data_connect", new_fn, fn_id, i, F->get().first, F->get().second);
- i++; // increment i
- }
-
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
- // make sure all Nodes get marked for selection so that they can be moved together
+ // Make sure all Nodes get marked for selection so that they can be moved together.
selections.insert(fn_id);
for (int k = 0; k < graph->get_child_count(); k++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(k));
@@ -4524,11 +4102,6 @@ void VisualScriptEditor::_menu_option(int p_what) {
}
}
- // Ensure Preview Selection is of newly created function node
- if (selections.size()) {
- EditorNode::get_singleton()->push_item(func_node.ptr());
- }
-
} break;
case REFRESH_GRAPH: {
_update_graph();
@@ -4536,16 +4109,16 @@ void VisualScriptEditor::_menu_option(int p_what) {
}
}
-// this is likely going to be very slow and I am not sure if I should keep it
-// but I hope that it will not be a problem considering that we won't be creating functions so frequently
-// and cyclic connections would be a problem but hopefully we won't let them get to this point
+// This is likely going to be very slow and I am not sure if I should keep it,
+// but I hope that it will not be a problem considering that we won't be creating functions so frequently,
+// and cyclic connections would be a problem but hopefully we won't let them get to this point.
void VisualScriptEditor::_get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes) {
- for (const List<VisualScript::SequenceConnection>::Element *E = p_seqs.front(); E; E = E->next()) {
- int from = E->get().from_node;
- int to = E->get().to_node;
+ for (const VisualScript::SequenceConnection &E : p_seqs) {
+ int from = E.from_node;
+ int to = E.to_node;
if (from == p_node && p_selected.has(to)) {
- // this is an interior connection move forward to the to node
+ // This is an interior connection move forward to the to node.
_get_ends(to, p_seqs, p_selected, r_end_nodes);
} else if (from == p_node && !p_selected.has(to)) {
r_end_nodes.insert(from);
@@ -4566,36 +4139,36 @@ void VisualScriptEditor::_member_rmb_selected(const Vector2 &p_pos) {
TreeItem *root = members->get_root();
- Ref<Texture2D> del_icon = Control::get_theme_icon("Remove", "EditorIcons");
+ Ref<Texture2D> del_icon = Control::get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"));
- Ref<Texture2D> edit_icon = Control::get_theme_icon("Edit", "EditorIcons");
+ Ref<Texture2D> edit_icon = Control::get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"));
- if (ti->get_parent() == root->get_children()) {
+ if (ti->get_parent() == root->get_first_child()) {
member_type = MEMBER_FUNCTION;
member_name = ti->get_text(0);
member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT);
member_popup->add_separator();
- member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("visual_script_editor/delete_selected"), MEMBER_REMOVE);
+ member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("ui_graph_delete"), MEMBER_REMOVE);
member_popup->popup();
return;
}
- if (ti->get_parent() == root->get_children()->get_next()) {
+ if (ti->get_parent() == root->get_first_child()->get_next()) {
member_type = MEMBER_VARIABLE;
member_name = ti->get_text(0);
member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT);
member_popup->add_separator();
- member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("visual_script_editor/delete_selected"), MEMBER_REMOVE);
+ member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("ui_graph_delete"), MEMBER_REMOVE);
member_popup->popup();
return;
}
- if (ti->get_parent() == root->get_children()->get_next()->get_next()) {
+ if (ti->get_parent() == root->get_first_child()->get_next()->get_next()) {
member_type = MEMBER_SIGNAL;
member_name = ti->get_text(0);
member_popup->add_icon_shortcut(edit_icon, ED_GET_SHORTCUT("visual_script_editor/edit_member"), MEMBER_EDIT);
member_popup->add_separator();
- member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("visual_script_editor/delete_selected"), MEMBER_REMOVE);
+ member_popup->add_icon_shortcut(del_icon, ED_GET_SHORTCUT("ui_graph_delete"), MEMBER_REMOVE);
member_popup->popup();
return;
}
@@ -4605,34 +4178,29 @@ void VisualScriptEditor::_member_option(int p_option) {
switch (member_type) {
case MEMBER_FUNCTION: {
if (p_option == MEMBER_REMOVE) {
- //delete the function
+ // Delete the function.
String name = member_name;
-
+ List<String> lst;
+ int fn_node = script->get_function_node_id(name);
undo_redo->create_action(TTR("Remove Function"));
undo_redo->add_do_method(script.ptr(), "remove_function", name);
- undo_redo->add_undo_method(script.ptr(), "add_function", name);
- List<int> nodes;
- script->get_node_list(name, &nodes);
- for (List<int>::Element *E = nodes.front(); E; E = E->next()) {
- undo_redo->add_undo_method(script.ptr(), "add_node", name, E->get(), script->get_node(name, E->get()), script->get_node_position(name, E->get()));
- }
-
- List<VisualScript::SequenceConnection> seq_connections;
-
- script->get_sequence_connection_list(name, &seq_connections);
-
- for (List<VisualScript::SequenceConnection>::Element *E = seq_connections.front(); E; E = E->next()) {
- undo_redo->add_undo_method(script.ptr(), "sequence_connect", name, E->get().from_node, E->get().from_output, E->get().to_node);
+ undo_redo->add_do_method(script.ptr(), "remove_node", fn_node);
+ undo_redo->add_undo_method(script.ptr(), "add_function", name, fn_node);
+ undo_redo->add_undo_method(script.ptr(), "add_node", fn_node, script->get_node(fn_node), script->get_node_position(fn_node));
+ List<VisualScript::SequenceConnection> seqcons;
+ script->get_sequence_connection_list(&seqcons);
+ for (const VisualScript::SequenceConnection &E : seqcons) {
+ if (E.from_node == fn_node) {
+ undo_redo->add_undo_method(script.ptr(), "sequence_connect", fn_node, E.from_output, E.to_node);
+ }
}
-
- List<VisualScript::DataConnection> data_connections;
-
- script->get_data_connection_list(name, &data_connections);
-
- for (List<VisualScript::DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
- undo_redo->add_undo_method(script.ptr(), "data_connect", name, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port);
+ List<VisualScript::DataConnection> datcons;
+ script->get_data_connection_list(&datcons);
+ for (const VisualScript::DataConnection &E : datcons) {
+ if (E.from_node == fn_node) {
+ undo_redo->add_undo_method(script.ptr(), "data_connect", fn_node, E.from_port, E.to_node, E.to_port);
+ }
}
-
undo_redo->add_do_method(this, "_update_members");
undo_redo->add_undo_method(this, "_update_members");
undo_redo->add_do_method(this, "_update_graph");
@@ -4692,6 +4260,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));
@@ -4702,18 +4279,14 @@ void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("_create_new_node_from_name", &VisualScriptEditor::_create_new_node_from_name);
- ClassDB::bind_method("get_drag_data_fw", &VisualScriptEditor::get_drag_data_fw);
- ClassDB::bind_method("can_drop_data_fw", &VisualScriptEditor::can_drop_data_fw);
- ClassDB::bind_method("drop_data_fw", &VisualScriptEditor::drop_data_fw);
-
- ClassDB::bind_method("_input", &VisualScriptEditor::_input);
+ ClassDB::bind_method("_get_drag_data_fw", &VisualScriptEditor::get_drag_data_fw);
+ ClassDB::bind_method("_can_drop_data_fw", &VisualScriptEditor::can_drop_data_fw);
+ ClassDB::bind_method("_drop_data_fw", &VisualScriptEditor::drop_data_fw);
ClassDB::bind_method("_update_graph_connections", &VisualScriptEditor::_update_graph_connections);
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() {
@@ -4725,15 +4298,16 @@ VisualScriptEditor::VisualScriptEditor() {
saved_position = Vector2(0, 0);
edit_menu = memnew(MenuButton);
+ edit_menu->set_shortcut_context(this);
edit_menu->set_text(TTR("Edit"));
edit_menu->set_switch_on_hover(true);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/delete_selected"), EDIT_DELETE_NODES);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_graph_delete"), EDIT_DELETE_NODES);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/toggle_breakpoint"), EDIT_TOGGLE_BREAKPOINT);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/find_node_type"), EDIT_FIND_NODE_TYPE);
edit_menu->get_popup()->add_separator();
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/copy_nodes"), EDIT_COPY_NODES);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/cut_nodes"), EDIT_CUT_NODES);
- edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/paste_nodes"), EDIT_PASTE_NODES);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_copy"), EDIT_COPY_NODES);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_cut"), EDIT_CUT_NODES);
+ edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("ui_paste"), EDIT_PASTE_NODES);
edit_menu->get_popup()->add_separator();
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/create_function"), EDIT_CREATE_FUNCTION);
edit_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("visual_script_editor/refresh_nodes"), REFRESH_GRAPH);
@@ -4741,7 +4315,7 @@ VisualScriptEditor::VisualScriptEditor() {
members_section = memnew(VBoxContainer);
// Add but wait until done setting up this.
- ScriptEditor::get_singleton()->get_left_list_split()->call_deferred("add_child", members_section);
+ ScriptEditor::get_singleton()->get_left_list_split()->call_deferred(SNAME("add_child"), members_section);
members_section->set_v_size_flags(SIZE_EXPAND_FILL);
CheckButton *tool_script_check = memnew(CheckButton);
@@ -4773,7 +4347,7 @@ VisualScriptEditor::VisualScriptEditor() {
function_name_box = memnew(LineEdit);
function_name_edit->add_child(function_name_box);
function_name_box->connect("gui_input", callable_mp(this, &VisualScriptEditor::_fn_name_box_input));
- function_name_box->set_expand_to_text_length(true);
+ function_name_box->set_expand_to_text_length_enabled(true);
add_child(function_name_edit);
/// Actual Graph ///
@@ -4781,17 +4355,31 @@ VisualScriptEditor::VisualScriptEditor() {
graph = memnew(GraphEdit);
add_child(graph);
graph->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- graph->set_anchors_and_margins_preset(Control::PRESET_WIDE);
+ graph->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
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("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));
graph->set_drag_forwarding(this);
+ float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity");
+ graph->set_minimap_opacity(graph_minimap_opacity);
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();
@@ -4853,8 +4441,8 @@ VisualScriptEditor::VisualScriptEditor() {
function_create_dialog = memnew(ConfirmationDialog);
function_create_dialog->set_title(TTR("Create Function"));
function_create_dialog->add_child(function_vb);
- function_create_dialog->get_ok()->set_text(TTR("Create"));
- function_create_dialog->get_ok()->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function));
+ function_create_dialog->get_ok_button()->set_text(TTR("Create"));
+ function_create_dialog->get_ok_button()->connect("pressed", callable_mp(this, &VisualScriptEditor::_create_function));
add_child(function_create_dialog);
select_func_text = memnew(Label);
@@ -4865,9 +4453,9 @@ VisualScriptEditor::VisualScriptEditor() {
add_child(select_func_text);
hint_text = memnew(Label);
- hint_text->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -100);
- hint_text->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0);
- hint_text->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0);
+ 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);
graph->add_child(hint_text);
@@ -4897,7 +4485,7 @@ VisualScriptEditor::VisualScriptEditor() {
graph->connect("connection_to_empty", callable_mp(this, &VisualScriptEditor::_graph_connect_to_empty));
edit_signal_dialog = memnew(AcceptDialog);
- edit_signal_dialog->get_ok()->set_text(TTR("Close"));
+ edit_signal_dialog->get_ok_button()->set_text(TTR("Close"));
add_child(edit_signal_dialog);
signal_editor = memnew(VisualScriptEditorSignalEdit);
@@ -4907,7 +4495,7 @@ VisualScriptEditor::VisualScriptEditor() {
edit_signal_edit->edit(signal_editor);
edit_variable_dialog = memnew(AcceptDialog);
- edit_variable_dialog->get_ok()->set_text(TTR("Close"));
+ edit_variable_dialog->get_ok_button()->set_text(TTR("Close"));
add_child(edit_variable_dialog);
variable_editor = memnew(VisualScriptEditorVariableEdit);
@@ -4926,21 +4514,15 @@ VisualScriptEditor::VisualScriptEditor() {
updating_members = false;
set_process_input(true);
- set_process_unhandled_input(true);
default_value_edit = memnew(CustomPropertyEditor);
add_child(default_value_edit);
default_value_edit->connect("variant_changed", callable_mp(this, &VisualScriptEditor::_default_value_changed));
- method_select = memnew(VisualScriptPropertySelector);
- add_child(method_select);
- method_select->connect("selected", callable_mp(this, &VisualScriptEditor::_selected_method));
- error_line = -1;
-
new_connect_node_select = memnew(VisualScriptPropertySelector);
add_child(new_connect_node_select);
new_connect_node_select->connect("selected", callable_mp(this, &VisualScriptEditor::_selected_connect_node));
- new_connect_node_select->get_cancel()->connect("pressed", callable_mp(this, &VisualScriptEditor::_cancel_connect_node));
+ new_connect_node_select->get_cancel_button()->connect("pressed", callable_mp(this, &VisualScriptEditor::_cancel_connect_node));
new_virtual_method_select = memnew(VisualScriptPropertySelector);
add_child(new_virtual_method_select);
@@ -4972,15 +4554,11 @@ void VisualScriptEditor::free_clipboard() {
static void register_editor_callback() {
ScriptEditor::register_create_script_editor_function(create_editor);
- ED_SHORTCUT("visual_script_editor/delete_selected", TTR("Delete Selected"), KEY_DELETE);
- 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/copy_nodes", TTR("Copy Nodes"), KEY_MASK_CMD + KEY_C);
- ED_SHORTCUT("visual_script_editor/cut_nodes", TTR("Cut Nodes"), KEY_MASK_CMD + KEY_X);
- ED_SHORTCUT("visual_script_editor/paste_nodes", TTR("Paste Nodes"), KEY_MASK_CMD + KEY_V);
- 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() {
@@ -4988,44 +4566,47 @@ void VisualScriptEditor::register_editor() {
EditorNode::add_plugin_init_callback(register_editor_callback);
}
-Ref<VisualScriptNode> _VisualScriptEditor::create_node_custom(const String &p_name) {
+void VisualScriptEditor::validate() {
+}
+
+// VisualScriptCustomNodes
+
+Ref<VisualScriptNode> VisualScriptCustomNodes::create_node_custom(const String &p_name) {
Ref<VisualScriptCustomNode> node;
- node.instance();
+ node.instantiate();
node->set_script(singleton->custom_nodes[p_name]);
return node;
}
-_VisualScriptEditor *_VisualScriptEditor::singleton = nullptr;
-Map<String, REF> _VisualScriptEditor::custom_nodes;
+VisualScriptCustomNodes *VisualScriptCustomNodes::singleton = nullptr;
+Map<String, REF> VisualScriptCustomNodes::custom_nodes;
-_VisualScriptEditor::_VisualScriptEditor() {
+VisualScriptCustomNodes::VisualScriptCustomNodes() {
singleton = this;
}
-_VisualScriptEditor::~_VisualScriptEditor() {
+VisualScriptCustomNodes::~VisualScriptCustomNodes() {
custom_nodes.clear();
}
-void _VisualScriptEditor::add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script) {
+void VisualScriptCustomNodes::add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script) {
String node_name = "custom/" + p_category + "/" + p_name;
custom_nodes.insert(node_name, p_script);
- VisualScriptLanguage::singleton->add_register_func(node_name, &_VisualScriptEditor::create_node_custom);
- emit_signal("custom_nodes_updated");
+ VisualScriptLanguage::singleton->add_register_func(node_name, &VisualScriptCustomNodes::create_node_custom);
+ emit_signal(SNAME("custom_nodes_updated"));
}
-void _VisualScriptEditor::remove_custom_node(const String &p_name, const String &p_category) {
+void VisualScriptCustomNodes::remove_custom_node(const String &p_name, const String &p_category) {
String node_name = "custom/" + p_category + "/" + p_name;
custom_nodes.erase(node_name);
VisualScriptLanguage::singleton->remove_register_func(node_name);
- emit_signal("custom_nodes_updated");
+ emit_signal(SNAME("custom_nodes_updated"));
}
-void _VisualScriptEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_custom_node", "name", "category", "script"), &_VisualScriptEditor::add_custom_node);
- ClassDB::bind_method(D_METHOD("remove_custom_node", "name", "category"), &_VisualScriptEditor::remove_custom_node);
+void VisualScriptCustomNodes::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("add_custom_node", "name", "category", "script"), &VisualScriptCustomNodes::add_custom_node);
+ ClassDB::bind_method(D_METHOD("remove_custom_node", "name", "category"), &VisualScriptCustomNodes::remove_custom_node);
ADD_SIGNAL(MethodInfo("custom_nodes_updated"));
}
-void VisualScriptEditor::validate() {
-}
#endif
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/editor/visual_script_editor.h
index 66e435741f..fd1db2bc43 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/editor/visual_script_editor.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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;
@@ -43,13 +43,14 @@ class VisualScriptEditorVariableEdit;
#ifdef TOOLS_ENABLED
+// TODO: Maybe this class should be refactored.
+// See https://github.com/godotengine/godot/issues/51913
class VisualScriptEditor : public ScriptEditorBase {
GDCLASS(VisualScriptEditor, ScriptEditorBase);
enum {
TYPE_SEQUENCE = 1000,
INDEX_BASE_SEQUENCE = 1024
-
};
enum {
@@ -60,11 +61,10 @@ class VisualScriptEditor : public ScriptEditorBase {
EDIT_CUT_NODES,
EDIT_PASTE_NODES,
EDIT_CREATE_FUNCTION,
- REFRESH_GRAPH
+ REFRESH_GRAPH,
};
enum PortAction {
-
CREATE_CALL_SET_GET,
CREATE_ACTION,
};
@@ -72,7 +72,6 @@ class VisualScriptEditor : public ScriptEditorBase {
enum MemberAction {
MEMBER_EDIT,
MEMBER_REMOVE
-
};
enum MemberType {
@@ -94,6 +93,8 @@ class VisualScriptEditor : public ScriptEditorBase {
ConfirmationDialog *function_create_dialog;
GraphEdit *graph;
+ HBoxContainer *status_bar;
+ Button *toggle_scripts_button;
VisualScriptEditorSignalEdit *signal_editor;
@@ -136,9 +137,8 @@ class VisualScriptEditor : public ScriptEditorBase {
Vector<Pair<Variant::Type, String>> args;
};
+ Map<StringName, Color> node_colors;
HashMap<StringName, Ref<StyleBox>> node_styles;
- StringName edited_func;
- StringName default_func;
void _update_graph_connections();
void _update_graph(int p_only_id = -1);
@@ -177,21 +177,24 @@ class VisualScriptEditor : public ScriptEditorBase {
Vector2 mouse_up_position;
- void _port_action_menu(int p_option, const StringName &p_func);
+ void _port_action_menu(int p_option);
void connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id);
+ NodePath drop_path;
+ Node *drop_node = nullptr;
+ Vector2 drop_position;
void _selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting = true);
void connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id);
void _cancel_connect_node();
- int _create_new_node_from_name(const String &p_text, const Vector2 &p_point, const StringName &p_func = StringName());
+ int _create_new_node_from_name(const String &p_text, const Vector2 &p_point);
void _selected_new_virtual_method(const String &p_text, const String &p_category, const bool p_connecting);
int error_line;
void _node_selected(Node *p_node);
- void _center_on_node(const StringName &p_func, int p_id);
+ void _center_on_node(int p_id);
void _node_filter_changed(const String &p_text);
void _change_base_type_callback();
@@ -202,7 +205,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _begin_node_move();
void _end_node_move();
- void _move_node(const StringName &p_func, int p_id, const Vector2 &p_to);
+ void _move_node(int p_id, const Vector2 &p_to);
void _get_ends(int p_node, const List<VisualScript::SequenceConnection> &p_seqs, const Set<int> &p_selected, Set<int> &r_end_nodes);
@@ -212,7 +215,7 @@ class VisualScriptEditor : public ScriptEditorBase {
void _graph_disconnected(const String &p_from, int p_from_slot, const String &p_to, int p_to_slot);
void _graph_connect_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_pos);
- void _node_ports_changed(const String &p_func, int p_id);
+ void _node_ports_changed(int p_id);
void _node_create();
void _update_available_nodes();
@@ -228,15 +231,14 @@ class VisualScriptEditor : public ScriptEditorBase {
void _update_node_size(int p_id);
void _port_name_focus_out(const Node *p_name_box, int p_id, int p_port, bool is_input);
- Vector2 _get_available_pos(bool centered = true, Vector2 ofs = Vector2()) const;
- StringName _get_function_of_node(int p_id) const;
+ Vector2 _get_pos_in_graph(Vector2 p_point) const;
+ Vector2 _get_available_pos(bool p_centered = true, Vector2 p_pos = Vector2()) const;
- void _move_nodes_with_rescan(const StringName &p_func_from, const StringName &p_func_to, int p_id);
- bool node_has_sequence_connections(const StringName &p_func, int p_id);
+ bool node_has_sequence_connections(int p_id);
void _generic_search(String p_base_type = "", Vector2 pos = Vector2(), bool node_centered = false);
- void _input(const Ref<InputEvent> &p_event);
+ virtual void input(const Ref<InputEvent> &p_event) override;
void _graph_gui_input(const Ref<InputEvent> &p_event);
void _members_gui_input(const Ref<InputEvent> &p_event);
void _fn_name_box_input(const Ref<InputEvent> &p_event);
@@ -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();
@@ -273,9 +277,6 @@ class VisualScriptEditor : public ScriptEditorBase {
void _graph_ofs_changed(const Vector2 &p_ofs);
void _comment_node_resized(const Vector2 &p_new_size, int p_node);
- int selecting_method_id;
- void _selected_method(const String &p_method, const String &p_type, const bool p_connecting);
-
void _draw_color_over_button(Object *obj, Color p_color);
void _button_resource_previewed(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud);
@@ -284,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();
@@ -314,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;
@@ -321,39 +326,45 @@ public:
virtual void set_tooltip_request_func(String p_method, Object *p_obj) override;
virtual Control *get_edit_menu() override;
virtual void clear_edit_menu() override;
+ virtual void set_find_replace_bar(FindReplaceBar *p_bar) override { p_bar->hide(); }; // Not needed here.
virtual bool can_lose_focus_on_node_selection() override { return false; }
virtual void validate() override;
+ virtual Control *get_base_editor() const override;
+
static void register_editor();
static void free_clipboard();
+ void update_toggle_scripts_button() override;
+
VisualScriptEditor();
~VisualScriptEditor();
};
// Singleton
-class _VisualScriptEditor : public Object {
- GDCLASS(_VisualScriptEditor, Object);
+class VisualScriptCustomNodes : public Object {
+ GDCLASS(VisualScriptCustomNodes, Object);
friend class VisualScriptLanguage;
protected:
static void _bind_methods();
- static _VisualScriptEditor *singleton;
+ static VisualScriptCustomNodes *singleton;
static Map<String, REF> custom_nodes;
static Ref<VisualScriptNode> create_node_custom(const String &p_name);
public:
- static _VisualScriptEditor *get_singleton() { return singleton; }
+ static VisualScriptCustomNodes *get_singleton() { return singleton; }
void add_custom_node(const String &p_name, const String &p_category, const Ref<Script> &p_script);
void remove_custom_node(const String &p_name, const String &p_category);
- _VisualScriptEditor();
- ~_VisualScriptEditor();
+ VisualScriptCustomNodes();
+ ~VisualScriptCustomNodes();
};
+
#endif
#endif // VISUALSCRIPT_EDITOR_H
diff --git a/modules/visual_script/visual_script_property_selector.cpp b/modules/visual_script/editor/visual_script_property_selector.cpp
index 3c44faab90..02307b712c 100644
--- a/modules/visual_script/visual_script_property_selector.cpp
+++ b/modules/visual_script/editor/visual_script_property_selector.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,14 +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"
@@ -50,15 +51,15 @@ 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: {
- search_options->call("_gui_input", k);
+ case Key::UP:
+ case Key::DOWN:
+ case Key::PAGEUP:
+ case Key::PAGEDOWN: {
+ search_options->gui_input(k);
search_box->accept_event();
TreeItem *root = search_options->get_root();
- if (!root->get_children()) {
+ if (!root->get_first_child()) {
break;
}
@@ -73,6 +74,8 @@ void VisualScriptPropertySelector::_sbox_input(const Ref<InputEvent> &p_ie) {
current->select(0);
} break;
+ default:
+ break;
}
}
}
@@ -92,43 +95,49 @@ void VisualScriptPropertySelector::_update_search() {
base = ClassDB::get_parent_class_nocheck(base);
}
- for (List<StringName>::Element *E = base_list.front(); E; E = E->next()) {
+ for (const StringName &E : base_list) {
List<MethodInfo> methods;
List<PropertyInfo> props;
TreeItem *category = nullptr;
Ref<Texture2D> type_icons[Variant::VARIANT_MAX] = {
- vbc->get_theme_icon("Variant", "EditorIcons"),
- vbc->get_theme_icon("bool", "EditorIcons"),
- vbc->get_theme_icon("int", "EditorIcons"),
- vbc->get_theme_icon("float", "EditorIcons"),
- vbc->get_theme_icon("String", "EditorIcons"),
- vbc->get_theme_icon("Vector2", "EditorIcons"),
- vbc->get_theme_icon("Rect2", "EditorIcons"),
- vbc->get_theme_icon("Vector3", "EditorIcons"),
- vbc->get_theme_icon("Transform2D", "EditorIcons"),
- vbc->get_theme_icon("Plane", "EditorIcons"),
- vbc->get_theme_icon("Quat", "EditorIcons"),
- vbc->get_theme_icon("AABB", "EditorIcons"),
- vbc->get_theme_icon("Basis", "EditorIcons"),
- vbc->get_theme_icon("Transform", "EditorIcons"),
- vbc->get_theme_icon("Color", "EditorIcons"),
- vbc->get_theme_icon("Path", "EditorIcons"),
- vbc->get_theme_icon("RID", "EditorIcons"),
- vbc->get_theme_icon("Object", "EditorIcons"),
- vbc->get_theme_icon("Dictionary", "EditorIcons"),
- vbc->get_theme_icon("Array", "EditorIcons"),
- vbc->get_theme_icon("PackedByteArray", "EditorIcons"),
- vbc->get_theme_icon("PackedInt32Array", "EditorIcons"),
- vbc->get_theme_icon("PackedFloat32Array", "EditorIcons"),
- vbc->get_theme_icon("PackedInt64Array", "EditorIcons"),
- vbc->get_theme_icon("PackedFloat64Array", "EditorIcons"),
- vbc->get_theme_icon("PackedStringArray", "EditorIcons"),
- vbc->get_theme_icon("PackedVector2Array", "EditorIcons"),
- vbc->get_theme_icon("PackedVector3Array", "EditorIcons"),
- vbc->get_theme_icon("PackedColorArray", "EditorIcons")
+ vbc->get_theme_icon(SNAME("Variant"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("bool"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("int"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("float"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("String"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Vector2i"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Rect2"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Rect2i"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Vector3i"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Transform2D"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Plane"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Quaternion"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("AABB"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Basis"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Color"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("StringName"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("NodePath"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("RID"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("MiniObject"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Callable"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Signal"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Dictionary"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("Array"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("PackedByteArray"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("PackedInt32Array"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("PackedInt64Array"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("PackedFloat32Array"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("PackedFloat64Array"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("PackedStringArray"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("PackedVector2Array"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("PackedVector3Array"), SNAME("EditorIcons")),
+ vbc->get_theme_icon(SNAME("PackedColorArray"), SNAME("EditorIcons"))
};
{
- String b = String(E->get());
+ String b = String(E);
category = search_options->create_item(root);
if (category) {
category->set_text(0, b.replace_first("*", ""));
@@ -147,30 +156,30 @@ void VisualScriptPropertySelector::_update_search() {
if (Object::cast_to<Script>(obj)) {
Object::cast_to<Script>(obj)->get_script_property_list(&props);
} else {
- ClassDB::get_property_list(E->get(), &props, true);
+ ClassDB::get_property_list(E, &props, true);
}
}
- for (List<PropertyInfo>::Element *F = props.front(); F; F = F->next()) {
- if (!(F->get().usage & PROPERTY_USAGE_EDITOR) && !(F->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) {
+ for (const PropertyInfo &F : props) {
+ if (!(F.usage & PROPERTY_USAGE_EDITOR) && !(F.usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) {
continue;
}
- if (type_filter.size() && type_filter.find(F->get().type) == -1) {
+ if (type_filter.size() && type_filter.find(F.type) == -1) {
continue;
}
// capitalize() also converts underscore to space, we'll match again both possible styles
- String get_text_raw = String(vformat(TTR("Get %s"), F->get().name));
+ String get_text_raw = String(vformat(TTR("Get %s"), F.name));
String get_text = get_text_raw.capitalize();
- String set_text_raw = String(vformat(TTR("Set %s"), F->get().name));
+ String set_text_raw = String(vformat(TTR("Set %s"), F.name));
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) {
TreeItem *item = search_options->create_item(category ? category : root);
item->set_text(0, get_text);
- item->set_metadata(0, F->get().name);
- item->set_icon(0, type_icons[F->get().type]);
+ item->set_metadata(0, F.name);
+ item->set_icon(0, type_icons[F.type]);
item->set_metadata(1, "get");
item->set_collapsed(true);
item->set_selectable(0, true);
@@ -182,8 +191,8 @@ void VisualScriptPropertySelector::_update_search() {
if (input == String() || 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->get().name);
- item->set_icon(0, type_icons[F->get().type]);
+ item->set_metadata(0, F.name);
+ item->set_icon(0, type_icons[F.type]);
item->set_metadata(1, "set");
item->set_selectable(0, true);
item->set_selectable(1, false);
@@ -196,7 +205,7 @@ void VisualScriptPropertySelector::_update_search() {
if (type != Variant::NIL) {
Variant v;
Callable::CallError ce;
- v = Variant::construct(type, nullptr, 0, ce);
+ Variant::construct(type, v, nullptr, 0, ce);
v.get_method_list(&methods);
} else {
Object *obj = ObjectDB::get_instance(script);
@@ -204,7 +213,7 @@ void VisualScriptPropertySelector::_update_search() {
Object::cast_to<Script>(obj)->get_script_method_list(&methods);
}
- ClassDB::get_method_list(E->get(), &methods, true, true);
+ ClassDB::get_method_list(E, &methods, true, true);
}
}
for (List<MethodInfo>::Element *M = methods.front(); M; M = M->next()) {
@@ -252,7 +261,7 @@ void VisualScriptPropertySelector::_update_search() {
TreeItem *item = search_options->create_item(category ? category : root);
item->set_text(0, desc);
- item->set_icon(0, vbc->get_theme_icon("MemberMethod", "EditorIcons"));
+ item->set_icon(0, vbc->get_theme_icon(SNAME("MemberMethod"), SNAME("EditorIcons")));
item->set_metadata(0, name);
item->set_selectable(0, true);
@@ -264,7 +273,7 @@ void VisualScriptPropertySelector::_update_search() {
item->set_metadata(2, connecting);
}
- if (category && category->get_children() == nullptr) {
+ if (category && category->get_first_child() == nullptr) {
memdelete(category); //old category was unused
}
}
@@ -309,14 +318,14 @@ void VisualScriptPropertySelector::_update_search() {
found = true;
}
- get_ok()->set_disabled(root->get_children() == nullptr);
+ get_ok_button()->set_disabled(root->get_first_child() == nullptr);
}
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) {
TreeItem *item = search_options->create_item(root);
item->set_text(0, text);
- item->set_icon(0, vbc->get_theme_icon("VisualScript", "EditorIcons"));
+ item->set_icon(0, vbc->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons")));
item->set_metadata(0, name);
item->set_metadata(1, "action");
item->set_selectable(0, true);
@@ -333,11 +342,11 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt
List<String> fnodes;
VisualScriptLanguage::singleton->get_registered_node_names(&fnodes);
- for (List<String>::Element *E = fnodes.front(); E; E = E->next()) {
- if (!E->get().begins_with(root_filter)) {
+ for (const String &E : fnodes) {
+ if (!E.begins_with(root_filter)) {
continue;
}
- Vector<String> path = E->get().split("/");
+ Vector<String> path = E.split("/");
// check if the name has the filter
bool in_filter = false;
@@ -348,7 +357,7 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt
} else {
in_filter = false;
}
- if (E->get().findn(tx_filters[i]) != -1) {
+ if (E.findn(tx_filters[i]) != -1) {
in_filter = true;
break;
}
@@ -357,9 +366,9 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt
continue;
}
- bool in_modifier = p_modifiers.empty();
+ bool in_modifier = p_modifiers.is_empty();
for (Set<String>::Element *F = p_modifiers.front(); F && in_modifier; F = F->next()) {
- if (E->get().findn(F->get()) != -1) {
+ if (E.findn(F->get()) != -1) {
in_modifier = true;
}
}
@@ -368,7 +377,7 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt
}
TreeItem *item = search_options->create_item(root);
- Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(E->get());
+ Ref<VisualScriptNode> vnode = VisualScriptLanguage::singleton->create_node_from_name(E);
Ref<VisualScriptOperator> vnode_operator = vnode;
String type_name;
if (vnode_operator.is_valid()) {
@@ -400,9 +409,9 @@ void VisualScriptPropertySelector::get_visual_node_names(const String &root_filt
}
item->set_text(0, type_name + String("").join(desc));
- item->set_icon(0, vbc->get_theme_icon("VisualScript", "EditorIcons"));
+ item->set_icon(0, vbc->get_theme_icon(SNAME("VisualScript"), SNAME("EditorIcons")));
item->set_selectable(0, true);
- item->set_metadata(0, E->get());
+ item->set_metadata(0, E);
item->set_selectable(0, true);
item->set_metadata(1, "visualscript");
item->set_selectable(1, false);
@@ -416,7 +425,7 @@ void VisualScriptPropertySelector::_confirmed() {
if (!ti) {
return;
}
- emit_signal("selected", ti->get_metadata(0), ti->get_metadata(1), ti->get_metadata(2));
+ emit_signal(SNAME("selected"), ti->get_metadata(0), ti->get_metadata(1), ti->get_metadata(2));
set_visible(false);
}
@@ -437,7 +446,7 @@ void VisualScriptPropertySelector::_item_selected() {
class_type = base_type;
}
- DocData *dd = EditorHelp::get_doc_data();
+ DocTools *dd = EditorHelp::get_doc_data();
String text;
String at_class = class_type;
@@ -468,10 +477,11 @@ void VisualScriptPropertySelector::_item_selected() {
at_class = ClassDB::get_parent_class_nocheck(at_class);
}
- Map<String, DocData::ClassDoc>::Element *T = dd->class_list.find(class_type);
+ Vector<String> functions = name.rsplit("/", false);
+ at_class = functions.size() > 3 ? functions[functions.size() - 2] : class_type;
+ Map<String, DocData::ClassDoc>::Element *T = dd->class_list.find(at_class);
if (T) {
for (int i = 0; i < T->get().methods.size(); i++) {
- Vector<String> functions = name.rsplit("/", false, 1);
if (T->get().methods[i].name == functions[functions.size() - 1]) {
text = DTR(T->get().methods[i].description);
}
@@ -704,8 +714,8 @@ VisualScriptPropertySelector::VisualScriptPropertySelector() {
search_box->connect("gui_input", callable_mp(this, &VisualScriptPropertySelector::_sbox_input));
search_options = memnew(Tree);
vbc->add_margin_child(TTR("Matches:"), search_options, true);
- get_ok()->set_text(TTR("Open"));
- get_ok()->set_disabled(true);
+ get_ok_button()->set_text(TTR("Open"));
+ get_ok_button()->set_disabled(true);
register_text_enter(search_box);
set_hide_on_ok(false);
search_options->connect("item_activated", callable_mp(this, &VisualScriptPropertySelector::_confirmed));
diff --git a/modules/visual_script/visual_script_property_selector.h b/modules/visual_script/editor/visual_script_property_selector.h
index cc49b2863d..7a87f3d3ee 100644
--- a/modules/visual_script/visual_script_property_selector.h
+++ b/modules/visual_script/editor/visual_script_property_selector.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/visual_script/icons/VisualScript.svg b/modules/visual_script/icons/VisualScript.svg
index f6475d590e..2352ba5d87 100644
--- a/modules/visual_script/icons/VisualScript.svg
+++ b/modules/visual_script/icons/VisualScript.svg
@@ -1,6 +1 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<ellipse cx="3" cy="1039.4" r="2" fill="#6e6e6e"/>
-<path transform="translate(0 1036.4)" d="m7 1l-0.56445 2.2578a5 5 0 0 0 -0.68945 0.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -0.28516 0.68555l-2.2539 0.5625v2h5.2715a2 2 0 0 1 -0.27148 -1 2 2 0 0 1 2 -2 2 2 0 0 1 2 2 2 2 0 0 1 -0.26953 1h5.2695v-2l-2.2578-0.56445a5 5 0 0 0 -0.2793 -0.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -0.68555 -0.28516l-0.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3 -3v-3h-2v3a1 1 0 0 1 -1 1v-4h-2zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324 -1 2 2 0 0 0 0 -2 2 2 0 0 0 -1.7324 -1h2v-2h-2z" fill="#e0e0e0"/>
-</g>
-</svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><ellipse cx="3" cy="1039.4" fill="#6e6e6e"/><path d="m7 1-.56445 2.2578a5 5 0 0 0 -.68945.2793l-1.9883-1.1934-1.4141 1.4141 1.1953 1.9941a5 5 0 0 0 -.28516.68555l-2.2539.5625v2h5.2715a2 2 0 0 1 -.27148-1 2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1 -.26953 1h5.2695v-2l-2.2578-.56445a5 5 0 0 0 -.2793-.6875l1.1934-1.9902-1.4141-1.4141-1.9941 1.1953a5 5 0 0 0 -.68555-.28516l-.5625-2.2539h-2zm-4 9v6h2a3 3 0 0 0 3-3v-3h-2v3a1 1 0 0 1 -1 1v-4zm8 0a2 2 0 0 0 -1.7324 1 2 2 0 0 0 0 2 2 2 0 0 0 1.7324 1h-2v2h2a2 2 0 0 0 1.7324-1 2 2 0 0 0 0-2 2 2 0 0 0 -1.7324-1h2v-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/></g></svg>
diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp
index 8afed1229f..6f56fbbc70 100644
--- a/modules/visual_script/register_types.cpp
+++ b/modules/visual_script/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,11 +30,10 @@
#include "register_types.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#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,8 +41,10 @@
#include "visual_script_yield_nodes.h"
VisualScriptLanguage *visual_script_language = nullptr;
+
#ifdef TOOLS_ENABLED
-static _VisualScriptEditor *vs_editor_singleton = nullptr;
+#include "editor/visual_script_editor.h"
+static VisualScriptCustomNodes *vs_custom_nodes_singleton = nullptr;
#endif
void register_visual_script_types() {
@@ -51,59 +52,59 @@ void register_visual_script_types() {
//script_language_gd->init();
ScriptServer::register_language(visual_script_language);
- ClassDB::register_class<VisualScript>();
- ClassDB::register_virtual_class<VisualScriptNode>();
- ClassDB::register_class<VisualScriptFunctionState>();
- ClassDB::register_class<VisualScriptFunction>();
- ClassDB::register_virtual_class<VisualScriptLists>();
- ClassDB::register_class<VisualScriptComposeArray>();
- ClassDB::register_class<VisualScriptOperator>();
- ClassDB::register_class<VisualScriptVariableSet>();
- ClassDB::register_class<VisualScriptVariableGet>();
- ClassDB::register_class<VisualScriptConstant>();
- ClassDB::register_class<VisualScriptIndexGet>();
- ClassDB::register_class<VisualScriptIndexSet>();
- ClassDB::register_class<VisualScriptGlobalConstant>();
- ClassDB::register_class<VisualScriptClassConstant>();
- ClassDB::register_class<VisualScriptMathConstant>();
- ClassDB::register_class<VisualScriptBasicTypeConstant>();
- ClassDB::register_class<VisualScriptEngineSingleton>();
- ClassDB::register_class<VisualScriptSceneNode>();
- ClassDB::register_class<VisualScriptSceneTree>();
- ClassDB::register_class<VisualScriptResourcePath>();
- ClassDB::register_class<VisualScriptSelf>();
- ClassDB::register_class<VisualScriptCustomNode>();
- ClassDB::register_class<VisualScriptSubCall>();
- ClassDB::register_class<VisualScriptComment>();
- ClassDB::register_class<VisualScriptConstructor>();
- ClassDB::register_class<VisualScriptLocalVar>();
- ClassDB::register_class<VisualScriptLocalVarSet>();
- ClassDB::register_class<VisualScriptInputAction>();
- ClassDB::register_class<VisualScriptDeconstruct>();
- ClassDB::register_class<VisualScriptPreload>();
- ClassDB::register_class<VisualScriptTypeCast>();
-
- ClassDB::register_class<VisualScriptFunctionCall>();
- ClassDB::register_class<VisualScriptPropertySet>();
- ClassDB::register_class<VisualScriptPropertyGet>();
+ GDREGISTER_CLASS(VisualScript);
+ GDREGISTER_VIRTUAL_CLASS(VisualScriptNode);
+ GDREGISTER_CLASS(VisualScriptFunctionState);
+ GDREGISTER_CLASS(VisualScriptFunction);
+ GDREGISTER_VIRTUAL_CLASS(VisualScriptLists);
+ GDREGISTER_CLASS(VisualScriptComposeArray);
+ GDREGISTER_CLASS(VisualScriptOperator);
+ GDREGISTER_CLASS(VisualScriptVariableSet);
+ GDREGISTER_CLASS(VisualScriptVariableGet);
+ GDREGISTER_CLASS(VisualScriptConstant);
+ GDREGISTER_CLASS(VisualScriptIndexGet);
+ GDREGISTER_CLASS(VisualScriptIndexSet);
+ GDREGISTER_CLASS(VisualScriptGlobalConstant);
+ GDREGISTER_CLASS(VisualScriptClassConstant);
+ GDREGISTER_CLASS(VisualScriptMathConstant);
+ GDREGISTER_CLASS(VisualScriptBasicTypeConstant);
+ GDREGISTER_CLASS(VisualScriptEngineSingleton);
+ GDREGISTER_CLASS(VisualScriptSceneNode);
+ GDREGISTER_CLASS(VisualScriptSceneTree);
+ GDREGISTER_CLASS(VisualScriptResourcePath);
+ GDREGISTER_CLASS(VisualScriptSelf);
+ GDREGISTER_CLASS(VisualScriptCustomNode);
+ GDREGISTER_CLASS(VisualScriptSubCall);
+ GDREGISTER_CLASS(VisualScriptComment);
+ GDREGISTER_CLASS(VisualScriptConstructor);
+ GDREGISTER_CLASS(VisualScriptLocalVar);
+ GDREGISTER_CLASS(VisualScriptLocalVarSet);
+ GDREGISTER_CLASS(VisualScriptInputAction);
+ GDREGISTER_CLASS(VisualScriptDeconstruct);
+ GDREGISTER_CLASS(VisualScriptPreload);
+ GDREGISTER_CLASS(VisualScriptTypeCast);
+
+ GDREGISTER_CLASS(VisualScriptFunctionCall);
+ GDREGISTER_CLASS(VisualScriptPropertySet);
+ GDREGISTER_CLASS(VisualScriptPropertyGet);
//ClassDB::register_type<VisualScriptScriptCall>();
- ClassDB::register_class<VisualScriptEmitSignal>();
+ GDREGISTER_CLASS(VisualScriptEmitSignal);
- ClassDB::register_class<VisualScriptReturn>();
- ClassDB::register_class<VisualScriptCondition>();
- ClassDB::register_class<VisualScriptWhile>();
- ClassDB::register_class<VisualScriptIterator>();
- ClassDB::register_class<VisualScriptSequence>();
- //ClassDB::register_class<VisualScriptInputFilter>();
- ClassDB::register_class<VisualScriptSwitch>();
- ClassDB::register_class<VisualScriptSelect>();
+ GDREGISTER_CLASS(VisualScriptReturn);
+ GDREGISTER_CLASS(VisualScriptCondition);
+ GDREGISTER_CLASS(VisualScriptWhile);
+ GDREGISTER_CLASS(VisualScriptIterator);
+ GDREGISTER_CLASS(VisualScriptSequence);
+ //GDREGISTER_CLASS(VisualScriptInputFilter);
+ GDREGISTER_CLASS(VisualScriptSwitch);
+ GDREGISTER_CLASS(VisualScriptSelect);
- ClassDB::register_class<VisualScriptYield>();
- ClassDB::register_class<VisualScriptYieldSignal>();
+ GDREGISTER_CLASS(VisualScriptYield);
+ GDREGISTER_CLASS(VisualScriptYieldSignal);
- ClassDB::register_class<VisualScriptBuiltinFunc>();
+ GDREGISTER_CLASS(VisualScriptBuiltinFunc);
- ClassDB::register_class<VisualScriptExpression>();
+ GDREGISTER_CLASS(VisualScriptExpression);
register_visual_script_nodes();
register_visual_script_func_nodes();
@@ -114,10 +115,10 @@ void register_visual_script_types() {
#ifdef TOOLS_ENABLED
ClassDB::set_current_api(ClassDB::API_EDITOR);
- ClassDB::register_class<_VisualScriptEditor>();
+ GDREGISTER_CLASS(VisualScriptCustomNodes);
ClassDB::set_current_api(ClassDB::API_CORE);
- vs_editor_singleton = memnew(_VisualScriptEditor);
- Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptEditor", _VisualScriptEditor::get_singleton()));
+ vs_custom_nodes_singleton = memnew(VisualScriptCustomNodes);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptCustomNodes", VisualScriptCustomNodes::get_singleton()));
VisualScriptEditor::register_editor();
#endif
@@ -130,8 +131,8 @@ void unregister_visual_script_types() {
#ifdef TOOLS_ENABLED
VisualScriptEditor::free_clipboard();
- if (vs_editor_singleton) {
- memdelete(vs_editor_singleton);
+ if (vs_custom_nodes_singleton) {
+ memdelete(vs_custom_nodes_singleton);
}
#endif
if (visual_script_language) {
diff --git a/modules/visual_script/register_types.h b/modules/visual_script/register_types.h
index c18c2930b1..b02a93ebc1 100644
--- a/modules/visual_script/register_types.h
+++ b/modules/visual_script/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index 1af4b46e22..700cc85672 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,13 +30,13 @@
#include "visual_script.h"
+#include "core/config/project_settings.h"
#include "core/core_string_names.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "scene/main/node.h"
#include "visual_script_nodes.h"
-//used by editor, this is not really saved
+// Used by editor, this is not really saved.
void VisualScriptNode::set_breakpoint(bool p_breakpoint) {
breakpoint = p_breakpoint;
}
@@ -46,7 +46,7 @@ bool VisualScriptNode::is_breakpoint() const {
}
void VisualScriptNode::ports_changed_notify() {
- emit_signal("ports_changed");
+ emit_signal(SNAME("ports_changed"));
}
void VisualScriptNode::set_default_input_value(int p_port, const Variant &p_value) {
@@ -55,8 +55,8 @@ void VisualScriptNode::set_default_input_value(int p_port, const Variant &p_valu
default_input_values[p_port] = p_value;
#ifdef TOOLS_ENABLED
- for (Set<VisualScript *>::Element *E = scripts_used.front(); E; E = E->next()) {
- E->get()->set_edited(true);
+ if (script_used.is_valid()) {
+ script_used->set_edited(true);
}
#endif
}
@@ -73,28 +73,28 @@ void VisualScriptNode::_set_default_input_values(Array p_values) {
void VisualScriptNode::validate_input_default_values() {
default_input_values.resize(MAX(default_input_values.size(), get_input_value_port_count())); //let it grow as big as possible, we don't want to lose values on resize
- //actually validate on save
+ // Actually validate on save.
for (int i = 0; i < get_input_value_port_count(); i++) {
Variant::Type expected = get_input_value_port_info(i).type;
if (expected == Variant::NIL || expected == default_input_values[i].get_type()) {
continue;
} else {
- //not the same, reconvert
+ // Not the same, reconvert.
Callable::CallError ce;
Variant existing = default_input_values[i];
const Variant *existingp = &existing;
- default_input_values[i] = Variant::construct(expected, &existingp, 1, ce, false);
+ Variant::construct(expected, default_input_values[i], &existingp, 1, ce);
if (ce.error != Callable::CallError::CALL_OK) {
//could not convert? force..
- default_input_values[i] = Variant::construct(expected, nullptr, 0, ce, false);
+ Variant::construct(expected, default_input_values[i], nullptr, 0, ce);
}
}
}
}
Array VisualScriptNode::_get_default_input_values() const {
- //validate on save, since on load there is little info about this
+ // Validate on save, since on load there is little info about this.
Array values = default_input_values;
values.resize(get_input_value_port_count());
@@ -113,11 +113,13 @@ 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"));
}
VisualScriptNode::TypeGuess VisualScriptNode::guess_output_type(TypeGuess *p_inputs, int p_output) const {
+ ERR_FAIL_COND_V(get_output_value_port_count() <= p_output, TypeGuess());
+
PropertyInfo pinfo = get_output_value_port_info(p_output);
TypeGuess tg;
@@ -131,15 +133,10 @@ VisualScriptNode::TypeGuess VisualScriptNode::guess_output_type(TypeGuess *p_inp
}
Ref<VisualScript> VisualScriptNode::get_visual_script() const {
- if (scripts_used.size()) {
- return Ref<VisualScript>(scripts_used.front()->get());
- }
-
- return Ref<VisualScript>();
+ return script_used;
}
VisualScriptNode::VisualScriptNode() {
- breakpoint = false;
}
////////////////
@@ -147,8 +144,6 @@ VisualScriptNode::VisualScriptNode() {
/////////////////////
VisualScriptNodeInstance::VisualScriptNodeInstance() {
- sequence_outputs = nullptr;
- input_ports = nullptr;
}
VisualScriptNodeInstance::~VisualScriptNodeInstance() {
@@ -165,13 +160,15 @@ VisualScriptNodeInstance::~VisualScriptNodeInstance() {
}
}
-void VisualScript::add_function(const StringName &p_name) {
+void VisualScript::add_function(const StringName &p_name, int p_func_node_id) {
ERR_FAIL_COND(instances.size());
ERR_FAIL_COND(!String(p_name).is_valid_identifier());
ERR_FAIL_COND(functions.has(p_name));
+ ERR_FAIL_COND(variables.has(p_name));
+ ERR_FAIL_COND(custom_signals.has(p_name));
functions[p_name] = Function();
- functions[p_name].scroll = Vector2(-50, -100);
+ functions[p_name].func_id = p_func_node_id;
}
bool VisualScript::has_function(const StringName &p_name) const {
@@ -182,11 +179,7 @@ void VisualScript::remove_function(const StringName &p_name) {
ERR_FAIL_COND(instances.size());
ERR_FAIL_COND(!functions.has(p_name));
- for (Map<int, Function::NodeData>::Element *E = functions[p_name].nodes.front(); E; E = E->next()) {
- E->get().node->disconnect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed));
- E->get().node->scripts_used.erase(this);
- }
-
+ // Let the editor handle the node removal.
functions.erase(p_name);
}
@@ -207,53 +200,36 @@ void VisualScript::rename_function(const StringName &p_name, const StringName &p
functions.erase(p_name);
}
-void VisualScript::set_function_scroll(const StringName &p_name, const Vector2 &p_scroll) {
- ERR_FAIL_COND(!functions.has(p_name));
- functions[p_name].scroll = p_scroll;
+void VisualScript::set_scroll(const Vector2 &p_scroll) {
+ scroll = p_scroll;
}
-Vector2 VisualScript::get_function_scroll(const StringName &p_name) const {
- ERR_FAIL_COND_V(!functions.has(p_name), Vector2());
- return functions[p_name].scroll;
+Vector2 VisualScript::get_scroll() const {
+ return scroll;
}
void VisualScript::get_function_list(List<StringName> *r_functions) const {
- for (const Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) {
- r_functions->push_back(E->key());
- }
-
- r_functions->sort_custom<StringName::AlphCompare>();
+ functions.get_key_list(r_functions);
+ // r_functions->sort_custom<StringName::AlphCompare>(); // Don't force sorting.
}
int VisualScript::get_function_node_id(const StringName &p_name) const {
ERR_FAIL_COND_V(!functions.has(p_name), -1);
- return functions[p_name].function_id;
+ return functions[p_name].func_id;
}
void VisualScript::_node_ports_changed(int p_id) {
- StringName function;
-
- for (Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) {
- if (E->get().nodes.has(p_id)) {
- function = E->key();
- break;
- }
- }
-
- ERR_FAIL_COND(function == StringName());
-
- Function &func = functions[function];
- Ref<VisualScriptNode> vsn = func.nodes[p_id].node;
+ Ref<VisualScriptNode> vsn = nodes[p_id].node;
vsn->validate_input_default_values();
- //must revalidate all the functions
+ // Must revalidate all the functions.
{
List<SequenceConnection> to_remove;
- for (Set<SequenceConnection>::Element *E = func.sequence_connections.front(); E; E = E->next()) {
+ for (Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) {
if (E->get().from_node == p_id && E->get().from_output >= vsn->get_output_sequence_port_count()) {
to_remove.push_back(E->get());
}
@@ -263,7 +239,7 @@ void VisualScript::_node_ports_changed(int p_id) {
}
while (to_remove.size()) {
- func.sequence_connections.erase(to_remove.front()->get());
+ sequence_connections.erase(to_remove.front()->get());
to_remove.pop_front();
}
}
@@ -271,7 +247,7 @@ void VisualScript::_node_ports_changed(int p_id) {
{
List<DataConnection> to_remove;
- for (Set<DataConnection>::Element *E = func.data_connections.front(); E; E = E->next()) {
+ for (Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
if (E->get().from_node == p_id && E->get().from_port >= vsn->get_output_value_port_count()) {
to_remove.push_back(E->get());
}
@@ -281,63 +257,48 @@ void VisualScript::_node_ports_changed(int p_id) {
}
while (to_remove.size()) {
- func.data_connections.erase(to_remove.front()->get());
+ data_connections.erase(to_remove.front()->get());
to_remove.pop_front();
}
}
#ifdef TOOLS_ENABLED
- set_edited(true); //something changed, let's set as edited
- emit_signal("node_ports_changed", function, p_id);
+ set_edited(true); // Something changed, let's set as edited.
+ emit_signal(SNAME("node_ports_changed"), p_id);
#endif
}
-void VisualScript::add_node(const StringName &p_func, int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos) {
+void VisualScript::add_node(int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos) {
ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!functions.has(p_func));
-
- for (Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) {
- ERR_FAIL_COND(E->get().nodes.has(p_id)); //id can exist only one in script, even for different functions
- }
-
- Function &func = functions[p_func];
+ ERR_FAIL_COND(nodes.has(p_id)); // ID can exist only one in script.
+ ERR_FAIL_COND(p_node.is_null());
- if (Object::cast_to<VisualScriptFunction>(*p_node)) {
- //the function indeed
- ERR_FAIL_COND_MSG(func.function_id >= 0, "A function node has already been set here.");
-
- func.function_id = p_id;
- }
-
- Function::NodeData nd;
+ NodeData nd;
nd.node = p_node;
nd.pos = p_pos;
Ref<VisualScriptNode> vsn = p_node;
vsn->connect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed), varray(p_id));
- vsn->scripts_used.insert(this);
- vsn->validate_input_default_values(); // Validate when fully loaded
+ vsn->script_used = Ref<VisualScript>(this);
+ vsn->validate_input_default_values(); // Validate when fully loaded.
- func.nodes[p_id] = nd;
+ nodes[p_id] = nd;
}
-void VisualScript::remove_node(const StringName &p_func, int p_id) {
+void VisualScript::remove_node(int p_id) {
ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!functions.has(p_func));
- Function &func = functions[p_func];
-
- ERR_FAIL_COND(!func.nodes.has(p_id));
+ ERR_FAIL_COND(!nodes.has(p_id));
{
List<SequenceConnection> to_remove;
- for (Set<SequenceConnection>::Element *E = func.sequence_connections.front(); E; E = E->next()) {
+ for (Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) {
if (E->get().from_node == p_id || E->get().to_node == p_id) {
to_remove.push_back(E->get());
}
}
while (to_remove.size()) {
- func.sequence_connections.erase(to_remove.front()->get());
+ sequence_connections.erase(to_remove.front()->get());
to_remove.pop_front();
}
}
@@ -345,122 +306,88 @@ void VisualScript::remove_node(const StringName &p_func, int p_id) {
{
List<DataConnection> to_remove;
- for (Set<DataConnection>::Element *E = func.data_connections.front(); E; E = E->next()) {
+ for (Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
if (E->get().from_node == p_id || E->get().to_node == p_id) {
to_remove.push_back(E->get());
}
}
while (to_remove.size()) {
- func.data_connections.erase(to_remove.front()->get());
+ data_connections.erase(to_remove.front()->get());
to_remove.pop_front();
}
}
- if (Object::cast_to<VisualScriptFunction>(func.nodes[p_id].node.ptr())) {
- func.function_id = -1; //revert to invalid
- }
-
- func.nodes[p_id].node->disconnect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed));
- func.nodes[p_id].node->scripts_used.erase(this);
+ nodes[p_id].node->disconnect("ports_changed", callable_mp(this, &VisualScript::_node_ports_changed));
+ nodes[p_id].node->script_used.unref();
- func.nodes.erase(p_id);
+ nodes.erase(p_id);
}
-bool VisualScript::has_node(const StringName &p_func, int p_id) const {
- ERR_FAIL_COND_V(!functions.has(p_func), false);
- const Function &func = functions[p_func];
-
- return func.nodes.has(p_id);
+bool VisualScript::has_node(int p_id) const {
+ return nodes.has(p_id);
}
-Ref<VisualScriptNode> VisualScript::get_node(const StringName &p_func, int p_id) const {
- ERR_FAIL_COND_V(!functions.has(p_func), Ref<VisualScriptNode>());
- const Function &func = functions[p_func];
-
- ERR_FAIL_COND_V(!func.nodes.has(p_id), Ref<VisualScriptNode>());
+Ref<VisualScriptNode> VisualScript::get_node(int p_id) const {
+ ERR_FAIL_COND_V(!nodes.has(p_id), Ref<VisualScriptNode>());
- return func.nodes[p_id].node;
+ return nodes[p_id].node;
}
-void VisualScript::set_node_position(const StringName &p_func, int p_id, const Point2 &p_pos) {
+void VisualScript::set_node_position(int p_id, const Point2 &p_pos) {
ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!functions.has(p_func));
- Function &func = functions[p_func];
-
- ERR_FAIL_COND(!func.nodes.has(p_id));
- func.nodes[p_id].pos = p_pos;
+ ERR_FAIL_COND(!nodes.has(p_id));
+ nodes[p_id].pos = p_pos;
}
-Point2 VisualScript::get_node_position(const StringName &p_func, int p_id) const {
- ERR_FAIL_COND_V(!functions.has(p_func), Point2());
- const Function &func = functions[p_func];
-
- ERR_FAIL_COND_V(!func.nodes.has(p_id), Point2());
- return func.nodes[p_id].pos;
+Point2 VisualScript::get_node_position(int p_id) const {
+ ERR_FAIL_COND_V(!nodes.has(p_id), Point2());
+ return nodes[p_id].pos;
}
-void VisualScript::get_node_list(const StringName &p_func, List<int> *r_nodes) const {
- ERR_FAIL_COND(!functions.has(p_func));
- const Function &func = functions[p_func];
-
- for (const Map<int, Function::NodeData>::Element *E = func.nodes.front(); E; E = E->next()) {
- r_nodes->push_back(E->key());
- }
+void VisualScript::get_node_list(List<int> *r_nodes) const {
+ nodes.get_key_list(r_nodes);
}
-void VisualScript::sequence_connect(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node) {
+void VisualScript::sequence_connect(int p_from_node, int p_from_output, int p_to_node) {
ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!functions.has(p_func));
- Function &func = functions[p_func];
SequenceConnection sc;
sc.from_node = p_from_node;
sc.from_output = p_from_output;
sc.to_node = p_to_node;
- ERR_FAIL_COND(func.sequence_connections.has(sc));
+ ERR_FAIL_COND(sequence_connections.has(sc));
- func.sequence_connections.insert(sc);
+ sequence_connections.insert(sc);
}
-void VisualScript::sequence_disconnect(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node) {
- ERR_FAIL_COND(!functions.has(p_func));
- Function &func = functions[p_func];
-
+void VisualScript::sequence_disconnect(int p_from_node, int p_from_output, int p_to_node) {
SequenceConnection sc;
sc.from_node = p_from_node;
sc.from_output = p_from_output;
sc.to_node = p_to_node;
- ERR_FAIL_COND(!func.sequence_connections.has(sc));
+ ERR_FAIL_COND(!sequence_connections.has(sc));
- func.sequence_connections.erase(sc);
+ sequence_connections.erase(sc);
}
-bool VisualScript::has_sequence_connection(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node) const {
- ERR_FAIL_COND_V(!functions.has(p_func), false);
- const Function &func = functions[p_func];
-
+bool VisualScript::has_sequence_connection(int p_from_node, int p_from_output, int p_to_node) const {
SequenceConnection sc;
sc.from_node = p_from_node;
sc.from_output = p_from_output;
sc.to_node = p_to_node;
- return func.sequence_connections.has(sc);
+ return sequence_connections.has(sc);
}
-void VisualScript::get_sequence_connection_list(const StringName &p_func, List<SequenceConnection> *r_connection) const {
- ERR_FAIL_COND(!functions.has(p_func));
- const Function &func = functions[p_func];
-
- for (const Set<SequenceConnection>::Element *E = func.sequence_connections.front(); E; E = E->next()) {
+void VisualScript::get_sequence_connection_list(List<SequenceConnection> *r_connection) const {
+ for (const Set<SequenceConnection>::Element *E = sequence_connections.front(); E; E = E->next()) {
r_connection->push_back(E->get());
}
}
-void VisualScript::data_connect(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
+void VisualScript::data_connect(int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
ERR_FAIL_COND(instances.size());
- ERR_FAIL_COND(!functions.has(p_func));
- Function &func = functions[p_func];
DataConnection dc;
dc.from_node = p_from_node;
@@ -468,72 +395,55 @@ void VisualScript::data_connect(const StringName &p_func, int p_from_node, int p
dc.to_node = p_to_node;
dc.to_port = p_to_port;
- ERR_FAIL_COND(func.data_connections.has(dc));
+ ERR_FAIL_COND(data_connections.has(dc));
- func.data_connections.insert(dc);
+ data_connections.insert(dc);
}
-void VisualScript::data_disconnect(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
- ERR_FAIL_COND(!functions.has(p_func));
- Function &func = functions[p_func];
-
+void VisualScript::data_disconnect(int p_from_node, int p_from_port, int p_to_node, int p_to_port) {
DataConnection dc;
dc.from_node = p_from_node;
dc.from_port = p_from_port;
dc.to_node = p_to_node;
dc.to_port = p_to_port;
- ERR_FAIL_COND(!func.data_connections.has(dc));
+ ERR_FAIL_COND(!data_connections.has(dc));
- func.data_connections.erase(dc);
+ data_connections.erase(dc);
}
-bool VisualScript::has_data_connection(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const {
- ERR_FAIL_COND_V(!functions.has(p_func), false);
- const Function &func = functions[p_func];
-
+bool VisualScript::has_data_connection(int p_from_node, int p_from_port, int p_to_node, int p_to_port) const {
DataConnection dc;
dc.from_node = p_from_node;
dc.from_port = p_from_port;
dc.to_node = p_to_node;
dc.to_port = p_to_port;
- return func.data_connections.has(dc);
+ return data_connections.has(dc);
}
-bool VisualScript::is_input_value_port_connected(const StringName &p_func, int p_node, int p_port) const {
- ERR_FAIL_COND_V(!functions.has(p_func), false);
- const Function &func = functions[p_func];
-
- for (const Set<DataConnection>::Element *E = func.data_connections.front(); E; E = E->next()) {
+bool VisualScript::is_input_value_port_connected(int p_node, int p_port) const {
+ for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
if (E->get().to_node == p_node && E->get().to_port == p_port) {
return true;
}
}
-
return false;
}
-bool VisualScript::get_input_value_port_connection_source(const StringName &p_func, int p_node, int p_port, int *r_node, int *r_port) const {
- ERR_FAIL_COND_V(!functions.has(p_func), false);
- const Function &func = functions[p_func];
-
- for (const Set<DataConnection>::Element *E = func.data_connections.front(); E; E = E->next()) {
+bool VisualScript::get_input_value_port_connection_source(int p_node, int p_port, int *r_node, int *r_port) const {
+ for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
if (E->get().to_node == p_node && E->get().to_port == p_port) {
*r_node = E->get().from_node;
*r_port = E->get().from_port;
return true;
}
}
-
return false;
}
-void VisualScript::get_data_connection_list(const StringName &p_func, List<DataConnection> *r_connection) const {
- ERR_FAIL_COND(!functions.has(p_func));
- const Function &func = functions[p_func];
-
- for (const Set<DataConnection>::Element *E = func.data_connections.front(); E; E = E->next()) {
+void VisualScript::get_data_connection_list(List<DataConnection> *r_connection) const {
+ for (const Set<DataConnection>::Element *E = data_connections.front(); E; E = E->next()) {
r_connection->push_back(E->get());
}
}
@@ -653,11 +563,8 @@ Dictionary VisualScript::_get_variable_info(const StringName &p_name) const {
}
void VisualScript::get_variable_list(List<StringName> *r_variables) const {
- for (Map<StringName, Variable>::Element *E = variables.front(); E; E = E->next()) {
- r_variables->push_back(E->key());
- }
-
- r_variables->sort_custom<StringName::AlphCompare>();
+ variables.get_key_list(r_variables);
+ // r_variables->sort_custom<StringName::AlphCompare>(); // Don't force it.
}
void VisualScript::set_instance_base_type(const StringName &p_type) {
@@ -680,24 +587,19 @@ void VisualScript::rename_variable(const StringName &p_name, const StringName &p
variables[p_new_name] = variables[p_name];
variables.erase(p_name);
-
- List<StringName> funcs;
- get_function_list(&funcs);
- for (List<StringName>::Element *F = funcs.front(); F; F = F->next()) { // loop through all the functions
- List<int> ids;
- get_node_list(F->get(), &ids);
- for (List<int>::Element *E = ids.front(); E; E = E->next()) {
- Ref<VisualScriptVariableGet> nodeget = get_node(F->get(), E->get());
- if (nodeget.is_valid()) {
- if (nodeget->get_variable() == p_name) {
- nodeget->set_variable(p_new_name);
- }
- } else {
- Ref<VisualScriptVariableSet> nodeset = get_node(F->get(), E->get());
- if (nodeset.is_valid()) {
- if (nodeset->get_variable() == p_name) {
- nodeset->set_variable(p_new_name);
- }
+ List<int> ids;
+ get_node_list(&ids);
+ for (int &E : ids) {
+ Ref<VisualScriptVariableGet> nodeget = get_node(E);
+ if (nodeget.is_valid()) {
+ if (nodeget->get_variable() == p_name) {
+ nodeget->set_variable(p_new_name);
+ }
+ } else {
+ Ref<VisualScriptVariableSet> nodeset = get_node(E);
+ if (nodeset.is_valid()) {
+ if (nodeset->get_variable() == p_name) {
+ nodeset->set_variable(p_new_name);
}
}
}
@@ -800,31 +702,32 @@ 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>();
}
int VisualScript::get_available_id() const {
- int max_id = 0;
- for (Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) {
- if (E->get().nodes.empty()) {
- continue;
+ // This is infinitely increasing,
+ // so one might want to implement a better solution,
+ // if the there is a case for huge number of nodes to be added to visual script.
+ List<int> nds;
+ nodes.get_key_list(&nds);
+ int max = -1;
+ for (const int &E : nds) {
+ if (E > max) {
+ max = E;
}
-
- int last_id = E->get().nodes.back()->key();
- max_id = MAX(max_id, last_id + 1);
}
-
- return max_id;
+ return (max + 1);
}
/////////////////////////////////
-bool VisualScript::can_instance() const {
- return true; //ScriptServer::is_scripting_enabled();
+bool VisualScript::can_instantiate() const {
+ return true; // ScriptServer::is_scripting_enabled();
}
StringName VisualScript::get_instance_base_type() const {
@@ -832,7 +735,7 @@ StringName VisualScript::get_instance_base_type() const {
}
Ref<Script> VisualScript::get_base_script() const {
- return Ref<Script>(); // no inheritance in visual script
+ return Ref<Script>(); // No inheritance in visual script.
}
#ifdef TOOLS_ENABLED
@@ -842,20 +745,23 @@ void VisualScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder)
void VisualScript::_update_placeholders() {
if (placeholders.size() == 0) {
- return; //no bother if no placeholders
+ return; // No bother if no placeholders.
}
List<PropertyInfo> pinfo;
Map<StringName, Variant> values;
- for (Map<StringName, Variable>::Element *E = variables.front(); E; E = E->next()) {
- if (!E->get()._export) {
+ List<StringName> keys;
+ variables.get_key_list(&keys);
+
+ for (const StringName &E : keys) {
+ if (!variables[E]._export) {
continue;
}
- PropertyInfo p = E->get().info;
- p.name = String(E->key());
+ PropertyInfo p = variables[E].info;
+ p.name = String(E);
pinfo.push_back(p);
- values[p.name] = E->get().default_value;
+ values[p.name] = variables[E].default_value;
}
for (Set<PlaceHolderScriptInstance *>::Element *E = placeholders.front(); E; E = E->next()) {
@@ -875,17 +781,19 @@ ScriptInstance *VisualScript::instance_create(Object *p_this) {
List<PropertyInfo> pinfo;
Map<StringName, Variant> values;
- for (Map<StringName, Variable>::Element *E = variables.front(); E; E = E->next()) {
- if (!E->get()._export) {
+ List<StringName> keys;
+ variables.get_key_list(&keys);
+
+ for (const StringName &E : keys) {
+ if (!variables[E]._export) {
continue;
}
- PropertyInfo p = E->get().info;
- p.name = String(E->key());
+ PropertyInfo p = variables[E].info;
+ p.name = String(E);
pinfo.push_back(p);
- values[p.name] = E->get().default_value;
+ values[p.name] = variables[E].default_value;
}
-
sins->update(pinfo, values);
return sins;
@@ -928,7 +836,7 @@ bool VisualScript::is_tool() const {
}
bool VisualScript::is_valid() const {
- return true; //always valid
+ return true; // Always valid.
}
ScriptLanguage *VisualScript::get_language() const {
@@ -940,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);
}
@@ -964,11 +872,14 @@ bool VisualScript::get_property_default_value(const StringName &p_property, Vari
}
void VisualScript::get_script_method_list(List<MethodInfo> *p_list) const {
- for (Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) {
+ List<StringName> funcs;
+ functions.get_key_list(&funcs);
+
+ for (const StringName &E : funcs) {
MethodInfo mi;
- mi.name = E->key();
- if (E->get().function_id >= 0) {
- Ref<VisualScriptFunction> func = E->get().nodes[E->get().function_id].node;
+ mi.name = E;
+ if (functions[E].func_id >= 0) {
+ Ref<VisualScriptFunction> func = nodes[functions[E].func_id].node;
if (func.is_valid()) {
for (int i = 0; i < func->get_argument_count(); i++) {
PropertyInfo arg;
@@ -988,15 +899,15 @@ bool VisualScript::has_method(const StringName &p_method) const {
}
MethodInfo VisualScript::get_method_info(const StringName &p_method) const {
- const Map<StringName, Function>::Element *E = functions.find(p_method);
- if (!E) {
+ const Function funct = functions[p_method];
+ if (funct.func_id == -1) {
return MethodInfo();
}
MethodInfo mi;
- mi.name = E->key();
- if (E->get().function_id >= 0) {
- Ref<VisualScriptFunction> func = E->get().nodes[E->get().function_id].node;
+ mi.name = p_method;
+ if (funct.func_id >= 0) {
+ Ref<VisualScriptFunction> func = nodes[funct.func_id].node;
if (func.is_valid()) {
for (int i = 0; i < func->get_argument_count(); i++) {
PropertyInfo arg;
@@ -1018,96 +929,36 @@ void VisualScript::get_script_property_list(List<PropertyInfo> *p_list) const {
List<StringName> vars;
get_variable_list(&vars);
- for (List<StringName>::Element *E = vars.front(); E; E = E->next()) {
- //if (!variables[E->get()]._export)
+ for (const StringName &E : vars) {
+ //if (!variables[E]._export)
// continue;
- PropertyInfo pi = variables[E->get()].info;
+ PropertyInfo pi = variables[E].info;
pi.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
p_list->push_back(pi);
}
}
int VisualScript::get_member_line(const StringName &p_member) const {
-#ifdef TOOLS_ENABLED
- if (has_function(p_member)) {
- for (Map<int, Function::NodeData>::Element *E = functions[p_member].nodes.front(); E; E = E->next()) {
- if (Object::cast_to<VisualScriptFunction>(E->get().node.ptr())) {
- return E->key();
- }
- }
- }
-#endif
- return -1;
+ return functions[p_member].func_id; // will be -1 if not found
}
#ifdef TOOLS_ENABLED
bool VisualScript::are_subnodes_edited() const {
- for (const Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) {
- for (const Map<int, Function::NodeData>::Element *F = E->get().nodes.front(); F; F = F->next()) {
- if (F->get().node->is_edited()) {
- return true;
- }
+ List<int> keys;
+ nodes.get_key_list(&keys);
+ for (const int &F : keys) {
+ if (nodes[F].node->is_edited()) {
+ return true;
}
}
-
return false;
}
#endif
-Vector<ScriptNetData> VisualScript::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> VisualScript::get_rpc_methods() const {
return rpc_functions;
}
-uint16_t VisualScript::get_rpc_method_id(const StringName &p_method) const {
- for (int i = 0; i < rpc_functions.size(); i++) {
- if (rpc_functions[i].name == p_method) {
- return i;
- }
- }
- return UINT16_MAX;
-}
-
-StringName VisualScript::get_rpc_method(const uint16_t p_rpc_method_id) const {
- ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), StringName());
- return rpc_functions[p_rpc_method_id].name;
-}
-
-MultiplayerAPI::RPCMode VisualScript::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
- ERR_FAIL_COND_V(p_rpc_method_id >= rpc_functions.size(), MultiplayerAPI::RPC_MODE_DISABLED);
- return rpc_functions[p_rpc_method_id].mode;
-}
-
-MultiplayerAPI::RPCMode VisualScript::get_rpc_mode(const StringName &p_method) const {
- return get_rpc_mode_by_id(get_rpc_method_id(p_method));
-}
-
-Vector<ScriptNetData> VisualScript::get_rset_properties() const {
- return rpc_variables;
-}
-
-uint16_t VisualScript::get_rset_property_id(const StringName &p_variable) const {
- for (int i = 0; i < rpc_variables.size(); i++) {
- if (rpc_variables[i].name == p_variable) {
- return i;
- }
- }
- return UINT16_MAX;
-}
-
-StringName VisualScript::get_rset_property(const uint16_t p_rset_property_id) const {
- ERR_FAIL_COND_V(p_rset_property_id >= rpc_variables.size(), StringName());
- return rpc_variables[p_rset_property_id].name;
-}
-
-MultiplayerAPI::RPCMode VisualScript::get_rset_mode_by_id(const uint16_t p_rset_variable_id) const {
- ERR_FAIL_COND_V(p_rset_variable_id >= rpc_variables.size(), MultiplayerAPI::RPC_MODE_DISABLED);
- return rpc_variables[p_rset_variable_id].mode;
-}
-
-MultiplayerAPI::RPCMode VisualScript::get_rset_mode(const StringName &p_variable) const {
- return get_rset_mode_by_id(get_rset_property_id(p_variable));
-}
-
void VisualScript::_set_data(const Dictionary &p_data) {
Dictionary d = p_data;
if (d.has("base_type")) {
@@ -1140,86 +991,42 @@ void VisualScript::_set_data(const Dictionary &p_data) {
Array funcs = d["functions"];
functions.clear();
- Vector2 last_pos = Vector2(-100 * funcs.size(), -100 * funcs.size()); // this is the center of the last fn box
- Vector2 last_size = Vector2(0.0, 0.0);
-
for (int i = 0; i < funcs.size(); i++) {
Dictionary func = funcs[i];
-
- StringName name = func["name"];
- //int id=func["function_id"];
- add_function(name);
-
- set_function_scroll(name, func["scroll"]);
-
- Array nodes = func["nodes"];
-
- if (!d.has("vs_unify") && nodes.size() > 0) {
- Vector2 top_left = nodes[1];
- Vector2 bottom_right = nodes[1];
-
- for (int j = 0; j < nodes.size(); j += 3) {
- Point2 pos = nodes[j + 1];
- if (pos.y > top_left.y) {
- top_left.y = pos.y;
- }
- if (pos.y < bottom_right.y) {
- bottom_right.y = pos.y;
- }
- if (pos.x > bottom_right.x) {
- bottom_right.x = pos.x;
- }
- if (pos.x < top_left.x) {
- top_left.x = pos.x;
- }
- }
-
- Vector2 size = Vector2(bottom_right.x - top_left.x, top_left.y - bottom_right.y);
-
- Vector2 offset = last_pos + (last_size / 2.0) + (size / 2.0); // dunno I might just keep it in one axis but diagonal feels better....
-
- last_pos = offset;
- last_size = size;
-
- for (int j = 0; j < nodes.size(); j += 3) {
- add_node(name, nodes[j], nodes[j + 2], offset + nodes[j + 1]); // also add an additional buffer if you want to
- }
-
- } else {
- for (int j = 0; j < nodes.size(); j += 3) {
- add_node(name, nodes[j], nodes[j + 2], nodes[j + 1]);
- }
+ add_function(func["name"], func["function_id"]);
+ }
+ {
+ Array nodes = d["nodes"];
+ for (int i = 0; i < nodes.size(); i += 3) {
+ add_node(nodes[i], nodes[i + 2], nodes[i + 1]);
}
- Array sequence_connections = func["sequence_connections"];
+ Array sequence_connections = d["sequence_connections"];
for (int j = 0; j < sequence_connections.size(); j += 3) {
- sequence_connect(name, sequence_connections[j + 0], sequence_connections[j + 1], sequence_connections[j + 2]);
+ sequence_connect(sequence_connections[j + 0], sequence_connections[j + 1], sequence_connections[j + 2]);
}
- Array data_connections = func["data_connections"];
-
+ Array data_connections = d["data_connections"];
for (int j = 0; j < data_connections.size(); j += 4) {
- data_connect(name, data_connections[j + 0], data_connections[j + 1], data_connections[j + 2], data_connections[j + 3]);
+ data_connect(data_connections[j + 0], data_connections[j + 1], data_connections[j + 2], data_connections[j + 3]);
}
}
+ is_tool_script = d["is_tool_script"];
+ scroll = d["scroll"];
- if (d.has("is_tool_script")) {
- is_tool_script = d["is_tool_script"];
- } else {
- is_tool_script = false;
- }
-
- // Takes all the rpc methods
+ // Takes all the rpc methods.
rpc_functions.clear();
- rpc_variables.clear();
- for (Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) {
- if (E->get().function_id >= 0 && E->get().nodes.find(E->get().function_id)) {
- Ref<VisualScriptFunction> vsf = E->get().nodes[E->get().function_id].node;
+ List<StringName> fns;
+ functions.get_key_list(&fns);
+ for (const StringName &E : fns) {
+ 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) {
- ScriptNetData nd;
- nd.name = E->key();
- nd.mode = vsf->get_rpc_mode();
+ 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 = Multiplayer::TRANSFER_MODE_RELIABLE; // TODO
if (rpc_functions.find(nd) == -1) {
rpc_functions.push_back(nd);
}
@@ -1228,33 +1035,34 @@ void VisualScript::_set_data(const Dictionary &p_data) {
}
}
- // Visual script doesn't have rset :(
-
// Sort so we are 100% that they are always the same.
- rpc_functions.sort_custom<SortNetData>();
+ rpc_functions.sort_custom<Multiplayer::SortRPCConfig>();
}
Dictionary VisualScript::_get_data() const {
Dictionary d;
d["base_type"] = base_type;
+
Array vars;
- for (const Map<StringName, Variable>::Element *E = variables.front(); E; E = E->next()) {
- Dictionary var = _get_variable_info(E->key());
- var["name"] = E->key(); //make sure it's the right one
- var["default_value"] = E->get().default_value;
- var["export"] = E->get()._export;
+ List<StringName> var_names;
+ variables.get_key_list(&var_names);
+ for (const StringName &E : var_names) {
+ Dictionary var = _get_variable_info(E);
+ var["name"] = E; // Make sure it's the right one.
+ var["default_value"] = variables[E].default_value;
+ var["export"] = variables[E]._export;
vars.push_back(var);
}
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;
@@ -1264,78 +1072,73 @@ Dictionary VisualScript::_get_data() const {
d["signals"] = sigs;
Array funcs;
-
- for (const Map<StringName, Function>::Element *E = functions.front(); E; E = E->next()) {
+ List<StringName> func_names;
+ functions.get_key_list(&func_names);
+ for (const StringName &E : func_names) {
Dictionary func;
- func["name"] = E->key();
- func["function_id"] = E->get().function_id;
- func["scroll"] = E->get().scroll;
-
- Array nodes;
-
- for (const Map<int, Function::NodeData>::Element *F = E->get().nodes.front(); F; F = F->next()) {
- nodes.push_back(F->key());
- nodes.push_back(F->get().pos);
- nodes.push_back(F->get().node);
- }
-
- func["nodes"] = nodes;
-
- Array sequence_connections;
-
- for (const Set<SequenceConnection>::Element *F = E->get().sequence_connections.front(); F; F = F->next()) {
- sequence_connections.push_back(F->get().from_node);
- sequence_connections.push_back(F->get().from_output);
- sequence_connections.push_back(F->get().to_node);
- }
-
- func["sequence_connections"] = sequence_connections;
-
- Array data_connections;
+ func["name"] = E;
+ func["function_id"] = functions[E].func_id;
+ funcs.push_back(func);
+ }
+ d["functions"] = funcs;
- for (const Set<DataConnection>::Element *F = E->get().data_connections.front(); F; F = F->next()) {
- data_connections.push_back(F->get().from_node);
- data_connections.push_back(F->get().from_port);
- data_connections.push_back(F->get().to_node);
- data_connections.push_back(F->get().to_port);
- }
+ Array nds;
+ List<int> node_ids;
+ nodes.get_key_list(&node_ids);
+ for (const int &F : node_ids) {
+ nds.push_back(F);
+ nds.push_back(nodes[F].pos);
+ nds.push_back(nodes[F].node);
+ }
+ d["nodes"] = nds;
- func["data_connections"] = data_connections;
+ Array seqconns;
+ for (const Set<SequenceConnection>::Element *F = sequence_connections.front(); F; F = F->next()) {
+ seqconns.push_back(F->get().from_node);
+ seqconns.push_back(F->get().from_output);
+ seqconns.push_back(F->get().to_node);
+ }
+ d["sequence_connections"] = seqconns;
- funcs.push_back(func);
+ Array dataconns;
+ for (const Set<DataConnection>::Element *F = data_connections.front(); F; F = F->next()) {
+ dataconns.push_back(F->get().from_node);
+ dataconns.push_back(F->get().from_port);
+ dataconns.push_back(F->get().to_node);
+ dataconns.push_back(F->get().to_port);
}
+ d["data_connections"] = dataconns;
- d["functions"] = funcs;
d["is_tool_script"] = is_tool_script;
- d["vs_unify"] = true;
+ d["scroll"] = scroll;
return d;
}
void VisualScript::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_function", "name"), &VisualScript::add_function);
+ ClassDB::bind_method(D_METHOD("add_function", "name", "func_node_id"), &VisualScript::add_function);
ClassDB::bind_method(D_METHOD("has_function", "name"), &VisualScript::has_function);
ClassDB::bind_method(D_METHOD("remove_function", "name"), &VisualScript::remove_function);
ClassDB::bind_method(D_METHOD("rename_function", "name", "new_name"), &VisualScript::rename_function);
- ClassDB::bind_method(D_METHOD("set_function_scroll", "name", "ofs"), &VisualScript::set_function_scroll);
- ClassDB::bind_method(D_METHOD("get_function_scroll", "name"), &VisualScript::get_function_scroll);
+ ClassDB::bind_method(D_METHOD("set_scroll", "ofs"), &VisualScript::set_scroll);
+ ClassDB::bind_method(D_METHOD("get_scroll"), &VisualScript::get_scroll);
- ClassDB::bind_method(D_METHOD("add_node", "func", "id", "node", "position"), &VisualScript::add_node, DEFVAL(Point2()));
- ClassDB::bind_method(D_METHOD("remove_node", "func", "id"), &VisualScript::remove_node);
+ ClassDB::bind_method(D_METHOD("add_node", "id", "node", "position"), &VisualScript::add_node, DEFVAL(Point2()));
+ ClassDB::bind_method(D_METHOD("remove_node", "id"), &VisualScript::remove_node);
ClassDB::bind_method(D_METHOD("get_function_node_id", "name"), &VisualScript::get_function_node_id);
- ClassDB::bind_method(D_METHOD("get_node", "func", "id"), &VisualScript::get_node);
- ClassDB::bind_method(D_METHOD("has_node", "func", "id"), &VisualScript::has_node);
- ClassDB::bind_method(D_METHOD("set_node_position", "func", "id", "position"), &VisualScript::set_node_position);
- ClassDB::bind_method(D_METHOD("get_node_position", "func", "id"), &VisualScript::get_node_position);
+ ClassDB::bind_method(D_METHOD("get_node", "id"), &VisualScript::get_node);
+ ClassDB::bind_method(D_METHOD("has_node", "id"), &VisualScript::has_node);
+ ClassDB::bind_method(D_METHOD("set_node_position", "id", "position"), &VisualScript::set_node_position);
+ ClassDB::bind_method(D_METHOD("get_node_position", "id"), &VisualScript::get_node_position);
- ClassDB::bind_method(D_METHOD("sequence_connect", "func", "from_node", "from_output", "to_node"), &VisualScript::sequence_connect);
- ClassDB::bind_method(D_METHOD("sequence_disconnect", "func", "from_node", "from_output", "to_node"), &VisualScript::sequence_disconnect);
- ClassDB::bind_method(D_METHOD("has_sequence_connection", "func", "from_node", "from_output", "to_node"), &VisualScript::has_sequence_connection);
+ ClassDB::bind_method(D_METHOD("sequence_connect", "from_node", "from_output", "to_node"), &VisualScript::sequence_connect);
+ ClassDB::bind_method(D_METHOD("sequence_disconnect", "from_node", "from_output", "to_node"), &VisualScript::sequence_disconnect);
+ ClassDB::bind_method(D_METHOD("has_sequence_connection", "from_node", "from_output", "to_node"), &VisualScript::has_sequence_connection);
- ClassDB::bind_method(D_METHOD("data_connect", "func", "from_node", "from_port", "to_node", "to_port"), &VisualScript::data_connect);
- ClassDB::bind_method(D_METHOD("data_disconnect", "func", "from_node", "from_port", "to_node", "to_port"), &VisualScript::data_disconnect);
- ClassDB::bind_method(D_METHOD("has_data_connection", "func", "from_node", "from_port", "to_node", "to_port"), &VisualScript::has_data_connection);
+ ClassDB::bind_method(D_METHOD("data_connect", "from_node", "from_port", "to_node", "to_port"), &VisualScript::data_connect);
+ ClassDB::bind_method(D_METHOD("data_disconnect", "from_node", "from_port", "to_node", "to_port"), &VisualScript::data_disconnect);
+ ClassDB::bind_method(D_METHOD("has_data_connection", "from_node", "from_port", "to_node", "to_port"), &VisualScript::has_data_connection);
ClassDB::bind_method(D_METHOD("add_variable", "name", "default_value", "export"), &VisualScript::add_variable, DEFVAL(Variant()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("has_variable", "name"), &VisualScript::has_variable);
@@ -1369,9 +1172,9 @@ 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::STRING, "function"), PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("node_ports_changed", PropertyInfo(Variant::INT, "id")));
}
VisualScript::VisualScript() {
@@ -1380,16 +1183,12 @@ VisualScript::VisualScript() {
}
bool VisualScript::inherits_script(const Ref<Script> &p_script) const {
- return this == p_script.ptr(); //there is no inheritance in visual scripts, so this is enough
-}
-
-StringName VisualScript::get_default_func() const {
- return StringName("f_312843592");
+ return this == p_script.ptr(); // There is no inheritance in visual scripts, so this is enough.
}
-Set<int> VisualScript::get_output_sequence_ports_connected(const String &edited_func, int from_node) {
+Set<int> VisualScript::get_output_sequence_ports_connected(int from_node) {
List<VisualScript::SequenceConnection> *sc = memnew(List<VisualScript::SequenceConnection>);
- get_sequence_connection_list(edited_func, sc);
+ get_sequence_connection_list(sc);
Set<int> connected;
for (List<VisualScript::SequenceConnection>::Element *E = sc->front(); E; E = E->next()) {
if (E->get().from_node == from_node) {
@@ -1401,8 +1200,11 @@ Set<int> VisualScript::get_output_sequence_ports_connected(const String &edited_
}
VisualScript::~VisualScript() {
- while (!functions.empty()) {
- remove_function(functions.front()->key());
+ // Remove all nodes and stuff that hold data refs.
+ List<int> nds;
+ nodes.get_key_list(&nds);
+ for (const int &E : nds) {
+ remove_node(E);
}
}
@@ -1430,20 +1232,21 @@ bool VisualScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
}
void VisualScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
- for (const Map<StringName, VisualScript::Variable>::Element *E = script->variables.front(); E; E = E->next()) {
- if (!E->get()._export) {
+ List<StringName> vars;
+ script->variables.get_key_list(&vars);
+ for (const StringName &E : vars) {
+ if (!script->variables[E]._export) {
continue;
}
- PropertyInfo p = E->get().info;
- p.name = String(E->key());
+ PropertyInfo p = script->variables[E].info;
+ p.name = String(E);
p.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
p_properties->push_back(p);
}
}
Variant::Type VisualScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
- const Map<StringName, VisualScript::Variable>::Element *E = script->variables.find(p_name);
- if (!E) {
+ if (!script->variables.has(p_name)) {
if (r_is_valid) {
*r_is_valid = false;
}
@@ -1454,19 +1257,17 @@ Variant::Type VisualScriptInstance::get_property_type(const StringName &p_name,
*r_is_valid = true;
}
- return E->get().info.type;
+ return script->variables[p_name].info.type;
}
void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
- for (const Map<StringName, VisualScript::Function>::Element *E = script->functions.front(); E; E = E->next()) {
- if (E->key() == script->get_default_func()) {
- continue;
- }
-
+ List<StringName> fns;
+ script->functions.get_key_list(&fns);
+ for (const StringName &E : fns) {
MethodInfo mi;
- mi.name = E->key();
- if (E->get().function_id >= 0 && E->get().nodes.has(E->get().function_id)) {
- Ref<VisualScriptFunction> vsf = E->get().nodes[E->get().function_id].node;
+ mi.name = E;
+ if (script->functions[E].func_id >= 0 && script->nodes.has(script->functions[E].func_id)) {
+ Ref<VisualScriptFunction> vsf = script->nodes[script->functions[E].func_id].node;
if (vsf.is_valid()) {
for (int i = 0; i < vsf->get_argument_count(); i++) {
PropertyInfo arg;
@@ -1476,21 +1277,16 @@ void VisualScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
mi.arguments.push_back(arg);
}
- if (!vsf->is_sequenced()) { //assumed constant if not sequenced
+ if (!vsf->is_sequenced()) { // Assumed constant if not sequenced.
mi.flags |= METHOD_FLAG_CONST;
}
}
}
-
p_list->push_back(mi);
}
}
bool VisualScriptInstance::has_method(const StringName &p_method) const {
- if (p_method == script->get_default_func()) {
- return false;
- }
-
return script->functions.has(p_method);
}
@@ -1506,7 +1302,7 @@ void VisualScriptInstance::_dependency_step(VisualScriptNodeInstance *node, int
pass_stack[node->pass_idx] = p_pass;
- if (!node->dependencies.empty()) {
+ if (!node->dependencies.is_empty()) {
int dc = node->dependencies.size();
VisualScriptNodeInstance **deps = node->dependencies.ptrw();
@@ -1522,10 +1318,10 @@ void VisualScriptInstance::_dependency_step(VisualScriptNodeInstance *node, int
int index = node->input_ports[i] & VisualScriptNodeInstance::INPUT_MASK;
if (node->input_ports[i] & VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT) {
- //is a default value (unassigned input port)
+ // Is a default value (unassigned input port).
input_args[i] = &default_values[index];
} else {
- //regular temporary in stack
+ // Regular temporary in stack.
input_args[i] = &variant_stack[index];
}
}
@@ -1536,7 +1332,7 @@ void VisualScriptInstance::_dependency_step(VisualScriptNodeInstance *node, int
Variant *working_mem = node->working_mem_idx >= 0 ? &variant_stack[node->working_mem_idx] : (Variant *)nullptr;
node->step(input_args, output_args, VisualScriptNodeInstance::START_MODE_BEGIN_SEQUENCE, working_mem, r_error, error_str);
- //ignore return
+ // Ignore return.
if (r_error.error != Callable::CallError::CALL_OK) {
*r_error_node = node;
}
@@ -1547,7 +1343,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
ERR_FAIL_COND_V(!F, Variant());
Function *f = &F->get();
- //this call goes separate, so it can e yielded and suspended
+ // This call goes separate, so it can be yielded and suspended.
Variant *variant_stack = (Variant *)p_stack;
bool *sequence_bits = (bool *)(variant_stack + f->max_stack);
const Variant **input_args = (const Variant **)(sequence_bits + f->node_count);
@@ -1573,27 +1369,27 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
#endif
while (true) {
- p_pass++; //increment pass
+ p_pass++; // Increment pass.
current_node_id = node->get_id();
VSDEBUG("==========AT NODE: " + itos(current_node_id) + " base: " + node->get_base_node()->get_class_name());
VSDEBUG("AT STACK POS: " + itos(flow_stack_pos));
- //setup working mem
+ // Setup working mem.
working_mem = node->working_mem_idx >= 0 ? &variant_stack[node->working_mem_idx] : (Variant *)nullptr;
VSDEBUG("WORKING MEM: " + itos(node->working_mem_idx));
if (current_node_id == f->node) {
- //if function node, set up function arguments from beginning of stack
+ // If function node, set up function arguments from beginning of stack.
for (int i = 0; i < f->argument_count; i++) {
input_args[i] = &variant_stack[i];
}
} else {
- //run dependencies first
+ // Run dependencies first.
- if (!node->dependencies.empty()) {
+ if (!node->dependencies.is_empty()) {
int dc = node->dependencies.size();
VisualScriptNodeInstance **deps = node->dependencies.ptrw();
@@ -1608,18 +1404,18 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
}
if (!error) {
- //setup input pointers normally
+ // Setup input pointers normally.
VSDEBUG("INPUT PORTS: " + itos(node->input_port_count));
for (int i = 0; i < node->input_port_count; i++) {
int index = node->input_ports[i] & VisualScriptNodeInstance::INPUT_MASK;
if (node->input_ports[i] & VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT) {
- //is a default value (unassigned input port)
+ // Is a default value (unassigned input port).
input_args[i] = &default_values[index];
VSDEBUG("\tPORT " + itos(i) + " DEFAULT VAL");
} else {
- //regular temporary in stack
+ // Regular temporary in stack.
input_args[i] = &variant_stack[index];
VSDEBUG("PORT " + itos(i) + " AT STACK " + itos(index));
}
@@ -1631,7 +1427,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
break;
}
- //setup output pointers
+ // Setup output pointers.
VSDEBUG("OUTPUT PORTS: " + itos(node->output_port_count));
for (int i = 0; i < node->output_port_count; i++) {
@@ -1639,15 +1435,15 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
VSDEBUG("PORT " + itos(i) + " AT STACK " + itos(node->output_ports[i]));
}
- //do step
+ // Do step.
VisualScriptNodeInstance::StartMode start_mode;
{
if (p_resuming_yield) {
start_mode = VisualScriptNodeInstance::START_MODE_RESUME_YIELD;
- p_resuming_yield = false; // should resume only the first time
+ p_resuming_yield = false; // Should resume only the first time.
} else if (flow_stack && (flow_stack[flow_stack_pos] & VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT)) {
- //if there is a push bit, it means we are continuing a sequence
+ // If there is a push bit, it means we are continuing a sequence.
start_mode = VisualScriptNodeInstance::START_MODE_CONTINUE_SEQUENCE;
} else {
start_mode = VisualScriptNodeInstance::START_MODE_BEGIN_SEQUENCE;
@@ -1659,13 +1455,13 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
int ret = node->step(input_args, output_args, start_mode, working_mem, r_error, error_str);
if (r_error.error != Callable::CallError::CALL_OK) {
- //use error from step
+ // Use error from step.
error = true;
break;
}
if (ret & VisualScriptNodeInstance::STEP_YIELD_BIT) {
- //yielded!
+ // Yielded!
if (node->get_working_memory_size() == 0) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
error_str = RTR("A node yielded without working memory, please read the docs on how to yield properly!");
@@ -1681,7 +1477,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
break;
}
- //step 1, capture all state
+ // Step 1, capture all state.
state->instance_id = get_owner_ptr()->get_instance_id();
state->script_id = get_script()->get_instance_id();
state->instance = this;
@@ -1692,12 +1488,12 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
state->flow_stack_pos = flow_stack_pos;
state->stack.resize(p_stack_size);
state->pass = p_pass;
- copymem(state->stack.ptrw(), p_stack, p_stack_size);
- //step 2, run away, return directly
+ memcpy(state->stack.ptrw(), p_stack, p_stack_size);
+ // Step 2, run away, return directly.
r_error.error = Callable::CallError::CALL_OK;
#ifdef DEBUG_ENABLED
- //will re-enter later, so exiting
+ // Will re-enter later, so exiting.
if (EngineDebugger::is_active()) {
VisualScriptLanguage::singleton->exit_function();
}
@@ -1742,18 +1538,18 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
error_str = RTR("Return value must be assigned to first element of node working memory! Fix your node please.");
error = true;
} else {
- //assign from working memory, first element
+ // Assign from working memory, first element.
return_value = *working_mem;
}
VSDEBUG("EXITING FUNCTION - VALUE " + String(return_value));
- break; //exit function requested, bye
+ break; // Exit function requested, bye
}
- VisualScriptNodeInstance *next = nullptr; //next node
+ VisualScriptNodeInstance *next = nullptr; // Next node.
if ((ret == output || ret & VisualScriptNodeInstance::STEP_FLAG_PUSH_STACK_BIT) && node->sequence_output_count) {
- //if no exit bit was set, and has sequence outputs, guess next node
+ // If no exit bit was set, and has sequence outputs, guess next node.
if (output >= node->sequence_output_count) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
error_str = RTR("Node returned an invalid sequence output: ") + itos(output);
@@ -1762,25 +1558,25 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
}
next = node->sequence_outputs[output];
- VSDEBUG("GOT NEXT NODE - " + (next ? itos(next->get_id()) : "NULL"));
+ VSDEBUG("GOT NEXT NODE - " + (next ? itos(next->get_id()) : "Null"));
}
if (flow_stack) {
- //update flow stack pos (may have changed)
+ // Update flow stack pos (may have changed).
flow_stack[flow_stack_pos] = current_node_id;
- //add stack push bit if requested
+ // Add stack push bit if requested.
if (ret & VisualScriptNodeInstance::STEP_FLAG_PUSH_STACK_BIT) {
flow_stack[flow_stack_pos] |= VisualScriptNodeInstance::FLOW_STACK_PUSHED_BIT;
- sequence_bits[node->sequence_index] = true; //remember sequence bit
+ sequence_bits[node->sequence_index] = true; // Remember sequence bit.
VSDEBUG("NEXT SEQ - FLAG BIT");
} else {
- sequence_bits[node->sequence_index] = false; //forget sequence bit
+ sequence_bits[node->sequence_index] = false; // Forget sequence bit.
VSDEBUG("NEXT SEQ - NORMAL");
}
if (ret & VisualScriptNodeInstance::STEP_FLAG_GO_BACK_BIT) {
- //go back request
+ // Go back request.
if (flow_stack_pos > 0) {
flow_stack_pos--;
@@ -1788,20 +1584,20 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
VSDEBUG("NEXT IS GO BACK");
} else {
VSDEBUG("NEXT IS GO BACK, BUT NO NEXT SO EXIT");
- break; //simply exit without value or error
+ break; // Simply exit without value or error.
}
} else if (next) {
if (sequence_bits[next->sequence_index]) {
- // what happened here is that we are entering a node that is in the middle of doing a sequence (pushed stack) from the front
+ // What happened here is that we are entering a node that is in the middle of doing a sequence (pushed stack) from the front
// because each node has a working memory, we can't really do a sub-sequence
// as a result, the sequence will be restarted and the stack will roll back to find where this node
- // started the sequence
+ // started the sequence.
bool found = false;
for (int i = flow_stack_pos; i >= 0; i--) {
if ((flow_stack[i] & VisualScriptNodeInstance::FLOW_STACK_MASK) == next->get_id()) {
- flow_stack_pos = i; //roll back and remove bit
+ flow_stack_pos = i; // Roll back and remove bit.
flow_stack[i] = next->get_id();
sequence_bits[next->sequence_index] = false;
found = true;
@@ -1819,7 +1615,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
VSDEBUG("RE-ENTERED A LOOP, RETURNED STACK POS TO - " + itos(flow_stack_pos));
} else {
- // check for stack overflow
+ // Check for stack overflow.
if (flow_stack_pos + 1 >= flow_max) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
error_str = RTR("Stack overflow with stack depth: ") + itos(output);
@@ -1836,7 +1632,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
}
} else {
- //no next node, try to go back in stack to pushed bit
+ // No next node, try to go back in stack to pushed bit.
bool found = false;
@@ -1852,22 +1648,22 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
if (!found) {
VSDEBUG("NO NEXT NODE, NO GO BACK, EXITING");
- break; //done, couldn't find a push stack bit
+ break; // Done, couldn't find a push stack bit.
}
VSDEBUG("NO NEXT NODE, GO BACK TO: " + itos(flow_stack_pos));
}
} else {
- node = next; //stackless mode, simply assign next node
+ node = next; // Stackless mode, simply assign next node.
}
}
if (error) {
- //error
- // function, file, line, error, explanation
+ // Error
+ // Function, file, line, error, explanation.
String err_file = script->get_path();
String err_func = p_method;
- int err_line = current_node_id; //not a line but it works as one
+ 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()) {
@@ -1892,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);
}
//}
@@ -1906,7 +1702,7 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
}
#endif
- //clean up variant stack
+ // Clean up variant stack.
for (int i = 0; i < f->max_stack; i++) {
variant_stack[i].~Variant();
}
@@ -1954,10 +1750,10 @@ Variant VisualScriptInstance::call(const StringName &p_method, const Variant **p
int *pass_stack = flow_stack ? (int *)(flow_stack + flow_max) : (int *)nullptr;
for (int i = 0; i < f->node_count; i++) {
- sequence_bits[i] = false; //all starts as false
+ sequence_bits[i] = false; // All starts as false.
}
- zeromem(pass_stack, f->pass_stack_size * sizeof(int));
+ memset(pass_stack, 0, f->pass_stack_size * sizeof(int));
Map<int, VisualScriptNodeInstance *>::Element *E = instances.find(f->node);
if (!E) {
@@ -1988,12 +1784,12 @@ Variant VisualScriptInstance::call(const StringName &p_method, const Variant **p
return Variant();
}
- //allocate variant stack
+ // Allocate variant stack.
for (int i = 0; i < f->max_stack; i++) {
memnew_placement(&variant_stack[i], Variant);
}
- //allocate function arguments (must be copied for yield to work properly)
+ // Allocate function arguments (must be copied for yield to work properly).
for (int i = 0; i < p_argcount; i++) {
variant_stack[i] = *p_args[i];
}
@@ -2002,12 +1798,12 @@ Variant VisualScriptInstance::call(const StringName &p_method, const Variant **p
}
void VisualScriptInstance::notification(int p_notification) {
- //do nothing as this is called using virtual
+ // Do nothing as this is called using virtual.
Variant what = p_notification;
const Variant *whatp = &what;
Callable::CallError ce;
- call(VisualScriptLanguage::singleton->notification, &whatp, 1, ce); //do as call
+ call(VisualScriptLanguage::singleton->notification, &whatp, 1, ce); // Do as call.
}
String VisualScriptInstance::to_string(bool *r_valid) {
@@ -2037,46 +1833,10 @@ Ref<Script> VisualScriptInstance::get_script() const {
return script;
}
-Vector<ScriptNetData> VisualScriptInstance::get_rpc_methods() const {
+const Vector<Multiplayer::RPCConfig> VisualScriptInstance::get_rpc_methods() const {
return script->get_rpc_methods();
}
-uint16_t VisualScriptInstance::get_rpc_method_id(const StringName &p_method) const {
- return script->get_rpc_method_id(p_method);
-}
-
-StringName VisualScriptInstance::get_rpc_method(const uint16_t p_rpc_method_id) const {
- return script->get_rpc_method(p_rpc_method_id);
-}
-
-MultiplayerAPI::RPCMode VisualScriptInstance::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const {
- return script->get_rpc_mode_by_id(p_rpc_method_id);
-}
-
-MultiplayerAPI::RPCMode VisualScriptInstance::get_rpc_mode(const StringName &p_method) const {
- return script->get_rpc_mode(p_method);
-}
-
-Vector<ScriptNetData> VisualScriptInstance::get_rset_properties() const {
- return script->get_rset_properties();
-}
-
-uint16_t VisualScriptInstance::get_rset_property_id(const StringName &p_variable) const {
- return script->get_rset_property_id(p_variable);
-}
-
-StringName VisualScriptInstance::get_rset_property(const uint16_t p_rset_property_id) const {
- return script->get_rset_property(p_rset_property_id);
-}
-
-MultiplayerAPI::RPCMode VisualScriptInstance::get_rset_mode_by_id(const uint16_t p_rset_variable_id) const {
- return script->get_rset_mode_by_id(p_rset_variable_id);
-}
-
-MultiplayerAPI::RPCMode VisualScriptInstance::get_rset_mode(const StringName &p_variable) const {
- return script->get_rset_mode(p_variable);
-}
-
void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_owner) {
script = p_script;
owner = p_owner;
@@ -2085,210 +1845,237 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
max_input_args = 0;
max_output_args = 0;
- if (Object::cast_to<Node>(p_owner)) {
- //turn on these if they exist and base is a node
- Node *node = Object::cast_to<Node>(p_owner);
- if (p_script->functions.has("_process")) {
- node->set_process(true);
- }
- if (p_script->functions.has("_physics_process")) {
- node->set_physics_process(true);
- }
- if (p_script->functions.has("_input")) {
- node->set_process_input(true);
- }
- if (p_script->functions.has("_unhandled_input")) {
- node->set_process_unhandled_input(true);
- }
- if (p_script->functions.has("_unhandled_key_input")) {
- node->set_process_unhandled_key_input(true);
+ // Setup variables.
+ {
+ List<StringName> keys;
+ script->variables.get_key_list(&keys);
+ for (const StringName &E : keys) {
+ variables[E] = script->variables[E].default_value;
}
}
- for (const Map<StringName, VisualScript::Variable>::Element *E = script->variables.front(); E; E = E->next()) {
- variables[E->key()] = E->get().default_value;
- }
-
- for (const Map<StringName, VisualScript::Function>::Element *E = script->functions.front(); E; E = E->next()) {
- if (E->key() == script->get_default_func()) {
- continue;
- }
-
- Function function;
- function.node = E->get().function_id;
- function.max_stack = 0;
- function.flow_stack_size = 0;
- function.pass_stack_size = 0;
- function.node_count = 0;
-
- Map<StringName, int> local_var_indices;
-
- if (function.node < 0) {
- VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No start node in function: " + String(E->key()));
-
- ERR_CONTINUE(function.node < 0);
- }
-
- {
- Ref<VisualScriptFunction> func_node = script->get_node(E->key(), E->get().function_id);
-
- if (func_node.is_null()) {
- VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No VisualScriptFunction typed start node in function: " + String(E->key()));
+ // Setup functions from sequence trees.
+ {
+ List<StringName> keys;
+ script->functions.get_key_list(&keys);
+ for (const StringName &E : keys) {
+ const VisualScript::Function vsfn = p_script->functions[E];
+ Function function;
+ function.node = vsfn.func_id;
+ function.max_stack = 0;
+ function.flow_stack_size = 0;
+ function.pass_stack_size = 0;
+ function.node_count = 0;
+
+ Map<StringName, int> local_var_indices;
+
+ if (function.node < 0) {
+ VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No start node in function: " + String(E));
+ ERR_CONTINUE(function.node < 0);
}
- ERR_CONTINUE(!func_node.is_valid());
-
- function.argument_count = func_node->get_argument_count();
- function.max_stack += function.argument_count;
- function.flow_stack_size = func_node->is_stack_less() ? 0 : func_node->get_stack_size();
- max_input_args = MAX(max_input_args, function.argument_count);
- }
-
- //multiple passes are required to set up this complex thing..
-
- //first create the nodes
- for (const Map<int, VisualScript::Function::NodeData>::Element *F = E->get().nodes.front(); F; F = F->next()) {
- Ref<VisualScriptNode> node = F->get().node;
-
- VisualScriptNodeInstance *instance = node->instance(this); //create instance
- ERR_FAIL_COND(!instance);
+ {
+ Ref<VisualScriptFunction> func_node = script->get_node(vsfn.func_id);
- instance->base = node.ptr();
+ if (func_node.is_null()) {
+ VisualScriptLanguage::singleton->debug_break_parse(get_script()->get_path(), 0, "No VisualScriptFunction typed start node in function: " + String(E));
+ }
- instance->id = F->key();
- instance->input_port_count = node->get_input_value_port_count();
- instance->input_ports = nullptr;
- instance->output_port_count = node->get_output_value_port_count();
- instance->output_ports = nullptr;
- instance->sequence_output_count = node->get_output_sequence_port_count();
- instance->sequence_index = function.node_count++;
- instance->sequence_outputs = nullptr;
- instance->pass_idx = -1;
+ ERR_CONTINUE(!func_node.is_valid());
- if (instance->input_port_count) {
- instance->input_ports = memnew_arr(int, instance->input_port_count);
- for (int i = 0; i < instance->input_port_count; i++) {
- instance->input_ports[i] = -1; //if not assigned, will become default value
- }
+ function.argument_count = func_node->get_argument_count();
+ function.max_stack += function.argument_count;
+ function.flow_stack_size = func_node->is_stack_less() ? 0 : func_node->get_stack_size();
+ max_input_args = MAX(max_input_args, function.argument_count);
}
-
- if (instance->output_port_count) {
- instance->output_ports = memnew_arr(int, instance->output_port_count);
- for (int i = 0; i < instance->output_port_count; i++) {
- instance->output_ports[i] = -1; //if not assigned, will output to trash
+ // Function nodes graphs.
+ Set<VisualScript::SequenceConnection> seqconns;
+ Set<VisualScript::DataConnection> dataconns;
+ Set<int> node_ids;
+ node_ids.insert(function.node);
+ {
+ List<int> nd_queue;
+ nd_queue.push_back(function.node);
+ while (!nd_queue.is_empty()) {
+ for (const Set<VisualScript::SequenceConnection>::Element *F = script->sequence_connections.front(); F; F = F->next()) {
+ if (nd_queue.front()->get() == F->get().from_node && !node_ids.has(F->get().to_node)) {
+ nd_queue.push_back(F->get().to_node);
+ node_ids.insert(F->get().to_node);
+ }
+ if (nd_queue.front()->get() == F->get().from_node && !seqconns.has(F->get())) {
+ seqconns.insert(F->get());
+ }
+ }
+ nd_queue.pop_front();
}
- }
-
- if (instance->sequence_output_count) {
- instance->sequence_outputs = memnew_arr(VisualScriptNodeInstance *, instance->sequence_output_count);
- for (int i = 0; i < instance->sequence_output_count; i++) {
- instance->sequence_outputs[i] = nullptr; //if it remains null, flow ends here
+ HashMap<int, HashMap<int, Pair<int, int>>> dc_lut; // :: to -> to_port -> (from, from_port)
+ for (const Set<VisualScript::DataConnection>::Element *F = script->data_connections.front(); F; F = F->next()) {
+ dc_lut[F->get().to_node][F->get().to_port] = Pair<int, int>(F->get().from_node, F->get().from_port);
+ }
+ for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) {
+ nd_queue.push_back(F->get());
+ }
+ List<int> dc_keys;
+ while (!nd_queue.is_empty()) {
+ int ky = nd_queue.front()->get();
+ dc_lut[ky].get_key_list(&dc_keys);
+ for (const int &F : dc_keys) {
+ VisualScript::DataConnection dc;
+ dc.from_node = dc_lut[ky][F].first;
+ dc.from_port = dc_lut[ky][F].second;
+ dc.to_node = ky;
+ dc.to_port = F;
+ dataconns.insert(dc);
+ nd_queue.push_back(dc.from_node);
+ node_ids.insert(dc.from_node);
+ }
+ dc_keys.clear(); // Necessary as get_key_list does a push_back not a set.
+ nd_queue.pop_front();
}
}
- if (Object::cast_to<VisualScriptLocalVar>(node.ptr()) || Object::cast_to<VisualScriptLocalVarSet>(*node)) {
- //working memory is shared only for this node, for the same variables
- Ref<VisualScriptLocalVar> vslv = node;
-
- StringName var_name;
+ //Multiple passes are required to set up this complex thing..
+ //First create the nodes.
+ for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) {
+ Ref<VisualScriptNode> node = script->nodes[F->get()].node;
+
+ VisualScriptNodeInstance *instance = node->instantiate(this); // Create instance.
+ ERR_FAIL_COND(!instance);
+
+ instance->base = node.ptr();
+
+ instance->id = F->get();
+ instance->input_port_count = node->get_input_value_port_count();
+ instance->input_ports = nullptr;
+ instance->output_port_count = node->get_output_value_port_count();
+ instance->output_ports = nullptr;
+ instance->sequence_output_count = node->get_output_sequence_port_count();
+ instance->sequence_index = function.node_count++;
+ instance->sequence_outputs = nullptr;
+ instance->pass_idx = -1;
+
+ if (instance->input_port_count) {
+ instance->input_ports = memnew_arr(int, instance->input_port_count);
+ for (int i = 0; i < instance->input_port_count; i++) {
+ instance->input_ports[i] = -1; // If not assigned, will become default value.
+ }
+ }
- if (Object::cast_to<VisualScriptLocalVar>(*node)) {
- var_name = String(Object::cast_to<VisualScriptLocalVar>(*node)->get_var_name()).strip_edges();
- } else {
- var_name = String(Object::cast_to<VisualScriptLocalVarSet>(*node)->get_var_name()).strip_edges();
+ if (instance->output_port_count) {
+ instance->output_ports = memnew_arr(int, instance->output_port_count);
+ for (int i = 0; i < instance->output_port_count; i++) {
+ instance->output_ports[i] = -1; // If not assigned, will output to trash.
+ }
}
- if (!local_var_indices.has(var_name)) {
- local_var_indices[var_name] = function.max_stack;
- function.max_stack++;
+ if (instance->sequence_output_count) {
+ instance->sequence_outputs = memnew_arr(VisualScriptNodeInstance *, instance->sequence_output_count);
+ for (int i = 0; i < instance->sequence_output_count; i++) {
+ instance->sequence_outputs[i] = nullptr; // If it remains null, flow ends here.
+ }
}
- instance->working_mem_idx = local_var_indices[var_name];
+ if (Object::cast_to<VisualScriptLocalVar>(node.ptr()) || Object::cast_to<VisualScriptLocalVarSet>(*node)) {
+ // Working memory is shared only for this node, for the same variables.
+ Ref<VisualScriptLocalVar> vslv = node;
- } else if (instance->get_working_memory_size()) {
- instance->working_mem_idx = function.max_stack;
- function.max_stack += instance->get_working_memory_size();
- } else {
- instance->working_mem_idx = -1; //no working mem
- }
+ StringName var_name;
- max_input_args = MAX(max_input_args, instance->input_port_count);
- max_output_args = MAX(max_output_args, instance->output_port_count);
+ if (Object::cast_to<VisualScriptLocalVar>(*node)) {
+ var_name = String(Object::cast_to<VisualScriptLocalVar>(*node)->get_var_name()).strip_edges();
+ } else {
+ var_name = String(Object::cast_to<VisualScriptLocalVarSet>(*node)->get_var_name()).strip_edges();
+ }
- instances[F->key()] = instance;
- }
+ if (!local_var_indices.has(var_name)) {
+ local_var_indices[var_name] = function.max_stack;
+ function.max_stack++;
+ }
- function.trash_pos = function.max_stack++; //create pos for trash
+ instance->working_mem_idx = local_var_indices[var_name];
- //second pass, do data connections
+ } else if (instance->get_working_memory_size()) {
+ instance->working_mem_idx = function.max_stack;
+ function.max_stack += instance->get_working_memory_size();
+ } else {
+ instance->working_mem_idx = -1; //no working mem
+ }
- for (const Set<VisualScript::DataConnection>::Element *F = E->get().data_connections.front(); F; F = F->next()) {
- VisualScript::DataConnection dc = F->get();
- ERR_CONTINUE(!instances.has(dc.from_node));
- VisualScriptNodeInstance *from = instances[dc.from_node];
- ERR_CONTINUE(!instances.has(dc.to_node));
- VisualScriptNodeInstance *to = instances[dc.to_node];
- ERR_CONTINUE(dc.from_port >= from->output_port_count);
- ERR_CONTINUE(dc.to_port >= to->input_port_count);
+ max_input_args = MAX(max_input_args, instance->input_port_count);
+ max_output_args = MAX(max_output_args, instance->output_port_count);
- if (from->output_ports[dc.from_port] == -1) {
- int stack_pos = function.max_stack++;
- from->output_ports[dc.from_port] = stack_pos;
+ instances[F->get()] = instance;
}
- if (from->get_sequence_output_count() == 0 && to->dependencies.find(from) == -1) {
- //if the node we are reading from has no output sequence, we must call step() before reading from it.
- if (from->pass_idx == -1) {
- from->pass_idx = function.pass_stack_size;
- function.pass_stack_size++;
+ function.trash_pos = function.max_stack++; // create pos for trash
+
+ // Second pass, do data connections.
+ for (const Set<VisualScript::DataConnection>::Element *F = dataconns.front(); F; F = F->next()) {
+ VisualScript::DataConnection dc = F->get();
+ ERR_CONTINUE(!instances.has(dc.from_node));
+ VisualScriptNodeInstance *from = instances[dc.from_node];
+ ERR_CONTINUE(!instances.has(dc.to_node));
+ VisualScriptNodeInstance *to = instances[dc.to_node];
+ ERR_CONTINUE(dc.from_port >= from->output_port_count);
+ ERR_CONTINUE(dc.to_port >= to->input_port_count);
+
+ if (from->output_ports[dc.from_port] == -1) {
+ int stack_pos = function.max_stack++;
+ from->output_ports[dc.from_port] = stack_pos;
}
- to->dependencies.push_back(from);
- }
-
- to->input_ports[dc.to_port] = from->output_ports[dc.from_port]; //read from wherever the stack is
- }
- //third pass, do sequence connections
+ if (from->get_sequence_output_count() == 0 && to->dependencies.find(from) == -1) {
+ // If the node we are reading from has no output sequence, we must call step() before reading from it.
+ if (from->pass_idx == -1) {
+ from->pass_idx = function.pass_stack_size;
+ function.pass_stack_size++;
+ }
+ to->dependencies.push_back(from);
+ }
- for (const Set<VisualScript::SequenceConnection>::Element *F = E->get().sequence_connections.front(); F; F = F->next()) {
- VisualScript::SequenceConnection sc = F->get();
- ERR_CONTINUE(!instances.has(sc.from_node));
- VisualScriptNodeInstance *from = instances[sc.from_node];
- ERR_CONTINUE(!instances.has(sc.to_node));
- VisualScriptNodeInstance *to = instances[sc.to_node];
- ERR_CONTINUE(sc.from_output >= from->sequence_output_count);
+ to->input_ports[dc.to_port] = from->output_ports[dc.from_port]; // Read from wherever the stack is.
+ }
- from->sequence_outputs[sc.from_output] = to;
- }
+ // Third pass, do sequence connections.
+ for (const Set<VisualScript::SequenceConnection>::Element *F = seqconns.front(); F; F = F->next()) {
+ VisualScript::SequenceConnection sc = F->get();
+ ERR_CONTINUE(!instances.has(sc.from_node));
+ VisualScriptNodeInstance *from = instances[sc.from_node];
+ ERR_CONTINUE(!instances.has(sc.to_node));
+ VisualScriptNodeInstance *to = instances[sc.to_node];
+ ERR_CONTINUE(sc.from_output >= from->sequence_output_count);
- //fourth pass:
- // 1) unassigned input ports to default values
- // 2) connect unassigned output ports to trash
+ from->sequence_outputs[sc.from_output] = to;
+ }
- for (const Map<int, VisualScript::Function::NodeData>::Element *F = E->get().nodes.front(); F; F = F->next()) {
- ERR_CONTINUE(!instances.has(F->key()));
+ //fourth pass:
+ // 1) unassigned input ports to default values
+ // 2) connect unassigned output ports to trash
+ for (const Set<int>::Element *F = node_ids.front(); F; F = F->next()) {
+ ERR_CONTINUE(!instances.has(F->get()));
- Ref<VisualScriptNode> node = F->get().node;
- VisualScriptNodeInstance *instance = instances[F->key()];
+ Ref<VisualScriptNode> node = script->nodes[F->get()].node;
+ VisualScriptNodeInstance *instance = instances[F->get()];
- // connect to default values
- for (int i = 0; i < instance->input_port_count; i++) {
- if (instance->input_ports[i] == -1) {
- //unassigned, connect to default val
- instance->input_ports[i] = default_values.size() | VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT;
- default_values.push_back(node->get_default_input_value(i));
+ // Connect to default values.
+ for (int i = 0; i < instance->input_port_count; i++) {
+ if (instance->input_ports[i] == -1) {
+ // Unassigned, connect to default val.
+ instance->input_ports[i] = default_values.size() | VisualScriptNodeInstance::INPUT_DEFAULT_VALUE_BIT;
+ default_values.push_back(node->get_default_input_value(i));
+ }
}
- }
- // connect to trash
- for (int i = 0; i < instance->output_port_count; i++) {
- if (instance->output_ports[i] == -1) {
- instance->output_ports[i] = function.trash_pos; //trash is same for all
+ // Connect to trash.
+ for (int i = 0; i < instance->output_port_count; i++) {
+ if (instance->output_ports[i] == -1) {
+ instance->output_ports[i] = function.trash_pos; //trash is same for all
+ }
}
}
- }
- functions[E->key()] = function;
+ functions[E] = function;
+ }
}
}
@@ -2306,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);
}
}
@@ -2354,7 +2141,7 @@ Variant VisualScriptFunctionState::_signal_callback(const Variant **p_args, int
Variant *working_mem = ((Variant *)stack.ptr()) + working_mem_index;
- *working_mem = args; //arguments go to working mem.
+ *working_mem = args; // Arguments go to working mem.
Variant ret = instance->_call_internal(function, stack.ptrw(), stack.size(), node, flow_stack_pos, pass, true, r_error);
function = StringName(); //invalidate
@@ -2362,12 +2149,13 @@ Variant VisualScriptFunctionState::_signal_callback(const Variant **p_args, int
}
void VisualScriptFunctionState::connect_to_signal(Object *p_obj, const String &p_signal, Array p_binds) {
+ ERR_FAIL_NULL(p_obj);
Vector<Variant> binds;
for (int i = 0; i < p_binds.size(); i++) {
binds.push_back(p_binds[i]);
}
binds.push_back(Ref<VisualScriptFunctionState>(this)); //add myself on the back to avoid dying from unreferencing
- p_obj->connect_compat(p_signal, this, "_signal_callback", binds, CONNECT_ONESHOT);
+ p_obj->connect(p_signal, Callable(this, "_signal_callback"), binds, CONNECT_ONESHOT);
}
bool VisualScriptFunctionState::is_valid() const {
@@ -2388,7 +2176,7 @@ Variant VisualScriptFunctionState::resume(Array p_args) {
Variant *working_mem = ((Variant *)stack.ptr()) + working_mem_index;
- *working_mem = p_args; //arguments go to working mem.
+ *working_mem = p_args; // Arguments go to working mem.
Variant ret = instance->_call_internal(function, stack.ptrw(), stack.size(), node, flow_stack_pos, pass, true, r_error);
function = StringName(); //invalidate
@@ -2397,7 +2185,7 @@ Variant VisualScriptFunctionState::resume(Array p_args) {
void VisualScriptFunctionState::_bind_methods() {
ClassDB::bind_method(D_METHOD("connect_to_signal", "obj", "signals", "args"), &VisualScriptFunctionState::connect_to_signal);
- ClassDB::bind_method(D_METHOD("resume", "args"), &VisualScriptFunctionState::resume, DEFVAL(Variant()));
+ ClassDB::bind_method(D_METHOD("resume", "args"), &VisualScriptFunctionState::resume, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("is_valid"), &VisualScriptFunctionState::is_valid);
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "_signal_callback", &VisualScriptFunctionState::_signal_callback, MethodInfo("_signal_callback"));
}
@@ -2443,6 +2231,10 @@ void VisualScriptLanguage::finish() {
void VisualScriptLanguage::get_reserved_words(List<String> *p_words) const {
}
+bool VisualScriptLanguage::is_control_flow_keyword(String p_keyword) const {
+ return false;
+}
+
void VisualScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
}
@@ -2451,7 +2243,7 @@ void VisualScriptLanguage::get_string_delimiters(List<String> *p_delimiters) con
Ref<Script> VisualScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
Ref<VisualScript> script;
- script.instance();
+ script.instantiate();
script->set_instance_base_type(p_base_class_name);
return script;
}
@@ -2465,7 +2257,7 @@ void VisualScriptLanguage::make_template(const String &p_class_name, const Strin
script->set_instance_base_type(p_base_class_name);
}
-bool VisualScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_functions, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
+bool VisualScriptLanguage::validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors, List<ScriptLanguage::Warning> *r_warnings, Set<int> *r_safe_lines) const {
return false;
}
@@ -2498,7 +2290,7 @@ void VisualScriptLanguage::add_global_constant(const StringName &p_variable, con
/* DEBUGGER FUNCTIONS */
bool VisualScriptLanguage::debug_break_parse(const String &p_file, int p_node, const String &p_error) {
- //break because of parse error
+ // Break because of parse error.
if (EngineDebugger::is_active() && Thread::get_caller_id() == Thread::get_main_id()) {
_debug_parse_err_node = p_node;
@@ -2578,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);
@@ -2624,22 +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) {
@@ -2657,17 +2432,17 @@ void VisualScriptLanguage::debug_get_stack_level_members(int p_level, List<Strin
List<StringName> vars;
vs->get_variable_list(&vars);
- for (List<StringName>::Element *E = vars.front(); E; E = E->next()) {
+ for (const StringName &E : vars) {
Variant v;
- if (_call_stack[l].instance->get_variable(E->get(), &v)) {
- p_members->push_back("variables/" + E->get());
+ if (_call_stack[l].instance->get_variable(E, &v)) {
+ p_members->push_back("variables/" + E);
p_values->push_back(v);
}
}
}
void VisualScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {
- //no globals are really reachable in gdscript
+ // No globals are really reachable in gdscript.
}
String VisualScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) {
@@ -2725,25 +2500,19 @@ 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);
}
}
VisualScriptLanguage::VisualScriptLanguage() {
- notification = "_notification";
- _step = "_step";
- _subcall = "_subcall";
singleton = this;
- _debug_parse_err_node = -1;
- _debug_parse_err_file = "";
- _debug_call_stack_pos = 0;
int dmcs = GLOBAL_DEF("debug/settings/visual_script/max_call_stack", 1024);
ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/visual_script/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/visual_script/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024
if (EngineDebugger::is_active()) {
- //debugging enabled!
+ // Debugging enabled!
_debug_max_call_stack = dmcs;
_call_stack = memnew_arr(CallLevel, _debug_max_call_stack + 1);
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index cb5ed37ba1..39cef8f68b 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,8 +33,9 @@
#include "core/debugger/engine_debugger.h"
#include "core/debugger/script_debugger.h"
+#include "core/doc_data.h"
+#include "core/object/script_language.h"
#include "core/os/thread.h"
-#include "core/script_language.h"
class VisualScriptInstance;
class VisualScriptNodeInstance;
@@ -45,10 +46,10 @@ class VisualScriptNode : public Resource {
friend class VisualScript;
- Set<VisualScript *> scripts_used;
+ Ref<VisualScript> script_used;
Array default_input_values;
- bool breakpoint;
+ bool breakpoint = false;
void _set_default_input_values(Array p_values);
Array _get_default_input_values() const;
@@ -82,20 +83,16 @@ public:
virtual String get_text() const;
virtual String get_category() const = 0;
- //used by editor, this is not really saved
+ // Used by editor, this is not really saved.
void set_breakpoint(bool p_breakpoint);
bool is_breakpoint() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) = 0;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) = 0;
struct TypeGuess {
- Variant::Type type;
+ Variant::Type type = Variant::NIL;
StringName gdclass;
Ref<Script> script;
-
- TypeGuess() {
- type = Variant::NIL;
- }
};
virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const;
@@ -105,27 +102,27 @@ public:
class VisualScriptNodeInstance {
friend class VisualScriptInstance;
- friend class VisualScriptLanguage; //for debugger
+ friend class VisualScriptLanguage; // For debugger.
- enum { //input argument addressing
+ enum { // Input argument addressing.
INPUT_SHIFT = 1 << 24,
INPUT_MASK = INPUT_SHIFT - 1,
INPUT_DEFAULT_VALUE_BIT = INPUT_SHIFT, // from unassigned input port, using default value (edited by user)
};
- int id;
- int sequence_index;
- VisualScriptNodeInstance **sequence_outputs;
- int sequence_output_count;
+ int id = 0;
+ int sequence_index = 0;
+ VisualScriptNodeInstance **sequence_outputs = nullptr;
+ int sequence_output_count = 0;
Vector<VisualScriptNodeInstance *> dependencies;
- int *input_ports;
- int input_port_count;
- int *output_ports;
- int output_port_count;
- int working_mem_idx;
- int pass_idx;
+ int *input_ports = nullptr;
+ int input_port_count = 0;
+ int *output_ports = nullptr;
+ int output_port_count = 0;
+ int working_mem_idx = 0;
+ int pass_idx = 0;
- VisualScriptNode *base;
+ VisualScriptNode *base = nullptr;
public:
enum StartMode {
@@ -137,13 +134,13 @@ public:
enum {
STEP_SHIFT = 1 << 24,
STEP_MASK = STEP_SHIFT - 1,
- STEP_FLAG_PUSH_STACK_BIT = STEP_SHIFT, //push bit to stack
- STEP_FLAG_GO_BACK_BIT = STEP_SHIFT << 1, //go back to previous node
- STEP_NO_ADVANCE_BIT = STEP_SHIFT << 2, //do not advance past this node
- STEP_EXIT_FUNCTION_BIT = STEP_SHIFT << 3, //return from function
- STEP_YIELD_BIT = STEP_SHIFT << 4, //yield (will find VisualScriptFunctionState state in first working memory)
+ STEP_FLAG_PUSH_STACK_BIT = STEP_SHIFT, // push bit to stack
+ STEP_FLAG_GO_BACK_BIT = STEP_SHIFT << 1, // go back to previous node
+ STEP_NO_ADVANCE_BIT = STEP_SHIFT << 2, // do not advance past this node
+ STEP_EXIT_FUNCTION_BIT = STEP_SHIFT << 3, // return from function
+ STEP_YIELD_BIT = STEP_SHIFT << 4, // yield (will find VisualScriptFunctionState state in first working memory)
- FLOW_STACK_PUSHED_BIT = 1 << 30, //in flow stack, means bit was pushed (must go back here if end of sequence)
+ FLOW_STACK_PUSHED_BIT = 1 << 30, // in flow stack, means bit was pushed (must go back here if end of sequence)
FLOW_STACK_MASK = FLOW_STACK_PUSHED_BIT - 1
};
@@ -156,7 +153,7 @@ public:
virtual int get_working_memory_size() const { return 0; }
- virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) = 0; //do a step, return which sequence port to go out
+ virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) = 0; // Do a step, return which sequence port to go out.
Ref<VisualScriptNode> get_base_node() { return Ref<VisualScriptNode>(base); }
@@ -177,7 +174,7 @@ public:
uint64_t from_output : 16;
uint64_t to_node : 24;
};
- uint64_t id;
+ uint64_t id = 0;
};
bool operator<(const SequenceConnection &p_connection) const {
@@ -193,7 +190,7 @@ public:
uint64_t to_node : 24;
uint64_t to_port : 8;
};
- uint64_t id;
+ uint64_t id = 0;
};
bool operator<(const DataConnection &p_connection) const {
@@ -207,40 +204,37 @@ private:
StringName base_type;
struct Argument {
String name;
- Variant::Type type;
+ Variant::Type type = Variant::Type::NIL;
};
- struct Function {
- struct NodeData {
- Point2 pos;
- Ref<VisualScriptNode> node;
- };
-
- Map<int, NodeData> nodes;
-
- Set<SequenceConnection> sequence_connections;
+ struct NodeData {
+ Point2 pos;
+ Ref<VisualScriptNode> node;
+ };
- Set<DataConnection> data_connections;
+ HashMap<int, NodeData> nodes; // Can be a sparse map.
- int function_id;
+ Set<SequenceConnection> sequence_connections;
+ Set<DataConnection> data_connections;
- Vector2 scroll;
+ Vector2 scroll;
- Function() { function_id = -1; }
+ struct Function {
+ int func_id;
+ Function() { func_id = -1; }
};
struct Variable {
PropertyInfo info;
Variant default_value;
- bool _export;
- // add getter & setter options here
+ bool _export = false;
+ // Add getter & setter options here.
};
- Map<StringName, Function> functions;
- Map<StringName, Variable> variables;
+ HashMap<StringName, Function> functions;
+ HashMap<StringName, Variable> variables;
Map<StringName, Vector<Argument>> custom_signals;
- Vector<ScriptNetData> rpc_functions;
- Vector<ScriptNetData> rpc_variables;
+ Vector<Multiplayer::RPCConfig> rpc_functions;
Map<Object *, VisualScriptInstance *> instances;
@@ -248,7 +242,7 @@ private:
#ifdef TOOLS_ENABLED
Set<PlaceHolderScriptInstance *> placeholders;
- //void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
+ // void _update_placeholder(PlaceHolderScriptInstance *p_placeholder);
virtual void _placeholder_erased(PlaceHolderScriptInstance *p_placeholder) override;
void _update_placeholders();
#endif
@@ -266,37 +260,38 @@ protected:
public:
bool inherits_script(const Ref<Script> &p_script) const override;
- // TODO: Remove it in future when breaking changes are acceptable
- StringName get_default_func() const;
- void add_function(const StringName &p_name);
+ void set_scroll(const Vector2 &p_scroll);
+ Vector2 get_scroll() const;
+
+ void add_function(const StringName &p_name, int p_func_node_id);
bool has_function(const StringName &p_name) const;
void remove_function(const StringName &p_name);
void rename_function(const StringName &p_name, const StringName &p_new_name);
- void set_function_scroll(const StringName &p_name, const Vector2 &p_scroll);
- Vector2 get_function_scroll(const StringName &p_name) const;
void get_function_list(List<StringName> *r_functions) const;
int get_function_node_id(const StringName &p_name) const;
void set_tool_enabled(bool p_enabled);
- void add_node(const StringName &p_func, int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos = Point2());
- void remove_node(const StringName &p_func, int p_id);
- bool has_node(const StringName &p_func, int p_id) const;
- Ref<VisualScriptNode> get_node(const StringName &p_func, int p_id) const;
- void set_node_position(const StringName &p_func, int p_id, const Point2 &p_pos);
- Point2 get_node_position(const StringName &p_func, int p_id) const;
- void get_node_list(const StringName &p_func, List<int> *r_nodes) const;
-
- void sequence_connect(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node);
- void sequence_disconnect(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node);
- bool has_sequence_connection(const StringName &p_func, int p_from_node, int p_from_output, int p_to_node) const;
- void get_sequence_connection_list(const StringName &p_func, List<SequenceConnection> *r_connection) const;
-
- void data_connect(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
- void data_disconnect(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
- bool has_data_connection(const StringName &p_func, int p_from_node, int p_from_port, int p_to_node, int p_to_port) const;
- void get_data_connection_list(const StringName &p_func, List<DataConnection> *r_connection) const;
- bool is_input_value_port_connected(const StringName &p_func, int p_node, int p_port) const;
- bool get_input_value_port_connection_source(const StringName &p_func, int p_node, int p_port, int *r_node, int *r_port) const;
+ void add_node(int p_id, const Ref<VisualScriptNode> &p_node, const Point2 &p_pos = Point2());
+ void remove_node(int p_id);
+ bool has_node(int p_id) const;
+ Ref<VisualScriptNode> get_node(int p_id) const;
+ void set_node_position(int p_id, const Point2 &p_pos);
+ Point2 get_node_position(int p_id) const;
+ void get_node_list(List<int> *r_nodes) const;
+
+ void sequence_connect(int p_from_node, int p_from_output, int p_to_node);
+ void sequence_disconnect(int p_from_node, int p_from_output, int p_to_node);
+ bool has_sequence_connection(int p_from_node, int p_from_output, int p_to_node) const;
+ void get_sequence_connection_list(List<SequenceConnection> *r_connection) const;
+ Set<int> get_output_sequence_ports_connected(int from_node);
+
+ void data_connect(int p_from_node, int p_from_port, int p_to_node, int p_to_port);
+ void data_disconnect(int p_from_node, int p_from_port, int p_to_node, int p_to_port);
+ bool has_data_connection(int p_from_node, int p_from_port, int p_to_node, int p_to_port) const;
+ void get_data_connection_list(List<DataConnection> *r_connection) const;
+
+ bool is_input_value_port_connected(int p_node, int p_port) const;
+ bool get_input_value_port_connection_source(int p_node, int p_port, int *r_node, int *r_port) const;
void add_variable(const StringName &p_name, const Variant &p_default_value = Variant(), bool p_export = false);
bool has_variable(const StringName &p_name) const;
@@ -330,7 +325,7 @@ public:
void set_instance_base_type(const StringName &p_type);
- virtual bool can_instance() const override;
+ virtual bool can_instantiate() const override;
virtual Ref<Script> get_base_script() const override;
virtual StringName get_instance_base_type() const override;
@@ -342,6 +337,13 @@ public:
virtual void set_source_code(const String &p_code) override;
virtual Error reload(bool p_keep_state = false) override;
+#ifdef TOOLS_ENABLED
+ virtual const Vector<DocData::ClassDoc> &get_documentation() const override {
+ static Vector<DocData::ClassDoc> docs;
+ return docs;
+ }
+#endif // TOOLS_ENABLED
+
virtual bool is_tool() const override;
virtual bool is_valid() const override;
@@ -360,17 +362,7 @@ public:
virtual int get_member_line(const StringName &p_member) const override;
- virtual Vector<ScriptNetData> get_rpc_methods() const override;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const override;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const override;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const override;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const override;
-
- virtual Vector<ScriptNetData> get_rset_properties() const override;
- virtual uint16_t get_rset_property_id(const StringName &p_property) const override;
- virtual StringName get_rset_property(const uint16_t p_rset_property_id) const override;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const override;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const override;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const override;
#ifdef TOOLS_ENABLED
virtual bool are_subnodes_edited() const;
@@ -381,35 +373,35 @@ public:
};
class VisualScriptInstance : public ScriptInstance {
- Object *owner;
+ Object *owner = nullptr;
Ref<VisualScript> script;
- Map<StringName, Variant> variables; //using variable path, not script
+ Map<StringName, Variant> variables; // Using variable path, not script.
Map<int, VisualScriptNodeInstance *> instances;
struct Function {
- int node;
- int max_stack;
- int trash_pos;
- int flow_stack_size;
- int pass_stack_size;
- int node_count;
- int argument_count;
+ int node = 0;
+ int max_stack = 0;
+ int trash_pos = 0;
+ int flow_stack_size = 0;
+ int pass_stack_size = 0;
+ int node_count = 0;
+ int argument_count = 0;
};
Map<StringName, Function> functions;
Vector<Variant> default_values;
- int max_input_args, max_output_args;
+ int max_input_args = 0;
+ int max_output_args = 0;
StringName source;
void _dependency_step(VisualScriptNodeInstance *node, int p_pass, int *pass_stack, const Variant **input_args, Variant **output_args, Variant *variant_stack, Callable::CallError &r_error, String &error_str, VisualScriptNodeInstance **r_error_node);
Variant _call_internal(const StringName &p_method, void *p_stack, int p_stack_size, VisualScriptNodeInstance *p_node, int p_flow_stack_pos, int p_pass, bool p_resuming_yield, Callable::CallError &r_error);
- //Map<StringName,Function> functions;
- friend class VisualScriptFunctionState; //for yield
- friend class VisualScriptLanguage; //for debugger
+ friend class VisualScriptFunctionState; // For yield.
+ friend class VisualScriptLanguage; // For debugger.
public:
virtual bool set(const StringName &p_name, const Variant &p_value);
virtual bool get(const StringName &p_name, Variant &r_ret) const;
@@ -451,36 +443,26 @@ public:
virtual ScriptLanguage *get_language();
- virtual Vector<ScriptNetData> get_rpc_methods() const;
- virtual uint16_t get_rpc_method_id(const StringName &p_method) const;
- virtual StringName get_rpc_method(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rpc_mode(const StringName &p_method) const;
-
- virtual Vector<ScriptNetData> get_rset_properties() const;
- virtual uint16_t get_rset_property_id(const StringName &p_property) const;
- virtual StringName get_rset_property(const uint16_t p_rset_property_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode_by_id(const uint16_t p_rpc_method_id) const;
- virtual MultiplayerAPI::RPCMode get_rset_mode(const StringName &p_variable) const;
+ virtual const Vector<Multiplayer::RPCConfig> get_rpc_methods() const;
VisualScriptInstance();
~VisualScriptInstance();
};
-class VisualScriptFunctionState : public Reference {
- GDCLASS(VisualScriptFunctionState, Reference);
+class VisualScriptFunctionState : public RefCounted {
+ GDCLASS(VisualScriptFunctionState, RefCounted);
friend class VisualScriptInstance;
ObjectID instance_id;
ObjectID script_id;
- VisualScriptInstance *instance;
+ VisualScriptInstance *instance = nullptr;
StringName function;
Vector<uint8_t> stack;
- int working_mem_index;
- int variant_stack_size;
- VisualScriptNodeInstance *node;
- int flow_stack_pos;
- int pass;
+ int working_mem_index = 0;
+ int variant_stack_size = 0;
+ VisualScriptNodeInstance *node = nullptr;
+ int flow_stack_pos = 0;
+ int pass = 0;
Variant _signal_callback(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
@@ -501,25 +483,25 @@ class VisualScriptLanguage : public ScriptLanguage {
Map<String, VisualScriptNodeRegisterFunc> register_funcs;
struct CallLevel {
- Variant *stack;
- Variant **work_mem;
- const StringName *function;
- VisualScriptInstance *instance;
- int *current_id;
+ Variant *stack = nullptr;
+ Variant **work_mem = nullptr;
+ const StringName *function = nullptr;
+ VisualScriptInstance *instance = nullptr;
+ int *current_id = nullptr;
};
- int _debug_parse_err_node;
- String _debug_parse_err_file;
+ int _debug_parse_err_node = -1;
+ String _debug_parse_err_file = "";
String _debug_error;
- int _debug_call_stack_pos;
+ int _debug_call_stack_pos = 0;
int _debug_max_call_stack;
CallLevel *_call_stack;
public:
- StringName notification;
+ StringName notification = "_notification";
StringName _get_output_port_unsequenced;
- StringName _step;
- StringName _subcall;
+ StringName _step = "_step";
+ StringName _subcall = "_subcall";
static VisualScriptLanguage *singleton;
@@ -530,7 +512,7 @@ public:
_FORCE_INLINE_ void enter_function(VisualScriptInstance *p_instance, const StringName *p_function, Variant *p_stack, Variant **p_work_mem, int *current_id) {
if (Thread::get_main_id() != Thread::get_caller_id()) {
- return; //no support for other threads than main for now
+ return; // No support for other threads than main for now.
}
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0) {
@@ -538,7 +520,7 @@ public:
}
if (_debug_call_stack_pos >= _debug_max_call_stack) {
- //stack overflow
+ // Stack overflow.
_debug_error = "Stack Overflow (Stack Size: " + itos(_debug_max_call_stack) + ")";
EngineDebugger::get_script_debugger()->debug(this);
return;
@@ -554,7 +536,7 @@ public:
_FORCE_INLINE_ void exit_function() {
if (Thread::get_main_id() != Thread::get_caller_id()) {
- return; //no support for other threads than main for now
+ return; // No support for other threads than main for now.
}
if (EngineDebugger::get_script_debugger()->get_lines_left() > 0 && EngineDebugger::get_script_debugger()->get_depth() >= 0) {
@@ -583,12 +565,13 @@ public:
/* EDITOR FUNCTIONS */
virtual void get_reserved_words(List<String> *p_words) const;
+ virtual bool is_control_flow_keyword(String p_keyword) const;
virtual void get_comment_delimiters(List<String> *p_delimiters) const;
virtual void get_string_delimiters(List<String> *p_delimiters) const;
virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
virtual bool is_using_templates();
virtual void make_template(const String &p_class_name, const String &p_base_class_name, Ref<Script> &p_script);
- virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const;
+ virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, Set<int> *r_safe_lines = nullptr) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
virtual bool supports_builtin_mode() const;
@@ -632,11 +615,11 @@ public:
~VisualScriptLanguage();
};
-//aid for registering
+// Aid for registering.
template <class T>
static Ref<VisualScriptNode> create_node_generic(const String &p_name) {
Ref<T> node;
- node.instance();
+ node.instantiate();
return node;
}
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 177f9192b8..7ae85ea415 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,13 +30,12 @@
#include "visual_script_builtin_funcs.h"
-#include "core/class_db.h"
-#include "core/func_ref.h"
#include "core/io/marshalls.h"
#include "core/math/math_funcs.h"
+#include "core/object/class_db.h"
+#include "core/object/ref_counted.h"
#include "core/os/os.h"
-#include "core/reference.h"
-#include "core/variant_parser.h"
+#include "core/variant/variant_parser.h"
const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX] = {
"sin",
@@ -64,32 +63,31 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"is_inf",
"ease",
"step_decimals",
- "stepify",
+ "snapped",
"lerp",
"inverse_lerp",
"range_lerp",
"move_toward",
- "dectime",
"randomize",
"randi",
"randf",
- "rand_range",
+ "randi_range",
+ "randf_range",
+ "randfn",
"seed",
"rand_seed",
"deg2rad",
"rad2deg",
"linear2db",
"db2linear",
- "polar2cartesian",
- "cartesian2polar",
"wrapi",
"wrapf",
+ "pingpong",
"max",
"min",
"clamp",
"nearest_po2",
"weakref",
- "funcref",
"convert",
"typeof",
"type_exists",
@@ -98,11 +96,11 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX
"print",
"printerr",
"printraw",
+ "print_verbose",
"var2str",
"str2var",
"var2bytes",
"bytes2var",
- "color_named",
"smoothstep",
"posmod",
"lerp_angle",
@@ -134,6 +132,8 @@ 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:
return false;
@@ -143,7 +143,7 @@ bool VisualScriptBuiltinFunc::has_input_sequence_port() const {
int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) {
switch (p_func) {
case MATH_RANDOMIZE:
- case MATH_RAND:
+ case MATH_RANDI:
case MATH_RANDF:
return 0;
case MATH_SIN:
@@ -181,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:
@@ -191,24 +192,22 @@ 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_STEPIFY:
- case MATH_RANDOM:
- case MATH_POLAR2CARTESIAN:
- case MATH_CARTESIAN2POLAR:
+ case MATH_SNAPPED:
+ case MATH_RANDI_RANGE:
+ case MATH_RANDF_RANGE:
+ case MATH_RANDFN:
case LOGIC_MAX:
case LOGIC_MIN:
- case FUNC_FUNCREF:
case TYPE_CONVERT:
- case COLORN:
return 2;
case MATH_LERP:
case MATH_LERP_ANGLE:
case MATH_INVERSE_LERP:
case MATH_SMOOTHSTEP:
case MATH_MOVE_TOWARD:
- case MATH_DECTIME:
case MATH_WRAP:
case MATH_WRAPF:
case LOGIC_CLAMP:
@@ -231,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:
@@ -310,7 +310,7 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
case MATH_STEP_DECIMALS: {
return PropertyInfo(Variant::FLOAT, "step");
} break;
- case MATH_STEPIFY: {
+ case MATH_SNAPPED: {
if (p_idx == 0) {
return PropertyInfo(Variant::FLOAT, "s");
} else {
@@ -351,26 +351,31 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
return PropertyInfo(Variant::FLOAT, "delta");
}
} break;
- case MATH_DECTIME: {
+ case MATH_RANDOMIZE:
+ case MATH_RANDI:
+ case MATH_RANDF: {
+ } break;
+ case MATH_RANDI_RANGE: {
if (p_idx == 0) {
- return PropertyInfo(Variant::FLOAT, "value");
- } else if (p_idx == 1) {
- return PropertyInfo(Variant::FLOAT, "amount");
+ return PropertyInfo(Variant::INT, "from");
} else {
- return PropertyInfo(Variant::FLOAT, "step");
+ return PropertyInfo(Variant::INT, "to");
}
} break;
- case MATH_RANDOMIZE:
- case MATH_RAND:
- case MATH_RANDF: {
- } break;
- case MATH_RANDOM: {
+ case MATH_RANDF_RANGE: {
if (p_idx == 0) {
return PropertyInfo(Variant::FLOAT, "from");
} else {
return PropertyInfo(Variant::FLOAT, "to");
}
} break;
+ case MATH_RANDFN: {
+ if (p_idx == 0) {
+ return PropertyInfo(Variant::FLOAT, "mean");
+ } else {
+ return PropertyInfo(Variant::FLOAT, "deviation");
+ }
+ } break;
case MATH_SEED:
case MATH_RANDSEED: {
return PropertyInfo(Variant::INT, "seed");
@@ -387,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: {
@@ -426,13 +424,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
case OBJ_WEAKREF: {
return PropertyInfo(Variant::OBJECT, "source");
} break;
- case FUNC_FUNCREF: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::OBJECT, "instance");
- } else {
- return PropertyInfo(Variant::STRING, "funcname");
- }
- } break;
case TYPE_CONVERT: {
if (p_idx == 0) {
return PropertyInfo(Variant::NIL, "what");
@@ -455,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: {
@@ -477,13 +469,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const
return PropertyInfo(Variant::BOOL, "allow_objects");
}
} break;
- case COLORN: {
- if (p_idx == 0) {
- return PropertyInfo(Variant::STRING, "name");
- } else {
- return PropertyInfo(Variant::FLOAT, "alpha");
- }
- } break;
case FUNC_MAX: {
}
}
@@ -538,26 +523,26 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
case MATH_STEP_DECIMALS: {
t = Variant::INT;
} break;
- case MATH_STEPIFY:
+ case MATH_SNAPPED:
case MATH_LERP:
case MATH_LERP_ANGLE:
case MATH_INVERSE_LERP:
case MATH_RANGE_LERP:
case MATH_SMOOTHSTEP:
case MATH_MOVE_TOWARD:
- case MATH_DECTIME: {
- t = Variant::FLOAT;
-
- } break;
case MATH_RANDOMIZE: {
} break;
- case MATH_RAND: {
+ case MATH_RANDI: {
t = Variant::INT;
} break;
case MATH_RANDF:
- case MATH_RANDOM: {
+ case MATH_RANDFN:
+ case MATH_RANDF_RANGE: {
t = Variant::FLOAT;
} break;
+ case MATH_RANDI_RANGE: {
+ t = Variant::INT;
+ } break;
case MATH_SEED: {
} break;
case MATH_RANDSEED: {
@@ -571,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;
@@ -593,10 +575,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
t = Variant::OBJECT;
} break;
- case FUNC_FUNCREF: {
- t = Variant::OBJECT;
-
- } break;
case TYPE_CONVERT: {
} break;
case TEXT_ORD:
@@ -619,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;
@@ -637,9 +617,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
t = Variant::BOOL;
}
} break;
- case COLORN: {
- t = Variant::COLOR;
- } break;
case FUNC_MAX: {
}
}
@@ -649,7 +626,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons
/*
String VisualScriptBuiltinFunc::get_caption() const {
-
return "BuiltinFunc";
}
@@ -662,7 +638,7 @@ String VisualScriptBuiltinFunc::get_caption() const {
void VisualScriptBuiltinFunc::set_func(BuiltinFunc p_which) {
ERR_FAIL_INDEX(p_which, FUNC_MAX);
func = p_which;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -738,7 +714,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
case VisualScriptBuiltinFunc::MATH_POSMOD: {
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
- *r_return = Math::posmod((int)*p_inputs[0], (int)*p_inputs[1]);
+ *r_return = Math::posmod((int64_t)*p_inputs[0], (int64_t)*p_inputs[1]);
} break;
case VisualScriptBuiltinFunc::MATH_FLOOR: {
VALIDATE_ARG_NUM(0);
@@ -808,10 +784,10 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
VALIDATE_ARG_NUM(0);
*r_return = Math::step_decimals((double)*p_inputs[0]);
} break;
- case VisualScriptBuiltinFunc::MATH_STEPIFY: {
+ case VisualScriptBuiltinFunc::MATH_SNAPPED: {
VALIDATE_ARG_NUM(0);
VALIDATE_ARG_NUM(1);
- *r_return = Math::stepify((double)*p_inputs[0], (double)*p_inputs[1]);
+ *r_return = Math::snapped((double)*p_inputs[0], (double)*p_inputs[1]);
} break;
case VisualScriptBuiltinFunc::MATH_LERP: {
VALIDATE_ARG_NUM(0);
@@ -851,27 +827,31 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
VALIDATE_ARG_NUM(2);
*r_return = Math::move_toward((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
} break;
- case VisualScriptBuiltinFunc::MATH_DECTIME: {
- VALIDATE_ARG_NUM(0);
- VALIDATE_ARG_NUM(1);
- VALIDATE_ARG_NUM(2);
- *r_return = Math::dectime((double)*p_inputs[0], (double)*p_inputs[1], (double)*p_inputs[2]);
- } break;
case VisualScriptBuiltinFunc::MATH_RANDOMIZE: {
Math::randomize();
} break;
- case VisualScriptBuiltinFunc::MATH_RAND: {
+ case VisualScriptBuiltinFunc::MATH_RANDI: {
*r_return = Math::rand();
} break;
case VisualScriptBuiltinFunc::MATH_RANDF: {
*r_return = Math::randf();
} break;
- case VisualScriptBuiltinFunc::MATH_RANDOM: {
+ 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_RANDFN: {
+ VALIDATE_ARG_NUM(0);
+ VALIDATE_ARG_NUM(1);
+ *r_return = Math::randfn((double)*p_inputs[0], (double)*p_inputs[1]);
+ } break;
case VisualScriptBuiltinFunc::MATH_SEED: {
VALIDATE_ARG_NUM(0);
uint64_t seed = *p_inputs[0];
@@ -904,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: {
- 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: {
+ case VisualScriptBuiltinFunc::MATH_PINGPONG: {
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);
@@ -1013,30 +984,6 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
}
} break;
- case VisualScriptBuiltinFunc::FUNC_FUNCREF: {
- if (p_inputs[0]->get_type() != Variant::OBJECT) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 0;
- r_error.expected = Variant::OBJECT;
-
- return;
- }
- if (p_inputs[1]->get_type() != Variant::STRING && p_inputs[1]->get_type() != Variant::NODE_PATH) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = Variant::STRING;
-
- return;
- }
-
- Ref<FuncRef> fr = memnew(FuncRef);
-
- fr->set_instance(*p_inputs[0]);
- fr->set_function(*p_inputs[1]);
-
- *r_return = fr;
-
- } break;
case VisualScriptBuiltinFunc::TYPE_CONVERT: {
VALIDATE_ARG_NUM(1);
int type = *p_inputs[1];
@@ -1048,7 +995,7 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
return;
} else {
- *r_return = Variant::construct(Variant::Type(type), p_inputs, 1, r_error);
+ Variant::construct(Variant::Type(type), *r_return, p_inputs, 1, r_error);
}
} break;
case VisualScriptBuiltinFunc::TYPE_OF: {
@@ -1110,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);
@@ -1198,15 +1149,6 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in
*r_return = ret;
} break;
- case VisualScriptBuiltinFunc::COLORN: {
- VALIDATE_ARG_NUM(1);
-
- Color color = Color::named(*p_inputs[0]);
- color.a = *p_inputs[1];
-
- *r_return = String(color);
-
- } break;
default: {
}
}
@@ -1229,7 +1171,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptBuiltinFunc::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptBuiltinFunc::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceBuiltinFunc *instance = memnew(VisualScriptNodeInstanceBuiltinFunc);
instance->node = this;
instance->instance = p_instance;
@@ -1276,32 +1218,31 @@ void VisualScriptBuiltinFunc::_bind_methods() {
BIND_ENUM_CONSTANT(MATH_ISINF);
BIND_ENUM_CONSTANT(MATH_EASE);
BIND_ENUM_CONSTANT(MATH_STEP_DECIMALS);
- BIND_ENUM_CONSTANT(MATH_STEPIFY);
+ BIND_ENUM_CONSTANT(MATH_SNAPPED);
BIND_ENUM_CONSTANT(MATH_LERP);
BIND_ENUM_CONSTANT(MATH_INVERSE_LERP);
BIND_ENUM_CONSTANT(MATH_RANGE_LERP);
BIND_ENUM_CONSTANT(MATH_MOVE_TOWARD);
- BIND_ENUM_CONSTANT(MATH_DECTIME);
BIND_ENUM_CONSTANT(MATH_RANDOMIZE);
- BIND_ENUM_CONSTANT(MATH_RAND);
+ BIND_ENUM_CONSTANT(MATH_RANDI);
BIND_ENUM_CONSTANT(MATH_RANDF);
- BIND_ENUM_CONSTANT(MATH_RANDOM);
+ 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);
BIND_ENUM_CONSTANT(LOGIC_NEAREST_PO2);
BIND_ENUM_CONSTANT(OBJ_WEAKREF);
- BIND_ENUM_CONSTANT(FUNC_FUNCREF);
BIND_ENUM_CONSTANT(TYPE_CONVERT);
BIND_ENUM_CONSTANT(TYPE_OF);
BIND_ENUM_CONSTANT(TYPE_EXISTS);
@@ -1310,11 +1251,11 @@ 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);
BIND_ENUM_CONSTANT(BYTES_TO_VAR);
- BIND_ENUM_CONSTANT(COLORN);
BIND_ENUM_CONSTANT(MATH_SMOOTHSTEP);
BIND_ENUM_CONSTANT(MATH_POSMOD);
BIND_ENUM_CONSTANT(MATH_LERP_ANGLE);
@@ -1366,18 +1307,19 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/ease", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_EASE>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/step_decimals", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_STEP_DECIMALS>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/stepify", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_STEPIFY>);
+ VisualScriptLanguage::singleton->add_register_func("functions/built_in/snapped", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SNAPPED>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/lerp_angle", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LERP_ANGLE>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/inverse_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_INVERSE_LERP>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/range_lerp", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANGE_LERP>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/smoothstep", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_SMOOTHSTEP>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/move_toward", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_MOVE_TOWARD>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/dectime", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DECTIME>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/randomize", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOMIZE>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/rand", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAND>);
+ 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/random", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RANDOM>);
+ 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>);
@@ -1385,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>);
@@ -1396,7 +1337,6 @@ void register_visual_script_builtin_func_node() {
VisualScriptLanguage::singleton->add_register_func("functions/built_in/nearest_po2", create_builtin_func_node<VisualScriptBuiltinFunc::LOGIC_NEAREST_PO2>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/weakref", create_builtin_func_node<VisualScriptBuiltinFunc::OBJ_WEAKREF>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/funcref", create_builtin_func_node<VisualScriptBuiltinFunc::FUNC_FUNCREF>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/convert", create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_CONVERT>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/typeof", create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_OF>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/type_exists", create_builtin_func_node<VisualScriptBuiltinFunc::TYPE_EXISTS>);
@@ -1406,9 +1346,9 @@ 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>);
VisualScriptLanguage::singleton->add_register_func("functions/built_in/bytes2var", create_builtin_func_node<VisualScriptBuiltinFunc::BYTES_TO_VAR>);
- VisualScriptLanguage::singleton->add_register_func("functions/built_in/color_named", create_builtin_func_node<VisualScriptBuiltinFunc::COLORN>);
}
diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h
index aee2ed79ce..f71a053f7d 100644
--- a/modules/visual_script/visual_script_builtin_funcs.h
+++ b/modules/visual_script/visual_script_builtin_funcs.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -63,32 +63,31 @@ public:
MATH_ISINF,
MATH_EASE,
MATH_STEP_DECIMALS,
- MATH_STEPIFY,
+ MATH_SNAPPED,
MATH_LERP,
MATH_INVERSE_LERP,
MATH_RANGE_LERP,
MATH_MOVE_TOWARD,
- MATH_DECTIME,
MATH_RANDOMIZE,
- MATH_RAND,
+ MATH_RANDI,
MATH_RANDF,
- MATH_RANDOM,
+ 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,
LOGIC_NEAREST_PO2,
OBJ_WEAKREF,
- FUNC_FUNCREF,
TYPE_CONVERT,
TYPE_OF,
TYPE_EXISTS,
@@ -97,11 +96,11 @@ public:
TEXT_PRINT,
TEXT_PRINTERR,
TEXT_PRINTRAW,
+ TEXT_PRINT_VERBOSE,
VAR_TO_STR,
STR_TO_VAR,
VAR_TO_BYTES,
BYTES_TO_VAR,
- COLORN,
MATH_SMOOTHSTEP,
MATH_POSMOD,
MATH_LERP_ANGLE,
@@ -140,7 +139,7 @@ public:
void set_func(BuiltinFunc p_which);
BuiltinFunc get_func();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptBuiltinFunc(VisualScriptBuiltinFunc::BuiltinFunc func);
VisualScriptBuiltinFunc();
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index 60a439b291..55c707890f 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -63,7 +63,7 @@ bool VisualScriptExpression::_set(const StringName &p_name, const Variant &p_val
}
expression_dirty = true;
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
return true;
}
@@ -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"));
@@ -526,10 +526,10 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
r_token.value = Math_TAU;
} else if (id == "INF") {
r_token.type = TK_CONSTANT;
- r_token.value = Math_INF;
+ r_token.value = INFINITY;
} else if (id == "NAN") {
r_token.type = TK_CONSTANT;
- r_token.value = Math_NAN;
+ r_token.value = NAN;
} else if (id == "not") {
r_token.type = TK_OP_NOT;
} else if (id == "or") {
@@ -1054,7 +1054,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
}
}
- /* Reduce the set set of expressions and place them in an operator tree, respecting precedence */
+ /* Reduce the set of expressions and place them in an operator tree, respecting precedence */
while (expression.size() > 1) {
int next_op = -1;
@@ -1341,7 +1341,7 @@ public:
}
bool valid;
- r_ret = base.get_named(index->name, &valid);
+ r_ret = base.get_named(index->name, valid);
if (!valid) {
r_error_str = "Invalid index '" + String(index->name) + "' for base of type " + Variant::get_type_name(base.get_type()) + ".";
return true;
@@ -1405,7 +1405,7 @@ public:
argp.write[i] = &arr[i];
}
- r_ret = Variant::construct(constructor->data_type, (const Variant **)argp.ptr(), argp.size(), ce);
+ Variant::construct(constructor->data_type, r_ret, (const Variant **)argp.ptr(), argp.size(), ce);
if (ce.error != Callable::CallError::CALL_OK) {
r_error_str = "Invalid arguments to construct '" + Variant::get_type_name(constructor->data_type) + "'.";
@@ -1463,7 +1463,7 @@ public:
argp.write[i] = &arr[i];
}
- r_ret = base.call(call->method, (const Variant **)argp.ptr(), argp.size(), ce);
+ base.call(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
if (ce.error != Callable::CallError::CALL_OK) {
r_error_str = "On call to '" + String(call->method) + "':";
@@ -1498,7 +1498,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptExpression::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptExpression::instantiate(VisualScriptInstance *p_instance) {
_compile_expression();
VisualScriptNodeInstanceExpression *instance = memnew(VisualScriptNodeInstanceExpression);
instance->instance = p_instance;
@@ -1506,13 +1506,20 @@ VisualScriptNodeInstance *VisualScriptExpression::instance(VisualScriptInstance
return instance;
}
+void VisualScriptExpression::reset_state() {
+ if (nodes) {
+ memdelete(nodes);
+ nodes = nullptr;
+ root = nullptr;
+ }
+
+ error_str = String();
+ error_set = false;
+ str_ofs = 0;
+ inputs.clear();
+}
+
VisualScriptExpression::VisualScriptExpression() {
- output_type = Variant::NIL;
- expression_dirty = true;
- error_set = true;
- root = nullptr;
- nodes = nullptr;
- sequenced = false;
}
VisualScriptExpression::~VisualScriptExpression() {
diff --git a/modules/visual_script/visual_script_expression.h b/modules/visual_script/visual_script_expression.h
index 2b3b25842d..ef16222b42 100644
--- a/modules/visual_script/visual_script_expression.h
+++ b/modules/visual_script/visual_script_expression.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -39,20 +39,18 @@ class VisualScriptExpression : public VisualScriptNode {
friend class VisualScriptNodeInstanceExpression;
struct Input {
- Variant::Type type;
+ Variant::Type type = Variant::NIL;
String name;
-
- Input() { type = Variant::NIL; }
};
Vector<Input> inputs;
- Variant::Type output_type;
+ Variant::Type output_type = Variant::NIL;
String expression;
- bool sequenced;
- int str_ofs;
- bool expression_dirty;
+ bool sequenced = false;
+ int str_ofs = 0;
+ bool expression_dirty = true;
bool _compile_expression();
@@ -114,7 +112,7 @@ class VisualScriptExpression : public VisualScriptNode {
Error _get_token(Token &r_token);
String error_str;
- bool error_set;
+ bool error_set = true;
struct ENode {
enum Type {
@@ -131,11 +129,10 @@ class VisualScriptExpression : public VisualScriptNode {
TYPE_CALL
};
- ENode *next;
+ ENode *next = nullptr;
- Type type;
+ Type type = Type::TYPE_SELF;
- ENode() { next = nullptr; }
virtual ~ENode() {
if (next) {
memdelete(next);
@@ -144,17 +141,17 @@ class VisualScriptExpression : public VisualScriptNode {
};
struct Expression {
- bool is_op;
+ bool is_op = false;
union {
Variant::Operator op;
- ENode *node;
+ ENode *node = nullptr;
};
};
ENode *_parse_expression();
struct InputNode : public ENode {
- int index;
+ int index = 0;
InputNode() {
type = TYPE_INPUT;
}
@@ -168,9 +165,9 @@ class VisualScriptExpression : public VisualScriptNode {
};
struct OperatorNode : public ENode {
- Variant::Operator op;
+ Variant::Operator op = Variant::Operator::OP_ADD;
- ENode *nodes[2];
+ ENode *nodes[2] = { nullptr, nullptr };
OperatorNode() {
type = TYPE_OPERATOR;
@@ -184,8 +181,8 @@ class VisualScriptExpression : public VisualScriptNode {
};
struct IndexNode : public ENode {
- ENode *base;
- ENode *index;
+ ENode *base = nullptr;
+ ENode *index = nullptr;
IndexNode() {
type = TYPE_INDEX;
@@ -193,7 +190,7 @@ class VisualScriptExpression : public VisualScriptNode {
};
struct NamedIndexNode : public ENode {
- ENode *base;
+ ENode *base = nullptr;
StringName name;
NamedIndexNode() {
@@ -202,7 +199,7 @@ class VisualScriptExpression : public VisualScriptNode {
};
struct ConstructorNode : public ENode {
- Variant::Type data_type;
+ Variant::Type data_type = Variant::Type::NIL;
Vector<ENode *> arguments;
ConstructorNode() {
@@ -211,7 +208,7 @@ class VisualScriptExpression : public VisualScriptNode {
};
struct CallNode : public ENode {
- ENode *base;
+ ENode *base = nullptr;
StringName method;
Vector<ENode *> arguments;
@@ -235,7 +232,7 @@ class VisualScriptExpression : public VisualScriptNode {
};
struct BuiltinFuncNode : public ENode {
- VisualScriptBuiltinFunc::BuiltinFunc func;
+ VisualScriptBuiltinFunc::BuiltinFunc func = VisualScriptBuiltinFunc::BuiltinFunc::BYTES_TO_VAR;
Vector<ENode *> arguments;
BuiltinFuncNode() {
type = TYPE_BUILTIN_FUNC;
@@ -250,8 +247,8 @@ class VisualScriptExpression : public VisualScriptNode {
return node;
}
- ENode *root;
- ENode *nodes;
+ ENode *root = nullptr;
+ ENode *nodes = nullptr;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
@@ -259,6 +256,8 @@ protected:
void _get_property_list(List<PropertyInfo> *p_list) const;
public:
+ virtual void reset_state() override;
+
virtual int get_output_sequence_port_count() const override;
virtual bool has_input_sequence_port() const override;
@@ -274,7 +273,7 @@ public:
virtual String get_text() const override;
virtual String get_category() const override { return "operators"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptExpression();
~VisualScriptExpression();
diff --git a/modules/visual_script/visual_script_flow_control.cpp b/modules/visual_script/visual_script_flow_control.cpp
index 3ed20fab35..62a4f465cb 100644
--- a/modules/visual_script/visual_script_flow_control.cpp
+++ b/modules/visual_script/visual_script_flow_control.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,9 +30,9 @@
#include "visual_script_flow_control.h"
+#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
#include "core/os/keyboard.h"
-#include "core/project_settings.h"
//////////////////////////////////////////
////////////////RETURN////////////////////
@@ -138,7 +138,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptReturn::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptReturn::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceReturn *instance = memnew(VisualScriptNodeInstanceReturn);
instance->node = this;
instance->instance = p_instance;
@@ -154,7 +154,7 @@ VisualScriptReturn::VisualScriptReturn() {
template <bool with_value>
static Ref<VisualScriptNode> create_return_node(const String &p_name) {
Ref<VisualScriptReturn> node;
- node.instance();
+ node.instantiate();
node->set_enable_return_value(with_value);
return node;
}
@@ -231,7 +231,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptCondition::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptCondition::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceCondition *instance = memnew(VisualScriptNodeInstanceCondition);
instance->node = this;
instance->instance = p_instance;
@@ -311,7 +311,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptWhile::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptWhile::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceWhile *instance = memnew(VisualScriptNodeInstanceWhile);
instance->node = this;
instance->instance = p_instance;
@@ -435,7 +435,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptIterator::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptIterator::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceIterator *instance = memnew(VisualScriptNodeInstanceIterator);
instance->node = this;
instance->instance = p_instance;
@@ -534,7 +534,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptSequence::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptSequence::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceSequence *instance = memnew(VisualScriptNodeInstanceSequence);
instance->node = this;
instance->instance = p_instance;
@@ -618,7 +618,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptSwitch::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptSwitch::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceSwitch *instance = memnew(VisualScriptNodeInstanceSwitch);
instance->instance = p_instance;
instance->case_count = case_values.size();
@@ -628,7 +628,7 @@ VisualScriptNodeInstance *VisualScriptSwitch::instance(VisualScriptInstance *p_i
bool VisualScriptSwitch::_set(const StringName &p_name, const Variant &p_value) {
if (String(p_name) == "case_count") {
case_values.resize(p_value);
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
return true;
}
@@ -638,7 +638,7 @@ bool VisualScriptSwitch::_set(const StringName &p_name, const Variant &p_value)
ERR_FAIL_INDEX_V(idx, case_values.size(), false);
case_values.write[idx].type = Variant::Type(int(p_value));
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
return true;
@@ -677,6 +677,10 @@ void VisualScriptSwitch::_get_property_list(List<PropertyInfo> *p_list) const {
}
}
+void VisualScriptSwitch::reset_state() {
+ case_values.clear();
+}
+
void VisualScriptSwitch::_bind_methods() {
}
@@ -733,7 +737,7 @@ void VisualScriptTypeCast::set_base_type(const StringName &p_type) {
}
base_type = p_type;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -747,7 +751,7 @@ void VisualScriptTypeCast::set_base_script(const String &p_path) {
}
script = p_path;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -796,7 +800,7 @@ public:
}
if (!ResourceCache::has(script)) {
- //if the script is not in use by anyone, we can safely assume whathever we got is not casting to it.
+ //if the script is not in use by anyone, we can safely assume whatever we got is not casting to it.
return 1;
}
Ref<Script> cast_script = Ref<Resource>(ResourceCache::get(script));
@@ -827,7 +831,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptTypeCast::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptTypeCast::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceTypeCast *instance = memnew(VisualScriptNodeInstanceTypeCast);
instance->instance = p_instance;
instance->base_type = base_type;
@@ -848,11 +852,11 @@ void VisualScriptTypeCast::_bind_methods() {
}
String script_ext_hint;
- for (List<String>::Element *E = script_extensions.front(); E; E = E->next()) {
+ for (const String &E : script_extensions) {
if (script_ext_hint != String()) {
script_ext_hint += ",";
}
- script_ext_hint += "*." + E->get();
+ script_ext_hint += "*." + E;
}
ADD_PROPERTY(PropertyInfo(Variant::STRING, "base_type", PROPERTY_HINT_TYPE_STRING, "Object"), "set_base_type", "get_base_type");
diff --git a/modules/visual_script/visual_script_flow_control.h b/modules/visual_script/visual_script_flow_control.h
index 1d0d6d103b..73822fcc37 100644
--- a/modules/visual_script/visual_script_flow_control.h
+++ b/modules/visual_script/visual_script_flow_control.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -64,7 +64,7 @@ public:
void set_enable_return_value(bool p_enable);
bool is_return_value_enabled() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptReturn();
};
@@ -91,7 +91,7 @@ public:
virtual String get_text() const override;
virtual String get_category() const override { return "flow_control"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptCondition();
};
@@ -118,7 +118,7 @@ public:
virtual String get_text() const override;
virtual String get_category() const override { return "flow_control"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptWhile();
};
@@ -145,7 +145,7 @@ public:
virtual String get_text() const override;
virtual String get_category() const override { return "flow_control"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptIterator();
};
@@ -177,7 +177,7 @@ public:
void set_steps(int p_steps);
int get_steps() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptSequence();
};
@@ -202,6 +202,8 @@ protected:
static void _bind_methods();
public:
+ virtual void reset_state() override;
+
virtual int get_output_sequence_port_count() const override;
virtual bool has_input_sequence_port() const override;
@@ -218,7 +220,7 @@ public:
virtual String get_text() const override;
virtual String get_category() const override { return "flow_control"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptSwitch();
};
@@ -256,7 +258,7 @@ public:
virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptTypeCast();
};
diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp
index f13377f3c8..a2ad38bf01 100644
--- a/modules/visual_script/visual_script_func_nodes.cpp
+++ b/modules/visual_script/visual_script_func_nodes.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "visual_script_func_nodes.h"
-#include "core/engine.h"
+#include "core/config/engine.h"
#include "core/io/resource_loader.h"
#include "core/os/os.h"
#include "scene/main/node.h"
@@ -42,7 +42,7 @@
//////////////////////////////////////////
int VisualScriptFunctionCall::get_output_sequence_port_count() const {
- if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function))) {
+ if ((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_builtin_method_const(basic_type, function))) {
return 0;
} else {
return 1;
@@ -50,7 +50,7 @@ int VisualScriptFunctionCall::get_output_sequence_port_count() const {
}
bool VisualScriptFunctionCall::has_input_sequence_port() const {
- return !((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_method_const(basic_type, function)));
+ return !((method_cache.flags & METHOD_FLAG_CONST && call_mode != CALL_MODE_INSTANCE) || (call_mode == CALL_MODE_BASIC_TYPE && Variant::is_builtin_method_const(basic_type, function)));
}
#ifdef TOOLS_ENABLED
@@ -130,7 +130,11 @@ StringName VisualScriptFunctionCall::_get_base_type() const {
int VisualScriptFunctionCall::get_input_value_port_count() const {
if (call_mode == CALL_MODE_BASIC_TYPE) {
- Vector<Variant::Type> types = Variant::get_method_argument_types(basic_type, function);
+ Vector<Variant::Type> types;
+ int argc = Variant::get_builtin_method_argument_count(basic_type, function);
+ for (int i = 0; i < argc; i++) {
+ types.push_back(Variant::get_builtin_method_argument_type(basic_type, function, i));
+ }
return types.size() + (rpc_call_mode >= RPC_RELIABLE_TO_ID ? 1 : 0) + 1;
} else {
@@ -147,8 +151,7 @@ int VisualScriptFunctionCall::get_input_value_port_count() const {
int VisualScriptFunctionCall::get_output_value_port_count() const {
if (call_mode == CALL_MODE_BASIC_TYPE) {
- bool returns = false;
- Variant::get_method_return_type(basic_type, function, &returns);
+ bool returns = Variant::has_builtin_method_return_value(basic_type, function);
return returns ? 1 : 0;
} else {
@@ -195,10 +198,7 @@ PropertyInfo VisualScriptFunctionCall::get_input_value_port_info(int p_idx) cons
#ifdef DEBUG_METHODS_ENABLED
if (call_mode == CALL_MODE_BASIC_TYPE) {
- Vector<StringName> names = Variant::get_method_argument_names(basic_type, function);
- Vector<Variant::Type> types = Variant::get_method_argument_types(basic_type, function);
- return PropertyInfo(types[p_idx], names[p_idx]);
-
+ return PropertyInfo(Variant::get_builtin_method_argument_type(basic_type, function, p_idx), Variant::get_builtin_method_argument_name(basic_type, function, p_idx));
} else {
MethodBind *mb = ClassDB::get_method(_get_base_type(), function);
if (mb) {
@@ -220,7 +220,7 @@ PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) con
#ifdef DEBUG_METHODS_ENABLED
if (call_mode == CALL_MODE_BASIC_TYPE) {
- return PropertyInfo(Variant::get_method_return_type(basic_type, function), "");
+ return PropertyInfo(Variant::get_builtin_method_return_type(basic_type, function), "");
} else {
if (call_mode == CALL_MODE_INSTANCE) {
if (p_idx == 0) {
@@ -234,7 +234,6 @@ PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) con
/*MethodBind *mb = ClassDB::get_method(_get_base_type(),function);
if (mb) {
-
ret = mb->get_argument_info(-1);
} else {*/
@@ -255,25 +254,32 @@ PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) con
}
String VisualScriptFunctionCall::get_caption() const {
- if (call_mode == CALL_MODE_SELF) {
- return " " + String(function) + "()";
- }
- if (call_mode == CALL_MODE_SINGLETON) {
- return String(singleton) + ":" + String(function) + "()";
- } else if (call_mode == CALL_MODE_BASIC_TYPE) {
- return Variant::get_type_name(basic_type) + "." + String(function) + "()";
- } else if (call_mode == CALL_MODE_NODE_PATH) {
- return " [" + String(base_path.simplified()) + "]." + String(function) + "()";
- } else {
- return " " + base_type + "." + String(function) + "()";
- }
+ return " " + String(function) + "()";
}
String VisualScriptFunctionCall::get_text() const {
+ String text;
+
+ if (call_mode == CALL_MODE_BASIC_TYPE) {
+ text = String("On ") + Variant::get_type_name(basic_type);
+ } else if (call_mode == CALL_MODE_INSTANCE) {
+ text = String("On ") + base_type;
+ } else if (call_mode == CALL_MODE_NODE_PATH) {
+ text = "[" + String(base_path.simplified()) + "]";
+ } else if (call_mode == CALL_MODE_SELF) {
+ text = "On Self";
+ } else if (call_mode == CALL_MODE_SINGLETON) {
+ text = String(singleton) + ":" + String(function) + "()";
+ }
+
if (rpc_call_mode) {
- return "RPC";
+ text += " RPC";
+ if (rpc_call_mode == RPC_UNRELIABLE || rpc_call_mode == RPC_UNRELIABLE_TO_ID) {
+ text += " UNREL";
+ }
}
- return "";
+
+ return text;
}
void VisualScriptFunctionCall::set_basic_type(Variant::Type p_type) {
@@ -282,7 +288,7 @@ void VisualScriptFunctionCall::set_basic_type(Variant::Type p_type) {
}
basic_type = p_type;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -296,7 +302,7 @@ void VisualScriptFunctionCall::set_base_type(const StringName &p_type) {
}
base_type = p_type;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -310,7 +316,7 @@ void VisualScriptFunctionCall::set_base_script(const String &p_path) {
}
base_script = p_path;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -329,7 +335,7 @@ void VisualScriptFunctionCall::set_singleton(const StringName &p_type) {
base_type = obj->get_class();
}
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -419,14 +425,14 @@ void VisualScriptFunctionCall::set_function(const StringName &p_type) {
function = p_type;
if (call_mode == CALL_MODE_BASIC_TYPE) {
- use_default_args = Variant::get_method_default_arguments(basic_type, function).size();
+ use_default_args = Variant::get_builtin_method_default_arguments(basic_type, function).size();
} else {
//update all caches
_update_method_cache();
}
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -440,7 +446,7 @@ void VisualScriptFunctionCall::set_base_path(const NodePath &p_type) {
}
base_path = p_type;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -454,7 +460,7 @@ void VisualScriptFunctionCall::set_call_mode(CallMode p_mode) {
}
call_mode = p_mode;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -477,7 +483,7 @@ void VisualScriptFunctionCall::set_rpc_call_mode(VisualScriptFunctionCall::RPCCa
}
rpc_call_mode = p_mode;
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
}
VisualScriptFunctionCall::RPCCallMode VisualScriptFunctionCall::get_rpc_call_mode() const {
@@ -508,35 +514,35 @@ 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;
}
}
if (property.name == "base_script") {
if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
if (property.name == "basic_type") {
if (call_mode != CALL_MODE_BASIC_TYPE) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
if (property.name == "singleton") {
if (call_mode != CALL_MODE_SINGLETON) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
} else {
List<Engine::Singleton> names;
Engine::get_singleton()->get_singletons(&names);
property.hint = PROPERTY_HINT_ENUM;
String sl;
- for (List<Engine::Singleton>::Element *E = names.front(); E; E = E->next()) {
+ for (const Engine::Singleton &E : names) {
if (sl != String()) {
sl += ",";
}
- sl += E->get().name;
+ sl += E.name;
}
property.hint_string = sl;
}
@@ -544,11 +550,11 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
if (property.name == "node_path") {
if (call_mode != CALL_MODE_NODE_PATH) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
} else {
Node *bnode = _get_base_node();
if (bnode) {
- property.hint_string = bnode->get_path(); //convert to loong string
+ property.hint_string = bnode->get_path(); //convert to long string
}
}
}
@@ -606,7 +612,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
int mc = 0;
if (call_mode == CALL_MODE_BASIC_TYPE) {
- mc = Variant::get_method_default_arguments(basic_type, function).size();
+ mc = Variant::get_builtin_method_default_arguments(basic_type, function).size();
} else {
MethodBind *mb = ClassDB::get_method(_get_base_type(), function);
if (mb) {
@@ -615,7 +621,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
}
if (mc == 0) {
- property.usage = 0; //do not show
+ property.usage = PROPERTY_USAGE_NONE; //do not show
} else {
property.hint_string = "0," + itos(mc) + ",1";
}
@@ -623,7 +629,7 @@ void VisualScriptFunctionCall::_validate_property(PropertyInfo &property) const
if (property.name == "rpc_call_mode") {
if (call_mode == CALL_MODE_BASIC_TYPE) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
}
@@ -677,11 +683,11 @@ void VisualScriptFunctionCall::_bind_methods() {
}
String script_ext_hint;
- for (List<String>::Element *E = script_extensions.front(); E; E = E->next()) {
+ for (const String &E : script_extensions) {
if (script_ext_hint != String()) {
script_ext_hint += ",";
}
- script_ext_hint += "*." + E->get();
+ script_ext_hint += "*." + E;
}
ADD_PROPERTY(PropertyInfo(Variant::INT, "call_mode", PROPERTY_HINT_ENUM, "Self,Node Path,Instance,Basic Type,Singleton"), "set_call_mode", "get_call_mode");
@@ -690,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");
@@ -738,20 +744,22 @@ public:
}
int to_id = 0;
- bool reliable = true;
+ //bool reliable = true;
if (rpc_mode >= VisualScriptFunctionCall::RPC_RELIABLE_TO_ID) {
to_id = *p_args[0];
p_args += 1;
p_argcount -= 1;
- if (rpc_mode == VisualScriptFunctionCall::RPC_UNRELIABLE_TO_ID) {
- reliable = false;
- }
- } else if (rpc_mode == VisualScriptFunctionCall::RPC_UNRELIABLE) {
- reliable = false;
+ //if (rpc_mode == VisualScriptFunctionCall::RPC_UNRELIABLE_TO_ID) {
+ //reliable = false;
+ //}
}
+ //else if (rpc_mode == VisualScriptFunctionCall::RPC_UNRELIABLE) {
+ //reliable = false;
+ //}
- node->rpcp(to_id, !reliable, function, p_args, p_argcount);
+ // TODO reliable?
+ node->rpcp(to_id, function, p_args, p_argcount);
return true;
}
@@ -805,19 +813,21 @@ public:
} else if (returns) {
if (call_mode == VisualScriptFunctionCall::CALL_MODE_INSTANCE) {
if (returns >= 2) {
- *p_outputs[1] = v.call(function, p_inputs + 1, input_args, r_error);
+ v.call(function, p_inputs + 1, input_args, *p_outputs[1], r_error);
} else if (returns == 1) {
- v.call(function, p_inputs + 1, input_args, r_error);
+ Variant ret;
+ v.call(function, p_inputs + 1, input_args, ret, r_error);
} else {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
r_error_str = "Invalid returns count for call_mode == CALL_MODE_INSTANCE";
return 0;
}
} else {
- *p_outputs[0] = v.call(function, p_inputs + 1, input_args, r_error);
+ v.call(function, p_inputs + 1, input_args, *p_outputs[0], r_error);
}
} else {
- v.call(function, p_inputs + 1, input_args, r_error);
+ Variant ret;
+ v.call(function, p_inputs + 1, input_args, ret, r_error);
}
if (call_mode == VisualScriptFunctionCall::CALL_MODE_INSTANCE) {
@@ -853,7 +863,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptFunctionCall::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptFunctionCall::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceFunctionCall *instance = memnew(VisualScriptNodeInstanceFunctionCall);
instance->node = this;
instance->instance = p_instance;
@@ -888,7 +898,7 @@ VisualScriptFunctionCall::VisualScriptFunctionCall() {
template <VisualScriptFunctionCall::CallMode cmode>
static Ref<VisualScriptNode> create_function_call_node(const String &p_name) {
Ref<VisualScriptFunctionCall> node;
- node.instance();
+ node.instantiate();
node->set_call_mode(cmode);
return node;
}
@@ -898,11 +908,11 @@ static Ref<VisualScriptNode> create_function_call_node(const String &p_name) {
//////////////////////////////////////////
int VisualScriptPropertySet::get_output_sequence_port_count() const {
- return call_mode != CALL_MODE_BASIC_TYPE ? 1 : 0;
+ return 1;
}
bool VisualScriptPropertySet::has_input_sequence_port() const {
- return call_mode != CALL_MODE_BASIC_TYPE;
+ return 1;
}
Node *VisualScriptPropertySet::_get_base_node() const {
@@ -976,7 +986,7 @@ void VisualScriptPropertySet::_adjust_input_index(PropertyInfo &pinfo) const {
if (index != StringName()) {
Variant v;
Callable::CallError ce;
- v = Variant::construct(pinfo.type, nullptr, 0, ce);
+ Variant::construct(pinfo.type, v, nullptr, 0, ce);
Variant i = v.get(index);
pinfo.type = i.get_type();
}
@@ -987,31 +997,33 @@ PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const
if (p_idx == 0) {
PropertyInfo pi;
pi.type = (call_mode == CALL_MODE_INSTANCE ? Variant::OBJECT : basic_type);
- pi.name = (call_mode == CALL_MODE_INSTANCE ? String("instance") : Variant::get_type_name(basic_type).to_lower());
- _adjust_input_index(pi);
+ pi.name = "instance";
return pi;
}
}
List<PropertyInfo> props;
ClassDB::get_property_list(_get_base_type(), &props, false);
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- if (E->get().name == property) {
- PropertyInfo pinfo = PropertyInfo(E->get().type, "value", PROPERTY_HINT_TYPE_STRING, E->get().hint_string);
+ for (const PropertyInfo &E : props) {
+ if (E.name == property) {
+ String detail_prop_name = property;
+ if (index != StringName()) {
+ detail_prop_name += "." + String(index);
+ }
+ PropertyInfo pinfo = PropertyInfo(E.type, detail_prop_name, E.hint, E.hint_string);
_adjust_input_index(pinfo);
return pinfo;
}
}
PropertyInfo pinfo = type_cache;
- pinfo.name = "value";
_adjust_input_index(pinfo);
return pinfo;
}
PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) const {
if (call_mode == CALL_MODE_BASIC_TYPE) {
- return PropertyInfo(basic_type, "out");
+ return PropertyInfo(basic_type, "pass");
} else if (call_mode == CALL_MODE_INSTANCE) {
return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type());
} else {
@@ -1033,17 +1045,18 @@ String VisualScriptPropertySet::get_caption() const {
}
String VisualScriptPropertySet::get_text() const {
+ if (!has_input_sequence_port()) {
+ return "";
+ }
if (call_mode == CALL_MODE_BASIC_TYPE) {
return String("On ") + Variant::get_type_name(basic_type);
+ } else if (call_mode == CALL_MODE_INSTANCE) {
+ return String("On ") + base_type;
+ } else if (call_mode == CALL_MODE_NODE_PATH) {
+ return " [" + String(base_path.simplified()) + "]";
+ } else {
+ return "On Self";
}
-
- static const char *cname[3] = {
- "Self",
- "Scene Node",
- "Instance"
- };
-
- return String("On ") + cname[call_mode];
}
void VisualScriptPropertySet::_update_base_type() {
@@ -1066,7 +1079,7 @@ void VisualScriptPropertySet::set_basic_type(Variant::Type p_type) {
}
basic_type = p_type;
- _change_notify();
+ notify_property_list_changed();
_update_base_type();
ports_changed_notify();
}
@@ -1081,7 +1094,7 @@ void VisualScriptPropertySet::set_base_type(const StringName &p_type) {
}
base_type = p_type;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1095,7 +1108,7 @@ void VisualScriptPropertySet::set_base_script(const String &p_path) {
}
base_script = p_path;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1117,14 +1130,14 @@ void VisualScriptPropertySet::_update_cache() {
Variant v;
Callable::CallError ce;
- v = Variant::construct(basic_type, nullptr, 0, ce);
+ Variant::construct(basic_type, v, nullptr, 0, ce);
List<PropertyInfo> pinfo;
v.get_property_list(&pinfo);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().name == property) {
- type_cache = E->get();
+ for (const PropertyInfo &E : pinfo) {
+ if (E.name == property) {
+ type_cache = E;
}
}
@@ -1173,9 +1186,9 @@ void VisualScriptPropertySet::_update_cache() {
script->get_script_property_list(&pinfo);
}
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().name == property) {
- type_cache = E->get();
+ for (const PropertyInfo &E : pinfo) {
+ if (E.name == property) {
+ type_cache = E;
return;
}
}
@@ -1190,7 +1203,7 @@ void VisualScriptPropertySet::set_property(const StringName &p_type) {
property = p_type;
index = StringName();
_update_cache();
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1205,7 +1218,7 @@ void VisualScriptPropertySet::set_base_path(const NodePath &p_type) {
base_path = p_type;
_update_base_type();
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1220,7 +1233,7 @@ void VisualScriptPropertySet::set_call_mode(CallMode p_mode) {
call_mode = p_mode;
_update_base_type();
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1242,7 +1255,7 @@ void VisualScriptPropertySet::set_index(const StringName &p_type) {
}
index = p_type;
_update_cache();
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1258,7 +1271,7 @@ void VisualScriptPropertySet::set_assign_op(AssignOp p_op) {
assign_op = p_op;
_update_cache();
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1269,29 +1282,29 @@ 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;
}
}
if (property.name == "base_script") {
if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
if (property.name == "basic_type") {
if (call_mode != CALL_MODE_BASIC_TYPE) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
if (property.name == "node_path") {
if (call_mode != CALL_MODE_NODE_PATH) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
} else {
Node *bnode = _get_base_node();
if (bnode) {
- property.hint_string = bnode->get_path(); //convert to loong string
+ property.hint_string = bnode->get_path(); //convert to long string
}
}
}
@@ -1336,19 +1349,20 @@ void VisualScriptPropertySet::_validate_property(PropertyInfo &property) const {
if (property.name == "index") {
Callable::CallError ce;
- Variant v = Variant::construct(type_cache.type, nullptr, 0, ce);
+ Variant v;
+ Variant::construct(type_cache.type, v, nullptr, 0, ce);
List<PropertyInfo> plist;
v.get_property_list(&plist);
String options = "";
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- options += "," + E->get().name;
+ for (const PropertyInfo &E : plist) {
+ options += "," + E.name;
}
property.hint = PROPERTY_HINT_ENUM;
property.hint_string = options;
property.type = Variant::STRING;
if (options == "") {
- property.usage = 0; //hide if type has no usable index
+ property.usage = PROPERTY_USAGE_NONE; //hide if type has no usable index
}
}
}
@@ -1396,17 +1410,17 @@ void VisualScriptPropertySet::_bind_methods() {
}
String script_ext_hint;
- for (List<String>::Element *E = script_extensions.front(); E; E = E->next()) {
+ for (const String &E : script_extensions) {
if (script_ext_hint != String()) {
script_ext_hint += ",";
}
- script_ext_hint += "*." + E->get();
+ script_ext_hint += "*." + E;
}
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");
@@ -1449,11 +1463,11 @@ public:
_FORCE_INLINE_ void _process_get(Variant &source, const Variant &p_argument, bool &valid) {
if (index != StringName() && assign_op == VisualScriptPropertySet::ASSIGN_OP_NONE) {
- source.set_named(index, p_argument, &valid);
+ source.set_named(index, p_argument, valid);
} else {
Variant value;
if (index != StringName()) {
- value = source.get_named(index, &valid);
+ value = source.get_named(index, valid);
} else {
value = source;
}
@@ -1497,7 +1511,7 @@ public:
}
if (index != StringName()) {
- source.set_named(index, value, &valid);
+ source.set_named(index, value, valid);
} else {
source = value;
}
@@ -1562,12 +1576,12 @@ public:
bool valid;
if (needs_get) {
- Variant value = v.get_named(property, &valid);
+ Variant value = v.get_named(property, valid);
_process_get(value, *p_inputs[1], valid);
- v.set_named(property, value, &valid);
+ v.set_named(property, value, valid);
} else {
- v.set_named(property, *p_inputs[1], &valid);
+ v.set_named(property, *p_inputs[1], valid);
}
if (!valid) {
@@ -1583,7 +1597,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptPropertySet::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptPropertySet::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstancePropertySet *instance = memnew(VisualScriptNodeInstancePropertySet);
instance->node = this;
instance->instance = p_instance;
@@ -1614,7 +1628,7 @@ VisualScriptPropertySet::VisualScriptPropertySet() {
template <VisualScriptPropertySet::CallMode cmode>
static Ref<VisualScriptNode> create_property_set_node(const String &p_name) {
Ref<VisualScriptPropertySet> node;
- node.instance();
+ node.instantiate();
node->set_call_mode(cmode);
return node;
}
@@ -1703,7 +1717,9 @@ int VisualScriptPropertyGet::get_input_value_port_count() const {
}
int VisualScriptPropertyGet::get_output_value_port_count() const {
- return 1;
+ int pc = (call_mode == CALL_MODE_BASIC_TYPE || call_mode == CALL_MODE_INSTANCE) ? 2 : 1;
+
+ return pc;
}
String VisualScriptPropertyGet::get_output_sequence_port_text(int p_port) const {
@@ -1723,33 +1739,46 @@ PropertyInfo VisualScriptPropertyGet::get_input_value_port_info(int p_idx) const
}
PropertyInfo VisualScriptPropertyGet::get_output_value_port_info(int p_idx) const {
- List<PropertyInfo> props;
- ClassDB::get_property_list(_get_base_type(), &props, false);
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- if (E->get().name == property) {
- return PropertyInfo(E->get().type, "value." + String(index));
+ if (call_mode == CALL_MODE_BASIC_TYPE && p_idx == 0) {
+ return PropertyInfo(basic_type, "pass");
+ } else if (call_mode == CALL_MODE_INSTANCE && p_idx == 0) {
+ return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type());
+ } else {
+ List<PropertyInfo> props;
+ ClassDB::get_property_list(_get_base_type(), &props, false);
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ if (E->get().name == property) {
+ PropertyInfo pinfo = PropertyInfo(E->get().type, String(property) + "." + String(index), E->get().hint, E->get().hint_string);
+ _adjust_input_index(pinfo);
+ return pinfo;
+ }
}
}
- return PropertyInfo(type_cache, "value");
+ PropertyInfo pinfo = PropertyInfo(type_cache, "value");
+ _adjust_input_index(pinfo);
+ return pinfo;
}
String VisualScriptPropertyGet::get_caption() const {
- return String("Get ") + property;
+ String prop = String("Get ") + property;
+ if (index != StringName()) {
+ prop += "." + String(index);
+ }
+
+ return prop;
}
String VisualScriptPropertyGet::get_text() const {
if (call_mode == CALL_MODE_BASIC_TYPE) {
return String("On ") + Variant::get_type_name(basic_type);
+ } else if (call_mode == CALL_MODE_INSTANCE) {
+ return String("On ") + base_type;
+ } else if (call_mode == CALL_MODE_NODE_PATH) {
+ return " [" + String(base_path.simplified()) + "]";
+ } else {
+ return "On Self";
}
-
- static const char *cname[3] = {
- "Self",
- "Scene Node",
- "Instance"
- };
-
- return String("On ") + cname[call_mode];
}
void VisualScriptPropertyGet::set_base_type(const StringName &p_type) {
@@ -1758,7 +1787,7 @@ void VisualScriptPropertyGet::set_base_type(const StringName &p_type) {
}
base_type = p_type;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1772,7 +1801,7 @@ void VisualScriptPropertyGet::set_base_script(const String &p_path) {
}
base_script = p_path;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1786,14 +1815,14 @@ void VisualScriptPropertyGet::_update_cache() {
Variant v;
Callable::CallError ce;
- v = Variant::construct(basic_type, nullptr, 0, ce);
+ Variant::construct(basic_type, v, nullptr, 0, ce);
List<PropertyInfo> pinfo;
v.get_property_list(&pinfo);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- if (E->get().name == property) {
- type_cache = E->get().type;
+ for (const PropertyInfo &E : pinfo) {
+ if (E.name == property) {
+ type_cache = E.type;
return;
}
}
@@ -1869,7 +1898,7 @@ void VisualScriptPropertyGet::set_property(const StringName &p_type) {
property = p_type;
_update_cache();
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1883,7 +1912,7 @@ void VisualScriptPropertyGet::set_base_path(const NodePath &p_type) {
}
base_path = p_type;
- _change_notify();
+ notify_property_list_changed();
_update_base_type();
ports_changed_notify();
}
@@ -1898,7 +1927,7 @@ void VisualScriptPropertyGet::set_call_mode(CallMode p_mode) {
}
call_mode = p_mode;
- _change_notify();
+ notify_property_list_changed();
_update_base_type();
ports_changed_notify();
}
@@ -1913,7 +1942,7 @@ void VisualScriptPropertyGet::set_basic_type(Variant::Type p_type) {
}
basic_type = p_type;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1929,13 +1958,26 @@ Variant::Type VisualScriptPropertyGet::_get_type_cache() const {
return type_cache;
}
+void VisualScriptPropertyGet::_adjust_input_index(PropertyInfo &pinfo) const {
+ if (index != StringName()) {
+ Variant v;
+ Callable::CallError ce;
+ Variant::construct(pinfo.type, v, nullptr, 0, ce);
+ Variant i = v.get(index);
+ pinfo.type = i.get_type();
+ pinfo.name = String(property) + "." + index;
+ } else {
+ pinfo.name = String(property);
+ }
+}
+
void VisualScriptPropertyGet::set_index(const StringName &p_type) {
if (index == p_type) {
return;
}
index = p_type;
_update_cache();
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1946,29 +1988,29 @@ 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;
}
}
if (property.name == "base_script") {
if (call_mode != CALL_MODE_INSTANCE) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
if (property.name == "basic_type") {
if (call_mode != CALL_MODE_BASIC_TYPE) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
if (property.name == "node_path") {
if (call_mode != CALL_MODE_NODE_PATH) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
} else {
Node *bnode = _get_base_node();
if (bnode) {
- property.hint_string = bnode->get_path(); //convert to loong string
+ property.hint_string = bnode->get_path(); //convert to long string
}
}
}
@@ -2012,19 +2054,20 @@ void VisualScriptPropertyGet::_validate_property(PropertyInfo &property) const {
if (property.name == "index") {
Callable::CallError ce;
- Variant v = Variant::construct(type_cache, nullptr, 0, ce);
+ Variant v;
+ Variant::construct(type_cache, v, nullptr, 0, ce);
List<PropertyInfo> plist;
v.get_property_list(&plist);
String options = "";
- for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) {
- options += "," + E->get().name;
+ for (const PropertyInfo &E : plist) {
+ options += "," + E.name;
}
property.hint = PROPERTY_HINT_ENUM;
property.hint_string = options;
property.type = Variant::STRING;
if (options == "") {
- property.usage = 0; //hide if type has no usable index
+ property.usage = PROPERTY_USAGE_NONE; //hide if type has no usable index
}
}
}
@@ -2069,17 +2112,17 @@ void VisualScriptPropertyGet::_bind_methods() {
}
String script_ext_hint;
- for (List<String>::Element *E = script_extensions.front(); E; E = E->next()) {
+ for (const String &E : script_extensions) {
if (script_ext_hint != String()) {
script_ext_hint += ",";
}
- script_ext_hint += "." + E->get();
+ script_ext_hint += "." + E;
}
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");
@@ -2088,6 +2131,7 @@ void VisualScriptPropertyGet::_bind_methods() {
BIND_ENUM_CONSTANT(CALL_MODE_SELF);
BIND_ENUM_CONSTANT(CALL_MODE_NODE_PATH);
BIND_ENUM_CONSTANT(CALL_MODE_INSTANCE);
+ BIND_ENUM_CONSTANT(CALL_MODE_BASIC_TYPE);
}
class VisualScriptNodeInstancePropertyGet : public VisualScriptNodeInstance {
@@ -2110,7 +2154,7 @@ public:
*p_outputs[0] = object->get(property, &valid);
if (index != StringName()) {
- *p_outputs[0] = p_outputs[0]->get_named(index);
+ *p_outputs[0] = p_outputs[0]->get_named(index, valid);
}
if (!valid) {
@@ -2139,7 +2183,7 @@ public:
*p_outputs[0] = another->get(property, &valid);
if (index != StringName()) {
- *p_outputs[0] = p_outputs[0]->get_named(index);
+ *p_outputs[0] = p_outputs[0]->get_named(index, valid);
}
if (!valid) {
@@ -2153,15 +2197,17 @@ public:
bool valid;
Variant v = *p_inputs[0];
- *p_outputs[0] = v.get(property, &valid);
+ *p_outputs[1] = v.get(property, &valid);
if (index != StringName()) {
- *p_outputs[0] = p_outputs[0]->get_named(index);
+ *p_outputs[1] = p_outputs[1]->get_named(index, valid);
}
if (!valid) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
r_error_str = RTR("Invalid index property name.");
}
+
+ *p_outputs[0] = v;
};
}
@@ -2169,7 +2215,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptPropertyGet::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptPropertyGet::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstancePropertyGet *instance = memnew(VisualScriptNodeInstancePropertyGet);
instance->node = this;
instance->instance = p_instance;
@@ -2191,7 +2237,7 @@ VisualScriptPropertyGet::VisualScriptPropertyGet() {
template <VisualScriptPropertyGet::CallMode cmode>
static Ref<VisualScriptNode> create_property_get_node(const String &p_name) {
Ref<VisualScriptPropertyGet> node;
- node.instance();
+ node.instantiate();
node->set_call_mode(cmode);
return node;
}
@@ -2257,7 +2303,7 @@ void VisualScriptEmitSignal::set_signal(const StringName &p_type) {
name = p_type;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -2277,11 +2323,11 @@ void VisualScriptEmitSignal::_validate_property(PropertyInfo &property) const {
}
String ml;
- for (List<StringName>::Element *E = sigs.front(); E; E = E->next()) {
+ for (const StringName &E : sigs) {
if (ml != String()) {
ml += ",";
}
- ml += E->get();
+ ml += E;
}
property.hint_string = ml;
@@ -2315,7 +2361,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptEmitSignal::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptEmitSignal::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceEmitSignal *instance = memnew(VisualScriptNodeInstanceEmitSignal);
instance->node = this;
instance->instance = p_instance;
@@ -2334,7 +2380,7 @@ static Ref<VisualScriptNode> create_basic_type_call_node(const String &p_name) {
String method = path[3];
Ref<VisualScriptFunctionCall> node;
- node.instance();
+ node.instantiate();
Variant::Type type = Variant::VARIANT_MAX;
@@ -2367,12 +2413,13 @@ void register_visual_script_func_nodes() {
Variant::Type t = Variant::Type(i);
String type_name = Variant::get_type_name(t);
Callable::CallError ce;
- Variant vt = Variant::construct(t, nullptr, 0, ce);
+ Variant vt;
+ Variant::construct(t, vt, nullptr, 0, ce);
List<MethodInfo> ml;
vt.get_method_list(&ml);
- for (List<MethodInfo>::Element *E = ml.front(); E; E = E->next()) {
- VisualScriptLanguage::singleton->add_register_func("functions/by_type/" + type_name + "/" + E->get().name, create_basic_type_call_node);
+ for (const MethodInfo &E : ml) {
+ VisualScriptLanguage::singleton->add_register_func("functions/by_type/" + type_name + "/" + E.name, create_basic_type_call_node);
}
}
}
diff --git a/modules/visual_script/visual_script_func_nodes.h b/modules/visual_script/visual_script_func_nodes.h
index 8372df561f..cca08455f9 100644
--- a/modules/visual_script/visual_script_func_nodes.h
+++ b/modules/visual_script/visual_script_func_nodes.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -125,7 +125,7 @@ public:
void set_rpc_call_mode(RPCCallMode p_mode);
RPCCallMode get_rpc_call_mode() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
@@ -231,7 +231,7 @@ public:
void set_assign_op(AssignOp p_op);
AssignOp get_assign_op() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
VisualScriptPropertySet();
@@ -272,6 +272,8 @@ private:
void _set_type_cache(Variant::Type p_type);
Variant::Type _get_type_cache() const;
+ void _adjust_input_index(PropertyInfo &pinfo) const;
+
protected:
virtual void _validate_property(PropertyInfo &property) const override;
@@ -314,7 +316,7 @@ public:
void set_index(const StringName &p_type);
StringName get_index() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptPropertyGet();
};
@@ -351,7 +353,7 @@ public:
void set_signal(const StringName &p_type);
StringName get_signal() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptEmitSignal();
};
diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp
index 28122ade99..471d8ef0ae 100644
--- a/modules/visual_script/visual_script_nodes.cpp
+++ b/modules/visual_script/visual_script_nodes.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,11 +30,11 @@
#include "visual_script_nodes.h"
-#include "core/engine.h"
-#include "core/global_constants.h"
+#include "core/config/engine.h"
+#include "core/config/project_settings.h"
+#include "core/core_constants.h"
#include "core/input/input.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
@@ -57,7 +57,7 @@ bool VisualScriptFunction::_set(const StringName &p_name, const Variant &p_value
arguments.write[i].type = Variant::NIL;
}
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
return true;
}
if (String(p_name).begins_with("argument_")) {
@@ -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;
}
@@ -163,7 +163,7 @@ void VisualScriptFunction::_get_property_list(List<PropertyInfo> *p_list) const
p_list->push_back(PropertyInfo(Variant::INT, "stack/size", PROPERTY_HINT_RANGE, "1,100000"));
}
p_list->push_back(PropertyInfo(Variant::BOOL, "stack/stackless"));
- p_list->push_back(PropertyInfo(Variant::INT, "rpc/mode", PROPERTY_HINT_ENUM, "Disabled,Remote,Master,Puppet,Remote Sync,Master Sync,Puppet Sync"));
+ p_list->push_back(PropertyInfo(Variant::INT, "rpc/mode", PROPERTY_HINT_ENUM, "Disabled,Any,Authority"));
}
int VisualScriptFunction::get_output_sequence_port_count() const {
@@ -191,7 +191,10 @@ PropertyInfo VisualScriptFunction::get_input_value_port_info(int p_idx) const {
}
PropertyInfo VisualScriptFunction::get_output_value_port_info(int p_idx) const {
- ERR_FAIL_INDEX_V(p_idx, arguments.size(), PropertyInfo());
+ // Need to check it without ERR_FAIL_COND, to prevent warnings from appearing on node creation via dragging.
+ if (p_idx < 0 || p_idx >= arguments.size()) {
+ return PropertyInfo();
+ }
PropertyInfo out;
out.type = arguments[p_idx].type;
out.name = arguments[p_idx].name;
@@ -258,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;
}
@@ -296,23 +299,31 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptFunction::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptFunction::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceFunction *instance = memnew(VisualScriptNodeInstanceFunction);
instance->node = this;
instance->instance = p_instance;
return instance;
}
+void VisualScriptFunction::reset_state() {
+ arguments.clear();
+ stack_size = 256;
+ stack_less = false;
+ sequenced = true;
+ 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) {
stack_less = p_enable;
- _change_notify();
+ notify_property_list_changed();
}
bool VisualScriptFunction::is_stack_less() const {
@@ -421,7 +432,7 @@ bool VisualScriptLists::_set(const StringName &p_name, const Variant &p_value) {
inputports.write[i].type = Variant::NIL;
}
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
return true;
}
if (String(p_name).begins_with("input_") && is_input_port_editable()) {
@@ -457,7 +468,7 @@ bool VisualScriptLists::_set(const StringName &p_name, const Variant &p_value) {
outputports.write[i].type = Variant::NIL;
}
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
return true;
}
if (String(p_name).begins_with("output_") && is_output_port_editable()) {
@@ -578,7 +589,7 @@ void VisualScriptLists::add_input_data_port(Variant::Type p_type, const String &
}
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
}
void VisualScriptLists::set_input_data_port_type(int p_idx, Variant::Type p_type) {
@@ -590,7 +601,7 @@ void VisualScriptLists::set_input_data_port_type(int p_idx, Variant::Type p_type
inputports.write[p_idx].type = p_type;
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
}
void VisualScriptLists::set_input_data_port_name(int p_idx, const String &p_name) {
@@ -602,7 +613,7 @@ void VisualScriptLists::set_input_data_port_name(int p_idx, const String &p_name
inputports.write[p_idx].name = p_name;
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
}
void VisualScriptLists::remove_input_data_port(int p_argidx) {
@@ -615,7 +626,7 @@ void VisualScriptLists::remove_input_data_port(int p_argidx) {
inputports.remove(p_argidx);
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
}
// output data port interaction
@@ -634,7 +645,7 @@ void VisualScriptLists::add_output_data_port(Variant::Type p_type, const String
}
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
}
void VisualScriptLists::set_output_data_port_type(int p_idx, Variant::Type p_type) {
@@ -646,7 +657,7 @@ void VisualScriptLists::set_output_data_port_type(int p_idx, Variant::Type p_typ
outputports.write[p_idx].type = p_type;
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
}
void VisualScriptLists::set_output_data_port_name(int p_idx, const String &p_name) {
@@ -658,7 +669,7 @@ void VisualScriptLists::set_output_data_port_name(int p_idx, const String &p_nam
outputports.write[p_idx].name = p_name;
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
}
void VisualScriptLists::remove_output_data_port(int p_argidx) {
@@ -671,7 +682,7 @@ void VisualScriptLists::remove_output_data_port(int p_argidx) {
outputports.remove(p_argidx);
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
}
// sequences
@@ -687,6 +698,13 @@ bool VisualScriptLists::is_sequenced() const {
return sequenced;
}
+void VisualScriptLists::reset_state() {
+ inputports.clear();
+ outputports.clear();
+ sequenced = false;
+ flags = 0;
+}
+
VisualScriptLists::VisualScriptLists() {
// initialize
sequenced = false;
@@ -776,7 +794,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptComposeArray::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptComposeArray::instantiate(VisualScriptInstance *p_instance) {
VisualScriptComposeArrayNode *instance = memnew(VisualScriptComposeArrayNode);
instance->input_count = inputports.size();
return instance;
@@ -828,7 +846,6 @@ PropertyInfo VisualScriptOperator::get_input_value_port_info(int p_idx) const {
{ Variant::NIL, Variant::NIL }, //OP_NEGATE,
{ Variant::NIL, Variant::NIL }, //OP_POSITIVE,
{ Variant::INT, Variant::INT }, //OP_MODULE,
- { Variant::STRING, Variant::STRING }, //OP_STRING_CONCAT,
//bitwise
{ Variant::INT, Variant::INT }, //OP_SHIFT_LEFT,
{ Variant::INT, Variant::INT }, //OP_SHIFT_RIGHT,
@@ -873,7 +890,6 @@ PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const {
Variant::NIL, //OP_NEGATE,
Variant::NIL, //OP_POSITIVE,
Variant::INT, //OP_MODULE,
- Variant::STRING, //OP_STRING_CONCAT,
//bitwise
Variant::INT, //OP_SHIFT_LEFT,
Variant::INT, //OP_SHIFT_RIGHT,
@@ -899,73 +915,135 @@ PropertyInfo VisualScriptOperator::get_output_value_port_info(int p_idx) const {
return pinfo;
}
-static const char *op_names[] = {
- //comparison
- "Are Equal", //OP_EQUAL,
- "Are Not Equal", //OP_NOT_EQUAL,
- "Less Than", //OP_LESS,
- "Less Than or Equal", //OP_LESS_EQUAL,
- "Greater Than", //OP_GREATER,
- "Greater Than or Equal", //OP_GREATER_EQUAL,
- //mathematic
- "Add", //OP_ADD,
- "Subtract", //OP_SUBTRACT,
- "Multiply", //OP_MULTIPLY,
- "Divide", //OP_DIVIDE,
- "Negate", //OP_NEGATE,
- "Positive", //OP_POSITIVE,
- "Remainder", //OP_MODULE,
- "Concatenate", //OP_STRING_CONCAT,
- //bitwise
- "Bit Shift Left", //OP_SHIFT_LEFT,
- "Bit Shift Right", //OP_SHIFT_RIGHT,
- "Bit And", //OP_BIT_AND,
- "Bit Or", //OP_BIT_OR,
- "Bit Xor", //OP_BIT_XOR,
- "Bit Negate", //OP_BIT_NEGATE,
- //logic
- "And", //OP_AND,
- "Or", //OP_OR,
- "Xor", //OP_XOR,
- "Not", //OP_NOT,
- //containment
- "In", //OP_IN,
-};
-
String VisualScriptOperator::get_caption() const {
- static const char32_t *op_names[] = {
- //comparison
- U"A = B", //OP_EQUAL,
- U"A \u2260 B", //OP_NOT_EQUAL,
- U"A < B", //OP_LESS,
- U"A \u2264 B", //OP_LESS_EQUAL,
- U"A > B", //OP_GREATER,
- U"A \u2265 B", //OP_GREATER_EQUAL,
- //mathematic
- U"A + B", //OP_ADD,
- U"A - B", //OP_SUBTRACT,
- U"A \u00D7 B", //OP_MULTIPLY,
- U"A \u00F7 B", //OP_DIVIDE,
- U"\u00AC A", //OP_NEGATE,
- U"+ A", //OP_POSITIVE,
- U"A mod B", //OP_MODULE,
- U"A .. B", //OP_STRING_CONCAT,
- //bitwise
- U"A << B", //OP_SHIFT_LEFT,
- U"A >> B", //OP_SHIFT_RIGHT,
- U"A & B", //OP_BIT_AND,
- U"A | B", //OP_BIT_OR,
- U"A ^ B", //OP_BIT_XOR,
- U"~A", //OP_BIT_NEGATE,
- //logic
- U"A and B", //OP_AND,
- U"A or B", //OP_OR,
- U"A xor B", //OP_XOR,
- U"not A", //OP_NOT,
- U"A in B", //OP_IN,
-
- };
- return op_names[op];
+ switch (op) {
+ // comparison
+ case Variant::OP_EQUAL:
+ return U"A = B";
+ case Variant::OP_NOT_EQUAL:
+ return U"A \u2260 B";
+ case Variant::OP_LESS:
+ return U"A < B";
+ case Variant::OP_LESS_EQUAL:
+ return U"A \u2264 B";
+ case Variant::OP_GREATER:
+ return U"A > B";
+ case Variant::OP_GREATER_EQUAL:
+ return U"A \u2265 B";
+
+ // mathematic
+ case Variant::OP_ADD:
+ return U"A + B";
+ case Variant::OP_SUBTRACT:
+ return U"A - B";
+ case Variant::OP_MULTIPLY:
+ return U"A \u00D7 B";
+ case Variant::OP_DIVIDE:
+ return U"A \u00F7 B";
+ case Variant::OP_NEGATE:
+ return U"\u00AC A";
+ case Variant::OP_POSITIVE:
+ return U"+ A";
+ case Variant::OP_MODULE:
+ return U"A mod B";
+
+ // bitwise
+ case Variant::OP_SHIFT_LEFT:
+ return U"A << B";
+ case Variant::OP_SHIFT_RIGHT:
+ return U"A >> B";
+ case Variant::OP_BIT_AND:
+ return U"A & B";
+ case Variant::OP_BIT_OR:
+ return U"A | B";
+ case Variant::OP_BIT_XOR:
+ return U"A ^ B";
+ case Variant::OP_BIT_NEGATE:
+ return U"~A";
+
+ // logic
+ case Variant::OP_AND:
+ return U"A and B";
+ case Variant::OP_OR:
+ return U"A or B";
+ case Variant::OP_XOR:
+ return U"A xor B";
+ case Variant::OP_NOT:
+ return U"not A";
+ case Variant::OP_IN:
+ return U"A in B";
+
+ default: {
+ ERR_FAIL_V_MSG(
+ U"Unknown node",
+ U"Unknown node type encountered, caption not available.");
+ }
+ }
+}
+
+String VisualScriptOperator::get_operator_name(Variant::Operator p_op) {
+ switch (p_op) {
+ // comparison
+ case Variant::OP_EQUAL:
+ return "Are Equal";
+ case Variant::OP_NOT_EQUAL:
+ return "Are Not Equal";
+ case Variant::OP_LESS:
+ return "Less Than";
+ case Variant::OP_LESS_EQUAL:
+ return "Less Than or Equal";
+ case Variant::OP_GREATER:
+ return "Greater Than";
+ case Variant::OP_GREATER_EQUAL:
+ return "Greater Than or Equal";
+
+ // mathematic
+ case Variant::OP_ADD:
+ return "Add";
+ case Variant::OP_SUBTRACT:
+ return "Subtract";
+ case Variant::OP_MULTIPLY:
+ return "Multiply";
+ case Variant::OP_DIVIDE:
+ return "Divide";
+ case Variant::OP_NEGATE:
+ return "Negate";
+ case Variant::OP_POSITIVE:
+ return "Positive";
+ case Variant::OP_MODULE:
+ return "Remainder";
+
+ // bitwise
+ case Variant::OP_SHIFT_LEFT:
+ return "Bit Shift Left";
+ case Variant::OP_SHIFT_RIGHT:
+ return "Bit Shift Right";
+ case Variant::OP_BIT_AND:
+ return "Bit And";
+ case Variant::OP_BIT_OR:
+ return "Bit Or";
+ case Variant::OP_BIT_XOR:
+ return "Bit Xor";
+ case Variant::OP_BIT_NEGATE:
+ return "Bit Negate";
+
+ // logic
+ case Variant::OP_AND:
+ return "And";
+ case Variant::OP_OR:
+ return "Or";
+ case Variant::OP_XOR:
+ return "Xor";
+ case Variant::OP_NOT:
+ return "Not";
+ case Variant::OP_IN:
+ return "In";
+
+ default: {
+ ERR_FAIL_INDEX_V(p_op, Variant::OP_MAX, "");
+ return "Unknown Operator";
+ }
+ }
}
void VisualScriptOperator::set_operator(Variant::Operator p_op) {
@@ -1005,7 +1083,7 @@ void VisualScriptOperator::_bind_methods() {
if (i > 0) {
types += ",";
}
- types += op_names[i];
+ types += get_operator_name(static_cast<Variant::Operator>(i));
}
String argt = "Any";
@@ -1038,9 +1116,9 @@ public:
r_error_str = *p_outputs[0];
} else {
if (unary) {
- r_error_str = String(op_names[op]) + RTR(": Invalid argument of type: ") + Variant::get_type_name(p_inputs[0]->get_type());
+ r_error_str = String(Variant::get_operator_name(op)) + RTR(": Invalid argument of type: ") + Variant::get_type_name(p_inputs[0]->get_type());
} else {
- r_error_str = String(op_names[op]) + RTR(": Invalid arguments: ") + "A: " + Variant::get_type_name(p_inputs[0]->get_type()) + " B: " + Variant::get_type_name(p_inputs[1]->get_type());
+ r_error_str = String(Variant::get_operator_name(op)) + RTR(": Invalid arguments: ") + "A: " + Variant::get_type_name(p_inputs[0]->get_type()) + " B: " + Variant::get_type_name(p_inputs[1]->get_type());
}
}
}
@@ -1049,7 +1127,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptOperator::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptOperator::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceOperator *instance = memnew(VisualScriptNodeInstanceOperator);
instance->unary = get_input_value_port_count() == 1;
instance->op = op;
@@ -1064,7 +1142,7 @@ VisualScriptOperator::VisualScriptOperator() {
template <Variant::Operator OP>
static Ref<VisualScriptNode> create_op_node(const String &p_name) {
Ref<VisualScriptOperator> node;
- node.instance();
+ node.instantiate();
node->set_operator(OP);
return node;
}
@@ -1156,7 +1234,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptSelect::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptSelect::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceSelect *instance = memnew(VisualScriptNodeInstanceSelect);
return instance;
}
@@ -1228,12 +1306,12 @@ void VisualScriptVariableGet::_validate_property(PropertyInfo &property) const {
vs->get_variable_list(&vars);
String vhint;
- for (List<StringName>::Element *E = vars.front(); E; E = E->next()) {
+ for (const StringName &E : vars) {
if (vhint != String()) {
vhint += ",";
}
- vhint += E->get().operator String();
+ vhint += E.operator String();
}
property.hint = PROPERTY_HINT_ENUM;
@@ -1264,7 +1342,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptVariableGet::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptVariableGet::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceVariableGet *instance = memnew(VisualScriptNodeInstanceVariableGet);
instance->node = this;
instance->instance = p_instance;
@@ -1338,12 +1416,12 @@ void VisualScriptVariableSet::_validate_property(PropertyInfo &property) const {
vs->get_variable_list(&vars);
String vhint;
- for (List<StringName>::Element *E = vars.front(); E; E = E->next()) {
+ for (const StringName &E : vars) {
if (vhint != String()) {
vhint += ",";
}
- vhint += E->get().operator String();
+ vhint += E.operator String();
}
property.hint = PROPERTY_HINT_ENUM;
@@ -1376,7 +1454,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptVariableSet::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptVariableSet::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceVariableSet *instance = memnew(VisualScriptNodeInstanceVariableSet);
instance->node = this;
instance->instance = p_instance;
@@ -1433,9 +1511,9 @@ void VisualScriptConstant::set_constant_type(Variant::Type p_type) {
type = p_type;
Callable::CallError ce;
- value = Variant::construct(type, nullptr, 0, ce);
+ Variant::construct(type, value, nullptr, 0, ce);
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
}
Variant::Type VisualScriptConstant::get_constant_type() const {
@@ -1459,7 +1537,7 @@ void VisualScriptConstant::_validate_property(PropertyInfo &property) const {
if (property.name == "value") {
property.type = type;
if (type == Variant::NIL) {
- property.usage = 0; //do not save if nil
+ property.usage = PROPERTY_USAGE_NONE; //do not save if nil
}
}
}
@@ -1491,7 +1569,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptConstant::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptConstant::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceConstant *instance = memnew(VisualScriptNodeInstanceConstant);
instance->constant = value;
return instance;
@@ -1584,7 +1662,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptPreload::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptPreload::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstancePreload *instance = memnew(VisualScriptNodeInstancePreload);
instance->preload = preload;
return instance;
@@ -1649,7 +1727,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptIndexGet::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptIndexGet::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceIndexGet *instance = memnew(VisualScriptNodeInstanceIndexGet);
return instance;
}
@@ -1719,7 +1797,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptIndexSet::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptIndexSet::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceIndexSet *instance = memnew(VisualScriptNodeInstanceIndexSet);
return instance;
}
@@ -1756,7 +1834,7 @@ PropertyInfo VisualScriptGlobalConstant::get_input_value_port_info(int p_idx) co
}
PropertyInfo VisualScriptGlobalConstant::get_output_value_port_info(int p_idx) const {
- String name = GlobalConstants::get_global_constant_name(index);
+ String name = CoreConstants::get_global_constant_name(index);
return PropertyInfo(Variant::INT, name);
}
@@ -1766,7 +1844,7 @@ String VisualScriptGlobalConstant::get_caption() const {
void VisualScriptGlobalConstant::set_global_constant(int p_which) {
index = p_which;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1780,12 +1858,12 @@ public:
//virtual int get_working_memory_size() const { return 0; }
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) {
- *p_outputs[0] = GlobalConstants::get_global_constant_value(index);
+ *p_outputs[0] = CoreConstants::get_global_constant_value(index);
return 0;
}
};
-VisualScriptNodeInstance *VisualScriptGlobalConstant::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptGlobalConstant::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceGlobalConstant *instance = memnew(VisualScriptNodeInstanceGlobalConstant);
instance->index = index;
return instance;
@@ -1797,11 +1875,11 @@ void VisualScriptGlobalConstant::_bind_methods() {
String cc;
- for (int i = 0; i < GlobalConstants::get_global_constant_count(); i++) {
+ for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
if (i > 0) {
cc += ",";
}
- cc += GlobalConstants::get_global_constant_name(i);
+ cc += CoreConstants::get_global_constant_name(i);
}
ADD_PROPERTY(PropertyInfo(Variant::INT, "constant", PROPERTY_HINT_ENUM, cc), "set_global_constant", "get_global_constant");
}
@@ -1852,7 +1930,7 @@ String VisualScriptClassConstant::get_caption() const {
void VisualScriptClassConstant::set_class_constant(const StringName &p_which) {
name = p_which;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1866,8 +1944,8 @@ void VisualScriptClassConstant::set_base_type(const StringName &p_which) {
ClassDB::get_integer_constant_list(base_type, &constants, true);
if (constants.size() > 0) {
bool found_name = false;
- for (List<String>::Element *E = constants.front(); E; E = E->next()) {
- if (E->get() == name) {
+ for (const String &E : constants) {
+ if (E == name) {
found_name = true;
break;
}
@@ -1878,7 +1956,7 @@ void VisualScriptClassConstant::set_base_type(const StringName &p_which) {
} else {
name = "";
}
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -1903,7 +1981,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptClassConstant::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptClassConstant::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceClassConstant *instance = memnew(VisualScriptNodeInstanceClassConstant);
instance->value = ClassDB::get_integer_constant(base_type, name, &instance->valid);
return instance;
@@ -1915,11 +1993,11 @@ void VisualScriptClassConstant::_validate_property(PropertyInfo &property) const
ClassDB::get_integer_constant_list(base_type, &constants, true);
property.hint_string = "";
- for (List<String>::Element *E = constants.front(); E; E = E->next()) {
+ for (const String &E : constants) {
if (property.hint_string != String()) {
property.hint_string += ",";
}
- property.hint_string += E->get();
+ property.hint_string += E;
}
}
}
@@ -1985,7 +2063,7 @@ String VisualScriptBasicTypeConstant::get_text() const {
void VisualScriptBasicTypeConstant::set_basic_type_constant(const StringName &p_which) {
name = p_which;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -2000,8 +2078,8 @@ void VisualScriptBasicTypeConstant::set_basic_type(Variant::Type p_which) {
Variant::get_constants_for_type(type, &constants);
if (constants.size() > 0) {
bool found_name = false;
- for (List<StringName>::Element *E = constants.front(); E; E = E->next()) {
- if (E->get() == name) {
+ for (const StringName &E : constants) {
+ if (E == name) {
found_name = true;
break;
}
@@ -2012,7 +2090,7 @@ void VisualScriptBasicTypeConstant::set_basic_type(Variant::Type p_which) {
} else {
name = "";
}
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -2037,7 +2115,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptBasicTypeConstant::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptBasicTypeConstant::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceBasicTypeConstant *instance = memnew(VisualScriptNodeInstanceBasicTypeConstant);
instance->value = Variant::get_constant_value(type, name, &instance->valid);
return instance;
@@ -2049,15 +2127,15 @@ void VisualScriptBasicTypeConstant::_validate_property(PropertyInfo &property) c
Variant::get_constants_for_type(type, &constants);
if (constants.size() == 0) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
return;
}
property.hint_string = "";
- for (List<StringName>::Element *E = constants.front(); E; E = E->next()) {
+ for (const StringName &E : constants) {
if (property.hint_string != String()) {
property.hint_string += ",";
}
- property.hint_string += String(E->get());
+ property.hint_string += String(E);
}
}
}
@@ -2104,8 +2182,8 @@ double VisualScriptMathConstant::const_value[MATH_CONSTANT_MAX] = {
Math_TAU,
2.71828182845904523536,
Math::sqrt(2.0),
- Math_INF,
- Math_NAN
+ INFINITY,
+ NAN
};
int VisualScriptMathConstant::get_output_sequence_port_count() const {
@@ -2142,7 +2220,7 @@ String VisualScriptMathConstant::get_caption() const {
void VisualScriptMathConstant::set_math_constant(MathConstant p_which) {
constant = p_which;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -2161,7 +2239,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptMathConstant::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptMathConstant::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceMathConstant *instance = memnew(VisualScriptNodeInstanceMathConstant);
instance->value = const_value[constant];
return instance;
@@ -2235,7 +2313,7 @@ String VisualScriptEngineSingleton::get_caption() const {
void VisualScriptEngineSingleton::set_singleton(const String &p_string) {
singleton = p_string;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -2255,7 +2333,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptEngineSingleton::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptEngineSingleton::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceEngineSingleton *instance = memnew(VisualScriptNodeInstanceEngineSingleton);
instance->singleton = Engine::get_singleton()->get_singleton_object(singleton);
return instance;
@@ -2280,15 +2358,15 @@ void VisualScriptEngineSingleton::_validate_property(PropertyInfo &property) con
Engine::get_singleton()->get_singletons(&singletons);
- for (List<Engine::Singleton>::Element *E = singletons.front(); E; E = E->next()) {
- if (E->get().name == "VS" || E->get().name == "PS" || E->get().name == "PS2D" || E->get().name == "AS" || E->get().name == "TS" || E->get().name == "SS" || E->get().name == "SS2D") {
+ for (const Engine::Singleton &E : singletons) {
+ if (E.name == "VS" || E.name == "PS" || E.name == "PS2D" || E.name == "AS" || E.name == "TS" || E.name == "SS" || E.name == "SS2D") {
continue; //skip these, too simple named
}
if (cc != String()) {
cc += ",";
}
- cc += E->get().name;
+ cc += E.name;
}
property.hint = PROPERTY_HINT_ENUM;
@@ -2344,7 +2422,7 @@ String VisualScriptSceneNode::get_caption() const {
void VisualScriptSceneNode::set_node_path(const NodePath &p_path) {
path = p_path;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -2381,7 +2459,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptSceneNode::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptSceneNode::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceSceneNode *instance = memnew(VisualScriptNodeInstanceSceneNode);
instance->node = this;
instance->instance = p_instance;
@@ -2561,7 +2639,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptSceneTree::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptSceneTree::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceSceneTree *instance = memnew(VisualScriptNodeInstanceSceneTree);
instance->node = this;
instance->instance = p_instance;
@@ -2622,7 +2700,7 @@ String VisualScriptResourcePath::get_caption() const {
void VisualScriptResourcePath::set_resource_path(const String &p_path) {
path = p_path;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -2642,7 +2720,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptResourcePath::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptResourcePath::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceResourcePath *instance = memnew(VisualScriptNodeInstanceResourcePath);
instance->path = path;
return instance;
@@ -2714,7 +2792,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptSelf::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptSelf::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceSelf *instance = memnew(VisualScriptNodeInstanceSelf);
instance->instance = p_instance;
return instance;
@@ -2747,36 +2825,41 @@ VisualScriptSelf::VisualScriptSelf() {
//////////////////////////////////////////
int VisualScriptCustomNode::get_output_sequence_port_count() const {
- if (get_script_instance() && get_script_instance()->has_method("_get_output_sequence_port_count")) {
- return get_script_instance()->call("_get_output_sequence_port_count");
+ int ret;
+ if (GDVIRTUAL_CALL(_get_output_sequence_port_count, ret)) {
+ return ret;
}
return 0;
}
bool VisualScriptCustomNode::has_input_sequence_port() const {
- if (get_script_instance() && get_script_instance()->has_method("_has_input_sequence_port")) {
- return get_script_instance()->call("_has_input_sequence_port");
+ bool ret;
+ if (GDVIRTUAL_CALL(_has_input_sequence_port, ret)) {
+ return ret;
}
return false;
}
int VisualScriptCustomNode::get_input_value_port_count() const {
- if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_count")) {
- return get_script_instance()->call("_get_input_value_port_count");
+ int ret;
+ if (GDVIRTUAL_CALL(_get_input_value_port_count, ret)) {
+ return ret;
}
return 0;
}
int VisualScriptCustomNode::get_output_value_port_count() const {
- if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_count")) {
- return get_script_instance()->call("_get_output_value_port_count");
+ int ret;
+ if (GDVIRTUAL_CALL(_get_output_value_port_count, ret)) {
+ return ret;
}
return 0;
}
String VisualScriptCustomNode::get_output_sequence_port_text(int p_port) const {
- if (get_script_instance() && get_script_instance()->has_method("_get_output_sequence_port_text")) {
- return get_script_instance()->call("_get_output_sequence_port_text", p_port);
+ String ret;
+ if (GDVIRTUAL_CALL(_get_output_sequence_port_text, p_port, ret)) {
+ return ret;
}
return String();
@@ -2784,43 +2867,101 @@ String VisualScriptCustomNode::get_output_sequence_port_text(int p_port) const {
PropertyInfo VisualScriptCustomNode::get_input_value_port_info(int p_idx) const {
PropertyInfo info;
- if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_type")) {
- info.type = Variant::Type(int(get_script_instance()->call("_get_input_value_port_type", p_idx)));
+ {
+ int type;
+ if (GDVIRTUAL_CALL(_get_input_value_port_type, p_idx, type)) {
+ info.type = Variant::Type(type);
+ }
+ }
+ {
+ String name;
+ if (GDVIRTUAL_CALL(_get_input_value_port_name, p_idx, name)) {
+ info.name = name;
+ }
+ }
+ {
+ int hint;
+ if (GDVIRTUAL_CALL(_get_input_value_port_hint, p_idx, hint)) {
+ info.hint = PropertyHint(hint);
+ }
}
- if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_name")) {
- info.name = get_script_instance()->call("_get_input_value_port_name", p_idx);
+
+ {
+ String hint_string;
+ if (GDVIRTUAL_CALL(_get_input_value_port_hint_string, p_idx, hint_string)) {
+ info.hint_string = hint_string;
+ }
}
+
return info;
}
PropertyInfo VisualScriptCustomNode::get_output_value_port_info(int p_idx) const {
PropertyInfo info;
- if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_type")) {
- info.type = Variant::Type(int(get_script_instance()->call("_get_output_value_port_type", p_idx)));
+ {
+ int type;
+ if (GDVIRTUAL_CALL(_get_output_value_port_type, p_idx, type)) {
+ info.type = Variant::Type(type);
+ }
+ }
+ {
+ String name;
+ if (GDVIRTUAL_CALL(_get_output_value_port_name, p_idx, name)) {
+ info.name = name;
+ }
+ }
+ {
+ int hint;
+ if (GDVIRTUAL_CALL(_get_output_value_port_hint, p_idx, hint)) {
+ info.hint = PropertyHint(hint);
+ }
}
- if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_name")) {
- info.name = get_script_instance()->call("_get_output_value_port_name", p_idx);
+
+ {
+ String hint_string;
+ if (GDVIRTUAL_CALL(_get_output_value_port_hint_string, p_idx, hint_string)) {
+ info.hint_string = hint_string;
+ }
}
return info;
}
+VisualScriptCustomNode::TypeGuess VisualScriptCustomNode::guess_output_type(TypeGuess *p_inputs, int p_output) const {
+ TypeGuess tg;
+ PropertyInfo pi = VisualScriptCustomNode::get_output_value_port_info(p_output);
+ tg.type = pi.type;
+ if (pi.type == Variant::OBJECT) {
+ if (pi.hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ if (pi.hint_string.is_resource_file()) {
+ tg.script = ResourceLoader::load(pi.hint_string);
+ } else if (ClassDB::class_exists(pi.hint_string)) {
+ tg.gdclass = pi.hint_string;
+ }
+ }
+ }
+ return tg;
+}
+
String VisualScriptCustomNode::get_caption() const {
- if (get_script_instance() && get_script_instance()->has_method("_get_caption")) {
- return get_script_instance()->call("_get_caption");
+ String ret;
+ if (GDVIRTUAL_CALL(_get_caption, ret)) {
+ return ret;
}
return "CustomNode";
}
String VisualScriptCustomNode::get_text() const {
- if (get_script_instance() && get_script_instance()->has_method("_get_text")) {
- return get_script_instance()->call("_get_text");
+ String ret;
+ if (GDVIRTUAL_CALL(_get_text, ret)) {
+ return ret;
}
return "";
}
String VisualScriptCustomNode::get_category() const {
- if (get_script_instance() && get_script_instance()->has_method("_get_category")) {
- return get_script_instance()->call("_get_category");
+ String ret;
+ if (GDVIRTUAL_CALL(_get_category, ret)) {
+ return ret;
}
return "Custom";
}
@@ -2835,14 +2976,7 @@ public:
virtual int get_working_memory_size() const { return work_mem_size; }
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) {
- if (node->get_script_instance()) {
-#ifdef DEBUG_ENABLED
- if (!node->get_script_instance()->has_method(VisualScriptLanguage::singleton->_step)) {
- r_error_str = RTR("Custom node has no _step() method, can't process graph.");
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return 0;
- }
-#endif
+ if (GDVIRTUAL_IS_OVERRIDDEN_PTR(node, _step)) {
Array in_values;
Array out_values;
Array work_mem;
@@ -2863,7 +2997,8 @@ public:
int ret_out;
- Variant ret = node->get_script_instance()->call(VisualScriptLanguage::singleton->_step, in_values, out_values, p_start_mode, work_mem);
+ Variant ret;
+ GDVIRTUAL_CALL_PTR(node, _step, in_values, out_values, p_start_mode, work_mem, ret);
if (ret.get_type() == Variant::STRING) {
r_error_str = ret;
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
@@ -2889,55 +3024,56 @@ public:
}
return ret_out;
+ } else {
+ r_error_str = RTR("Custom node has no _step() method, can't process graph.");
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
}
return 0;
}
};
-VisualScriptNodeInstance *VisualScriptCustomNode::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptCustomNode::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceCustomNode *instance = memnew(VisualScriptNodeInstanceCustomNode);
instance->instance = p_instance;
instance->node = this;
instance->in_count = get_input_value_port_count();
instance->out_count = get_output_value_port_count();
- if (get_script_instance() && get_script_instance()->has_method("_get_working_memory_size")) {
- instance->work_mem_size = get_script_instance()->call("_get_working_memory_size");
- } else {
- instance->work_mem_size = 0;
- }
+ instance->work_mem_size = 0;
+ GDVIRTUAL_CALL(_get_working_memory_size, instance->work_mem_size);
return instance;
}
void VisualScriptCustomNode::_script_changed() {
- call_deferred("ports_changed_notify");
+ call_deferred(SNAME("ports_changed_notify"));
}
void VisualScriptCustomNode::_bind_methods() {
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_sequence_port_count"));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_has_input_sequence_port"));
-
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_output_sequence_port_text", PropertyInfo(Variant::INT, "idx")));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_value_port_count"));
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_value_port_count"));
+ GDVIRTUAL_BIND(_get_output_sequence_port_count);
+ GDVIRTUAL_BIND(_has_input_sequence_port);
+ GDVIRTUAL_BIND(_get_output_sequence_port_text, "seq_idx");
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_value_port_type", PropertyInfo(Variant::INT, "idx")));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_input_value_port_name", PropertyInfo(Variant::INT, "idx")));
+ GDVIRTUAL_BIND(_get_input_value_port_count);
+ GDVIRTUAL_BIND(_get_input_value_port_type, "input_idx");
+ GDVIRTUAL_BIND(_get_input_value_port_name, "input_idx");
+ GDVIRTUAL_BIND(_get_input_value_port_hint, "input_idx");
+ GDVIRTUAL_BIND(_get_input_value_port_hint_string, "input_idx");
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_value_port_type", PropertyInfo(Variant::INT, "idx")));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_output_value_port_name", PropertyInfo(Variant::INT, "idx")));
+ GDVIRTUAL_BIND(_get_output_value_port_count);
+ GDVIRTUAL_BIND(_get_output_value_port_type, "output_idx");
+ GDVIRTUAL_BIND(_get_output_value_port_name, "output_idx");
+ GDVIRTUAL_BIND(_get_output_value_port_hint, "output_idx");
+ GDVIRTUAL_BIND(_get_output_value_port_hint_string, "output_idx");
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_caption"));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_text"));
- BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_category"));
+ GDVIRTUAL_BIND(_get_caption);
+ GDVIRTUAL_BIND(_get_text);
+ GDVIRTUAL_BIND(_get_category);
- BIND_VMETHOD(MethodInfo(Variant::INT, "_get_working_memory_size"));
+ GDVIRTUAL_BIND(_get_working_memory_size);
- MethodInfo stepmi(Variant::NIL, "_step", PropertyInfo(Variant::ARRAY, "inputs"), PropertyInfo(Variant::ARRAY, "outputs"), PropertyInfo(Variant::INT, "start_mode"), PropertyInfo(Variant::ARRAY, "working_mem"));
- stepmi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- BIND_VMETHOD(stepmi);
+ GDVIRTUAL_BIND(_step, "inputs", "outputs", "start_mode", "working_mem");
BIND_ENUM_CONSTANT(START_MODE_BEGIN_SEQUENCE);
BIND_ENUM_CONSTANT(START_MODE_CONTINUE_SEQUENCE);
@@ -3046,7 +3182,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptSubCall::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptSubCall::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceSubCall *instance = memnew(VisualScriptNodeInstanceSubCall);
instance->instance = p_instance;
Ref<Script> script = get_script();
@@ -3060,9 +3196,7 @@ VisualScriptNodeInstance *VisualScriptSubCall::instance(VisualScriptInstance *p_
}
void VisualScriptSubCall::_bind_methods() {
- MethodInfo scmi(Variant::NIL, "_subcall", PropertyInfo(Variant::NIL, "arguments"));
- scmi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- BIND_VMETHOD(scmi);
+ // Since this is script only, registering virtual function is no longer valid. Will have to go in docs.
}
VisualScriptSubCall::VisualScriptSubCall() {
@@ -3159,7 +3293,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptComment::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptComment::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceComment *instance = memnew(VisualScriptNodeInstanceComment);
instance->instance = p_instance;
return instance;
@@ -3257,7 +3391,7 @@ public:
virtual int step(const Variant **p_inputs, Variant **p_outputs, StartMode p_start_mode, Variant *p_working_mem, Callable::CallError &r_error, String &r_error_str) {
Callable::CallError ce;
- *p_outputs[0] = Variant::construct(type, p_inputs, argcount, ce);
+ Variant::construct(type, *p_outputs[0], p_inputs, argcount, ce);
if (ce.error != Callable::CallError::CALL_OK) {
r_error_str = "Invalid arguments for constructor";
}
@@ -3266,7 +3400,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptConstructor::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptConstructor::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceConstructor *instance = memnew(VisualScriptNodeInstanceConstructor);
instance->instance = p_instance;
instance->type = type;
@@ -3281,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() {
@@ -3295,7 +3429,7 @@ static Ref<VisualScriptNode> create_constructor_node(const String &p_name) {
ERR_FAIL_COND_V(!constructor_map.has(p_name), Ref<VisualScriptNode>());
Ref<VisualScriptConstructor> vsc;
- vsc.instance();
+ vsc.instantiate();
vsc->set_constructor_type(constructor_map[p_name].first);
vsc->set_constructor(constructor_map[p_name].second);
@@ -3376,7 +3510,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptLocalVar::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptLocalVar::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceLocalVar *instance = memnew(VisualScriptNodeInstanceLocalVar);
instance->instance = p_instance;
instance->name = name;
@@ -3484,7 +3618,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptLocalVarSet::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptLocalVarSet::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceLocalVarSet *instance = memnew(VisualScriptNodeInstanceLocalVarSet);
instance->instance = p_instance;
instance->name = name;
@@ -3621,7 +3755,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptInputAction::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptInputAction::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceInputAction *instance = memnew(VisualScriptNodeInstanceInputAction);
instance->instance = p_instance;
instance->action = name;
@@ -3639,9 +3773,7 @@ void VisualScriptInputAction::_validate_property(PropertyInfo &property) const {
ProjectSettings::get_singleton()->get_property_list(&pinfo);
Vector<String> al;
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
- const PropertyInfo &pi = E->get();
-
+ for (const PropertyInfo &pi : pinfo) {
if (!pi.name.begins_with("input/")) {
continue;
}
@@ -3729,15 +3861,15 @@ void VisualScriptDeconstruct::_update_elements() {
elements.clear();
Variant v;
Callable::CallError ce;
- v = Variant::construct(type, nullptr, 0, ce);
+ Variant::construct(type, v, nullptr, 0, ce);
List<PropertyInfo> pinfo;
v.get_property_list(&pinfo);
- for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ for (const PropertyInfo &E : pinfo) {
Element e;
- e.name = E->get().name;
- e.type = E->get().type;
+ e.name = E.name;
+ e.type = E.type;
elements.push_back(e);
}
}
@@ -3750,7 +3882,7 @@ void VisualScriptDeconstruct::set_deconstruct_type(Variant::Type p_type) {
type = p_type;
_update_elements();
ports_changed_notify();
- _change_notify(); //to make input appear/disappear
+ notify_property_list_changed(); //to make input appear/disappear
}
Variant::Type VisualScriptDeconstruct::get_deconstruct_type() const {
@@ -3799,7 +3931,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptDeconstruct::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptDeconstruct::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceDeconstruct *instance = memnew(VisualScriptNodeInstanceDeconstruct);
instance->instance = p_instance;
instance->outputs.resize(elements.size());
@@ -3826,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() {
@@ -3836,7 +3968,7 @@ VisualScriptDeconstruct::VisualScriptDeconstruct() {
template <Variant::Type T>
static Ref<VisualScriptNode> create_node_deconst_typed(const String &p_name) {
Ref<VisualScriptDeconstruct> node;
- node.instance();
+ node.instantiate();
node->set_deconstruct_type(T);
return node;
}
@@ -3881,7 +4013,6 @@ void register_visual_script_nodes() {
VisualScriptLanguage::singleton->add_register_func("operators/math/negate", create_op_node<Variant::OP_NEGATE>);
VisualScriptLanguage::singleton->add_register_func("operators/math/positive", create_op_node<Variant::OP_POSITIVE>);
VisualScriptLanguage::singleton->add_register_func("operators/math/remainder", create_op_node<Variant::OP_MODULE>);
- VisualScriptLanguage::singleton->add_register_func("operators/math/string_concat", create_op_node<Variant::OP_STRING_CONCAT>);
//bitwise
VisualScriptLanguage::singleton->add_register_func("operators/bitwise/shift_left", create_op_node<Variant::OP_SHIFT_LEFT>);
VisualScriptLanguage::singleton->add_register_func("operators/bitwise/shift_right", create_op_node<Variant::OP_SHIFT_RIGHT>);
@@ -3906,34 +4037,34 @@ void register_visual_script_nodes() {
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::RECT2I), create_node_deconst_typed<Variant::Type::RECT2I>);
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::TRANSFORM2D), create_node_deconst_typed<Variant::Type::TRANSFORM2D>);
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::PLANE), create_node_deconst_typed<Variant::Type::PLANE>);
- VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::QUAT), create_node_deconst_typed<Variant::Type::QUAT>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::QUATERNION), create_node_deconst_typed<Variant::Type::QUATERNION>);
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::AABB), create_node_deconst_typed<Variant::Type::AABB>);
VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::BASIS), create_node_deconst_typed<Variant::Type::BASIS>);
- VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::TRANSFORM), create_node_deconst_typed<Variant::Type::TRANSFORM>);
+ VisualScriptLanguage::singleton->add_register_func("functions/deconstruct/" + Variant::get_type_name(Variant::Type::TRANSFORM3D), create_node_deconst_typed<Variant::Type::TRANSFORM3D>);
VisualScriptLanguage::singleton->add_register_func("functions/compose_array", create_node_generic<VisualScriptComposeArray>);
for (int i = 1; i < Variant::VARIANT_MAX; i++) {
List<MethodInfo> constructors;
Variant::get_constructor_list(Variant::Type(i), &constructors);
- for (List<MethodInfo>::Element *E = constructors.front(); E; E = E->next()) {
- if (E->get().arguments.size() > 0) {
+ for (const MethodInfo &E : constructors) {
+ if (E.arguments.size() > 0) {
String name = "functions/constructors/" + Variant::get_type_name(Variant::Type(i)) + "(";
- for (int j = 0; j < E->get().arguments.size(); j++) {
+ for (int j = 0; j < E.arguments.size(); j++) {
if (j > 0) {
name += ", ";
}
- if (E->get().arguments.size() == 1) {
- name += Variant::get_type_name(E->get().arguments[j].type);
+ if (E.arguments.size() == 1) {
+ name += Variant::get_type_name(E.arguments[j].type);
} else {
- name += E->get().arguments[j].name;
+ name += E.arguments[j].name;
}
}
name += ")";
VisualScriptLanguage::singleton->add_register_func(name, create_constructor_node);
Pair<Variant::Type, MethodInfo> pair;
pair.first = Variant::Type(i);
- pair.second = E->get();
+ pair.second = E;
constructor_map[name] = pair;
}
}
diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h
index b6061f8838..78881f0a53 100644
--- a/modules/visual_script/visual_script_nodes.h
+++ b/modules/visual_script/visual_script_nodes.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,6 +31,8 @@
#ifndef VISUAL_SCRIPT_NODES_H
#define VISUAL_SCRIPT_NODES_H
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/script_language.h"
#include "visual_script.h"
class VisualScriptFunction : public VisualScriptNode {
@@ -47,7 +49,7 @@ class VisualScriptFunction : public VisualScriptNode {
bool stack_less;
int stack_size;
- MultiplayerAPI::RPCMode rpc_mode;
+ Multiplayer::RPCMode rpc_mode;
bool sequenced;
protected:
@@ -88,16 +90,12 @@ 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_rpc_mode(Multiplayer::RPCMode p_mode);
+ Multiplayer::RPCMode get_rpc_mode() const;
- void set_return_type(Variant::Type p_type);
- Variant::Type get_return_type() const;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
- void set_rpc_mode(MultiplayerAPI::RPCMode p_mode);
- MultiplayerAPI::RPCMode get_rpc_mode() const;
-
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual void reset_state() override;
VisualScriptFunction();
};
@@ -134,6 +132,8 @@ protected:
static void _bind_methods();
public:
+ virtual void reset_state() override;
+
virtual bool is_output_port_editable() const;
virtual bool is_output_port_name_editable() const;
virtual bool is_output_port_type_editable() const;
@@ -188,7 +188,7 @@ public:
virtual String get_text() const override;
virtual String get_category() const override { return "functions"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptComposeArray();
};
@@ -223,7 +223,9 @@ public:
void set_typed(Variant::Type p_op);
Variant::Type get_typed() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ static String get_operator_name(Variant::Operator p_op);
+
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptOperator();
};
@@ -255,7 +257,7 @@ public:
void set_typed(Variant::Type p_op);
Variant::Type get_typed() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptSelect();
};
@@ -287,7 +289,7 @@ public:
void set_variable(StringName p_variable);
StringName get_variable() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptVariableGet();
};
@@ -319,7 +321,7 @@ public:
void set_variable(StringName p_variable);
StringName get_variable() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptVariableSet();
};
@@ -355,7 +357,7 @@ public:
void set_constant_value(Variant p_value);
Variant get_constant_value() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptConstant();
};
@@ -386,7 +388,7 @@ public:
void set_preload(const Ref<Resource> &p_preload);
Ref<Resource> get_preload() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptPreload();
};
@@ -409,7 +411,7 @@ public:
virtual String get_caption() const override;
virtual String get_category() const override { return "operators"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptIndexGet();
};
@@ -432,7 +434,7 @@ public:
virtual String get_caption() const override;
virtual String get_category() const override { return "operators"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptIndexSet();
};
@@ -462,7 +464,7 @@ public:
void set_global_constant(int p_which);
int get_global_constant();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptGlobalConstant();
};
@@ -498,7 +500,7 @@ public:
void set_base_type(const StringName &p_which);
StringName get_base_type();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptClassConstant();
};
@@ -535,7 +537,7 @@ public:
void set_basic_type(Variant::Type p_which);
Variant::Type get_basic_type() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptBasicTypeConstant();
};
@@ -582,7 +584,7 @@ public:
void set_math_constant(MathConstant p_which);
MathConstant get_math_constant();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptMathConstant();
};
@@ -617,7 +619,7 @@ public:
void set_singleton(const String &p_string);
String get_singleton();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
@@ -651,7 +653,7 @@ public:
void set_node_path(const NodePath &p_path);
NodePath get_node_path();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
@@ -680,7 +682,7 @@ public:
virtual String get_caption() const override;
virtual String get_category() const override { return "data"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
@@ -713,7 +715,7 @@ public:
void set_resource_path(const String &p_path);
String get_resource_path();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptResourcePath();
};
@@ -739,7 +741,7 @@ public:
virtual String get_caption() const override;
virtual String get_category() const override { return "data"; }
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
@@ -751,6 +753,30 @@ class VisualScriptCustomNode : public VisualScriptNode {
protected:
static void _bind_methods();
+ friend class VisualScriptNodeInstanceCustomNode;
+ GDVIRTUAL0RC(int, _get_output_sequence_port_count)
+ GDVIRTUAL0RC(bool, _has_input_sequence_port)
+ GDVIRTUAL1RC(String, _get_output_sequence_port_text, int)
+
+ GDVIRTUAL0RC(int, _get_input_value_port_count)
+ GDVIRTUAL1RC(int, _get_input_value_port_type, int)
+ GDVIRTUAL1RC(String, _get_input_value_port_name, int)
+ GDVIRTUAL1RC(int, _get_input_value_port_hint, int)
+ GDVIRTUAL1RC(String, _get_input_value_port_hint_string, int)
+
+ GDVIRTUAL0RC(int, _get_output_value_port_count)
+ GDVIRTUAL1RC(int, _get_output_value_port_type, int)
+ GDVIRTUAL1RC(String, _get_output_value_port_name, int)
+ GDVIRTUAL1RC(int, _get_output_value_port_hint, int)
+ GDVIRTUAL1RC(String, _get_output_value_port_hint_string, int)
+
+ GDVIRTUAL0RC(String, _get_caption)
+ GDVIRTUAL0RC(String, _get_text)
+ GDVIRTUAL0RC(String, _get_category)
+
+ GDVIRTUAL0RC(int, _get_working_memory_size)
+
+ GDVIRTUAL4RC(Variant, _step, Array, Array, int, Array)
public:
enum StartMode { //replicated for step
@@ -784,7 +810,9 @@ public:
virtual String get_text() const override;
virtual String get_category() const override;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
+
+ virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override;
void _script_changed();
@@ -815,7 +843,7 @@ public:
virtual String get_text() const override;
virtual String get_category() const override;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptSubCall();
};
@@ -855,7 +883,7 @@ public:
void set_size(const Size2 &p_size);
Size2 get_size() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptComment();
};
@@ -890,7 +918,7 @@ public:
void set_constructor(const Dictionary &p_info);
Dictionary get_constructor() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptConstructor();
};
@@ -925,7 +953,7 @@ public:
void set_var_type(Variant::Type p_type);
Variant::Type get_var_type() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptLocalVar();
};
@@ -961,7 +989,7 @@ public:
void set_var_type(Variant::Type p_type);
Variant::Type get_var_type() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptLocalVarSet();
};
@@ -1006,7 +1034,7 @@ public:
void set_action_mode(Mode p_mode);
Mode get_action_mode() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptInputAction();
};
@@ -1052,7 +1080,7 @@ public:
void set_deconstruct_type(Variant::Type p_type);
Variant::Type get_deconstruct_type() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptDeconstruct();
};
diff --git a/modules/visual_script/visual_script_yield_nodes.cpp b/modules/visual_script/visual_script_yield_nodes.cpp
index dd07cc45a7..4b89c9ccd0 100644
--- a/modules/visual_script/visual_script_yield_nodes.cpp
+++ b/modules/visual_script/visual_script_yield_nodes.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -93,7 +93,7 @@ String VisualScriptYield::get_text() const {
class VisualScriptNodeInstanceYield : public VisualScriptNodeInstance {
public:
VisualScriptYield::YieldMode mode;
- float wait_time;
+ double wait_time;
virtual int get_working_memory_size() const { return 1; } //yield needs at least 1
//virtual bool is_output_port_unsequenced(int p_idx) const { return false; }
@@ -113,7 +113,7 @@ public:
}
Ref<VisualScriptFunctionState> state;
- state.instance();
+ state.instantiate();
int ret = STEP_YIELD_BIT;
switch (mode) {
@@ -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());
@@ -138,7 +138,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptYield::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptYield::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceYield *instance = memnew(VisualScriptNodeInstanceYield);
//instance->instance=p_instance;
instance->mode = yield_mode;
@@ -152,14 +152,14 @@ void VisualScriptYield::set_yield_mode(YieldMode p_mode) {
}
yield_mode = p_mode;
ports_changed_notify();
- _change_notify();
+ notify_property_list_changed();
}
VisualScriptYield::YieldMode VisualScriptYield::get_yield_mode() {
return yield_mode;
}
-void VisualScriptYield::set_wait_time(float p_time) {
+void VisualScriptYield::set_wait_time(double p_time) {
if (wait_time == p_time) {
return;
}
@@ -167,14 +167,14 @@ void VisualScriptYield::set_wait_time(float p_time) {
ports_changed_notify();
}
-float VisualScriptYield::get_wait_time() {
+double VisualScriptYield::get_wait_time() {
return wait_time;
}
void VisualScriptYield::_validate_property(PropertyInfo &property) const {
if (property.name == "wait_time") {
if (yield_mode != YIELD_WAIT) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
}
}
}
@@ -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);
@@ -202,7 +202,7 @@ VisualScriptYield::VisualScriptYield() {
template <VisualScriptYield::YieldMode MODE>
static Ref<VisualScriptNode> create_yield_node(const String &p_name) {
Ref<VisualScriptYield> node;
- node.instance();
+ node.instantiate();
node->set_yield_mode(MODE);
return node;
}
@@ -359,7 +359,7 @@ void VisualScriptYieldSignal::set_base_type(const StringName &p_type) {
base_type = p_type;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -374,7 +374,7 @@ void VisualScriptYieldSignal::set_signal(const StringName &p_type) {
signal = p_type;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -389,7 +389,7 @@ void VisualScriptYieldSignal::set_base_path(const NodePath &p_type) {
base_path = p_type;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -404,7 +404,7 @@ void VisualScriptYieldSignal::set_call_mode(CallMode p_mode) {
call_mode = p_mode;
- _change_notify();
+ notify_property_list_changed();
ports_changed_notify();
}
@@ -415,17 +415,17 @@ 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;
}
}
if (property.name == "node_path") {
if (call_mode != CALL_MODE_NODE_PATH) {
- property.usage = 0;
+ property.usage = PROPERTY_USAGE_NONE;
} else {
Node *bnode = _get_base_node();
if (bnode) {
- property.hint_string = bnode->get_path(); //convert to loong string
+ property.hint_string = bnode->get_path(); //convert to long string
}
}
}
@@ -438,21 +438,21 @@ void VisualScriptYieldSignal::_validate_property(PropertyInfo &property) const {
ClassDB::get_signal_list(_get_base_type(), &methods);
List<String> mstring;
- for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) {
- if (E->get().name.begins_with("_")) {
+ for (const MethodInfo &E : methods) {
+ if (E.name.begins_with("_")) {
continue;
}
- mstring.push_back(E->get().name.get_slice(":", 0));
+ mstring.push_back(E.name.get_slice(":", 0));
}
mstring.sort();
String ml;
- for (List<String>::Element *E = mstring.front(); E; E = E->next()) {
+ for (const String &E : mstring) {
if (ml != String()) {
ml += ",";
}
- ml += E->get();
+ ml += E;
}
property.hint_string = ml;
@@ -548,7 +548,7 @@ public:
}
Ref<VisualScriptFunctionState> state;
- state.instance();
+ state.instantiate();
state->connect_to_signal(object, signal, Array());
@@ -559,7 +559,7 @@ public:
}
};
-VisualScriptNodeInstance *VisualScriptYieldSignal::instance(VisualScriptInstance *p_instance) {
+VisualScriptNodeInstance *VisualScriptYieldSignal::instantiate(VisualScriptInstance *p_instance) {
VisualScriptNodeInstanceYieldSignal *instance = memnew(VisualScriptNodeInstanceYieldSignal);
instance->node = this;
instance->instance = p_instance;
@@ -578,7 +578,7 @@ VisualScriptYieldSignal::VisualScriptYieldSignal() {
template <VisualScriptYieldSignal::CallMode cmode>
static Ref<VisualScriptNode> create_yield_signal_node(const String &p_name) {
Ref<VisualScriptYieldSignal> node;
- node.instance();
+ node.instantiate();
node->set_call_mode(cmode);
return node;
}
diff --git a/modules/visual_script/visual_script_yield_nodes.h b/modules/visual_script/visual_script_yield_nodes.h
index 7a72211027..6005ff30b0 100644
--- a/modules/visual_script/visual_script_yield_nodes.h
+++ b/modules/visual_script/visual_script_yield_nodes.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,7 +47,7 @@ public:
private:
YieldMode yield_mode;
- float wait_time;
+ double wait_time;
protected:
virtual void _validate_property(PropertyInfo &property) const override;
@@ -73,10 +73,10 @@ public:
void set_yield_mode(YieldMode p_mode);
YieldMode get_yield_mode();
- void set_wait_time(float p_time);
- float get_wait_time();
+ void set_wait_time(double p_time);
+ double get_wait_time();
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptYield();
};
@@ -135,7 +135,7 @@ public:
void set_call_mode(CallMode p_mode);
CallMode get_call_mode() const;
- virtual VisualScriptNodeInstance *instance(VisualScriptInstance *p_instance) override;
+ virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override;
VisualScriptYieldSignal();
};
diff --git a/modules/vorbis/SCsub b/modules/vorbis/SCsub
index 05d46757d3..322314487f 100644
--- a/modules/vorbis/SCsub
+++ b/modules/vorbis/SCsub
@@ -3,14 +3,12 @@
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()
-stub = True
-
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_libvorbis"]:
thirdparty_dir = "#thirdparty/libvorbis/"
thirdparty_sources = [
@@ -51,7 +49,16 @@ if env["builtin_libvorbis"]:
env_thirdparty = env_vorbis.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
+
+# Godot source files
+
+module_obj = []
+
+env_vorbis.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
-# Module files
-env_vorbis.add_source_files(env.modules_sources, "register_types.cpp")
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
new file mode 100644
index 0000000000..d913c115d9
--- /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) {
+ //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 5070f2a078..59a1318a6b 100644
--- a/modules/stb_vorbis/audio_stream_ogg_vorbis.h
+++ b/modules/vorbis/audio_stream_ogg_vorbis.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,31 +28,51 @@
/* 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;
- stb_vorbis_alloc ogg_alloc;
- uint32_t frames_mixed;
- bool active;
- int loops;
+ 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 void _mix_internal(AudioFrame *p_buffer, int p_frames) override;
+ virtual int _mix_internal(AudioFrame *p_buffer, int p_frames) override;
virtual float get_stream_sampling_rate() override;
public:
@@ -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;
- uint32_t data_len;
+ int channels = 1;
+ float length = 0.0;
+ bool loop = false;
+ float loop_offset = 0.0;
- int decode_mem_size;
- float sample_rate;
- int channels;
- float length;
- bool loop;
- float loop_offset;
- 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/vorbis/doc_classes/AudioStreamOGGVorbis.xml b/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml
new file mode 100644
index 0000000000..4cd278fe83
--- /dev/null
+++ b/modules/vorbis/doc_classes/AudioStreamOGGVorbis.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamOGGVorbis" inherits="AudioStream" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <members>
+ <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>
+</class>
diff --git a/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml b/modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml
index f32a4f0a23..05c70d88da 100644
--- a/modules/gdnative/doc_classes/WebRTCDataChannelGDNative.xml
+++ b/modules/vorbis/doc_classes/AudioStreamPlaybackOGGVorbis.xml
@@ -1,13 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebRTCDataChannelGDNative" inherits="WebRTCDataChannel" 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 8874b3887b..de3f41afdd 100644
--- a/modules/vorbis/register_types.cpp
+++ b/modules/vorbis/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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/register_types.h b/modules/vorbis/register_types.h
index 7fa0dfdeef..1497e6f5e4 100644
--- a/modules/vorbis/register_types.h
+++ b/modules/vorbis/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
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 47f0039328..8565e0deb8 100644
--- a/modules/stb_vorbis/resource_importer_ogg_vorbis.h
+++ b/modules/vorbis/resource_importer_ogg_vorbis.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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 247b4ead37..0000000000
--- a/modules/webm/SCsub
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/usr/bin/env python
-
-Import("env")
-Import("env_modules")
-
-env_webm = env_modules.Clone()
-
-# Thirdparty source files
-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(env.modules_sources, thirdparty_sources)
-
-# Godot source files
-env_webm.add_source_files(env.modules_sources, "*.cpp")
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 2edbc08cc8..0000000000
--- a/modules/webm/doc_classes/VideoStreamWebm.xml
+++ /dev/null
@@ -1,32 +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] 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">
- </return>
- <description>
- Returns the WebM video file handled by this [VideoStreamWebm].
- </description>
- </method>
- <method name="set_file">
- <return type="void">
- </return>
- <argument index="0" name="file" type="String">
- </argument>
- <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 d0744fa313..0000000000
--- a/modules/webm/libvpx/SCsub
+++ /dev/null
@@ -1,387 +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" or env["platform"] == "server") 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:
- 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"
- or env["platform"] == "server"
- ):
- 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 6a02e3a87a..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-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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 832e14d91a..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-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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/os/file_access.h"
-#include "core/os/os.h"
-#include "core/project_settings.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() != (size_t)pos) {
- file->seek(pos);
- }
- if (file->get_buffer(buf, len) == len) {
- return 0;
- }
- }
- return -1;
- }
-
- virtual int Length(long long *total, long long *available) {
- if (file) {
- const size_t len = file->get_len();
- 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.instance();
- 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_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) {
- //Not implemented
-}
-
-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, bool p_no_cache) {
- 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 25675cb248..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-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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, bool p_no_cache = false);
- 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 58f2bb35e6..4c0c2f7893 100644
--- a/modules/webp/SCsub
+++ b/modules/webp/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_webp = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_libwebp"]:
thirdparty_dir = "#thirdparty/libwebp/"
thirdparty_sources = [
@@ -130,7 +133,16 @@ if env["builtin_libwebp"]:
env_thirdparty = env_webp.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_webp.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_webp.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/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
index d5c80e7909..5bebad2b53 100644
--- a/modules/webp/image_loader_webp.cpp
+++ b/modules/webp/image_loader_webp.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,16 +30,17 @@
#include "image_loader_webp.h"
+#include "core/config/project_settings.h"
#include "core/io/marshalls.h"
#include "core/os/os.h"
-#include "core/print_string.h"
+#include "core/string/print_string.h"
#include <stdlib.h>
#include <webp/decode.h>
#include <webp/encode.h>
static Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quality) {
- ERR_FAIL_COND_V(p_image.is_null() || p_image->empty(), Vector<uint8_t>());
+ ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
Ref<Image> img = p_image->duplicate();
if (img->detect_alpha()) {
@@ -68,13 +69,78 @@ static Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quali
w[1] = 'E';
w[2] = 'B';
w[3] = 'P';
- copymem(&w[4], dst_buff, dst_size);
- free(dst_buff);
+ memcpy(&w[4], dst_buff, dst_size);
+ WebPFree(dst_buff);
return dst;
}
-static Ref<Image> _webp_lossy_unpack(const Vector<uint8_t> &p_buffer) {
+static Vector<uint8_t> _webp_lossless_pack(const Ref<Image> &p_image) {
+ ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
+
+ int compression_level = ProjectSettings::get_singleton()->get("rendering/textures/lossless_compression/webp_compression_level");
+ compression_level = CLAMP(compression_level, 0, 9);
+
+ Ref<Image> img = p_image->duplicate();
+ if (img->detect_alpha()) {
+ img->convert(Image::FORMAT_RGBA8);
+ } else {
+ img->convert(Image::FORMAT_RGB8);
+ }
+
+ Size2 s(img->get_width(), img->get_height());
+ Vector<uint8_t> data = img->get_data();
+ const uint8_t *r = data.ptr();
+
+ // we need to use the more complex API in order to access the 'exact' flag...
+
+ WebPConfig config;
+ WebPPicture pic;
+ if (!WebPConfigInit(&config) || !WebPConfigLosslessPreset(&config, compression_level) || !WebPPictureInit(&pic)) {
+ ERR_FAIL_V(Vector<uint8_t>());
+ }
+
+ WebPMemoryWriter wrt;
+ config.exact = 1;
+ pic.use_argb = 1;
+ pic.width = s.width;
+ pic.height = s.height;
+ pic.writer = WebPMemoryWrite;
+ pic.custom_ptr = &wrt;
+ WebPMemoryWriterInit(&wrt);
+
+ bool success_import = false;
+ if (img->get_format() == Image::FORMAT_RGB8) {
+ success_import = WebPPictureImportRGB(&pic, r, 3 * s.width);
+ } else {
+ success_import = WebPPictureImportRGBA(&pic, r, 4 * s.width);
+ }
+ bool success_encode = false;
+ if (success_import) {
+ success_encode = WebPEncode(&config, &pic);
+ }
+ WebPPictureFree(&pic);
+
+ if (!success_encode) {
+ WebPMemoryWriterClear(&wrt);
+ ERR_FAIL_V_MSG(Vector<uint8_t>(), "WebP packing failed.");
+ }
+
+ // copy from wrt
+ Vector<uint8_t> dst;
+ dst.resize(4 + wrt.size);
+ uint8_t *w = dst.ptrw();
+ w[0] = 'W';
+ w[1] = 'E';
+ w[2] = 'B';
+ w[3] = 'P';
+ memcpy(&w[4], wrt.mem, wrt.size);
+ WebPMemoryWriterClear(&wrt);
+
+ return dst;
+}
+
+static Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) {
int size = p_buffer.size() - 4;
ERR_FAIL_COND_V(size <= 0, Ref<Image>());
const uint8_t *r = p_buffer.ptr();
@@ -139,7 +205,7 @@ Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
static Ref<Image> _webp_mem_loader_func(const uint8_t *p_png, int p_size) {
Ref<Image> img;
- img.instance();
+ img.instantiate();
Error err = webp_load_image_from_buffer(img.ptr(), p_png, p_size);
ERR_FAIL_COND_V(err, Ref<Image>());
return img;
@@ -147,7 +213,7 @@ static Ref<Image> _webp_mem_loader_func(const uint8_t *p_png, int p_size) {
Error ImageLoaderWEBP::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
Vector<uint8_t> src_image;
- int src_image_len = f->get_len();
+ uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
src_image.resize(src_image_len);
@@ -168,6 +234,7 @@ void ImageLoaderWEBP::get_recognized_extensions(List<String> *p_extensions) cons
ImageLoaderWEBP::ImageLoaderWEBP() {
Image::_webp_mem_loader_func = _webp_mem_loader_func;
- Image::lossy_packer = _webp_lossy_pack;
- Image::lossy_unpacker = _webp_lossy_unpack;
+ Image::webp_lossy_packer = _webp_lossy_pack;
+ Image::webp_lossless_packer = _webp_lossless_pack;
+ Image::webp_unpacker = _webp_unpack;
}
diff --git a/modules/webp/image_loader_webp.h b/modules/webp/image_loader_webp.h
index 49a7407600..9ea3056a19 100644
--- a/modules/webp/image_loader_webp.h
+++ b/modules/webp/image_loader_webp.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/webp/register_types.cpp b/modules/webp/register_types.cpp
index 0788b06309..ea9af72418 100644
--- a/modules/webp/register_types.cpp
+++ b/modules/webp/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/webp/register_types.h b/modules/webp/register_types.h
index d574d7be1d..59d6894bf6 100644
--- a/modules/webp/register_types.h
+++ b/modules/webp/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/webrtc/SCsub b/modules/webrtc/SCsub
index 20b4c8f8d2..e6b9959840 100644
--- a/modules/webrtc/SCsub
+++ b/modules/webrtc/SCsub
@@ -3,13 +3,10 @@
Import("env")
Import("env_modules")
-# Thirdparty source files
-
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.
+ env.AddJSLibraries(["library_godot_webrtc.js"])
env_webrtc.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/webrtc/config.py b/modules/webrtc/config.py
index 0a075ccef1..4ad918833a 100644
--- a/modules/webrtc/config.py
+++ b/modules/webrtc/config.py
@@ -10,7 +10,9 @@ def get_doc_classes():
return [
"WebRTCPeerConnection",
"WebRTCDataChannel",
- "WebRTCMultiplayer",
+ "WebRTCMultiplayerPeer",
+ "WebRTCPeerConnectionExtension",
+ "WebRTCDataChannelExtension",
]
diff --git a/modules/webrtc/doc_classes/WebRTCDataChannel.xml b/modules/webrtc/doc_classes/WebRTCDataChannel.xml
index 5c90038b9a..cf5735bab5 100644
--- a/modules/webrtc/doc_classes/WebRTCDataChannel.xml
+++ b/modules/webrtc/doc_classes/WebRTCDataChannel.xml
@@ -8,81 +8,76 @@
</tutorials>
<methods>
<method name="close">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Closes this data channel, notifying the other peer.
</description>
</method>
+ <method name="get_buffered_amount" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the number of bytes currently queued to be sent over this channel.
+ </description>
+ </method>
<method name="get_id" qualifiers="const">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Returns the id assigned to this channel during creation (or auto-assigned during negotiation).
If the channel is not negotiated out-of-band the id will only be available after the connection is established (will return [code]65535[/code] until then).
</description>
</method>
<method name="get_label" qualifiers="const">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Returns the label assigned to this channel during creation.
</description>
</method>
<method name="get_max_packet_life_time" qualifiers="const">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Returns the [code]maxPacketLifeTime[/code] value assigned to this channel during creation.
Will be [code]65535[/code] if not specified.
</description>
</method>
<method name="get_max_retransmits" qualifiers="const">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Returns the [code]maxRetransmits[/code] value assigned to this channel during creation.
Will be [code]65535[/code] if not specified.
</description>
</method>
<method name="get_protocol" qualifiers="const">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Returns the sub-protocol assigned to this channel during creation. An empty string if not specified.
</description>
</method>
<method name="get_ready_state" qualifiers="const">
- <return type="int" enum="WebRTCDataChannel.ChannelState">
- </return>
+ <return type="int" enum="WebRTCDataChannel.ChannelState" />
<description>
Returns the current state of this channel, see [enum ChannelState].
</description>
</method>
<method name="is_negotiated" qualifiers="const">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if this channel was created with out-of-band configuration.
</description>
</method>
<method name="is_ordered" qualifiers="const">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if this channel was created with ordering enabled (default).
</description>
</method>
<method name="poll">
- <return type="int" enum="Error">
- </return>
+ <return type="int" enum="Error" />
<description>
Reserved, but not used for now.
</description>
</method>
<method name="was_string_packet" qualifiers="const">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if the last received packet was transferred as text. See [member write_mode].
</description>
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/WebRTCMultiplayer.xml b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
index 5b9459bc27..a8360a4d45 100644
--- a/modules/webrtc/doc_classes/WebRTCMultiplayer.xml
+++ b/modules/webrtc/doc_classes/WebRTCMultiplayerPeer.xml
@@ -1,89 +1,71 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebRTCMultiplayer" inherits="NetworkedMultiplayerPeer" version="4.0">
+<class name="WebRTCMultiplayerPeer" inherits="MultiplayerPeer" version="4.0">
<brief_description>
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 NetworkedMultiplayerPeer.connection_succeeded] and [signal NetworkedMultiplayerPeer.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 [NetworkedMultiplayerPeer].
+ [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>
<methods>
<method name="add_peer">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="peer" type="WebRTCPeerConnection">
- </argument>
- <argument index="1" name="peer_id" type="int">
- </argument>
- <argument index="2" name="unreliable_lifetime" type="int" default="1">
- </argument>
+ <return type="int" enum="Error" />
+ <argument index="0" name="peer" type="WebRTCPeerConnection" />
+ <argument index="1" name="peer_id" type="int" />
+ <argument index="2" name="unreliable_lifetime" type="int" default="1" />
<description>
Add a new peer to the mesh with the given [code]peer_id[/code]. The [WebRTCPeerConnection] must be in state [constant WebRTCPeerConnection.STATE_NEW].
Three channels will be created for reliable, unreliable, and ordered transport. The value of [code]unreliable_lifetime[/code] will be passed to the [code]maxPacketLifetime[/code] option when creating unreliable and ordered channels (see [method WebRTCPeerConnection.create_data_channel]).
</description>
</method>
<method name="close">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Close all the add peer connections and channels, freeing all resources.
</description>
</method>
<method name="get_peer">
- <return type="Dictionary">
- </return>
- <argument index="0" name="peer_id" type="int">
- </argument>
+ <return type="Dictionary" />
+ <argument index="0" name="peer_id" type="int" />
<description>
Return a dictionary representation of the peer with given [code]peer_id[/code] with three keys. [code]connection[/code] containing the [WebRTCPeerConnection] to this peer, [code]channels[/code] an array of three [WebRTCDataChannel], and [code]connected[/code] a boolean representing if the peer connection is currently connected (all three channels are open).
</description>
</method>
<method name="get_peers">
- <return type="Dictionary">
- </return>
+ <return type="Dictionary" />
<description>
Returns a dictionary which keys are the peer ids and values the peer representation as in [method get_peer].
</description>
</method>
<method name="has_peer">
- <return type="bool">
- </return>
- <argument index="0" name="peer_id" type="int">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="peer_id" type="int" />
<description>
Returns [code]true[/code] if the given [code]peer_id[/code] is in the peers map (it might not be connected though).
</description>
</method>
<method name="initialize">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="peer_id" type="int">
- </argument>
- <argument index="1" name="server_compatibility" type="bool" default="false">
- </argument>
+ <return type="int" enum="Error" />
+ <argument index="0" name="peer_id" type="int" />
+ <argument index="1" name="server_compatibility" type="bool" default="false" />
+ <argument index="2" name="channels_config" type="Array" default="[]" />
<description>
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 NetworkedMultiplayerPeer.CONNECTION_CONNECTED] and [signal NetworkedMultiplayerPeer.connection_succeeded] will not be emitted.
- If [code]server_compatibilty[/code] is [code]true[/code] the peer will suppress all [signal NetworkedMultiplayerPeer.peer_connected] signals until a peer with id [constant NetworkedMultiplayerPeer.TARGET_PEER_SERVER] connects and then emit [signal NetworkedMultiplayerPeer.connection_succeeded]. After that the signal [signal NetworkedMultiplayerPeer.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 NetworkedMultiplayerPeer.server_disconnected] will be emitted and state will become [constant NetworkedMultiplayerPeer.CONNECTION_CONNECTED].
+ 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 TransferMode] which will be used to create extra channels (WebRTC only supports one transfer mode per channel).
</description>
</method>
<method name="remove_peer">
- <return type="void">
- </return>
- <argument index="0" name="peer_id" type="int">
- </argument>
+ <return type="void" />
+ <argument index="0" name="peer_id" type="int" />
<description>
- Remove the peer with given [code]peer_id[/code] from the mesh. If the peer was connected, and [signal NetworkedMultiplayerPeer.peer_connected] was emitted for it, then [signal NetworkedMultiplayerPeer.peer_disconnected] will be emitted.
+ Remove the peer with given [code]peer_id[/code] from the mesh. If the peer was connected, and [signal MultiplayerPeer.peer_connected] was emitted for it, then [signal MultiplayerPeer.peer_disconnected] will be emitted.
</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="NetworkedMultiplayerPeer.TransferMode" default="2" />
- </members>
- <constants>
- </constants>
</class>
diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
index c80b903e39..618fe14137 100644
--- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
+++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebRTCPeerConnection" inherits="Reference" version="4.0">
+<class name="WebRTCPeerConnection" inherits="RefCounted" version="4.0">
<brief_description>
Interface to a WebRTC peer connection.
</brief_description>
@@ -15,41 +15,33 @@
</tutorials>
<methods>
<method name="add_ice_candidate">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="media" type="String">
- </argument>
- <argument index="1" name="index" type="int">
- </argument>
- <argument index="2" name="name" type="String">
- </argument>
+ <return type="int" enum="Error" />
+ <argument index="0" name="media" type="String" />
+ <argument index="1" name="index" type="int" />
+ <argument index="2" name="name" type="String" />
<description>
Add an ice candidate generated by a remote peer (and received over the signaling server). See [signal ice_candidate_created].
</description>
</method>
<method name="close">
- <return type="void">
- </return>
+ <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">
- <return type="WebRTCDataChannel">
- </return>
- <argument index="0" name="label" type="String">
- </argument>
+ <return type="WebRTCDataChannel" />
+ <argument index="0" name="label" type="String" />
<argument index="1" name="options" type="Dictionary" default="{
-
-}">
- </argument>
+}" />
<description>
Returns a new [WebRTCDataChannel] (or [code]null[/code] on failure) with given [code]label[/code] and optionally configured via the [code]options[/code] dictionary. This method can only be called when the connection is in state [constant STATE_NEW].
There are two ways to create a working data channel: either call [method create_data_channel] on only one of the peer and listen to [signal data_channel_received] on the other, or call [method create_data_channel] on both peers, with the same values, and the [code]negotiated[/code] option set to [code]true[/code].
Valid [code]options[/code] are:
[codeblock]
{
- "negotiated": true, # When set to true (default off), means the channel is negotiated out of band. "id" must be set too. data_channel_received will not be called.
+ "negotiated": true, # When set to true (default off), means the channel is negotiated out of band. "id" must be set too. "data_channel_received" will not be called.
"id": 1, # When "negotiated" is true this value must also be set to the same value on both peer.
# Only one of maxRetransmits and maxPacketLifeTime can be specified, not both. They make the channel unreliable (but also better at real time).
@@ -64,27 +56,22 @@
</description>
</method>
<method name="create_offer">
- <return type="int" enum="Error">
- </return>
+ <return type="int" enum="Error" />
<description>
Creates a new SDP offer to start a WebRTC connection with a remote peer. At least one [WebRTCDataChannel] must have been created before calling this method.
If this functions returns [constant OK], [signal session_description_created] will be called when the session is ready to be sent.
</description>
</method>
<method name="get_connection_state" qualifiers="const">
- <return type="int" enum="WebRTCPeerConnection.ConnectionState">
- </return>
+ <return type="int" enum="WebRTCPeerConnection.ConnectionState" />
<description>
Returns the connection state. See [enum ConnectionState].
</description>
</method>
<method name="initialize">
- <return type="int" enum="Error">
- </return>
+ <return type="int" enum="Error" />
<argument index="0" name="configuration" type="Dictionary" default="{
-
-}">
- </argument>
+}" />
<description>
Re-initialize this peer connection, closing any previously active connection, and going back to state [constant STATE_NEW]. A dictionary of [code]options[/code] can be passed to configure the peer connection.
Valid [code]options[/code] are:
@@ -105,31 +92,24 @@
</description>
</method>
<method name="poll">
- <return type="int" enum="Error">
- </return>
+ <return type="int" enum="Error" />
<description>
Call this method frequently (e.g. in [method Node._process] or [method Node._physics_process]) to properly receive signals.
</description>
</method>
<method name="set_local_description">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="type" type="String">
- </argument>
- <argument index="1" name="sdp" type="String">
- </argument>
+ <return type="int" enum="Error" />
+ <argument index="0" name="type" type="String" />
+ <argument index="1" name="sdp" type="String" />
<description>
Sets the SDP description of the local peer. This should be called in response to [signal session_description_created].
After calling this function the peer will start emitting [signal ice_candidate_created] (unless an [enum Error] different from [constant OK] is returned).
</description>
</method>
<method name="set_remote_description">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="type" type="String">
- </argument>
- <argument index="1" name="sdp" type="String">
- </argument>
+ <return type="int" enum="Error" />
+ <argument index="0" name="type" type="String" />
+ <argument index="1" name="sdp" type="String" />
<description>
Sets the SDP description of the remote peer. This should be called with the values generated by a remote peer and received over the signaling server.
If [code]type[/code] is [code]offer[/code] the peer will emit [signal session_description_created] with the appropriate answer.
@@ -139,29 +119,23 @@
</methods>
<signals>
<signal name="data_channel_received">
- <argument index="0" name="channel" type="Object">
- </argument>
+ <argument index="0" name="channel" type="Object" />
<description>
Emitted when a new in-band channel is received, i.e. when the channel was created with [code]negotiated: false[/code] (default).
The object will be an instance of [WebRTCDataChannel]. You must keep a reference of it or it will be closed automatically. See [method create_data_channel].
</description>
</signal>
<signal name="ice_candidate_created">
- <argument index="0" name="media" type="String">
- </argument>
- <argument index="1" name="index" type="int">
- </argument>
- <argument index="2" name="name" type="String">
- </argument>
+ <argument index="0" name="media" type="String" />
+ <argument index="1" name="index" type="int" />
+ <argument index="2" name="name" type="String" />
<description>
Emitted when a new ICE candidate has been created. The three parameters are meant to be passed to the remote peer over the signaling server.
</description>
</signal>
<signal name="session_description_created">
- <argument index="0" name="type" type="String">
- </argument>
- <argument index="1" name="sdp" type="String">
- </argument>
+ <argument index="0" name="type" type="String" />
+ <argument index="1" name="sdp" type="String" />
<description>
Emitted after a successful call to [method create_offer] or [method set_remote_description] (when it generates an answer). The parameters are meant to be passed to [method set_local_description] on this object, and sent to the remote peer over the signaling server.
</description>
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/library_godot_webrtc.js b/modules/webrtc/library_godot_webrtc.js
new file mode 100644
index 0000000000..a0a6c21be3
--- /dev/null
+++ b/modules/webrtc/library_godot_webrtc.js
@@ -0,0 +1,438 @@
+/*************************************************************************/
+/* library_godot_webrtc.js */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+const GodotRTCDataChannel = {
+ // Our socket implementation that forwards events to C++.
+ $GodotRTCDataChannel__deps: ['$IDHandler', '$GodotRuntime'],
+ $GodotRTCDataChannel: {
+ connect: function (p_id, p_on_open, p_on_message, p_on_error, p_on_close) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+
+ ref.binaryType = 'arraybuffer';
+ ref.onopen = function (event) {
+ p_on_open();
+ };
+ ref.onclose = function (event) {
+ p_on_close();
+ };
+ ref.onerror = function (event) {
+ p_on_error();
+ };
+ ref.onmessage = function (event) {
+ let buffer;
+ let is_string = 0;
+ if (event.data instanceof ArrayBuffer) {
+ buffer = new Uint8Array(event.data);
+ } else if (event.data instanceof Blob) {
+ GodotRuntime.error('Blob type not supported');
+ return;
+ } else if (typeof event.data === 'string') {
+ is_string = 1;
+ const enc = new TextEncoder('utf-8');
+ buffer = new Uint8Array(enc.encode(event.data));
+ } else {
+ GodotRuntime.error('Unknown message type');
+ return;
+ }
+ const len = buffer.length * buffer.BYTES_PER_ELEMENT;
+ const out = GodotRuntime.malloc(len);
+ HEAPU8.set(buffer, out);
+ p_on_message(out, len, is_string);
+ GodotRuntime.free(out);
+ };
+ },
+
+ close: function (p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ ref.onopen = null;
+ ref.onmessage = null;
+ ref.onerror = null;
+ ref.onclose = null;
+ ref.close();
+ },
+
+ get_prop: function (p_id, p_prop, p_def) {
+ const ref = IDHandler.get(p_id);
+ return (ref && ref[p_prop] !== undefined) ? ref[p_prop] : p_def;
+ },
+ },
+
+ godot_js_rtc_datachannel_ready_state_get__sig: 'ii',
+ godot_js_rtc_datachannel_ready_state_get: function (p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return 3; // CLOSED
+ }
+
+ switch (ref.readyState) {
+ case 'connecting':
+ return 0;
+ case 'open':
+ return 1;
+ case 'closing':
+ return 2;
+ case 'closed':
+ default:
+ return 3;
+ }
+ },
+
+ godot_js_rtc_datachannel_send__sig: 'iiiii',
+ godot_js_rtc_datachannel_send: function (p_id, p_buffer, p_length, p_raw) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return 1;
+ }
+
+ const bytes_array = new Uint8Array(p_length);
+ for (let i = 0; i < p_length; i++) {
+ bytes_array[i] = GodotRuntime.getHeapValue(p_buffer + i, 'i8');
+ }
+
+ if (p_raw) {
+ ref.send(bytes_array.buffer);
+ } else {
+ const string = new TextDecoder('utf-8').decode(bytes_array);
+ ref.send(string);
+ }
+ return 0;
+ },
+
+ godot_js_rtc_datachannel_is_ordered__sig: 'ii',
+ godot_js_rtc_datachannel_is_ordered: function (p_id) {
+ return GodotRTCDataChannel.get_prop(p_id, 'ordered', true);
+ },
+
+ godot_js_rtc_datachannel_id_get__sig: 'ii',
+ godot_js_rtc_datachannel_id_get: function (p_id) {
+ return GodotRTCDataChannel.get_prop(p_id, 'id', 65535);
+ },
+
+ godot_js_rtc_datachannel_max_packet_lifetime_get__sig: 'ii',
+ godot_js_rtc_datachannel_max_packet_lifetime_get: function (p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return 65535;
+ }
+ if (ref['maxPacketLifeTime'] !== undefined) {
+ return ref['maxPacketLifeTime'];
+ } else if (ref['maxRetransmitTime'] !== undefined) {
+ // Guess someone didn't appreciate the standardization process.
+ return ref['maxRetransmitTime'];
+ }
+ return 65535;
+ },
+
+ godot_js_rtc_datachannel_max_retransmits_get__sig: 'ii',
+ godot_js_rtc_datachannel_max_retransmits_get: function (p_id) {
+ return GodotRTCDataChannel.get_prop(p_id, 'maxRetransmits', 65535);
+ },
+
+ godot_js_rtc_datachannel_is_negotiated__sig: 'ii',
+ godot_js_rtc_datachannel_is_negotiated: function (p_id) {
+ return GodotRTCDataChannel.get_prop(p_id, 'negotiated', 65535);
+ },
+
+ godot_js_rtc_datachannel_get_buffered_amount__sig: 'ii',
+ godot_js_rtc_datachannel_get_buffered_amount: function (p_id) {
+ return GodotRTCDataChannel.get_prop(p_id, 'bufferedAmount', 0);
+ },
+
+ godot_js_rtc_datachannel_label_get__sig: 'ii',
+ godot_js_rtc_datachannel_label_get: function (p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref || !ref.label) {
+ return 0;
+ }
+ return GodotRuntime.allocString(ref.label);
+ },
+
+ godot_js_rtc_datachannel_protocol_get__sig: 'ii',
+ godot_js_rtc_datachannel_protocol_get: function (p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref || !ref.protocol) {
+ return 0;
+ }
+ return GodotRuntime.allocString(ref.protocol);
+ },
+
+ godot_js_rtc_datachannel_destroy__sig: 'vi',
+ godot_js_rtc_datachannel_destroy: function (p_id) {
+ GodotRTCDataChannel.close(p_id);
+ IDHandler.remove(p_id);
+ },
+
+ godot_js_rtc_datachannel_connect__sig: 'viiiiii',
+ godot_js_rtc_datachannel_connect: function (p_id, p_ref, p_on_open, p_on_message, p_on_error, p_on_close) {
+ const onopen = GodotRuntime.get_func(p_on_open).bind(null, p_ref);
+ const onmessage = GodotRuntime.get_func(p_on_message).bind(null, p_ref);
+ const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_ref);
+ const onclose = GodotRuntime.get_func(p_on_close).bind(null, p_ref);
+ GodotRTCDataChannel.connect(p_id, onopen, onmessage, onerror, onclose);
+ },
+
+ godot_js_rtc_datachannel_close__sig: 'vi',
+ godot_js_rtc_datachannel_close: function (p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ GodotRTCDataChannel.close(p_id);
+ },
+};
+
+autoAddDeps(GodotRTCDataChannel, '$GodotRTCDataChannel');
+mergeInto(LibraryManager.library, GodotRTCDataChannel);
+
+const GodotRTCPeerConnection = {
+ $GodotRTCPeerConnection__deps: ['$IDHandler', '$GodotRuntime', '$GodotRTCDataChannel'],
+ $GodotRTCPeerConnection: {
+ onstatechange: function (p_id, p_conn, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ let state;
+ switch (p_conn.iceConnectionState) {
+ case 'new':
+ state = 0;
+ break;
+ case 'checking':
+ state = 1;
+ break;
+ case 'connected':
+ case 'completed':
+ state = 2;
+ break;
+ case 'disconnected':
+ state = 3;
+ break;
+ case 'failed':
+ state = 4;
+ break;
+ case 'closed':
+ default:
+ state = 5;
+ break;
+ }
+ callback(state);
+ },
+
+ onicecandidate: function (p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref || !event.candidate) {
+ return;
+ }
+
+ const c = event.candidate;
+ const candidate_str = GodotRuntime.allocString(c.candidate);
+ const mid_str = GodotRuntime.allocString(c.sdpMid);
+ callback(mid_str, c.sdpMLineIndex, candidate_str);
+ GodotRuntime.free(candidate_str);
+ GodotRuntime.free(mid_str);
+ },
+
+ ondatachannel: function (p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+
+ const cid = IDHandler.add(event.channel);
+ callback(cid);
+ },
+
+ onsession: function (p_id, callback, session) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ const type_str = GodotRuntime.allocString(session.type);
+ const sdp_str = GodotRuntime.allocString(session.sdp);
+ callback(type_str, sdp_str);
+ GodotRuntime.free(type_str);
+ GodotRuntime.free(sdp_str);
+ },
+
+ onerror: function (p_id, callback, error) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ GodotRuntime.error(error);
+ callback();
+ },
+ },
+
+ godot_js_rtc_pc_create__sig: 'iiiiii',
+ godot_js_rtc_pc_create: function (p_config, p_ref, p_on_state_change, p_on_candidate, p_on_datachannel) {
+ const onstatechange = GodotRuntime.get_func(p_on_state_change).bind(null, p_ref);
+ const oncandidate = GodotRuntime.get_func(p_on_candidate).bind(null, p_ref);
+ const ondatachannel = GodotRuntime.get_func(p_on_datachannel).bind(null, p_ref);
+
+ const config = JSON.parse(GodotRuntime.parseString(p_config));
+ let conn = null;
+ try {
+ conn = new RTCPeerConnection(config);
+ } catch (e) {
+ GodotRuntime.error(e);
+ return 0;
+ }
+
+ const base = GodotRTCPeerConnection;
+ const id = IDHandler.add(conn);
+ conn.oniceconnectionstatechange = base.onstatechange.bind(null, id, conn, onstatechange);
+ conn.onicecandidate = base.onicecandidate.bind(null, id, oncandidate);
+ conn.ondatachannel = base.ondatachannel.bind(null, id, ondatachannel);
+ return id;
+ },
+
+ godot_js_rtc_pc_close__sig: 'vi',
+ godot_js_rtc_pc_close: function (p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ ref.close();
+ },
+
+ godot_js_rtc_pc_destroy__sig: 'vi',
+ godot_js_rtc_pc_destroy: function (p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ ref.oniceconnectionstatechange = null;
+ ref.onicecandidate = null;
+ ref.ondatachannel = null;
+ IDHandler.remove(p_id);
+ },
+
+ godot_js_rtc_pc_offer_create__sig: 'viiii',
+ godot_js_rtc_pc_offer_create: function (p_id, p_obj, p_on_session, p_on_error) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ const onsession = GodotRuntime.get_func(p_on_session).bind(null, p_obj);
+ const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
+ ref.createOffer().then(function (session) {
+ GodotRTCPeerConnection.onsession(p_id, onsession, session);
+ }).catch(function (error) {
+ GodotRTCPeerConnection.onerror(p_id, onerror, error);
+ });
+ },
+
+ godot_js_rtc_pc_local_description_set__sig: 'viiiii',
+ godot_js_rtc_pc_local_description_set: function (p_id, p_type, p_sdp, p_obj, p_on_error) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ const type = GodotRuntime.parseString(p_type);
+ const sdp = GodotRuntime.parseString(p_sdp);
+ const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
+ ref.setLocalDescription({
+ 'sdp': sdp,
+ 'type': type,
+ }).catch(function (error) {
+ GodotRTCPeerConnection.onerror(p_id, onerror, error);
+ });
+ },
+
+ godot_js_rtc_pc_remote_description_set__sig: 'viiiiii',
+ godot_js_rtc_pc_remote_description_set: function (p_id, p_type, p_sdp, p_obj, p_session_created, p_on_error) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ const type = GodotRuntime.parseString(p_type);
+ const sdp = GodotRuntime.parseString(p_sdp);
+ const onerror = GodotRuntime.get_func(p_on_error).bind(null, p_obj);
+ const onsession = GodotRuntime.get_func(p_session_created).bind(null, p_obj);
+ ref.setRemoteDescription({
+ 'sdp': sdp,
+ 'type': type,
+ }).then(function () {
+ if (type !== 'offer') {
+ return Promise.resolve();
+ }
+ return ref.createAnswer().then(function (session) {
+ GodotRTCPeerConnection.onsession(p_id, onsession, session);
+ });
+ }).catch(function (error) {
+ GodotRTCPeerConnection.onerror(p_id, onerror, error);
+ });
+ },
+
+ godot_js_rtc_pc_ice_candidate_add__sig: 'viiii',
+ godot_js_rtc_pc_ice_candidate_add: function (p_id, p_mid_name, p_mline_idx, p_sdp) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ const sdpMidName = GodotRuntime.parseString(p_mid_name);
+ const sdpName = GodotRuntime.parseString(p_sdp);
+ ref.addIceCandidate(new RTCIceCandidate({
+ 'candidate': sdpName,
+ 'sdpMid': sdpMidName,
+ 'sdpMlineIndex': p_mline_idx,
+ }));
+ },
+
+ godot_js_rtc_pc_datachannel_create__deps: ['$GodotRTCDataChannel'],
+ godot_js_rtc_pc_datachannel_create__sig: 'iiii',
+ godot_js_rtc_pc_datachannel_create: function (p_id, p_label, p_config) {
+ try {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return 0;
+ }
+
+ const label = GodotRuntime.parseString(p_label);
+ const config = JSON.parse(GodotRuntime.parseString(p_config));
+
+ const channel = ref.createDataChannel(label, config);
+ return IDHandler.add(channel);
+ } catch (e) {
+ GodotRuntime.error(e);
+ return 0;
+ }
+ },
+};
+
+autoAddDeps(GodotRTCPeerConnection, '$GodotRTCPeerConnection');
+mergeInto(LibraryManager.library, GodotRTCPeerConnection);
diff --git a/modules/webrtc/register_types.cpp b/modules/webrtc/register_types.cpp
index 5b296b1ac6..8110e4a048 100644
--- a/modules/webrtc/register_types.cpp
+++ b/modules/webrtc/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,19 +29,13 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/project_settings.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.h"
+#include "webrtc_data_channel_extension.h"
+#include "webrtc_peer_connection_extension.h"
void register_webrtc_types() {
#define _SET_HINT(NAME, _VAL_, _MAX_) \
@@ -50,19 +44,13 @@ 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
- ClassDB::register_class<WebRTCPeerConnectionGDNative>();
- ClassDB::register_class<WebRTCDataChannelGDNative>();
-#endif
- ClassDB::register_virtual_class<WebRTCDataChannel>();
- ClassDB::register_class<WebRTCMultiplayer>();
+ GDREGISTER_CLASS(WebRTCPeerConnectionExtension);
+
+ GDREGISTER_VIRTUAL_CLASS(WebRTCDataChannel);
+ GDREGISTER_CLASS(WebRTCDataChannelExtension);
+
+ GDREGISTER_CLASS(WebRTCMultiplayerPeer);
}
void unregister_webrtc_types() {}
diff --git a/modules/webrtc/register_types.h b/modules/webrtc/register_types.h
index 8f5b9e8452..710ee88a28 100644
--- a/modules/webrtc/register_types.h
+++ b/modules/webrtc/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/webrtc/webrtc_data_channel.cpp b/modules/webrtc/webrtc_data_channel.cpp
index 7566532982..ca520a733d 100644
--- a/modules/webrtc/webrtc_data_channel.cpp
+++ b/modules/webrtc/webrtc_data_channel.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,7 +29,7 @@
/*************************************************************************/
#include "webrtc_data_channel.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
void WebRTCDataChannel::_bind_methods() {
ClassDB::bind_method(D_METHOD("poll"), &WebRTCDataChannel::poll);
@@ -46,6 +46,7 @@ void WebRTCDataChannel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_max_retransmits"), &WebRTCDataChannel::get_max_retransmits);
ClassDB::bind_method(D_METHOD("get_protocol"), &WebRTCDataChannel::get_protocol);
ClassDB::bind_method(D_METHOD("is_negotiated"), &WebRTCDataChannel::is_negotiated);
+ ClassDB::bind_method(D_METHOD("get_buffered_amount"), &WebRTCDataChannel::get_buffered_amount);
ADD_PROPERTY(PropertyInfo(Variant::INT, "write_mode", PROPERTY_HINT_ENUM), "set_write_mode", "get_write_mode");
diff --git a/modules/webrtc/webrtc_data_channel.h b/modules/webrtc/webrtc_data_channel.h
index 1407f1e3bd..809d35c6e3 100644
--- a/modules/webrtc/webrtc_data_channel.h
+++ b/modules/webrtc/webrtc_data_channel.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -70,6 +70,8 @@ public:
virtual String get_protocol() const = 0;
virtual bool is_negotiated() const = 0;
+ virtual int get_buffered_amount() const = 0;
+
virtual Error poll() = 0;
virtual void close() = 0;
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 03396d207d..eec96b4c62 100644
--- a/modules/webrtc/webrtc_data_channel_gdnative.h
+++ b/modules/webrtc/webrtc_data_channel_extension.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_data_channel_gdnative.h */
+/* webrtc_data_channel_extension.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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;
@@ -60,6 +56,7 @@ public:
virtual int get_max_retransmits() const override;
virtual String get_protocol() const override;
virtual bool is_negotiated() const override;
+ virtual int get_buffered_amount() const override;
virtual Error poll() override;
virtual void close() override;
@@ -71,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 67ad2c07ce..0000000000
--- a/modules/webrtc/webrtc_data_channel_gdnative.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/*************************************************************************/
-/* webrtc_data_channel_gdnative.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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);
-}
-
-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_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp
index 2c648ba9f9..31d6a0568c 100644
--- a/modules/webrtc/webrtc_data_channel_js.cpp
+++ b/modules/webrtc/webrtc_data_channel_js.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,65 +34,59 @@
#include "emscripten.h"
extern "C" {
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_error(void *obj) {
- WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
- peer->_on_error();
-}
+typedef void (*RTCChOnOpen)(void *p_obj);
+typedef void (*RTCChOnMessage)(void *p_obj, const uint8_t *p_buffer, int p_size, int p_is_string);
+typedef void (*RTCChOnClose)(void *p_obj);
+typedef void (*RTCChOnError)(void *p_obj);
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_open(void *obj) {
- WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
- peer->_on_open();
+extern int godot_js_rtc_datachannel_ready_state_get(int p_id);
+extern int godot_js_rtc_datachannel_send(int p_id, const uint8_t *p_buffer, int p_length, int p_raw);
+extern int godot_js_rtc_datachannel_is_ordered(int p_id);
+extern int godot_js_rtc_datachannel_id_get(int p_id);
+extern int godot_js_rtc_datachannel_max_packet_lifetime_get(int p_id);
+extern int godot_js_rtc_datachannel_max_retransmits_get(int p_id);
+extern int godot_js_rtc_datachannel_is_negotiated(int p_id);
+extern int godot_js_rtc_datachannel_get_buffered_amount(int p_id);
+extern char *godot_js_rtc_datachannel_label_get(int p_id); // Must free the returned string.
+extern char *godot_js_rtc_datachannel_protocol_get(int p_id); // Must free the returned string.
+extern void godot_js_rtc_datachannel_destroy(int p_id);
+extern void godot_js_rtc_datachannel_connect(int p_id, void *p_obj, RTCChOnOpen p_on_open, RTCChOnMessage p_on_message, RTCChOnError p_on_error, RTCChOnClose p_on_close);
+extern void godot_js_rtc_datachannel_close(int p_id);
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_close(void *obj) {
- WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
- peer->_on_close();
+void WebRTCDataChannelJS::_on_open(void *p_obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
+ peer->in_buffer.resize(peer->_in_buffer_shift);
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ch_message(void *obj, uint8_t *p_data, uint32_t p_size, bool p_is_string) {
- WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(obj);
- peer->_on_message(p_data, p_size, p_is_string);
-}
+void WebRTCDataChannelJS::_on_close(void *p_obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
+ peer->close();
}
-void WebRTCDataChannelJS::_on_open() {
- in_buffer.resize(_in_buffer_shift);
+void WebRTCDataChannelJS::_on_error(void *p_obj) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
+ peer->close();
}
-void WebRTCDataChannelJS::_on_close() {
- close();
-}
+void WebRTCDataChannelJS::_on_message(void *p_obj, const uint8_t *p_data, int p_size, int p_is_string) {
+ WebRTCDataChannelJS *peer = static_cast<WebRTCDataChannelJS *>(p_obj);
+ RingBuffer<uint8_t> &in_buffer = peer->in_buffer;
-void WebRTCDataChannelJS::_on_error() {
- close();
-}
-
-void WebRTCDataChannelJS::_on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
ERR_FAIL_COND_MSG(in_buffer.space_left() < (int)(p_size + 5), "Buffer full! Dropping data.");
uint8_t is_string = p_is_string ? 1 : 0;
in_buffer.write((uint8_t *)&p_size, 4);
in_buffer.write((uint8_t *)&is_string, 1);
in_buffer.write(p_data, p_size);
- queue_count++;
+ peer->queue_count++;
}
void WebRTCDataChannelJS::close() {
in_buffer.resize(0);
queue_count = 0;
_was_string = false;
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- if (!dict) return;
- var channel = dict["channel"];
- channel.onopen = null;
- channel.onclose = null;
- channel.onerror = null;
- channel.onmessage = null;
- channel.close();
- }, _js_id);
- /* clang-format on */
+ godot_js_rtc_datachannel_close(_js_id);
}
Error WebRTCDataChannelJS::poll() {
@@ -100,24 +94,7 @@ Error WebRTCDataChannelJS::poll() {
}
WebRTCDataChannelJS::ChannelState WebRTCDataChannelJS::get_ready_state() const {
- /* clang-format off */
- return (ChannelState) EM_ASM_INT({
- var dict = Module.IDHandler.get($0);
- if (!dict) return 3; // CLOSED
- var channel = dict["channel"];
- switch(channel.readyState) {
- case "connecting":
- return 0;
- case "open":
- return 1;
- case "closing":
- return 2;
- case "closed":
- return 3;
- }
- return 3; // CLOSED
- }, _js_id);
- /* clang-format on */
+ return (ChannelState)godot_js_rtc_datachannel_ready_state_get(_js_id);
}
int WebRTCDataChannelJS::get_available_packet_count() const {
@@ -157,27 +134,7 @@ Error WebRTCDataChannelJS::put_packet(const uint8_t *p_buffer, int p_buffer_size
ERR_FAIL_COND_V(get_ready_state() != STATE_OPEN, ERR_UNCONFIGURED);
int is_bin = _write_mode == WebRTCDataChannel::WRITE_MODE_BINARY ? 1 : 0;
-
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var channel = dict["channel"];
- var bytes_array = new Uint8Array($2);
- var i = 0;
-
- for(i=0; i<$2; i++) {
- bytes_array[i] = getValue($1+i, 'i8');
- }
-
- if ($3) {
- channel.send(bytes_array.buffer);
- } else {
- var string = new TextDecoder("utf-8").decode(bytes_array);
- channel.send(string);
- }
- }, _js_id, p_buffer, p_buffer_size, is_bin);
- /* clang-format on */
-
+ godot_js_rtc_datachannel_send(_js_id, p_buffer, p_buffer_size, is_bin);
return OK;
}
@@ -201,46 +158,20 @@ String WebRTCDataChannelJS::get_label() const {
return _label;
}
-/* clang-format off */
-#define _JS_GET(PROP, DEF) \
-EM_ASM_INT({ \
- var dict = Module.IDHandler.get($0); \
- if (!dict || !dict["channel"]) { \
- return DEF; \
- } \
- var out = dict["channel"].PROP; \
- return out === null ? DEF : out; \
-}, _js_id)
-/* clang-format on */
-
bool WebRTCDataChannelJS::is_ordered() const {
- return _JS_GET(ordered, true);
+ return godot_js_rtc_datachannel_is_ordered(_js_id);
}
int WebRTCDataChannelJS::get_id() const {
- return _JS_GET(id, 65535);
+ return godot_js_rtc_datachannel_id_get(_js_id);
}
int WebRTCDataChannelJS::get_max_packet_life_time() const {
- // Can't use macro, webkit workaround.
- /* clang-format off */
- return EM_ASM_INT({
- var dict = Module.IDHandler.get($0);
- if (!dict || !dict["channel"]) {
- return 65535;
- }
- if (dict["channel"].maxRetransmitTime !== undefined) {
- // Guess someone didn't appreciate the standardization process.
- return dict["channel"].maxRetransmitTime;
- }
- var out = dict["channel"].maxPacketLifeTime;
- return out === null ? 65535 : out;
- }, _js_id);
- /* clang-format on */
+ return godot_js_rtc_datachannel_max_packet_lifetime_get(_js_id);
}
int WebRTCDataChannelJS::get_max_retransmits() const {
- return _JS_GET(maxRetransmits, 65535);
+ return godot_js_rtc_datachannel_max_retransmits_get(_js_id);
}
String WebRTCDataChannelJS::get_protocol() const {
@@ -248,117 +179,35 @@ String WebRTCDataChannelJS::get_protocol() const {
}
bool WebRTCDataChannelJS::is_negotiated() const {
- return _JS_GET(negotiated, false);
+ return godot_js_rtc_datachannel_is_negotiated(_js_id);
+}
+
+int WebRTCDataChannelJS::get_buffered_amount() const {
+ return godot_js_rtc_datachannel_get_buffered_amount(_js_id);
}
WebRTCDataChannelJS::WebRTCDataChannelJS() {
- queue_count = 0;
- _was_string = false;
- _write_mode = WRITE_MODE_BINARY;
- _js_id = 0;
}
WebRTCDataChannelJS::WebRTCDataChannelJS(int js_id) {
- queue_count = 0;
- _was_string = false;
- _write_mode = WRITE_MODE_BINARY;
_js_id = js_id;
- /* clang-format off */
- EM_ASM({
- var c_ptr = $0;
- var dict = Module.IDHandler.get($1);
- if (!dict) return;
- var channel = dict["channel"];
- dict["ptr"] = c_ptr;
-
- channel.binaryType = "arraybuffer";
- channel.onopen = function (evt) {
- ccall("_emrtc_on_ch_open",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- channel.onclose = function (evt) {
- ccall("_emrtc_on_ch_close",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- channel.onerror = function (evt) {
- ccall("_emrtc_on_ch_error",
- "void",
- ["number"],
- [c_ptr]
- );
- };
- channel.onmessage = function(event) {
- var buffer;
- var is_string = 0;
- if (event.data instanceof ArrayBuffer) {
- buffer = new Uint8Array(event.data);
- } else if (event.data instanceof Blob) {
- console.error("Blob type not supported");
- return;
- } else if (typeof event.data === "string") {
- is_string = 1;
- var enc = new TextEncoder("utf-8");
- buffer = new Uint8Array(enc.encode(event.data));
- } else {
- console.error("Unknown message type");
- return;
- }
- var len = buffer.length*buffer.BYTES_PER_ELEMENT;
- var out = _malloc(len);
- HEAPU8.set(buffer, out);
- ccall("_emrtc_on_ch_message",
- "void",
- ["number", "number", "number", "number"],
- [c_ptr, out, len, is_string]
- );
- _free(out);
- }
-
- }, this, js_id);
+ godot_js_rtc_datachannel_connect(js_id, this, &_on_open, &_on_message, &_on_error, &_on_close);
// Parse label
- char *str;
- str = (char *)EM_ASM_INT({
- var dict = Module.IDHandler.get($0);
- if (!dict || !dict["channel"]) return 0;
- var str = dict["channel"].label;
- var len = lengthBytesUTF8(str)+1;
- var ptr = _malloc(str);
- stringToUTF8(str, ptr, len+1);
- return ptr;
- }, js_id);
- if(str != nullptr) {
- _label.parse_utf8(str);
- EM_ASM({ _free($0) }, str);
+ char *label = godot_js_rtc_datachannel_label_get(js_id);
+ if (label) {
+ _label.parse_utf8(label);
+ free(label);
}
- str = (char *)EM_ASM_INT({
- var dict = Module.IDHandler.get($0);
- if (!dict || !dict["channel"]) return 0;
- var str = dict["channel"].protocol;
- var len = lengthBytesUTF8(str)+1;
- var ptr = _malloc(str);
- stringToUTF8(str, ptr, len+1);
- return ptr;
- }, js_id);
- if(str != nullptr) {
- _protocol.parse_utf8(str);
- EM_ASM({ _free($0) }, str);
+ char *protocol = godot_js_rtc_datachannel_protocol_get(js_id);
+ if (protocol) {
+ _protocol.parse_utf8(protocol);
+ free(protocol);
}
- /* clang-format on */
}
WebRTCDataChannelJS::~WebRTCDataChannelJS() {
close();
- /* clang-format off */
- EM_ASM({
- Module.IDHandler.remove($0);
- }, _js_id);
- /* clang-format on */
-};
+ godot_js_rtc_datachannel_destroy(_js_id);
+}
#endif
diff --git a/modules/webrtc/webrtc_data_channel_js.h b/modules/webrtc/webrtc_data_channel_js.h
index 7545910e66..5cd6a32ed9 100644
--- a/modules/webrtc/webrtc_data_channel_js.h
+++ b/modules/webrtc/webrtc_data_channel_js.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -42,24 +42,24 @@ private:
String _label;
String _protocol;
- bool _was_string;
- WriteMode _write_mode;
+ bool _was_string = false;
+ WriteMode _write_mode = WRITE_MODE_BINARY;
enum {
PACKET_BUFFER_SIZE = 65536 - 5 // 4 bytes for the size, 1 for for type
};
- int _js_id;
+ int _js_id = 0;
RingBuffer<uint8_t> in_buffer;
- int queue_count;
+ int queue_count = 0;
uint8_t packet_buffer[PACKET_BUFFER_SIZE];
-public:
- void _on_open();
- void _on_close();
- void _on_error();
- void _on_message(uint8_t *p_data, uint32_t p_size, bool p_is_string);
+ static void _on_open(void *p_obj);
+ static void _on_close(void *p_obj);
+ static void _on_error(void *p_obj);
+ static void _on_message(void *p_obj, const uint8_t *p_data, int p_size, int p_is_string);
+public:
virtual void set_write_mode(WriteMode mode) override;
virtual WriteMode get_write_mode() const override;
virtual bool was_string_packet() const override;
@@ -72,6 +72,7 @@ public:
virtual int get_max_retransmits() const override;
virtual String get_protocol() const override;
virtual bool is_negotiated() const override;
+ virtual int get_buffered_amount() const override;
virtual Error poll() override;
virtual void close() override;
diff --git a/modules/webrtc/webrtc_multiplayer.cpp b/modules/webrtc/webrtc_multiplayer_peer.cpp
index e0c0cad68c..133bd71ddb 100644
--- a/modules/webrtc/webrtc_multiplayer.cpp
+++ b/modules/webrtc/webrtc_multiplayer_peer.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_multiplayer.cpp */
+/* webrtc_multiplayer_peer.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,51 +28,43 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "webrtc_multiplayer.h"
+#include "webrtc_multiplayer_peer.h"
#include "core/io/marshalls.h"
#include "core/os/os.h"
-void WebRTCMultiplayer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility"), &WebRTCMultiplayer::initialize, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayer::add_peer, DEFVAL(1));
- ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayer::remove_peer);
- ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayer::has_peer);
- ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebRTCMultiplayer::get_peer);
- ClassDB::bind_method(D_METHOD("get_peers"), &WebRTCMultiplayer::get_peers);
- ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayer::close);
+void WebRTCMultiplayerPeer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("initialize", "peer_id", "server_compatibility", "channels_config"), &WebRTCMultiplayerPeer::initialize, DEFVAL(false), DEFVAL(Array()));
+ ClassDB::bind_method(D_METHOD("add_peer", "peer", "peer_id", "unreliable_lifetime"), &WebRTCMultiplayerPeer::add_peer, DEFVAL(1));
+ ClassDB::bind_method(D_METHOD("remove_peer", "peer_id"), &WebRTCMultiplayerPeer::remove_peer);
+ ClassDB::bind_method(D_METHOD("has_peer", "peer_id"), &WebRTCMultiplayerPeer::has_peer);
+ ClassDB::bind_method(D_METHOD("get_peer", "peer_id"), &WebRTCMultiplayerPeer::get_peer);
+ ClassDB::bind_method(D_METHOD("get_peers"), &WebRTCMultiplayerPeer::get_peers);
+ ClassDB::bind_method(D_METHOD("close"), &WebRTCMultiplayerPeer::close);
}
-void WebRTCMultiplayer::set_transfer_mode(TransferMode p_mode) {
- transfer_mode = p_mode;
-}
-
-NetworkedMultiplayerPeer::TransferMode WebRTCMultiplayer::get_transfer_mode() const {
- return transfer_mode;
-}
-
-void WebRTCMultiplayer::set_target_peer(int p_peer_id) {
+void WebRTCMultiplayerPeer::set_target_peer(int p_peer_id) {
target_peer = p_peer_id;
}
-/* Returns the ID of the NetworkedMultiplayerPeer who sent the most recent packet: */
-int WebRTCMultiplayer::get_packet_peer() const {
+/* Returns the ID of the MultiplayerPeer who sent the most recent packet: */
+int WebRTCMultiplayerPeer::get_packet_peer() const {
return next_packet_peer;
}
-bool WebRTCMultiplayer::is_server() const {
+bool WebRTCMultiplayerPeer::is_server() const {
return unique_id == TARGET_PEER_SERVER;
}
-void WebRTCMultiplayer::poll() {
+void WebRTCMultiplayerPeer::poll() {
if (peer_map.size() == 0) {
return;
}
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()) {
@@ -85,7 +77,7 @@ void WebRTCMultiplayer::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
@@ -100,7 +92,7 @@ void WebRTCMultiplayer::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;
@@ -108,34 +100,34 @@ void WebRTCMultiplayer::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
- for (List<int>::Element *E = remove.front(); E; E = E->next()) {
- remove_peer(E->get());
- if (next_packet_peer == E->get()) {
+ for (int &E : remove) {
+ remove_peer(E);
+ if (next_packet_peer == E) {
next_packet_peer = 0;
}
}
// Signal newly connected peers
- for (List<int>::Element *E = add.front(); E; E = E->next()) {
+ for (int &E : add) {
// Already connected to server: simply notify new peer.
// NOTE: Mesh is always connected.
if (connection_status == CONNECTION_CONNECTED) {
- emit_signal("peer_connected", E->get());
+ emit_signal(SNAME("peer_connected"), E);
}
// Server emulation mode suppresses peer_conencted until server connects.
- if (server_compat && E->get() == TARGET_PEER_SERVER) {
+ if (server_compat && E == TARGET_PEER_SERVER) {
// Server connected.
connection_status = CONNECTION_CONNECTED;
- emit_signal("peer_connected", TARGET_PEER_SERVER);
- emit_signal("connection_succeeded");
+ 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("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.
@@ -147,15 +139,15 @@ void WebRTCMultiplayer::poll() {
}
}
-void WebRTCMultiplayer::_find_next_peer() {
+void WebRTCMultiplayerPeer::_find_next_peer() {
Map<int, Ref<ConnectedPeer>>::Element *E = peer_map.find(next_packet_peer);
if (E) {
E = E->next();
}
// After last.
while (E) {
- for (List<Ref<WebRTCDataChannel>>::Element *F = E->get()->channels.front(); F; F = F->next()) {
- if (F->get()->get_available_packet_count()) {
+ for (const Ref<WebRTCDataChannel> &F : E->get()->channels) {
+ if (F->get_available_packet_count()) {
next_packet_peer = E->key();
return;
}
@@ -165,8 +157,8 @@ void WebRTCMultiplayer::_find_next_peer() {
E = peer_map.front();
// Before last
while (E) {
- for (List<Ref<WebRTCDataChannel>>::Element *F = E->get()->channels.front(); F; F = F->next()) {
- if (F->get()->get_available_packet_count()) {
+ for (const Ref<WebRTCDataChannel> &F : E->get()->channels) {
+ if (F->get_available_packet_count()) {
next_packet_peer = E->key();
return;
}
@@ -180,20 +172,38 @@ void WebRTCMultiplayer::_find_next_peer() {
next_packet_peer = 0;
}
-void WebRTCMultiplayer::set_refuse_new_connections(bool p_enable) {
- refuse_connections = p_enable;
-}
-
-bool WebRTCMultiplayer::is_refusing_new_connections() const {
- return refuse_connections;
-}
-
-NetworkedMultiplayerPeer::ConnectionStatus WebRTCMultiplayer::get_connection_status() const {
+MultiplayerPeer::ConnectionStatus WebRTCMultiplayerPeer::get_connection_status() const {
return connection_status;
}
-Error WebRTCMultiplayer::initialize(int p_self_id, bool p_server_compat) {
- ERR_FAIL_COND_V(p_self_id < 0 || p_self_id > ~(1 << 31), ERR_INVALID_PARAMETER);
+Error WebRTCMultiplayerPeer::initialize(int p_self_id, bool p_server_compat, Array p_channels_config) {
+ 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.Multiplayer::TransferMode'");
+ int mode = p_channels_config[i].operator int();
+ // Initialize data channel configurations.
+ Dictionary cfg;
+ cfg["id"] = CH_RESERVED_MAX + i + 1;
+ cfg["negotiated"] = true;
+ cfg["ordered"] = true;
+
+ switch (mode) {
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED:
+ cfg["maxPacketLifetime"] = 1;
+ break;
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE:
+ cfg["maxPacketLifetime"] = 1;
+ cfg["ordered"] = false;
+ break;
+ 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.Multiplayer::TransferMode'. Got: %d", mode));
+ }
+ channels_config.push_back(cfg);
+ }
+
unique_id = p_self_id;
server_compat = p_server_compat;
@@ -206,46 +216,46 @@ Error WebRTCMultiplayer::initialize(int p_self_id, bool p_server_compat) {
return OK;
}
-int WebRTCMultiplayer::get_unique_id() const {
+int WebRTCMultiplayerPeer::get_unique_id() const {
ERR_FAIL_COND_V(connection_status == CONNECTION_DISCONNECTED, 1);
return unique_id;
}
-void WebRTCMultiplayer::_peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict) {
+void WebRTCMultiplayerPeer::_peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict) {
Array channels;
- for (List<Ref<WebRTCDataChannel>>::Element *F = p_connected_peer->channels.front(); F; F = F->next()) {
- channels.push_back(F->get());
+ for (Ref<WebRTCDataChannel> &F : p_connected_peer->channels) {
+ channels.push_back(F);
}
r_dict["connection"] = p_connected_peer->connection;
r_dict["connected"] = p_connected_peer->connected;
r_dict["channels"] = channels;
}
-bool WebRTCMultiplayer::has_peer(int p_peer_id) {
+bool WebRTCMultiplayerPeer::has_peer(int p_peer_id) {
return peer_map.has(p_peer_id);
}
-Dictionary WebRTCMultiplayer::get_peer(int p_peer_id) {
+Dictionary WebRTCMultiplayerPeer::get_peer(int p_peer_id) {
ERR_FAIL_COND_V(!peer_map.has(p_peer_id), Dictionary());
Dictionary out;
_peer_to_dict(peer_map[p_peer_id], out);
return out;
}
-Dictionary WebRTCMultiplayer::get_peers() {
+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;
}
-Error WebRTCMultiplayer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime) {
+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);
@@ -260,46 +270,52 @@ Error WebRTCMultiplayer::add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_i
cfg["id"] = 1;
peer->channels[CH_RELIABLE] = p_peer->create_data_channel("reliable", cfg);
- ERR_FAIL_COND_V(!peer->channels[CH_RELIABLE].is_valid(), FAILED);
+ ERR_FAIL_COND_V(peer->channels[CH_RELIABLE].is_null(), FAILED);
cfg["id"] = 2;
cfg["maxPacketLifetime"] = p_unreliable_lifetime;
peer->channels[CH_ORDERED] = p_peer->create_data_channel("ordered", cfg);
- ERR_FAIL_COND_V(!peer->channels[CH_ORDERED].is_valid(), FAILED);
+ ERR_FAIL_COND_V(peer->channels[CH_ORDERED].is_null(), FAILED);
cfg["id"] = 3;
cfg["ordered"] = false;
peer->channels[CH_UNRELIABLE] = p_peer->create_data_channel("unreliable", cfg);
- ERR_FAIL_COND_V(!peer->channels[CH_UNRELIABLE].is_valid(), FAILED);
+ ERR_FAIL_COND_V(peer->channels[CH_UNRELIABLE].is_null(), FAILED);
+
+ for (const Dictionary &dict : channels_config) {
+ Ref<WebRTCDataChannel> ch = p_peer->create_data_channel(String::num_int64(dict["id"]), dict);
+ ERR_FAIL_COND_V(ch.is_null(), FAILED);
+ peer->channels.push_back(ch);
+ }
peer_map[p_peer_id] = peer; // add the new peer connection to the peer_map
return OK;
}
-void WebRTCMultiplayer::remove_peer(int p_peer_id) {
+void WebRTCMultiplayerPeer::remove_peer(int p_peer_id) {
ERR_FAIL_COND(!peer_map.has(p_peer_id));
Ref<ConnectedPeer> peer = peer_map[p_peer_id];
peer_map.erase(p_peer_id);
if (peer->connected) {
peer->connected = false;
- emit_signal("peer_disconnected", p_peer_id);
+ emit_signal(SNAME("peer_disconnected"), p_peer_id);
if (server_compat && p_peer_id == TARGET_PEER_SERVER) {
- emit_signal("server_disconnected");
+ emit_signal(SNAME("server_disconnected"));
connection_status = CONNECTION_DISCONNECTED;
}
}
}
-Error WebRTCMultiplayer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+Error WebRTCMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
// Peer not available
if (next_packet_peer == 0 || !peer_map.has(next_packet_peer)) {
_find_next_peer();
ERR_FAIL_V(ERR_UNAVAILABLE);
}
- for (List<Ref<WebRTCDataChannel>>::Element *E = peer_map[next_packet_peer]->channels.front(); E; E = E->next()) {
- if (E->get()->get_available_packet_count()) {
- Error err = E->get()->get_packet(r_buffer, r_buffer_size);
+ for (Ref<WebRTCDataChannel> &E : peer_map[next_packet_peer]->channels) {
+ if (E->get_available_packet_count()) {
+ Error err = E->get_packet(r_buffer, r_buffer_size);
_find_next_peer();
return err;
}
@@ -309,20 +325,24 @@ Error WebRTCMultiplayer::get_packet(const uint8_t **r_buffer, int &r_buffer_size
ERR_FAIL_V(ERR_BUG);
}
-Error WebRTCMultiplayer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+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 = CH_RELIABLE;
- switch (transfer_mode) {
- case TRANSFER_MODE_RELIABLE:
- ch = CH_RELIABLE;
- break;
- case TRANSFER_MODE_UNRELIABLE_ORDERED:
- ch = CH_ORDERED;
- break;
- case TRANSFER_MODE_UNRELIABLE:
- ch = CH_UNRELIABLE;
- break;
+ int ch = get_transfer_channel();
+ if (ch == 0) {
+ switch (get_transfer_mode()) {
+ case Multiplayer::TRANSFER_MODE_RELIABLE:
+ ch = CH_RELIABLE;
+ break;
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE_ORDERED:
+ ch = CH_ORDERED;
+ break;
+ case Multiplayer::TRANSFER_MODE_UNRELIABLE:
+ ch = CH_UNRELIABLE;
+ break;
+ }
+ } else {
+ ch += CH_RESERVED_MAX - 1;
}
Map<int, Ref<ConnectedPeer>>::Element *E = nullptr;
@@ -331,62 +351,53 @@ Error WebRTCMultiplayer::put_packet(const uint8_t *p_buffer, int p_buffer_size)
E = peer_map.find(target_peer);
ERR_FAIL_COND_V_MSG(!E, ERR_INVALID_PARAMETER, "Invalid target peer: " + itos(target_peer) + ".");
- ERR_FAIL_COND_V(E->value()->channels.size() <= ch, ERR_BUG);
- ERR_FAIL_COND_V(!E->value()->channels[ch].is_valid(), ERR_BUG);
+ ERR_FAIL_COND_V_MSG(E->value()->channels.size() <= ch, ERR_INVALID_PARAMETER, vformat("Unable to send packet on channel %d, max channels: %d", ch, E->value()->channels.size()));
+ ERR_FAIL_COND_V(E->value()->channels[ch].is_null(), ERR_BUG);
return E->value()->channels[ch]->put_packet(p_buffer, p_buffer_size);
} else {
int exclude = -target_peer;
- 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(F->value()->channels.size() <= ch || !F->value()->channels[ch].is_valid());
- 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;
}
-int WebRTCMultiplayer::get_available_packet_count() const {
+int WebRTCMultiplayerPeer::get_available_packet_count() const {
if (next_packet_peer == 0) {
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 (List<Ref<WebRTCDataChannel>>::Element *F = E->get()->channels.front(); F; F = F->next()) {
- size += F->get()->get_available_packet_count();
+ for (const KeyValue<int, Ref<ConnectedPeer>> &E : peer_map) {
+ for (const Ref<WebRTCDataChannel> &F : E.value->channels) {
+ size += F->get_available_packet_count();
}
}
return size;
}
-int WebRTCMultiplayer::get_max_packet_size() const {
+int WebRTCMultiplayerPeer::get_max_packet_size() const {
return 1200;
}
-void WebRTCMultiplayer::close() {
+void WebRTCMultiplayerPeer::close() {
peer_map.clear();
+ channels_config.clear();
unique_id = 0;
next_packet_peer = 0;
target_peer = 0;
connection_status = CONNECTION_DISCONNECTED;
}
-WebRTCMultiplayer::WebRTCMultiplayer() {
- unique_id = 0;
- next_packet_peer = 0;
- target_peer = 0;
- client_count = 0;
- transfer_mode = TRANSFER_MODE_RELIABLE;
- refuse_connections = false;
- connection_status = CONNECTION_DISCONNECTED;
- server_compat = false;
-}
-
-WebRTCMultiplayer::~WebRTCMultiplayer() {
+WebRTCMultiplayerPeer::~WebRTCMultiplayerPeer() {
close();
}
diff --git a/modules/webrtc/webrtc_multiplayer.h b/modules/webrtc/webrtc_multiplayer_peer.h
index fb37bd7722..4a7e9ad7c8 100644
--- a/modules/webrtc/webrtc_multiplayer.h
+++ b/modules/webrtc/webrtc_multiplayer_peer.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_multiplayer.h */
+/* webrtc_multiplayer_peer.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,11 +31,11 @@
#ifndef WEBRTC_MULTIPLAYER_H
#define WEBRTC_MULTIPLAYER_H
-#include "core/io/networked_multiplayer_peer.h"
+#include "core/multiplayer/multiplayer_peer.h"
#include "webrtc_peer_connection.h"
-class WebRTCMultiplayer : public NetworkedMultiplayerPeer {
- GDCLASS(WebRTCMultiplayer, NetworkedMultiplayerPeer);
+class WebRTCMultiplayerPeer : public MultiplayerPeer {
+ GDCLASS(WebRTCMultiplayerPeer, MultiplayerPeer);
protected:
static void _bind_methods();
@@ -48,7 +48,7 @@ private:
CH_RESERVED_MAX = 3
};
- class ConnectedPeer : public Reference {
+ class ConnectedPeer : public RefCounted {
public:
Ref<WebRTCPeerConnection> connection;
List<Ref<WebRTCDataChannel>> channels;
@@ -62,25 +62,24 @@ private:
}
};
- uint32_t unique_id;
- int target_peer;
- int client_count;
- bool refuse_connections;
- ConnectionStatus connection_status;
- TransferMode transfer_mode;
- int next_packet_peer;
- bool server_compat;
+ uint32_t unique_id = 0;
+ int target_peer = 0;
+ int client_count = 0;
+ ConnectionStatus connection_status = CONNECTION_DISCONNECTED;
+ int next_packet_peer = 0;
+ bool server_compat = false;
Map<int, Ref<ConnectedPeer>> peer_map;
+ List<Dictionary> channels_config;
void _peer_to_dict(Ref<ConnectedPeer> p_connected_peer, Dictionary &r_dict);
void _find_next_peer();
public:
- WebRTCMultiplayer();
- ~WebRTCMultiplayer();
+ WebRTCMultiplayerPeer() {}
+ ~WebRTCMultiplayerPeer();
- Error initialize(int p_self_id, bool p_server_compat = false);
+ Error initialize(int p_self_id, bool p_server_compat = false, Array p_channels_config = Array());
Error add_peer(Ref<WebRTCPeerConnection> p_peer, int p_peer_id, int p_unreliable_lifetime = 1);
void remove_peer(int p_peer_id);
bool has_peer(int p_peer_id);
@@ -94,9 +93,7 @@ public:
int get_available_packet_count() const override;
int get_max_packet_size() const override;
- // NetworkedMultiplayerPeer
- void set_transfer_mode(TransferMode p_mode) override;
- TransferMode get_transfer_mode() const override;
+ // MultiplayerPeer
void set_target_peer(int p_peer_id) override;
int get_unique_id() const override;
@@ -106,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 670924bca2..ad28aa76c7 100644
--- a/modules/webrtc/webrtc_peer_connection.cpp
+++ b/modules/webrtc/webrtc_peer_connection.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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 7366c3d0e8..e2ef3e55ad 100644
--- a/modules/webrtc/webrtc_peer_connection.h
+++ b/modules/webrtc/webrtc_peer_connection.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,8 +34,8 @@
#include "core/io/packet_peer.h"
#include "modules/webrtc/webrtc_data_channel.h"
-class WebRTCPeerConnection : public Reference {
- GDCLASS(WebRTCPeerConnection, Reference);
+class WebRTCPeerConnection : public RefCounted {
+ GDCLASS(WebRTCPeerConnection, RefCounted);
public:
enum ConnectionState {
@@ -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 846b65c466..b3c2039fc1 100644
--- a/modules/webrtc/webrtc_peer_connection_gdnative.h
+++ b/modules/webrtc/webrtc_peer_connection_extension.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* webrtc_peer_connection_gdnative.h */
+/* webrtc_peer_connection_extension.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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 aaa45d3a54..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-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 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.cpp b/modules/webrtc/webrtc_peer_connection_js.cpp
index 593c3a5162..ed3459d5f8 100644
--- a/modules/webrtc/webrtc_peer_connection_js.cpp
+++ b/modules/webrtc/webrtc_peer_connection_js.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,119 +34,34 @@
#include "webrtc_data_channel_js.h"
-#include "core/io/json.h"
#include "emscripten.h"
-extern "C" {
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_ice_candidate(void *obj, char *p_MidName, int p_MlineIndexName, char *p_sdpName) {
- WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
- peer->emit_signal("ice_candidate_created", String(p_MidName), p_MlineIndexName, String(p_sdpName));
+void WebRTCPeerConnectionJS::_on_ice_candidate(void *p_obj, const char *p_mid_name, int p_mline_idx, const char *p_candidate) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+ peer->emit_signal(SNAME("ice_candidate_created"), String(p_mid_name), p_mline_idx, String(p_candidate));
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_session_description_created(void *obj, char *p_type, char *p_offer) {
- WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
- peer->emit_signal("session_description_created", String(p_type), String(p_offer));
+void WebRTCPeerConnectionJS::_on_session_created(void *p_obj, const char *p_type, const char *p_session) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+ peer->emit_signal(SNAME("session_description_created"), String(p_type), String(p_session));
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_connection_state_changed(void *obj) {
- WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
- peer->_on_connection_state_changed();
+void WebRTCPeerConnectionJS::_on_connection_state_changed(void *p_obj, int p_state) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+ peer->_conn_state = (ConnectionState)p_state;
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_on_error() {
+void WebRTCPeerConnectionJS::_on_error(void *p_obj) {
ERR_PRINT("RTCPeerConnection error!");
}
-EMSCRIPTEN_KEEPALIVE void _emrtc_emit_channel(void *obj, int p_id) {
- WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(obj);
- peer->emit_signal("data_channel_received", Ref<WebRTCDataChannelJS>(new WebRTCDataChannelJS(p_id)));
-}
-}
-
-void _emrtc_create_pc(int p_id, const Dictionary &p_config) {
- String config = JSON::print(p_config);
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var c_ptr = dict["ptr"];
- var config = JSON.parse(UTF8ToString($1));
- // Setup local connaction
- var conn = null;
- try {
- conn = new RTCPeerConnection(config);
- } catch (e) {
- console.log(e);
- return;
- }
- conn.oniceconnectionstatechange = function(event) {
- if (!Module.IDHandler.get($0)) return;
- ccall("_emrtc_on_connection_state_changed", "void", ["number"], [c_ptr]);
- };
- conn.onicecandidate = function(event) {
- if (!Module.IDHandler.get($0)) return;
- if (!event.candidate) return;
-
- var c = event.candidate;
- // should emit on ice candidate
- ccall("_emrtc_on_ice_candidate",
- "void",
- ["number", "string", "number", "string"],
- [c_ptr, c.sdpMid, c.sdpMLineIndex, c.candidate]
- );
- };
- conn.ondatachannel = function (evt) {
- var dict = Module.IDHandler.get($0);
- if (!dict) {
- return;
- }
- var id = Module.IDHandler.add({"channel": evt.channel, "ptr": null});
- ccall("_emrtc_emit_channel",
- "void",
- ["number", "number"],
- [c_ptr, id]
- );
- };
- dict["conn"] = conn;
- }, p_id, config.utf8().get_data());
- /* clang-format on */
-}
-
-void WebRTCPeerConnectionJS::_on_connection_state_changed() {
- /* clang-format off */
- _conn_state = (ConnectionState)EM_ASM_INT({
- var dict = Module.IDHandler.get($0);
- if (!dict) return 5; // CLOSED
- var conn = dict["conn"];
- switch(conn.iceConnectionState) {
- case "new":
- return 0;
- case "checking":
- return 1;
- case "connected":
- case "completed":
- return 2;
- case "disconnected":
- return 3;
- case "failed":
- return 4;
- case "closed":
- return 5;
- }
- return 5; // CLOSED
- }, _js_id);
- /* clang-format on */
+void WebRTCPeerConnectionJS::_on_data_channel(void *p_obj, int p_id) {
+ WebRTCPeerConnectionJS *peer = static_cast<WebRTCPeerConnectionJS *>(p_obj);
+ peer->emit_signal(SNAME("data_channel_received"), Ref<WebRTCDataChannelJS>(new WebRTCDataChannelJS(p_id)));
}
void WebRTCPeerConnectionJS::close() {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- if (!dict) return;
- if (dict["conn"]) {
- dict["conn"].close();
- }
- }, _js_id);
- /* clang-format on */
+ godot_js_rtc_pc_close(_js_id);
_conn_state = STATE_CLOSED;
}
@@ -154,46 +69,12 @@ Error WebRTCPeerConnectionJS::create_offer() {
ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
_conn_state = STATE_CONNECTING;
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var onError = function(error) {
- console.error(error);
- ccall("_emrtc_on_error", "void", [], []);
- };
- var onCreated = function(offer) {
- ccall("_emrtc_session_description_created",
- "void",
- ["number", "string", "string"],
- [c_ptr, offer.type, offer.sdp]
- );
- };
- conn.createOffer().then(onCreated).catch(onError);
- }, _js_id);
- /* clang-format on */
+ godot_js_rtc_pc_offer_create(_js_id, this, &_on_session_created, &_on_error);
return OK;
}
Error WebRTCPeerConnectionJS::set_local_description(String type, String sdp) {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var type = UTF8ToString($1);
- var sdp = UTF8ToString($2);
- var onError = function(error) {
- console.error(error);
- ccall("_emrtc_on_error", "void", [], []);
- };
- conn.setLocalDescription({
- "sdp": sdp,
- "type": type
- }).catch(onError);
- }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
- /* clang-format on */
+ godot_js_rtc_pc_local_description_set(_js_id, type.utf8().get_data(), sdp.utf8().get_data(), this, &_on_error);
return OK;
}
@@ -202,83 +83,32 @@ Error WebRTCPeerConnectionJS::set_remote_description(String type, String sdp) {
ERR_FAIL_COND_V(_conn_state != STATE_NEW, FAILED);
_conn_state = STATE_CONNECTING;
}
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var type = UTF8ToString($1);
- var sdp = UTF8ToString($2);
-
- var onError = function(error) {
- console.error(error);
- ccall("_emrtc_on_error", "void", [], []);
- };
- var onCreated = function(offer) {
- ccall("_emrtc_session_description_created",
- "void",
- ["number", "string", "string"],
- [c_ptr, offer.type, offer.sdp]
- );
- };
- var onSet = function() {
- if (type != "offer") {
- return;
- }
- conn.createAnswer().then(onCreated);
- };
- conn.setRemoteDescription({
- "sdp": sdp,
- "type": type
- }).then(onSet).catch(onError);
- }, _js_id, type.utf8().get_data(), sdp.utf8().get_data());
- /* clang-format on */
+ godot_js_rtc_pc_remote_description_set(_js_id, type.utf8().get_data(), sdp.utf8().get_data(), this, &_on_session_created, &_on_error);
return OK;
}
Error WebRTCPeerConnectionJS::add_ice_candidate(String sdpMidName, int sdpMlineIndexName, String sdpName) {
- /* clang-format off */
- EM_ASM({
- var dict = Module.IDHandler.get($0);
- var conn = dict["conn"];
- var c_ptr = dict["ptr"];
- var sdpMidName = UTF8ToString($1);
- var sdpMlineIndexName = UTF8ToString($2);
- var sdpName = UTF8ToString($3);
- conn.addIceCandidate(new RTCIceCandidate({
- "candidate": sdpName,
- "sdpMid": sdpMidName,
- "sdpMlineIndex": sdpMlineIndexName
- }));
- }, _js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
- /* clang-format on */
+ godot_js_rtc_pc_ice_candidate_add(_js_id, sdpMidName.utf8().get_data(), sdpMlineIndexName, sdpName.utf8().get_data());
return OK;
}
Error WebRTCPeerConnectionJS::initialize(Dictionary p_config) {
- _emrtc_create_pc(_js_id, p_config);
- return OK;
+ if (_js_id) {
+ godot_js_rtc_pc_destroy(_js_id);
+ _js_id = 0;
+ }
+ _conn_state = STATE_NEW;
+
+ String config = Variant(p_config).to_json_string();
+ _js_id = godot_js_rtc_pc_create(config.utf8().get_data(), this, &_on_connection_state_changed, &_on_ice_candidate, &_on_data_channel);
+ return _js_id ? OK : FAILED;
}
Ref<WebRTCDataChannel> WebRTCPeerConnectionJS::create_data_channel(String p_channel, Dictionary p_channel_config) {
- String config = JSON::print(p_channel_config);
- /* clang-format off */
- int id = EM_ASM_INT({
- try {
- var dict = Module.IDHandler.get($0);
- if (!dict) return 0;
- var label = UTF8ToString($1);
- var config = JSON.parse(UTF8ToString($2));
- var conn = dict["conn"];
- return Module.IDHandler.add({
- "channel": conn.createDataChannel(label, config),
- "ptr": null
- })
- } catch (e) {
- return 0;
- }
- }, _js_id, p_channel.utf8().get_data(), config.utf8().get_data());
- /* clang-format on */
+ ERR_FAIL_COND_V(_conn_state != STATE_NEW, nullptr);
+
+ String config = Variant(p_channel_config).to_json_string();
+ int id = godot_js_rtc_pc_datachannel_create(_js_id, p_channel.utf8().get_data(), config.utf8().get_data());
ERR_FAIL_COND_V(id == 0, nullptr);
return memnew(WebRTCDataChannelJS(id));
}
@@ -293,22 +123,17 @@ WebRTCPeerConnection::ConnectionState WebRTCPeerConnectionJS::get_connection_sta
WebRTCPeerConnectionJS::WebRTCPeerConnectionJS() {
_conn_state = STATE_NEW;
+ _js_id = 0;
- /* clang-format off */
- _js_id = EM_ASM_INT({
- return Module.IDHandler.add({"conn": null, "ptr": $0});
- }, this);
- /* clang-format on */
Dictionary config;
- _emrtc_create_pc(_js_id, config);
+ initialize(config);
}
WebRTCPeerConnectionJS::~WebRTCPeerConnectionJS() {
close();
- /* clang-format off */
- EM_ASM({
- Module.IDHandler.remove($0);
- }, _js_id);
- /* clang-format on */
+ if (_js_id) {
+ godot_js_rtc_pc_destroy(_js_id);
+ _js_id = 0;
+ }
};
#endif
diff --git a/modules/webrtc/webrtc_peer_connection_js.h b/modules/webrtc/webrtc_peer_connection_js.h
index cdaf3068e3..d2beccaf03 100644
--- a/modules/webrtc/webrtc_peer_connection_js.h
+++ b/modules/webrtc/webrtc_peer_connection_js.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -35,16 +35,34 @@
#include "webrtc_peer_connection.h"
+extern "C" {
+typedef void (*RTCOnIceConnectionStateChange)(void *p_obj, int p_state);
+typedef void (*RTCOnIceCandidate)(void *p_obj, const char *p_mid, int p_mline_idx, const char *p_candidate);
+typedef void (*RTCOnDataChannel)(void *p_obj, int p_id);
+typedef void (*RTCOnSession)(void *p_obj, const char *p_type, const char *p_sdp);
+typedef void (*RTCOnError)(void *p_obj);
+extern int godot_js_rtc_pc_create(const char *p_config, void *p_obj, RTCOnIceConnectionStateChange p_on_state_change, RTCOnIceCandidate p_on_candidate, RTCOnDataChannel p_on_datachannel);
+extern void godot_js_rtc_pc_close(int p_id);
+extern void godot_js_rtc_pc_destroy(int p_id);
+extern void godot_js_rtc_pc_offer_create(int p_id, void *p_obj, RTCOnSession p_on_session, RTCOnError p_on_error);
+extern void godot_js_rtc_pc_local_description_set(int p_id, const char *p_type, const char *p_sdp, void *p_obj, RTCOnError p_on_error);
+extern void godot_js_rtc_pc_remote_description_set(int p_id, const char *p_type, const char *p_sdp, void *p_obj, RTCOnSession p_on_session, RTCOnError p_on_error);
+extern void godot_js_rtc_pc_ice_candidate_add(int p_id, const char *p_mid_name, int p_mline_idx, const char *p_sdo);
+extern int godot_js_rtc_pc_datachannel_create(int p_id, const char *p_label, const char *p_config);
+}
+
class WebRTCPeerConnectionJS : public WebRTCPeerConnection {
private:
int _js_id;
ConnectionState _conn_state;
-public:
- static WebRTCPeerConnection *_create() { return memnew(WebRTCPeerConnectionJS); }
- static void make_default() { WebRTCPeerConnection::_create = WebRTCPeerConnectionJS::_create; }
+ static void _on_connection_state_changed(void *p_obj, int p_state);
+ static void _on_ice_candidate(void *p_obj, const char *p_mid_name, int p_mline_idx, const char *p_candidate);
+ static void _on_data_channel(void *p_obj, int p_channel);
+ static void _on_session_created(void *p_obj, const char *p_type, const char *p_session);
+ static void _on_error(void *p_obj);
- void _on_connection_state_changed();
+public:
virtual ConnectionState get_connection_state() const;
virtual Error initialize(Dictionary configuration = Dictionary());
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index af60055855..4c022c43cf 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -3,28 +3,46 @@
Import("env")
Import("env_modules")
-# Thirdparty source files
-
env_ws = env_modules.Clone()
-if env["builtin_wslay"] and not env["platform"] == "javascript": # already builtin for javascript
- wslay_dir = "#thirdparty/wslay/"
- wslay_sources = [
+thirdparty_obj = []
+
+if env["platform"] == "javascript":
+ # Our JavaScript/C++ interface.
+ env.AddJSLibraries(["library_godot_websocket.js"])
+
+elif env["builtin_wslay"]:
+ # Thirdparty source files
+ thirdparty_dir = "#thirdparty/wslay/"
+ thirdparty_sources = [
"wslay_net.c",
"wslay_event.c",
"wslay_queue.c",
"wslay_stack.c",
"wslay_frame.c",
]
- wslay_sources = [wslay_dir + s for s in wslay_sources]
- env_ws.Prepend(CPPPATH=[wslay_dir + "includes/"])
+ thirdparty_sources = [thirdparty_dir + s for s in thirdparty_sources]
+
+ env_ws.Prepend(CPPPATH=[thirdparty_dir + "includes/"])
env_ws.Append(CPPDEFINES=["HAVE_CONFIG_H"])
+
if env["platform"] == "windows" or env["platform"] == "uwp":
env_ws.Append(CPPDEFINES=["HAVE_WINSOCK2_H"])
else:
env_ws.Append(CPPDEFINES=["HAVE_NETINET_IN_H"])
- env_wslay = env_ws.Clone()
- env_wslay.disable_warnings()
- env_wslay.add_source_files(env.modules_sources, wslay_sources)
-env_ws.add_source_files(env.modules_sources, "*.cpp")
+ env_thirdparty = env_ws.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_ws.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/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml
index 45db49c913..4b515c12a1 100644
--- a/modules/websocket/doc_classes/WebSocketClient.xml
+++ b/modules/websocket/doc_classes/WebSocketClient.xml
@@ -5,53 +5,45 @@
</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].
- After starting the client ([method connect_to_url]), you will need to [method NetworkedMultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]).
+ 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>
<methods>
<method name="connect_to_url">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="url" type="String">
- </argument>
- <argument index="1" name="protocols" type="PackedStringArray" default="PackedStringArray( )">
- </argument>
- <argument index="2" name="gd_mp_api" type="bool" default="false">
- </argument>
- <argument index="3" name="custom_headers" type="PackedStringArray" default="PackedStringArray( )">
- </argument>
+ <return type="int" enum="Error" />
+ <argument index="0" name="url" type="String" />
+ <argument index="1" name="protocols" type="PackedStringArray" default="PackedStringArray()" />
+ <argument index="2" name="gd_mp_api" type="bool" default="false" />
+ <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.
[b]Note:[/b] Specifying [code]custom_headers[/code] is not supported in HTML5 exports due to browsers restrictions.
</description>
</method>
<method name="disconnect_from_host">
- <return type="void">
- </return>
- <argument index="0" name="code" type="int" default="1000">
- </argument>
- <argument index="1" name="reason" type="String" default="&quot;&quot;">
- </argument>
+ <return type="void" />
+ <argument index="0" name="code" type="int" default="1000" />
+ <argument index="1" name="reason" type="String" default="&quot;&quot;" />
<description>
Disconnects this client from the connected host. See [method WebSocketPeer.close] for more information.
</description>
</method>
<method name="get_connected_host" qualifiers="const">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Return the IP address of the currently connected host.
</description>
</method>
<method name="get_connected_port" qualifiers="const">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Return the IP port of the currently connected host.
</description>
@@ -69,8 +61,7 @@
</members>
<signals>
<signal name="connection_closed">
- <argument index="0" name="was_clean_close" type="bool">
- </argument>
+ <argument index="0" name="was_clean_close" type="bool" />
<description>
Emitted when the connection to the server is closed. [code]was_clean_close[/code] will be [code]true[/code] if the connection was shutdown cleanly.
</description>
@@ -81,8 +72,7 @@
</description>
</signal>
<signal name="connection_established">
- <argument index="0" name="protocol" type="String">
- </argument>
+ <argument index="0" name="protocol" type="String" />
<description>
Emitted when a connection with the server is established, [code]protocol[/code] will contain the sub-protocol agreed with the server.
</description>
@@ -94,15 +84,11 @@
</description>
</signal>
<signal name="server_close_request">
- <argument index="0" name="code" type="int">
- </argument>
- <argument index="1" name="reason" type="String">
- </argument>
+ <argument index="0" name="code" type="int" />
+ <argument index="1" name="reason" type="String" />
<description>
Emitted when the server requests a clean close. You should keep polling until you get a [signal connection_closed] signal to achieve the clean close. See [method WebSocketPeer.close] for more details.
</description>
</signal>
</signals>
- <constants>
- </constants>
</class>
diff --git a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
index 0679acf78a..8d8ab220e2 100644
--- a/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketMultiplayerPeer.xml
@@ -1,34 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="WebSocketMultiplayerPeer" inherits="NetworkedMultiplayerPeer" version="4.0">
+<class name="WebSocketMultiplayerPeer" inherits="MultiplayerPeer" version="4.0">
<brief_description>
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>
<methods>
<method name="get_peer" qualifiers="const">
- <return type="WebSocketPeer">
- </return>
- <argument index="0" name="peer_id" type="int">
- </argument>
+ <return type="WebSocketPeer" />
+ <argument index="0" name="peer_id" type="int" />
<description>
Returns the [WebSocketPeer] associated to the given [code]peer_id[/code].
</description>
</method>
<method name="set_buffers">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="input_buffer_size_kb" type="int">
- </argument>
- <argument index="1" name="input_max_packets" type="int">
- </argument>
- <argument index="2" name="output_buffer_size_kb" type="int">
- </argument>
- <argument index="3" name="output_max_packets" type="int">
- </argument>
+ <return type="int" enum="Error" />
+ <argument index="0" name="input_buffer_size_kb" type="int" />
+ <argument index="1" name="input_max_packets" type="int" />
+ <argument index="2" name="output_buffer_size_kb" type="int" />
+ <argument index="3" name="output_max_packets" type="int" />
<description>
Configures the buffer sizes for this WebSocket peer. Default values can be specified in the Project Settings under [code]network/limits[/code]. For server, values are meant per connected peer.
The first two parameters define the size and queued packets limits of the input buffer, the last two of the output buffer.
@@ -37,20 +31,13 @@
</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="NetworkedMultiplayerPeer.TransferMode" default="2" />
- </members>
<signals>
<signal name="peer_packet">
- <argument index="0" name="peer_source" type="int">
- </argument>
+ <argument index="0" name="peer_source" type="int" />
<description>
Emitted when a packet is received from a peer.
[b]Note:[/b] This signal is only emitted when the client or server is configured to use Godot multiplayer API.
</description>
</signal>
</signals>
- <constants>
- </constants>
</class>
diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml
index 5125956416..2f455c32fd 100644
--- a/modules/websocket/doc_classes/WebSocketPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketPeer.xml
@@ -4,19 +4,16 @@
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>
</tutorials>
<methods>
<method name="close">
- <return type="void">
- </return>
- <argument index="0" name="code" type="int" default="1000">
- </argument>
- <argument index="1" name="reason" type="String" default="&quot;&quot;">
- </argument>
+ <return type="void" />
+ <argument index="0" name="code" type="int" default="1000" />
+ <argument index="1" name="reason" type="String" default="&quot;&quot;" />
<description>
Closes this WebSocket connection. [code]code[/code] is the status code for the closure (see RFC 6455 section 7.4 for a list of valid status codes). [code]reason[/code] is the human readable reason for closing the connection (can be any UTF-8 string that's smaller than 123 bytes).
[b]Note:[/b] To achieve a clean close, you will need to keep polling until either [signal WebSocketClient.connection_closed] or [signal WebSocketServer.client_disconnected] is received.
@@ -24,57 +21,54 @@
</description>
</method>
<method name="get_connected_host" qualifiers="const">
- <return type="String">
- </return>
+ <return type="String" />
<description>
Returns the IP address of the connected peer.
[b]Note:[/b] Not available in the HTML5 export.
</description>
</method>
<method name="get_connected_port" qualifiers="const">
- <return type="int">
- </return>
+ <return type="int" />
<description>
Returns the remote port of the connected peer.
[b]Note:[/b] Not available in the HTML5 export.
</description>
</method>
+ <method name="get_current_outbound_buffered_amount" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the current amount of data in the outbound websocket buffer. [b]Note:[/b] HTML5 exports use WebSocket.bufferedAmount, while other platforms use an internal buffer.
+ </description>
+ </method>
<method name="get_write_mode" qualifiers="const">
- <return type="int" enum="WebSocketPeer.WriteMode">
- </return>
+ <return type="int" enum="WebSocketPeer.WriteMode" />
<description>
Gets the current selected write mode. See [enum WriteMode].
</description>
</method>
<method name="is_connected_to_host" qualifiers="const">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if this peer is currently connected.
</description>
</method>
<method name="set_no_delay">
- <return type="void">
- </return>
- <argument index="0" name="enabled" type="bool">
- </argument>
+ <return type="void" />
+ <argument index="0" name="enabled" type="bool" />
<description>
Disable Nagle's algorithm on the underling TCP socket (default). See [method StreamPeerTCP.set_no_delay] for more information.
[b]Note:[/b] Not available in the HTML5 export.
</description>
</method>
<method name="set_write_mode">
- <return type="void">
- </return>
- <argument index="0" name="mode" type="int" enum="WebSocketPeer.WriteMode">
- </argument>
+ <return type="void" />
+ <argument index="0" name="mode" type="int" enum="WebSocketPeer.WriteMode" />
<description>
Sets the socket to use the given [enum WriteMode].
</description>
</method>
<method name="was_string_packet" qualifiers="const">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if the last received packet was sent as a text payload. See [enum WriteMode].
</description>
diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml
index f7805209e2..f901b089ea 100644
--- a/modules/websocket/doc_classes/WebSocketServer.xml
+++ b/modules/websocket/doc_classes/WebSocketServer.xml
@@ -5,78 +5,63 @@
</brief_description>
<description>
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 NetworkedMultiplayerPeer.poll] it at regular intervals (e.g. inside [method Node._process]). When clients connect, disconnect, or send data, you will receive the appropriate signal.
+ 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>
<methods>
<method name="disconnect_peer">
- <return type="void">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="code" type="int" default="1000">
- </argument>
- <argument index="2" name="reason" type="String" default="&quot;&quot;">
- </argument>
+ <return type="void" />
+ <argument index="0" name="id" type="int" />
+ <argument index="1" name="code" type="int" default="1000" />
+ <argument index="2" name="reason" type="String" default="&quot;&quot;" />
<description>
Disconnects the peer identified by [code]id[/code] from the server. See [method WebSocketPeer.close] for more information.
</description>
</method>
<method name="get_peer_address" qualifiers="const">
- <return type="String">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
+ <return type="String" />
+ <argument index="0" name="id" type="int" />
<description>
Returns the IP address of the given peer.
</description>
</method>
<method name="get_peer_port" qualifiers="const">
- <return type="int">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
+ <return type="int" />
+ <argument index="0" name="id" type="int" />
<description>
Returns the remote port of the given peer.
</description>
</method>
<method name="has_peer" qualifiers="const">
- <return type="bool">
- </return>
- <argument index="0" name="id" type="int">
- </argument>
+ <return type="bool" />
+ <argument index="0" name="id" type="int" />
<description>
Returns [code]true[/code] if a peer with the given ID is connected.
</description>
</method>
<method name="is_listening" qualifiers="const">
- <return type="bool">
- </return>
+ <return type="bool" />
<description>
Returns [code]true[/code] if the server is actively listening on a port.
</description>
</method>
<method name="listen">
- <return type="int" enum="Error">
- </return>
- <argument index="0" name="port" type="int">
- </argument>
- <argument index="1" name="protocols" type="PackedStringArray" default="PackedStringArray( )">
- </argument>
- <argument index="2" name="gd_mp_api" type="bool" default="false">
- </argument>
+ <return type="int" enum="Error" />
+ <argument index="0" name="port" type="int" />
+ <argument index="1" name="protocols" type="PackedStringArray" default="PackedStringArray()" />
+ <argument index="2" name="gd_mp_api" type="bool" default="false" />
<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>
<method name="stop">
- <return type="void">
- </return>
+ <return type="void" />
<description>
Stops the server and clear its state.
</description>
@@ -89,6 +74,9 @@
<member name="ca_chain" type="X509Certificate" setter="set_ca_chain" getter="get_ca_chain">
When using SSL (see [member private_key] and [member ssl_certificate]), you can set this to a valid [X509Certificate] to be provided as additional CA chain information during the SSL handshake.
</member>
+ <member name="handshake_timeout" type="float" setter="set_handshake_timeout" getter="get_handshake_timeout" default="3.0">
+ The time in seconds before a pending client (i.e. a client that has not yet finished the HTTP handshake) is considered stale and forcefully disconnected.
+ </member>
<member name="private_key" type="CryptoKey" setter="set_private_key" getter="get_private_key">
When set to a valid [CryptoKey] (along with [member ssl_certificate]) will cause the server to require SSL instead of regular TCP (i.e. the [code]wss://[/code] protocol).
</member>
@@ -98,43 +86,35 @@
</members>
<signals>
<signal name="client_close_request">
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="code" type="int">
- </argument>
- <argument index="2" name="reason" type="String">
- </argument>
+ <argument index="0" name="id" type="int" />
+ <argument index="1" name="code" type="int" />
+ <argument index="2" name="reason" type="String" />
<description>
Emitted when a client requests a clean close. You should keep polling until you get a [signal client_disconnected] signal with the same [code]id[/code] to achieve the clean close. See [method WebSocketPeer.close] for more details.
</description>
</signal>
<signal name="client_connected">
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="protocol" type="String">
- </argument>
+ <argument index="0" name="id" type="int" />
+ <argument index="1" name="protocol" type="String" />
+ <argument index="2" name="resource_name" type="String" />
<description>
- Emitted when a new client connects. "protocol" will be the sub-protocol agreed with the client.
+ Emitted when a new client connects. "protocol" will be the sub-protocol agreed with the client, and "resource_name" will be the resource name of the URI the peer used.
+ "resource_name" is a path (at the very least a single forward slash) and potentially a query string.
</description>
</signal>
<signal name="client_disconnected">
- <argument index="0" name="id" type="int">
- </argument>
- <argument index="1" name="was_clean_close" type="bool">
- </argument>
+ <argument index="0" name="id" type="int" />
+ <argument index="1" name="was_clean_close" type="bool" />
<description>
Emitted when a client disconnects. [code]was_clean_close[/code] will be [code]true[/code] if the connection was shutdown cleanly.
</description>
</signal>
<signal name="data_received">
- <argument index="0" name="id" type="int">
- </argument>
+ <argument index="0" name="id" type="int" />
<description>
Emitted when a new message is received.
[b]Note:[/b] This signal is [i]not[/i] emitted when used as high-level multiplayer peer.
</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 95ea7ceafa..d248433d82 100644
--- a/modules/websocket/editor_debugger_server_websocket.cpp
+++ b/modules/websocket/editor_debugger_server_websocket.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "editor_debugger_server_websocket.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
#include "editor/editor_settings.h"
#include "modules/websocket/remote_debugger_peer_websocket.h"
@@ -48,11 +48,19 @@ void EditorDebuggerServerWebSocket::poll() {
server->poll();
}
-Error EditorDebuggerServerWebSocket::start() {
- int remote_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
- Vector<String> protocols;
- protocols.push_back("binary"); // compatibility with EMSCRIPTEN TCP-to-WebSocket layer.
- return server->listen(remote_port, protocols);
+Error EditorDebuggerServerWebSocket::start(const String &p_uri) {
+ int bind_port = (int)EditorSettings::get_singleton()->get("network/debug/remote_port");
+ String bind_host = EditorSettings::get_singleton()->get("network/debug/remote_host");
+ 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);
+ }
+ 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(bind_port, compatible_protocols);
}
void EditorDebuggerServerWebSocket::stop() {
diff --git a/modules/websocket/editor_debugger_server_websocket.h b/modules/websocket/editor_debugger_server_websocket.h
index 861f389aab..14ab0109b2 100644
--- a/modules/websocket/editor_debugger_server_websocket.h
+++ b/modules/websocket/editor_debugger_server_websocket.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,12 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SCRIPT_EDITOR_DEBUGGER_WEBSOCKET_H
-#define SCRIPT_EDITOR_DEBUGGER_WEBSOCKET_H
-
-#include "modules/websocket/websocket_server.h"
+#ifndef EDITOR_DEBUGGER_SERVER_WEBSOCKET_H
+#define EDITOR_DEBUGGER_SERVER_WEBSOCKET_H
#include "editor/debugger/editor_debugger_server.h"
+#include "modules/websocket/websocket_server.h"
class EditorDebuggerServerWebSocket : public EditorDebuggerServer {
GDCLASS(EditorDebuggerServerWebSocket, EditorDebuggerServer);
@@ -49,7 +48,7 @@ public:
void _peer_disconnected(int p_peer, bool p_was_clean);
void poll() override;
- Error start() override;
+ Error start(const String &p_uri) override;
void stop() override;
bool is_active() const override;
bool is_connection_available() const override;
@@ -59,4 +58,4 @@ public:
~EditorDebuggerServerWebSocket();
};
-#endif // SCRIPT_EDITOR_DEBUGGER_WEBSOCKET_H
+#endif // EDITOR_DEBUGGER_SERVER_WEBSOCKET_H
diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp
index 7c31449709..5cd94e978f 100644
--- a/modules/websocket/emws_client.cpp
+++ b/modules/websocket/emws_client.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,18 +31,17 @@
#ifdef JAVASCRIPT_ENABLED
#include "emws_client.h"
+#include "core/config/project_settings.h"
#include "core/io/ip.h"
-#include "core/project_settings.h"
#include "emscripten.h"
-extern "C" {
-EMSCRIPTEN_KEEPALIVE void _esws_on_connect(void *obj, char *proto) {
+void EMWSClient::_esws_on_connect(void *obj, char *proto) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
client->_is_connecting = false;
client->_on_connect(String(proto));
}
-EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_data_size, int p_is_string) {
+void EMWSClient::_esws_on_message(void *obj, const uint8_t *p_data, int p_data_size, int p_is_string) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
Error err = static_cast<EMWSPeer *>(*client->get_peer(1))->read_msg(p_data, p_data_size, p_is_string == 1);
@@ -50,21 +49,26 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_message(void *obj, uint8_t *p_data, int p_dat
client->_on_peer_packet();
}
-EMSCRIPTEN_KEEPALIVE void _esws_on_error(void *obj) {
+void EMWSClient::_esws_on_error(void *obj) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
client->_is_connecting = false;
client->_on_error();
}
-EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int was_clean) {
+void EMWSClient::_esws_on_close(void *obj, int code, const char *reason, int was_clean) {
EMWSClient *client = static_cast<EMWSClient *>(obj);
client->_on_close_request(code, String(reason));
client->_is_connecting = false;
+ client->disconnect_from_host();
client->_on_disconnect(was_clean != 0);
}
-}
Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) {
+ if (_js_id) {
+ godot_js_websocket_destroy(_js_id);
+ _js_id = 0;
+ }
+
String proto_string;
for (int i = 0; i < p_protocols.size(); i++) {
if (i != 0)
@@ -75,7 +79,7 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
String str = "ws://";
if (p_custom_headers.size()) {
- WARN_PRINT_ONCE("Custom headers are not supported in in HTML5 platform.");
+ WARN_PRINT_ONCE("Custom headers are not supported in HTML5 platform.");
}
if (p_ssl) {
str = "wss://";
@@ -84,106 +88,17 @@ Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
}
}
str += p_host + ":" + itos(p_port) + p_path;
-
_is_connecting = true;
- /* clang-format off */
- int peer_sock = EM_ASM_INT({
- var proto_str = UTF8ToString($2);
- var socket = null;
- try {
- if (proto_str) {
- socket = new WebSocket(UTF8ToString($1), proto_str.split(","));
- } else {
- socket = new WebSocket(UTF8ToString($1));
- }
- } catch (e) {
- return -1;
- }
- var c_ptr = Module.IDHandler.get($0);
- socket.binaryType = "arraybuffer";
-
- // Connection opened
- socket.addEventListener("open", function (event) {
- if (!Module.IDHandler.has($0))
- return; // Godot Object is gone!
- ccall("_esws_on_connect",
- "void",
- ["number", "string"],
- [c_ptr, socket.protocol]
- );
- });
-
- // Listen for messages
- socket.addEventListener("message", function (event) {
- if (!Module.IDHandler.has($0))
- return; // Godot Object is gone!
- var buffer;
- var is_string = 0;
- if (event.data instanceof ArrayBuffer) {
-
- buffer = new Uint8Array(event.data);
-
- } else if (event.data instanceof Blob) {
-
- alert("Blob type not supported");
- return;
-
- } else if (typeof event.data === "string") {
-
- is_string = 1;
- var enc = new TextEncoder("utf-8");
- buffer = new Uint8Array(enc.encode(event.data));
-
- } else {
-
- alert("Unknown message type");
- return;
-
- }
- var len = buffer.length*buffer.BYTES_PER_ELEMENT;
- var out = _malloc(len);
- HEAPU8.set(buffer, out);
- ccall("_esws_on_message",
- "void",
- ["number", "number", "number", "number"],
- [c_ptr, out, len, is_string]
- );
- _free(out);
- });
-
- socket.addEventListener("error", function (event) {
- if (!Module.IDHandler.has($0))
- return; // Godot Object is gone!
- ccall("_esws_on_error",
- "void",
- ["number"],
- [c_ptr]
- );
- });
-
- socket.addEventListener("close", function (event) {
- if (!Module.IDHandler.has($0))
- return; // Godot Object is gone!
- var was_clean = 0;
- if (event.wasClean)
- was_clean = 1;
- ccall("_esws_on_close",
- "void",
- ["number", "number", "string", "number"],
- [c_ptr, event.code, event.reason, was_clean]
- );
- });
-
- return Module.IDHandler.add(socket);
- }, _js_id, str.utf8().get_data(), proto_string.utf8().get_data());
- /* clang-format on */
- if (peer_sock == -1)
+
+ _js_id = godot_js_websocket_create(this, str.utf8().get_data(), proto_string.utf8().get_data(), &_esws_on_connect, &_esws_on_message, &_esws_on_error, &_esws_on_close);
+ if (!_js_id) {
return FAILED;
+ }
- static_cast<Ref<EMWSPeer>>(_peer)->set_sock(peer_sock, _in_buf_size, _in_pkt_size);
+ static_cast<Ref<EMWSPeer>>(_peer)->set_sock(_js_id, _in_buf_size, _in_pkt_size, _out_buf_size);
return OK;
-};
+}
void EMWSClient::poll() {
}
@@ -192,7 +107,7 @@ Ref<WebSocketPeer> EMWSClient::get_peer(int p_peer_id) const {
return _peer;
}
-NetworkedMultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() const {
+MultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() const {
if (_peer->is_connected_to_host()) {
if (_is_connecting)
return CONNECTION_CONNECTING;
@@ -200,19 +115,19 @@ NetworkedMultiplayerPeer::ConnectionStatus EMWSClient::get_connection_status() c
}
return CONNECTION_DISCONNECTED;
-};
+}
void EMWSClient::disconnect_from_host(int p_code, String p_reason) {
_peer->close(p_code, p_reason);
-};
+}
-IP_Address EMWSClient::get_connected_host() const {
- ERR_FAIL_V_MSG(IP_Address(), "Not supported in HTML5 export.");
-};
+IPAddress EMWSClient::get_connected_host() const {
+ ERR_FAIL_V_MSG(IPAddress(), "Not supported in HTML5 export.");
+}
uint16_t EMWSClient::get_connected_port() const {
ERR_FAIL_V_MSG(0, "Not supported in HTML5 export.");
-};
+}
int EMWSClient::get_max_packet_size() const {
return (1 << _in_buf_size) - PROTO_SIZE;
@@ -221,30 +136,20 @@ int EMWSClient::get_max_packet_size() const {
Error EMWSClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
_in_buf_size = nearest_shift(p_in_buffer - 1) + 10;
_in_pkt_size = nearest_shift(p_in_packets - 1);
+ _out_buf_size = nearest_shift(p_out_buffer - 1) + 10;
return OK;
}
EMWSClient::EMWSClient() {
- _in_buf_size = DEF_BUF_SHIFT;
- _in_pkt_size = DEF_PKT_SHIFT;
-
- _is_connecting = false;
_peer = Ref<EMWSPeer>(memnew(EMWSPeer));
- /* clang-format off */
- _js_id = EM_ASM_INT({
- return Module.IDHandler.add($0);
- }, this);
- /* clang-format on */
-};
+}
EMWSClient::~EMWSClient() {
disconnect_from_host();
_peer = Ref<EMWSPeer>();
- /* clang-format off */
- EM_ASM({
- Module.IDHandler.remove($0);
- }, _js_id);
- /* clang-format on */
-};
+ if (_js_id) {
+ godot_js_websocket_destroy(_js_id);
+ }
+}
#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h
index ab8a0612bb..3b0b8395b9 100644
--- a/modules/websocket/emws_client.h
+++ b/modules/websocket/emws_client.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#ifdef JAVASCRIPT_ENABLED
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "emws_peer.h"
#include "websocket_client.h"
@@ -41,18 +41,23 @@ class EMWSClient : public WebSocketClient {
GDCIIMPL(EMWSClient, WebSocketClient);
private:
- int _in_buf_size;
- int _in_pkt_size;
- int _js_id;
+ int _js_id = 0;
+ bool _is_connecting = false;
+ int _in_buf_size = DEF_BUF_SHIFT;
+ int _in_pkt_size = DEF_PKT_SHIFT;
+ int _out_buf_size = DEF_BUF_SHIFT;
-public:
- bool _is_connecting;
+ static void _esws_on_connect(void *obj, char *proto);
+ static void _esws_on_message(void *obj, const uint8_t *p_data, int p_data_size, int p_is_string);
+ static void _esws_on_error(void *obj);
+ static void _esws_on_close(void *obj, int code, const char *reason, int was_clean);
+public:
Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets);
Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>());
Ref<WebSocketPeer> get_peer(int p_peer_id) const;
void disconnect_from_host(int p_code = 1000, String p_reason = "");
- IP_Address get_connected_host() const;
+ IPAddress get_connected_host() const;
uint16_t get_connected_port() const;
virtual ConnectionStatus get_connection_status() const;
int get_max_packet_size() const;
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index 749f45451a..035d036b90 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,10 +33,11 @@
#include "emws_peer.h"
#include "core/io/ip.h"
-void EMWSPeer::set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size) {
+void EMWSPeer::set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size) {
peer_sock = p_sock;
_in_buffer.resize(p_in_pkt_size, p_in_buf_size);
_packet_buffer.resize((1 << p_in_buf_size));
+ _out_buf_size = p_out_buf_size;
}
void EMWSPeer::set_write_mode(WriteMode p_mode) {
@@ -47,40 +48,22 @@ EMWSPeer::WriteMode EMWSPeer::get_write_mode() const {
return write_mode;
}
-Error EMWSPeer::read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string) {
+Error EMWSPeer::read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_string) {
uint8_t is_string = p_is_string ? 1 : 0;
return _in_buffer.write_packet(p_data, p_size, &is_string);
}
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) + p_buffer_size >= (1ULL << _out_buf_size)), ERR_OUT_OF_MEMORY);
+
int is_bin = write_mode == WebSocketPeer::WRITE_MODE_BINARY ? 1 : 0;
- /* clang-format off */
- EM_ASM({
- var sock = Module.IDHandler.get($0);
- var bytes_array = new Uint8Array($2);
- var i = 0;
-
- for(i=0; i<$2; i++) {
- bytes_array[i] = getValue($1+i, 'i8');
- }
-
- try {
- if ($3) {
- sock.send(bytes_array.buffer);
- } else {
- var string = new TextDecoder("utf-8").decode(bytes_array);
- sock.send(string);
- }
- } catch (e) {
- return 1;
- }
- return 0;
- }, peer_sock, p_buffer, p_buffer_size, is_bin);
- /* clang-format on */
+ if (godot_js_websocket_send(peer_sock, p_buffer, p_buffer_size, is_bin) != 0) {
+ return FAILED;
+ }
return OK;
-};
+}
Error EMWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
if (_in_buffer.packets_left() == 0)
@@ -94,57 +77,54 @@ Error EMWSPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
r_buffer_size = read;
return OK;
-};
+}
int EMWSPeer::get_available_packet_count() const {
return _in_buffer.packets_left();
-};
+}
+
+int EMWSPeer::get_current_outbound_buffered_amount() const {
+ if (peer_sock != -1) {
+ return godot_js_websocket_buffered_amount(peer_sock);
+ }
+ return 0;
+}
bool EMWSPeer::was_string_packet() const {
return _is_string;
-};
+}
bool EMWSPeer::is_connected_to_host() const {
return peer_sock != -1;
-};
+}
void EMWSPeer::close(int p_code, String p_reason) {
if (peer_sock != -1) {
- /* clang-format off */
- EM_ASM({
- var sock = Module.IDHandler.get($0);
- var code = $1;
- var reason = UTF8ToString($2);
- sock.close(code, reason);
- Module.IDHandler.remove($0);
- }, peer_sock, p_code, p_reason.utf8().get_data());
- /* clang-format on */
+ godot_js_websocket_close(peer_sock, p_code, p_reason.utf8().get_data());
}
_is_string = 0;
_in_buffer.clear();
peer_sock = -1;
-};
+}
-IP_Address EMWSPeer::get_connected_host() const {
- ERR_FAIL_V_MSG(IP_Address(), "Not supported in HTML5 export.");
+IPAddress EMWSPeer::get_connected_host() const {
+ ERR_FAIL_V_MSG(IPAddress(), "Not supported in HTML5 export.");
};
uint16_t EMWSPeer::get_connected_port() const {
ERR_FAIL_V_MSG(0, "Not supported in HTML5 export.");
-};
+}
void EMWSPeer::set_no_delay(bool p_enabled) {
ERR_FAIL_MSG("'set_no_delay' is not supported in HTML5 export.");
}
EMWSPeer::EMWSPeer() {
- peer_sock = -1;
- write_mode = WRITE_MODE_BINARY;
close();
-};
+}
EMWSPeer::~EMWSPeer() {
close();
-};
+}
#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h
index 9c00f2d58b..6e93ea31a2 100644
--- a/modules/websocket/emws_peer.h
+++ b/modules/websocket/emws_peer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,35 +33,50 @@
#ifdef JAVASCRIPT_ENABLED
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/io/packet_peer.h"
-#include "core/ring_buffer.h"
+#include "core/templates/ring_buffer.h"
#include "emscripten.h"
#include "packet_buffer.h"
#include "websocket_peer.h"
+extern "C" {
+typedef void (*WSOnOpen)(void *p_ref, char *p_protocol);
+typedef void (*WSOnMessage)(void *p_ref, const uint8_t *p_buf, int p_buf_len, int p_is_string);
+typedef void (*WSOnClose)(void *p_ref, int p_code, const char *p_reason, int p_is_clean);
+typedef void (*WSOnError)(void *p_ref);
+
+extern int godot_js_websocket_create(void *p_ref, const char *p_url, const char *p_proto, WSOnOpen p_on_open, WSOnMessage p_on_message, WSOnError p_on_error, WSOnClose p_on_close);
+extern int godot_js_websocket_send(int p_id, const uint8_t *p_buf, int p_buf_len, int p_raw);
+extern int godot_js_websocket_buffered_amount(int p_id);
+extern void godot_js_websocket_close(int p_id, int p_code, const char *p_reason);
+extern void godot_js_websocket_destroy(int p_id);
+}
+
class EMWSPeer : public WebSocketPeer {
GDCIIMPL(EMWSPeer, WebSocketPeer);
private:
- int peer_sock;
- WriteMode write_mode;
+ int peer_sock = -1;
+ WriteMode write_mode = WRITE_MODE_BINARY;
Vector<uint8_t> _packet_buffer;
PacketBuffer<uint8_t> _in_buffer;
- uint8_t _is_string;
+ uint8_t _is_string = 0;
+ int _out_buf_size = 0;
public:
- Error read_msg(uint8_t *p_data, uint32_t p_size, bool p_is_string);
- void set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size);
+ Error read_msg(const uint8_t *p_data, uint32_t p_size, bool p_is_string);
+ void set_sock(int p_sock, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size);
virtual int get_available_packet_count() const;
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
virtual int get_max_packet_size() const { return _packet_buffer.size(); };
+ virtual int get_current_outbound_buffered_amount() const;
virtual void close(int p_code = 1000, String p_reason = "");
virtual bool is_connected_to_host() const;
- virtual IP_Address get_connected_host() const;
+ virtual IPAddress get_connected_host() const;
virtual uint16_t get_connected_port() const;
virtual WriteMode get_write_mode() const;
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
index 9d43283d3e..4a4f09a943 100644
--- a/modules/websocket/emws_server.cpp
+++ b/modules/websocket/emws_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -58,8 +58,8 @@ Vector<String> EMWSServer::get_protocols() const {
return out;
}
-IP_Address EMWSServer::get_peer_address(int p_peer_id) const {
- return IP_Address();
+IPAddress EMWSServer::get_peer_address(int p_peer_id) const {
+ return IPAddress();
}
int EMWSServer::get_peer_port(int p_peer_id) const {
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
index bb6f35a711..d36e3a3557 100644
--- a/modules/websocket/emws_server.h
+++ b/modules/websocket/emws_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#ifdef JAVASCRIPT_ENABLED
-#include "core/reference.h"
+#include "core/object/ref_counted.h"
#include "emws_peer.h"
#include "websocket_server.h"
@@ -47,7 +47,7 @@ public:
bool is_listening() const;
bool has_peer(int p_id) const;
Ref<WebSocketPeer> get_peer(int p_id) const;
- IP_Address get_peer_address(int p_peer_id) const;
+ IPAddress get_peer_address(int p_peer_id) const;
int get_peer_port(int p_peer_id) const;
void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "");
int get_max_packet_size() const;
diff --git a/modules/websocket/library_godot_websocket.js b/modules/websocket/library_godot_websocket.js
new file mode 100644
index 0000000000..dd2fd1e94f
--- /dev/null
+++ b/modules/websocket/library_godot_websocket.js
@@ -0,0 +1,202 @@
+/*************************************************************************/
+/* library_godot_websocket.js */
+/*************************************************************************/
+/* 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. */
+/*************************************************************************/
+
+const GodotWebSocket = {
+ // Our socket implementation that forwards events to C++.
+ $GodotWebSocket__deps: ['$IDHandler', '$GodotRuntime'],
+ $GodotWebSocket: {
+ // Connection opened, report selected protocol
+ _onopen: function (p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return; // Godot object is gone.
+ }
+ const c_str = GodotRuntime.allocString(ref.protocol);
+ callback(c_str);
+ GodotRuntime.free(c_str);
+ },
+
+ // Message received, report content and type (UTF8 vs binary)
+ _onmessage: function (p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return; // Godot object is gone.
+ }
+ let buffer;
+ let is_string = 0;
+ if (event.data instanceof ArrayBuffer) {
+ buffer = new Uint8Array(event.data);
+ } else if (event.data instanceof Blob) {
+ GodotRuntime.error('Blob type not supported');
+ return;
+ } else if (typeof event.data === 'string') {
+ is_string = 1;
+ const enc = new TextEncoder('utf-8');
+ buffer = new Uint8Array(enc.encode(event.data));
+ } else {
+ GodotRuntime.error('Unknown message type');
+ return;
+ }
+ const len = buffer.length * buffer.BYTES_PER_ELEMENT;
+ const out = GodotRuntime.malloc(len);
+ HEAPU8.set(buffer, out);
+ callback(out, len, is_string);
+ GodotRuntime.free(out);
+ },
+
+ // An error happened, 'onclose' will be called after this.
+ _onerror: function (p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return; // Godot object is gone.
+ }
+ callback();
+ },
+
+ // Connection is closed, this is always fired. Report close code, reason, and clean status.
+ _onclose: function (p_id, callback, event) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return; // Godot object is gone.
+ }
+ const c_str = GodotRuntime.allocString(event.reason);
+ callback(event.code, c_str, event.wasClean ? 1 : 0);
+ GodotRuntime.free(c_str);
+ },
+
+ // Send a message
+ send: function (p_id, p_data) {
+ const ref = IDHandler.get(p_id);
+ if (!ref || ref.readyState !== ref.OPEN) {
+ return 1; // Godot object is gone or socket is not in a ready state.
+ }
+ ref.send(p_data);
+ return 0;
+ },
+
+ // Get current bufferedAmount
+ bufferedAmount: function (p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return 0; // Godot object is gone.
+ }
+ return ref.bufferedAmount;
+ },
+
+ create: function (socket, p_on_open, p_on_message, p_on_error, p_on_close) {
+ const id = IDHandler.add(socket);
+ socket.onopen = GodotWebSocket._onopen.bind(null, id, p_on_open);
+ socket.onmessage = GodotWebSocket._onmessage.bind(null, id, p_on_message);
+ socket.onerror = GodotWebSocket._onerror.bind(null, id, p_on_error);
+ socket.onclose = GodotWebSocket._onclose.bind(null, id, p_on_close);
+ return id;
+ },
+
+ // Closes the JavaScript WebSocket (if not already closing) associated to a given C++ object.
+ close: function (p_id, p_code, p_reason) {
+ const ref = IDHandler.get(p_id);
+ if (ref && ref.readyState < ref.CLOSING) {
+ const code = p_code;
+ const reason = GodotRuntime.parseString(p_reason);
+ ref.close(code, reason);
+ }
+ },
+
+ // Deletes the reference to a C++ object (closing any connected socket if necessary).
+ destroy: function (p_id) {
+ const ref = IDHandler.get(p_id);
+ if (!ref) {
+ return;
+ }
+ GodotWebSocket.close(p_id, 1001, '');
+ IDHandler.remove(p_id);
+ ref.onopen = null;
+ ref.onmessage = null;
+ ref.onerror = null;
+ ref.onclose = null;
+ },
+ },
+
+ godot_js_websocket_create__sig: 'iiiiiiii',
+ godot_js_websocket_create: function (p_ref, p_url, p_proto, p_on_open, p_on_message, p_on_error, p_on_close) {
+ const on_open = GodotRuntime.get_func(p_on_open).bind(null, p_ref);
+ const on_message = GodotRuntime.get_func(p_on_message).bind(null, p_ref);
+ const on_error = GodotRuntime.get_func(p_on_error).bind(null, p_ref);
+ const on_close = GodotRuntime.get_func(p_on_close).bind(null, p_ref);
+ const url = GodotRuntime.parseString(p_url);
+ const protos = GodotRuntime.parseString(p_proto);
+ let socket = null;
+ try {
+ if (protos) {
+ socket = new WebSocket(url, protos.split(','));
+ } else {
+ socket = new WebSocket(url);
+ }
+ } catch (e) {
+ return 0;
+ }
+ socket.binaryType = 'arraybuffer';
+ return GodotWebSocket.create(socket, on_open, on_message, on_error, on_close);
+ },
+
+ godot_js_websocket_send__sig: 'iiiii',
+ godot_js_websocket_send: function (p_id, p_buf, p_buf_len, p_raw) {
+ const bytes_array = new Uint8Array(p_buf_len);
+ let i = 0;
+ for (i = 0; i < p_buf_len; i++) {
+ bytes_array[i] = GodotRuntime.getHeapValue(p_buf + i, 'i8');
+ }
+ let out = bytes_array.buffer;
+ if (!p_raw) {
+ out = new TextDecoder('utf-8').decode(bytes_array);
+ }
+ return GodotWebSocket.send(p_id, out);
+ },
+
+ godot_js_websocket_buffered_amount__sig: 'ii',
+ godot_js_websocket_buffered_amount: function (p_id) {
+ return GodotWebSocket.bufferedAmount(p_id);
+ },
+
+ godot_js_websocket_close__sig: 'viii',
+ godot_js_websocket_close: function (p_id, p_code, p_reason) {
+ const code = p_code;
+ const reason = GodotRuntime.parseString(p_reason);
+ GodotWebSocket.close(p_id, code, reason);
+ },
+
+ godot_js_websocket_destroy__sig: 'vi',
+ godot_js_websocket_destroy: function (p_id) {
+ GodotWebSocket.destroy(p_id);
+ },
+};
+
+autoAddDeps(GodotWebSocket, '$GodotWebSocket');
+mergeInto(LibraryManager.library, GodotWebSocket);
diff --git a/modules/websocket/packet_buffer.h b/modules/websocket/packet_buffer.h
index 9973efe297..e99a379767 100644
--- a/modules/websocket/packet_buffer.h
+++ b/modules/websocket/packet_buffer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,7 @@
#ifndef PACKET_BUFFER_H
#define PACKET_BUFFER_H
-#include "core/os/copymem.h"
-#include "core/ring_buffer.h"
+#include "core/templates/ring_buffer.h"
template <class T>
class PacketBuffer {
@@ -66,7 +65,7 @@ public:
if (p_info) {
_Packet p;
p.size = p_size;
- copymem(&p.info, p_info, sizeof(T));
+ memcpy(&p.info, p_info, sizeof(T));
_packets.write(p);
}
@@ -86,7 +85,7 @@ public:
ERR_FAIL_COND_V(p_bytes < (int)p.size, ERR_OUT_OF_MEMORY);
r_read = p.size;
- copymem(r_info, &p.info, sizeof(T));
+ memcpy(r_info, &p.info, sizeof(T));
_payload.read(r_payload, p.size);
return OK;
}
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index bc50de414e..7c742b1b89 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,8 +29,8 @@
/*************************************************************************/
#include "register_types.h"
-#include "core/error_macros.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
+#include "core/error/error_macros.h"
#ifdef JAVASCRIPT_ENABLED
#include "emscripten.h"
#include "emws_client.h"
@@ -63,7 +63,7 @@ void register_websocket_types() {
WSLServer::make_default();
#endif
- ClassDB::register_virtual_class<WebSocketMultiplayerPeer>();
+ GDREGISTER_VIRTUAL_CLASS(WebSocketMultiplayerPeer);
ClassDB::register_custom_instance_class<WebSocketServer>();
ClassDB::register_custom_instance_class<WebSocketClient>();
ClassDB::register_custom_instance_class<WebSocketPeer>();
diff --git a/modules/websocket/register_types.h b/modules/websocket/register_types.h
index bb7be57ab3..3884db67b7 100644
--- a/modules/websocket/register_types.h
+++ b/modules/websocket/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
diff --git a/modules/websocket/remote_debugger_peer_websocket.cpp b/modules/websocket/remote_debugger_peer_websocket.cpp
index a67a959e31..c9591cc564 100644
--- a/modules/websocket/remote_debugger_peer_websocket.cpp
+++ b/modules/websocket/remote_debugger_peer_websocket.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -30,7 +30,7 @@
#include "remote_debugger_peer_websocket.h"
-#include "core/project_settings.h"
+#include "core/config/project_settings.h"
Error RemoteDebuggerPeerWebSocket::connect_to_host(const String &p_uri) {
Vector<String> protocols;
diff --git a/modules/websocket/remote_debugger_peer_websocket.h b/modules/websocket/remote_debugger_peer_websocket.h
index bb03e5e892..590d925dcc 100644
--- a/modules/websocket/remote_debugger_peer_websocket.h
+++ b/modules/websocket/remote_debugger_peer_websocket.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,8 +28,8 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef SCRIPT_DEBUGGER_WEBSOCKET_H
-#define SCRIPT_DEBUGGER_WEBSOCKET_H
+#ifndef REMOTE_DEBUGGER_PEER_WEBSOCKET_H
+#define REMOTE_DEBUGGER_PEER_WEBSOCKET_H
#ifdef JAVASCRIPT_ENABLED
#include "modules/websocket/emws_client.h"
@@ -62,4 +62,4 @@ public:
RemoteDebuggerPeerWebSocket(Ref<WebSocketPeer> p_peer = Ref<WebSocketPeer>());
};
-#endif // SCRIPT_DEBUGGER_WEBSOCKET_H
+#endif // REMOTE_DEBUGGER_PEER_WEBSOCKET_H
diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp
index 8feaa9af5a..bf35c91c7f 100644
--- a/modules/websocket/websocket_client.cpp
+++ b/modules/websocket/websocket_client.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,6 @@
GDCINULL(WebSocketClient);
WebSocketClient::WebSocketClient() {
- verify_ssl = true;
}
WebSocketClient::~WebSocketClient() {
@@ -43,35 +42,22 @@ Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_proto
_is_multiplayer = gd_mp_api;
String host = p_url;
- String path = "/";
- int p_len = -1;
- int port = 80;
+ String path;
+ String scheme;
+ int port = 0;
+ Error err = p_url.parse_url(scheme, host, port, path);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);
+
bool ssl = false;
- if (host.begins_with("wss://")) {
- ssl = true; // we should implement this
- host = host.substr(6, host.length() - 6);
- port = 443;
- } else {
- ssl = false;
- if (host.begins_with("ws://")) {
- host = host.substr(5, host.length() - 5);
- }
+ if (scheme == "wss://") {
+ ssl = true;
}
-
- // Path
- p_len = host.find("/");
- if (p_len != -1) {
- path = host.substr(p_len, host.length() - p_len);
- host = host.substr(0, p_len);
+ if (port == 0) {
+ port = ssl ? 443 : 80;
}
-
- // Port
- p_len = host.rfind(":");
- if (p_len != -1 && p_len == host.find(":")) {
- port = host.substr(p_len, host.length() - p_len).to_int();
- host = host.substr(0, p_len);
+ if (path.is_empty()) {
+ path = "/";
}
-
return connect_to_host(host, path, port, ssl, p_protocols, p_custom_headers);
}
@@ -100,7 +86,7 @@ void WebSocketClient::_on_peer_packet() {
if (_is_multiplayer) {
_process_multiplayer(get_peer(1), 1);
} else {
- emit_signal("data_received");
+ emit_signal(SNAME("data_received"));
}
}
@@ -108,27 +94,27 @@ void WebSocketClient::_on_connect(String p_protocol) {
if (_is_multiplayer) {
// need to wait for ID confirmation...
} else {
- emit_signal("connection_established", p_protocol);
+ emit_signal(SNAME("connection_established"), p_protocol);
}
}
void WebSocketClient::_on_close_request(int p_code, String p_reason) {
- emit_signal("server_close_request", p_code, p_reason);
+ emit_signal(SNAME("server_close_request"), p_code, p_reason);
}
void WebSocketClient::_on_disconnect(bool p_was_clean) {
if (_is_multiplayer) {
- emit_signal("connection_failed");
+ emit_signal(SNAME("connection_failed"));
} else {
- emit_signal("connection_closed", p_was_clean);
+ emit_signal(SNAME("connection_closed"), p_was_clean);
}
}
void WebSocketClient::_on_error() {
if (_is_multiplayer) {
- emit_signal("connection_failed");
+ emit_signal(SNAME("connection_failed"));
} else {
- emit_signal("connection_error");
+ emit_signal(SNAME("connection_error"));
}
}
@@ -140,12 +126,12 @@ void WebSocketClient::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled);
ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled");
+ 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", 0), "set_trusted_ssl_certificate", "get_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");
ADD_SIGNAL(MethodInfo("data_received"));
ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol")));
diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h
index 1053be22e3..c7f17f1ffb 100644
--- a/modules/websocket/websocket_client.h
+++ b/modules/websocket/websocket_client.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define WEBSOCKET_CLIENT_H
#include "core/crypto/crypto.h"
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "websocket_multiplayer_peer.h"
#include "websocket_peer.h"
@@ -42,7 +42,7 @@ class WebSocketClient : public WebSocketMultiplayerPeer {
protected:
Ref<WebSocketPeer> _peer;
- bool verify_ssl;
+ bool verify_ssl = true;
Ref<X509Certificate> ssl_cert;
static void _bind_methods();
@@ -57,7 +57,7 @@ public:
virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) = 0;
virtual void disconnect_from_host(int p_code = 1000, String p_reason = "") = 0;
- virtual IP_Address get_connected_host() const = 0;
+ virtual IPAddress get_connected_host() const = 0;
virtual uint16_t get_connected_port() const = 0;
virtual bool is_server() const override;
diff --git a/modules/websocket/websocket_macros.h b/modules/websocket/websocket_macros.h
index cf4545b435..2ca60a3b61 100644
--- a/modules/websocket/websocket_macros.h
+++ b/modules/websocket/websocket_macros.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -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 fa2fe891a5..e54bfbca12 100644
--- a/modules/websocket/websocket_multiplayer_peer.cpp
+++ b/modules/websocket/websocket_multiplayer_peer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,50 +33,21 @@
#include "core/os/os.h"
WebSocketMultiplayerPeer::WebSocketMultiplayerPeer() {
- _is_multiplayer = false;
- _peer_id = 0;
- _target_peer = 0;
- _refusing = false;
-
- _current_packet.source = 0;
- _current_packet.destination = 0;
- _current_packet.size = 0;
- _current_packet.data = nullptr;
}
WebSocketMultiplayerPeer::~WebSocketMultiplayerPeer() {
_clear();
}
-int WebSocketMultiplayerPeer::_gen_unique_id() const {
- uint32_t hash = 0;
-
- while (hash == 0 || hash == 1) {
- hash = hash_djb2_one_32(
- (uint32_t)OS::get_singleton()->get_ticks_usec());
- hash = hash_djb2_one_32(
- (uint32_t)OS::get_singleton()->get_unix_time(), hash);
- hash = hash_djb2_one_32(
- (uint32_t)OS::get_singleton()->get_data_path().hash64(), hash);
- hash = hash_djb2_one_32(
- (uint32_t)((uint64_t)this), hash); //rely on aslr heap
- hash = hash_djb2_one_32(
- (uint32_t)((uint64_t)&hash), hash); //rely on aslr stack
- hash = hash & 0x7FFFFFFF; // make it compatible with unsigned, since negatie id is used for exclusion
- }
-
- return hash;
-}
-
void WebSocketMultiplayerPeer::_clear() {
_peer_map.clear();
if (_current_packet.data != nullptr) {
memfree(_current_packet.data);
}
- for (List<Packet>::Element *E = _incoming_packets.front(); E; E = E->next()) {
- memfree(E->get().data);
- E->get().data = nullptr;
+ for (Packet &E : _incoming_packets) {
+ memfree(E.data);
+ E.data = nullptr;
}
_incoming_packets.clear();
@@ -108,6 +79,8 @@ Error WebSocketMultiplayerPeer::get_packet(const uint8_t **r_buffer, int &r_buff
_current_packet.data = nullptr;
}
+ ERR_FAIL_COND_V(_incoming_packets.size() == 0, ERR_UNAVAILABLE);
+
_current_packet = _incoming_packets.front()->get();
_incoming_packets.pop_front();
@@ -130,17 +103,8 @@ Error WebSocketMultiplayerPeer::put_packet(const uint8_t *p_buffer, int p_buffer
}
//
-// NetworkedMultiplayerPeer
+// MultiplayerPeer
//
-void WebSocketMultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
- // Websocket uses TCP, reliable
-}
-
-NetworkedMultiplayerPeer::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;
}
@@ -156,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());
@@ -177,10 +133,10 @@ Vector<uint8_t> WebSocketMultiplayerPeer::_make_pkt(uint8_t p_type, int32_t p_fr
out.resize(PROTO_SIZE + p_data_size);
uint8_t *w = out.ptrw();
- copymem(&w[0], &p_type, 1);
- copymem(&w[1], &p_from, 4);
- copymem(&w[5], &p_to, 4);
- copymem(&w[PROTO_SIZE], p_data, p_data_size);
+ memcpy(&w[0], &p_type, 1);
+ memcpy(&w[1], &p_from, 4);
+ memcpy(&w[5], &p_to, 4);
+ memcpy(&w[PROTO_SIZE], p_data, p_data_size);
return out;
}
@@ -192,10 +148,10 @@ 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 newwly added peer (already confirmed)
+ continue; // Skip the newly added peer (already confirmed)
}
// Send new peer to others
@@ -206,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);
}
@@ -220,9 +176,9 @@ void WebSocketMultiplayerPeer::_store_pkt(int32_t p_source, int32_t p_dest, cons
packet.size = p_data_size;
packet.source = p_source;
packet.destination = p_dest;
- copymem(packet.data, &p_data[PROTO_SIZE], p_data_size);
+ memcpy(packet.data, &p_data[PROTO_SIZE], p_data_size);
_incoming_packets.push_back(packet);
- emit_signal("peer_packet", p_source);
+ emit_signal(SNAME("peer_packet"), p_source);
}
Error WebSocketMultiplayerPeer::_server_relay(int32_t p_from, int32_t p_to, const uint8_t *p_buffer, uint32_t p_buffer_size) {
@@ -230,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
@@ -272,9 +228,9 @@ void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, u
uint8_t type = 0;
uint32_t from = 0;
int32_t to = 0;
- copymem(&type, in_buffer, 1);
- copymem(&from, &in_buffer[1], 4);
- copymem(&to, &in_buffer[5], 4);
+ memcpy(&type, in_buffer, 1);
+ memcpy(&from, &in_buffer[1], 4);
+ memcpy(&to, &in_buffer[5], 4);
if (is_server()) { // Server can resend
@@ -308,22 +264,22 @@ void WebSocketMultiplayerPeer::_process_multiplayer(Ref<WebSocketPeer> p_peer, u
// System message
ERR_FAIL_COND(data_size < 4);
int id = 0;
- copymem(&id, &in_buffer[PROTO_SIZE], 4);
+ memcpy(&id, &in_buffer[PROTO_SIZE], 4);
switch (type) {
case SYS_ADD: // Add peer
_peer_map[id] = Ref<WebSocketPeer>();
- emit_signal("peer_connected", id);
+ emit_signal(SNAME("peer_connected"), id);
if (id == 1) { // We just connected to the server
- emit_signal("connection_succeeded");
+ emit_signal(SNAME("connection_succeeded"));
}
break;
case SYS_DEL: // Remove peer
_peer_map.erase(id);
- emit_signal("peer_disconnected", id);
+ emit_signal(SNAME("peer_disconnected"), id);
break;
- case SYS_ID: // Helo, server assigned ID
+ case SYS_ID: // Hello, server assigned ID
_peer_id = id;
break;
default:
diff --git a/modules/websocket/websocket_multiplayer_peer.h b/modules/websocket/websocket_multiplayer_peer.h
index af26aef21e..380edf67ed 100644
--- a/modules/websocket/websocket_multiplayer_peer.h
+++ b/modules/websocket/websocket_multiplayer_peer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,13 +31,13 @@
#ifndef WEBSOCKET_MULTIPLAYER_PEER_H
#define WEBSOCKET_MULTIPLAYER_PEER_H
-#include "core/error_list.h"
-#include "core/io/networked_multiplayer_peer.h"
-#include "core/list.h"
+#include "core/error/error_list.h"
+#include "core/multiplayer/multiplayer_peer.h"
+#include "core/templates/list.h"
#include "websocket_peer.h"
-class WebSocketMultiplayerPeer : public NetworkedMultiplayerPeer {
- GDCLASS(WebSocketMultiplayerPeer, NetworkedMultiplayerPeer);
+class WebSocketMultiplayerPeer : public MultiplayerPeer {
+ GDCLASS(WebSocketMultiplayerPeer, MultiplayerPeer);
private:
Vector<uint8_t> _make_pkt(uint8_t p_type, int32_t p_from, int32_t p_to, const uint8_t *p_data, uint32_t p_data_size);
@@ -55,37 +55,31 @@ protected:
};
struct Packet {
- int source;
- int destination;
- uint8_t *data;
- uint32_t size;
+ int source = 0;
+ int destination = 0;
+ uint8_t *data = nullptr;
+ uint32_t size = 0;
};
List<Packet> _incoming_packets;
Map<int, Ref<WebSocketPeer>> _peer_map;
Packet _current_packet;
- bool _is_multiplayer;
- int _target_peer;
- int _peer_id;
- int _refusing;
+ bool _is_multiplayer = false;
+ int _target_peer = 0;
+ int _peer_id = 0;
static void _bind_methods();
void _send_add(int32_t p_peer_id);
void _send_sys(Ref<WebSocketPeer> p_peer, uint8_t p_type, int32_t p_peer_id);
void _send_del(int32_t p_peer_id);
- int _gen_unique_id() const;
public:
- /* NetworkedMultiplayerPeer */
- void set_transfer_mode(TransferMode p_mode) override;
- TransferMode get_transfer_mode() const override;
+ /* MultiplayerPeer */
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_peer.cpp b/modules/websocket/websocket_peer.cpp
index 30a5972330..ee13040821 100644
--- a/modules/websocket/websocket_peer.cpp
+++ b/modules/websocket/websocket_peer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -47,6 +47,7 @@ void WebSocketPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketPeer::get_connected_host);
ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketPeer::get_connected_port);
ClassDB::bind_method(D_METHOD("set_no_delay", "enabled"), &WebSocketPeer::set_no_delay);
+ ClassDB::bind_method(D_METHOD("get_current_outbound_buffered_amount"), &WebSocketPeer::get_current_outbound_buffered_amount);
BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
index a61943b615..517b8600d6 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,7 +31,7 @@
#ifndef WEBSOCKETPEER_H
#define WEBSOCKETPEER_H
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/io/packet_peer.h"
#include "websocket_macros.h"
@@ -55,10 +55,11 @@ public:
virtual void close(int p_code = 1000, String p_reason = "") = 0;
virtual bool is_connected_to_host() const = 0;
- virtual IP_Address get_connected_host() const = 0;
+ virtual IPAddress get_connected_host() const = 0;
virtual uint16_t get_connected_port() const = 0;
virtual bool was_string_packet() const = 0;
virtual void set_no_delay(bool p_enabled) = 0;
+ virtual int get_current_outbound_buffered_amount() const = 0;
WebSocketPeer();
~WebSocketPeer();
diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp
index b20b925dec..e7f90fdeba 100644
--- a/modules/websocket/websocket_server.cpp
+++ b/modules/websocket/websocket_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -34,7 +34,7 @@ GDCINULL(WebSocketServer);
WebSocketServer::WebSocketServer() {
_peer_id = 1;
- bind_ip = IP_Address("*");
+ bind_ip = IPAddress("*");
}
WebSocketServer::~WebSocketServer() {
@@ -50,32 +50,36 @@ 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);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "private_key", PROPERTY_HINT_RESOURCE_TYPE, "CryptoKey", 0), "set_private_key", "get_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);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_ssl_certificate", "get_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);
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ca_chain", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_ca_chain", "get_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);
+ ClassDB::bind_method(D_METHOD("set_handshake_timeout", "timeout"), &WebSocketServer::set_handshake_timeout);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handshake_timeout"), "set_handshake_timeout", "get_handshake_timeout");
ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason")));
ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close")));
- ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol")));
+ ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol"), PropertyInfo(Variant::STRING, "resource_name")));
ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id")));
}
-IP_Address WebSocketServer::get_bind_ip() const {
+IPAddress WebSocketServer::get_bind_ip() const {
return bind_ip;
}
-void WebSocketServer::set_bind_ip(const IP_Address &p_bind_ip) {
+void WebSocketServer::set_bind_ip(const IPAddress &p_bind_ip) {
ERR_FAIL_COND(is_listening());
ERR_FAIL_COND(!p_bind_ip.is_valid() && !p_bind_ip.is_wildcard());
bind_ip = p_bind_ip;
@@ -108,7 +112,16 @@ void WebSocketServer::set_ca_chain(Ref<X509Certificate> p_ca_chain) {
ca_chain = p_ca_chain;
}
-NetworkedMultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const {
+float WebSocketServer::get_handshake_timeout() const {
+ return handshake_timeout / 1000.0;
+}
+
+void WebSocketServer::set_handshake_timeout(float p_timeout) {
+ ERR_FAIL_COND(p_timeout <= 0.0);
+ handshake_timeout = p_timeout * 1000;
+}
+
+MultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const {
if (is_listening()) {
return CONNECTION_CONNECTED;
}
@@ -124,17 +137,17 @@ void WebSocketServer::_on_peer_packet(int32_t p_peer_id) {
if (_is_multiplayer) {
_process_multiplayer(get_peer(p_peer_id), p_peer_id);
} else {
- emit_signal("data_received", p_peer_id);
+ emit_signal(SNAME("data_received"), p_peer_id);
}
}
-void WebSocketServer::_on_connect(int32_t p_peer_id, String p_protocol) {
+void WebSocketServer::_on_connect(int32_t p_peer_id, String p_protocol, String p_resource_name) {
if (_is_multiplayer) {
// Send add to clients
_send_add(p_peer_id);
- emit_signal("peer_connected", p_peer_id);
+ emit_signal(SNAME("peer_connected"), p_peer_id);
} else {
- emit_signal("client_connected", p_peer_id, p_protocol);
+ emit_signal(SNAME("client_connected"), p_peer_id, p_protocol, p_resource_name);
}
}
@@ -142,12 +155,12 @@ void WebSocketServer::_on_disconnect(int32_t p_peer_id, bool p_was_clean) {
if (_is_multiplayer) {
// Send delete to clients
_send_del(p_peer_id);
- emit_signal("peer_disconnected", p_peer_id);
+ emit_signal(SNAME("peer_disconnected"), p_peer_id);
} else {
- emit_signal("client_disconnected", p_peer_id, p_was_clean);
+ emit_signal(SNAME("client_disconnected"), p_peer_id, p_was_clean);
}
}
void WebSocketServer::_on_close_request(int32_t p_peer_id, int p_code, String p_reason) {
- emit_signal("client_close_request", p_peer_id, p_code, p_reason);
+ emit_signal(SNAME("client_close_request"), p_peer_id, p_code, p_reason);
}
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
index 064ad4f179..c4d651471f 100644
--- a/modules/websocket/websocket_server.h
+++ b/modules/websocket/websocket_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -32,7 +32,7 @@
#define WEBSOCKET_H
#include "core/crypto/crypto.h"
-#include "core/reference.h"
+#include "core/object/ref_counted.h"
#include "websocket_multiplayer_peer.h"
#include "websocket_peer.h"
@@ -40,7 +40,7 @@ class WebSocketServer : public WebSocketMultiplayerPeer {
GDCLASS(WebSocketServer, WebSocketMultiplayerPeer);
GDCICLASS(WebSocketServer);
- IP_Address bind_ip;
+ IPAddress bind_ip;
protected:
static void _bind_methods();
@@ -48,6 +48,7 @@ protected:
Ref<CryptoKey> private_key;
Ref<X509Certificate> ssl_cert;
Ref<X509Certificate> ca_chain;
+ uint32_t handshake_timeout = 3000;
public:
virtual Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) = 0;
@@ -57,17 +58,17 @@ public:
virtual bool is_server() const override;
ConnectionStatus get_connection_status() const override;
- virtual IP_Address get_peer_address(int p_peer_id) const = 0;
+ virtual IPAddress get_peer_address(int p_peer_id) const = 0;
virtual int get_peer_port(int p_peer_id) const = 0;
virtual void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "") = 0;
void _on_peer_packet(int32_t p_peer_id);
- void _on_connect(int32_t p_peer_id, String p_protocol);
+ void _on_connect(int32_t p_peer_id, String p_protocol, String p_resource_name);
void _on_disconnect(int32_t p_peer_id, bool p_was_clean);
void _on_close_request(int32_t p_peer_id, int p_code, String p_reason);
- IP_Address get_bind_ip() const;
- void set_bind_ip(const IP_Address &p_bind_ip);
+ IPAddress get_bind_ip() const;
+ void set_bind_ip(const IPAddress &p_bind_ip);
Ref<CryptoKey> get_private_key() const;
void set_private_key(Ref<CryptoKey> p_key);
@@ -78,6 +79,9 @@ public:
Ref<X509Certificate> get_ca_chain() const;
void set_ca_chain(Ref<X509Certificate> p_ca_chain);
+ float get_handshake_timeout() const;
+ void set_handshake_timeout(float p_timeout);
+
WebSocketServer();
~WebSocketServer();
};
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
index 7d16c2e99f..26c0176ea4 100644
--- a/modules/websocket/wsl_client.cpp
+++ b/modules/websocket/wsl_client.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,8 +31,8 @@
#ifndef JAVASCRIPT_ENABLED
#include "wsl_client.h"
+#include "core/config/project_settings.h"
#include "core/io/ip.h"
-#include "core/project_settings.h"
void WSLClient::_do_handshake() {
if (_requested < _request.size() - 1) {
@@ -158,24 +158,31 @@ bool WSLClient::_verify_headers(String &r_protocol) {
Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) {
ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(p_path.is_empty(), ERR_INVALID_PARAMETER);
_peer = Ref<WSLPeer>(memnew(WSLPeer));
- IP_Address addr;
- if (!p_host.is_valid_ip_address()) {
- addr = IP::get_singleton()->resolve_hostname(p_host);
+ if (p_host.is_valid_ip_address()) {
+ ip_candidates.clear();
+ ip_candidates.push_back(IPAddress(p_host));
} else {
- addr = p_host;
+ ip_candidates = IP::get_singleton()->resolve_hostname_addresses(p_host);
}
- ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(ip_candidates.is_empty(), ERR_INVALID_PARAMETER);
String port = "";
if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
port = ":" + itos(p_port);
}
- Error err = _tcp->connect_to_host(addr, p_port);
+ Error err = ERR_BUG; // Should be at least one entry.
+ while (ip_candidates.size() > 0) {
+ err = _tcp->connect_to_host(ip_candidates.pop_front(), p_port);
+ if (err == OK) {
+ break;
+ }
+ }
if (err != OK) {
_tcp->disconnect_from_host();
_on_error();
@@ -184,6 +191,7 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
_connection = _tcp;
_use_ssl = p_ssl;
_host = p_host;
+ _port = p_port;
// Strip edges from protocols.
_protocols.resize(p_protocols.size());
String *pw = _protocols.ptrw();
@@ -243,6 +251,7 @@ void WSLClient::poll() {
_on_error();
break;
case StreamPeerTCP::STATUS_CONNECTED: {
+ ip_candidates.clear();
Ref<StreamPeerSSL> ssl;
if (_use_ssl) {
if (_connection == _tcp) {
@@ -273,6 +282,12 @@ void WSLClient::poll() {
_do_handshake();
} break;
case StreamPeerTCP::STATUS_ERROR:
+ while (ip_candidates.size() > 0) {
+ _tcp->disconnect_from_host();
+ if (_tcp->connect_to_host(ip_candidates.pop_front(), _port) == OK) {
+ return;
+ }
+ }
disconnect_from_host();
_on_error();
break;
@@ -287,7 +302,7 @@ Ref<WebSocketPeer> WSLClient::get_peer(int p_peer_id) const {
return _peer;
}
-NetworkedMultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() const {
+MultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() const {
if (_peer->is_connected_to_host()) {
return CONNECTION_CONNECTED;
}
@@ -314,10 +329,12 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) {
memset(_resp_buf, 0, sizeof(_resp_buf));
_resp_pos = 0;
+
+ ip_candidates.clear();
}
-IP_Address WSLClient::get_connected_host() const {
- ERR_FAIL_COND_V(!_peer->is_connected_to_host(), IP_Address());
+IPAddress WSLClient::get_connected_host() const {
+ ERR_FAIL_COND_V(!_peer->is_connected_to_host(), IPAddress());
return _peer->get_connected_host();
}
@@ -337,13 +354,8 @@ Error WSLClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer
}
WSLClient::WSLClient() {
- _in_buf_size = DEF_BUF_SHIFT;
- _in_pkt_size = DEF_PKT_SHIFT;
- _out_buf_size = DEF_BUF_SHIFT;
- _out_pkt_size = DEF_PKT_SHIFT;
-
- _peer.instance();
- _tcp.instance();
+ _peer.instantiate();
+ _tcp.instantiate();
disconnect_from_host();
}
diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h
index 8355a5a737..3972977910 100644
--- a/modules/websocket/wsl_client.h
+++ b/modules/websocket/wsl_client.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,7 +33,7 @@
#ifndef JAVASCRIPT_ENABLED
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/io/stream_peer_ssl.h"
#include "core/io/stream_peer_tcp.h"
#include "websocket_client.h"
@@ -44,27 +44,29 @@ class WSLClient : public WebSocketClient {
GDCIIMPL(WSLClient, WebSocketClient);
private:
- int _in_buf_size;
- int _in_pkt_size;
- int _out_buf_size;
- int _out_pkt_size;
+ int _in_buf_size = DEF_BUF_SHIFT;
+ int _in_pkt_size = DEF_PKT_SHIFT;
+ int _out_buf_size = DEF_BUF_SHIFT;
+ int _out_pkt_size = DEF_PKT_SHIFT;
Ref<WSLPeer> _peer;
Ref<StreamPeerTCP> _tcp;
Ref<StreamPeer> _connection;
CharString _request;
- int _requested;
+ int _requested = 0;
uint8_t _resp_buf[WSL_MAX_HEADER_SIZE];
- int _resp_pos;
+ int _resp_pos = 0;
String _response;
String _key;
String _host;
+ int _port;
+ Array ip_candidates;
Vector<String> _protocols;
- bool _use_ssl;
+ bool _use_ssl = false;
void _do_handshake();
bool _verify_headers(String &r_protocol);
@@ -75,7 +77,7 @@ public:
int get_max_packet_size() const;
Ref<WebSocketPeer> get_peer(int p_peer_id) const;
void disconnect_from_host(int p_code = 1000, String p_reason = "");
- IP_Address get_connected_host() const;
+ IPAddress get_connected_host() const;
uint16_t get_connected_port() const;
virtual ConnectionStatus get_connection_status() const;
virtual void poll();
diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp
index bf1ba43f8a..fc520ec57c 100644
--- a/modules/websocket/wsl_peer.cpp
+++ b/modules/websocket/wsl_peer.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -205,7 +205,9 @@ void WSLPeer::make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigne
ERR_FAIL_COND(p_data == nullptr);
_in_buffer.resize(p_in_pkt_size, p_in_buf_size);
- _packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size)));
+ _packet_buffer.resize(1 << p_in_buf_size);
+ _out_buf_size = p_out_buf_size;
+ _out_pkt_size = p_out_pkt_size;
_data = p_data;
_data->peer = this;
@@ -239,14 +241,16 @@ 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) + 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;
}
@@ -280,6 +284,12 @@ int WSLPeer::get_available_packet_count() const {
return _in_buffer.packets_left();
}
+int WSLPeer::get_current_outbound_buffered_amount() const {
+ ERR_FAIL_COND_V(!_data, 0);
+
+ return wslay_event_get_queued_msg_length(_data->ctx);
+}
+
bool WSLPeer::was_string_packet() const {
return _is_string;
}
@@ -305,8 +315,8 @@ void WSLPeer::close(int p_code, String p_reason) {
_packet_buffer.resize(0);
}
-IP_Address WSLPeer::get_connected_host() const {
- ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), IP_Address());
+IPAddress WSLPeer::get_connected_host() const {
+ ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), IPAddress());
return _data->tcp->get_connected_host();
}
@@ -329,10 +339,6 @@ void WSLPeer::invalidate() {
}
WSLPeer::WSLPeer() {
- _data = nullptr;
- _is_string = 0;
- close_code = -1;
- write_mode = WRITE_MODE_BINARY;
}
WSLPeer::~WSLPeer() {
diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h
index fe4abfb64c..260d4b183d 100644
--- a/modules/websocket/wsl_peer.h
+++ b/modules/websocket/wsl_peer.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -33,10 +33,10 @@
#ifndef JAVASCRIPT_ENABLED
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/io/packet_peer.h"
#include "core/io/stream_peer_tcp.h"
-#include "core/ring_buffer.h"
+#include "core/templates/ring_buffer.h"
#include "packet_buffer.h"
#include "websocket_peer.h"
#include "wslay/wslay.h"
@@ -48,29 +48,17 @@ class WSLPeer : public WebSocketPeer {
public:
struct PeerData {
- bool polling;
- bool destroy;
- bool valid;
- bool is_server;
- bool closing;
- void *obj;
- void *peer;
+ bool polling = false;
+ bool destroy = false;
+ bool valid = false;
+ bool is_server = false;
+ bool closing = false;
+ void *obj = nullptr;
+ void *peer = nullptr;
Ref<StreamPeer> conn;
Ref<StreamPeerTCP> tcp;
- int id;
- wslay_event_context_ptr ctx;
-
- PeerData() {
- polling = false;
- destroy = false;
- valid = false;
- is_server = false;
- id = 1;
- ctx = nullptr;
- obj = nullptr;
- closing = false;
- peer = nullptr;
- }
+ int id = 1;
+ wslay_event_context_ptr ctx = nullptr;
};
static String compute_key_response(String p_key);
@@ -80,17 +68,20 @@ private:
static bool _wsl_poll(struct PeerData *p_data);
static void _wsl_destroy(struct PeerData **p_data);
- struct PeerData *_data;
- uint8_t _is_string;
+ struct PeerData *_data = nullptr;
+ uint8_t _is_string = 0;
// Our packet info is just a boolean (is_string), using uint8_t for it.
PacketBuffer<uint8_t> _in_buffer;
Vector<uint8_t> _packet_buffer;
- WriteMode write_mode;
+ WriteMode write_mode = WRITE_MODE_BINARY;
+
+ int _out_buf_size = 0;
+ int _out_pkt_size = 0;
public:
- int close_code;
+ int close_code = -1;
String close_reason;
void poll(); // Used by client and server.
@@ -98,11 +89,12 @@ public:
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
virtual int get_max_packet_size() const { return _packet_buffer.size(); };
+ virtual int get_current_outbound_buffered_amount() const;
virtual void close_now();
virtual void close(int p_code = 1000, String p_reason = "");
virtual bool is_connected_to_host() const;
- virtual IP_Address get_connected_host() const;
+ virtual IPAddress get_connected_host() const;
virtual uint16_t get_connected_port() const;
virtual WriteMode get_write_mode() const;
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
index da7bfc70c0..514b2d055f 100644
--- a/modules/websocket/wsl_server.cpp
+++ b/modules/websocket/wsl_server.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -31,19 +31,10 @@
#ifndef JAVASCRIPT_ENABLED
#include "wsl_server.h"
+#include "core/config/project_settings.h"
#include "core/os/os.h"
-#include "core/project_settings.h"
-
-WSLServer::PendingPeer::PendingPeer() {
- use_ssl = false;
- time = 0;
- has_request = false;
- response_sent = 0;
- req_pos = 0;
- memset(req_buf, 0, sizeof(req_buf));
-}
-bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols) {
+bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols, String &r_resource_name) {
Vector<String> psa = String((char *)req_buf).split("\r\n");
int len = psa.size();
ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers, got: " + itos(len) + ", expected >= 4.");
@@ -54,6 +45,7 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols) {
// Wrong protocol
ERR_FAIL_COND_V_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", false, "Invalid method or HTTP version.");
+ r_resource_name = req[1];
Map<String, String> headers;
for (int i = 1; i < len; i++) {
Vector<String> header = psa[i].split(":", false, 1);
@@ -104,28 +96,33 @@ bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols) {
return true;
}
-Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols) {
- if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT) {
+Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols, uint64_t p_timeout, String &r_resource_name) {
+ if (OS::get_singleton()->get_ticks_msec() - time > p_timeout) {
+ print_verbose(vformat("WebSocket handshake timed out after %.3f seconds.", p_timeout * 0.001));
return ERR_TIMEOUT;
}
+
if (use_ssl) {
Ref<StreamPeerSSL> ssl = static_cast<Ref<StreamPeerSSL>>(connection);
if (ssl.is_null()) {
- return FAILED;
+ ERR_FAIL_V_MSG(ERR_BUG, "Couldn't get StreamPeerSSL for WebSocket handshake.");
}
ssl->poll();
if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) {
return ERR_BUSY;
} else if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
+ print_verbose(vformat("WebSocket SSL connection error during handshake (StreamPeerSSL status code %d).", ssl->get_status()));
return FAILED;
}
}
+
if (!has_request) {
int read = 0;
while (true) {
- ERR_FAIL_COND_V_MSG(req_pos >= WSL_MAX_HEADER_SIZE, ERR_OUT_OF_MEMORY, "Response headers too big.");
+ ERR_FAIL_COND_V_MSG(req_pos >= WSL_MAX_HEADER_SIZE, ERR_OUT_OF_MEMORY, "WebSocket response headers are too big.");
Error err = connection->get_partial_data(&req_buf[req_pos], 1, read);
if (err != OK) { // Got an error
+ print_verbose(vformat("WebSocket error while getting partial data (StreamPeer error code %d).", err));
return FAILED;
} else if (read != 1) { // Busy, wait next poll
return ERR_BUSY;
@@ -134,7 +131,7 @@ Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols) {
int l = req_pos;
if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
r[l - 3] = '\0';
- if (!_parse_request(p_protocols)) {
+ if (!_parse_request(p_protocols, r_resource_name)) {
return FAILED;
}
String s = "HTTP/1.1 101 Switching Protocols\r\n";
@@ -152,17 +149,21 @@ Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols) {
req_pos += 1;
}
}
+
if (has_request && response_sent < response.size() - 1) {
int sent = 0;
Error err = connection->put_partial_data((const uint8_t *)response.get_data() + response_sent, response.size() - response_sent - 1, sent);
if (err != OK) {
+ print_verbose(vformat("WebSocket error while putting partial data (StreamPeer error code %d).", err));
return err;
}
response_sent += sent;
}
+
if (response_sent < response.size() - 1) {
return ERR_BUSY;
}
+
return OK;
}
@@ -181,23 +182,24 @@ 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 (List<int>::Element *E = remove_ids.front(); E; E = E->next()) {
- _peer_map.erase(E->get());
+ for (int &E : remove_ids) {
+ _peer_map.erase(E);
}
remove_ids.clear();
List<Ref<PendingPeer>> remove_peers;
- for (List<Ref<PendingPeer>>::Element *E = _pending.front(); E; E = E->next()) {
- Ref<PendingPeer> ppeer = E->get();
- Error err = ppeer->do_handshake(_protocols);
+ for (const Ref<PendingPeer> &E : _pending) {
+ String resource_name;
+ Ref<PendingPeer> ppeer = E;
+ Error err = ppeer->do_handshake(_protocols, handshake_timeout, resource_name);
if (err == ERR_BUSY) {
continue;
} else if (err != OK) {
@@ -205,7 +207,7 @@ void WSLServer::poll() {
continue;
}
// Creating new peer
- int32_t id = _gen_unique_id();
+ int32_t id = generate_unique_id();
WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData);
data->obj = this;
@@ -220,10 +222,10 @@ void WSLServer::poll() {
_peer_map[id] = ws_peer;
remove_peers.push_back(ppeer);
- _on_connect(id, ppeer->protocol);
+ _on_connect(id, ppeer->protocol, resource_name);
}
- for (List<Ref<PendingPeer>>::Element *E = remove_peers.front(); E; E = E->next()) {
- _pending.erase(E->get());
+ for (const Ref<PendingPeer> &E : remove_peers) {
+ _pending.erase(E);
}
remove_peers.clear();
@@ -263,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();
@@ -281,8 +283,8 @@ Ref<WebSocketPeer> WSLServer::get_peer(int p_id) const {
return _peer_map[p_id];
}
-IP_Address WSLServer::get_peer_address(int p_peer_id) const {
- ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address());
+IPAddress WSLServer::get_peer_address(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), IPAddress());
return _peer_map[p_peer_id]->get_connected_host();
}
@@ -310,11 +312,7 @@ Error WSLServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer
}
WSLServer::WSLServer() {
- _in_buf_size = DEF_BUF_SHIFT;
- _in_pkt_size = DEF_PKT_SHIFT;
- _out_buf_size = DEF_BUF_SHIFT;
- _out_pkt_size = DEF_PKT_SHIFT;
- _server.instance();
+ _server.instantiate();
}
WSLServer::~WSLServer() {
diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h
index f86de02797..508b5a12a1 100644
--- a/modules/websocket/wsl_server.h
+++ b/modules/websocket/wsl_server.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -40,42 +40,38 @@
#include "core/io/stream_peer_tcp.h"
#include "core/io/tcp_server.h"
-#define WSL_SERVER_TIMEOUT 1000
-
class WSLServer : public WebSocketServer {
GDCIIMPL(WSLServer, WebSocketServer);
private:
- class PendingPeer : public Reference {
+ class PendingPeer : public RefCounted {
private:
- bool _parse_request(const Vector<String> p_protocols);
+ bool _parse_request(const Vector<String> p_protocols, String &r_resource_name);
public:
Ref<StreamPeerTCP> tcp;
Ref<StreamPeer> connection;
- bool use_ssl;
+ bool use_ssl = false;
- int time;
- uint8_t req_buf[WSL_MAX_HEADER_SIZE];
- int req_pos;
+ uint64_t time = 0;
+ uint8_t req_buf[WSL_MAX_HEADER_SIZE] = {};
+ int req_pos = 0;
String key;
String protocol;
- bool has_request;
+ bool has_request = false;
CharString response;
- int response_sent;
-
- PendingPeer();
+ int response_sent = 0;
- Error do_handshake(const Vector<String> p_protocols);
+ Error do_handshake(const Vector<String> p_protocols, uint64_t p_timeout, String &r_resource_name);
};
- int _in_buf_size;
- int _in_pkt_size;
- int _out_buf_size;
- int _out_pkt_size;
+ int _in_buf_size = DEF_BUF_SHIFT;
+ int _in_pkt_size = DEF_PKT_SHIFT;
+ int _out_buf_size = DEF_BUF_SHIFT;
+ int _out_pkt_size = DEF_PKT_SHIFT;
List<Ref<PendingPeer>> _pending;
- Ref<TCP_Server> _server;
+ Ref<TCPServer> _server;
Vector<String> _protocols;
public:
@@ -86,7 +82,7 @@ public:
int get_max_packet_size() const;
bool has_peer(int p_id) const;
Ref<WebSocketPeer> get_peer(int p_id) const;
- IP_Address get_peer_address(int p_peer_id) const;
+ IPAddress get_peer_address(int p_peer_id) const;
int get_peer_port(int p_peer_id) const;
void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "");
virtual void poll();
diff --git a/modules/webxr/SCsub b/modules/webxr/SCsub
new file mode 100644
index 0000000000..0a96af0811
--- /dev/null
+++ b/modules/webxr/SCsub
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+if env["platform"] == "javascript":
+ env.AddJSLibraries(["native/library_godot_webxr.js"])
+ env.AddJSExterns(["native/webxr.externs.js"])
+
+env_webxr = env_modules.Clone()
+env_webxr.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/webxr/config.py b/modules/webxr/config.py
new file mode 100644
index 0000000000..f676ef3483
--- /dev/null
+++ b/modules/webxr/config.py
@@ -0,0 +1,14 @@
+def can_build(env, platform):
+ return not env["disable_3d"]
+
+
+def configure(env):
+ pass
+
+
+def get_doc_classes():
+ return ["WebXRInterface"]
+
+
+def get_doc_path():
+ return "doc_classes"
diff --git a/modules/webxr/doc_classes/WebXRInterface.xml b/modules/webxr/doc_classes/WebXRInterface.xml
new file mode 100644
index 0000000000..6e224a8242
--- /dev/null
+++ b/modules/webxr/doc_classes/WebXRInterface.xml
@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="WebXRInterface" inherits="XRInterface" version="4.0">
+ <brief_description>
+ AR/VR interface using WebXR.
+ </brief_description>
+ <description>
+ WebXR is an open standard that allows creating VR and AR applications that run in the web browser.
+ As such, this interface is only available when running in an HTML5 export.
+ WebXR supports a wide range of devices, from the very capable (like Valve Index, HTC Vive, Oculus Rift and Quest) down to the much less capable (like Google Cardboard, Oculus Go, GearVR, or plain smartphones).
+ Since WebXR is based on Javascript, it makes extensive use of callbacks, which means that [WebXRInterface] is forced to use signals, where other AR/VR interfaces would instead use functions that return a result immediately. This makes [WebXRInterface] quite a bit more complicated to initialize than other AR/VR interfaces.
+ Here's the minimum code required to start an immersive VR session:
+ [codeblock]
+ extends Node3D
+
+ var webxr_interface
+ var vr_supported = false
+
+ func _ready():
+ # We assume this node has a button as a child.
+ # This button is for the user to consent to entering immersive VR mode.
+ $Button.connect("pressed", self, "_on_Button_pressed")
+
+ webxr_interface = XRServer.find_interface("WebXR")
+ if webxr_interface:
+ # WebXR uses a lot of asynchronous callbacks, so we connect to various
+ # signals in order to receive them.
+ webxr_interface.connect("session_supported", self, "_webxr_session_supported")
+ webxr_interface.connect("session_started", self, "_webxr_session_started")
+ webxr_interface.connect("session_ended", self, "_webxr_session_ended")
+ webxr_interface.connect("session_failed", self, "_webxr_session_failed")
+
+ # This returns immediately - our _webxr_session_supported() method
+ # (which we connected to the "session_supported" signal above) will
+ # be called sometime later to let us know if it's supported or not.
+ webxr_interface.is_session_supported("immersive-vr")
+
+ func _webxr_session_supported(session_mode, supported):
+ if session_mode == 'immersive-vr':
+ vr_supported = supported
+
+ func _on_Button_pressed():
+ if not vr_supported:
+ OS.alert("Your browser doesn't support VR")
+ return
+
+ # We want an immersive VR session, as opposed to AR ('immersive-ar') or a
+ # simple 3DoF viewer ('viewer').
+ webxr_interface.session_mode = 'immersive-vr'
+ # 'bounded-floor' is room scale, 'local-floor' is a standing or sitting
+ # experience (it puts you 1.6m above the ground if you have 3DoF headset),
+ # whereas as 'local' puts you down at the XROrigin.
+ # This list means it'll first try to request 'bounded-floor', then
+ # fallback on 'local-floor' and ultimately 'local', if nothing else is
+ # supported.
+ webxr_interface.requested_reference_space_types = 'bounded-floor, local-floor, local'
+ # In order to use 'local-floor' or 'bounded-floor' we must also
+ # mark the features as required or optional.
+ webxr_interface.required_features = 'local-floor'
+ webxr_interface.optional_features = 'bounded-floor'
+
+ # This will return false if we're unable to even request the session,
+ # however, it can still fail asynchronously later in the process, so we
+ # only know if it's really succeeded or failed when our
+ # _webxr_session_started() or _webxr_session_failed() methods are called.
+ if not webxr_interface.initialize():
+ OS.alert("Failed to initialize")
+ return
+
+ func _webxr_session_started():
+ $Button.visible = false
+ # This tells Godot to start rendering to the headset.
+ get_viewport().xr = true
+ # This will be the reference space type you ultimately got, out of the
+ # types that you requested above. This is useful if you want the game to
+ # work a little differently in 'bounded-floor' versus 'local-floor'.
+ print ("Reference space type: " + webxr_interface.reference_space_type)
+
+ func _webxr_session_ended():
+ $Button.visible = true
+ # If the user exits immersive mode, then we tell Godot to render to the web
+ # page again.
+ get_viewport().xr = false
+
+ func _webxr_session_failed(message):
+ OS.alert("Failed to initialize: " + message)
+ [/codeblock]
+ 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.
+ 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>
+ <link title="How to make a VR game for WebXR with Godot">https://www.snopekgames.com/blog/2020/how-make-vr-game-webxr-godot</link>
+ </tutorials>
+ <methods>
+ <method name="get_controller" qualifiers="const">
+ <return type="XRPositionalTracker" />
+ <argument index="0" name="controller_id" type="int" />
+ <description>
+ Gets an [XRPositionalTracker] for the given [code]controller_id[/code].
+ In the context of WebXR, a "controller" can be an advanced VR controller like the Oculus Touch or Index controllers, or even a tap on the screen, a spoken voice command or a button press on the device itself. When a non-traditional controller is used, interpret the position and orientation of the [XRPositionalTracker] as a ray pointing at the object the user wishes to interact with.
+ Use this method to get information about the controller that triggered one of these signals:
+ - [signal selectstart]
+ - [signal select]
+ - [signal selectend]
+ - [signal squeezestart]
+ - [signal squeeze]
+ - [signal squeezestart]
+ </description>
+ </method>
+ <method name="is_session_supported">
+ <return type="void" />
+ <argument index="0" name="session_mode" type="String" />
+ <description>
+ Checks if the given [code]session_mode[/code] is supported by the user's browser.
+ Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRSessionMode]WebXR's XRSessionMode[/url], including: [code]"immersive-vr"[/code], [code]"immersive-ar"[/code], and [code]"inline"[/code].
+ This method returns nothing, instead it emits the [signal session_supported] signal with the result.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="bounds_geometry" type="PackedVector3Array" setter="" getter="get_bounds_geometry">
+ The vertices of a polygon which defines the boundaries of the user's play area.
+ This will only be available if [member reference_space_type] is [code]"bounded-floor"[/code] and only on certain browsers and devices that support it.
+ The [signal reference_space_reset] signal may indicate when this changes.
+ </member>
+ <member name="optional_features" type="String" setter="set_optional_features" getter="get_optional_features">
+ A comma-seperated list of optional features used by [method XRInterface.initialize] when setting up the WebXR session.
+ If a user's browser or device doesn't support one of the given features, initialization will continue, but you won't be able to use the requested feature.
+ This doesn't have any effect on the interface when already initialized.
+ Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType]WebXR's XRReferenceSpaceType[/url]. If you want to use a particular reference space type, it must be listed in either [member required_features] or [member optional_features].
+ </member>
+ <member name="reference_space_type" type="String" setter="" getter="get_reference_space_type">
+ The reference space type (from the list of requested types set in the [member requested_reference_space_types] property), that was ultimately used by [method XRInterface.initialize] when setting up the WebXR session.
+ Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType]WebXR's XRReferenceSpaceType[/url]. If you want to use a particular reference space type, it must be listed in either [member required_features] or [member optional_features].
+ </member>
+ <member name="requested_reference_space_types" type="String" setter="set_requested_reference_space_types" getter="get_requested_reference_space_types">
+ A comma-seperated list of reference space types used by [method XRInterface.initialize] when setting up the WebXR session.
+ The reference space types are requested in order, and the first on supported by the users device or browser will be used. The [member reference_space_type] property contains the reference space type that was ultimately used.
+ This doesn't have any effect on the interface when already initialized.
+ Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType]WebXR's XRReferenceSpaceType[/url]. If you want to use a particular reference space type, it must be listed in either [member required_features] or [member optional_features].
+ </member>
+ <member name="required_features" type="String" setter="set_required_features" getter="get_required_features">
+ A comma-seperated list of required features used by [method XRInterface.initialize] when setting up the WebXR session.
+ If a user's browser or device doesn't support one of the given features, initialization will fail and [signal session_failed] will be emitted.
+ This doesn't have any effect on the interface when already initialized.
+ Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpaceType]WebXR's XRReferenceSpaceType[/url]. If you want to use a particular reference space type, it must be listed in either [member required_features] or [member optional_features].
+ </member>
+ <member name="session_mode" type="String" setter="set_session_mode" getter="get_session_mode">
+ The session mode used by [method XRInterface.initialize] when setting up the WebXR session.
+ This doesn't have any effect on the interface when already initialized.
+ Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRSessionMode]WebXR's XRSessionMode[/url], including: [code]"immersive-vr"[/code], [code]"immersive-ar"[/code], and [code]"inline"[/code].
+ </member>
+ <member name="visibility_state" type="String" setter="" getter="get_visibility_state">
+ Indicates if the WebXR session's imagery is visible to the user.
+ Possible values come from [url=https://developer.mozilla.org/en-US/docs/Web/API/XRVisibilityState]WebXR's XRVisibilityState[/url], including [code]"hidden"[/code], [code]"visible"[/code], and [code]"visible-blurred"[/code].
+ </member>
+ </members>
+ <signals>
+ <signal name="reference_space_reset">
+ <description>
+ Emitted to indicate that the reference space has been reset or reconfigured.
+ When (or whether) this is emitted depends on the user's browser or device, but may include when the user has changed the dimensions of their play space (which you may be able to access via [member bounds_geometry]) or pressed/held a button to recenter their position.
+ See [url=https://developer.mozilla.org/en-US/docs/Web/API/XRReferenceSpace/reset_event]WebXR's XRReferenceSpace reset event[/url] for more information.
+ </description>
+ </signal>
+ <signal name="select">
+ <argument index="0" name="controller_id" type="int" />
+ <description>
+ Emitted after one of the "controllers" has finished its "primary action".
+ Use [method get_controller] to get more information about the controller.
+ </description>
+ </signal>
+ <signal name="selectend">
+ <argument index="0" name="controller_id" type="int" />
+ <description>
+ Emitted when one of the "controllers" has finished its "primary action".
+ Use [method get_controller] to get more information about the controller.
+ </description>
+ </signal>
+ <signal name="selectstart">
+ <argument index="0" name="controller_id" type="int" />
+ <description>
+ Emitted when one of the "controllers" has started its "primary action".
+ Use [method get_controller] to get more information about the controller.
+ </description>
+ </signal>
+ <signal name="session_ended">
+ <description>
+ Emitted when the user ends the WebXR session (which can be done using UI from the browser or device).
+ At this point, you should do [code]get_viewport().xr = false[/code] to instruct Godot to resume rendering to the screen.
+ </description>
+ </signal>
+ <signal name="session_failed">
+ <argument index="0" name="message" type="String" />
+ <description>
+ Emitted by [method XRInterface.initialize] if the session fails to start.
+ [code]message[/code] may optionally contain an error message from WebXR, or an empty string if no message is available.
+ </description>
+ </signal>
+ <signal name="session_started">
+ <description>
+ Emitted by [method XRInterface.initialize] if the session is successfully started.
+ At this point, it's safe to do [code]get_viewport().xr = true[/code] to instruct Godot to start rendering to the AR/VR device.
+ </description>
+ </signal>
+ <signal name="session_supported">
+ <argument index="0" name="session_mode" type="String" />
+ <argument index="1" name="supported" type="bool" />
+ <description>
+ Emitted by [method is_session_supported] to indicate if the given [code]session_mode[/code] is supported or not.
+ </description>
+ </signal>
+ <signal name="squeeze">
+ <argument index="0" name="controller_id" type="int" />
+ <description>
+ Emitted after one of the "controllers" has finished its "primary squeeze action".
+ Use [method get_controller] to get more information about the controller.
+ </description>
+ </signal>
+ <signal name="squeezeend">
+ <argument index="0" name="controller_id" type="int" />
+ <description>
+ Emitted when one of the "controllers" has finished its "primary squeeze action".
+ Use [method get_controller] to get more information about the controller.
+ </description>
+ </signal>
+ <signal name="squeezestart">
+ <argument index="0" name="controller_id" type="int" />
+ <description>
+ Emitted when one of the "controllers" has started its "primary squeeze action".
+ Use [method get_controller] to get more information about the controller.
+ </description>
+ </signal>
+ <signal name="visibility_state_changed">
+ <description>
+ Emitted when [member visibility_state] has changed.
+ </description>
+ </signal>
+ </signals>
+</class>
diff --git a/modules/webxr/godot_webxr.h b/modules/webxr/godot_webxr.h
new file mode 100644
index 0000000000..7aac0a6508
--- /dev/null
+++ b/modules/webxr/godot_webxr.h
@@ -0,0 +1,85 @@
+/*************************************************************************/
+/* godot_webxr.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_WEBXR_H
+#define GODOT_WEBXR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "stddef.h"
+
+typedef void (*GodotWebXRSupportedCallback)(char *p_session_mode, int p_supported);
+typedef void (*GodotWebXRStartedCallback)(char *p_reference_space_type);
+typedef void (*GodotWebXREndedCallback)();
+typedef void (*GodotWebXRFailedCallback)(char *p_message);
+typedef void (*GodotWebXRControllerCallback)();
+typedef void (*GodotWebXRInputEventCallback)(char *p_signal_name, int p_controller_id);
+typedef void (*GodotWebXRSimpleEventCallback)(char *p_signal_name);
+
+extern int godot_webxr_is_supported();
+extern void godot_webxr_is_session_supported(const char *p_session_mode, GodotWebXRSupportedCallback p_callback);
+
+extern void godot_webxr_initialize(
+ const char *p_session_mode,
+ const char *p_required_features,
+ const char *p_optional_features,
+ const char *p_requested_reference_space_types,
+ GodotWebXRStartedCallback p_on_session_started,
+ GodotWebXREndedCallback p_on_session_ended,
+ GodotWebXRFailedCallback p_on_session_failed,
+ GodotWebXRControllerCallback p_on_controller_changed,
+ GodotWebXRInputEventCallback p_on_input_event,
+ GodotWebXRSimpleEventCallback p_on_simple_event);
+extern void godot_webxr_uninitialize();
+
+extern int godot_webxr_get_view_count();
+extern int *godot_webxr_get_render_target_size();
+extern float *godot_webxr_get_transform_for_eye(int p_eye);
+extern float *godot_webxr_get_projection_for_eye(int p_eye);
+extern int godot_webxr_get_external_texture_for_eye(int p_eye);
+extern void godot_webxr_commit_for_eye(int p_eye);
+
+extern void godot_webxr_sample_controller_data();
+extern int godot_webxr_get_controller_count();
+extern int godot_webxr_is_controller_connected(int p_controller);
+extern float *godot_webxr_get_controller_transform(int p_controller);
+extern int *godot_webxr_get_controller_buttons(int p_controller);
+extern int *godot_webxr_get_controller_axes(int p_controller);
+
+extern char *godot_webxr_get_visibility_state();
+extern int *godot_webxr_get_bounds_geometry();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GODOT_WEBXR_H */
diff --git a/modules/webxr/native/library_godot_webxr.js b/modules/webxr/native/library_godot_webxr.js
new file mode 100644
index 0000000000..c4b21defce
--- /dev/null
+++ b/modules/webxr/native/library_godot_webxr.js
@@ -0,0 +1,670 @@
+/*************************************************************************/
+/* library_godot_webxr.js */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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. */
+/*************************************************************************/
+const GodotWebXR = {
+ $GodotWebXR__deps: ['$Browser', '$GL', '$GodotRuntime'],
+ $GodotWebXR: {
+ gl: null,
+
+ texture_ids: [null, null],
+ textures: [null, null],
+
+ session: null,
+ space: null,
+ frame: null,
+ pose: null,
+
+ // Monkey-patch the requestAnimationFrame() used by Emscripten for the main
+ // loop, so that we can swap it out for XRSession.requestAnimationFrame()
+ // when an XR session is started.
+ orig_requestAnimationFrame: null,
+ requestAnimationFrame: (callback) => {
+ if (GodotWebXR.session && GodotWebXR.space) {
+ const onFrame = function (time, frame) {
+ GodotWebXR.frame = frame;
+ GodotWebXR.pose = frame.getViewerPose(GodotWebXR.space);
+ callback(time);
+ GodotWebXR.frame = null;
+ GodotWebXR.pose = null;
+ };
+ GodotWebXR.session.requestAnimationFrame(onFrame);
+ } else {
+ GodotWebXR.orig_requestAnimationFrame(callback);
+ }
+ },
+ monkeyPatchRequestAnimationFrame: (enable) => {
+ if (GodotWebXR.orig_requestAnimationFrame === null) {
+ GodotWebXR.orig_requestAnimationFrame = Browser.requestAnimationFrame;
+ }
+ Browser.requestAnimationFrame = enable
+ ? GodotWebXR.requestAnimationFrame : GodotWebXR.orig_requestAnimationFrame;
+ },
+ pauseResumeMainLoop: () => {
+ // Once both GodotWebXR.session and GodotWebXR.space are set or
+ // unset, our monkey-patched requestAnimationFrame() should be
+ // enabled or disabled. When using the WebXR API Emulator, this
+ // gets picked up automatically, however, in the Oculus Browser
+ // on the Quest, we need to pause and resume the main loop.
+ Browser.mainLoop.pause();
+ window.setTimeout(function () {
+ Browser.mainLoop.resume();
+ }, 0);
+ },
+
+ // Some custom WebGL code for blitting our eye textures to the
+ // framebuffer we get from WebXR.
+ shaderProgram: null,
+ programInfo: null,
+ buffer: null,
+ // Vertex shader source.
+ vsSource: `
+ const vec2 scale = vec2(0.5, 0.5);
+ attribute vec4 aVertexPosition;
+
+ varying highp vec2 vTextureCoord;
+
+ void main () {
+ gl_Position = aVertexPosition;
+ vTextureCoord = aVertexPosition.xy * scale + scale;
+ }
+ `,
+ // Fragment shader source.
+ fsSource: `
+ varying highp vec2 vTextureCoord;
+
+ uniform sampler2D uSampler;
+
+ void main() {
+ gl_FragColor = texture2D(uSampler, vTextureCoord);
+ }
+ `,
+
+ initShaderProgram: (gl, vsSource, fsSource) => {
+ const vertexShader = GodotWebXR.loadShader(gl, gl.VERTEX_SHADER, vsSource);
+ const fragmentShader = GodotWebXR.loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
+
+ const shaderProgram = gl.createProgram();
+ gl.attachShader(shaderProgram, vertexShader);
+ gl.attachShader(shaderProgram, fragmentShader);
+ gl.linkProgram(shaderProgram);
+
+ if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
+ GodotRuntime.error(`Unable to initialize the shader program: ${gl.getProgramInfoLog(shaderProgram)}`);
+ return null;
+ }
+
+ return shaderProgram;
+ },
+ loadShader: (gl, type, source) => {
+ const shader = gl.createShader(type);
+ gl.shaderSource(shader, source);
+ gl.compileShader(shader);
+
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
+ GodotRuntime.error(`An error occurred compiling the shader: ${gl.getShaderInfoLog(shader)}`);
+ gl.deleteShader(shader);
+ return null;
+ }
+
+ return shader;
+ },
+ initBuffer: (gl) => {
+ const positionBuffer = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
+ const positions = [
+ -1.0, -1.0,
+ 1.0, -1.0,
+ -1.0, 1.0,
+ 1.0, 1.0,
+ ];
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
+ return positionBuffer;
+ },
+ blitTexture: (gl, texture) => {
+ if (GodotWebXR.shaderProgram === null) {
+ GodotWebXR.shaderProgram = GodotWebXR.initShaderProgram(gl, GodotWebXR.vsSource, GodotWebXR.fsSource);
+ GodotWebXR.programInfo = {
+ program: GodotWebXR.shaderProgram,
+ attribLocations: {
+ vertexPosition: gl.getAttribLocation(GodotWebXR.shaderProgram, 'aVertexPosition'),
+ },
+ uniformLocations: {
+ uSampler: gl.getUniformLocation(GodotWebXR.shaderProgram, 'uSampler'),
+ },
+ };
+ GodotWebXR.buffer = GodotWebXR.initBuffer(gl);
+ }
+
+ const orig_program = gl.getParameter(gl.CURRENT_PROGRAM);
+ gl.useProgram(GodotWebXR.shaderProgram);
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, GodotWebXR.buffer);
+ gl.vertexAttribPointer(GodotWebXR.programInfo.attribLocations.vertexPosition, 2, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(GodotWebXR.programInfo.attribLocations.vertexPosition);
+
+ gl.activeTexture(gl.TEXTURE0);
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.uniform1i(GodotWebXR.programInfo.uniformLocations.uSampler, 0);
+
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+ // Restore state.
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ gl.disableVertexAttribArray(GodotWebXR.programInfo.attribLocations.vertexPosition);
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
+ gl.useProgram(orig_program);
+ },
+
+ // Holds the controllers list between function calls.
+ controllers: [],
+
+ // Updates controllers array, where the left hand (or sole tracker) is
+ // the first element, and the right hand is the second element, and any
+ // others placed at the 3rd position and up.
+ sampleControllers: () => {
+ if (!GodotWebXR.session || !GodotWebXR.frame) {
+ return;
+ }
+
+ let other_index = 2;
+ const controllers = [];
+ GodotWebXR.session.inputSources.forEach((input_source) => {
+ if (input_source.targetRayMode === 'tracked-pointer') {
+ if (input_source.handedness === 'right') {
+ controllers[1] = input_source;
+ } else if (input_source.handedness === 'left' || !controllers[0]) {
+ controllers[0] = input_source;
+ }
+ } else {
+ controllers[other_index++] = input_source;
+ }
+ });
+ GodotWebXR.controllers = controllers;
+ },
+
+ getControllerId: (input_source) => GodotWebXR.controllers.indexOf(input_source),
+ },
+
+ godot_webxr_is_supported__proxy: 'sync',
+ godot_webxr_is_supported__sig: 'i',
+ godot_webxr_is_supported: function () {
+ return !!navigator.xr;
+ },
+
+ godot_webxr_is_session_supported__proxy: 'sync',
+ godot_webxr_is_session_supported__sig: 'vii',
+ godot_webxr_is_session_supported: function (p_session_mode, p_callback) {
+ const session_mode = GodotRuntime.parseString(p_session_mode);
+ const cb = GodotRuntime.get_func(p_callback);
+ if (navigator.xr) {
+ navigator.xr.isSessionSupported(session_mode).then(function (supported) {
+ const c_str = GodotRuntime.allocString(session_mode);
+ cb(c_str, supported ? 1 : 0);
+ GodotRuntime.free(c_str);
+ });
+ } else {
+ const c_str = GodotRuntime.allocString(session_mode);
+ cb(c_str, 0);
+ GodotRuntime.free(c_str);
+ }
+ },
+
+ godot_webxr_initialize__deps: ['emscripten_webgl_get_current_context'],
+ godot_webxr_initialize__proxy: 'sync',
+ godot_webxr_initialize__sig: 'viiiiiiiiii',
+ godot_webxr_initialize: function (p_session_mode, p_required_features, p_optional_features, p_requested_reference_spaces, p_on_session_started, p_on_session_ended, p_on_session_failed, p_on_controller_changed, p_on_input_event, p_on_simple_event) {
+ GodotWebXR.monkeyPatchRequestAnimationFrame(true);
+
+ const session_mode = GodotRuntime.parseString(p_session_mode);
+ const required_features = GodotRuntime.parseString(p_required_features).split(',').map((s) => s.trim()).filter((s) => s !== '');
+ const optional_features = GodotRuntime.parseString(p_optional_features).split(',').map((s) => s.trim()).filter((s) => s !== '');
+ const requested_reference_space_types = GodotRuntime.parseString(p_requested_reference_spaces).split(',').map((s) => s.trim());
+ const onstarted = GodotRuntime.get_func(p_on_session_started);
+ const onended = GodotRuntime.get_func(p_on_session_ended);
+ const onfailed = GodotRuntime.get_func(p_on_session_failed);
+ const oncontroller = GodotRuntime.get_func(p_on_controller_changed);
+ const oninputevent = GodotRuntime.get_func(p_on_input_event);
+ const onsimpleevent = GodotRuntime.get_func(p_on_simple_event);
+
+ const session_init = {};
+ if (required_features.length > 0) {
+ session_init['requiredFeatures'] = required_features;
+ }
+ if (optional_features.length > 0) {
+ session_init['optionalFeatures'] = optional_features;
+ }
+
+ navigator.xr.requestSession(session_mode, session_init).then(function (session) {
+ GodotWebXR.session = session;
+
+ session.addEventListener('end', function (evt) {
+ onended();
+ });
+
+ session.addEventListener('inputsourceschange', function (evt) {
+ let controller_changed = false;
+ [evt.added, evt.removed].forEach((lst) => {
+ lst.forEach((input_source) => {
+ if (input_source.targetRayMode === 'tracked-pointer') {
+ controller_changed = true;
+ }
+ });
+ });
+ if (controller_changed) {
+ oncontroller();
+ }
+ });
+
+ ['selectstart', 'select', 'selectend', 'squeezestart', 'squeeze', 'squeezeend'].forEach((input_event) => {
+ session.addEventListener(input_event, function (evt) {
+ const c_str = GodotRuntime.allocString(input_event);
+ oninputevent(c_str, GodotWebXR.getControllerId(evt.inputSource));
+ GodotRuntime.free(c_str);
+ });
+ });
+
+ session.addEventListener('visibilitychange', function (evt) {
+ const c_str = GodotRuntime.allocString('visibility_state_changed');
+ onsimpleevent(c_str);
+ GodotRuntime.free(c_str);
+ });
+
+ const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef
+ const gl = GL.getContext(gl_context_handle).GLctx;
+ GodotWebXR.gl = gl;
+
+ gl.makeXRCompatible().then(function () {
+ session.updateRenderState({
+ baseLayer: new XRWebGLLayer(session, gl),
+ });
+
+ function onReferenceSpaceSuccess(reference_space, reference_space_type) {
+ GodotWebXR.space = reference_space;
+
+ // Using reference_space.addEventListener() crashes when
+ // using the polyfill with the WebXR Emulator extension,
+ // so we set the event property instead.
+ reference_space.onreset = function (evt) {
+ const c_str = GodotRuntime.allocString('reference_space_reset');
+ onsimpleevent(c_str);
+ GodotRuntime.free(c_str);
+ };
+
+ // Now that both GodotWebXR.session and GodotWebXR.space are
+ // set, we need to pause and resume the main loop for the XR
+ // main loop to kick in.
+ GodotWebXR.pauseResumeMainLoop();
+
+ // Call in setTimeout() so that errors in the onstarted()
+ // callback don't bubble up here and cause Godot to try the
+ // next reference space.
+ window.setTimeout(function () {
+ const c_str = GodotRuntime.allocString(reference_space_type);
+ onstarted(c_str);
+ GodotRuntime.free(c_str);
+ }, 0);
+ }
+
+ function requestReferenceSpace() {
+ const reference_space_type = requested_reference_space_types.shift();
+ session.requestReferenceSpace(reference_space_type)
+ .then((refSpace) => {
+ onReferenceSpaceSuccess(refSpace, reference_space_type);
+ })
+ .catch(() => {
+ if (requested_reference_space_types.length === 0) {
+ const c_str = GodotRuntime.allocString('Unable to get any of the requested reference space types');
+ onfailed(c_str);
+ GodotRuntime.free(c_str);
+ } else {
+ requestReferenceSpace();
+ }
+ });
+ }
+
+ requestReferenceSpace();
+ }).catch(function (error) {
+ const c_str = GodotRuntime.allocString(`Unable to make WebGL context compatible with WebXR: ${error}`);
+ onfailed(c_str);
+ GodotRuntime.free(c_str);
+ });
+ }).catch(function (error) {
+ const c_str = GodotRuntime.allocString(`Unable to start session: ${error}`);
+ onfailed(c_str);
+ GodotRuntime.free(c_str);
+ });
+ },
+
+ godot_webxr_uninitialize__proxy: 'sync',
+ godot_webxr_uninitialize__sig: 'v',
+ godot_webxr_uninitialize: function () {
+ if (GodotWebXR.session) {
+ GodotWebXR.session.end()
+ // Prevent exception when session has already ended.
+ .catch((e) => { });
+ }
+
+ // Clean-up the textures we allocated for each view.
+ const gl = GodotWebXR.gl;
+ for (let i = 0; i < GodotWebXR.textures.length; i++) {
+ const texture = GodotWebXR.textures[i];
+ if (texture !== null) {
+ gl.deleteTexture(texture);
+ }
+ GodotWebXR.textures[i] = null;
+
+ const texture_id = GodotWebXR.texture_ids[i];
+ if (texture_id !== null) {
+ GL.textures[texture_id] = null;
+ }
+ GodotWebXR.texture_ids[i] = null;
+ }
+
+ GodotWebXR.session = null;
+ GodotWebXR.space = null;
+ GodotWebXR.frame = null;
+ GodotWebXR.pose = null;
+
+ // Disable the monkey-patched window.requestAnimationFrame() and
+ // pause/restart the main loop to activate it on all platforms.
+ GodotWebXR.monkeyPatchRequestAnimationFrame(false);
+ GodotWebXR.pauseResumeMainLoop();
+ },
+
+ godot_webxr_get_view_count__proxy: 'sync',
+ godot_webxr_get_view_count__sig: 'i',
+ godot_webxr_get_view_count: function () {
+ if (!GodotWebXR.session || !GodotWebXR.pose) {
+ return 0;
+ }
+ return GodotWebXR.pose.views.length;
+ },
+
+ godot_webxr_get_render_target_size__proxy: 'sync',
+ godot_webxr_get_render_target_size__sig: 'i',
+ godot_webxr_get_render_target_size: function () {
+ if (!GodotWebXR.session || !GodotWebXR.pose) {
+ return 0;
+ }
+
+ const glLayer = GodotWebXR.session.renderState.baseLayer;
+ const view = GodotWebXR.pose.views[0];
+ const viewport = glLayer.getViewport(view);
+
+ const buf = GodotRuntime.malloc(2 * 4);
+ GodotRuntime.setHeapValue(buf + 0, viewport.width, 'i32');
+ GodotRuntime.setHeapValue(buf + 4, viewport.height, 'i32');
+ return buf;
+ },
+
+ godot_webxr_get_transform_for_eye__proxy: 'sync',
+ godot_webxr_get_transform_for_eye__sig: 'ii',
+ godot_webxr_get_transform_for_eye: function (p_eye) {
+ if (!GodotWebXR.session || !GodotWebXR.pose) {
+ return 0;
+ }
+
+ const views = GodotWebXR.pose.views;
+ let matrix;
+ if (p_eye === 0) {
+ matrix = GodotWebXR.pose.transform.matrix;
+ } else {
+ matrix = views[p_eye - 1].transform.matrix;
+ }
+ const buf = GodotRuntime.malloc(16 * 4);
+ for (let i = 0; i < 16; i++) {
+ GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float');
+ }
+ return buf;
+ },
+
+ godot_webxr_get_projection_for_eye__proxy: 'sync',
+ godot_webxr_get_projection_for_eye__sig: 'ii',
+ godot_webxr_get_projection_for_eye: function (p_eye) {
+ if (!GodotWebXR.session || !GodotWebXR.pose) {
+ return 0;
+ }
+
+ const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0;
+ const matrix = GodotWebXR.pose.views[view_index].projectionMatrix;
+ const buf = GodotRuntime.malloc(16 * 4);
+ for (let i = 0; i < 16; i++) {
+ GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float');
+ }
+ return buf;
+ },
+
+ godot_webxr_get_external_texture_for_eye__proxy: 'sync',
+ godot_webxr_get_external_texture_for_eye__sig: 'ii',
+ godot_webxr_get_external_texture_for_eye: function (p_eye) {
+ if (!GodotWebXR.session) {
+ return 0;
+ }
+
+ const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0;
+ if (GodotWebXR.texture_ids[view_index]) {
+ return GodotWebXR.texture_ids[view_index];
+ }
+
+ // Check pose separately and after returning the cached texture id,
+ // because we won't get a pose in some cases if we lose tracking, and
+ // we don't want to return 0 just because tracking was lost.
+ if (!GodotWebXR.pose) {
+ return 0;
+ }
+
+ const glLayer = GodotWebXR.session.renderState.baseLayer;
+ const view = GodotWebXR.pose.views[view_index];
+ const viewport = glLayer.getViewport(view);
+ const gl = GodotWebXR.gl;
+
+ const texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, viewport.width, viewport.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+
+ const texture_id = GL.getNewId(GL.textures);
+ GL.textures[texture_id] = texture;
+ GodotWebXR.textures[view_index] = texture;
+ GodotWebXR.texture_ids[view_index] = texture_id;
+ return texture_id;
+ },
+
+ godot_webxr_commit_for_eye__proxy: 'sync',
+ godot_webxr_commit_for_eye__sig: 'vi',
+ godot_webxr_commit_for_eye: function (p_eye) {
+ if (!GodotWebXR.session || !GodotWebXR.pose) {
+ return;
+ }
+
+ const view_index = (p_eye === 2 /* ARVRInterface::EYE_RIGHT */) ? 1 : 0;
+ const glLayer = GodotWebXR.session.renderState.baseLayer;
+ const view = GodotWebXR.pose.views[view_index];
+ const viewport = glLayer.getViewport(view);
+ const gl = GodotWebXR.gl;
+
+ const orig_framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
+ const orig_viewport = gl.getParameter(gl.VIEWPORT);
+
+ // Bind to WebXR's framebuffer.
+ gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
+ gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
+
+ GodotWebXR.blitTexture(gl, GodotWebXR.textures[view_index]);
+
+ // Restore state.
+ gl.bindFramebuffer(gl.FRAMEBUFFER, orig_framebuffer);
+ gl.viewport(orig_viewport[0], orig_viewport[1], orig_viewport[2], orig_viewport[3]);
+ },
+
+ godot_webxr_sample_controller_data__proxy: 'sync',
+ godot_webxr_sample_controller_data__sig: 'v',
+ godot_webxr_sample_controller_data: function () {
+ GodotWebXR.sampleControllers();
+ },
+
+ godot_webxr_get_controller_count__proxy: 'sync',
+ godot_webxr_get_controller_count__sig: 'i',
+ godot_webxr_get_controller_count: function () {
+ if (!GodotWebXR.session || !GodotWebXR.frame) {
+ return 0;
+ }
+ return GodotWebXR.controllers.length;
+ },
+
+ godot_webxr_is_controller_connected__proxy: 'sync',
+ godot_webxr_is_controller_connected__sig: 'ii',
+ godot_webxr_is_controller_connected: function (p_controller) {
+ if (!GodotWebXR.session || !GodotWebXR.frame) {
+ return false;
+ }
+ return !!GodotWebXR.controllers[p_controller];
+ },
+
+ godot_webxr_get_controller_transform__proxy: 'sync',
+ godot_webxr_get_controller_transform__sig: 'ii',
+ godot_webxr_get_controller_transform: function (p_controller) {
+ if (!GodotWebXR.session || !GodotWebXR.frame) {
+ return 0;
+ }
+
+ const controller = GodotWebXR.controllers[p_controller];
+ if (!controller) {
+ return 0;
+ }
+
+ const frame = GodotWebXR.frame;
+ const space = GodotWebXR.space;
+
+ const pose = frame.getPose(controller.targetRaySpace, space);
+ if (!pose) {
+ // This can mean that the controller lost tracking.
+ return 0;
+ }
+ const matrix = pose.transform.matrix;
+
+ const buf = GodotRuntime.malloc(16 * 4);
+ for (let i = 0; i < 16; i++) {
+ GodotRuntime.setHeapValue(buf + (i * 4), matrix[i], 'float');
+ }
+ return buf;
+ },
+
+ godot_webxr_get_controller_buttons__proxy: 'sync',
+ godot_webxr_get_controller_buttons__sig: 'ii',
+ godot_webxr_get_controller_buttons: function (p_controller) {
+ if (GodotWebXR.controllers.length === 0) {
+ return 0;
+ }
+
+ const controller = GodotWebXR.controllers[p_controller];
+ if (!controller || !controller.gamepad) {
+ return 0;
+ }
+
+ const button_count = controller.gamepad.buttons.length;
+
+ const buf = GodotRuntime.malloc((button_count + 1) * 4);
+ GodotRuntime.setHeapValue(buf, button_count, 'i32');
+ for (let i = 0; i < button_count; i++) {
+ GodotRuntime.setHeapValue(buf + 4 + (i * 4), controller.gamepad.buttons[i].value, 'float');
+ }
+ return buf;
+ },
+
+ godot_webxr_get_controller_axes__proxy: 'sync',
+ godot_webxr_get_controller_axes__sig: 'ii',
+ godot_webxr_get_controller_axes: function (p_controller) {
+ if (GodotWebXR.controllers.length === 0) {
+ return 0;
+ }
+
+ const controller = GodotWebXR.controllers[p_controller];
+ if (!controller || !controller.gamepad) {
+ return 0;
+ }
+
+ const axes_count = controller.gamepad.axes.length;
+
+ const buf = GodotRuntime.malloc((axes_count + 1) * 4);
+ GodotRuntime.setHeapValue(buf, axes_count, 'i32');
+ for (let i = 0; i < axes_count; i++) {
+ let value = controller.gamepad.axes[i];
+ if (i === 1 || i === 3) {
+ // Invert the Y-axis on thumbsticks and trackpads, in order to
+ // match OpenXR and other XR platform SDKs.
+ value *= -1.0;
+ }
+ GodotRuntime.setHeapValue(buf + 4 + (i * 4), value, 'float');
+ }
+ return buf;
+ },
+
+ godot_webxr_get_visibility_state__proxy: 'sync',
+ godot_webxr_get_visibility_state__sig: 'i',
+ godot_webxr_get_visibility_state: function () {
+ if (!GodotWebXR.session || !GodotWebXR.session.visibilityState) {
+ return 0;
+ }
+
+ return GodotRuntime.allocString(GodotWebXR.session.visibilityState);
+ },
+
+ godot_webxr_get_bounds_geometry__proxy: 'sync',
+ godot_webxr_get_bounds_geometry__sig: 'i',
+ godot_webxr_get_bounds_geometry: function () {
+ if (!GodotWebXR.space || !GodotWebXR.space.boundsGeometry) {
+ return 0;
+ }
+
+ const point_count = GodotWebXR.space.boundsGeometry.length;
+ if (point_count === 0) {
+ return 0;
+ }
+
+ const buf = GodotRuntime.malloc(((point_count * 3) + 1) * 4);
+ GodotRuntime.setHeapValue(buf, point_count, 'i32');
+ for (let i = 0; i < point_count; i++) {
+ const point = GodotWebXR.space.boundsGeometry[i];
+ GodotRuntime.setHeapValue(buf + ((i * 3) + 1) * 4, point.x, 'float');
+ GodotRuntime.setHeapValue(buf + ((i * 3) + 2) * 4, point.y, 'float');
+ GodotRuntime.setHeapValue(buf + ((i * 3) + 3) * 4, point.z, 'float');
+ }
+
+ return buf;
+ },
+};
+
+autoAddDeps(GodotWebXR, '$GodotWebXR');
+mergeInto(LibraryManager.library, GodotWebXR);
diff --git a/modules/webxr/native/webxr.externs.js b/modules/webxr/native/webxr.externs.js
new file mode 100644
index 0000000000..9ea105aa93
--- /dev/null
+++ b/modules/webxr/native/webxr.externs.js
@@ -0,0 +1,499 @@
+/**
+ * @type {XR}
+ */
+Navigator.prototype.xr;
+
+/**
+ * @constructor
+ */
+function XRSessionInit() {};
+
+/**
+ * @type {Array<string>}
+ */
+XRSessionInit.prototype.requiredFeatures;
+
+/**
+ * @type {Array<string>}
+ */
+XRSessionInit.prototype.optionalFeatures;
+
+/**
+ * @constructor
+ */
+function XR() {}
+
+/**
+ * @type {?function (Event)}
+ */
+XR.prototype.ondevicechanged;
+
+/**
+ * @param {string} mode
+ *
+ * @return {!Promise<boolean>}
+ */
+XR.prototype.isSessionSupported = function(mode) {};
+
+/**
+ * @param {string} mode
+ * @param {XRSessionInit} options
+ *
+ * @return {!Promise<XRSession>}
+ */
+XR.prototype.requestSession = function(mode, options) {};
+
+/**
+ * @constructor
+ */
+function XRSession() {}
+
+/**
+ * @type {XRRenderState}
+ */
+XRSession.prototype.renderState;
+
+/**
+ * @type {Array<XRInputSource>}
+ */
+XRSession.prototype.inputSources;
+
+/**
+ * @type {string}
+ */
+XRSession.prototype.visibilityState;
+
+/**
+ * @type {?function (Event)}
+ */
+XRSession.prototype.onend;
+
+/**
+ * @type {?function (XRInputSourcesChangeEvent)}
+ */
+XRSession.prototype.oninputsourceschange;
+
+/**
+ * @type {?function (XRInputSourceEvent)}
+ */
+XRSession.prototype.onselectstart;
+
+/**
+ * @type {?function (XRInputSourceEvent)}
+ */
+XRSession.prototype.onselect;
+
+/**
+ * @type {?function (XRInputSourceEvent)}
+ */
+XRSession.prototype.onselectend;
+
+/**
+ * @type {?function (XRInputSourceEvent)}
+ */
+XRSession.prototype.onsqueezestart;
+
+/**
+ * @type {?function (XRInputSourceEvent)}
+ */
+XRSession.prototype.onsqueeze;
+
+/**
+ * @type {?function (XRInputSourceEvent)}
+ */
+XRSession.prototype.onsqueezeend;
+
+/**
+ * @type {?function (Event)}
+ */
+XRSession.prototype.onvisibilitychange;
+
+/**
+ * @param {XRRenderStateInit} state
+ * @return {void}
+ */
+XRSession.prototype.updateRenderState = function (state) {};
+
+/**
+ * @param {XRFrameRequestCallback} callback
+ * @return {number}
+ */
+XRSession.prototype.requestAnimationFrame = function (callback) {};
+
+/**
+ * @param {number} handle
+ * @return {void}
+ */
+XRSession.prototype.cancelAnimationFrame = function (handle) {};
+
+/**
+ * @return {Promise<void>}
+ */
+XRSession.prototype.end = function () {};
+
+/**
+ * @param {string} referenceSpaceType
+ * @return {Promise<XRReferenceSpace>}
+ */
+XRSession.prototype.requestReferenceSpace = function (referenceSpaceType) {};
+
+/**
+ * @typedef {function(number, XRFrame): undefined}
+ */
+var XRFrameRequestCallback;
+
+/**
+ * @constructor
+ */
+function XRRenderStateInit() {}
+
+/**
+ * @type {number}
+ */
+XRRenderStateInit.prototype.depthNear;
+
+/**
+ * @type {number}
+ */
+XRRenderStateInit.prototype.depthFar;
+
+/**
+ * @type {number}
+ */
+XRRenderStateInit.prototype.inlineVerticalFieldOfView;
+
+/**
+ * @type {?XRWebGLLayer}
+ */
+XRRenderStateInit.prototype.baseLayer;
+
+/**
+ * @constructor
+ */
+function XRRenderState() {};
+
+/**
+ * @type {number}
+ */
+XRRenderState.prototype.depthNear;
+
+/**
+ * @type {number}
+ */
+XRRenderState.prototype.depthFar;
+
+/**
+ * @type {?number}
+ */
+XRRenderState.prototype.inlineVerticalFieldOfView;
+
+/**
+ * @type {?XRWebGLLayer}
+ */
+XRRenderState.prototype.baseLayer;
+
+/**
+ * @constructor
+ */
+function XRFrame() {}
+
+/**
+ * @type {XRSession}
+ */
+XRFrame.prototype.session;
+
+/**
+ * @param {XRReferenceSpace} referenceSpace
+ * @return {?XRViewerPose}
+ */
+XRFrame.prototype.getViewerPose = function (referenceSpace) {};
+
+/**
+ *
+ * @param {XRSpace} space
+ * @param {XRSpace} baseSpace
+ * @return {XRPose}
+ */
+XRFrame.prototype.getPose = function (space, baseSpace) {};
+
+/**
+ * @constructor
+ */
+function XRReferenceSpace() {};
+
+/**
+ * @type {Array<DOMPointReadOnly>}
+ */
+XRReferenceSpace.prototype.boundsGeometry;
+
+/**
+ * @param {XRRigidTransform} originOffset
+ * @return {XRReferenceSpace}
+ */
+XRReferenceSpace.prototype.getOffsetReferenceSpace = function(originOffset) {};
+
+/**
+ * @type {?function (Event)}
+ */
+XRReferenceSpace.prototype.onreset;
+
+/**
+ * @constructor
+ */
+function XRRigidTransform() {};
+
+/**
+ * @type {DOMPointReadOnly}
+ */
+XRRigidTransform.prototype.position;
+
+/**
+ * @type {DOMPointReadOnly}
+ */
+XRRigidTransform.prototype.orientation;
+
+/**
+ * @type {Float32Array}
+ */
+XRRigidTransform.prototype.matrix;
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRRigidTransform.prototype.inverse;
+
+/**
+ * @constructor
+ */
+function XRView() {}
+
+/**
+ * @type {string}
+ */
+XRView.prototype.eye;
+
+/**
+ * @type {Float32Array}
+ */
+XRView.prototype.projectionMatrix;
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRView.prototype.transform;
+
+/**
+ * @constructor
+ */
+function XRViewerPose() {}
+
+/**
+ * @type {Array<XRView>}
+ */
+XRViewerPose.prototype.views;
+
+/**
+ * @constructor
+ */
+function XRViewport() {}
+
+/**
+ * @type {number}
+ */
+XRViewport.prototype.x;
+
+/**
+ * @type {number}
+ */
+XRViewport.prototype.y;
+
+/**
+ * @type {number}
+ */
+XRViewport.prototype.width;
+
+/**
+ * @type {number}
+ */
+XRViewport.prototype.height;
+
+/**
+ * @constructor
+ */
+function XRWebGLLayerInit() {};
+
+/**
+ * @type {boolean}
+ */
+XRWebGLLayerInit.prototype.antialias;
+
+/**
+ * @type {boolean}
+ */
+XRWebGLLayerInit.prototype.depth;
+
+/**
+ * @type {boolean}
+ */
+XRWebGLLayerInit.prototype.stencil;
+
+/**
+ * @type {boolean}
+ */
+XRWebGLLayerInit.prototype.alpha;
+
+/**
+ * @type {boolean}
+ */
+XRWebGLLayerInit.prototype.ignoreDepthValues;
+
+/**
+ * @type {boolean}
+ */
+XRWebGLLayerInit.prototype.ignoreDepthValues;
+
+/**
+ * @type {number}
+ */
+XRWebGLLayerInit.prototype.framebufferScaleFactor;
+
+/**
+ * @constructor
+ *
+ * @param {XRSession} session
+ * @param {WebGLRenderContext|WebGL2RenderingContext} ctx
+ * @param {?XRWebGLLayerInit} options
+ */
+function XRWebGLLayer(session, ctx, options) {}
+
+/**
+ * @type {boolean}
+ */
+XRWebGLLayer.prototype.antialias;
+
+/**
+ * @type {boolean}
+ */
+XRWebGLLayer.prototype.ignoreDepthValues;
+
+/**
+ * @type {number}
+ */
+XRWebGLLayer.prototype.framebufferWidth;
+
+/**
+ * @type {number}
+ */
+XRWebGLLayer.prototype.framebufferHeight;
+
+/**
+ * @type {WebGLFramebuffer}
+ */
+XRWebGLLayer.prototype.framebuffer;
+
+/**
+ * @param {XRView} view
+ * @return {?XRViewport}
+ */
+XRWebGLLayer.prototype.getViewport = function(view) {};
+
+/**
+ * @param {XRSession} session
+ * @return {number}
+ */
+XRWebGLLayer.prototype.getNativeFramebufferScaleFactor = function (session) {};
+
+/**
+ * @constructor
+ */
+function WebGLRenderingContextBase() {};
+
+/**
+ * @return {Promise<void>}
+ */
+WebGLRenderingContextBase.prototype.makeXRCompatible = function () {};
+
+/**
+ * @constructor
+ */
+function XRInputSourcesChangeEvent() {};
+
+/**
+ * @type {Array<XRInputSource>}
+ */
+XRInputSourcesChangeEvent.prototype.added;
+
+/**
+ * @type {Array<XRInputSource>}
+ */
+XRInputSourcesChangeEvent.prototype.removed;
+
+/**
+ * @constructor
+ */
+function XRInputSourceEvent() {};
+
+/**
+ * @type {XRFrame}
+ */
+XRInputSourceEvent.prototype.frame;
+
+/**
+ * @type {XRInputSource}
+ */
+XRInputSourceEvent.prototype.inputSource;
+
+/**
+ * @constructor
+ */
+function XRInputSource() {};
+
+/**
+ * @type {Gamepad}
+ */
+XRInputSource.prototype.gamepad;
+
+/**
+ * @type {XRSpace}
+ */
+XRInputSource.prototype.gripSpace;
+
+/**
+ * @type {string}
+ */
+XRInputSource.prototype.handedness;
+
+/**
+ * @type {string}
+ */
+XRInputSource.prototype.profiles;
+
+/**
+ * @type {string}
+ */
+XRInputSource.prototype.targetRayMode;
+
+/**
+ * @type {XRSpace}
+ */
+XRInputSource.prototype.targetRaySpace;
+
+/**
+ * @constructor
+ */
+function XRSpace() {};
+
+/**
+ * @constructor
+ */
+function XRPose() {};
+
+/**
+ * @type {XRRigidTransform}
+ */
+XRPose.prototype.transform;
+
+/**
+ * @type {boolean}
+ */
+XRPose.prototype.emulatedPosition;
diff --git a/modules/webxr/register_types.cpp b/modules/webxr/register_types.cpp
new file mode 100644
index 0000000000..16b483c39e
--- /dev/null
+++ b/modules/webxr/register_types.cpp
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* 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 "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
+ 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/arkit/register_types.h b/modules/webxr/register_types.h
index f8939a1e3f..f0c5a4bd79 100644
--- a/modules/arkit/register_types.h
+++ b/modules/webxr/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -28,10 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef ARKIT_REGISTER_TYPES_H
-#define ARKIT_REGISTER_TYPES_H
+#ifndef WEBXR_REGISTER_TYPES_H
+#define WEBXR_REGISTER_TYPES_H
-void register_arkit_types();
-void unregister_arkit_types();
+void register_webxr_types();
+void unregister_webxr_types();
-#endif // ARKIT_REGISTER_TYPES_H
+#endif // WEBXR_REGISTER_TYPES_H
diff --git a/modules/webxr/webxr_interface.cpp b/modules/webxr/webxr_interface.cpp
new file mode 100644
index 0000000000..3e8e75bf0e
--- /dev/null
+++ b/modules/webxr/webxr_interface.cpp
@@ -0,0 +1,71 @@
+/*************************************************************************/
+/* webxr_interface.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 "webxr_interface.h"
+#include <stdlib.h>
+
+void WebXRInterface::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("is_session_supported", "session_mode"), &WebXRInterface::is_session_supported);
+ ClassDB::bind_method(D_METHOD("set_session_mode", "session_mode"), &WebXRInterface::set_session_mode);
+ ClassDB::bind_method(D_METHOD("get_session_mode"), &WebXRInterface::get_session_mode);
+ ClassDB::bind_method(D_METHOD("set_required_features", "required_features"), &WebXRInterface::set_required_features);
+ ClassDB::bind_method(D_METHOD("get_required_features"), &WebXRInterface::get_required_features);
+ ClassDB::bind_method(D_METHOD("set_optional_features", "optional_features"), &WebXRInterface::set_optional_features);
+ ClassDB::bind_method(D_METHOD("get_optional_features"), &WebXRInterface::get_optional_features);
+ ClassDB::bind_method(D_METHOD("get_reference_space_type"), &WebXRInterface::get_reference_space_type);
+ ClassDB::bind_method(D_METHOD("set_requested_reference_space_types", "requested_reference_space_types"), &WebXRInterface::set_requested_reference_space_types);
+ ClassDB::bind_method(D_METHOD("get_requested_reference_space_types"), &WebXRInterface::get_requested_reference_space_types);
+ ClassDB::bind_method(D_METHOD("get_controller", "controller_id"), &WebXRInterface::get_controller);
+ ClassDB::bind_method(D_METHOD("get_visibility_state"), &WebXRInterface::get_visibility_state);
+ ClassDB::bind_method(D_METHOD("get_bounds_geometry"), &WebXRInterface::get_bounds_geometry);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "session_mode", PROPERTY_HINT_NONE), "set_session_mode", "get_session_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "required_features", PROPERTY_HINT_NONE), "set_required_features", "get_required_features");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "optional_features", PROPERTY_HINT_NONE), "set_optional_features", "get_optional_features");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "requested_reference_space_types", PROPERTY_HINT_NONE), "set_requested_reference_space_types", "get_requested_reference_space_types");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "reference_space_type", PROPERTY_HINT_NONE), "", "get_reference_space_type");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "visibility_state", PROPERTY_HINT_NONE), "", "get_visibility_state");
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "bounds_geometry", PROPERTY_HINT_NONE), "", "get_bounds_geometry");
+
+ ADD_SIGNAL(MethodInfo("session_supported", PropertyInfo(Variant::STRING, "session_mode"), PropertyInfo(Variant::BOOL, "supported")));
+ ADD_SIGNAL(MethodInfo("session_started"));
+ ADD_SIGNAL(MethodInfo("session_ended"));
+ ADD_SIGNAL(MethodInfo("session_failed", PropertyInfo(Variant::STRING, "message")));
+
+ ADD_SIGNAL(MethodInfo("selectstart", PropertyInfo(Variant::INT, "controller_id")));
+ ADD_SIGNAL(MethodInfo("select", PropertyInfo(Variant::INT, "controller_id")));
+ ADD_SIGNAL(MethodInfo("selectend", PropertyInfo(Variant::INT, "controller_id")));
+ ADD_SIGNAL(MethodInfo("squeezestart", PropertyInfo(Variant::INT, "controller_id")));
+ ADD_SIGNAL(MethodInfo("squeeze", PropertyInfo(Variant::INT, "controller_id")));
+ ADD_SIGNAL(MethodInfo("squeezeend", PropertyInfo(Variant::INT, "controller_id")));
+
+ ADD_SIGNAL(MethodInfo("visibility_state_changed"));
+ ADD_SIGNAL(MethodInfo("reference_space_reset"));
+}
diff --git a/modules/webxr/webxr_interface.h b/modules/webxr/webxr_interface.h
new file mode 100644
index 0000000000..366235fcd5
--- /dev/null
+++ b/modules/webxr/webxr_interface.h
@@ -0,0 +1,65 @@
+/*************************************************************************/
+/* webxr_interface.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 WEBXR_INTERFACE_H
+#define WEBXR_INTERFACE_H
+
+#include "servers/xr/xr_interface.h"
+#include "servers/xr/xr_positional_tracker.h"
+
+/**
+ @author David Snopek <david.snopek@snopekgames.com>
+
+ The WebXR interface is a VR/AR interface that can be used on the web.
+*/
+
+class WebXRInterface : public XRInterface {
+ GDCLASS(WebXRInterface, XRInterface);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void is_session_supported(const String &p_session_mode) = 0;
+ virtual void set_session_mode(String p_session_mode) = 0;
+ virtual String get_session_mode() const = 0;
+ virtual void set_required_features(String p_required_features) = 0;
+ virtual String get_required_features() const = 0;
+ virtual void set_optional_features(String p_optional_features) = 0;
+ virtual String get_optional_features() const = 0;
+ virtual void set_requested_reference_space_types(String p_requested_reference_space_types) = 0;
+ virtual String get_requested_reference_space_types() const = 0;
+ virtual String get_reference_space_type() const = 0;
+ virtual Ref<XRPositionalTracker> get_controller(int p_controller_id) const = 0;
+ virtual String get_visibility_state() const = 0;
+ virtual PackedVector3Array get_bounds_geometry() const = 0;
+};
+
+#endif // WEBXR_INTERFACE_H
diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp
new file mode 100644
index 0000000000..2676b3cf80
--- /dev/null
+++ b/modules/webxr/webxr_interface_js.cpp
@@ -0,0 +1,520 @@
+/*************************************************************************/
+/* webxr_interface_js.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 JAVASCRIPT_ENABLED
+
+#include "webxr_interface_js.h"
+#include "core/input/input.h"
+#include "core/os/os.h"
+#include "emscripten.h"
+#include "godot_webxr.h"
+#include "servers/rendering/renderer_compositor.h"
+#include <stdlib.h>
+
+void _emwebxr_on_session_supported(char *p_session_mode, int p_supported) {
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL(xr_server);
+
+ Ref<XRInterface> interface = xr_server->find_interface("WebXR");
+ ERR_FAIL_COND(interface.is_null());
+
+ String session_mode = String(p_session_mode);
+ interface->emit_signal(SNAME("session_supported"), session_mode, p_supported ? true : false);
+}
+
+void _emwebxr_on_session_started(char *p_reference_space_type) {
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL(xr_server);
+
+ Ref<XRInterface> interface = xr_server->find_interface("WebXR");
+ ERR_FAIL_COND(interface.is_null());
+
+ String reference_space_type = String(p_reference_space_type);
+ ((WebXRInterfaceJS *)interface.ptr())->_set_reference_space_type(reference_space_type);
+ interface->emit_signal(SNAME("session_started"));
+}
+
+void _emwebxr_on_session_ended() {
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL(xr_server);
+
+ Ref<XRInterface> interface = xr_server->find_interface("WebXR");
+ ERR_FAIL_COND(interface.is_null());
+
+ interface->uninitialize();
+ interface->emit_signal(SNAME("session_ended"));
+}
+
+void _emwebxr_on_session_failed(char *p_message) {
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL(xr_server);
+
+ Ref<XRInterface> interface = xr_server->find_interface("WebXR");
+ ERR_FAIL_COND(interface.is_null());
+
+ interface->uninitialize();
+
+ String message = String(p_message);
+ interface->emit_signal(SNAME("session_failed"), message);
+}
+
+void _emwebxr_on_controller_changed() {
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL(xr_server);
+
+ Ref<XRInterface> interface = xr_server->find_interface("WebXR");
+ ERR_FAIL_COND(interface.is_null());
+
+ ((WebXRInterfaceJS *)interface.ptr())->_on_controller_changed();
+}
+
+extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_input_event(char *p_signal_name, int p_input_source) {
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL(xr_server);
+
+ Ref<XRInterface> interface = xr_server->find_interface("WebXR");
+ ERR_FAIL_COND(interface.is_null());
+
+ StringName signal_name = StringName(p_signal_name);
+ interface->emit_signal(signal_name, p_input_source + 1);
+}
+
+extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_simple_event(char *p_signal_name) {
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL(xr_server);
+
+ Ref<XRInterface> interface = xr_server->find_interface("WebXR");
+ ERR_FAIL_COND(interface.is_null());
+
+ StringName signal_name = StringName(p_signal_name);
+ interface->emit_signal(signal_name);
+}
+
+void WebXRInterfaceJS::is_session_supported(const String &p_session_mode) {
+ godot_webxr_is_session_supported(p_session_mode.utf8().get_data(), &_emwebxr_on_session_supported);
+}
+
+void WebXRInterfaceJS::set_session_mode(String p_session_mode) {
+ session_mode = p_session_mode;
+}
+
+String WebXRInterfaceJS::get_session_mode() const {
+ return session_mode;
+}
+
+void WebXRInterfaceJS::set_required_features(String p_required_features) {
+ required_features = p_required_features;
+}
+
+String WebXRInterfaceJS::get_required_features() const {
+ return required_features;
+}
+
+void WebXRInterfaceJS::set_optional_features(String p_optional_features) {
+ optional_features = p_optional_features;
+}
+
+String WebXRInterfaceJS::get_optional_features() const {
+ return optional_features;
+}
+
+void WebXRInterfaceJS::set_requested_reference_space_types(String p_requested_reference_space_types) {
+ requested_reference_space_types = p_requested_reference_space_types;
+}
+
+String WebXRInterfaceJS::get_requested_reference_space_types() const {
+ return requested_reference_space_types;
+}
+
+void WebXRInterfaceJS::_set_reference_space_type(String p_reference_space_type) {
+ reference_space_type = p_reference_space_type;
+}
+
+String WebXRInterfaceJS::get_reference_space_type() const {
+ return reference_space_type;
+}
+
+Ref<XRPositionalTracker> WebXRInterfaceJS::get_controller(int p_controller_id) const {
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL_V(xr_server, Ref<XRPositionalTracker>());
+
+ // 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 {
+ char *c_str = godot_webxr_get_visibility_state();
+ if (c_str) {
+ String visibility_state = String(c_str);
+ free(c_str);
+
+ return visibility_state;
+ }
+ return String();
+}
+
+PackedVector3Array WebXRInterfaceJS::get_bounds_geometry() const {
+ PackedVector3Array ret;
+
+ int *js_bounds = godot_webxr_get_bounds_geometry();
+ if (js_bounds) {
+ ret.resize(js_bounds[0]);
+ for (int i = 0; i < js_bounds[0]; i++) {
+ float *js_vector3 = ((float *)js_bounds) + (i * 3) + 1;
+ ret.set(i, Vector3(js_vector3[0], js_vector3[1], js_vector3[2]));
+ }
+ free(js_bounds);
+ }
+
+ return ret;
+}
+
+StringName WebXRInterfaceJS::get_name() const {
+ return "WebXR";
+};
+
+uint32_t WebXRInterfaceJS::get_capabilities() const {
+ return XRInterface::XR_STEREO | XRInterface::XR_MONO;
+};
+
+uint32_t WebXRInterfaceJS::get_view_count() {
+ return godot_webxr_get_view_count();
+};
+
+bool WebXRInterfaceJS::is_initialized() const {
+ return (initialized);
+};
+
+bool WebXRInterfaceJS::initialize() {
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL_V(xr_server, false);
+
+ if (!initialized) {
+ if (!godot_webxr_is_supported()) {
+ return false;
+ }
+
+ if (requested_reference_space_types.size() == 0) {
+ 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);
+
+ // Clear render_targetsize to make sure it gets reset to the new size.
+ // Clearing in uninitialize() doesn't work because a frame can still be
+ // rendered after it's called, which will fill render_targetsize again.
+ render_targetsize.width = 0;
+ render_targetsize.height = 0;
+
+ initialized = true;
+
+ godot_webxr_initialize(
+ session_mode.utf8().get_data(),
+ required_features.utf8().get_data(),
+ optional_features.utf8().get_data(),
+ requested_reference_space_types.utf8().get_data(),
+ &_emwebxr_on_session_started,
+ &_emwebxr_on_session_ended,
+ &_emwebxr_on_session_failed,
+ &_emwebxr_on_controller_changed,
+ &_emwebxr_on_input_event,
+ &_emwebxr_on_simple_event);
+ };
+
+ return true;
+};
+
+void WebXRInterfaceJS::uninitialize() {
+ if (initialized) {
+ XRServer *xr_server = XRServer::get_singleton();
+ 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();
+
+ reference_space_type = "";
+ initialized = false;
+ };
+};
+
+Transform3D WebXRInterfaceJS::_js_matrix_to_transform(float *p_js_matrix) {
+ Transform3D transform;
+
+ transform.basis.elements[0].x = p_js_matrix[0];
+ transform.basis.elements[1].x = p_js_matrix[1];
+ transform.basis.elements[2].x = p_js_matrix[2];
+ transform.basis.elements[0].y = p_js_matrix[4];
+ transform.basis.elements[1].y = p_js_matrix[5];
+ transform.basis.elements[2].y = p_js_matrix[6];
+ transform.basis.elements[0].z = p_js_matrix[8];
+ transform.basis.elements[1].z = p_js_matrix[9];
+ transform.basis.elements[2].z = p_js_matrix[10];
+ transform.origin.x = p_js_matrix[12];
+ transform.origin.y = p_js_matrix[13];
+ transform.origin.z = p_js_matrix[14];
+
+ return transform;
+}
+
+Size2 WebXRInterfaceJS::get_render_target_size() {
+ if (render_targetsize.width != 0 && render_targetsize.height != 0) {
+ return render_targetsize;
+ }
+
+ int *js_size = godot_webxr_get_render_target_size();
+ if (!initialized || js_size == nullptr) {
+ // As a temporary default (until WebXR is fully initialized), use half the window size.
+ Size2 temp = DisplayServer::get_singleton()->window_get_size();
+ temp.width /= 2.0;
+ return temp;
+ }
+
+ render_targetsize.width = js_size[0];
+ render_targetsize.height = js_size[1];
+
+ free(js_size);
+
+ return render_targetsize;
+};
+
+Transform3D WebXRInterfaceJS::get_camera_transform() {
+ Transform3D transform_for_eye;
+
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL_V(xr_server, transform_for_eye);
+
+ float *js_matrix = godot_webxr_get_transform_for_eye(0);
+ if (!initialized || js_matrix == nullptr) {
+ return transform_for_eye;
+ }
+
+ transform_for_eye = _js_matrix_to_transform(js_matrix);
+ free(js_matrix);
+
+ return xr_server->get_reference_frame() * transform_for_eye;
+};
+
+Transform3D WebXRInterfaceJS::get_transform_for_view(uint32_t p_view, const Transform3D &p_cam_transform) {
+ Transform3D transform_for_eye;
+
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL_V(xr_server, transform_for_eye);
+
+ float *js_matrix = godot_webxr_get_transform_for_eye(p_view + 1);
+ if (!initialized || js_matrix == nullptr) {
+ transform_for_eye = p_cam_transform;
+ return transform_for_eye;
+ }
+
+ transform_for_eye = _js_matrix_to_transform(js_matrix);
+ free(js_matrix);
+
+ return p_cam_transform * xr_server->get_reference_frame() * transform_for_eye;
+};
+
+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);
+ if (!initialized || js_matrix == nullptr) {
+ return eye;
+ }
+
+ int k = 0;
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ eye.matrix[i][j] = js_matrix[k++];
+ }
+ }
+
+ free(js_matrix);
+
+ // Copied from godot_oculus_mobile's ovr_mobile_session.cpp
+ eye.matrix[2][2] = -(p_z_far + p_z_near) / (p_z_far - p_z_near);
+ eye.matrix[3][2] = -(2.0f * p_z_far * p_z_near) / (p_z_far - p_z_near);
+
+ return eye;
+}
+
+Vector<BlitToScreen> WebXRInterfaceJS::commit_views(RID p_render_target, const Rect2 &p_screen_rect) {
+ Vector<BlitToScreen> blit_to_screen;
+
+ if (!initialized) {
+ return blit_to_screen;
+ }
+
+ // @todo Refactor this to be based on "views" rather than "eyes".
+ godot_webxr_commit_for_eye(1);
+ if (godot_webxr_get_view_count() > 1) {
+ godot_webxr_commit_for_eye(2);
+ }
+
+ return blit_to_screen;
+};
+
+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;
+ }
+
+ for (int i = 0; i < controller_count; i++) {
+ _update_tracker(i);
+ }
+ };
+};
+
+void WebXRInterfaceJS::_update_tracker(int p_controller_id) {
+ XRServer *xr_server = XRServer::get_singleton();
+ ERR_FAIL_NULL(xr_server);
+
+ // 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_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);
+ }
+ xr_server->add_tracker(tracker);
+ }
+
+ 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_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++) {
+ 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++) {
+ 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();
+ }
+}
+
+void WebXRInterfaceJS::_on_controller_changed() {
+ // Register "virtual" gamepads with Godot for the ones we get from WebXR.
+ godot_webxr_sample_controller_data();
+ 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", "");
+ controllers_state[i] = controller_connected;
+ }
+ }
+}
+
+WebXRInterfaceJS::WebXRInterfaceJS() {
+ initialized = false;
+ session_mode = "inline";
+ requested_reference_space_types = "local";
+};
+
+WebXRInterfaceJS::~WebXRInterfaceJS() {
+ // and make sure we cleanup if we haven't already
+ if (initialized) {
+ uninitialize();
+ };
+};
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h
new file mode 100644
index 0000000000..6e6548c946
--- /dev/null
+++ b/modules/webxr/webxr_interface_js.h
@@ -0,0 +1,105 @@
+/*************************************************************************/
+/* webxr_interface_js.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 WEBXR_INTERFACE_JS_H
+#define WEBXR_INTERFACE_JS_H
+
+#ifdef JAVASCRIPT_ENABLED
+
+#include "webxr_interface.h"
+
+/**
+ @author David Snopek <david.snopek@snopekgames.com>
+
+ The WebXR interface is a VR/AR interface that can be used on the web.
+*/
+
+class WebXRInterfaceJS : public WebXRInterface {
+ GDCLASS(WebXRInterfaceJS, WebXRInterface);
+
+private:
+ bool initialized;
+ Ref<XRPositionalTracker> head_tracker;
+
+ String session_mode;
+ String required_features;
+ String optional_features;
+ 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);
+ void _update_tracker(int p_controller_id);
+
+public:
+ virtual void is_session_supported(const String &p_session_mode) override;
+ virtual void set_session_mode(String p_session_mode) override;
+ virtual String get_session_mode() const override;
+ virtual void set_required_features(String p_required_features) override;
+ virtual String get_required_features() const override;
+ virtual void set_optional_features(String p_optional_features) override;
+ virtual String get_optional_features() const override;
+ virtual void set_requested_reference_space_types(String p_requested_reference_space_types) override;
+ virtual String get_requested_reference_space_types() const override;
+ void _set_reference_space_type(String p_reference_space_type);
+ virtual String get_reference_space_type() const override;
+ virtual Ref<XRPositionalTracker> get_controller(int p_controller_id) const override;
+ virtual String get_visibility_state() const override;
+ virtual PackedVector3Array get_bounds_geometry() const override;
+
+ virtual StringName get_name() const override;
+ virtual uint32_t get_capabilities() const override;
+
+ virtual bool is_initialized() const override;
+ virtual bool initialize() override;
+ virtual void uninitialize() 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, 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;
+
+ void _on_controller_changed();
+
+ WebXRInterfaceJS();
+ ~WebXRInterfaceJS();
+};
+
+#endif // JAVASCRIPT_ENABLED
+
+#endif // WEBXR_INTERFACE_JS_H
diff --git a/modules/xatlas_unwrap/SCsub b/modules/xatlas_unwrap/SCsub
index c659349d05..aa6bdaea33 100644
--- a/modules/xatlas_unwrap/SCsub
+++ b/modules/xatlas_unwrap/SCsub
@@ -6,6 +6,9 @@ Import("env_modules")
env_xatlas_unwrap = env_modules.Clone()
# Thirdparty source files
+
+thirdparty_obj = []
+
if env["builtin_xatlas"]:
thirdparty_dir = "#thirdparty/xatlas/"
thirdparty_sources = [
@@ -17,7 +20,16 @@ if env["builtin_xatlas"]:
env_thirdparty = env_xatlas_unwrap.Clone()
env_thirdparty.disable_warnings()
- env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+ env.modules_sources += thirdparty_obj
+
# Godot source files
-env_xatlas_unwrap.add_source_files(env.modules_sources, "*.cpp")
+
+module_obj = []
+
+env_xatlas_unwrap.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/xatlas_unwrap/register_types.cpp b/modules/xatlas_unwrap/register_types.cpp
index 6242009f67..58a6216b1e 100644
--- a/modules/xatlas_unwrap/register_types.cpp
+++ b/modules/xatlas_unwrap/register_types.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */
@@ -29,26 +29,19 @@
/*************************************************************************/
#include "register_types.h"
-
-#include "core/error_macros.h"
-
#include "core/crypto/crypto_core.h"
-
#include "thirdparty/xatlas/xatlas.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache);
+extern bool (*array_mesh_lightmap_unwrap_callback)(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y);
-bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, float **r_uvs, int **r_vertices, int *r_vertex_count, int **r_indices, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y, int *&r_cache_data, unsigned int &r_cache_size, bool &r_used_cache) {
+bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_vertices, const float *p_normals, int p_vertex_count, const int *p_indices, int p_index_count, const uint8_t *p_cache_data, bool *r_use_cache, uint8_t **r_mesh_cache, int *r_mesh_cache_size, float **r_uv, int **r_vertex, int *r_vertex_count, int **r_index, int *r_index_count, int *r_size_hint_x, int *r_size_hint_y) {
CryptoCore::MD5Context ctx;
ctx.start();
ctx.update((unsigned char *)&p_texel_size, sizeof(float));
ctx.update((unsigned char *)p_indices, sizeof(int) * p_index_count);
- ctx.update((unsigned char *)p_vertices, sizeof(float) * p_vertex_count);
- ctx.update((unsigned char *)p_normals, sizeof(float) * p_vertex_count);
+ ctx.update((unsigned char *)p_vertices, sizeof(float) * p_vertex_count * 3);
+ ctx.update((unsigned char *)p_normals, sizeof(float) * p_vertex_count * 3);
unsigned char hash[16];
ctx.finish(hash);
@@ -56,38 +49,37 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
bool cached = false;
unsigned int cache_idx = 0;
- if (r_used_cache && r_cache_size) {
- //Check if hash is in cache data
+ *r_mesh_cache = nullptr;
+ *r_mesh_cache_size = 0;
- int *cache_data = r_cache_data;
+ if (p_cache_data) {
+ //Check if hash is in cache data
+ int *cache_data = (int *)p_cache_data;
int n_entries = cache_data[0];
- unsigned int r_idx = 1;
+ unsigned int read_idx = 1;
for (int i = 0; i < n_entries; ++i) {
- if (memcmp(&cache_data[r_idx], hash, 16) == 0) {
+ if (memcmp(&cache_data[read_idx], hash, 16) == 0) {
cached = true;
- cache_idx = r_idx;
+ cache_idx = read_idx;
break;
}
- r_idx += 4; // hash
- r_idx += 2; // size hint
+ read_idx += 4; // hash
+ read_idx += 2; // size hint
- int vertex_count = cache_data[r_idx];
- r_idx += 1; // vertex count
- r_idx += vertex_count; // vertex
- r_idx += vertex_count * 2; // uvs
+ int vertex_count = cache_data[read_idx];
+ read_idx += 1; // vertex count
+ read_idx += vertex_count; // vertex
+ read_idx += vertex_count * 2; // uvs
- int index_count = cache_data[r_idx];
- r_idx += 1; // index count
- r_idx += index_count; // indices
+ int index_count = cache_data[read_idx];
+ read_idx += 1; // index count
+ read_idx += index_count; // indices
}
}
- if (r_used_cache && cached) {
- int *cache_data = r_cache_data;
-
- // Return cache data pointer to the caller
- r_cache_data = &cache_data[cache_idx];
+ if (cached) {
+ int *cache_data = (int *)p_cache_data;
cache_idx += 4;
@@ -99,92 +91,92 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
// Load vertices
*r_vertex_count = cache_data[cache_idx];
cache_idx++;
- *r_vertices = &cache_data[cache_idx];
+ *r_vertex = &cache_data[cache_idx];
cache_idx += *r_vertex_count;
// Load UVs
- *r_uvs = (float *)&cache_data[cache_idx];
+ *r_uv = (float *)&cache_data[cache_idx];
cache_idx += *r_vertex_count * 2;
// Load indices
*r_index_count = cache_data[cache_idx];
cache_idx++;
- *r_indices = &cache_data[cache_idx];
-
- // Return cache data size to the caller
- r_cache_size = sizeof(int) * (4 + 2 + 1 + *r_vertex_count + (*r_vertex_count * 2) + 1 + *r_index_count); // hash + size hint + vertex_count + vertices + uvs + index_count + indices
- r_used_cache = true;
- return true;
- }
-
- //set up input mesh
- xatlas::MeshDecl input_mesh;
- input_mesh.indexData = p_indices;
- input_mesh.indexCount = p_index_count;
- input_mesh.indexFormat = xatlas::IndexFormat::UInt32;
-
- input_mesh.vertexCount = p_vertex_count;
- input_mesh.vertexPositionData = p_vertices;
- input_mesh.vertexPositionStride = sizeof(float) * 3;
- input_mesh.vertexNormalData = p_normals;
- input_mesh.vertexNormalStride = sizeof(uint32_t) * 3;
- input_mesh.vertexUvData = nullptr;
- input_mesh.vertexUvStride = 0;
-
- xatlas::ChartOptions chart_options;
- xatlas::PackOptions pack_options;
-
- pack_options.maxChartSize = 4096;
- pack_options.blockAlign = true;
- pack_options.padding = 1;
- pack_options.texelsPerUnit = 1.0 / p_texel_size;
-
- xatlas::Atlas *atlas = xatlas::Create();
- printf("Adding mesh..\n");
- xatlas::AddMeshError::Enum err = xatlas::AddMesh(atlas, input_mesh, 1);
- ERR_FAIL_COND_V_MSG(err != xatlas::AddMeshError::Enum::Success, false, xatlas::StringForEnum(err));
-
- printf("Generate..\n");
- xatlas::Generate(atlas, chart_options, xatlas::ParameterizeOptions(), pack_options);
-
- *r_size_hint_x = atlas->width;
- *r_size_hint_y = atlas->height;
-
- float w = *r_size_hint_x;
- float h = *r_size_hint_y;
-
- if (w == 0 || h == 0) {
- return false; //could not bake because there is no area
- }
+ *r_index = &cache_data[cache_idx];
+ } else {
+ // set up input mesh
+ xatlas::MeshDecl input_mesh;
+ input_mesh.indexData = p_indices;
+ input_mesh.indexCount = p_index_count;
+ input_mesh.indexFormat = xatlas::IndexFormat::UInt32;
+
+ input_mesh.vertexCount = p_vertex_count;
+ input_mesh.vertexPositionData = p_vertices;
+ input_mesh.vertexPositionStride = sizeof(float) * 3;
+ input_mesh.vertexNormalData = p_normals;
+ input_mesh.vertexNormalStride = sizeof(uint32_t) * 3;
+ input_mesh.vertexUvData = nullptr;
+ input_mesh.vertexUvStride = 0;
+
+ xatlas::ChartOptions chart_options;
+ chart_options.fixWinding = true;
+
+ xatlas::PackOptions pack_options;
+ pack_options.padding = 1;
+ pack_options.maxChartSize = 4094; // Lightmap atlassing needs 2 for padding between meshes, so 4096-2
+ pack_options.blockAlign = true;
+ pack_options.texelsPerUnit = 1.0 / p_texel_size;
+
+ xatlas::Atlas *atlas = xatlas::Create();
+
+ xatlas::AddMeshError err = xatlas::AddMesh(atlas, input_mesh, 1);
+ ERR_FAIL_COND_V_MSG(err != xatlas::AddMeshError::Success, false, xatlas::StringForEnum(err));
+
+ xatlas::Generate(atlas, chart_options, pack_options);
+
+ *r_size_hint_x = atlas->width;
+ *r_size_hint_y = atlas->height;
+
+ float w = *r_size_hint_x;
+ float h = *r_size_hint_y;
+
+ if (w == 0 || h == 0) {
+ xatlas::Destroy(atlas);
+ return false; //could not bake because there is no area
+ }
- const xatlas::Mesh &output = atlas->meshes[0];
+ const xatlas::Mesh &output = atlas->meshes[0];
+
+ *r_vertex = (int *)memalloc(sizeof(int) * output.vertexCount);
+ ERR_FAIL_NULL_V_MSG(*r_vertex, false, "Out of memory.");
+ *r_uv = (float *)memalloc(sizeof(float) * output.vertexCount * 2);
+ ERR_FAIL_NULL_V_MSG(*r_uv, false, "Out of memory.");
+ *r_index = (int *)memalloc(sizeof(int) * output.indexCount);
+ ERR_FAIL_NULL_V_MSG(*r_index, false, "Out of memory.");
+
+ float max_x = 0;
+ float max_y = 0;
+ for (uint32_t i = 0; i < output.vertexCount; i++) {
+ (*r_vertex)[i] = output.vertexArray[i].xref;
+ (*r_uv)[i * 2 + 0] = output.vertexArray[i].uv[0] / w;
+ (*r_uv)[i * 2 + 1] = output.vertexArray[i].uv[1] / h;
+ max_x = MAX(max_x, output.vertexArray[i].uv[0]);
+ max_y = MAX(max_y, output.vertexArray[i].uv[1]);
+ }
- *r_vertices = (int *)malloc(sizeof(int) * output.vertexCount);
- *r_uvs = (float *)malloc(sizeof(float) * output.vertexCount * 2);
- *r_indices = (int *)malloc(sizeof(int) * output.indexCount);
+ *r_vertex_count = output.vertexCount;
- float max_x = 0;
- float max_y = 0;
- for (uint32_t i = 0; i < output.vertexCount; i++) {
- (*r_vertices)[i] = output.vertexArray[i].xref;
- (*r_uvs)[i * 2 + 0] = output.vertexArray[i].uv[0] / w;
- (*r_uvs)[i * 2 + 1] = output.vertexArray[i].uv[1] / h;
- max_x = MAX(max_x, output.vertexArray[i].uv[0]);
- max_y = MAX(max_y, output.vertexArray[i].uv[1]);
- }
+ for (uint32_t i = 0; i < output.indexCount; i++) {
+ (*r_index)[i] = output.indexArray[i];
+ }
- printf("Final texture size: %f,%f - max %f,%f\n", w, h, max_x, max_y);
- *r_vertex_count = output.vertexCount;
+ *r_index_count = output.indexCount;
- for (uint32_t i = 0; i < output.indexCount; i++) {
- (*r_indices)[i] = output.indexArray[i];
+ xatlas::Destroy(atlas);
}
- *r_index_count = output.indexCount;
-
- xatlas::Destroy(atlas);
+ if (*r_use_cache) {
+ // Build cache data for current mesh
- if (r_used_cache) {
unsigned int new_cache_size = 4 + 2 + 1 + *r_vertex_count + (*r_vertex_count * 2) + 1 + *r_index_count; // hash + size hint + vertex_count + vertices + uvs + index_count + indices
new_cache_size *= sizeof(int);
int *new_cache_data = (int *)memalloc(new_cache_size);
@@ -204,11 +196,11 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
new_cache_idx++;
// vertices
- memcpy(&new_cache_data[new_cache_idx], *r_vertices, sizeof(int) * *r_vertex_count);
+ memcpy(&new_cache_data[new_cache_idx], *r_vertex, sizeof(int) * (*r_vertex_count));
new_cache_idx += *r_vertex_count;
// uvs
- memcpy(&new_cache_data[new_cache_idx], *r_uvs, sizeof(float) * *r_vertex_count * 2);
+ memcpy(&new_cache_data[new_cache_idx], *r_uv, sizeof(float) * (*r_vertex_count) * 2);
new_cache_idx += *r_vertex_count * 2;
// index count
@@ -216,15 +208,15 @@ bool xatlas_mesh_lightmap_unwrap_callback(float p_texel_size, const float *p_ver
new_cache_idx++;
// indices
- memcpy(&new_cache_data[new_cache_idx], *r_indices, sizeof(int) * *r_index_count);
- new_cache_idx += *r_index_count;
+ memcpy(&new_cache_data[new_cache_idx], *r_index, sizeof(int) * (*r_index_count));
// Return cache data to the caller
- r_cache_data = new_cache_data;
- r_cache_size = new_cache_size;
- r_used_cache = false;
+ *r_mesh_cache = (uint8_t *)new_cache_data;
+ *r_mesh_cache_size = new_cache_size;
}
+ *r_use_cache = cached; // Return whether cache was used.
+
return true;
}
diff --git a/modules/xatlas_unwrap/register_types.h b/modules/xatlas_unwrap/register_types.h
index fe924bab96..2ad729f172 100644
--- a/modules/xatlas_unwrap/register_types.h
+++ b/modules/xatlas_unwrap/register_types.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* 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 */