aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml38
-rw-r--r--AUTHORS.md4
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--DONORS.md82
-rw-r--r--README.md2
-rw-r--r--core/array.cpp8
-rw-r--r--core/array.h2
-rw-r--r--core/bind/core_bind.cpp25
-rw-r--r--core/bind/core_bind.h1
-rw-r--r--core/class_db.cpp8
-rw-r--r--core/class_db.h4
-rw-r--r--core/dictionary.cpp4
-rw-r--r--core/dictionary.h2
-rw-r--r--core/error_macros.h10
-rw-r--r--core/image.cpp190
-rw-r--r--core/image.h5
-rw-r--r--core/input_map.cpp81
-rw-r--r--core/input_map.h13
-rw-r--r--core/io/http_client.cpp22
-rw-r--r--core/io/image_loader.cpp2
-rw-r--r--core/io/multiplayer_api.cpp722
-rw-r--r--core/io/multiplayer_api.h87
-rw-r--r--core/io/pck_packer.cpp8
-rw-r--r--core/io/resource_format_binary.cpp8
-rw-r--r--core/io/resource_saver.cpp2
-rw-r--r--core/io/stream_peer_ssl.cpp31
-rw-r--r--core/io/stream_peer_ssl.h1
-rw-r--r--core/math/geometry.h13
-rw-r--r--core/math/math_2d.cpp10
-rw-r--r--core/math/math_2d.h2
-rw-r--r--core/math/math_funcs.h4
-rw-r--r--core/math/matrix3.cpp63
-rw-r--r--core/math/matrix3.h15
-rw-r--r--core/math/quat.cpp2
-rw-r--r--core/math/quick_hull.cpp2
-rw-r--r--core/math/transform.cpp4
-rw-r--r--core/math/vector3.h6
-rw-r--r--core/node_path.cpp3
-rw-r--r--core/oa_hash_map.h617
-rw-r--r--core/os/input.cpp6
-rw-r--r--core/os/input.h6
-rw-r--r--core/os/input_event.cpp85
-rw-r--r--core/os/input_event.h22
-rw-r--r--core/os/keyboard.cpp121
-rw-r--r--core/os/keyboard.h1
-rw-r--r--core/os/os.cpp2
-rw-r--r--core/os/os.h12
-rw-r--r--core/project_settings.cpp28
-rw-r--r--core/project_settings.h2
-rw-r--r--core/register_core_types.cpp2
-rw-r--r--core/resource.cpp2
-rw-r--r--core/script_language.h4
-rw-r--r--core/self_list.h41
-rw-r--r--core/string_db.cpp13
-rw-r--r--core/string_db.h1
-rw-r--r--core/ustring.cpp94
-rw-r--r--core/ustring.h4
-rw-r--r--core/variant.cpp6
-rw-r--r--core/variant.h2
-rw-r--r--core/variant_call.cpp24
-rw-r--r--core/variant_op.cpp34
-rw-r--r--doc/classes/@GDScript.xml3
-rw-r--r--doc/classes/ARVRAnchor.xml2
-rw-r--r--doc/classes/ARVRServer.xml36
-rw-r--r--doc/classes/AnimationPlayer.xml12
-rw-r--r--doc/classes/Array.xml5
-rw-r--r--doc/classes/ArrayMesh.xml10
-rw-r--r--doc/classes/AudioServer.xml20
-rw-r--r--doc/classes/AudioStreamPlayer.xml1
-rw-r--r--doc/classes/AudioStreamPlayer2D.xml1
-rw-r--r--doc/classes/AudioStreamPlayer3D.xml3
-rw-r--r--doc/classes/Basis.xml1
-rw-r--r--doc/classes/Camera.xml24
-rw-r--r--doc/classes/CanvasItemMaterial.xml3
-rw-r--r--doc/classes/CanvasLayer.xml6
-rw-r--r--doc/classes/CapsuleShape2D.xml2
-rw-r--r--doc/classes/CheckBox.xml2
-rw-r--r--doc/classes/CollisionShape.xml2
-rw-r--r--doc/classes/CollisionShape2D.xml2
-rw-r--r--doc/classes/ColorPickerButton.xml2
-rw-r--r--doc/classes/ConeTwistJoint.xml2
-rw-r--r--doc/classes/Control.xml10
-rw-r--r--doc/classes/Dictionary.xml3
-rw-r--r--doc/classes/EditorPlugin.xml24
-rw-r--r--doc/classes/Engine.xml6
-rw-r--r--doc/classes/File.xml4
-rw-r--r--doc/classes/Generic6DOFJoint.xml14
-rw-r--r--doc/classes/GraphEdit.xml1
-rw-r--r--doc/classes/GridContainer.xml2
-rw-r--r--doc/classes/HTTPClient.xml6
-rw-r--r--doc/classes/HTTPRequest.xml2
-rw-r--r--doc/classes/Image.xml78
-rw-r--r--doc/classes/Input.xml2
-rw-r--r--doc/classes/InterpolatedCamera.xml7
-rw-r--r--doc/classes/ItemList.xml45
-rw-r--r--doc/classes/LineEdit.xml5
-rw-r--r--doc/classes/LinkButton.xml2
-rw-r--r--doc/classes/MultiplayerAPI.xml131
-rw-r--r--doc/classes/Navigation.xml22
-rw-r--r--doc/classes/Navigation2D.xml9
-rw-r--r--doc/classes/Node.xml31
-rw-r--r--doc/classes/OS.xml99
-rw-r--r--doc/classes/Object.xml1
-rw-r--r--doc/classes/OptionButton.xml7
-rw-r--r--doc/classes/PackedScene.xml9
-rw-r--r--doc/classes/PoolByteArray.xml2
-rw-r--r--doc/classes/PoolColorArray.xml2
-rw-r--r--doc/classes/PoolIntArray.xml2
-rw-r--r--doc/classes/PoolRealArray.xml2
-rw-r--r--doc/classes/PoolStringArray.xml2
-rw-r--r--doc/classes/PoolVector2Array.xml2
-rw-r--r--doc/classes/PoolVector3Array.xml2
-rw-r--r--doc/classes/Popup.xml1
-rw-r--r--doc/classes/PopupMenu.xml61
-rw-r--r--doc/classes/Position3D.xml2
-rw-r--r--doc/classes/Quat.xml1
-rw-r--r--doc/classes/RichTextLabel.xml15
-rw-r--r--doc/classes/SceneTree.xml56
-rw-r--r--doc/classes/Script.xml1
-rw-r--r--doc/classes/ScrollBar.xml1
-rw-r--r--doc/classes/ScrollContainer.xml14
-rw-r--r--doc/classes/Spatial.xml6
-rw-r--r--doc/classes/SplitContainer.xml1
-rw-r--r--doc/classes/String.xml36
-rw-r--r--doc/classes/SurfaceTool.xml3
-rw-r--r--doc/classes/TabContainer.xml16
-rw-r--r--doc/classes/Tabs.xml16
-rw-r--r--doc/classes/TextEdit.xml16
-rw-r--r--doc/classes/TextureProgress.xml6
-rw-r--r--doc/classes/TileMap.xml2
-rw-r--r--doc/classes/TileSet.xml24
-rw-r--r--doc/classes/Timer.xml4
-rw-r--r--doc/classes/Transform.xml5
-rw-r--r--doc/classes/Tween.xml111
-rw-r--r--doc/classes/Vector2.xml4
-rw-r--r--doc/classes/VehicleWheel.xml12
-rw-r--r--doc/classes/VideoPlayer.xml6
-rw-r--r--doc/classes/Viewport.xml3
-rw-r--r--doc/classes/VisualServer.xml2
-rw-r--r--doc/classes/WebSocketClient.xml4
-rw-r--r--drivers/alsa/audio_driver_alsa.cpp143
-rw-r--r--drivers/alsa/audio_driver_alsa.h13
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.cpp225
-rw-r--r--drivers/coreaudio/audio_driver_coreaudio.h23
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.cpp9
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.h2
-rw-r--r--drivers/gles2/shader_compiler_gles2.cpp6
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp105
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h5
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp11
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp84
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h4
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp4
-rw-r--r--drivers/gles3/shaders/canvas.glsl117
-rw-r--r--drivers/gles3/shaders/scene.glsl6
-rw-r--r--drivers/gles3/shaders/tonemap.glsl4
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp436
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.h28
-rw-r--r--drivers/rtaudio/audio_driver_rtaudio.cpp2
-rw-r--r--drivers/unix/file_access_unix.cpp8
-rw-r--r--drivers/unix/ip_unix.cpp6
-rw-r--r--drivers/unix/os_unix.cpp6
-rw-r--r--drivers/unix/stream_peer_tcp_posix.cpp21
-rw-r--r--drivers/unix/tcp_server_posix.cpp17
-rw-r--r--drivers/wasapi/audio_driver_wasapi.cpp159
-rw-r--r--drivers/wasapi/audio_driver_wasapi.h6
-rw-r--r--drivers/windows/file_access_windows.cpp38
-rw-r--r--drivers/windows/stream_peer_tcp_winsock.cpp4
-rw-r--r--drivers/windows/tcp_server_winsock.cpp1
-rw-r--r--editor/animation_editor.cpp12
-rw-r--r--editor/code_editor.cpp67
-rw-r--r--editor/collada/collada.cpp4
-rw-r--r--editor/connections_dialog.cpp674
-rw-r--r--editor/connections_dialog.h85
-rw-r--r--editor/create_dialog.cpp30
-rw-r--r--editor/dependency_editor.cpp63
-rw-r--r--editor/dependency_editor.h3
-rw-r--r--editor/editor_audio_buses.cpp4
-rw-r--r--editor/editor_data.cpp39
-rw-r--r--editor/editor_data.h2
-rw-r--r--editor/editor_export.cpp11
-rw-r--r--editor/editor_export.h3
-rw-r--r--editor/editor_file_dialog.cpp3
-rw-r--r--editor/editor_fonts.cpp65
-rw-r--r--editor/editor_help.cpp27
-rw-r--r--editor/editor_help.h1
-rw-r--r--editor/editor_node.cpp169
-rw-r--r--editor/editor_node.h1
-rw-r--r--editor/editor_plugin.cpp7
-rw-r--r--editor/editor_plugin.h1
-rw-r--r--editor/editor_settings.cpp23
-rw-r--r--editor/editor_themes.cpp12
-rw-r--r--editor/export_template_manager.cpp8
-rw-r--r--editor/filesystem_dock.cpp130
-rw-r--r--editor/filesystem_dock.h8
-rw-r--r--editor/find_in_files.cpp824
-rw-r--r--editor/find_in_files.h184
-rw-r--r--editor/groups_editor.cpp437
-rw-r--r--editor/groups_editor.h66
-rw-r--r--editor/icons/icon_add_split.svg78
-rw-r--r--editor/icons/icon_asset_lib.svg2
-rw-r--r--editor/icons/icon_audio_bus_layout.svg4
-rw-r--r--editor/icons/icon_audio_stream_player.svg4
-rw-r--r--editor/icons/icon_audio_stream_player_2_d.svg4
-rw-r--r--editor/icons/icon_audio_stream_player_3_d.svg4
-rw-r--r--editor/icons/icon_audio_stream_sample.svg4
-rw-r--r--editor/icons/icon_bone_2_d.svg61
-rw-r--r--editor/icons/icon_bus_vu_db.svg4
-rw-r--r--editor/icons/icon_bus_vu_empty.svg4
-rw-r--r--editor/icons/icon_bus_vu_full.svg4
-rw-r--r--editor/icons/icon_c_s_g_box.svg6
-rw-r--r--editor/icons/icon_c_s_g_capsule.svg6
-rw-r--r--editor/icons/icon_c_s_g_combiner.svg8
-rw-r--r--editor/icons/icon_c_s_g_cylinder.svg6
-rw-r--r--editor/icons/icon_c_s_g_mesh.svg6
-rw-r--r--editor/icons/icon_c_s_g_polygon.svg6
-rw-r--r--editor/icons/icon_c_s_g_sphere.svg6
-rw-r--r--editor/icons/icon_c_s_g_torus.svg6
-rw-r--r--editor/icons/icon_color_rect.svg5
-rw-r--r--editor/icons/icon_editor_position.svg4
-rw-r--r--editor/icons/icon_editor_position_previous.svg3
-rw-r--r--editor/icons/icon_editor_position_unselected.svg4
-rw-r--r--editor/icons/icon_insert_after.svg7
-rw-r--r--editor/icons/icon_insert_before.svg7
-rw-r--r--editor/icons/icon_key_hover.svg5
-rw-r--r--editor/icons/icon_key_selected.svg5
-rw-r--r--editor/icons/icon_move_down.svg4
-rw-r--r--editor/icons/icon_move_left.svg3
-rw-r--r--editor/icons/icon_move_right.svg3
-rw-r--r--editor/icons/icon_move_up.svg4
-rw-r--r--editor/icons/icon_paint_vertex.svg58
-rw-r--r--editor/icons/icon_panel.svg4
-rw-r--r--editor/icons/icon_panel_container.svg4
-rw-r--r--editor/icons/icon_physical_bone.svg77
-rw-r--r--editor/icons/icon_translation.svg2
-rw-r--r--editor/icons/icon_unpaint_vertex.svg58
-rw-r--r--editor/import/editor_import_collada.cpp4
-rw-r--r--editor/import/editor_scene_importer_gltf.cpp15
-rw-r--r--editor/import/resource_importer_scene.cpp8
-rw-r--r--editor/import/resource_importer_texture.cpp2
-rw-r--r--editor/import/resource_importer_wav.cpp28
-rw-r--r--editor/import_dock.cpp2
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp32
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp10
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp22
-rw-r--r--editor/plugins/asset_library_editor_plugin.h1
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp1033
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h44
-rw-r--r--editor/plugins/collision_polygon_editor_plugin.cpp93
-rw-r--r--editor/plugins/collision_polygon_editor_plugin.h25
-rw-r--r--editor/plugins/item_list_editor_plugin.cpp28
-rw-r--r--editor/plugins/item_list_editor_plugin.h4
-rw-r--r--editor/plugins/mesh_instance_editor_plugin.cpp6
-rw-r--r--editor/plugins/navigation_mesh_generator.cpp34
-rw-r--r--editor/plugins/particles_2d_editor_plugin.cpp4
-rw-r--r--editor/plugins/particles_editor_plugin.cpp2
-rw-r--r--editor/plugins/path_editor_plugin.cpp11
-rw-r--r--editor/plugins/physical_bone_plugin.cpp123
-rw-r--r--editor/plugins/physical_bone_plugin.h78
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp510
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.h31
-rw-r--r--editor/plugins/script_editor_plugin.cpp200
-rw-r--r--editor/plugins/script_editor_plugin.h25
-rw-r--r--editor/plugins/script_text_editor.cpp81
-rw-r--r--editor/plugins/script_text_editor.h9
-rw-r--r--editor/plugins/shader_graph_editor_plugin.cpp10
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.cpp5
-rw-r--r--editor/plugins/skeleton_editor_plugin.cpp189
-rw-r--r--editor/plugins/skeleton_editor_plugin.h95
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp86
-rw-r--r--editor/plugins/sprite_editor_plugin.cpp5
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp28
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp10
-rw-r--r--editor/plugins/theme_editor_plugin.cpp64
-rw-r--r--editor/plugins/tile_map_editor_plugin.cpp73
-rw-r--r--editor/plugins/tile_map_editor_plugin.h21
-rw-r--r--editor/plugins/tile_set_editor_plugin.cpp13
-rw-r--r--editor/project_export.cpp28
-rw-r--r--editor/project_export.h1
-rw-r--r--editor/project_manager.cpp4
-rw-r--r--editor/project_settings_editor.cpp237
-rw-r--r--editor/project_settings_editor.h8
-rw-r--r--editor/property_editor.cpp83
-rw-r--r--editor/scene_tree_dock.cpp107
-rw-r--r--editor/scene_tree_editor.cpp7
-rw-r--r--editor/script_editor_debugger.cpp23
-rw-r--r--editor/settings_config_dialog.cpp29
-rw-r--r--editor/spatial_editor_gizmos.cpp141
-rw-r--r--editor/spatial_editor_gizmos.h13
-rw-r--r--editor/translations/af.po46
-rw-r--r--editor/translations/ar.po52
-rw-r--r--editor/translations/bg.po46
-rw-r--r--editor/translations/bn.po60
-rw-r--r--editor/translations/ca.po64
-rw-r--r--editor/translations/cs.po51
-rw-r--r--editor/translations/da.po97
-rw-r--r--editor/translations/de.po100
-rw-r--r--editor/translations/de_CH.po51
-rw-r--r--editor/translations/editor.pot154
-rw-r--r--editor/translations/el.po62
-rw-r--r--editor/translations/es.po135
-rw-r--r--editor/translations/es_AR.po62
-rw-r--r--editor/translations/fa.po155
-rw-r--r--editor/translations/fi.po60
-rw-r--r--editor/translations/fr.po389
-rw-r--r--editor/translations/he.po155
-rw-r--r--editor/translations/hi.po46
-rw-r--r--editor/translations/hu.po306
-rw-r--r--editor/translations/id.po78
-rw-r--r--editor/translations/is.po46
-rw-r--r--editor/translations/it.po164
-rw-r--r--editor/translations/ja.po71
-rw-r--r--editor/translations/ko.po88
-rw-r--r--editor/translations/lt.po46
-rw-r--r--editor/translations/nb.po348
-rw-r--r--editor/translations/nl.po626
-rw-r--r--editor/translations/pl.po67
-rw-r--r--editor/translations/pr.po46
-rw-r--r--editor/translations/pt_BR.po119
-rw-r--r--editor/translations/pt_PT.po113
-rw-r--r--editor/translations/ro.po551
-rw-r--r--editor/translations/ru.po98
-rw-r--r--editor/translations/sk.po46
-rw-r--r--editor/translations/sl.po396
-rw-r--r--editor/translations/sr_Cyrl.po50
-rw-r--r--editor/translations/sr_Latn.po7954
-rw-r--r--editor/translations/sv.po46
-rw-r--r--editor/translations/ta.po46
-rw-r--r--editor/translations/th.po62
-rw-r--r--editor/translations/tr.po96
-rw-r--r--editor/translations/uk.po79
-rw-r--r--editor/translations/ur_PK.po46
-rw-r--r--editor/translations/vi.po89
-rw-r--r--editor/translations/zh_CN.po189
-rw-r--r--editor/translations/zh_HK.po51
-rw-r--r--editor/translations/zh_TW.po240
-rw-r--r--main/SCsub2
-rw-r--r--main/input_default.cpp158
-rw-r--r--main/input_default.h23
-rw-r--r--main/main.cpp25
-rw-r--r--main/tests/test_gui.cpp4
-rw-r--r--main/tests/test_oa_hash_map.cpp6
-rw-r--r--methods.py21
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj34
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-568h@2x.pngbin564 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-667h@2x.pngbin817 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.pngbin32836 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.pngbin2582 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.pngbin3131 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.pngbin33309 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.pngbin1676 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.pngbin33309 -> 0 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json102
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.pngbin0 -> 13558 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.pngbin0 -> 17911 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.pngbin0 -> 23513 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.pngbin0 -> 53789 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.pngbin0 -> 51462 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.pngbin0 -> 18386 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.pngbin0 -> 61558 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.pngbin0 -> 56053 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.pngbin0 -> 53983 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.pngbin0 -> 18753 bytes
-rw-r--r--misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.pngbin0 -> 62336 bytes
-rw-r--r--misc/dist/linux/godot.62
-rwxr-xr-xmisc/travis/scons-local-osx.sh4
-rw-r--r--modules/bullet/area_bullet.cpp24
-rw-r--r--modules/bullet/area_bullet.h4
-rw-r--r--modules/bullet/collision_object_bullet.cpp10
-rw-r--r--modules/bullet/godot_result_callbacks.cpp34
-rw-r--r--modules/bullet/godot_result_callbacks.h5
-rw-r--r--modules/bullet/shape_bullet.cpp58
-rw-r--r--modules/bullet/shape_bullet.h7
-rw-r--r--modules/bullet/space_bullet.cpp20
-rw-r--r--modules/csg/SCsub9
-rw-r--r--modules/csg/config.py5
-rw-r--r--modules/csg/csg.cpp1488
-rw-r--r--modules/csg/csg.h206
-rw-r--r--modules/csg/csg_gizmos.cpp315
-rw-r--r--modules/csg/csg_gizmos.h30
-rw-r--r--modules/csg/csg_shape.cpp2125
-rw-r--r--modules/csg/csg_shape.h360
-rw-r--r--modules/csg/register_types.cpp59
-rw-r--r--modules/csg/register_types.h32
-rw-r--r--modules/enet/networked_multiplayer_enet.cpp240
-rw-r--r--modules/enet/networked_multiplayer_enet.h9
-rw-r--r--modules/gdnative/SCsub4
-rw-r--r--modules/gdnative/doc_classes/GDNativeLibrary.xml8
-rw-r--r--modules/gdnative/gdnative.cpp231
-rw-r--r--modules/gdnative/gdnative.h6
-rw-r--r--modules/gdnative/gdnative_api.json17
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h9
-rw-r--r--modules/gdnative/nativescript/godot_nativescript.cpp22
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp89
-rw-r--r--modules/gdnative/nativescript/nativescript.h6
-rw-r--r--modules/gdnative/pluginscript/register_types.cpp4
-rw-r--r--modules/gdscript/gdscript_editor.cpp69
-rw-r--r--modules/gdscript/gdscript_function.cpp4
-rw-r--r--modules/gdscript/gdscript_highlighter.cpp262
-rw-r--r--modules/gdscript/gdscript_highlighter.h56
-rw-r--r--modules/gdscript/gdscript_parser.cpp70
-rw-r--r--modules/gdscript/register_types.cpp2
-rw-r--r--modules/gridmap/grid_map_editor_plugin.cpp16
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.cpp24
-rwxr-xr-xmodules/mbedtls/stream_peer_mbed_tls.h2
-rw-r--r--modules/mono/config.py42
-rw-r--r--modules/mono/csharp_script.cpp41
-rw-r--r--modules/mono/editor/bindings_generator.cpp169
-rw-r--r--modules/mono/editor/bindings_generator.h43
-rw-r--r--modules/mono/editor/godotsharp_builds.cpp13
-rw-r--r--modules/mono/editor/mono_bottom_panel.cpp32
-rw-r--r--modules/mono/glue/cs_files/AABB.cs102
-rw-r--r--modules/mono/glue/cs_files/Basis.cs145
-rw-r--r--modules/mono/glue/cs_files/Color.cs96
-rw-r--r--modules/mono/glue/cs_files/DebuggingUtils.cs4
-rw-r--r--modules/mono/glue/cs_files/GD.cs12
-rw-r--r--modules/mono/glue/cs_files/GodotMethodAttribute.cs2
-rw-r--r--modules/mono/glue/cs_files/GodotSynchronizationContext.cs1
-rw-r--r--modules/mono/glue/cs_files/GodotTaskScheduler.cs2
-rw-r--r--modules/mono/glue/cs_files/IAwaiter.cs1
-rw-r--r--modules/mono/glue/cs_files/MarshalUtils.cs6
-rw-r--r--modules/mono/glue/cs_files/Mathf.cs104
-rw-r--r--modules/mono/glue/cs_files/MathfEx.cs39
-rw-r--r--modules/mono/glue/cs_files/Plane.cs17
-rw-r--r--modules/mono/glue/cs_files/Quat.cs49
-rw-r--r--modules/mono/glue/cs_files/Rect2.cs53
-rw-r--r--modules/mono/glue/cs_files/SignalAttribute.cs3
-rw-r--r--modules/mono/glue/cs_files/SignalAwaiter.cs12
-rw-r--r--modules/mono/glue/cs_files/StringExtensions.cs76
-rw-r--r--modules/mono/glue/cs_files/Transform.cs21
-rw-r--r--modules/mono/glue/cs_files/Transform2D.cs51
-rw-r--r--modules/mono/glue/cs_files/Vector2.cs78
-rw-r--r--modules/mono/glue/cs_files/Vector3.cs73
-rw-r--r--modules/mono/mono_reg_utils.py2
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp2
-rw-r--r--modules/pvr/texture_loader_pvr.cpp4
-rw-r--r--modules/theora/video_stream_theora.cpp20
-rw-r--r--modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml2
-rw-r--r--modules/visual_script/doc_classes/VisualScriptMathConstant.xml4
-rw-r--r--modules/visual_script/register_types.cpp2
-rw-r--r--modules/visual_script/visual_script.cpp1
-rw-r--r--modules/visual_script/visual_script.h2
-rw-r--r--modules/visual_script/visual_script_editor.cpp6
-rw-r--r--modules/visual_script/visual_script_editor.h3
-rw-r--r--modules/visual_script/visual_script_expression.cpp13
-rw-r--r--modules/webm/libvpx/SCsub2
-rw-r--r--modules/websocket/emws_peer.cpp6
-rw-r--r--modules/websocket/emws_server.cpp13
-rw-r--r--modules/websocket/emws_server.h3
-rw-r--r--modules/websocket/lws_client.cpp20
-rw-r--r--modules/websocket/lws_peer.cpp44
-rw-r--r--modules/websocket/lws_server.cpp18
-rw-r--r--modules/websocket/lws_server.h3
-rw-r--r--modules/websocket/websocket_client.cpp16
-rw-r--r--modules/websocket/websocket_client.h4
-rw-r--r--modules/websocket/websocket_peer.cpp2
-rw-r--r--modules/websocket/websocket_server.cpp3
-rw-r--r--modules/websocket/websocket_server.h4
-rw-r--r--platform/android/export/export.cpp16
-rw-r--r--platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java12
-rw-r--r--platform/android/java/src/org/godotengine/godot/GodotView.java4
-rw-r--r--platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java14
-rw-r--r--platform/android/java_glue.cpp27
-rw-r--r--platform/android/os_android.cpp49
-rw-r--r--platform/android/os_android.h1
-rw-r--r--platform/iphone/SCsub7
-rw-r--r--platform/iphone/app_delegate.h1
-rw-r--r--platform/iphone/app_delegate.mm182
-rw-r--r--platform/iphone/export/export.cpp67
-rw-r--r--platform/iphone/gl_view.h2
-rw-r--r--platform/iphone/gl_view.mm24
-rw-r--r--platform/iphone/in_app_store.mm1
-rw-r--r--platform/iphone/os_iphone.cpp51
-rw-r--r--platform/iphone/os_iphone.h7
-rw-r--r--platform/iphone/power_iphone.cpp2
-rw-r--r--platform/javascript/detect.py27
-rw-r--r--platform/javascript/engine.js40
-rw-r--r--platform/javascript/http_client_javascript.cpp2
-rw-r--r--platform/javascript/os_javascript.cpp35
-rw-r--r--platform/javascript/pre.js2
-rw-r--r--platform/osx/detect.py1
-rw-r--r--platform/osx/godot_main_osx.mm2
-rw-r--r--platform/osx/os_osx.mm118
-rw-r--r--platform/uwp/app.cpp81
-rw-r--r--platform/uwp/app.h1
-rw-r--r--platform/uwp/detect.py2
-rw-r--r--platform/uwp/export/export.cpp8
-rw-r--r--platform/windows/os_windows.cpp68
-rw-r--r--platform/x11/detect.py4
-rw-r--r--platform/x11/os_x11.cpp29
-rw-r--r--platform/x11/os_x11.h1
-rw-r--r--scene/2d/animated_sprite.cpp4
-rw-r--r--scene/2d/animated_sprite.h1
-rw-r--r--scene/2d/back_buffer_copy.cpp4
-rw-r--r--scene/2d/back_buffer_copy.h1
-rw-r--r--scene/2d/canvas_item.cpp30
-rw-r--r--scene/2d/canvas_item.h52
-rw-r--r--scene/2d/collision_polygon_2d.cpp4
-rw-r--r--scene/2d/collision_polygon_2d.h1
-rw-r--r--scene/2d/collision_shape_2d.cpp5
-rw-r--r--scene/2d/collision_shape_2d.h1
-rw-r--r--scene/2d/light_2d.cpp4
-rw-r--r--scene/2d/light_2d.h1
-rw-r--r--scene/2d/light_occluder_2d.cpp6
-rw-r--r--scene/2d/light_occluder_2d.h2
-rw-r--r--scene/2d/line_2d.cpp7
-rw-r--r--scene/2d/line_2d.h1
-rw-r--r--scene/2d/node_2d.cpp54
-rw-r--r--scene/2d/node_2d.h8
-rw-r--r--scene/2d/parallax_layer.cpp2
-rw-r--r--scene/2d/path_2d.cpp7
-rw-r--r--scene/2d/path_2d.h1
-rw-r--r--scene/2d/polygon_2d.cpp174
-rw-r--r--scene/2d/polygon_2d.h25
-rw-r--r--scene/2d/position_2d.cpp4
-rw-r--r--scene/2d/position_2d.h1
-rw-r--r--scene/2d/ray_cast_2d.cpp10
-rw-r--r--scene/2d/screen_button.cpp4
-rw-r--r--scene/2d/screen_button.h1
-rw-r--r--scene/2d/skeleton_2d.cpp86
-rw-r--r--scene/2d/skeleton_2d.h11
-rw-r--r--scene/2d/sprite.cpp32
-rw-r--r--scene/2d/sprite.h4
-rw-r--r--scene/2d/tile_map.cpp65
-rw-r--r--scene/2d/tile_map.h1
-rw-r--r--scene/2d/visibility_notifier_2d.cpp4
-rw-r--r--scene/2d/visibility_notifier_2d.h1
-rw-r--r--scene/3d/camera.h4
-rw-r--r--scene/3d/collision_polygon.cpp5
-rw-r--r--scene/3d/collision_polygon.h2
-rw-r--r--scene/3d/particles.cpp35
-rw-r--r--scene/3d/path.cpp5
-rw-r--r--scene/3d/physics_body.cpp1111
-rw-r--r--scene/3d/physics_body.h264
-rw-r--r--scene/3d/ray_cast.cpp10
-rw-r--r--scene/3d/skeleton.cpp113
-rw-r--r--scene/3d/skeleton.h32
-rw-r--r--scene/3d/spatial.cpp23
-rw-r--r--scene/3d/spatial.h7
-rw-r--r--scene/3d/vehicle_body.cpp12
-rw-r--r--scene/3d/vehicle_body.h2
-rw-r--r--scene/3d/voxel_light_baker.cpp4
-rw-r--r--scene/animation/animation_player.cpp35
-rw-r--r--scene/animation/animation_player.h1
-rw-r--r--scene/animation/animation_tree_player.cpp9
-rw-r--r--scene/gui/base_button.cpp2
-rw-r--r--scene/gui/color_picker.cpp9
-rw-r--r--scene/gui/color_picker.h2
-rw-r--r--scene/gui/control.cpp41
-rw-r--r--scene/gui/control.h8
-rw-r--r--scene/gui/grid_container.cpp40
-rw-r--r--scene/gui/grid_container.h1
-rw-r--r--scene/gui/item_list.cpp26
-rw-r--r--scene/gui/item_list.h4
-rw-r--r--scene/gui/line_edit.cpp80
-rw-r--r--scene/gui/line_edit.h7
-rw-r--r--scene/gui/menu_button.cpp2
-rw-r--r--scene/gui/option_button.cpp4
-rw-r--r--scene/gui/popup_menu.cpp149
-rw-r--r--scene/gui/popup_menu.h18
-rw-r--r--scene/gui/rich_text_label.cpp114
-rw-r--r--scene/gui/rich_text_label.h4
-rw-r--r--scene/gui/scroll_bar.cpp16
-rw-r--r--scene/gui/scroll_container.cpp78
-rw-r--r--scene/gui/scroll_container.h6
-rw-r--r--scene/gui/tab_container.cpp161
-rw-r--r--scene/gui/tab_container.h12
-rw-r--r--scene/gui/tabs.cpp118
-rw-r--r--scene/gui/tabs.h7
-rw-r--r--scene/gui/text_edit.cpp635
-rw-r--r--scene/gui/text_edit.h170
-rw-r--r--scene/gui/texture_progress.cpp72
-rw-r--r--scene/gui/texture_progress.h12
-rw-r--r--scene/gui/tree.cpp16
-rw-r--r--scene/gui/video_player.cpp15
-rw-r--r--scene/main/canvas_layer.cpp40
-rw-r--r--scene/main/canvas_layer.h7
-rw-r--r--scene/main/node.cpp287
-rw-r--r--scene/main/node.h21
-rw-r--r--scene/main/scene_tree.cpp501
-rw-r--r--scene/main/scene_tree.h46
-rwxr-xr-xscene/main/timer.cpp7
-rwxr-xr-xscene/main/timer.h2
-rw-r--r--scene/main/viewport.cpp40
-rw-r--r--scene/main/viewport.h6
-rw-r--r--scene/register_scene_types.cpp4
-rw-r--r--scene/resources/curve.cpp166
-rw-r--r--scene/resources/curve.h4
-rw-r--r--scene/resources/default_theme/default_theme.cpp8
-rw-r--r--scene/resources/material.cpp6
-rw-r--r--scene/resources/material.h3
-rw-r--r--scene/resources/mesh.cpp7
-rw-r--r--scene/resources/packed_scene.cpp2
-rw-r--r--scene/resources/primitive_meshes.cpp18
-rw-r--r--scene/resources/primitive_meshes.h4
-rw-r--r--scene/resources/scene_format_text.cpp13
-rw-r--r--scene/resources/style_box.cpp6
-rw-r--r--scene/resources/surface_tool.cpp10
-rw-r--r--scene/resources/surface_tool.h2
-rw-r--r--scene/resources/tile_set.cpp27
-rw-r--r--scene/resources/tile_set.h8
-rw-r--r--servers/arvr_server.cpp2
-rw-r--r--servers/audio_server.cpp30
-rw-r--r--servers/audio_server.h7
-rw-r--r--servers/physics/body_pair_sw.cpp1
-rw-r--r--servers/physics/collision_object_sw.h3
-rw-r--r--servers/physics_2d/body_pair_2d_sw.cpp1
-rw-r--r--servers/physics_2d/collision_object_2d_sw.cpp28
-rw-r--r--servers/physics_2d/collision_object_2d_sw.h5
-rw-r--r--servers/physics_2d/shape_2d_sw.cpp4
-rw-r--r--servers/physics_2d/space_2d_sw.cpp1
-rw-r--r--servers/visual/rasterizer.h7
-rw-r--r--servers/visual/shader_language.cpp82
-rw-r--r--servers/visual/shader_language.h2
-rw-r--r--servers/visual/shader_types.cpp9
-rw-r--r--servers/visual/visual_server_canvas.cpp6
-rw-r--r--servers/visual/visual_server_canvas.h4
-rw-r--r--servers/visual/visual_server_raster.h5
-rw-r--r--servers/visual/visual_server_viewport.cpp36
-rw-r--r--servers/visual/visual_server_viewport.h3
-rw-r--r--servers/visual/visual_server_wrap_mt.h5
-rw-r--r--servers/visual_server.cpp5
-rw-r--r--servers/visual_server.h5
-rw-r--r--thirdparty/README.md22
-rw-r--r--thirdparty/fonts/Hack_Regular.ttfbin307420 -> 309408 bytes
-rw-r--r--thirdparty/fonts/LICENSE_Hack.md4
-rw-r--r--thirdparty/fonts/NotoSansUI_Bold.ttfbin0 -> 311496 bytes
-rw-r--r--thirdparty/glad/KHR/khrplatform.h16
-rw-r--r--thirdparty/glad/glad.c27
-rw-r--r--thirdparty/glad/glad/glad.h4
-rw-r--r--thirdparty/lws/client/client.c20
-rw-r--r--thirdparty/lws/client/ssl-client.c14
-rw-r--r--thirdparty/lws/context.c2
-rw-r--r--thirdparty/lws/libwebsockets.c11
-rw-r--r--thirdparty/lws/libwebsockets.h7
-rw-r--r--thirdparty/lws/lws_config.h36
-rw-r--r--thirdparty/lws/mbedtls_verify.diff74
-rw-r--r--thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h1
-rw-r--r--thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h4
-rw-r--r--thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c31
-rw-r--r--thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c79
-rw-r--r--thirdparty/lws/misc/lejp.c2
-rw-r--r--thirdparty/lws/misc/sha-1.c2
-rw-r--r--thirdparty/lws/output.c10
-rw-r--r--thirdparty/lws/pollfd.c7
-rw-r--r--thirdparty/lws/private-libwebsockets.h5
-rw-r--r--thirdparty/lws/server/ssl-server.c2
-rw-r--r--thirdparty/lws/service.c17
-rw-r--r--thirdparty/lws/ssl.c15
-rw-r--r--thirdparty/mbedtls/1453.diff120
-rw-r--r--thirdparty/mbedtls/include/mbedtls/asn1.h15
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ccm.h5
-rw-r--r--thirdparty/mbedtls/include/mbedtls/check_config.h4
-rw-r--r--thirdparty/mbedtls/include/mbedtls/config.h30
-rw-r--r--thirdparty/mbedtls/include/mbedtls/dhm.h2
-rw-r--r--thirdparty/mbedtls/include/mbedtls/md2.h44
-rw-r--r--thirdparty/mbedtls/include/mbedtls/md4.h46
-rw-r--r--thirdparty/mbedtls/include/mbedtls/md5.h46
-rw-r--r--thirdparty/mbedtls/include/mbedtls/oid.h18
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ripemd160.h43
-rw-r--r--thirdparty/mbedtls/include/mbedtls/rsa.h12
-rw-r--r--thirdparty/mbedtls/include/mbedtls/sha1.h46
-rw-r--r--thirdparty/mbedtls/include/mbedtls/sha256.h50
-rw-r--r--thirdparty/mbedtls/include/mbedtls/sha512.h49
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ssl.h18
-rw-r--r--thirdparty/mbedtls/include/mbedtls/ssl_internal.h3
-rw-r--r--thirdparty/mbedtls/include/mbedtls/version.h8
-rw-r--r--thirdparty/mbedtls/library/aes.c4
-rw-r--r--thirdparty/mbedtls/library/bignum.c2
-rw-r--r--thirdparty/mbedtls/library/ctr_drbg.c2
-rw-r--r--thirdparty/mbedtls/library/debug.c2
-rw-r--r--thirdparty/mbedtls/library/entropy_poll.c16
-rw-r--r--thirdparty/mbedtls/library/md2.c40
-rw-r--r--thirdparty/mbedtls/library/md4.c41
-rw-r--r--thirdparty/mbedtls/library/md5.c41
-rw-r--r--thirdparty/mbedtls/library/memory_buffer_alloc.c41
-rw-r--r--thirdparty/mbedtls/library/net_sockets.c9
-rw-r--r--thirdparty/mbedtls/library/oid.c45
-rw-r--r--thirdparty/mbedtls/library/pem.c5
-rw-r--r--thirdparty/mbedtls/library/pkcs5.c4
-rw-r--r--thirdparty/mbedtls/library/pkparse.c73
-rw-r--r--thirdparty/mbedtls/library/platform.c2
-rw-r--r--thirdparty/mbedtls/library/ripemd160.c41
-rw-r--r--thirdparty/mbedtls/library/rsa.c124
-rw-r--r--thirdparty/mbedtls/library/sha1.c41
-rw-r--r--thirdparty/mbedtls/library/sha256.c43
-rw-r--r--thirdparty/mbedtls/library/sha512.c43
-rw-r--r--thirdparty/mbedtls/library/ssl_cli.c23
-rw-r--r--thirdparty/mbedtls/library/ssl_tls.c50
-rw-r--r--thirdparty/mbedtls/library/version.c2
-rw-r--r--thirdparty/mbedtls/library/version_features.c3
-rw-r--r--thirdparty/mbedtls/library/x509_crl.c65
-rw-r--r--thirdparty/mbedtls/library/x509_crt.c27
694 files changed, 34160 insertions, 8258 deletions
diff --git a/.travis.yml b/.travis.yml
index 4bad241c5..57580c1c4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,8 @@ env:
global:
- SCONS_CACHE=$HOME/.scons_cache
- SCONS_CACHE_LIMIT=1024
- - OPTIONS="verbose=yes progress=no openmp=no gdnative_wrapper=yes"
+ - OPTIONS="verbose=yes progress=no gdnative_wrapper=yes"
+ - secure: AnjB84ZZYDxDQdWwsT9PJrD5tEnwcauzvVeSG+us2GxgfyJ2HFArkZ/IjlvbsfYIiiHghJ2Ssy9yPtUT921BtNNHoWuN/8YQziGrlwiSfLGmS/n8GFH22OYwzDSa7UC7ODts5La2I4+JzrdtF933TwE+4QzH4E3GyaKbznh402E=
cache:
directories:
@@ -18,7 +19,7 @@ matrix:
include:
- env: STATIC_CHECKS=yes
os: linux
- compiler: clang
+ compiler: gcc
- env: GODOT_TARGET=x11 TOOLS=yes CACHE_NAME=${GODOT_TARGET}-gcc-tools"
os: linux
compiler: gcc
@@ -31,12 +32,14 @@ matrix:
- env: GODOT_TARGET=android TOOLS=no CACHE_NAME=${GODOT_TARGET}-gcc
os: linux
compiler: gcc
- #- env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-clang-tools
- # os: osx
- # compiler: clang
- #- env: GODOT_TARGET=iphone TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang
- # os: osx
- # compiler: clang
+ - env: GODOT_TARGET=osx TOOLS=yes CACHE_NAME=${GODOT_TARGET}-clang-tools
+ os: osx
+ osx_image: xcode9.3
+ compiler: clang
+ - env: GODOT_TARGET=iphone TOOLS=no CACHE_NAME=${GODOT_TARGET}-clang
+ os: osx
+ osx_image: xcode9.3
+ compiler: clang
addons:
apt:
@@ -69,6 +72,25 @@ addons:
# For style checks.
- clang-format-5.0
+ coverity_scan:
+ project:
+ name: "godotengine/godot"
+ description: "Godot Engine Coverity scans"
+ notification_email: coverity@godotengine.org
+ build_command_prepend: ""
+ build_command: "scons p=x11 -j2 $OPTIONS"
+ branch_pattern: coverity_scan
+
+before_install:
+ - if [ "$STATIC_CHECKS" = "yes" ]; then
+ unset SCONS_CACHE;
+ else
+ if [ "$TRAVIS_BRANCH" = "coverity_scan" ]; then
+ echo "This job runs in the Coverity Scan branch and is not the STATIC_CHECKS job meant for it, so aborting with exit code 0.";
+ travis_terminate 0;
+ fi;
+ fi
+
install:
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$GODOT_TARGET" = "android" ]; then
misc/travis/android-tools-linux.sh;
diff --git a/AUTHORS.md b/AUTHORS.md
index 3d7a6adf6..67563298f 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -97,6 +97,7 @@ name is available.
Ramesh Ravone (RameshRavone)
Ray Koopa (RayKoopa)
Rémi Verschelde (akien-mga)
+ Roberto F. Arroyo (robfram)
Ruslan Mustakov (endragor)
Saniko (sanikoyes)
SaracenOne
@@ -108,6 +109,7 @@ name is available.
Wilhem Barbier (nounoursheureux)
Will Nations (willnationsdev)
Wilson E. Alvarez (Rubonnek)
+ Xavier Cho (mysticfall)
Yuri Roubinski (Chaosus)
Zher Huei Lee (leezh)
ZuBsPaCe
@@ -117,7 +119,7 @@ name is available.
m4nu3lf
marynate
mrezai
- rraallvv
romulox-x
+ rraallvv
sersoong
yg2f (SuperUserNameMan)
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 0f421741b..6b6ee4c50 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -157,7 +157,7 @@ License: OFL-1.1
Files: ./thirdparty/fonts/Hack_Regular.ttf
Comment: Hack font
-Copyright: 2017, Source Foundry Authors
+Copyright: 2018, Source Foundry Authors
2003, Bitstream Inc.
License: Expat and Bitstream Vera Fonts Copyright
diff --git a/DONORS.md b/DONORS.md
index 52c8da4c0..c0ab24390 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -27,12 +27,14 @@ generous deed immortalized in the next stable release of Godot Engine.
Christian Uldall Pedersen
Christopher Igoe
Christoph Woinke
- codetrotter
+ Claudiu Dumitru
E Hewert
Hein-Pieter van Braam
+ Igors Vaitkus
Jamal Alyafei
Jay Sistar
Matthieu Huvé
+ Mike King
Nathan Warden
Neal Gompa (Conan Kudo)
Pascal Julien
@@ -50,6 +52,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Asdf
cheese65536
Jake Bo
+ Kris Michael
Manuele Finocchiaro
Officine Pixel S.n.c.
Rémi Verschelde
@@ -58,20 +61,24 @@ generous deed immortalized in the next stable release of Godot Engine.
Allen Schade
Andreas Schüle
Austen McRae
- Benjamin Botwin
Bernhard Liebl
+ Catalin Moldovan
+ DeepSquid
+ Duane Johnson
+ Florian Breisch
+ Gary Oberbrunner
Johannes Wuensch
Josep G. Camarasa
- Kris Michael
+ Joshua Lesperance
Libre-Dépanne
- Mike King
+ Matthew Bennett
+ Paul LaMotte
Ranoller
- Rob Messick
Svenne Krap
Timothy Hagberg
BanjoNode2D
- Brandon
+ Beliar
Chris Serino
Conrad Curry
Craig Smith
@@ -80,54 +87,62 @@ generous deed immortalized in the next stable release of Godot Engine.
Dexter Miguel
Garrett Dockins
Guilherme Felipe de C. G. da Silva
- Harman Bains
- Henrique Alves
- Karsten Bock
+ John
+ Justo Delgado Baudí
Laurence Bannister
Rami
Robert Willes
Robin Arys
+ Rufus Xavier Sarsaparilla
ScottMakesGames
Testus Maximus
Thomas Bjarnelöf
+ William Connell
Wojciech Chojnacki
Xavier Tan
Zaq Poi
+ Alessandra Pereyra
Alexey Dyadchenko
Amanda Haldy
- Arnaud Verstuyf
Chris Brown
+ Chris Petrich
Chris Wilson
Cody Parker
D
Daniel Eliasinski
+ E.G.
Eric Monson
flesk
François Cantin
G Barnes
GGGames.org
+ Giovanni Solimeno
+ Hasen Judy
Heath Hayes
Jeppe Zapp
Jeremi Biernacki
joe513
+ John O'Mahoney
Jordan M Lucas
Juraj Móza
Justin Arnold
- Justo Delgado Baudí
Leandro Voltolino
Lisandro Lorea
Markus Wiesner
- Marty Plumbo
Marvin
+ Mohammad Taleb
Nick Nikitin
Pablo Cholaky
Patrick Schnorbus
Pete Goodwin
Phyronnaz
+ SeokHui Lee
+ Simon De Greve
+ Sofox
Ted
- Travis Womack
Trent McPheron
+ Vladimir
## Silver donors
@@ -137,52 +152,58 @@ generous deed immortalized in the next stable release of Godot Engine.
Alder Stefano
Alessandro Senese
Álvaro Domínguez López
- Andrea Badii
- Andrew Thomas
+ Anders Jensen-Urstad
Anthony Bongiovanni
Arda Erol
Arthur S. Muszynski
+ Aubrey Falconer
Avencherus
Bastian Böhm
Benedikt
Benjamin Beshara
Ben Vercammen
Blair Allen
- Bryan Crow
- Bryanna M
+ Brandon
Bryan Stevenson
Carwyn Edwards
Casey Foote
Chris Chapin
Christian Baune
Christian Winter
+ Christopher Schmitt
Collin Shooltz
Daniel Egger
Daniel Kaplan
- Daniel Langegger
+ DanielMaximiano
Daniel Mircea
+ David
David Cravens
David May
Dominik Wetzel
+ Edward Herbert
Eric Martini
Fabian Becker
fengjiongmax
Francesco Lisi
+ Frédéric Alix
G3Dev sàrl
Geequlim
Gerrit Großkopf
Gerrit Procee
Gilberto K. Otubo
Guldoman
- HeartBeast
Heribert Hirth
+ hubert jenkins
Hunter Jones
ialex32x
+ Ivan Vodopiviz
+ Jahn Johansen
Jaime Ruiz-Borau Vizárraga
- Jed Rose
+ Jed
Jeff Hungerford
Joel Fivat
- Johannes du Randt
+ Johan Lindberg
+ Jonas Rudlang
Jonas Yamazaki
Jonathan Martin
Jonathan Nieto
@@ -197,11 +218,12 @@ generous deed immortalized in the next stable release of Godot Engine.
Kevin Boyer
Kevin Kamper Meejach Petersen
Klavdij Voncina
+ Krzysztof Jankowski
Lars pfeffer
Linus Lind Lundgren
+ Macil
magodev
Martin Eigel
- Martin Novák
Matthew Fitzpatrick
Matthias Hölzl
Max R.R. Collada
@@ -216,12 +238,15 @@ generous deed immortalized in the next stable release of Godot Engine.
Moritz Laass
nee
Neil Blakey-Milner
+ Nick Pavlica
Niclas Eriksen
- Nik Lee
+ Nicolás Montaña
+ Nicolas SAN AGUSTIN
Niko Leopold
nivardus
Noi Sek
Oleg Tyshchenko
+ Oleksandr Yemets
Pablo Seibelt
Pan Ip
Pat LaBine
@@ -229,15 +254,15 @@ generous deed immortalized in the next stable release of Godot Engine.
Patric Vormstein
Paul Mason
Paweł Kowal
- Pedro Luz
Pierre-Igor Berthet
Pietro Vertechi
Piotr Kaczmarski
Richman Stewart
- Rodolfo Baeza
Roger Burgess
Roger Smith
Roman Tinkov
+ Ryan Whited
+ Samuel El-Borai
Sasori Olkof
Scott D. Yelich
Sootstone
@@ -245,10 +270,15 @@ generous deed immortalized in the next stable release of Godot Engine.
Thibault Barbaroux
Thomas Bell
Thomas Herzog & Xananax
+ Thomas Kurz
+ Tomasz Wacławek
Tom Larrow
Tyler Stafos
UltyX
- Victor Holt
+ Victor
+ Victor Gonzalez Fernandez
+ Viktor Ferenczi
+ werner mendizabal
Wout Standaert
Yu He
diff --git a/README.md b/README.md
index 6bb07a8c4..8ad738c23 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ Before being open sourced in February 2014, Godot had been developed by Juan
Linietsky and Ariel Manzur (both still maintaining the project) for several
years as an in-house engine, used to publish several work-for-hire titles.
-![Screenshot of a 3D scene in Godot Engine](http://download.tuxfamily.org/godotengine/media/screenshots/editor_3d_fracteed.jpg)
+![Screenshot of a 3D scene in Godot Engine](https://download.tuxfamily.org/godotengine/media/screenshots/editor_3d_fracteed.jpg)
### Getting the engine
diff --git a/core/array.cpp b/core/array.cpp
index 0ddac1662..9e3250fd4 100644
--- a/core/array.cpp
+++ b/core/array.cpp
@@ -35,8 +35,8 @@
#include "variant.h"
#include "vector.h"
-struct ArrayPrivate {
-
+class ArrayPrivate {
+public:
SafeRefCount refcount;
Vector<Variant> array;
};
@@ -211,13 +211,13 @@ const Variant &Array::get(int p_idx) const {
return operator[](p_idx);
}
-Array Array::duplicate() const {
+Array Array::duplicate(bool p_deep) const {
Array new_arr;
int element_count = size();
new_arr.resize(element_count);
for (int i = 0; i < element_count; i++) {
- new_arr[i] = get(i);
+ new_arr[i] = p_deep ? get(i).duplicate(p_deep) : get(i);
}
return new_arr;
diff --git a/core/array.h b/core/array.h
index 684a8e265..e549a886e 100644
--- a/core/array.h
+++ b/core/array.h
@@ -88,7 +88,7 @@ public:
Variant pop_back();
Variant pop_front();
- Array duplicate() const;
+ Array duplicate(bool p_deep = false) const;
Array(const Array &p_from);
Array();
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index 20adf7ff0..a51f2397c 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -288,6 +288,10 @@ void _OS::set_window_size(const Size2 &p_size) {
OS::get_singleton()->set_window_size(p_size);
}
+Rect2 _OS::get_window_safe_area() const {
+ return OS::get_singleton()->get_window_safe_area();
+}
+
void _OS::set_window_fullscreen(bool p_enabled) {
OS::get_singleton()->set_window_fullscreen(p_enabled);
}
@@ -635,8 +639,8 @@ uint64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const {
unsigned int second = ((datetime.has(SECOND_KEY)) ? static_cast<unsigned int>(datetime[SECOND_KEY]) : 0);
unsigned int minute = ((datetime.has(MINUTE_KEY)) ? static_cast<unsigned int>(datetime[MINUTE_KEY]) : 0);
unsigned int hour = ((datetime.has(HOUR_KEY)) ? static_cast<unsigned int>(datetime[HOUR_KEY]) : 0);
- unsigned int day = ((datetime.has(DAY_KEY)) ? static_cast<unsigned int>(datetime[DAY_KEY]) : 0);
- unsigned int month = ((datetime.has(MONTH_KEY)) ? static_cast<unsigned int>(datetime[MONTH_KEY]) - 1 : 0);
+ unsigned int day = ((datetime.has(DAY_KEY)) ? static_cast<unsigned int>(datetime[DAY_KEY]) : 1);
+ unsigned int month = ((datetime.has(MONTH_KEY)) ? static_cast<unsigned int>(datetime[MONTH_KEY]) : 1);
unsigned int year = ((datetime.has(YEAR_KEY)) ? static_cast<unsigned int>(datetime[YEAR_KEY]) : 0);
/// How many days come before each month (0-12)
@@ -656,15 +660,15 @@ uint64_t _OS::get_unix_time_from_datetime(Dictionary datetime) const {
ERR_EXPLAIN("Invalid hour value of: " + itos(hour));
ERR_FAIL_COND_V(hour > 23, 0);
- ERR_EXPLAIN("Invalid month value of: " + itos(month + 1));
- ERR_FAIL_COND_V(month + 1 > 12, 0);
+ ERR_EXPLAIN("Invalid month value of: " + itos(month));
+ ERR_FAIL_COND_V(month > 12 || month == 0, 0);
// Do this check after month is tested as valid
- ERR_EXPLAIN("Invalid day value of: " + itos(day) + " which is larger than " + itos(MONTH_DAYS_TABLE[LEAPYEAR(year)][month]));
- ERR_FAIL_COND_V(day > MONTH_DAYS_TABLE[LEAPYEAR(year)][month], 0);
+ ERR_EXPLAIN("Invalid day value of: " + itos(day) + " which is larger than " + itos(MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1]) + " or 0");
+ ERR_FAIL_COND_V(day > MONTH_DAYS_TABLE[LEAPYEAR(year)][month - 1] || day == 0, 0);
// Calculate all the seconds from months past in this year
- uint64_t SECONDS_FROM_MONTHS_PAST_THIS_YEAR = DAYS_PAST_THIS_YEAR_TABLE[LEAPYEAR(year)][month] * SECONDS_PER_DAY;
+ uint64_t SECONDS_FROM_MONTHS_PAST_THIS_YEAR = DAYS_PAST_THIS_YEAR_TABLE[LEAPYEAR(year)][month - 1] * SECONDS_PER_DAY;
uint64_t SECONDS_FROM_YEARS_PAST = 0;
for (unsigned int iyear = EPOCH_YR; iyear < year; iyear++) {
@@ -1032,9 +1036,9 @@ void _OS::_bind_methods() {
//ClassDB::bind_method(D_METHOD("get_fullscreen_mode_list","screen"),&_OS::get_fullscreen_mode_list,DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_video_driver_count"), &_OS::get_video_driver_count);
- ClassDB::bind_method(D_METHOD("get_video_driver_name"), &_OS::get_video_driver_name);
+ ClassDB::bind_method(D_METHOD("get_video_driver_name", "driver"), &_OS::get_video_driver_name);
ClassDB::bind_method(D_METHOD("get_audio_driver_count"), &_OS::get_audio_driver_count);
- ClassDB::bind_method(D_METHOD("get_audio_driver_name"), &_OS::get_audio_driver_name);
+ ClassDB::bind_method(D_METHOD("get_audio_driver_name", "driver"), &_OS::get_audio_driver_name);
ClassDB::bind_method(D_METHOD("get_screen_count"), &_OS::get_screen_count);
ClassDB::bind_method(D_METHOD("get_current_screen"), &_OS::get_current_screen);
@@ -1046,6 +1050,7 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_window_position", "position"), &_OS::set_window_position);
ClassDB::bind_method(D_METHOD("get_window_size"), &_OS::get_window_size);
ClassDB::bind_method(D_METHOD("set_window_size", "size"), &_OS::set_window_size);
+ ClassDB::bind_method(D_METHOD("get_window_safe_area"), &_OS::get_window_safe_area);
ClassDB::bind_method(D_METHOD("set_window_fullscreen", "enabled"), &_OS::set_window_fullscreen);
ClassDB::bind_method(D_METHOD("is_window_fullscreen"), &_OS::is_window_fullscreen);
ClassDB::bind_method(D_METHOD("set_window_resizable", "enabled"), &_OS::set_window_resizable);
@@ -2408,7 +2413,7 @@ _Thread::_Thread() {
_Thread::~_Thread() {
if (active) {
- ERR_EXPLAIN("Reference to a Thread object object was lost while the thread is still running..");
+ ERR_EXPLAIN("Reference to a Thread object object was lost while the thread is still running...");
}
ERR_FAIL_COND(active == true);
}
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index c40abd51f..1790c6875 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -162,6 +162,7 @@ public:
virtual void set_window_position(const Point2 &p_position);
virtual Size2 get_window_size() const;
virtual Size2 get_real_window_size() const;
+ virtual Rect2 get_window_safe_area() const;
virtual void set_window_size(const Size2 &p_size);
virtual void set_window_fullscreen(bool p_enabled);
virtual bool is_window_fullscreen() const;
diff --git a/core/class_db.cpp b/core/class_db.cpp
index 291dc87e1..59b100e28 100644
--- a/core/class_db.cpp
+++ b/core/class_db.cpp
@@ -254,11 +254,13 @@ HashMap<StringName, StringName, StringNameHasher> ClassDB::compat_classes;
ClassDB::ClassInfo::ClassInfo() {
+ api = API_NONE;
creation_func = NULL;
inherits_ptr = NULL;
disabled = false;
exposed = false;
}
+
ClassDB::ClassInfo::~ClassInfo() {
}
@@ -355,7 +357,7 @@ uint64_t ClassDB::get_api_hash(APIType p_api) {
ClassInfo *t = classes.getptr(E->get());
ERR_FAIL_COND_V(!t, 0);
- if (t->api != p_api)
+ if (t->api != p_api || !t->exposed)
continue;
hash = hash_djb2_one_64(t->name.hash(), hash);
hash = hash_djb2_one_64(t->inherits.hash(), hash);
@@ -651,7 +653,6 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName
}
type->constant_map[p_name] = p_constant;
-#ifdef DEBUG_METHODS_ENABLED
String enum_name = p_enum;
if (enum_name != String()) {
@@ -670,6 +671,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class, const StringName
}
}
+#ifdef DEBUG_METHODS_ENABLED
type->constant_order.push_back(p_name);
#endif
}
@@ -725,7 +727,6 @@ int ClassDB::get_integer_constant(const StringName &p_class, const StringName &p
return 0;
}
-#ifdef DEBUG_METHODS_ENABLED
StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
OBJTYPE_RLOCK;
@@ -794,7 +795,6 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_
type = type->inherits_ptr;
}
}
-#endif
void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) {
diff --git a/core/class_db.h b/core/class_db.h
index d74317239..2c77ffe65 100644
--- a/core/class_db.h
+++ b/core/class_db.h
@@ -116,10 +116,10 @@ public:
ClassInfo *inherits_ptr;
HashMap<StringName, MethodBind *, StringNameHasher> method_map;
HashMap<StringName, int, StringNameHasher> constant_map;
+ HashMap<StringName, List<StringName> > enum_map;
HashMap<StringName, MethodInfo, StringNameHasher> signal_map;
List<PropertyInfo> property_list;
#ifdef DEBUG_METHODS_ENABLED
- HashMap<StringName, List<StringName> > enum_map;
List<StringName> constant_order;
List<StringName> method_order;
Set<StringName> methods_in_properties;
@@ -344,11 +344,9 @@ public:
static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false);
static int get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success = NULL);
-#ifdef DEBUG_METHODS_ENABLED
static StringName get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
static void get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance = false);
static void get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance = false);
-#endif
static StringName get_category(const StringName &p_node);
diff --git a/core/dictionary.cpp b/core/dictionary.cpp
index e3f4aa5f2..ba0de9586 100644
--- a/core/dictionary.cpp
+++ b/core/dictionary.cpp
@@ -211,7 +211,7 @@ const Variant *Dictionary::next(const Variant *p_key) const {
return NULL;
}
-Dictionary Dictionary::duplicate() const {
+Dictionary Dictionary::duplicate(bool p_deep) const {
Dictionary n;
@@ -219,7 +219,7 @@ Dictionary Dictionary::duplicate() const {
get_key_list(&keys);
for (List<Variant>::Element *E = keys.front(); E; E = E->next()) {
- n[E->get()] = operator[](E->get());
+ n[E->get()] = p_deep ? operator[](E->get()).duplicate(p_deep) : operator[](E->get());
}
return n;
diff --git a/core/dictionary.h b/core/dictionary.h
index f001f2d5e..9eef265d5 100644
--- a/core/dictionary.h
+++ b/core/dictionary.h
@@ -75,7 +75,7 @@ public:
Array keys() const;
Array values() const;
- Dictionary duplicate() const;
+ Dictionary duplicate(bool p_deep = false) const;
Dictionary(const Dictionary &p_from);
Dictionary();
diff --git a/core/error_macros.h b/core/error_macros.h
index b8d0c7e0c..168b2e06f 100644
--- a/core/error_macros.h
+++ b/core/error_macros.h
@@ -311,4 +311,14 @@ extern bool _err_error_exists;
_err_error_exists = false; \
}
+#define WARN_DEPRECATED \
+ { \
+ static bool warning_shown=false;\
+ if (!warning_shown) {\
+ _err_print_error(FUNCTION_STR, __FILE__, __LINE__,"This method has been deprecated and will be removed in the future", ERR_HANDLER_WARNING); \
+ _err_error_exists = false; \
+ warning_shown=true;\
+ }\
+ }
+
#endif
diff --git a/core/image.cpp b/core/image.cpp
index 07e705265..51fbe75de 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -366,6 +366,8 @@ int Image::get_mipmap_count() const {
template <uint32_t read_bytes, bool read_alpha, uint32_t write_bytes, bool write_alpha, bool read_gray, bool write_gray>
static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p_dst) {
+ uint32_t max_bytes = MAX(read_bytes, write_bytes);
+
for (int y = 0; y < p_height; y++) {
for (int x = 0; x < p_width; x++) {
@@ -379,7 +381,8 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p
rgba[1] = rofs[0];
rgba[2] = rofs[0];
} else {
- for (uint32_t i = 0; i < MAX(read_bytes, write_bytes); i++) {
+
+ for (uint32_t i = 0; i < max_bytes; i++) {
rgba[i] = (i < read_bytes) ? rofs[i] : 0;
}
@@ -937,7 +940,7 @@ bool Image::_can_modify(Format p_format) const {
return p_format <= FORMAT_RGBE9995;
}
-template <int CC>
+template <int CC, bool renormalize>
static void _generate_po2_mipmap(const uint8_t *p_src, uint8_t *p_dst, uint32_t p_width, uint32_t p_height) {
//fast power of 2 mipmap generation
@@ -963,6 +966,19 @@ static void _generate_po2_mipmap(const uint8_t *p_src, uint8_t *p_dst, uint32_t
dst_ptr[j] = val >> 2;
}
+ if (renormalize) {
+ Vector3 n(dst_ptr[0] / 255.0, dst_ptr[1] / 255.0, dst_ptr[2] / 255.0);
+ n *= 2.0;
+ n -= Vector3(1, 1, 1);
+ n.normalize();
+ n += Vector3(1, 1, 1);
+ n *= 0.5;
+ n *= 255;
+ dst_ptr[0] = CLAMP(int(n.x), 0, 255);
+ dst_ptr[1] = CLAMP(int(n.y), 0, 255);
+ dst_ptr[2] = CLAMP(int(n.z), 0, 255);
+ }
+
dst_ptr += CC;
rup_ptr += CC * 2;
rdown_ptr += CC * 2;
@@ -1045,11 +1061,11 @@ void Image::shrink_x2() {
switch (format) {
case FORMAT_L8:
- case FORMAT_R8: _generate_po2_mipmap<1>(r.ptr(), w.ptr(), width, height); break;
- case FORMAT_LA8: _generate_po2_mipmap<2>(r.ptr(), w.ptr(), width, height); break;
- case FORMAT_RG8: _generate_po2_mipmap<2>(r.ptr(), w.ptr(), width, height); break;
- case FORMAT_RGB8: _generate_po2_mipmap<3>(r.ptr(), w.ptr(), width, height); break;
- case FORMAT_RGBA8: _generate_po2_mipmap<4>(r.ptr(), w.ptr(), width, height); break;
+ case FORMAT_R8: _generate_po2_mipmap<1, false>(r.ptr(), w.ptr(), width, height); break;
+ case FORMAT_LA8: _generate_po2_mipmap<2, false>(r.ptr(), w.ptr(), width, height); break;
+ case FORMAT_RG8: _generate_po2_mipmap<2, false>(r.ptr(), w.ptr(), width, height); break;
+ case FORMAT_RGB8: _generate_po2_mipmap<3, false>(r.ptr(), w.ptr(), width, height); break;
+ case FORMAT_RGBA8: _generate_po2_mipmap<4, false>(r.ptr(), w.ptr(), width, height); break;
default: {}
}
}
@@ -1060,7 +1076,7 @@ void Image::shrink_x2() {
}
}
-Error Image::generate_mipmaps() {
+Error Image::generate_mipmaps(bool p_renormalize) {
if (!_can_modify(format)) {
ERR_EXPLAIN("Cannot generate mipmaps in indexed, compressed or custom image formats.");
@@ -1077,61 +1093,40 @@ Error Image::generate_mipmaps() {
PoolVector<uint8_t>::Write wp = data.write();
- if (next_power_of_2(width) == uint32_t(width) && next_power_of_2(height) == uint32_t(height)) {
- //use fast code for powers of 2
- int prev_ofs = 0;
- int prev_h = height;
- int prev_w = width;
+ int prev_ofs = 0;
+ int prev_h = height;
+ int prev_w = width;
- for (int i = 1; i < mmcount; i++) {
+ for (int i = 1; i < mmcount; i++) {
- int ofs, w, h;
- _get_mipmap_offset_and_size(i, ofs, w, h);
+ int ofs, w, h;
+ _get_mipmap_offset_and_size(i, ofs, w, h);
- switch (format) {
+ switch (format) {
- case FORMAT_L8:
- case FORMAT_R8: _generate_po2_mipmap<1>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
- case FORMAT_LA8:
- case FORMAT_RG8: _generate_po2_mipmap<2>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
- case FORMAT_RGB8: _generate_po2_mipmap<3>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
- case FORMAT_RGBA8: _generate_po2_mipmap<4>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
- default: {}
- }
+ case FORMAT_L8:
+ case FORMAT_R8: _generate_po2_mipmap<1, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
+ case FORMAT_LA8:
+ case FORMAT_RG8: _generate_po2_mipmap<2, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h); break;
+ case FORMAT_RGB8:
+ if (p_renormalize)
+ _generate_po2_mipmap<3, true>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h);
+ else
+ _generate_po2_mipmap<3, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h);
- prev_ofs = ofs;
- prev_w = w;
- prev_h = h;
+ break;
+ case FORMAT_RGBA8:
+ if (p_renormalize)
+ _generate_po2_mipmap<4, true>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h);
+ else
+ _generate_po2_mipmap<4, false>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h);
+ break;
+ default: {}
}
- } else {
- //use slow code..
-
- //use bilinear filtered code for non powers of 2
- int prev_ofs = 0;
- int prev_h = height;
- int prev_w = width;
-
- for (int i = 1; i < mmcount; i++) {
-
- int ofs, w, h;
- _get_mipmap_offset_and_size(i, ofs, w, h);
-
- switch (format) {
-
- case FORMAT_L8:
- case FORMAT_R8: _scale_bilinear<1>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break;
- case FORMAT_LA8:
- case FORMAT_RG8: _scale_bilinear<2>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break;
- case FORMAT_RGB8: _scale_bilinear<3>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break;
- case FORMAT_RGBA8: _scale_bilinear<4>(&wp[prev_ofs], &wp[ofs], prev_w, prev_h, w, h); break;
- default: {}
- }
-
- prev_ofs = ofs;
- prev_w = w;
- prev_h = h;
- }
+ prev_ofs = ofs;
+ prev_w = w;
+ prev_h = h;
}
mipmaps = true;
@@ -1166,6 +1161,9 @@ PoolVector<uint8_t> Image::get_data() const {
void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format) {
+ ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH);
+ ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT);
+
int mm = 0;
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
data.resize(size);
@@ -1630,6 +1628,12 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Po
ERR_FAIL_COND(format != p_src->format);
Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect);
+
+ if (p_dest.x < 0)
+ clipped_src_rect.position.x = ABS(p_dest.x);
+ if (p_dest.y < 0)
+ clipped_src_rect.position.y = ABS(p_dest.y);
+
if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0)
return;
@@ -1678,6 +1682,12 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co
ERR_FAIL_COND(format != p_src->format);
Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect);
+
+ if (p_dest.x < 0)
+ clipped_src_rect.position.x = ABS(p_dest.x);
+ if (p_dest.y < 0)
+ clipped_src_rect.position.y = ABS(p_dest.y);
+
if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0)
return;
@@ -1729,6 +1739,12 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const P
ERR_FAIL_COND(format != p_src->format);
Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect);
+
+ if (p_dest.x < 0)
+ clipped_src_rect.position.x = ABS(p_dest.x);
+ if (p_dest.y < 0)
+ clipped_src_rect.position.y = ABS(p_dest.y);
+
if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0)
return;
@@ -1777,6 +1793,12 @@ void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, c
ERR_FAIL_COND(format != p_src->format);
Rect2i clipped_src_rect = Rect2i(0, 0, p_src->width, p_src->height).clip(p_src_rect);
+
+ if (p_dest.x < 0)
+ clipped_src_rect.position.x = ABS(p_dest.x);
+ if (p_dest.y < 0)
+ clipped_src_rect.position.y = ABS(p_dest.y);
+
if (clipped_src_rect.size.x <= 0 || clipped_src_rect.size.y <= 0)
return;
@@ -1912,6 +1934,10 @@ void Image::unlock() {
write_lock = PoolVector<uint8_t>::Write();
}
+Color Image::get_pixelv(const Point2 &p_src) const {
+ return get_pixel(p_src.x, p_src.y);
+}
+
Color Image::get_pixel(int p_x, int p_y) const {
uint8_t *ptr = write_lock.ptr();
@@ -2058,6 +2084,10 @@ Color Image::get_pixel(int p_x, int p_y) const {
return Color();
}
+void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) {
+ return set_pixel(p_dst.x, p_dst.y, p_color);
+}
+
void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
uint8_t *ptr = write_lock.ptr();
@@ -2249,7 +2279,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("crop", "width", "height"), &Image::crop);
ClassDB::bind_method(D_METHOD("flip_x"), &Image::flip_x);
ClassDB::bind_method(D_METHOD("flip_y"), &Image::flip_y);
- ClassDB::bind_method(D_METHOD("generate_mipmaps"), &Image::generate_mipmaps);
+ ClassDB::bind_method(D_METHOD("generate_mipmaps", "renormalize"), &Image::generate_mipmaps, DEFVAL(false));
ClassDB::bind_method(D_METHOD("clear_mipmaps"), &Image::clear_mipmaps);
ClassDB::bind_method(D_METHOD("create", "width", "height", "use_mipmaps", "format"), &Image::_create_empty);
@@ -2271,6 +2301,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha);
ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear);
ClassDB::bind_method(D_METHOD("normalmap_to_xy"), &Image::normalmap_to_xy);
+ ClassDB::bind_method(D_METHOD("bumpmap_to_normalmap", "bump_scale"), &Image::bumpmap_to_normalmap, DEFVAL(1.0));
ClassDB::bind_method(D_METHOD("blit_rect", "src", "src_rect", "dst"), &Image::blit_rect);
ClassDB::bind_method(D_METHOD("blit_rect_mask", "src", "mask", "src_rect", "dst"), &Image::blit_rect_mask);
@@ -2288,8 +2319,10 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("lock"), &Image::lock);
ClassDB::bind_method(D_METHOD("unlock"), &Image::unlock);
- ClassDB::bind_method(D_METHOD("set_pixel", "x", "y", "color"), &Image::set_pixel);
+ ClassDB::bind_method(D_METHOD("get_pixelv", "src"), &Image::get_pixelv);
ClassDB::bind_method(D_METHOD("get_pixel", "x", "y"), &Image::get_pixel);
+ ClassDB::bind_method(D_METHOD("set_pixelv", "dst", "color"), &Image::set_pixelv);
+ ClassDB::bind_method(D_METHOD("set_pixel", "x", "y", "color"), &Image::set_pixel);
ClassDB::bind_method(D_METHOD("load_png_from_buffer", "buffer"), &Image::load_png_from_buffer);
ClassDB::bind_method(D_METHOD("load_jpg_from_buffer", "buffer"), &Image::load_jpg_from_buffer);
@@ -2379,6 +2412,47 @@ void Image::normalmap_to_xy() {
convert(Image::FORMAT_LA8);
}
+void Image::bumpmap_to_normalmap(float bump_scale) {
+ ERR_FAIL_COND(!_can_modify(format));
+ convert(Image::FORMAT_RF);
+
+ PoolVector<uint8_t> result_image; //rgba output
+ result_image.resize(width * height * 4);
+
+ {
+ PoolVector<uint8_t>::Read rp = data.read();
+ PoolVector<uint8_t>::Write wp = result_image.write();
+
+ unsigned char *write_ptr = wp.ptr();
+ float *read_ptr = (float *)rp.ptr();
+
+ for (int ty = 0; ty < height; ty++) {
+ int py = ty + 1;
+ if (py >= height) py -= height;
+
+ for (int tx = 0; tx < width; tx++) {
+ int px = tx + 1;
+ if (px >= width) px -= width;
+ float here = read_ptr[ty * width + tx];
+ float to_right = read_ptr[ty * width + px];
+ float above = read_ptr[py * width + tx];
+ Vector3 up = Vector3(0, 1, (here - above) * bump_scale);
+ Vector3 across = Vector3(1, 0, (to_right - here) * bump_scale);
+
+ Vector3 normal = across.cross(up);
+ normal.normalize();
+
+ write_ptr[((ty * width + tx) << 2) + 0] = (127.5 + normal.x * 127.5);
+ write_ptr[((ty * width + tx) << 2) + 1] = (127.5 + normal.y * 127.5);
+ write_ptr[((ty * width + tx) << 2) + 2] = (127.5 + normal.z * 127.5);
+ write_ptr[((ty * width + tx) << 2) + 3] = 255;
+ }
+ }
+ }
+ format = FORMAT_RGBA8;
+ data = result_image;
+}
+
void Image::srgb_to_linear() {
if (data.size() == 0)
diff --git a/core/image.h b/core/image.h
index e962787ae..80a0c339d 100644
--- a/core/image.h
+++ b/core/image.h
@@ -217,7 +217,7 @@ public:
/**
* Generate a mipmap to an image (creates an image 1/4 the size, with averaging of 4->1)
*/
- Error generate_mipmaps();
+ Error generate_mipmaps(bool p_renormalize = false);
void clear_mipmaps();
@@ -284,6 +284,7 @@ public:
void premultiply_alpha();
void srgb_to_linear();
void normalmap_to_xy();
+ void bumpmap_to_normalmap(float bump_scale = 1.0);
void blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest);
void blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2 &p_src_rect, const Point2 &p_dest);
@@ -320,7 +321,9 @@ public:
DetectChannels get_detected_channels();
+ Color get_pixelv(const Point2 &p_src) const;
Color get_pixel(int p_x, int p_y) const;
+ void set_pixelv(const Point2 &p_dest, const Color &p_color);
void set_pixel(int p_x, int p_y, const Color &p_color);
void copy_internals_from(const Ref<Image> &p_image) {
diff --git a/core/input_map.cpp b/core/input_map.cpp
index bd03d6119..d33f40cbc 100644
--- a/core/input_map.cpp
+++ b/core/input_map.cpp
@@ -35,27 +35,32 @@
InputMap *InputMap::singleton = NULL;
+int InputMap::ALL_DEVICES = -1;
+
void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action);
ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::_get_actions);
- ClassDB::bind_method(D_METHOD("add_action", "action"), &InputMap::add_action);
+ ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.5f));
ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action);
+ ClassDB::bind_method(D_METHOD("action_set_deadzone", "deadzone"), &InputMap::action_set_deadzone);
ClassDB::bind_method(D_METHOD("action_add_event", "action", "event"), &InputMap::action_add_event);
ClassDB::bind_method(D_METHOD("action_has_event", "action", "event"), &InputMap::action_has_event);
ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event);
+ ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events);
ClassDB::bind_method(D_METHOD("get_action_list", "action"), &InputMap::_get_action_list);
ClassDB::bind_method(D_METHOD("event_is_action", "event", "action"), &InputMap::event_is_action);
ClassDB::bind_method(D_METHOD("load_from_globals"), &InputMap::load_from_globals);
}
-void InputMap::add_action(const StringName &p_action) {
+void InputMap::add_action(const StringName &p_action, float p_deadzone) {
ERR_FAIL_COND(input_map.has(p_action));
input_map[p_action] = Action();
static int last_id = 1;
input_map[p_action].id = last_id;
+ input_map[p_action].deadzone = p_deadzone;
last_id++;
}
@@ -94,19 +99,21 @@ List<StringName> InputMap::get_actions() const {
return actions;
}
-List<Ref<InputEvent> >::Element *InputMap::_find_event(List<Ref<InputEvent> > &p_list, const Ref<InputEvent> &p_event, bool p_action_test) const {
+List<Ref<InputEvent> >::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength) const {
- for (List<Ref<InputEvent> >::Element *E = p_list.front(); E; E = E->next()) {
+ for (List<Ref<InputEvent> >::Element *E = p_action.inputs.front(); E; E = E->next()) {
const Ref<InputEvent> e = E->get();
//if (e.type != Ref<InputEvent>::KEY && e.device != p_event.device) -- unsure about the KEY comparison, why is this here?
// continue;
- if (e->get_device() != p_event->get_device())
- continue;
- if (e->action_match(p_event))
- return E;
+ int device = e->get_device();
+ if (device == ALL_DEVICES || device == p_event->get_device()) {
+ if (e->action_match(p_event, p_pressed, p_strength, p_action.deadzone)) {
+ return E;
+ }
+ }
}
return NULL;
@@ -117,11 +124,18 @@ bool InputMap::has_action(const StringName &p_action) const {
return input_map.has(p_action);
}
+void InputMap::action_set_deadzone(const StringName &p_action, float p_deadzone) {
+
+ ERR_FAIL_COND(!input_map.has(p_action));
+
+ input_map[p_action].deadzone = p_deadzone;
+}
+
void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
ERR_FAIL_COND(!input_map.has(p_action));
- if (_find_event(input_map[p_action].inputs, p_event))
+ if (_find_event(input_map[p_action], p_event))
return; //already gots
input_map[p_action].inputs.push_back(p_event);
@@ -130,18 +144,25 @@ void InputMap::action_add_event(const StringName &p_action, const Ref<InputEvent
bool InputMap::action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
ERR_FAIL_COND_V(!input_map.has(p_action), false);
- return (_find_event(input_map[p_action].inputs, p_event) != NULL);
+ return (_find_event(input_map[p_action], p_event) != NULL);
}
void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(!input_map.has(p_action));
- List<Ref<InputEvent> >::Element *E = _find_event(input_map[p_action].inputs, p_event);
+ List<Ref<InputEvent> >::Element *E = _find_event(input_map[p_action], p_event);
if (E)
input_map[p_action].inputs.erase(E);
}
+void InputMap::action_erase_events(const StringName &p_action) {
+
+ ERR_FAIL_COND(!input_map.has(p_action));
+
+ input_map[p_action].inputs.clear();
+}
+
Array InputMap::_get_action_list(const StringName &p_action) {
Array ret;
@@ -166,19 +187,33 @@ const List<Ref<InputEvent> > *InputMap::get_action_list(const StringName &p_acti
}
bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const {
+ return event_get_action_status(p_event, p_action);
+}
+bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed, float *p_strength) const {
Map<StringName, Action>::Element *E = input_map.find(p_action);
if (!E) {
ERR_EXPLAIN("Request for nonexistent InputMap action: " + String(p_action));
ERR_FAIL_COND_V(!E, false);
}
- Ref<InputEventAction> iea = p_event;
- if (iea.is_valid()) {
- return iea->get_action() == p_action;
+ Ref<InputEventAction> input_event_action = p_event;
+ if (input_event_action.is_valid()) {
+ return input_event_action->get_action() == p_action;
}
- return _find_event(E->get().inputs, p_event, true) != NULL;
+ bool pressed;
+ float strength;
+ List<Ref<InputEvent> >::Element *event = _find_event(E->get(), p_event, &pressed, &strength);
+ if (event != NULL) {
+ if (p_pressed != NULL)
+ *p_pressed = pressed;
+ if (p_strength != NULL)
+ *p_strength = strength;
+ return true;
+ } else {
+ return false;
+ }
}
const Map<StringName, InputMap::Action> &InputMap::get_action_map() const {
@@ -200,16 +235,16 @@ void InputMap::load_from_globals() {
String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
- add_action(name);
-
- Array va = ProjectSettings::get_singleton()->get(pi.name);
-
- for (int i = 0; i < va.size(); i++) {
+ Dictionary action = ProjectSettings::get_singleton()->get(pi.name);
+ float deadzone = action.has("deadzone") ? (float)action["deadzone"] : 0.5f;
+ Array events = action["events"];
- Ref<InputEvent> ie = va[i];
- if (ie.is_null())
+ add_action(name, deadzone);
+ for (int i = 0; i < events.size(); i++) {
+ Ref<InputEvent> event = events[i];
+ if (event.is_null())
continue;
- action_add_event(name, ie);
+ action_add_event(name, event);
}
}
}
diff --git a/core/input_map.h b/core/input_map.h
index 84d90f6f2..bdec75c65 100644
--- a/core/input_map.h
+++ b/core/input_map.h
@@ -39,8 +39,14 @@ class InputMap : public Object {
GDCLASS(InputMap, Object);
public:
+ /**
+ * A special value used to signify that a given Action can be triggered by any device
+ */
+ static int ALL_DEVICES;
+
struct Action {
int id;
+ float deadzone;
List<Ref<InputEvent> > inputs;
};
@@ -49,7 +55,7 @@ private:
mutable Map<StringName, Action> input_map;
- List<Ref<InputEvent> >::Element *_find_event(List<Ref<InputEvent> > &p_list, const Ref<InputEvent> &p_event, bool p_action_test = false) const;
+ List<Ref<InputEvent> >::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool *p_pressed = NULL, float *p_strength = NULL) const;
Array _get_action_list(const StringName &p_action);
Array _get_actions();
@@ -62,15 +68,18 @@ public:
bool has_action(const StringName &p_action) const;
List<StringName> get_actions() const;
- void add_action(const StringName &p_action);
+ void add_action(const StringName &p_action, float p_deadzone = 0.5);
void erase_action(const StringName &p_action);
+ void action_set_deadzone(const StringName &p_action, float p_deadzone);
void action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event);
bool action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event);
void action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event);
+ void action_erase_events(const StringName &p_action);
const List<Ref<InputEvent> > *get_action_list(const StringName &p_action);
bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const;
+ bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool *p_pressed = NULL, float *p_strength = NULL) const;
const Map<StringName, Action> &get_action_map() const;
void load_from_globals();
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 4d72f744e..9e301ccac 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -618,7 +618,27 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
String query = "";
Array keys = p_dict.keys();
for (int i = 0; i < keys.size(); ++i) {
- query += "&" + String(keys[i]).http_escape() + "=" + String(p_dict[keys[i]]).http_escape();
+ String encoded_key = String(keys[i]).http_escape();
+ Variant value = p_dict[keys[i]];
+ switch (value.get_type()) {
+ case Variant::ARRAY: {
+ // Repeat the key with every values
+ Array values = value;
+ for (int j = 0; j < values.size(); ++j) {
+ query += "&" + encoded_key + "=" + String(values[j]).http_escape();
+ }
+ break;
+ }
+ case Variant::NIL: {
+ // Add the key with no value
+ query += "&" + encoded_key;
+ break;
+ }
+ default: {
+ // Add the key-value pair
+ query += "&" + encoded_key + "=" + String(value).http_escape();
+ }
+ }
}
query.erase(0, 1);
return query;
diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp
index 999c9a8ca..8ebd9d6cd 100644
--- a/core/io/image_loader.cpp
+++ b/core/io/image_loader.cpp
@@ -37,7 +37,7 @@ bool ImageFormatLoader::recognize(const String &p_extension) const {
get_recognized_extensions(&extensions);
for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- if (E->get().nocasecmp_to(p_extension.get_extension()) == 0)
+ if (E->get().nocasecmp_to(p_extension) == 0)
return true;
}
diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp
new file mode 100644
index 000000000..cbe7f87d9
--- /dev/null
+++ b/core/io/multiplayer_api.cpp
@@ -0,0 +1,722 @@
+#include "core/io/multiplayer_api.h"
+#include "core/io/marshalls.h"
+#include "scene/main/node.h"
+
+void MultiplayerAPI::poll() {
+
+ if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED)
+ return;
+
+ network_peer->poll();
+
+ if (!network_peer.is_valid()) //it's possible that polling might have resulted in a disconnection, so check here
+ return;
+
+ while (network_peer->get_available_packet_count()) {
+
+ int sender = network_peer->get_packet_peer();
+ const uint8_t *packet;
+ int len;
+
+ Error err = network_peer->get_packet(&packet, len);
+ if (err != OK) {
+ ERR_PRINT("Error getting packet!");
+ }
+
+ rpc_sender_id = sender;
+ _process_packet(sender, packet, len);
+ rpc_sender_id = 0;
+
+ if (!network_peer.is_valid()) {
+ break; //it's also possible that a packet or RPC caused a disconnection, so also check here
+ }
+ }
+}
+
+void MultiplayerAPI::clear() {
+ connected_peers.clear();
+ path_get_cache.clear();
+ path_send_cache.clear();
+ last_send_cache_id = 1;
+}
+
+void MultiplayerAPI::set_root_node(Node *p_node) {
+ root_node = p_node;
+}
+
+void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer) {
+
+ if (network_peer.is_valid()) {
+ network_peer->disconnect("peer_connected", this, "add_peer");
+ network_peer->disconnect("peer_disconnected", this, "del_peer");
+ network_peer->disconnect("connection_succeeded", this, "connected_to_server");
+ network_peer->disconnect("connection_failed", this, "connection_failed");
+ network_peer->disconnect("server_disconnected", this, "server_disconnected");
+ clear();
+ }
+
+ network_peer = p_peer;
+
+ ERR_EXPLAIN("Supplied NetworkedNetworkPeer must be connecting or connected.");
+ ERR_FAIL_COND(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED);
+
+ if (network_peer.is_valid()) {
+ network_peer->connect("peer_connected", this, "add_peer");
+ network_peer->connect("peer_disconnected", this, "del_peer");
+ network_peer->connect("connection_succeeded", this, "connected_to_server");
+ network_peer->connect("connection_failed", this, "connection_failed");
+ network_peer->connect("server_disconnected", this, "server_disconnected");
+ }
+}
+
+Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const {
+ return network_peer;
+}
+
+void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
+
+ ERR_FAIL_COND(root_node == NULL);
+ ERR_FAIL_COND(p_packet_len < 5);
+
+ uint8_t packet_type = p_packet[0];
+
+ switch (packet_type) {
+
+ case NETWORK_COMMAND_SIMPLIFY_PATH: {
+
+ _process_simplify_path(p_from, p_packet, p_packet_len);
+ } break;
+
+ case NETWORK_COMMAND_CONFIRM_PATH: {
+
+ _process_confirm_path(p_from, p_packet, p_packet_len);
+ } break;
+
+ case NETWORK_COMMAND_REMOTE_CALL:
+ case NETWORK_COMMAND_REMOTE_SET: {
+
+ ERR_FAIL_COND(p_packet_len < 6);
+
+ Node *node = _process_get_node(p_from, p_packet, p_packet_len);
+
+ ERR_FAIL_COND(node == NULL);
+
+ //detect cstring end
+ int len_end = 5;
+ for (; len_end < p_packet_len; len_end++) {
+ if (p_packet[len_end] == 0) {
+ break;
+ }
+ }
+
+ ERR_FAIL_COND(len_end >= p_packet_len);
+
+ StringName name = String::utf8((const char *)&p_packet[5]);
+
+ if (packet_type == NETWORK_COMMAND_REMOTE_CALL) {
+
+ _process_rpc(node, name, p_from, p_packet, p_packet_len, len_end + 1);
+
+ } else {
+
+ _process_rset(node, name, p_from, p_packet, p_packet_len, len_end + 1);
+ }
+
+ } break;
+ }
+}
+
+Node *MultiplayerAPI::_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len) {
+
+ uint32_t target = decode_uint32(&p_packet[1]);
+ Node *node = NULL;
+
+ if (target & 0x80000000) {
+ //use full path (not cached yet)
+
+ int ofs = target & 0x7FFFFFFF;
+ ERR_FAIL_COND_V(ofs >= p_packet_len, NULL);
+
+ String paths;
+ paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
+
+ NodePath np = paths;
+
+ node = root_node->get_node(np);
+
+ if (!node)
+ ERR_PRINTS("Failed to get path from RPC: " + String(np));
+ } else {
+ //use cached path
+ int id = target;
+
+ Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
+ ERR_FAIL_COND_V(!E, NULL);
+
+ Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id);
+ ERR_FAIL_COND_V(!F, NULL);
+
+ PathGetCache::NodeInfo *ni = &F->get();
+ //do proper caching later
+
+ node = root_node->get_node(ni->path);
+ if (!node)
+ ERR_PRINTS("Failed to get cached path from RPC: " + String(ni->path));
+ }
+ return node;
+}
+
+void MultiplayerAPI::_process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
+ if (!p_node->can_call_rpc(p_name, p_from))
+ return;
+
+ ERR_FAIL_COND(p_offset >= p_packet_len);
+
+ int argc = p_packet[p_offset];
+ Vector<Variant> args;
+ Vector<const Variant *> argp;
+ args.resize(argc);
+ argp.resize(argc);
+
+ p_offset++;
+
+ for (int i = 0; i < argc; i++) {
+
+ ERR_FAIL_COND(p_offset >= p_packet_len);
+ int vlen;
+ Error err = decode_variant(args[i], &p_packet[p_offset], p_packet_len - p_offset, &vlen);
+ ERR_FAIL_COND(err != OK);
+ //args[i]=p_packet[3+i];
+ argp[i] = &args[i];
+ p_offset += vlen;
+ }
+
+ Variant::CallError ce;
+
+ p_node->call(p_name, (const Variant **)argp.ptr(), argc, ce);
+ if (ce.error != Variant::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(p_node, p_name, (const Variant **)argp.ptr(), argc, ce);
+ error = "RPC - " + error;
+ ERR_PRINTS(error);
+ }
+}
+
+void MultiplayerAPI::_process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset) {
+
+ if (!p_node->can_call_rset(p_name, p_from))
+ return;
+
+ ERR_FAIL_COND(p_offset >= p_packet_len);
+
+ Variant value;
+ decode_variant(value, &p_packet[p_offset], p_packet_len - p_offset);
+
+ bool valid;
+
+ p_node->set(p_name, value, &valid);
+ if (!valid) {
+ String error = "Error setting remote property '" + String(p_name) + "', not found in object of type " + p_node->get_class();
+ ERR_PRINTS(error);
+ }
+}
+
+void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+
+ ERR_FAIL_COND(p_packet_len < 5);
+ int id = decode_uint32(&p_packet[1]);
+
+ String paths;
+ paths.parse_utf8((const char *)&p_packet[5], p_packet_len - 5);
+
+ NodePath path = paths;
+
+ if (!path_get_cache.has(p_from)) {
+ path_get_cache[p_from] = PathGetCache();
+ }
+
+ PathGetCache::NodeInfo ni;
+ ni.path = path;
+ ni.instance = 0;
+
+ path_get_cache[p_from].nodes[id] = ni;
+
+ //send ack
+
+ //encode path
+ CharString pname = String(path).utf8();
+ int len = encode_cstring(pname.get_data(), NULL);
+
+ Vector<uint8_t> packet;
+
+ packet.resize(1 + len);
+ packet[0] = NETWORK_COMMAND_CONFIRM_PATH;
+ encode_cstring(pname.get_data(), &packet[1]);
+
+ network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ network_peer->set_target_peer(p_from);
+ network_peer->put_packet(packet.ptr(), packet.size());
+}
+
+void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len) {
+
+ String paths;
+ paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1);
+
+ NodePath path = paths;
+
+ PathSentCache *psc = path_send_cache.getptr(path);
+ ERR_FAIL_COND(!psc);
+
+ Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from);
+ ERR_FAIL_COND(!E);
+ E->get() = true;
+}
+
+bool MultiplayerAPI::_send_confirm_path(NodePath p_path, PathSentCache *psc, int p_target) {
+ bool has_all_peers = true;
+ List<int> peers_to_add; //if one is missing, take note to add it
+
+ for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
+
+ if (p_target < 0 && E->get() == -p_target)
+ continue; //continue, excluded
+
+ if (p_target > 0 && E->get() != p_target)
+ continue; //continue, not for this peer
+
+ Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
+
+ if (!F || F->get() == false) {
+ //path was not cached, or was cached but is unconfirmed
+ if (!F) {
+ //not cached at all, take note
+ peers_to_add.push_back(E->get());
+ }
+
+ has_all_peers = false;
+ }
+ }
+
+ //those that need to be added, send a message for this
+
+ for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) {
+
+ //encode function name
+ CharString pname = String(p_path).utf8();
+ int len = encode_cstring(pname.get_data(), NULL);
+
+ Vector<uint8_t> packet;
+
+ packet.resize(1 + 4 + len);
+ packet[0] = NETWORK_COMMAND_SIMPLIFY_PATH;
+ encode_uint32(psc->id, &packet[1]);
+ encode_cstring(pname.get_data(), &packet[5]);
+
+ network_peer->set_target_peer(E->get()); //to all of you
+ network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
+ network_peer->put_packet(packet.ptr(), packet.size());
+
+ psc->confirmed_peers.insert(E->get(), false); //insert into confirmed, but as false since it was not confirmed
+ }
+
+ return has_all_peers;
+}
+
+void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) {
+
+ if (network_peer.is_null()) {
+ ERR_EXPLAIN("Attempt to remote call/set when networking is not active in SceneTree.");
+ ERR_FAIL();
+ }
+
+ if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING) {
+ ERR_EXPLAIN("Attempt to remote call/set when networking is not connected yet in SceneTree.");
+ ERR_FAIL();
+ }
+
+ if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) {
+ ERR_EXPLAIN("Attempt to remote call/set when networking is disconnected.");
+ ERR_FAIL();
+ }
+
+ if (p_argcount > 255) {
+ ERR_EXPLAIN("Too many arguments >255.");
+ ERR_FAIL();
+ }
+
+ if (p_to != 0 && !connected_peers.has(ABS(p_to))) {
+ if (p_to == network_peer->get_unique_id()) {
+ ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: " + itos(network_peer->get_unique_id()));
+ } else {
+ ERR_EXPLAIN("Attempt to remote call unexisting ID: " + itos(p_to));
+ }
+
+ ERR_FAIL();
+ }
+
+ NodePath from_path = (root_node->get_path()).rel_path_to(p_from->get_path());
+ ERR_FAIL_COND(from_path.is_empty());
+
+ //see if the path is cached
+ PathSentCache *psc = path_send_cache.getptr(from_path);
+ if (!psc) {
+ //path is not cached, create
+ path_send_cache[from_path] = PathSentCache();
+ psc = path_send_cache.getptr(from_path);
+ psc->id = last_send_cache_id++;
+ }
+
+ //create base packet, lots of hardcode because it must be tight
+
+ int ofs = 0;
+
+#define MAKE_ROOM(m_amount) \
+ if (packet_cache.size() < m_amount) packet_cache.resize(m_amount);
+
+ //encode type
+ MAKE_ROOM(1);
+ packet_cache[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
+ ofs += 1;
+
+ //encode ID
+ MAKE_ROOM(ofs + 4);
+ encode_uint32(psc->id, &(packet_cache[ofs]));
+ ofs += 4;
+
+ //encode function name
+ CharString name = String(p_name).utf8();
+ int len = encode_cstring(name.get_data(), NULL);
+ MAKE_ROOM(ofs + len);
+ encode_cstring(name.get_data(), &(packet_cache[ofs]));
+ ofs += len;
+
+ if (p_set) {
+ //set argument
+ Error err = encode_variant(*p_arg[0], NULL, len);
+ ERR_FAIL_COND(err != OK);
+ MAKE_ROOM(ofs + len);
+ encode_variant(*p_arg[0], &(packet_cache[ofs]), len);
+ ofs += len;
+
+ } else {
+ //call arguments
+ MAKE_ROOM(ofs + 1);
+ packet_cache[ofs] = p_argcount;
+ ofs += 1;
+ for (int i = 0; i < p_argcount; i++) {
+ Error err = encode_variant(*p_arg[i], NULL, len);
+ ERR_FAIL_COND(err != OK);
+ MAKE_ROOM(ofs + len);
+ encode_variant(*p_arg[i], &(packet_cache[ofs]), len);
+ ofs += len;
+ }
+ }
+
+ //see if all peers have cached path (is so, call can be fast)
+ bool has_all_peers = _send_confirm_path(from_path, psc, p_to);
+
+ //take chance and set transfer mode, since all send methods will use it
+ network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
+
+ if (has_all_peers) {
+
+ //they all have verified paths, so send fast
+ network_peer->set_target_peer(p_to); //to all of you
+ network_peer->put_packet(packet_cache.ptr(), ofs); //a message with love
+ } else {
+ //not all verified path, so send one by one
+
+ //apend path at the end, since we will need it for some packets
+ CharString pname = String(from_path).utf8();
+ int path_len = encode_cstring(pname.get_data(), NULL);
+ MAKE_ROOM(ofs + path_len);
+ encode_cstring(pname.get_data(), &(packet_cache[ofs]));
+
+ for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
+
+ if (p_to < 0 && E->get() == -p_to)
+ continue; //continue, excluded
+
+ if (p_to > 0 && E->get() != p_to)
+ continue; //continue, not for this peer
+
+ Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
+ ERR_CONTINUE(!F); //should never happen
+
+ network_peer->set_target_peer(E->get()); //to this one specifically
+
+ if (F->get() == true) {
+ //this one confirmed path, so use id
+ encode_uint32(psc->id, &(packet_cache[1]));
+ network_peer->put_packet(packet_cache.ptr(), ofs);
+ } else {
+ //this one did not confirm path yet, so use entire path (sorry!)
+ encode_uint32(0x80000000 | ofs, &(packet_cache[1])); //offset to path and flag
+ network_peer->put_packet(packet_cache.ptr(), ofs + path_len);
+ }
+ }
+ }
+}
+
+void MultiplayerAPI::add_peer(int p_id) {
+ connected_peers.insert(p_id);
+ path_get_cache.insert(p_id, PathGetCache());
+ emit_signal("network_peer_connected", p_id);
+}
+
+void MultiplayerAPI::del_peer(int p_id) {
+ connected_peers.erase(p_id);
+ path_get_cache.erase(p_id); //I no longer need your cache, sorry
+ emit_signal("network_peer_disconnected", p_id);
+}
+
+void MultiplayerAPI::connected_to_server() {
+
+ emit_signal("connected_to_server");
+}
+
+void MultiplayerAPI::connection_failed() {
+
+ emit_signal("connection_failed");
+}
+
+void MultiplayerAPI::server_disconnected() {
+
+ emit_signal("server_disconnected");
+}
+
+bool _should_call_native(Node::RPCMode mode, bool is_master, bool &r_skip_rpc) {
+
+ switch (mode) {
+
+ case Node::RPC_MODE_DISABLED: {
+ //do nothing
+ } break;
+ case Node::RPC_MODE_REMOTE: {
+ //do nothing also, no need to call local
+ } break;
+ case Node::RPC_MODE_SYNC: {
+ //call it, sync always results in call
+ return true;
+ } break;
+ case Node::RPC_MODE_MASTER: {
+ if (is_master)
+ r_skip_rpc = true; //no other master so..
+ return is_master;
+ } break;
+ case Node::RPC_MODE_SLAVE: {
+ return !is_master;
+ } break;
+ }
+ return false;
+}
+
+bool _should_call_script(ScriptInstance::RPCMode mode, bool is_master, bool &r_skip_rpc) {
+ switch (mode) {
+
+ case ScriptInstance::RPC_MODE_DISABLED: {
+ //do nothing
+ } break;
+ case ScriptInstance::RPC_MODE_REMOTE: {
+ //do nothing also, no need to call local
+ } break;
+ case ScriptInstance::RPC_MODE_SYNC: {
+ //call it, sync always results in call
+ return true;
+ } break;
+ case ScriptInstance::RPC_MODE_MASTER: {
+ if (is_master)
+ r_skip_rpc = true; //no other master so..
+ return is_master;
+ } break;
+ case ScriptInstance::RPC_MODE_SLAVE: {
+ return !is_master;
+ } break;
+ }
+ return false;
+}
+
+void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) {
+
+ ERR_FAIL_COND(!p_node->is_inside_tree());
+ ERR_FAIL_COND(!network_peer.is_valid());
+
+ int node_id = network_peer->get_unique_id();
+ bool skip_rpc = false;
+ bool call_local_native = false;
+ bool call_local_script = false;
+ bool is_master = p_node->is_network_master();
+
+ if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
+ //check that send mode can use local call
+
+ const Map<StringName, Node::RPCMode>::Element *E = p_node->get_node_rpc_mode(p_method);
+ if (E) {
+ call_local_native = _should_call_native(E->get(), is_master, skip_rpc);
+ }
+
+ if (call_local_native) {
+ // done below
+ } else if (p_node->get_script_instance()) {
+ //attempt with script
+ ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rpc_mode(p_method);
+ call_local_script = _should_call_script(rpc_mode, is_master, skip_rpc);
+ }
+ }
+
+ if (!skip_rpc) {
+ _send_rpc(p_node, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount);
+ }
+
+ if (call_local_native) {
+ Variant::CallError ce;
+ p_node->call(p_method, p_arg, p_argcount, ce);
+ if (ce.error != Variant::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
+ error = "rpc() aborted in local call: - " + error;
+ ERR_PRINTS(error);
+ return;
+ }
+ }
+
+ if (call_local_script) {
+ Variant::CallError ce;
+ ce.error = Variant::CallError::CALL_OK;
+ p_node->get_script_instance()->call(p_method, p_arg, p_argcount, ce);
+ if (ce.error != Variant::CallError::CALL_OK) {
+ String error = Variant::get_call_error_text(p_node, p_method, p_arg, p_argcount, ce);
+ error = "rpc() aborted in script local call: - " + error;
+ ERR_PRINTS(error);
+ return;
+ }
+ }
+}
+
+void MultiplayerAPI::rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) {
+
+ ERR_FAIL_COND(!p_node->is_inside_tree());
+ ERR_FAIL_COND(!network_peer.is_valid());
+
+ int node_id = network_peer->get_unique_id();
+ bool is_master = p_node->is_network_master();
+ bool skip_rset = false;
+
+ if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) {
+ //check that send mode can use local call
+
+ bool set_local = false;
+
+ const Map<StringName, Node::RPCMode>::Element *E = p_node->get_node_rset_mode(p_property);
+ if (E) {
+
+ set_local = _should_call_native(E->get(), is_master, skip_rset);
+ }
+
+ if (set_local) {
+ bool valid;
+ p_node->set(p_property, p_value, &valid);
+
+ if (!valid) {
+ String error = "rset() aborted in local set, property not found: - " + String(p_property);
+ ERR_PRINTS(error);
+ return;
+ }
+ } else if (p_node->get_script_instance()) {
+ //attempt with script
+ ScriptInstance::RPCMode rpc_mode = p_node->get_script_instance()->get_rset_mode(p_property);
+
+ set_local = _should_call_script(rpc_mode, is_master, skip_rset);
+
+ if (set_local) {
+
+ bool valid = p_node->get_script_instance()->set(p_property, p_value);
+
+ if (!valid) {
+ String error = "rset() aborted in local script set, property not found: - " + String(p_property);
+ ERR_PRINTS(error);
+ return;
+ }
+ }
+ }
+ }
+
+ if (skip_rset)
+ return;
+
+ const Variant *vptr = &p_value;
+
+ _send_rpc(p_node, p_peer_id, p_unreliable, true, p_property, &vptr, 1);
+}
+
+int MultiplayerAPI::get_network_unique_id() const {
+
+ ERR_FAIL_COND_V(!network_peer.is_valid(), 0);
+ return network_peer->get_unique_id();
+}
+
+bool MultiplayerAPI::is_network_server() const {
+
+ ERR_FAIL_COND_V(!network_peer.is_valid(), false);
+ return network_peer->is_server();
+}
+
+void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) {
+
+ ERR_FAIL_COND(!network_peer.is_valid());
+ network_peer->set_refuse_new_connections(p_refuse);
+}
+
+bool MultiplayerAPI::is_refusing_new_network_connections() const {
+
+ ERR_FAIL_COND_V(!network_peer.is_valid(), false);
+ return network_peer->is_refusing_new_connections();
+}
+
+Vector<int> MultiplayerAPI::get_network_connected_peers() const {
+
+ ERR_FAIL_COND_V(!network_peer.is_valid(), Vector<int>());
+
+ Vector<int> ret;
+ for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
+ ret.push_back(E->get());
+ }
+
+ return ret;
+}
+
+void MultiplayerAPI::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node);
+ ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer);
+ ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer);
+ ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id);
+ ClassDB::bind_method(D_METHOD("is_network_server"), &MultiplayerAPI::is_network_server);
+ ClassDB::bind_method(D_METHOD("get_rpc_sender_id"), &MultiplayerAPI::get_rpc_sender_id);
+ ClassDB::bind_method(D_METHOD("add_peer", "id"), &MultiplayerAPI::add_peer);
+ ClassDB::bind_method(D_METHOD("del_peer", "id"), &MultiplayerAPI::del_peer);
+ ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &MultiplayerAPI::set_network_peer);
+ ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
+ ClassDB::bind_method(D_METHOD("clear"), &MultiplayerAPI::clear);
+
+ ClassDB::bind_method(D_METHOD("connected_to_server"), &MultiplayerAPI::connected_to_server);
+ ClassDB::bind_method(D_METHOD("connection_failed"), &MultiplayerAPI::connection_failed);
+ ClassDB::bind_method(D_METHOD("server_disconnected"), &MultiplayerAPI::server_disconnected);
+ ClassDB::bind_method(D_METHOD("get_network_connected_peers"), &MultiplayerAPI::get_network_connected_peers);
+ ClassDB::bind_method(D_METHOD("set_refuse_new_network_connections", "refuse"), &MultiplayerAPI::set_refuse_new_network_connections);
+ ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections);
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer");
+
+ ADD_SIGNAL(MethodInfo("network_peer_connected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("network_peer_disconnected", PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("connected_to_server"));
+ ADD_SIGNAL(MethodInfo("connection_failed"));
+ ADD_SIGNAL(MethodInfo("server_disconnected"));
+}
+
+MultiplayerAPI::MultiplayerAPI() {
+ clear();
+}
+
+MultiplayerAPI::~MultiplayerAPI() {
+ clear();
+}
diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h
new file mode 100644
index 000000000..e7c6ffbea
--- /dev/null
+++ b/core/io/multiplayer_api.h
@@ -0,0 +1,87 @@
+#ifndef MULTIPLAYER_PROTOCOL_H
+#define MULTIPLAYER_PROTOCOL_H
+
+#include "core/io/networked_multiplayer_peer.h"
+#include "core/reference.h"
+
+class MultiplayerAPI : public Reference {
+
+ GDCLASS(MultiplayerAPI, Reference);
+
+private:
+ //path sent caches
+ struct PathSentCache {
+ Map<int, bool> confirmed_peers;
+ int id;
+ };
+
+ //path get caches
+ struct PathGetCache {
+ struct NodeInfo {
+ NodePath path;
+ ObjectID instance;
+ };
+
+ Map<int, NodeInfo> nodes;
+ };
+
+ Ref<NetworkedMultiplayerPeer> network_peer;
+ int rpc_sender_id;
+ Set<int> connected_peers;
+ HashMap<NodePath, PathSentCache> path_send_cache;
+ Map<int, PathGetCache> path_get_cache;
+ int last_send_cache_id;
+ Vector<uint8_t> packet_cache;
+ Node *root_node;
+
+protected:
+ static void _bind_methods();
+
+ void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
+ void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len);
+ void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len);
+ Node *_process_get_node(int p_from, const uint8_t *p_packet, int p_packet_len);
+ void _process_rpc(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
+ void _process_rset(Node *p_node, const StringName &p_name, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
+
+ void _send_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount);
+ bool _send_confirm_path(NodePath p_path, PathSentCache *psc, int p_from);
+
+public:
+ enum NetworkCommands {
+ NETWORK_COMMAND_REMOTE_CALL,
+ NETWORK_COMMAND_REMOTE_SET,
+ NETWORK_COMMAND_SIMPLIFY_PATH,
+ NETWORK_COMMAND_CONFIRM_PATH,
+ };
+
+ void poll();
+ void clear();
+ void set_root_node(Node *p_node);
+ void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer);
+ Ref<NetworkedMultiplayerPeer> get_network_peer() const;
+
+ // Called by Node.rpc
+ void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
+ // Called by Node.rset
+ void rsetp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value);
+
+ void add_peer(int p_id);
+ void del_peer(int p_id);
+ void connected_to_server();
+ void connection_failed();
+ void server_disconnected();
+
+ bool has_network_peer() const { return network_peer.is_valid(); }
+ Vector<int> get_network_connected_peers() const;
+ int get_rpc_sender_id() const { return rpc_sender_id; }
+ int get_network_unique_id() const;
+ bool is_network_server() const;
+ void set_refuse_new_network_connections(bool p_refuse);
+ bool is_refusing_new_network_connections() const;
+
+ MultiplayerAPI();
+ ~MultiplayerAPI();
+};
+
+#endif // MULTIPLAYER_PROTOCOL_H
diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp
index 596060221..b6377662d 100644
--- a/core/io/pck_packer.cpp
+++ b/core/io/pck_packer.cpp
@@ -29,8 +29,8 @@
/*************************************************************************/
#include "pck_packer.h"
-
#include "core/os/file_access.h"
+#include "version.h"
static uint64_t _align(uint64_t p_n, int p_alignment) {
@@ -70,9 +70,9 @@ Error PCKPacker::pck_start(const String &p_file, int p_alignment) {
alignment = p_alignment;
file->store_32(0x43504447); // MAGIC
- file->store_32(0); // # version
- file->store_32(0); // # major
- file->store_32(0); // # minor
+ file->store_32(1); // # version
+ file->store_32(VERSION_MAJOR); // # major
+ file->store_32(VERSION_MINOR); // # minor
file->store_32(0); // # revision
for (int i = 0; i < 16; i++) {
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 5dfe06790..0c626c197 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1162,9 +1162,11 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
ERR_FAIL_V(ERR_FILE_UNRECOGNIZED);
}
- fw->store_32(VERSION_MAJOR); //current version
- fw->store_32(VERSION_MINOR);
- fw->store_32(FORMAT_VERSION);
+ // Since we're not actually converting the file contents, leave the version
+ // numbers in the file untouched.
+ fw->store_32(ver_major);
+ fw->store_32(ver_minor);
+ fw->store_32(ver_format);
save_ustring(fw, get_ustring(f)); //type
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 609dd7e93..3dcd94880 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -56,7 +56,7 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t
for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
- if (E->get().nocasecmp_to(extension.get_extension()) == 0)
+ if (E->get().nocasecmp_to(extension) == 0)
recognized = true;
}
diff --git a/core/io/stream_peer_ssl.cpp b/core/io/stream_peer_ssl.cpp
index 07a01ff99..012ba78c6 100644
--- a/core/io/stream_peer_ssl.cpp
+++ b/core/io/stream_peer_ssl.cpp
@@ -29,6 +29,8 @@
/*************************************************************************/
#include "stream_peer_ssl.h"
+#include "os/file_access.h"
+#include "project_settings.h"
StreamPeerSSL *(*StreamPeerSSL::_create)() = NULL;
@@ -50,6 +52,35 @@ bool StreamPeerSSL::is_available() {
return available;
}
+PoolByteArray StreamPeerSSL::get_project_cert_array() {
+
+ PoolByteArray out;
+ String certs_path = GLOBAL_DEF("network/ssl/certificates", "");
+ ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt"));
+
+ if (certs_path != "") {
+
+ FileAccess *f = FileAccess::open(certs_path, FileAccess::READ);
+ if (f) {
+ int flen = f->get_len();
+ out.resize(flen + 1);
+ {
+ PoolByteArray::Write w = out.write();
+ f->get_buffer(w.ptr(), flen);
+ w[flen] = 0; //end f string
+ }
+
+ memdelete(f);
+
+#ifdef DEBUG_ENABLED
+ print_line("Loaded certs from '" + certs_path);
+#endif
+ }
+ }
+
+ return out;
+}
+
void StreamPeerSSL::_bind_methods() {
ClassDB::bind_method(D_METHOD("poll"), &StreamPeerSSL::poll);
diff --git a/core/io/stream_peer_ssl.h b/core/io/stream_peer_ssl.h
index f903438c2..77301a7c8 100644
--- a/core/io/stream_peer_ssl.h
+++ b/core/io/stream_peer_ssl.h
@@ -66,6 +66,7 @@ public:
static StreamPeerSSL *create();
+ static PoolByteArray get_project_cert_array();
static void load_certs_from_memory(const PoolByteArray &p_memory);
static bool is_available();
diff --git a/core/math/geometry.h b/core/math/geometry.h
index ca4363e12..73a53c53b 100644
--- a/core/math/geometry.h
+++ b/core/math/geometry.h
@@ -502,16 +502,15 @@ public:
}
static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) {
- int as_x = s.x - a.x;
- int as_y = s.y - a.y;
+ Vector2 an = a - s;
+ Vector2 bn = b - s;
+ Vector2 cn = c - s;
- bool s_ab = (b.x - a.x) * as_y - (b.y - a.y) * as_x > 0;
+ bool orientation = an.cross(bn) > 0;
- if (((c.x - a.x) * as_y - (c.y - a.y) * as_x > 0) == s_ab) return false;
+ if ((bn.cross(cn) > 0) != orientation) return false;
- if (((c.x - b.x) * (s.y - b.y) - (c.y - b.y) * (s.x - b.x) > 0) != s_ab) return false;
-
- return true;
+ return (cn.cross(an) > 0) == orientation;
}
static bool is_point_in_polygon(const Vector2 &p_point, const Vector<Vector2> &p_polygon);
diff --git a/core/math/math_2d.cpp b/core/math/math_2d.cpp
index 3767d298a..a053ffbd9 100644
--- a/core/math/math_2d.cpp
+++ b/core/math/math_2d.cpp
@@ -103,6 +103,16 @@ Vector2 Vector2::floor() const {
return Vector2(Math::floor(x), Math::floor(y));
}
+Vector2 Vector2::ceil() const {
+
+ return Vector2(Math::ceil(x), Math::ceil(y));
+}
+
+Vector2 Vector2::round() const {
+
+ return Vector2(Math::round(x), Math::round(y));
+}
+
Vector2 Vector2::rotated(real_t p_by) const {
Vector2 v;
diff --git a/core/math/math_2d.h b/core/math/math_2d.h
index e7188da85..611d47e3f 100644
--- a/core/math/math_2d.h
+++ b/core/math/math_2d.h
@@ -162,6 +162,8 @@ struct Vector2 {
}
Vector2 floor() const;
+ Vector2 ceil() const;
+ Vector2 round() const;
Vector2 snapped(const Vector2 &p_by) const;
real_t aspect() const { return width / height; }
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 26e87f009..20001bb9a 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -215,11 +215,11 @@ public:
}
static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) {
double rng = max - min;
- return min + (value - min) - (rng * Math::floor((value - min) / rng));
+ return value - (rng * Math::floor((value - min) / rng));
}
static _ALWAYS_INLINE_ float wrapf(float value, float min, float max) {
float rng = max - min;
- return min + (value - min) - (rng * Math::floor((value - min) / rng));
+ return value - (rng * Math::floor((value - min) / rng));
}
// double only, as these functions are mainly used by the editor and not performance-critical,
diff --git a/core/math/matrix3.cpp b/core/math/matrix3.cpp
index 189b1ef9b..b0b05d1ec 100644
--- a/core/math/matrix3.cpp
+++ b/core/math/matrix3.cpp
@@ -254,7 +254,7 @@ void Basis::set_scale(const Vector3 &p_scale) {
set_axis(2, get_axis(2).normalized() * p_scale.z);
}
-Vector3 Basis::get_scale() const {
+Vector3 Basis::get_scale_abs() const {
return Vector3(
Vector3(elements[0][0], elements[1][0], elements[2][0]).length(),
@@ -262,7 +262,13 @@ Vector3 Basis::get_scale() const {
Vector3(elements[0][2], elements[1][2], elements[2][2]).length());
}
-Vector3 Basis::get_signed_scale() const {
+Vector3 Basis::get_scale_local() const {
+ real_t det_sign = determinant() > 0 ? 1 : -1;
+ return det_sign * Vector3(elements[0].length(), elements[1].length(), elements[2].length());
+}
+
+// get_scale works with get_rotation, use get_scale_abs if you need to enforce positive signature.
+Vector3 Basis::get_scale() const {
// FIXME: We are assuming M = R.S (R is rotation and S is scaling), and use polar decomposition to extract R and S.
// A polar decomposition is M = O.P, where O is an orthogonal matrix (meaning rotation and reflection) and
// P is a positive semi-definite matrix (meaning it contains absolute values of scaling along its diagonal).
@@ -342,6 +348,14 @@ void Basis::rotate(const Vector3 &p_euler) {
*this = rotated(p_euler);
}
+Basis Basis::rotated(const Quat &p_quat) const {
+ return Basis(p_quat) * (*this);
+}
+
+void Basis::rotate(const Quat &p_quat) {
+ *this = rotated(p_quat);
+}
+
// TODO: rename this to get_rotation_euler
Vector3 Basis::get_rotation() const {
// Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,
@@ -371,6 +385,22 @@ void Basis::get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const {
m.get_axis_angle(p_axis, p_angle);
}
+void Basis::get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const {
+ // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,
+ // and returns the Euler angles corresponding to the rotation part, complementing get_scale().
+ // See the comment in get_scale() for further information.
+ Basis m = transposed();
+ m.orthonormalize();
+ real_t det = m.determinant();
+ if (det < 0) {
+ // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles.
+ m.scale(Vector3(-1, -1, -1));
+ }
+
+ m.get_axis_angle(p_axis, p_angle);
+ p_angle = -p_angle;
+}
+
// get_euler_xyz returns a vector containing the Euler angles in the format
// (a1,a2,a3), where a3 is the angle of the first rotation, and a1 is the last
// (following the convention they are commonly defined in the literature).
@@ -767,3 +797,32 @@ void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_phi) {
elements[2][1] = p_axis.y * p_axis.z * (1.0 - cosine) + p_axis.x * sine;
elements[2][2] = axis_sq.z + cosine * (1.0 - axis_sq.z);
}
+
+void Basis::set_axis_angle_scale(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) {
+ set_diagonal(p_scale);
+ rotate(p_axis, p_phi);
+}
+
+void Basis::set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale) {
+ set_diagonal(p_scale);
+ rotate(p_euler);
+}
+
+void Basis::set_quat_scale(const Quat &p_quat, const Vector3 &p_scale) {
+ set_diagonal(p_scale);
+ rotate(p_quat);
+}
+
+void Basis::set_diagonal(const Vector3 p_diag) {
+ elements[0][0] = p_diag.x;
+ elements[0][1] = 0;
+ elements[0][2] = 0;
+
+ elements[1][0] = 0;
+ elements[1][1] = p_diag.y;
+ elements[1][2] = 0;
+
+ elements[2][0] = 0;
+ elements[2][1] = 0;
+ elements[2][2] = p_diag.z;
+}
diff --git a/core/math/matrix3.h b/core/math/matrix3.h
index c42643572..fd383fc67 100644
--- a/core/math/matrix3.h
+++ b/core/math/matrix3.h
@@ -81,8 +81,12 @@ public:
void rotate(const Vector3 &p_euler);
Basis rotated(const Vector3 &p_euler) const;
+ void rotate(const Quat &p_quat);
+ Basis rotated(const Quat &p_quat) const;
+
Vector3 get_rotation() const;
void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const;
+ void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const;
Vector3 rotref_posscale_decomposition(Basis &rotref) const;
@@ -108,7 +112,12 @@ public:
void set_scale(const Vector3 &p_scale);
Vector3 get_scale() const;
- Vector3 get_signed_scale() const;
+ Vector3 get_scale_abs() const;
+ Vector3 get_scale_local() const;
+
+ void set_axis_angle_scale(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale);
+ void set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale);
+ void set_quat_scale(const Quat &p_quat, const Vector3 &p_scale);
// transposed dot products
_FORCE_INLINE_ real_t tdotx(const Vector3 &v) const {
@@ -140,6 +149,8 @@ public:
int get_orthogonal_index() const;
void set_orthogonal_index(int p_index);
+ void set_diagonal(const Vector3 p_diag);
+
bool is_orthogonal() const;
bool is_diagonal() const;
bool is_rotation() const;
@@ -219,6 +230,8 @@ public:
Basis(const Quat &p_quat) { set_quat(p_quat); };
Basis(const Vector3 &p_euler) { set_euler(p_euler); }
Basis(const Vector3 &p_axis, real_t p_phi) { set_axis_angle(p_axis, p_phi); }
+ Basis(const Vector3 &p_axis, real_t p_phi, const Vector3 &p_scale) { set_axis_angle_scale(p_axis, p_phi, p_scale); }
+ Basis(const Quat &p_quat, const Vector3 &p_scale) { set_quat_scale(p_quat, p_scale); }
_FORCE_INLINE_ Basis(const Vector3 &row0, const Vector3 &row1, const Vector3 &row2) {
elements[0] = row0;
diff --git a/core/math/quat.cpp b/core/math/quat.cpp
index 9aa8b537d..4f61401ac 100644
--- a/core/math/quat.cpp
+++ b/core/math/quat.cpp
@@ -89,7 +89,7 @@ void Quat::set_euler_yxz(const Vector3 &p_euler) {
set(sin_a1 * cos_a2 * sin_a3 + cos_a1 * sin_a2 * cos_a3,
sin_a1 * cos_a2 * cos_a3 - cos_a1 * sin_a2 * sin_a3,
- -sin_a1 * sin_a2 * cos_a3 + cos_a1 * sin_a2 * sin_a3,
+ -sin_a1 * sin_a2 * cos_a3 + cos_a1 * cos_a2 * sin_a3,
sin_a1 * sin_a2 * sin_a3 + cos_a1 * cos_a2 * cos_a3);
}
diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp
index 102e454e0..fc9041741 100644
--- a/core/math/quick_hull.cpp
+++ b/core/math/quick_hull.cpp
@@ -74,7 +74,7 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me
int longest_axis = aabb.get_longest_axis_index();
//first two vertices are the most distant
- int simplex[4];
+ int simplex[4] = { 0 };
{
real_t max = 0, min = 0;
diff --git a/core/math/transform.cpp b/core/math/transform.cpp
index f727d00e3..7cd186ca6 100644
--- a/core/math/transform.cpp
+++ b/core/math/transform.cpp
@@ -119,11 +119,11 @@ Transform Transform::interpolate_with(const Transform &p_transform, real_t p_c)
/* not sure if very "efficient" but good enough? */
- Vector3 src_scale = basis.get_signed_scale();
+ Vector3 src_scale = basis.get_scale();
Quat src_rot = basis.orthonormalized();
Vector3 src_loc = origin;
- Vector3 dst_scale = p_transform.basis.get_signed_scale();
+ Vector3 dst_scale = p_transform.basis.get_scale();
Quat dst_rot = p_transform.basis;
Vector3 dst_loc = p_transform.origin;
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 10ec4f564..3bbfd7627 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -103,6 +103,7 @@ struct Vector3 {
_FORCE_INLINE_ Vector3 floor() const;
_FORCE_INLINE_ Vector3 sign() const;
_FORCE_INLINE_ Vector3 ceil() const;
+ _FORCE_INLINE_ Vector3 round() const;
_FORCE_INLINE_ real_t distance_to(const Vector3 &p_b) const;
_FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_b) const;
@@ -204,6 +205,11 @@ Vector3 Vector3::ceil() const {
return Vector3(Math::ceil(x), Math::ceil(y), Math::ceil(z));
}
+Vector3 Vector3::round() const {
+
+ return Vector3(Math::round(x), Math::round(y), Math::round(z));
+}
+
Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const {
return Vector3(
diff --git a/core/node_path.cpp b/core/node_path.cpp
index cd7ad7753..64983fc09 100644
--- a/core/node_path.cpp
+++ b/core/node_path.cpp
@@ -264,8 +264,9 @@ NodePath NodePath::get_as_property_path() const {
Vector<StringName> new_path = data->subpath;
String initial_subname = data->path[0];
+
for (size_t i = 1; i < data->path.size(); i++) {
- initial_subname += i == 0 ? data->path[i].operator String() : "/" + data->path[i];
+ initial_subname += "/" + data->path[i];
}
new_path.insert(0, initial_subname);
diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h
index 280aea6a1..0b3b40f30 100644
--- a/core/oa_hash_map.h
+++ b/core/oa_hash_map.h
@@ -36,176 +36,181 @@
#include "os/copymem.h"
#include "os/memory.h"
-// uncomment this to disable initial local storage.
-#define OA_HASH_MAP_INITIAL_LOCAL_STORAGE
-
/**
- * This class implements a hash map datastructure that uses open addressing with
- * local probing.
- *
- * It can give huge performance improvements over a chained HashMap because of
- * the increased data locality.
- *
- * Because of that locality property it's important to not use "large" value
- * types as the "TData" type. If TData values are too big it can cause more
- * cache misses then chaining. If larger values are needed then storing those
- * in a separate array and using pointers or indices to reference them is the
- * better solution.
- *
- * This hash map also implements real-time incremental rehashing.
+ * A HashMap implementation that uses open addressing with robinhood hashing.
+ * Robinhood hashing swaps out entries that have a smaller probing distance
+ * than the to-be-inserted entry, that evens out the average probing distance
+ * and enables faster lookups.
*
+ * The entries are stored inplace, so huge keys or values might fill cache lines
+ * a lot faster.
*/
-template <class TKey, class TData,
- uint16_t INITIAL_NUM_ELEMENTS = 64,
+template <class TKey, class TValue,
class Hasher = HashMapHasherDefault,
class Comparator = HashMapComparatorDefault<TKey> >
class OAHashMap {
private:
-#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE
- TData local_data[INITIAL_NUM_ELEMENTS];
- TKey local_keys[INITIAL_NUM_ELEMENTS];
- uint32_t local_hashes[INITIAL_NUM_ELEMENTS];
- uint8_t local_flags[INITIAL_NUM_ELEMENTS / 4 + (INITIAL_NUM_ELEMENTS % 4 != 0 ? 1 : 0)];
-#endif
+ TValue *values;
+ TKey *keys;
+ uint32_t *hashes;
- struct {
- TData *data;
- TKey *keys;
- uint32_t *hashes;
+ uint32_t capacity;
- // This is actually an array of bits, 4 bit pairs per octet.
- // | ba ba ba ba | ba ba ba ba | ....
- //
- // if a is set it means that there is an element present.
- // if b is set it means that an element was deleted. This is needed for
- // the local probing to work without relocating any succeeding and
- // colliding entries.
- uint8_t *flags;
+ uint32_t num_elements;
- uint32_t capacity;
- } table, old_table;
+ static const uint32_t EMPTY_HASH = 0;
+ static const uint32_t DELETED_HASH_BIT = 1 << 31;
- bool is_rehashing;
- uint32_t rehash_position;
- uint32_t rehash_amount;
+ _FORCE_INLINE_ uint32_t _hash(const TKey &p_key) {
+ uint32_t hash = Hasher::hash(p_key);
- uint32_t elements;
+ if (hash == EMPTY_HASH) {
+ hash = EMPTY_HASH + 1;
+ } else if (hash & DELETED_HASH_BIT) {
+ hash &= ~DELETED_HASH_BIT;
+ }
- /* Methods */
+ return hash;
+ }
- // returns true if the value already existed, false if it's a new entry
- bool _raw_set_with_hash(uint32_t p_hash, const TKey &p_key, const TData &p_data) {
- for (int i = 0; i < table.capacity; i++) {
+ _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash) {
+ p_hash = p_hash & ~DELETED_HASH_BIT; // we don't care if it was deleted or not
- int pos = (p_hash + i) % table.capacity;
+ uint32_t original_pos = p_hash % capacity;
- int flags_pos = pos / 4;
- int flags_pos_offset = pos % 4;
+ return p_pos - original_pos;
+ }
- bool is_filled_flag = table.flags[flags_pos] & (1 << (2 * flags_pos_offset));
- bool is_deleted_flag = table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1));
+ _FORCE_INLINE_ void _construct(uint32_t p_pos, uint32_t p_hash, const TKey &p_key, const TValue &p_value) {
+ memnew_placement(&keys[p_pos], TKey(p_key));
+ memnew_placement(&values[p_pos], TValue(p_value));
+ hashes[p_pos] = p_hash;
- if (is_filled_flag) {
- if (table.hashes[pos] == p_hash && Comparator::compare(table.keys[pos], p_key)) {
- table.data[pos] = p_data;
- return true;
- }
- continue;
+ num_elements++;
+ }
+
+ bool _lookup_pos(const TKey &p_key, uint32_t &r_pos) {
+ uint32_t hash = _hash(p_key);
+ uint32_t pos = hash % capacity;
+ uint32_t distance = 0;
+
+ while (42) {
+ if (hashes[pos] == EMPTY_HASH) {
+ return false;
}
- table.keys[pos] = p_key;
- table.data[pos] = p_data;
- table.hashes[pos] = p_hash;
+ if (distance > _get_probe_length(pos, hashes[pos])) {
+ return false;
+ }
- table.flags[flags_pos] |= (1 << (2 * flags_pos_offset));
- table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset + 1));
+ if (hashes[pos] == hash && Comparator::compare(keys[pos], p_key)) {
+ r_pos = pos;
+ return true;
+ }
- return false;
+ pos = (pos + 1) % capacity;
+ distance++;
}
- return false;
}
-public:
- _FORCE_INLINE_ uint32_t get_capacity() const { return table.capacity; }
- _FORCE_INLINE_ uint32_t get_num_elements() const { return elements; }
+ void _insert_with_hash(uint32_t p_hash, const TKey &p_key, const TValue &p_value) {
- void set(const TKey &p_key, const TData &p_data) {
+ uint32_t hash = p_hash;
+ uint32_t distance = 0;
+ uint32_t pos = hash % capacity;
- uint32_t hash = Hasher::hash(p_key);
+ TKey key = p_key;
+ TValue value = p_value;
- // We don't progress the rehashing if the table just got resized
- // to keep the cost of this function low.
- if (is_rehashing) {
+ while (42) {
+ if (hashes[pos] == EMPTY_HASH) {
+ _construct(pos, hash, p_key, p_value);
- // rehash progress
+ return;
+ }
- for (int i = 0; i <= rehash_amount && rehash_position < old_table.capacity; rehash_position++) {
+ // not an empty slot, let's check the probing length of the existing one
+ uint32_t existing_probe_len = _get_probe_length(pos, hashes[pos]);
+ if (existing_probe_len < distance) {
- int flags_pos = rehash_position / 4;
- int flags_pos_offset = rehash_position % 4;
+ if (hashes[pos] & DELETED_HASH_BIT) {
+ // we found a place where we can fit in!
+ _construct(pos, hash, p_key, p_value);
- bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0;
- bool is_deleted_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0;
+ return;
+ }
- if (is_filled_flag) {
- _raw_set_with_hash(old_table.hashes[rehash_position], old_table.keys[rehash_position], old_table.data[rehash_position]);
+ SWAP(hash, hashes[pos]);
+ SWAP(key, keys[pos]);
+ SWAP(value, values[pos]);
+ distance = existing_probe_len;
+ }
- old_table.keys[rehash_position].~TKey();
- old_table.data[rehash_position].~TData();
+ pos = (pos + 1) % capacity;
+ distance++;
+ }
+ }
+ void _resize_and_rehash() {
- memnew_placement(&old_table.keys[rehash_position], TKey);
- memnew_placement(&old_table.data[rehash_position], TData);
+ TKey *old_keys = keys;
+ TValue *old_values = values;
+ uint32_t *old_hashes = hashes;
- old_table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset));
- old_table.flags[flags_pos] |= (1 << (2 * flags_pos_offset + 1));
- }
- }
+ uint32_t old_capacity = capacity;
- if (rehash_position >= old_table.capacity) {
+ capacity = old_capacity * 2;
+ num_elements = 0;
- // wohooo, we can get rid of the old table.
- is_rehashing = false;
+ keys = memnew_arr(TKey, capacity);
+ values = memnew_arr(TValue, capacity);
+ hashes = memnew_arr(uint32_t, capacity);
-#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE
- if (old_table.data == local_data) {
- // Everything is local, so no cleanup :P
- } else
-#endif
- {
- memdelete_arr(old_table.data);
- memdelete_arr(old_table.keys);
- memdelete_arr(old_table.hashes);
- memdelete_arr(old_table.flags);
- }
- }
+ for (int i = 0; i < capacity; i++) {
+ hashes[i] = 0;
}
- // Table is almost full, resize and start rehashing process.
- if (elements >= table.capacity * 0.7) {
+ for (uint32_t i = 0; i < old_capacity; i++) {
+ if (old_hashes[i] == EMPTY_HASH) {
+ continue;
+ }
+ if (old_hashes[i] & DELETED_HASH_BIT) {
+ continue;
+ }
- old_table.capacity = table.capacity;
- old_table.data = table.data;
- old_table.flags = table.flags;
- old_table.hashes = table.hashes;
- old_table.keys = table.keys;
+ _insert_with_hash(old_hashes[i], old_keys[i], old_values[i]);
+ }
- table.capacity = old_table.capacity * 2;
+ memdelete_arr(old_keys);
+ memdelete_arr(old_values);
+ memdelete_arr(old_hashes);
+ }
- table.data = memnew_arr(TData, table.capacity);
- table.flags = memnew_arr(uint8_t, table.capacity / 4 + (table.capacity % 4 != 0 ? 1 : 0));
- table.hashes = memnew_arr(uint32_t, table.capacity);
- table.keys = memnew_arr(TKey, table.capacity);
+public:
+ _FORCE_INLINE_ uint32_t get_capacity() const { return capacity; }
+ _FORCE_INLINE_ uint32_t get_num_elements() const { return num_elements; }
- zeromem(table.flags, table.capacity / 4 + (table.capacity % 4 != 0 ? 1 : 0));
+ void insert(const TKey &p_key, const TValue &p_value) {
- is_rehashing = true;
- rehash_position = 0;
- rehash_amount = (elements * 2) / (table.capacity * 0.7 - old_table.capacity);
+ if ((float)num_elements / (float)capacity > 0.9) {
+ _resize_and_rehash();
}
- if (!_raw_set_with_hash(hash, p_key, p_data))
- elements++;
+ uint32_t hash = _hash(p_key);
+
+ _insert_with_hash(hash, p_key, p_value);
+ }
+
+ void set(const TKey &p_key, const TValue &p_data) {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
+
+ if (exists) {
+ values[pos].~TValue();
+ memnew_placement(&values[pos], TValue(p_data));
+ } else {
+ insert(p_key, p_data);
+ }
}
/**
@@ -214,380 +219,108 @@ public:
* if r_data is not NULL then the value will be written to the object
* it points to.
*/
- bool lookup(const TKey &p_key, TData *r_data) {
-
- uint32_t hash = Hasher::hash(p_key);
-
- bool check_old_table = is_rehashing;
- bool check_new_table = true;
-
- // search for the key and return the value associated with it
- //
- // if we're rehashing we need to check both the old and the
- // current table. If we find a value in the old table we still
- // need to continue searching in the new table as it might have
- // been added after
-
- TData *value = NULL;
-
- for (int i = 0; i < table.capacity; i++) {
-
- if (!check_new_table && !check_old_table) {
-
- break;
- }
-
- // if we're rehashing check the old table
- if (check_old_table && i < old_table.capacity) {
-
- int pos = (hash + i) % old_table.capacity;
-
- int flags_pos = pos / 4;
- int flags_pos_offset = pos % 4;
-
- bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0;
- bool is_deleted_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0;
-
- if (is_filled_flag) {
- // found our entry?
- if (old_table.hashes[pos] == hash && Comparator::compare(old_table.keys[pos], p_key)) {
- value = &old_table.data[pos];
- check_old_table = false;
- }
- } else if (!is_deleted_flag) {
-
- // we hit an empty field here, we don't
- // need to further check this old table
- // because we know it's not in here.
-
- check_old_table = false;
- }
- }
+ bool lookup(const TKey &p_key, TValue &r_data) {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
- if (check_new_table) {
-
- int pos = (hash + i) % table.capacity;
-
- int flags_pos = pos / 4;
- int flags_pos_offset = pos % 4;
-
- bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0;
- bool is_deleted_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0;
-
- if (is_filled_flag) {
- // found our entry?
- if (table.hashes[pos] == hash && Comparator::compare(table.keys[pos], p_key)) {
- if (r_data != NULL)
- *r_data = table.data[pos];
- return true;
- }
- continue;
- } else if (is_deleted_flag) {
- continue;
- } else if (value != NULL) {
-
- // We found a value in the old table
- if (r_data != NULL)
- *r_data = *value;
- return true;
- } else {
- check_new_table = false;
- }
- }
- }
-
- if (value != NULL) {
- if (r_data != NULL)
- *r_data = *value;
+ if (exists) {
+ r_data.~TValue();
+ memnew_placement(&r_data, TValue(values[pos]));
return true;
}
+
return false;
}
_FORCE_INLINE_ bool has(const TKey &p_key) {
- return lookup(p_key, NULL);
+ uint32_t _pos = 0;
+ return _lookup_pos(p_key, _pos);
}
void remove(const TKey &p_key) {
- uint32_t hash = Hasher::hash(p_key);
-
- bool check_old_table = is_rehashing;
- bool check_new_table = true;
-
- for (int i = 0; i < table.capacity; i++) {
-
- if (!check_new_table && !check_old_table) {
- return;
- }
-
- // if we're rehashing check the old table
- if (check_old_table && i < old_table.capacity) {
-
- int pos = (hash + i) % old_table.capacity;
-
- int flags_pos = pos / 4;
- int flags_pos_offset = pos % 4;
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
- bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0;
- bool is_deleted_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0;
-
- if (is_filled_flag) {
- // found our entry?
- if (old_table.hashes[pos] == hash && Comparator::compare(old_table.keys[pos], p_key)) {
- old_table.keys[pos].~TKey();
- old_table.data[pos].~TData();
-
- memnew_placement(&old_table.keys[pos], TKey);
- memnew_placement(&old_table.data[pos], TData);
-
- old_table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset));
- old_table.flags[flags_pos] |= (1 << (2 * flags_pos_offset + 1));
-
- elements--;
- return;
- }
- } else if (!is_deleted_flag) {
-
- // we hit an empty field here, we don't
- // need to further check this old table
- // because we know it's not in here.
-
- check_old_table = false;
- }
- }
-
- if (check_new_table) {
-
- int pos = (hash + i) % table.capacity;
-
- int flags_pos = pos / 4;
- int flags_pos_offset = pos % 4;
-
- bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0;
- bool is_deleted_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset + 1))) > 0;
-
- if (is_filled_flag) {
- // found our entry?
- if (table.hashes[pos] == hash && Comparator::compare(table.keys[pos], p_key)) {
- table.keys[pos].~TKey();
- table.data[pos].~TData();
-
- memnew_placement(&table.keys[pos], TKey);
- memnew_placement(&table.data[pos], TData);
-
- table.flags[flags_pos] &= ~(1 << (2 * flags_pos_offset));
- table.flags[flags_pos] |= (1 << (2 * flags_pos_offset + 1));
-
- // don't return here, this value might still be in the old table
- // if it was already relocated.
-
- elements--;
- return;
- }
- continue;
- } else if (is_deleted_flag) {
- continue;
- } else {
- check_new_table = false;
- }
- }
+ if (!exists) {
+ return;
}
+
+ hashes[pos] |= DELETED_HASH_BIT;
+ values[pos].~TValue();
+ keys[pos].~TKey();
+ num_elements--;
}
struct Iterator {
bool valid;
- uint32_t hash;
-
const TKey *key;
- const TData *data;
+ const TValue *value;
private:
+ uint32_t pos;
friend class OAHashMap;
- bool was_from_old_table;
};
Iterator iter() const {
Iterator it;
- it.valid = false;
- it.was_from_old_table = false;
-
- bool check_old_table = is_rehashing;
-
- for (int i = 0; i < table.capacity; i++) {
-
- // if we're rehashing check the old table first
- if (check_old_table && i < old_table.capacity) {
-
- int pos = i;
-
- int flags_pos = pos / 4;
- int flags_pos_offset = pos % 4;
-
- bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0;
-
- if (is_filled_flag) {
- it.valid = true;
- it.hash = old_table.hashes[pos];
- it.data = &old_table.data[pos];
- it.key = &old_table.keys[pos];
-
- it.was_from_old_table = true;
-
- return it;
- }
- }
-
- {
-
- int pos = i;
-
- int flags_pos = pos / 4;
- int flags_pos_offset = pos % 4;
-
- bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0;
-
- if (is_filled_flag) {
- it.valid = true;
- it.hash = table.hashes[pos];
- it.data = &table.data[pos];
- it.key = &table.keys[pos];
-
- return it;
- }
- }
- }
+ it.valid = true;
+ it.pos = 0;
- return it;
+ return next_iter(it);
}
Iterator next_iter(const Iterator &p_iter) const {
+
if (!p_iter.valid) {
return p_iter;
}
Iterator it;
-
it.valid = false;
- it.was_from_old_table = false;
-
- bool check_old_table = is_rehashing;
-
- // we use this to skip the first check or not
- bool was_from_old_table = p_iter.was_from_old_table;
-
- int prev_index = (p_iter.data - (p_iter.was_from_old_table ? old_table.data : table.data));
-
- if (!was_from_old_table) {
- prev_index++;
- }
-
- for (int i = prev_index; i < table.capacity; i++) {
-
- // if we're rehashing check the old table first
- if (check_old_table && i < old_table.capacity && !was_from_old_table) {
-
- int pos = i;
-
- int flags_pos = pos / 4;
- int flags_pos_offset = pos % 4;
+ it.pos = p_iter.pos;
+ it.key = NULL;
+ it.value = NULL;
- bool is_filled_flag = (old_table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0;
+ for (uint32_t i = it.pos; i < capacity; i++) {
+ it.pos = i + 1;
- if (is_filled_flag) {
- it.valid = true;
- it.hash = old_table.hashes[pos];
- it.data = &old_table.data[pos];
- it.key = &old_table.keys[pos];
-
- it.was_from_old_table = true;
-
- return it;
- }
+ if (hashes[i] == EMPTY_HASH) {
+ continue;
}
-
- was_from_old_table = false;
-
- {
- int pos = i;
-
- int flags_pos = pos / 4;
- int flags_pos_offset = pos % 4;
-
- bool is_filled_flag = (table.flags[flags_pos] & (1 << (2 * flags_pos_offset))) > 0;
-
- if (is_filled_flag) {
- it.valid = true;
- it.hash = table.hashes[pos];
- it.data = &table.data[pos];
- it.key = &table.keys[pos];
-
- return it;
- }
+ if (hashes[i] & DELETED_HASH_BIT) {
+ continue;
}
+
+ it.valid = true;
+ it.key = &keys[i];
+ it.value = &values[i];
+ return it;
}
return it;
}
- OAHashMap(uint32_t p_initial_capacity = INITIAL_NUM_ELEMENTS) {
+ OAHashMap(uint32_t p_initial_capacity = 64) {
-#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE
+ capacity = p_initial_capacity;
+ num_elements = 0;
- if (p_initial_capacity <= INITIAL_NUM_ELEMENTS) {
- table.data = local_data;
- table.keys = local_keys;
- table.hashes = local_hashes;
- table.flags = local_flags;
+ keys = memnew_arr(TKey, p_initial_capacity);
+ values = memnew_arr(TValue, p_initial_capacity);
+ hashes = memnew_arr(uint32_t, p_initial_capacity);
- zeromem(table.flags, INITIAL_NUM_ELEMENTS / 4 + (INITIAL_NUM_ELEMENTS % 4 != 0 ? 1 : 0));
-
- table.capacity = INITIAL_NUM_ELEMENTS;
- elements = 0;
- } else
-#endif
- {
- table.data = memnew_arr(TData, p_initial_capacity);
- table.keys = memnew_arr(TKey, p_initial_capacity);
- table.hashes = memnew_arr(uint32_t, p_initial_capacity);
- table.flags = memnew_arr(uint8_t, p_initial_capacity / 4 + (p_initial_capacity % 4 != 0 ? 1 : 0));
-
- zeromem(table.flags, p_initial_capacity / 4 + (p_initial_capacity % 4 != 0 ? 1 : 0));
-
- table.capacity = p_initial_capacity;
- elements = 0;
+ for (int i = 0; i < p_initial_capacity; i++) {
+ hashes[i] = 0;
}
-
- is_rehashing = false;
- rehash_position = 0;
}
~OAHashMap() {
-#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE
- if (table.capacity <= INITIAL_NUM_ELEMENTS) {
- return; // Everything is local, so no cleanup :P
- }
-#endif
- if (is_rehashing) {
-
-#ifdef OA_HASH_MAP_INITIAL_LOCAL_STORAGE
- if (old_table.data == local_data) {
- // Everything is local, so no cleanup :P
- } else
-#endif
- {
- memdelete_arr(old_table.data);
- memdelete_arr(old_table.keys);
- memdelete_arr(old_table.hashes);
- memdelete_arr(old_table.flags);
- }
- }
- memdelete_arr(table.data);
- memdelete_arr(table.keys);
- memdelete_arr(table.hashes);
- memdelete_arr(table.flags);
+ memdelete_arr(keys);
+ memdelete_arr(values);
+ memdelete(hashes);
}
};
diff --git a/core/os/input.cpp b/core/os/input.cpp
index 3089ab2ce..a5b0f91e6 100644
--- a/core/os/input.cpp
+++ b/core/os/input.cpp
@@ -57,6 +57,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_action_pressed", "action"), &Input::is_action_pressed);
ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action"), &Input::is_action_just_pressed);
ClassDB::bind_method(D_METHOD("is_action_just_released", "action"), &Input::is_action_just_released);
+ ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &Input::get_action_strength);
ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false));
ClassDB::bind_method(D_METHOD("remove_joy_mapping", "guid"), &Input::remove_joy_mapping);
ClassDB::bind_method(D_METHOD("joy_connection_changed", "device", "connected", "name", "guid"), &Input::joy_connection_changed);
@@ -85,6 +86,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("warp_mouse_position", "to"), &Input::warp_mouse_position);
ClassDB::bind_method(D_METHOD("action_press", "action"), &Input::action_press);
ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release);
+ ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW));
ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2()));
ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event);
@@ -111,14 +113,14 @@ void Input::_bind_methods() {
BIND_ENUM_CONSTANT(CURSOR_HSPLIT);
BIND_ENUM_CONSTANT(CURSOR_HELP);
- ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "connected")));
+ ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "device"), PropertyInfo(Variant::BOOL, "connected")));
}
void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
#ifdef TOOLS_ENABLED
String pf = p_function;
- if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released")) {
+ if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength")) {
List<PropertyInfo> pinfo;
ProjectSettings::get_singleton()->get_property_list(&pinfo);
diff --git a/core/os/input.h b/core/os/input.h
index 9c7595ff7..001871c5d 100644
--- a/core/os/input.h
+++ b/core/os/input.h
@@ -85,6 +85,7 @@ public:
virtual bool is_action_pressed(const StringName &p_action) const = 0;
virtual bool is_action_just_pressed(const StringName &p_action) const = 0;
virtual bool is_action_just_released(const StringName &p_action) const = 0;
+ virtual float get_action_strength(const StringName &p_action) const = 0;
virtual float get_joy_axis(int p_device, int p_axis) const = 0;
virtual String get_joy_name(int p_idx) = 0;
@@ -117,8 +118,11 @@ public:
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const;
- virtual bool is_emulating_touchscreen() const = 0;
+ virtual bool is_emulating_touch_from_mouse() const = 0;
+ virtual bool is_emulating_mouse_from_touch() const = 0;
+ virtual CursorShape get_default_cursor_shape() = 0;
+ virtual void set_default_cursor_shape(CursorShape p_shape) = 0;
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) = 0;
virtual void set_mouse_in_window(bool p_in_window) = 0;
diff --git a/core/os/input_event.cpp b/core/os/input_event.cpp
index b9607632f..4ebb821a2 100644
--- a/core/os/input_event.cpp
+++ b/core/os/input_event.cpp
@@ -41,11 +41,6 @@ int InputEvent::get_device() const {
return device;
}
-bool InputEvent::is_pressed() const {
-
- return false;
-}
-
bool InputEvent::is_action(const StringName &p_action) const {
return InputMap::get_singleton()->event_is_action(Ref<InputEvent>((InputEvent *)this), p_action);
@@ -53,11 +48,29 @@ bool InputEvent::is_action(const StringName &p_action) const {
bool InputEvent::is_action_pressed(const StringName &p_action) const {
- return (is_pressed() && !is_echo() && is_action(p_action));
+ bool pressed;
+ bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed);
+ return valid && pressed && !is_echo();
}
+
bool InputEvent::is_action_released(const StringName &p_action) const {
- return (!is_pressed() && is_action(p_action));
+ bool pressed;
+ bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed);
+ return valid && !pressed;
+}
+
+float InputEvent::get_action_strength(const StringName &p_action) const {
+
+ bool pressed;
+ float strength;
+ bool valid = InputMap::get_singleton()->event_get_action_status(Ref<InputEvent>((InputEvent *)this), p_action, &pressed, &strength);
+ return valid ? strength : 0.0f;
+}
+
+bool InputEvent::is_pressed() const {
+
+ return false;
}
bool InputEvent::is_echo() const {
@@ -75,7 +88,7 @@ String InputEvent::as_text() const {
return String();
}
-bool InputEvent::action_match(const Ref<InputEvent> &p_event) const {
+bool InputEvent::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const {
return false;
}
@@ -95,15 +108,16 @@ void InputEvent::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_device", "device"), &InputEvent::set_device);
ClassDB::bind_method(D_METHOD("get_device"), &InputEvent::get_device);
- ClassDB::bind_method(D_METHOD("is_pressed"), &InputEvent::is_pressed);
ClassDB::bind_method(D_METHOD("is_action", "action"), &InputEvent::is_action);
ClassDB::bind_method(D_METHOD("is_action_pressed", "action"), &InputEvent::is_action_pressed);
ClassDB::bind_method(D_METHOD("is_action_released", "action"), &InputEvent::is_action_released);
+ ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &InputEvent::get_action_strength);
+
+ ClassDB::bind_method(D_METHOD("is_pressed"), &InputEvent::is_pressed);
ClassDB::bind_method(D_METHOD("is_echo"), &InputEvent::is_echo);
ClassDB::bind_method(D_METHOD("as_text"), &InputEvent::as_text);
- ClassDB::bind_method(D_METHOD("action_match", "event"), &InputEvent::action_match);
ClassDB::bind_method(D_METHOD("shortcut_match", "event"), &InputEvent::shortcut_match);
ClassDB::bind_method(D_METHOD("is_action_type"), &InputEvent::is_action_type);
@@ -281,7 +295,7 @@ String InputEventKey::as_text() const {
return kc;
}
-bool InputEventKey::action_match(const Ref<InputEvent> &p_event) const {
+bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const {
Ref<InputEventKey> key = p_event;
if (key.is_null())
@@ -290,7 +304,14 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event) const {
uint32_t code = get_scancode_with_modifiers();
uint32_t event_code = key->get_scancode_with_modifiers();
- return get_scancode() == key->get_scancode() && (!key->is_pressed() || (code & event_code) == code);
+ bool match = get_scancode() == key->get_scancode() && (!key->is_pressed() || (code & event_code) == code);
+ if (match) {
+ if (p_pressed != NULL)
+ *p_pressed = key->is_pressed();
+ if (p_strength != NULL)
+ *p_strength = (*p_pressed) ? 1.0f : 0.0f;
+ }
+ return match;
}
bool InputEventKey::shortcut_match(const Ref<InputEvent> &p_event) const {
@@ -446,13 +467,21 @@ Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, co
return mb;
}
-bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event) const {
+bool InputEventMouseButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const {
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_null())
return false;
- return mb->button_index == button_index;
+ bool match = mb->button_index == button_index;
+ if (match) {
+ if (p_pressed != NULL)
+ *p_pressed = mb->is_pressed();
+ if (p_strength != NULL)
+ *p_strength = (*p_pressed) ? 1.0f : 0.0f;
+ }
+
+ return match;
}
String InputEventMouseButton::as_text() const {
@@ -610,6 +639,7 @@ void InputEventJoypadMotion::set_axis_value(float p_value) {
axis_value = p_value;
}
+
float InputEventJoypadMotion::get_axis_value() const {
return axis_value;
@@ -617,16 +647,25 @@ float InputEventJoypadMotion::get_axis_value() const {
bool InputEventJoypadMotion::is_pressed() const {
- return Math::abs(axis_value) > 0.5f;
+ return Math::abs(axis_value) >= 0.5f;
}
-bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event) const {
+bool InputEventJoypadMotion::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const {
Ref<InputEventJoypadMotion> jm = p_event;
if (jm.is_null())
return false;
- return (axis == jm->axis && ((axis_value < 0) == (jm->axis_value < 0) || jm->axis_value == 0));
+ bool match = (axis == jm->axis); // Matches even if not in the same direction, but returns a "not pressed" event.
+ if (match) {
+ bool same_direction = (((axis_value < 0) == (jm->axis_value < 0)) || jm->axis_value == 0);
+ bool pressed = same_direction ? Math::abs(jm->get_axis_value()) >= p_deadzone : false;
+ if (p_pressed != NULL)
+ *p_pressed = pressed;
+ if (p_strength != NULL)
+ *p_strength = pressed ? CLAMP(Math::inverse_lerp(p_deadzone, 1.0f, Math::abs(jm->get_axis_value())), 0.0f, 1.0f) : 0.0f;
+ }
+ return match;
}
String InputEventJoypadMotion::as_text() const {
@@ -681,13 +720,21 @@ float InputEventJoypadButton::get_pressure() const {
return pressure;
}
-bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event) const {
+bool InputEventJoypadButton::action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const {
Ref<InputEventJoypadButton> jb = p_event;
if (jb.is_null())
return false;
- return button_index == jb->button_index;
+ bool match = button_index == jb->button_index;
+ if (match) {
+ if (p_pressed != NULL)
+ *p_pressed = jb->is_pressed();
+ if (p_strength != NULL)
+ *p_strength = (*p_pressed) ? 1.0f : 0.0f;
+ }
+
+ return match;
}
String InputEventJoypadButton::as_text() const {
diff --git a/core/os/input_event.h b/core/os/input_event.h
index 0a33ab18a..037649ed6 100644
--- a/core/os/input_event.h
+++ b/core/os/input_event.h
@@ -154,16 +154,21 @@ public:
void set_device(int p_device);
int get_device() const;
+ bool is_action(const StringName &p_action) const;
+ bool is_action_pressed(const StringName &p_action) const;
+ bool is_action_released(const StringName &p_action) const;
+ float get_action_strength(const StringName &p_action) const;
+
+ // To be removed someday, since they do not make sense for all events
virtual bool is_pressed() const;
- virtual bool is_action(const StringName &p_action) const;
- virtual bool is_action_pressed(const StringName &p_action) const;
- virtual bool is_action_released(const StringName &p_action) const;
virtual bool is_echo() const;
+ // ...-.
+
virtual String as_text() const;
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
- virtual bool action_match(const Ref<InputEvent> &p_event) const;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
virtual bool is_action_type() const;
@@ -244,7 +249,7 @@ public:
uint32_t get_scancode_with_modifiers() const;
- virtual bool action_match(const Ref<InputEvent> &p_event) const;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
virtual bool shortcut_match(const Ref<InputEvent> &p_event) const;
virtual bool is_action_type() const { return true; }
@@ -305,7 +310,7 @@ public:
bool is_doubleclick() const;
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const;
- virtual bool action_match(const Ref<InputEvent> &p_event) const;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
virtual bool is_action_type() const { return true; }
virtual String as_text() const;
@@ -352,7 +357,8 @@ public:
float get_axis_value() const;
virtual bool is_pressed() const;
- virtual bool action_match(const Ref<InputEvent> &p_event) const;
+
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
virtual bool is_action_type() const { return true; }
virtual String as_text() const;
@@ -379,7 +385,7 @@ public:
void set_pressure(float p_pressure);
float get_pressure() const;
- virtual bool action_match(const Ref<InputEvent> &p_event) const;
+ virtual bool action_match(const Ref<InputEvent> &p_event, bool *p_pressed, float *p_strength, float p_deadzone) const;
virtual bool is_action_type() const { return true; }
virtual String as_text() const;
diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp
index fa53cc85c..9dfc91e30 100644
--- a/core/os/keyboard.cpp
+++ b/core/os/keyboard.cpp
@@ -461,99 +461,6 @@ const char *find_keycode_name(int p_keycode) {
return "";
}
-struct _KeyCodeReplace {
- int from;
- int to;
-};
-
-static const _KeyCodeReplace _keycode_replace_qwertz[] = {
- { KEY_Y, KEY_Z },
- { KEY_Z, KEY_Y },
- { 0, 0 }
-};
-
-static const _KeyCodeReplace _keycode_replace_azerty[] = {
- { KEY_W, KEY_Z },
- { KEY_Z, KEY_W },
- { KEY_A, KEY_Q },
- { KEY_Q, KEY_A },
- { KEY_SEMICOLON, KEY_M },
- { KEY_M, KEY_SEMICOLON },
- { 0, 0 }
-};
-
-static const _KeyCodeReplace _keycode_replace_qzerty[] = {
- { KEY_W, KEY_Z },
- { KEY_Z, KEY_W },
- { KEY_SEMICOLON, KEY_M },
- { KEY_M, KEY_SEMICOLON },
- { 0, 0 }
-};
-
-static const _KeyCodeReplace _keycode_replace_dvorak[] = {
- { KEY_UNDERSCORE, KEY_BRACELEFT },
- { KEY_EQUAL, KEY_BRACERIGHT },
- { KEY_Q, KEY_APOSTROPHE },
- { KEY_W, KEY_COMMA },
- { KEY_E, KEY_PERIOD },
- { KEY_R, KEY_P },
- { KEY_T, KEY_Y },
- { KEY_Y, KEY_F },
- { KEY_U, KEY_G },
- { KEY_I, KEY_C },
- { KEY_O, KEY_R },
- { KEY_P, KEY_L },
- { KEY_BRACELEFT, KEY_SLASH },
- { KEY_BRACERIGHT, KEY_EQUAL },
- { KEY_A, KEY_A },
- { KEY_S, KEY_O },
- { KEY_D, KEY_E },
- { KEY_F, KEY_U },
- { KEY_G, KEY_I },
- { KEY_H, KEY_D },
- { KEY_J, KEY_H },
- { KEY_K, KEY_T },
- { KEY_L, KEY_N },
- { KEY_SEMICOLON, KEY_S },
- { KEY_APOSTROPHE, KEY_UNDERSCORE },
- { KEY_Z, KEY_SEMICOLON },
- { KEY_X, KEY_Q },
- { KEY_C, KEY_J },
- { KEY_V, KEY_K },
- { KEY_B, KEY_X },
- { KEY_N, KEY_B },
- { KEY_M, KEY_M },
- { KEY_COMMA, KEY_W },
- { KEY_PERIOD, KEY_V },
- { KEY_SLASH, KEY_Z },
- { 0, 0 }
-};
-
-static const _KeyCodeReplace _keycode_replace_neo[] = {
- { 0, 0 }
-};
-
-static const _KeyCodeReplace _keycode_replace_colemak[] = {
- { KEY_E, KEY_F },
- { KEY_R, KEY_P },
- { KEY_T, KEY_G },
- { KEY_Y, KEY_J },
- { KEY_U, KEY_L },
- { KEY_I, KEY_U },
- { KEY_O, KEY_Y },
- { KEY_P, KEY_SEMICOLON },
- { KEY_S, KEY_R },
- { KEY_D, KEY_S },
- { KEY_F, KEY_T },
- { KEY_G, KEY_D },
- { KEY_J, KEY_N },
- { KEY_K, KEY_E },
- { KEY_L, KEY_I },
- { KEY_SEMICOLON, KEY_O },
- { KEY_N, KEY_K },
- { 0, 0 }
-};
-
int keycode_get_count() {
const _KeyCodeText *kct = &_keycodes[0];
@@ -574,31 +481,3 @@ int keycode_get_value_by_index(int p_index) {
const char *keycode_get_name_by_index(int p_index) {
return _keycodes[p_index].text;
}
-
-int latin_keyboard_keycode_convert(int p_keycode) {
-
- const _KeyCodeReplace *kcr = NULL;
- switch (OS::get_singleton()->get_latin_keyboard_variant()) {
-
- case OS::LATIN_KEYBOARD_QWERTY: return p_keycode; break;
- case OS::LATIN_KEYBOARD_QWERTZ: kcr = _keycode_replace_qwertz; break;
- case OS::LATIN_KEYBOARD_AZERTY: kcr = _keycode_replace_azerty; break;
- case OS::LATIN_KEYBOARD_QZERTY: kcr = _keycode_replace_qzerty; break;
- case OS::LATIN_KEYBOARD_DVORAK: kcr = _keycode_replace_dvorak; break;
- case OS::LATIN_KEYBOARD_NEO: kcr = _keycode_replace_neo; break;
- case OS::LATIN_KEYBOARD_COLEMAK: kcr = _keycode_replace_colemak; break;
- default: return p_keycode;
- }
-
- if (!kcr) {
- return p_keycode;
- }
-
- while (kcr->from) {
- if (kcr->from == p_keycode)
- return kcr->to;
- kcr++;
- }
-
- return p_keycode;
-}
diff --git a/core/os/keyboard.h b/core/os/keyboard.h
index 4c253fa4c..a0e6f8b2e 100644
--- a/core/os/keyboard.h
+++ b/core/os/keyboard.h
@@ -331,6 +331,5 @@ const char *find_keycode_name(int p_keycode);
int keycode_get_count();
int keycode_get_value_by_index(int p_index);
const char *keycode_get_name_by_index(int p_index);
-int latin_keyboard_keycode_convert(int p_keycode);
#endif
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 618a4bbac..854d554b1 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -411,7 +411,7 @@ Error OS::set_cwd(const String &p_cwd) {
bool OS::has_touchscreen_ui_hint() const {
//return false;
- return Input::get_singleton() && Input::get_singleton()->is_emulating_touchscreen();
+ return Input::get_singleton() && Input::get_singleton()->is_emulating_touch_from_mouse();
}
int OS::get_free_static_memory() const {
diff --git a/core/os/os.h b/core/os/os.h
index 5a2c99878..943c0498f 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -205,6 +205,18 @@ public:
virtual void request_attention() {}
virtual void center_window();
+ // Returns window area free of hardware controls and other obstacles.
+ // The application should use this to determine where to place UI elements.
+ //
+ // Keep in mind the area returned is in window coordinates rather than
+ // viewport coordinates - you should perform the conversion on your own.
+ //
+ // The maximum size of the area is Rect2(0, 0, window_size.width, window_size.height).
+ virtual Rect2 get_window_safe_area() const {
+ Size2 window_size = get_window_size();
+ return Rect2(0, 0, window_size.width, window_size.height);
+ }
+
virtual void set_borderless_window(bool p_borderless) {}
virtual bool get_borderless_window() { return 0; }
diff --git a/core/project_settings.cpp b/core/project_settings.cpp
index 20d91e794..ac4a4b7d1 100644
--- a/core/project_settings.cpp
+++ b/core/project_settings.cpp
@@ -42,7 +42,7 @@
#include "variant_parser.h"
#include <zlib.h>
-#define FORMAT_VERSION 3
+#define FORMAT_VERSION 4
ProjectSettings *ProjectSettings::singleton = NULL;
@@ -262,6 +262,23 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack) {
return true;
}
+void ProjectSettings::_convert_to_last_version() {
+ if (!has_setting("config_version") || (int)get_setting("config_version") <= 3) {
+
+ // Converts the actions from array to dictionary (array of events to dictionary with deadzone + events)
+ for (Map<StringName, ProjectSettings::VariantContainer>::Element *E = props.front(); E; E = E->next()) {
+ Variant value = E->get().variant;
+ if (String(E->key()).begins_with("input/") && value.get_type() == Variant::ARRAY) {
+ Array array = value;
+ Dictionary action;
+ action["deadzone"] = Variant(0.5f);
+ action["events"] = array;
+ E->get().variant = action;
+ }
+ }
+ }
+}
+
Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bool p_upwards) {
//If looking for files in network, just use network!
@@ -390,6 +407,8 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
if (resource_path.length() && resource_path[resource_path.length() - 1] == '/')
resource_path = resource_path.substr(0, resource_path.length() - 1); // chop end
+ _convert_to_last_version();
+
return OK;
}
@@ -797,12 +816,11 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust
Variant _GLOBAL_DEF(const String &p_var, const Variant &p_default) {
Variant ret;
- if (ProjectSettings::get_singleton()->has_setting(p_var)) {
- ret = ProjectSettings::get_singleton()->get(p_var);
- } else {
+ if (!ProjectSettings::get_singleton()->has_setting(p_var)) {
ProjectSettings::get_singleton()->set(p_var, p_default);
- ret = p_default;
}
+ ret = ProjectSettings::get_singleton()->get(p_var);
+
ProjectSettings::get_singleton()->set_initial_value(p_var, p_default);
ProjectSettings::get_singleton()->set_builtin_order(p_var);
return ret;
diff --git a/core/project_settings.h b/core/project_settings.h
index 9b51bc3ac..b01e7855a 100644
--- a/core/project_settings.h
+++ b/core/project_settings.h
@@ -102,6 +102,8 @@ protected:
Error _save_custom_bnd(const String &p_file);
+ void _convert_to_last_version();
+
bool _load_resource_pack(const String &p_pack);
void _add_property_info_bind(const Dictionary &p_info);
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 006459c5f..2a611ccf6 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -42,6 +42,7 @@
#include "io/config_file.h"
#include "io/http_client.h"
#include "io/marshalls.h"
+#include "io/multiplayer_api.h"
#include "io/networked_multiplayer_peer.h"
#include "io/packet_peer.h"
#include "io/packet_peer_udp.h"
@@ -145,6 +146,7 @@ void register_core_types() {
ClassDB::register_virtual_class<PacketPeer>();
ClassDB::register_class<PacketPeerStream>();
ClassDB::register_virtual_class<NetworkedMultiplayerPeer>();
+ ClassDB::register_class<MultiplayerAPI>();
ClassDB::register_class<MainLoop>();
//ClassDB::register_type<OptimizedSaver>();
ClassDB::register_class<Translation>();
diff --git a/core/resource.cpp b/core/resource.cpp
index 2eeed50d9..179333aa1 100644
--- a/core/resource.cpp
+++ b/core/resource.cpp
@@ -226,7 +226,7 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const {
if (!(E->get().usage & PROPERTY_USAGE_STORAGE))
continue;
- Variant p = get(E->get().name);
+ Variant p = get(E->get().name).duplicate(true);
if (p.get_type() == Variant::OBJECT && p_subresources) {
RES sr = p;
diff --git a/core/script_language.h b/core/script_language.h
index 6d32fc054..0c1f99cea 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -221,7 +221,9 @@ public:
RESULT_CLASS,
RESULT_CLASS_CONSTANT,
RESULT_CLASS_PROPERTY,
- RESULT_CLASS_METHOD
+ RESULT_CLASS_METHOD,
+ RESULT_CLASS_ENUM,
+ RESULT_CLASS_TBD_GLOBALSCOPE
};
Type type;
Ref<Script> script;
diff --git a/core/self_list.h b/core/self_list.h
index e83afb66e..6e84e1cd5 100644
--- a/core/self_list.h
+++ b/core/self_list.h
@@ -39,6 +39,7 @@ public:
class List {
SelfList<T> *_first;
+ SelfList<T> *_last;
public:
void add(SelfList<T> *p_elem) {
@@ -48,47 +49,54 @@ public:
p_elem->_root = this;
p_elem->_next = _first;
p_elem->_prev = NULL;
- if (_first)
+
+ if (_first) {
_first->_prev = p_elem;
+
+ } else {
+ _last = p_elem;
+ }
+
_first = p_elem;
}
+
void add_last(SelfList<T> *p_elem) {
ERR_FAIL_COND(p_elem->_root);
- if (!_first) {
- add(p_elem);
- return;
- }
+ p_elem->_root = this;
+ p_elem->_next = NULL;
+ p_elem->_prev = _last;
- SelfList<T> *e = _first;
+ if (_last) {
+ _last->_next = p_elem;
- while (e->next()) {
- e = e->next();
+ } else {
+ _first = p_elem;
}
- e->_next = p_elem;
- p_elem->_prev = e->_next;
- p_elem->_root = this;
+ _last = p_elem;
}
void remove(SelfList<T> *p_elem) {
ERR_FAIL_COND(p_elem->_root != this);
if (p_elem->_next) {
-
p_elem->_next->_prev = p_elem->_prev;
}
- if (p_elem->_prev) {
+ if (p_elem->_prev) {
p_elem->_prev->_next = p_elem->_next;
}
if (_first == p_elem) {
-
_first = p_elem->_next;
}
+ if (_last == p_elem) {
+ _last = p_elem->_prev;
+ }
+
p_elem->_next = NULL;
p_elem->_prev = NULL;
p_elem->_root = NULL;
@@ -96,7 +104,10 @@ public:
_FORCE_INLINE_ SelfList<T> *first() { return _first; }
_FORCE_INLINE_ const SelfList<T> *first() const { return _first; }
- _FORCE_INLINE_ List() { _first = NULL; }
+ _FORCE_INLINE_ List() {
+ _first = NULL;
+ _last = NULL;
+ }
_FORCE_INLINE_ ~List() { ERR_FAIL_COND(_first != NULL); }
};
diff --git a/core/string_db.cpp b/core/string_db.cpp
index 6e1f88775..2475cbe3e 100644
--- a/core/string_db.cpp
+++ b/core/string_db.cpp
@@ -164,21 +164,14 @@ void StringName::operator=(const StringName &p_name) {
_data = p_name._data;
}
}
-/* was inlined
-StringName::operator String() const {
- if (_data)
- return _data->get_name();
-
- return "";
-}
-*/
StringName::StringName(const StringName &p_name) {
- ERR_FAIL_COND(!configured);
_data = NULL;
- if (p_name._data && p_name._data->refcount.ref()) {
+ ERR_FAIL_COND(!configured);
+
+ if (p_name._data && p_name._data->refcount.ref()) {
_data = p_name._data;
}
}
diff --git a/core/string_db.h b/core/string_db.h
index 28ca812a4..01d1ca403 100644
--- a/core/string_db.h
+++ b/core/string_db.h
@@ -67,6 +67,7 @@ class StringName {
_Data() {
cname = NULL;
next = prev = NULL;
+ idx = 0;
hash = 0;
}
};
diff --git a/core/ustring.cpp b/core/ustring.cpp
index a7a781083..921d20a6f 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -1151,7 +1151,7 @@ String String::num_uint64(uint64_t p_num, int base, bool capitalize_hex) {
c[chars] = 0;
n = p_num;
do {
- int mod = ABS(n % base);
+ int mod = n % base;
if (mod >= 10) {
char a = (capitalize_hex ? 'A' : 'a');
c[--chars] = a + (mod - 10);
@@ -1550,8 +1550,7 @@ String::String(const StrRange &p_range) {
int String::hex_to_int(bool p_with_prefix) const {
- int l = length();
- if (p_with_prefix && l < 3)
+ if (p_with_prefix && length() < 3)
return 0;
const CharType *s = ptr();
@@ -1560,17 +1559,13 @@ int String::hex_to_int(bool p_with_prefix) const {
if (sign < 0) {
s++;
- l--;
- if (p_with_prefix && l < 2)
- return 0;
}
if (p_with_prefix) {
if (s[0] != '0' || s[1] != 'x')
return 0;
s += 2;
- l -= 2;
- };
+ }
int hex = 0;
@@ -1596,8 +1591,7 @@ int String::hex_to_int(bool p_with_prefix) const {
int64_t String::hex_to_int64(bool p_with_prefix) const {
- int l = length();
- if (p_with_prefix && l < 3)
+ if (p_with_prefix && length() < 3)
return 0;
const CharType *s = ptr();
@@ -1606,17 +1600,13 @@ int64_t String::hex_to_int64(bool p_with_prefix) const {
if (sign < 0) {
s++;
- l--;
- if (p_with_prefix && l < 2)
- return 0;
}
if (p_with_prefix) {
if (s[0] != '0' || s[1] != 'x')
return 0;
s += 2;
- l -= 2;
- };
+ }
int64_t hex = 0;
@@ -2997,6 +2987,40 @@ String String::strip_escapes() const {
return substr(beg, end - beg);
}
+String String::lstrip(const Vector<CharType> &p_chars) const {
+
+ int len = length();
+ int beg;
+
+ for (beg = 0; beg < len; beg++) {
+
+ if (p_chars.find(operator[](beg)) == -1)
+ break;
+ }
+
+ if (beg == 0)
+ return *this;
+
+ return substr(beg, len - beg);
+}
+
+String String::rstrip(const Vector<CharType> &p_chars) const {
+
+ int len = length();
+ int end;
+
+ for (end = len - 1; end >= 0; end--) {
+
+ if (p_chars.find(operator[](end)) == -1)
+ break;
+ }
+
+ if (end == len - 1)
+ return *this;
+
+ return substr(0, end + 1);
+}
+
String String::simplify_path() const {
String s = *this;
@@ -3168,8 +3192,8 @@ String String::word_wrap(int p_chars_per_line) const {
String String::http_escape() const {
const CharString temp = utf8();
String res;
- for (int i = 0; i < length(); ++i) {
- CharType ord = temp[i];
+ for (int i = 0; i < temp.length(); ++i) {
+ char ord = temp[i];
if (ord == '.' || ord == '-' || ord == '_' || ord == '~' ||
(ord >= 'a' && ord <= 'z') ||
(ord >= 'A' && ord <= 'Z') ||
@@ -3178,9 +3202,9 @@ String String::http_escape() const {
} else {
char h_Val[3];
#if defined(__GNUC__) || defined(_MSC_VER)
- snprintf(h_Val, 3, "%.2X", ord);
+ snprintf(h_Val, 3, "%hhX", ord);
#else
- sprintf(h_Val, "%.2X", ord);
+ sprintf(h_Val, "%hhX", ord);
#endif
res += "%";
res += h_Val;
@@ -3448,6 +3472,24 @@ String String::pad_zeros(int p_digits) const {
return s;
}
+String String::trim_prefix(const String &p_prefix) const {
+
+ String s = *this;
+ if (s.begins_with(p_prefix)) {
+ return s.substr(p_prefix.length(), s.length() - p_prefix.length());
+ }
+ return s;
+}
+
+String String::trim_suffix(const String &p_suffix) const {
+
+ String s = *this;
+ if (s.ends_with(p_suffix)) {
+ return s.substr(0, s.length() - p_suffix.length());
+ }
+ return s;
+}
+
bool String::is_valid_integer() const {
int len = length();
@@ -3478,13 +3520,13 @@ bool String::is_valid_hex_number(bool p_with_prefix) const {
if (p_with_prefix) {
- if (len < 2)
+ if (len < 3)
return false;
if (operator[](from) != '0' || operator[](from + 1) != 'x') {
return false;
- };
+ }
from += 2;
- };
+ }
for (int i = from; i < len; i++) {
@@ -3492,7 +3534,7 @@ bool String::is_valid_hex_number(bool p_with_prefix) const {
if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
continue;
return false;
- };
+ }
return true;
};
@@ -3713,8 +3755,8 @@ String String::get_file() const {
String String::get_extension() const {
int pos = find_last(".");
- if (pos < 0)
- return *this;
+ if (pos < 0 || pos < MAX(find_last("/"), find_last("\\")))
+ return "";
return substr(pos + 1, length());
}
@@ -3792,7 +3834,7 @@ String String::percent_decode() const {
String String::get_basename() const {
int pos = find_last(".");
- if (pos < 0)
+ if (pos < 0 || pos < MAX(find_last("/"), find_last("\\")))
return *this;
return substr(0, pos);
diff --git a/core/ustring.h b/core/ustring.h
index bb676ce62..1ed694bb8 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -137,6 +137,8 @@ public:
String insert(int p_at_pos, const String &p_string) const;
String pad_decimals(int p_digits) const;
String pad_zeros(int p_digits) const;
+ String trim_prefix(const String &p_prefix) const;
+ String trim_suffix(const String &p_suffix) const;
String lpad(int min_length, const String &character = " ") const;
String rpad(int min_length, const String &character = " ") const;
String sprintf(const Array &values, bool *error) const;
@@ -188,6 +190,8 @@ public:
String dedent() const;
String strip_edges(bool left = true, bool right = true) const;
String strip_escapes() const;
+ String lstrip(const Vector<CharType> &p_chars) const;
+ String rstrip(const Vector<CharType> &p_chars) const;
String get_extension() const;
String get_basename() const;
String plus_file(const String &p_file) const;
diff --git a/core/variant.cpp b/core/variant.cpp
index 5d48c8785..a6df95e31 100644
--- a/core/variant.cpp
+++ b/core/variant.cpp
@@ -3167,7 +3167,11 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method,
if (ce.error == Variant::CallError::CALL_ERROR_INVALID_ARGUMENT) {
int errorarg = ce.argument;
- err_text = "Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(p_argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(ce.expected) + ".";
+ if (p_argptrs) {
+ err_text = "Cannot convert argument " + itos(errorarg + 1) + " from " + Variant::get_type_name(p_argptrs[errorarg]->get_type()) + " to " + Variant::get_type_name(ce.expected) + ".";
+ } else {
+ err_text = "Cannot convert argument " + itos(errorarg + 1) + " from [missing argptr, type unknown] to " + Variant::get_type_name(ce.expected) + ".";
+ }
} else if (ce.error == Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS) {
err_text = "Method expected " + itos(ce.argument) + " arguments, but called with " + itos(p_argcount) + ".";
} else if (ce.error == Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS) {
diff --git a/core/variant.h b/core/variant.h
index 0a4afada5..f227e4bfd 100644
--- a/core/variant.h
+++ b/core/variant.h
@@ -140,7 +140,6 @@ private:
::AABB *_aabb;
Basis *_basis;
Transform *_transform;
- RefPtr *_resource;
void *_ptr; //generic pointer
uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)];
} _data;
@@ -338,6 +337,7 @@ public:
}
void zero();
+ Variant duplicate(bool deep = false) const;
static void blend(const Variant &a, const Variant &b, float c, Variant &r_dst);
static void interpolate(const Variant &a, const Variant &b, float c, Variant &r_dst);
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index cda7dccf0..bd1cde5a8 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -153,9 +153,9 @@ struct _VariantCall {
funcdata.func = p_func;
funcdata.default_args = p_defaultarg;
funcdata._const = p_const;
+ funcdata.returns = p_has_return;
#ifdef DEBUG_ENABLED
funcdata.return_type = p_return;
- funcdata.returns = p_has_return;
#endif
if (p_argtype1.name) {
@@ -264,6 +264,8 @@ struct _VariantCall {
VCALL_LOCALMEM1R(String, right);
VCALL_LOCALMEM0R(String, dedent);
VCALL_LOCALMEM2R(String, strip_edges);
+ VCALL_LOCALMEM1R(String, lstrip);
+ VCALL_LOCALMEM1R(String, rstrip);
VCALL_LOCALMEM0R(String, get_extension);
VCALL_LOCALMEM0R(String, get_basename);
VCALL_LOCALMEM1R(String, plus_file);
@@ -296,6 +298,8 @@ struct _VariantCall {
VCALL_LOCALMEM0R(String, hex_to_int);
VCALL_LOCALMEM1R(String, pad_decimals);
VCALL_LOCALMEM1R(String, pad_zeros);
+ VCALL_LOCALMEM1R(String, trim_prefix);
+ VCALL_LOCALMEM1R(String, trim_suffix);
static void _call_String_to_ascii(Variant &r_ret, Variant &p_self, const Variant **p_args) {
@@ -340,6 +344,8 @@ struct _VariantCall {
VCALL_LOCALMEM1R(Vector2, rotated);
VCALL_LOCALMEM0R(Vector2, tangent);
VCALL_LOCALMEM0R(Vector2, floor);
+ VCALL_LOCALMEM0R(Vector2, ceil);
+ VCALL_LOCALMEM0R(Vector2, round);
VCALL_LOCALMEM1R(Vector2, snapped);
VCALL_LOCALMEM0R(Vector2, aspect);
VCALL_LOCALMEM1R(Vector2, dot);
@@ -382,6 +388,7 @@ struct _VariantCall {
VCALL_LOCALMEM0R(Vector3, abs);
VCALL_LOCALMEM0R(Vector3, floor);
VCALL_LOCALMEM0R(Vector3, ceil);
+ VCALL_LOCALMEM0R(Vector3, round);
VCALL_LOCALMEM1R(Vector3, distance_to);
VCALL_LOCALMEM1R(Vector3, distance_squared_to);
VCALL_LOCALMEM1R(Vector3, angle_to);
@@ -465,7 +472,7 @@ struct _VariantCall {
VCALL_LOCALMEM0R(Dictionary, hash);
VCALL_LOCALMEM0R(Dictionary, keys);
VCALL_LOCALMEM0R(Dictionary, values);
- VCALL_LOCALMEM0R(Dictionary, duplicate);
+ VCALL_LOCALMEM1R(Dictionary, duplicate);
VCALL_LOCALMEM2(Array, set);
VCALL_LOCALMEM1R(Array, get);
@@ -494,7 +501,7 @@ struct _VariantCall {
VCALL_LOCALMEM0(Array, shuffle);
VCALL_LOCALMEM2R(Array, bsearch);
VCALL_LOCALMEM4R(Array, bsearch_custom);
- VCALL_LOCALMEM0R(Array, duplicate);
+ VCALL_LOCALMEM1R(Array, duplicate);
VCALL_LOCALMEM0(Array, invert);
static void _call_PoolByteArray_get_string_from_ascii(Variant &r_ret, Variant &p_self, const Variant **p_args) {
@@ -1460,6 +1467,8 @@ void register_variant_methods() {
ADDFUNC1R(STRING, STRING, String, left, INT, "position", varray());
ADDFUNC1R(STRING, STRING, String, right, INT, "position", varray());
ADDFUNC2R(STRING, STRING, String, strip_edges, BOOL, "left", BOOL, "right", varray(true, true));
+ ADDFUNC1R(STRING, STRING, String, lstrip, STRING, "chars", varray());
+ ADDFUNC1R(STRING, STRING, String, rstrip, STRING, "chars", varray());
ADDFUNC0R(STRING, STRING, String, get_extension, varray());
ADDFUNC0R(STRING, STRING, String, get_basename, varray());
ADDFUNC1R(STRING, STRING, String, plus_file, STRING, "file", varray());
@@ -1493,6 +1502,8 @@ void register_variant_methods() {
ADDFUNC0R(STRING, INT, String, hex_to_int, varray());
ADDFUNC1R(STRING, STRING, String, pad_decimals, INT, "digits", varray());
ADDFUNC1R(STRING, STRING, String, pad_zeros, INT, "digits", varray());
+ ADDFUNC1R(STRING, STRING, String, trim_prefix, STRING, "prefix", varray());
+ ADDFUNC1R(STRING, STRING, String, trim_suffix, STRING, "suffix", varray());
ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, to_ascii, varray());
ADDFUNC0R(STRING, POOL_BYTE_ARRAY, String, to_utf8, varray());
@@ -1511,6 +1522,8 @@ void register_variant_methods() {
ADDFUNC1R(VECTOR2, VECTOR2, Vector2, rotated, REAL, "phi", varray());
ADDFUNC0R(VECTOR2, VECTOR2, Vector2, tangent, varray());
ADDFUNC0R(VECTOR2, VECTOR2, Vector2, floor, varray());
+ ADDFUNC0R(VECTOR2, VECTOR2, Vector2, ceil, varray());
+ ADDFUNC0R(VECTOR2, VECTOR2, Vector2, round, varray());
ADDFUNC1R(VECTOR2, VECTOR2, Vector2, snapped, VECTOR2, "by", varray());
ADDFUNC0R(VECTOR2, REAL, Vector2, aspect, varray());
ADDFUNC1R(VECTOR2, REAL, Vector2, dot, VECTOR2, "with", varray());
@@ -1552,6 +1565,7 @@ void register_variant_methods() {
ADDFUNC0R(VECTOR3, VECTOR3, Vector3, abs, varray());
ADDFUNC0R(VECTOR3, VECTOR3, Vector3, floor, varray());
ADDFUNC0R(VECTOR3, VECTOR3, Vector3, ceil, varray());
+ ADDFUNC0R(VECTOR3, VECTOR3, Vector3, round, varray());
ADDFUNC1R(VECTOR3, REAL, Vector3, distance_to, VECTOR3, "b", varray());
ADDFUNC1R(VECTOR3, REAL, Vector3, distance_squared_to, VECTOR3, "b", varray());
ADDFUNC1R(VECTOR3, REAL, Vector3, angle_to, VECTOR3, "to", varray());
@@ -1613,7 +1627,7 @@ void register_variant_methods() {
ADDFUNC0R(DICTIONARY, INT, Dictionary, hash, varray());
ADDFUNC0R(DICTIONARY, ARRAY, Dictionary, keys, varray());
ADDFUNC0R(DICTIONARY, ARRAY, Dictionary, values, varray());
- ADDFUNC0R(DICTIONARY, DICTIONARY, Dictionary, duplicate, varray());
+ ADDFUNC1R(DICTIONARY, DICTIONARY, Dictionary, duplicate, BOOL, "deep", varray(false));
ADDFUNC0R(ARRAY, INT, Array, size, varray());
ADDFUNC0R(ARRAY, BOOL, Array, empty, varray());
@@ -1641,7 +1655,7 @@ void register_variant_methods() {
ADDFUNC2R(ARRAY, INT, Array, bsearch, NIL, "value", BOOL, "before", varray(true));
ADDFUNC4R(ARRAY, INT, Array, bsearch_custom, NIL, "value", OBJECT, "obj", STRING, "func", BOOL, "before", varray(true));
ADDFUNC0NC(ARRAY, NIL, Array, invert, varray());
- ADDFUNC0RNC(ARRAY, ARRAY, Array, duplicate, varray());
+ ADDFUNC1R(ARRAY, ARRAY, Array, duplicate, BOOL, "deep", varray(false));
ADDFUNC0R(POOL_BYTE_ARRAY, INT, PoolByteArray, size, varray());
ADDFUNC2(POOL_BYTE_ARRAY, NIL, PoolByteArray, set, INT, "idx", INT, "byte", varray());
diff --git a/core/variant_op.cpp b/core/variant_op.cpp
index 842f5f0af..621af2dfb 100644
--- a/core/variant_op.cpp
+++ b/core/variant_op.cpp
@@ -1459,13 +1459,13 @@ void Variant::set_named(const StringName &p_index, const Variant &p_value, bool
v->a = p_value._data._int / 255.0;
valid = true;
} else if (p_index == CoreStringNames::singleton->h) {
- v->set_hsv(p_value._data._int, v->get_s(), v->get_v());
+ v->set_hsv(p_value._data._int, v->get_s(), v->get_v(), v->a);
valid = true;
} else if (p_index == CoreStringNames::singleton->s) {
- v->set_hsv(v->get_h(), p_value._data._int, v->get_v());
+ v->set_hsv(v->get_h(), p_value._data._int, v->get_v(), v->a);
valid = true;
} else if (p_index == CoreStringNames::singleton->v) {
- v->set_hsv(v->get_h(), v->get_v(), p_value._data._int);
+ v->set_hsv(v->get_h(), v->get_v(), p_value._data._int, v->a);
valid = true;
}
} else if (p_value.type == Variant::REAL) {
@@ -1495,13 +1495,13 @@ void Variant::set_named(const StringName &p_index, const Variant &p_value, bool
v->a = p_value._data._real / 255.0;
valid = true;
} else if (p_index == CoreStringNames::singleton->h) {
- v->set_hsv(p_value._data._real, v->get_s(), v->get_v());
+ v->set_hsv(p_value._data._real, v->get_s(), v->get_v(), v->a);
valid = true;
} else if (p_index == CoreStringNames::singleton->s) {
- v->set_hsv(v->get_h(), p_value._data._real, v->get_v());
+ v->set_hsv(v->get_h(), p_value._data._real, v->get_v(), v->a);
valid = true;
} else if (p_index == CoreStringNames::singleton->v) {
- v->set_hsv(v->get_h(), v->get_s(), p_value._data._real);
+ v->set_hsv(v->get_h(), v->get_s(), p_value._data._real, v->a);
valid = true;
}
}
@@ -2117,15 +2117,15 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid)
return;
} else if (*str == "h") {
valid = true;
- v->set_hsv(p_value, v->get_s(), v->get_v());
+ v->set_hsv(p_value, v->get_s(), v->get_v(), v->a);
return;
} else if (*str == "s") {
valid = true;
- v->set_hsv(v->get_h(), p_value, v->get_v());
+ v->set_hsv(v->get_h(), p_value, v->get_v(), v->a);
return;
} else if (*str == "v") {
valid = true;
- v->set_hsv(v->get_h(), v->get_s(), p_value);
+ v->set_hsv(v->get_h(), v->get_s(), p_value, v->a);
return;
} else if (*str == "r8") {
valid = true;
@@ -3415,6 +3415,19 @@ Variant Variant::iter_get(const Variant &r_iter, bool &r_valid) const {
return Variant();
}
+Variant Variant::duplicate(bool deep) const {
+ switch (type) {
+ // case OBJECT:
+ // return operator Object *()->duplicate();
+ case DICTIONARY:
+ return operator Dictionary().duplicate(deep);
+ case ARRAY:
+ return operator Array().duplicate(deep);
+ default:
+ return *this;
+ }
+}
+
void Variant::blend(const Variant &a, const Variant &b, float c, Variant &r_dst) {
if (a.type != b.type) {
if (a.is_num() && b.is_num()) {
@@ -3715,8 +3728,9 @@ static const char *_op_names[Variant::OP_MAX] = {
"*",
"/",
"- (negation)",
+ "+ (positive)",
"%",
- "..",
+ "+ (concatenation)",
"<<",
">>",
"&",
diff --git a/doc/classes/@GDScript.xml b/doc/classes/@GDScript.xml
index 07e428faa..acece0551 100644
--- a/doc/classes/@GDScript.xml
+++ b/doc/classes/@GDScript.xml
@@ -326,7 +326,8 @@
<argument index="0" name="s" type="float">
</argument>
<description>
- Raises the Euler's constant [b]e[/b] to the power of [code]s[/code] and returns it. [b]e[/b] has an approximate value of 2.71828.
+ 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.
[codeblock]
a = exp(2) # approximately 7.39
[/codeblock]
diff --git a/doc/classes/ARVRAnchor.xml b/doc/classes/ARVRAnchor.xml
index a8366e340..fa93d9668 100644
--- a/doc/classes/ARVRAnchor.xml
+++ b/doc/classes/ARVRAnchor.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ARVRAnchor" inherits="Spatial" category="Core" version="3.1">
<brief_description>
- Anchor point in AR Space
+ Anchor point in AR Space.
</brief_description>
<description>
The ARVR Anchor point is a spatial node that maps a real world location identified by the AR platform to a position within the game world. For example, as long as plane detection in ARKit is on, ARKit will identify and update the position of planes (tables, floors, etc) and create anchors for them.
diff --git a/doc/classes/ARVRServer.xml b/doc/classes/ARVRServer.xml
index 965fad196..a7d3e4668 100644
--- a/doc/classes/ARVRServer.xml
+++ b/doc/classes/ARVRServer.xml
@@ -36,6 +36,13 @@
Find an interface by its name. Say that you're making a game that uses specific capabilities of an AR/VR platform you can find the interface for that platform by name and initialize it.
</description>
</method>
+ <method name="get_hmd_transform">
+ <return type="Transform">
+ </return>
+ <description>
+ Returns the primary interface's transformation.
+ </description>
+ </method>
<method name="get_interface" qualifiers="const">
<return type="ARVRInterface">
</return>
@@ -59,6 +66,24 @@
Returns a list of available interfaces with both id and name of the interface.
</description>
</method>
+ <method name="get_last_commit_usec">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_last_frame_usec">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_last_process_usec">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_reference_frame" qualifiers="const">
<return type="Transform">
</return>
@@ -82,17 +107,10 @@
Get the number of trackers currently registered.
</description>
</method>
- <method name="set_primary_interface">
- <return type="void">
- </return>
- <argument index="0" name="interface" type="ARVRInterface">
- </argument>
- <description>
- Changes the primary interface to the specified interface. Again mostly exposed for GDNative interfaces.
- </description>
- </method>
</methods>
<members>
+ <member name="primary_interface" type="ARVRInterface" setter="set_primary_interface" getter="get_primary_interface">
+ </member>
<member name="world_scale" type="float" setter="set_world_scale" getter="get_world_scale">
Allows you to adjust the scale to your game's units. Most AR/VR platforms assume a scale of 1 game world unit = 1 meter in the real world.
</member>
diff --git a/doc/classes/AnimationPlayer.xml b/doc/classes/AnimationPlayer.xml
index 178c714a2..673bbbea5 100644
--- a/doc/classes/AnimationPlayer.xml
+++ b/doc/classes/AnimationPlayer.xml
@@ -8,7 +8,7 @@
</description>
<tutorials>
http://docs.godotengine.org/en/3.0/getting_started/step_by_step/animations.html
- http://docs.godotengine.org/en/3.0/tutorials/animation/index.html
+ http://docs.godotengine.org/en/3.0/tutorials/animation/index.html
</tutorials>
<demos>
</demos>
@@ -103,6 +103,13 @@
Get the blend time (in seconds) between two animations, referenced by their names.
</description>
</method>
+ <method name="get_playing_speed" qualifiers="const">
+ <return type="float">
+ </return>
+ <description>
+ Get the actual playing speed of current animation or 0 if not playing. This speed is the [code]playback_speed[/code] property multiplied by [code]custom_speed[/code] argument specified when calling the [code]play[/code] method.
+ </description>
+ </method>
<method name="has_animation" qualifiers="const">
<return type="bool">
</return>
@@ -131,8 +138,7 @@
<argument index="3" name="from_end" type="bool" default="false">
</argument>
<description>
- Play the animation with key [code]name[/code]. Custom speed and blend times can be set. If custom speed is negative (-1), 'from_end' being true can play the
- animation backwards.
+ Play the animation with key [code]name[/code]. Custom speed and blend times can be set. If custom speed is negative (-1), 'from_end' being true can play the animation backwards.
</description>
</method>
<method name="play_backwards">
diff --git a/doc/classes/Array.xml b/doc/classes/Array.xml
index 2e2176743..5f85751c1 100644
--- a/doc/classes/Array.xml
+++ b/doc/classes/Array.xml
@@ -131,8 +131,11 @@
<method name="duplicate">
<return type="Array">
</return>
+ <argument index="0" name="deep" type="bool" default="False">
+ </argument>
<description>
- Returns a copy of this [code]Array[/code].
+ Returns a copy of the array.
+ If [code]deep[/code] is [code]true[/code], a deep copy is be performed: all nested arrays and dictionaries are duplicated and will not be shared with the original array. If [code]false[/code], a shallow copy is made and references to the original nested arrays and dictionaries are kept, so that modifying a sub-array or dictionary in the copy will also impact those referenced in the source array.
</description>
</method>
<method name="empty">
diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index c25044b12..38aab5231 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -29,8 +29,11 @@
<argument index="3" name="compress_flags" type="int" default="97792">
</argument>
<description>
- Create a new surface ([method get_surface_count] that will become surf_idx for this.
- Surfaces are created to be rendered using a "primitive", which may be PRIMITIVE_POINTS, PRIMITIVE_LINES, PRIMITIVE_LINE_STRIP, PRIMITIVE_LINE_LOOP, PRIMITIVE_TRIANGLES, PRIMITIVE_TRIANGLE_STRIP, PRIMITIVE_TRIANGLE_FAN. (As a note, when using indices, it is recommended to only use just points, lines or triangles).
+ Creates a new surface.
+ Surfaces are created to be rendered using a "primitive", which may be PRIMITIVE_POINTS, PRIMITIVE_LINES, PRIMITIVE_LINE_STRIP, PRIMITIVE_LINE_LOOP, PRIMITIVE_TRIANGLES, PRIMITIVE_TRIANGLE_STRIP, PRIMITIVE_TRIANGLE_FAN. See [Mesh] for details. (As a note, when using indices, it is recommended to only use points, lines or triangles). [method get_surface_count] will become the surf_idx for this new surface.
+ The [code]arrays[/code] argument is an array of arrays. See [enum ArrayType] for the values used in this array. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array or be empty, except for [code]ARRAY_INDEX[/code] if it is used.
+ Adding an index array puts this function into "index mode" where the vertex and other arrays become the sources of data, and the index array defines the order of the vertices.
+ Godot uses clockwise winding order for front faces of triangle primitive modes.
</description>
</method>
<method name="center_geometry">
@@ -232,7 +235,8 @@
Array of bone weights, as a float array. Each element in groups of 4 floats.
</constant>
<constant name="ARRAY_INDEX" value="8" enum="ArrayType">
- Array of integers, used as indices referencing vertices. No index can be beyond the vertex array size.
+ [Array] of integers used as indices referencing vertices, colors, normals, tangents, and textures. All of those arrays must have the same number of elements as the vertex array. No index can be beyond the vertex array size. When this index array is present, it puts the function into "index mode," where the index selects the *i*'th vertex, normal, tangent, color, UV, etc. This means if you want to have different normals or colors along an edge, you have to duplicate the vertices.
+ For triangles, the index array is interpreted as triples, referring to the vertices of each triangle. For lines, the index array is in pairs indicating the start and end of each line.
</constant>
<constant name="ARRAY_MAX" value="9" enum="ArrayType">
</constant>
diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml
index 0be91427b..7b3ba632c 100644
--- a/doc/classes/AudioServer.xml
+++ b/doc/classes/AudioServer.xml
@@ -126,6 +126,18 @@
Returns the volume of the bus at index [code]bus_idx[/code] in dB.
</description>
</method>
+ <method name="get_device">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_device_list">
+ <return type="Array">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_mix_rate" qualifiers="const">
<return type="float">
</return>
@@ -313,6 +325,14 @@
Sets the volume of the bus at index [code]bus_idx[/code] to [code]volume_db[/code].
</description>
</method>
+ <method name="set_device">
+ <return type="void">
+ </return>
+ <argument index="0" name="arg0" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="swap_bus_effects">
<return type="void">
</return>
diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml
index 4a69695ae..7d9fcf3e0 100644
--- a/doc/classes/AudioStreamPlayer.xml
+++ b/doc/classes/AudioStreamPlayer.xml
@@ -57,6 +57,7 @@
If the audio configuration has more than two speakers, this sets the target channels. See [code]MIX_TARGET_*[/code] constants.
</member>
<member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale">
+ Changes the pitch and the tempo of the audio.
</member>
<member name="playing" type="bool" setter="_set_playing" getter="is_playing">
If [code]true[/code] audio is playing.
diff --git a/doc/classes/AudioStreamPlayer2D.xml b/doc/classes/AudioStreamPlayer2D.xml
index 495d37e6b..81beab245 100644
--- a/doc/classes/AudioStreamPlayer2D.xml
+++ b/doc/classes/AudioStreamPlayer2D.xml
@@ -63,6 +63,7 @@
Maximum distance from which audio is still hearable.
</member>
<member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale">
+ Changes the pitch and the tempo of the audio.
</member>
<member name="playing" type="bool" setter="_set_playing" getter="is_playing">
If [code]true[/code] audio is playing.
diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml
index 1ab518468..311efb749 100644
--- a/doc/classes/AudioStreamPlayer3D.xml
+++ b/doc/classes/AudioStreamPlayer3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioStreamPlayer3D" inherits="Spatial" category="Core" version="3.1">
<brief_description>
- Plays 3D sound in 3D space
+ Plays 3D sound in 3D space.
</brief_description>
<description>
Plays a sound effect with directed sound effects, dampens with distance if needed, generates effect of hearable position in space.
@@ -87,6 +87,7 @@
Decides if audio should pause when source is outside of 'max_distance' range.
</member>
<member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale">
+ Changes the pitch and the tempo of the audio.
</member>
<member name="playing" type="bool" setter="_set_playing" getter="is_playing">
If [code]true[/code], audio is playing.
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index f7270fa5f..554ed9996 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -8,6 +8,7 @@
For such use, it is composed of a scaling and a rotation matrix, in that order (M = R.S).
</description>
<tutorials>
+ http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/Camera.xml b/doc/classes/Camera.xml
index 0b7cd4ef7..7f7f152ae 100644
--- a/doc/classes/Camera.xml
+++ b/doc/classes/Camera.xml
@@ -14,15 +14,17 @@
<method name="clear_current">
<return type="void">
</return>
+ <argument index="0" name="enable_next" type="bool" default="true">
+ </argument>
<description>
- If this is the current Camera, remove it from being current. If it is inside the node tree, request to make the next Camera current, if any.
+ If this is the current Camera, remove it from being current. If [code]enable_next[/code] is true, request to make the next Camera current, if any.
</description>
</method>
<method name="get_camera_transform" qualifiers="const">
<return type="Transform">
</return>
<description>
- Get the camera transform. Subclassed cameras (such as CharacterCamera) may provide different transforms than the [Node] transform.
+ Gets the camera transform. Subclassed cameras (such as CharacterCamera) may provide different transforms than the [Node] transform.
</description>
</method>
<method name="is_position_behind" qualifiers="const">
@@ -31,14 +33,14 @@
<argument index="0" name="world_point" type="Vector3">
</argument>
<description>
- Returns [code]true[/code] if the given position is behind the Camera.
+ Returns [code]true[/code] if the given position is behind the Camera. Note that a position which returns [code]false[/code] may still be outside the Camera's field of view.
</description>
</method>
<method name="make_current">
<return type="void">
</return>
<description>
- Make this camera the current Camera for the [Viewport] (see class description). If the Camera Node is outside the scene tree, it will attempt to become current once it's added.
+ Makes this camera the current Camera for the [Viewport] (see class description). If the Camera Node is outside the scene tree, it will attempt to become current once it's added.
</description>
</method>
<method name="project_local_ray_normal" qualifiers="const">
@@ -56,7 +58,7 @@
<argument index="0" name="screen_point" type="Vector2">
</argument>
<description>
- Returns how a 2D coordinate in the Viewport rectangle maps to a 3D point in worldspace.
+ Returns the 3D point in worldspace that maps to the given 2D coordinate in the [Viewport] rectangle.
</description>
</method>
<method name="project_ray_normal" qualifiers="const">
@@ -87,7 +89,7 @@
<argument index="2" name="z_far" type="float">
</argument>
<description>
- Set the camera projection to orthogonal mode, by specifying a width and the [i]near[/i] and [i]far[/i] clip planes in worldspace units. (As a hint, 2D games often use this projection, with values specified in pixels)
+ Sets the camera projection to orthogonal mode, by specifying a width and the [i]near[/i] and [i]far[/i] clip planes in worldspace units. (As a hint, 2D games often use this projection, with values specified in pixels)
</description>
</method>
<method name="set_perspective">
@@ -100,7 +102,7 @@
<argument index="2" name="z_far" type="float">
</argument>
<description>
- Set the camera projection to perspective mode, by specifying a [i]FOV[/i] Y angle in degrees (FOV means Field of View), and the [i]near[/i] and [i]far[/i] clip planes in worldspace units.
+ Sets the camera projection to perspective mode, by specifying a [i]FOV[/i] Y angle in degrees (FOV means Field of View), and the [i]near[/i] and [i]far[/i] clip planes in worldspace units.
</description>
</method>
<method name="unproject_position" qualifiers="const">
@@ -109,7 +111,7 @@
<argument index="0" name="world_point" type="Vector3">
</argument>
<description>
- Returns how a 3D point in worldspace maps to a 2D coordinate in the [Viewport] rectangle.
+ Returns the 2D coordinate in the [Viewport] rectangle that maps to the given 3D point in worldspace.
</description>
</method>
</methods>
@@ -124,7 +126,7 @@
If not [code]DOPPLER_TRACKING_DISABLED[/code] this Camera will simulate the Doppler effect for objects changed in particular [code]_process[/code] methods. Default value: [code]DOPPLER_TRACKING_DISABLED[/code].
</member>
<member name="environment" type="Environment" setter="set_environment" getter="get_environment">
- Set the [Environment] to use for this Camera.
+ The [Environment] to use for this Camera.
</member>
<member name="far" type="float" setter="set_zfar" getter="get_zfar">
The distance to the far culling boundary for this Camera relative to its local z-axis.
@@ -136,7 +138,7 @@
The horizontal (X) offset of the Camera viewport.
</member>
<member name="keep_aspect" type="int" setter="set_keep_aspect_mode" getter="get_keep_aspect_mode" enum="Camera.KeepAspect">
- The axis to lock during [member fov]/[member size] adjustments.
+ The axis to lock during [member fov]/[member size] adjustments. Can be either [code]KEEP_WIDTH[/code] or [code]KEEP_HEIGHT[/code].
</member>
<member name="near" type="float" setter="set_znear" getter="get_znear">
The distance to the near culling boundary for this Camera relative to its local z-axis.
@@ -148,7 +150,7 @@
The camera's size measured as 1/2 the width or height. Only applicable in orthogonal mode. Since [member keep_aspect] locks on axis, [code]size[/code] sets the other axis' size length.
</member>
<member name="v_offset" type="float" setter="set_v_offset" getter="get_v_offset">
- The horizontal (Y) offset of the Camera viewport.
+ The vertical (Y) offset of the Camera viewport.
</member>
</members>
<constants>
diff --git a/doc/classes/CanvasItemMaterial.xml b/doc/classes/CanvasItemMaterial.xml
index fe7194dcf..354bc10cd 100644
--- a/doc/classes/CanvasItemMaterial.xml
+++ b/doc/classes/CanvasItemMaterial.xml
@@ -36,6 +36,9 @@
<constant name="BLEND_MODE_PREMULT_ALPHA" value="4" enum="BlendMode">
Mix blending mode. Colors are assumed to be premultiplied by the alpha (opacity) value.
</constant>
+ <constant name="BLEND_MODE_DISABLED" value="5" enum="BlendMode">
+ Disable blending mode. Colors including alpha are written as is. Only applicable for render targets with a transparent background. No lighting will be applied.
+ </constant>
<constant name="LIGHT_MODE_NORMAL" value="0" enum="LightMode">
Render the material using both light and non-light sensitive material properties.
</constant>
diff --git a/doc/classes/CanvasLayer.xml b/doc/classes/CanvasLayer.xml
index b2b093882..3e4d1b29f 100644
--- a/doc/classes/CanvasLayer.xml
+++ b/doc/classes/CanvasLayer.xml
@@ -13,11 +13,11 @@
<demos>
</demos>
<methods>
- <method name="get_world_2d" qualifiers="const">
- <return type="World2D">
+ <method name="get_canvas" qualifiers="const">
+ <return type="RID">
</return>
<description>
- Return the [World2D] used by this layer.
+ Returns the RID of the canvas used by this layer.
</description>
</method>
</methods>
diff --git a/doc/classes/CapsuleShape2D.xml b/doc/classes/CapsuleShape2D.xml
index 488c1ddc4..f05b19460 100644
--- a/doc/classes/CapsuleShape2D.xml
+++ b/doc/classes/CapsuleShape2D.xml
@@ -17,7 +17,7 @@
The capsule's height.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius">
- The capsules's radius.
+ The capsule's radius.
</member>
</members>
<constants>
diff --git a/doc/classes/CheckBox.xml b/doc/classes/CheckBox.xml
index d37f0422b..fb2cf64d9 100644
--- a/doc/classes/CheckBox.xml
+++ b/doc/classes/CheckBox.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="CheckBox" inherits="Button" category="Core" version="3.1">
<brief_description>
- Binary choice user interface widget
+ Binary choice user interface widget.
</brief_description>
<description>
A checkbox allows the user to make a binary choice (choosing only one of two possible options), for example Answer 'yes' or 'no'.
diff --git a/doc/classes/CollisionShape.xml b/doc/classes/CollisionShape.xml
index 9910de494..95fa1175c 100644
--- a/doc/classes/CollisionShape.xml
+++ b/doc/classes/CollisionShape.xml
@@ -4,7 +4,7 @@
Node that represents collision shape data in 3D space.
</brief_description>
<description>
- Editor facility for creating and editing collision shapes in 3D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area] to give it a detection shape, or add it to a [PhysicsBody] to give create solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape.
+ Editor facility for creating and editing collision shapes in 3D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area] to give it a detection shape, or add it to a [PhysicsBody] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape.
</description>
<tutorials>
http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html
diff --git a/doc/classes/CollisionShape2D.xml b/doc/classes/CollisionShape2D.xml
index 6d62a0ba6..3136f132b 100644
--- a/doc/classes/CollisionShape2D.xml
+++ b/doc/classes/CollisionShape2D.xml
@@ -4,7 +4,7 @@
Node that represents collision shape data in 2D space.
</brief_description>
<description>
- Editor facility for creating and editing collision shapes in 2D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area2D] to give it a detection shape, or add it to a [PhysicsBody2D] to give create solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape.
+ Editor facility for creating and editing collision shapes in 2D space. You can use this node to represent all sorts of collision shapes, for example, add this to an [Area2D] to give it a detection shape, or add it to a [PhysicsBody2D] to create a solid object. [b]IMPORTANT[/b]: this is an Editor-only helper to create shapes, use [method get_shape] to get the actual shape.
</description>
<tutorials>
http://docs.godotengine.org/en/3.0/tutorials/physics/physics_introduction.html
diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml
index 717f7e4ef..eb86dc8af 100644
--- a/doc/classes/ColorPickerButton.xml
+++ b/doc/classes/ColorPickerButton.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ColorPickerButton" inherits="Button" category="Core" version="3.1">
<brief_description>
- Button that pops out a [ColorPicker]
+ Button that pops out a [ColorPicker].
</brief_description>
<description>
Encapsulates a [ColorPicker] making it accesible by pressing a button, pressing the button will toggle the [ColorPicker] visibility
diff --git a/doc/classes/ConeTwistJoint.xml b/doc/classes/ConeTwistJoint.xml
index feb3262be..d6b2f191a 100644
--- a/doc/classes/ConeTwistJoint.xml
+++ b/doc/classes/ConeTwistJoint.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ConeTwistJoint" inherits="Joint" category="Core" version="3.1">
<brief_description>
- A twist joint between two 3D bodies
+ A twist joint between two 3D bodies.
</brief_description>
<description>
The joint can rotate the bodies across an axis defined by the local x-axes of the [Joint].
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml
index 722bb5d25..4b6a9cca8 100644
--- a/doc/classes/Control.xml
+++ b/doc/classes/Control.xml
@@ -158,7 +158,7 @@
</argument>
<description>
Forces drag and bypasses [method get_drag_data] and [method set_drag_preview] by passing [code]data[/code] and [code]preview[/code]. Drag will start even if the mouse is neither over nor pressed on this control.
- The methods [method can_drop_data] and [method drop_data] must be implemented on controls that want to recieve drop data.
+ The methods [method can_drop_data] and [method drop_data] must be implemented on controls that want to receive drop data.
</description>
</method>
<method name="get_begin" qualifiers="const">
@@ -208,7 +208,7 @@
<argument index="0" name="position" type="Vector2">
</argument>
<description>
- Godot calls this method to get data that can be dragged and dropped onto controls that expect drop data. Return null if there is no data to drag. Controls that want to recieve drop data should implement [method can_drop_data] and [method drop_data]. [code]position[/code] is local to this control. Drag may be forced with [method force_drag].
+ Godot calls this method to get data that can be dragged and dropped onto controls that expect drop data. Return null if there is no data to drag. Controls that want to receive drop data should implement [method can_drop_data] and [method drop_data]. [code]position[/code] is local to this control. Drag may be forced with [method force_drag].
A preview that will follow the mouse that should represent the data can be set with [method set_drag_preview]. A good time to set the preview is in this method.
[codeblock]
extends Control
@@ -773,6 +773,10 @@
<constant name="NOTIFICATION_MODAL_CLOSE" value="46">
Sent when an open modal dialog closes. See [member show_modal].
</constant>
+ <constant name="NOTIFICATION_SCROLL_BEGIN" value="47">
+ </constant>
+ <constant name="NOTIFICATION_SCROLL_END" value="48">
+ </constant>
<constant name="CURSOR_ARROW" value="0" enum="CursorShape">
Show the system's arrow mouse cursor when the user hovers the node. Use with [method set_default_cursor_shape].
</constant>
@@ -908,6 +912,8 @@
</constant>
<constant name="GROW_DIRECTION_END" value="1" enum="GrowDirection">
</constant>
+ <constant name="GROW_DIRECTION_BOTH" value="2" enum="GrowDirection">
+ </constant>
<constant name="ANCHOR_BEGIN" value="0" enum="Anchor">
Snaps one of the 4 anchor's sides to the origin of the node's [code]Rect[/code], in the top left. Use it with one of the [code]anchor_*[/code] member variables, like [member anchor_left]. To change all 4 anchors at once, use [method set_anchors_preset].
</constant>
diff --git a/doc/classes/Dictionary.xml b/doc/classes/Dictionary.xml
index d7118265f..9fa1e3ea6 100644
--- a/doc/classes/Dictionary.xml
+++ b/doc/classes/Dictionary.xml
@@ -19,7 +19,10 @@
<method name="duplicate">
<return type="Dictionary">
</return>
+ <argument index="0" name="deep" type="bool" default="False">
+ </argument>
<description>
+ Creates a copy of the dictionary, and returns it.
</description>
</method>
<method name="empty">
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index 0a8d95d25..846d6f18f 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -102,6 +102,21 @@
<description>
</description>
</method>
+ <method name="add_tool_menu_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="name" type="String">
+ </argument>
+ <argument index="1" name="handler" type="Object">
+ </argument>
+ <argument index="2" name="callback" type="String">
+ </argument>
+ <argument index="3" name="ud" type="Variant" default="null">
+ </argument>
+ <description>
+ Adds a custom menu to 'Project > Tools' as [code]name[/code] that calls [code]callback[/code] on an instance of [code]handler[/code] with a parameter [code]ud[/code] when user activates it.
+ </description>
+ </method>
<method name="add_tool_submenu_item">
<return type="void">
</return>
@@ -347,6 +362,15 @@
<description>
</description>
</method>
+ <method name="remove_tool_menu_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="name" type="String">
+ </argument>
+ <description>
+ Removes a menu [code]name[/code] from 'Project > Tools'.
+ </description>
+ </method>
<method name="save_external_data" qualifiers="virtual">
<return type="void">
</return>
diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml
index ea3c40434..6384b4d0f 100644
--- a/doc/classes/Engine.xml
+++ b/doc/classes/Engine.xml
@@ -46,9 +46,9 @@
<description>
Returns the current engine version information in a Dictionary.
- "major" - Holds the major version number as a String
- "minor" - Holds the minor version number as a String
- "patch" - Holds the patch version number as a String
+ "major" - Holds the major version number as an int
+ "minor" - Holds the minor version number as an int
+ "patch" - Holds the patch version number as an int
"status" - Holds the status (e.g. "beta", "rc1", "rc2", ... "stable") as a String
"build" - Holds the build name (e.g. "custom-build") as a String
"string" - major + minor + patch + status + build in a single String
diff --git a/doc/classes/File.xml b/doc/classes/File.xml
index 534323348..bd368e967 100644
--- a/doc/classes/File.xml
+++ b/doc/classes/File.xml
@@ -163,14 +163,14 @@
Returns a [String] saved in Pascal format from the file.
</description>
</method>
- <method name="get_path">
+ <method name="get_path" qualifiers="const">
<return type="String">
</return>
<description>
Returns the path as a [String] for the current open file.
</description>
</method>
- <method name="get_path_absolute">
+ <method name="get_path_absolute" qualifiers="const">
<return type="String">
</return>
<description>
diff --git a/doc/classes/Generic6DOFJoint.xml b/doc/classes/Generic6DOFJoint.xml
index f49f4231e..2aec6d7f4 100644
--- a/doc/classes/Generic6DOFJoint.xml
+++ b/doc/classes/Generic6DOFJoint.xml
@@ -4,7 +4,7 @@
The generic 6 degrees of freedom joint can implement a variety of joint-types by locking certain axes' rotation or translation.
</brief_description>
<description>
- The first 3 dof axes are linear axes, which represent translation of Bodies, and the latter 3 dof axes represent the angular motion. Each axis can be either locked, or limited.
+ The first 3 DOF axes are linear axes, which represent translation of Bodies, and the latter 3 DOF axes represent the angular motion. Each axis can be either locked, or limited.
</description>
<tutorials>
</tutorials>
@@ -18,7 +18,7 @@
The lower, the longer an impulse from one side takes to travel to the other side.
</member>
<member name="angular_limit_x/enabled" type="bool" setter="set_flag_x" getter="get_flag_x">
- If [code]true[/code] rotation across the x-axis is enabled.
+ If [code]true[/code] rotation across the x-axis is limited.
</member>
<member name="angular_limit_x/erp" type="float" setter="set_param_x" getter="get_param_x">
When rotating across x-axis, this error tolerance factor defines how much the correction gets slowed down. The lower, the slower.
@@ -42,7 +42,7 @@
The amount of rotational damping across the y-axis. The lower, the more dampening occurs.
</member>
<member name="angular_limit_y/enabled" type="bool" setter="set_flag_y" getter="get_flag_y">
- If [code]true[/code] rotation across the y-axis is enabled.
+ If [code]true[/code] rotation across the y-axis is limited.
</member>
<member name="angular_limit_y/erp" type="float" setter="set_param_y" getter="get_param_y">
When rotating across y-axis, this error tolerance factor defines how much the correction gets slowed down. The lower, the slower.
@@ -66,7 +66,7 @@
The amount of rotational damping across the z-axis. The lower, the more dampening occurs.
</member>
<member name="angular_limit_z/enabled" type="bool" setter="set_flag_z" getter="get_flag_z">
- If [code]true[/code] rotation across the z-axis is enabled.
+ If [code]true[/code] rotation across the z-axis is limited.
</member>
<member name="angular_limit_z/erp" type="float" setter="set_param_z" getter="get_param_z">
When rotating across z-axis, this error tolerance factor defines how much the correction gets slowed down. The lower, the slower.
@@ -117,7 +117,7 @@
The amount of damping that happens at the x-motion.
</member>
<member name="linear_limit_x/enabled" type="bool" setter="set_flag_x" getter="get_flag_x">
- If [code]true[/code] the linear motion across the x-axis is enabled.
+ If [code]true[/code] the linear motion across the x-axis is limited.
</member>
<member name="linear_limit_x/lower_distance" type="float" setter="set_param_x" getter="get_param_x">
The minimum difference between the pivot points' x-axis.
@@ -135,7 +135,7 @@
The amount of damping that happens at the y-motion.
</member>
<member name="linear_limit_y/enabled" type="bool" setter="set_flag_y" getter="get_flag_y">
- If [code]true[/code] the linear motion across the y-axis is enabled.
+ If [code]true[/code] the linear motion across the y-axis is limited.
</member>
<member name="linear_limit_y/lower_distance" type="float" setter="set_param_y" getter="get_param_y">
The minimum difference between the pivot points' y-axis.
@@ -153,7 +153,7 @@
The amount of damping that happens at the z-motion.
</member>
<member name="linear_limit_z/enabled" type="bool" setter="set_flag_z" getter="get_flag_z">
- If [code]true[/code] the linear motion across the z-axis is enabled.
+ If [code]true[/code] the linear motion across the z-axis is limited.
</member>
<member name="linear_limit_z/lower_distance" type="float" setter="set_param_z" getter="get_param_z">
The minimum difference between the pivot points' z-axis.
diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml
index 5fb12612a..605efd911 100644
--- a/doc/classes/GraphEdit.xml
+++ b/doc/classes/GraphEdit.xml
@@ -228,6 +228,7 @@
<argument index="0" name="node" type="Object">
</argument>
<description>
+ Emitted when a GraphNode is selected.
</description>
</signal>
<signal name="popup_request">
diff --git a/doc/classes/GridContainer.xml b/doc/classes/GridContainer.xml
index 7f70a8b7b..346ab9d35 100644
--- a/doc/classes/GridContainer.xml
+++ b/doc/classes/GridContainer.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GridContainer" inherits="Container" category="Core" version="3.1">
<brief_description>
- Grid container used to arrange elements in a grid like layout
+ Grid container used to arrange elements in a grid like layout.
</brief_description>
<description>
Grid container will arrange its children in a grid like structure, the grid columns are specified using the [method set_columns] method and the number of rows will be equal to the number of children in the container divided by the number of columns, for example: if the container has 5 children, and 2 columns, there will be 3 rows in the container. Notice that grid layout will preserve the columns and rows for every size of the container.
diff --git a/doc/classes/HTTPClient.xml b/doc/classes/HTTPClient.xml
index 5bd7c4cd3..018b548ef 100644
--- a/doc/classes/HTTPClient.xml
+++ b/doc/classes/HTTPClient.xml
@@ -111,6 +111,12 @@
String queryString = httpClient.query_string_from_dict(fields)
returns:= "username=user&amp;password=pass"
[/codeblock]
+ Furthermore, if a key has a null value, only the key itself is added, without equal sign and value. If the value is an array, for each value in it a pair with the same key is added.
+ [codeblock]
+ var fields = {"single": 123, "not_valued": null, "multiple": [22, 33, 44]}
+ String queryString = httpClient.query_string_from_dict(fields)
+ returns:= "single=123&amp;not_valued&amp;multiple=22&amp;multiple=33&amp;multiple=44"
+ [/codeblock]
</description>
</method>
<method name="read_response_body_chunk">
diff --git a/doc/classes/HTTPRequest.xml b/doc/classes/HTTPRequest.xml
index 010ef6d9d..ec9f86993 100644
--- a/doc/classes/HTTPRequest.xml
+++ b/doc/classes/HTTPRequest.xml
@@ -55,6 +55,8 @@
<argument index="4" name="request_data" type="String" default="&quot;&quot;">
</argument>
<description>
+ Creates request on the underlying [HTTPClient]. If there is no configuration errors, it tries to connect using [method HTTPClient.connect_to_host] and passes parameters onto [method HTTPClient.request].
+ Returns [code]OK[/code] if request is successfully created. (Does not imply that the server has responded), [code]ERR_UNCONFIGURED[/code] if not in the tree, [code]ERR_BUSY[/code] if still processing previous request, [code]ERR_INVALID_PARAMETER[/code] if given string is not a valid URL format, or [code]ERR_CANT_CONNECT[/code] if not using thread and the [HTTPClient] cannot connect to host.
</description>
</method>
</methods>
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index c1bd8d34e..ea61aced8 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -67,6 +67,14 @@
Blits [code]src_rect[/code] area from [code]src[/code] image to this image at the coordinates given by [code]dst[/code]. [code]src[/code] pixel is copied onto [code]dst[/code] if the corresponding [code]mask[/code] pixel's alpha value is not 0. [code]src[/code] image and [code]mask[/code] image [b]must[/b] have the same size (width and height) but they can have different formats.
</description>
</method>
+ <method name="bumpmap_to_normalmap">
+ <return type="void">
+ </return>
+ <argument index="0" name="bump_scale" type="float" default="1.0">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="clear_mipmaps">
<return type="void">
</return>
@@ -436,109 +444,109 @@
<constant name="FORMAT_LA8" value="1" enum="Format">
</constant>
<constant name="FORMAT_R8" value="2" enum="Format">
- OpenGL texture format RED with a single component and a bitdepth of 8.
+ OpenGL texture format RED with a single component and a bitdepth of 8.
</constant>
<constant name="FORMAT_RG8" value="3" enum="Format">
- OpenGL texture format RG with two components and a bitdepth of 8 for each.
+ OpenGL texture format RG with two components and a bitdepth of 8 for each.
</constant>
<constant name="FORMAT_RGB8" value="4" enum="Format">
- OpenGL texture format RGB with three components, each with a bitdepth of 8.
+ OpenGL texture format RGB with three components, each with a bitdepth of 8.
</constant>
<constant name="FORMAT_RGBA8" value="5" enum="Format">
- OpenGL texture format RGBA with four components, each with a bitdepth of 8.
+ OpenGL texture format RGBA with four components, each with a bitdepth of 8.
</constant>
<constant name="FORMAT_RGBA4444" value="6" enum="Format">
- OpenGL texture format RGBA with four components, each with a bitdepth of 4.
+ OpenGL texture format RGBA with four components, each with a bitdepth of 4.
</constant>
<constant name="FORMAT_RGBA5551" value="7" enum="Format">
- OpenGL texture format GL_RGB5_A1 where 5 bits of depth for each component of RGB and one bit for alpha.
+ OpenGL texture format GL_RGB5_A1 where 5 bits of depth for each component of RGB and one bit for alpha.
</constant>
<constant name="FORMAT_RF" value="8" enum="Format">
- OpenGL texture format GL_R32F where there's one component, a 32-bit floating-point value.
+ OpenGL texture format GL_R32F where there's one component, a 32-bit floating-point value.
</constant>
<constant name="FORMAT_RGF" value="9" enum="Format">
- OpenGL texture format GL_RG32F where there are two components, each a 32-bit floating-point values.
+ OpenGL texture format GL_RG32F where there are two components, each a 32-bit floating-point values.
</constant>
<constant name="FORMAT_RGBF" value="10" enum="Format">
- OpenGL texture format GL_RGB32F where there are three components, each a 32-bit floating-point values.
+ OpenGL texture format GL_RGB32F where there are three components, each a 32-bit floating-point values.
</constant>
<constant name="FORMAT_RGBAF" value="11" enum="Format">
- OpenGL texture format GL_RGBA32F where there are four components, each a 32-bit floating-point values.
+ OpenGL texture format GL_RGBA32F where there are four components, each a 32-bit floating-point values.
</constant>
<constant name="FORMAT_RH" value="12" enum="Format">
- OpenGL texture format GL_R32F where there's one component, a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_R32F where there's one component, a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGH" value="13" enum="Format">
- OpenGL texture format GL_RG32F where there's two components, each a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_RG32F where there's two components, each a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGBH" value="14" enum="Format">
- OpenGL texture format GL_RGB32F where there's three components, each a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_RGB32F where there's three components, each a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGBAH" value="15" enum="Format">
- OpenGL texture format GL_RGBA32F where there's four components, each a 16-bit "half-precision" floating-point value.
+ OpenGL texture format GL_RGBA32F where there's four components, each a 16-bit "half-precision" floating-point value.
</constant>
<constant name="FORMAT_RGBE9995" value="16" enum="Format">
- A special OpenGL texture format where the three color components have 9 bits of precision and all three share a single exponent.
+ A special OpenGL texture format where the three color components have 9 bits of precision and all three share a single exponent.
</constant>
<constant name="FORMAT_DXT1" value="17" enum="Format">
- The S3TC texture format that uses Block Compression 1, and is the smallest variation of S3TC, only providing 1 bit of alpha and color data being premultiplied with alpha. More information can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression.
+ The S3TC texture format that uses Block Compression 1, and is the smallest variation of S3TC, only providing 1 bit of alpha and color data being premultiplied with alpha. More information can be found at https://www.khronos.org/opengl/wiki/S3_Texture_Compression.
</constant>
<constant name="FORMAT_DXT3" value="18" enum="Format">
- The S3TC texture format that uses Block Compression 2, and color data is interpreted as not having been premultiplied by alpha. Well suited for images with sharp alpha transitions between translucent and opaque areas.
+ The S3TC texture format that uses Block Compression 2, and color data is interpreted as not having been premultiplied by alpha. Well suited for images with sharp alpha transitions between translucent and opaque areas.
</constant>
<constant name="FORMAT_DXT5" value="19" enum="Format">
- The S3TC texture format also known as Block Compression 3 or BC3 that contains 64 bits of alpha channel data followed by 64 bits of DXT1-encoded color data. Color data is not premultiplied by alpha, same as DXT3. DXT5 generally produces superior results for transparency gradients than DXT3.
+ The S3TC texture format also known as Block Compression 3 or BC3 that contains 64 bits of alpha channel data followed by 64 bits of DXT1-encoded color data. Color data is not premultiplied by alpha, same as DXT3. DXT5 generally produces superior results for transparency gradients than DXT3.
</constant>
<constant name="FORMAT_RGTC_R" value="20" enum="Format">
- Texture format that uses Red Green Texture Compression, normalizing the red channel data using the same compression algorithm that DXT5 uses for the alpha channel. More information can be found here https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression.
+ Texture format that uses Red Green Texture Compression, normalizing the red channel data using the same compression algorithm that DXT5 uses for the alpha channel. More information can be found here https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression.
</constant>
<constant name="FORMAT_RGTC_RG" value="21" enum="Format">
- Texture format that uses Red Green Texture Compression, normalizing the red and green channel data using the same compression algorithm that DXT5 uses for the alpha channel.
+ Texture format that uses Red Green Texture Compression, normalizing the red and green channel data using the same compression algorithm that DXT5 uses for the alpha channel.
</constant>
<constant name="FORMAT_BPTC_RGBA" value="22" enum="Format">
- Texture format that uses BPTC compression with unsigned normalized RGBA components. More information can be found at https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression.
+ Texture format that uses BPTC compression with unsigned normalized RGBA components. More information can be found at https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression.
</constant>
<constant name="FORMAT_BPTC_RGBF" value="23" enum="Format">
- Texture format that uses BPTC compression with signed floating-point RGB components.
+ Texture format that uses BPTC compression with signed floating-point RGB components.
</constant>
<constant name="FORMAT_BPTC_RGBFU" value="24" enum="Format">
- Texture format that uses BPTC compression with unsigned floating-point RGB components.
+ Texture format that uses BPTC compression with unsigned floating-point RGB components.
</constant>
<constant name="FORMAT_PVRTC2" value="25" enum="Format">
- Texture format used on PowerVR-supported mobile platforms, uses 2 bit color depth with no alpha. More information on PVRTC can be found here https://en.wikipedia.org/wiki/PVRTC.
+ Texture format used on PowerVR-supported mobile platforms, uses 2 bit color depth with no alpha. More information on PVRTC can be found here https://en.wikipedia.org/wiki/PVRTC.
</constant>
<constant name="FORMAT_PVRTC2A" value="26" enum="Format">
- Same as PVRTC2, but with an alpha component.
+ Same as PVRTC2, but with an alpha component.
</constant>
<constant name="FORMAT_PVRTC4" value="27" enum="Format">
- Similar to PVRTC2, but with 4 bit color depth and no alpha.
+ Similar to PVRTC2, but with 4 bit color depth and no alpha.
</constant>
<constant name="FORMAT_PVRTC4A" value="28" enum="Format">
- Same as PVRTC4, but with an alpha component.
+ Same as PVRTC4, but with an alpha component.
</constant>
<constant name="FORMAT_ETC" value="29" enum="Format">
- Ericsson Texture Compression format, also referred to as 'ETC1', and is part of the OpenGL ES graphics standard. An overview of the format is given at https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC1.
+ Ericsson Texture Compression format, also referred to as 'ETC1', and is part of the OpenGL ES graphics standard. An overview of the format is given at https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC1.
</constant>
<constant name="FORMAT_ETC2_R11" value="30" enum="Format">
- Ericsson Texture Compression format 2 variant R11_EAC, which provides one channel of unsigned data.
+ Ericsson Texture Compression format 2 variant R11_EAC, which provides one channel of unsigned data.
</constant>
<constant name="FORMAT_ETC2_R11S" value="31" enum="Format">
- Ericsson Texture Compression format 2 variant SIGNED_R11_EAC, which provides one channel of signed data.
+ Ericsson Texture Compression format 2 variant SIGNED_R11_EAC, which provides one channel of signed data.
</constant>
<constant name="FORMAT_ETC2_RG11" value="32" enum="Format">
- Ericsson Texture Compression format 2 variant RG11_EAC, which provides two channels of unsigned data.
+ Ericsson Texture Compression format 2 variant RG11_EAC, which provides two channels of unsigned data.
</constant>
<constant name="FORMAT_ETC2_RG11S" value="33" enum="Format">
- Ericsson Texture Compression format 2 variant SIGNED_RG11_EAC, which provides two channels of signed data.
+ Ericsson Texture Compression format 2 variant SIGNED_RG11_EAC, which provides two channels of signed data.
</constant>
<constant name="FORMAT_ETC2_RGB8" value="34" enum="Format">
- Ericsson Texture Compression format 2 variant RGB8, which is a followup of ETC1 and compresses RGB888 data.
+ Ericsson Texture Compression format 2 variant RGB8, which is a followup of ETC1 and compresses RGB888 data.
</constant>
<constant name="FORMAT_ETC2_RGBA8" value="35" enum="Format">
- Ericsson Texture Compression format 2 variant RGBA8, which compresses RGBA8888 data with full alpha support.
+ Ericsson Texture Compression format 2 variant RGBA8, which compresses RGBA8888 data with full alpha support.
</constant>
<constant name="FORMAT_ETC2_RGB8A1" value="36" enum="Format">
- Ericsson Texture Compression format 2 variant RGB8_PUNCHTHROUGH_ALPHA1, which compresses RGBA data to make alpha either fully transparent or fully opaque.
+ Ericsson Texture Compression format 2 variant RGB8_PUNCHTHROUGH_ALPHA1, which compresses RGBA data to make alpha either fully transparent or fully opaque.
</constant>
<constant name="FORMAT_MAX" value="37" enum="Format">
</constant>
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index f3bff5a14..f53790862 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -333,7 +333,7 @@
</methods>
<signals>
<signal name="joy_connection_changed">
- <argument index="0" name="index" type="int">
+ <argument index="0" name="device" type="int">
</argument>
<argument index="1" name="connected" type="bool">
</argument>
diff --git a/doc/classes/InterpolatedCamera.xml b/doc/classes/InterpolatedCamera.xml
index 9ee06b9d4..1ac7b5107 100644
--- a/doc/classes/InterpolatedCamera.xml
+++ b/doc/classes/InterpolatedCamera.xml
@@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="InterpolatedCamera" inherits="Camera" category="Core" version="3.1">
<brief_description>
+ Camera which moves toward another node.
</brief_description>
<description>
+ InterpolatedCamera is a [Camera] which smoothly moves to match a target node's position and rotation.
+ If it is not [member enabled] or does not have a valid target set, InterpolatedCamera acts like a normal Camera.
</description>
<tutorials>
</tutorials>
@@ -15,15 +18,19 @@
<argument index="0" name="target" type="Object">
</argument>
<description>
+ Sets the node to move toward and orient with.
</description>
</method>
</methods>
<members>
<member name="enabled" type="bool" setter="set_interpolation_enabled" getter="is_interpolation_enabled">
+ If [code]true[/code] and a target is set, the camera will move automatically.
</member>
<member name="speed" type="float" setter="set_speed" getter="get_speed">
+ How quickly the camera moves toward its target. Higher values will result in tighter camera motion.
</member>
<member name="target" type="NodePath" setter="set_target_path" getter="get_target_path">
+ The target's [NodePath].
</member>
</members>
<constants>
diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml
index e4db5aeb8..bd1d6be4f 100644
--- a/doc/classes/ItemList.xml
+++ b/doc/classes/ItemList.xml
@@ -87,6 +87,15 @@
<description>
</description>
</method>
+ <method name="get_item_icon_modulate" qualifiers="const">
+ <return type="Color">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <description>
+ Returns a [Color] modulating item's icon at the specified index.
+ </description>
+ </method>
<method name="get_item_icon_region" qualifiers="const">
<return type="Rect2">
</return>
@@ -135,6 +144,13 @@
Returns the current vertical scroll bar for the List.
</description>
</method>
+ <method name="is_anything_selected">
+ <return type="bool">
+ </return>
+ <description>
+ Returns [code]true[/code] if one or more items are selected.
+ </description>
+ </method>
<method name="is_item_disabled" qualifiers="const">
<return type="bool">
</return>
@@ -171,6 +187,17 @@
Returns whether or not item at the specified index is currently selected.
</description>
</method>
+ <method name="move_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="from_idx" type="int">
+ </argument>
+ <argument index="1" name="to_idx" type="int">
+ </argument>
+ <description>
+ Moves item at index [code]from_idx[/code] to [code]to_idx[/code].
+ </description>
+ </method>
<method name="remove_item">
<return type="void">
</return>
@@ -225,6 +252,17 @@
Set (or replace) icon of the item at the specified index.
</description>
</method>
+ <method name="set_item_icon_modulate">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="modulate" type="Color">
+ </argument>
+ <description>
+ Sets a modulating [Color] for item's icon at the specified index.
+ </description>
+ </method>
<method name="set_item_icon_region">
<return type="void">
</return>
@@ -306,6 +344,13 @@
Ensure item at specified index is not selected.
</description>
</method>
+ <method name="unselect_all">
+ <return type="void">
+ </return>
+ <description>
+ Ensure there are no items selected.
+ </description>
+ </method>
</methods>
<members>
<member name="allow_reselect" type="bool" setter="set_allow_reselect" getter="get_allow_reselect">
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index b6adf84ab..c31438283 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -110,7 +110,10 @@
Text shown when the [LineEdit] is empty. It is [b]not[/b] the [LineEdit]'s default value (see [member text]).
</member>
<member name="secret" type="bool" setter="set_secret" getter="is_secret">
- If [code]true[/code] every character is shown as "*".
+ If [code]true[/code], every character is replaced with the secret character (see [member secret_character]).
+ </member>
+ <member name="secret_character" type="string" setter="set_secret_character" getter="get_secret_character">
+ The character to use to mask secret input (defaults to "*"). Only a single character can be used as the secret character.
</member>
<member name="text" type="String" setter="set_text" getter="get_text">
String value of the [LineEdit].
diff --git a/doc/classes/LinkButton.xml b/doc/classes/LinkButton.xml
index f41980295..a09edfad8 100644
--- a/doc/classes/LinkButton.xml
+++ b/doc/classes/LinkButton.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="LinkButton" inherits="BaseButton" category="Core" version="3.1">
<brief_description>
- Simple button used to represent a link to some resource
+ Simple button used to represent a link to some resource.
</brief_description>
<description>
This kind of buttons are primarily used when the interaction with the button causes a context change (like linking to a web page).
diff --git a/doc/classes/MultiplayerAPI.xml b/doc/classes/MultiplayerAPI.xml
new file mode 100644
index 000000000..591d71517
--- /dev/null
+++ b/doc/classes/MultiplayerAPI.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="MultiplayerAPI" inherits="Reference" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="add_peer">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="clear">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="connected_to_server">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="connection_failed">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="del_peer">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_network_connected_peers" qualifiers="const">
+ <return type="PoolIntArray">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_network_unique_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_rpc_sender_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="has_network_peer" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_network_server" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="poll">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="server_disconnected">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="set_root_node">
+ <return type="void">
+ </return>
+ <argument index="0" name="node" type="Node">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="network_peer" type="NetworkedMultiplayerPeer" setter="set_network_peer" getter="get_network_peer">
+ </member>
+ <member name="refuse_new_network_connections" type="bool" setter="set_refuse_new_network_connections" getter="is_refusing_new_network_connections">
+ </member>
+ </members>
+ <signals>
+ <signal name="connected_to_server">
+ <description>
+ </description>
+ </signal>
+ <signal name="connection_failed">
+ <description>
+ </description>
+ </signal>
+ <signal name="network_peer_connected">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="network_peer_disconnected">
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </signal>
+ <signal name="server_disconnected">
+ <description>
+ </description>
+ </signal>
+ </signals>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/Navigation.xml b/doc/classes/Navigation.xml
index b3b9ada26..08f22d49d 100644
--- a/doc/classes/Navigation.xml
+++ b/doc/classes/Navigation.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Navigation" inherits="Spatial" category="Core" version="3.1">
<brief_description>
- A collection of [code]NavigationMesh[/code] resources and methods used for pathfinding.
+ Mesh-based navigation and pathfinding node.
</brief_description>
<description>
- The Navigation node is used for basic or advanced navigation. By default it will automatically collect all child [code]NavigationMesh[/code] resources, but they can also be added on the fly through scripting. It can be used for generating a simple path between two points or it can be used to ensure that a navigation agent is angled perfectly to the terrain it is navigating.
+ Provides navigation and pathfinding within a collection of [NavigationMesh]es. By default these will be automatically collected from child [NavigationMeshInstance] nodes, but they can also be added on the fly with [method navmesh_add]. In addition to basic pathfinding, this class also assists with aligning navigation agents with the meshes they are navigating on.
</description>
<tutorials>
</tutorials>
@@ -17,7 +17,7 @@
<argument index="0" name="to_point" type="Vector3">
</argument>
<description>
- Returns the closest navigation point to the point passed.
+ Returns the navigation point closest to the point given. Points are in local coordinate space.
</description>
</method>
<method name="get_closest_point_normal">
@@ -26,7 +26,7 @@
<argument index="0" name="to_point" type="Vector3">
</argument>
<description>
- Returns the surface normal of the navigation mesh at the point passed. For instance, if the point passed was at a 45 degree slope it would return something like (0.5,0.5,0). This is useful for rotating a navigation agent in accordance with the [code]NavigationMesh[/code].
+ Returns the surface normal at the navigation point closest to the point given. Useful for rotating a navigation agent according to the navigation mesh it moves on.
</description>
</method>
<method name="get_closest_point_owner">
@@ -35,7 +35,7 @@
<argument index="0" name="to_point" type="Vector3">
</argument>
<description>
- Returns the nearest [code]NavigationMeshInstance[/code] to the point passed.
+ Returns the owner of the [NavigationMesh] which contains the navigation point closest to the point given. This is usually a [NavigtionMeshInstance]. For meshes added via [method navmesh_add], returns the owner that was given (or [code]null[/code] if the [code]owner[/code] parameter was omitted).
</description>
</method>
<method name="get_closest_point_to_segment">
@@ -48,7 +48,7 @@
<argument index="2" name="use_collision" type="bool" default="false">
</argument>
<description>
- Returns the nearest point to the line segment passed. The third optional parameter takes collisions into account.
+ Returns the navigation point closest to the given line segment. When enabling [code]use_collision[/code], only considers intersection points between segment and navigation meshes. If multiple intersection points are found, the one closest to the segment start point is returned.
</description>
</method>
<method name="get_simple_path">
@@ -61,7 +61,7 @@
<argument index="2" name="optimize" type="bool" default="true">
</argument>
<description>
- Returns a path of points as a [code]PoolVector3Array[/code]. If [code]optimize[/code] is false the [code]NavigationMesh[/code] agent properties will be taken into account, otherwise it will return the nearest path and ignore agent radius, height, etc.
+ Returns the path between two given points. Points are in local coordinate space. If [code]optimize[/code] is [code]true[/code] (the default), the agent properties associated with each [NavigationMesh] (raidus, height, etc.) are considered in the path calculation, otherwise they are ignored.
</description>
</method>
<method name="navmesh_add">
@@ -74,7 +74,7 @@
<argument index="2" name="owner" type="Object" default="null">
</argument>
<description>
- Adds a [code]NavigationMesh[/code] to the list of NavigationMesh's in this node. Returns an id. Its position, rotation and scale are associated with the [code]Transform[/code] passed. The [code]Node[/code] (or [code]Object[/code]) that owns this node is an optional parameter.
+ Adds a [NavigationMesh]. Returns an ID for use with [method navmesh_remove] or [method navmesh_set_transform]. If given, a [Transform2D] is applied to the polygon. The optional [code]owner[/code] is used as return value for [method get_closest_point_owner].
</description>
</method>
<method name="navmesh_remove">
@@ -83,7 +83,7 @@
<argument index="0" name="id" type="int">
</argument>
<description>
- Removes a [code]NavigationMesh[/code] from the list of NavigationMesh's in this node.
+ Removes the [NavigationMesh] with the given ID.
</description>
</method>
<method name="navmesh_set_transform">
@@ -94,13 +94,13 @@
<argument index="1" name="xform" type="Transform">
</argument>
<description>
- Associates a [code]NavigationMesh[/code]'s id with a [code]Transform[/code]. Its position, rotation and scale are based on the [code]Transform[/code] passed.
+ Sets the transform applied to the [NavigationMesh] with the given ID.
</description>
</method>
</methods>
<members>
<member name="up_vector" type="Vector3" setter="set_up_vector" getter="get_up_vector">
- Defines which direction is up. The default defines 0,1,0 as up which is the world up direction. To make this a ceiling use 0,-1,0 to define down as up.
+ Defines which direction is up. By default this is [code](0, 1, 0)[/code], which is the world up direction.
</member>
</members>
<constants>
diff --git a/doc/classes/Navigation2D.xml b/doc/classes/Navigation2D.xml
index d2f451337..364da55f9 100644
--- a/doc/classes/Navigation2D.xml
+++ b/doc/classes/Navigation2D.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Navigation2D" inherits="Node2D" category="Core" version="3.1">
<brief_description>
- Class to assist with character navigation and pathfinding.
+ 2D navigation and pathfinding node.
</brief_description>
<description>
+ Navigation2D provides navigation and pathfinding within a 2D area, specified as a collection of [NavigationPolygon] resources. By default these are automatically collected from child [NavigationPolygonInstance] nodes, but they can also be added on the fly with [method navpoly_add].
</description>
<tutorials>
</tutorials>
@@ -16,6 +17,7 @@
<argument index="0" name="to_point" type="Vector2">
</argument>
<description>
+ Returns the navigation point closest to the point given. Points are in local coordinate space.
</description>
</method>
<method name="get_closest_point_owner">
@@ -24,6 +26,7 @@
<argument index="0" name="to_point" type="Vector2">
</argument>
<description>
+ Returns the owner of the [NavigationPolygon] which contains the navigation point closest to the point given. This is usually a [NavigtionPolygonInstance]. For polygons added via [method navpoly_add], returns the owner that was given (or [code]null[/code] if the [code]owner[/code] parameter was omitted).
</description>
</method>
<method name="get_simple_path">
@@ -36,6 +39,7 @@
<argument index="2" name="optimize" type="bool" default="true">
</argument>
<description>
+ Returns the path between two given points. Points are in local coordinate space. If [code]optimize[/code] is [code]true[/code] (the default), the path is smoothed by merging path segments where possible.
</description>
</method>
<method name="navpoly_add">
@@ -48,6 +52,7 @@
<argument index="2" name="owner" type="Object" default="null">
</argument>
<description>
+ Adds a [NavigationPolygon]. Returns an ID for use with [method navpoly_remove] or [method navpoly_set_transform]. If given, a [Transform2D] is applied to the polygon. The optional [code]owner[/code] is used as return value for [method get_closest_point_owner].
</description>
</method>
<method name="navpoly_remove">
@@ -56,6 +61,7 @@
<argument index="0" name="id" type="int">
</argument>
<description>
+ Removes the [NavigationPolygon] with the given ID.
</description>
</method>
<method name="navpoly_set_transform">
@@ -66,6 +72,7 @@
<argument index="1" name="xform" type="Transform2D">
</argument>
<description>
+ Sets the transform applied to the [NavigationPolygon] with the given ID.
</description>
</method>
</methods>
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index 97d09a66c..5d0f70bc5 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -453,7 +453,30 @@
<return type="void">
</return>
<description>
- Prints the scene hierarchy of this node and all it's children to stdout. Used mainly for debugging purposes.
+ Prints the tree to stdout. Used mainly for debugging purposes. This version displays the path relative to the current node, and is good for copy/pasting into the [method get_node] function. Example output:
+ [codeblock]
+ TheGame
+ TheGame/Menu
+ TheGame/Menu/Label
+ TheGame/Menu/Camera2D
+ TheGame/SplashScreen
+ TheGame/SplashScreen/Camera2D
+ [/codeblock]
+ </description>
+ </method>
+ <method name="print_tree_pretty">
+ <return type="void">
+ </return>
+ <description>
+ Similar to [method print_tree], this prints the tree to stdout. This version displays a more graphical representation similar to what is displayed in the scene inspector. It is useful for inspecting larger trees. Example output:
+ [codeblock]
+ ┖╴TheGame
+ ┠╴Menu
+ ┃ ┠╴Label
+ ┃ ┖╴Camera2D
+ ┖-SplashScreen
+ ┖╴Camera2D
+ [/codeblock]
</description>
</method>
<method name="propagate_call">
@@ -739,14 +762,18 @@
</method>
</methods>
<members>
+ <member name="custom_multiplayer_api" type="MultiplayerAPI" setter="set_custom_multiplayer_api" getter="get_custom_multiplayer_api">
+ </member>
<member name="filename" type="String" setter="set_filename" getter="get_filename">
When a scene is instanced from a file, its topmost node contains the filename from which it was loaded.
</member>
+ <member name="multiplayer_api" type="MultiplayerAPI" setter="" getter="get_multiplayer_api">
+ </member>
<member name="name" type="String" setter="set_name" getter="get_name">
The name of the node. This name is unique among the siblings (other child nodes from the same parent). When set to an existing name, the node will be automatically renamed
</member>
<member name="owner" type="Node" setter="set_owner" getter="get_owner">
- 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 SceneSaver) all the nodes it owns will be saved with it. This allows for the creation of complex [SceneTree]s, with instancing and subinstancing.
+ 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 [PackedScene]) all the nodes it owns will be saved with it. This allows for the creation of complex [SceneTree]s, with instancing and subinstancing.
</member>
<member name="pause_mode" type="int" setter="set_pause_mode" getter="get_pause_mode" enum="Node.PauseMode">
Pause mode. How the node will behave if the [SceneTree] is paused.
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index a98385f1d..d38a89874 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -40,6 +40,7 @@
<return type="void">
</return>
<description>
+ Centers the window on the screen if in windowed mode.
</description>
</method>
<method name="delay_msec" qualifiers="const">
@@ -48,7 +49,7 @@
<argument index="0" name="msec" type="int">
</argument>
<description>
- Delay executing of the current thread by given milliseconds.
+ Delay execution of the current thread by given milliseconds.
</description>
</method>
<method name="delay_usec" qualifiers="const">
@@ -57,7 +58,7 @@
<argument index="0" name="usec" type="int">
</argument>
<description>
- Delay executing of the current thread by given microseconds.
+ Delay execution of the current thread by given microseconds.
</description>
</method>
<method name="dump_memory_to_file">
@@ -67,7 +68,7 @@
</argument>
<description>
Dumps the memory allocation ringlist to a file (only works in debug).
- Entry format per line: "Address - Size - Description"
+ Entry format per line: "Address - Size - Description".
</description>
</method>
<method name="dump_resources_to_file">
@@ -77,7 +78,7 @@
</argument>
<description>
Dumps all used resources to file (only works in debug).
- Entry format per line: "Resource Type : Resource Location"
+ Entry format per line: "Resource Type : Resource Location".
At the end of the file is a statistic of all used Resource Types.
</description>
</method>
@@ -116,6 +117,22 @@
Returns the scancode of the given string (e.g. "Escape")
</description>
</method>
+ <method name="get_audio_driver_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ Returns the total number of available audio drivers.
+ </description>
+ </method>
+ <method name="get_audio_driver_name" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="driver" type="int">
+ </argument>
+ <description>
+ Returns the audio driver name for the given index.
+ </description>
+ </method>
<method name="get_cmdline_args">
<return type="PoolStringArray">
</return>
@@ -242,6 +259,7 @@
<return type="Vector2">
</return>
<description>
+ Returns the window size including decorations like window borders.
</description>
</method>
<method name="get_scancode_string" qualifiers="const">
@@ -366,7 +384,7 @@
<return type="int">
</return>
<description>
- Return the current unix timestamp.
+ Returns the current unix epoch timestamp.
</description>
</method>
<method name="get_unix_time_from_datetime" qualifiers="const">
@@ -391,10 +409,25 @@
If the project name is empty, [code]user://[/code] falls back to [code]res://[/code].
</description>
</method>
+ <method name="get_video_driver_count" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_video_driver_name" qualifiers="const">
+ <return type="String">
+ </return>
+ <argument index="0" name="driver" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_virtual_keyboard_height">
<return type="int">
</return>
<description>
+ Returns the on-screen keyboard's height in pixels. Returns 0 if there is no keyboard or it is currently hidden.
</description>
</method>
<method name="has_environment" qualifiers="const">
@@ -412,6 +445,7 @@
<argument index="0" name="tag_name" type="String">
</argument>
<description>
+ Returns [code]true[/code] if the feature for the given feature tag is supported in the currently running instance, depending on platform, build etc. Can be used to check whether you're currently running a debug build, on a certain platform or arch, etc. See feature tags documentation.
</description>
</method>
<method name="has_touchscreen_ui_hint" qualifiers="const">
@@ -478,6 +512,7 @@
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if the window should always be on top of other windows.
</description>
</method>
<method name="kill">
@@ -614,6 +649,7 @@
<argument index="0" name="enabled" type="bool">
</argument>
<description>
+ Sets whether the window should always be on top.
</description>
</method>
<method name="set_window_title">
@@ -654,27 +690,31 @@
The current screen index (starting from 0).
</member>
<member name="exit_code" type="int" setter="set_exit_code" getter="get_exit_code">
+ The exit code passed to the OS when the main loop exits.
</member>
<member name="keep_screen_on" type="bool" setter="set_keep_screen_on" getter="is_keep_screen_on">
+ If [code]true[/code] the engine tries to keep the screen on while the game is running. Useful on mobile.
</member>
<member name="low_processor_usage_mode" type="bool" setter="set_low_processor_usage_mode" getter="is_in_low_processor_usage_mode">
+ If [code]true[/code] the engine optimizes for low processor usage by only refreshing the screen if needed. Can improve battery consumption on mobile.
</member>
- <member name="screen_orientation" type="int" setter="set_screen_orientation" getter="get_screen_orientation" enum="_OS.ScreenOrientation">
+ <member name="screen_orientation" type="int" setter="set_screen_orientation" getter="get_screen_orientation" enum="OS.ScreenOrientation">
The current screen orientation.
</member>
<member name="vsync_enabled" type="bool" setter="set_use_vsync" getter="is_vsync_enabled">
+ If [code]true[/code] vertical synchronization (Vsync) is enabled.
</member>
<member name="window_borderless" type="bool" setter="set_borderless_window" getter="get_borderless_window">
- If [code]true[/code], removes the window frame.
+ If [code]true[/code] removes the window frame.
</member>
<member name="window_fullscreen" type="bool" setter="set_window_fullscreen" getter="is_window_fullscreen">
- If [code]true[/code], the window is fullscreen.
+ If [code]true[/code] the window is fullscreen.
</member>
<member name="window_maximized" type="bool" setter="set_window_maximized" getter="is_window_maximized">
- If [code]true[/code], the window is maximized.
+ If [code]true[/code] the window is maximized.
</member>
<member name="window_minimized" type="bool" setter="set_window_minimized" getter="is_window_minimized">
- If [code]true[/code], the window is minimized.
+ If [code]true[/code] the window is minimized.
</member>
<member name="window_position" type="Vector2" setter="set_window_position" getter="get_window_position">
The window position relative to the screen, the origin is the top left corner, +Y axis goes to the bottom and +X axis goes to the right.
@@ -688,82 +728,121 @@
</members>
<constants>
<constant name="DAY_SUNDAY" value="0" enum="Weekday">
+ Sunday.
</constant>
<constant name="DAY_MONDAY" value="1" enum="Weekday">
+ Monday.
</constant>
<constant name="DAY_TUESDAY" value="2" enum="Weekday">
+ Tuesday.
</constant>
<constant name="DAY_WEDNESDAY" value="3" enum="Weekday">
+ Wednesday.
</constant>
<constant name="DAY_THURSDAY" value="4" enum="Weekday">
+ Thursday.
</constant>
<constant name="DAY_FRIDAY" value="5" enum="Weekday">
+ Friday.
</constant>
<constant name="DAY_SATURDAY" value="6" enum="Weekday">
+ Saturday.
</constant>
<constant name="MONTH_JANUARY" value="1" enum="Month">
+ January.
</constant>
<constant name="MONTH_FEBRUARY" value="2" enum="Month">
+ February.
</constant>
<constant name="MONTH_MARCH" value="3" enum="Month">
+ March.
</constant>
<constant name="MONTH_APRIL" value="4" enum="Month">
+ April.
</constant>
<constant name="MONTH_MAY" value="5" enum="Month">
+ May.
</constant>
<constant name="MONTH_JUNE" value="6" enum="Month">
+ June.
</constant>
<constant name="MONTH_JULY" value="7" enum="Month">
+ July.
</constant>
<constant name="MONTH_AUGUST" value="8" enum="Month">
+ August.
</constant>
<constant name="MONTH_SEPTEMBER" value="9" enum="Month">
+ September.
</constant>
<constant name="MONTH_OCTOBER" value="10" enum="Month">
+ October.
</constant>
<constant name="MONTH_NOVEMBER" value="11" enum="Month">
+ November.
</constant>
<constant name="MONTH_DECEMBER" value="12" enum="Month">
+ December.
</constant>
<constant name="SCREEN_ORIENTATION_LANDSCAPE" value="0" enum="ScreenOrientation">
+ Landscape screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_PORTRAIT" value="1" enum="ScreenOrientation">
+ Portrait screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_REVERSE_LANDSCAPE" value="2" enum="ScreenOrientation">
+ Reverse landscape screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_REVERSE_PORTRAIT" value="3" enum="ScreenOrientation">
+ Reverse portrait screen orientation.
</constant>
<constant name="SCREEN_ORIENTATION_SENSOR_LANDSCAPE" value="4" enum="ScreenOrientation">
+ Uses landscape or reverse landscape based on the hardware sensor.
</constant>
<constant name="SCREEN_ORIENTATION_SENSOR_PORTRAIT" value="5" enum="ScreenOrientation">
+ Uses portrait or reverse portrait based on the hardware sensor.
</constant>
<constant name="SCREEN_ORIENTATION_SENSOR" value="6" enum="ScreenOrientation">
+ Uses most suitable orientation based on the hardware sensor.
</constant>
<constant name="SYSTEM_DIR_DESKTOP" value="0" enum="SystemDir">
+ Desktop directory path.
</constant>
<constant name="SYSTEM_DIR_DCIM" value="1" enum="SystemDir">
+ DCIM (Digital Camera Images) directory path.
</constant>
<constant name="SYSTEM_DIR_DOCUMENTS" value="2" enum="SystemDir">
+ Documents directory path.
</constant>
<constant name="SYSTEM_DIR_DOWNLOADS" value="3" enum="SystemDir">
+ Downloads directory path.
</constant>
<constant name="SYSTEM_DIR_MOVIES" value="4" enum="SystemDir">
+ Movies directory path.
</constant>
<constant name="SYSTEM_DIR_MUSIC" value="5" enum="SystemDir">
+ Music directory path.
</constant>
<constant name="SYSTEM_DIR_PICTURES" value="6" enum="SystemDir">
+ Pictures directory path.
</constant>
<constant name="SYSTEM_DIR_RINGTONES" value="7" enum="SystemDir">
+ Ringtones directory path.
</constant>
<constant name="POWERSTATE_UNKNOWN" value="0" enum="PowerState">
+ Unknown powerstate.
</constant>
<constant name="POWERSTATE_ON_BATTERY" value="1" enum="PowerState">
+ Unplugged, running on battery.
</constant>
<constant name="POWERSTATE_NO_BATTERY" value="2" enum="PowerState">
+ Plugged in, no battery available.
</constant>
<constant name="POWERSTATE_CHARGING" value="3" enum="PowerState">
+ Plugged in, battery charging.
</constant>
<constant name="POWERSTATE_CHARGED" value="4" enum="PowerState">
+ Plugged in, battery fully charged.
</constant>
</constants>
</class>
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index b5986ec0f..071783636 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -398,6 +398,7 @@
<signals>
<signal name="script_changed">
<description>
+ Emitted whenever the script of the Object is changed.
</description>
</signal>
</signals>
diff --git a/doc/classes/OptionButton.xml b/doc/classes/OptionButton.xml
index de6f60c38..8cb53dd98 100644
--- a/doc/classes/OptionButton.xml
+++ b/doc/classes/OptionButton.xml
@@ -194,6 +194,13 @@
</member>
</members>
<signals>
+ <signal name="item_focused">
+ <argument index="0" name="ID" type="int">
+ </argument>
+ <description>
+ This signal is emitted when user navigated to an item using [code]ui_up[/code] or [code]ui_down[/code] action. ID of the item selected is passed as argument (if no IDs were added, ID will be just the item index).
+ </description>
+ </signal>
<signal name="item_selected">
<argument index="0" name="ID" type="int">
</argument>
diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml
index cf89e6a2d..8d810bc9c 100644
--- a/doc/classes/PackedScene.xml
+++ b/doc/classes/PackedScene.xml
@@ -5,7 +5,14 @@
</brief_description>
<description>
A simplified interface to a scene file. Provides access to operations and checks that can be performed on the scene resource itself.
- TODO: explain ownership, and that node does not need to own itself
+ Can be used to save a node to a file. When saving, the node as well as all the node it owns get saved (see [code]owner[/code] property on [Node]). Note that the node doesn't need to own itself.
+ Example of saving a node:
+ [codeblock]
+ var scene = PackedScene.new()
+ var result = scene.pack(child)
+ if result == OK:
+ ResourceSaver.save("res://path/name.scn", scene) // or user://...
+ [/codeblock]
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PoolByteArray.xml b/doc/classes/PoolByteArray.xml
index 0118e7477..120d80ba3 100644
--- a/doc/classes/PoolByteArray.xml
+++ b/doc/classes/PoolByteArray.xml
@@ -4,7 +4,7 @@
Raw byte array.
</brief_description>
<description>
- Raw byte array. Contains bytes. Optimized for memory usage, can't fragment the memory.
+ Raw byte array. Contains bytes. Optimized for memory usage, can't fragment the memory. Note that this type is passed by value and not by reference.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PoolColorArray.xml b/doc/classes/PoolColorArray.xml
index 0a4221728..0c93a565f 100644
--- a/doc/classes/PoolColorArray.xml
+++ b/doc/classes/PoolColorArray.xml
@@ -4,7 +4,7 @@
Array of Colors
</brief_description>
<description>
- Array of Color, Contains colors. Optimized for memory usage, can't fragment the memory.
+ Array of Color, Contains colors. Optimized for memory usage, can't fragment the memory. Note that this type is passed by value and not by reference.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PoolIntArray.xml b/doc/classes/PoolIntArray.xml
index 7709a69c8..43cb5d77d 100644
--- a/doc/classes/PoolIntArray.xml
+++ b/doc/classes/PoolIntArray.xml
@@ -4,7 +4,7 @@
Integer Array.
</brief_description>
<description>
- Integer Array. Contains integers. Optimized for memory usage, can't fragment the memory.
+ Integer Array. Contains integers. Optimized for memory usage, can't fragment the memory. Note that this type is passed by value and not by reference.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PoolRealArray.xml b/doc/classes/PoolRealArray.xml
index 19f31f7df..0808b4410 100644
--- a/doc/classes/PoolRealArray.xml
+++ b/doc/classes/PoolRealArray.xml
@@ -4,7 +4,7 @@
Real Array.
</brief_description>
<description>
- Real Array. Array of floating point values. Can only contain floats. Optimized for memory usage, can't fragment the memory.
+ Real Array. Array of floating point values. Can only contain floats. Optimized for memory usage, can't fragment the memory. Note that this type is passed by value and not by reference.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PoolStringArray.xml b/doc/classes/PoolStringArray.xml
index 3d971d1ac..9f6c4306c 100644
--- a/doc/classes/PoolStringArray.xml
+++ b/doc/classes/PoolStringArray.xml
@@ -4,7 +4,7 @@
String Array.
</brief_description>
<description>
- String Array. Array of strings. Can only contain strings. Optimized for memory usage, can't fragment the memory.
+ String Array. Array of strings. Can only contain strings. Optimized for memory usage, can't fragment the memory. Note that this type is passed by value and not by reference.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PoolVector2Array.xml b/doc/classes/PoolVector2Array.xml
index 14c226d01..072281158 100644
--- a/doc/classes/PoolVector2Array.xml
+++ b/doc/classes/PoolVector2Array.xml
@@ -4,7 +4,7 @@
An Array of Vector2.
</brief_description>
<description>
- An Array specifically designed to hold Vector2.
+ An Array specifically designed to hold Vector2. Note that this type is passed by value and not by reference.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/PoolVector3Array.xml b/doc/classes/PoolVector3Array.xml
index 27ddfa10a..7aa5dfc09 100644
--- a/doc/classes/PoolVector3Array.xml
+++ b/doc/classes/PoolVector3Array.xml
@@ -4,7 +4,7 @@
An Array of Vector3.
</brief_description>
<description>
- An Array specifically designed to hold Vector3.
+ An Array specifically designed to hold Vector3. Note that this type is passed by value and not by reference.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/Popup.xml b/doc/classes/Popup.xml
index abf6ec323..db8d927c9 100644
--- a/doc/classes/Popup.xml
+++ b/doc/classes/Popup.xml
@@ -50,6 +50,7 @@
</methods>
<members>
<member name="popup_exclusive" type="bool" setter="set_exclusive" getter="is_exclusive">
+ If [code]true[/code] the popup will not be hidden when a click event occurs outside of it, or when it receives the [code]ui_cancel[/code] action event.
</member>
</members>
<signals>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 70e6fab5b..166a4be2b 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -108,6 +108,31 @@
Add a new item with text "label". An id can optionally be provided, as well as an accelerator keybinding. If no id is provided, one will be created from the index.
</description>
</method>
+ <method name="add_radio_check_item">
+ <return type="void">
+ </return>
+ <argument index="0" name="label" type="String">
+ </argument>
+ <argument index="1" name="id" type="int" default="-1">
+ </argument>
+ <argument index="2" name="accel" type="int" default="0">
+ </argument>
+ <description>
+ The same as [method add_check_item] but the inserted item will look as a radio button. Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups.
+ </description>
+ </method>
+ <method name="add_radio_check_shortcut">
+ <return type="void">
+ </return>
+ <argument index="0" name="shortcut" type="ShortCut">
+ </argument>
+ <argument index="1" name="id" type="int" default="-1">
+ </argument>
+ <argument index="2" name="global" type="bool" default="false">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="add_separator">
<return type="void">
</return>
@@ -239,7 +264,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
- Return whether the item at index "idx" has a checkbox. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually.
+ Return whether the item at index "idx" is checkable in some way, i.e., whether has a checkbox or radio button. Note that checkable items just display a checkmark or radio button, but don't have any built-in checking behavior and must be checked/unchecked manually.
</description>
</method>
<method name="is_item_checked" qualifiers="const">
@@ -248,7 +273,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
- Return the checkstate status of the item at index "idx".
+ Return whether the item at index "idx" is checked.
</description>
</method>
<method name="is_item_disabled" qualifiers="const">
@@ -260,6 +285,15 @@
Return whether the item at index "idx" is disabled. When it is disabled it can't be selected, or its action invoked.
</description>
</method>
+ <method name="is_item_radio_checkable" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <description>
+ Return whether the item at index "idx" has radio-button-style checkability. Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups.
+ </description>
+ </method>
<method name="is_item_separator" qualifiers="const">
<return type="bool">
</return>
@@ -300,6 +334,18 @@
Set whether the item at index "idx" has a checkbox. Note that checkable items just display a checkmark, but don't have any built-in checking behavior and must be checked/unchecked manually.
</description>
</method>
+ <method name="set_item_as_radio_checkable">
+ <return type="void">
+ </return>
+ <argument index="0" name="idx" type="int">
+ </argument>
+ <argument index="1" name="enable" type="bool">
+ </argument>
+ <description>
+ The same as [method set_item_as_checkable] but placing a radio button in case of enabling. If used for disabling, it's the same.
+ Remember this is just cosmetic and you have to add the logic for checking/unchecking items in radio groups.
+ </description>
+ </method>
<method name="set_item_as_separator">
<return type="void">
</return>
@@ -445,6 +491,13 @@
</member>
</members>
<signals>
+ <signal name="id_focused">
+ <argument index="0" name="ID" type="int">
+ </argument>
+ <description>
+ This event is emitted when user navigated to an item of some id using [code]ui_up[/code] or [code]ui_down[/code] action.
+ </description>
+ </signal>
<signal name="id_pressed">
<argument index="0" name="ID" type="int">
</argument>
@@ -483,6 +536,10 @@
</theme_item>
<theme_item name="panel_disabled" type="StyleBox">
</theme_item>
+ <theme_item name="radio_checked" type="Texture">
+ </theme_item>
+ <theme_item name="radio_unchecked" type="Texture">
+ </theme_item>
<theme_item name="separator" type="StyleBox">
</theme_item>
<theme_item name="submenu" type="Texture">
diff --git a/doc/classes/Position3D.xml b/doc/classes/Position3D.xml
index de29ac215..7c0875cc0 100644
--- a/doc/classes/Position3D.xml
+++ b/doc/classes/Position3D.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Position3D" inherits="Spatial" category="Core" version="3.1">
<brief_description>
- Generic 3D Position hint for editing
+ Generic 3D Position hint for editing.
</brief_description>
<description>
Generic 3D Position hint for editing. It's just like a plain [Spatial] but displays as a cross in the 3D-Editor at all times.
diff --git a/doc/classes/Quat.xml b/doc/classes/Quat.xml
index 3a258011b..33f2b9758 100644
--- a/doc/classes/Quat.xml
+++ b/doc/classes/Quat.xml
@@ -10,6 +10,7 @@
It can be used to perform SLERP (spherical-linear interpolation) between two rotations.
</description>
<tutorials>
+ http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html#interpolating-with-quaternions
</tutorials>
<demos>
</demos>
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 49bb1aed4..a6c63cfb7 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -75,6 +75,13 @@
Returns the number of visible lines.
</description>
</method>
+ <method name="get_content_height">
+ <return type="int">
+ </return>
+ <description>
+ Returns the height of the content.
+ </description>
+ </method>
<method name="newline">
<return type="void">
</return>
@@ -319,6 +326,14 @@
</theme_item>
<theme_item name="font_color_selected" type="Color">
</theme_item>
+ <theme_item name="font_color_shadow" type="Color">
+ </theme_item>
+ <theme_item name="shadow_as_outline" type="int">
+ </theme_item>
+ <theme_item name="shadow_offset_x" type="int">
+ </theme_item>
+ <theme_item name="shadow_offset_y" type="int">
+ </theme_item>
<theme_item name="italics_font" type="Font">
</theme_item>
<theme_item name="line_separation" type="int">
diff --git a/doc/classes/SceneTree.xml b/doc/classes/SceneTree.xml
index be9028830..dec0cbbcc 100644
--- a/doc/classes/SceneTree.xml
+++ b/doc/classes/SceneTree.xml
@@ -1,12 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="SceneTree" inherits="MainLoop" category="Core" version="3.1">
<brief_description>
+ SceneTree manages a hierarchy of nodes.
</brief_description>
<description>
+ As one of the most important classes, the [code]SceneTree[/code] manages the hierarchy of nodes in a scene as well as scenes themselves. Nodes can be added, retrieved and removed. The whole scene tree (and thus the current scene) can be paused. Scenes can be loaded, switched and reloaded. You can also use the SceneTree to organize your nodes into groups: every node can be assigned as many groups as you want to create, e.g. a "enemy" group. You can then iterate these groups or even call methods and set properties on all the group's members at once.
</description>
<tutorials>
http://docs.godotengine.org/en/3.0/getting_started/step_by_step/scene_tree.html
- http://docs.godotengine.org/en/3.0/tutorials/viewports/multiple_resolutions.html
+ http://docs.godotengine.org/en/3.0/tutorials/viewports/multiple_resolutions.html
</tutorials>
<demos>
</demos>
@@ -19,6 +21,7 @@
<argument index="1" name="method" type="String">
</argument>
<description>
+ Calls [code]method[/code] on each member of the given group.
</description>
</method>
<method name="call_group_flags" qualifiers="vararg">
@@ -31,6 +34,7 @@
<argument index="2" name="method" type="String">
</argument>
<description>
+ Calls [code]method[/code] on each member of the given group, respecting the given [enum GROUP_CALL] flags.
</description>
</method>
<method name="change_scene">
@@ -39,6 +43,7 @@
<argument index="0" name="path" type="String">
</argument>
<description>
+ Changes to the scene at the given [code]path[/code].
</description>
</method>
<method name="change_scene_to">
@@ -47,6 +52,7 @@
<argument index="0" name="packed_scene" type="PackedScene">
</argument>
<description>
+ Changes to the given [PackedScene].
</description>
</method>
<method name="create_timer">
@@ -57,6 +63,7 @@
<argument index="1" name="pause_mode_process" type="bool" default="true">
</argument>
<description>
+ Returns a [SceneTreeTimer] which will [signal SceneTreeTimer.timeout] after the given time in seconds elapsed in this SceneTree. If [code]pause_mode_process[/code] is set to false, pausing the SceneTree will also pause the timer.
</description>
</method>
<method name="get_frame" qualifiers="const">
@@ -69,18 +76,21 @@
<return type="PoolIntArray">
</return>
<description>
+ Returns the peer IDs of all connected peers of this SceneTree's [member network_peer].
</description>
</method>
<method name="get_network_unique_id" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the unique peer ID of this SceneTree's [member network_peer].
</description>
</method>
<method name="get_node_count" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the number of nodes in this SceneTree.
</description>
</method>
<method name="get_nodes_in_group">
@@ -89,12 +99,14 @@
<argument index="0" name="group" type="String">
</argument>
<description>
+ Returns all nodes assigned to the given group.
</description>
</method>
<method name="get_rpc_sender_id" qualifiers="const">
<return type="int">
</return>
<description>
+ Returns the sender's peer ID for the most recently received RPC call.
</description>
</method>
<method name="has_group" qualifiers="const">
@@ -103,26 +115,28 @@
<argument index="0" name="name" type="String">
</argument>
<description>
+ Returns [code]true[/code] if the given group exists.
</description>
</method>
<method name="has_network_peer" qualifiers="const">
<return type="bool">
</return>
<description>
- Returns true if there is a [NetworkedMultiplayerPeer] set (with [method SceneTree.set_network_peer]).
+ Returns [code]true[/code] if there is a [member network_peer] set.
</description>
</method>
<method name="is_input_handled">
<return type="bool">
</return>
<description>
+ Returns [code]true[/code] if the most recent InputEvent was marked as handled with [method set_input_as_handled].
</description>
</method>
<method name="is_network_server" qualifiers="const">
<return type="bool">
</return>
<description>
- Returns true if this SceneTree's [NetworkedMultiplayerPeer] is in server mode (listening for connections).
+ Returns [code]true[/code] if this SceneTree's [member network_peer] is in server mode (listening for connections).
</description>
</method>
<method name="notify_group">
@@ -133,6 +147,7 @@
<argument index="1" name="notification" type="int">
</argument>
<description>
+ Sends the given notification to all members of the [code]group[/code].
</description>
</method>
<method name="notify_group_flags">
@@ -145,6 +160,7 @@
<argument index="2" name="notification" type="int">
</argument>
<description>
+ Sends the given notification to all members of the [code]group[/code], respecting the given [enum GROUP_CALL] flags.
</description>
</method>
<method name="queue_delete">
@@ -153,18 +169,21 @@
<argument index="0" name="obj" type="Object">
</argument>
<description>
+ Queues the given object for deletion, delaying the call to [method Object.free] to after the current frame.
</description>
</method>
<method name="quit">
<return type="void">
</return>
<description>
+ Quits the application.
</description>
</method>
<method name="reload_current_scene">
<return type="int" enum="Error">
</return>
<description>
+ Reloads the currently active scene.
</description>
</method>
<method name="set_auto_accept_quit">
@@ -173,6 +192,7 @@
<argument index="0" name="enabled" type="bool">
</argument>
<description>
+ If [code]true[/code] the application automatically accepts quitting.
</description>
</method>
<method name="set_group">
@@ -185,6 +205,7 @@
<argument index="2" name="value" type="Variant">
</argument>
<description>
+ Sets the given [code]property[/code] to [code]value[/code] on all members of the given group.
</description>
</method>
<method name="set_group_flags">
@@ -199,12 +220,14 @@
<argument index="3" name="value" type="Variant">
</argument>
<description>
+ Sets the given [code]property[/code] to [code]value[/code] on all members of the given group, respecting the given [enum GROUP_CALL] flags.
</description>
</method>
<method name="set_input_as_handled">
<return type="void">
</return>
<description>
+ Marks the most recent input event as handled.
</description>
</method>
<method name="set_quit_on_go_back">
@@ -213,6 +236,7 @@
<argument index="0" name="enabled" type="bool">
</argument>
<description>
+ If [code]true[/code] the application quits automatically on going back (e.g. on Android).
</description>
</method>
<method name="set_screen_stretch">
@@ -227,37 +251,48 @@
<argument index="3" name="shrink" type="float" default="1">
</argument>
<description>
+ Configures screen stretching to the given [enum StretchMode], [enum StretchAspect], minimum size and [code]shrink[/code].
</description>
</method>
</methods>
<members>
<member name="current_scene" type="Node" setter="set_current_scene" getter="get_current_scene">
+ The current scene.
</member>
<member name="debug_collisions_hint" type="bool" setter="set_debug_collisions_hint" getter="is_debugging_collisions_hint">
</member>
<member name="debug_navigation_hint" type="bool" setter="set_debug_navigation_hint" getter="is_debugging_navigation_hint">
</member>
<member name="edited_scene_root" type="Node" setter="set_edited_scene_root" getter="get_edited_scene_root">
+ The root of the edited scene.
+ </member>
+ <member name="multiplayer_api" type="MultiplayerAPI" setter="set_multiplayer_api" getter="get_multiplayer_api">
</member>
<member name="network_peer" type="NetworkedMultiplayerPeer" setter="set_network_peer" getter="get_network_peer">
The peer object to handle the RPC system (effectively enabling networking when set). Depending on the peer itself, the SceneTree will become a network server (check with [method is_network_server()]) and will set root node's network mode to master (see NETWORK_MODE_* constants in [Node]), or it will become a regular peer with root node set to slave. All child nodes are set to inherit the network mode by default. Handling of networking-related events (connection, disconnection, new clients) is done by connecting to SceneTree's signals.
</member>
<member name="paused" type="bool" setter="set_pause" getter="is_paused">
+ If [code]true[/code] the SceneTree is paused.
</member>
<member name="refuse_new_network_connections" type="bool" setter="set_refuse_new_network_connections" getter="is_refusing_new_network_connections">
+ If [code]true[/code] the SceneTree's [member network_peer] refuses new incoming connections.
</member>
<member name="root" type="Viewport" setter="" getter="get_root">
+ The SceneTree's [Viewport].
</member>
<member name="use_font_oversampling" type="bool" setter="set_use_font_oversampling" getter="is_using_font_oversampling">
+ If [code]true[/code] font oversampling is used.
</member>
</members>
<signals>
<signal name="connected_to_server">
<description>
+ Emitted whenever this SceneTree's [member network_peer] successfully connected to a server. Only emitted on clients.
</description>
</signal>
<signal name="connection_failed">
<description>
+ Emitted whenever this SceneTree's [member network_peer] fails to establish a connection to a server. Only emitted on clients.
</description>
</signal>
<signal name="files_dropped">
@@ -266,67 +301,82 @@
<argument index="1" name="screen" type="int">
</argument>
<description>
+ Emitted whenever files are drag-and-dropped onto the window.
</description>
</signal>
<signal name="idle_frame">
<description>
+ Emitted immediately before [method Node._process] is called on every node in the SceneTree.
</description>
</signal>
<signal name="network_peer_connected">
<argument index="0" name="id" type="int">
</argument>
<description>
+ Emitted whenever this SceneTree's [member network_peer] connects with a new peer. ID is the peer ID of the new peer. Clients get notified when other clients connect to the same server. Upon connecting to a server, a client also receives this signal for the server (with ID being 1).
</description>
</signal>
<signal name="network_peer_disconnected">
<argument index="0" name="id" type="int">
</argument>
<description>
+ Emitted whenever this SceneTree's [member network_peer] disconnects from a peer. Clients get notified when other clients disconnect from the same server.
</description>
</signal>
<signal name="node_added">
<argument index="0" name="node" type="Object">
</argument>
<description>
+ Emitted whenever a node is added to the SceneTree.
</description>
</signal>
<signal name="node_configuration_warning_changed">
<argument index="0" name="node" type="Object">
</argument>
<description>
+ Emitted when a node's configuration changed. Only emitted in tool mode.
</description>
</signal>
<signal name="node_removed">
<argument index="0" name="node" type="Object">
</argument>
<description>
+ Emitted whenever a node is removed from the SceneTree.
</description>
</signal>
<signal name="physics_frame">
<description>
+ Emitted immediately before [method Node._physics_process] is called on every node in the SceneTree.
</description>
</signal>
<signal name="screen_resized">
<description>
+ Emitted whenever the screen resolution (fullscreen) or window size (windowed) changes.
</description>
</signal>
<signal name="server_disconnected">
<description>
+ Emitted whenever this SceneTree's [member network_peer] disconnected from server. Only emitted on clients.
</description>
</signal>
<signal name="tree_changed">
<description>
+ Emitted whenever the SceneTree hierarchy changed (children being moved or renamed, etc.).
</description>
</signal>
</signals>
<constants>
<constant name="GROUP_CALL_DEFAULT" value="0" enum="GroupCallFlags">
+ Call a group with no flags (default).
</constant>
<constant name="GROUP_CALL_REVERSE" value="1" enum="GroupCallFlags">
+ Call a group in reverse scene order.
</constant>
<constant name="GROUP_CALL_REALTIME" value="2" enum="GroupCallFlags">
+ Call a group immediately (calls are normally made on idle).
</constant>
<constant name="GROUP_CALL_UNIQUE" value="4" enum="GroupCallFlags">
+ Call a group only once even if the call is executed many times.
</constant>
<constant name="STRETCH_MODE_DISABLED" value="0" enum="StretchMode">
</constant>
diff --git a/doc/classes/Script.xml b/doc/classes/Script.xml
index 4257991e0..97f6a60f0 100644
--- a/doc/classes/Script.xml
+++ b/doc/classes/Script.xml
@@ -24,6 +24,7 @@
<return type="Script">
</return>
<description>
+ Returns the script directly inherited by this script.
</description>
</method>
<method name="get_instance_base_type" qualifiers="const">
diff --git a/doc/classes/ScrollBar.xml b/doc/classes/ScrollBar.xml
index d3e6c62e4..736b78021 100644
--- a/doc/classes/ScrollBar.xml
+++ b/doc/classes/ScrollBar.xml
@@ -19,6 +19,7 @@
<signals>
<signal name="scrolling">
<description>
+ Emitted whenever the scrollbar is being scrolled.
</description>
</signal>
</signals>
diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml
index 79b70fec9..64b0fcfd8 100644
--- a/doc/classes/ScrollContainer.xml
+++ b/doc/classes/ScrollContainer.xml
@@ -13,6 +13,8 @@
<methods>
</methods>
<members>
+ <member name="scroll_deadzone" type="int" setter="set_deadzone" getter="get_deadzone">
+ </member>
<member name="scroll_horizontal" type="int" setter="set_h_scroll" getter="get_h_scroll">
The current horizontal scroll value.
</member>
@@ -26,6 +28,18 @@
If [code]true[/code], enables vertical scrolling.
</member>
</members>
+ <signals>
+ <signal name="scroll_ended">
+ <description>
+ Emitted whenever scrolling stops.
+ </description>
+ </signal>
+ <signal name="scroll_started">
+ <description>
+ Emitted whenever scrolling is started.
+ </description>
+ </signal>
+ </signals>
<constants>
</constants>
</class>
diff --git a/doc/classes/Spatial.xml b/doc/classes/Spatial.xml
index 822a69998..9ef60109d 100644
--- a/doc/classes/Spatial.xml
+++ b/doc/classes/Spatial.xml
@@ -99,7 +99,9 @@
<argument index="1" name="up" type="Vector3">
</argument>
<description>
- Rotates itself to point into direction of target position. Operations take place in global space.
+ Rotates itself so that the local -Z axis points towards the [code]target[/code] position.
+ The transform will first be rotated around the given [code]up[/code] vector, and then fully aligned to the target by a further rotation around an axis perpendicular to both the [code]target[/code] and [code]up[/code] vectors.
+ Operations take place in global space.
</description>
</method>
<method name="look_at_from_position">
@@ -112,7 +114,7 @@
<argument index="2" name="up" type="Vector3">
</argument>
<description>
- Moves the node to specified position and then rotates itself to point into direction of target position. Operations take place in global space.
+ Moves the node to the specified [code]position[/code], and then rotates itself to point toward the [code]target[/code] as per [method look_at]. Operations take place in global space.
</description>
</method>
<method name="orthonormalize">
diff --git a/doc/classes/SplitContainer.xml b/doc/classes/SplitContainer.xml
index 692598f38..d56da6844 100644
--- a/doc/classes/SplitContainer.xml
+++ b/doc/classes/SplitContainer.xml
@@ -16,6 +16,7 @@
<member name="collapsed" type="bool" setter="set_collapsed" getter="is_collapsed">
</member>
<member name="dragger_visibility" type="int" setter="set_dragger_visibility" getter="get_dragger_visibility" enum="SplitContainer.DraggerVisibility">
+ Determines whether the dragger is visible.
</member>
<member name="split_offset" type="int" setter="set_split_offset" getter="get_split_offset">
</member>
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index d8d432e30..83fb76f28 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -490,6 +490,15 @@
Returns the string's amount of characters.
</description>
</method>
+ <method name="lstrip">
+ <return type="String">
+ </return>
+ <argument index="0" name="chars" type="String">
+ </argument>
+ <description>
+ Returns a copy of the string with characters removed from the left.
+ </description>
+ </method>
<method name="match">
<return type="bool">
</return>
@@ -634,6 +643,15 @@
Returns the right side of the string from a given position.
</description>
</method>
+ <method name="rstrip">
+ <return type="String">
+ </return>
+ <argument index="0" name="chars" type="String">
+ </argument>
+ <description>
+ Returns a copy of the string with characters removed from the right.
+ </description>
+ </method>
<method name="sha256_buffer">
<return type="PoolByteArray">
</return>
@@ -745,6 +763,24 @@
Converts the String (which is an array of characters) to [PoolByteArray] (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().
</description>
</method>
+ <method name="trim_prefix">
+ <return type="String">
+ </return>
+ <argument index="0" name="prefix" type="String">
+ </argument>
+ <description>
+ Removes a given string from the start if it starts with it or leaves the string unchanged.
+ </description>
+ </method>
+ <method name="trim_suffix">
+ <return type="String">
+ </return>
+ <argument index="0" name="suffix" type="String">
+ </argument>
+ <description>
+ Removes a given string from the end if it ends with it or leaves the string unchanged.
+ </description>
+ </method>
<method name="xml_escape">
<return type="String">
</return>
diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml
index d8644c541..7d78d7133 100644
--- a/doc/classes/SurfaceTool.xml
+++ b/doc/classes/SurfaceTool.xml
@@ -196,8 +196,11 @@
<method name="generate_normals">
<return type="void">
</return>
+ <argument index="0" name="flip" type="bool" default="false">
+ </argument>
<description>
Generates normals from Vertices so you do not have to do it manually.
+ Setting "flip" [code]true[/code] inverts resulting normals.
</description>
</method>
<method name="generate_tangents">
diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml
index 330487adf..11e94abc0 100644
--- a/doc/classes/TabContainer.xml
+++ b/doc/classes/TabContainer.xml
@@ -78,6 +78,12 @@
Returns the title of the tab at index [code]tab_idx[/code]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title].
</description>
</method>
+ <method name="get_tabs_rearrange_group" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="set_popup">
<return type="void">
</return>
@@ -120,11 +126,21 @@
Sets a title for the tab at index [code]tab_idx[/code]. Tab titles default to the name of the indexed child node, but this can be overridden with [method set_tab_title].
</description>
</method>
+ <method name="set_tabs_rearrange_group">
+ <return type="void">
+ </return>
+ <argument index="0" name="group_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="current_tab" type="int" setter="set_current_tab" getter="get_current_tab">
The current tab index. When set, this index's [Control] node's [code]visible[/code] property is set to [code]true[/code] and all others are set to [code]false[/code].
</member>
+ <member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled">
+ </member>
<member name="tab_align" type="int" setter="set_tab_align" getter="get_tab_align" enum="TabContainer.TabAlign">
The alignment of all tabs in the tab container. See the [code]ALIGN_*[/code] constants for details.
</member>
diff --git a/doc/classes/Tabs.xml b/doc/classes/Tabs.xml
index 615bc83c2..0d5f1f0ba 100644
--- a/doc/classes/Tabs.xml
+++ b/doc/classes/Tabs.xml
@@ -80,6 +80,12 @@
<description>
</description>
</method>
+ <method name="get_tabs_rearrange_group" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="move_tab">
<return type="void">
</return>
@@ -129,10 +135,20 @@
<description>
</description>
</method>
+ <method name="set_tabs_rearrange_group">
+ <return type="void">
+ </return>
+ <argument index="0" name="group_id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="current_tab" type="int" setter="set_current_tab" getter="get_current_tab">
</member>
+ <member name="drag_to_rearrange_enabled" type="bool" setter="set_drag_to_rearrange_enabled" getter="get_drag_to_rearrange_enabled">
+ </member>
<member name="scrolling_enabled" type="bool" setter="set_scrolling_enabled" getter="get_scrolling_enabled">
</member>
<member name="tab_align" type="int" setter="set_tab_align" getter="get_tab_align" enum="Tabs.TabAlign">
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 8a71cb205..76d94fbfb 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -130,6 +130,14 @@
<description>
</description>
</method>
+ <method name="get_keyword_color" qualifiers="const">
+ <return type="Color">
+ </return>
+ <argument index="0" name="keyword" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_line" qualifiers="const">
<return type="String">
</return>
@@ -193,6 +201,14 @@
<description>
</description>
</method>
+ <method name="has_keyword_color" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="keyword" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="insert_text_at_cursor">
<return type="void">
</return>
diff --git a/doc/classes/TextureProgress.xml b/doc/classes/TextureProgress.xml
index 18bd886b0..2ae4ef2cf 100644
--- a/doc/classes/TextureProgress.xml
+++ b/doc/classes/TextureProgress.xml
@@ -51,6 +51,12 @@
<member name="texture_under" type="Texture" setter="set_under_texture" getter="get_under_texture">
[Texture] that draws under the progress bar. The bar's background.
</member>
+ <member name="tint_over" type="Color" setter="set_tint_over" getter="get_tint_over">
+ </member>
+ <member name="tint_progress" type="Color" setter="set_tint_progress" getter="get_tint_progress">
+ </member>
+ <member name="tint_under" type="Color" setter="set_tint_under" getter="get_tint_under">
+ </member>
</members>
<constants>
<constant name="FILL_LEFT_TO_RIGHT" value="0" enum="FillMode">
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index 6ee948999..ec67370c7 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -178,6 +178,7 @@
<argument index="1" name="value" type="bool">
</argument>
<description>
+ Set any collision layer to be [code]true[/code] or [code]false[/code].
</description>
</method>
<method name="set_collision_mask_bit">
@@ -303,6 +304,7 @@
Tile origin at its center.
</constant>
<constant name="TILE_ORIGIN_BOTTOM_LEFT" value="2" enum="TileOrigin">
+ Tile origin at its bottom-left corner.
</constant>
</constants>
</class>
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index 10978970e..4e218f559 100644
--- a/doc/classes/TileSet.xml
+++ b/doc/classes/TileSet.xml
@@ -254,6 +254,14 @@
Return the texture offset of the tile.
</description>
</method>
+ <method name="tile_get_tile_mode" qualifiers="const">
+ <return type="int" enum="TileSet.TileMode">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="tile_set_light_occluder">
<return type="void">
</return>
@@ -410,6 +418,16 @@
Set the texture offset of the tile.
</description>
</method>
+ <method name="tile_set_tile_mode">
+ <return type="void">
+ </return>
+ <argument index="0" name="id" type="int">
+ </argument>
+ <argument index="1" name="tilemode" type="int" enum="TileSet.TileMode">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<constants>
<constant name="BITMASK_2X2" value="0" enum="BitmaskMode">
@@ -432,5 +450,11 @@
</constant>
<constant name="BIND_BOTTOMRIGHT" value="256" enum="AutotileBindings">
</constant>
+ <constant name="SINGLE_TILE" value="0" enum="TileMode">
+ </constant>
+ <constant name="AUTO_TILE" value="1" enum="TileMode">
+ </constant>
+ <constant name="ANIMATED_TILE" value="2" enum="TileMode">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml
index aba1b7818..c51a52d91 100644
--- a/doc/classes/Timer.xml
+++ b/doc/classes/Timer.xml
@@ -21,8 +21,10 @@
<method name="start">
<return type="void">
</return>
+ <argument index="0" name="time_sec" type="float" default="-1">
+ </argument>
<description>
- Starts the timer. This also resets the remaining time to [code]wait_time[/code].
+ Starts the timer. Sets [code]wait_time[/code] to [code]time_sec[/code] if [code]time_sec[/code] > 0. This also resets the remaining time to [code]wait_time[/code].
Note: this method will not resume a paused timer. See [method set_paused].
</description>
</method>
diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml
index df5c2ab3a..4567f1681 100644
--- a/doc/classes/Transform.xml
+++ b/doc/classes/Transform.xml
@@ -8,6 +8,7 @@
</description>
<tutorials>
http://docs.godotengine.org/en/3.0/tutorials/math/index.html
+ http://docs.godotengine.org/en/latest/tutorials/3d/using_transforms.html
</tutorials>
<demos>
</demos>
@@ -98,7 +99,9 @@
<argument index="1" name="up" type="Vector3">
</argument>
<description>
- Rotate the transform around the up vector to face the target.
+ Returns a copy of the transform rotated such that its -Z axis points towards the [code]target[/code] position.
+ The transform will first be rotated around the given [code]up[/code] vector, and then fully aligned to the target by a further rotation around an axis perpendicular to both the [code]target[/code] and [code]up[/code] vectors.
+ Operations take place in global space.
</description>
</method>
<method name="orthonormalized">
diff --git a/doc/classes/Tween.xml b/doc/classes/Tween.xml
index 54e445d3e..95405cc4f 100644
--- a/doc/classes/Tween.xml
+++ b/doc/classes/Tween.xml
@@ -1,18 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="Tween" inherits="Node" category="Core" version="3.1">
<brief_description>
- Node useful for animations with unknown start and end points.
+ Smoothly animates a node's properties over time.
</brief_description>
<description>
- Node useful for animations with unknown start and end points, procedural animations, making one node follow another, and other simple behavior.
- Because it is easy to get it wrong, here is a quick usage example:
+ Tweens are useful for animations requiring a numerical property to be interpolated over a range of values. The name *tween* comes from *in-betweening*, an animation technique where you specify *keyframes* and the computer interpolates the frames that appear between them.
+ Here is a brief usage example that causes a 2D node to move smoothly between two positions:
[codeblock]
var tween = get_node("Tween")
- tween.interpolate_property(get_node("Node2D_to_move"), "transform/origin", Vector2(0,0), Vector2(100,100), 1, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
+ tween.interpolate_property($Node2D, "position",
+ Vector2(0, 0), Vector2(100, 100), 1,
+ Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
tween.start()
[/codeblock]
- Some of the methods of this class require a property name. You can get the property name by hovering over the property in the inspector of the editor.
- Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an TRANS_* constant, and refers to the way the timing of the animation is handled (you might want to see [code]http://easings.net/[/code] for some examples). The second accepts an EASE_* constant, and controls the where [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different TRANS_* constants with EASE_IN_OUT, and use the one that looks best.
+ Many methods require a property name, such as "position" above. You can find the correct property name by hovering over the property in the Inspector.
+ Many of the methods accept [code]trans_type[/code] and [code]ease_type[/code]. The first accepts an [enum TransitionType] constant, and refers to the way the timing of the animation is handled (see [code]http://easings.net/[/code] for some examples). The second accepts an [enum EaseType] constant, and controls the where [code]trans_type[/code] is applied to the interpolation (in the beginning, the end, or both). If you don't know which transition and easing to pick, you can try different [enum TransitionType] constants with [enum EASE_IN_OUT], and use the one that looks best.
</description>
<tutorials>
</tutorials>
@@ -41,8 +43,8 @@
<argument index="8" name="delay" type="float" default="0">
</argument>
<description>
- Follow [code]method[/code] of [code]object[/code] and apply the returned value on [code]target_method[/code] of [code]target[/code], beginning from [code]initial_val[/code] for [code]duration[/code] seconds, [code]delay[/code] later. Methods are animated by calling them with consequitive values.
- [code]trans_type[/code] accepts TRANS_* constants, and is the way the animation is interpolated, while [code]ease_type[/code] accepts EASE_* constants, and controls the place of the interpolation (the beginning, the end, or both). You can read more about them in the class description.
+ Follows [code]method[/code] of [code]object[/code] and applies the returned value on [code]target_method[/code] of [code]target[/code], beginning from [code]initial_val[/code] for [code]duration[/code] seconds, [code]delay[/code] later. Methods are called with consecutive values.
+ Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information
</description>
</method>
<method name="follow_property">
@@ -67,15 +69,15 @@
<argument index="8" name="delay" type="float" default="0">
</argument>
<description>
- Follow [code]property[/code] of [code]object[/code] and apply it on [code]target_property[/code] of [code]target[/code], beginning from [code]initial_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. Note that [code]target:target_property[/code] would equal [code]object:property[/code] at the end of the tween.
- [code]trans_type[/code] accepts TRANS_* constants, and is the way the animation is interpolated, while [code]ease_type[/code] accepts EASE_* constants, and controls the place of the interpolation (the beginning, the end, or both). You can read more about them in the class description.
+ Follows [code]property[/code] of [code]object[/code] and applies it on [code]target_property[/code] of [code]target[/code], beginning from [code]initial_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later.
+ Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information
</description>
</method>
<method name="get_runtime" qualifiers="const">
<return type="float">
</return>
<description>
- Returns the time needed for all tweens to end in seconds, measured from the start. Thus, if you have two tweens, one ending 10 seconds after the start and the other - 20 seconds, it would return 20 seconds, as by that time all tweens would have finished.
+ Returns the total time needed for all tweens to end. If you have two tweens, one lasting 10 seconds and the other 20 seconds, it would return 20 seconds, as by that time all tweens would have finished.
</description>
</method>
<method name="interpolate_callback">
@@ -98,7 +100,7 @@
<argument index="7" name="arg5" type="Variant" default="null">
</argument>
<description>
- Call [code]callback[/code] of [code]object[/code] after [code]duration[/code]. [code]arg1[/code]-[code]arg5[/code] are arguments to be passed to the callback.
+ Calls [code]callback[/code] of [code]object[/code] after [code]duration[/code]. [code]arg1[/code]-[code]arg5[/code] are arguments to be passed to the callback.
</description>
</method>
<method name="interpolate_deferred_callback">
@@ -121,7 +123,7 @@
<argument index="7" name="arg5" type="Variant" default="null">
</argument>
<description>
- Call [code]callback[/code] of [code]object[/code] after [code]duration[/code] on the main thread (similar to [method Object.call_deferred]). [code]arg1[/code]-[code]arg5[/code] are arguments to be passed to the callback.
+ Calls [code]callback[/code] of [code]object[/code] after [code]duration[/code] on the main thread (similar to [method Object.call_deferred]). [code]arg1[/code]-[code]arg5[/code] are arguments to be passed to the callback.
</description>
</method>
<method name="interpolate_method">
@@ -144,8 +146,8 @@
<argument index="7" name="delay" type="float" default="0">
</argument>
<description>
- Animate [code]method[/code] of [code]object[/code] from [code]initial_val[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. Methods are animated by calling them with consecutive values.
- [code]trans_type[/code] accepts TRANS_* constants, and is the way the animation is interpolated, while [code]ease_type[/code] accepts EASE_* constants, and controls the place of the interpolation (the beginning, the end, or both). You can read more about them in the class description.
+ Animates [code]method[/code] of [code]object[/code] from [code]initial_val[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. Methods are called with consecutive values.
+ Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information
</description>
</method>
<method name="interpolate_property">
@@ -168,15 +170,15 @@
<argument index="7" name="delay" type="float" default="0">
</argument>
<description>
- Animate [code]property[/code] of [code]object[/code] from [code]initial_val[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later.
- [code]trans_type[/code] accepts TRANS_* constants, and is the way the animation is interpolated, while [code]ease_type[/code] accepts EASE_* constants, and controls the place of the interpolation (the beginning, the end, or both). You can read more about them in the class description.
+ Animates [code]property[/code] of [code]object[/code] from [code]initial_val[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later.
+ Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information
</description>
</method>
<method name="is_active" qualifiers="const">
<return type="bool">
</return>
<description>
- Returns true if any tweens are currently running, and false otherwise. Note that this method doesn't consider tweens that have ended.
+ Returns [code]true[/code] if any tweens are currently running. Note that this method doesn't consider tweens that have ended.
</description>
</method>
<method name="remove">
@@ -187,14 +189,14 @@
<argument index="1" name="key" type="String" default="&quot;&quot;">
</argument>
<description>
- Stop animating and completely remove a tween, given its object and property/method pair. Passing empty String as key will remove all tweens for given object.
+ Stops animation and removes a tween, given its object and property/method pair. By default, all tweens are removed, unless [code]key[/code] is specified.
</description>
</method>
<method name="remove_all">
<return type="bool">
</return>
<description>
- Stop animating and completely remove all tweens.
+ Stops animation and removes all tweens.
</description>
</method>
<method name="reset">
@@ -205,7 +207,7 @@
<argument index="1" name="key" type="String" default="&quot;&quot;">
</argument>
<description>
- Resets a tween to the initial value (the one given, not the one before the tween), given its object and property/method pair. Passing empty String as key will reset all tweens for given object.
+ Resets a tween to its initial value (the one given, not the one before the tween), given its object and property/method pair. By default, all tweens are removed, unless [code]key[/code] is specified.
</description>
</method>
<method name="reset_all">
@@ -223,14 +225,14 @@
<argument index="1" name="key" type="String" default="&quot;&quot;">
</argument>
<description>
- Continue animating a stopped tween, given its object and property/method pair. Passing empty String as key will resume all tweens for given object.
+ Continues animating a stopped tween, given its object and property/method pair. By default, all tweens are resumed, unless [code]key[/code] is specified.
</description>
</method>
<method name="resume_all">
<return type="bool">
</return>
<description>
- Continue animating all stopped tweens.
+ Continues animating all stopped tweens.
</description>
</method>
<method name="seek">
@@ -239,7 +241,7 @@
<argument index="0" name="time" type="float">
</argument>
<description>
- Seek the animation to the given [code]time[/code] in seconds.
+ Sets the interpolation to the given [code]time[/code] in seconds.
</description>
</method>
<method name="set_active">
@@ -248,14 +250,14 @@
<argument index="0" name="active" type="bool">
</argument>
<description>
- Activate/deactivate the tween. You can use this for pausing animations, though [method stop_all] and [method resume_all] might be more fit for this.
+ Activates/deactivates the tween. See also [method stop_all] and [method resume_all].
</description>
</method>
<method name="start">
<return type="bool">
</return>
<description>
- Start the tween node. You can define tweens both before and after this.
+ Starts the tween. You can define animations both before and after this.
</description>
</method>
<method name="stop">
@@ -266,14 +268,14 @@
<argument index="1" name="key" type="String" default="&quot;&quot;">
</argument>
<description>
- Stop animating a tween, given its object and property/method pair. Passing empty String as key will stop all tweens for given object.
+ Stops a tween, given its object and property/method pair. By default, all tweens are stopped, unless [code]key[/code] is specified.
</description>
</method>
<method name="stop_all">
<return type="bool">
</return>
<description>
- Stop animating all tweens.
+ Stops animating all tweens.
</description>
</method>
<method name="targeting_method">
@@ -298,8 +300,8 @@
<argument index="8" name="delay" type="float" default="0">
</argument>
<description>
- Animate [code]method[/code] of [code]object[/code] from the value returned by [code]initial.initial_method[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. Methods are animated by calling them with consecutive values.
- [code]trans_type[/code] accepts TRANS_* constants, and is the way the animation is interpolated, while [code]ease_type[/code] accepts EASE_* constants, and controls the place of the interpolation (the beginning, the end, or both). You can read more about them in the class description.
+ Animates [code]method[/code] of [code]object[/code] from the value returned by [code]initial_method[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later. Methods are animated by calling them with consecutive values.
+ Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information
</description>
</method>
<method name="targeting_property">
@@ -324,8 +326,8 @@
<argument index="8" name="delay" type="float" default="0">
</argument>
<description>
- Animate [code]property[/code] of [code]object[/code] from the current value of the [code]initial_val[/code] property of [code]initial[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later.
- [code]trans_type[/code] accepts TRANS_* constants, and is the way the animation is interpolated, while [code]ease_type[/code] accepts EASE_* constants, and controls the place of the interpolation (the beginning, the end, or both). You can read more about them in the class description.
+ Animates [code]property[/code] of [code]object[/code] from the current value of the [code]initial_val[/code] property of [code]initial[/code] to [code]final_val[/code] for [code]duration[/code] seconds, [code]delay[/code] seconds later.
+ Use [enum TransitionType] for [code]trans_type[/code] and [enum EaseType] for [code]ease_type[/code] parameters. These values control the timing and direction of the interpolation. See the class description for more information
</description>
</method>
<method name="tell" qualifiers="const">
@@ -338,12 +340,13 @@
</methods>
<members>
<member name="playback_process_mode" type="int" setter="set_tween_process_mode" getter="get_tween_process_mode" enum="Tween.TweenProcessMode">
+ The tween's animation process thread. See [enum TweenProcessMode]. Default value: [enum TWEEN_PROCESS_IDLE].
</member>
<member name="playback_speed" type="float" setter="set_speed_scale" getter="get_speed_scale">
- The speed multiplier of the tween. Set it to 1 for normal speed, 2 for two times nromal speed, and 0.5 for half of the normal speed. Setting it to 0 would pause the animation, but you might consider using [method set_active] or [method stop_all] and [method resume_all] for this.
+ The tween's speed multiplier. For example, set it to [code]1.0[/code] for normal speed, [code]2.0[/code] for two times normal speed, or [code]0.5[/code] for half of the normal speed. A value of [code]0[/code] pauses the animation, but see also [method set_active] or [method stop_all] for this.
</member>
<member name="repeat" type="bool" setter="set_repeat" getter="is_repeat">
- If [code]true[/code], the tween will repeat.
+ If [code]true[/code] the tween loops.
</member>
</members>
<signals>
@@ -353,7 +356,7 @@
<argument index="1" name="key" type="NodePath">
</argument>
<description>
- This signal is emitted when a tween ends.
+ Emitted when a tween ends.
</description>
</signal>
<signal name="tween_started">
@@ -362,7 +365,7 @@
<argument index="1" name="key" type="NodePath">
</argument>
<description>
- This signal is emitted when a tween starts.
+ Emitted when a tween starts.
</description>
</signal>
<signal name="tween_step">
@@ -375,61 +378,61 @@
<argument index="3" name="value" type="Object">
</argument>
<description>
- This signal is emitted each step of the tweening.
+ Emitted at each step of the animation.
</description>
</signal>
</signals>
<constants>
<constant name="TWEEN_PROCESS_PHYSICS" value="0" enum="TweenProcessMode">
- The [code]Tween[/code] should use [code]_physics_process[/code] for timekeeping when this is enabled.
+ The tween updates with the [code]_physics_process[/code] callback.
</constant>
<constant name="TWEEN_PROCESS_IDLE" value="1" enum="TweenProcessMode">
- The [code]Tween[/code] should use [code]_process[/code] for timekeeping when this is enabled (default).
+ The tween updates with the [code]_process[/code] callback.
</constant>
<constant name="TRANS_LINEAR" value="0" enum="TransitionType">
- Means that the animation is interpolated linearly.
+ The animation is interpolated linearly.
</constant>
<constant name="TRANS_SINE" value="1" enum="TransitionType">
- Means that the animation is interpolated using a sine wave.
+ The animation is interpolated using a sine function.
</constant>
<constant name="TRANS_QUINT" value="2" enum="TransitionType">
- Means that the animation is interpolated with a quinary (to the power of 5) function.
+ The animation is interpolated with a quintic (to the power of 5) function.
</constant>
<constant name="TRANS_QUART" value="3" enum="TransitionType">
- Means that the animation is interpolated with a quartic (to the power of 4) function.
+ The animation is interpolated with a quartic (to the power of 4) function.
</constant>
<constant name="TRANS_QUAD" value="4" enum="TransitionType">
- Means that the animation is interpolated with a quadratic (to the power of 2) function.
+ The animation is interpolated with a quadratic (to the power of 2) function.
</constant>
<constant name="TRANS_EXPO" value="5" enum="TransitionType">
- Means that the animation is interpolated with an exponential (some number to the power of x) function.
+ The animation is interpolated with an exponential (to the power of x) function.
</constant>
<constant name="TRANS_ELASTIC" value="6" enum="TransitionType">
- Means that the animation is interpolated with elasticity, wiggling around the edges.
+ The animation is interpolated with elasticity, wiggling around the edges.
</constant>
<constant name="TRANS_CUBIC" value="7" enum="TransitionType">
- Means that the animation is interpolated with a cubic (to the power of 3) function.
+ The animation is interpolated with a cubic (to the power of 3) function.
</constant>
<constant name="TRANS_CIRC" value="8" enum="TransitionType">
- Means that the animation is interpolated with a function using square roots.
+ The animation is interpolated with a function using square roots.
</constant>
<constant name="TRANS_BOUNCE" value="9" enum="TransitionType">
- Means that the animation is interpolated by bouncing at, but never surpassing, the end.
+ The animation is interpolated by bouncing at the end.
</constant>
<constant name="TRANS_BACK" value="10" enum="TransitionType">
- Means that the animation is interpolated backing out at edges.
+ The animation is interpolated backing out at ends.
</constant>
<constant name="EASE_IN" value="0" enum="EaseType">
- Signifies that the interpolation should be focused in the beginning.
+ The interpolation starts slowly and speeds up towards the end.
</constant>
<constant name="EASE_OUT" value="1" enum="EaseType">
- Signifies that the interpolation should be focused in the end.
+ The interpolation starts quickly and slows down towards the end.
</constant>
<constant name="EASE_IN_OUT" value="2" enum="EaseType">
- Signifies that the interpolation should be focused in both ends.
+ A combination of EASE_IN and EASE_OUT. The interpolation is slowest at both ends.
</constant>
<constant name="EASE_OUT_IN" value="3" enum="EaseType">
- Signifies that the interpolation should be focused in both ends, but they should be switched (a bit hard to explain, try it for yourself to be sure).
+ A combination of EASE_IN and EASE_OUT. The interpolation is fastest at both ends.
</constant>
</constants>
</class>
diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml
index b293ec336..5ffe80760 100644
--- a/doc/classes/Vector2.xml
+++ b/doc/classes/Vector2.xml
@@ -84,10 +84,10 @@
<method name="cross">
<return type="float">
</return>
- <argument index="0" name="b" type="Vector2">
+ <argument index="0" name="with" type="Vector2">
</argument>
<description>
- Returns the 2-dimensional analog of the cross product with [code]b[/code].
+ Returns the 2-dimensional analog of the cross product with the given Vector2.
</description>
</method>
<method name="cubic_interpolate">
diff --git a/doc/classes/VehicleWheel.xml b/doc/classes/VehicleWheel.xml
index 6f9f5ea2c..f2c6f5f28 100644
--- a/doc/classes/VehicleWheel.xml
+++ b/doc/classes/VehicleWheel.xml
@@ -31,16 +31,16 @@
The damping applied to the spring when the spring is being compressed. This value should be between 0.0 (no damping) and 1.0. A value of 0.0 means the car will keep bouncing as the spring keeps its energy. A good value for this is around 0.3 for a normal car, 0.5 for a race car.
</member>
<member name="damping_relaxation" type="float" setter="set_damping_relaxation" getter="get_damping_relaxation">
- The damping applied to the spring when relaxing. This value should be between 0.0 (no damping) and 1.0. This value should always be slighly higher then the [member damping_compression] property. For a [member damping_compression] value of 0.3, try a relaxation value of 0.5
+ The damping applied to the spring when relaxing. This value should be between 0.0 (no damping) and 1.0. This value should always be slightly higher than the [member damping_compression] property. For a [member damping_compression] value of 0.3, try a relaxation value of 0.5
</member>
<member name="suspension_max_force" type="float" setter="set_suspension_max_force" getter="get_suspension_max_force">
- The maximum force the spring can resist. This value should be higher then a quarter of the [member RigidBody.mass] of the [VehicleBody] or the spring will not carry the weight of the vehicle. Good results are often obtained by a value that is about 3x to 4x this number.
+ The maximum force the spring can resist. This value should be higher than a quarter of the [member RigidBody.mass] of the [VehicleBody] or the spring will not carry the weight of the vehicle. Good results are often obtained by a value that is about 3x to 4x this number.
</member>
<member name="suspension_stiffness" type="float" setter="set_suspension_stiffness" getter="get_suspension_stiffness">
- This value defines the stiffness of the suspension. Use a value lower then 50 for an offroad car, a value between 50 and 100 for a race car and try something around 200 for something like a Formula 1 car.
+ This value defines the stiffness of the suspension. Use a value lower than 50 for an off-road car, a value between 50 and 100 for a race car and try something around 200 for something like a Formula 1 car.
</member>
<member name="suspension_travel" type="float" setter="set_suspension_travel" getter="get_suspension_travel">
- This is the distance the suspension can travel. As Godots measures are in meters keep this setting relatively low. Try a value between 0.1 and 0.3 depending on the type of car .
+ This is the distance the suspension can travel. As Godot measures are in meters keep this setting relatively low. Try a value between 0.1 and 0.3 depending on the type of car .
</member>
<member name="use_as_steering" type="bool" setter="set_use_as_steering" getter="is_used_as_steering">
If true this wheel will be turned when the car steers.
@@ -49,14 +49,14 @@
If true this wheel transfers engine force to the ground to propel the vehicle forward.
</member>
<member name="wheel_friction_slip" type="float" setter="set_friction_slip" getter="get_friction_slip">
- This determines how much grip this wheel has. It is combined with the friction setting of the surface the wheel is in contact with. 0.0 means no grip, 1.0 is normal grip. For a drift car setup, try setting the grip of the rear wheels slightly lower then the front wheels, or use a lower value to simulate tire wear.
+ This determines how much grip this wheel has. It is combined with the friction setting of the surface the wheel is in contact with. 0.0 means no grip, 1.0 is normal grip. For a drift car setup, try setting the grip of the rear wheels slightly lower than the front wheels, or use a lower value to simulate tire wear.
It's best to set this to 1.0 when starting out.
</member>
<member name="wheel_radius" type="float" setter="set_radius" getter="get_radius">
The radius of the wheel in meters.
</member>
<member name="wheel_rest_length" type="float" setter="set_suspension_rest_length" getter="get_suspension_rest_length">
- This is the distance in meters the wheel is lowered from its origin point. Don't set this to 0.0 and move the wheel into position, instead move the origin point of your wheel (the gizmo in Godot) to the position the wheel will take when bottoming out, then use the rest lenght to move the wheel down to the position it should be in when the car is in rest.
+ This is the distance in meters the wheel is lowered from its origin point. Don't set this to 0.0 and move the wheel into position, instead move the origin point of your wheel (the gizmo in Godot) to the position the wheel will take when bottoming out, then use the rest length to move the wheel down to the position it should be in when the car is in rest.
</member>
<member name="wheel_roll_influence" type="float" setter="set_roll_influence" getter="get_roll_influence">
This value effects the roll of your vehicle. If set to 0.0 for all wheels your vehicle will be prone to rolling over while a value of 1.0 will resist body roll.
diff --git a/doc/classes/VideoPlayer.xml b/doc/classes/VideoPlayer.xml
index 48d09ccd9..d2639590a 100644
--- a/doc/classes/VideoPlayer.xml
+++ b/doc/classes/VideoPlayer.xml
@@ -72,6 +72,12 @@
<member name="volume_db" type="float" setter="set_volume_db" getter="get_volume_db">
</member>
</members>
+ <signals>
+ <signal name="finished">
+ <description>
+ </description>
+ </signal>
+ </signals>
<constants>
</constants>
</class>
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index a48520573..4878f7d93 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -212,6 +212,9 @@
<member name="hdr" type="bool" setter="set_hdr" getter="get_hdr">
If [code]true[/code] the viewport rendering will receive benefits from High Dynamic Range algorithm. Default value: [code]true[/code].
</member>
+ <member name="keep_3d_linear" type="bool" setter="set_keep_3d_linear" getter="get_keep_3d_linear">
+ If [code]true[/code] the result after 3D rendering will not have a linear to sRGB color conversion applied. This is important when the viewport is used as a render target where the result is used as a texture on a 3D object rendered in another viewport. It is also important if the viewport is used to create data that is not color based (noise, heightmaps, pickmaps, etc.). Do not enable this when the viewport is used as a texture on a 2D object or if the viewport is your final output.
+ </member>
<member name="msaa" type="int" setter="set_msaa" getter="get_msaa" enum="Viewport.MSAA">
The multisample anti-aliasing mode. Default value: [code]MSAA_DISABLED[/code].
</member>
diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml
index bd1a9fb3e..baebddf3e 100644
--- a/doc/classes/VisualServer.xml
+++ b/doc/classes/VisualServer.xml
@@ -328,7 +328,7 @@
</argument>
<description>
Adds a [Transform2D] command to the [CanvasItem]'s draw commands.
- This sets the extra_matrix uniform when executed. This affects the later command's of the canvas item.
+ This sets the extra_matrix uniform when executed. This affects the later commands of the canvas item.
</description>
</method>
<method name="canvas_item_add_texture_rect">
diff --git a/doc/classes/WebSocketClient.xml b/doc/classes/WebSocketClient.xml
index 79313c90c..496baa578 100644
--- a/doc/classes/WebSocketClient.xml
+++ b/doc/classes/WebSocketClient.xml
@@ -28,6 +28,10 @@
</description>
</method>
</methods>
+ <members>
+ <member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled">
+ </member>
+ </members>
<signals>
<signal name="connection_closed">
<description>
diff --git a/drivers/alsa/audio_driver_alsa.cpp b/drivers/alsa/audio_driver_alsa.cpp
index 0bb03d23e..1e17e7253 100644
--- a/drivers/alsa/audio_driver_alsa.cpp
+++ b/drivers/alsa/audio_driver_alsa.cpp
@@ -37,19 +37,20 @@
#include <errno.h>
-Error AudioDriverALSA::init() {
-
- active = false;
- thread_exited = false;
- exit_thread = false;
- pcm_open = false;
- samples_in = NULL;
- samples_out = NULL;
-
+Error AudioDriverALSA::init_device() {
mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE);
speaker_mode = SPEAKER_MODE_STEREO;
channels = 2;
+ // If there is a specified device check that it is really present
+ if (device_name != "Default") {
+ Array list = get_device_list();
+ if (list.find(device_name) == -1) {
+ device_name = "Default";
+ new_device = "Default";
+ }
+ }
+
int status;
snd_pcm_hw_params_t *hwparams;
snd_pcm_sw_params_t *swparams;
@@ -65,7 +66,16 @@ Error AudioDriverALSA::init() {
//6 chans - "plug:surround51"
//4 chans - "plug:surround40";
- status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ if (device_name == "Default") {
+ status = snd_pcm_open(&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ } else {
+ String device = device_name;
+ int pos = device.find(";");
+ if (pos != -1) {
+ device = device.substr(0, pos);
+ }
+ status = snd_pcm_open(&pcm_handle, device.utf8().get_data(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ }
ERR_FAIL_COND_V(status < 0, ERR_CANT_OPEN);
@@ -129,15 +139,28 @@ Error AudioDriverALSA::init() {
status = snd_pcm_sw_params(pcm_handle, swparams);
CHECK_FAIL(status < 0);
- samples_in = memnew_arr(int32_t, period_size * channels);
- samples_out = memnew_arr(int16_t, period_size * channels);
+ samples_in.resize(period_size * channels);
+ samples_out.resize(period_size * channels);
snd_pcm_nonblock(pcm_handle, 0);
- mutex = Mutex::create();
- thread = Thread::create(AudioDriverALSA::thread_func, this);
-
return OK;
+}
+
+Error AudioDriverALSA::init() {
+
+ active = false;
+ thread_exited = false;
+ exit_thread = false;
+ pcm_open = false;
+
+ Error err = init_device();
+ if (err == OK) {
+ mutex = Mutex::create();
+ thread = Thread::create(AudioDriverALSA::thread_func, this);
+ }
+
+ return err;
};
void AudioDriverALSA::thread_func(void *p_udata) {
@@ -152,7 +175,7 @@ void AudioDriverALSA::thread_func(void *p_udata) {
} else {
ad->lock();
- ad->audio_server_process(ad->period_size, ad->samples_in);
+ ad->audio_server_process(ad->period_size, ad->samples_in.ptrw());
ad->unlock();
@@ -167,7 +190,7 @@ void AudioDriverALSA::thread_func(void *p_udata) {
while (todo) {
if (ad->exit_thread)
break;
- uint8_t *src = (uint8_t *)ad->samples_out;
+ uint8_t *src = (uint8_t *)ad->samples_out.ptr();
int wrote = snd_pcm_writei(ad->pcm_handle, (void *)(src + (total * ad->channels)), todo);
if (wrote < 0) {
@@ -193,6 +216,26 @@ void AudioDriverALSA::thread_func(void *p_udata) {
total += wrote;
todo -= wrote;
};
+
+ // User selected a new device, finish the current one so we'll init the new device
+ if (ad->device_name != ad->new_device) {
+ ad->device_name = ad->new_device;
+ ad->finish_device();
+
+ Error err = ad->init_device();
+ if (err != OK) {
+ ERR_PRINT("ALSA: init_device error");
+ ad->device_name = "Default";
+ ad->new_device = "Default";
+
+ err = ad->init_device();
+ if (err != OK) {
+ ad->active = false;
+ ad->exit_thread = true;
+ break;
+ }
+ }
+ }
};
ad->thread_exited = true;
@@ -213,6 +256,49 @@ AudioDriver::SpeakerMode AudioDriverALSA::get_speaker_mode() const {
return speaker_mode;
};
+Array AudioDriverALSA::get_device_list() {
+
+ Array list;
+
+ list.push_back("Default");
+
+ void **hints;
+
+ if (snd_device_name_hint(-1, "pcm", &hints) < 0)
+ return list;
+
+ for (void **n = hints; *n != NULL; n++) {
+ char *name = snd_device_name_get_hint(*n, "NAME");
+ char *desc = snd_device_name_get_hint(*n, "DESC");
+
+ if (name != NULL && !strncmp(name, "plughw", 6)) {
+ if (desc) {
+ list.push_back(String(name) + ";" + String(desc));
+ } else {
+ list.push_back(String(name));
+ }
+ }
+
+ if (desc != NULL)
+ free(desc);
+ if (name != NULL)
+ free(name);
+ }
+ snd_device_name_free_hint(hints);
+
+ return list;
+}
+
+String AudioDriverALSA::get_device() {
+
+ return device_name;
+}
+
+void AudioDriverALSA::set_device(String device) {
+
+ new_device = device;
+}
+
void AudioDriverALSA::lock() {
if (!thread || !mutex)
@@ -227,6 +313,14 @@ void AudioDriverALSA::unlock() {
mutex->unlock();
};
+void AudioDriverALSA::finish_device() {
+
+ if (pcm_open) {
+ snd_pcm_close(pcm_handle);
+ pcm_open = NULL;
+ }
+}
+
void AudioDriverALSA::finish() {
if (!thread)
@@ -235,17 +329,13 @@ void AudioDriverALSA::finish() {
exit_thread = true;
Thread::wait_to_finish(thread);
- if (pcm_open)
- snd_pcm_close(pcm_handle);
-
- if (samples_in) {
- memdelete_arr(samples_in);
- memdelete_arr(samples_out);
- };
+ finish_device();
memdelete(thread);
- if (mutex)
+ if (mutex) {
memdelete(mutex);
+ mutex = NULL;
+ }
thread = NULL;
};
@@ -254,6 +344,9 @@ AudioDriverALSA::AudioDriverALSA() {
mutex = NULL;
thread = NULL;
pcm_handle = NULL;
+
+ device_name = "Default";
+ new_device = "Default";
};
AudioDriverALSA::~AudioDriverALSA(){
diff --git a/drivers/alsa/audio_driver_alsa.h b/drivers/alsa/audio_driver_alsa.h
index 8ed60dfdc..2878e100a 100644
--- a/drivers/alsa/audio_driver_alsa.h
+++ b/drivers/alsa/audio_driver_alsa.h
@@ -44,8 +44,14 @@ class AudioDriverALSA : public AudioDriver {
snd_pcm_t *pcm_handle;
- int32_t *samples_in;
- int16_t *samples_out;
+ String device_name;
+ String new_device;
+
+ Vector<int32_t> samples_in;
+ Vector<int16_t> samples_out;
+
+ Error init_device();
+ void finish_device();
static void thread_func(void *p_udata);
@@ -71,6 +77,9 @@ public:
virtual void start();
virtual int get_mix_rate() const;
virtual SpeakerMode get_speaker_mode() const;
+ virtual Array get_device_list();
+ virtual String get_device();
+ virtual void set_device(String device);
virtual void lock();
virtual void unlock();
virtual void finish();
diff --git a/drivers/coreaudio/audio_driver_coreaudio.cpp b/drivers/coreaudio/audio_driver_coreaudio.cpp
index 0b39f9ebc..6e451eabc 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.cpp
+++ b/drivers/coreaudio/audio_driver_coreaudio.cpp
@@ -37,16 +37,22 @@
#define kOutputBus 0
#ifdef OSX_ENABLED
-static OSStatus outputDeviceAddressCB(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inClientData) {
+OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID,
+ UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
+ void *inClientData) {
AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData;
- driver->reopen();
+ // If our selected device is the Default call set_device to update the
+ // kAudioOutputUnitProperty_CurrentDevice property
+ if (driver->device_name == "Default") {
+ driver->set_device("Default");
+ }
return noErr;
}
#endif
-Error AudioDriverCoreAudio::initDevice() {
+Error AudioDriverCoreAudio::init_device() {
AudioComponentDescription desc;
zeromem(&desc, sizeof(desc));
desc.componentType = kAudioUnitType_Output;
@@ -129,7 +135,7 @@ Error AudioDriverCoreAudio::initDevice() {
return OK;
}
-Error AudioDriverCoreAudio::finishDevice() {
+Error AudioDriverCoreAudio::finish_device() {
OSStatus result;
if (active) {
@@ -153,49 +159,18 @@ Error AudioDriverCoreAudio::init() {
channels = 2;
#ifdef OSX_ENABLED
- outputDeviceAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
- outputDeviceAddress.mScope = kAudioObjectPropertyScopeGlobal;
- outputDeviceAddress.mElement = kAudioObjectPropertyElementMaster;
+ AudioObjectPropertyAddress prop;
+ prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = kAudioObjectPropertyElementMaster;
- result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this);
+ result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
ERR_FAIL_COND_V(result != noErr, FAILED);
#endif
- return initDevice();
+ return init_device();
};
-Error AudioDriverCoreAudio::reopen() {
- bool restart = false;
-
- lock();
-
- if (active) {
- restart = true;
- }
-
- Error err = finishDevice();
- if (err != OK) {
- ERR_PRINT("finishDevice failed");
- unlock();
- return err;
- }
-
- err = initDevice();
- if (err != OK) {
- ERR_PRINT("initDevice failed");
- unlock();
- return err;
- }
-
- if (restart) {
- start();
- }
-
- unlock();
-
- return OK;
-}
-
OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
@@ -242,13 +217,24 @@ void AudioDriverCoreAudio::start() {
if (!active) {
OSStatus result = AudioOutputUnitStart(audio_unit);
if (result != noErr) {
- ERR_PRINT("AudioOutputUnitStart failed");
+ ERR_PRINT(("AudioOutputUnitStart failed, code: " + itos(result)).utf8().get_data());
} else {
active = true;
}
}
};
+void AudioDriverCoreAudio::stop() {
+ if (active) {
+ OSStatus result = AudioOutputUnitStop(audio_unit);
+ if (result != noErr) {
+ ERR_PRINT(("AudioOutputUnitStop failed, code: " + itos(result)).utf8().get_data());
+ } else {
+ active = false;
+ }
+ }
+}
+
int AudioDriverCoreAudio::get_mix_rate() const {
return mix_rate;
};
@@ -257,6 +243,150 @@ AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const {
return get_speaker_mode_by_total_channels(channels);
};
+#ifdef OSX_ENABLED
+
+Array AudioDriverCoreAudio::get_device_list() {
+
+ Array list;
+
+ list.push_back("Default");
+
+ AudioObjectPropertyAddress prop;
+
+ prop.mSelector = kAudioHardwarePropertyDevices;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = kAudioObjectPropertyElementMaster;
+
+ UInt32 size = 0;
+ AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &size);
+ AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size);
+ AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &size, audioDevices);
+
+ UInt32 deviceCount = size / sizeof(AudioDeviceID);
+ for (UInt32 i = 0; i < deviceCount; i++) {
+ prop.mScope = kAudioDevicePropertyScopeOutput;
+ prop.mSelector = kAudioDevicePropertyStreamConfiguration;
+
+ AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size);
+ AudioBufferList *bufferList = (AudioBufferList *)malloc(size);
+ AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList);
+
+ UInt32 outputChannelCount = 0;
+ for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++)
+ outputChannelCount += bufferList->mBuffers[j].mNumberChannels;
+
+ free(bufferList);
+
+ if (outputChannelCount >= 1) {
+ CFStringRef cfname;
+
+ size = sizeof(CFStringRef);
+ prop.mSelector = kAudioObjectPropertyName;
+
+ AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, &cfname);
+
+ CFIndex length = CFStringGetLength(cfname);
+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
+ char *buffer = (char *)malloc(maxSize);
+ if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
+ // Append the ID to the name in case we have devices with duplicate name
+ list.push_back(String(buffer) + " (" + itos(audioDevices[i]) + ")");
+ }
+
+ free(buffer);
+ }
+ }
+
+ free(audioDevices);
+
+ return list;
+}
+
+String AudioDriverCoreAudio::get_device() {
+
+ return device_name;
+}
+
+void AudioDriverCoreAudio::set_device(String device) {
+
+ device_name = device;
+ if (!active) {
+ return;
+ }
+
+ AudioDeviceID deviceId;
+ bool found = false;
+ if (device_name != "Default") {
+ AudioObjectPropertyAddress prop;
+
+ prop.mSelector = kAudioHardwarePropertyDevices;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = kAudioObjectPropertyElementMaster;
+
+ UInt32 size = 0;
+ AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, NULL, &size);
+ AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size);
+ AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, NULL, &size, audioDevices);
+
+ UInt32 deviceCount = size / sizeof(AudioDeviceID);
+ for (UInt32 i = 0; i < deviceCount && !found; i++) {
+ prop.mScope = kAudioDevicePropertyScopeOutput;
+ prop.mSelector = kAudioDevicePropertyStreamConfiguration;
+
+ AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size);
+ AudioBufferList *bufferList = (AudioBufferList *)malloc(size);
+ AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList);
+
+ UInt32 outputChannelCount = 0;
+ for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++)
+ outputChannelCount += bufferList->mBuffers[j].mNumberChannels;
+
+ free(bufferList);
+
+ if (outputChannelCount >= 1) {
+ CFStringRef cfname;
+
+ size = sizeof(CFStringRef);
+ prop.mSelector = kAudioObjectPropertyName;
+
+ AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, &cfname);
+
+ CFIndex length = CFStringGetLength(cfname);
+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
+ char *buffer = (char *)malloc(maxSize);
+ if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
+ String name = String(buffer) + " (" + itos(audioDevices[i]) + ")";
+ if (name == device_name) {
+ deviceId = audioDevices[i];
+ found = true;
+ }
+ }
+
+ free(buffer);
+ }
+ }
+
+ free(audioDevices);
+ }
+
+ if (!found) {
+ UInt32 size = sizeof(AudioDeviceID);
+ AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+
+ OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &size, &deviceId);
+ ERR_FAIL_COND(result != noErr);
+
+ found = true;
+ }
+
+ if (found) {
+ OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
+ ERR_FAIL_COND(result != noErr);
+ }
+}
+
+#endif
+
void AudioDriverCoreAudio::lock() {
if (mutex)
mutex->lock();
@@ -276,10 +406,15 @@ bool AudioDriverCoreAudio::try_lock() {
void AudioDriverCoreAudio::finish() {
OSStatus result;
- finishDevice();
+ finish_device();
#ifdef OSX_ENABLED
- result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this);
+ AudioObjectPropertyAddress prop;
+ prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
+ prop.mScope = kAudioObjectPropertyScopeGlobal;
+ prop.mElement = kAudioObjectPropertyElementMaster;
+
+ result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
if (result != noErr) {
ERR_PRINT("AudioObjectRemovePropertyListener failed");
}
@@ -309,6 +444,8 @@ AudioDriverCoreAudio::AudioDriverCoreAudio() {
buffer_frames = 0;
samples_in.clear();
+
+ device_name = "Default";
};
AudioDriverCoreAudio::~AudioDriverCoreAudio(){};
diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h
index 51256085d..c44e22552 100644
--- a/drivers/coreaudio/audio_driver_coreaudio.h
+++ b/drivers/coreaudio/audio_driver_coreaudio.h
@@ -43,12 +43,12 @@
class AudioDriverCoreAudio : public AudioDriver {
AudioComponentInstance audio_unit;
-#ifdef OSX_ENABLED
- AudioObjectPropertyAddress outputDeviceAddress;
-#endif
+
bool active;
Mutex *mutex;
+ String device_name;
+
int mix_rate;
unsigned int channels;
unsigned int buffer_frames;
@@ -56,14 +56,20 @@ class AudioDriverCoreAudio : public AudioDriver {
Vector<int32_t> samples_in;
+#ifdef OSX_ENABLED
+ static OSStatus output_device_address_cb(AudioObjectID inObjectID,
+ UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
+ void *inClientData);
+#endif
+
static OSStatus output_callback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList *ioData);
- Error initDevice();
- Error finishDevice();
+ Error init_device();
+ Error finish_device();
public:
const char *get_name() const {
@@ -74,12 +80,17 @@ public:
virtual void start();
virtual int get_mix_rate() const;
virtual SpeakerMode get_speaker_mode() const;
+#ifdef OSX_ENABLED
+ virtual Array get_device_list();
+ virtual String get_device();
+ virtual void set_device(String device);
+#endif
virtual void lock();
virtual void unlock();
virtual void finish();
bool try_lock();
- Error reopen();
+ void stop();
AudioDriverCoreAudio();
~AudioDriverCoreAudio();
diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp
index 5bca3ee54..6e7e1793e 100644
--- a/drivers/gles2/rasterizer_storage_gles2.cpp
+++ b/drivers/gles2/rasterizer_storage_gles2.cpp
@@ -452,6 +452,11 @@ void RasterizerStorageGLES2::texture_set_data(RID p_texture, const Ref<Image> &p
texture->mipmaps = mipmaps;
}
+void RasterizerStorageGLES2::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side) {
+ // TODO
+ ERR_PRINT("Not implemented (ask Karroffel to do it :p)");
+}
+
Ref<Image> RasterizerStorageGLES2::texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side) const {
Texture *texture = texture_owner.getornull(p_texture);
@@ -1297,6 +1302,10 @@ Transform2D RasterizerStorageGLES2::skeleton_bone_get_transform_2d(RID p_skeleto
return Transform2D();
}
+void RasterizerStorageGLES2::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) {
+
+}
+
void RasterizerStorageGLES2::update_dirty_skeletons() {
}
diff --git a/drivers/gles2/rasterizer_storage_gles2.h b/drivers/gles2/rasterizer_storage_gles2.h
index 9f8d8d100..b735f2e14 100644
--- a/drivers/gles2/rasterizer_storage_gles2.h
+++ b/drivers/gles2/rasterizer_storage_gles2.h
@@ -237,6 +237,7 @@ public:
virtual RID texture_create();
virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT);
virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT);
+ virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT);
virtual Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const;
virtual void texture_set_flags(RID p_texture, uint32_t p_flags);
virtual uint32_t texture_get_flags(RID p_texture) const;
@@ -555,6 +556,7 @@ public:
virtual Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const;
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform);
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
+ virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
/* Light API */
diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp
index f41bfe838..ad6c2f850 100644
--- a/drivers/gles2/shader_compiler_gles2.cpp
+++ b/drivers/gles2/shader_compiler_gles2.cpp
@@ -872,9 +872,9 @@ ShaderCompilerGLES2::ShaderCompilerGLES2() {
actions[VS::SHADER_PARTICLES].renames["EMISSION_TRANSFORM"] = "emission_transform";
actions[VS::SHADER_PARTICLES].renames["RANDOM_SEED"] = "random_seed";
- actions[VS::SHADER_SPATIAL].render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
- actions[VS::SHADER_SPATIAL].render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
- actions[VS::SHADER_SPATIAL].render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n";
+ actions[VS::SHADER_PARTICLES].render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n";
+ actions[VS::SHADER_PARTICLES].render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n";
+ actions[VS::SHADER_PARTICLES].render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n";
vertex_name = "vertex";
fragment_name = "fragment";
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index c33fdaa35..ff423bf0d 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -160,6 +160,7 @@ void RasterizerCanvasGLES3::canvas_begin() {
state.canvas_shader.set_conditional(CanvasShaderGLES3::SHADOW_FILTER_PCF13, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_DISTANCE_FIELD, false);
state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_NINEPATCH, false);
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SKELETON, false);
state.canvas_shader.set_custom_shader(0);
state.canvas_shader.bind();
@@ -180,6 +181,7 @@ void RasterizerCanvasGLES3::canvas_begin() {
glBindVertexArray(data.canvas_quad_array);
state.using_texture_rect = true;
state.using_ninepatch = false;
+ state.using_skeleton = false;
}
void RasterizerCanvasGLES3::canvas_end() {
@@ -284,6 +286,10 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepat
state.canvas_shader.set_uniform(CanvasShaderGLES3::FINAL_MODULATE, state.canvas_item_modulate);
state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform);
state.canvas_shader.set_uniform(CanvasShaderGLES3::EXTRA_MATRIX, state.extra_matrix);
+ if (state.using_skeleton) {
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::SKELETON_TRANSFORM, state.skeleton_transform);
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::SKELETON_TRANSFORM_INVERSE, state.skeleton_transform_inverse);
+ }
if (storage->frame.current_rt) {
state.canvas_shader.set_uniform(CanvasShaderGLES3::SCREEN_PIXEL_SIZE, Vector2(1.0 / storage->frame.current_rt->width, 1.0 / storage->frame.current_rt->height));
} else {
@@ -293,7 +299,7 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepat
state.using_ninepatch = p_ninepatch;
}
-void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor) {
+void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const int *p_bones, const float *p_weights) {
glBindVertexArray(data.polygon_buffer_pointer_array);
glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
@@ -301,11 +307,17 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun
uint32_t buffer_ofs = 0;
//vertex
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size);
+#endif
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_vertices);
glEnableVertexAttribArray(VS::ARRAY_VERTEX);
glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, false, sizeof(Vector2), ((uint8_t *)0) + buffer_ofs);
buffer_ofs += sizeof(Vector2) * p_vertex_count;
//color
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size);
+#endif
if (p_singlecolor) {
glDisableVertexAttribArray(VS::ARRAY_COLOR);
@@ -322,6 +334,10 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun
buffer_ofs += sizeof(Color) * p_vertex_count;
}
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size);
+#endif
+
if (p_uvs) {
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs);
@@ -333,6 +349,32 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun
glDisableVertexAttribArray(VS::ARRAY_TEX_UV);
}
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size);
+#endif
+
+ if (p_bones && p_weights) {
+
+ glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(int) * 4 * p_vertex_count, p_bones);
+ glEnableVertexAttribArray(VS::ARRAY_BONES);
+ //glVertexAttribPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, false, sizeof(int) * 4, ((uint8_t *)0) + buffer_ofs);
+ glVertexAttribIPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, sizeof(int) * 4, ((uint8_t *)0) + buffer_ofs);
+ buffer_ofs += sizeof(int) * 4 * p_vertex_count;
+
+ glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(float) * 4 * p_vertex_count, p_weights);
+ glEnableVertexAttribArray(VS::ARRAY_WEIGHTS);
+ glVertexAttribPointer(VS::ARRAY_WEIGHTS, 4, GL_FLOAT, false, sizeof(float) * 4, ((uint8_t *)0) + buffer_ofs);
+ buffer_ofs += sizeof(float) * 4 * p_vertex_count;
+
+ } else if (state.using_skeleton) {
+ glVertexAttribI4ui(VS::ARRAY_BONES, 0, 0, 0, 0);
+ glVertexAttrib4f(VS::ARRAY_WEIGHTS, 0, 0, 0, 0);
+ }
+
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size);
+#endif
+
//bind the indices buffer.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(int) * p_index_count, p_indices);
@@ -342,6 +384,12 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun
storage->frame.canvas_draw_commands++;
+ if (p_bones && p_weights) {
+ //not used so often, so disable when used
+ glDisableVertexAttribArray(VS::ARRAY_BONES);
+ glDisableVertexAttribArray(VS::ARRAY_WEIGHTS);
+ }
+
glBindVertexArray(0);
}
@@ -735,7 +783,8 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
Size2 texpixel_size(1.0 / texture->width, 1.0 / texture->height);
state.canvas_shader.set_uniform(CanvasShaderGLES3::COLOR_TEXPIXEL_SIZE, texpixel_size);
}
- _draw_polygon(polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1);
+
+ _draw_polygon(polygon->indices.ptr(), polygon->count, polygon->points.size(), polygon->points.ptr(), polygon->uvs.ptr(), polygon->colors.ptr(), polygon->colors.size() == 1, polygon->bones.ptr(), polygon->weights.ptr());
#ifdef GLES_OVER_GL
if (polygon->antialiased) {
glEnable(GL_LINE_SMOOTH);
@@ -921,7 +970,7 @@ void RasterizerCanvasGLES3::_canvas_item_render_commands(Item *p_item, Item *cur
}
_bind_canvas_texture(RID(), RID());
- _draw_polygon(indices, numpoints * 3, numpoints + 1, points, NULL, &circle->color, true);
+ _draw_polygon(indices, numpoints * 3, numpoints + 1, points, NULL, &circle->color, true, NULL, NULL);
//_draw_polygon(numpoints*3,indices,points,NULL,&circle->color,RID(),true);
//canvas_draw_circle(circle->indices.size(),circle->indices.ptr(),circle->points.ptr(),circle->uvs.ptr(),circle->colors.ptr(),circle->texture,circle->colors.size()==1);
@@ -1068,6 +1117,7 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
RID canvas_last_material;
bool prev_distance_field = false;
+ bool prev_use_skeleton = false;
while (p_item_list) {
@@ -1106,6 +1156,36 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
}
}
+ RasterizerStorageGLES3::Skeleton *skeleton = NULL;
+
+ {
+ //skeleton handling
+ if (ci->skeleton.is_valid()) {
+ skeleton = storage->skeleton_owner.getornull(ci->skeleton);
+ if (!skeleton->use_2d) {
+ skeleton = NULL;
+ } else {
+ state.skeleton_transform = p_transform * skeleton->base_transform_2d;
+ state.skeleton_transform_inverse = state.skeleton_transform.affine_inverse();
+ }
+ }
+
+ bool use_skeleton = skeleton != NULL;
+ if (prev_use_skeleton != use_skeleton) {
+ rebind_shader = true;
+ state.canvas_shader.set_conditional(CanvasShaderGLES3::USE_SKELETON, use_skeleton);
+ prev_use_skeleton = use_skeleton;
+ }
+
+ if (skeleton) {
+ glActiveTexture(GL_TEXTURE0 + storage->config.max_texture_image_units - 1);
+ glBindTexture(GL_TEXTURE_2D, skeleton->texture);
+ state.using_skeleton = true;
+ } else {
+ state.using_skeleton = false;
+ }
+ }
+
//begin rect
Item *material_owner = ci->material_owner ? ci->material_owner : ci;
@@ -1130,6 +1210,9 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
if (shader_ptr->canvas_item.uses_screen_texture && !state.canvas_texscreen_used) {
//copy if not copied before
_copy_texscreen(Rect2());
+
+ // blend mode will have been enabled so make sure we disable it again later on
+ last_blend_mode = last_blend_mode != RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED ? last_blend_mode : -1;
}
if (shader_ptr != shader_cache) {
@@ -1201,14 +1284,30 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
}
int blend_mode = shader_cache ? shader_cache->canvas_item.blend_mode : RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX;
+ if (blend_mode == RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED && (!storage->frame.current_rt || !storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT])) {
+ blend_mode = RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX;
+ }
bool unshaded = shader_cache && (shader_cache->canvas_item.light_mode == RasterizerStorageGLES3::Shader::CanvasItem::LIGHT_MODE_UNSHADED || blend_mode != RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX);
bool reclip = false;
if (last_blend_mode != blend_mode) {
+ if (last_blend_mode == RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED) {
+ // re-enable it
+ glEnable(GL_BLEND);
+ } else if (blend_mode == RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED) {
+ // disable it
+ glDisable(GL_BLEND);
+ }
switch (blend_mode) {
+ case RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_DISABLED: {
+
+ // nothing to do here
+
+ } break;
case RasterizerStorageGLES3::Shader::CanvasItem::BLEND_MODE_MIX: {
+
glBlendEquation(GL_FUNC_ADD);
if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index 1dc17f98d..bfaf1fdb4 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -84,6 +84,9 @@ public:
Color canvas_item_modulate;
Transform2D extra_matrix;
Transform2D final_transform;
+ bool using_skeleton;
+ Transform2D skeleton_transform;
+ Transform2D skeleton_transform_inverse;
} state;
@@ -123,7 +126,7 @@ public:
_FORCE_INLINE_ RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map);
_FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs);
- _FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
+ _FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const int *p_bones, const float *p_weights);
_FORCE_INLINE_ void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
_FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip);
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index ca39d9f96..0fb69494f 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -111,8 +111,6 @@ static void GLAPIENTRY _gl_debug_print(GLenum source, GLenum type, GLuint id, GL
strcpy(debType, "Portability");
else if (type == _EXT_DEBUG_TYPE_PERFORMANCE_ARB)
strcpy(debType, "Performance");
- else if (type == _EXT_DEBUG_TYPE_OTHER_ARB)
- strcpy(debType, "Other");
if (severity == _EXT_DEBUG_SEVERITY_HIGH_ARB)
strcpy(debSev, "High");
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 02d851288..8da2c2f9c 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -2085,9 +2085,9 @@ void RasterizerSceneGLES3::_render_list(RenderList::Element **p_elements, int p_
case RasterizerStorageGLES3::Shader::Spatial::BLEND_MODE_MUL: {
glBlendEquation(GL_FUNC_ADD);
if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT]) {
- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_DST_ALPHA, GL_ZERO);
} else {
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(GL_DST_COLOR, GL_ZERO, GL_ZERO, GL_ONE);
}
} break;
@@ -2362,7 +2362,11 @@ void RasterizerSceneGLES3::_draw_sky(RasterizerStorageGLES3::Sky *p_sky, const C
ERR_FAIL_COND(!tex);
glActiveTexture(GL_TEXTURE0);
- glBindTexture(tex->target, tex->tex_id);
+
+ if (tex->proxy && tex->proxy->tex_id)
+ glBindTexture(tex->target, tex->proxy->tex_id);
+ else
+ glBindTexture(tex->target, tex->tex_id);
if (storage->config.srgb_decode_supported && tex->srgb && !tex->using_srgb) {
@@ -3906,6 +3910,7 @@ void RasterizerSceneGLES3::_post_process(Environment *env, const CameraMatrix &p
state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_FILMIC_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_FILMIC);
state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_ACES_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_ACES);
state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_REINDHART_TONEMAPPER, env->tone_mapper == VS::ENV_TONE_MAPPER_REINHARDT);
+ state.tonemap_shader.set_conditional(TonemapShaderGLES3::KEEP_3D_LINEAR, storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_KEEP_3D_LINEAR]);
state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_AUTO_EXPOSURE, env->auto_exposure);
state.tonemap_shader.set_conditional(TonemapShaderGLES3::USE_GLOW_FILTER_BICUBIC, env->glow_bicubic_upscale);
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index a287dca1e..945df3545 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "rasterizer_storage_gles3.h"
+#include "engine.h"
#include "project_settings.h"
#include "rasterizer_canvas_gles3.h"
#include "rasterizer_scene_gles3.h"
@@ -827,6 +828,58 @@ void RasterizerStorageGLES3::texture_set_data(RID p_texture, const Ref<Image> &p
//texture_set_flags(p_texture,texture->flags);
}
+// Uploads pixel data to a sub-region of a texture, for the specified mipmap.
+// The texture pixels must have been allocated before, because most features seen in texture_set_data() make no sense in a partial update.
+// TODO If we want this to be usable without pre-filling pixels with a full image, we have to call glTexImage2D() with null data.
+void RasterizerStorageGLES3::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side) {
+
+ Texture *texture = texture_owner.get(p_texture);
+
+ ERR_FAIL_COND(!texture);
+ ERR_FAIL_COND(!texture->active);
+ ERR_FAIL_COND(texture->render_target);
+ ERR_FAIL_COND(texture->format != p_image->get_format());
+ ERR_FAIL_COND(p_image.is_null());
+ ERR_FAIL_COND(src_w <= 0 || src_h <= 0);
+ ERR_FAIL_COND(src_x < 0 || src_y < 0 || src_x + src_w > p_image->get_width() || src_y + src_h > p_image->get_height());
+ ERR_FAIL_COND(dst_x < 0 || dst_y < 0 || dst_x + src_w > texture->alloc_width || dst_y + src_h > texture->alloc_height);
+ ERR_FAIL_COND(p_dst_mip < 0 || p_dst_mip >= texture->mipmaps);
+
+ GLenum type;
+ GLenum format;
+ GLenum internal_format;
+ bool compressed;
+ bool srgb;
+
+ // Because OpenGL wants data as a dense array, we have to extract the sub-image if the source rect isn't the full image
+ Ref<Image> p_sub_img = p_image;
+ if (src_x > 0 || src_y > 0 || src_w != p_image->get_width() || src_h != p_image->get_height()) {
+ p_sub_img = p_image->get_rect(Rect2(src_x, src_y, src_w, src_h));
+ }
+
+ Ref<Image> img = _get_gl_image_and_format(p_sub_img, p_sub_img->get_format(), texture->flags, format, internal_format, type, compressed, srgb);
+
+ GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_cube_side] : GL_TEXTURE_2D;
+
+ PoolVector<uint8_t>::Read read = img->get_data().read();
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(texture->target, texture->tex_id);
+
+ int src_data_size = img->get_data().size();
+ int src_ofs = 0;
+
+ if (texture->compressed) {
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glCompressedTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, internal_format, src_data_size, &read[src_ofs]);
+
+ } else {
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ // `format` has to match the internal_format used when the texture was created
+ glTexSubImage2D(blit_target, p_dst_mip, dst_x, dst_y, src_w, src_h, format, type, &read[src_ofs]);
+ }
+}
+
Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side) const {
Texture *texture = texture_owner.get(p_texture);
@@ -1614,6 +1667,7 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
shaders.actions_canvas.render_mode_values["blend_sub"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_SUB);
shaders.actions_canvas.render_mode_values["blend_mul"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_MUL);
shaders.actions_canvas.render_mode_values["blend_premul_alpha"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_PMALPHA);
+ shaders.actions_canvas.render_mode_values["blend_disabled"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_DISABLED);
shaders.actions_canvas.render_mode_values["unshaded"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_UNSHADED);
shaders.actions_canvas.render_mode_values["light_only"] = Pair<int *, int>(&p_shader->canvas_item.light_mode, Shader::CanvasItem::LIGHT_MODE_LIGHT_ONLY);
@@ -1911,7 +1965,7 @@ void RasterizerStorageGLES3::material_set_param(RID p_material, const StringName
Variant RasterizerStorageGLES3::material_get_param(RID p_material, const StringName &p_param) const {
const Material *material = material_owner.get(p_material);
- ERR_FAIL_COND_V(!material, RID());
+ ERR_FAIL_COND_V(!material, Variant());
if (material->params.has(p_param))
return material->params[p_param];
@@ -4495,6 +4549,15 @@ Transform2D RasterizerStorageGLES3::skeleton_bone_get_transform_2d(RID p_skeleto
return ret;
}
+void RasterizerStorageGLES3::skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) {
+
+ Skeleton *skeleton = skeleton_owner.getornull(p_skeleton);
+
+ ERR_FAIL_COND(!skeleton->use_2d);
+
+ skeleton->base_transform_2d = p_base_transform;
+}
+
void RasterizerStorageGLES3::update_dirty_skeletons() {
glActiveTexture(GL_TEXTURE0);
@@ -5855,6 +5918,8 @@ void RasterizerStorageGLES3::update_particles() {
shaders.particles.set_uniform(ParticlesShaderGLES3::EMITTING, particles->emitting);
shaders.particles.set_uniform(ParticlesShaderGLES3::RANDOMNESS, particles->randomness);
+ bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0;
+
if (particles->clear && particles->pre_process_time > 0.0) {
float frame_time;
@@ -5872,7 +5937,15 @@ void RasterizerStorageGLES3::update_particles() {
}
if (particles->fixed_fps > 0) {
- float frame_time = 1.0 / particles->fixed_fps;
+ float frame_time;
+ float decr;
+ if (zero_time_scale) {
+ frame_time = 0.0;
+ decr = 1.0 / particles->fixed_fps;
+ } else {
+ frame_time = 1.0 / particles->fixed_fps;
+ decr = frame_time;
+ }
float delta = frame.delta;
if (delta > 0.1) { //avoid recursive stalls if fps goes below 10
delta = 0.1;
@@ -5883,13 +5956,16 @@ void RasterizerStorageGLES3::update_particles() {
while (todo >= frame_time) {
_particles_process(particles, frame_time);
- todo -= frame_time;
+ todo -= decr;
}
particles->frame_remainder = todo;
} else {
- _particles_process(particles, frame.delta);
+ if (zero_time_scale)
+ _particles_process(particles, 0.0);
+ else
+ _particles_process(particles, frame.delta);
}
particle_update_list.remove(particle_update_list.first());
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index ef2b24726..6b626cbd0 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -340,6 +340,7 @@ public:
virtual RID texture_create();
virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT);
virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT);
+ virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT);
virtual Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const;
virtual void texture_set_flags(RID p_texture, uint32_t p_flags);
virtual uint32_t texture_get_flags(RID p_texture) const;
@@ -420,6 +421,7 @@ public:
BLEND_MODE_SUB,
BLEND_MODE_MUL,
BLEND_MODE_PMALPHA,
+ BLEND_MODE_DISABLED,
};
int blend_mode;
@@ -868,6 +870,7 @@ public:
GLuint texture;
SelfList<Skeleton> update_list;
Set<RasterizerScene::InstanceBase *> instances; //instances using skeleton
+ Transform2D base_transform_2d;
Skeleton() :
update_list(this) {
@@ -891,6 +894,7 @@ public:
virtual Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const;
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform);
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const;
+ virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform);
/* Light API */
diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp
index 070c661c8..eb8d6c485 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -795,7 +795,6 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_HEIGHT"] = "light_height";
actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_COLOR"] = "light_color";
actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_UV"] = "light_uv";
- //actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT_SHADOW_COLOR"]="light_shadow_color";
actions[VS::SHADER_CANVAS_ITEM].renames["LIGHT"] = "light";
actions[VS::SHADER_CANVAS_ITEM].renames["SHADOW_COLOR"] = "shadow_color";
@@ -805,9 +804,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_CANVAS_ITEM].usage_defines["SCREEN_PIXEL_SIZE"] = "@SCREEN_UV";
actions[VS::SHADER_CANVAS_ITEM].usage_defines["NORMAL"] = "#define NORMAL_USED\n";
actions[VS::SHADER_CANVAS_ITEM].usage_defines["NORMALMAP"] = "#define NORMALMAP_USED\n";
- actions[VS::SHADER_CANVAS_ITEM].usage_defines["SHADOW_COLOR"] = "#define SHADOW_COLOR_USED\n";
actions[VS::SHADER_CANVAS_ITEM].usage_defines["LIGHT"] = "#define USE_LIGHT_SHADER_CODE\n";
-
actions[VS::SHADER_CANVAS_ITEM].render_mode_defines["skip_vertex_transform"] = "#define SKIP_TRANSFORM_USED\n";
/** SPATIAL SHADER **/
@@ -914,6 +911,7 @@ ShaderCompilerGLES3::ShaderCompilerGLES3() {
actions[VS::SHADER_SPATIAL].render_mode_defines["specular_phong"] = "#define SPECULAR_PHONG\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["specular_toon"] = "#define SPECULAR_TOON\n";
actions[VS::SHADER_SPATIAL].render_mode_defines["specular_disabled"] = "#define SPECULAR_DISABLED\n";
+ actions[VS::SHADER_SPATIAL].render_mode_defines["shadows_disabled"] = "#define SHADOWS_DISABLED\n";
/* PARTICLES SHADER */
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index f436ef06f..326aab4c7 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -4,6 +4,11 @@
layout(location=0) in highp vec2 vertex;
layout(location=3) in vec4 color_attrib;
+#ifdef USE_SKELETON
+layout(location=6) in uvec4 bone_indices; // attrib:6
+layout(location=7) in vec4 bone_weights; // attrib:7
+#endif
+
#ifdef USE_TEXTURE_RECT
uniform vec4 dst_rect;
@@ -51,6 +56,12 @@ out highp vec2 pixel_size_interp;
#endif
+#ifdef USE_SKELETON
+uniform mediump sampler2D skeleton_texture; // texunit:-1
+uniform highp mat4 skeleton_transform;
+uniform highp mat4 skeleton_transform_inverse;
+#endif
+
#ifdef USE_LIGHTING
layout(std140) uniform LightData { //ubo:1
@@ -75,7 +86,6 @@ out vec4 light_uv_interp;
out vec4 local_rot;
-
#ifdef USE_SHADOWS
out highp vec2 pos;
#endif
@@ -101,6 +111,7 @@ MATERIAL_UNIFORMS
#endif
+
VERTEX_SHADER_GLOBALS
void main() {
@@ -146,6 +157,7 @@ void main() {
#endif
+
#define extra_matrix extra_matrix2
{
@@ -175,6 +187,49 @@ VERTEX_SHADER_CODE
#endif
+#ifdef USE_SKELETON
+
+ if (bone_weights!=vec4(0.0)){ //must be a valid bone
+ //skeleton transform
+
+ ivec4 bone_indicesi = ivec4(bone_indices);
+
+ ivec2 tex_ofs = ivec2( bone_indicesi.x%256, (bone_indicesi.x/256)*2 );
+
+ highp mat2x4 m = mat2x4(
+ texelFetch(skeleton_texture,tex_ofs,0),
+ texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0)
+ ) * bone_weights.x;
+
+ tex_ofs = ivec2( bone_indicesi.y%256, (bone_indicesi.y/256)*2 );
+
+ m+= mat2x4(
+ texelFetch(skeleton_texture,tex_ofs,0),
+ texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0)
+ ) * bone_weights.y;
+
+ tex_ofs = ivec2( bone_indicesi.z%256, (bone_indicesi.z/256)*2 );
+
+ m+= mat2x4(
+ texelFetch(skeleton_texture,tex_ofs,0),
+ texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0)
+ ) * bone_weights.z;
+
+
+ tex_ofs = ivec2( bone_indicesi.w%256, (bone_indicesi.w/256)*2 );
+
+ m+= mat2x4(
+ texelFetch(skeleton_texture,tex_ofs,0),
+ texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0)
+ ) * bone_weights.w;
+
+ mat4 bone_matrix = skeleton_transform * transpose(mat4(m[0],m[1],vec4(0.0,0.0,1.0,0.0),vec4(0.0,0.0,0.0,1.0))) * skeleton_transform_inverse;
+
+ outvec = bone_matrix * outvec;
+ }
+
+#endif
+
gl_Position = projection_matrix * outvec;
#ifdef USE_LIGHTING
@@ -207,6 +262,7 @@ uniform mediump sampler2D color_texture; // texunit:0
uniform highp vec2 color_texpixel_size;
uniform mediump sampler2D normal_texture; // texunit:1
+
in highp vec2 uv_interp;
in mediump vec4 color_interp;
@@ -285,7 +341,19 @@ MATERIAL_UNIFORMS
FRAGMENT_SHADER_GLOBALS
-void light_compute(inout vec4 light,vec2 light_vec,float light_height,vec4 light_color,vec2 light_uv,vec4 shadow,vec3 normal,vec2 uv,vec2 screen_uv,vec4 color) {
+void light_compute(
+ inout vec4 light,
+ inout vec2 light_vec,
+ inout float light_height,
+ inout vec4 light_color,
+ vec2 light_uv,
+ inout vec4 shadow_color,
+ vec3 normal,
+ vec2 uv,
+#if defined(SCREEN_UV_USED)
+ vec2 screen_uv,
+#endif
+ vec4 color) {
#if defined(USE_LIGHT_SHADER_CODE)
@@ -462,39 +530,41 @@ FRAGMENT_SHADER_CODE
float att=1.0;
vec2 light_uv = light_uv_interp.xy;
- vec4 light = texture(light_texture,light_uv) * light_color;
-#if defined(SHADOW_COLOR_USED)
- vec4 shadow_color=vec4(0.0,0.0,0.0,0.0);
-#endif
+ vec4 light = texture(light_texture,light_uv);
if (any(lessThan(light_uv_interp.xy,vec2(0.0,0.0))) || any(greaterThanEqual(light_uv_interp.xy,vec2(1.0,1.0)))) {
color.a*=light_outside_alpha; //invisible
} else {
+ float real_light_height = light_height;
+ vec4 real_light_color = light_color;
+ vec4 real_light_shadow_color = light_shadow_color;
#if defined(USE_LIGHT_SHADER_CODE)
//light is written by the light shader
- light_compute(light,light_vec,light_height,light_color,light_uv,shadow,normal,uv,screen_uv,color);
+ light_compute(
+ light,
+ light_vec,
+ real_light_height,
+ real_light_color,
+ light_uv,
+ real_light_shadow_color,
+ normal,
+ uv,
+#if defined(SCREEN_UV_USED)
+ screen_uv,
+#endif
+ color);
+#endif
-#else
+ light *= real_light_color;
if (normal_used) {
-
- vec3 light_normal = normalize(vec3(light_vec,-light_height));
+ vec3 light_normal = normalize(vec3(light_vec,-real_light_height));
light*=max(dot(-light_normal,normal),0.0);
}
color*=light;
-/*
-#ifdef USE_NORMAL
- color.xy=local_rot.xy;//normal.xy;
- color.zw=vec2(0.0,1.0);
-#endif
-*/
-
-//light shader code
-#endif
-
#ifdef USE_SHADOWS
@@ -634,13 +704,8 @@ FRAGMENT_SHADER_CODE
#endif
-
-#if defined(SHADOW_COLOR_USED)
- color=mix(shadow_color,color,shadow_attenuation);
-#else
//color*=shadow_attenuation;
- color=mix(light_shadow_color,color,shadow_attenuation);
-#endif
+ color=mix(real_light_shadow_color,color,shadow_attenuation);
//use shadows
#endif
}
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index d3644bffd..f5481c597 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -1206,6 +1206,7 @@ void light_process_omni(int idx, vec3 vertex, vec3 eye_vec,vec3 normal,vec3 bino
float omni_attenuation = pow( max(1.0 - normalized_distance, 0.0), omni_lights[idx].light_direction_attenuation.w );
vec3 light_attenuation = vec3(omni_attenuation);
+#if !defined(SHADOWS_DISABLED)
if (omni_lights[idx].light_params.w>0.5) {
//there is a shadowmap
@@ -1252,6 +1253,7 @@ void light_process_omni(int idx, vec3 vertex, vec3 eye_vec,vec3 normal,vec3 bino
#endif
light_attenuation*=mix(omni_lights[idx].shadow_color_contact.rgb,vec3(1.0),shadow);
}
+#endif //SHADOWS_DISABLED
light_compute(normal,normalize(light_rel_vec),eye_vec,binormal,tangent,omni_lights[idx].light_color_energy.rgb,light_attenuation,albedo,transmission,omni_lights[idx].light_params.z*p_blob_intensity,roughness,metallic,rim * omni_attenuation,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light);
@@ -1270,6 +1272,7 @@ void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 bi
spot_attenuation*= 1.0 - pow( spot_rim, spot_lights[idx].light_params.x);
vec3 light_attenuation = vec3(spot_attenuation);
+#if !defined(SHADOWS_DISABLED)
if (spot_lights[idx].light_params.w>0.5) {
//there is a shadowmap
highp vec4 splane=(spot_lights[idx].shadow_matrix * vec4(vertex,1.0));
@@ -1287,6 +1290,7 @@ void light_process_spot(int idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 bi
#endif
light_attenuation*=mix(spot_lights[idx].shadow_color_contact.rgb,vec3(1.0),shadow);
}
+#endif //SHADOWS_DISABLED
light_compute(normal,normalize(light_rel_vec),eye_vec,binormal,tangent,spot_lights[idx].light_color_energy.rgb,light_attenuation,albedo,transmission,spot_lights[idx].light_params.z*p_blob_intensity,roughness,metallic,rim * spot_attenuation,rim_tint,clearcoat,clearcoat_gloss,anisotropy,diffuse_light,specular_light);
@@ -1785,6 +1789,7 @@ FRAGMENT_SHADER_CODE
float depth_z = -vertex.z;
#ifdef LIGHT_DIRECTIONAL_SHADOW
+#if !defined(SHADOWS_DISABLED)
#ifdef LIGHT_USE_PSSM4
if (depth_z < shadow_split_offsets.w) {
@@ -1927,6 +1932,7 @@ FRAGMENT_SHADER_CODE
}
+#endif // !defined(SHADOWS_DISABLED)
#endif //LIGHT_DIRECTIONAL_SHADOW
#ifdef USE_VERTEX_LIGHTING
diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl
index 2f671158b..a75871f08 100644
--- a/drivers/gles3/shaders/tonemap.glsl
+++ b/drivers/gles3/shaders/tonemap.glsl
@@ -258,9 +258,13 @@ void main() {
#endif
+#ifdef KEEP_3D_LINEAR
+ // leave color as is...
+#else
//regular Linear -> SRGB conversion
vec3 a = vec3(0.055);
color.rgb = mix( (vec3(1.0)+a)*pow(color.rgb,vec3(1.0/2.4))-a , 12.92*color.rgb , lessThan(color.rgb,vec3(0.0031308)));
+#endif
#if defined(USING_GLOW)
glow = mix( (vec3(1.0)+a)*pow(glow,vec3(1.0/2.4))-a , 12.92*glow , lessThan(glow,vec3(0.0031308)));
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 9639f0634..0f47949b4 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -37,195 +37,223 @@
#include "os/os.h"
#include "project_settings.h"
-void pa_state_cb(pa_context *c, void *userdata) {
- pa_context_state_t state;
- int *pa_ready = (int *)userdata;
+void AudioDriverPulseAudio::pa_state_cb(pa_context *c, void *userdata) {
+ AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata;
- state = pa_context_get_state(c);
- switch (state) {
- case PA_CONTEXT_FAILED:
+ switch (pa_context_get_state(c)) {
case PA_CONTEXT_TERMINATED:
- *pa_ready = 2;
+ case PA_CONTEXT_FAILED:
+ ad->pa_ready = -1;
break;
case PA_CONTEXT_READY:
- *pa_ready = 1;
+ ad->pa_ready = 1;
break;
}
}
-void sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
- unsigned int *channels = (unsigned int *)userdata;
+void AudioDriverPulseAudio::pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
+ AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata;
// If eol is set to a positive number, you're at the end of the list
if (eol > 0) {
return;
}
- *channels = l->channel_map.channels;
+ ad->pa_map = l->channel_map;
+ ad->pa_status++;
}
-void server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) {
- char *default_output = (char *)userdata;
+void AudioDriverPulseAudio::pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata) {
+ AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata;
- strncpy(default_output, i->default_sink_name, 1024);
+ ad->default_device = i->default_sink_name;
+ ad->pa_status++;
}
-static unsigned int detect_channels() {
-
- pa_mainloop *pa_ml;
- pa_mainloop_api *pa_mlapi;
- pa_operation *pa_op;
- pa_context *pa_ctx;
-
- int state = 0;
- int pa_ready = 0;
-
- char default_output[1024];
- unsigned int channels = 2;
-
- pa_ml = pa_mainloop_new();
- pa_mlapi = pa_mainloop_get_api(pa_ml);
- pa_ctx = pa_context_new(pa_mlapi, "Godot");
-
- int ret = pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL);
- if (ret < 0) {
- pa_context_unref(pa_ctx);
- pa_mainloop_free(pa_ml);
+void AudioDriverPulseAudio::detect_channels() {
- return 2;
- }
+ pa_channel_map_init_stereo(&pa_map);
- pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);
+ if (device_name == "Default") {
+ // Get the default output device name
+ pa_status = 0;
+ pa_operation *pa_op = pa_context_get_server_info(pa_ctx, &AudioDriverPulseAudio::pa_server_info_cb, (void *)this);
+ if (pa_op) {
+ while (pa_status == 0) {
+ int ret = pa_mainloop_iterate(pa_ml, 1, NULL);
+ if (ret < 0) {
+ ERR_PRINT("pa_mainloop_iterate error");
+ }
+ }
- // Wait until the pa server is ready
- while (pa_ready == 0) {
- pa_mainloop_iterate(pa_ml, 1, NULL);
+ pa_operation_unref(pa_op);
+ } else {
+ ERR_PRINT("pa_context_get_server_info error");
+ }
}
- // Check if there was an error connecting to the pa server
- if (pa_ready == 2) {
- pa_context_disconnect(pa_ctx);
- pa_context_unref(pa_ctx);
- pa_mainloop_free(pa_ml);
-
- return 2;
+ char device[1024];
+ if (device_name == "Default") {
+ strcpy(device, default_device.utf8().get_data());
+ } else {
+ strcpy(device, device_name.utf8().get_data());
}
- // Get the default output device name
- pa_op = pa_context_get_server_info(pa_ctx, &server_info_cb, (void *)default_output);
+ // Now using the device name get the amount of channels
+ pa_status = 0;
+ pa_operation *pa_op = pa_context_get_sink_info_by_name(pa_ctx, device, &AudioDriverPulseAudio::pa_sink_info_cb, (void *)this);
if (pa_op) {
- while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) {
- ret = pa_mainloop_iterate(pa_ml, 1, NULL);
+ while (pa_status == 0) {
+ int ret = pa_mainloop_iterate(pa_ml, 1, NULL);
if (ret < 0) {
ERR_PRINT("pa_mainloop_iterate error");
}
}
pa_operation_unref(pa_op);
-
- // Now using the device name get the amount of channels
- pa_op = pa_context_get_sink_info_by_name(pa_ctx, default_output, &sink_info_cb, (void *)&channels);
- if (pa_op) {
- while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) {
- ret = pa_mainloop_iterate(pa_ml, 1, NULL);
- if (ret < 0) {
- ERR_PRINT("pa_mainloop_iterate error");
- }
- }
-
- pa_operation_unref(pa_op);
- } else {
- ERR_PRINT("pa_context_get_sink_info_by_name error");
- }
} else {
- ERR_PRINT("pa_context_get_server_info error");
+ ERR_PRINT("pa_context_get_sink_info_by_name error");
}
-
- pa_context_disconnect(pa_ctx);
- pa_context_unref(pa_ctx);
- pa_mainloop_free(pa_ml);
-
- return channels;
}
-Error AudioDriverPulseAudio::init() {
+Error AudioDriverPulseAudio::init_device() {
- active = false;
- thread_exited = false;
- exit_thread = false;
-
- mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE);
+ // If there is a specified device check that it is really present
+ if (device_name != "Default") {
+ Array list = get_device_list();
+ if (list.find(device_name) == -1) {
+ device_name = "Default";
+ new_device = "Default";
+ }
+ }
// Detect the amount of channels PulseAudio is using
- // Note: If using an even amount of channels (2, 4, etc) channels and pa_channels will be equal,
- // if not then pa_channels will have the real amount of channels PulseAudio is using and channels
- // will have the amount of channels Godot is using (in this case it's pa_channels + 1)
- pa_channels = detect_channels();
- switch (pa_channels) {
+ // Note: If using an even amount of channels (2, 4, etc) channels and pa_map.channels will be equal,
+ // if not then pa_map.channels will have the real amount of channels PulseAudio is using and channels
+ // will have the amount of channels Godot is using (in this case it's pa_map.channels + 1)
+ detect_channels();
+ switch (pa_map.channels) {
case 1: // Mono
case 3: // Surround 2.1
case 5: // Surround 5.0
case 7: // Surround 7.0
- channels = pa_channels + 1;
+ channels = pa_map.channels + 1;
break;
case 2: // Stereo
case 4: // Surround 4.0
case 6: // Surround 5.1
case 8: // Surround 7.1
- channels = pa_channels;
+ channels = pa_map.channels;
break;
default:
- ERR_PRINTS("PulseAudio: Unsupported number of channels: " + itos(pa_channels));
- ERR_FAIL_V(ERR_CANT_OPEN);
+ WARN_PRINTS("PulseAudio: Unsupported number of channels: " + itos(pa_map.channels));
+ pa_channel_map_init_stereo(&pa_map);
+ channels = 2;
break;
}
- pa_sample_spec spec;
- spec.format = PA_SAMPLE_S16LE;
- spec.channels = pa_channels;
- spec.rate = mix_rate;
-
int latency = GLOBAL_DEF("audio/output_latency", DEFAULT_OUTPUT_LATENCY);
buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
- pa_buffer_size = buffer_frames * pa_channels;
+ pa_buffer_size = buffer_frames * pa_map.channels;
if (OS::get_singleton()->is_stdout_verbose()) {
- print_line("PulseAudio: detected " + itos(pa_channels) + " channels");
+ print_line("PulseAudio: detected " + itos(pa_map.channels) + " channels");
print_line("PulseAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms");
}
+ pa_sample_spec spec;
+ spec.format = PA_SAMPLE_S16LE;
+ spec.channels = pa_map.channels;
+ spec.rate = mix_rate;
+
+ pa_str = pa_stream_new(pa_ctx, "Sound", &spec, &pa_map);
+ if (pa_str == NULL) {
+ ERR_PRINTS("PulseAudio: pa_stream_new error: " + String(pa_strerror(pa_context_errno(pa_ctx))));
+ ERR_FAIL_V(ERR_CANT_OPEN);
+ }
+
pa_buffer_attr attr;
// set to appropriate buffer length (in bytes) from global settings
- attr.tlength = pa_buffer_size * sizeof(int16_t);
+ // Note: PulseAudio defaults to 4 fragments, which means that the actual
+ // latency is tlength / fragments. It seems that the PulseAudio has no way
+ // to get the fragments number so we're hardcoding this to the default of 4
+ const int fragments = 4;
+ attr.tlength = pa_buffer_size * sizeof(int16_t) * fragments;
// set them to be automatically chosen
attr.prebuf = (uint32_t)-1;
attr.maxlength = (uint32_t)-1;
attr.minreq = (uint32_t)-1;
- int error_code;
- pulse = pa_simple_new(NULL, // default server
- "Godot", // application name
- PA_STREAM_PLAYBACK,
- NULL, // default device
- "Sound", // stream description
- &spec,
- NULL, // use default channel map
- &attr, // use buffering attributes from above
- &error_code);
-
- if (pulse == NULL) {
- fprintf(stderr, "PulseAudio ERR: %s\n", pa_strerror(error_code));
- ERR_FAIL_COND_V(pulse == NULL, ERR_CANT_OPEN);
- }
+ const char *dev = device_name == "Default" ? NULL : device_name.utf8().get_data();
+ pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
+ int error_code = pa_stream_connect_playback(pa_str, dev, &attr, flags, NULL, NULL);
+ ERR_FAIL_COND_V(error_code < 0, ERR_CANT_OPEN);
samples_in.resize(buffer_frames * channels);
samples_out.resize(pa_buffer_size);
- mutex = Mutex::create();
- thread = Thread::create(AudioDriverPulseAudio::thread_func, this);
+ return OK;
+}
+
+Error AudioDriverPulseAudio::init() {
+
+ active = false;
+ thread_exited = false;
+ exit_thread = false;
+
+ mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE);
+
+ pa_ml = pa_mainloop_new();
+ ERR_FAIL_COND_V(pa_ml == NULL, ERR_CANT_OPEN);
+
+ pa_ctx = pa_context_new(pa_mainloop_get_api(pa_ml), "Godot");
+ ERR_FAIL_COND_V(pa_ctx == NULL, ERR_CANT_OPEN);
+
+ pa_ready = 0;
+ pa_context_set_state_callback(pa_ctx, pa_state_cb, (void *)this);
+
+ int ret = pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL);
+ if (ret < 0) {
+ if (pa_ctx) {
+ pa_context_unref(pa_ctx);
+ pa_ctx = NULL;
+ }
+
+ if (pa_ml) {
+ pa_mainloop_free(pa_ml);
+ pa_ml = NULL;
+ }
+
+ return ERR_CANT_OPEN;
+ }
+
+ while (pa_ready == 0) {
+ pa_mainloop_iterate(pa_ml, 1, NULL);
+ }
+
+ if (pa_ready < 0) {
+ if (pa_ctx) {
+ pa_context_disconnect(pa_ctx);
+ pa_context_unref(pa_ctx);
+ pa_ctx = NULL;
+ }
+
+ if (pa_ml) {
+ pa_mainloop_free(pa_ml);
+ pa_ml = NULL;
+ }
+
+ return ERR_CANT_OPEN;
+ }
+
+ Error err = init_device();
+ if (err == OK) {
+ mutex = Mutex::create();
+ thread = Thread::create(AudioDriverPulseAudio::thread_func, this);
+ }
return OK;
}
@@ -233,9 +261,24 @@ Error AudioDriverPulseAudio::init() {
float AudioDriverPulseAudio::get_latency() {
if (latency == 0) { //only do this once since it's approximate anyway
- int error_code;
- pa_usec_t palat = pa_simple_get_latency(pulse, &error_code);
- latency = double(palat) / 1000000.0;
+ lock();
+
+ pa_usec_t palat = 0;
+ if (pa_stream_get_state(pa_str) == PA_STREAM_READY) {
+ int negative = 0;
+
+ if (pa_stream_get_latency(pa_str, &palat, &negative) >= 0) {
+ if (negative) {
+ palat = 0;
+ }
+ }
+ }
+
+ if (palat > 0) {
+ latency = double(palat) / 1000000.0;
+ }
+
+ unlock();
}
return latency;
@@ -258,7 +301,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
ad->unlock();
- if (ad->channels == ad->pa_channels) {
+ if (ad->channels == ad->pa_map.channels) {
for (unsigned int i = 0; i < ad->pa_buffer_size; i++) {
ad->samples_out[i] = ad->samples_in[i] >> 16;
}
@@ -268,7 +311,7 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
unsigned int out_idx = 0;
for (unsigned int i = 0; i < ad->buffer_frames; i++) {
- for (unsigned int j = 0; j < ad->pa_channels - 1; j++) {
+ for (unsigned int j = 0; j < ad->pa_map.channels - 1; j++) {
ad->samples_out[out_idx++] = ad->samples_in[in_idx++] >> 16;
}
uint32_t l = ad->samples_in[in_idx++];
@@ -278,17 +321,66 @@ void AudioDriverPulseAudio::thread_func(void *p_udata) {
}
}
- // pa_simple_write always consumes the entire buffer
-
int error_code;
int byte_size = ad->pa_buffer_size * sizeof(int16_t);
- if (pa_simple_write(ad->pulse, ad->samples_out.ptr(), byte_size, &error_code) < 0) {
- // can't recover here
- fprintf(stderr, "PulseAudio failed and can't recover: %s\n", pa_strerror(error_code));
- ad->active = false;
- ad->exit_thread = true;
- break;
+
+ ad->lock();
+
+ int ret;
+ do {
+ ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL);
+ } while (ret > 0);
+
+ if (pa_stream_get_state(ad->pa_str) == PA_STREAM_READY) {
+ const void *ptr = ad->samples_out.ptr();
+ while (byte_size > 0) {
+ size_t bytes = pa_stream_writable_size(ad->pa_str);
+ if (bytes > 0) {
+ if (bytes > byte_size) {
+ bytes = byte_size;
+ }
+
+ ret = pa_stream_write(ad->pa_str, ptr, bytes, NULL, 0LL, PA_SEEK_RELATIVE);
+ if (ret >= 0) {
+ byte_size -= bytes;
+ ptr = (const char *)ptr + bytes;
+ }
+ } else {
+ ret = pa_mainloop_iterate(ad->pa_ml, 0, NULL);
+ if (ret == 0) {
+ // If pa_mainloop_iterate returns 0 sleep for 1 msec to wait
+ // for the stream to be able to process more bytes
+ ad->unlock();
+
+ OS::get_singleton()->delay_usec(1000);
+
+ ad->lock();
+ }
+ }
+ }
}
+
+ // User selected a new device, finish the current one so we'll init the new device
+ if (ad->device_name != ad->new_device) {
+ ad->device_name = ad->new_device;
+ ad->finish_device();
+
+ Error err = ad->init_device();
+ if (err != OK) {
+ ERR_PRINT("PulseAudio: init_device error");
+ ad->device_name = "Default";
+ ad->new_device = "Default";
+
+ err = ad->init_device();
+ if (err != OK) {
+ ad->active = false;
+ ad->exit_thread = true;
+ break;
+ }
+ }
+ }
+
+ ad->unlock();
}
ad->thread_exited = true;
@@ -309,6 +401,61 @@ AudioDriver::SpeakerMode AudioDriverPulseAudio::get_speaker_mode() const {
return get_speaker_mode_by_total_channels(channels);
}
+void AudioDriverPulseAudio::pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
+ AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)userdata;
+ int ctr = 0;
+
+ // If eol is set to a positive number, you're at the end of the list
+ if (eol > 0) {
+ return;
+ }
+
+ ad->pa_devices.push_back(l->name);
+ ad->pa_status++;
+}
+
+Array AudioDriverPulseAudio::get_device_list() {
+
+ pa_devices.clear();
+ pa_devices.push_back("Default");
+
+ if (pa_ctx == NULL) {
+ return pa_devices;
+ }
+
+ lock();
+
+ // Get the device list
+ pa_status = 0;
+ pa_operation *pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, (void *)this);
+ if (pa_op) {
+ while (pa_status == 0) {
+ int ret = pa_mainloop_iterate(pa_ml, 1, NULL);
+ if (ret < 0) {
+ ERR_PRINT("pa_mainloop_iterate error");
+ }
+ }
+
+ pa_operation_unref(pa_op);
+ } else {
+ ERR_PRINT("pa_context_get_server_info error");
+ }
+
+ unlock();
+
+ return pa_devices;
+}
+
+String AudioDriverPulseAudio::get_device() {
+
+ return device_name;
+}
+
+void AudioDriverPulseAudio::set_device(String device) {
+
+ new_device = device;
+}
+
void AudioDriverPulseAudio::lock() {
if (!thread || !mutex)
@@ -323,6 +470,15 @@ void AudioDriverPulseAudio::unlock() {
mutex->unlock();
}
+void AudioDriverPulseAudio::finish_device() {
+
+ if (pa_str) {
+ pa_stream_disconnect(pa_str);
+ pa_stream_unref(pa_str);
+ pa_str = NULL;
+ }
+}
+
void AudioDriverPulseAudio::finish() {
if (!thread)
@@ -331,9 +487,17 @@ void AudioDriverPulseAudio::finish() {
exit_thread = true;
Thread::wait_to_finish(thread);
- if (pulse) {
- pa_simple_free(pulse);
- pulse = NULL;
+ finish_device();
+
+ if (pa_ctx) {
+ pa_context_disconnect(pa_ctx);
+ pa_context_unref(pa_ctx);
+ pa_ctx = NULL;
+ }
+
+ if (pa_ml) {
+ pa_mainloop_free(pa_ml);
+ pa_ml = NULL;
}
memdelete(thread);
@@ -347,9 +511,16 @@ void AudioDriverPulseAudio::finish() {
AudioDriverPulseAudio::AudioDriverPulseAudio() {
+ pa_ml = NULL;
+ pa_ctx = NULL;
+ pa_str = NULL;
+
mutex = NULL;
thread = NULL;
- pulse = NULL;
+
+ device_name = "Default";
+ new_device = "Default";
+ default_device = "";
samples_in.clear();
samples_out.clear();
@@ -358,7 +529,8 @@ AudioDriverPulseAudio::AudioDriverPulseAudio() {
buffer_frames = 0;
pa_buffer_size = 0;
channels = 0;
- pa_channels = 0;
+ pa_ready = 0;
+ pa_status = 0;
active = false;
thread_exited = false;
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h
index 5737f2431..b471f5f9d 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.h
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.h
@@ -37,14 +37,21 @@
#include "core/os/thread.h"
#include "servers/audio_server.h"
-#include <pulse/simple.h>
+#include <pulse/pulseaudio.h>
class AudioDriverPulseAudio : public AudioDriver {
Thread *thread;
Mutex *mutex;
- pa_simple *pulse;
+ pa_mainloop *pa_ml;
+ pa_context *pa_ctx;
+ pa_stream *pa_str;
+ pa_channel_map pa_map;
+
+ String device_name;
+ String new_device;
+ String default_device;
Vector<int32_t> samples_in;
Vector<int16_t> samples_out;
@@ -53,7 +60,9 @@ class AudioDriverPulseAudio : public AudioDriver {
unsigned int buffer_frames;
unsigned int pa_buffer_size;
int channels;
- int pa_channels;
+ int pa_ready;
+ int pa_status;
+ Array pa_devices;
bool active;
bool thread_exited;
@@ -61,6 +70,16 @@ class AudioDriverPulseAudio : public AudioDriver {
float latency;
+ static void pa_state_cb(pa_context *c, void *userdata);
+ static void pa_sink_info_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata);
+ static void pa_server_info_cb(pa_context *c, const pa_server_info *i, void *userdata);
+ static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata);
+
+ Error init_device();
+ void finish_device();
+
+ void detect_channels();
+
static void thread_func(void *p_udata);
public:
@@ -72,6 +91,9 @@ public:
virtual void start();
virtual int get_mix_rate() const;
virtual SpeakerMode get_speaker_mode() const;
+ virtual Array get_device_list();
+ virtual String get_device();
+ virtual void set_device(String device);
virtual void lock();
virtual void unlock();
virtual void finish();
diff --git a/drivers/rtaudio/audio_driver_rtaudio.cpp b/drivers/rtaudio/audio_driver_rtaudio.cpp
index ed6f2e24e..457486797 100644
--- a/drivers/rtaudio/audio_driver_rtaudio.cpp
+++ b/drivers/rtaudio/audio_driver_rtaudio.cpp
@@ -133,7 +133,7 @@ Error AudioDriverRtAudio::init() {
break;
} catch (RtAudioError &e) {
// try with less channels
- ERR_PRINT("Unable to open audio, retrying with fewer channels..");
+ ERR_PRINT("Unable to open audio, retrying with fewer channels...");
switch (speaker_mode) {
case SPEAKER_SURROUND_51: speaker_mode = SPEAKER_MODE_STEREO; break;
diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index a7a3eef93..c25d34125 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -136,7 +136,7 @@ void FileAccessUnix::close() {
if (save_path != "") {
//unlink(save_path.utf8().get_data());
- //print_line("renaming..");
+ //print_line("renaming...");
int rename_error = rename((save_path + ".tmp").utf8().get_data(), save_path.utf8().get_data());
if (rename_error && close_fail_notify) {
@@ -184,7 +184,7 @@ size_t FileAccessUnix::get_position() const {
ERR_FAIL_COND_V(!f, 0);
- int pos = ftell(f);
+ long pos = ftell(f);
if (pos < 0) {
check_errors();
ERR_FAIL_V(0);
@@ -196,10 +196,10 @@ size_t FileAccessUnix::get_len() const {
ERR_FAIL_COND_V(!f, 0);
- int pos = ftell(f);
+ long pos = ftell(f);
ERR_FAIL_COND_V(pos < 0, 0);
ERR_FAIL_COND_V(fseek(f, 0, SEEK_END), 0);
- int size = ftell(f);
+ long size = ftell(f);
ERR_FAIL_COND_V(size < 0, 0);
ERR_FAIL_COND_V(fseek(f, pos, SEEK_SET), 0);
diff --git a/drivers/unix/ip_unix.cpp b/drivers/unix/ip_unix.cpp
index 032d91f0d..949609bb9 100644
--- a/drivers/unix/ip_unix.cpp
+++ b/drivers/unix/ip_unix.cpp
@@ -101,16 +101,18 @@ IP_Address IP_Unix::_resolve_hostname(const String &p_hostname, Type p_type) {
hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_ADDRCONFIG;
};
- hints.ai_flags &= !AI_NUMERICHOST;
+ hints.ai_flags &= ~AI_NUMERICHOST;
int s = getaddrinfo(p_hostname.utf8().get_data(), NULL, &hints, &result);
if (s != 0) {
- ERR_PRINT("getaddrinfo failed!");
+ ERR_PRINT("getaddrinfo failed! Cannot resolve hostname.");
return IP_Address();
};
if (result == NULL || result->ai_addr == NULL) {
ERR_PRINT("Invalid response from getaddrinfo");
+ if (result)
+ freeaddrinfo(result);
return IP_Address();
};
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 31c8e4ade..eeb3b31fc 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -349,6 +349,12 @@ Error OS_Unix::open_dynamic_library(const String p_path, void *&p_library_handle
String path = p_path;
+ if (FileAccess::exists(path) && path.is_rel_path()) {
+ // dlopen expects a slash, in this case a leading ./ for it to be interpreted as a relative path,
+ // otherwise it will end up searching various system directories for the lib instead and finally failing.
+ path = "./" + path;
+ }
+
if (!FileAccess::exists(path)) {
//this code exists so gdnative can load .so files from within the executable path
path = get_executable_path().get_base_dir().plus_file(p_path.get_file());
diff --git a/drivers/unix/stream_peer_tcp_posix.cpp b/drivers/unix/stream_peer_tcp_posix.cpp
index 17112e5ab..6d798f32f 100644
--- a/drivers/unix/stream_peer_tcp_posix.cpp
+++ b/drivers/unix/stream_peer_tcp_posix.cpp
@@ -124,11 +124,14 @@ void StreamPeerTCPPosix::set_socket(int p_sockfd, IP_Address p_host, int p_port,
sock_type = p_sock_type;
sockfd = p_sockfd;
#ifndef NO_FCNTL
- fcntl(sockfd, F_SETFL, O_NONBLOCK);
+ if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) {
+ WARN_PRINT("Error setting socket as non blocking");
+ }
#else
int bval = 1;
- ioctl(sockfd, FIONBIO, &bval);
-
+ if (ioctl(sockfd, FIONBIO, &bval) < 0) {
+ WARN_PRINT("Error setting socket as non blocking");
+ }
#endif
status = STATUS_CONNECTING;
@@ -150,10 +153,14 @@ Error StreamPeerTCPPosix::connect_to_host(const IP_Address &p_host, uint16_t p_p
};
#ifndef NO_FCNTL
- fcntl(sockfd, F_SETFL, O_NONBLOCK);
+ if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) {
+ WARN_PRINT("Error setting socket as non blocking");
+ }
#else
int bval = 1;
- ioctl(sockfd, FIONBIO, &bval);
+ if (ioctl(sockfd, FIONBIO, &bval) < 0) {
+ WARN_PRINT("Error setting socket as non blocking");
+ }
#endif
struct sockaddr_storage their_addr;
@@ -308,7 +315,9 @@ void StreamPeerTCPPosix::set_no_delay(bool p_enabled) {
ERR_FAIL_COND(!is_connected_to_host());
int flag = p_enabled ? 1 : 0;
- setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
+ if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) < 0) {
+ ERR_PRINT("Unable to set TCP no delay option");
+ }
}
bool StreamPeerTCPPosix::is_connected_to_host() const {
diff --git a/drivers/unix/tcp_server_posix.cpp b/drivers/unix/tcp_server_posix.cpp
index 07ffe3b00..67ab981f4 100644
--- a/drivers/unix/tcp_server_posix.cpp
+++ b/drivers/unix/tcp_server_posix.cpp
@@ -91,10 +91,14 @@ Error TCPServerPosix::listen(uint16_t p_port, const IP_Address &p_bind_address)
ERR_FAIL_COND_V(sockfd == -1, FAILED);
#ifndef NO_FCNTL
- fcntl(sockfd, F_SETFL, O_NONBLOCK);
+ if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) {
+ WARN_PRINT("Error setting socket as non blocking");
+ }
#else
int bval = 1;
- ioctl(sockfd, FIONBIO, &bval);
+ if (ioctl(sockfd, FIONBIO, &bval) < 0) {
+ WARN_PRINT("Error setting socket as non blocking");
+ }
#endif
int reuse = 1;
@@ -113,6 +117,7 @@ Error TCPServerPosix::listen(uint16_t p_port, const IP_Address &p_bind_address)
ERR_FAIL_V(FAILED);
};
} else {
+ close(sockfd);
return ERR_ALREADY_IN_USE;
};
@@ -157,10 +162,14 @@ Ref<StreamPeerTCP> TCPServerPosix::take_connection() {
int fd = accept(listen_sockfd, (struct sockaddr *)&their_addr, &size);
ERR_FAIL_COND_V(fd == -1, Ref<StreamPeerTCP>());
#ifndef NO_FCNTL
- fcntl(fd, F_SETFL, O_NONBLOCK);
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ WARN_PRINT("Error setting socket as non blocking");
+ }
#else
int bval = 1;
- ioctl(fd, FIONBIO, &bval);
+ if (ioctl(fd, FIONBIO, &bval) < 0) {
+ WARN_PRINT("Error setting socket as non blocking");
+ }
#endif
Ref<StreamPeerTCPPosix> conn = memnew(StreamPeerTCPPosix);
diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp
index 36acfb10d..e1680601a 100644
--- a/drivers/wasapi/audio_driver_wasapi.cpp
+++ b/drivers/wasapi/audio_driver_wasapi.cpp
@@ -35,6 +35,19 @@
#include "os/os.h"
#include "project_settings.h"
+#include <functiondiscoverykeys.h>
+
+#ifndef PKEY_Device_FriendlyName
+
+#undef DEFINE_PROPERTYKEY
+/* clang-format off */
+#define DEFINE_PROPERTYKEY(id, a, b, c, d, e, f, g, h, i, j, k, l) \
+ const PROPERTYKEY id = { { a, b, c, { d, e, f, g, h, i, j, k, } }, l };
+/* clang-format on */
+
+DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
+#endif
+
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioClient = __uuidof(IAudioClient);
@@ -121,7 +134,61 @@ Error AudioDriverWASAPI::init_device(bool reinit) {
HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator);
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
- hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
+ if (device_name == "Default") {
+ hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
+ } else {
+ IMMDeviceCollection *devices = NULL;
+
+ hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices);
+ ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
+
+ LPWSTR strId = NULL;
+ bool found = false;
+
+ UINT count = 0;
+ hr = devices->GetCount(&count);
+ ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
+
+ for (ULONG i = 0; i < count && !found; i++) {
+ IMMDevice *device = NULL;
+
+ hr = devices->Item(i, &device);
+ ERR_BREAK(hr != S_OK);
+
+ IPropertyStore *props = NULL;
+ hr = device->OpenPropertyStore(STGM_READ, &props);
+ ERR_BREAK(hr != S_OK);
+
+ PROPVARIANT propvar;
+ PropVariantInit(&propvar);
+
+ hr = props->GetValue(PKEY_Device_FriendlyName, &propvar);
+ ERR_BREAK(hr != S_OK);
+
+ if (device_name == String(propvar.pwszVal)) {
+ hr = device->GetId(&strId);
+ ERR_BREAK(hr != S_OK);
+
+ found = true;
+ }
+
+ PropVariantClear(&propvar);
+ props->Release();
+ device->Release();
+ }
+
+ if (found) {
+ hr = enumerator->GetDevice(strId, &device);
+ }
+
+ if (strId) {
+ CoTaskMemFree(strId);
+ }
+
+ if (device == NULL) {
+ hr = enumerator->GetDefaultAudioEndpoint(eRender, eConsole, &device);
+ }
+ }
if (reinit) {
// In case we're trying to re-initialize the device prevent throwing this error on the console,
// otherwise if there is currently no device available this will spam the console.
@@ -151,7 +218,6 @@ Error AudioDriverWASAPI::init_device(bool reinit) {
// Since we're using WASAPI Shared Mode we can't control any of these, we just tag along
wasapi_channels = pwfex->nChannels;
- mix_rate = pwfex->nSamplesPerSec;
format_tag = pwfex->wFormatTag;
bits_per_sample = pwfex->wBitsPerSample;
@@ -187,7 +253,14 @@ Error AudioDriverWASAPI::init_device(bool reinit) {
}
}
- hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, pwfex, NULL);
+ DWORD streamflags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
+ if (mix_rate != pwfex->nSamplesPerSec) {
+ streamflags |= AUDCLNT_STREAMFLAGS_RATEADJUST;
+ pwfex->nSamplesPerSec = mix_rate;
+ pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nChannels * (pwfex->wBitsPerSample / 8);
+ }
+
+ hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, streamflags, 0, 0, pwfex, NULL);
ERR_FAIL_COND_V(hr != S_OK, ERR_CANT_OPEN);
event = CreateEvent(NULL, FALSE, FALSE, NULL);
@@ -223,10 +296,11 @@ Error AudioDriverWASAPI::finish_device() {
if (audio_client) {
if (active) {
audio_client->Stop();
- audio_client->Release();
- audio_client = NULL;
active = false;
}
+
+ audio_client->Release();
+ audio_client = NULL;
}
if (render_client) {
@@ -244,6 +318,8 @@ Error AudioDriverWASAPI::finish_device() {
Error AudioDriverWASAPI::init() {
+ mix_rate = GLOBAL_DEF("audio/mix_rate", DEFAULT_MIX_RATE);
+
Error err = init_device();
if (err != OK) {
ERR_PRINT("WASAPI: init_device error");
@@ -285,6 +361,64 @@ AudioDriver::SpeakerMode AudioDriverWASAPI::get_speaker_mode() const {
return get_speaker_mode_by_total_channels(channels);
}
+Array AudioDriverWASAPI::get_device_list() {
+
+ Array list;
+ IMMDeviceCollection *devices = NULL;
+ IMMDeviceEnumerator *enumerator = NULL;
+
+ list.push_back(String("Default"));
+
+ CoInitialize(NULL);
+
+ HRESULT hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void **)&enumerator);
+ ERR_FAIL_COND_V(hr != S_OK, Array());
+
+ hr = enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &devices);
+ ERR_FAIL_COND_V(hr != S_OK, Array());
+
+ UINT count = 0;
+ hr = devices->GetCount(&count);
+ ERR_FAIL_COND_V(hr != S_OK, Array());
+
+ for (ULONG i = 0; i < count; i++) {
+ IMMDevice *device = NULL;
+
+ hr = devices->Item(i, &device);
+ ERR_BREAK(hr != S_OK);
+
+ IPropertyStore *props = NULL;
+ hr = device->OpenPropertyStore(STGM_READ, &props);
+ ERR_BREAK(hr != S_OK);
+
+ PROPVARIANT propvar;
+ PropVariantInit(&propvar);
+
+ hr = props->GetValue(PKEY_Device_FriendlyName, &propvar);
+ ERR_BREAK(hr != S_OK);
+
+ list.push_back(String(propvar.pwszVal));
+
+ PropVariantClear(&propvar);
+ props->Release();
+ device->Release();
+ }
+
+ devices->Release();
+ enumerator->Release();
+ return list;
+}
+
+String AudioDriverWASAPI::get_device() {
+
+ return device_name;
+}
+
+void AudioDriverWASAPI::set_device(String device) {
+
+ new_device = device;
+}
+
void AudioDriverWASAPI::write_sample(AudioDriverWASAPI *ad, BYTE *buffer, int i, int32_t sample) {
if (ad->format_tag == WAVE_FORMAT_PCM) {
switch (ad->bits_per_sample) {
@@ -400,7 +534,8 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
}
}
- if (default_device_changed) {
+ // If we're using the Default device and it changed finish it so we'll re-init the device
+ if (ad->device_name == "Default" && default_device_changed) {
Error err = ad->finish_device();
if (err != OK) {
ERR_PRINT("WASAPI: finish_device error");
@@ -409,6 +544,15 @@ void AudioDriverWASAPI::thread_func(void *p_udata) {
default_device_changed = false;
}
+ // User selected a new device, finish the current one so we'll init the new device
+ if (ad->device_name != ad->new_device) {
+ ad->device_name = ad->new_device;
+ Error err = ad->finish_device();
+ if (err != OK) {
+ ERR_PRINT("WASAPI: finish_device error");
+ }
+ }
+
if (!ad->audio_client) {
Error err = ad->init_device(true);
if (err == OK) {
@@ -483,6 +627,9 @@ AudioDriverWASAPI::AudioDriverWASAPI() {
thread_exited = false;
exit_thread = false;
active = false;
+
+ device_name = "Default";
+ new_device = "Default";
}
#endif
diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h
index 2b19f0cca..c97f4c288 100644
--- a/drivers/wasapi/audio_driver_wasapi.h
+++ b/drivers/wasapi/audio_driver_wasapi.h
@@ -49,6 +49,9 @@ class AudioDriverWASAPI : public AudioDriver {
Mutex *mutex;
Thread *thread;
+ String device_name;
+ String new_device;
+
WORD format_tag;
WORD bits_per_sample;
@@ -80,6 +83,9 @@ public:
virtual void start();
virtual int get_mix_rate() const;
virtual SpeakerMode get_speaker_mode() const;
+ virtual Array get_device_list();
+ virtual String get_device();
+ virtual void set_device(String device);
virtual void lock();
virtual void unlock();
virtual void finish();
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index e10f4d05e..aa0fd34e0 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -85,10 +85,31 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) {
return ERR_FILE_CANT_OPEN;
};
+#ifdef TOOLS_ENABLED
+ // Windows is case insensitive, but all other platforms are sensitive to it
+ // To ease cross-platform development, we issue a warning if users try to access
+ // a file using the wrong case (which *works* on Windows, but won't on other
+ // platforms).
+ if (p_mode_flags == READ) {
+ WIN32_FIND_DATAW d = { 0 };
+ HANDLE f = FindFirstFileW(path.c_str(), &d);
+ if (f) {
+ String fname = d.cFileName;
+ if (fname != String()) {
+
+ String base_file = path.get_file();
+ if (base_file != fname && base_file.findn(fname) == 0) {
+ WARN_PRINTS("Case mismatch opening requested file '" + base_file + "', stored as '" + fname + "' in the filesystem. This file will not open when exported to other case-sensitive platforms.");
+ }
+ }
+ FindClose(f);
+ }
+ }
+#endif
+
if (is_backup_save_enabled() && p_mode_flags & WRITE && !(p_mode_flags & READ)) {
save_path = path;
path = path + ".tmp";
- //print_line("saving instead to "+path);
}
f = _wfopen(path.c_str(), mode_string);
@@ -113,7 +134,7 @@ void FileAccessWindows::close() {
if (save_path != "") {
//unlink(save_path.utf8().get_data());
- //print_line("renaming..");
+ //print_line("renaming...");
//_wunlink(save_path.c_str()); //unlink if exists
//int rename_error = _wrename((save_path+".tmp").c_str(),save_path.c_str());
@@ -139,19 +160,22 @@ void FileAccessWindows::close() {
//atomic replace for existing file
rename_error = !ReplaceFileW(save_path.c_str(), (save_path + ".tmp").c_str(), NULL, 2 | 4, NULL, NULL);
}
- if (rename_error && close_fail_notify) {
- close_fail_notify(save_path);
- }
if (rename_error) {
attempts--;
- OS::get_singleton()->delay_usec(1000000); //wait 100msec and try again
+ OS::get_singleton()->delay_usec(100000); // wait 100msec and try again
}
}
- save_path = "";
if (rename_error) {
+ if (close_fail_notify) {
+ close_fail_notify(save_path);
+ }
+
ERR_EXPLAIN("Safe save failed. This may be a permissions problem, but also may happen because you are running a paranoid antivirus. If this is the case, please switch to Windows Defender or disable the 'safe save' option in editor settings. This makes it work, but increases the risk of file corruption in a crash.");
}
+
+ save_path = "";
+
ERR_FAIL_COND(rename_error);
}
}
diff --git a/drivers/windows/stream_peer_tcp_winsock.cpp b/drivers/windows/stream_peer_tcp_winsock.cpp
index 55775fc23..cb501ce35 100644
--- a/drivers/windows/stream_peer_tcp_winsock.cpp
+++ b/drivers/windows/stream_peer_tcp_winsock.cpp
@@ -335,7 +335,9 @@ Error StreamPeerTCPWinsock::connect_to_host(const IP_Address &p_host, uint16_t p
void StreamPeerTCPWinsock::set_no_delay(bool p_enabled) {
ERR_FAIL_COND(!is_connected_to_host());
int flag = p_enabled ? 1 : 0;
- setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int));
+ if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) != 0) {
+ ERR_PRINT("Unable to set TCP no delay option");
+ }
}
int StreamPeerTCPWinsock::get_available_bytes() const {
diff --git a/drivers/windows/tcp_server_winsock.cpp b/drivers/windows/tcp_server_winsock.cpp
index 413a0d19b..ddb955549 100644
--- a/drivers/windows/tcp_server_winsock.cpp
+++ b/drivers/windows/tcp_server_winsock.cpp
@@ -105,6 +105,7 @@ Error TCPServerWinsock::listen(uint16_t p_port, const IP_Address &p_bind_address
ERR_FAIL_V(FAILED);
};
} else {
+ closesocket(sockfd);
return ERR_ALREADY_IN_USE;
};
diff --git a/editor/animation_editor.cpp b/editor/animation_editor.cpp
index 439ec37e7..f7c8cac93 100644
--- a/editor/animation_editor.cpp
+++ b/editor/animation_editor.cpp
@@ -1076,6 +1076,9 @@ void AnimationKeyEditor::_track_editor_draw() {
if (!animation.is_valid()) {
v_scroll->hide();
h_scroll->hide();
+ length->set_editable(false);
+ step->set_editable(false);
+ loop->set_disabled(true);
menu_add_track->set_disabled(true);
menu_track->set_disabled(true);
edit_button->set_disabled(true);
@@ -1087,6 +1090,9 @@ void AnimationKeyEditor::_track_editor_draw() {
return;
}
+ length->set_editable(true);
+ step->set_editable(true);
+ loop->set_disabled(false);
menu_add_track->set_disabled(false);
menu_track->set_disabled(false);
edit_button->set_disabled(false);
@@ -3130,7 +3136,6 @@ void AnimationKeyEditor::set_animation(const Ref<Animation> &p_anim) {
timeline_pos = 0;
_clear_selection();
- _update_paths();
_update_menu();
selected_track = -1;
@@ -3857,6 +3862,7 @@ AnimationKeyEditor::AnimationKeyEditor() {
length->set_h_size_flags(SIZE_EXPAND_FILL);
length->set_stretch_ratio(1);
length->set_tooltip(TTR("Animation length (in seconds)."));
+ length->set_editable(false);
hb->add_child(length);
length->connect("value_changed", this, "_animation_len_changed");
@@ -3873,6 +3879,7 @@ AnimationKeyEditor::AnimationKeyEditor() {
step->set_h_size_flags(SIZE_EXPAND_FILL);
step->set_stretch_ratio(1);
step->set_tooltip(TTR("Cursor step snap (in seconds)."));
+ step->set_editable(false);
hb->add_child(step);
step->connect("value_changed", this, "_step_changed");
@@ -3882,6 +3889,7 @@ AnimationKeyEditor::AnimationKeyEditor() {
loop->connect("pressed", this, "_animation_loop_changed");
hb->add_child(loop);
loop->set_tooltip(TTR("Enable/Disable looping in animation."));
+ loop->set_disabled(true);
hb->add_child(memnew(VSeparator));
@@ -3919,7 +3927,7 @@ AnimationKeyEditor::AnimationKeyEditor() {
menu_track = memnew(MenuButton);
hb->add_child(menu_track);
menu_track->get_popup()->connect("id_pressed", this, "_menu_track");
- menu_track->set_tooltip(TTR("Track tools"));
+ menu_track->set_tooltip(TTR("Track Tools"));
edit_button = memnew(ToolButton);
edit_button->set_toggle_mode(true);
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 9ae9ab150..24e86770b 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -212,35 +212,39 @@ void FindReplaceBar::_replace_all() {
text_edit->begin_complex_operation();
- while (search_next()) {
+ if (search_current()) {
+ do {
+ // replace area
+ Point2i match_from(result_line, result_col);
+ Point2i match_to(result_line, result_col + search_text_len);
- // replace area
- Point2i match_from(result_line, result_col);
- Point2i match_to(result_line, result_col + search_text_len);
-
- if (match_from < prev_match)
- break; // done
+ if (match_from < prev_match) {
+ break; // done
+ }
- prev_match = Point2i(result_line, result_col + replace_text.length());
+ prev_match = Point2i(result_line, result_col + replace_text.length());
- text_edit->unfold_line(result_line);
- text_edit->select(result_line, result_col, result_line, match_to.y);
+ text_edit->unfold_line(result_line);
+ text_edit->select(result_line, result_col, result_line, match_to.y);
- if (selection_enabled && is_selection_only()) {
+ if (selection_enabled && is_selection_only()) {
+ if (match_from < selection_begin || match_to > selection_end) {
+ continue;
+ }
- if (match_from < selection_begin || match_to > selection_end)
- continue;
+ // replace but adjust selection bounds
+ text_edit->insert_text_at_cursor(replace_text);
+ if (match_to.x == selection_end.x) {
+ selection_end.y += replace_text.length() - search_text_len;
+ }
- // replace but adjust selection bounds
- text_edit->insert_text_at_cursor(replace_text);
- if (match_to.x == selection_end.x)
- selection_end.y += replace_text.length() - search_text_len;
- } else {
- // just replace
- text_edit->insert_text_at_cursor(replace_text);
- }
+ } else {
+ // just replace
+ text_edit->insert_text_at_cursor(replace_text);
+ }
- rc++;
+ rc++;
+ } while (search_next());
}
text_edit->end_complex_operation();
@@ -646,12 +650,12 @@ void CodeTextEditor::_text_editor_gui_input(const Ref<InputEvent> &p_event) {
}
void CodeTextEditor::_zoom_in() {
- font_resize_val += EDSCALE;
+ font_resize_val += MAX(EDSCALE, 1.0f);
_zoom_changed();
}
void CodeTextEditor::_zoom_out() {
- font_resize_val -= EDSCALE;
+ font_resize_val -= MAX(EDSCALE, 1.0f);
_zoom_changed();
}
@@ -673,7 +677,20 @@ void CodeTextEditor::_reset_zoom() {
void CodeTextEditor::_line_col_changed() {
line_nb->set_text(itos(text_editor->cursor_get_line() + 1));
- col_nb->set_text(itos(text_editor->cursor_get_column() + 1));
+
+ String line = text_editor->get_line(text_editor->cursor_get_line());
+
+ int positional_column = 0;
+
+ for (int i = 0; i < text_editor->cursor_get_column(); i++) {
+ if (line[i] == '\t') {
+ positional_column += text_editor->get_indent_size(); //tab size
+ } else {
+ positional_column += 1;
+ }
+ }
+
+ col_nb->set_text(itos(positional_column + 1));
}
void CodeTextEditor::_text_changed() {
diff --git a/editor/collada/collada.cpp b/editor/collada/collada.cpp
index 4ce57d7f6..734229d01 100644
--- a/editor/collada/collada.cpp
+++ b/editor/collada/collada.cpp
@@ -2266,10 +2266,8 @@ void Collada::_merge_skeletons2(VisualScene *p_vscene) {
}
node = node->parent;
}
- ERR_CONTINUE(!sk);
- if (!sk)
- continue; //bleh
+ ERR_CONTINUE(!sk);
if (!skeleton) {
skeleton = sk;
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index 3466d1798..7f9391774 100644
--- a/editor/connections_dialog.cpp
+++ b/editor/connections_dialog.cpp
@@ -35,6 +35,7 @@
#include "plugins/script_editor_plugin.h"
#include "print_string.h"
#include "scene/gui/label.h"
+#include "scene/gui/popup_menu.h"
class ConnectDialogBinds : public Object {
@@ -87,51 +88,12 @@ public:
}
};
-void ConnectDialog::_notification(int p_what) {
-
- if (p_what == NOTIFICATION_ENTER_TREE) {
- bind_editor->edit(cdbinds);
- }
-}
-
-void ConnectDialog::_tree_node_selected() {
-
- //dst_method_list->get_popup()->clear();
- Node *current = tree->get_selected();
-
- if (!current) {
- make_callback->hide();
- return;
- }
-
- if (current->get_script().is_null())
- make_callback->hide();
- else
- make_callback->show();
-
- dst_path->set_text(node->get_path_to(current));
-}
-
-void ConnectDialog::edit(Node *p_node) {
-
- node = p_node;
-
- //dst_method_list->get_popup()->clear();
-
- tree->set_selected(NULL);
- tree->set_marked(node, true);
- dst_path->set_text("");
- dst_method->set_text("");
- deferred->set_pressed(false);
- oneshot->set_pressed(false);
- cdbinds->params.clear();
- cdbinds->notify_changed();
-}
-
+/*
+Signal automatically called by parent dialog.
+*/
void ConnectDialog::ok_pressed() {
if (dst_method->get_text() == "") {
-
error->set_text(TTR("Method in target Node must be specified!"));
error->popup_centered_minsize();
return;
@@ -147,39 +109,35 @@ void ConnectDialog::ok_pressed() {
emit_signal("connected");
hide();
}
+
void ConnectDialog::_cancel_pressed() {
hide();
}
-NodePath ConnectDialog::get_dst_path() const {
-
- return dst_path->get_text();
-}
-
-bool ConnectDialog::get_deferred() const {
-
- return deferred->is_pressed();
-}
-
-bool ConnectDialog::get_oneshot() const {
-
- return oneshot->is_pressed();
-}
+/*
+Called each time a target node is selected within the target node tree.
+*/
+void ConnectDialog::_tree_node_selected() {
-StringName ConnectDialog::get_dst_method() const {
+ Node *current = tree->get_selected();
- String txt = dst_method->get_text();
- if (txt.find("(") != -1)
- txt = txt.left(txt.find("(")).strip_edges();
- return txt;
-}
+ if (!current) {
+ make_callback->hide();
+ return;
+ }
-Vector<Variant> ConnectDialog::get_binds() const {
+ if (current->get_script().is_null())
+ make_callback->hide();
+ else
+ make_callback->show();
- return cdbinds->params;
+ dst_path->set_text(source->get_path_to(current));
}
+/*
+Adds a new parameter bind to connection.
+*/
void ConnectDialog::_add_bind() {
if (cdbinds->params.size() >= VARIANT_ARG_MAX)
@@ -189,7 +147,6 @@ void ConnectDialog::_add_bind() {
Variant value;
switch (vt) {
-
case Variant::BOOL: value = false; break;
case Variant::INT: value = 0; break;
case Variant::REAL: value = 0.0; break;
@@ -203,7 +160,6 @@ void ConnectDialog::_add_bind() {
case Variant::BASIS: value = Basis(); break;
case Variant::TRANSFORM: value = Transform(); break;
case Variant::COLOR: value = Color(); break;
-
default: { ERR_FAIL(); } break;
}
@@ -213,6 +169,9 @@ void ConnectDialog::_add_bind() {
cdbinds->notify_changed();
}
+/*
+Remove parameter bind from connection.
+*/
void ConnectDialog::_remove_bind() {
String st = bind_editor->get_selected_path();
@@ -225,25 +184,106 @@ void ConnectDialog::_remove_bind() {
cdbinds->notify_changed();
}
+void ConnectDialog::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+ bind_editor->edit(cdbinds);
+ }
+}
+
+void ConnectDialog::_bind_methods() {
+
+ ClassDB::bind_method("_cancel", &ConnectDialog::_cancel_pressed);
+ ClassDB::bind_method("_tree_node_selected", &ConnectDialog::_tree_node_selected);
+ ClassDB::bind_method("_add_bind", &ConnectDialog::_add_bind);
+ ClassDB::bind_method("_remove_bind", &ConnectDialog::_remove_bind);
+
+ ADD_SIGNAL(MethodInfo("connected"));
+}
+
+Node *ConnectDialog::get_source() const {
+
+ return source;
+}
+
+StringName ConnectDialog::get_signal_name() const {
+
+ return signal;
+}
+
+NodePath ConnectDialog::get_dst_path() const {
+
+ return dst_path->get_text();
+}
+
void ConnectDialog::set_dst_node(Node *p_node) {
tree->set_selected(p_node);
}
+StringName ConnectDialog::get_dst_method_name() const {
+
+ String txt = dst_method->get_text();
+ if (txt.find("(") != -1)
+ txt = txt.left(txt.find("(")).strip_edges();
+ return txt;
+}
+
void ConnectDialog::set_dst_method(const StringName &p_method) {
dst_method->set_text(p_method);
}
-void ConnectDialog::_bind_methods() {
+Vector<Variant> ConnectDialog::get_binds() const {
- ClassDB::bind_method("_cancel", &ConnectDialog::_cancel_pressed);
- ClassDB::bind_method("_tree_node_selected", &ConnectDialog::_tree_node_selected);
+ return cdbinds->params;
+}
- ClassDB::bind_method("_add_bind", &ConnectDialog::_add_bind);
- ClassDB::bind_method("_remove_bind", &ConnectDialog::_remove_bind);
+bool ConnectDialog::get_deferred() const {
- ADD_SIGNAL(MethodInfo("connected"));
+ return deferred->is_pressed();
+}
+
+bool ConnectDialog::get_oneshot() const {
+
+ return oneshot->is_pressed();
+}
+
+/*
+Returns true if ConnectDialog is being used to edit an existing connection.
+*/
+bool ConnectDialog::is_editing() const {
+
+ return bEditMode;
+}
+
+/*
+Initialize ConnectDialog and populate fields with expected data.
+If creating a connection from scratch, sensible defaults are used.
+If editing an existing connection, previous data is retained.
+*/
+void ConnectDialog::init(Connection c, bool bEdit) {
+
+ source = static_cast<Node *>(c.source);
+ signal = c.signal;
+
+ tree->set_selected(NULL);
+ tree->set_marked(source, true);
+
+ set_dst_node(static_cast<Node *>(c.target));
+ set_dst_method(c.method);
+
+ bool bDeferred = (c.flags & CONNECT_DEFERRED) == CONNECT_DEFERRED;
+ bool bOneshot = (c.flags & CONNECT_ONESHOT) == CONNECT_ONESHOT;
+
+ deferred->set_pressed(bDeferred);
+ oneshot->set_pressed(bOneshot);
+
+ cdbinds->params.clear();
+ cdbinds->params = c.binds;
+ cdbinds->notify_changed();
+
+ bEditMode = bEdit;
}
ConnectDialog::ConnectDialog() {
@@ -261,6 +301,8 @@ ConnectDialog::ConnectDialog() {
tree = memnew(SceneTreeEditor(false));
tree->get_scene_tree()->connect("item_activated", this, "_ok");
+ tree->connect("node_selected", this, "_tree_node_selected");
+
vbc_left->add_margin_child(TTR("Connect To Node:"), tree, true);
VBoxContainer *vbc_right = memnew(VBoxContainer);
@@ -272,12 +314,10 @@ ConnectDialog::ConnectDialog() {
type_list = memnew(OptionButton);
type_list->set_h_size_flags(SIZE_EXPAND_FILL);
add_bind_hb->add_child(type_list);
-
type_list->add_item("bool", Variant::BOOL);
type_list->add_item("int", Variant::INT);
type_list->add_item("real", Variant::REAL);
type_list->add_item("string", Variant::STRING);
- //type_list->add_separator();
type_list->add_item("Vector2", Variant::VECTOR2);
type_list->add_item("Rect2", Variant::RECT2);
type_list->add_item("Vector3", Variant::VECTOR3);
@@ -286,12 +326,10 @@ ConnectDialog::ConnectDialog() {
type_list->add_item("AABB", Variant::AABB);
type_list->add_item("Basis", Variant::BASIS);
type_list->add_item("Transform", Variant::TRANSFORM);
- //type_list->add_separator();
type_list->add_item("Color", Variant::COLOR);
type_list->select(0);
Button *add_bind = memnew(Button);
-
add_bind->set_text(TTR("Add"));
add_bind_hb->add_child(add_bind);
add_bind->connect("pressed", this, "_add_bind");
@@ -318,8 +356,9 @@ ConnectDialog::ConnectDialog() {
dst_method->set_h_size_flags(SIZE_EXPAND_FILL);
dstm_hb->add_child(dst_method);
- /*dst_method_list = memnew( MenuButton );
- dst_method_list->set_text("List..");
+ /*
+ dst_method_list = memnew( MenuButton );
+ dst_method_list->set_text("List...");
dst_method_list->set_anchor( MARGIN_RIGHT, ANCHOR_END );
dst_method_list->set_anchor( MARGIN_LEFT, ANCHOR_END );
dst_method_list->set_anchor( MARGIN_TOP, ANCHOR_END );
@@ -327,7 +366,6 @@ ConnectDialog::ConnectDialog() {
dst_method_list->set_begin( Point2( 70,59) );
dst_method_list->set_end( Point2( 15,39 ) );
*/
- //add_child(dst_method_list);
make_callback = memnew(CheckButton);
make_callback->set_toggle_mode(true);
@@ -343,8 +381,6 @@ ConnectDialog::ConnectDialog() {
oneshot->set_text(TTR("Oneshot"));
dstm_hb->add_child(oneshot);
- tree->connect("node_selected", this, "_tree_node_selected");
-
set_as_toplevel(true);
cdbinds = memnew(ConnectDialogBinds);
@@ -356,134 +392,365 @@ ConnectDialog::ConnectDialog() {
}
ConnectDialog::~ConnectDialog() {
+
memdelete(cdbinds);
}
-void ConnectionsDock::_notification(int p_what) {
-
- if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
- update_tree();
- }
-}
+//ConnectionsDock ==========================
-void ConnectionsDock::_close() {
+struct _ConnectionsDockMethodInfoSort {
- hide();
-}
+ _FORCE_INLINE_ bool operator()(const MethodInfo &a, const MethodInfo &b) const {
+ return a.name < b.name;
+ }
+};
-void ConnectionsDock::_connect() {
+/*
+Post-ConnectDialog callback for creating/editing connections.
+Creates or edits connections based on state of the ConnectDialog when "Connect" is pressed.
+*/
+void ConnectionsDock::_make_or_edit_connection() {
TreeItem *it = tree->get_selected();
ERR_FAIL_COND(!it);
- String signal = it->get_metadata(0).operator Dictionary()["name"];
NodePath dst_path = connect_dialog->get_dst_path();
- Node *target = node->get_node(dst_path);
+ Node *target = selectedNode->get_node(dst_path);
ERR_FAIL_COND(!target);
- StringName dst_method = connect_dialog->get_dst_method();
+ Connection cToMake;
+ cToMake.source = connect_dialog->get_source();
+ cToMake.target = target;
+ cToMake.signal = connect_dialog->get_signal_name();
+ cToMake.method = connect_dialog->get_dst_method_name();
+ cToMake.binds = connect_dialog->get_binds();
bool defer = connect_dialog->get_deferred();
bool oshot = connect_dialog->get_oneshot();
- Vector<Variant> binds = connect_dialog->get_binds();
- PoolStringArray args = it->get_metadata(0).operator Dictionary()["args"];
- int flags = CONNECT_PERSIST | (defer ? CONNECT_DEFERRED : 0) | (oshot ? CONNECT_ONESHOT : 0);
+ cToMake.flags = CONNECT_PERSIST | (defer ? CONNECT_DEFERRED : 0) | (oshot ? CONNECT_ONESHOT : 0);
+
+ if (connect_dialog->is_editing()) {
+ _disconnect(*it);
+ _connect(cToMake);
+ } else {
+ _connect(cToMake);
+ }
+
+ if (connect_dialog->get_make_callback()) {
+ PoolStringArray args = it->get_metadata(0).operator Dictionary()["args"];
+ editor->emit_signal("script_add_function_request", target, cToMake.method, args);
+ hide();
+ }
+
+ update_tree();
+}
- undo_redo->create_action(vformat(TTR("Connect '%s' to '%s'"), signal, String(dst_method)));
- undo_redo->add_do_method(node, "connect", signal, target, dst_method, binds, flags);
- undo_redo->add_undo_method(node, "disconnect", signal, target, dst_method);
+/*
+Creates single connection w/ undo-redo functionality.
+*/
+void ConnectionsDock::_connect(Connection cToMake) {
+
+ Node *source = static_cast<Node *>(cToMake.source);
+ Node *target = static_cast<Node *>(cToMake.target);
+
+ if (!source || !target)
+ return;
+
+ undo_redo->create_action(vformat(TTR("Connect '%s' to '%s'"), String(cToMake.signal), String(cToMake.method)));
+
+ undo_redo->add_do_method(source, "connect", cToMake.signal, target, cToMake.method, cToMake.binds, cToMake.flags);
+ undo_redo->add_undo_method(source, "disconnect", cToMake.signal, target, cToMake.method);
undo_redo->add_do_method(this, "update_tree");
undo_redo->add_undo_method(this, "update_tree");
undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); //to force redraw of scene tree
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); //to force redraw of scene tree
+ undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
undo_redo->commit_action();
+}
- if (connect_dialog->get_make_callback()) {
+/*
+Break single connection w/ undo-redo functionality.
+*/
+void ConnectionsDock::_disconnect(TreeItem &item) {
- print_line("request connect");
- editor->emit_signal("script_add_function_request", target, dst_method, args);
- hide();
+ Connection c = item.get_metadata(0);
+ ERR_FAIL_COND(c.source != selectedNode); //shouldn't happen but...bugcheck
+
+ undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), c.signal, c.method));
+
+ undo_redo->add_do_method(selectedNode, "disconnect", c.signal, c.target, c.method);
+ undo_redo->add_undo_method(selectedNode, "connect", c.signal, c.target, c.method, c.binds, c.flags);
+ undo_redo->add_do_method(this, "update_tree");
+ undo_redo->add_undo_method(this, "update_tree");
+ undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); //to force redraw of scene tree
+ undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+
+ undo_redo->commit_action();
+}
+
+/*
+Break all conections of currently selected signal.
+Can undo-redo as a single action.
+*/
+void ConnectionsDock::_disconnect_all() {
+
+ TreeItem *item = tree->get_selected();
+
+ if (!_is_item_signal(*item))
+ return;
+
+ TreeItem *child = item->get_children();
+ String signalName = item->get_metadata(0).operator Dictionary()["name"];
+ undo_redo->create_action(vformat(TTR("Disconnect all from signal: '%s'"), signalName));
+
+ while (child) {
+ Connection c = child->get_metadata(0);
+ undo_redo->add_do_method(selectedNode, "disconnect", c.signal, c.target, c.method);
+ undo_redo->add_undo_method(selectedNode, "connect", c.signal, c.target, c.method, c.binds, c.flags);
+ child = child->get_next();
}
- update_tree();
+ undo_redo->add_do_method(this, "update_tree");
+ undo_redo->add_undo_method(this, "update_tree");
+ undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+ undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree");
+
+ undo_redo->commit_action();
}
-void ConnectionsDock::_connect_pressed() {
+void ConnectionsDock::_tree_item_selected() {
TreeItem *item = tree->get_selected();
- if (!item) {
- //no idea how this happened, but disable
+ if (!item) { //Unlikely. Disable button just in case.
+ connect_button->set_text(TTR("Connect..."));
connect_button->set_disabled(true);
+ } else if (_is_item_signal(*item)) {
+ connect_button->set_text(TTR("Connect..."));
+ connect_button->set_disabled(false);
+ } else {
+ connect_button->set_text(TTR("Disconnect"));
+ connect_button->set_disabled(false);
+ }
+}
+
+void ConnectionsDock::_tree_item_activated() { //"Activation" on double-click.
+
+ TreeItem *item = tree->get_selected();
+
+ if (!item)
return;
+
+ if (_is_item_signal(*item)) {
+ _open_connection_dialog(*item);
+ } else {
+ _go_to_script(*item);
}
- if (item->get_parent() == tree->get_root() || item->get_parent()->get_parent() == tree->get_root()) {
- //a signal - connect
- String signal = item->get_metadata(0).operator Dictionary()["name"];
- String signalname = signal;
- String midname = node->get_name();
- for (int i = 0; i < midname.length(); i++) {
- CharType c = midname[i];
- if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') {
- //all good
- } else if (c == ' ') {
+}
+
+bool ConnectionsDock::_is_item_signal(TreeItem &item) {
+
+ return (item.get_parent() == tree->get_root() || item.get_parent()->get_parent() == tree->get_root());
+}
+
+/*
+Open connection dialog with TreeItem data to CREATE a brand-new connection.
+*/
+void ConnectionsDock::_open_connection_dialog(TreeItem &item) {
+
+ String signal = item.get_metadata(0).operator Dictionary()["name"];
+ String signalname = signal;
+ String midname = selectedNode->get_name();
+ for (int i = 0; i < midname.length(); i++) { //TODO: Regex filter may be cleaner.
+ CharType c = midname[i];
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_')) {
+ if (c == ' ') {
+ //Replace spaces with underlines.
c = '_';
} else {
+ //Remove any other characters.
midname.remove(i);
i--;
continue;
}
-
- midname[i] = c;
}
+ midname[i] = c;
+ }
+
+ Node *dst_node = selectedNode->get_owner() ? selectedNode->get_owner() : selectedNode;
+ StringName dst_method = "_on_" + midname + "_" + signal;
- connect_dialog->edit(node);
+ Connection c;
+ c.source = selectedNode;
+ c.signal = StringName(signalname);
+ c.target = dst_node;
+ c.method = dst_method;
+
+ connect_dialog->init(c);
+ connect_dialog->set_title(TTR("Connect Signal: ") + signalname);
+ connect_dialog->popup_centered_ratio();
+}
+
+/*
+Open connection dialog with Connection data to EDIT an existing connection.
+*/
+void ConnectionsDock::_open_connection_dialog(Connection cToEdit) {
+
+ Node *src = static_cast<Node *>(cToEdit.source);
+ Node *dst = static_cast<Node *>(cToEdit.target);
+
+ if (src && dst) {
+ connect_dialog->init(cToEdit, true);
+ connect_dialog->set_title(TTR("Edit Connection: ") + cToEdit.signal);
connect_dialog->popup_centered_ratio();
- connect_dialog->set_title(TTR("Connecting Signal:") + " " + signalname);
- connect_dialog->set_dst_method("_on_" + midname + "_" + signal);
- connect_dialog->set_dst_node(node->get_owner() ? node->get_owner() : node);
+ }
+}
+
+/*
+Open slot method location in script editor.
+*/
+void ConnectionsDock::_go_to_script(TreeItem &item) {
+
+ if (_is_item_signal(item))
+ return;
+
+ Connection c = item.get_metadata(0);
+ ERR_FAIL_COND(c.source != selectedNode); //shouldn't happen but...bugcheck
+
+ if (!c.target)
+ return;
+ Ref<Script> script = c.target->get_script();
+
+ if (script.is_null())
+ return;
+
+ if (script.is_valid() && ScriptEditor::get_singleton()->script_goto_method(script, c.method)) {
+ editor->call("_editor_select", EditorNode::EDITOR_SCRIPT);
+ }
+}
+
+void ConnectionsDock::_handle_signal_menu_option(int option) {
+
+ TreeItem *item = tree->get_selected();
+
+ if (!item)
+ return;
+
+ switch (option) {
+ case CONNECT: {
+ _open_connection_dialog(*item);
+ } break;
+ case DISCONNECT_ALL: {
+ StringName signalName = item->get_metadata(0).operator Dictionary()["name"];
+ disconnect_all_dialog->set_text(TTR("Are you sure you want to remove all connections from the \"") + signalName + "\" signal?");
+ disconnect_all_dialog->popup_centered();
+ } break;
+ }
+}
+
+void ConnectionsDock::_handle_slot_menu_option(int option) {
+
+ TreeItem *item = tree->get_selected();
+
+ if (!item)
+ return;
+
+ switch (option) {
+ case EDIT: {
+ Connection c = item->get_metadata(0);
+ _open_connection_dialog(c);
+ } break;
+ case GO_TO_SCRIPT: {
+ _go_to_script(*item);
+ } break;
+ case DISCONNECT: {
+ _disconnect(*item);
+ update_tree();
+ } break;
+ }
+}
+
+void ConnectionsDock::_rmb_pressed(Vector2 position) {
+
+ TreeItem *item = tree->get_selected();
+
+ if (!item)
+ return;
+
+ Vector2 global_position = tree->get_global_position() + position;
+
+ if (_is_item_signal(*item)) {
+ signal_menu->set_position(global_position);
+ signal_menu->popup();
} else {
- //a slot- disconnect
- Connection c = item->get_metadata(0);
- ERR_FAIL_COND(c.source != node); //shouldn't happen but...bugcheck
+ slot_menu->set_position(global_position);
+ slot_menu->popup();
+ }
+}
+
+void ConnectionsDock::_close() {
+
+ hide();
+}
- undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), c.signal, c.method));
- undo_redo->add_do_method(node, "disconnect", c.signal, c.target, c.method);
- undo_redo->add_undo_method(node, "connect", c.signal, c.target, c.method, Vector<Variant>(), c.flags);
- undo_redo->add_do_method(this, "update_tree");
- undo_redo->add_undo_method(this, "update_tree");
- undo_redo->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); //to force redraw of scene tree
- undo_redo->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor(), "update_tree"); //to force redraw of scene tree
- undo_redo->commit_action();
+void ConnectionsDock::_connect_pressed() {
+
+ TreeItem *item = tree->get_selected();
+ if (!item) {
+ connect_button->set_disabled(true);
+ return;
+ }
- c.source->disconnect(c.signal, c.target, c.method);
+ if (_is_item_signal(*item)) {
+ _open_connection_dialog(*item);
+ } else {
+ _disconnect(*item);
update_tree();
}
}
-struct _ConnectionsDockMethodInfoSort {
+void ConnectionsDock::_notification(int p_what) {
- _FORCE_INLINE_ bool operator()(const MethodInfo &a, const MethodInfo &b) const {
- return a.name < b.name;
+ if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
+ update_tree();
}
-};
+}
+
+void ConnectionsDock::_bind_methods() {
+
+ ClassDB::bind_method("_make_or_edit_connection", &ConnectionsDock::_make_or_edit_connection);
+ ClassDB::bind_method("_disconnect_all", &ConnectionsDock::_disconnect_all);
+ ClassDB::bind_method("_tree_item_selected", &ConnectionsDock::_tree_item_selected);
+ ClassDB::bind_method("_tree_item_activated", &ConnectionsDock::_tree_item_activated);
+ ClassDB::bind_method("_handle_signal_menu_option", &ConnectionsDock::_handle_signal_menu_option);
+ ClassDB::bind_method("_handle_slot_menu_option", &ConnectionsDock::_handle_slot_menu_option);
+ ClassDB::bind_method("_rmb_pressed", &ConnectionsDock::_rmb_pressed);
+ ClassDB::bind_method("_close", &ConnectionsDock::_close);
+ ClassDB::bind_method("_connect_pressed", &ConnectionsDock::_connect_pressed);
+ ClassDB::bind_method("update_tree", &ConnectionsDock::update_tree);
+}
+
+void ConnectionsDock::set_node(Node *p_node) {
+
+ selectedNode = p_node;
+ update_tree();
+}
void ConnectionsDock::update_tree() {
tree->clear();
- if (!node)
+ if (!selectedNode)
return;
TreeItem *root = tree->create_item();
List<MethodInfo> node_signals;
- node->get_signal_list(&node_signals);
+ selectedNode->get_signal_list(&node_signals);
//node_signals.sort_custom<_ConnectionsDockMethodInfoSort>();
bool did_script = false;
- StringName base = node->get_class();
+ StringName base = selectedNode->get_class();
while (base) {
@@ -493,7 +760,7 @@ void ConnectionsDock::update_tree() {
if (!did_script) {
- Ref<Script> scr = node->get_script();
+ Ref<Script> scr = selectedNode->get_script();
if (scr.is_valid()) {
scr->get_script_signal_list(&node_signals);
if (scr->get_path().is_resource_file())
@@ -563,7 +830,7 @@ void ConnectionsDock::update_tree() {
item->set_icon(0, get_icon("Signal", "EditorIcons"));
List<Object::Connection> connections;
- node->get_signal_connection_list(mi.name, &connections);
+ selectedNode->get_signal_connection_list(mi.name, &connections);
for (List<Object::Connection>::Element *F = connections.front(); F; F = F->next()) {
@@ -575,7 +842,7 @@ void ConnectionsDock::update_tree() {
if (!target)
continue;
- String path = String(node->get_path_to(target)) + " :: " + c.method + "()";
+ String path = String(selectedNode->get_path_to(target)) + " :: " + c.method + "()";
if (c.flags & CONNECT_DEFERRED)
path += " (deferred)";
if (c.flags & CONNECT_ONESHOT)
@@ -610,88 +877,6 @@ void ConnectionsDock::update_tree() {
connect_button->set_disabled(true);
}
-void ConnectionsDock::set_node(Node *p_node) {
-
- node = p_node;
- update_tree();
-}
-
-void ConnectionsDock::_something_selected() {
-
- TreeItem *item = tree->get_selected();
- if (!item) {
- //no idea how this happened, but disable
- connect_button->set_text(TTR("Connect.."));
- connect_button->set_disabled(true);
-
- } else if (item->get_parent() == tree->get_root() || item->get_parent()->get_parent() == tree->get_root()) {
- //a signal - connect
- connect_button->set_text(TTR("Connect.."));
- connect_button->set_disabled(false);
-
- } else {
- //a slot- disconnect
- connect_button->set_text(TTR("Disconnect"));
- connect_button->set_disabled(false);
- }
-}
-
-void ConnectionsDock::_something_activated() {
-
- TreeItem *item = tree->get_selected();
-
- if (!item)
- return;
-
- if (item->get_parent() == tree->get_root() || item->get_parent()->get_parent() == tree->get_root()) {
- // a signal - connect
- String signal = item->get_metadata(0).operator Dictionary()["name"];
- String midname = node->get_name();
- for (int i = 0; i < midname.length(); i++) {
- CharType c = midname[i];
- if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_') {
- //all good
- } else if (c == ' ') {
- c = '_';
- } else {
- midname.remove(i);
- i--;
- continue;
- }
-
- midname[i] = c;
- }
-
- connect_dialog->edit(node);
- connect_dialog->popup_centered_ratio();
- connect_dialog->set_dst_method("_on_" + midname + "_" + signal);
- connect_dialog->set_dst_node(node->get_owner() ? node->get_owner() : node);
- } else {
- // a slot - go to target method
- Connection c = item->get_metadata(0);
- ERR_FAIL_COND(c.source != node); //shouldn't happen but...bugcheck
-
- if (!c.target)
- return;
-
- Ref<Script> script = c.target->get_script();
-
- if (script.is_valid() && ScriptEditor::get_singleton()->script_goto_method(script, c.method)) {
- editor->call("_editor_select", EditorNode::EDITOR_SCRIPT);
- }
- }
-}
-
-void ConnectionsDock::_bind_methods() {
-
- ClassDB::bind_method("_connect", &ConnectionsDock::_connect);
- ClassDB::bind_method("_something_selected", &ConnectionsDock::_something_selected);
- ClassDB::bind_method("_something_activated", &ConnectionsDock::_something_activated);
- ClassDB::bind_method("_close", &ConnectionsDock::_close);
- ClassDB::bind_method("_connect_pressed", &ConnectionsDock::_connect_pressed);
- ClassDB::bind_method("update_tree", &ConnectionsDock::update_tree);
-}
-
ConnectionsDock::ConnectionsDock(EditorNode *p_editor) {
editor = p_editor;
@@ -705,6 +890,7 @@ ConnectionsDock::ConnectionsDock(EditorNode *p_editor) {
tree->set_hide_root(true);
vbc->add_child(tree);
tree->set_v_size_flags(SIZE_EXPAND_FILL);
+ tree->set_allow_rmb_select(true);
connect_button = memnew(Button);
connect_button->set_text(TTR("Connect"));
@@ -713,15 +899,29 @@ ConnectionsDock::ConnectionsDock(EditorNode *p_editor) {
hb->add_spacer();
hb->add_child(connect_button);
connect_button->connect("pressed", this, "_connect_pressed");
- //add_child(tree);
connect_dialog = memnew(ConnectDialog);
connect_dialog->set_as_toplevel(true);
add_child(connect_dialog);
- remove_confirm = memnew(ConfirmationDialog);
- remove_confirm->set_as_toplevel(true);
- add_child(remove_confirm);
+ disconnect_all_dialog = memnew(ConfirmationDialog);
+ disconnect_all_dialog->set_as_toplevel(true);
+ add_child(disconnect_all_dialog);
+ disconnect_all_dialog->connect("confirmed", this, "_disconnect_all");
+ disconnect_all_dialog->set_text(TTR("Are you sure you want to remove all connections from this signal?"));
+
+ signal_menu = memnew(PopupMenu);
+ add_child(signal_menu);
+ signal_menu->connect("id_pressed", this, "_handle_signal_menu_option");
+ signal_menu->add_item(TTR("Connect..."), CONNECT);
+ signal_menu->add_item(TTR("Disconnect All"), DISCONNECT_ALL);
+
+ slot_menu = memnew(PopupMenu);
+ add_child(slot_menu);
+ slot_menu->connect("id_pressed", this, "_handle_slot_menu_option");
+ slot_menu->add_item(TTR("Edit..."), EDIT);
+ slot_menu->add_item(TTR("Go To Method"), GO_TO_SCRIPT);
+ slot_menu->add_item(TTR("Disconnect"), DISCONNECT);
/*
node_only->set_anchor( MARGIN_TOP, ANCHOR_END );
@@ -732,10 +932,10 @@ ConnectionsDock::ConnectionsDock(EditorNode *p_editor) {
node_only->set_end( Point2( 10,44) );
*/
- remove_confirm->connect("confirmed", this, "_remove_confirm");
- connect_dialog->connect("connected", this, "_connect");
- tree->connect("item_selected", this, "_something_selected");
- tree->connect("item_activated", this, "_something_activated");
+ connect_dialog->connect("connected", this, "_make_or_edit_connection");
+ tree->connect("item_selected", this, "_tree_item_selected");
+ tree->connect("item_activated", this, "_tree_item_activated");
+ tree->connect("item_rmb_selected", this, "_rmb_pressed");
add_constant_override("separation", 3 * EDSCALE);
}
diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h
index 5e26e00f6..932ff693e 100644
--- a/editor/connections_dialog.h
+++ b/editor/connections_dialog.h
@@ -28,6 +28,10 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+/**
+@author Juan Linietsky <reduzio@gmail.com>
+*/
+
#ifndef CONNECTIONS_DIALOG_H
#define CONNECTIONS_DIALOG_H
@@ -42,27 +46,28 @@
#include "scene/gui/tree.h"
#include "undo_redo.h"
-/**
-@author Juan Linietsky <reduzio@gmail.com>
-*/
-
+class PopupMenu;
class ConnectDialogBinds;
class ConnectDialog : public ConfirmationDialog {
GDCLASS(ConnectDialog, ConfirmationDialog);
- ConfirmationDialog *error;
+ Node *source;
+ StringName signal;
LineEdit *dst_path;
LineEdit *dst_method;
+ ConnectDialogBinds *cdbinds;
+ bool bEditMode;
+
SceneTreeEditor *tree;
+ ConfirmationDialog *error;
+ PropertyEditor *bind_editor;
OptionButton *type_list;
CheckButton *deferred;
CheckButton *oneshot;
CheckButton *make_callback;
- PropertyEditor *bind_editor;
- Node *node;
- ConnectDialogBinds *cdbinds;
+
void ok_pressed();
void _cancel_pressed();
void _tree_node_selected();
@@ -74,37 +79,71 @@ protected:
static void _bind_methods();
public:
- bool get_make_callback() { return make_callback->is_visible() && make_callback->is_pressed(); }
+ Node *get_source() const;
+ StringName get_signal_name() const;
NodePath get_dst_path() const;
- StringName get_dst_method() const;
+ void set_dst_node(Node *p_node);
+ StringName get_dst_method_name() const;
+ void set_dst_method(const StringName &p_method);
+ Vector<Variant> get_binds() const;
+
+ bool get_make_callback() { return make_callback->is_visible() && make_callback->is_pressed(); }
bool get_deferred() const;
bool get_oneshot() const;
- Vector<Variant> get_binds() const;
- void set_dst_method(const StringName &p_method);
- void set_dst_node(Node *p_node);
+ bool is_editing() const;
- void edit(Node *p_node);
+ void init(Connection c, bool bEdit = false);
ConnectDialog();
~ConnectDialog();
};
+//========================================
+
class ConnectionsDock : public VBoxContainer {
GDCLASS(ConnectionsDock, VBoxContainer);
- Button *connect_button;
- EditorNode *editor;
- Node *node;
+ //Right-click Pop-up Menu Options.
+ enum SignalMenuOption {
+ CONNECT,
+ DISCONNECT_ALL
+ };
+
+ enum SlotMenuOption {
+ EDIT,
+ GO_TO_SCRIPT,
+ DISCONNECT
+ };
+
+ Node *selectedNode;
Tree *tree;
- ConfirmationDialog *remove_confirm;
+ EditorNode *editor;
+
+ ConfirmationDialog *disconnect_all_dialog;
ConnectDialog *connect_dialog;
+ Button *connect_button;
+ PopupMenu *signal_menu;
+ PopupMenu *slot_menu;
+ UndoRedo *undo_redo;
+
+ void _make_or_edit_connection();
+ void _connect(Connection cToMake);
+ void _disconnect(TreeItem &item);
+ void _disconnect_all();
+ void _tree_item_selected();
+ void _tree_item_activated();
+ bool _is_item_signal(TreeItem &item);
+
+ void _open_connection_dialog(TreeItem &item);
+ void _open_connection_dialog(Connection cToEdit);
+ void _go_to_script(TreeItem &item);
+
+ void _handle_signal_menu_option(int option);
+ void _handle_slot_menu_option(int option);
+ void _rmb_pressed(Vector2 position);
void _close();
- void _connect();
- void _something_selected();
- void _something_activated();
- UndoRedo *undo_redo;
protected:
void _connect_pressed();
@@ -113,9 +152,7 @@ protected:
public:
void set_undoredo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }
-
void set_node(Node *p_node);
- String get_selected_type();
void update_tree();
ConnectionsDock(EditorNode *p_editor = NULL);
diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp
index 13ef6e9e0..36978e37a 100644
--- a/editor/create_dialog.cpp
+++ b/editor/create_dialog.cpp
@@ -87,7 +87,14 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode) {
if (EditorSettings::get_singleton()->has_setting("interface/dialogs/create_new_node_bounds")) {
popup(EditorSettings::get_singleton()->get("interface/dialogs/create_new_node_bounds"));
} else {
- popup_centered_ratio();
+
+ Size2 popup_size = Size2(900, 700) * editor_get_scale();
+ Size2 window_size = get_viewport_rect().size;
+
+ popup_size.x = MIN(window_size.x * 0.8, popup_size.x);
+ popup_size.y = MIN(window_size.y * 0.8, popup_size.y);
+
+ popup_centered(popup_size);
}
if (p_dont_clear) {
@@ -433,26 +440,7 @@ Object *CreateDialog::instance_selected() {
custom = md;
if (custom != String()) {
- if (EditorNode::get_editor_data().get_custom_types().has(custom)) {
-
- for (int i = 0; i < EditorNode::get_editor_data().get_custom_types()[custom].size(); i++) {
- if (EditorNode::get_editor_data().get_custom_types()[custom][i].name == selected->get_text(0)) {
- Ref<Texture> icon = EditorNode::get_editor_data().get_custom_types()[custom][i].icon;
- Ref<Script> script = EditorNode::get_editor_data().get_custom_types()[custom][i].script;
- String name = selected->get_text(0);
-
- Object *ob = ClassDB::instance(custom);
- ERR_FAIL_COND_V(!ob, NULL);
- if (ob->is_class("Node")) {
- ob->call("set_name", name);
- }
- ob->set_script(script.get_ref_ptr());
- if (icon.is_valid())
- ob->set_meta("_editor_icon", icon);
- return ob;
- }
- }
- }
+ return EditorNode::get_editor_data().instance_custom_type(selected->get_text(0), custom);
} else {
return ClassDB::instance(selected->get_text(0));
}
diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp
index 19fd297f6..953d78732 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -472,17 +472,18 @@ void DependencyRemoveDialog::_build_removed_dependency_tree(const Vector<Removed
void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector<String> &p_files) {
all_remove_files.clear();
- to_delete.clear();
+ dirs_to_delete.clear();
+ files_to_delete.clear();
owners->clear();
for (int i = 0; i < p_folders.size(); ++i) {
String folder = p_folders[i].ends_with("/") ? p_folders[i] : (p_folders[i] + "/");
_find_files_in_removed_folder(EditorFileSystem::get_singleton()->get_filesystem_path(folder), folder);
- to_delete.push_back(folder);
+ dirs_to_delete.push_back(folder);
}
for (int i = 0; i < p_files.size(); ++i) {
all_remove_files[p_files[i]] = String();
- to_delete.push_back(p_files[i]);
+ files_to_delete.push_back(p_files[i]);
}
Vector<RemovedDependency> removed_deps;
@@ -502,29 +503,49 @@ void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector<
}
void DependencyRemoveDialog::ok_pressed() {
- bool files_only = true;
- for (int i = 0; i < to_delete.size(); ++i) {
- if (to_delete[i].ends_with("/")) {
- files_only = false;
- } else if (ResourceCache::has(to_delete[i])) {
- Resource *res = ResourceCache::get(to_delete[i]);
- res->set_path(""); //clear reference to path
+
+ if (dirs_to_delete.size() == 0) {
+ //If we only deleted files we should only need to tell the file system about the files we touched.
+ for (int i = 0; i < files_to_delete.size(); ++i)
+ EditorFileSystem::get_singleton()->update_file(files_to_delete[i]);
+ } else {
+
+ for (int i = 0; i < files_to_delete.size(); ++i) {
+ if (ResourceCache::has(files_to_delete[i])) {
+ Resource *res = ResourceCache::get(files_to_delete[i]);
+ res->set_path("");
+ }
+ String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/");
+ print_line("Moving to trash: " + path);
+ Error err = OS::get_singleton()->move_to_trash(path);
+ if (err != OK) {
+ EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + files_to_delete[i] + "\n");
+ }
}
- String path = OS::get_singleton()->get_resource_dir() + to_delete[i].replace_first("res://", "/");
- print_line("Moving to trash: " + path);
- Error err = OS::get_singleton()->move_to_trash(path);
- if (err != OK) {
- EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + to_delete[i] + "\n");
+ for (int i = 0; i < dirs_to_delete.size(); ++i) {
+ String path = OS::get_singleton()->get_resource_dir() + dirs_to_delete[i].replace_first("res://", "/");
+ print_line("Moving to trash: " + path);
+ Error err = OS::get_singleton()->move_to_trash(path);
+ if (err != OK) {
+ EditorNode::get_singleton()->add_io_error(TTR("Cannot remove:") + "\n" + dirs_to_delete[i] + "\n");
+ }
}
- }
- if (files_only) {
- //If we only deleted files we should only need to tell the file system about the files we touched.
- for (int i = 0; i < to_delete.size(); ++i) {
- EditorFileSystem::get_singleton()->update_file(to_delete[i]);
+ // if some dirs would be deleted, favorite dirs need to be updated
+ Vector<String> previous_favorite_dirs = EditorSettings::get_singleton()->get_favorite_dirs();
+ Vector<String> new_favorite_dirs;
+
+ for (int i = 0; i < previous_favorite_dirs.size(); ++i) {
+ if (dirs_to_delete.find(previous_favorite_dirs[i] + "/") < 0) {
+ new_favorite_dirs.push_back(previous_favorite_dirs[i]);
+ }
}
- } else {
+
+ if (new_favorite_dirs.size() < previous_favorite_dirs.size()) {
+ EditorSettings::get_singleton()->set_favorite_dirs(new_favorite_dirs);
+ }
+
EditorFileSystem::get_singleton()->scan_changes();
}
}
diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h
index 16135c352..4f268de74 100644
--- a/editor/dependency_editor.h
+++ b/editor/dependency_editor.h
@@ -102,7 +102,8 @@ class DependencyRemoveDialog : public ConfirmationDialog {
Tree *owners;
Map<String, String> all_remove_files;
- Vector<String> to_delete;
+ Vector<String> dirs_to_delete;
+ Vector<String> files_to_delete;
struct RemovedDependency {
String file;
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index ac4402d50..a08443722 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -1021,7 +1021,7 @@ void EditorAudioBuses::_select_layout() {
void EditorAudioBuses::_save_as_layout() {
file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
- file_dialog->set_title(TTR("Save Audio Bus Layout As.."));
+ file_dialog->set_title(TTR("Save Audio Bus Layout As..."));
file_dialog->set_current_path(edited_path);
file_dialog->popup_centered_ratio();
new_layout = false;
@@ -1030,7 +1030,7 @@ void EditorAudioBuses::_save_as_layout() {
void EditorAudioBuses::_new_layout() {
file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
- file_dialog->set_title(TTR("Location for New Layout.."));
+ file_dialog->set_title(TTR("Location for New Layout..."));
file_dialog->set_current_path(edited_path);
file_dialog->popup_centered_ratio();
new_layout = true;
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 95ed40d88..b584107bc 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -364,6 +364,14 @@ void EditorData::notify_edited_scene_changed() {
}
}
+void EditorData::notify_resource_saved(const Ref<Resource> &p_resource) {
+
+ for (int i = 0; i < editor_plugins.size(); i++) {
+
+ editor_plugins[i]->notify_resource_saved(p_resource);
+ }
+}
+
void EditorData::clear_editor_states() {
for (int i = 0; i < editor_plugins.size(); i++) {
@@ -452,6 +460,31 @@ void EditorData::add_custom_type(const String &p_type, const String &p_inherits,
custom_types[p_inherits].push_back(ct);
}
+Object *EditorData::instance_custom_type(const String &p_type, const String &p_inherits) {
+
+ if (get_custom_types().has(p_inherits)) {
+
+ for (int i = 0; i < get_custom_types()[p_inherits].size(); i++) {
+ if (get_custom_types()[p_inherits][i].name == p_type) {
+ Ref<Texture> icon = get_custom_types()[p_inherits][i].icon;
+ Ref<Script> script = get_custom_types()[p_inherits][i].script;
+
+ Object *ob = ClassDB::instance(p_inherits);
+ ERR_FAIL_COND_V(!ob, NULL);
+ if (ob->is_class("Node")) {
+ ob->call("set_name", p_type);
+ }
+ ob->set_script(script.get_ref_ptr());
+ if (icon.is_valid())
+ ob->set_meta("_editor_icon", icon);
+ return ob;
+ }
+ }
+ }
+
+ return NULL;
+}
+
void EditorData::remove_custom_type(const String &p_type) {
for (Map<String, Vector<CustomType> >::Element *E = custom_types.front(); E; E = E->next()) {
@@ -563,18 +596,16 @@ bool EditorData::check_and_update_scene(int p_idx) {
bool must_reload = _find_updated_instances(edited_scene[p_idx].root, edited_scene[p_idx].root, checked_scenes);
- print_line("MUST RELOAD? " + itos(must_reload));
-
if (must_reload) {
Ref<PackedScene> pscene;
pscene.instance();
EditorProgress ep("update_scene", TTR("Updating Scene"), 2);
- ep.step(TTR("Storing local changes.."), 0);
+ ep.step(TTR("Storing local changes..."), 0);
//pack first, so it stores diffs to previous version of saved scene
Error err = pscene->pack(edited_scene[p_idx].root);
ERR_FAIL_COND_V(err != OK, false);
- ep.step(TTR("Updating scene.."), 1);
+ ep.step(TTR("Updating scene..."), 1);
Node *new_scene = pscene->instance(PackedScene::GEN_EDIT_STATE_MAIN);
ERR_FAIL_COND_V(!new_scene, false);
diff --git a/editor/editor_data.h b/editor/editor_data.h
index 844145853..5a0b58464 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -171,6 +171,7 @@ public:
void restore_editor_global_states();
void add_custom_type(const String &p_type, const String &p_inherits, const Ref<Script> &p_script, const Ref<Texture> &p_icon);
+ Object *instance_custom_type(const String &p_type, const String &p_inherits);
void remove_custom_type(const String &p_type);
const Map<String, Vector<CustomType> > &get_custom_types() const { return custom_types; }
@@ -203,6 +204,7 @@ public:
void save_edited_scene_state(EditorSelection *p_selection, EditorHistory *p_history, const Dictionary &p_custom);
Dictionary restore_edited_scene_state(EditorSelection *p_selection, EditorHistory *p_history);
void notify_edited_scene_changed();
+ void notify_resource_saved(const Ref<Resource> &p_resource);
EditorData();
};
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index 7456cc902..7739b08ef 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -911,6 +911,16 @@ Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, co
return OK;
}
+Error EditorExportPlatform::export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+ return save_pack(p_preset, p_path);
+}
+
+Error EditorExportPlatform::export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+ return save_zip(p_preset, p_path);
+}
+
void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags) {
String host = EditorSettings::get_singleton()->get("network/debug/remote_host");
@@ -1262,6 +1272,7 @@ bool EditorExportPlatformPC::can_export(const Ref<EditorExportPreset> &p_preset,
String err;
bool valid = true;
+ bool use64 = p_preset->get("binary_format/64_bits");
if (use64 && (!exists_export_template(debug_file_64, &err) || !exists_export_template(release_file_64, &err))) {
valid = false;
diff --git a/editor/editor_export.h b/editor/editor_export.h
index e85176927..1d0b89cf1 100644
--- a/editor/editor_export.h
+++ b/editor/editor_export.h
@@ -243,6 +243,8 @@ public:
virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const = 0;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) = 0;
+ virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
+ virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0);
virtual void get_platform_features(List<String> *r_features) = 0;
EditorExportPlatform();
@@ -373,7 +375,6 @@ class EditorExportPlatformPC : public EditorExportPlatform {
Set<String> extra_features;
- bool use64;
int chmod_flags;
public:
diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp
index c52f25e66..8a8a21543 100644
--- a/editor/editor_file_dialog.cpp
+++ b/editor/editor_file_dialog.cpp
@@ -578,7 +578,7 @@ void EditorFileDialog::_item_list_rmb_clicked(const Vector2 &p_pos) {
item_menu->set_size(Size2(1, 1));
if (can_create_dir) {
- item_menu->add_icon_item(get_icon("folder", "FileDialog"), TTR("New Folder.."), ITEM_MENU_NEW_FOLDER, KEY_MASK_CMD | KEY_N);
+ item_menu->add_icon_item(get_icon("folder", "FileDialog"), TTR("New Folder..."), ITEM_MENU_NEW_FOLDER, KEY_MASK_CMD | KEY_N);
}
item_menu->add_icon_item(get_icon("Reload", "EditorIcons"), TTR("Refresh"), ITEM_MENU_REFRESH, KEY_F5);
item_menu->add_separator();
@@ -1027,6 +1027,7 @@ void EditorFileDialog::invalidate() {
if (is_visible_in_tree()) {
update_file_list();
+ _update_favorites();
invalidated = false;
} else {
invalidated = true;
diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp
index 2ec3cdb08..26f16e282 100644
--- a/editor/editor_fonts.cpp
+++ b/editor/editor_fonts.cpp
@@ -85,6 +85,20 @@ static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_valign, int p
m_name->set_spacing(DynamicFont::SPACING_BOTTOM, -EDSCALE); \
MAKE_FALLBACKS(m_name);
+#define MAKE_BOLD_FONT(m_name, m_size) \
+ Ref<DynamicFont> m_name; \
+ m_name.instance(); \
+ m_name->set_size(m_size); \
+ if (CustomFont.is_valid()) { \
+ m_name->set_font_data(CustomFontBold); \
+ m_name->add_fallback(DefaultFontBold); \
+ } else { \
+ m_name->set_font_data(DefaultFontBold); \
+ } \
+ m_name->set_spacing(DynamicFont::SPACING_TOP, -EDSCALE); \
+ m_name->set_spacing(DynamicFont::SPACING_BOTTOM, -EDSCALE); \
+ MAKE_FALLBACKS(m_name);
+
#define MAKE_SOURCE_FONT(m_name, m_size) \
Ref<DynamicFont> m_name; \
m_name.instance(); \
@@ -102,25 +116,37 @@ static Ref<BitmapFont> make_font(int p_height, int p_ascent, int p_valign, int p
void editor_register_fonts(Ref<Theme> p_theme) {
/* Custom font */
- String custom_font = EditorSettings::get_singleton()->get("interface/editor/main_font");
DynamicFontData::Hinting font_hinting = (DynamicFontData::Hinting)(int)EditorSettings::get_singleton()->get("interface/editor/main_font_hinting");
+
+ String custom_font_path = EditorSettings::get_singleton()->get("interface/editor/main_font");
Ref<DynamicFontData> CustomFont;
- if (custom_font.length() > 0) {
+ if (custom_font_path.length() > 0) {
CustomFont.instance();
CustomFont->set_hinting(font_hinting);
- CustomFont->set_font_path(custom_font);
+ CustomFont->set_font_path(custom_font_path);
CustomFont->set_force_autohinter(true); //just looks better..i think?
}
+ /* Custom Bold font */
+
+ String custom_font_path_bold = EditorSettings::get_singleton()->get("interface/editor/main_font_bold");
+ Ref<DynamicFontData> CustomFontBold;
+ if (custom_font_path_bold.length() > 0) {
+ CustomFontBold.instance();
+ CustomFontBold->set_hinting(font_hinting);
+ CustomFontBold->set_font_path(custom_font_path_bold);
+ CustomFontBold->set_force_autohinter(true); //just looks better..i think?
+ }
+
/* Custom source code font */
- String custom_font_source = EditorSettings::get_singleton()->get("interface/editor/code_font");
+ String custom_font_path_source = EditorSettings::get_singleton()->get("interface/editor/code_font");
DynamicFontData::Hinting font_source_hinting = (DynamicFontData::Hinting)(int)EditorSettings::get_singleton()->get("interface/editor/code_font_hinting");
Ref<DynamicFontData> CustomFontSource;
- if (custom_font_source.length() > 0) {
+ if (custom_font_path_source.length() > 0) {
CustomFontSource.instance();
CustomFontSource->set_hinting(font_source_hinting);
- CustomFontSource->set_font_path(custom_font_source);
+ CustomFontSource->set_font_path(custom_font_path_source);
}
/* Droid Sans */
@@ -131,6 +157,12 @@ void editor_register_fonts(Ref<Theme> p_theme) {
DefaultFont->set_font_ptr(_font_NotoSansUI_Regular, _font_NotoSansUI_Regular_size);
DefaultFont->set_force_autohinter(true); //just looks better..i think?
+ Ref<DynamicFontData> DefaultFontBold;
+ DefaultFontBold.instance();
+ DefaultFont->set_hinting(font_hinting);
+ DefaultFontBold->set_font_ptr(_font_NotoSansUI_Bold, _font_NotoSansUI_Bold_size);
+ DefaultFontBold->set_force_autohinter(true); // just looks better..i think?
+
Ref<DynamicFontData> FontFallback;
FontFallback.instance();
FontFallback->set_hinting(font_hinting);
@@ -170,29 +202,38 @@ void editor_register_fonts(Ref<Theme> p_theme) {
//dfd->set_force_autohinter(true); //just looks better..i think?
int default_font_size = int(EditorSettings::get_singleton()->get("interface/editor/main_font_size")) * EDSCALE;
- MAKE_DEFAULT_FONT(df, default_font_size);
+ // Default font
+ MAKE_DEFAULT_FONT(df, default_font_size);
p_theme->set_default_theme_font(df);
- MAKE_DEFAULT_FONT(df_title, default_font_size + 2 * EDSCALE);
+ // Bold font
+ MAKE_BOLD_FONT(df_bold, default_font_size);
+ p_theme->set_font("bold", "EditorFonts", df_bold);
+
+ // Title font
+ MAKE_BOLD_FONT(df_title, default_font_size + 2 * EDSCALE);
p_theme->set_font("title", "EditorFonts", df_title);
- MAKE_DEFAULT_FONT(df_doc_title, int(EDITOR_DEF("text_editor/help/help_title_font_size", 23)) * EDSCALE);
+ // Doc font
+ MAKE_BOLD_FONT(df_doc_title, int(EDITOR_DEF("text_editor/help/help_title_font_size", 23)) * EDSCALE);
MAKE_DEFAULT_FONT(df_doc, int(EDITOR_DEF("text_editor/help/help_font_size", 15)) * EDSCALE);
p_theme->set_font("doc", "EditorFonts", df_doc);
p_theme->set_font("doc_title", "EditorFonts", df_doc_title);
+ MAKE_SOURCE_FONT(df_doc_code, int(EDITOR_DEF("text_editor/help/help_source_font_size", 14)) * EDSCALE);
+ p_theme->set_font("doc_source", "EditorFonts", df_doc_code);
+
+ // Ruler font
MAKE_DEFAULT_FONT(df_rulers, 8 * EDSCALE);
p_theme->set_font("rulers", "EditorFonts", df_rulers);
+ // Code font
MAKE_SOURCE_FONT(df_code, int(EditorSettings::get_singleton()->get("interface/editor/code_font_size")) * EDSCALE);
p_theme->set_font("source", "EditorFonts", df_code);
- MAKE_SOURCE_FONT(df_doc_code, int(EDITOR_DEF("text_editor/help/help_source_font_size", 14)) * EDSCALE);
- p_theme->set_font("doc_source", "EditorFonts", df_doc_code);
-
MAKE_SOURCE_FONT(df_output_code, int(EDITOR_DEF("run/output/font_size", 13)) * EDSCALE);
p_theme->set_font("output_source", "EditorFonts", df_output_code);
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 7f76cf1af..f3be02a8c 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -1172,7 +1172,12 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) {
class_desc->push_indent(1);
Vector<DocData::ConstantDoc> enum_list = E->get();
+ Map<String, int> enumValuesContainer;
+ int enumStartingLine = enum_line[E->key()];
+
for (int i = 0; i < enum_list.size(); i++) {
+ if (cd.name == "@GlobalScope")
+ enumValuesContainer[enum_list[i].name] = enumStartingLine;
class_desc->push_font(doc_code_font);
class_desc->push_color(headline_color);
@@ -1200,6 +1205,9 @@ Error EditorHelp::_goto_desc(const String &p_class, int p_vscr) {
class_desc->add_newline();
}
+ if (cd.name == "@GlobalScope")
+ enum_values_line[E->key()] = enumValuesContainer;
+
class_desc->pop();
class_desc->add_newline();
@@ -1485,21 +1493,32 @@ void EditorHelp::_help_callback(const String &p_topic) {
if (method_line.has(name))
line = method_line[name];
} else if (what == "class_property") {
-
if (property_line.has(name))
line = property_line[name];
} else if (what == "class_enum") {
-
if (enum_line.has(name))
line = enum_line[name];
} else if (what == "class_theme_item") {
-
if (theme_property_line.has(name))
line = theme_property_line[name];
} else if (what == "class_constant") {
-
if (constant_line.has(name))
line = constant_line[name];
+ } else if (what == "class_global") {
+ if (constant_line.has(name))
+ line = constant_line[name];
+ else {
+ Map<String, Map<String, int> >::Element *iter = enum_values_line.front();
+ while (true) {
+ if (iter->value().has(name)) {
+ line = iter->value()[name];
+ break;
+ } else if (iter == enum_values_line.back())
+ break;
+ else
+ iter = iter->next();
+ }
+ }
}
class_desc->call_deferred("scroll_to_line", line);
diff --git a/editor/editor_help.h b/editor/editor_help.h
index aa84aa611..0f93e1b55 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -152,6 +152,7 @@ class EditorHelp : public VBoxContainer {
Map<String, int> theme_property_line;
Map<String, int> constant_line;
Map<String, int> enum_line;
+ Map<String, Map<String, int> > enum_values_line;
int description_line;
RichTextLabel *class_desc;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 7936cf446..fd5a6dffc 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -93,6 +93,7 @@
#include "editor/plugins/particles_editor_plugin.h"
#include "editor/plugins/path_2d_editor_plugin.h"
#include "editor/plugins/path_editor_plugin.h"
+#include "editor/plugins/physical_bone_plugin.h"
#include "editor/plugins/polygon_2d_editor_plugin.h"
#include "editor/plugins/resource_preloader_editor_plugin.h"
#include "editor/plugins/script_editor_plugin.h"
@@ -100,6 +101,7 @@
#include "editor/plugins/shader_editor_plugin.h"
#include "editor/plugins/shader_graph_editor_plugin.h"
#include "editor/plugins/skeleton_2d_editor_plugin.h"
+#include "editor/plugins/skeleton_editor_plugin.h"
#include "editor/plugins/spatial_editor_plugin.h"
#include "editor/plugins/sprite_editor_plugin.h"
#include "editor/plugins/sprite_frames_editor_plugin.h"
@@ -469,25 +471,29 @@ void EditorNode::_fs_changed() {
preset.unref();
}
if (preset.is_null()) {
- String err = "Unknown export preset: " + export_defer.preset;
- ERR_PRINT(err.utf8().get_data());
+ String errstr = "Unknown export preset: " + export_defer.preset;
+ ERR_PRINTS(errstr);
} else {
Ref<EditorExportPlatform> platform = preset->get_platform();
if (platform.is_null()) {
- String err = "Preset \"" + export_defer.preset + "\" doesn't have a platform.";
- ERR_PRINT(err.utf8().get_data());
+ String errstr = "Preset \"" + export_defer.preset + "\" doesn't have a platform.";
+ ERR_PRINTS(errstr);
} else {
// ensures export_project does not loop infinitely, because notifications may
// come during the export
export_defer.preset = "";
+ Error err = OK;
if (!preset->is_runnable() && (export_defer.path.ends_with(".pck") || export_defer.path.ends_with(".zip"))) {
if (export_defer.path.ends_with(".zip")) {
- platform->save_zip(preset, export_defer.path);
+ err = platform->export_zip(preset, export_defer.debug, export_defer.path);
} else if (export_defer.path.ends_with(".pck")) {
- platform->save_pack(preset, export_defer.path);
+ err = platform->export_pack(preset, export_defer.debug, export_defer.path);
}
} else {
- platform->export_project(preset, export_defer.debug, export_defer.path, /*p_flags*/ 0);
+ err = platform->export_project(preset, export_defer.debug, export_defer.path);
+ }
+ if (err != OK) {
+ ERR_PRINTS(vformat(TTR("Project export failed with error code %d."), (int)err));
}
}
}
@@ -627,6 +633,7 @@ void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const St
((Resource *)p_resource.ptr())->set_path(path);
emit_signal("resource_saved", p_resource);
+ editor_data.notify_resource_saved(p_resource);
}
void EditorNode::save_resource(const Ref<Resource> &p_resource) {
@@ -689,7 +696,7 @@ void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String
file->set_current_path(existing);
}
file->popup_centered_ratio();
- file->set_title(TTR("Save Resource As.."));
+ file->set_title(TTR("Save Resource As..."));
}
void EditorNode::_menu_option(int p_option) {
@@ -707,7 +714,7 @@ void EditorNode::_dialog_display_save_error(String p_file, Error p_error) {
if (p_error) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
switch (p_error) {
@@ -734,7 +741,7 @@ void EditorNode::_dialog_display_load_error(String p_file, Error p_error) {
if (p_error) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
switch (p_error) {
@@ -1022,7 +1029,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
if (!scene) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation can't be done without a tree root."));
accept->popup_centered_minsize();
return;
@@ -1053,7 +1060,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
if (err != OK) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."));
accept->popup_centered_minsize();
return;
@@ -1064,7 +1071,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
Node *dummy_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
if (!dummy_scene) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("Couldn't save scene. Likely dependencies (instances or inheritance) couldn't be satisfied."));
accept->popup_centered_minsize();
return;
@@ -1204,7 +1211,7 @@ void EditorNode::_dialog_action(String p_file) {
if (ml.is_null()) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("Can't load MeshLibrary for merging!"));
accept->popup_centered_minsize();
return;
@@ -1221,7 +1228,7 @@ void EditorNode::_dialog_action(String p_file) {
if (err) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("Error saving MeshLibrary!"));
accept->popup_centered_minsize();
return;
@@ -1230,33 +1237,29 @@ void EditorNode::_dialog_action(String p_file) {
} break;
case FILE_EXPORT_TILESET: {
- Ref<TileSet> ml;
- if (FileAccess::exists(p_file)) {
- ml = ResourceLoader::load(p_file, "TileSet");
+ Ref<TileSet> tileset;
+ if (FileAccess::exists(p_file) && file_export_lib_merge->is_pressed()) {
+ tileset = ResourceLoader::load(p_file, "TileSet");
- if (ml.is_null()) {
- if (file_export_lib_merge->is_pressed()) {
- current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
- accept->set_text(TTR("Can't load TileSet for merging!"));
- accept->popup_centered_minsize();
- return;
- }
- } else if (!file_export_lib_merge->is_pressed()) {
- ml->clear();
+ if (tileset.is_null()) {
+ current_option = -1;
+ accept->get_ok()->set_text(TTR("I see..."));
+ accept->set_text(TTR("Can't load TileSet for merging!"));
+ accept->popup_centered_minsize();
+ return;
}
} else {
- ml = Ref<TileSet>(memnew(TileSet));
+ tileset = Ref<TileSet>(memnew(TileSet));
}
- TileSetEditor::update_library_file(editor_data.get_edited_scene_root(), ml, true);
+ TileSetEditor::update_library_file(editor_data.get_edited_scene_root(), tileset, true);
- Error err = ResourceSaver::save(p_file, ml);
+ Error err = ResourceSaver::save(p_file, tileset);
if (err) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("Error saving TileSet!"));
accept->popup_centered_minsize();
return;
@@ -1746,7 +1749,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
if (!scene) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("There is no defined scene to run."));
accept->popup_centered_minsize();
return;
@@ -1803,7 +1806,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
if (scene->get_filename() == "") {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("Current scene was never saved, please save it prior to running."));
accept->popup_centered_minsize();
return;
@@ -1837,7 +1840,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
if (error != OK) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("Could not start subprocess!"));
accept->popup_centered_minsize();
return;
@@ -1897,13 +1900,13 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case FILE_QUICK_OPEN_SCENE: {
quick_open->popup("PackedScene", true);
- quick_open->set_title(TTR("Quick Open Scene.."));
+ quick_open->set_title(TTR("Quick Open Scene..."));
} break;
case FILE_QUICK_OPEN_SCRIPT: {
quick_open->popup("Script", true);
- quick_open->set_title(TTR("Quick Open Script.."));
+ quick_open->set_title(TTR("Quick Open Script..."));
} break;
case FILE_OPEN_PREV: {
@@ -1958,7 +1961,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
if (!scene) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation can't be done without a tree root."));
accept->popup_centered_minsize();
break;
@@ -1993,7 +1996,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
file->set_current_path(existing);
}
file->popup_centered_ratio();
- file->set_title(TTR("Save Scene As.."));
+ file->set_title(TTR("Save Scene As..."));
} break;
@@ -2024,7 +2027,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
if (!editor_data.get_edited_scene_root()) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation can't be done without a scene."));
accept->popup_centered_minsize();
break;
@@ -2047,7 +2050,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
//Make sure that the scene has a root before trying to convert to tileset
if (!editor_data.get_edited_scene_root()) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation can't be done without a root node."));
accept->popup_centered_minsize();
break;
@@ -2075,7 +2078,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
if (!editor_data.get_edited_scene_root()) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation can't be done without a selected node."));
accept->popup_centered_minsize();
break;
@@ -2298,7 +2301,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
if (run_custom_filename.empty() || editor_run.get_status() == EditorRun::STATUS_STOP) {
_menu_option_confirm(RUN_STOP, true);
quick_run->popup("PackedScene", true);
- quick_run->set_title(TTR("Quick Run Scene.."));
+ quick_run->set_title(TTR("Quick Run Scene..."));
play_custom_scene_button->set_pressed(false);
} else {
String last_custom_scene = run_custom_filename;
@@ -2477,7 +2480,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
EditorSettings::get_singleton()->set_project_metadata("editor_options", "update_always", true);
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This option is deprecated. Situations where refresh must be forced are now considered a bug. Please report."));
accept->popup_centered_minsize();
} break;
@@ -3892,6 +3895,53 @@ void EditorNode::_update_dock_slots_visibility() {
}
}
+void EditorNode::_dock_tab_changed(int p_tab) {
+
+ // update visibility but dont set current tab
+ VSplitContainer *splits[DOCK_SLOT_MAX / 2] = {
+ left_l_vsplit,
+ left_r_vsplit,
+ right_l_vsplit,
+ right_r_vsplit,
+ };
+
+ if (!docks_visible) {
+
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+ dock_slot[i]->hide();
+ }
+
+ for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) {
+ splits[i]->hide();
+ }
+
+ right_hsplit->hide();
+ bottom_panel->hide();
+ } else {
+ for (int i = 0; i < DOCK_SLOT_MAX; i++) {
+
+ if (dock_slot[i]->get_tab_count())
+ dock_slot[i]->show();
+ else
+ dock_slot[i]->hide();
+ }
+
+ for (int i = 0; i < DOCK_SLOT_MAX / 2; i++) {
+ bool in_use = dock_slot[i * 2 + 0]->get_tab_count() || dock_slot[i * 2 + 1]->get_tab_count();
+ if (in_use)
+ splits[i]->show();
+ else
+ splits[i]->hide();
+ }
+ bottom_panel->show();
+
+ if (right_l_vsplit->is_visible() || right_r_vsplit->is_visible())
+ right_hsplit->show();
+ else
+ right_hsplit->hide();
+ }
+}
+
void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section) {
for (int i = 0; i < DOCK_SLOT_MAX; i++) {
@@ -4762,6 +4812,7 @@ void EditorNode::_bind_methods() {
ClassDB::bind_method("_dock_popup_exit", &EditorNode::_dock_popup_exit);
ClassDB::bind_method("_dock_move_left", &EditorNode::_dock_move_left);
ClassDB::bind_method("_dock_move_right", &EditorNode::_dock_move_right);
+ ClassDB::bind_method("_dock_tab_changed", &EditorNode::_dock_tab_changed);
ClassDB::bind_method("_layout_menu_option", &EditorNode::_layout_menu_option);
@@ -4838,7 +4889,7 @@ EditorNode::EditorNode() {
if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton()) {
//only if no touchscreen ui hint, set emulation
- id->set_emulate_touch(false); //just disable just in case
+ id->set_emulate_touch_from_mouse(false); //just disable just in case
}
id->set_custom_mouse_cursor(RES());
}
@@ -5121,6 +5172,9 @@ EditorNode::EditorNode() {
dock_slot[i]->set_popup(dock_select_popup);
dock_slot[i]->connect("pre_popup_pressed", this, "_dock_pre_popup", varray(i));
dock_slot[i]->set_tab_align(TabContainer::ALIGN_LEFT);
+ dock_slot[i]->set_drag_to_rearrange_enabled(true);
+ dock_slot[i]->set_tabs_rearrange_group(1);
+ dock_slot[i]->connect("tab_changed", this, "_dock_tab_changed");
}
dock_drag_timer = memnew(Timer);
@@ -5158,6 +5212,7 @@ EditorNode::EditorNode() {
scene_tabs->set_tab_align(Tabs::ALIGN_LEFT);
scene_tabs->set_tab_close_display_policy((bool(EDITOR_DEF("interface/scene_tabs/always_show_close_button", false)) ? Tabs::CLOSE_BUTTON_SHOW_ALWAYS : Tabs::CLOSE_BUTTON_SHOW_ACTIVE_ONLY));
scene_tabs->set_min_width(int(EDITOR_DEF("interface/scene_tabs/minimum_width", 50)) * EDSCALE);
+ scene_tabs->set_drag_to_rearrange_enabled(true);
scene_tabs->connect("tab_changed", this, "_scene_tab_changed");
scene_tabs->connect("right_button_pressed", this, "_scene_tab_script_edited");
scene_tabs->connect("tab_close", this, "_scene_tab_closed");
@@ -5267,32 +5322,32 @@ EditorNode::EditorNode() {
ED_SHORTCUT("editor/next_tab", TTR("Next tab"), KEY_MASK_CMD + KEY_TAB);
ED_SHORTCUT("editor/prev_tab", TTR("Previous tab"), KEY_MASK_CMD + KEY_MASK_SHIFT + KEY_TAB);
- ED_SHORTCUT("editor/filter_files", TTR("Filter Files.."), KEY_MASK_ALT + KEY_MASK_CMD + KEY_P);
+ ED_SHORTCUT("editor/filter_files", TTR("Filter Files..."), KEY_MASK_ALT + KEY_MASK_CMD + KEY_P);
PopupMenu *p;
file_menu->set_tooltip(TTR("Operations with scene files."));
p = file_menu->get_popup();
p->add_shortcut(ED_SHORTCUT("editor/new_scene", TTR("New Scene")), FILE_NEW_SCENE);
- p->add_shortcut(ED_SHORTCUT("editor/new_inherited_scene", TTR("New Inherited Scene..")), FILE_NEW_INHERITED_SCENE);
- p->add_shortcut(ED_SHORTCUT("editor/open_scene", TTR("Open Scene.."), KEY_MASK_CMD + KEY_O), FILE_OPEN_SCENE);
+ p->add_shortcut(ED_SHORTCUT("editor/new_inherited_scene", TTR("New Inherited Scene...")), FILE_NEW_INHERITED_SCENE);
+ p->add_shortcut(ED_SHORTCUT("editor/open_scene", TTR("Open Scene..."), KEY_MASK_CMD + KEY_O), FILE_OPEN_SCENE);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("editor/save_scene", TTR("Save Scene"), KEY_MASK_CMD + KEY_S), FILE_SAVE_SCENE);
- p->add_shortcut(ED_SHORTCUT("editor/save_scene_as", TTR("Save Scene As.."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_AS_SCENE);
+ p->add_shortcut(ED_SHORTCUT("editor/save_scene_as", TTR("Save Scene As..."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_AS_SCENE);
p->add_shortcut(ED_SHORTCUT("editor/save_all_scenes", TTR("Save all Scenes"), KEY_MASK_ALT + KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_ALL_SCENES);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("editor/close_scene", TTR("Close Scene"), KEY_MASK_SHIFT + KEY_MASK_CTRL + KEY_W), FILE_CLOSE);
p->add_separator();
p->add_submenu_item(TTR("Open Recent"), "RecentScenes", FILE_OPEN_RECENT);
p->add_separator();
- p->add_shortcut(ED_SHORTCUT("editor/quick_open_scene", TTR("Quick Open Scene.."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_O), FILE_QUICK_OPEN_SCENE);
- p->add_shortcut(ED_SHORTCUT("editor/quick_open_script", TTR("Quick Open Script.."), KEY_MASK_ALT + KEY_MASK_CMD + KEY_O), FILE_QUICK_OPEN_SCRIPT);
+ p->add_shortcut(ED_SHORTCUT("editor/quick_open_scene", TTR("Quick Open Scene..."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_O), FILE_QUICK_OPEN_SCENE);
+ p->add_shortcut(ED_SHORTCUT("editor/quick_open_script", TTR("Quick Open Script..."), KEY_MASK_ALT + KEY_MASK_CMD + KEY_O), FILE_QUICK_OPEN_SCRIPT);
p->add_separator();
PopupMenu *pm_export = memnew(PopupMenu);
pm_export->set_name("Export");
p->add_child(pm_export);
- p->add_submenu_item(TTR("Convert To.."), "Export");
- pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_MeshLibrary", TTR("MeshLibrary..")), FILE_EXPORT_MESH_LIBRARY);
- pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_TileSet", TTR("TileSet..")), FILE_EXPORT_TILESET);
+ p->add_submenu_item(TTR("Convert To..."), "Export");
+ pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_MeshLibrary", TTR("MeshLibrary...")), FILE_EXPORT_MESH_LIBRARY);
+ pm_export->add_shortcut(ED_SHORTCUT("editor/convert_to_TileSet", TTR("TileSet...")), FILE_EXPORT_TILESET);
pm_export->connect("id_pressed", this, "_menu_option");
p->add_separator();
@@ -5531,7 +5586,7 @@ EditorNode::EditorNode() {
resource_save_button->set_icon(gui_base->get_icon("Save", "EditorIcons"));
prop_editor_hb->add_child(resource_save_button);
resource_save_button->get_popup()->add_item(TTR("Save"), RESOURCE_SAVE);
- resource_save_button->get_popup()->add_item(TTR("Save As.."), RESOURCE_SAVE_AS);
+ resource_save_button->get_popup()->add_item(TTR("Save As..."), RESOURCE_SAVE_AS);
resource_save_button->get_popup()->connect("id_pressed", this, "_menu_option");
resource_save_button->set_focus_mode(Control::FOCUS_NONE);
resource_save_button->set_disabled(true);
@@ -5806,7 +5861,7 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(ParticlesEditorPlugin(this)));
add_editor_plugin(memnew(ResourcePreloaderEditorPlugin(this)));
add_editor_plugin(memnew(ItemListEditorPlugin(this)));
- add_editor_plugin(memnew(CollisionPolygonEditorPlugin(this)));
+ add_editor_plugin(memnew(Polygon3DEditorPlugin(this)));
add_editor_plugin(memnew(CollisionPolygon2DEditorPlugin(this)));
add_editor_plugin(memnew(TileSetEditorPlugin(this)));
add_editor_plugin(memnew(TileMapEditorPlugin(this)));
@@ -5828,6 +5883,8 @@ EditorNode::EditorNode() {
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
add_editor_plugin(memnew(AudioBusesEditorPlugin(audio_bus_editor)));
add_editor_plugin(memnew(NavigationMeshEditorPlugin(this)));
+ add_editor_plugin(memnew(SkeletonEditorPlugin(this)));
+ add_editor_plugin(memnew(PhysicalBonePlugin(this)));
// FIXME: Disabled as (according to reduz) users were complaining that it gets in the way
// Waiting for PropertyEditor rewrite (planned for 3.1) to be refactored.
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 90bebffca..f774fa0a2 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -569,6 +569,7 @@ private:
void _save_docks_to_config(Ref<ConfigFile> p_layout, const String &p_section);
void _load_docks_from_config(Ref<ConfigFile> p_layout, const String &p_section);
void _update_dock_slots_visibility();
+ void _dock_tab_changed(int p_tab);
bool restoring_scenes;
void _save_open_scenes_to_config(Ref<ConfigFile> p_layout, const String &p_section);
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 4f38c0188..2e4e88716 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -125,7 +125,7 @@ Vector<Ref<Texture> > EditorInterface::make_mesh_previews(const Vector<Ref<Mesh>
xform.origin.z -= rot_aabb.size.z * 2;
RID inst = VS::get_singleton()->instance_create2(mesh->get_rid(), scenario);
VS::get_singleton()->instance_set_transform(inst, xform);
- ep.step(TTR("Thumbnail.."), i);
+ ep.step(TTR("Thumbnail..."), i);
Main::iteration();
Main::iteration();
Ref<Image> img = VS::get_singleton()->texture_get_data(viewport_texture);
@@ -473,6 +473,10 @@ void EditorPlugin::notify_scene_closed(const String &scene_filepath) {
emit_signal("scene_closed", scene_filepath);
}
+void EditorPlugin::notify_resource_saved(const Ref<Resource> &p_resource) {
+ emit_signal("resource_saved", p_resource);
+}
+
Ref<SpatialEditorGizmo> EditorPlugin::create_spatial_gizmo(Spatial *p_spatial) {
//??
if (get_script_instance() && get_script_instance()->has_method("create_spatial_gizmo")) {
@@ -757,6 +761,7 @@ void EditorPlugin::_bind_methods() {
ADD_SIGNAL(MethodInfo("scene_changed", PropertyInfo(Variant::OBJECT, "scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node")));
ADD_SIGNAL(MethodInfo("scene_closed", PropertyInfo(Variant::STRING, "filepath")));
ADD_SIGNAL(MethodInfo("main_screen_changed", PropertyInfo(Variant::STRING, "screen_name")));
+ ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
BIND_ENUM_CONSTANT(CONTAINER_TOOLBAR);
BIND_ENUM_CONSTANT(CONTAINER_SPATIAL_EDITOR_MENU);
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 05cc6e07e..ebc4afdfe 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -167,6 +167,7 @@ public:
void notify_main_screen_changed(const String &screen_name);
void notify_scene_changed(const Node *scn_root);
void notify_scene_closed(const String &scene_filepath);
+ void notify_resource_saved(const Ref<Resource> &p_resource);
virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial);
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event);
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 85f6d99c6..6eae7be9d 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -298,6 +298,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
hints["interface/editor/code_font_hinting"] = PropertyInfo(Variant::INT, "interface/editor/code_font_hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/editor/main_font", "");
hints["interface/editor/main_font"] = PropertyInfo(Variant::STRING, "interface/editor/main_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
+ _initial_set("interface/editor/main__bold_font", "");
+ hints["interface/editor/main_font_bold"] = PropertyInfo(Variant::STRING, "interface/editor/main_bold_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/editor/code_font", "");
hints["interface/editor/code_font"] = PropertyInfo(Variant::STRING, "interface/editor/code_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/editor/dim_editor_on_dialog_popup", true);
@@ -371,7 +373,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/line_numbers/code_folding", true);
_initial_set("text_editor/line_numbers/show_line_length_guideline", false);
_initial_set("text_editor/line_numbers/line_length_guideline_column", 80);
- hints["text_editor/line_numbers/line_length_guideline_column"] = PropertyInfo(Variant::INT, "text_editor/line_numbers/line_length_guideline_column", PROPERTY_HINT_RANGE, "20, 160, 10");
+ hints["text_editor/line_numbers/line_length_guideline_column"] = PropertyInfo(Variant::INT, "text_editor/line_numbers/line_length_guideline_column", PROPERTY_HINT_RANGE, "20, 160, 1");
_initial_set("text_editor/open_scripts/smooth_scrolling", true);
_initial_set("text_editor/open_scripts/v_scroll_speed", 80);
@@ -385,7 +387,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("text_editor/cursor/block_caret", false);
_initial_set("text_editor/cursor/caret_blink", true);
_initial_set("text_editor/cursor/caret_blink_speed", 0.65);
- hints["text_editor/cursor/caret_blink_speed"] = PropertyInfo(Variant::REAL, "text_editor/cursor/caret_blink_speed", PROPERTY_HINT_RANGE, "0.1, 10, 0.1");
+ hints["text_editor/cursor/caret_blink_speed"] = PropertyInfo(Variant::REAL, "text_editor/cursor/caret_blink_speed", PROPERTY_HINT_RANGE, "0.1, 10, 0.01");
_initial_set("text_editor/cursor/right_click_moves_caret", true);
_initial_set("text_editor/completion/auto_brace_complete", false);
@@ -400,8 +402,17 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/grid_map/pick_distance", 5000.0);
- _initial_set("editors/3d/grid_color", Color::html("808080"));
- hints["editors/3d/grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/grid_color", PROPERTY_HINT_COLOR_NO_ALPHA, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
+ _initial_set("editors/3d/primary_grid_color", Color::html("909090"));
+ hints["editors/3d/primary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/primary_grid_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
+
+ _initial_set("editors/3d/secondary_grid_color", Color::html("606060"));
+ hints["editors/3d/secondary_grid_color"] = PropertyInfo(Variant::COLOR, "editors/3d/secondary_grid_color", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
+
+ _initial_set("editors/3d/grid_size", 50);
+ hints["editors/3d/grid_size"] = PropertyInfo(Variant::INT, "editors/3d/grid_size", PROPERTY_HINT_RANGE, "1,500,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
+
+ _initial_set("editors/3d/primary_grid_steps", 10);
+ hints["editors/3d/primary_grid_steps"] = PropertyInfo(Variant::INT, "editors/3d/primary_grid_steps", PROPERTY_HINT_RANGE, "1,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("editors/3d/default_fov", 70.0);
_initial_set("editors/3d/default_z_near", 0.05);
@@ -454,9 +465,11 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/2d/guides_color", Color(0.6, 0.0, 0.8));
_initial_set("editors/2d/bone_width", 5);
_initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.9));
- _initial_set("editors/2d/bone_color2", Color(0.75, 0.75, 0.75, 0.9));
+ _initial_set("editors/2d/bone_color2", Color(0.6, 0.6, 0.6, 0.9));
_initial_set("editors/2d/bone_selected_color", Color(0.9, 0.45, 0.45, 0.9));
_initial_set("editors/2d/bone_ik_color", Color(0.9, 0.9, 0.45, 0.9));
+ _initial_set("editors/2d/bone_outline_color", Color(0.35, 0.35, 0.35));
+ _initial_set("editors/2d/bone_outline_size", 2);
_initial_set("editors/2d/keep_margins_when_changing_anchors", false);
_initial_set("editors/2d/warped_mouse_panning", true);
_initial_set("editors/2d/simple_spacebar_panning", false);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index b10f785a2..bf7236cc2 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -626,8 +626,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_color_disabled", "PopupMenu", font_color_disabled);
theme->set_icon("checked", "PopupMenu", theme->get_icon("GuiChecked", "EditorIcons"));
theme->set_icon("unchecked", "PopupMenu", theme->get_icon("GuiUnchecked", "EditorIcons"));
- theme->set_icon("radio_checked", "PopupMenu", theme->get_icon("GuiChecked", "EditorIcons"));
- theme->set_icon("radio_unchecked", "PopupMenu", theme->get_icon("GuiUnchecked", "EditorIcons"));
+ theme->set_icon("radio_checked", "PopupMenu", theme->get_icon("GuiRadioChecked", "EditorIcons"));
+ theme->set_icon("radio_unchecked", "PopupMenu", theme->get_icon("GuiRadioUnchecked", "EditorIcons"));
theme->set_icon("submenu", "PopupMenu", theme->get_icon("ArrowRight", "EditorIcons"));
theme->set_icon("visibility_hidden", "PopupMenu", theme->get_icon("GuiVisibilityHidden", "EditorIcons"));
theme->set_icon("visibility_visible", "PopupMenu", theme->get_icon("GuiVisibilityVisible", "EditorIcons"));
@@ -891,6 +891,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
//RichTextLabel
theme->set_color("default_color", "RichTextLabel", font_color);
+ theme->set_color("font_color_shadow", "RichTextLabel", Color(0, 0, 0, 0));
+ theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * EDSCALE);
+ theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * EDSCALE);
+ theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * EDSCALE);
theme->set_stylebox("focus", "RichTextLabel", make_empty_stylebox());
theme->set_stylebox("normal", "RichTextLabel", style_tree_bg);
@@ -908,6 +912,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_constant("shadow_as_outline", "Label", 0 * EDSCALE);
theme->set_constant("line_spacing", "Label", 3 * EDSCALE);
+ // LinkButton
+ theme->set_stylebox("focus", "LinkButton", style_empty);
+ theme->set_color("font_color", "LinkButton", font_color);
+
// TooltipPanel
Ref<StyleBoxFlat> style_tooltip = style_popup->duplicate();
style_tooltip->set_bg_color(Color(mono_color.r, mono_color.g, mono_color.b, 0.9));
diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp
index 101deb912..a39c8b220 100644
--- a/editor/export_template_manager.cpp
+++ b/editor/export_template_manager.cpp
@@ -128,7 +128,7 @@ void ExportTemplateManager::_download_template(const String &p_version) {
memdelete(template_list->get_child(0));
}
template_downloader->popup_centered_minsize();
- template_list_state->set_text(TTR("Retrieving mirrors, please wait.."));
+ template_list_state->set_text(TTR("Retrieving mirrors, please wait..."));
template_download_progress->set_max(100);
template_download_progress->set_value(0);
request_mirror->request("https://godotengine.org/mirrorlist/" + p_version + ".json");
@@ -433,7 +433,7 @@ void ExportTemplateManager::_begin_template_download(const String &p_url) {
template_download_progress->set_max(100);
template_download_progress->set_value(0);
template_download_progress->show();
- template_list_state->set_text(TTR("Connecting to Mirror.."));
+ template_list_state->set_text(TTR("Connecting to Mirror..."));
}
void ExportTemplateManager::_notification(int p_what) {
@@ -459,13 +459,13 @@ void ExportTemplateManager::_notification(int p_what) {
status = TTR("Can't Resolve");
errored = true;
break;
- case HTTPClient::STATUS_CONNECTING: status = TTR("Connecting.."); break;
+ case HTTPClient::STATUS_CONNECTING: status = TTR("Connecting..."); break;
case HTTPClient::STATUS_CANT_CONNECT:
status = TTR("Can't Connect");
errored = true;
break;
case HTTPClient::STATUS_CONNECTED: status = TTR("Connected"); break;
- case HTTPClient::STATUS_REQUESTING: status = TTR("Requesting.."); break;
+ case HTTPClient::STATUS_REQUESTING: status = TTR("Requesting..."); break;
case HTTPClient::STATUS_BODY:
status = TTR("Downloading");
if (download_templates->get_body_size() > 0) {
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 9bfa50148..e7741c792 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -533,6 +533,7 @@ void FileSystemDock::_update_files(bool p_keep_selection) {
filelist.push_back(fi);
}
+ filelist.sort();
}
String oi = "Object";
@@ -719,12 +720,13 @@ void FileSystemDock::_push_to_history() {
button_hist_next->set_disabled(history_pos == history.size() - 1);
}
-void FileSystemDock::_get_all_files_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files) const {
+void FileSystemDock::_get_all_items_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files, Vector<String> &folders) const {
if (efsd == NULL)
return;
for (int i = 0; i < efsd->get_subdir_count(); i++) {
- _get_all_files_in_dir(efsd->get_subdir(i), files);
+ folders.push_back(efsd->get_subdir(i)->get_path());
+ _get_all_items_in_dir(efsd->get_subdir(i), files, folders);
}
for (int i = 0; i < efsd->get_file_count(); i++) {
files.push_back(efsd->get_file_path(i));
@@ -746,7 +748,8 @@ void FileSystemDock::_find_remaps(EditorFileSystemDirectory *efsd, const Map<Str
}
}
-void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_new_path, Map<String, String> &p_renames) const {
+void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_new_path,
+ Map<String, String> &p_file_renames, Map<String, String> &p_folder_renames) const {
//Ensure folder paths end with "/"
String old_path = (p_item.is_file || p_item.path.ends_with("/")) ? p_item.path : (p_item.path + "/");
String new_path = (p_item.is_file || p_new_path.ends_with("/")) ? p_new_path : (p_new_path + "/");
@@ -763,11 +766,13 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_
}
//Build a list of files which will have new paths as a result of this operation
- Vector<String> changed_paths;
+ Vector<String> file_changed_paths;
+ Vector<String> folder_changed_paths;
if (p_item.is_file) {
- changed_paths.push_back(old_path);
+ file_changed_paths.push_back(old_path);
} else {
- _get_all_files_in_dir(EditorFileSystem::get_singleton()->get_filesystem_path(old_path), changed_paths);
+ folder_changed_paths.push_back(old_path);
+ _get_all_items_in_dir(EditorFileSystem::get_singleton()->get_filesystem_path(old_path), file_changed_paths, folder_changed_paths);
}
DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
@@ -783,12 +788,12 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_
}
// update scene if it is open
- for (int i = 0; i < changed_paths.size(); ++i) {
- String new_item_path = p_item.is_file ? new_path : changed_paths[i].replace_first(old_path, new_path);
- if (ResourceLoader::get_resource_type(new_item_path) == "PackedScene" && editor->is_scene_open(changed_paths[i])) {
+ for (int i = 0; i < file_changed_paths.size(); ++i) {
+ String new_item_path = p_item.is_file ? new_path : file_changed_paths[i].replace_first(old_path, new_path);
+ if (ResourceLoader::get_resource_type(new_item_path) == "PackedScene" && editor->is_scene_open(file_changed_paths[i])) {
EditorData *ed = &editor->get_editor_data();
for (int j = 0; j < ed->get_edited_scene_count(); j++) {
- if (ed->get_scene_path(j) == changed_paths[i]) {
+ if (ed->get_scene_path(j) == file_changed_paths[i]) {
ed->get_edited_scene_root(j)->set_filename(new_item_path);
break;
}
@@ -797,9 +802,12 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_
}
//Only treat as a changed dependency if it was successfully moved
- for (int i = 0; i < changed_paths.size(); ++i) {
- p_renames[changed_paths[i]] = changed_paths[i].replace_first(old_path, new_path);
- print_line(" Remap: " + changed_paths[i] + " -> " + p_renames[changed_paths[i]]);
+ for (int i = 0; i < file_changed_paths.size(); ++i) {
+ p_file_renames[file_changed_paths[i]] = file_changed_paths[i].replace_first(old_path, new_path);
+ print_line(" Remap: " + file_changed_paths[i] + " -> " + p_file_renames[file_changed_paths[i]]);
+ }
+ for (int i = 0; i < folder_changed_paths.size(); ++i) {
+ p_folder_renames[folder_changed_paths[i]] = folder_changed_paths[i].replace_first(old_path, new_path);
}
} else {
EditorNode::get_singleton()->add_io_error(TTR("Error moving:") + "\n" + old_path + "\n");
@@ -912,13 +920,32 @@ void FileSystemDock::_update_dependencies_after_move(const Map<String, String> &
}
}
+void FileSystemDock::_update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const {
+
+ Vector<String> favorite_dirs = EditorSettings::get_singleton()->get_favorite_dirs();
+ Vector<String> new_favorite_dirs;
+
+ for (int i = 0; i < favorite_dirs.size(); i++) {
+ String old_path = favorite_dirs[i] + "/";
+
+ if (p_renames.has(old_path)) {
+ String new_path = p_renames[old_path];
+ new_favorite_dirs.push_back(new_path.substr(0, new_path.length() - 1));
+ } else {
+ new_favorite_dirs.push_back(favorite_dirs[i]);
+ }
+ }
+
+ EditorSettings::get_singleton()->set_favorite_dirs(new_favorite_dirs);
+}
+
void FileSystemDock::_make_dir_confirm() {
String dir_name = make_dir_dialog_text->get_text().strip_edges();
if (dir_name.length() == 0) {
EditorNode::get_singleton()->show_warning(TTR("No name provided"));
return;
- } else if (dir_name.find("/") != -1 || dir_name.find("\\") != -1 || dir_name.find(":") != -1 || dir_name.ends_with(".")) {
+ } else if (dir_name.find("/") != -1 || dir_name.find("\\") != -1 || dir_name.find(":") != -1 || dir_name.ends_with(".") || dir_name.ends_with(" ")) {
EditorNode::get_singleton()->show_warning(TTR("Provided name contains invalid characters"));
return;
}
@@ -970,10 +997,12 @@ void FileSystemDock::_rename_operation_confirm() {
}
memdelete(da);
- Map<String, String> renames;
- _try_move_item(to_rename, new_path, renames);
- _update_dependencies_after_move(renames);
- _update_resource_paths_after_move(renames);
+ Map<String, String> file_renames;
+ Map<String, String> folder_renames;
+ _try_move_item(to_rename, new_path, file_renames, folder_renames);
+ _update_dependencies_after_move(file_renames);
+ _update_resource_paths_after_move(file_renames);
+ _update_favorite_dirs_list_after_move(folder_renames);
//Rescan everything
print_line("call rescan!");
@@ -1017,15 +1046,17 @@ void FileSystemDock::_duplicate_operation_confirm() {
void FileSystemDock::_move_operation_confirm(const String &p_to_path) {
- Map<String, String> renames;
+ Map<String, String> file_renames;
+ Map<String, String> folder_renames;
for (int i = 0; i < to_move.size(); i++) {
String old_path = to_move[i].path.ends_with("/") ? to_move[i].path.substr(0, to_move[i].path.length() - 1) : to_move[i].path;
String new_path = p_to_path.plus_file(old_path.get_file());
- _try_move_item(to_move[i], new_path, renames);
+ _try_move_item(to_move[i], new_path, file_renames, folder_renames);
}
- _update_dependencies_after_move(renames);
- _update_resource_paths_after_move(renames);
+ _update_dependencies_after_move(file_renames);
+ _update_resource_paths_after_move(file_renames);
+ _update_favorite_dirs_list_after_move(folder_renames);
print_line("call rescan!");
_rescan();
@@ -1034,8 +1065,19 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path) {
void FileSystemDock::_file_option(int p_option) {
switch (p_option) {
case FILE_SHOW_IN_EXPLORER: {
- String dir = ProjectSettings::get_singleton()->globalize_path(this->path);
- OS::get_singleton()->shell_open(String("file://") + dir);
+
+ String path = this->path;
+
+ // first try to grab directory from selected file, so that it works for searched files
+ int idx = files->get_current();
+
+ if (idx >= 0 && idx < files->get_item_count()) {
+ path = files->get_item_metadata(idx);
+ path = path.get_base_dir();
+ }
+
+ path = ProjectSettings::get_singleton()->globalize_path(path);
+ OS::get_singleton()->shell_open(String("file://") + path);
} break;
case FILE_OPEN: {
for (int i = 0; i < files->get_item_count(); i++) {
@@ -1195,6 +1237,15 @@ void FileSystemDock::_file_option(int p_option) {
make_dir_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE);
make_dir_dialog_text->grab_focus();
} break;
+ case FILE_NEW_SCRIPT: {
+ String tarDir = path;
+ if (tarDir != "res://" && !tarDir.ends_with("/")) {
+ tarDir += "/";
+ }
+
+ make_script_dialog_text->config("Node", tarDir + "new_script.gd");
+ make_script_dialog_text->popup_centered(Size2(300, 300) * EDSCALE);
+ } break;
case FILE_COPY_PATH: {
int idx = files->get_current();
if (idx < 0 || idx >= files->get_item_count())
@@ -1307,12 +1358,12 @@ void FileSystemDock::_dir_rmb_pressed(const Vector2 &p_pos) {
folder_options->add_separator();
folder_options->add_item(TTR("Copy Path"), FOLDER_COPY_PATH);
if (fpath != "res://") {
- folder_options->add_item(TTR("Rename.."), FOLDER_RENAME);
- folder_options->add_item(TTR("Move To.."), FOLDER_MOVE);
+ folder_options->add_item(TTR("Rename..."), FOLDER_RENAME);
+ folder_options->add_item(TTR("Move To..."), FOLDER_MOVE);
folder_options->add_item(TTR("Delete"), FOLDER_REMOVE);
}
folder_options->add_separator();
- folder_options->add_item(TTR("New Folder.."), FOLDER_NEW_FOLDER);
+ folder_options->add_item(TTR("New Folder..."), FOLDER_NEW_FOLDER);
folder_options->add_item(TTR("Show In File Manager"), FOLDER_SHOW_IN_EXPLORER);
}
folder_options->set_position(tree->get_global_position() + p_pos);
@@ -1597,8 +1648,8 @@ void FileSystemDock::_files_list_rmb_select(int p_item, const Vector2 &p_pos) {
}
if (filenames.size() == 1) {
- file_options->add_item(TTR("Edit Dependencies.."), FILE_DEPENDENCIES);
- file_options->add_item(TTR("View Owners.."), FILE_OWNERS);
+ file_options->add_item(TTR("Edit Dependencies..."), FILE_DEPENDENCIES);
+ file_options->add_item(TTR("View Owners..."), FILE_OWNERS);
file_options->add_separator();
}
@@ -1611,15 +1662,16 @@ void FileSystemDock::_files_list_rmb_select(int p_item, const Vector2 &p_pos) {
if (num_items >= 1) {
if (num_items == 1) {
file_options->add_item(TTR("Copy Path"), FILE_COPY_PATH);
- file_options->add_item(TTR("Rename.."), FILE_RENAME);
- file_options->add_item(TTR("Duplicate.."), FILE_DUPLICATE);
+ file_options->add_item(TTR("Rename..."), FILE_RENAME);
+ file_options->add_item(TTR("Duplicate..."), FILE_DUPLICATE);
}
- file_options->add_item(TTR("Move To.."), FILE_MOVE);
+ file_options->add_item(TTR("Move To..."), FILE_MOVE);
file_options->add_item(TTR("Delete"), FILE_REMOVE);
file_options->add_separator();
}
- file_options->add_item(TTR("New Folder.."), FILE_NEW_FOLDER);
+ file_options->add_item(TTR("New Folder..."), FILE_NEW_FOLDER);
+ file_options->add_item(TTR("New Script..."), FILE_NEW_SCRIPT);
file_options->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER);
file_options->set_position(files->get_global_position() + p_pos);
@@ -1630,7 +1682,8 @@ void FileSystemDock::_rmb_pressed(const Vector2 &p_pos) {
file_options->clear();
file_options->set_size(Size2(1, 1));
- file_options->add_item(TTR("New Folder.."), FILE_NEW_FOLDER);
+ file_options->add_item(TTR("New Folder..."), FILE_NEW_FOLDER);
+ file_options->add_item(TTR("New Script..."), FILE_NEW_SCRIPT);
file_options->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER);
file_options->set_position(files->get_global_position() + p_pos);
file_options->popup();
@@ -1660,6 +1713,8 @@ void FileSystemDock::_files_gui_input(Ref<InputEvent> p_event) {
_file_option(FILE_COPY_PATH);
} else if (ED_IS_SHORTCUT("filesystem_dock/delete", p_event)) {
_file_option(FILE_REMOVE);
+ } else if (ED_IS_SHORTCUT("filesystem_dock/rename", p_event)) {
+ _file_option(FILE_RENAME);
}
}
}
@@ -1770,6 +1825,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
ED_SHORTCUT("filesystem_dock/copy_path", TTR("Copy Path"), KEY_MASK_CMD | KEY_C);
ED_SHORTCUT("filesystem_dock/duplicate", TTR("Duplicate..."), KEY_MASK_CMD | KEY_D);
ED_SHORTCUT("filesystem_dock/delete", TTR("Delete"), KEY_DELETE);
+ ED_SHORTCUT("filesystem_dock/rename", TTR("Rename"));
HBoxContainer *toolbar_hbc = memnew(HBoxContainer);
add_child(toolbar_hbc);
@@ -1889,7 +1945,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
add_child(scanning_vb);
Label *slabel = memnew(Label);
- slabel->set_text(TTR("Scanning Files,\nPlease Wait.."));
+ slabel->set_text(TTR("Scanning Files,\nPlease Wait..."));
slabel->set_align(Label::ALIGN_CENTER);
scanning_vb->add_child(slabel);
@@ -1943,6 +1999,10 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
make_dir_dialog->register_text_enter(make_dir_dialog_text);
make_dir_dialog->connect("confirmed", this, "_make_dir_confirm");
+ make_script_dialog_text = memnew(ScriptCreateDialog);
+ make_script_dialog_text->set_title(TTR("Create Script"));
+ add_child(make_script_dialog_text);
+
updating_tree = false;
initialized = false;
import_dock_needs_update = false;
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 377316d1b..e59d4c96e 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -50,6 +50,7 @@
#include "dependency_editor.h"
#include "editor_dir_dialog.h"
#include "editor_file_system.h"
+#include "script_create_dialog.h"
class EditorNode;
@@ -75,6 +76,7 @@ private:
FILE_REIMPORT,
FILE_INFO,
FILE_NEW_FOLDER,
+ FILE_NEW_SCRIPT,
FILE_SHOW_IN_EXPLORER,
FILE_COPY_PATH
};
@@ -126,6 +128,7 @@ private:
LineEdit *duplicate_dialog_text;
ConfirmationDialog *make_dir_dialog;
LineEdit *make_dir_dialog_text;
+ ScriptCreateDialog *make_script_dialog_text;
class FileOrFolder {
public:
@@ -176,12 +179,13 @@ private:
void _file_selected();
void _dir_selected();
- void _get_all_files_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files) const;
+ void _get_all_items_in_dir(EditorFileSystemDirectory *efsd, Vector<String> &files, Vector<String> &folders) const;
void _find_remaps(EditorFileSystemDirectory *efsd, const Map<String, String> &renames, Vector<String> &to_remaps) const;
- void _try_move_item(const FileOrFolder &p_item, const String &p_new_path, Map<String, String> &p_renames) const;
+ void _try_move_item(const FileOrFolder &p_item, const String &p_new_path, Map<String, String> &p_file_renames, Map<String, String> &p_folder_renames) const;
void _try_duplicate_item(const FileOrFolder &p_item, const String &p_new_path) const;
void _update_dependencies_after_move(const Map<String, String> &p_renames) const;
void _update_resource_paths_after_move(const Map<String, String> &p_renames) const;
+ void _update_favorite_dirs_list_after_move(const Map<String, String> &p_renames) const;
void _make_dir_confirm();
void _rename_operation_confirm();
diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp
new file mode 100644
index 000000000..74ea46838
--- /dev/null
+++ b/editor/find_in_files.cpp
@@ -0,0 +1,824 @@
+/*************************************************************************/
+/* find_in_files.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (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 "find_in_files.h"
+#include "core/os/dir_access.h"
+#include "core/os/os.h"
+#include "editor_scale.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/check_box.h"
+#include "scene/gui/file_dialog.h"
+#include "scene/gui/grid_container.h"
+#include "scene/gui/item_list.h"
+#include "scene/gui/label.h"
+#include "scene/gui/line_edit.h"
+#include "scene/gui/progress_bar.h"
+
+#define ROOT_PREFIX "res://"
+
+const char *FindInFiles::SIGNAL_RESULT_FOUND = "result_found";
+const char *FindInFiles::SIGNAL_FINISHED = "finished";
+
+// TODO Would be nice in Vector and PoolVectors
+template <typename T>
+inline void pop_back(T &container) {
+ container.resize(container.size() - 1);
+}
+
+// TODO Copied from TextEdit private, would be nice to extract it in a single place
+static bool is_text_char(CharType c) {
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+}
+
+FindInFiles::FindInFiles() {
+ _root_prefix = ROOT_PREFIX;
+ _extension_filter.insert("gd");
+ _extension_filter.insert("cs");
+ _searching = false;
+ _whole_words = true;
+ _match_case = true;
+}
+
+void FindInFiles::set_search_text(String p_pattern) {
+ _pattern = p_pattern;
+}
+
+void FindInFiles::set_whole_words(bool p_whole_word) {
+ _whole_words = p_whole_word;
+}
+
+void FindInFiles::set_match_case(bool p_match_case) {
+ _match_case = p_match_case;
+}
+
+void FindInFiles::set_folder(String folder) {
+ _root_dir = folder;
+}
+
+void FindInFiles::set_filter(const Set<String> &exts) {
+ _extension_filter = exts;
+}
+
+void FindInFiles::_notification(int p_notification) {
+ if (p_notification == NOTIFICATION_PROCESS) {
+ _process();
+ }
+}
+
+void FindInFiles::start() {
+ if (_pattern == "") {
+ print_line("Nothing to search, pattern is empty");
+ emit_signal(SIGNAL_FINISHED);
+ return;
+ }
+ if (_extension_filter.size() == 0) {
+ print_line("Nothing to search, filter matches no files");
+ emit_signal(SIGNAL_FINISHED);
+ return;
+ }
+
+ // Init search
+ _current_dir = "";
+ PoolStringArray init_folder;
+ init_folder.append(_root_dir);
+ _folders_stack.push_back(init_folder);
+
+ _initial_files_count = 0;
+
+ _searching = true;
+ set_process(true);
+}
+
+void FindInFiles::stop() {
+ _searching = false;
+ _current_dir = "";
+ set_process(false);
+}
+
+void FindInFiles::_process() {
+ // This part can be moved to a thread if needed
+
+ OS &os = *OS::get_singleton();
+ float duration = 0.0;
+ while (duration < 1.0 / 120.0) {
+ float time_before = os.get_ticks_msec();
+ _iterate();
+ duration += (os.get_ticks_msec() - time_before);
+ }
+}
+
+void FindInFiles::_iterate() {
+
+ if (_folders_stack.size() != 0) {
+
+ // Scan folders first so we can build a list of files and have progress info later
+
+ PoolStringArray &folders_to_scan = _folders_stack[_folders_stack.size() - 1];
+
+ if (folders_to_scan.size() != 0) {
+ // Scan one folder below
+
+ String folder_name = folders_to_scan[folders_to_scan.size() - 1];
+ pop_back(folders_to_scan);
+
+ _current_dir = _current_dir.plus_file(folder_name);
+
+ PoolStringArray sub_dirs;
+ _scan_dir(_root_prefix + _current_dir, sub_dirs);
+
+ _folders_stack.push_back(sub_dirs);
+
+ } else {
+ // Go back one level
+
+ pop_back(_folders_stack);
+ _current_dir = _current_dir.get_base_dir();
+
+ if (_folders_stack.size() == 0) {
+ // All folders scanned
+ _initial_files_count = _files_to_scan.size();
+ }
+ }
+
+ } else if (_files_to_scan.size() != 0) {
+
+ // Then scan files
+
+ String fpath = _files_to_scan[_files_to_scan.size() - 1];
+ pop_back(_files_to_scan);
+ _scan_file(fpath);
+
+ } else {
+ print_line("Search complete");
+ set_process(false);
+ _current_dir = "";
+ _searching = false;
+ emit_signal(SIGNAL_FINISHED);
+ }
+}
+
+float FindInFiles::get_progress() const {
+ if (_initial_files_count != 0) {
+ return static_cast<float>(_initial_files_count - _files_to_scan.size()) / static_cast<float>(_initial_files_count);
+ }
+ return 0;
+}
+
+void FindInFiles::_scan_dir(String path, PoolStringArray &out_folders) {
+
+ DirAccess *dir = DirAccess::open(path);
+ if (dir == NULL) {
+ print_line("Cannot open directory! " + path);
+ return;
+ }
+
+ dir->list_dir_begin();
+
+ for (int i = 0; i < 1000; ++i) {
+ String file = dir->get_next();
+
+ if (file == "")
+ break;
+
+ // Ignore special dirs and hidden dirs (such as .git and .import)
+ if (file == "." || file == ".." || file.begins_with("."))
+ continue;
+
+ if (dir->current_is_dir())
+ out_folders.append(file);
+
+ else {
+ String file_ext = file.get_extension();
+ if (_extension_filter.has(file_ext)) {
+ _files_to_scan.push_back(path.plus_file(file));
+ }
+ }
+ }
+}
+
+void FindInFiles::_scan_file(String fpath) {
+
+ FileAccess *f = FileAccess::open(fpath, FileAccess::READ);
+ if (f == NULL) {
+ print_line(String("Cannot open file ") + fpath);
+ return;
+ }
+
+ int line_number = 0;
+
+ while (!f->eof_reached()) {
+
+ // line number starts at 1
+ ++line_number;
+
+ int begin = 0;
+ int end = 0;
+
+ String line = f->get_line();
+
+ // Find all occurrences in the current line
+ while (true) {
+ begin = _match_case ? line.find(_pattern, end) : line.findn(_pattern, end);
+
+ if (begin == -1)
+ break;
+
+ end = begin + _pattern.length();
+
+ if (_whole_words) {
+ if (begin > 0 && is_text_char(line[begin - 1])) {
+ continue;
+ }
+ if (end < line.size() && is_text_char(line[end])) {
+ continue;
+ }
+ }
+
+ emit_signal(SIGNAL_RESULT_FOUND, fpath, line_number, begin, end, line);
+ }
+ }
+
+ f->close();
+}
+
+void FindInFiles::_bind_methods() {
+
+ ADD_SIGNAL(MethodInfo(SIGNAL_RESULT_FOUND,
+ PropertyInfo(Variant::STRING, "path"),
+ PropertyInfo(Variant::INT, "line_number"),
+ PropertyInfo(Variant::INT, "begin"),
+ PropertyInfo(Variant::INT, "end"),
+ PropertyInfo(Variant::STRING, "text")));
+
+ ADD_SIGNAL(MethodInfo(SIGNAL_FINISHED));
+}
+
+//-----------------------------------------------------------------------------
+const char *FindInFilesDialog::SIGNAL_FIND_REQUESTED = "find_requested";
+const char *FindInFilesDialog::SIGNAL_REPLACE_REQUESTED = "replace_requested";
+
+FindInFilesDialog::FindInFilesDialog() {
+
+ set_custom_minimum_size(Size2(400, 190));
+ set_resizable(true);
+ set_title(TTR("Find in files"));
+
+ VBoxContainer *vbc = memnew(VBoxContainer);
+ vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 8 * EDSCALE);
+ vbc->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 8 * EDSCALE);
+ vbc->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -8 * EDSCALE);
+ vbc->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -8 * EDSCALE);
+ add_child(vbc);
+
+ GridContainer *gc = memnew(GridContainer);
+ gc->set_columns(2);
+ vbc->add_child(gc);
+
+ Label *find_label = memnew(Label);
+ find_label->set_text(TTR("Find: "));
+ gc->add_child(find_label);
+
+ _search_text_line_edit = memnew(LineEdit);
+ _search_text_line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ _search_text_line_edit->connect("text_changed", this, "_on_search_text_modified");
+ _search_text_line_edit->connect("text_entered", this, "_on_search_text_entered");
+ gc->add_child(_search_text_line_edit);
+
+ {
+ Control *placeholder = memnew(Control);
+ gc->add_child(placeholder);
+ }
+
+ {
+ HBoxContainer *hbc = memnew(HBoxContainer);
+
+ _whole_words_checkbox = memnew(CheckBox);
+ _whole_words_checkbox->set_text(TTR("Whole words"));
+ _whole_words_checkbox->set_pressed(true);
+ hbc->add_child(_whole_words_checkbox);
+
+ _match_case_checkbox = memnew(CheckBox);
+ _match_case_checkbox->set_text(TTR("Match case"));
+ _match_case_checkbox->set_pressed(true);
+ hbc->add_child(_match_case_checkbox);
+
+ gc->add_child(hbc);
+ }
+
+ Label *folder_label = memnew(Label);
+ folder_label->set_text(TTR("Folder: "));
+ gc->add_child(folder_label);
+
+ {
+ HBoxContainer *hbc = memnew(HBoxContainer);
+
+ Label *prefix_label = memnew(Label);
+ prefix_label->set_text(ROOT_PREFIX);
+ hbc->add_child(prefix_label);
+
+ _folder_line_edit = memnew(LineEdit);
+ _folder_line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ hbc->add_child(_folder_line_edit);
+
+ Button *folder_button = memnew(Button);
+ folder_button->set_text("...");
+ folder_button->connect("pressed", this, "_on_folder_button_pressed");
+ hbc->add_child(folder_button);
+
+ _folder_dialog = memnew(FileDialog);
+ _folder_dialog->set_mode(FileDialog::MODE_OPEN_DIR);
+ _folder_dialog->connect("dir_selected", this, "_on_folder_selected");
+ add_child(_folder_dialog);
+
+ gc->add_child(hbc);
+ }
+
+ Label *filter_label = memnew(Label);
+ filter_label->set_text(TTR("Filter: "));
+ gc->add_child(filter_label);
+
+ {
+ HBoxContainer *hbc = memnew(HBoxContainer);
+
+ Vector<String> exts;
+ exts.push_back("gd");
+ exts.push_back("cs");
+
+ for (int i = 0; i < exts.size(); ++i) {
+ CheckBox *cb = memnew(CheckBox);
+ cb->set_text(exts[i]);
+ cb->set_pressed(true);
+ hbc->add_child(cb);
+ _filters.push_back(cb);
+ }
+
+ gc->add_child(hbc);
+ }
+
+ {
+ Control *placeholder = memnew(Control);
+ placeholder->set_custom_minimum_size(Size2(0, EDSCALE * 16));
+ vbc->add_child(placeholder);
+ }
+
+ {
+ HBoxContainer *hbc = memnew(HBoxContainer);
+ hbc->set_alignment(HBoxContainer::ALIGN_CENTER);
+
+ _find_button = memnew(Button);
+ _find_button->set_text(TTR("Find..."));
+ _find_button->connect("pressed", this, "_on_find_button_pressed");
+ _find_button->set_disabled(true);
+ hbc->add_child(_find_button);
+
+ {
+ Control *placeholder = memnew(Control);
+ placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0));
+ hbc->add_child(placeholder);
+ }
+
+ _replace_button = memnew(Button);
+ _replace_button->set_text(TTR("Replace..."));
+ _replace_button->connect("pressed", this, "_on_replace_button_pressed");
+ _replace_button->set_disabled(true);
+ hbc->add_child(_replace_button);
+
+ {
+ Control *placeholder = memnew(Control);
+ placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0));
+ hbc->add_child(placeholder);
+ }
+
+ Button *cancel_button = memnew(Button);
+ cancel_button->set_text(TTR("Cancel"));
+ cancel_button->connect("pressed", this, "hide");
+ hbc->add_child(cancel_button);
+
+ vbc->add_child(hbc);
+ }
+}
+
+void FindInFilesDialog::set_search_text(String text) {
+ _search_text_line_edit->set_text(text);
+}
+
+String FindInFilesDialog::get_search_text() const {
+ String text = _search_text_line_edit->get_text();
+ return text.strip_edges();
+}
+
+bool FindInFilesDialog::is_match_case() const {
+ return _match_case_checkbox->is_pressed();
+}
+
+bool FindInFilesDialog::is_whole_words() const {
+ return _whole_words_checkbox->is_pressed();
+}
+
+String FindInFilesDialog::get_folder() const {
+ String text = _folder_line_edit->get_text();
+ return text.strip_edges();
+}
+
+Set<String> FindInFilesDialog::get_filter() const {
+ Set<String> filters;
+ for (int i = 0; i < _filters.size(); ++i) {
+ CheckBox *cb = _filters[i];
+ if (cb->is_pressed()) {
+ filters.insert(_filters[i]->get_text());
+ }
+ }
+ return filters;
+}
+
+void FindInFilesDialog::_notification(int p_what) {
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
+ if (is_visible()) {
+ // Doesn't work more than once if not deferred...
+ _search_text_line_edit->call_deferred("grab_focus");
+ _search_text_line_edit->select_all();
+ }
+ }
+}
+
+void FindInFilesDialog::_on_folder_button_pressed() {
+ _folder_dialog->popup_centered_ratio();
+}
+
+void FindInFilesDialog::_on_find_button_pressed() {
+ emit_signal(SIGNAL_FIND_REQUESTED);
+ hide();
+}
+
+void FindInFilesDialog::_on_replace_button_pressed() {
+ emit_signal(SIGNAL_REPLACE_REQUESTED);
+ hide();
+}
+
+void FindInFilesDialog::_on_search_text_modified(String text) {
+
+ ERR_FAIL_COND(!_find_button);
+ ERR_FAIL_COND(!_replace_button);
+
+ _find_button->set_disabled(get_search_text().empty());
+ _replace_button->set_disabled(get_search_text().empty());
+}
+
+void FindInFilesDialog::_on_search_text_entered(String text) {
+ // This allows to trigger a global search without leaving the keyboard
+ if (!_find_button->is_disabled())
+ _on_find_button_pressed();
+}
+
+void FindInFilesDialog::_on_folder_selected(String path) {
+ int i = path.find("://");
+ if (i != -1)
+ path = path.right(i + 3);
+ _folder_line_edit->set_text(path);
+}
+
+void FindInFilesDialog::_bind_methods() {
+
+ ClassDB::bind_method("_on_folder_button_pressed", &FindInFilesDialog::_on_folder_button_pressed);
+ ClassDB::bind_method("_on_find_button_pressed", &FindInFilesDialog::_on_find_button_pressed);
+ ClassDB::bind_method("_on_replace_button_pressed", &FindInFilesDialog::_on_replace_button_pressed);
+ ClassDB::bind_method("_on_folder_selected", &FindInFilesDialog::_on_folder_selected);
+ ClassDB::bind_method("_on_search_text_modified", &FindInFilesDialog::_on_search_text_modified);
+ ClassDB::bind_method("_on_search_text_entered", &FindInFilesDialog::_on_search_text_entered);
+
+ ADD_SIGNAL(MethodInfo(SIGNAL_FIND_REQUESTED));
+ ADD_SIGNAL(MethodInfo(SIGNAL_REPLACE_REQUESTED));
+}
+
+//-----------------------------------------------------------------------------
+const char *FindInFilesPanel::SIGNAL_RESULT_SELECTED = "result_selected";
+const char *FindInFilesPanel::SIGNAL_FILES_MODIFIED = "files_modified";
+
+FindInFilesPanel::FindInFilesPanel() {
+
+ _finder = memnew(FindInFiles);
+ _finder->connect(FindInFiles::SIGNAL_RESULT_FOUND, this, "_on_result_found");
+ _finder->connect(FindInFiles::SIGNAL_FINISHED, this, "_on_finished");
+ add_child(_finder);
+
+ VBoxContainer *vbc = memnew(VBoxContainer);
+ vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 0);
+ vbc->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 0);
+ vbc->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0);
+ vbc->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0);
+ add_child(vbc);
+
+ {
+ HBoxContainer *hbc = memnew(HBoxContainer);
+
+ Label *find_label = memnew(Label);
+ find_label->set_text(TTR("Find: "));
+ hbc->add_child(find_label);
+
+ _search_text_label = memnew(Label);
+ _search_text_label->add_font_override("font", get_font("source", "EditorFonts"));
+ hbc->add_child(_search_text_label);
+
+ _progress_bar = memnew(ProgressBar);
+ _progress_bar->set_h_size_flags(SIZE_EXPAND_FILL);
+ hbc->add_child(_progress_bar);
+ set_progress_visible(false);
+
+ _status_label = memnew(Label);
+ hbc->add_child(_status_label);
+
+ _cancel_button = memnew(Button);
+ _cancel_button->set_text(TTR("Cancel"));
+ _cancel_button->connect("pressed", this, "_on_cancel_button_clicked");
+ _cancel_button->set_disabled(true);
+ hbc->add_child(_cancel_button);
+
+ vbc->add_child(hbc);
+ }
+
+ // In the future, this should be replaced by a more specific list container,
+ // which can highlight text regions and change opacity for enabled/disabled states
+ _results_display = memnew(ItemList);
+ _results_display->add_font_override("font", get_font("source", "EditorFonts"));
+ _results_display->set_v_size_flags(SIZE_EXPAND_FILL);
+ _results_display->connect("item_selected", this, "_on_result_selected");
+ vbc->add_child(_results_display);
+
+ {
+ _replace_container = memnew(HBoxContainer);
+
+ Label *replace_label = memnew(Label);
+ replace_label->set_text(TTR("Replace: "));
+ _replace_container->add_child(replace_label);
+
+ _replace_line_edit = memnew(LineEdit);
+ _replace_line_edit->set_h_size_flags(SIZE_EXPAND_FILL);
+ _replace_line_edit->connect("text_changed", this, "_on_replace_text_changed");
+ _replace_container->add_child(_replace_line_edit);
+
+ _replace_all_button = memnew(Button);
+ _replace_all_button->set_text(TTR("Replace all (no undo)"));
+ _replace_all_button->connect("pressed", this, "_on_replace_all_clicked");
+ _replace_container->add_child(_replace_all_button);
+
+ _replace_container->hide();
+
+ vbc->add_child(_replace_container);
+ }
+}
+
+void FindInFilesPanel::set_with_replace(bool with_replace) {
+
+ _replace_container->set_visible(with_replace);
+}
+
+void FindInFilesPanel::start_search() {
+
+ _results_display->clear();
+ _status_label->set_text(TTR("Searching..."));
+ _search_text_label->set_text(_finder->get_search_text());
+
+ set_process(true);
+ set_progress_visible(true);
+
+ _finder->start();
+
+ update_replace_buttons();
+ _cancel_button->set_disabled(false);
+}
+
+void FindInFilesPanel::stop_search() {
+
+ _finder->stop();
+
+ _status_label->set_text("");
+ update_replace_buttons();
+ set_progress_visible(false);
+ _cancel_button->set_disabled(true);
+}
+
+void FindInFilesPanel::_notification(int p_what) {
+ if (p_what == NOTIFICATION_PROCESS) {
+ _progress_bar->set_as_ratio(_finder->get_progress());
+ }
+}
+
+void FindInFilesPanel::_on_result_found(String fpath, int line_number, int begin, int end, String text) {
+
+ int i = _results_display->get_item_count();
+ _results_display->add_item(fpath + ": " + String::num(line_number) + ": " + text.replace("\t", " "));
+ _results_display->set_item_metadata(i, varray(fpath, line_number, begin, end));
+}
+
+void FindInFilesPanel::_on_finished() {
+
+ _status_label->set_text(TTR("Search complete"));
+ update_replace_buttons();
+ set_progress_visible(false);
+ _cancel_button->set_disabled(true);
+}
+
+void FindInFilesPanel::_on_cancel_button_clicked() {
+ stop_search();
+}
+
+void FindInFilesPanel::_on_result_selected(int i) {
+
+ Array meta = _results_display->get_item_metadata(i);
+ emit_signal(SIGNAL_RESULT_SELECTED, meta[0], meta[1], meta[2], meta[3]);
+}
+
+void FindInFilesPanel::_on_replace_text_changed(String text) {
+ update_replace_buttons();
+}
+
+void FindInFilesPanel::_on_replace_all_clicked() {
+
+ String replace_text = get_replace_text();
+ ERR_FAIL_COND(replace_text.empty());
+
+ String last_fpath;
+ PoolIntArray locations;
+ PoolStringArray modified_files;
+
+ for (int i = 0; i < _results_display->get_item_count(); ++i) {
+
+ Array meta = _results_display->get_item_metadata(i);
+
+ String fpath = meta[0];
+
+ // Results are sorted by file, so we can batch replaces
+ if (fpath != last_fpath) {
+ if (locations.size() != 0) {
+ apply_replaces_in_file(last_fpath, locations, replace_text);
+ modified_files.append(last_fpath);
+ locations.resize(0);
+ }
+ }
+
+ locations.append(meta[1]); // line_number
+ locations.append(meta[2]); // begin
+ locations.append(meta[3]); // end
+
+ last_fpath = fpath;
+ }
+
+ if (locations.size() != 0) {
+ apply_replaces_in_file(last_fpath, locations, replace_text);
+ modified_files.append(last_fpath);
+ }
+
+ // Hide replace bar so we can't trigger the action twice without doing a new search
+ set_with_replace(false);
+
+ emit_signal(SIGNAL_FILES_MODIFIED, modified_files);
+}
+
+// Same as get_line, but preserves line ending characters
+class ConservativeGetLine {
+public:
+ String get_line(FileAccess *f) {
+
+ _line_buffer.clear();
+
+ CharType c = f->get_8();
+
+ while (!f->eof_reached()) {
+
+ if (c == '\n') {
+ _line_buffer.push_back(c);
+ _line_buffer.push_back(0);
+ return String::utf8(_line_buffer.ptr());
+
+ } else if (c == '\0') {
+ _line_buffer.push_back(c);
+ return String::utf8(_line_buffer.ptr());
+
+ } else if (c != '\r') {
+ _line_buffer.push_back(c);
+ }
+
+ c = f->get_8();
+ }
+
+ _line_buffer.push_back(0);
+ return String::utf8(_line_buffer.ptr());
+ }
+
+private:
+ Vector<char> _line_buffer;
+};
+
+void FindInFilesPanel::apply_replaces_in_file(String fpath, PoolIntArray locations, String text) {
+
+ ERR_FAIL_COND(locations.size() % 3 != 0);
+
+ //print_line(String("Replacing {0} occurrences in {1}").format(varray(fpath, locations.size() / 3)));
+
+ // If the file is already open, I assume the editor will reload it.
+ // If there are unsaved changes, the user will be asked on focus,
+ // however that means either loosing changes or loosing replaces.
+
+ FileAccess *f = FileAccess::open(fpath, FileAccess::READ);
+ ERR_FAIL_COND(f == NULL);
+
+ String buffer;
+ int current_line = 1;
+
+ ConservativeGetLine conservative;
+
+ String line = conservative.get_line(f);
+
+ PoolIntArray::Read locations_read = locations.read();
+ for (int i = 0; i < locations.size(); i += 3) {
+
+ int repl_line_number = locations_read[i];
+ int repl_begin = locations_read[i + 1];
+ int repl_end = locations_read[i + 2];
+
+ while (current_line < repl_line_number) {
+ buffer += line;
+ line = conservative.get_line(f);
+ ++current_line;
+ }
+
+ line = line.left(repl_begin) + text + line.right(repl_end);
+ }
+
+ buffer += line;
+
+ while (!f->eof_reached()) {
+ buffer += conservative.get_line(f);
+ }
+
+ // Now the modified contents are in the buffer, rewrite the file with our changes
+
+ Error err = f->reopen(fpath, FileAccess::WRITE);
+ ERR_FAIL_COND(err != OK);
+
+ f->store_string(buffer);
+
+ f->close();
+}
+
+String FindInFilesPanel::get_replace_text() {
+ return _replace_line_edit->get_text().strip_edges();
+}
+
+void FindInFilesPanel::update_replace_buttons() {
+
+ String text = get_replace_text();
+ bool disabled = text.empty() || _finder->is_searching();
+
+ _replace_all_button->set_disabled(disabled);
+}
+
+void FindInFilesPanel::set_progress_visible(bool visible) {
+ _progress_bar->set_self_modulate(Color(1, 1, 1, visible ? 1 : 0));
+}
+
+void FindInFilesPanel::_bind_methods() {
+
+ ClassDB::bind_method("_on_result_found", &FindInFilesPanel::_on_result_found);
+ ClassDB::bind_method("_on_finished", &FindInFilesPanel::_on_finished);
+ ClassDB::bind_method("_on_cancel_button_clicked", &FindInFilesPanel::_on_cancel_button_clicked);
+ ClassDB::bind_method("_on_result_selected", &FindInFilesPanel::_on_result_selected);
+ ClassDB::bind_method("_on_replace_text_changed", &FindInFilesPanel::_on_replace_text_changed);
+ ClassDB::bind_method("_on_replace_all_clicked", &FindInFilesPanel::_on_replace_all_clicked);
+
+ ADD_SIGNAL(MethodInfo(SIGNAL_RESULT_SELECTED,
+ PropertyInfo(Variant::STRING, "path"),
+ PropertyInfo(Variant::INT, "line_number"),
+ PropertyInfo(Variant::INT, "begin"),
+ PropertyInfo(Variant::INT, "end")));
+
+ ADD_SIGNAL(MethodInfo(SIGNAL_FILES_MODIFIED, PropertyInfo(Variant::STRING, "paths")));
+}
diff --git a/editor/find_in_files.h b/editor/find_in_files.h
new file mode 100644
index 000000000..d57184960
--- /dev/null
+++ b/editor/find_in_files.h
@@ -0,0 +1,184 @@
+/*************************************************************************/
+/* find_in_files.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (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 FIND_IN_FILES_H
+#define FIND_IN_FILES_H
+
+#include "scene/gui/dialogs.h"
+
+// Performs the actual search
+class FindInFiles : public Node {
+ GDCLASS(FindInFiles, Node)
+public:
+ static const char *SIGNAL_RESULT_FOUND;
+ static const char *SIGNAL_FINISHED;
+
+ FindInFiles();
+
+ void set_search_text(String p_pattern);
+ void set_whole_words(bool p_whole_word);
+ void set_match_case(bool p_match_case);
+ void set_folder(String folder);
+ void set_filter(const Set<String> &exts);
+
+ String get_search_text() const { return _pattern; }
+
+ bool is_whole_words() const { return _whole_words; }
+ bool is_match_case() const { return _match_case; }
+
+ void start();
+ void stop();
+
+ bool is_searching() const { return _searching; }
+ float get_progress() const;
+
+protected:
+ void _notification(int p_notification);
+
+ static void _bind_methods();
+
+private:
+ void _process();
+ void _iterate();
+ void _scan_dir(String path, PoolStringArray &out_folders);
+ void _scan_file(String fpath);
+
+ // Config
+ String _pattern;
+ Set<String> _extension_filter;
+ String _root_prefix;
+ String _root_dir;
+ bool _whole_words;
+ bool _match_case;
+
+ // State
+ bool _searching;
+ String _current_dir;
+ Vector<PoolStringArray> _folders_stack;
+ Vector<String> _files_to_scan;
+ int _initial_files_count;
+};
+
+class LineEdit;
+class CheckBox;
+class FileDialog;
+
+// Prompts search parameters
+class FindInFilesDialog : public WindowDialog {
+ GDCLASS(FindInFilesDialog, WindowDialog)
+public:
+ static const char *SIGNAL_FIND_REQUESTED;
+ static const char *SIGNAL_REPLACE_REQUESTED;
+
+ FindInFilesDialog();
+
+ void set_search_text(String text);
+
+ String get_search_text() const;
+ bool is_match_case() const;
+ bool is_whole_words() const;
+ String get_folder() const;
+ Set<String> get_filter() const;
+
+protected:
+ static void _bind_methods();
+
+ void _notification(int p_what);
+
+private:
+ void _on_folder_button_pressed();
+ void _on_find_button_pressed();
+ void _on_replace_button_pressed();
+ void _on_folder_selected(String path);
+ void _on_search_text_modified(String text);
+ void _on_search_text_entered(String text);
+
+ LineEdit *_search_text_line_edit;
+ LineEdit *_folder_line_edit;
+ Vector<CheckBox *> _filters;
+ CheckBox *_match_case_checkbox;
+ CheckBox *_whole_words_checkbox;
+ Button *_find_button;
+ Button *_replace_button;
+ FileDialog *_folder_dialog;
+};
+
+class Button;
+class ItemList;
+class ProgressBar;
+
+// Display search results
+class FindInFilesPanel : public Control {
+ GDCLASS(FindInFilesPanel, Control)
+public:
+ static const char *SIGNAL_RESULT_SELECTED;
+ static const char *SIGNAL_FILES_MODIFIED;
+
+ FindInFilesPanel();
+
+ FindInFiles *get_finder() const { return _finder; }
+
+ void set_with_replace(bool with_replace);
+
+ void start_search();
+ void stop_search();
+
+protected:
+ static void _bind_methods();
+
+ void _notification(int p_what);
+
+private:
+ void _on_result_found(String fpath, int line_number, int begin, int end, String text);
+ void _on_finished();
+ void _on_cancel_button_clicked();
+ void _on_result_selected(int i);
+ void _on_replace_text_changed(String text);
+ void _on_replace_all_clicked();
+
+ void apply_replaces_in_file(String fpath, PoolIntArray locations, String text);
+
+ void update_replace_buttons();
+ String get_replace_text();
+ void set_progress_visible(bool visible);
+
+ FindInFiles *_finder;
+ Label *_search_text_label;
+ ItemList *_results_display;
+ Label *_status_label;
+ Button *_cancel_button;
+ ProgressBar *_progress_bar;
+
+ HBoxContainer *_replace_container;
+ LineEdit *_replace_line_edit;
+ Button *_replace_all_button;
+};
+
+#endif // FIND_IN_FILES_H
diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp
index bac18be4a..8443311a5 100644
--- a/editor/groups_editor.cpp
+++ b/editor/groups_editor.cpp
@@ -29,12 +29,426 @@
/*************************************************************************/
#include "groups_editor.h"
-
+#include "editor/scene_tree_editor.h"
#include "editor_node.h"
#include "scene/gui/box_container.h"
#include "scene/gui/label.h"
#include "scene/resources/packed_scene.h"
+void GroupDialog::ok_pressed() {
+}
+
+void GroupDialog::_cancel_pressed() {
+}
+
+void GroupDialog::_group_selected() {
+ nodes_to_add->clear();
+ add_node_root = nodes_to_add->create_item();
+
+ nodes_to_remove->clear();
+ remove_node_root = nodes_to_remove->create_item();
+
+ if (!groups->is_anything_selected()) {
+ return;
+ }
+
+ selected_group = groups->get_selected()->get_text(0);
+ _load_nodes(scene_tree->get_edited_scene_root());
+}
+
+void GroupDialog::_load_nodes(Node *p_current) {
+ String item_name = p_current->get_name();
+ if (p_current != scene_tree->get_edited_scene_root()) {
+ item_name = String(p_current->get_parent()->get_name()) + "/" + String(item_name);
+ }
+
+ bool keep = true;
+ Node *root = scene_tree->get_edited_scene_root();
+ Node *owner = p_current->get_owner();
+ if (owner != root && p_current != root && !owner && !root->is_editable_instance(owner)) {
+ keep = false;
+ }
+
+ TreeItem *node;
+ NodePath path = scene_tree->get_edited_scene_root()->get_path_to(p_current);
+ if (keep && p_current->is_in_group(selected_group)) {
+ if (remove_filter->get_text().is_subsequence_ofi(String(p_current->get_name()))) {
+ node = nodes_to_remove->create_item(remove_node_root);
+ keep = true;
+ } else {
+ keep = false;
+ }
+ } else if (keep && add_filter->get_text().is_subsequence_ofi(String(p_current->get_name()))) {
+ node = nodes_to_add->create_item(add_node_root);
+ keep = true;
+ } else {
+ keep = false;
+ }
+
+ if (keep) {
+ node->set_text(0, item_name);
+ node->set_metadata(0, path);
+ node->set_tooltip(0, path);
+
+ Ref<Texture> icon;
+ if (p_current->has_meta("_editor_icon")) {
+ icon = p_current->get_meta("_editor_icon");
+ } else {
+ icon = get_icon((has_icon(p_current->get_class(), "EditorIcons") ? p_current->get_class() : String("Object")), "EditorIcons");
+ }
+ node->set_icon(0, icon);
+
+ if (!_can_edit(p_current, selected_group)) {
+ node->set_selectable(0, false);
+ node->set_custom_color(0, get_color("disabled_font_color", "Editor"));
+ }
+ }
+
+ for (int i = 0; i < p_current->get_child_count(); i++) {
+ _load_nodes(p_current->get_child(i));
+ }
+}
+
+bool GroupDialog::_can_edit(Node *p_node, String p_group) {
+ Node *n = p_node;
+ bool can_edit = true;
+ while (n) {
+ Ref<SceneState> ss = (n == EditorNode::get_singleton()->get_edited_scene()) ? n->get_scene_inherited_state() : n->get_scene_instance_state();
+ if (ss.is_valid()) {
+ int path = ss->find_node_by_path(n->get_path_to(p_node));
+ if (path != -1) {
+ if (ss->is_node_in_group(path, p_group)) {
+ can_edit = false;
+ }
+ }
+ }
+ n = n->get_owner();
+ }
+ return can_edit;
+}
+
+void GroupDialog::_add_pressed() {
+ TreeItem *selected = nodes_to_add->get_selected();
+
+ if (!selected) {
+ return;
+ }
+
+ while (selected) {
+ Node *node = scene_tree->get_edited_scene_root()->get_node(selected->get_metadata(0));
+ node->add_to_group(selected_group, true);
+
+ selected = nodes_to_add->get_next_selected(selected);
+ }
+
+ _group_selected();
+ EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->update_tree();
+}
+
+void GroupDialog::_removed_pressed() {
+ TreeItem *selected = nodes_to_remove->get_selected();
+
+ if (!selected) {
+ return;
+ }
+
+ while (selected) {
+ Node *node = scene_tree->get_edited_scene_root()->get_node(selected->get_metadata(0));
+ node->remove_from_group(selected_group);
+
+ selected = nodes_to_add->get_next_selected(selected);
+ }
+
+ _group_selected();
+ EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->update_tree();
+}
+
+void GroupDialog::_remove_filter_changed(const String &p_filter) {
+ _group_selected();
+}
+
+void GroupDialog::_add_filter_changed(const String &p_filter) {
+ _group_selected();
+}
+
+void GroupDialog::_add_group_pressed() {
+ _add_group(add_group_text->get_text());
+ add_group_text->clear();
+}
+
+void GroupDialog::_group_renamed() {
+ TreeItem *renamed_group = groups->get_edited();
+ if (!renamed_group) {
+ return;
+ }
+
+ String name = renamed_group->get_text(0).strip_edges();
+ for (TreeItem *E = groups_root->get_children(); E; E = E->get_next()) {
+ if (E != renamed_group && E->get_text(0) == name) {
+ renamed_group->set_text(0, selected_group);
+ error->set_text(TTR("Group name already exists."));
+ error->popup_centered();
+ return;
+ }
+ }
+
+ if (name == "") {
+ renamed_group->set_text(0, selected_group);
+ error->set_text(TTR("invalid Group name."));
+ error->popup_centered();
+ return;
+ }
+
+ List<Node *> nodes;
+ scene_tree->get_nodes_in_group(selected_group, &nodes);
+ bool removed_all = true;
+ for (List<Node *>::Element *E = nodes.front(); E; E = E->next()) {
+ Node *node = E->get();
+ if (_can_edit(node, selected_group)) {
+ node->remove_from_group(selected_group);
+ node->add_to_group(name, true);
+ } else {
+ removed_all = false;
+ }
+ }
+
+ if (!removed_all) {
+ _add_group(selected_group);
+ }
+
+ selected_group = renamed_group->get_text(0);
+ _group_selected();
+}
+
+void GroupDialog::_add_group(String p_name) {
+
+ String name = p_name.strip_edges();
+ if (name == "" || groups->search_item_text(name)) {
+ return;
+ }
+
+ TreeItem *new_group = groups->create_item(groups_root);
+ new_group->set_text(0, name);
+ new_group->add_button(0, get_icon("Remove", "EditorIcons"), 0);
+ new_group->set_editable(0, true);
+}
+
+void GroupDialog::_load_groups(Node *p_current) {
+ List<Node::GroupInfo> gi;
+ p_current->get_groups(&gi);
+
+ for (List<Node::GroupInfo>::Element *E = gi.front(); E; E = E->next()) {
+ if (!E->get().persistent) {
+ continue;
+ }
+ _add_group(E->get().name);
+ }
+
+ for (int i = 0; i < p_current->get_child_count(); i++) {
+ _load_groups(p_current->get_child(i));
+ }
+}
+
+void GroupDialog::_delete_group_pressed(Object *p_item, int p_column, int p_id) {
+ TreeItem *ti = Object::cast_to<TreeItem>(p_item);
+ if (!ti)
+ return;
+
+ String name = ti->get_text(0);
+
+ List<Node *> nodes;
+ scene_tree->get_nodes_in_group(name, &nodes);
+ bool removed_all = true;
+ for (List<Node *>::Element *E = nodes.front(); E; E = E->next()) {
+ if (_can_edit(E->get(), name)) {
+ E->get()->remove_from_group(name);
+ } else {
+ removed_all = false;
+ }
+ }
+
+ if (removed_all) {
+ if (selected_group == name) {
+ add_filter->clear();
+ remove_filter->clear();
+ nodes_to_remove->clear();
+ nodes_to_add->clear();
+ groups->deselect_all();
+ selected_group = "";
+ }
+ groups_root->remove_child(ti);
+ }
+ EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->update_tree();
+}
+
+void GroupDialog::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ add_button->set_icon(get_icon("Forward", "EditorIcons"));
+ remove_button->set_icon(get_icon("Back", "EditorIcons"));
+ } break;
+ }
+}
+
+void GroupDialog::edit() {
+
+ popup_centered(Size2(600, 400));
+
+ groups->clear();
+ groups_root = groups->create_item();
+
+ nodes_to_add->clear();
+ nodes_to_remove->clear();
+
+ add_group_text->clear();
+ add_filter->clear();
+ remove_filter->clear();
+
+ _load_groups(scene_tree->get_edited_scene_root());
+}
+
+void GroupDialog::_bind_methods() {
+ ClassDB::bind_method("_cancel", &GroupDialog::_cancel_pressed);
+
+ ClassDB::bind_method("_add_pressed", &GroupDialog::_add_pressed);
+ ClassDB::bind_method("_removed_pressed", &GroupDialog::_removed_pressed);
+ ClassDB::bind_method("_delete_group_pressed", &GroupDialog::_delete_group_pressed);
+
+ ClassDB::bind_method("_group_selected", &GroupDialog::_group_selected);
+ ClassDB::bind_method("_add_group_pressed", &GroupDialog::_add_group_pressed);
+
+ ClassDB::bind_method("_add_filter_changed", &GroupDialog::_add_filter_changed);
+ ClassDB::bind_method("_remove_filter_changed", &GroupDialog::_remove_filter_changed);
+
+ ClassDB::bind_method("_group_renamed", &GroupDialog::_group_renamed);
+}
+
+GroupDialog::GroupDialog() {
+
+ scene_tree = SceneTree::get_singleton();
+
+ VBoxContainer *vbc = memnew(VBoxContainer);
+ add_child(vbc);
+
+ HBoxContainer *hbc = memnew(HBoxContainer);
+ vbc->add_child(hbc);
+ hbc->set_v_size_flags(SIZE_EXPAND_FILL);
+
+ VBoxContainer *vbc_left = memnew(VBoxContainer);
+ hbc->add_child(vbc_left);
+ vbc_left->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ Label *group_title = memnew(Label);
+ group_title->set_text(TTR("Groups"));
+ vbc_left->add_child(group_title);
+
+ groups = memnew(Tree);
+ vbc_left->add_child(groups);
+ groups->set_hide_root(true);
+ groups->set_v_size_flags(SIZE_EXPAND_FILL);
+ groups->set_select_mode(Tree::SELECT_SINGLE);
+ groups->set_allow_reselect(true);
+ groups->set_allow_rmb_select(true);
+ groups->connect("item_selected", this, "_group_selected");
+ groups->connect("button_pressed", this, "_delete_group_pressed");
+ groups->connect("item_edited", this, "_group_renamed");
+
+ HBoxContainer *chbc = memnew(HBoxContainer);
+ vbc_left->add_child(chbc);
+ chbc->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ add_group_text = memnew(LineEdit);
+ chbc->add_child(add_group_text);
+ add_group_text->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ Button *add_group_button = memnew(Button);
+ add_group_button->set_text("Add");
+ chbc->add_child(add_group_button);
+ add_group_button->connect("pressed", this, "_add_group_pressed");
+
+ VBoxContainer *vbc_add = memnew(VBoxContainer);
+ hbc->add_child(vbc_add);
+ vbc_add->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ Label *out_of_group_title = memnew(Label);
+ out_of_group_title->set_text(TTR("Nodes not in Group"));
+ vbc_add->add_child(out_of_group_title);
+
+ nodes_to_add = memnew(Tree);
+ vbc_add->add_child(nodes_to_add);
+ nodes_to_add->set_hide_root(true);
+ nodes_to_add->set_hide_folding(true);
+ nodes_to_add->set_v_size_flags(SIZE_EXPAND_FILL);
+ nodes_to_add->set_select_mode(Tree::SELECT_MULTI);
+ nodes_to_add->connect("item_selected", this, "_nodes_to_add_selected");
+
+ HBoxContainer *add_filter_hbc = memnew(HBoxContainer);
+ add_filter_hbc->add_constant_override("separate", 0);
+ vbc_add->add_child(add_filter_hbc);
+
+ add_filter = memnew(LineEdit);
+ add_filter->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_filter->set_placeholder(TTR("Filter nodes"));
+ add_filter_hbc->add_child(add_filter);
+ add_filter->connect("text_changed", this, "_add_filter_changed");
+
+ VBoxContainer *vbc_buttons = memnew(VBoxContainer);
+ hbc->add_child(vbc_buttons);
+ vbc_buttons->set_h_size_flags(SIZE_SHRINK_CENTER);
+ vbc_buttons->set_v_size_flags(SIZE_SHRINK_CENTER);
+
+ add_button = memnew(ToolButton);
+ add_button->set_text(TTR("Add"));
+ add_button->connect("pressed", this, "_add_pressed");
+
+ vbc_buttons->add_child(add_button);
+ vbc_buttons->add_spacer();
+ vbc_buttons->add_spacer();
+ vbc_buttons->add_spacer();
+
+ remove_button = memnew(ToolButton);
+ remove_button->set_text(TTR("Remove"));
+ remove_button->connect("pressed", this, "_removed_pressed");
+
+ vbc_buttons->add_child(remove_button);
+
+ VBoxContainer *vbc_remove = memnew(VBoxContainer);
+ hbc->add_child(vbc_remove);
+ vbc_remove->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ Label *in_group_title = memnew(Label);
+ in_group_title->set_text(TTR("Nodes in Group"));
+ vbc_remove->add_child(in_group_title);
+
+ nodes_to_remove = memnew(Tree);
+ vbc_remove->add_child(nodes_to_remove);
+ nodes_to_remove->set_v_size_flags(SIZE_EXPAND_FILL);
+ nodes_to_remove->set_hide_root(true);
+ nodes_to_remove->set_hide_folding(true);
+ nodes_to_remove->set_select_mode(Tree::SELECT_MULTI);
+ nodes_to_remove->connect("item_selected", this, "_node_to_remove_selected");
+
+ HBoxContainer *remove_filter_hbc = memnew(HBoxContainer);
+ remove_filter_hbc->add_constant_override("separate", 0);
+ vbc_remove->add_child(remove_filter_hbc);
+
+ remove_filter = memnew(LineEdit);
+ remove_filter->set_h_size_flags(SIZE_EXPAND_FILL);
+ remove_filter->set_placeholder(TTR("Filter nodes"));
+ remove_filter_hbc->add_child(remove_filter);
+ remove_filter->connect("text_changed", this, "_remove_filter_changed");
+
+ set_title("Group Editor");
+ get_cancel()->hide();
+ set_as_toplevel(true);
+
+ error = memnew(ConfirmationDialog);
+ add_child(error);
+ error->get_ok()->set_text(TTR("Close"));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
void GroupsEditor::_add_group(const String &p_group) {
if (!node)
@@ -146,11 +560,22 @@ void GroupsEditor::set_current(Node *p_node) {
update_tree();
}
+void GroupsEditor::_show_group_dialog() {
+ group_dialog->edit();
+}
+
+void GroupsEditor::_group_dialog_closed() {
+ update_tree();
+}
+
void GroupsEditor::_bind_methods() {
ClassDB::bind_method("_add_group", &GroupsEditor::_add_group);
ClassDB::bind_method("_remove_group", &GroupsEditor::_remove_group);
ClassDB::bind_method("update_tree", &GroupsEditor::update_tree);
+
+ ClassDB::bind_method("_show_group_dialog", &GroupsEditor::_show_group_dialog);
+ ClassDB::bind_method("_group_dialog_closed", &GroupsEditor::_group_dialog_closed);
}
GroupsEditor::GroupsEditor() {
@@ -159,6 +584,16 @@ GroupsEditor::GroupsEditor() {
VBoxContainer *vbc = this;
+ group_dialog = memnew(GroupDialog);
+ group_dialog->set_as_toplevel(true);
+ add_child(group_dialog);
+ group_dialog->connect("popup_hide", this, "_group_dialog_closed");
+
+ Button *group_dialog_button = memnew(Button);
+ group_dialog_button->set_text(TTR("Manage Groups"));
+ vbc->add_child(group_dialog_button);
+ group_dialog_button->connect("pressed", this, "_show_group_dialog");
+
HBoxContainer *hbc = memnew(HBoxContainer);
vbc->add_child(hbc);
diff --git a/editor/groups_editor.h b/editor/groups_editor.h
index ad3d5cd14..461cf0f8c 100644
--- a/editor/groups_editor.h
+++ b/editor/groups_editor.h
@@ -31,9 +31,13 @@
#ifndef GROUPS_EDITOR_H
#define GROUPS_EDITOR_H
+#include "editor/scene_tree_editor.h"
#include "scene/gui/button.h"
#include "scene/gui/dialogs.h"
+#include "scene/gui/item_list.h"
#include "scene/gui/line_edit.h"
+#include "scene/gui/popup.h"
+#include "scene/gui/tool_button.h"
#include "scene/gui/tree.h"
#include "undo_redo.h"
@@ -41,12 +45,71 @@
@author Juan Linietsky <reduzio@gmail.com>
*/
+class GroupDialog : public ConfirmationDialog {
+
+ GDCLASS(GroupDialog, ConfirmationDialog);
+
+ ConfirmationDialog *error;
+
+ SceneTree *scene_tree;
+ TreeItem *groups_root;
+
+ LineEdit *add_group_text;
+
+ Tree *groups;
+
+ Tree *nodes_to_add;
+ TreeItem *add_node_root;
+ LineEdit *add_filter;
+
+ Tree *nodes_to_remove;
+ TreeItem *remove_node_root;
+ LineEdit *remove_filter;
+
+ ToolButton *add_button;
+ ToolButton *remove_button;
+
+ String selected_group;
+
+ void ok_pressed();
+ void _cancel_pressed();
+ void _group_selected();
+
+ void _remove_filter_changed(const String &p_filter);
+ void _add_filter_changed(const String &p_filter);
+
+ void _add_pressed();
+ void _removed_pressed();
+ void _add_group_pressed();
+
+ void _group_renamed();
+
+ void _add_group(String p_name);
+ void _delete_group_pressed(Object *p_item, int p_column, int p_id);
+
+ bool _can_edit(Node *p_node, String p_group);
+
+ void _load_groups(Node *p_current);
+ void _load_nodes(Node *p_current);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void edit();
+
+ GroupDialog();
+};
+
class GroupsEditor : public VBoxContainer {
GDCLASS(GroupsEditor, VBoxContainer);
Node *node;
+ GroupDialog *group_dialog;
+
LineEdit *group_name;
Button *add;
Tree *tree;
@@ -58,6 +121,9 @@ class GroupsEditor : public VBoxContainer {
void _remove_group(Object *p_item, int p_column, int p_id);
void _close();
+ void _show_group_dialog();
+ void _group_dialog_closed();
+
protected:
static void _bind_methods();
diff --git a/editor/icons/icon_add_split.svg b/editor/icons/icon_add_split.svg
index 6cfd419e7..4555fceb7 100644
--- a/editor/icons/icon_add_split.svg
+++ b/editor/icons/icon_add_split.svg
@@ -1,72 +1,8 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="16"
- height="16"
- version="1.1"
- viewBox="0 0 16 16"
- id="svg4"
- sodipodi:docname="icon_add_split.svg"
- inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
- <metadata
- id="metadata10">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <defs
- id="defs8" />
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="3066"
- inkscape:window-height="1689"
- id="namedview6"
- showgrid="false"
- inkscape:zoom="41.7193"
- inkscape:cx="7.7924561"
- inkscape:cy="6.0148972"
- inkscape:window-x="134"
- inkscape:window-y="55"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg4" />
- <rect
- style="fill:#800000"
- id="rect12"
- width="1.8456686"
- height="2.0853658"
- x="0.62321275"
- y="6.9394455" />
- <rect
- style="fill:#800000"
- id="rect14"
- width="1.6299411"
- height="1.9894869"
- x="12.488225"
- y="7.1791425" />
- <rect
- style="fill:#e9afaf"
- id="rect16"
- width="10.067283"
- height="0.69512194"
- x="2.492851"
- y="7.7304463" />
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<path d="m3 13 10-10" fill="none" stroke="#f5f5f5" stroke-opacity=".39216" stroke-width="2"/>
+<g transform="translate(0 -1036.4)">
+<path transform="translate(0 1036.4)" d="m11 9v2h-2v2h2v2h2v-2h2v-2h-2v-2z" fill="#84ffb1"/>
+</g>
+<circle cx="4" cy="12" r="2" fill="none"/>
+<path d="m13 1a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2 -2 2 2 0 0 0 -2 -2zm-10 10a2 2 0 0 0 -2 2 2 2 0 0 0 2 2 2 2 0 0 0 2 -2 2 2 0 0 0 -2 -2z" fill="#e0e0e0"/>
</svg>
diff --git a/editor/icons/icon_asset_lib.svg b/editor/icons/icon_asset_lib.svg
index 1348c491f..967c5bf70 100644
--- a/editor/icons/icon_asset_lib.svg
+++ b/editor/icons/icon_asset_lib.svg
@@ -1,3 +1,3 @@
<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
-<path d="m8 1c-1.6569 0-3 1.3431-3 3v2h-4v7c0 1.108 0.89199 2 2 2h10c1.108 0 2-0.89199 2-2v-7h-4v-2c0-1.6569-1.3431-3-3-3zm0 2c0.55228 0 1 0.44772 1 1v2h-2v-2c0-0.55228 0.44772-1 1-1z" fill="#e0e0e0"/>
+<path d="m8 1c-1.6569 0-3 1.3431-3 3v2h-3c-0.66446 3.505e-4 -1.1438 0.6366-0.96094 1.2754l2 7c0.12287 0.42881 0.51487 0.7244 0.96094 0.72461h8c0.44606-2.09e-4 0.83806-0.2958 0.96094-0.72461l2-7c0.1829-0.63879-0.29648-1.275-0.96094-1.2754h-3v-2c0-1.6569-1.3431-3-3-3zm0 2c0.55228 0 1 0.44772 1 1v2h-2v-2c0-0.55228 0.44772-1 1-1z" fill="#e0e0e0"/>
</svg>
diff --git a/editor/icons/icon_audio_bus_layout.svg b/editor/icons/icon_audio_bus_layout.svg
index 9162722eb..fa6b60bc3 100644
--- a/editor/icons/icon_audio_bus_layout.svg
+++ b/editor/icons/icon_audio_bus_layout.svg
@@ -1,9 +1,9 @@
<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" x1="8" x2="8" y1="1" y2="15" gradientUnits="userSpaceOnUse">
-<stop stop-color="#ff8484" offset="0"/>
+<stop stop-color="#ff7a7a" offset="0"/>
<stop stop-color="#e1dc7a" offset=".5"/>
-<stop stop-color="#84ffb1" offset="1"/>
+<stop stop-color="#66ff9e" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(0 -1036.4)">
diff --git a/editor/icons/icon_audio_stream_player.svg b/editor/icons/icon_audio_stream_player.svg
index 218fd995a..754b72bc9 100644
--- a/editor/icons/icon_audio_stream_player.svg
+++ b/editor/icons/icon_audio_stream_player.svg
@@ -1,9 +1,9 @@
<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" x1="8" x2="8" y1="1" y2="15" gradientUnits="userSpaceOnUse">
-<stop stop-color="#ff8484" offset="0"/>
+<stop stop-color="#ff7a7a" offset="0"/>
<stop stop-color="#e1dc7a" offset=".5"/>
-<stop stop-color="#84ffb1" offset="1"/>
+<stop stop-color="#66ff9e" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(0 -1036.4)" shape-rendering="auto">
diff --git a/editor/icons/icon_audio_stream_player_2_d.svg b/editor/icons/icon_audio_stream_player_2_d.svg
index a431b84a5..0e9c0ca5b 100644
--- a/editor/icons/icon_audio_stream_player_2_d.svg
+++ b/editor/icons/icon_audio_stream_player_2_d.svg
@@ -1,9 +1,9 @@
<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" x1="8" x2="8" y1="1" y2="15" gradientUnits="userSpaceOnUse">
-<stop stop-color="#ff8484" offset="0"/>
+<stop stop-color="#ff7a7a" offset="0"/>
<stop stop-color="#e1dc7a" offset=".5"/>
-<stop stop-color="#84ffb1" offset="1"/>
+<stop stop-color="#66ff9e" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(0 -1036.4)" shape-rendering="auto">
diff --git a/editor/icons/icon_audio_stream_player_3_d.svg b/editor/icons/icon_audio_stream_player_3_d.svg
index 4ce9d6da5..d947586f6 100644
--- a/editor/icons/icon_audio_stream_player_3_d.svg
+++ b/editor/icons/icon_audio_stream_player_3_d.svg
@@ -1,9 +1,9 @@
<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" x1="8" x2="8" y1="1" y2="15" gradientUnits="userSpaceOnUse">
-<stop stop-color="#ff8484" offset="0"/>
+<stop stop-color="#ff7a7a" offset="0"/>
<stop stop-color="#e1dc7a" offset=".5"/>
-<stop stop-color="#84ffb1" offset="1"/>
+<stop stop-color="#66ff9e" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(0 -1036.4)" shape-rendering="auto">
diff --git a/editor/icons/icon_audio_stream_sample.svg b/editor/icons/icon_audio_stream_sample.svg
index f0be1dc30..a7c7232ee 100644
--- a/editor/icons/icon_audio_stream_sample.svg
+++ b/editor/icons/icon_audio_stream_sample.svg
@@ -1,9 +1,9 @@
<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" x1="8" x2="8" y1="1" y2="15" gradientUnits="userSpaceOnUse">
-<stop stop-color="#ff8484" offset="0"/>
+<stop stop-color="#ff7a7a" offset="0"/>
<stop stop-color="#e1dc7a" offset=".5"/>
-<stop stop-color="#84ffb1" offset="1"/>
+<stop stop-color="#66ff9e" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(0 -1036.4)">
diff --git a/editor/icons/icon_bone_2_d.svg b/editor/icons/icon_bone_2_d.svg
new file mode 100644
index 000000000..efcbc17e1
--- /dev/null
+++ b/editor/icons/icon_bone_2_d.svg
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg6"
+ sodipodi:docname="icon_bone_2d.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="836"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g4"
+ style="fill:#a5b7f3;fill-opacity:1">
+ <path
+ d="m10.478 1037.4a2.4664 2.4663 0 0 0 -1.7804 0.7205 2.4664 2.4663 0 0 0 -0.31408 3.1041l-3.559 3.5608a2.4664 2.4663 0 0 0 -3.1023 0.3121 2.4664 2.4663 0 0 0 0 3.4876 2.4664 2.4663 0 0 0 1.397 0.6955 2.4664 2.4663 0 0 0 0.69561 1.397 2.4664 2.4663 0 0 0 3.4877 0 2.4664 2.4663 0 0 0 0.31408 -3.1041l3.5609-3.5608a2.4664 2.4663 0 0 0 3.1004 -0.3102 2.4664 2.4663 0 0 0 0 -3.4875 2.4664 2.4663 0 0 0 -1.397 -0.6974 2.4664 2.4663 0 0 0 -0.69561 -1.3971 2.4664 2.4663 0 0 0 -1.7072 -0.7205z"
+ fill="#fc9c9c"
+ id="path2"
+ style="fill:#a5b7f3;fill-opacity:1" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_bus_vu_db.svg b/editor/icons/icon_bus_vu_db.svg
index 23bcd8841..236e41e1f 100644
--- a/editor/icons/icon_bus_vu_db.svg
+++ b/editor/icons/icon_bus_vu_db.svg
@@ -1,9 +1,9 @@
<svg width="32" height="128" version="1.1" viewBox="0 0 32 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" x1="16" x2="16" y2="128" gradientUnits="userSpaceOnUse">
-<stop stop-color="#ff8484" offset="0"/>
+<stop stop-color="#ff7a7a" offset="0"/>
<stop stop-color="#e1dc7a" offset=".5"/>
-<stop stop-color="#84ffb1" offset="1"/>
+<stop stop-color="#66ff9e" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(0 -924.36)">
diff --git a/editor/icons/icon_bus_vu_empty.svg b/editor/icons/icon_bus_vu_empty.svg
index 52c86ac70..60fddc535 100644
--- a/editor/icons/icon_bus_vu_empty.svg
+++ b/editor/icons/icon_bus_vu_empty.svg
@@ -1,9 +1,9 @@
<svg width="16" height="128" version="1.1" viewBox="0 0 16 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" x1="8" x2="8" y1="2" y2="126" gradientTransform="translate(0 924.36)" gradientUnits="userSpaceOnUse">
-<stop stop-color="#ff8484" offset="0"/>
+<stop stop-color="#ff7a7a" offset="0"/>
<stop stop-color="#e1dc7a" offset=".5"/>
-<stop stop-color="#84ffb1" offset="1"/>
+<stop stop-color="#66ff9e" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(0 -924.36)">
diff --git a/editor/icons/icon_bus_vu_full.svg b/editor/icons/icon_bus_vu_full.svg
index a91b8a06c..4f2ce5df1 100644
--- a/editor/icons/icon_bus_vu_full.svg
+++ b/editor/icons/icon_bus_vu_full.svg
@@ -1,9 +1,9 @@
<svg width="16" height="128" version="1.1" viewBox="0 0 16 128" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="a" x1="8" x2="8" y1="2" y2="126" gradientUnits="userSpaceOnUse">
-<stop stop-color="#ff8484" offset="0"/>
+<stop stop-color="#ff7a7a" offset="0"/>
<stop stop-color="#e1dc7a" offset=".5"/>
-<stop stop-color="#84ffb1" offset="1"/>
+<stop stop-color="#66ff9e" offset="1"/>
</linearGradient>
</defs>
<g transform="translate(0 -924.36)">
diff --git a/editor/icons/icon_c_s_g_box.svg b/editor/icons/icon_c_s_g_box.svg
new file mode 100644
index 000000000..67e34df44
--- /dev/null
+++ b/editor/icons/icon_c_s_g_box.svg
@@ -0,0 +1,6 @@
+<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>
diff --git a/editor/icons/icon_c_s_g_capsule.svg b/editor/icons/icon_c_s_g_capsule.svg
new file mode 100644
index 000000000..92a7b5a87
--- /dev/null
+++ b/editor/icons/icon_c_s_g_capsule.svg
@@ -0,0 +1,6 @@
+<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>
diff --git a/editor/icons/icon_c_s_g_combiner.svg b/editor/icons/icon_c_s_g_combiner.svg
new file mode 100644
index 000000000..cce2902e2
--- /dev/null
+++ b/editor/icons/icon_c_s_g_combiner.svg
@@ -0,0 +1,8 @@
+<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>
diff --git a/editor/icons/icon_c_s_g_cylinder.svg b/editor/icons/icon_c_s_g_cylinder.svg
new file mode 100644
index 000000000..645a74c79
--- /dev/null
+++ b/editor/icons/icon_c_s_g_cylinder.svg
@@ -0,0 +1,6 @@
+<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>
diff --git a/editor/icons/icon_c_s_g_mesh.svg b/editor/icons/icon_c_s_g_mesh.svg
new file mode 100644
index 000000000..6e940a4aa
--- /dev/null
+++ b/editor/icons/icon_c_s_g_mesh.svg
@@ -0,0 +1,6 @@
+<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>
diff --git a/editor/icons/icon_c_s_g_polygon.svg b/editor/icons/icon_c_s_g_polygon.svg
new file mode 100644
index 000000000..71b03cb8e
--- /dev/null
+++ b/editor/icons/icon_c_s_g_polygon.svg
@@ -0,0 +1,6 @@
+<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>
diff --git a/editor/icons/icon_c_s_g_sphere.svg b/editor/icons/icon_c_s_g_sphere.svg
new file mode 100644
index 000000000..f81b56699
--- /dev/null
+++ b/editor/icons/icon_c_s_g_sphere.svg
@@ -0,0 +1,6 @@
+<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>
diff --git a/editor/icons/icon_c_s_g_torus.svg b/editor/icons/icon_c_s_g_torus.svg
new file mode 100644
index 000000000..3d30aa47b
--- /dev/null
+++ b/editor/icons/icon_c_s_g_torus.svg
@@ -0,0 +1,6 @@
+<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>
diff --git a/editor/icons/icon_color_rect.svg b/editor/icons/icon_color_rect.svg
index c0cd07061..c2d4cf344 100644
--- a/editor/icons/icon_color_rect.svg
+++ b/editor/icons/icon_color_rect.svg
@@ -1,9 +1,6 @@
<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)">
-<rect x="1" y="1037.4" width="2" height="14" fill="#a5efac"/>
-<rect x="1" y="1049.4" width="14" height="2" fill="#a5efac"/>
-<rect x="1" y="1037.4" width="14" height="2" fill="#a5efac"/>
-<rect x="13" y="1037.4" width="2" height="14" fill="#a5efac"/>
+<path transform="translate(0 1036.4)" d="m1 1v14h14v-14zm2 2h10v10h-10z" fill="#a5efac"/>
<path d="m12 1048.4h-4.8l4.8-4.8z" fill="#70bfff" fill-rule="evenodd"/>
<path d="m4 1040.4h4.8l-4.8 4.8z" fill="#ff7070" fill-rule="evenodd"/>
<path d="m4 1048.4v-3.2l4.8-4.8h3.2v3.2l-4.8 4.8z" fill="#7aff70" fill-rule="evenodd"/>
diff --git a/editor/icons/icon_editor_position.svg b/editor/icons/icon_editor_position.svg
new file mode 100644
index 000000000..7657eb516
--- /dev/null
+++ b/editor/icons/icon_editor_position.svg
@@ -0,0 +1,4 @@
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<path d="m6 0v4.4199a4.2662 4.0576 0 0 0 -1.709 1.5801h-4.291v4h4.2949a4.2662 4.0576 0 0 0 1.7051 1.582v4.418h4v-4.4199a4.2662 4.0576 0 0 0 1.709 -1.5801h4.291v-4h-4.2949a4.2662 4.0576 0 0 0 -1.7051 -1.582v-4.418z" fill="#fff" fill-opacity=".70588"/>
+<path d="m7 1v3.0605a4.2662 4.0576 0 0 1 1 -0.11914 4.2662 4.0576 0 0 1 1 0.11914v-3.0605h-2zm1 4.0801a2.9201 2.9201 0 0 0 -2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 -2.9199 2.9201 2.9201 0 0 0 -2.9199 -2.9199zm-7 1.9199v2h2.8691a4.2662 4.0576 0 0 1 -0.13477 -1 4.2662 4.0576 0 0 1 0.13672 -1h-2.8711zm11.131 0a4.2662 4.0576 0 0 1 0.13477 1 4.2662 4.0576 0 0 1 -0.13672 1h2.8711v-2h-2.8691zm-5.1309 4.9395v3.0605h2v-3.0605a4.2662 4.0576 0 0 1 -1 0.11914 4.2662 4.0576 0 0 1 -1 -0.11914z" fill="#ff8484"/>
+</svg>
diff --git a/editor/icons/icon_editor_position_previous.svg b/editor/icons/icon_editor_position_previous.svg
new file mode 100644
index 000000000..180156e13
--- /dev/null
+++ b/editor/icons/icon_editor_position_previous.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<path d="m7 1v3.0605a4.2662 4.0576 0 0 1 1 -0.11914 4.2662 4.0576 0 0 1 1 0.11914v-3.0605h-2zm1 4.0801a2.9201 2.9201 0 0 0 -2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 -2.9199 2.9201 2.9201 0 0 0 -2.9199 -2.9199zm-7 1.9199v2h2.8691a4.2662 4.0576 0 0 1 -0.13477 -1 4.2662 4.0576 0 0 1 0.13672 -1h-2.8711zm11.131 0a4.2662 4.0576 0 0 1 0.13477 1 4.2662 4.0576 0 0 1 -0.13672 1h2.8711v-2h-2.8691zm-5.1309 4.9395v3.0605h2v-3.0605a4.2662 4.0576 0 0 1 -1 0.11914 4.2662 4.0576 0 0 1 -1 -0.11914z" fill="#69f" fill-opacity=".69804"/>
+</svg>
diff --git a/editor/icons/icon_editor_position_unselected.svg b/editor/icons/icon_editor_position_unselected.svg
new file mode 100644
index 000000000..3c7d479b8
--- /dev/null
+++ b/editor/icons/icon_editor_position_unselected.svg
@@ -0,0 +1,4 @@
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<path d="m6 0v4.4199a4.2662 4.0576 0 0 0 -1.709 1.5801h-4.291v4h4.2949a4.2662 4.0576 0 0 0 1.7051 1.582v4.418h4v-4.4199a4.2662 4.0576 0 0 0 1.709 -1.5801h4.291v-4h-4.2949a4.2662 4.0576 0 0 0 -1.7051 -1.582v-4.418h-4z" fill-opacity=".41077"/>
+<path d="m7 1v3.0605a4.2662 4.0576 0 0 1 1 -0.11914 4.2662 4.0576 0 0 1 1 0.11914v-3.0605h-2zm1 4.0801a2.9201 2.9201 0 0 0 -2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 2.9199 2.9201 2.9201 0 0 0 2.9199 -2.9199 2.9201 2.9201 0 0 0 -2.9199 -2.9199zm-7 1.9199v2h2.8691a4.2662 4.0576 0 0 1 -0.13477 -1 4.2662 4.0576 0 0 1 0.13672 -1h-2.8711zm11.131 0a4.2662 4.0576 0 0 1 0.13477 1 4.2662 4.0576 0 0 1 -0.13672 1h2.8711v-2h-2.8691zm-5.1309 4.9395v3.0605h2v-3.0605a4.2662 4.0576 0 0 1 -1 0.11914 4.2662 4.0576 0 0 1 -1 -0.11914z" fill="#d9d9d9"/>
+</svg>
diff --git a/editor/icons/icon_insert_after.svg b/editor/icons/icon_insert_after.svg
new file mode 100644
index 000000000..4696a2fc2
--- /dev/null
+++ b/editor/icons/icon_insert_after.svg
@@ -0,0 +1,7 @@
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<circle cx="4" cy="12" r="2" fill="none"/>
+<path d="m11.99 0.99023a1.0001 1.0001 0 0 0 -0.69726 1.7168l0.29297 0.29297h-2.5859v2h2.5859l-0.29297 0.29297a1.0001 1.0001 0 1 0 1.4141 1.4141l2-2a1.0001 1.0001 0 0 0 0 -1.4141l-2-2a1.0001 1.0001 0 0 0 -0.7168 -0.30273zm-8.9902 0.0097656c-1.108 0-2 0.892-2 2v2c0 1.108 0.892 2 2 2h2c1.108 0 2-0.892 2-2v-2c0-1.108-0.892-2-2-2h-2z" fill="#e0e0e0"/>
+<g transform="translate(0 -1036.4)">
+<path transform="translate(0 1036.4)" d="m11 9v2h-2v2h2v2h2v-2h2v-2h-2v-2z" fill="#84ffb1"/>
+</g>
+</svg>
diff --git a/editor/icons/icon_insert_before.svg b/editor/icons/icon_insert_before.svg
new file mode 100644
index 000000000..eb8514421
--- /dev/null
+++ b/editor/icons/icon_insert_before.svg
@@ -0,0 +1,7 @@
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<circle cx="4" cy="12" r="2" fill="none"/>
+<path d="m4.0096 0.99023a1.0001 1.0001 0 0 1 0.69726 1.7168l-0.29297 0.29297h2.5859v2h-2.5859l0.29297 0.29297a1.0001 1.0001 0 1 1 -1.4141 1.4141l-2-2a1.0001 1.0001 0 0 1 0 -1.4141l2-2a1.0001 1.0001 0 0 1 0.7168 -0.30273zm8.9902 0.0097656c1.108 0 2 0.892 2 2v2c0 1.108-0.892 2-2 2h-2c-1.108 0-2-0.892-2-2v-2c0-1.108 0.892-2 2-2z" fill="#e0e0e0"/>
+<g transform="translate(0 -1036.4)">
+<path transform="translate(0 1036.4)" d="m11 9v2h-2v2h2v2h2v-2h2v-2h-2v-2z" fill="#84ffb1"/>
+</g>
+</svg>
diff --git a/editor/icons/icon_key_hover.svg b/editor/icons/icon_key_hover.svg
new file mode 100644
index 000000000..4a3fab475
--- /dev/null
+++ b/editor/icons/icon_key_hover.svg
@@ -0,0 +1,5 @@
+<svg width="8" height="8" version="1.1" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg">
+<g transform="translate(0 -1044.4)">
+<rect transform="rotate(-45)" x="-741.53" y="741.08" width="6.1027" height="6.1027" ry=".76286" fill="#fff"/>
+</g>
+</svg>
diff --git a/editor/icons/icon_key_selected.svg b/editor/icons/icon_key_selected.svg
new file mode 100644
index 000000000..c73d31981
--- /dev/null
+++ b/editor/icons/icon_key_selected.svg
@@ -0,0 +1,5 @@
+<svg width="8" height="8" version="1.1" viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg">
+<g transform="translate(0 -1044.4)">
+<rect transform="rotate(-45)" x="-741.53" y="741.08" width="6.1027" height="6.1027" ry=".76286" fill="#84c2ff"/>
+</g>
+</svg>
diff --git a/editor/icons/icon_move_down.svg b/editor/icons/icon_move_down.svg
index 466fa1020..70c5abf9e 100644
--- a/editor/icons/icon_move_down.svg
+++ b/editor/icons/icon_move_down.svg
@@ -1,5 +1,3 @@
<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="m7.9964 1051.4a1.0002 1.0001 0 0 1 -0.77738 -0.377l-4.0002-5a1.0001 1 0 0 1 0.15626 -1.4043 1.0001 1 0 0 1 1.4063 0.1563l2.2189 2.7734v-5.1484a1.0001 1 0 0 1 1.0001 -1 1.0001 1 0 0 1 1.0001 1v5.1484l2.2189-2.7734a1.0001 1 0 0 1 1.4063 -0.1563 1.0001 1 0 0 1 0.15626 1.4043l-4.0002 5a1.0002 1.0001 0 0 1 -0.7852 0.377zm0.00391-12a1.0001 1 0 0 1 -1.0001 -1 1.0001 1 0 0 1 1.0001 -1 1.0001 1 0 0 1 1.0001 1 1.0001 1 0 0 1 -1.0001 1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian: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"/>
-</g>
+<path d="m6 1a1.0001 1.0001 0 1 0 0 2h4a1.0001 1.0001 0 1 0 0 -2zm2 4c-0.55231 0-1 0.4477-1 1v5.1484l-2.2188-2.7734c-0.34504-0.4317-0.97482-0.50165-1.4062-0.15625-0.4305 0.3449-0.5004 0.9732-0.15625 1.4043l4 5c0.18868 0.2369 0.4745 0.37695 0.77734 0.37695 0.30559 9e-4 0.59477-0.13795 0.78516-0.37695l4-5c0.34415-0.4311 0.27424-1.0594-0.15625-1.4043-0.43143-0.3454-1.0612-0.27545-1.4062 0.15625l-2.2188 2.7734v-5.1484c0-0.5523-0.44769-1-1-1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" 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"/>
</svg>
diff --git a/editor/icons/icon_move_left.svg b/editor/icons/icon_move_left.svg
new file mode 100644
index 000000000..bab817bfd
--- /dev/null
+++ b/editor/icons/icon_move_left.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<path d="m15 10a1.0001 1.0001 0 1 1 -2 0v-4a1.0001 1.0001 0 1 1 2 0zm-4-2c0 0.55231-0.4477 1-1 1h-5.1484l2.7734 2.2188c0.4317 0.34504 0.50165 0.97482 0.15625 1.4062-0.3449 0.4305-0.9732 0.5004-1.4043 0.15625l-5-4c-0.2369-0.18868-0.37695-0.4745-0.37695-0.77734-9e-4 -0.30559 0.13795-0.59477 0.37695-0.78516l5-4c0.4311-0.34415 1.0594-0.27424 1.4043 0.15625 0.3454 0.43143 0.27545 1.0612-0.15625 1.4062l-2.7734 2.2188h5.1484c0.5523 0 1 0.44769 1 1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" 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"/>
+</svg>
diff --git a/editor/icons/icon_move_right.svg b/editor/icons/icon_move_right.svg
new file mode 100644
index 000000000..7721633de
--- /dev/null
+++ b/editor/icons/icon_move_right.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+<path d="m1 10a1.0001 1.0001 0 1 0 2 0v-4a1.0001 1.0001 0 1 0 -2 0zm4-2c0 0.55231 0.4477 1 1 1h5.1484l-2.7734 2.2188c-0.4317 0.34504-0.50165 0.97482-0.15625 1.4062 0.3449 0.4305 0.9732 0.5004 1.4043 0.15625l5-4c0.2369-0.18868 0.37695-0.4745 0.37695-0.77734 9e-4 -0.30559-0.13795-0.59477-0.37695-0.78516l-5-4c-0.4311-0.34415-1.0594-0.27424-1.4043 0.15625-0.3454 0.43143-0.27545 1.0612 0.15625 1.4062l2.7734 2.2188h-5.1484c-0.5523 0-1 0.44769-1 1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" 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"/>
+</svg>
diff --git a/editor/icons/icon_move_up.svg b/editor/icons/icon_move_up.svg
index 6e148216d..06bb26fad 100644
--- a/editor/icons/icon_move_up.svg
+++ b/editor/icons/icon_move_up.svg
@@ -1,5 +1,3 @@
<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="m7.9964 1037.4a1.0002 1.0001 0 0 0 -0.77739 0.377l-4.0002 5a1.0001 1 0 0 0 0.15626 1.4043 1.0001 1 0 0 0 1.4063 -0.1563l2.2189-2.7734v5.1484a1.0001 1 0 0 0 1.0001 1 1.0001 1 0 0 0 1.0001 -1v-5.1484l2.2189 2.7734a1.0001 1 0 0 0 1.4063 0.1563 1.0001 1 0 0 0 0.15626 -1.4043l-4.0002-5a1.0002 1.0001 0 0 0 -0.7852 -0.377zm0.00391 12a1.0001 1 0 0 0 -1.0001 1 1.0001 1 0 0 0 1.0001 1 1.0001 1 0 0 0 1.0001 -1 1.0001 1 0 0 0 -1.0001 -1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-east-asian: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"/>
-</g>
+<path d="m6 15a1.0001 1.0001 0 1 1 0 -2h4a1.0001 1.0001 0 1 1 0 2zm2-4c-0.55231 0-1-0.4477-1-1v-5.1484l-2.2188 2.7734c-0.34504 0.4317-0.97482 0.50165-1.4062 0.15625-0.4305-0.3449-0.5004-0.9732-0.15625-1.4043l4-5c0.18868-0.2369 0.4745-0.37695 0.77734-0.37695 0.30559-9e-4 0.59477 0.13795 0.78516 0.37695l4 5c0.34415 0.4311 0.27424 1.0594-0.15625 1.4043-0.43143 0.3454-1.0612 0.27545-1.4062-0.15625l-2.2188-2.7734v5.1484c0 0.5523-0.44769 1-1 1z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" fill-opacity=".99608" 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"/>
</svg>
diff --git a/editor/icons/icon_paint_vertex.svg b/editor/icons/icon_paint_vertex.svg
new file mode 100644
index 000000000..b53e005da
--- /dev/null
+++ b/editor/icons/icon_paint_vertex.svg
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg4"
+ sodipodi:docname="icon_paint_vertex.svg"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="3066"
+ inkscape:window-height="1689"
+ id="namedview6"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="134"
+ inkscape:window-y="55"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg4" />
+ <ellipse
+ style="fill:#ffffff"
+ id="path12"
+ cx="8.3728809"
+ cy="8.1694918"
+ rx="6.6779661"
+ ry="6.0677967" />
+</svg>
diff --git a/editor/icons/icon_panel.svg b/editor/icons/icon_panel.svg
index aebf885e7..7bd3db7c0 100644
--- a/editor/icons/icon_panel.svg
+++ b/editor/icons/icon_panel.svg
@@ -1,5 +1,3 @@
<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="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2z" fill="#a5efac"/>
-</g>
+<path d="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2z" fill="#a5efac"/>
</svg>
diff --git a/editor/icons/icon_panel_container.svg b/editor/icons/icon_panel_container.svg
index a52493b66..df8a2c0a0 100644
--- a/editor/icons/icon_panel_container.svg
+++ b/editor/icons/icon_panel_container.svg
@@ -1,5 +1,3 @@
<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="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2zm0 2h10v10h-10z" fill="#a5efac"/>
-</g>
+<path d="m3 1c-1.1046 0-2 0.89543-2 2v10c0 1.1046 0.89543 2 2 2h10c1.1046 0 2-0.89543 2-2v-10c0-1.1046-0.89543-2-2-2zm0 2h10v10h-10z" fill="#a5efac"/>
</svg>
diff --git a/editor/icons/icon_physical_bone.svg b/editor/icons/icon_physical_bone.svg
new file mode 100644
index 000000000..2efcab3e2
--- /dev/null
+++ b/editor/icons/icon_physical_bone.svg
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="icon_physical_bone.svg">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1027"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="16"
+ inkscape:cx="14.674088"
+ inkscape:cy="7.3239349"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <g
+ id="g4505"
+ transform="translate(-2.5625,-18.4375)">
+ <path
+ inkscape:connector-curvature="0"
+ id="path6-9-8"
+ d="m 13.107422,19.382812 a 2.4664,2.4663 0 0 0 -1.78125,0.720704 2.4664,2.4663 0 0 0 -0.185547,0.21289 L 12.472656,22.75 10.867187,23.353516 7.453125,26.767578 a 2.4664,2.4663 0 0 0 -3.1015625,0.3125 2.4664,2.4663 0 0 0 0,3.488281 2.4664,2.4663 0 0 0 1.3964844,0.695313 2.4664,2.4663 0 0 0 0.6953125,1.396484 2.4664,2.4663 0 0 0 3.4882812,0 2.4664,2.4663 0 0 0 0.3144534,-3.103515 l 3.560547,-3.560547 a 2.4664,2.4663 0 0 0 3.099609,-0.310547 2.4664,2.4663 0 0 0 0,-3.488281 A 2.4664,2.4663 0 0 0 15.509766,21.5 2.4664,2.4663 0 0 0 14.814453,20.103516 2.4664,2.4663 0 0 0 13.107422,19.382812 Z"
+ style="fill:#fc9c9c" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="rect4140-5-1-4-3-7-9-03"
+ d="m 3.7211033,21.208326 0.9608286,4.82644 1.3962404,-0.524494 z"
+ style="opacity:1;fill:#fc9c9c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.42799997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="rect4140-5-1-4-3-7-9-5-3"
+ d="m 6.4843278,19.465234 0.9608285,4.82644 1.3962404,-0.524494 z"
+ style="opacity:1;fill:#fc9c9c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.42799997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ sodipodi:nodetypes="cccc"
+ id="rect4140-5-1-4-3-7-9-5-3-8"
+ d="m 9.6964655,19.33678 0.7108285,3.51394 1.39624,-0.524494 z"
+ style="opacity:1;fill:#fc9c9c;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.42799997;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/editor/icons/icon_translation.svg b/editor/icons/icon_translation.svg
index 81abe5070..fa6d7e1ff 100644
--- a/editor/icons/icon_translation.svg
+++ b/editor/icons/icon_translation.svg
@@ -1,5 +1,5 @@
<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="m2 1v14h10a2 2 0 0 0 2 -2v-10a2 2 0 0 0 -2 -2v5 2l-2-2-2 2v-2-5h-6z" fill="#e0e0e0"/>
+<path transform="translate(0 1036.4)" d="m4 1c-1.645 0-3 1.355-3 3s1.355 3 3 3c0.46079 0 0.89328-0.11549 1.2852-0.30469 0.18147 0.1867 0.43274 0.30469 0.71484 0.30469 0.554 0 1-0.446 1-1v-2-2c0-0.554-0.446-1-1-1-0.28152 0-0.53345 0.11683-0.71484 0.30273-0.39187-0.1892-0.82436-0.30273-1.2852-0.30273zm0 2c0.56412 0 1 0.4359 1 1s-0.43588 1-1 1-1-0.4359-1-1 0.43588-1 1-1zm6.8867 3.5293l-1.7891 0.89453 0.28906 0.57617h-2.3867v2h0.82031c0.13264 0.9292 0.4994 1.8938 1.1992 2.7305-0.61509 0.163-1.3569 0.26523-2.2656 0.26953l0.0097657 2c1.6777-0.01 3.0414-0.31328 4.1113-0.83398 1.07 0.5208 2.4336 0.82608 4.1113 0.83398l0.009766-2c-0.90873 0-1.6505-0.10653-2.2656-0.26953 0.7-0.8367 1.068-1.8013 1.2012-2.7305h1.0684v-2h-3.3789l-0.73438-1.4707zm-1.0234 3.4707h2.0234c-0.12578 0.5801-0.37537 1.147-0.83594 1.623-0.05313 0.055-0.11651 0.10676-0.17578 0.16016-0.05927-0.053-0.12265-0.10516-0.17578-0.16016-0.46056-0.476-0.71015-1.0429-0.83594-1.623z" color="#000000" color-rendering="auto" dominant-baseline="auto" fill="#e0e0e0" 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"/>
</g>
</svg>
diff --git a/editor/icons/icon_unpaint_vertex.svg b/editor/icons/icon_unpaint_vertex.svg
new file mode 100644
index 000000000..a4633ee80
--- /dev/null
+++ b/editor/icons/icon_unpaint_vertex.svg
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg4"
+ sodipodi:docname="icon_unpaint_vertex.svg"
+ inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="3066"
+ inkscape:window-height="1689"
+ id="namedview6"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="134"
+ inkscape:window-y="55"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg4" />
+ <ellipse
+ style="fill:#000000"
+ id="path12"
+ cx="8.3728809"
+ cy="8.1694918"
+ rx="6.6779661"
+ ry="6.0677967" />
+</svg>
diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp
index 863b13cbd..1d7545f18 100644
--- a/editor/import/editor_import_collada.cpp
+++ b/editor/import/editor_import_collada.cpp
@@ -138,7 +138,7 @@ Error ColladaImport::_populate_skeleton(Skeleton *p_skeleton, Collada::Node *p_n
//should map this bone to something for animation?
} else {
print_line("no rest: " + joint->sid);
- WARN_PRINT("Joint has no rest..");
+ WARN_PRINT("Joint has no rest...");
}
int id = r_bone++;
@@ -480,7 +480,7 @@ Error ColladaImport::_create_material(const String &p_target) {
}
}
- float roughness = Math::sqrt(1.0 - ((Math::log(effect.shininess) / Math::log(2.0)) / 8.0)); //not very right..
+ float roughness = (effect.shininess - 1.0) / 510;
material->set_roughness(roughness);
if (effect.double_sided) {
diff --git a/editor/import/editor_scene_importer_gltf.cpp b/editor/import/editor_scene_importer_gltf.cpp
index 321d29f2f..af79f9946 100644
--- a/editor/import/editor_scene_importer_gltf.cpp
+++ b/editor/import/editor_scene_importer_gltf.cpp
@@ -1732,14 +1732,19 @@ void EditorSceneImporterGLTF::_generate_bone(GLTFState &state, int p_node, Vecto
for (int i = 0; i < n->joints.size(); i++) {
ERR_FAIL_COND(n->joints[i].skin < 0);
- int bone_index = skeletons[n->joints[i].skin]->get_bone_count();
- skeletons[n->joints[i].skin]->add_bone(n->name);
+ int bone_index = n->joints[i].bone;
+
+ Skeleton *s = skeletons[n->joints[i].skin];
+ while (s->get_bone_count() <= bone_index) {
+ s->add_bone("Bone " + itos(s->get_bone_count()));
+ }
+
if (p_parent_bones.size()) {
- skeletons[n->joints[i].skin]->set_bone_parent(bone_index, p_parent_bones[i]);
+ s->set_bone_parent(bone_index, p_parent_bones[i]);
}
- skeletons[n->joints[i].skin]->set_bone_rest(bone_index, state.skins[n->joints[i].skin].bones[n->joints[i].bone].inverse_bind.affine_inverse());
+ s->set_bone_rest(bone_index, state.skins[n->joints[i].skin].bones[n->joints[i].bone].inverse_bind.affine_inverse());
- n->godot_nodes.push_back(skeletons[n->joints[i].skin]);
+ n->godot_nodes.push_back(s);
n->joints[i].godot_bone_index = bone_index;
parent_bones.push_back(bone_index);
}
diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp
index 44948b820..fdbf66f65 100644
--- a/editor/import/resource_importer_scene.cpp
+++ b/editor/import/resource_importer_scene.cpp
@@ -251,7 +251,7 @@ Node *ResourceImporterScene::_fix_node(Node *p_node, Node *p_root, Map<Ref<Array
Node *r = _fix_node(p_node->get_child(i), p_root, collision_map, p_light_bake_mode);
if (!r) {
- print_line("was erased..");
+ print_line("was erased...");
i--; //was erased
}
}
@@ -1152,7 +1152,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
String ext = src_path.get_extension().to_lower();
EditorProgress progress("import", TTR("Import Scene"), 104);
- progress.step(TTR("Importing Scene.."), 0);
+ progress.step(TTR("Importing Scene..."), 0);
for (Set<Ref<EditorSceneImporter> >::Element *E = importers.front(); E; E = E->next()) {
@@ -1324,7 +1324,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
_make_external_resources(scene, base_path, external_animations, keep_custom_tracks, external_materials, keep_materials, external_meshes, anim_map, mat_map, mesh_map);
}
- progress.step(TTR("Running Custom Script.."), 2);
+ progress.step(TTR("Running Custom Script..."), 2);
String post_import_script_path = p_options["nodes/custom_script"];
Ref<EditorScenePostImport> post_import_script;
@@ -1353,7 +1353,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
}
}
- progress.step(TTR("Saving.."), 104);
+ progress.step(TTR("Saving..."), 104);
if (external_scenes) {
//save sub-scenes as instances!
diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp
index 8119b84b7..beaa8d960 100644
--- a/editor/import/resource_importer_texture.cpp
+++ b/editor/import/resource_importer_texture.cpp
@@ -295,7 +295,7 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String
case COMPRESS_VIDEO_RAM: {
Ref<Image> image = p_image->duplicate();
- image->generate_mipmaps();
+ image->generate_mipmaps(p_force_normal);
if (p_force_rgbe && image->get_format() >= Image::FORMAT_R8 && image->get_format() <= Image::FORMAT_RGBE9995) {
image->convert(Image::FORMAT_RGBE9995);
diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
index 03155b3a4..debdeb1c4 100644
--- a/editor/import/resource_importer_wav.cpp
+++ b/editor/import/resource_importer_wav.cpp
@@ -304,17 +304,23 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
int limit_rate_hz = p_options["force/max_rate_hz"];
if (limit_rate && rate > limit_rate_hz && rate > 0 && frames > 0) {
//resampleeee!!!
- int new_data_frames = frames * limit_rate_hz / rate;
+ int new_data_frames = (int)(frames * (float)limit_rate_hz / (float)rate);
+
+ print_line("\tresampling ratio: " + rtos((float)limit_rate_hz / (float)rate));
+ print_line("\tnew frames: " + itos(new_data_frames));
+
Vector<float> new_data;
new_data.resize(new_data_frames * format_channels);
for (int c = 0; c < format_channels; c++) {
+ float frac = .0f;
+ int ipos = 0;
+
for (int i = 0; i < new_data_frames; i++) {
//simple cubic interpolation should be enough.
- float pos = float(i) * frames / new_data_frames;
- float mu = pos - Math::floor(pos);
- int ipos = int(Math::floor(pos));
+
+ float mu = frac;
float y0 = data[MAX(0, ipos - 1) * format_channels + c];
float y1 = data[ipos * format_channels + c];
@@ -330,14 +336,22 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
float res = (a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3);
new_data[i * format_channels + c] = res;
+
+ // update position and always keep fractional part within ]0...1]
+ // in order to avoid 32bit floating point precision errors
+
+ frac += (float)rate / (float)limit_rate_hz;
+ int tpos = (int)Math::floor(frac);
+ ipos += tpos;
+ frac -= tpos;
}
}
if (loop) {
-
- loop_begin = loop_begin * new_data_frames / frames;
- loop_end = loop_end * new_data_frames / frames;
+ loop_begin = (int)(loop_begin * (float)new_data_frames / (float)frames);
+ loop_end = (int)(loop_end * (float)new_data_frames / (float)frames);
}
+
data = new_data;
rate = limit_rate_hz;
frames = new_data_frames;
diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp
index ce2a4ec6d..128196be5 100644
--- a/editor/import_dock.cpp
+++ b/editor/import_dock.cpp
@@ -415,7 +415,7 @@ ImportDock::ImportDock() {
hb->add_child(import_as);
import_as->set_h_size_flags(SIZE_EXPAND_FILL);
preset = memnew(MenuButton);
- preset->set_text(TTR("Preset.."));
+ preset->set_text(TTR("Preset..."));
preset->get_popup()->connect("index_pressed", this, "_preset_selected");
hb->add_child(preset);
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 04c9246ae..b38797255 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -440,7 +440,7 @@ void AnimationPlayerEditor::_animation_save_as(const Ref<Resource> &p_resource)
file->set_current_path(existing);
}
file->popup_centered_ratio();
- file->set_title(TTR("Save Resource As.."));
+ file->set_title(TTR("Save Resource As..."));
current_option = RESOURCE_SAVE;
}
@@ -459,6 +459,12 @@ void AnimationPlayerEditor::_animation_remove_confirmed() {
Ref<Animation> anim = player->get_animation(current);
undo_redo->create_action(TTR("Remove Animation"));
+ if (player->get_autoplay() == current) {
+ undo_redo->add_do_method(player, "set_autoplay", "");
+ undo_redo->add_undo_method(player, "set_autoplay", current);
+ // Avoid having the autoplay icon linger around if there is only one animation in the player
+ undo_redo->add_do_method(this, "_animation_player_changed", player);
+ }
undo_redo->add_do_method(player, "remove_animation", current);
undo_redo->add_undo_method(player, "add_animation", current, anim);
undo_redo->add_do_method(this, "_animation_player_changed", player);
@@ -491,7 +497,7 @@ void AnimationPlayerEditor::_animation_name_edited() {
String new_name = name->get_text();
if (new_name == "" || new_name.find(":") != -1 || new_name.find("/") != -1) {
- error_dialog->set_text(TTR("ERROR: Invalid animation name!"));
+ error_dialog->set_text(TTR("Invalid animation name!"));
error_dialog->popup_centered_minsize();
return;
}
@@ -502,7 +508,7 @@ void AnimationPlayerEditor::_animation_name_edited() {
}
if (player->has_animation(new_name)) {
- error_dialog->set_text(TTR("ERROR: Animation name already exists!"));
+ error_dialog->set_text(TTR("Animation name already exists!"));
error_dialog->popup_centered_minsize();
return;
}
@@ -811,6 +817,8 @@ void AnimationPlayerEditor::_update_player() {
play_bw->set_disabled(animlist.size() == 0);
play_bw_from->set_disabled(animlist.size() == 0);
play_from->set_disabled(animlist.size() == 0);
+ frame->set_editable(animlist.size() != 0);
+ animation->set_disabled(animlist.size() == 0);
autoplay->set_disabled(animlist.size() == 0);
duplicate_anim->set_disabled(animlist.size() == 0);
rename_anim->set_disabled(animlist.size() == 0);
@@ -820,6 +828,7 @@ void AnimationPlayerEditor::_update_player() {
save_anim->set_disabled(animlist.size() == 0);
tool_anim->set_disabled(player == NULL);
onion_skinning->set_disabled(player == NULL);
+ pin->set_disabled(player == NULL);
int active_idx = -1;
for (List<StringName>::Element *E = animlist.front(); E; E = E->next()) {
@@ -1088,7 +1097,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
case TOOL_COPY_ANIM: {
if (!animation->get_item_count()) {
- error_dialog->set_text(TTR("ERROR: No animation to copy!"));
+ error_dialog->set_text(TTR("No animation to copy!"));
error_dialog->popup_centered_minsize();
return;
}
@@ -1103,7 +1112,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard();
if (!anim.is_valid()) {
- error_dialog->set_text(TTR("ERROR: No animation resource on clipboard!"));
+ error_dialog->set_text(TTR("No animation resource on clipboard!"));
error_dialog->popup_centered_minsize();
return;
}
@@ -1134,7 +1143,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
case TOOL_EDIT_RESOURCE: {
if (!animation->get_item_count()) {
- error_dialog->set_text(TTR("ERROR: No animation to edit!"));
+ error_dialog->set_text(TTR("No animation to edit!"));
error_dialog->popup_centered_minsize();
return;
}
@@ -1611,7 +1620,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
hb->add_child(load_anim);
save_anim = memnew(MenuButton);
- save_anim->set_tooltip(TTR("Save the current animation"));
+ save_anim->set_tooltip(TTR("Save the current animation."));
save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save", TTR("Save")), ANIM_SAVE);
save_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/save_as", TTR("Save As")), ANIM_SAVE_AS);
save_anim->set_focus_mode(Control::FOCUS_NONE);
@@ -1679,10 +1688,10 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
onion_skinning->get_popup()->add_separator();
onion_skinning->get_popup()->add_item(TTR("Depth"), -1);
onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true);
- onion_skinning->get_popup()->add_check_item(TTR("1 step"), ONION_SKINNING_1_STEP);
+ onion_skinning->get_popup()->add_radio_check_item(TTR("1 step"), ONION_SKINNING_1_STEP);
onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true);
- onion_skinning->get_popup()->add_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS);
- onion_skinning->get_popup()->add_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS);
+ onion_skinning->get_popup()->add_radio_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS);
+ onion_skinning->get_popup()->add_radio_check_item(TTR("3 steps"), ONION_SKINNING_3_STEPS);
onion_skinning->get_popup()->add_separator();
onion_skinning->get_popup()->add_check_item(TTR("Differences Only"), ONION_SKINNING_DIFFERENCES_ONLY);
onion_skinning->get_popup()->add_check_item(TTR("Force White Modulate"), ONION_SKINNING_FORCE_WHITE_MODULATE);
@@ -1691,6 +1700,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
pin = memnew(ToolButton);
pin->set_toggle_mode(true);
+ pin->set_tooltip(TTR("Pin AnimationPlayer"));
hb->add_child(pin);
resource_edit_anim = memnew(Button);
@@ -1718,7 +1728,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay
error_dialog = memnew(ConfirmationDialog);
error_dialog->get_ok()->set_text(TTR("Close"));
- error_dialog->set_text(TTR("Error!"));
+ error_dialog->set_title(TTR("Error!"));
add_child(error_dialog);
name_dialog->connect("confirmed", this, "_animation_name_edited");
diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp
index f0e186e4b..25582ae0b 100644
--- a/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_tree_editor_plugin.cpp
@@ -578,7 +578,7 @@ void AnimationTreeEditor::_draw_node(const StringName &p_node) {
if (anim_tree->animation_node_get_master_animation(p_node) != "")
text = anim_tree->animation_node_get_master_animation(p_node);
else if (anim.is_null())
- text = "load..";
+ text = "load...";
else
text = anim->get_name();
@@ -593,7 +593,7 @@ void AnimationTreeEditor::_draw_node(const StringName &p_node) {
case AnimationTreePlayer::NODE_TIMESCALE:
case AnimationTreePlayer::NODE_TRANSITION: {
- font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit..", font_color_title);
+ font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title);
} break;
default: editable = false;
}
@@ -756,6 +756,7 @@ void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) {
if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) {
node_popup->clear();
+ node_popup->set_size(Size2(1, 1));
node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT);
if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) {
node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT);
@@ -774,6 +775,7 @@ void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) {
if (rclick_type == CLICK_NODE) {
node_popup->clear();
+ node_popup->set_size(Size2(1, 1));
node_popup->add_item(TTR("Rename"), NODE_RENAME);
node_popup->add_item(TTR("Remove"), NODE_ERASE);
if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION)
@@ -1288,7 +1290,7 @@ AnimationTreeEditor::AnimationTreeEditor() {
p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK);
p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION);
p->add_separator();
- p->add_item(TTR("Import Animations.."), MENU_IMPORT_ANIMATIONS); // wtf
+ p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf
p->add_separator();
p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR);
@@ -1397,7 +1399,7 @@ AnimationTreeEditor::AnimationTreeEditor() {
filter_button->set_margin(MARGIN_RIGHT, -10);
edit_dialog->add_child(filter_button);
filter_button->hide();
- filter_button->set_text(TTR("Filters.."));
+ filter_button->set_text(TTR("Filters..."));
filter_button->connect("pressed", this, "_edit_filters");
set_clip_contents(true);
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index c203b4b81..05833704d 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -407,13 +407,13 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) {
switch (cstatus) {
case HTTPClient::STATUS_RESOLVING: {
- status->set_text(TTR("Resolving.."));
+ status->set_text(TTR("Resolving..."));
} break;
case HTTPClient::STATUS_CONNECTING: {
- status->set_text(TTR("Connecting.."));
+ status->set_text(TTR("Connecting..."));
} break;
case HTTPClient::STATUS_REQUESTING: {
- status->set_text(TTR("Requesting.."));
+ status->set_text(TTR("Requesting..."));
} break;
default: {}
}
@@ -639,7 +639,7 @@ const char *EditorAssetLibrary::support_key[SUPPORT_MAX] = {
void EditorAssetLibrary::_select_author(int p_id) {
- //opemn author window
+ // Open author window
}
void EditorAssetLibrary::_select_category(int p_id) {
@@ -659,16 +659,6 @@ void EditorAssetLibrary::_select_category(int p_id) {
void EditorAssetLibrary::_select_asset(int p_id) {
_api_request("asset/" + itos(p_id), REQUESTING_ASSET);
-
- /*
- if (description) {
- memdelete(description);
- }
-
-
- description = memnew( EditorAssetLibraryItemDescription );
- add_child(description);
- description->popup_centered_minsize();*/
}
void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PoolByteArray &p_data, int p_queue_id) {
@@ -774,7 +764,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
_image_update(p_code == HTTPClient::RESPONSE_NOT_MODIFIED, true, p_data, p_queue_id);
} else {
- WARN_PRINTS("Error getting PNG file for asset id " + itos(image_queue[p_queue_id].asset_id));
+ WARN_PRINTS("Error getting PNG file from URL: " + image_queue[p_queue_id].image_url);
Object *obj = ObjectDB::get_instance(image_queue[p_queue_id].target);
if (obj) {
obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("ErrorSign", "EditorIcons"));
@@ -1382,7 +1372,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
support = memnew(MenuButton);
search_hb2->add_child(support);
- support->set_text(TTR("Support.."));
+ support->set_text(TTR("Support..."));
support->get_popup()->add_check_item(TTR("Official"), SUPPORT_OFFICIAL);
support->get_popup()->add_check_item(TTR("Community"), SUPPORT_COMMUNITY);
support->get_popup()->add_check_item(TTR("Testing"), SUPPORT_TESTING);
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index 89b79d7cf..69a65dd3d 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -241,7 +241,6 @@ class EditorAssetLibrary : public PanelContainer {
bool active;
int queue_id;
- int asset_id;
ImageType image_type;
int image_index;
String image_url;
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 0184bff05..93aeca663 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -44,6 +44,7 @@
#include "scene/2d/particles_2d.h"
#include "scene/2d/polygon_2d.h"
#include "scene/2d/screen_button.h"
+#include "scene/2d/skeleton_2d.h"
#include "scene/2d/sprite.h"
#include "scene/gui/grid_container.h"
#include "scene/gui/nine_patch_rect.h"
@@ -196,11 +197,15 @@ void CanvasItemEditor::_snap_other_nodes(Point2 p_value, Point2 &r_current_snap,
Transform2D ci_transform = canvas_item->get_global_transform_with_canvas();
Transform2D to_snap_transform = p_to_snap ? p_to_snap->get_global_transform_with_canvas() : Transform2D();
if (fmod(ci_transform.get_rotation() - to_snap_transform.get_rotation(), (real_t)360.0) == 0.0) {
- Point2 begin = ci_transform.xform(canvas_item->_edit_get_rect().get_position());
- Point2 end = ci_transform.xform(canvas_item->_edit_get_rect().get_position() + canvas_item->_edit_get_rect().get_size());
-
- _snap_if_closer_point(p_value, begin, r_current_snap, r_snapped, ci_transform.get_rotation());
- _snap_if_closer_point(p_value, end, r_current_snap, r_snapped, ci_transform.get_rotation());
+ if (canvas_item->_edit_use_rect()) {
+ Point2 begin = ci_transform.xform(canvas_item->_edit_get_rect().get_position());
+ Point2 end = ci_transform.xform(canvas_item->_edit_get_rect().get_position() + canvas_item->_edit_get_rect().get_size());
+ _snap_if_closer_point(p_value, begin, r_current_snap, r_snapped, ci_transform.get_rotation());
+ _snap_if_closer_point(p_value, end, r_current_snap, r_snapped, ci_transform.get_rotation());
+ } else {
+ Point2 position = ci_transform.xform(Point2());
+ _snap_if_closer_point(p_value, position, r_current_snap, r_snapped, ci_transform.get_rotation());
+ }
}
}
for (int i = 0; i < p_current->get_child_count(); i++) {
@@ -221,23 +226,23 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
// Parent sides and center
if ((is_snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) {
- Point2 begin;
- Point2 end;
- bool can_snap = false;
if (const Control *c = Object::cast_to<Control>(p_canvas_item)) {
- begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(0, 0)));
- end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1)));
- can_snap = true;
- } else if (const CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_canvas_item->get_parent())) {
- begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position());
- end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position() + parent_ci->_edit_get_rect().get_size());
- can_snap = true;
- }
-
- if (can_snap) {
+ Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(0, 0)));
+ Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1)));
_snap_if_closer_point(p_target, begin, output, snapped, rotation);
_snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation);
_snap_if_closer_point(p_target, end, output, snapped, rotation);
+ } else if (const CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_canvas_item->get_parent())) {
+ if (parent_ci->_edit_use_rect()) {
+ Point2 begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position());
+ Point2 end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position() + parent_ci->_edit_get_rect().get_size());
+ _snap_if_closer_point(p_target, begin, output, snapped, rotation);
+ _snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation);
+ _snap_if_closer_point(p_target, end, output, snapped, rotation);
+ } else {
+ Point2 position = p_canvas_item->get_transform().affine_inverse().xform(Point2());
+ _snap_if_closer_point(p_target, position, output, snapped, rotation);
+ }
}
}
@@ -253,16 +258,23 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
// Self sides
if ((is_snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) {
- Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position());
- Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size());
- _snap_if_closer_point(p_target, begin, output, snapped, rotation);
- _snap_if_closer_point(p_target, end, output, snapped, rotation);
+ if (p_canvas_item->_edit_use_rect()) {
+ Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position());
+ Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size());
+ _snap_if_closer_point(p_target, begin, output, snapped, rotation);
+ _snap_if_closer_point(p_target, end, output, snapped, rotation);
+ }
}
// Self center
if ((is_snap_active && snap_node_center && (p_modes & SNAP_NODE_CENTER)) || (p_forced_modes & SNAP_NODE_CENTER)) {
- Point2 center = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size() / 2.0);
- _snap_if_closer_point(p_target, center, output, snapped, rotation);
+ if (p_canvas_item->_edit_use_rect()) {
+ Point2 center = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size() / 2.0);
+ _snap_if_closer_point(p_target, center, output, snapped, rotation);
+ } else {
+ Point2 position = p_canvas_item->get_global_transform_with_canvas().xform(Point2());
+ _snap_if_closer_point(p_target, position, output, snapped, rotation);
+ }
}
}
@@ -364,67 +376,89 @@ Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_li
// Handles the first element
CanvasItem *canvas_item = p_list.front()->get();
- Rect2 rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2());
+ Rect2 rect;
+ if (canvas_item->_edit_use_rect()) {
+ rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2());
+ } else {
+ rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(Point2()), Size2());
+ }
- // Handles the other ones
+ // Expand with the other ones
for (List<CanvasItem *>::Element *E = p_list.front(); E; E = E->next()) {
CanvasItem *canvas_item = E->get();
- Rect2 current_rect = canvas_item->_edit_get_rect();
Transform2D xform = canvas_item->get_global_transform_with_canvas();
- rect.expand_to(xform.xform(current_rect.position));
- rect.expand_to(xform.xform(current_rect.position + Vector2(current_rect.size.x, 0)));
- rect.expand_to(xform.xform(current_rect.position + current_rect.size));
- rect.expand_to(xform.xform(current_rect.position + Vector2(0, current_rect.size.y)));
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 current_rect = canvas_item->_edit_get_rect();
+
+ rect.expand_to(xform.xform(current_rect.position));
+ rect.expand_to(xform.xform(current_rect.position + Vector2(current_rect.size.x, 0)));
+ rect.expand_to(xform.xform(current_rect.position + current_rect.size));
+ rect.expand_to(xform.xform(current_rect.position + Vector2(0, current_rect.size.y)));
+ } else {
+ rect.expand_to(xform.xform(Point2()));
+ }
}
return rect;
}
-void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, Node *p_node, bool &r_first, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
+void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
if (!p_node)
return;
if (Object::cast_to<Viewport>(p_node))
return;
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ const CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
- bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != "";
- bool editable = inherited && EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node);
+ /*bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != "";
+ bool editable = !inherited || EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node);
bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_");
- if (!lock_children && (!inherited || editable)) {
- for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
- if (canvas_item && !canvas_item->is_set_as_toplevel()) {
- _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
- } else {
- CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node);
- _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform);
- }
+ if (!lock_children && editable) {}*/
+
+ for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
+ } else {
+ const CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node);
+ _expand_encompassing_rect_using_children(r_rect, p_node->get_child(i), r_first, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform);
}
}
if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) {
- Rect2 rect = canvas_item->_edit_get_rect();
Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform();
- if (r_first) {
- r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2());
- r_first = false;
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ if (r_first) {
+ r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2());
+ r_first = false;
+ }
+ if (r_rect.size != Size2()) {
+ r_rect.expand_to(xform.xform(rect.position));
+ r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0)));
+ r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y)));
+ r_rect.expand_to(xform.xform(rect.position + rect.size));
+ }
+ } else {
+ if (r_first) {
+ r_rect = Rect2(xform.xform(Point2()), Size2());
+ r_first = false;
+ } else {
+ r_rect.expand_to(xform.xform(Point2()));
+ }
}
- r_rect.expand_to(xform.xform(rect.position));
- r_rect.expand_to(xform.xform(rect.position + Point2(rect.size.x, 0)));
- r_rect.expand_to(xform.xform(rect.position + Point2(0, rect.size.y)));
- r_rect.expand_to(xform.xform(rect.position + rect.size));
}
}
-Rect2 CanvasItemEditor::_get_scene_encompassing_rect() {
+Rect2 CanvasItemEditor::_get_encompassing_rect(const Node *p_node) {
Rect2 rect;
bool first = true;
- _expand_encompassing_rect_using_children(rect, editor->get_edited_scene(), first);
+ _expand_encompassing_rect_using_children(rect, p_node, first);
+
return rect;
}
-void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int limit, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
+void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
if (!p_node)
return;
if (Object::cast_to<Viewport>(p_node))
@@ -434,21 +468,19 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
- if (canvas_item && !canvas_item->is_set_as_toplevel())
- _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, 0, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
- else {
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
+ } else {
CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
- _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, 0, Transform2D(), cl ? cl->get_transform() : p_canvas_xform); //use base transform
+ _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, Transform2D(), cl ? cl->get_transform() : p_canvas_xform);
}
- if (limit != 0 && r_items.size() >= limit)
+ if (p_limit != 0 && r_items.size() >= p_limit)
return;
}
- if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) {
-
+ if (canvas_item && canvas_item->is_visible_in_tree()) {
Transform2D xform = (p_parent_xform * p_canvas_xform * canvas_item->get_transform()).affine_inverse();
const real_t local_grab_distance = xform.basis_xform(Vector2(grab_distance, 0)).length();
-
if (canvas_item->_edit_is_selected_on_click(xform.xform(p_pos), local_grab_distance)) {
Node2D *node = Object::cast_to<Node2D>(canvas_item);
@@ -463,40 +495,81 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no
return;
}
-void CanvasItemEditor::_find_canvas_items_at_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
+void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit) {
+
+ Node *scene = editor->get_edited_scene();
+
+ _find_canvas_items_at_pos(p_pos, scene, r_items, p_limit);
+
+ //Remove invalid results
+ for (int i = 0; i < r_items.size(); i++) {
+ Node *node = r_items[i].item;
+
+ // Make sure the selected node is in the current scene, or editable
+ while (node && node != get_tree()->get_edited_scene_root() && node->get_owner() != scene && !scene->is_editable_instance(node->get_owner())) {
+ node = node->get_parent();
+ };
+
+ // Replace the node by the group if grouped
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(node);
+ while (node && node != scene->get_parent()) {
+ CanvasItem *canvas_item_tmp = Object::cast_to<CanvasItem>(node);
+ if (canvas_item_tmp && node->has_meta("_edit_group_")) {
+ canvas_item = canvas_item_tmp;
+ }
+ node = node->get_parent();
+ }
+
+ //Remove the item if invalid
+ if (!canvas_item || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || (canvas_item->has_meta("_edit_lock_") && canvas_item->get_meta("_edit_lock_"))) {
+ r_items.remove(i);
+ i--;
+ } else {
+ r_items[i].item = canvas_item;
+ }
+ }
+}
+
+void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
if (!p_node)
return;
if (Object::cast_to<Viewport>(p_node))
return;
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ Node *scene = editor->get_edited_scene();
- bool inherited = p_node != get_tree()->get_edited_scene_root() && p_node->get_filename() != "";
- bool editable = inherited && EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node);
+ bool editable = p_node == scene || p_node->get_owner() == scene || scene->is_editable_instance(p_node->get_owner());
bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_");
+ bool locked = p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_");
- if (!lock_children && (!inherited || editable)) {
+ if (!lock_children || !editable) {
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
if (canvas_item && !canvas_item->is_set_as_toplevel()) {
- _find_canvas_items_at_rect(p_rect, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
+ _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform);
} else {
CanvasLayer *canvas_layer = Object::cast_to<CanvasLayer>(p_node);
- _find_canvas_items_at_rect(p_rect, p_node->get_child(i), r_items, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform);
+ _find_canvas_items_in_rect(p_rect, p_node->get_child(i), r_items, Transform2D(), canvas_layer ? canvas_layer->get_transform() : p_canvas_xform);
}
}
}
- if (canvas_item && canvas_item->is_visible_in_tree() && !canvas_item->has_meta("_edit_lock_")) {
-
- Rect2 rect = canvas_item->_edit_get_rect();
+ if (canvas_item && canvas_item->is_visible_in_tree() && !locked && editable) {
Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform();
- if (p_rect.has_point(xform.xform(rect.position)) &&
- p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, 0))) &&
- p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, rect.size.y))) &&
- p_rect.has_point(xform.xform(rect.position + Vector2(0, rect.size.y)))) {
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ if (p_rect.has_point(xform.xform(rect.position)) &&
+ p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, 0))) &&
+ p_rect.has_point(xform.xform(rect.position + Vector2(rect.size.x, rect.size.y))) &&
+ p_rect.has_point(xform.xform(rect.position + Vector2(0, rect.size.y)))) {
- r_items->push_back(canvas_item);
+ r_items->push_back(canvas_item);
+ }
+ } else {
+ if (p_rect.has_point(xform.xform(Point2()))) {
+ r_items->push_back(canvas_item);
+ }
}
}
}
@@ -575,8 +648,11 @@ void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items
if (se) {
se->undo_state = canvas_item->_edit_get_state();
se->pre_drag_xform = canvas_item->get_global_transform_with_canvas();
- se->pre_drag_rect = canvas_item->_edit_get_rect();
-
+ if (canvas_item->_edit_use_rect()) {
+ se->pre_drag_rect = canvas_item->_edit_get_rect();
+ } else {
+ se->pre_drag_rect = Rect2();
+ }
se->pre_drag_bones_length = List<float>();
se->pre_drag_bones_undo_state = List<Dictionary>();
@@ -585,7 +661,7 @@ void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items
if (bone && bone->has_meta("_edit_bone_")) {
// Check if we have an IK chain
List<Node2D *> bone_ik_list;
- bool ik_found;
+ bool ik_found = false;
bone = Object::cast_to<Node2D>(bone->get_parent());
while (bone) {
bone_ik_list.push_back(bone);
@@ -853,7 +929,7 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve
bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> b = p_event;
if (b.is_valid()) {
- if (b->get_button_index() == BUTTON_WHEEL_DOWN) {
+ if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_DOWN) {
// Scroll or pan down
if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
@@ -865,7 +941,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
return true;
}
- if (b->get_button_index() == BUTTON_WHEEL_UP) {
+ if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_UP) {
// Scroll or pan up
if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
@@ -877,7 +953,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
return true;
}
- if (b->get_button_index() == BUTTON_WHEEL_LEFT) {
+ if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_LEFT) {
// Pan left
if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
view_offset.x -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
@@ -887,7 +963,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
}
}
- if (b->get_button_index() == BUTTON_WHEEL_RIGHT) {
+ if (b->is_pressed() && b->get_button_index() == BUTTON_WHEEL_RIGHT) {
// Pan right
if (bool(EditorSettings::get_singleton()->get("editors/2d/scroll_to_pan"))) {
view_offset.x += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
@@ -896,31 +972,60 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) {
return true;
}
}
- }
- Ref<InputEventMouseMotion> m = p_event;
- if (m.is_valid()) {
if (drag_type == DRAG_NONE) {
- if (((m->get_button_mask() & BUTTON_MASK_LEFT) && tool == TOOL_PAN) ||
- (m->get_button_mask() & BUTTON_MASK_MIDDLE) ||
- ((m->get_button_mask() & BUTTON_MASK_LEFT) && Input::get_singleton()->is_key_pressed(KEY_SPACE)) ||
- (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") && Input::get_singleton()->is_key_pressed(KEY_SPACE))) {
+ if (b->is_pressed() &&
+ (b->get_button_index() == BUTTON_MIDDLE ||
+ (b->get_button_index() == BUTTON_LEFT && tool == TOOL_PAN) ||
+ (b->get_button_index() == BUTTON_LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) {
// Pan the viewport
- Point2i relative;
- if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) {
- relative = Input::get_singleton()->warp_mouse_motion(m, viewport->get_global_rect());
- } else {
- relative = m->get_relative();
+ drag_type = DRAG_PAN;
+ }
+ }
+
+ if (drag_type == DRAG_PAN) {
+ if (!b->is_pressed()) {
+ // Stop panning the viewport (for any mouse button press)
+ drag_type = DRAG_NONE;
+ }
+ }
+ }
+
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid()) {
+ if (k->get_scancode() == KEY_SPACE && EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning")) {
+ if (drag_type == DRAG_NONE) {
+ if (k->is_pressed() && !k->is_echo()) {
+ //Pan the viewport
+ drag_type = DRAG_PAN;
+ }
+ } else if (drag_type == DRAG_PAN) {
+ if (!k->is_pressed()) {
+ // Stop panning the viewport (for any mouse button press)
+ drag_type = DRAG_NONE;
}
- view_offset.x -= relative.x / zoom;
- view_offset.y -= relative.y / zoom;
- _update_scrollbars();
- viewport->update();
- return true;
}
}
}
+ Ref<InputEventMouseMotion> m = p_event;
+ if (m.is_valid()) {
+ if (drag_type == DRAG_PAN) {
+ // Pan the viewport
+ Point2i relative;
+ if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) {
+ relative = Input::get_singleton()->warp_mouse_motion(m, viewport->get_global_rect());
+ } else {
+ relative = m->get_relative();
+ }
+ view_offset.x -= relative.x / zoom;
+ view_offset.y -= relative.y / zoom;
+ _update_scrollbars();
+ viewport->update();
+ return true;
+ }
+ }
+
Ref<InputEventMagnifyGesture> magnify_gesture = p_event;
if (magnify_gesture.is_valid()) {
// Zoom gesture
@@ -1290,54 +1395,55 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) {
List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1) {
CanvasItem *canvas_item = selection[0];
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
- Rect2 rect = canvas_item->_edit_get_rect();
- Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
-
- Vector2 endpoints[4] = {
- xform.xform(rect.position),
- xform.xform(rect.position + Vector2(rect.size.x, 0)),
- xform.xform(rect.position + rect.size),
- xform.xform(rect.position + Vector2(0, rect.size.y))
- };
+ Vector2 endpoints[4] = {
+ xform.xform(rect.position),
+ xform.xform(rect.position + Vector2(rect.size.x, 0)),
+ xform.xform(rect.position + rect.size),
+ xform.xform(rect.position + Vector2(0, rect.size.y))
+ };
- DragType dragger[] = {
- DRAG_TOP_LEFT,
- DRAG_TOP,
- DRAG_TOP_RIGHT,
- DRAG_RIGHT,
- DRAG_BOTTOM_RIGHT,
- DRAG_BOTTOM,
- DRAG_BOTTOM_LEFT,
- DRAG_LEFT
- };
+ DragType dragger[] = {
+ DRAG_TOP_LEFT,
+ DRAG_TOP,
+ DRAG_TOP_RIGHT,
+ DRAG_RIGHT,
+ DRAG_BOTTOM_RIGHT,
+ DRAG_BOTTOM,
+ DRAG_BOTTOM_LEFT,
+ DRAG_LEFT
+ };
- DragType resize_drag = DRAG_NONE;
- float radius = (select_handle->get_size().width / 2) * 1.5;
+ DragType resize_drag = DRAG_NONE;
+ float radius = (select_handle->get_size().width / 2) * 1.5;
- for (int i = 0; i < 4; i++) {
- int prev = (i + 3) % 4;
- int next = (i + 1) % 4;
+ for (int i = 0; i < 4; i++) {
+ int prev = (i + 3) % 4;
+ int next = (i + 1) % 4;
- Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized();
- ofs *= (select_handle->get_size().width / 2);
- ofs += endpoints[i];
- if (ofs.distance_to(b->get_position()) < radius)
- resize_drag = dragger[i * 2];
+ Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized();
+ ofs *= (select_handle->get_size().width / 2);
+ ofs += endpoints[i];
+ if (ofs.distance_to(b->get_position()) < radius)
+ resize_drag = dragger[i * 2];
- ofs = (endpoints[i] + endpoints[next]) / 2;
- ofs += (endpoints[next] - endpoints[i]).tangent().normalized() * (select_handle->get_size().width / 2);
- if (ofs.distance_to(b->get_position()) < radius)
- resize_drag = dragger[i * 2 + 1];
- }
+ ofs = (endpoints[i] + endpoints[next]) / 2;
+ ofs += (endpoints[next] - endpoints[i]).tangent().normalized() * (select_handle->get_size().width / 2);
+ if (ofs.distance_to(b->get_position()) < radius)
+ resize_drag = dragger[i * 2 + 1];
+ }
- if (resize_drag != DRAG_NONE) {
- drag_type = resize_drag;
- drag_from = transform.affine_inverse().xform(b->get_position());
- drag_selection = List<CanvasItem *>();
- drag_selection.push_back(canvas_item);
- _save_canvas_item_state(drag_selection);
- return true;
+ if (resize_drag != DRAG_NONE) {
+ drag_type = resize_drag;
+ drag_from = transform.affine_inverse().xform(b->get_position());
+ drag_selection = List<CanvasItem *>();
+ drag_selection.push_back(canvas_item);
+ _save_canvas_item_state(drag_selection);
+ return true;
+ }
}
}
}
@@ -1535,61 +1641,64 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
_save_canvas_item_state(drag_selection, true);
}
- _restore_canvas_item_state(drag_selection, true);
+ if (drag_selection.size() > 0) {
- bool move_local_base = k->get_alt();
- bool move_local_base_rotated = k->get_control() || k->get_metakey();
+ _restore_canvas_item_state(drag_selection, true);
- Vector2 dir;
- if (k->get_scancode() == KEY_UP)
- dir += Vector2(0, -1);
- else if (k->get_scancode() == KEY_DOWN)
- dir += Vector2(0, 1);
- else if (k->get_scancode() == KEY_LEFT)
- dir += Vector2(-1, 0);
- else if (k->get_scancode() == KEY_RIGHT)
- dir += Vector2(1, 0);
- if (k->get_shift())
- dir *= grid_step * Math::pow(2.0, grid_step_multiplier);
+ bool move_local_base = k->get_alt();
+ bool move_local_base_rotated = k->get_control() || k->get_metakey();
- drag_to += dir;
- if (k->get_shift())
- drag_to = drag_to.snapped(grid_step * Math::pow(2.0, grid_step_multiplier));
+ Vector2 dir;
+ if (k->get_scancode() == KEY_UP)
+ dir += Vector2(0, -1);
+ else if (k->get_scancode() == KEY_DOWN)
+ dir += Vector2(0, 1);
+ else if (k->get_scancode() == KEY_LEFT)
+ dir += Vector2(-1, 0);
+ else if (k->get_scancode() == KEY_RIGHT)
+ dir += Vector2(1, 0);
+ if (k->get_shift())
+ dir *= grid_step * Math::pow(2.0, grid_step_multiplier);
- Point2 previous_pos;
- if (drag_selection.size() == 1) {
- Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse();
- previous_pos = xform.xform(drag_selection[0]->_edit_get_position());
- } else {
- previous_pos = _get_encompassing_rect_from_list(drag_selection).position;
- }
+ drag_to += dir;
+ if (k->get_shift())
+ drag_to = drag_to.snapped(grid_step * Math::pow(2.0, grid_step_multiplier));
- Point2 new_pos;
- if (drag_selection.size() == 1) {
- Node2D *node_2d = Object::cast_to<Node2D>(drag_selection[0]);
- if (node_2d && move_local_base_rotated) {
- Transform2D m;
- m.rotate(node_2d->get_rotation());
- new_pos += m.xform(drag_to);
- } else if (move_local_base) {
- new_pos += drag_to;
+ Point2 previous_pos;
+ if (drag_selection.size() == 1) {
+ Transform2D xform = drag_selection[0]->get_global_transform_with_canvas() * drag_selection[0]->get_transform().affine_inverse();
+ previous_pos = xform.xform(drag_selection[0]->_edit_get_position());
+ } else {
+ previous_pos = _get_encompassing_rect_from_list(drag_selection).position;
+ }
+
+ Point2 new_pos;
+ if (drag_selection.size() == 1) {
+ Node2D *node_2d = Object::cast_to<Node2D>(drag_selection[0]);
+ if (node_2d && move_local_base_rotated) {
+ Transform2D m;
+ m.rotate(node_2d->get_rotation());
+ new_pos += m.xform(drag_to);
+ } else if (move_local_base) {
+ new_pos += drag_to;
+ } else {
+ new_pos = previous_pos + (drag_to - drag_from);
+ }
} else {
new_pos = previous_pos + (drag_to - drag_from);
}
- } else {
- new_pos = previous_pos + (drag_to - drag_from);
- }
- for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
- CanvasItem *canvas_item = E->get();
- CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform();
+ for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
+ CanvasItem *canvas_item = E->get();
+ CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
+ Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse() * canvas_item->get_transform();
- Node2D *node2d = Object::cast_to<Node2D>(canvas_item);
- if (node2d && se->pre_drag_bones_undo_state.size() > 0) {
- _solve_IK(node2d, new_pos);
- } else {
- canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos));
+ Node2D *node2d = Object::cast_to<Node2D>(canvas_item);
+ if (node2d && se->pre_drag_bones_undo_state.size() > 0) {
+ _solve_IK(node2d, new_pos);
+ } else {
+ canvas_item->_edit_set_position(canvas_item->_edit_get_position() + xform.xform(new_pos) - xform.xform(previous_pos));
+ }
}
}
return true;
@@ -1628,17 +1737,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
// Popup the selection menu list
Point2 click = transform.affine_inverse().xform(b->get_position());
- Node *scene = editor->get_edited_scene();
-
- _find_canvas_items_at_pos(click, scene, selection_results);
- for (int i = 0; i < selection_results.size(); i++) {
- CanvasItem *item = selection_results[i].item;
- if (item != scene && item->get_owner() != scene && !scene->is_editable_instance(item->get_owner())) {
- //invalid result
- selection_results.remove(i);
- i--;
- }
- }
+ _get_canvas_items_at_pos(click, selection_results);
if (selection_results.size() == 1) {
CanvasItem *item = selection_results[0].item;
@@ -1648,6 +1747,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
return true;
} else if (!selection_results.empty()) {
+ // Sorts items according the their z-index
selection_results.sort();
NodePath root_path = get_tree()->get_edited_scene_root()->get_path();
@@ -1672,8 +1772,6 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
selection_menu_additive_selection = b->get_shift();
selection_menu->set_global_position(b->get_global_position());
selection_menu->popup();
- selection_menu->call_deferred("grab_click_focus");
- selection_menu->set_invalidate_click_until_motion();
return true;
}
}
@@ -1689,7 +1787,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
// Find the item to select
CanvasItem *canvas_item = NULL;
Vector<_SelectResult> selection;
- _find_canvas_items_at_pos(click, scene, selection, editor_selection->get_selection().empty() ? 1 : 0);
+ _get_canvas_items_at_pos(click, selection, editor_selection->get_selection().empty() ? 1 : 0);
for (int i = 0; i < selection.size(); i++) {
if (editor_selection->is_selected(selection[i].item)) {
@@ -1708,22 +1806,6 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
if (!selection.empty())
canvas_item = selection[0].item;
- // Check if the canvas item is in a group, and select the group instead if it is the case
- CanvasItem *canvas_item_tmp = canvas_item;
- while (canvas_item_tmp) {
- if (canvas_item->has_meta("_edit_group_")) {
- canvas_item = canvas_item_tmp;
- }
- canvas_item_tmp = canvas_item_tmp->get_parent_item();
- }
-
- // Make sure the selected node is in the current scene
- Node *node = canvas_item;
- while (node && ((node != scene && node->get_owner() != scene) || !node->is_class("CanvasItem"))) {
- node = node->get_parent();
- };
- canvas_item = Object::cast_to<CanvasItem>(node);
-
if (!canvas_item) {
// Start a box selection
if (!b->get_shift()) {
@@ -1768,7 +1850,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
if (bsfrom.y > bsto.y)
SWAP(bsfrom.y, bsto.y);
- _find_canvas_items_at_rect(Rect2(bsfrom, bsto - bsfrom), scene, &selitems);
+ _find_canvas_items_in_rect(Rect2(bsfrom, bsto - bsfrom), scene, &selitems);
for (List<CanvasItem *>::Element *E = selitems.front(); E; E = E->next()) {
editor_selection->add_node(E->get());
}
@@ -1802,6 +1884,41 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
return false;
}
+bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventMouseMotion> m = p_event;
+ if (m.is_valid()) {
+ if (drag_type == DRAG_NONE && tool == TOOL_SELECT) {
+ Point2 click = transform.affine_inverse().xform(m->get_position());
+
+ //Checks if the hovered items changed, update the viewport if so
+ Vector<_SelectResult> hovering_results_tmp;
+ _get_canvas_items_at_pos(click, hovering_results_tmp);
+ hovering_results_tmp.sort();
+ bool changed = false;
+ if (hovering_results.size() == hovering_results_tmp.size()) {
+ for (int i = 0; i < hovering_results.size(); i++) {
+ if (hovering_results[i].item != hovering_results_tmp[i].item) {
+ changed = true;
+ break;
+ }
+ }
+ } else {
+ changed = true;
+ }
+
+ if (changed) {
+ hovering_results = hovering_results_tmp;
+ viewport->update();
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
bool accepted = false;
if ((accepted = _gui_input_rulers_and_guides(p_event))) {
@@ -1820,35 +1937,34 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
//printf("Rotate\n");
} else if ((accepted = _gui_input_move(p_event))) {
//printf("Move\n");
- } else if ((accepted = _gui_input_select(p_event))) {
- //printf("Selection\n");
} else if ((accepted = _gui_input_zoom_or_pan(p_event))) {
//printf("Zoom or pan\n");
+ } else if ((accepted = _gui_input_select(p_event))) {
+ //printf("Selection\n");
}
if (accepted)
accept_event();
+ // Handles the mouse hovering
+ _gui_input_hover(p_event);
+
// Change the cursor
CursorShape c = CURSOR_ARROW;
switch (drag_type) {
case DRAG_NONE:
- if (Input::get_singleton()->is_mouse_button_pressed(BUTTON_MIDDLE) || Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
- c = CURSOR_DRAG;
- } else {
- switch (tool) {
- case TOOL_MOVE:
- c = CURSOR_MOVE;
- break;
- case TOOL_EDIT_PIVOT:
- c = CURSOR_CROSS;
- break;
- case TOOL_PAN:
- c = CURSOR_DRAG;
- break;
- default:
- break;
- }
+ switch (tool) {
+ case TOOL_MOVE:
+ c = CURSOR_MOVE;
+ break;
+ case TOOL_EDIT_PIVOT:
+ c = CURSOR_CROSS;
+ break;
+ case TOOL_PAN:
+ c = CURSOR_DRAG;
+ break;
+ default:
+ break;
}
break;
case DRAG_LEFT:
@@ -1870,6 +1986,8 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) {
case DRAG_ALL:
c = CURSOR_MOVE;
break;
+ case DRAG_PAN:
+ c = CURSOR_DRAG;
default:
break;
}
@@ -2095,6 +2213,9 @@ void CanvasItemEditor::_draw_grid() {
void CanvasItemEditor::_draw_selection() {
Ref<Texture> pivot_icon = get_icon("EditorPivot", "EditorIcons");
+ Ref<Texture> position_icon = get_icon("EditorPosition", "EditorIcons");
+ Ref<Texture> previous_position_icon = get_icon("EditorPositionPrevious", "EditorIcons");
+
RID ci = viewport->get_canvas_item();
List<CanvasItem *> selection = _get_edited_canvas_items(false, false);
@@ -2104,8 +2225,6 @@ void CanvasItemEditor::_draw_selection() {
CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get());
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- Rect2 rect = canvas_item->_edit_get_rect();
-
// Draw the previous position if we are dragging the node
if (show_helpers &&
(drag_type == DRAG_ALL || drag_type == DRAG_ROTATE ||
@@ -2114,42 +2233,52 @@ void CanvasItemEditor::_draw_selection() {
const Transform2D pre_drag_xform = transform * se->pre_drag_xform;
const Color pre_drag_color = Color(0.4, 0.6, 1, 0.7);
- Vector2 pre_drag_endpoints[4] = {
+ if (canvas_item->_edit_use_rect()) {
+ Vector2 pre_drag_endpoints[4] = {
- pre_drag_xform.xform(se->pre_drag_rect.position),
- pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(se->pre_drag_rect.size.x, 0)),
- pre_drag_xform.xform(se->pre_drag_rect.position + se->pre_drag_rect.size),
- pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(0, se->pre_drag_rect.size.y))
- };
+ pre_drag_xform.xform(se->pre_drag_rect.position),
+ pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(se->pre_drag_rect.size.x, 0)),
+ pre_drag_xform.xform(se->pre_drag_rect.position + se->pre_drag_rect.size),
+ pre_drag_xform.xform(se->pre_drag_rect.position + Vector2(0, se->pre_drag_rect.size.y))
+ };
- for (int i = 0; i < 4; i++) {
- viewport->draw_line(pre_drag_endpoints[i], pre_drag_endpoints[(i + 1) % 4], pre_drag_color, 2);
+ for (int i = 0; i < 4; i++) {
+ viewport->draw_line(pre_drag_endpoints[i], pre_drag_endpoints[(i + 1) % 4], pre_drag_color, 2);
+ }
+ } else {
+ viewport->draw_texture(previous_position_icon, (pre_drag_xform.xform(Point2()) - (previous_position_icon->get_size() / 2)).floor());
}
}
Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
- VisualServer::get_singleton()->canvas_item_add_set_transform(ci, xform);
- // Draw the selected items surrounding boxes
- Vector2 endpoints[4] = {
- xform.xform(rect.position),
- xform.xform(rect.position + Vector2(rect.size.x, 0)),
- xform.xform(rect.position + rect.size),
- xform.xform(rect.position + Vector2(0, rect.size.y))
- };
+ // Draw the selected items position / surrounding boxes
+ if (canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ Vector2 endpoints[4] = {
+ xform.xform(rect.position),
+ xform.xform(rect.position + Vector2(rect.size.x, 0)),
+ xform.xform(rect.position + rect.size),
+ xform.xform(rect.position + Vector2(0, rect.size.y))
+ };
- Color c = Color(1, 0.6, 0.4, 0.7);
+ Color c = Color(1, 0.6, 0.4, 0.7);
- VisualServer::get_singleton()->canvas_item_add_set_transform(ci, Transform2D());
+ for (int i = 0; i < 4; i++) {
+ viewport->draw_line(endpoints[i], endpoints[(i + 1) % 4], c, 2);
+ }
+ } else {
- for (int i = 0; i < 4; i++) {
- viewport->draw_line(endpoints[i], endpoints[(i + 1) % 4], c, 2);
+ Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin());
+ viewport->draw_set_transform_matrix(transform);
+ viewport->draw_texture(position_icon, -(position_icon->get_size() / 2));
+ viewport->draw_set_transform_matrix(Transform2D());
}
if (single && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks
// Draw the pivot
if (canvas_item->_edit_get_pivot() != Vector2() || drag_type == DRAG_PIVOT || tool == TOOL_EDIT_PIVOT) { // This is not really clean :/
- viewport->draw_texture(pivot_icon, xform.xform(canvas_item->_edit_get_pivot()) + (-pivot_icon->get_size() / 2).floor());
+ viewport->draw_texture(pivot_icon, (xform.xform(canvas_item->_edit_get_pivot()) - (pivot_icon->get_size() / 2)).floor());
}
Control *control = Object::cast_to<Control>(canvas_item);
@@ -2331,7 +2460,14 @@ void CanvasItemEditor::_draw_selection() {
}
}
- if (tool == TOOL_SELECT) {
+ if (tool == TOOL_SELECT && canvas_item->_edit_use_rect()) {
+ Rect2 rect = canvas_item->_edit_get_rect();
+ Vector2 endpoints[4] = {
+ xform.xform(rect.position),
+ xform.xform(rect.position + Vector2(rect.size.x, 0)),
+ xform.xform(rect.position + rect.size),
+ xform.xform(rect.position + Vector2(0, rect.size.y))
+ };
for (int i = 0; i < 4; i++) {
// Draw the resize handles
int prev = (i + 3) % 4;
@@ -2447,43 +2583,52 @@ void CanvasItemEditor::_draw_bones() {
Color bone_color1 = EditorSettings::get_singleton()->get("editors/2d/bone_color1");
Color bone_color2 = EditorSettings::get_singleton()->get("editors/2d/bone_color2");
Color bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color");
+ Color bone_outline_color = EditorSettings::get_singleton()->get("editors/2d/bone_outline_color");
Color bone_selected_color = EditorSettings::get_singleton()->get("editors/2d/bone_selected_color");
+ int bone_outline_size = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size");
- for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
+ for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
- E->get().from = Vector2();
- E->get().to = Vector2();
+ Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().from));
+ Node2D *to_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().to));
- Node2D *n2d = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key()));
- if (!n2d)
+ if (!from_node->is_inside_tree())
+ continue; //may have been removed
+ if (!from_node)
continue;
- if (!n2d->get_parent())
+ if (!to_node && E->get().length == 0)
continue;
- CanvasItem *pi = n2d->get_parent_item();
+ Vector2 from = transform.xform(from_node->get_global_position());
+ Vector2 to;
- Node2D *pn2d = Object::cast_to<Node2D>(n2d->get_parent());
-
- if (!pn2d)
- continue;
-
- Vector2 from = transform.xform(pn2d->get_global_position());
- Vector2 to = transform.xform(n2d->get_global_position());
-
- E->get().from = from;
- E->get().to = to;
+ if (to_node)
+ to = transform.xform(to_node->get_global_position());
+ else
+ to = transform.xform(from_node->get_global_transform().xform(Vector2(E->get().length, 0)));
Vector2 rel = to - from;
Vector2 relt = rel.tangent().normalized() * bone_width;
+ Vector2 reln = rel.normalized();
+ Vector2 reltn = relt.normalized();
Vector<Vector2> bone_shape;
bone_shape.push_back(from);
bone_shape.push_back(from + rel * 0.2 + relt);
bone_shape.push_back(to);
bone_shape.push_back(from + rel * 0.2 - relt);
+
+ Vector<Vector2> bone_shape_outline;
+ bone_shape_outline.push_back(from + (-reln - reltn) * bone_outline_size);
+ bone_shape_outline.push_back(from + (-reln + reltn) * bone_outline_size);
+ bone_shape_outline.push_back(from + rel * 0.2 + relt + reltn * bone_outline_size);
+ bone_shape_outline.push_back(to + (reln + reltn) * bone_outline_size);
+ bone_shape_outline.push_back(to + (reln - reltn) * bone_outline_size);
+ bone_shape_outline.push_back(from + rel * 0.2 - relt - reltn * bone_outline_size);
+
Vector<Color> colors;
- if (pi->has_meta("_edit_ik_")) {
+ if (from_node->has_meta("_edit_ik_")) {
colors.push_back(bone_ik_color);
colors.push_back(bone_ik_color);
@@ -2496,68 +2641,213 @@ void CanvasItemEditor::_draw_bones() {
colors.push_back(bone_color2);
}
+ Vector<Color> outline_colors;
+
+ if (editor_selection->is_selected(from_node)) {
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ } else {
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ }
+
+ VisualServer::get_singleton()->canvas_item_add_polygon(ci, bone_shape_outline, outline_colors);
VisualServer::get_singleton()->canvas_item_add_primitive(ci, bone_shape, colors, Vector<Vector2>(), RID());
+ }
+ }
+}
- if (editor_selection->is_selected(pi)) {
- for (int i = 0; i < bone_shape.size(); i++) {
+void CanvasItemEditor::_draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
+ ERR_FAIL_COND(!p_node);
- VisualServer::get_singleton()->canvas_item_add_line(ci, bone_shape[i], bone_shape[(i + 1) % bone_shape.size()], bone_selected_color, 2);
- }
+ Node *scene = editor->get_edited_scene();
+ if (p_node != scene && p_node->get_owner() != scene && !scene->is_editable_instance(p_node->get_owner()))
+ return;
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ if (canvas_item && !canvas_item->is_visible())
+ return;
+
+ Transform2D parent_xform = p_parent_xform;
+ Transform2D canvas_xform = p_canvas_xform;
+
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ parent_xform = parent_xform * canvas_item->get_transform();
+ } else {
+ CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
+ parent_xform = Transform2D();
+ canvas_xform = cl ? cl->get_transform() : p_canvas_xform;
+ }
+
+ for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
+ _draw_invisible_nodes_positions(p_node->get_child(i), parent_xform, canvas_xform);
+ }
+
+ if (canvas_item && !canvas_item->_edit_use_rect() && !editor_selection->is_selected(canvas_item)) {
+ Transform2D xform = transform * canvas_xform * parent_xform;
+
+ // Draw the node's position
+ Ref<Texture> position_icon = get_icon("EditorPositionUnselected", "EditorIcons");
+ Transform2D transform = Transform2D(xform.get_rotation(), xform.get_origin());
+ viewport->draw_set_transform_matrix(transform);
+ viewport->draw_texture(position_icon, -position_icon->get_size() / 2, Color(1.0, 1.0, 1.0, 0.5));
+ viewport->draw_set_transform_matrix(Transform2D());
+ }
+}
+
+void CanvasItemEditor::_draw_hover() {
+ List<Rect2> previous_rects;
+
+ for (int i = 0; i < hovering_results.size(); i++) {
+ // Draw the node's name and icon
+ CanvasItem *canvas_item = hovering_results[i].item;
+
+ if (canvas_item->_edit_use_rect())
+ continue;
+
+ Transform2D xform = transform * canvas_item->get_global_transform_with_canvas();
+
+ // Get the resources
+ Ref<Texture> node_icon;
+ if (has_icon(canvas_item->get_class(), "EditorIcons"))
+ node_icon = get_icon(canvas_item->get_class(), "EditorIcons");
+ else
+ node_icon = get_icon("Object", "EditorIcons");
+ Ref<Font> font = get_font("font", "Label");
+ String node_name = canvas_item->get_name();
+ Size2 node_name_size = font->get_string_size(node_name);
+ Size2 item_size = Size2(node_icon->get_size().x + 4 + node_name_size.x, MAX(node_icon->get_size().y, node_name_size.y - 3));
+
+ Point2 pos = xform.get_origin() - Point2(0, item_size.y) + (Point2(node_icon->get_size().x, -node_icon->get_size().y) / 4);
+ // Rectify the position to avoid overlaping items
+ for (List<Rect2>::Element *E = previous_rects.front(); E; E = E->next()) {
+ if (E->get().intersects(Rect2(pos, item_size))) {
+ pos.y = E->get().get_position().y - item_size.y;
}
}
+
+ previous_rects.push_back(Rect2(pos, item_size));
+
+ // Draw the node icon and name
+ viewport->draw_texture(node_icon, pos, Color(1.0, 1.0, 1.0, 0.5));
+ viewport->draw_string(font, pos + Point2(node_icon->get_size().x + 4, item_size.y - 3), node_name, Color(1.0, 1.0, 1.0, 0.5));
}
}
-void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p_xform) {
+void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) {
ERR_FAIL_COND(!p_node);
- RID viewport_ci = viewport->get_canvas_item();
+ Node *scene = editor->get_edited_scene();
+ if (p_node != scene && p_node->get_owner() != scene && !scene->is_editable_instance(p_node->get_owner()))
+ return;
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ if (canvas_item && !canvas_item->is_visible())
+ return;
+
+ Transform2D parent_xform = p_parent_xform;
+ Transform2D canvas_xform = p_canvas_xform;
- Transform2D transform_ci = p_xform;
- CanvasItem *ci = Object::cast_to<CanvasItem>(p_node);
- if (ci)
- transform_ci = transform_ci * ci->get_transform();
+ if (canvas_item && !canvas_item->is_set_as_toplevel()) {
+ parent_xform = parent_xform * canvas_item->get_transform();
+ } else {
+ CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node);
+ parent_xform = Transform2D();
+ canvas_xform = cl ? cl->get_transform() : p_canvas_xform;
+ }
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
- _draw_locks_and_groups(p_node->get_child(i), transform_ci);
+ _draw_locks_and_groups(p_node->get_child(i), parent_xform, canvas_xform);
}
- if (ci) {
+ RID viewport_canvas_item = viewport->get_canvas_item();
+ if (canvas_item) {
+ float offset = 0;
+
Ref<Texture> lock = get_icon("LockViewport", "EditorIcons");
if (p_node->has_meta("_edit_lock_")) {
- lock->draw(viewport_ci, transform_ci.xform(Point2(0, 0)));
+ lock->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0));
+ offset += lock->get_size().x;
}
Ref<Texture> group = get_icon("GroupViewport", "EditorIcons");
- if (ci->has_meta("_edit_group_")) {
- Vector2 ofs = transform_ci.xform(Point2(0, 0));
- if (ci->has_meta("_edit_lock_"))
- ofs = Point2(ofs.x + lock->get_size().x, ofs.y);
- group->draw(viewport_ci, ofs);
+ if (canvas_item->has_meta("_edit_group_")) {
+ group->draw(viewport_canvas_item, (transform * canvas_xform * parent_xform).xform(Point2(0, 0)) + Point2(offset, 0));
+ //offset += group->get_size().x;
}
}
}
-void CanvasItemEditor::_build_bones_list(Node *p_node) {
- ERR_FAIL_COND(!p_node);
+bool CanvasItemEditor::_build_bones_list(Node *p_node) {
+ ERR_FAIL_COND_V(!p_node, false);
+
+ bool has_child_bones = false;
for (int i = 0; i < p_node->get_child_count(); i++) {
- _build_bones_list(p_node->get_child(i));
+ if (_build_bones_list(p_node->get_child(i))) {
+ has_child_bones = true;
+ }
}
- CanvasItem *c = Object::cast_to<CanvasItem>(p_node);
- if (c && c->is_visible_in_tree()) {
- if (c->has_meta("_edit_bone_")) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_node);
+ Node *scene = editor->get_edited_scene();
+ if (!canvas_item || !canvas_item->is_visible() || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner()))) {
+ return false;
+ }
- ObjectID id = c->get_instance_id();
- if (!bone_list.has(id)) {
- BoneList bone;
- bone_list[id] = bone;
+ Node *parent = canvas_item->get_parent();
+
+ if (Object::cast_to<Bone2D>(canvas_item)) {
+ if (Object::cast_to<Bone2D>(parent)) {
+ // Add as bone->parent relationship
+ BoneKey bk;
+ bk.from = parent->get_instance_id();
+ bk.to = canvas_item->get_instance_id();
+ if (!bone_list.has(bk)) {
+ BoneList b;
+ b.length = 0;
+ bone_list[bk] = b;
+ }
+
+ bone_list[bk].last_pass = bone_last_frame;
+ }
+
+ if (!has_child_bones) {
+ // Add a last bone if the Bone2D has no Bone2D child
+ BoneKey bk;
+ bk.from = canvas_item->get_instance_id();
+ bk.to = 0;
+ if (!bone_list.has(bk)) {
+ BoneList b;
+ b.length = 0;
+ bone_list[bk] = b;
}
+ bone_list[bk].last_pass = bone_last_frame;
+ }
- bone_list[id].last_pass = bone_last_frame;
+ return true;
+ }
+
+ if (canvas_item->has_meta("_edit_bone_")) {
+ // Add a "custom bone"
+ BoneKey bk;
+ bk.from = parent->get_instance_id();
+ bk.to = canvas_item->get_instance_id();
+ if (!bone_list.has(bk)) {
+ BoneList b;
+ b.length = 0;
+ bone_list[bk] = b;
}
+ bone_list[bk].last_pass = bone_last_frame;
}
+
+ return false;
}
void CanvasItemEditor::_draw_viewport() {
@@ -2599,8 +2889,10 @@ void CanvasItemEditor::_draw_viewport() {
_draw_grid();
_draw_selection();
_draw_axis();
- if (editor->get_edited_scene())
- _draw_locks_and_groups(editor->get_edited_scene(), transform);
+ if (editor->get_edited_scene()) {
+ _draw_locks_and_groups(editor->get_edited_scene());
+ _draw_invisible_nodes_positions(editor->get_edited_scene());
+ }
RID ci = viewport->get_canvas_item();
VisualServer::get_singleton()->canvas_item_add_set_transform(ci, Transform2D());
@@ -2620,6 +2912,7 @@ void CanvasItemEditor::_draw_viewport() {
if (show_guides)
_draw_guides();
_draw_focus();
+ _draw_hover();
}
void CanvasItemEditor::_notification(int p_what) {
@@ -2630,17 +2923,23 @@ void CanvasItemEditor::_notification(int p_what) {
int nb_control = 0;
int nb_having_pivot = 0;
+ // Update the viewport if the canvas_item changes
List<CanvasItem *> selection = _get_edited_canvas_items();
for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) {
CanvasItem *canvas_item = E->get();
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
- Rect2 r = canvas_item->_edit_get_rect();
+ Rect2 rect;
+ if (canvas_item->_edit_use_rect()) {
+ rect = canvas_item->_edit_get_rect();
+ } else {
+ rect = Rect2();
+ }
Transform2D xform = canvas_item->get_transform();
- if (r != se->prev_rect || xform != se->prev_xform) {
+ if (rect != se->prev_rect || xform != se->prev_xform) {
viewport->update();
- se->prev_rect = r;
+ se->prev_rect = rect;
se->prev_xform = xform;
}
@@ -2670,15 +2969,17 @@ void CanvasItemEditor::_notification(int p_what) {
nb_having_pivot++;
}
}
+
// Activate / Deactivate the pivot tool
pivot_button->set_disabled(nb_having_pivot == 0);
// Show / Hide the layout button
presets_menu->set_visible(nb_control > 0 && nb_control == selection.size());
- for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
+ // Update the viewport if bones changes
+ for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
- Object *b = ObjectDB::get_instance(E->key());
+ Object *b = ObjectDB::get_instance(E->key().from);
if (!b) {
viewport->update();
@@ -2686,13 +2987,22 @@ void CanvasItemEditor::_notification(int p_what) {
}
Node2D *b2 = Object::cast_to<Node2D>(b);
- if (!b2) {
+ if (!b2 || !b2->is_inside_tree()) {
continue;
}
- if (b2->get_global_transform() != E->get().xform) {
+ Transform2D global_xform = b2->get_global_transform();
+
+ if (global_xform != E->get().xform) {
+
+ E->get().xform = global_xform;
+ viewport->update();
+ }
+
+ Bone2D *bone = Object::cast_to<Bone2D>(b);
+ if (bone && bone->get_default_length() != E->get().length) {
- E->get().xform = b2->get_global_transform();
+ E->get().length = bone->get_default_length();
viewport->update();
}
}
@@ -2708,12 +3018,19 @@ void CanvasItemEditor::_notification(int p_what) {
AnimationPlayerEditor::singleton->get_key_editor()->connect("visibility_changed", this, "_keying_changed");
_keying_changed();
+ get_tree()->connect("node_added", this, "_tree_changed", varray());
+ get_tree()->connect("node_removed", this, "_tree_changed", varray());
} else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
select_sb->set_texture(get_icon("EditorRect2D", "EditorIcons"));
}
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ get_tree()->disconnect("node_added", this, "_tree_changed");
+ get_tree()->disconnect("node_removed", this, "_tree_changed");
+ }
+
if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
select_button->set_icon(get_icon("ToolSelect", "EditorIcons"));
list_select_button->set_icon(get_icon("ListSelect", "EditorIcons"));
@@ -2798,6 +3115,47 @@ void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
editor_selection->add_node(p_canvas_item);
}
+void CanvasItemEditor::_queue_update_bone_list() {
+
+ if (bone_list_dirty)
+ return;
+
+ call_deferred("_update_bone_list");
+ bone_list_dirty = true;
+}
+
+void CanvasItemEditor::_update_bone_list() {
+
+ bone_last_frame++;
+
+ if (editor->get_edited_scene()) {
+ _build_bones_list(editor->get_edited_scene());
+ }
+
+ List<Map<BoneKey, BoneList>::Element *> bone_to_erase;
+ for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
+ if (E->get().last_pass != bone_last_frame) {
+ bone_to_erase.push_back(E);
+ continue;
+ }
+
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E->key().from));
+ if (!node || !node->is_inside_tree() || (node != get_tree()->get_edited_scene_root() && !get_tree()->get_edited_scene_root()->is_a_parent_of(node))) {
+ bone_to_erase.push_back(E);
+ continue;
+ }
+ }
+ while (bone_to_erase.size()) {
+ bone_list.erase(bone_to_erase.front()->get());
+ bone_to_erase.pop_front();
+ }
+ bone_list_dirty = false;
+}
+
+void CanvasItemEditor::_tree_changed(Node *) {
+ _queue_update_bone_list();
+}
+
void CanvasItemEditor::_update_scrollbars() {
updating_scroll = true;
@@ -2822,27 +3180,12 @@ void CanvasItemEditor::_update_scrollbars() {
Size2 screen_rect = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
Rect2 local_rect = Rect2(Point2(), viewport->get_size() - Size2(vmin.width, hmin.height));
- bone_last_frame++;
-
- if (editor->get_edited_scene()) {
- _build_bones_list(editor->get_edited_scene());
- }
-
- List<Map<ObjectID, BoneList>::Element *> bone_to_erase;
- for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
- if (E->get().last_pass != bone_last_frame) {
- bone_to_erase.push_back(E);
- }
- }
- while (bone_to_erase.size()) {
- bone_list.erase(bone_to_erase.front()->get());
- bone_to_erase.pop_front();
- }
+ _queue_update_bone_list();
// Calculate scrollable area
Rect2 canvas_item_rect = Rect2(Point2(), screen_rect);
if (editor->get_edited_scene()) {
- Rect2 content_rect = _get_scene_encompassing_rect();
+ Rect2 content_rect = _get_encompassing_rect(editor->get_edited_scene());
canvas_item_rect.expand_to(content_rect.position);
canvas_item_rect.expand_to(content_rect.position + content_rect.size);
}
@@ -2982,7 +3325,7 @@ void CanvasItemEditor::_zoom_on_position(float p_zoom, Point2 p_position) {
}
void CanvasItemEditor::_button_zoom_minus() {
- _zoom_on_position(zoom / 2.0, viewport_scrollable->get_size() / 2.0);
+ _zoom_on_position(zoom / 1.5, viewport_scrollable->get_size() / 2.0);
}
void CanvasItemEditor::_button_zoom_reset() {
@@ -2990,7 +3333,7 @@ void CanvasItemEditor::_button_zoom_reset() {
}
void CanvasItemEditor::_button_zoom_plus() {
- _zoom_on_position(zoom * 2.0, viewport_scrollable->get_size() / 2.0);
+ _zoom_on_position(zoom * 1.5, viewport_scrollable->get_size() / 2.0);
}
void CanvasItemEditor::_button_toggle_snap(bool p_status) {
@@ -3541,7 +3884,12 @@ void CanvasItemEditor::_focus_selection(int p_op) {
//if (!canvas_item->is_visible_in_tree()) continue;
++count;
- Rect2 item_rect = canvas_item->_edit_get_rect();
+ Rect2 item_rect;
+ if (canvas_item->_edit_use_rect()) {
+ item_rect = canvas_item->_edit_get_rect();
+ } else {
+ item_rect = Rect2();
+ }
Vector2 pos = canvas_item->get_global_transform().get_origin();
Vector2 scale = canvas_item->get_global_transform().get_scale();
@@ -3596,6 +3944,9 @@ void CanvasItemEditor::_bind_methods() {
ClassDB::bind_method("_draw_viewport", &CanvasItemEditor::_draw_viewport);
ClassDB::bind_method("_gui_input_viewport", &CanvasItemEditor::_gui_input_viewport);
ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed);
+ ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list);
+ ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed);
+
ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed);
ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide);
ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state);
@@ -3799,6 +4150,7 @@ void CanvasItemEditor::focus_selection() {
CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
+ bone_list_dirty = false;
tool = TOOL_SELECT;
undo_redo = p_editor->get_undo_redo();
editor = p_editor;
@@ -3858,16 +4210,19 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
zoom_minus = memnew(ToolButton);
zoom_hb->add_child(zoom_minus);
zoom_minus->connect("pressed", this, "_button_zoom_minus");
+ zoom_minus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_minus", TTR("Zoom out"), KEY_MASK_CMD | KEY_MINUS));
zoom_minus->set_focus_mode(FOCUS_NONE);
zoom_reset = memnew(ToolButton);
zoom_hb->add_child(zoom_reset);
zoom_reset->connect("pressed", this, "_button_zoom_reset");
+ zoom_reset->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_reset", TTR("Zoom reset"), KEY_MASK_CMD | KEY_0));
zoom_reset->set_focus_mode(FOCUS_NONE);
zoom_plus = memnew(ToolButton);
zoom_hb->add_child(zoom_plus);
zoom_plus->connect("pressed", this, "_button_zoom_plus");
+ zoom_plus->set_shortcut(ED_SHORTCUT("canvas_item_editor/zoom_plus", TTR("Zoom in"), KEY_MASK_CMD | KEY_PLUS));
zoom_plus->set_focus_mode(FOCUS_NONE);
updating_scroll = false;
@@ -3980,13 +4335,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
p = skeleton_menu->get_popup();
p->set_hide_on_checkable_item_selection(false);
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Bones"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES);
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Bones")), SKELETON_CLEAR_BONES);
- p->add_separator();
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_show_bones", TTR("Show Bones")), SKELETON_SHOW_BONES);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_set_ik_chain", TTR("Make IK Chain")), SKELETON_SET_IK_CHAIN);
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_ik_chain", TTR("Clear IK Chain")), SKELETON_CLEAR_IK_CHAIN);
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Custom Bone(s) from Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Custom Bones")), SKELETON_CLEAR_BONES);
p->connect("id_pressed", this, "_popup_callback");
hb->add_child(memnew(VSeparator));
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index da7ce9172..a1957b892 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -194,7 +194,8 @@ class CanvasItemEditor : public VBoxContainer {
DRAG_V_GUIDE,
DRAG_H_GUIDE,
DRAG_DOUBLE_GUIDE,
- DRAG_KEY_MOVE
+ DRAG_KEY_MOVE,
+ DRAG_PAN
};
EditorSelection *editor_selection;
@@ -258,16 +259,29 @@ class CanvasItemEditor : public VBoxContainer {
};
Vector<_SelectResult> selection_results;
+ Vector<_SelectResult> hovering_results;
struct BoneList {
Transform2D xform;
- Vector2 from;
- Vector2 to;
+ float length;
uint64_t last_pass;
};
+
uint64_t bone_last_frame;
- Map<ObjectID, BoneList> bone_list;
+
+ struct BoneKey {
+ ObjectID from;
+ ObjectID to;
+ _FORCE_INLINE_ bool operator<(const BoneKey &p_key) const {
+ if (from == p_key.from)
+ return to < p_key.to;
+ else
+ return from < p_key.from;
+ }
+ };
+
+ Map<BoneKey, BoneList> bone_list;
struct PoseClipboard {
Vector2 pos;
@@ -335,8 +349,10 @@ class CanvasItemEditor : public VBoxContainer {
Ref<ShortCut> multiply_grid_step_shortcut;
Ref<ShortCut> divide_grid_step_shortcut;
- void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
- void _find_canvas_items_at_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit = 0);
+
+ void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
bool _select_click_on_item(CanvasItem *item, Point2 p_click_pos, bool p_append);
ConfirmationDialog *snap_dialog;
@@ -362,12 +378,12 @@ class CanvasItemEditor : public VBoxContainer {
void _selection_menu_hide();
UndoRedo *undo_redo;
- void _build_bones_list(Node *p_node);
+ bool _build_bones_list(Node *p_node);
List<CanvasItem *> _get_edited_canvas_items(bool retreive_locked = false, bool remove_canvas_item_if_parent_in_selection = true);
Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list);
- void _expand_encompassing_rect_using_children(Rect2 &p_rect, Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
- Rect2 _get_scene_encompassing_rect();
+ void _expand_encompassing_rect_using_children(Rect2 &p_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ Rect2 _get_encompassing_rect(const Node *p_node);
Object *_get_editor_data(Object *p_what);
@@ -387,7 +403,9 @@ class CanvasItemEditor : public VBoxContainer {
void _draw_selection();
void _draw_axis();
void _draw_bones();
- void _draw_locks_and_groups(Node *p_node, const Transform2D &p_xform);
+ void _draw_invisible_nodes_positions(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ void _draw_locks_and_groups(Node *p_node, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D());
+ void _draw_hover();
void _draw_viewport();
@@ -400,6 +418,7 @@ class CanvasItemEditor : public VBoxContainer {
bool _gui_input_select(const Ref<InputEvent> &p_event);
bool _gui_input_zoom_or_pan(const Ref<InputEvent> &p_event);
bool _gui_input_rulers_and_guides(const Ref<InputEvent> &p_event);
+ bool _gui_input_hover(const Ref<InputEvent> &p_event);
void _gui_input_viewport(const Ref<InputEvent> &p_event);
@@ -426,6 +445,11 @@ class CanvasItemEditor : public VBoxContainer {
HSplitContainer *palette_split;
VSplitContainer *bottom_split;
+ bool bone_list_dirty;
+ void _queue_update_bone_list();
+ void _update_bone_list();
+ void _tree_changed(Node *);
+
friend class CanvasItemEditorPlugin;
protected:
diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp
index 4410242d9..e837359d0 100644
--- a/editor/plugins/collision_polygon_editor_plugin.cpp
+++ b/editor/plugins/collision_polygon_editor_plugin.cpp
@@ -33,10 +33,12 @@
#include "canvas_item_editor_plugin.h"
#include "editor/editor_settings.h"
#include "os/file_access.h"
+#include "os/input.h"
+#include "os/keyboard.h"
#include "scene/3d/camera.h"
#include "spatial_editor_plugin.h"
-void CollisionPolygonEditor::_notification(int p_what) {
+void Polygon3DEditor::_notification(int p_what) {
switch (p_what) {
@@ -53,15 +55,15 @@ void CollisionPolygonEditor::_notification(int p_what) {
return;
}
- if (node->get_depth() != prev_depth) {
+ if (_get_depth() != prev_depth) {
_polygon_draw();
- prev_depth = node->get_depth();
+ prev_depth = _get_depth();
}
} break;
}
}
-void CollisionPolygonEditor::_node_removed(Node *p_node) {
+void Polygon3DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
node = NULL;
@@ -72,7 +74,7 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) {
}
}
-void CollisionPolygonEditor::_menu_option(int p_option) {
+void Polygon3DEditor::_menu_option(int p_option) {
switch (p_option) {
@@ -91,10 +93,10 @@ void CollisionPolygonEditor::_menu_option(int p_option) {
}
}
-void CollisionPolygonEditor::_wip_close() {
+void Polygon3DEditor::_wip_close() {
undo_redo->create_action(TTR("Create Poly3D"));
- undo_redo->add_undo_method(node, "set_polygon", node->get_polygon());
+ undo_redo->add_undo_method(node, "set_polygon", node->call("get_polygon"));
undo_redo->add_do_method(node, "set_polygon", wip);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
@@ -107,14 +109,14 @@ void CollisionPolygonEditor::_wip_close() {
undo_redo->commit_action();
}
-bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
+bool Polygon3DEditor::forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) {
if (!node)
return false;
Transform gt = node->get_global_transform();
Transform gi = gt.affine_inverse();
- float depth = node->get_depth() * 0.5;
+ float depth = _get_depth() * 0.5;
Vector3 n = gt.basis.get_axis(2).normalized();
Plane p(gt.origin + n * depth, n);
@@ -135,9 +137,11 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
Vector2 cpoint(spoint.x, spoint.y);
- cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
+ //DO NOT snap here, it's confusing in 3D for adding points.
+ //Let the snap happen when the point is being moved, instead.
+ //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
- Vector<Vector2> poly = node->get_polygon();
+ Vector<Vector2> poly = node->call("get_polygon");
//first check if a point is to be added (segment split)
real_t grab_threshold = EDITOR_DEF("editors/poly_editor/point_grab_radius", 8);
@@ -154,6 +158,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
wip.push_back(cpoint);
wip_active = true;
edited_point_pos = cpoint;
+ snap_ignore = false;
_polygon_draw();
edited_point = 1;
return true;
@@ -168,6 +173,7 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
wip.push_back(cpoint);
edited_point = wip.size();
+ snap_ignore = false;
_polygon_draw();
return true;
}
@@ -226,8 +232,10 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
poly.insert(closest_idx + 1, cpoint);
edited_point = closest_idx + 1;
edited_point_pos = cpoint;
- node->set_polygon(poly);
+ node->call("set_polygon", poly);
_polygon_draw();
+ snap_ignore = true;
+
return true;
}
} else {
@@ -255,11 +263,14 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
edited_point = closest_idx;
edited_point_pos = poly[closest_idx];
_polygon_draw();
+ snap_ignore = false;
return true;
}
}
} else {
+ snap_ignore = false;
+
if (edited_point != -1) {
//apply
@@ -315,7 +326,6 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
-
if (edited_point != -1 && (wip_active || mm->get_button_mask() & BUTTON_MASK_LEFT)) {
Vector2 gpoint = mm->get_position();
@@ -332,7 +342,13 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
Vector2 cpoint(spoint.x, spoint.y);
- cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
+ if (snap_ignore && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) {
+ snap_ignore = false;
+ }
+
+ if (!snap_ignore) {
+ cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint);
+ }
edited_point_pos = cpoint;
_polygon_draw();
@@ -341,7 +357,16 @@ bool CollisionPolygonEditor::forward_spatial_gui_input(Camera *p_camera, const R
return false;
}
-void CollisionPolygonEditor::_polygon_draw() {
+
+float Polygon3DEditor::_get_depth() {
+
+ if (bool(node->call("_has_editable_3d_polygon_no_depth")))
+ return 0;
+
+ return float(node->call("get_depth"));
+}
+
+void Polygon3DEditor::_polygon_draw() {
if (!node)
return;
@@ -351,9 +376,9 @@ void CollisionPolygonEditor::_polygon_draw() {
if (wip_active)
poly = wip;
else
- poly = node->get_polygon();
+ poly = node->call("get_polygon");
- float depth = node->get_depth() * 0.5;
+ float depth = _get_depth() * 0.5;
imgeom->clear();
imgeom->set_material_override(line_material);
@@ -464,13 +489,13 @@ void CollisionPolygonEditor::_polygon_draw() {
m->surface_set_material(0, handle_material);
}
-void CollisionPolygonEditor::edit(Node *p_collision_polygon) {
+void Polygon3DEditor::edit(Node *p_collision_polygon) {
if (p_collision_polygon) {
- node = Object::cast_to<CollisionPolygon>(p_collision_polygon);
+ node = Object::cast_to<Spatial>(p_collision_polygon);
//Enable the pencil tool if the polygon is empty
- if (node->get_polygon().size() == 0) {
+ if (Vector<Vector2>(node->call("get_polygon")).size() == 0) {
_menu_option(MODE_CREATE);
}
wip.clear();
@@ -491,14 +516,14 @@ void CollisionPolygonEditor::edit(Node *p_collision_polygon) {
}
}
-void CollisionPolygonEditor::_bind_methods() {
+void Polygon3DEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_menu_option"), &CollisionPolygonEditor::_menu_option);
- ClassDB::bind_method(D_METHOD("_polygon_draw"), &CollisionPolygonEditor::_polygon_draw);
- ClassDB::bind_method(D_METHOD("_node_removed"), &CollisionPolygonEditor::_node_removed);
+ ClassDB::bind_method(D_METHOD("_menu_option"), &Polygon3DEditor::_menu_option);
+ ClassDB::bind_method(D_METHOD("_polygon_draw"), &Polygon3DEditor::_polygon_draw);
+ ClassDB::bind_method(D_METHOD("_node_removed"), &Polygon3DEditor::_node_removed);
}
-CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) {
+Polygon3DEditor::Polygon3DEditor(EditorNode *p_editor) {
node = NULL;
editor = p_editor;
@@ -543,24 +568,26 @@ CollisionPolygonEditor::CollisionPolygonEditor(EditorNode *p_editor) {
m.instance();
pointsm->set_mesh(m);
pointsm->set_transform(Transform(Basis(), Vector3(0, 0, 0.00001)));
+
+ snap_ignore = false;
}
-CollisionPolygonEditor::~CollisionPolygonEditor() {
+Polygon3DEditor::~Polygon3DEditor() {
memdelete(imgeom);
}
-void CollisionPolygonEditorPlugin::edit(Object *p_object) {
+void Polygon3DEditorPlugin::edit(Object *p_object) {
collision_polygon_editor->edit(Object::cast_to<Node>(p_object));
}
-bool CollisionPolygonEditorPlugin::handles(Object *p_object) const {
+bool Polygon3DEditorPlugin::handles(Object *p_object) const {
- return p_object->is_class("CollisionPolygon");
+ return Object::cast_to<Spatial>(p_object) && bool(p_object->call("_is_editable_3d_polygon"));
}
-void CollisionPolygonEditorPlugin::make_visible(bool p_visible) {
+void Polygon3DEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
collision_polygon_editor->show();
@@ -571,14 +598,14 @@ void CollisionPolygonEditorPlugin::make_visible(bool p_visible) {
}
}
-CollisionPolygonEditorPlugin::CollisionPolygonEditorPlugin(EditorNode *p_node) {
+Polygon3DEditorPlugin::Polygon3DEditorPlugin(EditorNode *p_node) {
editor = p_node;
- collision_polygon_editor = memnew(CollisionPolygonEditor(p_node));
+ collision_polygon_editor = memnew(Polygon3DEditor(p_node));
SpatialEditor::get_singleton()->add_control_to_menu_panel(collision_polygon_editor);
collision_polygon_editor->hide();
}
-CollisionPolygonEditorPlugin::~CollisionPolygonEditorPlugin() {
+Polygon3DEditorPlugin::~Polygon3DEditorPlugin() {
}
diff --git a/editor/plugins/collision_polygon_editor_plugin.h b/editor/plugins/collision_polygon_editor_plugin.h
index f1f215b09..4229808e2 100644
--- a/editor/plugins/collision_polygon_editor_plugin.h
+++ b/editor/plugins/collision_polygon_editor_plugin.h
@@ -44,9 +44,9 @@
class CanvasItemEditor;
-class CollisionPolygonEditor : public HBoxContainer {
+class Polygon3DEditor : public HBoxContainer {
- GDCLASS(CollisionPolygonEditor, HBoxContainer);
+ GDCLASS(Polygon3DEditor, HBoxContainer);
UndoRedo *undo_redo;
enum Mode {
@@ -66,7 +66,7 @@ class CollisionPolygonEditor : public HBoxContainer {
EditorNode *editor;
Panel *panel;
- CollisionPolygon *node;
+ Spatial *node;
ImmediateGeometry *imgeom;
MeshInstance *pointsm;
Ref<ArrayMesh> m;
@@ -78,6 +78,7 @@ class CollisionPolygonEditor : public HBoxContainer {
Vector<Vector2> pre_move_edit;
Vector<Vector2> wip;
bool wip_active;
+ bool snap_ignore;
float prev_depth;
@@ -85,6 +86,8 @@ class CollisionPolygonEditor : public HBoxContainer {
void _polygon_draw();
void _menu_option(int p_option);
+ float _get_depth();
+
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
@@ -93,28 +96,28 @@ protected:
public:
virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event);
void edit(Node *p_collision_polygon);
- CollisionPolygonEditor(EditorNode *p_editor);
- ~CollisionPolygonEditor();
+ Polygon3DEditor(EditorNode *p_editor);
+ ~Polygon3DEditor();
};
-class CollisionPolygonEditorPlugin : public EditorPlugin {
+class Polygon3DEditorPlugin : public EditorPlugin {
- GDCLASS(CollisionPolygonEditorPlugin, EditorPlugin);
+ GDCLASS(Polygon3DEditorPlugin, EditorPlugin);
- CollisionPolygonEditor *collision_polygon_editor;
+ Polygon3DEditor *collision_polygon_editor;
EditorNode *editor;
public:
virtual bool forward_spatial_gui_input(Camera *p_camera, const Ref<InputEvent> &p_event) { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
- virtual String get_name() const { return "CollisionPolygon"; }
+ virtual String get_name() const { return "Polygon3DEditor"; }
bool has_main_screen() const { return false; }
virtual void edit(Object *p_object);
virtual bool handles(Object *p_object) const;
virtual void make_visible(bool p_visible);
- CollisionPolygonEditorPlugin(EditorNode *p_node);
- ~CollisionPolygonEditorPlugin();
+ Polygon3DEditorPlugin(EditorNode *p_node);
+ ~Polygon3DEditorPlugin();
};
#endif // COLLISION_POLYGON_EDITOR_PLUGIN_H
diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp
index 5020f5b85..8b44f672b 100644
--- a/editor/plugins/item_list_editor_plugin.cpp
+++ b/editor/plugins/item_list_editor_plugin.cpp
@@ -42,9 +42,18 @@ bool ItemListPlugin::_set(const StringName &p_name, const Variant &p_value) {
set_item_text(idx, p_value);
else if (what == "icon")
set_item_icon(idx, p_value);
- else if (what == "checkable")
- set_item_checkable(idx, p_value);
- else if (what == "checked")
+ else if (what == "checkable") {
+ // This keeps compatibility to/from versions where this property was a boolean, before radio buttons
+ switch ((int)p_value) {
+ case 0:
+ case 1:
+ set_item_checkable(idx, p_value);
+ break;
+ case 2:
+ set_item_radio_checkable(idx, true);
+ break;
+ }
+ } else if (what == "checked")
set_item_checked(idx, p_value);
else if (what == "id")
set_item_id(idx, p_value);
@@ -68,9 +77,14 @@ bool ItemListPlugin::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_item_text(idx);
else if (what == "icon")
r_ret = get_item_icon(idx);
- else if (what == "checkable")
- r_ret = is_item_checkable(idx);
- else if (what == "checked")
+ else if (what == "checkable") {
+ // This keeps compatibility to/from versions where this property was a boolean, before radio buttons
+ if (!is_item_checkable(idx)) {
+ r_ret = 0;
+ } else {
+ r_ret = is_item_radio_checkable(idx) ? 2 : 1;
+ }
+ } else if (what == "checked")
r_ret = is_item_checked(idx);
else if (what == "id")
r_ret = get_item_id(idx);
@@ -95,7 +109,7 @@ void ItemListPlugin::_get_property_list(List<PropertyInfo> *p_list) const {
int flags = get_flags();
if (flags & FLAG_CHECKABLE) {
- p_list->push_back(PropertyInfo(Variant::BOOL, base + "checkable"));
+ p_list->push_back(PropertyInfo(Variant::BOOL, base + "checkable", PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"));
p_list->push_back(PropertyInfo(Variant::BOOL, base + "checked"));
}
diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h
index bd7d3db10..d6a071b9b 100644
--- a/editor/plugins/item_list_editor_plugin.h
+++ b/editor/plugins/item_list_editor_plugin.h
@@ -74,7 +74,9 @@ public:
virtual Ref<Texture> get_item_icon(int p_idx) const { return Ref<Texture>(); };
virtual void set_item_checkable(int p_idx, bool p_check) {}
+ virtual void set_item_radio_checkable(int p_idx, bool p_check) {}
virtual bool is_item_checkable(int p_idx) const { return false; };
+ virtual bool is_item_radio_checkable(int p_idx) const { return false; };
virtual void set_item_checked(int p_idx, bool p_checked) {}
virtual bool is_item_checked(int p_idx) const { return false; };
@@ -145,7 +147,9 @@ public:
virtual Ref<Texture> get_item_icon(int p_idx) const { return pp->get_item_icon(p_idx); }
virtual void set_item_checkable(int p_idx, bool p_check) { pp->set_item_as_checkable(p_idx, p_check); }
+ virtual void set_item_radio_checkable(int p_idx, bool p_check) { pp->set_item_as_radio_checkable(p_idx, p_check); }
virtual bool is_item_checkable(int p_idx) const { return pp->is_item_checkable(p_idx); }
+ virtual bool is_item_radio_checkable(int p_idx) const { return pp->is_item_radio_checkable(p_idx); }
virtual void set_item_checked(int p_idx, bool p_checked) { pp->set_item_checked(p_idx, p_checked); }
virtual bool is_item_checked(int p_idx) const { return pp->is_item_checked(p_idx); }
diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp
index cb5f7ba76..74b6f14c2 100644
--- a/editor/plugins/mesh_instance_editor_plugin.cpp
+++ b/editor/plugins/mesh_instance_editor_plugin.cpp
@@ -344,6 +344,10 @@ void MeshInstanceEditor::_create_outline_mesh() {
err_dialog->set_text(TTR("Mesh has not surface to create outlines from!"));
err_dialog->popup_centered_minsize();
return;
+ } else if (mesh->get_surface_count() == 1 && mesh->surface_get_primitive_type(0) != Mesh::PRIMITIVE_TRIANGLES) {
+ err_dialog->set_text(TTR("Mesh primitive type is not PRIMITIVE_TRIANGLES!"));
+ err_dialog->popup_centered_minsize();
+ return;
}
Ref<Mesh> mesho = mesh->create_outline(outline_size->get_value());
@@ -396,7 +400,7 @@ MeshInstanceEditor::MeshInstanceEditor() {
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH);
options->get_popup()->add_separator();
- options->get_popup()->add_item(TTR("Create Outline Mesh.."), MENU_OPTION_CREATE_OUTLINE_MESH);
+ options->get_popup()->add_item(TTR("Create Outline Mesh..."), MENU_OPTION_CREATE_OUTLINE_MESH);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("View UV1"), MENU_OPTION_DEBUG_UV1);
options->get_popup()->add_item(TTR("View UV2"), MENU_OPTION_DEBUG_UV2);
diff --git a/editor/plugins/navigation_mesh_generator.cpp b/editor/plugins/navigation_mesh_generator.cpp
index 5d4e5520f..0537c5c31 100644
--- a/editor/plugins/navigation_mesh_generator.cpp
+++ b/editor/plugins/navigation_mesh_generator.cpp
@@ -280,26 +280,20 @@ void NavigationMeshGenerator::bake(Ref<NavigationMesh> p_nav_mesh, Node *p_node)
_build_recast_navigation_mesh(p_nav_mesh, &ep, hf, chf, cset, poly_mesh, detail_mesh, vertices, indices);
- if (hf) {
- rcFreeHeightField(hf);
- hf = 0;
- }
- if (chf) {
- rcFreeCompactHeightfield(chf);
- chf = 0;
- }
- if (cset) {
- rcFreeContourSet(cset);
- cset = 0;
- }
- if (poly_mesh) {
- rcFreePolyMesh(poly_mesh);
- poly_mesh = 0;
- }
- if (detail_mesh) {
- rcFreePolyMeshDetail(detail_mesh);
- detail_mesh = 0;
- }
+ rcFreeHeightField(hf);
+ hf = 0;
+
+ rcFreeCompactHeightfield(chf);
+ chf = 0;
+
+ rcFreeContourSet(cset);
+ cset = 0;
+
+ rcFreePolyMesh(poly_mesh);
+ poly_mesh = 0;
+
+ rcFreePolyMeshDetail(detail_mesh);
+ detail_mesh = 0;
}
ep.step(TTR("Done!"), 11);
}
diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp
index abee9a989..6d1107975 100644
--- a/editor/plugins/particles_2d_editor_plugin.cpp
+++ b/editor/plugins/particles_2d_editor_plugin.cpp
@@ -94,7 +94,7 @@ void Particles2DEditorPlugin::_generate_visibility_rect() {
while (running < time) {
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
- ep.step("Generating..", int(running), true);
+ ep.step("Generating...", int(running), true);
OS::get_singleton()->delay_usec(1000);
Rect2 capture = particles->capture_rect();
@@ -229,7 +229,7 @@ void Particles2DEditorPlugin::_generate_emission_mask() {
valid_normals.resize(vpc);
}
- ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image.."));
+ ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image..."));
ERR_FAIL_COND(valid_positions.size() == 0);
PoolVector<uint8_t> texdata;
diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp
index d129b6c5c..7728995a9 100644
--- a/editor/plugins/particles_editor_plugin.cpp
+++ b/editor/plugins/particles_editor_plugin.cpp
@@ -129,7 +129,7 @@ void ParticlesEditor::_generate_aabb() {
while (running < time) {
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
- ep.step("Generating..", int(running), true);
+ ep.step("Generating...", int(running), true);
OS::get_singleton()->delay_usec(1000);
AABB capture = node->capture_aabb();
diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp
index 056219a57..6dde639c5 100644
--- a/editor/plugins/path_editor_plugin.cpp
+++ b/editor/plugins/path_editor_plugin.cpp
@@ -167,18 +167,12 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p
Vector3 ofs;
- if (p_cancel) {
-
- return;
- }
-
if (t == 0) {
-
if (p_cancel) {
-
c->set_point_in(p_idx, p_restore);
return;
}
+
ur->create_action(TTR("Set Curve In Position"));
ur->add_do_method(c.ptr(), "set_point_in", idx, c->get_point_in(idx));
ur->add_undo_method(c.ptr(), "set_point_in", idx, p_restore);
@@ -186,10 +180,11 @@ void PathSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p
} else {
if (p_cancel) {
-
c->set_point_out(idx, p_restore);
+
return;
}
+
ur->create_action(TTR("Set Curve Out Position"));
ur->add_do_method(c.ptr(), "set_point_out", idx, c->get_point_out(idx));
ur->add_undo_method(c.ptr(), "set_point_out", idx, p_restore);
diff --git a/editor/plugins/physical_bone_plugin.cpp b/editor/plugins/physical_bone_plugin.cpp
new file mode 100644
index 000000000..42f1adcad
--- /dev/null
+++ b/editor/plugins/physical_bone_plugin.cpp
@@ -0,0 +1,123 @@
+/*************************************************************************/
+/* physical_bone_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 "physical_bone_plugin.h"
+#include "editor/plugins/spatial_editor_plugin.h"
+#include "scene/3d/physics_body.h"
+
+void PhysicalBoneEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_on_toggle_button_transform_joint", "is_pressed"), &PhysicalBoneEditor::_on_toggle_button_transform_joint);
+}
+
+void PhysicalBoneEditor::_on_toggle_button_transform_joint(bool p_is_pressed) {
+
+ _set_move_joint();
+}
+
+void PhysicalBoneEditor::_set_move_joint() {
+ if (selected) {
+ selected->_set_gizmo_move_joint(button_transform_joint->is_pressed());
+ }
+}
+
+PhysicalBoneEditor::PhysicalBoneEditor(EditorNode *p_editor) :
+ editor(p_editor),
+ selected(NULL) {
+
+ spatial_editor_hb = memnew(HBoxContainer);
+ spatial_editor_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ spatial_editor_hb->set_alignment(BoxContainer::ALIGN_BEGIN);
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb);
+
+ spatial_editor_hb->add_child(memnew(VSeparator));
+
+ button_transform_joint = memnew(ToolButton);
+ spatial_editor_hb->add_child(button_transform_joint);
+
+ button_transform_joint->set_text(TTR("Move joint"));
+ button_transform_joint->set_icon(SpatialEditor::get_singleton()->get_icon("PhysicalBone", "EditorIcons"));
+ button_transform_joint->set_toggle_mode(true);
+ button_transform_joint->connect("toggled", this, "_on_toggle_button_transform_joint");
+
+ hide();
+}
+
+PhysicalBoneEditor::~PhysicalBoneEditor() {
+ // TODO the spatial_editor_hb should be removed from SpatialEditor, but in this moment it's not possible
+ for (int i = spatial_editor_hb->get_child_count() - 1; 0 <= i; --i) {
+ Node *n = spatial_editor_hb->get_child(i);
+ spatial_editor_hb->remove_child(n);
+ memdelete(n);
+ }
+ memdelete(spatial_editor_hb);
+}
+
+void PhysicalBoneEditor::set_selected(PhysicalBone *p_pb) {
+
+ button_transform_joint->set_pressed(false);
+
+ _set_move_joint();
+ selected = p_pb;
+ _set_move_joint();
+}
+
+void PhysicalBoneEditor::hide() {
+ spatial_editor_hb->hide();
+}
+
+void PhysicalBoneEditor::show() {
+ spatial_editor_hb->show();
+}
+
+PhysicalBonePlugin::PhysicalBonePlugin(EditorNode *p_editor) :
+ editor(p_editor),
+ selected(NULL) {
+
+ physical_bone_editor = memnew(PhysicalBoneEditor(editor));
+}
+
+void PhysicalBonePlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+
+ physical_bone_editor->show();
+ } else {
+
+ physical_bone_editor->hide();
+ physical_bone_editor->set_selected(NULL);
+ selected = NULL;
+ }
+}
+
+void PhysicalBonePlugin::edit(Object *p_node) {
+ selected = static_cast<PhysicalBone *>(p_node); // Trust it
+ ERR_FAIL_COND(!selected);
+
+ physical_bone_editor->set_selected(selected);
+}
diff --git a/editor/plugins/physical_bone_plugin.h b/editor/plugins/physical_bone_plugin.h
new file mode 100644
index 000000000..9e7a50307
--- /dev/null
+++ b/editor/plugins/physical_bone_plugin.h
@@ -0,0 +1,78 @@
+/*************************************************************************/
+/* physical_bone_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 PHYSICAL_BONE_PLUGIN_H
+#define PHYSICAL_BONE_PLUGIN_H
+
+#include "editor/editor_node.h"
+
+class PhysicalBoneEditor : public Object {
+ GDCLASS(PhysicalBoneEditor, Object);
+
+ EditorNode *editor;
+ HBoxContainer *spatial_editor_hb;
+ ToolButton *button_transform_joint;
+
+ PhysicalBone *selected;
+
+protected:
+ static void _bind_methods();
+
+private:
+ void _on_toggle_button_transform_joint(bool p_is_pressed);
+ void _set_move_joint();
+
+public:
+ PhysicalBoneEditor(EditorNode *p_editor);
+ ~PhysicalBoneEditor();
+
+ void set_selected(PhysicalBone *p_pb);
+
+ void hide();
+ void show();
+};
+
+class PhysicalBonePlugin : public EditorPlugin {
+ GDCLASS(PhysicalBonePlugin, EditorPlugin);
+
+ EditorNode *editor;
+ PhysicalBone *selected;
+ PhysicalBoneEditor *physical_bone_editor;
+
+public:
+ virtual String get_name() const { return "PhysicalBone"; }
+ virtual bool handles(Object *p_object) const { return p_object->is_class("PhysicalBone"); }
+ virtual void make_visible(bool p_visible);
+ virtual void edit(Object *p_node);
+
+ PhysicalBonePlugin(EditorNode *p_editor);
+};
+
+#endif
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index 3a169bd78..f04e0a801 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -35,7 +35,7 @@
#include "os/file_access.h"
#include "os/input.h"
#include "os/keyboard.h"
-
+#include "scene/2d/skeleton_2d.h"
Node2D *Polygon2DEditor::_get_node() const {
return node;
@@ -66,6 +66,8 @@ void Polygon2DEditor::_notification(int p_what) {
uv_button[UV_MODE_SCALE]->set_icon(get_icon("ToolScale", "EditorIcons"));
uv_button[UV_MODE_ADD_SPLIT]->set_icon(get_icon("AddSplit", "EditorIcons"));
uv_button[UV_MODE_REMOVE_SPLIT]->set_icon(get_icon("DeleteSplit", "EditorIcons"));
+ uv_button[UV_MODE_PAINT_WEIGHT]->set_icon(get_icon("PaintVertex", "EditorIcons"));
+ uv_button[UV_MODE_CLEAR_WEIGHT]->set_icon(get_icon("UnpaintVertex", "EditorIcons"));
b_snap_grid->set_icon(get_icon("Grid", "EditorIcons"));
b_snap_enable->set_icon(get_icon("SnapGrid", "EditorIcons"));
@@ -78,31 +80,167 @@ void Polygon2DEditor::_notification(int p_what) {
}
}
+void Polygon2DEditor::_sync_bones() {
+
+ print_line("syncinc");
+ if (!node->has_node(node->get_skeleton())) {
+ error->set_text(TTR("The skeleton property of the Polygon2D does not point to a Skeleton2D node"));
+ error->popup_centered_minsize();
+ return;
+ }
+
+ Node *sn = node->get_node(node->get_skeleton());
+ Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(sn);
+
+ if (!skeleton) {
+ error->set_text(TTR("The skeleton property of the Polygon2D does not point to a Skeleton2D node"));
+ error->popup_centered_minsize();
+ return;
+ }
+
+ Array prev_bones = node->call("_get_bones");
+ node->clear_bones();
+
+ print_line("bones in skeleton: " + itos(skeleton->get_bone_count()));
+
+ for (int i = 0; i < skeleton->get_bone_count(); i++) {
+ NodePath path = skeleton->get_path_to(skeleton->get_bone(i));
+ PoolVector<float> weights;
+ int wc = node->get_polygon().size();
+
+ for (int j = 0; j < prev_bones.size(); j += 2) {
+ NodePath pvp = prev_bones[j];
+ PoolVector<float> pv = prev_bones[j + 1];
+ if (pvp == path && pv.size() == wc) {
+ weights = pv;
+ }
+ }
+
+ if (weights.size() == 0) { //create them
+ weights.resize(node->get_polygon().size());
+ PoolVector<float>::Write w = weights.write();
+ for (int j = 0; j < wc; j++) {
+ w[j] = 0.0;
+ }
+ }
+
+ node->add_bone(path, weights);
+ }
+ Array new_bones = node->call("_get_bones");
+
+ undo_redo->create_action(TTR("Sync bones"));
+ undo_redo->add_do_method(node, "_set_bones", new_bones);
+ undo_redo->add_undo_method(node, "_set_bones", prev_bones);
+ undo_redo->add_do_method(uv_edit_draw, "update");
+ undo_redo->add_undo_method(uv_edit_draw, "update");
+ undo_redo->add_do_method(this, "_update_bone_list");
+ undo_redo->add_undo_method(this, "_update_bone_list");
+ undo_redo->commit_action();
+}
+
+void Polygon2DEditor::_update_bone_list() {
+
+ NodePath selected;
+ while (bone_scroll_vb->get_child_count()) {
+ CheckBox *cb = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(0));
+ if (cb && cb->is_pressed()) {
+ selected = cb->get_meta("bone_path");
+ }
+ memdelete(bone_scroll_vb->get_child(0));
+ }
+
+ Ref<ButtonGroup> bg;
+ bg.instance();
+ for (int i = 0; i < node->get_bone_count(); i++) {
+ CheckBox *cb = memnew(CheckBox);
+ NodePath np = node->get_bone_path(i);
+ String name;
+ if (np.get_name_count()) {
+ name = np.get_name(np.get_name_count() - 1);
+ }
+ if (name == String()) {
+ name = "Bone " + itos(i);
+ }
+ cb->set_text(name);
+ cb->set_button_group(bg);
+ cb->set_meta("bone_path", np);
+ bone_scroll_vb->add_child(cb);
+
+ if (np == selected)
+ cb->set_pressed(true);
+
+ cb->connect("pressed", this, "_bone_paint_selected", varray(i));
+ }
+
+ uv_edit_draw->update();
+}
+
+void Polygon2DEditor::_bone_paint_selected(int p_index) {
+ uv_edit_draw->update();
+}
+
void Polygon2DEditor::_uv_edit_mode_select(int p_mode) {
- if (p_mode == 0) {
+ if (p_mode == 0) { //uv
uv_button[UV_MODE_CREATE]->hide();
for (int i = UV_MODE_MOVE; i <= UV_MODE_SCALE; i++) {
uv_button[i]->show();
}
uv_button[UV_MODE_ADD_SPLIT]->hide();
uv_button[UV_MODE_REMOVE_SPLIT]->hide();
+ uv_button[UV_MODE_PAINT_WEIGHT]->hide();
+ uv_button[UV_MODE_CLEAR_WEIGHT]->hide();
_uv_mode(UV_MODE_EDIT_POINT);
- } else if (p_mode == 1) {
+ bone_scroll_main_vb->hide();
+ bone_paint_strength->hide();
+ bone_paint_radius->hide();
+ bone_paint_radius_label->hide();
+
+ } else if (p_mode == 1) { //poly
for (int i = 0; i <= UV_MODE_SCALE; i++) {
uv_button[i]->show();
}
uv_button[UV_MODE_ADD_SPLIT]->hide();
uv_button[UV_MODE_REMOVE_SPLIT]->hide();
+ uv_button[UV_MODE_PAINT_WEIGHT]->hide();
+ uv_button[UV_MODE_CLEAR_WEIGHT]->hide();
_uv_mode(UV_MODE_EDIT_POINT);
- } else {
+
+ bone_scroll_main_vb->hide();
+ bone_paint_strength->hide();
+ bone_paint_radius->hide();
+ bone_paint_radius_label->hide();
+
+ } else if (p_mode == 2) { //splits
for (int i = 0; i <= UV_MODE_SCALE; i++) {
uv_button[i]->hide();
}
uv_button[UV_MODE_ADD_SPLIT]->show();
uv_button[UV_MODE_REMOVE_SPLIT]->show();
+ uv_button[UV_MODE_PAINT_WEIGHT]->hide();
+ uv_button[UV_MODE_CLEAR_WEIGHT]->hide();
_uv_mode(UV_MODE_ADD_SPLIT);
+
+ bone_scroll_main_vb->hide();
+ bone_paint_strength->hide();
+ bone_paint_radius->hide();
+ bone_paint_radius_label->hide();
+
+ } else if (p_mode == 3) { //bones´
+ for (int i = 0; i <= UV_MODE_REMOVE_SPLIT; i++) {
+ uv_button[i]->hide();
+ }
+ uv_button[UV_MODE_PAINT_WEIGHT]->show();
+ uv_button[UV_MODE_CLEAR_WEIGHT]->show();
+ _uv_mode(UV_MODE_PAINT_WEIGHT);
+
+ bone_scroll_main_vb->show();
+ bone_paint_strength->show();
+ bone_paint_radius->show();
+ bone_paint_radius_label->show();
+ _update_bone_list();
+ bone_paint_pos = Vector2(-100000, -100000); //send brush away when switching
}
uv_edit_draw->update();
@@ -176,6 +314,9 @@ void Polygon2DEditor::_menu_option(int p_option) {
undo_redo->commit_action();
} break;
+ case UVEDIT_GRID_SETTINGS: {
+ grid_settings->popup_centered_minsize();
+ } break;
default: {
AbstractPolygon2DEditor::_menu_option(p_option);
} break;
@@ -239,41 +380,44 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_drag_from = Vector2(mb->get_position().x, mb->get_position().y);
uv_drag = true;
- uv_prev = node->get_uv();
+ points_prev = node->get_uv();
if (uv_edit_mode[0]->is_pressed()) { //edit uv
- uv_prev = node->get_uv();
+ points_prev = node->get_uv();
} else { //edit polygon
- uv_prev = node->get_polygon();
+ points_prev = node->get_polygon();
}
uv_move_current = uv_mode;
if (uv_move_current == UV_MODE_CREATE) {
if (!uv_create) {
- uv_prev.resize(0);
+ points_prev.resize(0);
Vector2 tuv = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
- uv_prev.push_back(tuv);
+ points_prev.push_back(tuv);
uv_create_to = tuv;
- uv_drag_index = 0;
+ point_drag_index = 0;
uv_drag_from = tuv;
uv_drag = true;
uv_create = true;
uv_create_uv_prev = node->get_uv();
uv_create_poly_prev = node->get_polygon();
+ uv_create_bones_prev = node->call("_get_bones");
splits_prev = node->get_splits();
- node->set_polygon(uv_prev);
- node->set_uv(uv_prev);
+ node->set_polygon(points_prev);
+ node->set_uv(points_prev);
} else {
Vector2 tuv = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
- if (uv_prev.size() > 3 && tuv.distance_to(uv_prev[0]) < 8) {
+ if (points_prev.size() > 3 && tuv.distance_to(points_prev[0]) < 8) {
undo_redo->create_action(TTR("Create Polygon & UV"));
undo_redo->add_do_method(node, "set_uv", node->get_uv());
- undo_redo->add_undo_method(node, "set_uv", uv_prev);
+ undo_redo->add_undo_method(node, "set_uv", points_prev);
undo_redo->add_do_method(node, "set_polygon", node->get_polygon());
- undo_redo->add_undo_method(node, "set_polygon", uv_prev);
+ undo_redo->add_undo_method(node, "set_polygon", points_prev);
+ undo_redo->add_do_method(node, "clear_bones");
+ undo_redo->add_undo_method(node, "_set_bones", node->call("_get_bones"));
undo_redo->add_do_method(uv_edit_draw, "update");
undo_redo->add_undo_method(uv_edit_draw, "update");
undo_redo->commit_action();
@@ -281,12 +425,12 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_create = false;
_uv_mode(UV_MODE_EDIT_POINT);
} else {
- uv_prev.push_back(tuv);
- uv_drag_index = uv_prev.size() - 1;
+ points_prev.push_back(tuv);
+ point_drag_index = points_prev.size() - 1;
uv_drag_from = tuv;
}
- node->set_polygon(uv_prev);
- node->set_uv(uv_prev);
+ node->set_polygon(points_prev);
+ node->set_uv(points_prev);
}
}
@@ -302,34 +446,34 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (uv_move_current == UV_MODE_EDIT_POINT) {
- uv_drag_index = -1;
- for (int i = 0; i < uv_prev.size(); i++) {
+ point_drag_index = -1;
+ for (int i = 0; i < points_prev.size(); i++) {
- Vector2 tuv = mtx.xform(uv_prev[i]);
+ Vector2 tuv = mtx.xform(points_prev[i]);
if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) {
uv_drag_from = tuv;
- uv_drag_index = i;
+ point_drag_index = i;
}
}
- if (uv_drag_index == -1) {
+ if (point_drag_index == -1) {
uv_drag = false;
}
}
if (uv_move_current == UV_MODE_ADD_SPLIT) {
- int drag_index = -1;
- drag_index = -1;
- for (int i = 0; i < uv_prev.size(); i++) {
+ int split_to_index = -1;
+ split_to_index = -1;
+ for (int i = 0; i < points_prev.size(); i++) {
- Vector2 tuv = mtx.xform(uv_prev[i]);
+ Vector2 tuv = mtx.xform(points_prev[i]);
if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) {
- drag_index = i;
+ split_to_index = i;
}
}
- if (drag_index == -1) {
+ if (split_to_index == -1) {
split_create = false;
return;
}
@@ -337,41 +481,65 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (split_create) {
split_create = false;
- if (drag_index < uv_drag_index) {
- SWAP(drag_index, uv_drag_index);
+ if (split_to_index < point_drag_index) {
+ SWAP(split_to_index, point_drag_index);
}
bool valid = true;
- if (drag_index == uv_drag_index) {
+ String split_error;
+ if (split_to_index == point_drag_index) {
+ split_error = TTR("Split point with itself.");
valid = false;
}
- if (drag_index + 1 == uv_drag_index) {
+ if (split_to_index + 1 == point_drag_index) {
//not a split,goes along the edge
+ split_error = TTR("Split can't form an existing edge.");
valid = false;
}
- if (drag_index == uv_prev.size() - 1 && uv_drag_index == 0) {
+ if (split_to_index == points_prev.size() - 1 && point_drag_index == 0) {
//not a split,goes along the edge
+ split_error = TTR("Split can't form an existing edge.");
valid = false;
}
+
for (int i = 0; i < splits_prev.size(); i += 2) {
- if (splits_prev[i] == uv_drag_index && splits_prev[i + 1] == drag_index) {
+
+ if (splits_prev[i] == point_drag_index && splits_prev[i + 1] == split_to_index) {
//already exists
+ split_error = TTR("Split already exists.");
valid = false;
+ break;
}
- if (splits_prev[i] > uv_drag_index && splits_prev[i + 1] > drag_index) {
- //crossing
- valid = false;
+
+ int a_state; //-1, outside split, 0 split point, +1, inside split
+ if (point_drag_index == splits_prev[i] || point_drag_index == splits_prev[i + 1]) {
+ a_state = 0;
+ } else if (point_drag_index < splits_prev[i] || point_drag_index > splits_prev[i + 1]) {
+ a_state = -1;
+ } else {
+ a_state = 1;
}
- if (splits_prev[i] < uv_drag_index && splits_prev[i + 1] < drag_index) {
- //crossing opposite direction
+ int b_state; //-1, outside split, 0 split point, +1, inside split
+ if (split_to_index == splits_prev[i] || split_to_index == splits_prev[i + 1]) {
+ b_state = 0;
+ } else if (split_to_index < splits_prev[i] || split_to_index > splits_prev[i + 1]) {
+ b_state = -1;
+ } else {
+ b_state = 1;
+ }
+
+ if (b_state * a_state < 0) {
+ //crossing
+ split_error = "Split crosses another split.";
valid = false;
+ break;
}
}
if (valid) {
- splits_prev.push_back(uv_drag_index);
- splits_prev.push_back(drag_index);
+ splits_prev.push_back(point_drag_index);
+ splits_prev.push_back(split_to_index);
undo_redo->create_action(TTR("Add Split"));
@@ -381,13 +549,14 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
undo_redo->add_undo_method(uv_edit_draw, "update");
undo_redo->commit_action();
} else {
- error->set_text(TTR("Invalid Split"));
+ error->set_text(TTR("Invalid Split: ") + split_error);
error->popup_centered_minsize();
}
} else {
- uv_drag_index = drag_index;
+ point_drag_index = split_to_index;
split_create = true;
+ splits_prev = node->get_splits();
uv_create_to = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
}
}
@@ -395,11 +564,11 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (uv_move_current == UV_MODE_REMOVE_SPLIT) {
for (int i = 0; i < splits_prev.size(); i += 2) {
- if (splits_prev[i] < 0 || splits_prev[i] >= uv_prev.size())
+ if (splits_prev[i] < 0 || splits_prev[i] >= points_prev.size())
continue;
- if (splits_prev[i + 1] < 0 || splits_prev[i] >= uv_prev.size())
+ if (splits_prev[i + 1] < 0 || splits_prev[i] >= points_prev.size())
continue;
- Vector2 e[2] = { mtx.xform(uv_prev[splits_prev[i]]), mtx.xform(uv_prev[splits_prev[i + 1]]) };
+ Vector2 e[2] = { mtx.xform(points_prev[splits_prev[i]]), mtx.xform(points_prev[splits_prev[i + 1]]) };
Vector2 mp = Vector2(mb->get_position().x, mb->get_position().y);
Vector2 cp = Geometry::get_closest_point_to_segment_2d(mp, e);
if (cp.distance_to(mp) < 8) {
@@ -419,22 +588,50 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
}
}
+ if (uv_move_current == UV_MODE_PAINT_WEIGHT || uv_move_current == UV_MODE_CLEAR_WEIGHT) {
+
+ int bone_selected = -1;
+ for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) {
+ CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i));
+ if (c && c->is_pressed()) {
+ bone_selected = i;
+ break;
+ }
+ }
+
+ if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == points_prev.size()) {
+
+ prev_weights = node->get_bone_weights(bone_selected);
+ bone_painting = true;
+ bone_painting_bone = bone_selected;
+ }
+ }
+
} else if (uv_drag && !uv_create) {
undo_redo->create_action(TTR("Transform UV Map"));
if (uv_edit_mode[0]->is_pressed()) { //edit uv
undo_redo->add_do_method(node, "set_uv", node->get_uv());
- undo_redo->add_undo_method(node, "set_uv", uv_prev);
+ undo_redo->add_undo_method(node, "set_uv", points_prev);
} else if (uv_edit_mode[1]->is_pressed()) { //edit polygon
undo_redo->add_do_method(node, "set_polygon", node->get_polygon());
- undo_redo->add_undo_method(node, "set_polygon", uv_prev);
+ undo_redo->add_undo_method(node, "set_polygon", points_prev);
}
undo_redo->add_do_method(uv_edit_draw, "update");
undo_redo->add_undo_method(uv_edit_draw, "update");
undo_redo->commit_action();
uv_drag = false;
+ } else if (bone_painting) {
+
+ undo_redo->create_action(TTR("Paint bone weights"));
+ undo_redo->add_do_method(node, "set_bone_weights", bone_painting_bone, node->get_bone_weights(bone_painting_bone));
+ undo_redo->add_undo_method(node, "set_bone_weights", bone_painting_bone, prev_weights);
+ undo_redo->add_do_method(uv_edit_draw, "update");
+ undo_redo->add_undo_method(uv_edit_draw, "update");
+ undo_redo->commit_action();
+ bone_painting = false;
}
} else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed()) {
@@ -445,20 +642,23 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_create = false;
node->set_uv(uv_create_uv_prev);
node->set_polygon(uv_create_poly_prev);
+ node->call("_set_bones", uv_create_bones_prev);
node->set_splits(splits_prev);
uv_edit_draw->update();
} else if (uv_drag) {
uv_drag = false;
if (uv_edit_mode[0]->is_pressed()) { //edit uv
- node->set_uv(uv_prev);
+ node->set_uv(points_prev);
} else if (uv_edit_mode[1]->is_pressed()) { //edit polygon
- node->set_polygon(uv_prev);
+ node->set_polygon(points_prev);
}
uv_edit_draw->update();
} else if (split_create) {
split_create = false;
uv_edit_draw->update();
+ } else if (bone_painting) {
+ node->set_bone_weights(bone_painting_bone, prev_weights);
}
} else if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) {
@@ -494,8 +694,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
} break;
case UV_MODE_EDIT_POINT: {
- PoolVector<Vector2> uv_new = uv_prev;
- uv_new.set(uv_drag_index, uv_new[uv_drag_index] + drag);
+ PoolVector<Vector2> uv_new = points_prev;
+ uv_new.set(point_drag_index, uv_new[point_drag_index] + drag);
if (uv_edit_mode[0]->is_pressed()) { //edit uv
node->set_uv(uv_new);
@@ -505,7 +705,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
} break;
case UV_MODE_MOVE: {
- PoolVector<Vector2> uv_new = uv_prev;
+ PoolVector<Vector2> uv_new = points_prev;
for (int i = 0; i < uv_new.size(); i++)
uv_new.set(i, uv_new[i] + drag);
@@ -519,16 +719,16 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
case UV_MODE_ROTATE: {
Vector2 center;
- PoolVector<Vector2> uv_new = uv_prev;
+ PoolVector<Vector2> uv_new = points_prev;
for (int i = 0; i < uv_new.size(); i++)
- center += uv_prev[i];
+ center += points_prev[i];
center /= uv_new.size();
float angle = (uv_drag_from - mtx.xform(center)).normalized().angle_to((uv_drag_to - mtx.xform(center)).normalized());
for (int i = 0; i < uv_new.size(); i++) {
- Vector2 rel = uv_prev[i] - center;
+ Vector2 rel = points_prev[i] - center;
rel = rel.rotated(angle);
uv_new.set(i, center + rel);
}
@@ -543,10 +743,10 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
case UV_MODE_SCALE: {
Vector2 center;
- PoolVector<Vector2> uv_new = uv_prev;
+ PoolVector<Vector2> uv_new = points_prev;
for (int i = 0; i < uv_new.size(); i++)
- center += uv_prev[i];
+ center += points_prev[i];
center /= uv_new.size();
float from_dist = uv_drag_from.distance_to(mtx.xform(center));
@@ -557,7 +757,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
float scale = to_dist / from_dist;
for (int i = 0; i < uv_new.size(); i++) {
- Vector2 rel = uv_prev[i] - center;
+ Vector2 rel = points_prev[i] - center;
rel = rel * scale;
uv_new.set(i, center + rel);
}
@@ -569,10 +769,40 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
}
} break;
}
+
+ if (bone_painting) {
+ bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y);
+ PoolVector<float> painted_weights = node->get_bone_weights(bone_painting_bone);
+
+ {
+ int pc = painted_weights.size();
+ float amount = bone_paint_strength->get_value();
+ float radius = bone_paint_radius->get_value() * EDSCALE;
+
+ if (uv_mode == UV_MODE_CLEAR_WEIGHT) {
+ amount = -amount;
+ }
+
+ PoolVector<float>::Write w = painted_weights.write();
+ PoolVector<float>::Read r = prev_weights.read();
+ PoolVector<Vector2>::Read rv = points_prev.read();
+
+ for (int i = 0; i < pc; i++) {
+ if (mtx.xform(rv[i]).distance_to(bone_paint_pos) < radius) {
+ w[i] = CLAMP(r[i] + amount, 0, 1);
+ }
+ }
+ }
+
+ node->set_bone_weights(bone_painting_bone, painted_weights);
+ }
uv_edit_draw->update();
} else if (split_create) {
uv_create_to = mtx.affine_inverse().xform(Vector2(mm->get_position().x, mm->get_position().y));
uv_edit_draw->update();
+ } else if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) {
+ bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y);
+ uv_edit_draw->update();
}
}
@@ -607,6 +837,8 @@ void Polygon2DEditor::_uv_draw() {
if (base_tex.is_null())
return;
+ String warning;
+
Transform2D mtx;
mtx.elements[2] = -uv_draw_ofs;
mtx.scale_basis(Vector2(uv_draw_zoom, uv_draw_zoom));
@@ -649,6 +881,24 @@ void Polygon2DEditor::_uv_draw() {
uvs = node->get_polygon();
}
+ PoolVector<float>::Read weight_r;
+
+ if (uv_edit_mode[3]->is_pressed()) {
+ int bone_selected = -1;
+ for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) {
+ CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i));
+ if (c && c->is_pressed()) {
+ bone_selected = i;
+ break;
+ }
+ }
+
+ if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == uvs.size()) {
+
+ weight_r = node->get_bone_weights(bone_selected).read();
+ }
+ }
+
Ref<Texture> handle = get_icon("EditorHandle", "EditorIcons");
Rect2 rect(Point2(), mtx.basis_xform(base_tex->get_size()));
@@ -662,12 +912,19 @@ void Polygon2DEditor::_uv_draw() {
next_point = uv_create_to;
}
uv_edit_draw->draw_line(mtx.xform(uvs[i]), mtx.xform(next_point), Color(0.9, 0.5, 0.5), 2);
- uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5);
+ if (weight_r.ptr()) {
+
+ Vector2 draw_pos = mtx.xform(uvs[i]);
+ float weight = weight_r[i];
+ uv_edit_draw->draw_rect(Rect2(draw_pos - Vector2(2, 2) * EDSCALE, Vector2(5, 5) * EDSCALE), Color(weight, weight, weight, 1.0));
+ } else {
+ uv_edit_draw->draw_texture(handle, mtx.xform(uvs[i]) - handle->get_size() * 0.5);
+ }
rect.expand_to(mtx.basis_xform(uvs[i]));
}
if (split_create) {
- Vector2 from = uvs[uv_drag_index];
+ Vector2 from = uvs[point_drag_index];
Vector2 to = uv_create_to;
uv_edit_draw->draw_line(mtx.xform(from), mtx.xform(to), Color(0.9, 0.5, 0.5), 2);
}
@@ -682,6 +939,55 @@ void Polygon2DEditor::_uv_draw() {
uv_edit_draw->draw_line(mtx.xform(uvs[idx_from]), mtx.xform(uvs[idx_to]), Color(0.9, 0.5, 0.5), 2);
}
+ if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) {
+
+ NodePath bone_path;
+ for (int i = 0; i < bone_scroll_vb->get_child_count(); i++) {
+ CheckBox *c = Object::cast_to<CheckBox>(bone_scroll_vb->get_child(i));
+ if (c && c->is_pressed()) {
+ bone_path = node->get_bone_path(i);
+ break;
+ }
+ }
+
+ //draw skeleton
+ NodePath skeleton_path = node->get_skeleton();
+ if (node->has_node(skeleton_path)) {
+ Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(node->get_node(skeleton_path));
+ if (skeleton) {
+ for (int i = 0; i < skeleton->get_bone_count(); i++) {
+
+ Bone2D *bone = skeleton->get_bone(i);
+ if (bone->get_rest() == Transform2D(0, 0, 0, 0, 0, 0))
+ continue; //not set
+
+ bool current = bone_path == skeleton->get_path_to(bone);
+
+ for (int j = 0; j < bone->get_child_count(); j++) {
+
+ Node2D *n = Object::cast_to<Node2D>(bone->get_child(j));
+ if (!n)
+ continue;
+
+ bool edit_bone = n->has_meta("_edit_bone_") && n->get_meta("_edit_bone_");
+ if (edit_bone) {
+
+ Transform2D bone_xform = node->get_global_transform().affine_inverse() * (skeleton->get_global_transform() * bone->get_skeleton_rest());
+ Transform2D endpoint_xform = bone_xform * n->get_transform();
+
+ Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5);
+ uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), current ? 5 : 4);
+ uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, current ? 3 : 2);
+ }
+ }
+ }
+ }
+ }
+
+ //draw paint circle
+ uv_edit_draw->draw_circle(bone_paint_pos, bone_paint_radius->get_value() * EDSCALE, Color(1, 1, 1, 0.1));
+ }
+
rect = rect.grow(200);
updating_uv_scroll = true;
uv_hscroll->set_min(rect.position.x);
@@ -711,6 +1017,10 @@ void Polygon2DEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_set_snap_step_x"), &Polygon2DEditor::_set_snap_step_x);
ClassDB::bind_method(D_METHOD("_set_snap_step_y"), &Polygon2DEditor::_set_snap_step_y);
ClassDB::bind_method(D_METHOD("_uv_edit_mode_select"), &Polygon2DEditor::_uv_edit_mode_select);
+ ClassDB::bind_method(D_METHOD("_sync_bones"), &Polygon2DEditor::_sync_bones);
+ ClassDB::bind_method(D_METHOD("_update_bone_list"), &Polygon2DEditor::_update_bone_list);
+
+ ClassDB::bind_method(D_METHOD("_bone_paint_selected"), &Polygon2DEditor::_bone_paint_selected);
}
Vector2 Polygon2DEditor::snap_point(Vector2 p_target) const {
@@ -755,19 +1065,25 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
uv_edit_mode[2] = memnew(ToolButton);
uv_mode_hb->add_child(uv_edit_mode[2]);
uv_edit_mode[2]->set_toggle_mode(true);
+ uv_edit_mode[3] = memnew(ToolButton);
+ uv_mode_hb->add_child(uv_edit_mode[3]);
+ uv_edit_mode[3]->set_toggle_mode(true);
uv_edit_mode[0]->set_text(TTR("UV"));
uv_edit_mode[0]->set_pressed(true);
uv_edit_mode[1]->set_text(TTR("Poly"));
uv_edit_mode[2]->set_text(TTR("Splits"));
+ uv_edit_mode[3]->set_text(TTR("Bones"));
uv_edit_mode[0]->set_button_group(uv_edit_group);
uv_edit_mode[1]->set_button_group(uv_edit_group);
uv_edit_mode[2]->set_button_group(uv_edit_group);
+ uv_edit_mode[3]->set_button_group(uv_edit_group);
uv_edit_mode[0]->connect("pressed", this, "_uv_edit_mode_select", varray(0));
uv_edit_mode[1]->connect("pressed", this, "_uv_edit_mode_select", varray(1));
uv_edit_mode[2]->connect("pressed", this, "_uv_edit_mode_select", varray(2));
+ uv_edit_mode[3]->connect("pressed", this, "_uv_edit_mode_select", varray(3));
uv_mode_hb->add_child(memnew(VSeparator));
@@ -788,11 +1104,38 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
uv_button[4]->set_tooltip(TTR("Scale Polygon"));
uv_button[5]->set_tooltip(TTR("Connect two points to make a split"));
uv_button[6]->set_tooltip(TTR("Select a split to erase it"));
+ uv_button[7]->set_tooltip(TTR("Paint weights with specified intensity"));
+ uv_button[8]->set_tooltip(TTR("UnPaint weights with specified intensity"));
uv_button[0]->hide();
uv_button[5]->hide();
uv_button[6]->hide();
+ uv_button[7]->hide();
+ uv_button[8]->hide();
uv_button[1]->set_pressed(true);
+
+ bone_paint_strength = memnew(HSlider);
+ uv_mode_hb->add_child(bone_paint_strength);
+ bone_paint_strength->set_custom_minimum_size(Size2(75 * EDSCALE, 0));
+ bone_paint_strength->set_v_size_flags(SIZE_SHRINK_CENTER);
+ bone_paint_strength->set_min(0);
+ bone_paint_strength->set_max(1);
+ bone_paint_strength->set_step(0.01);
+ bone_paint_strength->set_value(0.5);
+
+ bone_paint_radius_label = memnew(Label(" " + TTR("Radius:") + " "));
+ uv_mode_hb->add_child(bone_paint_radius_label);
+ bone_paint_radius = memnew(SpinBox);
+ uv_mode_hb->add_child(bone_paint_radius);
+
+ bone_paint_strength->hide();
+ bone_paint_radius->hide();
+ bone_paint_radius_label->hide();
+ bone_paint_radius->set_min(1);
+ bone_paint_radius->set_max(100);
+ bone_paint_radius->set_step(1);
+ bone_paint_radius->set_value(32);
+
HBoxContainer *uv_main_hb = memnew(HBoxContainer);
uv_main_vb->add_child(uv_main_hb);
uv_edit_draw = memnew(Control);
@@ -806,6 +1149,8 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
uv_menu->get_popup()->add_item(TTR("UV->Polygon"), UVEDIT_UV_TO_POLYGON);
uv_menu->get_popup()->add_separator();
uv_menu->get_popup()->add_item(TTR("Clear UV"), UVEDIT_UV_CLEAR);
+ uv_menu->get_popup()->add_separator();
+ uv_menu->get_popup()->add_item(TTR("Grid Settings"), UVEDIT_GRID_SETTINGS);
uv_menu->get_popup()->connect("id_pressed", this, "_menu_option");
uv_mode_hb->add_child(memnew(VSeparator));
@@ -828,8 +1173,11 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
b_snap_grid->set_tooltip(TTR("Show Grid"));
b_snap_grid->connect("toggled", this, "_set_show_grid");
- uv_mode_hb->add_child(memnew(VSeparator));
- uv_mode_hb->add_child(memnew(Label(TTR("Grid Offset:"))));
+ grid_settings = memnew(AcceptDialog);
+ grid_settings->set_title(TTR("Configure Grid:"));
+ add_child(grid_settings);
+ VBoxContainer *grid_settings_vb = memnew(VBoxContainer);
+ grid_settings->add_child(grid_settings_vb);
SpinBox *sb_off_x = memnew(SpinBox);
sb_off_x->set_min(-256);
@@ -838,7 +1186,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
sb_off_x->set_value(snap_offset.x);
sb_off_x->set_suffix("px");
sb_off_x->connect("value_changed", this, "_set_snap_off_x");
- uv_mode_hb->add_child(sb_off_x);
+ grid_settings_vb->add_margin_child(TTR("Grid Offset X:"), sb_off_x);
SpinBox *sb_off_y = memnew(SpinBox);
sb_off_y->set_min(-256);
@@ -847,10 +1195,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
sb_off_y->set_value(snap_offset.y);
sb_off_y->set_suffix("px");
sb_off_y->connect("value_changed", this, "_set_snap_off_y");
- uv_mode_hb->add_child(sb_off_y);
-
- uv_mode_hb->add_child(memnew(VSeparator));
- uv_mode_hb->add_child(memnew(Label(TTR("Grid Step:"))));
+ grid_settings_vb->add_margin_child(TTR("Grid Offset Y:"), sb_off_y);
SpinBox *sb_step_x = memnew(SpinBox);
sb_step_x->set_min(-256);
@@ -859,7 +1204,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
sb_step_x->set_value(snap_step.x);
sb_step_x->set_suffix("px");
sb_step_x->connect("value_changed", this, "_set_snap_step_x");
- uv_mode_hb->add_child(sb_step_x);
+ grid_settings_vb->add_margin_child(TTR("Grid Step X:"), sb_step_x);
SpinBox *sb_step_y = memnew(SpinBox);
sb_step_y->set_min(-256);
@@ -868,7 +1213,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
sb_step_y->set_value(snap_step.y);
sb_step_y->set_suffix("px");
sb_step_y->connect("value_changed", this, "_set_snap_step_y");
- uv_mode_hb->add_child(sb_step_y);
+ grid_settings_vb->add_margin_child(TTR("Grid Step Y:"), sb_step_y);
uv_mode_hb->add_child(memnew(VSeparator));
uv_icon_zoom = memnew(TextureRect);
@@ -878,8 +1223,10 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
uv_zoom->set_max(4);
uv_zoom->set_value(1);
uv_zoom->set_step(0.01);
+ uv_zoom->set_v_size_flags(SIZE_SHRINK_CENTER);
+
uv_mode_hb->add_child(uv_zoom);
- uv_zoom->set_custom_minimum_size(Size2(200, 0));
+ uv_zoom->set_custom_minimum_size(Size2(80 * EDSCALE, 0));
uv_zoom_value = memnew(SpinBox);
uv_zoom->share(uv_zoom_value);
uv_zoom_value->set_custom_minimum_size(Size2(50, 0));
@@ -893,14 +1240,29 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
uv_main_vb->add_child(uv_hscroll);
uv_hscroll->connect("value_changed", this, "_uv_scroll_changed");
+ bone_scroll_main_vb = memnew(VBoxContainer);
+ bone_scroll_main_vb->hide();
+ sync_bones = memnew(Button(TTR("Sync Bones to Polygon")));
+ bone_scroll_main_vb->add_child(sync_bones);
+ uv_main_hb->add_child(bone_scroll_main_vb);
+ bone_scroll = memnew(ScrollContainer);
+ bone_scroll->set_v_scroll(true);
+ bone_scroll->set_h_scroll(false);
+ bone_scroll_main_vb->add_child(bone_scroll);
+ bone_scroll->set_v_size_flags(SIZE_EXPAND_FILL);
+ bone_scroll_vb = memnew(VBoxContainer);
+ bone_scroll->add_child(bone_scroll_vb);
+ sync_bones->connect("pressed", this, "_sync_bones");
+
uv_edit_draw->connect("draw", this, "_uv_draw");
uv_edit_draw->connect("gui_input", this, "_uv_input");
uv_draw_zoom = 1.0;
- uv_drag_index = -1;
+ point_drag_index = -1;
uv_drag = false;
uv_create = false;
updating_uv_scroll = false;
split_create = false;
+ bone_painting = false;
error = memnew(AcceptDialog);
add_child(error);
diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h
index 8631ffb9a..f9b42a21c 100644
--- a/editor/plugins/polygon_2d_editor_plugin.h
+++ b/editor/plugins/polygon_2d_editor_plugin.h
@@ -32,7 +32,7 @@
#define POLYGON_2D_EDITOR_PLUGIN_H
#include "editor/plugins/abstract_polygon_2d_editor.h"
-
+#include "scene/gui/scroll_container.h"
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -45,7 +45,8 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
MODE_EDIT_UV = MODE_CONT,
UVEDIT_POLYGON_TO_UV,
UVEDIT_UV_TO_POLYGON,
- UVEDIT_UV_CLEAR
+ UVEDIT_UV_CLEAR,
+ UVEDIT_GRID_SETTINGS
};
@@ -57,10 +58,12 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
UV_MODE_SCALE,
UV_MODE_ADD_SPLIT,
UV_MODE_REMOVE_SPLIT,
+ UV_MODE_PAINT_WEIGHT,
+ UV_MODE_CLEAR_WEIGHT,
UV_MODE_MAX
};
- ToolButton *uv_edit_mode[3];
+ ToolButton *uv_edit_mode[4];
Ref<ButtonGroup> uv_edit_group;
Polygon2D *node;
@@ -78,15 +81,32 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
MenuButton *uv_menu;
TextureRect *uv_icon_zoom;
+ VBoxContainer *bone_scroll_main_vb;
+ ScrollContainer *bone_scroll;
+ VBoxContainer *bone_scroll_vb;
+ Button *sync_bones;
+ HSlider *bone_paint_strength;
+ SpinBox *bone_paint_radius;
+ Label *bone_paint_radius_label;
+ bool bone_painting;
+ int bone_painting_bone;
+ PoolVector<float> prev_weights;
+ Vector2 bone_paint_pos;
+ AcceptDialog *grid_settings;
+
+ void _sync_bones();
+ void _update_bone_list();
+
Vector2 uv_draw_ofs;
float uv_draw_zoom;
- PoolVector<Vector2> uv_prev;
+ PoolVector<Vector2> points_prev;
PoolVector<Vector2> uv_create_uv_prev;
PoolVector<Vector2> uv_create_poly_prev;
+ Array uv_create_bones_prev;
PoolVector<int> splits_prev;
Vector2 uv_create_to;
- int uv_drag_index;
+ int point_drag_index;
bool uv_drag;
bool uv_create;
bool split_create;
@@ -118,6 +138,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
void _set_snap_step_y(float p_val);
void _uv_edit_mode_select(int p_mode);
+ void _bone_paint_selected(int p_index);
protected:
virtual Node2D *_get_node() const;
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 09388870f..fa674e1e3 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -39,9 +39,11 @@
#include "core/project_settings.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/find_in_files.h"
#include "editor/node_dock.h"
#include "editor/script_editor_debugger.h"
#include "scene/main/viewport.h"
+#include "script_text_editor.h"
/*** SCRIPT EDITOR ****/
@@ -54,6 +56,8 @@ void ScriptEditorBase::_bind_methods() {
ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line")));
ADD_SIGNAL(MethodInfo("request_save_history"));
ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what")));
+ // TODO This signal is no use for VisualScript...
+ ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text")));
}
static bool _can_open_in_editor(Script *p_script) {
@@ -298,15 +302,9 @@ void ScriptEditor::_script_created(Ref<Script> p_script) {
void ScriptEditor::_goto_script_line2(int p_line) {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
- if (!current)
- return;
-
- current->goto_line(p_line);
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ current->goto_line(p_line);
}
void ScriptEditor::_goto_script_line(REF p_script, int p_line) {
@@ -316,19 +314,22 @@ void ScriptEditor::_goto_script_line(REF p_script, int p_line) {
if (edit(p_script, p_line, 0)) {
editor->push_item(p_script.ptr());
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
- if (!current)
- return;
-
- current->goto_line(p_line, true);
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ current->goto_line(p_line, true);
}
}
}
+ScriptEditorBase *ScriptEditor::_get_current_editor() const {
+
+ int selected = tab_container->get_current_tab();
+ if (selected < 0 || selected >= tab_container->get_child_count())
+ return NULL;
+
+ return Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
+}
+
void ScriptEditor::_update_history_arrows() {
script_back->set_disabled(history_pos <= 0);
@@ -587,7 +588,7 @@ void ScriptEditor::_close_docs_tab() {
}
void ScriptEditor::_copy_script_path() {
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_current_tab()));
+ ScriptEditorBase *se = _get_current_editor();
Ref<Script> script = se->get_edited_script();
OS::get_singleton()->set_clipboard(script->get_path());
}
@@ -819,11 +820,8 @@ void ScriptEditor::_file_dialog_action(String p_file) {
Ref<Script> ScriptEditor::_get_current_script() {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return NULL;
+ ScriptEditorBase *current = _get_current_editor();
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
if (current) {
return current->get_edited_script();
} else {
@@ -886,7 +884,7 @@ void ScriptEditor::_menu_option(int p_option) {
file_dialog->add_filter("*.tet");
file_dialog->set_current_path(EditorSettings::get_singleton()->get_text_editor_themes_dir().plus_file(EditorSettings::get_singleton()->get("text_editor/theme/color_theme")));
file_dialog->popup_centered_ratio();
- file_dialog->set_title(TTR("Save Theme As.."));
+ file_dialog->set_title(TTR("Save Theme As..."));
} break;
case SEARCH_HELP: {
@@ -939,11 +937,7 @@ void ScriptEditor::_menu_option(int p_option) {
}
}
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
+ ScriptEditorBase *current = _get_current_editor();
if (current) {
switch (p_option) {
@@ -1033,7 +1027,7 @@ void ScriptEditor::_menu_option(int p_option) {
_copy_script_path();
} break;
case SHOW_IN_FILE_SYSTEM: {
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_current_tab()));
+ ScriptEditorBase *se = _get_current_editor();
Ref<Script> script = se->get_edited_script();
FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
file_system_dock->navigate_to_path(script->get_path());
@@ -1223,6 +1217,17 @@ void ScriptEditor::_notification(int p_what) {
recent_scripts->set_as_minsize();
} break;
+ case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: {
+
+ if (is_visible()) {
+ find_in_files_button->show();
+ } else {
+ find_in_files->hide();
+ find_in_files_button->hide();
+ }
+
+ } break;
+
default:
break;
}
@@ -1230,15 +1235,11 @@ void ScriptEditor::_notification(int p_what) {
bool ScriptEditor::can_take_away_focus() const {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return true;
-
- ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
- if (!current)
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ return current->can_lose_focus_on_node_selection();
+ else
return true;
-
- return current->can_lose_focus_on_node_selection();
}
void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) {
@@ -1315,20 +1316,13 @@ void ScriptEditor::ensure_focus_current() {
if (!is_inside_tree())
return;
- int cidx = tab_container->get_current_tab();
- if (cidx < 0 || cidx >= tab_container->get_tab_count())
- return;
-
- Control *c = Object::cast_to<Control>(tab_container->get_child(cidx));
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(c);
- if (!se)
- return;
- se->ensure_focus();
+ ScriptEditorBase *current = _get_current_editor();
+ if (current)
+ current->ensure_focus();
}
void ScriptEditor::_members_overview_selected(int p_idx) {
- Node *current = tab_container->get_child(tab_container->get_current_tab());
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (!se) {
return;
}
@@ -1362,18 +1356,12 @@ void ScriptEditor::ensure_select_current() {
if (tab_container->get_child_count() && tab_container->get_current_tab() >= 0) {
- Node *current = tab_container->get_child(tab_container->get_current_tab());
-
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (se) {
- Ref<Script> script = se->get_edited_script();
-
if (!grab_focus_block && is_visible_in_tree())
se->ensure_focus();
}
-
- EditorHelp *eh = Object::cast_to<EditorHelp>(current);
}
_update_selected_editor_menu();
@@ -1413,12 +1401,7 @@ struct _ScriptEditorItemData {
void ScriptEditor::_update_members_overview_visibility() {
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- Node *current = tab_container->get_child(tab_container->get_current_tab());
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (!se) {
members_overview->set_visible(false);
return;
@@ -1434,12 +1417,7 @@ void ScriptEditor::_update_members_overview_visibility() {
void ScriptEditor::_update_members_overview() {
members_overview->clear();
- int selected = tab_container->get_current_tab();
- if (selected < 0 || selected >= tab_container->get_child_count())
- return;
-
- Node *current = tab_container->get_child(tab_container->get_current_tab());
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(current);
+ ScriptEditorBase *se = _get_current_editor();
if (!se) {
return;
}
@@ -1778,6 +1756,20 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool
}
ERR_FAIL_COND_V(!se, false);
+ bool highlighter_set = false;
+ for (int i = 0; i < syntax_highlighters_func_count; i++) {
+ SyntaxHighlighter *highlighter = syntax_highlighters_funcs[i]();
+ se->add_syntax_highlighter(highlighter);
+
+ if (!highlighter_set) {
+ List<String> languages = highlighter->get_supported_languages();
+ if (languages.find(p_script->get_language()->get_name())) {
+ se->set_syntax_highlighter(highlighter);
+ highlighter_set = true;
+ }
+ }
+ }
+
tab_container->add_child(se);
se->set_edited_script(p_script);
se->set_tooltip_request_func("_get_debug_tooltip", this);
@@ -1799,6 +1791,7 @@ bool ScriptEditor::edit(const Ref<Script> &p_script, int p_line, int p_col, bool
se->connect("request_open_script_at_line", this, "_goto_script_line");
se->connect("go_to_help", this, "_help_class_goto");
se->connect("request_save_history", this, "_save_history");
+ se->connect("search_in_files_requested", this, "_on_find_in_files_requested");
//test for modification, maybe the script was not edited but was loaded
@@ -2494,6 +2487,14 @@ void ScriptEditor::_open_script_request(const String &p_path) {
}
}
+int ScriptEditor::syntax_highlighters_func_count = 0;
+CreateSyntaxHighlighterFunc ScriptEditor::syntax_highlighters_funcs[ScriptEditor::SYNTAX_HIGHLIGHTER_FUNC_MAX];
+
+void ScriptEditor::register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func) {
+ ERR_FAIL_COND(syntax_highlighters_func_count == SYNTAX_HIGHLIGHTER_FUNC_MAX);
+ syntax_highlighters_funcs[syntax_highlighters_func_count++] = p_func;
+}
+
int ScriptEditor::script_editor_func_count = 0;
CreateScriptEditorFunc ScriptEditor::script_editor_funcs[ScriptEditor::SCRIPT_EDITOR_FUNC_MAX];
@@ -2508,6 +2509,48 @@ void ScriptEditor::_script_changed() {
NodeDock::singleton->update_lists();
}
+void ScriptEditor::_on_find_in_files_requested(String text) {
+
+ find_in_files_dialog->set_search_text(text);
+ find_in_files_dialog->popup_centered_minsize();
+}
+
+void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) {
+
+ Ref<Resource> res = ResourceLoader::load(fpath);
+ edit(res);
+
+ ScriptEditorBase *seb = _get_current_editor();
+
+ ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(seb);
+ if (ste) {
+ ste->goto_line_selection(line_number - 1, begin, end);
+ }
+}
+
+void ScriptEditor::_start_find_in_files(bool with_replace) {
+
+ FindInFiles *f = find_in_files->get_finder();
+
+ f->set_search_text(find_in_files_dialog->get_search_text());
+ f->set_match_case(find_in_files_dialog->is_match_case());
+ f->set_whole_words(find_in_files_dialog->is_match_case());
+ f->set_folder(find_in_files_dialog->get_folder());
+ f->set_filter(find_in_files_dialog->get_filter());
+
+ find_in_files->set_with_replace(with_replace);
+ find_in_files->start_search();
+
+ find_in_files_button->set_pressed(true);
+ find_in_files->show();
+}
+
+void ScriptEditor::_on_find_in_files_modified_files(PoolStringArray paths) {
+
+ _test_script_times_on_disk();
+ _update_modified_scripts_for_external_editor();
+}
+
void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_file_dialog_action", &ScriptEditor::_file_dialog_action);
@@ -2555,6 +2598,10 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_script_list_gui_input", &ScriptEditor::_script_list_gui_input);
ClassDB::bind_method("_script_changed", &ScriptEditor::_script_changed);
ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts);
+ ClassDB::bind_method("_on_find_in_files_requested", &ScriptEditor::_on_find_in_files_requested);
+ ClassDB::bind_method("_start_find_in_files", &ScriptEditor::_start_find_in_files);
+ ClassDB::bind_method("_on_find_in_files_result_selected", &ScriptEditor::_on_find_in_files_result_selected);
+ ClassDB::bind_method("_on_find_in_files_modified_files", &ScriptEditor::_on_find_in_files_modified_files);
ClassDB::bind_method(D_METHOD("get_drag_data_fw", "point", "from"), &ScriptEditor::get_drag_data_fw);
ClassDB::bind_method(D_METHOD("can_drop_data_fw", "point", "data", "from"), &ScriptEditor::can_drop_data_fw);
@@ -2649,7 +2696,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save", TTR("Save"), KEY_MASK_ALT | KEY_MASK_CMD | KEY_S), FILE_SAVE);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As..")), FILE_SAVE_AS);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As...")), FILE_SAVE_AS);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_S), FILE_SAVE_ALL);
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), FILE_TOOL_RELOAD_SOFT);
@@ -2678,7 +2725,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
script_search_menu = memnew(MenuButton);
menu_hb->add_child(script_search_menu);
script_search_menu->set_text(TTR("Search"));
- script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find.."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND);
+ script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND);
script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT);
script_search_menu->get_popup()->connect("id_pressed", this, "_menu_option");
script_search_menu->hide();
@@ -2816,6 +2863,19 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
add_child(help_index);
help_index->connect("open_class", this, "_help_class_open");
+ find_in_files_dialog = memnew(FindInFilesDialog);
+ find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_FIND_REQUESTED, this, "_start_find_in_files", varray(false));
+ find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, this, "_start_find_in_files", varray(true));
+ add_child(find_in_files_dialog);
+ find_in_files = memnew(FindInFilesPanel);
+ find_in_files_button = editor->add_bottom_panel_item(TTR("Search results"), find_in_files);
+ find_in_files_button->set_tooltip(TTR("Search in files"));
+ find_in_files->set_custom_minimum_size(Size2(0, 200));
+ find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, this, "_on_find_in_files_result_selected");
+ find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, this, "_on_find_in_files_modified_files");
+ find_in_files->hide();
+ find_in_files_button->hide();
+
history_pos = -1;
//debugger_gui->hide();
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index bcc604d99..9f37b18d7 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -80,6 +80,9 @@ protected:
static void _bind_methods();
public:
+ virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0;
+ virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter) = 0;
+
virtual void apply_code() = 0;
virtual Ref<Script> get_edited_script() const = 0;
virtual Vector<String> get_functions() = 0;
@@ -112,9 +115,12 @@ public:
ScriptEditorBase() {}
};
+typedef SyntaxHighlighter *(*CreateSyntaxHighlighterFunc)();
typedef ScriptEditorBase *(*CreateScriptEditorFunc)(const Ref<Script> &p_script);
class EditorScriptCodeCompletionCache;
+class FindInFilesDialog;
+class FindInFilesPanel;
class ScriptEditor : public PanelContainer {
@@ -213,13 +219,21 @@ class ScriptEditor : public PanelContainer {
ToolButton *script_back;
ToolButton *script_forward;
+ FindInFilesDialog *find_in_files_dialog;
+ FindInFilesPanel *find_in_files;
+ Button *find_in_files_button;
+
enum {
- SCRIPT_EDITOR_FUNC_MAX = 32
+ SCRIPT_EDITOR_FUNC_MAX = 32,
+ SYNTAX_HIGHLIGHTER_FUNC_MAX = 32
};
static int script_editor_func_count;
static CreateScriptEditorFunc script_editor_funcs[SCRIPT_EDITOR_FUNC_MAX];
+ static int syntax_highlighters_func_count;
+ static CreateSyntaxHighlighterFunc syntax_highlighters_funcs[SYNTAX_HIGHLIGHTER_FUNC_MAX];
+
struct ScriptHistory {
Control *control;
@@ -296,6 +310,8 @@ class ScriptEditor : public PanelContainer {
void _update_window_menu();
void _script_created(Ref<Script> p_script);
+ ScriptEditorBase *_get_current_editor() const;
+
void _save_layout();
void _editor_settings_changed();
void _autosave_scripts();
@@ -351,6 +367,11 @@ class ScriptEditor : public PanelContainer {
Ref<Script> _get_current_script();
Array _get_open_scripts() const;
+ void _on_find_in_files_requested(String text);
+ void _on_find_in_files_result_selected(String fpath, int line_number, int begin, int end);
+ void _start_find_in_files(bool with_replace);
+ void _on_find_in_files_modified_files(PoolStringArray paths);
+
static void _open_script_request(const String &p_path);
static ScriptEditor *script_editor;
@@ -399,7 +420,9 @@ public:
ScriptEditorDebugger *get_debugger() { return debugger; }
void set_live_auto_reload_running_scripts(bool p_enabled);
+ static void register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func);
static void register_create_script_editor_function(CreateScriptEditorFunc p_func);
+
ScriptEditor(EditorNode *p_editor);
~ScriptEditor();
};
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index c8ea2f79f..45f5e667f 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -529,6 +529,14 @@ void ScriptTextEditor::goto_line(int p_line, bool p_with_error) {
tx->call_deferred("cursor_set_line", p_line);
}
+void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) {
+ TextEdit *tx = code_editor->get_text_edit();
+ tx->unfold_line(p_line);
+ tx->call_deferred("cursor_set_line", p_line);
+ tx->call_deferred("cursor_set_column", p_begin);
+ tx->select(p_line, p_begin, p_line, p_end);
+}
+
void ScriptTextEditor::ensure_focus() {
code_editor->get_text_edit()->grab_focus();
@@ -573,6 +581,7 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) {
ERR_FAIL_COND(!script.is_null());
script = p_script;
+ _set_theme_for_script();
code_editor->get_text_edit()->set_text(script->get_source_code());
code_editor->get_text_edit()->clear_undo_history();
@@ -580,8 +589,6 @@ void ScriptTextEditor::set_edited_script(const Ref<Script> &p_script) {
emit_signal("name_changed");
code_editor->update_line_and_column();
-
- _set_theme_for_script();
}
void ScriptTextEditor::_validate_script() {
@@ -739,6 +746,8 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
_goto_line(p_row);
+ result.class_name = result.class_name.trim_prefix("_");
+
switch (result.type) {
case ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION: {
@@ -789,6 +798,26 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
emit_signal("go_to_help", "class_method:" + result.class_name + ":" + result.class_member);
} break;
+ case ScriptLanguage::LookupResult::RESULT_CLASS_ENUM: {
+
+ StringName cname = result.class_name;
+ StringName success;
+ while (true) {
+ success = ClassDB::get_integer_constant_enum(cname, result.class_member, true);
+ if (success != StringName()) {
+ result.class_name = cname;
+ cname = ClassDB::get_parent_class(cname);
+ } else {
+ break;
+ }
+ }
+
+ emit_signal("go_to_help", "class_enum:" + result.class_name + ":" + result.class_member);
+
+ } break;
+ case ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE: {
+ emit_signal("go_to_help", "class_global:" + result.class_name + ":" + result.class_member);
+ } break;
}
}
}
@@ -980,6 +1009,10 @@ void ScriptTextEditor::_edit_option(int p_op) {
}
int next_line = to_line + 1;
+ if (to_line >= tx->get_line_count() - 1) {
+ tx->set_line(to_line, tx->get_line(to_line) + "\n");
+ }
+
tx->begin_complex_operation();
for (int i = from_line; i <= to_line; i++) {
@@ -1154,6 +1187,15 @@ void ScriptTextEditor::_edit_option(int p_op) {
code_editor->get_find_replace_bar()->popup_replace();
} break;
+ case SEARCH_IN_FILES: {
+
+ String selected_text = code_editor->get_text_edit()->get_selection_text();
+
+ // Yep, because it doesn't make sense to instance this dialog for every single script open...
+ // So this will be delegated to the ScriptEditor
+ emit_signal("search_in_files_requested", selected_text);
+
+ } break;
case SEARCH_LOCATE_FUNCTION: {
quick_open->popup(get_functions());
@@ -1245,11 +1287,26 @@ void ScriptTextEditor::_edit_option(int p_op) {
}
}
+void ScriptTextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+ highlighters[p_highlighter->get_name()] = p_highlighter;
+ highlighter_menu->get_popup()->add_item(p_highlighter->get_name());
+}
+
+void ScriptTextEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+ TextEdit *te = code_editor->get_text_edit();
+ te->_set_syntax_highlighting(p_highlighter);
+}
+
+void ScriptTextEditor::_change_syntax_highlighter(int p_idx) {
+ set_syntax_highlighter(highlighters[highlighter_menu->get_popup()->get_item_text(p_idx)]);
+}
+
void ScriptTextEditor::_bind_methods() {
ClassDB::bind_method("_validate_script", &ScriptTextEditor::_validate_script);
ClassDB::bind_method("_load_theme_settings", &ScriptTextEditor::_load_theme_settings);
ClassDB::bind_method("_breakpoint_toggled", &ScriptTextEditor::_breakpoint_toggled);
+ ClassDB::bind_method("_change_syntax_highlighter", &ScriptTextEditor::_change_syntax_highlighter);
ClassDB::bind_method("_edit_option", &ScriptTextEditor::_edit_option);
ClassDB::bind_method("_goto_line", &ScriptTextEditor::_goto_line);
ClassDB::bind_method("_lookup_symbol", &ScriptTextEditor::_lookup_symbol);
@@ -1626,6 +1683,8 @@ ScriptTextEditor::ScriptTextEditor() {
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_previous"), SEARCH_FIND_PREV);
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/replace"), SEARCH_REPLACE);
search_menu->get_popup()->add_separator();
+ search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/find_in_files"), SEARCH_IN_FILES);
+ search_menu->get_popup()->add_separator();
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_function"), SEARCH_LOCATE_FUNCTION);
search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE);
search_menu->get_popup()->add_separator();
@@ -1635,6 +1694,14 @@ ScriptTextEditor::ScriptTextEditor() {
edit_hb->add_child(edit_menu);
+ highlighters["Standard"] = NULL;
+
+ highlighter_menu = memnew(MenuButton);
+ highlighter_menu->set_text(TTR("Syntax Highlighter"));
+ highlighter_menu->get_popup()->add_item("Standard");
+ highlighter_menu->get_popup()->connect("id_pressed", this, "_change_syntax_highlighter");
+ edit_hb->add_child(highlighter_menu);
+
quick_open = memnew(ScriptEditorQuickOpen);
add_child(quick_open);
quick_open->connect("goto_line", this, "_goto_line");
@@ -1692,13 +1759,15 @@ void ScriptTextEditor::register_editor() {
ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Convert To Lowercase"), KEY_MASK_SHIFT | KEY_F3);
ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KEY_MASK_SHIFT | KEY_F2);
- ED_SHORTCUT("script_text_editor/find", TTR("Find.."), KEY_MASK_CMD | KEY_F);
+ ED_SHORTCUT("script_text_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F);
ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_F3);
ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3);
- ED_SHORTCUT("script_text_editor/replace", TTR("Replace.."), KEY_MASK_CMD | KEY_R);
+ ED_SHORTCUT("script_text_editor/replace", TTR("Replace..."), KEY_MASK_CMD | KEY_R);
+
+ ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F);
- ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function.."), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_F);
- ED_SHORTCUT("script_text_editor/goto_line", TTR("Goto Line.."), KEY_MASK_CMD | KEY_L);
+ ED_SHORTCUT("script_text_editor/goto_function", TTR("Goto Function..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F);
+ ED_SHORTCUT("script_text_editor/goto_line", TTR("Goto Line..."), KEY_MASK_CMD | KEY_L);
ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_SHIFT | KEY_F1);
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 22e8fbce2..a93e1a6fa 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -49,6 +49,7 @@ class ScriptTextEditor : public ScriptEditorBase {
HBoxContainer *edit_hb;
MenuButton *edit_menu;
+ MenuButton *highlighter_menu;
MenuButton *search_menu;
PopupMenu *context_menu;
@@ -105,6 +106,7 @@ class ScriptTextEditor : public ScriptEditorBase {
SEARCH_REPLACE,
SEARCH_LOCATE_FUNCTION,
SEARCH_GOTO_LINE,
+ SEARCH_IN_FILES,
DEBUG_TOGGLE_BREAKPOINT,
DEBUG_REMOVE_ALL_BREAKPOINTS,
DEBUG_GOTO_NEXT_BREAKPOINT,
@@ -125,6 +127,9 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ Map<String, SyntaxHighlighter *> highlighters;
+ void _change_syntax_highlighter(int p_idx);
+
void _edit_option(int p_op);
void _make_context_menu(bool p_selection, bool p_color, bool p_can_fold, bool p_is_folded);
void _text_edit_gui_input(const Ref<InputEvent> &ev);
@@ -145,6 +150,9 @@ protected:
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
public:
+ virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+ virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+
virtual void apply_code();
virtual Ref<Script> get_edited_script() const;
virtual Vector<String> get_functions();
@@ -163,6 +171,7 @@ public:
virtual void tag_saved_version();
virtual void goto_line(int p_line, bool p_with_error = false);
+ void goto_line_selection(int p_line, int p_begin, int p_end);
virtual void reload(bool p_soft);
virtual void get_breakpoints(List<int> *p_breakpoints);
diff --git a/editor/plugins/shader_graph_editor_plugin.cpp b/editor/plugins/shader_graph_editor_plugin.cpp
index 59085c203..1a9d980fe 100644
--- a/editor/plugins/shader_graph_editor_plugin.cpp
+++ b/editor/plugins/shader_graph_editor_plugin.cpp
@@ -1478,7 +1478,7 @@ void ShaderGraphView::_create_node(int p_id) {
case ShaderGraph::NODE_XFORM_CONST: {
gn->set_title("XForm");
ToolButton *edit = memnew( ToolButton );
- edit->set_text("edit..");
+ edit->set_text("edit...");
edit->connect("pressed",this,"_xform_const_changed",varray(p_id,edit));
gn->add_child(edit);
gn->set_slot(0,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
@@ -2289,7 +2289,7 @@ void ShaderGraphView::_create_node(int p_id) {
le->set_text(graph->input_node_get_name(type,p_id));
le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
ToolButton *edit = memnew( ToolButton );
- edit->set_text("edit..");
+ edit->set_text("edit...");
edit->connect("pressed",this,"_xform_input_changed",varray(p_id,edit));
gn->add_child(edit);
gn->set_slot(1,false,0,Color(),true,ShaderGraph::SLOT_TYPE_XFORM,typecol[ShaderGraph::SLOT_TYPE_XFORM]);
@@ -2310,7 +2310,7 @@ void ShaderGraphView::_create_node(int p_id) {
tex->set_mouse_filter(MOUSE_FILTER_PASS);
tex->set_texture(graph->texture_input_node_get_value(type,p_id));
ToolButton *edit = memnew( ToolButton );
- edit->set_text("edit..");
+ edit->set_text("edit...");
edit->connect("pressed",this,"_tex_edited",varray(p_id,edit));
gn->add_child(edit);
@@ -2345,7 +2345,7 @@ void ShaderGraphView::_create_node(int p_id) {
le->connect("text_entered",this,"_input_name_changed",varray(p_id,le));
ToolButton *edit = memnew( ToolButton );
- edit->set_text("edit..");
+ edit->set_text("edit...");
edit->connect("pressed",this,"_cube_edited",varray(p_id,edit));
gn->add_child(edit);
@@ -2769,8 +2769,6 @@ void ShaderGraphEditor::_popup_requested(const Vector2 &p_position)
popup->set_global_position(p_position);
popup->set_size( Size2( 200, 0) );
popup->popup();
- popup->call_deferred("grab_click_focus");
- popup->set_invalidate_click_until_motion();
}
void ShaderGraphEditor::_notification(int p_what) {
diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp
index 6bf94b95e..e372f792d 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_2d_editor_plugin.cpp
@@ -20,6 +20,10 @@ void Skeleton2DEditor::edit(Skeleton2D *p_sprite) {
void Skeleton2DEditor::_menu_option(int p_option) {
+ if (!node) {
+ return;
+ }
+
switch (p_option) {
case MENU_OPTION_MAKE_REST: {
@@ -107,6 +111,7 @@ Skeleton2DEditorPlugin::Skeleton2DEditorPlugin(EditorNode *p_node) {
editor = p_node;
sprite_editor = memnew(Skeleton2DEditor);
editor->get_viewport()->add_child(sprite_editor);
+ make_visible(false);
//sprite_editor->options->hide();
}
diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp
new file mode 100644
index 000000000..c41e3b546
--- /dev/null
+++ b/editor/plugins/skeleton_editor_plugin.cpp
@@ -0,0 +1,189 @@
+/*************************************************************************/
+/* skeleton_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 "skeleton_editor_plugin.h"
+#include "scene/3d/collision_shape.h"
+#include "scene/3d/physics_body.h"
+#include "scene/3d/physics_joint.h";
+#include "scene/resources/capsule_shape.h"
+#include "scene/resources/sphere_shape.h"
+#include "spatial_editor_plugin.h"
+
+void SkeletonEditor::_on_click_option(int p_option) {
+ if (!skeleton) {
+ return;
+ }
+
+ switch (p_option) {
+ case MENU_OPTION_CREATE_PHYSICAL_SKELETON: {
+ create_physical_skeleton();
+ } break;
+ }
+}
+
+void SkeletonEditor::create_physical_skeleton() {
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ Node *owner = skeleton == get_tree()->get_edited_scene_root() ? skeleton : skeleton->get_owner();
+
+ const int bc = skeleton->get_bone_count();
+
+ if (!bc) {
+ return;
+ }
+
+ Vector<BoneInfo> bones_infos;
+ bones_infos.resize(bc);
+
+ for (int bone_id = 0; bc > bone_id; ++bone_id) {
+
+ const int parent = skeleton->get_bone_parent(bone_id);
+ const int parent_parent = skeleton->get_bone_parent(parent);
+
+ if (parent < 0) {
+
+ bones_infos[bone_id].relative_rest = skeleton->get_bone_rest(bone_id);
+
+ } else {
+
+ bones_infos[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id);
+
+ /// create physical bone on parent
+ if (!bones_infos[parent].physical_bone) {
+
+ bones_infos[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos);
+
+ ur->create_action(TTR("Create physical bones"));
+ ur->add_do_method(skeleton, "add_child", bones_infos[parent].physical_bone);
+ ur->add_do_reference(bones_infos[parent].physical_bone);
+ ur->add_undo_method(skeleton, "remove_child", bones_infos[parent].physical_bone);
+ ur->commit_action();
+
+ bones_infos[parent].physical_bone->set_bone_name(skeleton->get_bone_name(parent));
+ bones_infos[parent].physical_bone->set_owner(owner);
+ bones_infos[parent].physical_bone->get_child(0)->set_owner(owner); // set shape owner
+
+ /// Create joint between parent of parent
+ if (-1 != parent_parent) {
+
+ bones_infos[parent].physical_bone->set_joint_type(PhysicalBone::JOINT_TYPE_PIN);
+ }
+ }
+ }
+ }
+}
+
+PhysicalBone *SkeletonEditor::create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos) {
+
+ real_t half_height(skeleton->get_bone_rest(bone_child_id).origin.length() * 0.5);
+ real_t radius(half_height * 0.2);
+
+ CapsuleShape *bone_shape_capsule = memnew(CapsuleShape);
+ bone_shape_capsule->set_height((half_height - radius) * 2);
+ bone_shape_capsule->set_radius(radius);
+
+ CollisionShape *bone_shape = memnew(CollisionShape);
+ bone_shape->set_shape(bone_shape_capsule);
+
+ Transform body_transform;
+ body_transform.origin = Vector3(0, 0, -half_height);
+
+ Transform joint_transform;
+ joint_transform.origin = Vector3(0, 0, half_height);
+
+ PhysicalBone *physical_bone = memnew(PhysicalBone);
+ physical_bone->add_child(bone_shape);
+ physical_bone->set_name("Physical Bone " + skeleton->get_bone_name(bone_id));
+ physical_bone->set_body_offset(body_transform);
+ physical_bone->set_joint_offset(joint_transform);
+ return physical_bone;
+}
+
+void SkeletonEditor::edit(Skeleton *p_node) {
+ skeleton = p_node;
+}
+
+void SkeletonEditor::_node_removed(Node *p_node) {
+
+ if (p_node == skeleton) {
+ skeleton = NULL;
+ options->hide();
+ }
+}
+
+void SkeletonEditor::_bind_methods() {
+ ClassDB::bind_method("_on_click_option", &SkeletonEditor::_on_click_option);
+}
+
+SkeletonEditor::SkeletonEditor() {
+ options = memnew(MenuButton);
+ SpatialEditor::get_singleton()->add_control_to_menu_panel(options);
+
+ options->set_text(TTR("Skeleton"));
+ options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Skeleton", "EditorIcons"));
+
+ options->get_popup()->add_item(TTR("Create physical skeleton"), MENU_OPTION_CREATE_PHYSICAL_SKELETON);
+
+ options->get_popup()->connect("id_pressed", this, "_on_click_option");
+ options->hide();
+}
+
+SkeletonEditor::~SkeletonEditor() {
+ SpatialEditor::get_singleton()->remove_child(options);
+ memdelete(options);
+}
+
+void SkeletonEditorPlugin::edit(Object *p_object) {
+ skeleton_editor->edit(Object::cast_to<Skeleton>(p_object));
+}
+
+bool SkeletonEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("Skeleton");
+}
+
+void SkeletonEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ skeleton_editor->options->show();
+ } else {
+
+ skeleton_editor->options->hide();
+ skeleton_editor->edit(NULL);
+ }
+}
+
+SkeletonEditorPlugin::SkeletonEditorPlugin(EditorNode *p_node) {
+ editor = p_node;
+ skeleton_editor = memnew(SkeletonEditor);
+ editor->get_viewport()->add_child(skeleton_editor);
+}
+
+SkeletonEditorPlugin::~SkeletonEditorPlugin() {
+ editor->get_viewport()->remove_child(skeleton_editor);
+ memdelete(skeleton_editor);
+}
diff --git a/editor/plugins/skeleton_editor_plugin.h b/editor/plugins/skeleton_editor_plugin.h
new file mode 100644
index 000000000..b9bdf9190
--- /dev/null
+++ b/editor/plugins/skeleton_editor_plugin.h
@@ -0,0 +1,95 @@
+/*************************************************************************/
+/* skeleton_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (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 SKELETON_EDITOR_PLUGIN_H
+#define SKELETON_EDITOR_PLUGIN_H
+
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/3d/skeleton.h"
+
+class PhysicalBone;
+class Joint;
+
+class SkeletonEditor : public Node {
+ GDCLASS(SkeletonEditor, Node);
+
+ enum Menu {
+ MENU_OPTION_CREATE_PHYSICAL_SKELETON
+ };
+
+ struct BoneInfo {
+ PhysicalBone *physical_bone;
+ Transform relative_rest; // Relative to skeleton node
+ BoneInfo() :
+ physical_bone(NULL) {}
+ };
+
+ Skeleton *skeleton;
+
+ MenuButton *options;
+
+ void _on_click_option(int p_option);
+
+ friend class SkeletonEditorPlugin;
+
+protected:
+ void _node_removed(Node *p_node);
+ static void _bind_methods();
+
+ void create_physical_skeleton();
+ PhysicalBone *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
+
+public:
+ void edit(Skeleton *p_mesh);
+
+ SkeletonEditor();
+ ~SkeletonEditor();
+};
+
+class SkeletonEditorPlugin : public EditorPlugin {
+
+ GDCLASS(SkeletonEditorPlugin, EditorPlugin);
+
+ EditorNode *editor;
+ SkeletonEditor *skeleton_editor;
+
+public:
+ virtual String get_name() const { return "Skeleton"; }
+ virtual bool has_main_screen() const { return false; }
+ virtual void edit(Object *p_object);
+ virtual bool handles(Object *p_object) const;
+ virtual void make_visible(bool p_visible);
+
+ SkeletonEditorPlugin(EditorNode *p_node);
+ ~SkeletonEditorPlugin();
+};
+
+#endif // SKELETON_EDITOR_PLUGIN_H
diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp
index e48414052..5b713ef3c 100644
--- a/editor/plugins/spatial_editor_plugin.cpp
+++ b/editor/plugins/spatial_editor_plugin.cpp
@@ -72,6 +72,14 @@
#define MIN_FOV 0.01
#define MAX_FOV 179
+#ifdef TOOLS_ENABLED
+#define get_global_gizmo_transform get_global_gizmo_transform
+#define get_local_gizmo_transform get_local_gizmo_transform
+#else
+#define get_global_gizmo_transform get_global_transform
+#define get_local_gizmo_transform get_transform
+#endif
+
void SpatialEditorViewport::_update_camera(float p_interp_delta) {
bool is_orthogonal = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL;
@@ -584,8 +592,8 @@ void SpatialEditorViewport::_compute_edit(const Point2 &p_point) {
if (!se)
continue;
- se->original = se->sp->get_global_transform();
- se->original_local = se->sp->get_transform();
+ se->original = se->sp->get_global_gizmo_transform();
+ se->original_local = se->sp->get_local_gizmo_transform();
}
}
@@ -886,8 +894,6 @@ void SpatialEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
selection_menu->set_global_position(b->get_global_position());
selection_menu->popup();
- selection_menu->call_deferred("grab_click_focus");
- selection_menu->set_invalidate_click_until_motion();
}
}
@@ -1186,7 +1192,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (!se)
continue;
- undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_transform());
+ undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
undo_redo->add_undo_method(sp, "set_global_transform", se->original);
}
undo_redo->commit_action();
@@ -2152,7 +2158,7 @@ void SpatialEditorViewport::_notification(int p_what) {
se->aabb = vi ? vi->get_aabb() : AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
}
- Transform t = sp->get_global_transform();
+ Transform t = sp->get_global_gizmo_transform();
t.translate(se->aabb.position);
// apply AABB scaling before item's global transform
@@ -2505,7 +2511,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
xform.scale_basis(sp->get_scale());
undo_redo->add_do_method(sp, "set_global_transform", xform);
- undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform());
+ undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
}
undo_redo->commit_action();
} break;
@@ -2963,7 +2969,7 @@ void SpatialEditorViewport::focus_selection() {
if (!se)
continue;
- center += sp->get_global_transform().origin;
+ center += sp->get_global_gizmo_transform().origin;
count++;
}
@@ -3045,7 +3051,7 @@ AABB SpatialEditorViewport::_calculate_spatial_bounds(const Spatial *p_parent, c
MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(child);
if (mesh_instance) {
AABB mesh_instance_bounds = mesh_instance->get_aabb();
- mesh_instance_bounds.position += mesh_instance->get_global_transform().origin - p_parent->get_global_transform().origin;
+ mesh_instance_bounds.position += mesh_instance->get_global_gizmo_transform().origin - p_parent->get_global_gizmo_transform().origin;
bounds.merge_with(mesh_instance_bounds);
}
bounds = _calculate_spatial_bounds(child, bounds);
@@ -3123,7 +3129,7 @@ bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const P
if (!scene.is_valid()) { // invalid scene
return false;
} else {
- instanced_scene = scene->instance();
+ instanced_scene = scene->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
}
}
}
@@ -3156,7 +3162,7 @@ bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const P
Transform global_transform;
Spatial *parent_spatial = Object::cast_to<Spatial>(parent);
if (parent_spatial)
- global_transform = parent_spatial->get_global_transform();
+ global_transform = parent_spatial->get_global_gizmo_transform();
global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point));
@@ -3285,7 +3291,7 @@ void SpatialEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p
}
}
if (list.size() != 1) {
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation requires a single selected node."));
accept->popup_centered_minsize();
_remove_preview();
@@ -3353,14 +3359,14 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/front_view"), VIEW_FRONT);
view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/rear_view"), VIEW_REAR);
view_menu->get_popup()->add_separator();
- view_menu->get_popup()->add_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE);
- view_menu->get_popup()->add_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL);
+ view_menu->get_popup()->add_radio_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE);
+ view_menu->get_popup()->add_radio_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true);
view_menu->get_popup()->add_separator();
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL);
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME);
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW);
- view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW);
+ view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS);
view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true);
view_menu->get_popup()->add_separator();
view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("View Environment")), VIEW_ENVIRONMENT);
@@ -3789,7 +3795,8 @@ void SpatialEditor::update_transform_gizmo() {
if (!se)
continue;
- Transform xf = se->sp->get_global_transform();
+ Transform xf = se->sp->get_global_gizmo_transform();
+
if (first) {
center.position = xf.origin;
first = false;
@@ -4056,7 +4063,7 @@ void SpatialEditor::_xform_dialog_action() {
bool post = xform_type->get_selected() > 0;
- Transform tr = sp->get_global_transform();
+ Transform tr = sp->get_global_gizmo_transform();
if (post)
tr = tr * t;
else {
@@ -4066,7 +4073,7 @@ void SpatialEditor::_xform_dialog_action() {
}
undo_redo->add_do_method(sp, "set_global_transform", tr);
- undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform());
+ undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
}
undo_redo->commit_action();
}
@@ -4600,7 +4607,10 @@ void SpatialEditor::_init_grid() {
PoolVector<Color> grid_colors[3];
PoolVector<Vector3> grid_points[3];
- Color grid_color = EditorSettings::get_singleton()->get("editors/3d/grid_color");
+ Color primary_grid_color = EditorSettings::get_singleton()->get("editors/3d/primary_grid_color");
+ Color secondary_grid_color = EditorSettings::get_singleton()->get("editors/3d/secondary_grid_color");
+ int grid_size = EditorSettings::get_singleton()->get("editors/3d/grid_size");
+ int primary_grid_steps = EditorSettings::get_singleton()->get("editors/3d/primary_grid_steps");
for (int i = 0; i < 3; i++) {
Vector3 axis;
@@ -4610,19 +4620,17 @@ void SpatialEditor::_init_grid() {
Vector3 axis_n2;
axis_n2[(i + 2) % 3] = 1;
-#define ORIGIN_GRID_SIZE 50
-
- for (int j = -ORIGIN_GRID_SIZE; j <= ORIGIN_GRID_SIZE; j++) {
- Vector3 p1 = axis_n1 * j + axis_n2 * -ORIGIN_GRID_SIZE;
+ for (int j = -grid_size; j <= grid_size; j++) {
+ Vector3 p1 = axis_n1 * j + axis_n2 * -grid_size;
Vector3 p1_dest = p1 * (-axis_n2 + axis_n1);
- Vector3 p2 = axis_n2 * j + axis_n1 * -ORIGIN_GRID_SIZE;
+ Vector3 p2 = axis_n2 * j + axis_n1 * -grid_size;
Vector3 p2_dest = p2 * (-axis_n1 + axis_n2);
- Color line_color = grid_color;
+ Color line_color = secondary_grid_color;
if (j == 0) {
continue;
- } else if (j % 10 == 0) {
- line_color *= 1.5;
+ } else if (j % primary_grid_steps == 0) {
+ line_color = primary_grid_color;
}
grid_points[i].push_back(p1);
@@ -5084,8 +5092,6 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
ED_SHORTCUT("spatial_editor/tool_rotate", TTR("Tool Rotate"), KEY_E);
ED_SHORTCUT("spatial_editor/tool_scale", TTR("Tool Scale"), KEY_R);
- ED_SHORTCUT("spatial_editor/display_wireframe", TTR("Display Wireframe"), KEY_Z);
-
ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KEY_MASK_SHIFT + KEY_F);
PopupMenu *p;
@@ -5095,9 +5101,9 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
hbc_menu->add_child(transform_menu);
p = transform_menu->get_popup();
- p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap..")), MENU_TRANSFORM_CONFIGURE_SNAP);
+ p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap...")), MENU_TRANSFORM_CONFIGURE_SNAP);
p->add_separator();
- p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog..")), MENU_TRANSFORM_DIALOG);
+ p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG);
p->connect("id_pressed", this, "_menu_item_pressed");
@@ -5111,12 +5117,12 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) {
accept = memnew(AcceptDialog);
editor->get_gui_base()->add_child(accept);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
p->add_separator();
p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp
index 99aabcb3e..49816fe2a 100644
--- a/editor/plugins/sprite_editor_plugin.cpp
+++ b/editor/plugins/sprite_editor_plugin.cpp
@@ -80,6 +80,10 @@ Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float
void SpriteEditor::_menu_option(int p_option) {
+ if (!node) {
+ return;
+ }
+
switch (p_option) {
case MENU_OPTION_CREATE_MESH_2D: {
@@ -389,6 +393,7 @@ SpriteEditorPlugin::SpriteEditorPlugin(EditorNode *p_node) {
editor = p_node;
sprite_editor = memnew(SpriteEditor);
editor->get_viewport()->add_child(sprite_editor);
+ make_visible(false);
//sprite_editor->options->hide();
}
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index d8d0a6f01..a9afc7a67 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -45,6 +45,12 @@ void SpriteFramesEditor::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
load->set_icon(get_icon("Load", "EditorIcons"));
+ copy->set_icon(get_icon("ActionCopy", "EditorIcons"));
+ paste->set_icon(get_icon("ActionPaste", "EditorIcons"));
+ empty->set_icon(get_icon("InsertBefore", "EditorIcons"));
+ empty2->set_icon(get_icon("InsertAfter", "EditorIcons"));
+ move_up->set_icon(get_icon("MoveLeft", "EditorIcons"));
+ move_down->set_icon(get_icon("MoveRight", "EditorIcons"));
_delete->set_icon(get_icon("Remove", "EditorIcons"));
new_anim->set_icon(get_icon("New", "EditorIcons"));
remove_anim->set_icon(get_icon("Remove", "EditorIcons"));
@@ -458,8 +464,6 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) {
List<StringName> anim_names;
- anim_names.sort_custom<StringName::AlphCompare>();
-
frames->get_animation_list(&anim_names);
anim_names.sort_custom<StringName::AlphCompare>();
@@ -738,27 +742,35 @@ SpriteFramesEditor::SpriteFramesEditor() {
hbc->add_child(load);
copy = memnew(Button);
- copy->set_text(TTR("Copy"));
+ copy->set_flat(true);
+ copy->set_tooltip(TTR("Copy"));
hbc->add_child(copy);
paste = memnew(Button);
- paste->set_text(TTR("Paste"));
+ paste->set_flat(true);
+ paste->set_tooltip(TTR("Paste"));
hbc->add_child(paste);
empty = memnew(Button);
- empty->set_text(TTR("Insert Empty (Before)"));
+ empty->set_flat(true);
+ empty->set_tooltip(TTR("Insert Empty (Before)"));
hbc->add_child(empty);
empty2 = memnew(Button);
- empty2->set_text(TTR("Insert Empty (After)"));
+ empty2->set_flat(true);
+ empty2->set_tooltip(TTR("Insert Empty (After)"));
hbc->add_child(empty2);
+ hbc->add_spacer(false);
+
move_up = memnew(Button);
- move_up->set_text(TTR("Move (Before)"));
+ move_up->set_flat(true);
+ move_up->set_tooltip(TTR("Move (Before)"));
hbc->add_child(move_up);
move_down = memnew(Button);
- move_down->set_text(TTR("Move (After)"));
+ move_down->set_flat(true);
+ move_down->set_tooltip(TTR("Move (After)"));
hbc->add_child(move_down);
_delete = memnew(Button);
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index 4367fe897..5ba393168 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -805,12 +805,10 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) {
snap_mode_button->set_text(TTR("<None>"));
PopupMenu *p = snap_mode_button->get_popup();
p->set_hide_on_checkable_item_selection(false);
- p->add_item(TTR("<None>"), 0);
- p->add_item(TTR("Pixel Snap"), 1);
- p->add_item(TTR("Grid Snap"), 2);
- p->add_item(TTR("Auto Slice"), 3);
- for (int i = 0; i < 4; i++)
- p->set_item_as_checkable(i, true);
+ p->add_radio_check_item(TTR("<None>"), 0);
+ p->add_radio_check_item(TTR("Pixel Snap"), 1);
+ p->add_radio_check_item(TTR("Grid Snap"), 2);
+ p->add_radio_check_item(TTR("Auto Slice"), 3);
p->set_item_checked(0, true);
p->connect("id_pressed", this, "_set_snap_mode");
hb_grid = memnew(HBoxContainer);
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 295d9439a..92b95963f 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -632,7 +632,7 @@ ThemeEditor::ThemeEditor() {
main_vb->add_child(hb_menu);
theme_menu = memnew(MenuButton);
- theme_menu->set_text(TTR("Edit theme.."));
+ theme_menu->set_text(TTR("Edit theme..."));
theme_menu->set_flat(false);
theme_menu->set_tooltip(TTR("Theme editing menu."));
theme_menu->get_popup()->add_item(TTR("Add Item"), POPUP_ADD);
@@ -691,7 +691,11 @@ ThemeEditor::ThemeEditor() {
test_menu_button->get_popup()->add_separator();
test_menu_button->get_popup()->add_check_item(TTR("Check Item"));
test_menu_button->get_popup()->add_check_item(TTR("Checked Item"));
- test_menu_button->get_popup()->set_item_checked(2, true);
+ test_menu_button->get_popup()->set_item_checked(3, true);
+ test_menu_button->get_popup()->add_separator();
+ test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item"));
+ test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item"));
+ test_menu_button->get_popup()->set_item_checked(6, true);
first_vb->add_child(test_menu_button);
OptionButton *test_option_button = memnew(OptionButton);
@@ -740,7 +744,7 @@ ThemeEditor::ThemeEditor() {
item = test_tree->create_item(test_tree->get_root());
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
item->set_editable(0, true);
- item->set_text(0, "check");
+ item->set_text(0, "Check");
item = test_tree->create_item(test_tree->get_root());
item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
item->set_editable(0, true);
@@ -749,7 +753,7 @@ ThemeEditor::ThemeEditor() {
item = test_tree->create_item(test_tree->get_root());
item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
item->set_editable(0, true);
- item->set_text(0, TTR("Have,Many,Several,Options!"));
+ item->set_text(0, TTR("Has,Many,Options"));
item->set_range(0, 2);
VBoxContainer *third_vb = memnew(VBoxContainer);
@@ -780,58 +784,6 @@ ThemeEditor::ThemeEditor() {
main_hb->add_constant_override("separation", 20 * EDSCALE);
- /*
- test_h_scroll = memnew( HScrollBar );
- test_h_scroll->set_position( Point2( 25, 225 ) );
- test_h_scroll->set_size( Point2( 150, 5 ) );
- panel->add_child(test_h_scroll);
-
- line_edit = memnew( LineEdit );
- line_edit->set_position( Point2( 25, 275 ) );
- line_edit->set_size( Point2( 150, 5 ) );
- line_edit->set_text("Line Edit");
- panel->add_child(line_edit);
-
- test_v_scroll = memnew( VScrollBar );
- test_v_scroll->set_position( Point2( 200, 25 ) );
- test_v_scroll->set_size( Point2( 5, 150 ) );
- panel->add_child(test_v_scroll);
-
- test_tree = memnew(Tree);
- test_tree->set_position( Point2( 300, 25 ) );
- test_tree->set_size( Point2( 200, 200 ) );
- panel->add_child(test_tree);
-
-
- TreeItem *item = test_tree->create_item();
- item->set_editable(0,true);
- item->set_text(0,"root");
- item = test_tree->create_item( test_tree->get_root() );
- item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
- item->set_editable(0,true);
- item->set_text(0,"check");
- item = test_tree->create_item( test_tree->get_root() );
- item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
- item->set_editable(0,true);
- item->set_range_config(0,0,20,0.1);
- item->set_range(0,2);
- item = test_tree->create_item( test_tree->get_root() );
- item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE);
- item->set_editable(0,true);
- item->set_text(0,"Have,Many,Several,Options!"));
- item->set_range(0,2);
-
- Button *fd_button= memnew( Button );
- fd_button->set_position(Point2(300,275));
- fd_button->set_text("Open File Dialog");
- panel->add_child(fd_button);
-
- test_file_dialog = memnew( EditorFileDialog );
- panel->add_child(test_file_dialog);
-
- fd_button->connect("pressed", this,"_open_file_dialog");
-*/
-
add_del_dialog = memnew(ConfirmationDialog);
add_del_dialog->hide();
add_child(add_del_dialog);
diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp
index c5c7272ed..72b3af5a0 100644
--- a/editor/plugins/tile_map_editor_plugin.cpp
+++ b/editor/plugins/tile_map_editor_plugin.cpp
@@ -78,6 +78,7 @@ void TileMapEditor::_notification(int p_what) {
p->set_item_icon(p->get_item_index(OPTION_PAINTING), get_icon("Edit", "EditorIcons"));
p->set_item_icon(p->get_item_index(OPTION_PICK_TILE), get_icon("ColorPick", "EditorIcons"));
p->set_item_icon(p->get_item_index(OPTION_SELECT), get_icon("ToolSelect", "EditorIcons"));
+ p->set_item_icon(p->get_item_index(OPTION_MOVE), get_icon("ToolMove", "EditorIcons"));
p->set_item_icon(p->get_item_index(OPTION_DUPLICATE), get_icon("Duplicate", "EditorIcons"));
p->set_item_icon(p->get_item_index(OPTION_ERASE_SELECTION), get_icon("Remove", "EditorIcons"));
@@ -156,6 +157,14 @@ void TileMapEditor::_menu_option(int p_option) {
undo_redo->commit_action();
} break;
+ case OPTION_MOVE: {
+
+ if (selection_active) {
+ _update_copydata();
+ tool = TOOL_MOVING;
+ canvas_item_editor->update();
+ }
+ } break;
}
}
@@ -715,7 +724,11 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (mb->get_shift()) {
+#ifdef APPLE_STYLE_KEYS
+ if (mb->get_command())
+#else
if (mb->get_control())
+#endif
tool = TOOL_RECTANGLE_PAINT;
else
tool = TOOL_LINE_PAINT;
@@ -725,9 +738,11 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return true;
}
-
+#ifdef APPLE_STYLE_KEYS
+ if (mb->get_command()) {
+#else
if (mb->get_control()) {
-
+#endif
tool = TOOL_PICKING;
_pick_tile(over_tile);
@@ -829,6 +844,29 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
copydata.clear();
canvas_item_editor->update();
+ } else if (tool == TOOL_MOVING) {
+
+ Point2 ofs = over_tile - rectangle.position;
+
+ undo_redo->create_action(TTR("Move"));
+ undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
+ for (int i = rectangle.position.y; i <= rectangle.position.y + rectangle.size.y; i++) {
+ for (int j = rectangle.position.x; j <= rectangle.position.x + rectangle.size.x; j++) {
+
+ _set_cell(Point2i(j, i), TileMap::INVALID_CELL, false, false, false);
+ }
+ }
+ for (List<TileData>::Element *E = copydata.front(); E; E = E->next()) {
+
+ _set_cell(E->get().pos + ofs, E->get().cell, E->get().flip_h, E->get().flip_v, E->get().transpose);
+ }
+ undo_redo->add_do_method(node, "set", "tile_data", node->get("tile_data"));
+ undo_redo->commit_action();
+
+ copydata.clear();
+ selection_active = false;
+
+ canvas_item_editor->update();
} else if (tool == TOOL_SELECTING) {
@@ -888,6 +926,16 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return true;
}
+ if (tool == TOOL_MOVING) {
+
+ tool = TOOL_NONE;
+ copydata.clear();
+
+ canvas_item_editor->update();
+
+ return true;
+ }
+
if (tool == TOOL_NONE) {
paint_undo.clear();
@@ -898,8 +946,11 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
undo_redo->add_undo_method(node, "set", "tile_data", node->get("tile_data"));
if (mb->get_shift()) {
-
+#ifdef APPLE_STYLE_KEYS
+ if (mb->get_command())
+#else
if (mb->get_control())
+#endif
tool = TOOL_RECTANGLE_ERASE;
else
tool = TOOL_LINE_ERASE;
@@ -1095,7 +1146,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (k->get_scancode() == KEY_ESCAPE) {
- if (tool == TOOL_DUPLICATING)
+ if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING)
copydata.clear();
else if (tool == TOOL_SELECTING || selection_active)
selection_active = false;
@@ -1150,6 +1201,14 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
return true;
}
}
+ if (ED_IS_SHORTCUT("tile_map_editor/move_selection", p_event)) {
+ if (selection_active) {
+ _update_copydata();
+ tool = TOOL_MOVING;
+ canvas_item_editor->update();
+ return true;
+ }
+ }
if (ED_IS_SHORTCUT("tile_map_editor/find_tile", p_event)) {
search_box->select_all();
search_box->grab_focus();
@@ -1159,18 +1218,21 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
if (ED_IS_SHORTCUT("tile_map_editor/mirror_x", p_event)) {
flip_h = !flip_h;
mirror_x->set_pressed(flip_h);
+ _update_transform_buttons();
canvas_item_editor->update();
return true;
}
if (ED_IS_SHORTCUT("tile_map_editor/mirror_y", p_event)) {
flip_v = !flip_v;
mirror_y->set_pressed(flip_v);
+ _update_transform_buttons();
canvas_item_editor->update();
return true;
}
if (ED_IS_SHORTCUT("tile_map_editor/transpose", p_event)) {
transpose = !transpose;
transp->set_pressed(transpose);
+ _update_transform_buttons();
canvas_item_editor->update();
return true;
}
@@ -1343,7 +1405,7 @@ void TileMapEditor::forward_draw_over_viewport(Control *p_overlay) {
_draw_cell(id, Point2i(j, i), flip_h, flip_v, transpose, xform);
}
}
- } else if (tool == TOOL_DUPLICATING) {
+ } else if (tool == TOOL_DUPLICATING || tool == TOOL_MOVING) {
if (copydata.empty())
return;
@@ -1590,6 +1652,7 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) {
p->add_item(TTR("Pick Tile"), OPTION_PICK_TILE, KEY_CONTROL);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B), OPTION_SELECT);
+ p->add_shortcut(ED_SHORTCUT("tile_map_editor/move_selection", TTR("Move Selection"), KEY_MASK_CMD + KEY_M), OPTION_MOVE);
p->add_shortcut(ED_SHORTCUT("tile_map_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_CMD + KEY_D), OPTION_DUPLICATE);
p->add_shortcut(ED_GET_SHORTCUT("tile_map_editor/erase_selection"), OPTION_ERASE_SELECTION);
p->add_separator();
diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h
index 2d582d030..642870aec 100644
--- a/editor/plugins/tile_map_editor_plugin.h
+++ b/editor/plugins/tile_map_editor_plugin.h
@@ -61,6 +61,7 @@ class TileMapEditor : public VBoxContainer {
TOOL_BUCKET,
TOOL_PICKING,
TOOL_DUPLICATING,
+ TOOL_MOVING
};
enum Options {
@@ -72,6 +73,7 @@ class TileMapEditor : public VBoxContainer {
OPTION_ERASE_SELECTION,
OPTION_PAINTING,
OPTION_FIX_INVALID,
+ OPTION_MOVE
};
TileMap *node;
@@ -123,12 +125,11 @@ class TileMapEditor : public VBoxContainer {
bool yf;
bool tr;
- CellOp() {
- idx = -1;
- xf = false;
- yf = false;
- tr = false;
- }
+ CellOp() :
+ idx(TileMap::INVALID_CELL),
+ xf(false),
+ yf(false),
+ tr(false) {}
};
Map<Point2i, CellOp> paint_undo;
@@ -139,8 +140,12 @@ class TileMapEditor : public VBoxContainer {
bool flip_h;
bool flip_v;
bool transpose;
- int auto_x;
- int auto_y;
+
+ TileData() :
+ cell(TileMap::INVALID_CELL),
+ flip_h(false),
+ flip_v(false),
+ transpose(false) {}
};
List<TileData> copydata;
diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp
index 231143972..385fa24ad 100644
--- a/editor/plugins/tile_set_editor_plugin.cpp
+++ b/editor/plugins/tile_set_editor_plugin.cpp
@@ -149,6 +149,7 @@ void TileSetEditor::_import_node(Node *p_node, Ref<TileSet> p_library) {
p_library->tile_set_light_occluder(id, occluder);
p_library->tile_set_occluder_offset(id, -phys_offset);
p_library->tile_set_navigation_polygon_offset(id, -phys_offset);
+ p_library->tile_set_z_index(id, mi->get_z_index());
}
}
@@ -278,10 +279,11 @@ void TileSetEditor::_changed_callback(Object *p_changed, const char *p_prop) {
preview->set_region_rect(tileset->tile_get_region(get_current_tile()));
} else if (p_prop == StringName("name")) {
update_tile_list_icon();
- } else if (p_prop == StringName("texture") || p_prop == StringName("tile_mode")) {
+ } else if (p_prop == StringName("texture") || p_prop == StringName("modulate") || p_prop == StringName("tile_mode")) {
_on_tile_list_selected(get_current_tile());
workspace->update();
preview->set_texture(tileset->tile_get_texture(get_current_tile()));
+ preview->set_modulate(tileset->tile_get_modulate(get_current_tile()));
preview->set_region_rect(tileset->tile_get_region(get_current_tile()));
if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::AUTO_TILE)
property_editor->show();
@@ -578,6 +580,7 @@ void TileSetEditor::_on_tile_list_selected(int p_index) {
if (get_current_tile() >= 0) {
current_item_index = p_index;
preview->set_texture(tileset->tile_get_texture(get_current_tile()));
+ preview->set_modulate(tileset->tile_get_modulate(get_current_tile()));
preview->set_region_rect(tileset->tile_get_region(get_current_tile()));
workspace->set_custom_minimum_size(tileset->tile_get_region(get_current_tile()).size);
update_workspace_tile_mode();
@@ -803,7 +806,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
Vector2 coord((int)(mb->get_position().x / (spacing + size.x)), (int)(mb->get_position().y / (spacing + size.y)));
Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
pos = mb->get_position() - pos;
- uint16_t bit;
+ uint16_t bit = 0;
if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
if (pos.x < size.x / 2) {
if (pos.y < size.y / 2) {
@@ -866,7 +869,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) {
Vector2 coord((int)(mm->get_position().x / (spacing + size.x)), (int)(mm->get_position().y / (spacing + size.y)));
Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y));
pos = mm->get_position() - pos;
- uint16_t bit;
+ uint16_t bit = 0;
if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) {
if (pos.x < size.x / 2) {
if (pos.y < size.y / 2) {
@@ -1144,7 +1147,7 @@ void TileSetEditor::_on_tool_clicked(int p_tool) {
case EDITMODE_COLLISION: {
if (!edited_collision_shape.is_null()) {
Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile());
- int index;
+ int index = -1;
for (int i = 0; i < sd.size(); i++) {
if (sd[i].shape == edited_collision_shape) {
index = i;
@@ -1736,6 +1739,7 @@ void TileSetEditor::update_tile_list() {
region.position += pos;
}
tile_list->set_item_icon_region(tile_list->get_item_count() - 1, region);
+ tile_list->set_item_icon_modulate(tile_list->get_item_count() - 1, tileset->tile_get_modulate(E->get()));
}
if (tile_list->get_item_count() > 0 && selected_tile < tile_list->get_item_count()) {
tile_list->select(selected_tile);
@@ -1763,6 +1767,7 @@ void TileSetEditor::update_tile_list_icon() {
tile_list->set_item_metadata(current_idx, E->get());
tile_list->set_item_icon(current_idx, tileset->tile_get_texture(E->get()));
tile_list->set_item_icon_region(current_idx, region);
+ tile_list->set_item_icon_modulate(current_idx, tileset->tile_get_modulate(E->get()));
tile_list->set_item_text(current_idx, tileset->tile_get_name(E->get()));
current_idx += 1;
}
diff --git a/editor/project_export.cpp b/editor/project_export.cpp
index 4dbd9d500..9f87fc82b 100644
--- a/editor/project_export.cpp
+++ b/editor/project_export.cpp
@@ -81,7 +81,14 @@ void ProjectExportDialog::popup_export() {
if (EditorSettings::get_singleton()->has_setting("interface/dialogs/export_bounds")) {
popup(EditorSettings::get_singleton()->get("interface/dialogs/export_bounds"));
} else {
- popup_centered_ratio();
+
+ Size2 popup_size = Size2(900, 700) * editor_get_scale();
+ Size2 window_size = get_viewport_rect().size;
+
+ popup_size.x = MIN(window_size.x * 0.8, popup_size.x);
+ popup_size.y = MIN(window_size.y * 0.8, popup_size.y);
+
+ popup_centered(popup_size);
}
}
@@ -207,9 +214,9 @@ void ProjectExportDialog::_edit_preset(int p_index) {
TreeItem *patch_add = patches->create_item(patch_root);
patch_add->set_metadata(0, patchlist.size());
if (patchlist.size() == 0)
- patch_add->set_text(0, "Add initial export..");
+ patch_add->set_text(0, "Add initial export...");
else
- patch_add->set_text(0, "Add previous patches..");
+ patch_add->set_text(0, "Add previous patches...");
patch_add->add_button(0, get_icon("folder", "FileDialog"), 1);
@@ -703,9 +710,9 @@ void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) {
ERR_FAIL_COND(platform.is_null());
if (p_path.ends_with(".zip")) {
- platform->save_zip(current, p_path);
+ platform->export_zip(current, export_pck_zip_debug->is_pressed(), p_path);
} else if (p_path.ends_with(".pck")) {
- platform->save_pack(current, p_path);
+ platform->export_pack(current, export_pck_zip_debug->is_pressed(), p_path);
}
}
@@ -803,7 +810,7 @@ ProjectExportDialog::ProjectExportDialog() {
preset_vb->add_child(preset_hb);
add_preset = memnew(MenuButton);
- add_preset->set_text(TTR("Add.."));
+ add_preset->set_text(TTR("Add..."));
add_preset->get_popup()->connect("index_pressed", this, "_add_preset");
preset_hb->add_child(add_preset);
MarginContainer *mc = memnew(MarginContainer);
@@ -981,11 +988,20 @@ ProjectExportDialog::ProjectExportDialog() {
export_debug->set_pressed(true);
export_project->get_vbox()->add_child(export_debug);
+ export_pck_zip_debug = memnew(CheckButton);
+ export_pck_zip_debug->set_text(TTR("Export With Debug"));
+ export_pck_zip_debug->set_pressed(true);
+ export_pck_zip->get_vbox()->add_child(export_pck_zip_debug);
+
set_hide_on_ok(false);
editor_icons = "EditorIcons";
default_filename = EditorSettings::get_singleton()->get_project_metadata("export_options", "default_filename", String());
+
+ if (default_filename == "") {
+ default_filename = ProjectSettings::get_singleton()->get("application/config/name");
+ }
}
ProjectExportDialog::~ProjectExportDialog() {
diff --git a/editor/project_export.h b/editor/project_export.h
index 6c7474376..b62254974 100644
--- a/editor/project_export.h
+++ b/editor/project_export.h
@@ -131,6 +131,7 @@ private:
FileDialog *export_pck_zip;
FileDialog *export_project;
CheckButton *export_debug;
+ CheckButton *export_pck_zip_debug;
void _open_export_template_manager();
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index de98c89db..666f08cb2 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -250,8 +250,10 @@ private:
void _create_folder() {
- if (project_name->get_text() == "" || created_folder_path != "")
+ if (project_name->get_text() == "" || created_folder_path != "" || project_name->get_text().ends_with(".") || project_name->get_text().ends_with(" ")) {
+ set_message(TTR("Invalid Project Name."), MESSAGE_WARNING);
return;
+ }
DirAccess *d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (d->change_dir(project_path->get_text()) == OK) {
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 75523cd84..a4265b4e3 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -31,6 +31,7 @@
#include "project_settings_editor.h"
#include "core/global_constants.h"
+#include "core/input_map.h"
#include "core/os/keyboard.h"
#include "core/project_settings.h"
#include "core/translation.h"
@@ -159,7 +160,7 @@ void ProjectSettingsEditor::_action_edited() {
ti->set_text(0, old_name);
add_at = "input/" + old_name;
- message->set_text(TTR("Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or '\"'"));
+ message->set_text(TTR("Invalid action name. It cannot be empty nor contain '/', ':', '=', '\\', or '\"'."));
message->popup_centered(Size2(300, 100) * EDSCALE);
return;
}
@@ -177,15 +178,15 @@ void ProjectSettingsEditor::_action_edited() {
}
int order = ProjectSettings::get_singleton()->get_order(add_at);
- Array va = ProjectSettings::get_singleton()->get(add_at);
+ Dictionary action = ProjectSettings::get_singleton()->get(add_at);
setting = true;
undo_redo->create_action(TTR("Rename Input Action Event"));
undo_redo->add_do_method(ProjectSettings::get_singleton(), "clear", add_at);
- undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", action_prop, va);
+ undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", action_prop, action);
undo_redo->add_do_method(ProjectSettings::get_singleton(), "set_order", action_prop, order);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", action_prop);
- undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", add_at, va);
+ undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", add_at, action);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set_order", add_at, order);
undo_redo->add_do_method(this, "_update_actions");
undo_redo->add_undo_method(this, "_update_actions");
@@ -202,8 +203,9 @@ void ProjectSettingsEditor::_device_input_add() {
Ref<InputEvent> ie;
String name = add_at;
int idx = edit_idx;
- Array old_val = ProjectSettings::get_singleton()->get(name);
- Array arr = old_val.duplicate();
+ Dictionary old_val = ProjectSettings::get_singleton()->get(name);
+ Dictionary action = old_val.duplicate();
+ Array events = action["events"];
switch (add_type) {
@@ -212,11 +214,11 @@ void ProjectSettingsEditor::_device_input_add() {
Ref<InputEventMouseButton> mb;
mb.instance();
mb->set_button_index(device_index->get_selected() + 1);
- mb->set_device(device_id->get_value());
+ mb->set_device(_get_current_device());
- for (int i = 0; i < arr.size(); i++) {
+ for (int i = 0; i < events.size(); i++) {
- Ref<InputEventMouseButton> aie = arr[i];
+ Ref<InputEventMouseButton> aie = events[i];
if (aie.is_null())
continue;
if (aie->get_device() == mb->get_device() && aie->get_button_index() == mb->get_button_index()) {
@@ -233,19 +235,26 @@ void ProjectSettingsEditor::_device_input_add() {
jm.instance();
jm->set_axis(device_index->get_selected() >> 1);
jm->set_axis_value(device_index->get_selected() & 1 ? 1 : -1);
- jm->set_device(device_id->get_value());
+ jm->set_device(_get_current_device());
- for (int i = 0; i < arr.size(); i++) {
+ bool should_update_event = true;
+ Variant deadzone = device_special_value->get_value();
+ for (int i = 0; i < events.size(); i++) {
- Ref<InputEventJoypadMotion> aie = arr[i];
+ Ref<InputEventJoypadMotion> aie = events[i];
if (aie.is_null())
continue;
if (aie->get_device() == jm->get_device() && aie->get_axis() == jm->get_axis() && aie->get_axis_value() == jm->get_axis_value()) {
- return;
+ should_update_event = false;
+ break;
}
}
+ if (!should_update_event && deadzone == action["deadzone"])
+ return;
+
ie = jm;
+ action["deadzone"] = deadzone;
} break;
case INPUT_JOY_BUTTON: {
@@ -254,11 +263,11 @@ void ProjectSettingsEditor::_device_input_add() {
jb.instance();
jb->set_button_index(device_index->get_selected());
- jb->set_device(device_id->get_value());
+ jb->set_device(_get_current_device());
- for (int i = 0; i < arr.size(); i++) {
+ for (int i = 0; i < events.size(); i++) {
- Ref<InputEventJoypadButton> aie = arr[i];
+ Ref<InputEventJoypadButton> aie = events[i];
if (aie.is_null())
continue;
if (aie->get_device() == jb->get_device() && aie->get_button_index() == jb->get_button_index()) {
@@ -271,14 +280,15 @@ void ProjectSettingsEditor::_device_input_add() {
default: {}
}
- if (idx < 0 || idx >= arr.size()) {
- arr.push_back(ie);
+ if (idx < 0 || idx >= events.size()) {
+ events.push_back(ie);
} else {
- arr[idx] = ie;
+ events[idx] = ie;
}
+ action["events"] = events;
undo_redo->create_action(TTR("Add Input Action Event"));
- undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, arr);
+ undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, old_val);
undo_redo->add_do_method(this, "_update_actions");
undo_redo->add_undo_method(this, "_update_actions");
@@ -289,6 +299,20 @@ void ProjectSettingsEditor::_device_input_add() {
_show_last_added(ie, name);
}
+void ProjectSettingsEditor::_set_current_device(int i_device) {
+ device_id->select(i_device + 1);
+}
+
+int ProjectSettingsEditor::_get_current_device() {
+ return device_id->get_selected() - 1;
+}
+
+String ProjectSettingsEditor::_get_device_string(int i_device) {
+ if (i_device == InputMap::ALL_DEVICES)
+ return TTR("All Devices");
+ return TTR("Device") + " " + itos(i_device);
+}
+
void ProjectSettingsEditor::_press_a_key_confirm() {
if (last_wait_for_key.is_null())
@@ -305,12 +329,13 @@ void ProjectSettingsEditor::_press_a_key_confirm() {
String name = add_at;
int idx = edit_idx;
- Array old_val = ProjectSettings::get_singleton()->get(name);
- Array arr = old_val.duplicate();
+ Dictionary old_val = ProjectSettings::get_singleton()->get(name);
+ Dictionary action = old_val.duplicate();
+ Array events = action["events"];
- for (int i = 0; i < arr.size(); i++) {
+ for (int i = 0; i < events.size(); i++) {
- Ref<InputEventKey> aie = arr[i];
+ Ref<InputEventKey> aie = events[i];
if (aie.is_null())
continue;
if (aie->get_scancode_with_modifiers() == ie->get_scancode_with_modifiers()) {
@@ -318,14 +343,15 @@ void ProjectSettingsEditor::_press_a_key_confirm() {
}
}
- if (idx < 0 || idx >= arr.size()) {
- arr.push_back(ie);
+ if (idx < 0 || idx >= events.size()) {
+ events.push_back(ie);
} else {
- arr[idx] = ie;
+ events[idx] = ie;
}
+ action["events"] = events;
undo_redo->create_action(TTR("Add Input Action Event"));
- undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, arr);
+ undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, old_val);
undo_redo->add_do_method(this, "_update_actions");
undo_redo->add_undo_method(this, "_update_actions");
@@ -399,10 +425,13 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
case INPUT_KEY: {
- press_a_key_label->set_text(TTR("Press a Key.."));
+ press_a_key_label->set_text(TTR("Press a Key..."));
last_wait_for_key = Ref<InputEvent>();
press_a_key->popup_centered(Size2(250, 80) * EDSCALE);
press_a_key->grab_focus();
+
+ device_special_value_label->hide();
+ device_special_value->hide();
} break;
case INPUT_MOUSE_BUTTON: {
@@ -422,12 +451,15 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
Ref<InputEventMouseButton> mb = p_exiting_event;
if (mb.is_valid()) {
device_index->select(mb->get_button_index() - 1);
- device_id->set_value(mb->get_device());
+ _set_current_device(mb->get_device());
device_input->get_ok()->set_text(TTR("Change"));
} else {
- device_id->set_value(0);
+ _set_current_device(0);
device_input->get_ok()->set_text(TTR("Add"));
}
+
+ device_special_value_label->hide();
+ device_special_value->hide();
} break;
case INPUT_JOY_MOTION: {
@@ -443,12 +475,21 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
Ref<InputEventJoypadMotion> jm = p_exiting_event;
if (jm.is_valid()) {
device_index->select(jm->get_axis() * 2 + (jm->get_axis_value() > 0 ? 1 : 0));
- device_id->set_value(jm->get_device());
+ _set_current_device(jm->get_device());
device_input->get_ok()->set_text(TTR("Change"));
} else {
- device_id->set_value(0);
+ _set_current_device(0);
device_input->get_ok()->set_text(TTR("Add"));
}
+
+ device_special_value_label->set_text(TTR("Deadzone (global to the action):"));
+ device_special_value_label->show();
+ device_special_value->set_min(0.0f);
+ device_special_value->set_max(1.0f);
+ device_special_value->set_step(0.01f);
+ Dictionary action = ProjectSettings::get_singleton()->get(add_at);
+ device_special_value->set_value(action.has("deadzone") ? action["deadzone"] : Variant(0.5f));
+ device_special_value->show();
} break;
case INPUT_JOY_BUTTON: {
@@ -464,13 +505,15 @@ void ProjectSettingsEditor::_add_item(int p_item, Ref<InputEvent> p_exiting_even
Ref<InputEventJoypadButton> jb = p_exiting_event;
if (jb.is_valid()) {
device_index->select(jb->get_button_index());
- device_id->set_value(jb->get_device());
+ _set_current_device(jb->get_device());
device_input->get_ok()->set_text(TTR("Change"));
} else {
- device_id->set_value(0);
+ _set_current_device(0);
device_input->get_ok()->set_text(TTR("Add"));
}
+ device_special_value_label->hide();
+ device_special_value->hide();
} break;
default: {}
}
@@ -507,18 +550,17 @@ void ProjectSettingsEditor::_action_activated() {
String name = "input/" + ti->get_parent()->get_text(0);
int idx = ti->get_metadata(0);
- Array va = ProjectSettings::get_singleton()->get(name);
+ Dictionary action = ProjectSettings::get_singleton()->get(name);
+ Array events = action["events"];
- ERR_FAIL_INDEX(idx, va.size());
-
- Ref<InputEvent> ie = va[idx];
-
- if (ie.is_null())
+ ERR_FAIL_INDEX(idx, events.size());
+ Ref<InputEvent> event = events[idx];
+ if (event.is_null())
return;
add_at = name;
edit_idx = idx;
- _edit_item(ie);
+ _edit_item(event);
}
void ProjectSettingsEditor::_action_button_pressed(Object *p_obj, int p_column, int p_id) {
@@ -528,6 +570,7 @@ void ProjectSettingsEditor::_action_button_pressed(Object *p_obj, int p_column,
ERR_FAIL_COND(!ti);
if (p_id == 1) {
+ // Add action event
Point2 ofs = input_editor->get_global_position();
Rect2 ir = input_editor->get_item_rect(ti);
ir.position.y -= input_editor->get_scroll().y;
@@ -539,14 +582,12 @@ void ProjectSettingsEditor::_action_button_pressed(Object *p_obj, int p_column,
edit_idx = -1;
} else if (p_id == 2) {
- //remove
+ // Remove
if (ti->get_parent() == input_editor->get_root()) {
-
- //remove main thing
-
+ // Remove action
String name = "input/" + ti->get_text(0);
- Variant old_val = ProjectSettings::get_singleton()->get(name);
+ Dictionary old_val = ProjectSettings::get_singleton()->get(name);
int order = ProjectSettings::get_singleton()->get_order(name);
undo_redo->create_action(TTR("Erase Input Action"));
@@ -560,24 +601,19 @@ void ProjectSettingsEditor::_action_button_pressed(Object *p_obj, int p_column,
undo_redo->commit_action();
} else {
- //remove action
+ // Remove action event
String name = "input/" + ti->get_parent()->get_text(0);
- Variant old_val = ProjectSettings::get_singleton()->get(name);
+ Dictionary old_val = ProjectSettings::get_singleton()->get(name);
+ Dictionary action = old_val.duplicate();
int idx = ti->get_metadata(0);
- Array va = old_val;
-
- ERR_FAIL_INDEX(idx, va.size());
-
- for (int i = idx; i < va.size() - 1; i++) {
-
- va[i] = va[i + 1];
- }
-
- va.resize(va.size() - 1);
+ Array events = action["events"];
+ ERR_FAIL_INDEX(idx, events.size());
+ events.remove(idx);
+ action["events"] = events;
undo_redo->create_action(TTR("Erase Input Action Event"));
- undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, va);
+ undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "set", name, old_val);
undo_redo->add_do_method(this, "_update_actions");
undo_redo->add_undo_method(this, "_update_actions");
@@ -586,30 +622,31 @@ void ProjectSettingsEditor::_action_button_pressed(Object *p_obj, int p_column,
undo_redo->commit_action();
}
} else if (p_id == 3) {
- //edit
+ // Edit
if (ti->get_parent() == input_editor->get_root()) {
-
+ // Edit action name
ti->set_as_cursor(0);
input_editor->edit_selected();
} else {
- //edit action
+ // Edit action event
String name = "input/" + ti->get_parent()->get_text(0);
int idx = ti->get_metadata(0);
- Array va = ProjectSettings::get_singleton()->get(name);
+ Dictionary action = ProjectSettings::get_singleton()->get(name);
- ERR_FAIL_INDEX(idx, va.size());
+ Array events = action["events"];
+ ERR_FAIL_INDEX(idx, events.size());
- Ref<InputEvent> ie = va[idx];
+ Ref<InputEvent> event = events[idx];
- if (ie.is_null())
+ if (event.is_null())
return;
ti->set_as_cursor(0);
add_at = name;
edit_idx = idx;
- _edit_item(ie);
+ _edit_item(event);
}
}
}
@@ -645,17 +682,18 @@ void ProjectSettingsEditor::_update_actions() {
}
item->set_custom_bg_color(0, get_color("prop_subsection", "Editor"));
- Array actions = ProjectSettings::get_singleton()->get(pi.name);
+ Dictionary action = ProjectSettings::get_singleton()->get(pi.name);
+ Array events = action["events"];
- for (int i = 0; i < actions.size(); i++) {
+ for (int i = 0; i < events.size(); i++) {
- Ref<InputEvent> ie = actions[i];
- if (ie.is_null())
+ Ref<InputEvent> event = events[i];
+ if (event.is_null())
continue;
TreeItem *action = input_editor->create_item(item);
- Ref<InputEventKey> k = ie;
+ Ref<InputEventKey> k = event;
if (k.is_valid()) {
String str = keycode_get_string(k->get_scancode()).capitalize();
@@ -672,11 +710,11 @@ void ProjectSettingsEditor::_update_actions() {
action->set_icon(0, get_icon("Keyboard", "EditorIcons"));
}
- Ref<InputEventJoypadButton> jb = ie;
+ Ref<InputEventJoypadButton> jb = event;
if (jb.is_valid()) {
- String str = TTR("Device") + " " + itos(jb->get_device()) + ", " + TTR("Button") + " " + itos(jb->get_button_index());
+ String str = _get_device_string(jb->get_device()) + ", " + TTR("Button") + " " + itos(jb->get_button_index());
if (jb->get_button_index() >= 0 && jb->get_button_index() < JOY_BUTTON_MAX)
str += String() + " (" + _button_names[jb->get_button_index()] + ").";
else
@@ -686,10 +724,10 @@ void ProjectSettingsEditor::_update_actions() {
action->set_icon(0, get_icon("JoyButton", "EditorIcons"));
}
- Ref<InputEventMouseButton> mb = ie;
+ Ref<InputEventMouseButton> mb = event;
if (mb.is_valid()) {
- String str = TTR("Device") + " " + itos(mb->get_device()) + ", ";
+ String str = _get_device_string(mb->get_device()) + ", ";
switch (mb->get_button_index()) {
case BUTTON_LEFT: str += TTR("Left Button."); break;
case BUTTON_RIGHT: str += TTR("Right Button."); break;
@@ -703,21 +741,21 @@ void ProjectSettingsEditor::_update_actions() {
action->set_icon(0, get_icon("Mouse", "EditorIcons"));
}
- Ref<InputEventJoypadMotion> jm = ie;
+ Ref<InputEventJoypadMotion> jm = event;
if (jm.is_valid()) {
int ax = jm->get_axis();
int n = 2 * ax + (jm->get_axis_value() < 0 ? 0 : 1);
String desc = _axis_names[n];
- String str = TTR("Device") + " " + itos(jm->get_device()) + ", " + TTR("Axis") + " " + itos(ax) + " " + (jm->get_axis_value() < 0 ? "-" : "+") + desc + ".";
+ String str = _get_device_string(jm->get_device()) + ", " + TTR("Axis") + " " + itos(ax) + " " + (jm->get_axis_value() < 0 ? "-" : "+") + desc + ".";
action->set_text(0, str);
action->set_icon(0, get_icon("JoyAxis", "EditorIcons"));
}
action->add_button(0, get_icon("Edit", "EditorIcons"), 3, false, TTR("Edit"));
action->add_button(0, get_icon("Remove", "EditorIcons"), 2, false, TTR("Remove"));
action->set_metadata(0, i);
- action->set_meta("__input", ie);
+ action->set_meta("__input", event);
}
}
@@ -730,7 +768,14 @@ void ProjectSettingsEditor::popup_project_settings() {
if (EditorSettings::get_singleton()->has_setting("interface/dialogs/project_settings_bounds")) {
popup(EditorSettings::get_singleton()->get("interface/dialogs/project_settings_bounds"));
} else {
- popup_centered_ratio();
+
+ Size2 popup_size = Size2(900, 700) * editor_get_scale();
+ Size2 window_size = get_viewport_rect().size;
+
+ popup_size.x = MIN(window_size.x * 0.8, popup_size.x);
+ popup_size.y = MIN(window_size.y * 0.8, popup_size.y);
+
+ popup_centered(popup_size);
}
globals_editor->update_category_list();
_update_translations();
@@ -877,10 +922,12 @@ void ProjectSettingsEditor::_action_adds(String) {
void ProjectSettingsEditor::_action_add() {
- Array va;
+ Dictionary action;
+ action["events"] = Array();
+ action["deadzone"] = 0.5f;
String name = "input/" + action_name->get_text();
undo_redo->create_action(TTR("Add Input Action"));
- undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, va);
+ undo_redo->add_do_method(ProjectSettings::get_singleton(), "set", name, action);
undo_redo->add_undo_method(ProjectSettings::get_singleton(), "clear", name);
undo_redo->add_do_method(this, "_update_actions");
undo_redo->add_undo_method(this, "_update_actions");
@@ -1692,7 +1739,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
add_prop_bar->add_child(memnew(VSeparator));
popup_copy_to_feature = memnew(MenuButton);
- popup_copy_to_feature->set_text(TTR("Override For.."));
+ popup_copy_to_feature->set_text(TTR("Override For..."));
popup_copy_to_feature->set_disabled(true);
add_prop_bar->add_child(popup_copy_to_feature);
@@ -1756,7 +1803,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
add_child(press_a_key);
l = memnew(Label);
- l->set_text(TTR("Press a Key.."));
+ l->set_text(TTR("Press a Key..."));
l->set_anchors_and_margins_preset(Control::PRESET_WIDE);
l->set_align(Label::ALIGN_CENTER);
l->set_margin(MARGIN_TOP, 20);
@@ -1781,8 +1828,10 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
l->set_text(TTR("Device:"));
vbc_left->add_child(l);
- device_id = memnew(SpinBox);
- device_id->set_value(0);
+ device_id = memnew(OptionButton);
+ for (int i = -1; i < 8; i++)
+ device_id->add_item(_get_device_string(i));
+ _set_current_device(0);
vbc_left->add_child(device_id);
VBoxContainer *vbc_right = memnew(VBoxContainer);
@@ -1797,6 +1846,14 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
device_index = memnew(OptionButton);
vbc_right->add_child(device_index);
+ l = memnew(Label);
+ l->set_text(TTR("Special value:"));
+ vbc_right->add_child(l);
+ device_special_value_label = l;
+
+ device_special_value = memnew(SpinBox);
+ vbc_right->add_child(device_special_value);
+
setting = false;
//translations
@@ -1817,7 +1874,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
tvb->add_child(thb);
thb->add_child(memnew(Label(TTR("Translations:"))));
thb->add_spacer();
- Button *addtr = memnew(Button(TTR("Add..")));
+ Button *addtr = memnew(Button(TTR("Add...")));
addtr->connect("pressed", this, "_translation_file_open");
thb->add_child(addtr);
VBoxContainer *tmc = memnew(VBoxContainer);
@@ -1841,7 +1898,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
tvb->add_child(thb);
thb->add_child(memnew(Label(TTR("Resources:"))));
thb->add_spacer();
- Button *addtr = memnew(Button(TTR("Add..")));
+ Button *addtr = memnew(Button(TTR("Add...")));
addtr->connect("pressed", this, "_translation_res_file_open");
thb->add_child(addtr);
VBoxContainer *tmc = memnew(VBoxContainer);
@@ -1862,7 +1919,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
tvb->add_child(thb);
thb->add_child(memnew(Label(TTR("Remaps by Locale:"))));
thb->add_spacer();
- addtr = memnew(Button(TTR("Add..")));
+ addtr = memnew(Button(TTR("Add...")));
addtr->connect("pressed", this, "_translation_res_option_file_open");
translation_res_option_add_button = addtr;
thb->add_child(addtr);
diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h
index 9364b0ff2..b8bfdcd87 100644
--- a/editor/project_settings_editor.h
+++ b/editor/project_settings_editor.h
@@ -80,9 +80,11 @@ class ProjectSettingsEditor : public AcceptDialog {
ConfirmationDialog *press_a_key;
Label *press_a_key_label;
ConfirmationDialog *device_input;
- SpinBox *device_id;
+ OptionButton *device_id;
OptionButton *device_index;
Label *device_index_label;
+ SpinBox *device_special_value;
+ Label *device_special_value_label;
MenuButton *popup_copy_to_feature;
LineEdit *action_name;
@@ -170,6 +172,10 @@ protected:
void _notification(int p_what);
static void _bind_methods();
+ int _get_current_device();
+ void _set_current_device(int i_device);
+ String _get_device_string(int i_device);
+
public:
void add_translation(const String &p_translation);
static ProjectSettingsEditor *get_singleton() { return singleton; }
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index 043add046..e0063925b 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -285,6 +285,11 @@ void CustomPropertyEditor::_menu_option(int p_which) {
}
Object *obj = ClassDB::instance(intype);
+
+ if (!obj) {
+ obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+ }
+
ERR_BREAK(!obj);
Resource *res = Object::cast_to<Resource>(obj);
ERR_BREAK(!res);
@@ -481,7 +486,7 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
type_button->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -3 * EDSCALE);
type_button->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -25 * EDSCALE);
type_button->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -7 * EDSCALE);
- type_button->set_text(TTR("Preset.."));
+ type_button->set_text(TTR("Preset..."));
type_button->get_popup()->clear();
type_button->get_popup()->add_item(TTR("Linear"), EASING_LINEAR);
type_button->get_popup()->add_item(TTR("Ease In"), EASING_EASE_IN);
@@ -525,14 +530,14 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
if (hint == PROPERTY_HINT_FILE || hint == PROPERTY_HINT_GLOBAL_FILE) {
List<String> names;
- names.push_back(TTR("File.."));
+ names.push_back(TTR("File..."));
names.push_back(TTR("Clear"));
config_action_buttons(names);
} else if (hint == PROPERTY_HINT_DIR || hint == PROPERTY_HINT_GLOBAL_DIR) {
List<String> names;
- names.push_back(TTR("Dir.."));
+ names.push_back(TTR("Dir..."));
names.push_back(TTR("Clear"));
config_action_buttons(names);
} else if (hint == PROPERTY_HINT_ENUM) {
@@ -877,6 +882,12 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
} else if (hint_text != "") {
int idx = 0;
+ Vector<EditorData::CustomType> custom_resources;
+
+ if (EditorNode::get_editor_data().get_custom_types().has("Resource")) {
+ custom_resources = EditorNode::get_editor_data().get_custom_types()["Resource"];
+ }
+
for (int i = 0; i < hint_text.get_slice_count(","); i++) {
String base = hint_text.get_slice(",", i);
@@ -885,6 +896,11 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
valid_inheritors.insert(base);
List<StringName> inheritors;
ClassDB::get_inheriters_from_class(base.strip_edges(), &inheritors);
+
+ for (int i = 0; i < custom_resources.size(); i++) {
+ inheritors.push_back(custom_resources[i].name);
+ }
+
List<StringName>::Element *E = inheritors.front();
while (E) {
valid_inheritors.insert(E->get());
@@ -893,14 +909,34 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant::
for (Set<String>::Element *E = valid_inheritors.front(); E; E = E->next()) {
String t = E->get();
- if (!ClassDB::can_instance(t))
+
+ bool is_custom_resource = false;
+ Ref<Texture> icon;
+ if (!custom_resources.empty()) {
+ for (int i = 0; i < custom_resources.size(); i++) {
+ if (custom_resources[i].name == t) {
+ is_custom_resource = true;
+ if (custom_resources[i].icon.is_valid())
+ icon = custom_resources[i].icon;
+ break;
+ }
+ }
+ }
+
+ if (!is_custom_resource && !ClassDB::can_instance(t))
continue;
+
inheritors_array.push_back(t);
int id = TYPE_BASE_ID + idx;
- if (has_icon(t, "EditorIcons")) {
- menu->add_icon_item(get_icon(t, "EditorIcons"), vformat(TTR("New %s"), t), id);
+ if (!icon.is_valid() && has_icon(t, "EditorIcons")) {
+ icon = get_icon(t, "EditorIcons");
+ }
+
+ if (icon.is_valid()) {
+
+ menu->add_icon_item(icon, vformat(TTR("New %s"), t), id);
} else {
menu->add_item(vformat(TTR("New %s"), t), id);
@@ -1094,6 +1130,10 @@ void CustomPropertyEditor::_type_create_selected(int p_idx) {
Object *obj = ClassDB::instance(intype);
+ if (!obj) {
+ obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+ }
+
ERR_FAIL_COND(!obj);
Resource *res = Object::cast_to<Resource>(obj);
@@ -1291,6 +1331,11 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
if (hint == PROPERTY_HINT_RESOURCE_TYPE) {
Object *obj = ClassDB::instance(intype);
+
+ if (!obj) {
+ obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+ }
+
ERR_BREAK(!obj);
Resource *res = Object::cast_to<Resource>(obj);
ERR_BREAK(!res);
@@ -3228,22 +3273,34 @@ void PropertyEditor::update_tree() {
while (hint.begins_with(itos(Variant::ARRAY) + ":")) {
type_name += "<Array";
type_name_suffix += ">";
- hint = hint.substr(2, hint.size() - 2);
+ hint = hint.right(2);
}
if (hint.find(":") >= 0) {
- hint = hint.substr(0, hint.find(":"));
+ int colon_pos = hint.find(":");
+ String hint_string = hint.right(colon_pos + 1);
+ hint = hint.left(colon_pos);
+
+ PropertyHint property_hint = PROPERTY_HINT_NONE;
+
if (hint.find("/") >= 0) {
- hint = hint.substr(0, hint.find("/"));
+ int slash_pos = hint.find("/");
+ property_hint = PropertyHint(hint.right(slash_pos + 1).to_int());
+ hint = hint.left(slash_pos);
+ }
+
+ if (property_hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ type_name += "<" + hint_string;
+ } else {
+ type_name += "<" + Variant::get_type_name(Variant::Type(hint.to_int()));
}
- type_name += "<" + Variant::get_type_name(Variant::Type(hint.to_int()));
type_name_suffix += ">";
}
type_name += type_name_suffix;
if (v.is_array())
- item->set_text(1, type_name + "[" + itos(v.call("size")) + "]");
+ item->set_text(1, type_name + "(" + itos(v.call("size")) + ")");
else
- item->set_text(1, type_name + "[]");
+ item->set_text(1, type_name + "()");
if (show_type_icons)
item->set_icon(0, get_icon("PoolByteArray", "EditorIcons"));
@@ -3939,7 +3996,7 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) {
if (_might_be_in_instance() && _get_instanced_node_original_property(prop, vorig)) {
- _edit_set(prop, vorig);
+ _edit_set(prop, vorig.duplicate(true)); // Set, making sure to duplicate arrays properly
return;
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index d5ec858c3..ba661813d 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -107,7 +107,12 @@ void SceneTreeDock::_unhandled_key_input(Ref<InputEvent> p_event) {
void SceneTreeDock::instance(const String &p_file) {
Node *parent = scene_tree->get_selected();
- if (!parent || !edited_scene) {
+
+ if (!parent) {
+ Node *parent = edited_scene;
+ };
+
+ if (!edited_scene) {
current_option = -1;
accept->get_ok()->set_text(TTR("OK :("));
@@ -379,7 +384,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (scene_tree->get_selected() == edited_scene) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation can't be done on the tree root."));
accept->popup_centered_minsize();
break;
@@ -440,7 +445,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (editor_selection->is_selected(edited_scene)) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation can't be done on the tree root."));
accept->popup_centered_minsize();
break;
@@ -510,7 +515,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (editor_selection->is_selected(edited_scene)) {
current_option = -1;
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation can't be done on the tree root."));
accept->popup_centered_minsize();
break;
@@ -571,7 +576,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
Node *scene = editor_data->get_edited_scene_root();
if (!scene) {
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation can't be done without a scene."));
accept->popup_centered_minsize();
break;
@@ -580,7 +585,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
List<Node *> selection = editor_selection->get_selected_node_list();
if (selection.size() != 1) {
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation requires a single selected node."));
accept->popup_centered_minsize();
break;
@@ -589,14 +594,14 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
Node *tocopy = selection.front()->get();
if (tocopy == scene) {
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("Can not perform with the root node."));
accept->popup_centered_minsize();
break;
}
if (tocopy != editor_data->get_edited_scene_root() && tocopy->get_filename() != "") {
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation can't be done on instanced scenes."));
accept->popup_centered_minsize();
break;
@@ -620,7 +625,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
new_scene_from_dialog->set_current_path(existing);
new_scene_from_dialog->popup_centered_ratio();
- new_scene_from_dialog->set_title(TTR("Save New Scene As.."));
+ new_scene_from_dialog->set_title(TTR("Save New Scene As..."));
} break;
case TOOL_COPY_NODE_PATH: {
List<Node *> selection = editor_selection->get_selected_node_list();
@@ -916,9 +921,6 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP
if (!r_rem_anims)
r_rem_anims = &rem_anims;
- if (!bool(EDITOR_DEF("editors/animation/autorename_animation_tracks", true)))
- return;
-
if (!p_base) {
p_base = edited_scene;
@@ -927,7 +929,54 @@ void SceneTreeDock::perform_node_renames(Node *p_base, List<Pair<NodePath, NodeP
if (!p_base)
return;
- if (Object::cast_to<AnimationPlayer>(p_base)) {
+ // Renaming node paths used in script instances
+ if (p_base->get_script_instance()) {
+
+ ScriptInstance *si = p_base->get_script_instance();
+
+ if (si) {
+
+ List<PropertyInfo> properties;
+ si->get_property_list(&properties);
+
+ for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) {
+
+ String propertyname = E->get().name;
+ Variant p = p_base->get(propertyname);
+ if (p.get_type() == Variant::NODE_PATH) {
+
+ // Goes through all paths to check if its matching
+ for (List<Pair<NodePath, NodePath> >::Element *E = p_renames->front(); E; E = E->next()) {
+
+ NodePath root_path = p_base->get_path();
+
+ NodePath rel_path_old = root_path.rel_path_to(E->get().first);
+
+ NodePath rel_path_new = E->get().second;
+
+ // if not empty, get new relative path
+ if (E->get().second != NodePath()) {
+ rel_path_new = root_path.rel_path_to(E->get().second);
+ }
+
+ // if old path detected, then it needs to be replaced with the new one
+ if (p == rel_path_old) {
+
+ editor_data->get_undo_redo().add_do_property(p_base, propertyname, rel_path_new);
+ editor_data->get_undo_redo().add_undo_property(p_base, propertyname, rel_path_old);
+
+ p_base->set(propertyname, rel_path_new);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ bool autorename_animation_tracks = bool(EDITOR_DEF("editors/animation/autorename_animation_tracks", true));
+
+ if (autorename_animation_tracks && Object::cast_to<AnimationPlayer>(p_base)) {
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_base);
List<StringName> anims;
@@ -1158,7 +1207,30 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V
editor_data->get_undo_redo().add_do_method(new_parent, "move_child", node, p_position_in_parent + inc);
ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
+ String old_name = former_names[ni];
String new_name = new_parent->validate_child_name(node);
+
+ // name was modified, fix the path renames
+ if (old_name.casecmp_to(new_name) != 0) {
+
+ // Fix the to name to have the new name
+ NodePath old_new_name = path_renames[ni].second;
+ NodePath new_path;
+
+ Vector<StringName> unfixed_new_names = old_new_name.get_names();
+ Vector<StringName> fixed_new_names;
+
+ // Get last name and replace with fixed new name
+ for (int a = 0; a < (unfixed_new_names.size() - 1); a++) {
+ fixed_new_names.push_back(unfixed_new_names[a]);
+ }
+ fixed_new_names.push_back(new_name);
+
+ NodePath fixed_node_path = NodePath(fixed_new_names, true);
+
+ path_renames[ni].second = fixed_node_path;
+ }
+
editor_data->get_undo_redo().add_do_method(sed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, -1);
editor_data->get_undo_redo().add_undo_method(sed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)) + "/" + new_name), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index());
@@ -1539,7 +1611,7 @@ void SceneTreeDock::_new_scene_from(String p_file) {
List<Node *> selection = editor_selection->get_selected_node_list();
if (selection.size() != 1) {
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("This operation requires a single selected node."));
accept->popup_centered_minsize();
return;
@@ -1557,7 +1629,7 @@ void SceneTreeDock::_new_scene_from(String p_file) {
memdelete(copy);
if (err != OK) {
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("Couldn't save new scene. Likely dependencies (instances) couldn't be satisfied."));
accept->popup_centered_minsize();
return;
@@ -1569,14 +1641,14 @@ void SceneTreeDock::_new_scene_from(String p_file) {
err = ResourceSaver::save(p_file, sdata, flg);
if (err != OK) {
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("Error saving scene."));
accept->popup_centered_minsize();
return;
}
_replace_with_branch_scene(p_file, base);
} else {
- accept->get_ok()->set_text(TTR("I see.."));
+ accept->get_ok()->set_text(TTR("I see..."));
accept->set_text(TTR("Error duplicating scene to save it."));
accept->popup_centered_minsize();
return;
@@ -1794,6 +1866,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) {
subresources.clear();
menu_subresources->clear();
+ menu_subresources->set_size(Size2(1, 1));
_add_children_to_popup(selection.front()->get(), 0);
if (menu->get_item_count() > 0)
menu->add_separator();
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index e38347a65..64d278c0c 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -644,11 +644,12 @@ void SceneTreeEditor::_renamed() {
ERR_FAIL_COND(!n);
String new_name = which->get_text(0);
- if (new_name.find(".") != -1 || new_name.find("/") != -1) {
+ if (!Node::_validate_node_name(new_name)) {
- error->set_text(TTR("Invalid node name, the following characters are not allowed:") + "\n \".\", \"/\"");
+ error->set_text(TTR("Invalid node name, the following characters are not allowed:") + "\n" + Node::invalid_character);
error->popup_centered_minsize();
- new_name = n->get_name();
+
+ which->set_text(0, new_name);
}
if (new_name == n->get_name())
diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp
index e9529eb1c..50519e2c6 100644
--- a/editor/script_editor_debugger.cpp
+++ b/editor/script_editor_debugger.cpp
@@ -710,25 +710,6 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
error_list->set_item_metadata(error_list->get_item_count() - 1, stack);
error_count++;
- /*
- int count = p_data[1];
-
- Array cstack;
-
- OutputError oe = errors.front()->get();
-
- packet_peer_stream->put_var(oe.hr);
- packet_peer_stream->put_var(oe.min);
- packet_peer_stream->put_var(oe.sec);
- packet_peer_stream->put_var(oe.msec);
- packet_peer_stream->put_var(oe.source_func);
- packet_peer_stream->put_var(oe.source_file);
- packet_peer_stream->put_var(oe.source_line);
- packet_peer_stream->put_var(oe.error);
- packet_peer_stream->put_var(oe.error_descr);
- packet_peer_stream->put_var(oe.warning);
- packet_peer_stream->put_var(oe.callstack);
- */
} else if (p_msg == "profile_sig") {
//cache a signature
@@ -755,6 +736,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
EditorProfiler::Metric::Category::Item item;
item.calls = 1;
item.line = 0;
+
item.name = "Physics Time";
item.total = metric.physics_time;
item.self = item.total;
@@ -792,8 +774,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da
for (int i = 0; i < values.size(); i += 2) {
EditorProfiler::Metric::Category::Item item;
- item.name = values[i];
item.calls = 1;
+ item.line = 0;
+ item.name = values[i];
item.self = values[i + 1];
item.total = item.self;
item.signature = "categ::" + name + "::" + item.name;
diff --git a/editor/settings_config_dialog.cpp b/editor/settings_config_dialog.cpp
index d75ef5df8..c3e9e4ab6 100644
--- a/editor/settings_config_dialog.cpp
+++ b/editor/settings_config_dialog.cpp
@@ -101,7 +101,14 @@ void EditorSettingsDialog::popup_edit_settings() {
if (EditorSettings::get_singleton()->has_setting("interface/dialogs/editor_settings_bounds")) {
popup(EditorSettings::get_singleton()->get("interface/dialogs/editor_settings_bounds"));
} else {
- popup_centered_ratio(0.7);
+
+ Size2 popup_size = Size2(900, 700) * editor_get_scale();
+ Size2 window_size = get_viewport_rect().size;
+
+ popup_size.x = MIN(window_size.x * 0.8, popup_size.x);
+ popup_size.y = MIN(window_size.y * 0.8, popup_size.y);
+
+ popup_centered(popup_size);
}
_focus_current_search_box();
@@ -199,6 +206,14 @@ void EditorSettingsDialog::_update_icons() {
void EditorSettingsDialog::_update_shortcuts() {
+ Map<String, bool> collapsed;
+
+ if (shortcuts->get_root() && shortcuts->get_root()->get_children()) {
+ for (TreeItem *item = shortcuts->get_root()->get_children(); item; item = item->get_next()) {
+ collapsed[item->get_text(0)] = item->is_collapsed();
+ }
+ }
+
shortcuts->clear();
List<String> slist;
@@ -223,7 +238,13 @@ void EditorSettingsDialog::_update_shortcuts() {
section = sections[section_name];
} else {
section = shortcuts->create_item(root);
- section->set_text(0, section_name.capitalize());
+
+ String item_name = section_name.capitalize();
+ section->set_text(0, item_name);
+
+ if (collapsed.has(item_name)) {
+ section->set_collapsed(collapsed[item_name]);
+ }
sections[section_name] = section;
section->set_custom_bg_color(0, get_color("prop_subsection", "Editor"));
@@ -263,7 +284,7 @@ void EditorSettingsDialog::_shortcut_button_pressed(Object *p_item, int p_column
Ref<ShortCut> sc = EditorSettings::get_singleton()->get_shortcut(item);
if (p_idx == 0) {
- press_a_key_label->set_text(TTR("Press a Key.."));
+ press_a_key_label->set_text(TTR("Press a Key..."));
last_wait_for_key = Ref<InputEventKey>();
press_a_key->popup_centered(Size2(250, 80) * EDSCALE);
press_a_key->grab_focus();
@@ -457,7 +478,7 @@ EditorSettingsDialog::EditorSettingsDialog() {
add_child(press_a_key);
Label *l = memnew(Label);
- l->set_text(TTR("Press a Key.."));
+ l->set_text(TTR("Press a Key..."));
l->set_anchors_and_margins_preset(Control::PRESET_WIDE);
l->set_align(Label::ALIGN_CENTER);
l->set_margin(MARGIN_TOP, 20);
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index 8c90d86b9..2652b0976 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -299,13 +299,24 @@ void EditorSpatialGizmo::add_handles(const Vector<Vector3> &p_handles, bool p_bi
}
}
-void EditorSpatialGizmo::add_solid_box(Ref<Material> &p_material, Vector3 p_size) {
+void EditorSpatialGizmo::add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position) {
ERR_FAIL_COND(!spatial_node);
CubeMesh cubem;
cubem.set_size(p_size);
+
+ Array arrays = cubem.surface_get_arrays(0);
+ PoolVector3Array vertex = arrays[VS::ARRAY_VERTEX];
+ PoolVector3Array::Write w = vertex.write();
+
+ for (int i = 0; i < vertex.size(); ++i) {
+ w[i] += p_position;
+ }
+
+ arrays[VS::ARRAY_VERTEX] = vertex;
+
Ref<ArrayMesh> m = memnew(ArrayMesh);
- m->add_surface_from_arrays(cubem.surface_get_primitive_type(0), cubem.surface_get_arrays(0));
+ m->add_surface_from_arrays(cubem.surface_get_primitive_type(0), arrays);
m->surface_set_material(0, p_material);
add_mesh(m);
}
@@ -1523,6 +1534,120 @@ SkeletonSpatialGizmo::SkeletonSpatialGizmo(Skeleton *p_skel) {
set_spatial_node(p_skel);
}
+PhysicalBoneSpatialGizmo::PhysicalBoneSpatialGizmo(PhysicalBone *p_pb) :
+ EditorSpatialGizmo(),
+ physical_bone(p_pb) {
+ set_spatial_node(p_pb);
+}
+
+void PhysicalBoneSpatialGizmo::redraw() {
+
+ clear();
+
+ if (!physical_bone)
+ return;
+
+ Skeleton *sk(physical_bone->find_skeleton_parent());
+ PhysicalBone *pb(sk->get_physical_bone(physical_bone->get_bone_id()));
+ PhysicalBone *pbp(sk->get_physical_bone_parent(physical_bone->get_bone_id()));
+
+ Vector<Vector3> points;
+
+ switch (physical_bone->get_joint_type()) {
+ case PhysicalBone::JOINT_TYPE_PIN: {
+
+ PinJointSpatialGizmo::CreateGizmo(physical_bone->get_joint_offset(), points);
+ } break;
+ case PhysicalBone::JOINT_TYPE_CONE: {
+
+ const PhysicalBone::ConeJointData *cjd(static_cast<const PhysicalBone::ConeJointData *>(physical_bone->get_joint_data()));
+ ConeTwistJointSpatialGizmo::CreateGizmo(
+ physical_bone->get_joint_offset(),
+ physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
+ pb ? pb->get_global_transform() : Transform(),
+ pbp ? pbp->get_global_transform() : Transform(),
+ cjd->swing_span,
+ cjd->twist_span,
+ points,
+ pb ? &points : NULL,
+ pbp ? &points : NULL);
+ } break;
+ case PhysicalBone::JOINT_TYPE_HINGE: {
+
+ const PhysicalBone::HingeJointData *hjd(static_cast<const PhysicalBone::HingeJointData *>(physical_bone->get_joint_data()));
+ HingeJointSpatialGizmo::CreateGizmo(
+ physical_bone->get_joint_offset(),
+ physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
+ pb ? pb->get_global_transform() : Transform(),
+ pbp ? pbp->get_global_transform() : Transform(),
+ hjd->angular_limit_lower,
+ hjd->angular_limit_upper,
+ hjd->angular_limit_enabled,
+ points,
+ pb ? &points : NULL,
+ pbp ? &points : NULL);
+ } break;
+ case PhysicalBone::JOINT_TYPE_SLIDER: {
+
+ const PhysicalBone::SliderJointData *sjd(static_cast<const PhysicalBone::SliderJointData *>(physical_bone->get_joint_data()));
+ SliderJointSpatialGizmo::CreateGizmo(
+ physical_bone->get_joint_offset(),
+ physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
+ pb ? pb->get_global_transform() : Transform(),
+ pbp ? pbp->get_global_transform() : Transform(),
+ sjd->angular_limit_lower,
+ sjd->angular_limit_upper,
+ sjd->linear_limit_lower,
+ sjd->linear_limit_upper,
+ points,
+ pb ? &points : NULL,
+ pbp ? &points : NULL);
+ } break;
+ case PhysicalBone::JOINT_TYPE_6DOF: {
+
+ const PhysicalBone::SixDOFJointData *sdofjd(static_cast<const PhysicalBone::SixDOFJointData *>(physical_bone->get_joint_data()));
+ Generic6DOFJointSpatialGizmo::CreateGizmo(
+ physical_bone->get_joint_offset(),
+
+ physical_bone->get_global_transform() * physical_bone->get_joint_offset(),
+ pb ? pb->get_global_transform() : Transform(),
+ pbp ? pbp->get_global_transform() : Transform(),
+
+ sdofjd->axis_data[0].angular_limit_lower,
+ sdofjd->axis_data[0].angular_limit_upper,
+ sdofjd->axis_data[0].linear_limit_lower,
+ sdofjd->axis_data[0].linear_limit_upper,
+ sdofjd->axis_data[0].angular_limit_enabled,
+ sdofjd->axis_data[0].linear_limit_enabled,
+
+ sdofjd->axis_data[1].angular_limit_lower,
+ sdofjd->axis_data[1].angular_limit_upper,
+ sdofjd->axis_data[1].linear_limit_lower,
+ sdofjd->axis_data[1].linear_limit_upper,
+ sdofjd->axis_data[1].angular_limit_enabled,
+ sdofjd->axis_data[1].linear_limit_enabled,
+
+ sdofjd->axis_data[2].angular_limit_lower,
+ sdofjd->axis_data[2].angular_limit_upper,
+ sdofjd->axis_data[2].linear_limit_lower,
+ sdofjd->axis_data[2].linear_limit_upper,
+ sdofjd->axis_data[2].angular_limit_enabled,
+ sdofjd->axis_data[2].linear_limit_enabled,
+
+ points,
+ pb ? &points : NULL,
+ pbp ? &points : NULL);
+ } break;
+ default:
+ return;
+ }
+
+ Ref<Material> material = create_material("joint_material", EDITOR_GET("editors/3d_gizmos/gizmo_colors/joint"));
+
+ add_collision_segments(points);
+ add_lines(points, material);
+}
+
// FIXME: Kept as reference for reimplementation in 3.1+
#if 0
void RoomSpatialGizmo::redraw() {
@@ -2411,7 +2536,7 @@ void ParticlesGizmo::redraw() {
gizmo_color.a = 0.1;
Ref<Material> solid_material = create_material("particles_solid_material", gizmo_color);
- add_solid_box(solid_material, aabb.get_size());
+ add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0);
}
//add_unscaled_billboard(SpatialEditorGizmos::singleton->visi,0.05);
@@ -3724,6 +3849,12 @@ Ref<SpatialEditorGizmo> SpatialEditorGizmos::get_gizmo(Spatial *p_spatial) {
return lsg;
}
+ if (Object::cast_to<PhysicalBone>(p_spatial)) {
+
+ Ref<PhysicalBoneSpatialGizmo> pbsg = memnew(PhysicalBoneSpatialGizmo(Object::cast_to<PhysicalBone>(p_spatial)));
+ return pbsg;
+ }
+
if (Object::cast_to<Position3D>(p_spatial)) {
Ref<Position3DSpatialGizmo> lsg = memnew(Position3DSpatialGizmo(Object::cast_to<Position3D>(p_spatial)));
@@ -4052,9 +4183,9 @@ SpatialEditorGizmos::SpatialEditorGizmos() {
for (int k = 0; k < 3; k++) {
if (i < 3)
- face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[j][(i + k) % 3] = v[k];
else
- face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[3 - j][(i + k) % 3] = -v[k];
}
}
//tri 1
diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h
index eedab7fde..c5dc36cb2 100644
--- a/editor/spatial_editor_gizmos.h
+++ b/editor/spatial_editor_gizmos.h
@@ -105,7 +105,7 @@ protected:
void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh, const AABB &p_bounds = AABB());
void add_unscaled_billboard(const Ref<Material> &p_material, float p_scale = 1);
void add_handles(const Vector<Vector3> &p_handles, bool p_billboard = false, bool p_secondary = false);
- void add_solid_box(Ref<Material> &p_material, Vector3 p_size);
+ void add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3());
void set_spatial_node(Spatial *p_node);
const Spatial *get_spatial_node() const { return spatial_node; }
@@ -214,6 +214,17 @@ public:
SkeletonSpatialGizmo(Skeleton *p_skel = NULL);
};
+class PhysicalBoneSpatialGizmo : public EditorSpatialGizmo {
+ GDCLASS(PhysicalBoneSpatialGizmo, EditorSpatialGizmo);
+
+ PhysicalBone *physical_bone;
+
+public:
+ //virtual Transform get_global_gizmo_transform();
+ virtual void redraw();
+ PhysicalBoneSpatialGizmo(PhysicalBone *p_pb = NULL);
+};
+
#if 0
class PortalSpatialGizmo : public EditorSpatialGizmo {
diff --git a/editor/translations/af.po b/editor/translations/af.po
index 0f5a2b890..8644e2031 100644
--- a/editor/translations/af.po
+++ b/editor/translations/af.po
@@ -1407,6 +1407,10 @@ msgstr "Vee uit"
msgid "Clear Output"
msgstr "Afvoer:"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Fout tydens storing van hulpbron!"
@@ -3636,6 +3640,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3921,6 +3933,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5583,6 +5599,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6062,7 +6086,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6206,10 +6232,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7250,6 +7272,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7891,10 +7917,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/ar.po b/editor/translations/ar.po
index 7e9f89429..a57dc0f0c 100644
--- a/editor/translations/ar.po
+++ b/editor/translations/ar.po
@@ -1401,6 +1401,10 @@ msgstr "خالي"
msgid "Clear Output"
msgstr "أخلاء الخرج"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "خطأ في حفظ المورد!"
@@ -3687,6 +3691,16 @@ msgid "Show Guides"
msgstr "أظهر الموجهات"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "إظهار الشبكة"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "أظهر المساعدات"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "نصف المُحدد"
@@ -3976,6 +3990,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "الميش ليس لديه سطح لكي ينشئ حدود منه!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "لا يمكن إنشاء الحد!"
@@ -5637,6 +5655,15 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "إضافة عنصر"
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6112,7 +6139,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6256,10 +6285,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "لا يمكن أن يحتوي علي '/' أو ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7305,6 +7330,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "إنشاء الحل..."
@@ -7945,10 +7974,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8029,6 +8068,9 @@ msgstr ""
msgid "Invalid font size."
msgstr ""
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "لا يمكن أن يحتوي علي '/' أو ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/bg.po b/editor/translations/bg.po
index 24b934505..741f6ab20 100644
--- a/editor/translations/bg.po
+++ b/editor/translations/bg.po
@@ -1375,6 +1375,10 @@ msgstr "Изчистване"
msgid "Clear Output"
msgstr "Нова сцена"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3630,6 +3634,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3916,6 +3928,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5590,6 +5606,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6079,7 +6103,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6223,10 +6249,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7305,6 +7327,10 @@ msgstr "Настройки"
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7979,10 +8005,20 @@ msgstr ""
"работи."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/bn.po b/editor/translations/bn.po
index ff5533e22..b8cd30b56 100644
--- a/editor/translations/bn.po
+++ b/editor/translations/bn.po
@@ -1428,6 +1428,10 @@ msgstr "পরিস্কার করুন/ক্লীয়ার"
msgid "Clear Output"
msgstr "আউটপুট/ফলাফল"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "রিসোর্স সংরক্ষণে সমস্যা হয়েছে!"
@@ -3841,6 +3845,16 @@ msgid "Show Guides"
msgstr "বোন্‌/হাড় দেখান"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "অরিজিন দেখুন"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "১ টি Viewport"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "নির্বাচনকে কেন্দ্রীভূত করুন"
@@ -4143,6 +4157,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "প্রান্তরেখা তৈরি করার জন্য প্রয়োজনীয় Mesh এর কোনো পৃষ্ঠতল নেই!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "প্রান্তরেখা তৈরি করা সম্ভব হয়নি!"
@@ -5885,6 +5903,16 @@ msgid "Checked Item"
msgstr "চিহ্নিত আইটেম"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "বস্তু যোগ করুন"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "চিহ্নিত আইটেম"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "আছে"
@@ -6415,8 +6443,10 @@ msgid "Mouse Button"
msgstr "মাউসের বোতাম"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "অকার্যকর অ্যাকশন ('/' বা ':' ছাড়া কিছুই যাবে না)।"
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6567,11 +6597,6 @@ msgstr "ইনপুট অপসারণ করুন"
#: editor/project_settings_editor.cpp
#, fuzzy
-msgid "Can't contain '/' or ':'"
-msgstr "নোডের সাথে সংযুক্ত করুন:"
-
-#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Already existing"
msgstr "স্থায়ীয়তা টগল করুন"
@@ -7685,6 +7710,10 @@ msgstr "স্ন্যাপ সেটিংস"
msgid "Pick Distance:"
msgstr "ইন্সট্যান্স:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
#, fuzzy
msgid "Generating solution..."
@@ -8408,12 +8437,22 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "Path এর দিক অবশ্যই একটি কার্যকর Spatial নোডের এর দিকে নির্দেশ করাতে হবে।"
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"প্রতি দৃশ্যে (অথবা ইন্সট্যান্সড দৃশ্যের সম্মেলনে) সর্বোচ্চ একটি দৃশ্যমান WorldEnvironment "
"সম্ভব।"
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8505,6 +8544,13 @@ msgstr "ফন্ট তুলতে/লোডে সমস্যা হয়ে
msgid "Invalid font size."
msgstr "ফন্টের আকার অগ্রহনযোগ্য।"
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "অকার্যকর অ্যাকশন ('/' বা ':' ছাড়া কিছুই যাবে না)।"
+
+#, fuzzy
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "নোডের সাথে সংযুক্ত করুন:"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/ca.po b/editor/translations/ca.po
index 5af9d98ec..a1f92b531 100644
--- a/editor/translations/ca.po
+++ b/editor/translations/ca.po
@@ -9,7 +9,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-02-27 02:34+0000\n"
+"PO-Revision-Date: 2018-03-10 03:34+0000\n"
"Last-Translator: Roger Blanco Ribera <roger.blancoribera@gmail.com>\n"
"Language-Team: Catalan <https://hosted.weblate.org/projects/godot-engine/"
"godot/ca/>\n"
@@ -1398,6 +1398,10 @@ msgstr "Neteja"
msgid "Clear Output"
msgstr "Buida la Sortida"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Error en desar recurs!"
@@ -3702,6 +3706,16 @@ msgid "Show Guides"
msgstr "Mostra les guies"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Mostra l'Origen"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 Vista"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Centra la Selecció"
@@ -3991,6 +4005,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "La Malla manca d'una superfície on delinear-hi els contorns!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "No es pot crear el contorn!"
@@ -5659,6 +5677,16 @@ msgid "Checked Item"
msgstr "Element validat"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Afegeix un Element"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Element validat"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Té"
@@ -6137,7 +6165,7 @@ msgid ""
"Would you like to explore the official example projects in the Asset Library?"
msgstr ""
"Encara no teniu cap projecte.\n"
-"Voleu explorar els projectes oficials d'exemple a la llibreria activa?"
+"Voleu explorar els projectes d'exemple oficials a la Biblioteca d'Actius?"
#: editor/project_settings_editor.cpp
msgid "Key "
@@ -6156,8 +6184,10 @@ msgid "Mouse Button"
msgstr "Botó del ratolí"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "L'Acció no és vàlida (no es pot utilitzar ' / ' o ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6300,10 +6330,6 @@ msgid "Delete Item"
msgstr "Esborra l'Element"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "No pot contenir '/' o ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Ja existeix"
@@ -7362,6 +7388,10 @@ msgstr "Configuració del GridMap"
msgid "Pick Distance:"
msgstr "Trieu la distància:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "S'està generant la solució..."
@@ -7412,7 +7442,7 @@ msgstr "Avisos"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "Final de la traça de la pila d'excepció interna"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -8070,12 +8100,22 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "Cal que la propietat Camí assenyali cap a un node Spatial vàlid."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Només es permet un sol WorldEnvironment per escena ( o conjunt d'escenes "
"instanciades)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8173,6 +8213,12 @@ msgstr "Error carregant lletra."
msgid "Invalid font size."
msgstr "La mida de la lletra no és vàlida."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "L'Acció no és vàlida (no es pot utilitzar ' / ' o ':')."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "No pot contenir '/' o ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/cs.po b/editor/translations/cs.po
index aa29f1603..ce26418cb 100644
--- a/editor/translations/cs.po
+++ b/editor/translations/cs.po
@@ -1401,6 +1401,10 @@ msgstr "Vyčistit"
msgid "Clear Output"
msgstr "Vymazat výstup"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3672,6 +3676,16 @@ msgstr "Zobrazit vodítka"
#: editor/plugins/canvas_item_editor_plugin.cpp
#, fuzzy
+msgid "Show Origin"
+msgstr "Zobrazit mřížku"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "Zobrazit pomocné"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
msgid "Center Selection"
msgstr "Vycentrovat výběr"
@@ -3962,6 +3976,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Mesh némá povrch z jakého vytvořit obrysy!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Nelze vytvořit obrys!"
@@ -5643,6 +5661,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6125,7 +6151,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6270,10 +6298,6 @@ msgid "Delete Item"
msgstr "Odstranit položku"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "Nesmí obsaovat '/' nebo ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7333,6 +7357,10 @@ msgstr "Nastavení GridMap"
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -8032,12 +8060,22 @@ msgstr ""
"uzel Particles2D."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Na každou scénu (nebo skupinu instancovaných scén) je povolen pouze jeden "
"WorldEnvironment."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8130,6 +8168,9 @@ msgstr "Chyba nahrávání fontu."
msgid "Invalid font size."
msgstr "Neplatná velikost fontu."
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Nesmí obsaovat '/' nebo ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/da.po b/editor/translations/da.po
index ca47dc00e..349706f6e 100644
--- a/editor/translations/da.po
+++ b/editor/translations/da.po
@@ -3,22 +3,24 @@
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
#
+# Dankse Memes <purplelops@gmail.com>, 2018.
# David Lamhauge <davidlamhauge@gmail.com>, 2016.
+# Esben Damkjær Sørensen <esben@damkjaergaard.com>, 2018.
# Kim Nielsen <kimmowich@stofanet.dk>, 2017.
# Michael Madsen <mim@michael-madsen.dk>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2017-12-20 15:42+0000\n"
-"Last-Translator: Kim Nielsen <kimmowich@stofanet.dk>\n"
+"PO-Revision-Date: 2018-04-22 07:35+0000\n"
+"Last-Translator: Dankse Memes <purplelops@gmail.com>\n"
"Language-Team: Danish <https://hosted.weblate.org/projects/godot-engine/"
"godot/da/>\n"
"Language: da\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 2.18\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -29,9 +31,8 @@ msgid "All Selection"
msgstr "All selection"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Keyframe Time"
-msgstr "Anim Skift Værdi"
+msgstr "Anim Skift Keyframetid"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
@@ -42,9 +43,8 @@ msgid "Anim Change Transform"
msgstr "Anim Skift Transformering"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Keyframe Value"
-msgstr "Anim Skift Værdi"
+msgstr "Anim Skift Keyframeværdi"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
@@ -497,9 +497,8 @@ msgid "Connecting Signal:"
msgstr "Forbindelses Signal:"
#: editor/connections_dialog.cpp
-#, fuzzy
msgid "Disconnect '%s' from '%s'"
-msgstr "Forbind '%s' til '%s'"
+msgstr "Afbryd '%s' fra '%s'"
#: editor/connections_dialog.cpp
msgid "Connect.."
@@ -515,9 +514,8 @@ msgid "Signals"
msgstr "Signaler"
#: editor/create_dialog.cpp
-#, fuzzy
msgid "Change %s Type"
-msgstr "Skift Base Type"
+msgstr "Skift %s Type"
#: editor/create_dialog.cpp editor/project_settings_editor.cpp
#: modules/visual_script/visual_script_editor.cpp
@@ -525,9 +523,8 @@ msgid "Change"
msgstr "Skift"
#: editor/create_dialog.cpp
-#, fuzzy
msgid "Create New %s"
-msgstr "Opret Nyt"
+msgstr "Opret Ny %s"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp
@@ -639,7 +636,7 @@ msgstr ""
#: editor/dependency_editor.cpp
#, fuzzy
msgid "Cannot remove:"
-msgstr "Kan ikke fjerne:\n"
+msgstr "Kan ikke fjerne:"
#: editor/dependency_editor.cpp
msgid "Error loading:"
@@ -722,9 +719,8 @@ msgid "Lead Developer"
msgstr "Ledende Udvikler"
#: editor/editor_about.cpp
-#, fuzzy
msgid "Project Manager "
-msgstr "Projektleder"
+msgstr "Projektleder "
#: editor/editor_about.cpp
msgid "Developers"
@@ -817,9 +813,8 @@ msgid "Install"
msgstr "Installér"
#: editor/editor_asset_installer.cpp
-#, fuzzy
msgid "Package Installer"
-msgstr "Pakke Installatør"
+msgstr "Pakkeinstallatør"
#: editor/editor_audio_buses.cpp
msgid "Speakers"
@@ -901,7 +896,7 @@ msgstr "Slet Effect"
#: editor/editor_audio_buses.cpp
msgid "Audio"
-msgstr ""
+msgstr "Lyd"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus"
@@ -1083,11 +1078,11 @@ msgstr "Opdatere scene.."
#: editor/editor_data.cpp
msgid "[empty]"
-msgstr ""
+msgstr "[tom]"
#: editor/editor_data.cpp
msgid "[unsaved]"
-msgstr ""
+msgstr "[ikke gemt]"
#: editor/editor_dir_dialog.cpp
msgid "Please select a base directory first"
@@ -1127,18 +1122,16 @@ msgid "Packing"
msgstr "Pakker"
#: editor/editor_export.cpp platform/javascript/export/export.cpp
-#, fuzzy
msgid "Template file not found:"
-msgstr "Skabelon fil ikke fundet:\n"
+msgstr "Skabelonfil ikke fundet:"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "File Exists, Overwrite?"
msgstr "Filen Eksisterer, Overskrives?"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
-#, fuzzy
msgid "Select Current Folder"
-msgstr "Opret mappe"
+msgstr "Vælg nurværende mappe"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
msgid "Copy Path"
@@ -1244,9 +1237,8 @@ msgid "File:"
msgstr "Fil:"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
-#, fuzzy
msgid "Must use a valid extension."
-msgstr "Skal bruge en gyldig extension."
+msgstr "Du skal bruge en gyldig udvidelse."
#: editor/editor_file_system.cpp
msgid "ScanSources"
@@ -1345,7 +1337,7 @@ msgstr "Beskrivelse"
#: editor/editor_help.cpp
#, fuzzy
msgid "Online Tutorials:"
-msgstr "Online Dokumentation"
+msgstr "Online Dokumentation:"
#: editor/editor_help.cpp
#, fuzzy
@@ -1416,6 +1408,10 @@ msgstr "Clear"
msgid "Clear Output"
msgstr "Output"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Fejl, kan ikke gemme ressource!"
@@ -3742,6 +3738,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -4029,6 +4033,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5702,6 +5710,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6184,7 +6200,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6329,10 +6347,6 @@ msgid "Delete Item"
msgstr "Slet Valgte"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "Kan ikke indeholde '/' eller ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7379,6 +7393,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -8073,12 +8091,22 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "Stien skal pege på en gyldig fysisk node for at virke."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Kun én WorldEnvironment er tilladt pr. scene (eller et sæt af instanserede "
"scener)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8169,6 +8197,9 @@ msgstr "Error loading skrifttype."
msgid "Invalid font size."
msgstr "Ugyldig skriftstørrelse."
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Kan ikke indeholde '/' eller ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/de.po b/editor/translations/de.po
index 87f69c3fd..2087c7f4b 100644
--- a/editor/translations/de.po
+++ b/editor/translations/de.po
@@ -15,10 +15,12 @@
# hyperglow <greensoma@web.de>, 2016.
# Jan Groß <jan@grossit.de>, 2016.
# Kim <github@aggsol.de>, 2017.
+# Neicul <neicul@gmx.de>, 2018.
# Oliver Ruehl <oliver@ruehldesign.co>, 2016-2017.
# Paul-Vincent Roll <paviro@me.com>, 2016.
# Peter Friedland <peter_friedland@gmx.de>, 2016.
# No need for a name <endoplasmatik@gmx.net>, 2016.
+# Sönke <me@eknoes.de>, 2018.
# So Wieso <sowieso@dukun.de>, 2016-2018.
# Tim Schellenberg <smwleod@gmail.com>, 2017.
# Timo Schwarzer <account@timoschwarzer.com>, 2016-2018.
@@ -28,8 +30,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-02-12 08:36+0000\n"
-"Last-Translator: So Wieso <sowieso@dukun.de>\n"
+"PO-Revision-Date: 2018-04-18 15:38+0000\n"
+"Last-Translator: Neicul <neicul@gmx.de>\n"
"Language-Team: German <https://hosted.weblate.org/projects/godot-engine/"
"godot/de/>\n"
"Language: de\n"
@@ -37,7 +39,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 2.19-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -344,7 +346,7 @@ msgstr "Skalierungsverhältnis:"
#: editor/animation_editor.cpp
msgid "Call Functions in Which Node?"
-msgstr "Node dessen Funktionen aufgerufen werden sollen auswählen."
+msgstr "Funktionen in welcher Node aufrufen?"
#: editor/animation_editor.cpp
msgid "Remove invalid keys"
@@ -1423,6 +1425,10 @@ msgstr "Löschen"
msgid "Clear Output"
msgstr "Ausgabe löschen"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Fehler beim speichern der Ressource!"
@@ -1622,7 +1628,7 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
-"Es ist keine Hauptszene wurde jemals bestimmt, soll eine ausgewählt werden?\n"
+"Es wurde noch keine Hauptszene bestimmt, soll eine ausgewählt werden?\n"
"Dies kann später in den Projekteinstellungen unter der Kategorie ‚Anwendung‘ "
"geändert werden."
@@ -3479,8 +3485,8 @@ msgstr ""
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid "Failed creating lightmap images, make sure path is writable."
msgstr ""
-"Erstellung der Lightmap-Bilder fehlgeschlagen. Ist der Speicherpfad "
-"beschreibbar?"
+"Erstellung der Lightmap-Bilder fehlgeschlagen. Stellen Sie sicher, dass der "
+"Speicherpfad beschreibbar ist."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid "Bake Lightmaps"
@@ -3739,6 +3745,16 @@ msgid "Show Guides"
msgstr "Hilfslinien anzeigen"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Zeige Ursprung"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "Eine Ansicht"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Auswahl zentrieren"
@@ -4029,6 +4045,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Mesh hat keine Oberfläche von der Umrisse erzeugt werden könnten!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Konnte keinen Umriss erzeugen!"
@@ -5700,6 +5720,16 @@ msgid "Checked Item"
msgstr "Überprüftes Element"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Element hinzufügen"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Überprüftes Element"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Enthält"
@@ -6015,11 +6045,12 @@ msgid "Invalid project path (changed anything?)."
msgstr "Ungültiger Projektpfad (etwas geändert?)."
#: editor/project_manager.cpp
-#, fuzzy
msgid ""
"Couldn't load project.godot in project path (error %d). It may be missing or "
"corrupted."
-msgstr "project.godot des Projektpfads konnte nicht bearbeitet werden."
+msgstr ""
+"Konnte project.godot im Projektpfad nicht laden (Fehler %d). Sie könnte "
+"fehlen oder beschädigt worden sein."
#: editor/project_manager.cpp
msgid "Couldn't edit project.godot in project path."
@@ -6198,8 +6229,10 @@ msgid "Mouse Button"
msgstr "Maustaste"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Ungültiger Name für Aktion (alle Zeichen außer ‚/‘ und ‚:‘ möglich)."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6342,10 +6375,6 @@ msgid "Delete Item"
msgstr "Eintrag löschen"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "Darf nicht ‚/‘ oder ‚:‘ beinhalten"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Existiert bereits"
@@ -7293,18 +7322,16 @@ msgid "Object can't provide a length."
msgstr "Objekt kann keine Länge vorweisen."
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Next Plane"
-msgstr "Nächster Tab"
+msgstr "Nächste Ebene"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Previous Plane"
-msgstr "Vorheriger Tab"
+msgstr "Vorherige Ebene"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Plane:"
-msgstr ""
+msgstr "Ebene:"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Next Floor"
@@ -7410,6 +7437,10 @@ msgstr "GridMap-Einstellungen"
msgid "Pick Distance:"
msgstr "Auswahlradius:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "Lösungen erzeugen..."
@@ -7440,7 +7471,7 @@ msgstr "Mono"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "About C# support"
-msgstr ""
+msgstr "Über die C#-Unterstützung"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Create C# solution"
@@ -7460,7 +7491,7 @@ msgstr "Warnungen"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "Ende des inneren Exception-Stack-Traces"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7646,7 +7677,7 @@ msgstr "Sequenz"
#: modules/visual_script/visual_script_editor.cpp
msgid "Switch"
-msgstr "Schalter"
+msgstr "Switch"
#: modules/visual_script/visual_script_editor.cpp
msgid "Iterator"
@@ -7654,7 +7685,7 @@ msgstr "Iterator"
#: modules/visual_script/visual_script_editor.cpp
msgid "While"
-msgstr "Während"
+msgstr "While"
#: modules/visual_script/visual_script_editor.cpp
msgid "Return"
@@ -8027,11 +8058,11 @@ msgstr "ARVROrigin benötigt ein ARVRCamera-Unterobjekt"
#: scene/3d/baked_lightmap.cpp
msgid "%d%%"
-msgstr ""
+msgstr "%d%%"
#: scene/3d/baked_lightmap.cpp
msgid "(Time Left: %d:%02d s)"
-msgstr ""
+msgstr "(Verbleibende Zeit: %d:%20d s)"
#: scene/3d/baked_lightmap.cpp
msgid "Plotting Meshes: "
@@ -8132,12 +8163,22 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "Die Pfad-Eigenschaft muss auf ein gültiges Spatial-Node verweisen."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Pro Szene (oder einem Satz von instanzierten Szenen) ist nur ein einziges "
"WorldEnvironment erlaubt."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8239,6 +8280,13 @@ msgstr "Fehler beim Laden der Schriftart."
msgid "Invalid font size."
msgstr "Ungültige Schriftgröße."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr ""
+#~ "Ungültiger Name für Aktion (alle Zeichen außer ‚/‘ und ‚:‘ möglich)."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Darf nicht ‚/‘ oder ‚:‘ beinhalten"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/de_CH.po b/editor/translations/de_CH.po
index a9b1b8a99..ea942bb7c 100644
--- a/editor/translations/de_CH.po
+++ b/editor/translations/de_CH.po
@@ -1385,6 +1385,10 @@ msgstr ""
msgid "Clear Output"
msgstr "Script hinzufügen"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3663,6 +3667,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3954,6 +3966,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5639,6 +5655,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6132,7 +6156,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6277,11 +6303,6 @@ msgid "Delete Item"
msgstr "Node(s) löschen"
#: editor/project_settings_editor.cpp
-#, fuzzy
-msgid "Can't contain '/' or ':'"
-msgstr "Verbindung zu Node:"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7348,6 +7369,10 @@ msgstr "Projekteinstellungen"
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -8031,10 +8056,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "Die Pfad-Variable muss auf einen gültigen Particles2D Node verweisen."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8119,6 +8154,10 @@ msgid "Invalid font size."
msgstr ""
#, fuzzy
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Verbindung zu Node:"
+
+#, fuzzy
#~ msgid "Can't write file."
#~ msgstr "Neues Projekt erstellen"
diff --git a/editor/translations/editor.pot b/editor/translations/editor.pot
index 7a13a5bcc..61d67d708 100644
--- a/editor/translations/editor.pot
+++ b/editor/translations/editor.pot
@@ -488,7 +488,7 @@ msgid "Disconnect '%s' from '%s'"
msgstr ""
#: editor/connections_dialog.cpp
-msgid "Connect.."
+msgid "Connect..."
msgstr ""
#: editor/connections_dialog.cpp
@@ -898,11 +898,11 @@ msgid "Move Audio Bus"
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Save Audio Bus Layout As.."
+msgid "Save Audio Bus Layout As..."
msgstr ""
#: editor/editor_audio_buses.cpp
-msgid "Location for New Layout.."
+msgid "Location for New Layout..."
msgstr ""
#: editor/editor_audio_buses.cpp
@@ -1038,11 +1038,11 @@ msgid "Updating Scene"
msgstr ""
#: editor/editor_data.cpp
-msgid "Storing local changes.."
+msgid "Storing local changes..."
msgstr ""
#: editor/editor_data.cpp
-msgid "Updating scene.."
+msgid "Updating scene..."
msgstr ""
#: editor/editor_data.cpp
@@ -1111,7 +1111,7 @@ msgid "Show In File Manager"
msgstr ""
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
-msgid "New Folder.."
+msgid "New Folder..."
msgstr ""
#: editor/editor_file_dialog.cpp
@@ -1364,17 +1364,21 @@ msgstr ""
msgid "Clear Output"
msgstr ""
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
-msgid "Save Resource As.."
+msgid "Save Resource As..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
#: editor/scene_tree_dock.cpp
-msgid "I see.."
+msgid "I see..."
msgstr ""
#: editor/editor_node.cpp
@@ -1579,11 +1583,11 @@ msgid "Open Base Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Scene.."
+msgid "Quick Open Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Open Script.."
+msgid "Quick Open Script..."
msgstr ""
#: editor/editor_node.cpp
@@ -1595,7 +1599,7 @@ msgid "Save changes to '%s' before closing?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Save Scene As.."
+msgid "Save Scene As..."
msgstr ""
#: editor/editor_node.cpp
@@ -1647,7 +1651,7 @@ msgid "This action cannot be undone. Revert anyway?"
msgstr ""
#: editor/editor_node.cpp
-msgid "Quick Run Scene.."
+msgid "Quick Run Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1792,7 +1796,7 @@ msgid "Previous tab"
msgstr ""
#: editor/editor_node.cpp
-msgid "Filter Files.."
+msgid "Filter Files..."
msgstr ""
#: editor/editor_node.cpp
@@ -1804,11 +1808,11 @@ msgid "New Scene"
msgstr ""
#: editor/editor_node.cpp
-msgid "New Inherited Scene.."
+msgid "New Inherited Scene..."
msgstr ""
#: editor/editor_node.cpp
-msgid "Open Scene.."
+msgid "Open Scene..."
msgstr ""
#: editor/editor_node.cpp
@@ -1828,15 +1832,15 @@ msgid "Open Recent"
msgstr ""
#: editor/editor_node.cpp
-msgid "Convert To.."
+msgid "Convert To..."
msgstr ""
#: editor/editor_node.cpp
-msgid "MeshLibrary.."
+msgid "MeshLibrary..."
msgstr ""
#: editor/editor_node.cpp
-msgid "TileSet.."
+msgid "TileSet..."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
@@ -2081,7 +2085,7 @@ msgid "Save the currently edited resource."
msgstr ""
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
-msgid "Save As.."
+msgid "Save As..."
msgstr ""
#: editor/editor_node.cpp
@@ -2190,7 +2194,7 @@ msgid "Creating Mesh Previews"
msgstr ""
#: editor/editor_plugin.cpp
-msgid "Thumbnail.."
+msgid "Thumbnail..."
msgstr ""
#: editor/editor_plugin_settings.cpp
@@ -2341,7 +2345,7 @@ msgid "(Current)"
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Retrieving mirrors, please wait.."
+msgid "Retrieving mirrors, please wait..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2417,7 +2421,7 @@ msgid "Error requesting url: "
msgstr ""
#: editor/export_template_manager.cpp
-msgid "Connecting to Mirror.."
+msgid "Connecting to Mirror..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2434,7 +2438,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Connecting.."
+msgid "Connecting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2447,7 +2451,7 @@ msgstr ""
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Requesting.."
+msgid "Requesting..."
msgstr ""
#: editor/export_template_manager.cpp
@@ -2579,11 +2583,11 @@ msgid "Collapse all"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Rename.."
+msgid "Rename..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Move To.."
+msgid "Move To..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2595,15 +2599,15 @@ msgid "Instance"
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Edit Dependencies.."
+msgid "Edit Dependencies..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "View Owners.."
+msgid "View Owners..."
msgstr ""
#: editor/filesystem_dock.cpp
-msgid "Duplicate.."
+msgid "Duplicate..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2629,7 +2633,7 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid ""
"Scanning Files,\n"
-"Please Wait.."
+"Please Wait..."
msgstr ""
#: editor/filesystem_dock.cpp
@@ -2695,7 +2699,7 @@ msgid "Import Scene"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Importing Scene.."
+msgid "Importing Scene..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2707,7 +2711,7 @@ msgid "Generating for Mesh: "
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Running Custom Script.."
+msgid "Running Custom Script..."
msgstr ""
#: editor/import/resource_importer_scene.cpp
@@ -2723,7 +2727,7 @@ msgid "Error running post-import script:"
msgstr ""
#: editor/import/resource_importer_scene.cpp
-msgid "Saving.."
+msgid "Saving..."
msgstr ""
#: editor/import_dock.cpp
@@ -2743,7 +2747,7 @@ msgid "Import As:"
msgstr ""
#: editor/import_dock.cpp editor/property_editor.cpp
-msgid "Preset.."
+msgid "Preset..."
msgstr ""
#: editor/import_dock.cpp
@@ -3157,7 +3161,7 @@ msgid "Transition Node"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Import Animations.."
+msgid "Import Animations..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3165,7 +3169,7 @@ msgid "Edit Node Filters"
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
-msgid "Filters.."
+msgid "Filters..."
msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
@@ -3233,7 +3237,7 @@ msgid "Fetching:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Resolving.."
+msgid "Resolving..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3300,7 +3304,7 @@ msgid "Site:"
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
-msgid "Support.."
+msgid "Support..."
msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -3583,6 +3587,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3868,6 +3880,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -3896,7 +3912,7 @@ msgid "Create Convex Collision Sibling"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
-msgid "Create Outline Mesh.."
+msgid "Create Outline Mesh..."
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
@@ -4101,7 +4117,7 @@ msgid "Error loading image:"
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
-msgid "No pixels with transparency > 128 in image.."
+msgid "No pixels with transparency > 128 in image..."
msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
@@ -4462,7 +4478,7 @@ msgid "Import Theme"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-msgid "Save Theme As.."
+msgid "Save Theme As..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4559,7 +4575,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
-msgid "Find.."
+msgid "Find..."
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
@@ -4765,15 +4781,15 @@ msgid "Find Previous"
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Replace.."
+msgid "Replace..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Function.."
+msgid "Goto Function..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
-msgid "Goto Line.."
+msgid "Goto Line..."
msgstr ""
#: editor/plugins/script_text_editor.cpp
@@ -5224,11 +5240,11 @@ msgid "Transform"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Configure Snap.."
+msgid "Configure Snap..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-msgid "Transform Dialog.."
+msgid "Transform Dialog..."
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
@@ -5481,7 +5497,7 @@ msgid "Remove All"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
-msgid "Edit theme.."
+msgid "Edit theme..."
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
@@ -5529,6 +5545,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -5729,7 +5753,7 @@ msgid "Presets"
msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
-msgid "Add.."
+msgid "Add..."
msgstr ""
#: editor/project_export.cpp
@@ -6004,7 +6028,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6032,7 +6058,7 @@ msgid "Control+"
msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
-msgid "Press a Key.."
+msgid "Press a Key..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6148,10 +6174,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -6220,7 +6242,7 @@ msgid "Property:"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Override For.."
+msgid "Override For..."
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6316,11 +6338,11 @@ msgid "Easing Out-In"
msgstr ""
#: editor/property_editor.cpp
-msgid "File.."
+msgid "File..."
msgstr ""
#: editor/property_editor.cpp
-msgid "Dir.."
+msgid "Dir..."
msgstr ""
#: editor/property_editor.cpp
@@ -6491,7 +6513,7 @@ msgid "This operation can't be done on instanced scenes."
msgstr ""
#: editor/scene_tree_dock.cpp
-msgid "Save New Scene As.."
+msgid "Save New Scene As..."
msgstr ""
#: editor/scene_tree_dock.cpp
@@ -7190,6 +7212,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7826,10 +7852,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/el.po b/editor/translations/el.po
index 6eea5da60..2bf8d790a 100644
--- a/editor/translations/el.po
+++ b/editor/translations/el.po
@@ -8,7 +8,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-02-27 22:19+0000\n"
+"PO-Revision-Date: 2018-03-05 16:04+0000\n"
"Last-Translator: George Tsiamasiotis <gtsiam@windowslive.com>\n"
"Language-Team: Greek <https://hosted.weblate.org/projects/godot-engine/godot/"
"el/>\n"
@@ -1394,6 +1394,10 @@ msgstr "Εκκαθάριση"
msgid "Clear Output"
msgstr "Εκκαθάριση εξόδου"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Σφάλμα κατά την αποθήκευση πόρου!"
@@ -3716,6 +3720,16 @@ msgid "Show Guides"
msgstr "Εμφάνιση οδηγών"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Προβολή Αρχής"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 Οπτική γωνία"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Κεντράρισμα επιλογής"
@@ -4005,6 +4019,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Το πλέγμα δεν έχει επιφάνει από την οποία να δημιουργήσει περιγράματα!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Δεν ήταν δυνατή η δημιουργία περιγράμματος!"
@@ -5679,6 +5697,16 @@ msgid "Checked Item"
msgstr "Επιλεγμένο στοιχείο"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Προσθήκη στοιχείου"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Επιλεγμένο στοιχείο"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Έχει"
@@ -6180,8 +6208,10 @@ msgid "Mouse Button"
msgstr "Κουμπί ποντικιού"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Μη έγκυρη ενέργεια (Όλα επιτρέποντα εκτός από το '/' και το ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6324,10 +6354,6 @@ msgid "Delete Item"
msgstr "Διαγραφή αντικειμένου"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "Δεν μπορεί να περιέχει '/' ή ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Υπάρχει ήδη"
@@ -7394,6 +7420,10 @@ msgstr "Ρυθμίσεις GridMap"
msgid "Pick Distance:"
msgstr "Επιλογή απόστασης:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "Δημιουργία λύσης..."
@@ -7444,7 +7474,7 @@ msgstr "Προειδοποιήσεις"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "Τέλος ιχνηλάτησης στοίβας εσωτερικής εξαίρεσης"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -8113,12 +8143,22 @@ msgstr ""
"δουλέψει αυτός ο κόμβος."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Μόνο ένα WorldEnvironment επιτρέπεται σε κάθε σκηνή (ή σύνολο στιγμιοτύπων "
"σκηνών)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8216,6 +8256,12 @@ msgstr "Σφάλμα κατά την φόρτωση της γραμματοσε
msgid "Invalid font size."
msgstr "Μη έγκυρο μέγεθος γραμματοσειράς."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Μη έγκυρη ενέργεια (Όλα επιτρέποντα εκτός από το '/' και το ':')."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Δεν μπορεί να περιέχει '/' ή ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/es.po b/editor/translations/es.po
index b9b342114..86188201c 100644
--- a/editor/translations/es.po
+++ b/editor/translations/es.po
@@ -6,6 +6,7 @@
# Addiel Lucena Perez <addiell2017@gmail.com>, 2017.
# Aleix Sanchis <aleixsanchis@hotmail.com>, 2017.
# Alejandro Alvarez <eliluminado00@gmail.com>, 2017.
+# Avocado <avocadosan42@gmail.com>, 2018.
# BLaDoM GUY <simplybladom@gmail.com>, 2017.
# Carlos López <genetita@gmail.com>, 2016.
# David Arranz <davarrcal@hotmail.com>, 2018.
@@ -13,6 +14,7 @@
# Dharkael <izhe@hotmail.es>, 2017.
# Diego López <diegodario21@gmail.com>, 2017.
# Gustavo Leon <gleondiaz@gmail.com>, 2017-2018.
+# Javier Ocampos <xavier.ocampos@gmail.com>, 2018.
# Juan Quiroga <juanquiroga9@gmail.com>, 2017.
# Kiji Pixel <raccoon.fella@gmail.com>, 2017.
# Lisandro Lorea <lisandrolorea@gmail.com>, 2016-2017.
@@ -24,13 +26,14 @@
# Sebastian Silva <sebastian@fuentelibre.org>, 2016.
# Swyter <swyterzone@gmail.com>, 2016-2017.
# Vazquinhos <vazquinhos@gmail.com>, 2018.
+# Yovani Damián <blackblex@gmail.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-03-03 09:49+0000\n"
-"Last-Translator: David Arranz <davarrcal@hotmail.com>\n"
+"PO-Revision-Date: 2018-05-03 02:11+0000\n"
+"Last-Translator: Javier Ocampos <xavier.ocampos@gmail.com>\n"
"Language-Team: Spanish <https://hosted.weblate.org/projects/godot-engine/"
"godot/es/>\n"
"Language: es\n"
@@ -38,7 +41,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 2.20-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -46,7 +49,7 @@ msgstr "Desactivado"
#: editor/animation_editor.cpp
msgid "All Selection"
-msgstr "Toda la selección"
+msgstr "Toda la Selección"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Time"
@@ -224,31 +227,31 @@ msgstr "Crear"
#: editor/animation_editor.cpp
msgid "Anim Create & Insert"
-msgstr "Crear e insertar animación"
+msgstr "Crear e Insertar Animación"
#: editor/animation_editor.cpp
msgid "Anim Insert Track & Key"
-msgstr "Insertar pista y clave de animación"
+msgstr "Insertar Pista y Clave de Animación"
#: editor/animation_editor.cpp
msgid "Anim Insert Key"
-msgstr "Insertar clave de animación"
+msgstr "Insertar clave de Animación"
#: editor/animation_editor.cpp
msgid "Change Anim Len"
-msgstr "Cambiar duración de animación"
+msgstr "Cambiar duración de Animación"
#: editor/animation_editor.cpp
msgid "Change Anim Loop"
-msgstr "Cambiar repeticiones de animación"
+msgstr "Cambiar bucle de Animación"
#: editor/animation_editor.cpp
msgid "Anim Create Typed Value Key"
-msgstr "Crear clave de valor de tipo para animación"
+msgstr "Crear clave de valor tipado para Animación"
#: editor/animation_editor.cpp
msgid "Anim Insert"
-msgstr "Insertar animación"
+msgstr "Insertar Animación"
#: editor/animation_editor.cpp
msgid "Anim Scale Keys"
@@ -256,7 +259,7 @@ msgstr "Escalar Claves de Animación"
#: editor/animation_editor.cpp
msgid "Anim Add Call Track"
-msgstr "Añadir «call track» de Animación"
+msgstr "Añadir Call Track de Animación"
#: editor/animation_editor.cpp
msgid "Animation zoom."
@@ -264,11 +267,11 @@ msgstr "Zoom de Animación."
#: editor/animation_editor.cpp
msgid "Length (s):"
-msgstr "Duración (s):"
+msgstr "Duración (seg):"
#: editor/animation_editor.cpp
msgid "Animation length (in seconds)."
-msgstr "Duración de Animación (en segundos)."
+msgstr "Duración de la Animación (en segundos)."
#: editor/animation_editor.cpp
msgid "Step (s):"
@@ -346,7 +349,7 @@ msgstr "Relación de Escalado:"
#: editor/animation_editor.cpp
msgid "Call Functions in Which Node?"
-msgstr "¿En qué nodo quieres llamar funciones?"
+msgstr "¿Desde que nodo quieres realizar llamadas a funciones?"
#: editor/animation_editor.cpp
msgid "Remove invalid keys"
@@ -847,15 +850,15 @@ msgstr "Añadir Efecto"
#: editor/editor_audio_buses.cpp
msgid "Rename Audio Bus"
-msgstr "Renombrar bus de audio"
+msgstr "Renombrar Bus de Audio"
#: editor/editor_audio_buses.cpp
msgid "Change Audio Bus Volume"
-msgstr "Cambiar volumen del bus de audio"
+msgstr "Cambiar Volumen de Bus de Audio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Solo"
-msgstr "Alternar Audio Bus Solo"
+msgstr "Act./Desact. Solo de Bus de Audio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Mute"
@@ -867,7 +870,7 @@ msgstr "Alternar puenteado de efectos en Bus de Audio"
#: editor/editor_audio_buses.cpp
msgid "Select Audio Bus Send"
-msgstr "Seleccionar envío a bus de audio"
+msgstr "Seleccionar Envío de Audio Bus"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus Effect"
@@ -879,7 +882,7 @@ msgstr "Mover Efecto de Bus"
#: editor/editor_audio_buses.cpp
msgid "Delete Bus Effect"
-msgstr "Borrar Efecto de Bus"
+msgstr "Eliminar Efecto de Bus"
#: editor/editor_audio_buses.cpp
msgid "Audio Bus, Drag and Drop to rearrange."
@@ -895,7 +898,7 @@ msgstr "Silenciar"
#: editor/editor_audio_buses.cpp
msgid "Bypass"
-msgstr "Puentear"
+msgstr "Bypass"
#: editor/editor_audio_buses.cpp
msgid "Bus options"
@@ -1036,7 +1039,7 @@ msgstr "No está en la ruta de recursos."
#: editor/editor_autoload_settings.cpp
msgid "Add AutoLoad"
-msgstr "Añadir «AutoLoad»"
+msgstr "Añadir AutoLoad"
#: editor/editor_autoload_settings.cpp
msgid "Autoload '%s' already exists!"
@@ -1422,6 +1425,10 @@ msgstr "Borrar todo"
msgid "Clear Output"
msgstr "Borrar salida"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "¡Hubo un error al guardar el recurso!"
@@ -1977,7 +1984,7 @@ msgstr "Ejecutar script"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Export"
-msgstr "Exportar"
+msgstr "Export"
#: editor/editor_node.cpp
msgid "Tools"
@@ -3082,7 +3089,7 @@ msgstr "Autoreproducir al cargar"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Edit Target Blend Times"
-msgstr "Editar tiempos de mezcla de selección"
+msgstr "Editar tiempos de mezcla con otras animaciones"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation Tools"
@@ -3740,6 +3747,16 @@ msgid "Show Guides"
msgstr "Mostrar guías"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Ver origen"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 visor"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Centrar selección"
@@ -4031,6 +4048,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "¡La malla no tiene superficie de la que crear contornos!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "¡No se pudo crear el contorno!"
@@ -4835,7 +4856,7 @@ msgstr "Minúscula"
#: editor/plugins/script_text_editor.cpp
msgid "Capitalize"
-msgstr "Convertir en Mayúsculas"
+msgstr "Letra Capital"
#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp
#: scene/gui/text_edit.cpp
@@ -5702,6 +5723,16 @@ msgid "Checked Item"
msgstr "Casilla de verificación activa"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Añadir elemento"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Casilla de verificación activa"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Tiene"
@@ -6203,8 +6234,10 @@ msgid "Mouse Button"
msgstr "Botón del ratón"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "La acción no es correcta (no puedes utilizar «/» o «:»)."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6347,10 +6380,6 @@ msgid "Delete Item"
msgstr "Eliminar elemento"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "No puede contener '/' o ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Ya existe"
@@ -6730,7 +6759,7 @@ msgid ""
"satisfied."
msgstr ""
"No se pudo guardar la escena nueva. Es posible que no se hayan podido "
-"satisfacer las dependencias (instancias)."
+"resolver las dependencias (instancias)."
#: editor/scene_tree_dock.cpp
msgid "Error saving scene."
@@ -6766,7 +6795,7 @@ msgstr "Cambiar tipo"
#: editor/scene_tree_dock.cpp
msgid "Attach Script"
-msgstr "Adjuntar script"
+msgstr "Añadir Script"
#: editor/scene_tree_dock.cpp
msgid "Clear Script"
@@ -6806,7 +6835,7 @@ msgstr "Filtrar nodos"
#: editor/scene_tree_dock.cpp
msgid "Attach a new or existing script for the selected node."
-msgstr "Adjuntar un script nuevo o existente al nodo seleccionado."
+msgstr "Añadir un script nuevo o existente al nodo seleccionado."
#: editor/scene_tree_dock.cpp
msgid "Clear a script for the selected node."
@@ -6987,7 +7016,7 @@ msgstr "Cargar archivo de script existente"
#: editor/script_create_dialog.cpp
msgid "Language"
-msgstr "Idioma"
+msgstr "Lenguaje"
#: editor/script_create_dialog.cpp
msgid "Inherits"
@@ -7007,7 +7036,7 @@ msgstr "Script integrado"
#: editor/script_create_dialog.cpp
msgid "Attach Node Script"
-msgstr "Adjuntar script de nodo"
+msgstr "Añadir script de nodo"
#: editor/script_editor_debugger.cpp
msgid "Remote "
@@ -7051,11 +7080,11 @@ msgstr "Error de copia"
#: editor/script_editor_debugger.cpp
msgid "Inspect Previous Instance"
-msgstr "Inspeccionar instancia anterior"
+msgstr "Inspeccionar Instancia Anterior"
#: editor/script_editor_debugger.cpp
msgid "Inspect Next Instance"
-msgstr "Inspeccionar instancia siguiente"
+msgstr "Inspeccionar Instancia Siguiente"
#: editor/script_editor_debugger.cpp
msgid "Stack Frames"
@@ -7111,7 +7140,7 @@ msgstr "Tipo"
#: editor/script_editor_debugger.cpp
msgid "Format"
-msgstr "Formato"
+msgstr "Format"
#: editor/script_editor_debugger.cpp
msgid "Usage"
@@ -7243,7 +7272,7 @@ msgstr "Bibliotecas: "
#: modules/gdnative/register_types.cpp
msgid "GDNative"
-msgstr "\"GDNative\""
+msgstr "GDNative"
#: modules/gdscript/gdscript_functions.cpp
#: modules/visual_script/visual_script_builtin_funcs.cpp
@@ -7415,6 +7444,10 @@ msgstr "Ajustes del GridMap"
msgid "Pick Distance:"
msgstr "Seleccionar distancia:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "Generando solución..."
@@ -7465,7 +7498,7 @@ msgstr "Advertencias"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "Fin del reporte de la pila de excepciones"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7651,7 +7684,7 @@ msgstr "Secuencia"
#: modules/visual_script/visual_script_editor.cpp
msgid "Switch"
-msgstr "Interruptor"
+msgstr "Switch"
#: modules/visual_script/visual_script_editor.cpp
msgid "Iterator"
@@ -7667,7 +7700,7 @@ msgstr "Devuelve (\"Return\")"
#: modules/visual_script/visual_script_editor.cpp
msgid "Call"
-msgstr "Llamada (\"Call\")"
+msgstr "Call"
#: modules/visual_script/visual_script_editor.cpp
msgid "Get"
@@ -8132,12 +8165,22 @@ msgstr ""
"La propiedad Path debe apuntar a un nodo Spatial válido para funcionar."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Solo se permite un WorldEnvironment por escena (o conjunto de escenas "
"instanciadas)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8234,6 +8277,12 @@ msgstr "Error al cargar la tipografía."
msgid "Invalid font size."
msgstr "Tamaño de tipografía incorrecto."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "La acción no es correcta (no puedes utilizar «/» o «:»)."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "No puede contener '/' o ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/es_AR.po b/editor/translations/es_AR.po
index f2d650820..304fc7dbc 100644
--- a/editor/translations/es_AR.po
+++ b/editor/translations/es_AR.po
@@ -12,7 +12,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-02-24 21:43+0000\n"
+"PO-Revision-Date: 2018-03-04 06:03+0000\n"
"Last-Translator: Lisandro Lorea <lisandrolorea@gmail.com>\n"
"Language-Team: Spanish (Argentina) <https://hosted.weblate.org/projects/"
"godot-engine/godot/es_AR/>\n"
@@ -1401,6 +1401,10 @@ msgstr "Limpiar"
msgid "Clear Output"
msgstr "Limpiar Salida"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Error al guardar el recurso!"
@@ -3717,6 +3721,16 @@ msgid "Show Guides"
msgstr "Mostrar guías"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Ver Origen"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 Viewport"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Centrar Selección"
@@ -4006,6 +4020,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "El mesh no tiene una superficie de donde crear contornos(outlines)!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "No se pudo crear el outline!"
@@ -5676,6 +5694,16 @@ msgid "Checked Item"
msgstr "Item Tildado"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Agregar Item"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Item Tildado"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Tiene"
@@ -6180,8 +6208,10 @@ msgid "Mouse Button"
msgstr "Botón de Mouse"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Acción Invalida (cualquier cosa va menos '/' o ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6324,10 +6354,6 @@ msgid "Delete Item"
msgstr "Eliminar Ítem"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "No puede contener '/' o ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Ya existe"
@@ -7389,6 +7415,10 @@ msgstr "Ajustes de GridMap"
msgid "Pick Distance:"
msgstr "Elegir Instancia:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "Generando solución..."
@@ -7439,7 +7469,7 @@ msgstr "Advertencias"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "Fin del stack trace de excepción interna"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -8097,12 +8127,22 @@ msgstr ""
"La propiedad Path debe apuntar a un nodo Spatial valido para funcionar."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Solo se permite un WorldEnvironment por escena (o conjunto de escenas "
"instanciadas)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8199,6 +8239,12 @@ msgstr "Error cargando tipografía."
msgid "Invalid font size."
msgstr "Tamaño de tipografía inválido."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Acción Invalida (cualquier cosa va menos '/' o ':')."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "No puede contener '/' o ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/fa.po b/editor/translations/fa.po
index 7bcbeb15f..8b7fdcbb7 100644
--- a/editor/translations/fa.po
+++ b/editor/translations/fa.po
@@ -4,6 +4,7 @@
# This file is distributed under the same license as the Godot source code.
#
# alabd14313 <alabd14313@yahoo.com>, 2016.
+# Dante Marshal <Marshal.Devilhunter@gmail.com>, 2018.
# hamed nasib <cghamed752@chmail.ir>, 2016.
# Hasan Hejdari Nasab <hsn6@openmailbox.org>, 2017.
# rezapouya <r.pouya@chmail.ir>, 2016.
@@ -12,15 +13,15 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2017-11-26 16:49+0000\n"
-"Last-Translator: sayyed hamed nasib <cghamed752@chmail.ir>\n"
+"PO-Revision-Date: 2018-04-29 05:39+0000\n"
+"Last-Translator: Dante Marshal <Marshal.Devilhunter@gmail.com>\n"
"Language-Team: Persian <https://hosted.weblate.org/projects/godot-engine/"
"godot/fa/>\n"
"Language: fa\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 2.18-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -31,9 +32,8 @@ msgid "All Selection"
msgstr "همه‌ی انتخاب ها"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Keyframe Time"
-msgstr "مقدار را در انیمیشن تغییر بده"
+msgstr "تغییر زمان فریم کلید در انیمیشن"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
@@ -44,9 +44,8 @@ msgid "Anim Change Transform"
msgstr "انتقال را در انیمیشن تغییر بده"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Keyframe Value"
-msgstr "مقدار را در انیمیشن تغییر بده"
+msgstr "تغییر مقدار فریم کلید در انیمیشن"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
@@ -54,52 +53,51 @@ msgstr "فراخوانی را در انیمیشن تغییر بده"
#: editor/animation_editor.cpp
msgid "Anim Add Track"
-msgstr ""
+msgstr "افزودن ترَک به انیمیشن"
#: editor/animation_editor.cpp
msgid "Anim Duplicate Keys"
-msgstr ""
+msgstr "تکرار کلید‌های انیمیشن"
#: editor/animation_editor.cpp
msgid "Move Anim Track Up"
-msgstr ""
+msgstr "انتقال ترک انیمشین به بالا"
#: editor/animation_editor.cpp
msgid "Move Anim Track Down"
-msgstr ""
+msgstr "انتقال ترک انیمشین به پایین"
#: editor/animation_editor.cpp
msgid "Remove Anim Track"
-msgstr ""
+msgstr "حذف ترک انیمشین"
#: editor/animation_editor.cpp
msgid "Set Transitions to:"
-msgstr ""
+msgstr "تنظیم گذار‌ها به :"
#: editor/animation_editor.cpp
msgid "Anim Track Rename"
-msgstr ""
+msgstr "تغییر نام ترک انیمشین"
#: editor/animation_editor.cpp
msgid "Anim Track Change Interpolation"
-msgstr ""
+msgstr "تغییر سبک الحاق ترک انیمیشن"
#: editor/animation_editor.cpp
msgid "Anim Track Change Value Mode"
-msgstr ""
+msgstr "تغییر حالت مقدار ترک انیمیشن"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Track Change Wrap Mode"
-msgstr "مقدار را در انیمیشن تغییر بده"
+msgstr "تغییر حالت بسته شدن ترک انیمشین"
#: editor/animation_editor.cpp
msgid "Edit Node Curve"
-msgstr ""
+msgstr "ویرایش منحنی گره"
#: editor/animation_editor.cpp
msgid "Edit Selection Curve"
-msgstr ""
+msgstr "ویرایش منحنی انتخاب شده"
#: editor/animation_editor.cpp
msgid "Anim Delete Keys"
@@ -377,7 +375,6 @@ msgid "No Matches"
msgstr "تطبیقی ندارد"
#: editor/code_editor.cpp
-#, fuzzy
msgid "Replaced %d occurrence(s)."
msgstr "تعداد d% رخداد جایگزین شد."
@@ -430,6 +427,8 @@ msgid ""
"Target method not found! Specify a valid method or attach a script to target "
"Node."
msgstr ""
+"متد هدف پیدا نشد! لطفا یک متد صحیح مشخص کنید یا یک اسکریپت به گره هدف الحاق "
+"کنید."
#: editor/connections_dialog.cpp
msgid "Connect To Node:"
@@ -499,9 +498,8 @@ msgid "Connecting Signal:"
msgstr "اتصال سیگنال:"
#: editor/connections_dialog.cpp
-#, fuzzy
msgid "Disconnect '%s' from '%s'"
-msgstr "'s%' را به 's%' متصل کن"
+msgstr "'s%' را از 's%' جدا کن"
#: editor/connections_dialog.cpp
msgid "Connect.."
@@ -517,9 +515,8 @@ msgid "Signals"
msgstr "سیگنال‌ها"
#: editor/create_dialog.cpp
-#, fuzzy
msgid "Change %s Type"
-msgstr "تغییر نوع"
+msgstr "تغییر نوع %s"
#: editor/create_dialog.cpp editor/project_settings_editor.cpp
#: modules/visual_script/visual_script_editor.cpp
@@ -527,18 +524,17 @@ msgid "Change"
msgstr "تغییر بده"
#: editor/create_dialog.cpp
-#, fuzzy
msgid "Create New %s"
-msgstr "ساختن جدید"
+msgstr "ساختن %s جدید"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp
msgid "Favorites:"
-msgstr ""
+msgstr "برگزیده‌ها:"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
msgid "Recent:"
-msgstr ""
+msgstr "اخیر:"
#: editor/create_dialog.cpp editor/editor_node.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -640,8 +636,9 @@ msgstr ""
"آیا در هر صورت حذف شوند؟(بدون برگشت)"
#: editor/dependency_editor.cpp
+#, fuzzy
msgid "Cannot remove:"
-msgstr ""
+msgstr "ناتوانی در حذف :"
#: editor/dependency_editor.cpp
msgid "Error loading:"
@@ -697,80 +694,82 @@ msgstr "حذف کن"
#: editor/dictionary_property_edit.cpp
msgid "Change Dictionary Key"
-msgstr ""
+msgstr "تغییر کلید دیکشنری"
#: editor/dictionary_property_edit.cpp
msgid "Change Dictionary Value"
-msgstr "تغییر ارزش دیکشنری"
+msgstr "تغییر مقدار دیکشنری"
#: editor/editor_about.cpp
msgid "Thanks from the Godot community!"
-msgstr ""
+msgstr "با تشکر از سوی جامعه‌ی Godot!"
#: editor/editor_about.cpp
msgid "Thanks!"
-msgstr "تشکرات!"
+msgstr "با تشکر !"
#: editor/editor_about.cpp
msgid "Godot Engine contributors"
-msgstr ""
+msgstr "شرکت‌کنندگان در ساخت موتور Godot"
#: editor/editor_about.cpp
msgid "Project Founders"
-msgstr "برپا کننده های پروژه"
+msgstr "بنیان‌گذاران پروژه"
#: editor/editor_about.cpp
msgid "Lead Developer"
-msgstr ""
+msgstr "توسعه‌دهنده‌ی اصلی"
#: editor/editor_about.cpp
-#, fuzzy
msgid "Project Manager "
-msgstr "مدیر پروژه"
+msgstr "مدیر پروژه "
#: editor/editor_about.cpp
msgid "Developers"
-msgstr ""
+msgstr "توسعه دهندگان"
#: editor/editor_about.cpp
msgid "Authors"
msgstr "مؤلفان"
#: editor/editor_about.cpp
+#, fuzzy
msgid "Platinum Sponsors"
-msgstr ""
+msgstr "اسپانسر‌های درجه ۱"
#: editor/editor_about.cpp
+#, fuzzy
msgid "Gold Sponsors"
-msgstr ""
+msgstr "اسپانسر‌های درجه ۲"
#: editor/editor_about.cpp
+#, fuzzy
msgid "Mini Sponsors"
-msgstr ""
+msgstr "اسپانسر‌های دیگر"
#: editor/editor_about.cpp
msgid "Gold Donors"
-msgstr ""
+msgstr "اهداکنندگان طلایی"
#: editor/editor_about.cpp
msgid "Silver Donors"
-msgstr ""
+msgstr "اهداکنندگان نقره‌ای"
#: editor/editor_about.cpp
msgid "Bronze Donors"
-msgstr ""
+msgstr "اهداکنندگان برنزی"
#: editor/editor_about.cpp
msgid "Donors"
-msgstr ""
+msgstr "اهدا کنندگان"
#: editor/editor_about.cpp
msgid "License"
-msgstr ""
+msgstr "مجوز"
#: editor/editor_about.cpp
msgid "Thirdparty License"
-msgstr ""
+msgstr "مجوز‌های شخص ثالث"
#: editor/editor_about.cpp
msgid ""
@@ -1333,7 +1332,7 @@ msgstr ""
#: editor/editor_help.cpp
msgid "Description"
-msgstr "توضیح"
+msgstr "توضیحات"
#: editor/editor_help.cpp
msgid "Online Tutorials:"
@@ -1398,6 +1397,10 @@ msgstr "پاک کردن"
msgid "Clear Output"
msgstr "خروجی"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -2046,7 +2049,7 @@ msgstr "انجمن"
#: editor/editor_node.cpp
msgid "About"
-msgstr "معرفی"
+msgstr "درباره"
#: editor/editor_node.cpp
msgid "Play the project."
@@ -3646,6 +3649,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3934,6 +3945,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5623,6 +5638,15 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "افزودن مورد"
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6113,7 +6137,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6258,10 +6284,6 @@ msgid "Delete Item"
msgstr "حذف مورد"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "نمی‌تواند شامل '/' یا ':' باشد"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "پیش از این وجود داشته است"
@@ -7340,6 +7362,10 @@ msgstr "ترجیحات"
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7582,14 +7608,16 @@ msgid "Sequence"
msgstr "دنباله"
#: modules/visual_script/visual_script_editor.cpp
+#, fuzzy
msgid "Switch"
-msgstr "گزینه"
+msgstr "سوییچ"
#: modules/visual_script/visual_script_editor.cpp
msgid "Iterator"
msgstr "تکرارکننده"
#: modules/visual_script/visual_script_editor.cpp
+#, fuzzy
msgid "While"
msgstr "تا زمانی که"
@@ -8049,12 +8077,22 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "دارایی Path باید به یک گره Particles2D معتبر اشاره کند تا کار کند."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"فقط یک WorldEnvironment در هر صحنه (یا مجموعه ای از صحنه های نمونه‌گذاری شده) "
"مجاز است."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8145,6 +8183,9 @@ msgstr "خطای بارگذاری قلم."
msgid "Invalid font size."
msgstr "اندازهٔ قلم نامعتبر."
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "نمی‌تواند شامل '/' یا ':' باشد"
+
#~ msgid "Can't write file."
#~ msgstr "ناتوان در نوشتن پرونده."
diff --git a/editor/translations/fi.po b/editor/translations/fi.po
index efc69a02b..139983464 100644
--- a/editor/translations/fi.po
+++ b/editor/translations/fi.po
@@ -1437,6 +1437,10 @@ msgstr "Tyhjennä"
msgid "Clear Output"
msgstr "Tuloste"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Virhe tallennettaessa resurssia!"
@@ -3811,6 +3815,16 @@ msgid "Show Guides"
msgstr "Näytä apuviivat"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Näytä origo"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 näyttöruutu"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Valinta keskikohtaan"
@@ -4105,6 +4119,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Ääriviivoja ei voitu luoda!"
@@ -5834,6 +5852,16 @@ msgid "Checked Item"
msgstr "Valittu"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Lisää"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Valittu"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "On"
@@ -6343,8 +6371,10 @@ msgid "Mouse Button"
msgstr "Hiiren painike"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Virheellinen tapahtuma (muut käy, paitsi '/' tai ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6490,11 +6520,6 @@ msgid "Delete Item"
msgstr "Poista syöte"
#: editor/project_settings_editor.cpp
-#, fuzzy
-msgid "Can't contain '/' or ':'"
-msgstr "Yhdistä Nodeen:"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "On jo olemassa"
@@ -7577,6 +7602,10 @@ msgstr "Näyttöruudun asetukset"
msgid "Pick Distance:"
msgstr "Poimi tile"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -8240,10 +8269,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8339,6 +8378,13 @@ msgstr "Virhe fontin latauksessa."
msgid "Invalid font size."
msgstr "Virheellinen fonttikoko."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Virheellinen tapahtuma (muut käy, paitsi '/' tai ':')."
+
+#, fuzzy
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Yhdistä Nodeen:"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/fr.po b/editor/translations/fr.po
index d9fd96081..56969fe97 100644
--- a/editor/translations/fr.po
+++ b/editor/translations/fr.po
@@ -3,7 +3,7 @@
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
#
-# Antoine Carrier <ac.g392@gmail.com>, 2017.
+# Antoine Carrier <ac.g392@gmail.com>, 2017-2018.
# ARocherVj <a.rocher.vj@gmail.com>, 2017.
# Arthur Templé <tuturtemple@gmail.com>, 2018.
# Brice <bbric@free.fr>, 2016.
@@ -13,31 +13,36 @@
# finkiki <specialpopol@gmx.fr>, 2016.
# Gilles Roudiere <gilles.roudiere@gmail.com>, 2017-2018.
# Hugo Locurcio <hugo.l@openmailbox.org>, 2016-2018.
+# John Bernier <john.bp@unknit.net>, 2018.
# Kanabenki <lucien.menassol@gmail.com>, 2017.
-# keltwookie <keltwookie@protonmail.com>, 2017.
+# keltwookie <keltwookie@protonmail.com>, 2017-2018.
+# LL <lu.lecocq@free.fr>, 2018.
# Luc Stepniewski <lior@gradstein.info>, 2017.
# Marc <marc.gilleron@gmail.com>, 2016-2017.
# Nathan Lovato <nathan.lovato.art@gmail.com>, 2017.
# Nicolas <flaithotw@gmail.com>, 2017.
# Nicolas Lehuen <nicolas@lehuen.com>, 2016.
# Nobelix <noe.le.cam@laposte.net>, 2017.
-# Omicron <tritonic.dev@gmail.com>, 2016.
+# Omicron <tritonic.dev@gmail.com>, 2016, 2018.
# Onyx Steinheim <thevoxelmanonyx@gmail.com>, 2016.
# Przemyslaw Gasinski <gasinski.przemek@protonmail.ch>, 2017.
# rafeu <duchainer@gmail.com>, 2016-2017.
+# rawida <rawida@tempinbox.com>, 2018.
# Rémi Verschelde <rverschelde@gmail.com>, 2016-2017.
# Robin Arys <robinarys@hotmail.com>, 2017.
# Roger BR <drai_kin@hotmail.com>, 2016.
+# salty64 <cedric.arrabie@univ-pau.fr>, 2018.
# Thomas Baijot <thomasbaijot@gmail.com>, 2016.
-# Tommy Melançon-Roy <tommel1234@hotmail.com>, 2017.
-# Xananax <xananax@yelostudio.com>, 2017.
+# Tommy Melançon-Roy <tommel1234@hotmail.com>, 2017-2018.
+# Willow <theotimefd@aol.com>, 2018.
+# Xananax <xananax@yelostudio.com>, 2017-2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-02-24 02:36+0000\n"
-"Last-Translator: Cindy Dallaire <c.dallaire93@gmail.com>\n"
+"PO-Revision-Date: 2018-05-02 21:48+0000\n"
+"Last-Translator: Omicron <omicron666.dev@gmail.com>\n"
"Language-Team: French <https://hosted.weblate.org/projects/godot-engine/"
"godot/fr/>\n"
"Language: fr\n"
@@ -45,7 +50,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 2.20-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -61,7 +66,7 @@ msgstr "Changer l'heure de l'animation des images clés"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr "Animation Changer la transition"
+msgstr "Transition du changement d'animation"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
@@ -832,7 +837,7 @@ msgstr "Paquetage installé avec succès !"
#: editor/editor_asset_installer.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Success!"
-msgstr "Succès!"
+msgstr "Succès !"
#: editor/editor_asset_installer.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -861,15 +866,15 @@ msgstr "Modifier le volume audio du bus"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Solo"
-msgstr "Basculer vers transport audio solo"
+msgstr "Activer/désactiver le mode solo pour le bus audio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Mute"
-msgstr "Basculer vers désactivation de transport audio"
+msgstr "Activer/désactiver le mode muet pour le bus audio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Bypass Effects"
-msgstr "Basculer vers effets de contournement de transport audio"
+msgstr "Activer/désactiver le contournement du bus audio"
#: editor/editor_audio_buses.cpp
msgid "Select Audio Bus Send"
@@ -897,11 +902,11 @@ msgstr "Solo"
#: editor/editor_audio_buses.cpp
msgid "Mute"
-msgstr "Assourdir"
+msgstr "Rendre muet"
#: editor/editor_audio_buses.cpp
msgid "Bypass"
-msgstr "Contournement"
+msgstr "Contourner"
#: editor/editor_audio_buses.cpp
msgid "Bus options"
@@ -1104,7 +1109,7 @@ msgstr "Mise à jour de la scène…"
#: editor/editor_data.cpp
msgid "[empty]"
-msgstr "(vide)"
+msgstr "[vide]"
#: editor/editor_data.cpp
msgid "[unsaved]"
@@ -1157,7 +1162,7 @@ msgstr "Le fichier existe, l'écraser ?"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Select Current Folder"
-msgstr "Selectionner le dossier actuel"
+msgstr "Sélectionner le dossier courant"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
msgid "Copy Path"
@@ -1227,7 +1232,7 @@ msgstr "Basculer les fichiers cachés"
#: editor/editor_file_dialog.cpp
msgid "Toggle Favorite"
-msgstr "Basculer le favori"
+msgstr "Basculer l'état favori"
#: editor/editor_file_dialog.cpp
msgid "Toggle Mode"
@@ -1369,8 +1374,8 @@ msgid ""
"$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/"
"url][/color]."
msgstr ""
-"Pas de tutoriels disponibles pour cette classe, vous pouvez [color=$color]"
-"[url=$url]en contribuer un[/url][/color] ou [color=$color][url=$url2]en "
+"Il n'y a pas de tutoriels disponibles pour cette classe, vous pouvez [color="
+"$color][url=$url]en créer un[/url][/color] ou [color=$color][url=$url2]en "
"demander un[/url][/color]."
#: editor/editor_help.cpp
@@ -1386,8 +1391,8 @@ msgid ""
"There is currently no description for this property. Please help us by "
"[color=$color][url=$url]contributing one[/url][/color]!"
msgstr ""
-"Pas de description disponible pour cette propriété. [color=$color][url="
-"$url]Contribuez[/url][/color] pour nous aider!"
+"Il n'y a pas de description disponible pour cette propriété. Aidez-nous en "
+"[color=$color][url=$url]en créant[/url][/color] une !"
#: editor/editor_help.cpp
msgid "Methods"
@@ -1402,8 +1407,8 @@ msgid ""
"There is currently no description for this method. Please help us by [color="
"$color][url=$url]contributing one[/url][/color]!"
msgstr ""
-"Pas de description disponible pour cette méthode. [color=$color][url="
-"$url]Contribuez[/url][/color] pour nous aider!"
+"Il n'y a pas de description disponible pour cette méthode. Aidez-nous en "
+"[color=$color][url=$url]en créant[/url][/color] une !"
#: editor/editor_help.cpp
msgid "Search Text"
@@ -1428,6 +1433,10 @@ msgstr "Effacer"
msgid "Clear Output"
msgstr "Effacer la sortie"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Erreur d'enregistrement de la ressource !"
@@ -1459,7 +1468,7 @@ msgstr "Impossible d'ouvrir '%s'."
#: editor/editor_node.cpp
msgid "Error while parsing '%s'."
-msgstr "Erreur lors de l'analyse de '%s'."
+msgstr "Erreur lors de l'analyse syntaxique de '%s'."
#: editor/editor_node.cpp
msgid "Unexpected end of file '%s'."
@@ -1467,7 +1476,7 @@ msgstr "Fin de fichier inattendue '%s'."
#: editor/editor_node.cpp
msgid "Missing '%s' or its dependencies."
-msgstr "Fichier '%s' ou dépendances manquantes."
+msgstr "La scène '%s' ou ses dépendances sont introuvables."
#: editor/editor_node.cpp
msgid "Error while loading '%s'."
@@ -1539,10 +1548,10 @@ msgid ""
"Please read the documentation relevant to importing scenes to better "
"understand this workflow."
msgstr ""
-"Cette ressource appartient à une scène qui a été importée, elle n'est donc "
-"pas éditable.\n"
-"Veuillez lire la documentation concernant l'import de scènes afin de mieux "
-"comprendre son déroulement."
+"Cette ressource n'est pas éditable car elle appartient à une scène "
+"importée.\n"
+"Merci de lire la documentation concernant l'import des scènes pour en "
+"comprendre le processus."
#: editor/editor_node.cpp
msgid ""
@@ -1550,16 +1559,15 @@ msgid ""
"Changes to it will not be kept when saving the current scene."
msgstr ""
"Cette ressource appartient a une scène qui a été instanciée ou héritée.\n"
-"Ses changements ne seront pas retenus lors de la sauvegarde de la scène "
-"actuelle."
+"Ses modifications seront perdues lors de la sauvegarde de la scène actuelle."
#: editor/editor_node.cpp
msgid ""
"This resource was imported, so it's not editable. Change its settings in the "
"import panel and then re-import."
msgstr ""
-"Cette ressource a été importée, aucune modification n’est possible. Modifiez "
-"ses paramètres dans le menu d'importation et, ensuite, réimportez-la."
+"Cette ressource a été importée, elle n'est donc pas éditable. Modifiez ses "
+"paramètres dans le panneau d'importation et, ensuite, réimportez-la."
#: editor/editor_node.cpp
msgid ""
@@ -1569,9 +1577,9 @@ msgid ""
"understand this workflow."
msgstr ""
"Cette scène a été importée, les changements ne seront donc pas conservés.\n"
-"L'instancier ou en hériter permettra de faire ces changements\n"
+"L'instancier ou l'hériter permettra de faire ces changements.\n"
"Veuillez lire la documentation concernant l'importation des scènes afin de "
-"mieux comprendre ce déroulement."
+"mieux comprendre ce processus."
#: editor/editor_node.cpp
msgid ""
@@ -1579,17 +1587,18 @@ msgid ""
"Please read the documentation relevant to debugging to better understand "
"this workflow."
msgstr ""
-"Ceci est un objet distant, les changements ne seront donc pas conservés.\n"
-"Veuillez lire la documentation concernant le débogage afin de mieux "
-"comprendre ce fonctionnement."
+"Ceci est un objet distant, les changements qui y sont faits ne seront pas "
+"sauvegardés.\n"
+"Merci de lire la section de la documentation portant sur le débogage pour "
+"mieux comprendre ce mécanisme."
#: editor/editor_node.cpp
msgid "Expand all properties"
-msgstr "Développer tout"
+msgstr "Développer toutes les propriétés"
#: editor/editor_node.cpp
msgid "Collapse all properties"
-msgstr "Réduire tout"
+msgstr "Réduire toutes les propriétés"
#: editor/editor_node.cpp
msgid "Copy Params"
@@ -1715,7 +1724,7 @@ msgstr "Exporter une bibliothèque de maillages"
#: editor/editor_node.cpp
msgid "This operation can't be done without a root node."
-msgstr "Cette opération ne peut être réalisée sans nœud racine."
+msgstr "Cette opération ne peut être réalisée sans noeud parent."
#: editor/editor_node.cpp
msgid "Export Tile Set"
@@ -1779,8 +1788,8 @@ msgid ""
"considered a bug. Please report."
msgstr ""
"Cette option est dépréciée. Les situations dans lesquelles un "
-"rafraîchissement doit être forcé sont désormais considérés comme bug. "
-"Veuillez le signaler."
+"rafraîchissement doit être forcé sont désormais considérées comme un bogue. "
+"Merci de le signaler."
#: editor/editor_node.cpp
msgid "Pick a Main Scene"
@@ -1789,8 +1798,8 @@ msgstr "Choisir une scène principale"
#: editor/editor_node.cpp
msgid "Unable to enable addon plugin at: '%s' parsing of config failed."
msgstr ""
-"Impossible d'activer le plugin depuis : '%s' la lecture de la configuration "
-"a échoué."
+"Impossible d'activer le greffon depuis : '%s' l’analyse syntaxique de la "
+"configuration a échoué."
#: editor/editor_node.cpp
msgid "Unable to find script field for addon plugin at: 'res://addons/%s'."
@@ -1845,7 +1854,7 @@ msgstr "La scène « %s » a des dépendences cassées :"
#: editor/editor_node.cpp
msgid "Clear Recent Scenes"
-msgstr "Retirer les scènes récentes"
+msgstr "Vider la liste des scènes récentes"
#: editor/editor_node.cpp
msgid "Save Layout"
@@ -1862,7 +1871,7 @@ msgstr "Par défaut"
#: editor/editor_node.cpp
msgid "Switch Scene Tab"
-msgstr "Basculer entre les onglets de scène"
+msgstr "Basculer entre onglets de scène"
#: editor/editor_node.cpp
msgid "%d more files or folders"
@@ -1886,7 +1895,7 @@ msgstr "Mode Sans Distraction"
#: editor/editor_node.cpp
msgid "Toggle distraction-free mode."
-msgstr "Basculer vers mode sans-distraction."
+msgstr "Basculer en mode sans distraction."
#: editor/editor_node.cpp
msgid "Add a new scene."
@@ -2093,7 +2102,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Editor"
-msgstr "Editeur"
+msgstr "Éditeur"
#: editor/editor_node.cpp editor/settings_config_dialog.cpp
msgid "Editor Settings"
@@ -2105,7 +2114,7 @@ msgstr "Disposition de l'éditeur"
#: editor/editor_node.cpp
msgid "Toggle Fullscreen"
-msgstr "Basculer le mode plein écran"
+msgstr "Activer/Désactiver le plein écran"
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Manage Export Templates"
@@ -2240,7 +2249,7 @@ msgstr "Propriétés de l'objet."
#: editor/editor_node.cpp
msgid "Changes may be lost!"
-msgstr "Les changements risquent d’être perdus !"
+msgstr "Les modifications risquent d'être perdues !"
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
#: editor/project_manager.cpp
@@ -2309,7 +2318,7 @@ msgstr "Ouvrir éditeur 3D"
#: editor/editor_node.cpp
msgid "Open Script Editor"
-msgstr "Ouvrir éditeur de script"
+msgstr "Ouvrir l'éditeur de script"
#: editor/editor_node.cpp editor/project_manager.cpp
msgid "Open Asset Library"
@@ -2317,11 +2326,11 @@ msgstr "Ouvrir bibliothèque de ressource"
#: editor/editor_node.cpp
msgid "Open the next Editor"
-msgstr "Ouvrir éditeur suivant"
+msgstr "Ouvrir l'éditeur suivant"
#: editor/editor_node.cpp
msgid "Open the previous Editor"
-msgstr "Ouvrir éditeur précédant"
+msgstr "Ouvrir l'éditeur précédant"
#: editor/editor_plugin.cpp
msgid "Creating Mesh Previews"
@@ -2390,7 +2399,7 @@ msgstr "Inclusif"
#: editor/editor_profiler.cpp
msgid "Self"
-msgstr "Soi"
+msgstr "Self"
#: editor/editor_profiler.cpp
msgid "Frame #:"
@@ -2398,7 +2407,7 @@ msgstr "Frame # :"
#: editor/editor_profiler.cpp
msgid "Time"
-msgstr "Temps :"
+msgstr "Temps"
#: editor/editor_profiler.cpp
msgid "Calls"
@@ -2442,7 +2451,7 @@ msgstr "Avez-vous oublié la méthode « _run » ?"
#: editor/editor_settings.cpp
msgid "Default (Same as Editor)"
-msgstr "Par défaut (même que l'éditeur)"
+msgstr "Par défaut (le même que l'éditeur)"
#: editor/editor_sub_scene.cpp
msgid "Select Node(s) to Import"
@@ -2517,9 +2526,8 @@ msgid ""
"No download links found for this version. Direct download is only available "
"for official releases."
msgstr ""
-"Aucun lien de téléchargement n'a été trouvé pour cette version. Le "
-"téléchargement direct est uniquement disponible pour les versions "
-"officielles."
+"Aucun lien de téléchargement trouvé pour cette version. Le téléchargement "
+"direct est uniquement disponible pour les versions officielles."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -2539,7 +2547,7 @@ msgstr "Pas de réponse."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Request Failed."
-msgstr "Requête échouée."
+msgstr "Échec de la requête."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -2645,7 +2653,9 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid "Cannot navigate to '%s' as it has not been found in the file system!"
-msgstr "Impossible d'accédez à '%s' car celui-ci n'a pas été trouvé !"
+msgstr ""
+"Impossible d'accédez à '%s' car celui-ci n'existe pas dans le système de "
+"fichiers !"
#: editor/filesystem_dock.cpp
msgid "View items as a grid of thumbnails"
@@ -2683,11 +2693,11 @@ msgstr "Impossible de mettre à jour les dépendences :"
#: editor/filesystem_dock.cpp
msgid "No name provided"
-msgstr "Aucun nom fourni"
+msgstr "Aucun nom renseigné"
#: editor/filesystem_dock.cpp
msgid "Provided name contains invalid characters"
-msgstr "Le nom fourni contient des caractères invalides"
+msgstr "Le nom renseigné contient des caractères invalides"
#: editor/filesystem_dock.cpp
msgid "No name provided."
@@ -3093,7 +3103,7 @@ msgstr "Lecture automatique au chargement"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Edit Target Blend Times"
-msgstr "Modifier les temps de mélange des cibles"
+msgstr "Modifier les temps de mélange de la cible"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Animation Tools"
@@ -3141,7 +3151,7 @@ msgstr "3 étapes"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Differences Only"
-msgstr "seul les différence"
+msgstr "Différences seules"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Force White Modulate"
@@ -3466,7 +3476,7 @@ msgstr "Officiel"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Testing"
-msgstr "En test"
+msgstr "Tester"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Assets ZIP File"
@@ -3558,11 +3568,11 @@ msgstr "créer un nouveau guide horizontal"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Remove horizontal guide"
-msgstr "Créer un nouveau guide horizontal"
+msgstr "Supprimer le guide horizontal"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Create new horizontal and vertical guides"
-msgstr "Créer des nouveaux guides horizontaux et verticaux"
+msgstr "Créer de nouveaux guides horizontaux et verticaux"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Edit IK Chain"
@@ -3637,7 +3647,7 @@ msgstr "Mode navigation"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Toggles snapping"
-msgstr "Active le magnétisme"
+msgstr "Activer/Désactiver le magnétisme de grille"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Use Snap"
@@ -3645,7 +3655,7 @@ msgstr "Aligner sur la grille"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snapping options"
-msgstr "Options de magnétisme"
+msgstr "Options du magnétisme"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to grid"
@@ -3657,7 +3667,7 @@ msgstr "Rotation alignée"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Configure Snap..."
-msgstr "Configurer la grille…"
+msgstr "Configurer le magnétisme…"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap Relative"
@@ -3669,7 +3679,7 @@ msgstr "Aligner au pixel près"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Smart snapping"
-msgstr "Alignement intelligent"
+msgstr "Magnétisme intelligent"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to parent"
@@ -3677,15 +3687,15 @@ msgstr "Aimanter au parent"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node anchor"
-msgstr "Accrocher à l'ancre du noeud"
+msgstr "Accrocher à l'ancre du nœud"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node sides"
-msgstr "Accrocher aux flancs du noeud"
+msgstr "Accrocher aux flancs du nœud"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to other nodes"
-msgstr "Accrocher aux autres noeuds"
+msgstr "Accrocher aux autres nœuds"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to guides"
@@ -3752,6 +3762,16 @@ msgid "Show Guides"
msgstr "Montrer les guides"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Afficher l'origine"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 vue"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Centrer sur la sélection"
@@ -4041,6 +4061,10 @@ msgstr ""
"Le maillage n'a pas de surfaces où des contours pourraient être créées !"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Impossible de créer le contour !"
@@ -4219,7 +4243,7 @@ msgstr "Calcul de la taille de la grille..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Creating heightfield..."
-msgstr "Création de la heightmap…"
+msgstr "Création du champ de hauteur…"
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Marking walkable triangles..."
@@ -4227,7 +4251,7 @@ msgstr "Marquage des triangles parcourables..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Constructing compact heightfield..."
-msgstr "Construction d'une heightmap compacte..."
+msgstr "Construction d'un champ de hauteur compact..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Eroding walkable area..."
@@ -4734,7 +4758,7 @@ msgstr "Lancer"
#: editor/plugins/script_editor_plugin.cpp
msgid "Toggle Scripts Panel"
-msgstr "Basculer vers le panneau de scripts"
+msgstr "Afficher/Cacher la panneau des scripts"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4769,7 +4793,7 @@ msgstr "Garder le débogueur ouvert"
#: editor/plugins/script_editor_plugin.cpp
msgid "Debug with external editor"
-msgstr "Deboguer avec un éditeur externe"
+msgstr "Déboguer avec un éditeur externe"
#: editor/plugins/script_editor_plugin.cpp
msgid "Open Godot online documentation"
@@ -4912,7 +4936,7 @@ msgstr "Convertir indentations en espaces"
#: editor/plugins/script_text_editor.cpp
msgid "Convert Indent To Tabs"
-msgstr "Convertir indentations en espaces"
+msgstr "Convertir les indentations en tabulations"
#: editor/plugins/script_text_editor.cpp
msgid "Auto Indent"
@@ -4997,7 +5021,7 @@ msgstr "Modifier un opérateur RVB"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Toggle Rot Only"
-msgstr "Basculer seulement la rotation"
+msgstr "Basculer en mode rotation seule"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Function"
@@ -5165,7 +5189,7 @@ msgstr "Vertex"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "FPS"
-msgstr "Images par secondes"
+msgstr "IPS"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Top View."
@@ -5405,7 +5429,7 @@ msgstr "Outil échelle"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Toggle Freelook"
-msgstr "Activer mode vue libre"
+msgstr "Basculer en vue libre"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform"
@@ -5578,7 +5602,7 @@ msgstr "Insérer vide (après)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Move (Before)"
-msgstr "Déplacer avant"
+msgstr "Déplacer (Avant)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Move (After)"
@@ -5674,7 +5698,7 @@ msgstr "Éditer le thème..."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme editing menu."
-msgstr "Menu d'édition des thèmes..."
+msgstr "Menu d'édition des thèmes."
#: editor/plugins/theme_editor_plugin.cpp
msgid "Add Class Items"
@@ -5694,7 +5718,7 @@ msgstr "Créer un nouveau modèle d'éditeur"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Create From Current Editor Theme"
-msgstr "Créer à partir du thème éditeur actuel"
+msgstr "Créer à partir du thème actuel de l'éditeur"
#: editor/plugins/theme_editor_plugin.cpp
msgid "CheckBox Radio1"
@@ -5717,6 +5741,16 @@ msgid "Checked Item"
msgstr "Item coché"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Ajouter un item"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Item coché"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Possède"
@@ -5877,21 +5911,24 @@ msgid ""
"Select sub-tile to use as icon, this will be also used on invalid autotile "
"bindings."
msgstr ""
+"Sélectionne une ressource à utiliser comme icône, celle-ci sera aussi "
+"utilisée sur les liaisons autotile invalides."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid ""
"LMB: set bit on.\n"
"RMB: set bit off."
msgstr ""
+"Clic gauche : Activer\n"
+"Clic droit : Désactiver"
#: editor/plugins/tile_set_editor_plugin.cpp
-#, fuzzy
msgid "Select current edited sub-tile."
-msgstr "Enregistrer la ressource actuellement modifiée."
+msgstr "Enregistrer la ressource en cours de modification."
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Select sub-tile to change its priority."
-msgstr ""
+msgstr "Sélectionner une sous-case pour changer sa priorité."
#: editor/progress_dialog.cpp scene/gui/dialogs.cpp
msgid "Cancel"
@@ -5899,7 +5936,7 @@ msgstr "Annuler"
#: editor/project_export.cpp
msgid "Runnable"
-msgstr "Activable"
+msgstr "Exécutable"
#: editor/project_export.cpp
msgid "Delete patch '%s' from list?"
@@ -5961,7 +5998,7 @@ msgstr ""
#: editor/project_export.cpp
msgid "Patches"
-msgstr "Patches"
+msgstr "Patchs"
#: editor/project_export.cpp
msgid "Make Patch"
@@ -6004,16 +6041,14 @@ msgid "Please choose a 'project.godot' file."
msgstr "Veuillez choisir un fichier 'project.godot'."
#: editor/project_manager.cpp
-#, fuzzy
msgid "Please choose an empty folder."
-msgstr "Veuillez choisir un fichier 'project.godot'."
+msgstr "Veuillez choisir un dossier vide."
#: editor/project_manager.cpp
msgid "Imported Project"
msgstr "Projet importé"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Couldn't create folder."
msgstr "Impossible de créer le dossier."
@@ -6030,12 +6065,12 @@ msgid "Invalid project path (changed anything?)."
msgstr "Chemin de projet non valide (avez-vous changé quelque chose ?)."
#: editor/project_manager.cpp
-#, fuzzy
msgid ""
"Couldn't load project.godot in project path (error %d). It may be missing or "
"corrupted."
msgstr ""
-"Impossible de modifier le fichier project.godot dans le chemin du projet."
+"Impossible de charger le fichier project.godot dans le chemin du projet "
+"(erreur %d). Le fichier est manquant ou corrompu."
#: editor/project_manager.cpp
msgid "Couldn't edit project.godot in project path."
@@ -6063,27 +6098,24 @@ msgid "Import Existing Project"
msgstr "Importer un projet existant"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Import & Edit"
-msgstr "Importer et ouvrir"
+msgstr "Importer et Modifier"
#: editor/project_manager.cpp
msgid "Create New Project"
msgstr "Créer un nouveau projet"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Create & Edit"
-msgstr "Créer Émetteur"
+msgstr "Créer et ouvrir"
#: editor/project_manager.cpp
msgid "Install Project:"
msgstr "Installer projet :"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Install & Edit"
-msgstr "Installer"
+msgstr "Installer et ouvrir"
#: editor/project_manager.cpp
msgid "Project Name:"
@@ -6219,8 +6251,10 @@ msgid "Mouse Button"
msgstr "Bouton de souris"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Action invalide (tout passe, sauf « / » ou « : »)."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6303,9 +6337,8 @@ msgid "Joypad Button Index:"
msgstr "Index de bouton de la manette de jeu :"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Erase Input Action"
-msgstr "Effacer l'événement d'action d'entrée"
+msgstr "Effacer l'action d'entrée"
#: editor/project_settings_editor.cpp
msgid "Erase Input Action Event"
@@ -6348,9 +6381,8 @@ msgid "Add Global Property"
msgstr "Ajouter une propriété globale"
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Select a setting item first!"
-msgstr "Choisissez d'abord un élément de réglage !"
+msgstr "Sélectionnez d'abord un élément à configurer !"
#: editor/project_settings_editor.cpp
msgid "No property '%s' exists."
@@ -6365,10 +6397,6 @@ msgid "Delete Item"
msgstr "Supprimer élément"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "Ne peut pas contenir de '/' ou ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Existe déjà"
@@ -6589,9 +6617,8 @@ msgid "On"
msgstr "Activé"
#: editor/property_editor.cpp
-#, fuzzy
msgid "[Empty]"
-msgstr "Ajouter vide"
+msgstr "[Vide]"
#: editor/property_editor.cpp modules/visual_script/visual_script_editor.cpp
msgid "Set"
@@ -6685,7 +6712,7 @@ msgstr "Instancier scène(s)"
#: editor/scene_tree_dock.cpp
msgid "This operation can't be done on the tree root."
-msgstr "Cette opération ne peut être réalisée sur la racine de l'arbre."
+msgstr "Cette opération ne peut être réalisée sur la racine de l'arborescence."
#: editor/scene_tree_dock.cpp
msgid "Move Node In Parent"
@@ -6760,7 +6787,6 @@ msgid "Error duplicating scene to save it."
msgstr "Erreur de duplication de la scène afin de l'enregistrer."
#: editor/scene_tree_dock.cpp
-#, fuzzy
msgid "Sub-Resources"
msgstr "Ressources secondaires :"
@@ -6907,7 +6933,7 @@ msgstr ""
#: editor/scene_tree_editor.cpp
msgid "Toggle Visibility"
-msgstr "Basculer visibilité"
+msgstr "Basculer la visibilité"
#: editor/scene_tree_editor.cpp
msgid "Invalid node name, the following characters are not allowed:"
@@ -7056,7 +7082,7 @@ msgstr "Fonction :"
#: editor/script_editor_debugger.cpp
msgid "Pick one or more items from the list to display the graph."
msgstr ""
-"Chosissez un ou plusieurs éléments dans la liste pour afficher le graphique."
+"Sélectionnez un ou plusieurs éléments de la liste pour afficher le graphique."
#: editor/script_editor_debugger.cpp modules/mono/editor/mono_bottom_panel.cpp
msgid "Errors"
@@ -7067,9 +7093,8 @@ msgid "Child Process Connected"
msgstr "Processus enfant connecté"
#: editor/script_editor_debugger.cpp
-#, fuzzy
msgid "Copy Error"
-msgstr "Erreurs de chargement"
+msgstr "Erreurs de copie"
#: editor/script_editor_debugger.cpp
msgid "Inspect Previous Instance"
@@ -7224,47 +7249,40 @@ msgid "Select dependencies of the library for this entry"
msgstr "Sélectionnez les dépendances de la librairie pour cette entrée"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
-#, fuzzy
msgid "Remove current entry"
-msgstr "Supprimer point de courbe"
+msgstr "Supprimer l’entrée"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Double click to create a new entry"
msgstr "Double-cliquez pour créer une nouvelle entrée"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
-#, fuzzy
msgid "Platform:"
-msgstr "Platform:"
+msgstr "Plateforme :"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
-#, fuzzy
msgid "Platform"
-msgstr "Copier vers la plate-forme…"
+msgstr "Plateforme"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
-#, fuzzy
msgid "Dynamic Library"
-msgstr "Bibliothèque"
+msgstr "Bibliothèque dynamique"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
-#, fuzzy
msgid "Add an architecture entry"
-msgstr "Ajouter une entrée architecturale"
+msgstr "Ajouter une entrée architecture"
#: modules/gdnative/gdnative_library_editor_plugin.cpp
-#, fuzzy
msgid "GDNativeLibrary"
-msgstr "GDNative"
+msgstr "GDNativeLibrary"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
msgid "Library"
msgstr "Bibliothèque"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
-#, fuzzy
msgid "Status"
-msgstr "État :"
+msgstr "État"
#: modules/gdnative/gdnative_library_singleton_editor.cpp
msgid "Libraries: "
@@ -7326,25 +7344,22 @@ msgid "Object can't provide a length."
msgstr "L'objet ne peut fournir une longueur."
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Next Plane"
-msgstr "Onglet suivant"
+msgstr "Plan suivant"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Previous Plane"
-msgstr "Onglet precedent"
+msgstr "Plan précédent"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Plane:"
-msgstr "Onglet :"
+msgstr "Plan :"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Next Floor"
msgstr "Étage suivant"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Previous Floor"
msgstr "Onglet précédent"
@@ -7444,34 +7459,33 @@ msgstr "Paramètres GridMap"
msgid "Pick Distance:"
msgstr "Choisissez distance :"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Generating solution..."
-msgstr "Création des coutours..."
+msgstr "Génération de la solution en cours..."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating C# project..."
msgstr "Création du projet C# ..."
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Failed to create solution."
-msgstr "Impossible de créer le contour !"
+msgstr "Impossible de créer la solution."
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Failed to save solution."
-msgstr "Impossible de charger la ressource."
+msgstr "Impossible de sauvegarder la solution."
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Done"
msgstr "C'est fait !"
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Failed to create C# project."
-msgstr "Impossible de charger la ressource."
+msgstr "Impossible de créer le projet C#."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Mono"
@@ -7482,27 +7496,24 @@ msgid "About C# support"
msgstr "À propos du support C#"
#: modules/mono/editor/godotsharp_editor.cpp
-#, fuzzy
msgid "Create C# solution"
-msgstr "Créer le contour"
+msgstr "Créer la solution C#"
#: modules/mono/editor/mono_bottom_panel.cpp
msgid "Builds"
msgstr "Constructions"
#: modules/mono/editor/mono_bottom_panel.cpp
-#, fuzzy
msgid "Build Project"
-msgstr "Projet"
+msgstr "Compiler le projet"
#: modules/mono/editor/mono_bottom_panel.cpp
-#, fuzzy
msgid "Warnings"
-msgstr "Avertissement"
+msgstr "Avertissements"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "Fin de la trace d'appel (stack trace) intrinsèque"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7865,34 +7876,28 @@ msgid "Run exported HTML in the system's default browser."
msgstr "Exécutez le HTML exporté dans le navigateur par défaut du système."
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Could not write file:"
-msgstr "Impossible d'écrire le fichier:\n"
+msgstr "Impossible d'écrire le fichier :"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Could not open template for export:"
-msgstr "Impossible d'ouvrir le modèle pour exportation:\n"
+msgstr "Impossible d'ouvrir le modèle pour exportation :"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Invalid export template:"
-msgstr "Modèle d'exportation non valide :\n"
+msgstr "Modèle d'exportation non valide :"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Could not read custom HTML shell:"
-msgstr "Impossible de lire le shell HTML :\n"
+msgstr "Impossible de lire le shell HTML personnalisé :"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Could not read boot splash image file:"
-msgstr "Impossible de lire l'image de démarrage :\n"
+msgstr "Impossible de lire l'image de démarrage :"
#: platform/javascript/export/export.cpp
-#, fuzzy
msgid "Using default boot splash image."
-msgstr "Impossible de lire l'image de démarrage :\n"
+msgstr "Impossible de lire l'image de démarrage."
#: scene/2d/animated_sprite.cpp
msgid ""
@@ -7916,6 +7921,9 @@ msgid ""
"Consider adding CollisionShape2D or CollisionPolygon2D children nodes to "
"define its shape."
msgstr ""
+"Ce nœud n'a aucune forme enfant, et ne peut donc interagir avec l'espace.\n"
+"Considérez ajouter un nœud enfant CollisionShape2D ou un CollisionPolygon2D "
+"pour définir sa forme."
#: scene/2d/collision_polygon_2d.cpp
msgid ""
@@ -8066,30 +8074,27 @@ msgstr "ARVROrigin requiert un nœud enfant ARVRCamera"
#: scene/3d/baked_lightmap.cpp
msgid "%d%%"
-msgstr ""
+msgstr "%d%%"
#: scene/3d/baked_lightmap.cpp
msgid "(Time Left: %d:%02d s)"
-msgstr ""
+msgstr "(Temps restant: %d:%02d s)"
#: scene/3d/baked_lightmap.cpp
-#, fuzzy
msgid "Plotting Meshes: "
-msgstr "Tracer les maillages"
+msgstr "Tracer les maillages : "
#: scene/3d/baked_lightmap.cpp
-#, fuzzy
msgid "Plotting Lights:"
-msgstr "Tracer les maillages"
+msgstr "Tracer les lumières :"
#: scene/3d/baked_lightmap.cpp scene/3d/gi_probe.cpp
msgid "Finishing Plot"
msgstr "Finalisation du tracer"
#: scene/3d/baked_lightmap.cpp
-#, fuzzy
msgid "Lighting Meshes: "
-msgstr "Tracer les maillages"
+msgstr "Tracer les maillages : "
#: scene/3d/collision_object.cpp
msgid ""
@@ -8097,6 +8102,9 @@ msgid ""
"Consider adding CollisionShape or CollisionPolygon children nodes to define "
"its shape."
msgstr ""
+"Ce nœud n'a aucune forme enfant, il ne peut donc interagir avec l'espace.\n"
+"Considérez ajouter un nœud enfant CollisionShape ou CollisionPolygon pour "
+"définir sa forme."
#: scene/3d/collision_polygon.cpp
msgid ""
@@ -8169,12 +8177,22 @@ msgstr ""
"La propriété Path doit pointer vers un nœud Spatial valide pour fonctionner."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Seul un WorldEnvironnement ne peut être utilisé par scène (ou ensemble de "
"scènes instanciées)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8188,8 +8206,8 @@ msgid ""
"VehicleWheel serves to provide a wheel system to a VehicleBody. Please use "
"it as a child of a VehicleBody."
msgstr ""
-"VehicleWheel ajoute un système de roues à un VehicleBody. Veuillez "
-"l'utiliser en tant qu'enfant d'un VehicleBody."
+"VehicleWheel permet de fournir un système de roue à un VehicleBody. Merci de "
+"l'utiliser comme enfant d'un VehicleBody."
#: scene/gui/color_picker.cpp
msgid "Raw Mode"
@@ -8209,7 +8227,7 @@ msgstr "Veuillez confirmer…"
#: scene/gui/file_dialog.cpp
msgid "Select this Folder"
-msgstr "Sélectionnez ce dossier"
+msgstr "Sélectionner ce dossier"
#: scene/gui/popup.cpp
msgid ""
@@ -8237,13 +8255,12 @@ msgid "(Other)"
msgstr "(Autre)"
#: scene/main/scene_tree.cpp
-#, fuzzy
msgid ""
"Default Environment as specified in Project Settings (Rendering -> "
"Environment -> Default Environment) could not be loaded."
msgstr ""
"L'environnement par défaut spécifié dans les réglages du projet (Rendu -> "
-"Viewport -> Environnement par défaut) ne peut pas être chargé."
+"Environnement -> Environnement par défaut) ne peut pas être chargé."
#: scene/main/viewport.cpp
msgid ""
@@ -8273,6 +8290,12 @@ msgstr "Erreur lors du chargement de la police."
msgid "Invalid font size."
msgstr "Taille de police invalide."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Action invalide (tout passe, sauf « / » ou « : »)."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Ne peut pas contenir de '/' ou ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/he.po b/editor/translations/he.po
index a5f727b8e..ee0a66cec 100644
--- a/editor/translations/he.po
+++ b/editor/translations/he.po
@@ -3,6 +3,7 @@
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
#
+# Daniel <lorddaniel09@gmail.com>, 2018.
# Ben Golan <golanben4@gmail.com>, 2017.
# Luc Stepniewski <lior@gradstein.info>, 2017.
# Yaron Shahrabani <sh.yaron@gmail.com>, 2018.
@@ -10,8 +11,8 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-02-22 12:37+0000\n"
-"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n"
+"PO-Revision-Date: 2018-04-18 16:38+0000\n"
+"Last-Translator: Daniel <lorddaniel09@gmail.com>\n"
"Language-Team: Hebrew <https://hosted.weblate.org/projects/godot-engine/"
"godot/he/>\n"
"Language: he\n"
@@ -19,7 +20,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=4; plural=(n == 1) ? 0 : ((n == 2) ? 1 : ((n > 10 && "
"n % 10 == 0) ? 2 : 3));\n"
-"X-Generator: Weblate 2.20-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -31,19 +32,19 @@ msgstr "כל הבחירה"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Time"
-msgstr ""
+msgstr "שינוי זמן פריים-מפתח אנימציה"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr ""
+msgstr "שינוי מיקום אנימציה"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
-msgstr ""
+msgstr "שינוי מיקום אנימציה"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Value"
-msgstr ""
+msgstr "שינוי ערך פריים-מפתח אנימציה"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
@@ -1373,6 +1374,10 @@ msgstr "מחיקה"
msgid "Clear Output"
msgstr "מחיקת הפלט"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "שגיאה בשמירת המשאב!"
@@ -3603,6 +3608,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3888,6 +3901,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -4658,7 +4675,7 @@ msgstr "שמירה מחדש"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Debugger"
-msgstr "מנפה שגיאות"
+msgstr "ניפוי שגיאות"
#: editor/plugins/script_editor_plugin.cpp
msgid ""
@@ -5391,7 +5408,7 @@ msgstr ""
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Animations"
-msgstr "הנפשות"
+msgstr "אנימציות"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Speed (FPS):"
@@ -5554,6 +5571,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -5963,6 +5988,8 @@ msgid ""
"Language changed.\n"
"The UI will update next time the editor or project manager starts."
msgstr ""
+"השפה הוחלפה.\n"
+"מנשק המשתמש יתעדכן בפעם הבאה שהעורך או מנהל המיזמים מתחיל."
#: editor/project_manager.cpp
msgid ""
@@ -5972,11 +5999,11 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Project Manager"
-msgstr ""
+msgstr "מנהל המיזמים"
#: editor/project_manager.cpp
msgid "Project List"
-msgstr ""
+msgstr "רשימת המיזמים"
#: editor/project_manager.cpp
msgid "Scan"
@@ -5984,27 +6011,27 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Select a Folder to Scan"
-msgstr ""
+msgstr "נא לבחור תיקייה לסריקה"
#: editor/project_manager.cpp
msgid "New Project"
-msgstr ""
+msgstr "מיזם חדש"
#: editor/project_manager.cpp
msgid "Templates"
-msgstr ""
+msgstr "תבניות"
#: editor/project_manager.cpp
msgid "Exit"
-msgstr ""
+msgstr "יציאה"
#: editor/project_manager.cpp
msgid "Restart Now"
-msgstr ""
+msgstr "להפעיל מחדש כעת"
#: editor/project_manager.cpp
msgid "Can't run project"
-msgstr ""
+msgstr "לא ניתן להריץ מיזם"
#: editor/project_manager.cpp
msgid ""
@@ -6014,7 +6041,7 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Key "
-msgstr ""
+msgstr "מקש "
#: editor/project_settings_editor.cpp
msgid "Joy Button"
@@ -6026,15 +6053,17 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Mouse Button"
-msgstr ""
+msgstr "כפתור עכבר"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
-msgstr ""
+msgstr "הפעולה ‚%s’ כבר קיימת!"
#: editor/project_settings_editor.cpp
msgid "Rename Input Action Event"
@@ -6058,47 +6087,47 @@ msgstr ""
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "Press a Key.."
-msgstr ""
+msgstr "נא ללחוץ על מקש…"
#: editor/project_settings_editor.cpp
msgid "Mouse Button Index:"
-msgstr ""
+msgstr "מפתח כפתורי עכבר:"
#: editor/project_settings_editor.cpp
msgid "Left Button"
-msgstr ""
+msgstr "כפתור שמאלי"
#: editor/project_settings_editor.cpp
msgid "Right Button"
-msgstr ""
+msgstr "כפתור ימני"
#: editor/project_settings_editor.cpp
msgid "Middle Button"
-msgstr ""
+msgstr "כפתור אמצעי"
#: editor/project_settings_editor.cpp
msgid "Wheel Up Button"
-msgstr ""
+msgstr "כפתור גלגלת למעלה"
#: editor/project_settings_editor.cpp
msgid "Wheel Down Button"
-msgstr ""
+msgstr "כפתור גלגלת למטה"
#: editor/project_settings_editor.cpp
msgid "Button 6"
-msgstr ""
+msgstr "כפתור 6"
#: editor/project_settings_editor.cpp
msgid "Button 7"
-msgstr ""
+msgstr "כפתור 7"
#: editor/project_settings_editor.cpp
msgid "Button 8"
-msgstr ""
+msgstr "כפתור 8"
#: editor/project_settings_editor.cpp
msgid "Button 9"
-msgstr ""
+msgstr "כפתור 9"
#: editor/project_settings_editor.cpp
msgid "Joypad Axis Index:"
@@ -6122,47 +6151,47 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Add Event"
-msgstr ""
+msgstr "הוספת אירוע"
#: editor/project_settings_editor.cpp
msgid "Device"
-msgstr ""
+msgstr "התקן"
#: editor/project_settings_editor.cpp
msgid "Button"
-msgstr ""
+msgstr "כפתור"
#: editor/project_settings_editor.cpp
msgid "Left Button."
-msgstr ""
+msgstr "כפתור שמאלי."
#: editor/project_settings_editor.cpp
msgid "Right Button."
-msgstr ""
+msgstr "כפתור ימני."
#: editor/project_settings_editor.cpp
msgid "Middle Button."
-msgstr ""
+msgstr "כפתור אמצעי."
#: editor/project_settings_editor.cpp
msgid "Wheel Up."
-msgstr ""
+msgstr "גלגלת למעלה."
#: editor/project_settings_editor.cpp
msgid "Wheel Down."
-msgstr ""
+msgstr "גלגלת למטה."
#: editor/project_settings_editor.cpp
msgid "Add Global Property"
-msgstr ""
+msgstr "הוספת מאפיין גלובלי"
#: editor/project_settings_editor.cpp
msgid "Select a setting item first!"
-msgstr ""
+msgstr "יש לבחור פריט הגדרה קודם כל!"
#: editor/project_settings_editor.cpp
msgid "No property '%s' exists."
-msgstr ""
+msgstr "לא קיים מאפיין ‚%s’."
#: editor/project_settings_editor.cpp
msgid "Setting '%s' is internal, and it can't be deleted."
@@ -6170,11 +6199,7 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Delete Item"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
+msgstr "מחיקת פריט"
#: editor/project_settings_editor.cpp
msgid "Already existing"
@@ -7100,14 +7125,12 @@ msgid "Object can't provide a length."
msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Next Plane"
-msgstr "הלשונית הבאה"
+msgstr "המישור הבא"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Previous Plane"
-msgstr "הלשונית הקודמת"
+msgstr "המישור הקודם"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Plane:"
@@ -7217,6 +7240,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr "בחירת מרחק:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "הפתרון נוצר…"
@@ -7665,7 +7692,7 @@ msgstr ""
#: scene/2d/collision_polygon_2d.cpp
msgid "An empty CollisionPolygon2D has no effect on collision."
-msgstr ""
+msgstr "ל־CollisionPolygon2D ריק אין השפעה על התנגשות."
#: scene/2d/collision_shape_2d.cpp
msgid ""
@@ -7774,7 +7801,7 @@ msgstr ""
#: scene/3d/baked_lightmap.cpp
msgid "(Time Left: %d:%02d s)"
-msgstr ""
+msgstr "(זמן שנותר: %d:%02d שנ׳)"
#: scene/3d/baked_lightmap.cpp
msgid "Plotting Meshes: "
@@ -7854,10 +7881,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -7872,11 +7909,11 @@ msgstr ""
#: scene/gui/color_picker.cpp
msgid "Raw Mode"
-msgstr ""
+msgstr "מצב גולמי"
#: scene/gui/color_picker.cpp
msgid "Add current color as a preset"
-msgstr ""
+msgstr "הוספת הצבע הנוכחי כערכה"
#: scene/gui/dialogs.cpp
msgid "Alert!"
@@ -7884,11 +7921,11 @@ msgstr ""
#: scene/gui/dialogs.cpp
msgid "Please Confirm..."
-msgstr ""
+msgstr "נא לאמת…"
#: scene/gui/file_dialog.cpp
msgid "Select this Folder"
-msgstr ""
+msgstr "בחירת התיקייה"
#: scene/gui/popup.cpp
msgid ""
@@ -7906,7 +7943,7 @@ msgstr ""
#: scene/gui/tree.cpp
msgid "(Other)"
-msgstr ""
+msgstr "(אחר)"
#: scene/main/scene_tree.cpp
msgid ""
diff --git a/editor/translations/hi.po b/editor/translations/hi.po
index cba5b3059..e01793586 100644
--- a/editor/translations/hi.po
+++ b/editor/translations/hi.po
@@ -1412,6 +1412,10 @@ msgstr ""
msgid "Clear Output"
msgstr ""
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3639,6 +3643,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3924,6 +3936,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5586,6 +5602,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6063,7 +6087,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6207,10 +6233,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7250,6 +7272,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7887,10 +7913,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/hu.po b/editor/translations/hu.po
index 3b728caaf..b6151574e 100644
--- a/editor/translations/hu.po
+++ b/editor/translations/hu.po
@@ -4,14 +4,14 @@
# This file is distributed under the same license as the Godot source code.
#
# Nagy Lajos <neutron9707@gmail.com>, 2017.
-# Sandor Domokos <sandor.domokos@gmail.com>, 2017.
+# Sandor Domokos <sandor.domokos@gmail.com>, 2017-2018.
# Varga Dániel <danikah.danikah@gmail.com>, 2016-2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-02-24 12:37+0000\n"
-"Last-Translator: Varga Dániel <danikah.danikah@gmail.com>\n"
+"PO-Revision-Date: 2018-04-03 06:36+0000\n"
+"Last-Translator: Sandor Domokos <sandor.domokos@gmail.com>\n"
"Language-Team: Hungarian <https://hosted.weblate.org/projects/godot-engine/"
"godot/hu/>\n"
"Language: hu\n"
@@ -26,47 +26,47 @@ msgstr "Tiltva"
#: editor/animation_editor.cpp
msgid "All Selection"
-msgstr "Minden Kiválasztás"
+msgstr "Minden kiválasztás"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Time"
-msgstr "Animáció Kulcsképkocka Idő Változtatása"
+msgstr "Animáció kulcsképkocka idő változtatás"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr "Animáció Átmenet Váltás"
+msgstr "Animáció átmenet változtatás"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
-msgstr "Animáció Transzformáció Váltás"
+msgstr "Animáció transzformáció változtatás"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Value"
-msgstr "Animáció Kulcsképkocka Érték"
+msgstr "Animáció kulcsképkocka érték változtatás"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
-msgstr "Animáció Hívás Váltás"
+msgstr "Animáció hívás változtatás"
#: editor/animation_editor.cpp
msgid "Anim Add Track"
-msgstr "Animáció Nyomvonal Hozzáadása"
+msgstr "Animáció nyomvonal hozzáadás"
#: editor/animation_editor.cpp
msgid "Anim Duplicate Keys"
-msgstr "Animáció Kulcs Megkettőzése"
+msgstr "Animáció kulcsok megkettőzése"
#: editor/animation_editor.cpp
msgid "Move Anim Track Up"
-msgstr "Animáció Nyomvonal Felfelé Mozgatása"
+msgstr "Animáció nyomvonal felfelé mozgatás"
#: editor/animation_editor.cpp
msgid "Move Anim Track Down"
-msgstr "Animáció Nyomvonal Lefelé Mozgatása"
+msgstr "Animáció nyomvonal lefelé mozgatás"
#: editor/animation_editor.cpp
msgid "Remove Anim Track"
-msgstr "Animáció Nyomvonal Eltávolítása"
+msgstr "Animáció nyomvonal eltávolítás"
#: editor/animation_editor.cpp
msgid "Set Transitions to:"
@@ -74,44 +74,44 @@ msgstr "Átmenet beállítása erre:"
#: editor/animation_editor.cpp
msgid "Anim Track Rename"
-msgstr "Animáció Nyomvonal Átnevezése"
+msgstr "Animáció nyomvonal átnevezés"
#: editor/animation_editor.cpp
msgid "Anim Track Change Interpolation"
-msgstr "Animáció Nyomvonal Interpoláció Változtatása"
+msgstr "Animáció nyomvonal interpoláció változtatás"
#: editor/animation_editor.cpp
msgid "Anim Track Change Value Mode"
-msgstr "Animáció Nyomvonal Érték Mód Változtatása"
+msgstr "Animáció nyomvonal érték mód változtatás"
#: editor/animation_editor.cpp
msgid "Anim Track Change Wrap Mode"
-msgstr "Animáció Nyomvonal Takarási Mód Változtatása"
+msgstr "Animáció nyomvonal takarási mód változtatás"
#: editor/animation_editor.cpp
msgid "Edit Node Curve"
-msgstr "Node Görbe Szerkesztése"
+msgstr "Node görbe szerkesztés"
#: editor/animation_editor.cpp
msgid "Edit Selection Curve"
-msgstr "Kiválasztás Görbe Szerkesztése"
+msgstr "Kiválasztás görbe szerkesztés"
#: editor/animation_editor.cpp
msgid "Anim Delete Keys"
-msgstr "Animáció Kulcs Törlése"
+msgstr "Animáció kulcs törlés"
#: editor/animation_editor.cpp editor/plugins/tile_map_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Duplicate Selection"
-msgstr "Kiválasztás Megkettőzése"
+msgstr "Kiválasztás megkettőzés"
#: editor/animation_editor.cpp
msgid "Duplicate Transposed"
-msgstr "Áthelyezettek Megkettőzése"
+msgstr "Áthelyezettek megkettőzés"
#: editor/animation_editor.cpp
msgid "Remove Selection"
-msgstr "Kiválasztás Eltávolítása"
+msgstr "Kiválasztás eltávolítás"
#: editor/animation_editor.cpp
msgid "Continuous"
@@ -127,27 +127,27 @@ msgstr "Érzékelő"
#: editor/animation_editor.cpp
msgid "Anim Add Key"
-msgstr "Animáció Kulcs Hozzáadása"
+msgstr "Animáció kulcs hozzáadás"
#: editor/animation_editor.cpp
msgid "Anim Move Keys"
-msgstr "Animáció Kulcs Mozgatása"
+msgstr "Animáció kulcs mozgatás"
#: editor/animation_editor.cpp
msgid "Scale Selection"
-msgstr "Kiválasztás Átméretezése"
+msgstr "Kiválasztás átméretezés"
#: editor/animation_editor.cpp
msgid "Scale From Cursor"
-msgstr "Átméretezés A Kurzortól"
+msgstr "Átméretezés a kurzortól"
#: editor/animation_editor.cpp
msgid "Goto Next Step"
-msgstr "Ugrás A Következő Lépésre"
+msgstr "Ugrás a következő lépésre"
#: editor/animation_editor.cpp
msgid "Goto Prev Step"
-msgstr "Ugrás Az Előző Lépésre"
+msgstr "Ugrás az előző lépésre"
#: editor/animation_editor.cpp editor/plugins/curve_editor_plugin.cpp
#: editor/property_editor.cpp
@@ -180,11 +180,11 @@ msgstr "Átmenetek"
#: editor/animation_editor.cpp
msgid "Optimize Animation"
-msgstr "Animáció Optimalizálása"
+msgstr "Animáció optimalizálás"
#: editor/animation_editor.cpp
msgid "Clean-Up Animation"
-msgstr "Animáció Megtisztítása"
+msgstr "Animáció megtisztítás"
#: editor/animation_editor.cpp
msgid "Create NEW track for %s and insert key?"
@@ -204,39 +204,39 @@ msgstr "Létrehozás"
#: editor/animation_editor.cpp
msgid "Anim Create & Insert"
-msgstr "Animáció Létrehozása És Beillesztése"
+msgstr "Animáció létrehozás és beillesztés"
#: editor/animation_editor.cpp
msgid "Anim Insert Track & Key"
-msgstr "Animáció Nyomvonal És Kulcs Beillesztése"
+msgstr "Animáció nyomvonal és kulcs beillesztés"
#: editor/animation_editor.cpp
msgid "Anim Insert Key"
-msgstr "Animáció Kulcs Beillesztése"
+msgstr "Animáció kulcs beillesztés"
#: editor/animation_editor.cpp
msgid "Change Anim Len"
-msgstr "Animáció Hossz Változtatása"
+msgstr "Animáció hossz változtatás"
#: editor/animation_editor.cpp
msgid "Change Anim Loop"
-msgstr "Animáció Loop Megváltoztatása"
+msgstr "Animáció hurok változtatás"
#: editor/animation_editor.cpp
msgid "Anim Create Typed Value Key"
-msgstr "Animáció Típusos Érték Kulcs Létrehozása"
+msgstr "Animáció típusos érték kulcs létrehozás"
#: editor/animation_editor.cpp
msgid "Anim Insert"
-msgstr "Animáció Beilleszt"
+msgstr "Animáció beillesztés"
#: editor/animation_editor.cpp
msgid "Anim Scale Keys"
-msgstr "Animáció Kulcsok Nyújtása"
+msgstr "Animáció kulcsok nyújtás"
#: editor/animation_editor.cpp
msgid "Anim Add Call Track"
-msgstr "Animáció Hívási Nyomvonal Hozzáadása"
+msgstr "Animáció hívási nyomvonal hozzáadás"
#: editor/animation_editor.cpp
msgid "Animation zoom."
@@ -326,7 +326,7 @@ msgstr "Méretezési arány:"
#: editor/animation_editor.cpp
msgid "Call Functions in Which Node?"
-msgstr "Melyik Node-ban Hívjon Funkciókat?"
+msgstr "Melyik Node-ban hívjon funkciókat?"
#: editor/animation_editor.cpp
msgid "Remove invalid keys"
@@ -566,8 +566,8 @@ msgid ""
"Scene '%s' is currently being edited.\n"
"Changes will not take effect unless reloaded."
msgstr ""
-"'%s' Jelenet éppen szerkesztés alatt ál.\n"
-"A változások nem lépnek érvénybe, ha csak újra nem tölti a jelenetet."
+"'%s' Scene éppen szerkesztés alatt áll.\n"
+"A változások újratöltés után lépnek érvénybe."
#: editor/dependency_editor.cpp
msgid ""
@@ -1045,7 +1045,7 @@ msgstr "Útvonal:"
#: editor/editor_autoload_settings.cpp
msgid "Node Name:"
-msgstr "Node Neve:"
+msgstr "Node neve:"
#: editor/editor_autoload_settings.cpp editor/editor_profiler.cpp
#: editor/project_manager.cpp editor/settings_config_dialog.cpp
@@ -1058,7 +1058,7 @@ msgstr "Egyke"
#: editor/editor_data.cpp
msgid "Updating Scene"
-msgstr "Jelenet Frissítése"
+msgstr "Scene Frissítése"
#: editor/editor_data.cpp
msgid "Storing local changes.."
@@ -1066,7 +1066,7 @@ msgstr "Helyi módosítások eltárolása.."
#: editor/editor_data.cpp
msgid "Updating scene.."
-msgstr "Jelenet frissítése.."
+msgstr "Scene frissítése.."
#: editor/editor_data.cpp
msgid "[empty]"
@@ -1394,6 +1394,10 @@ msgstr "Töröl"
msgid "Clear Output"
msgstr "Kimenet Törlése"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Hiba történt az erőforrás mentésekor!"
@@ -1441,7 +1445,7 @@ msgstr "Hiba történt '%s' betöltése közben."
#: editor/editor_node.cpp
msgid "Saving Scene"
-msgstr "Jelenet Mentése"
+msgstr "Scene mentése"
#: editor/editor_node.cpp
msgid "Analyzing"
@@ -1460,8 +1464,8 @@ msgid ""
"Couldn't save scene. Likely dependencies (instances or inheritance) couldn't "
"be satisfied."
msgstr ""
-"Nem sikerült a jelenet mentése. Valószínű, hogy a függőségei (példányok vagy "
-"öröklések) nem voltak kielégíthetők."
+"Nem sikerült a Scene mentése. Valószínű, hogy a függőségei (példányok vagy "
+"öröklések) nem voltak megfelelőek."
#: editor/editor_node.cpp
msgid "Failed to load resource."
@@ -1516,10 +1520,9 @@ msgid ""
"This resource belongs to a scene that was instanced or inherited.\n"
"Changes to it will not be kept when saving the current scene."
msgstr ""
-"Ez az erőforrás egy olyan jelenethez tartozik, amit példányosítottak, vagy "
-"amit örökölt a jelenet.\n"
-"A rajta végzett módosítások nem lesznek megtartva a jelenlegi jelenet "
-"elmentésekor."
+"Ez az erőforrás egy olyan Scene-hez tartozik amit példányosítottak vagy "
+"örökölt.\n"
+"A módosítások nem lesznek megtartva a jelenlegi Scene mentésekor."
#: editor/editor_node.cpp
msgid ""
@@ -1591,7 +1594,7 @@ msgstr "Megnyitás Súgóban"
#: editor/editor_node.cpp
msgid "There is no defined scene to run."
-msgstr "Nincs meghatározva jelenet a futtatáshoz."
+msgstr "Nincs meghatározva Scene a futtatáshoz."
#: editor/editor_node.cpp
msgid ""
@@ -1609,8 +1612,7 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
-"A kiválasztott '%s' jelenet nem létezik, kiválaszt most egy érvényes "
-"jelenetet?\n"
+"A kiválasztott '%s' Scene nem létezik, kiválaszt most egy érvényeset?\n"
"Ezt megváltoztathatja később a \"Projekt Beállításokban\" az \"Alkalmazás\" "
"kategóriában."
@@ -1620,15 +1622,15 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
-"A kiválasztott '%s' jelenet nem egy jelenetfájl, kiválaszt most egy érvényes "
-"jelenetet?\n"
+"A kiválasztott '%s' Scene nem egy Scene fájl, kiválaszt most egy "
+"érvényeset?\n"
"Ezt megváltoztathatja később a \"Projekt Beállításokban\" az \"Alkalmazás\" "
"kategóriában."
#: editor/editor_node.cpp
msgid "Current scene was never saved, please save it prior to running."
msgstr ""
-"A jelenlegi jelenet soha nem volt még mentve, mentse el a futtatás előtt."
+"A jelenlegi Scene soha nem volt még mentve, mentse el a futtatás előtt."
#: editor/editor_node.cpp
msgid "Could not start subprocess!"
@@ -1636,19 +1638,19 @@ msgstr "Az alprocesszt nem lehetett elindítani!"
#: editor/editor_node.cpp
msgid "Open Scene"
-msgstr "Jelenet Megnyitása"
+msgstr "Scene megnyitás"
#: editor/editor_node.cpp
msgid "Open Base Scene"
-msgstr "Alap Jelenet Megnyitása"
+msgstr "Alap Scene megnyitás"
#: editor/editor_node.cpp
msgid "Quick Open Scene.."
-msgstr "Jelenet Gyors Megnyitása.."
+msgstr "Scene gyors megnyitás"
#: editor/editor_node.cpp
msgid "Quick Open Script.."
-msgstr "Szkript Gyors Megnyitása.."
+msgstr "Szkript gyors megnyitás"
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1660,7 +1662,7 @@ msgstr "Bezárás előtt menti a '%s'-n végzett módosításokat?"
#: editor/editor_node.cpp
msgid "Save Scene As.."
-msgstr "Jelenet Mentése Másként.."
+msgstr "Scene mentés másként"
#: editor/editor_node.cpp
msgid "No"
@@ -1672,27 +1674,27 @@ msgstr "Igen"
#: editor/editor_node.cpp
msgid "This scene has never been saved. Save before running?"
-msgstr "Ez a jelenet soha nem volt mentve. Menti futtatás előtt?"
+msgstr "Ez a Scene még soha nem volt mentve. Menti futtatás előtt?"
#: editor/editor_node.cpp editor/scene_tree_dock.cpp
msgid "This operation can't be done without a scene."
-msgstr "Ezt a műveletet nem lehet végrehajtani jelenet nélkül."
+msgstr "Ezt a műveletet nem lehet végrehajtani egy Scene nélkül."
#: editor/editor_node.cpp
msgid "Export Mesh Library"
-msgstr "Mesh Könyvtár Exportálása"
+msgstr "Mesh könyvtár exportálás"
#: editor/editor_node.cpp
msgid "This operation can't be done without a root node."
-msgstr "Ezt a műveletet nem lehet végrehajtani gyökér node nélkül."
+msgstr "Ezt a műveletet nem lehet végrehajtani gyökér Node nélkül."
#: editor/editor_node.cpp
msgid "Export Tile Set"
-msgstr "Tile Set Exportálása"
+msgstr "Tile Set exportálás"
#: editor/editor_node.cpp
msgid "This operation can't be done without a selected node."
-msgstr "Ezt a műveletet nem lehet végrehajtani egy kiválaszott node nélkül."
+msgstr "Ezt a műveletet nem lehet végrehajtani egy kiválaszott Node nélkül."
#: editor/editor_node.cpp
msgid "Current scene not saved. Open anyway?"
@@ -1712,7 +1714,7 @@ msgstr "Ez a művelet nem vonható vissza. Visszaállítja mindenképp?"
#: editor/editor_node.cpp
msgid "Quick Run Scene.."
-msgstr "Jelenet Gyors Futtatása.."
+msgstr "Scene gyors futtatás"
#: editor/editor_node.cpp
msgid "Quit"
@@ -1732,13 +1734,12 @@ msgstr "Mentés és Kilépés"
#: editor/editor_node.cpp
msgid "Save changes to the following scene(s) before quitting?"
-msgstr ""
-"Elmenti a következő jelenet(ek)en végzett változtatásokat kilépés előtt?"
+msgstr "Elmenti a következő Scene(ek)en végzett változtatásokat kilépés előtt?"
#: editor/editor_node.cpp
msgid "Save changes the following scene(s) before opening Project Manager?"
msgstr ""
-"Elmenti a következő jelenet(ek)en végzett változtatásokat a Projektkezelő "
+"Elmenti a következő Scene(ek)en végzett változtatásokat a Projektkezelő "
"megnyitása előtt?"
#: editor/editor_node.cpp
@@ -1787,9 +1788,8 @@ msgid ""
"Scene '%s' was automatically imported, so it can't be modified.\n"
"To make changes to it, a new inherited scene can be created."
msgstr ""
-"A(z) '%s' jelenet automatikusan be lett importálva, ezért nem módosítható.\n"
-"Ahhoz, hogy változtatásokat végezhessen rajta, egy új, azt öröklő jelenetet "
-"hozhat létre."
+"A(z) '%s' Scene automatikusan be lett importálva, ezért nem módosítható.\n"
+"Változtatások végzéséhez egy új öröklött Scene-t hozhat létre."
#: editor/editor_node.cpp editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
@@ -1801,9 +1801,9 @@ msgid ""
"Error loading scene, it must be inside the project path. Use 'Import' to "
"open the scene, then save it inside the project path."
msgstr ""
-"Hiba történt a jelenet betöltésekor, benne kell, hogy legyen a projekt "
-"útvonalában. Használja az 'import' lehetőséget a jelenet megnyitására, majd "
-"mentse el a projekt útvonalán belülre."
+"Hiba történt a Scene betöltésekor, benne kell lennnie a projekt útvonalában. "
+"Használja az 'import' lehetőséget a Scene megnyitására, majd mentse el a "
+"projekt útvonalán belülre."
#: editor/editor_node.cpp
msgid "Scene '%s' has broken dependencies:"
@@ -1828,7 +1828,7 @@ msgstr "Alapértelmezett"
#: editor/editor_node.cpp
msgid "Switch Scene Tab"
-msgstr "Jelenet Fül Váltása"
+msgstr "Scene fül váltás"
#: editor/editor_node.cpp
msgid "%d more files or folders"
@@ -1860,7 +1860,7 @@ msgstr "Hozzáad egy új jelenetet."
#: editor/editor_node.cpp
msgid "Scene"
-msgstr "Jelenet"
+msgstr "Scene"
#: editor/editor_node.cpp
msgid "Go to previously opened scene."
@@ -1880,31 +1880,31 @@ msgstr "Fájlok Szűrése.."
#: editor/editor_node.cpp
msgid "Operations with scene files."
-msgstr "Műveletek a jelenet fájlokkal."
+msgstr "Műveletek Scene fájlokkal."
#: editor/editor_node.cpp
msgid "New Scene"
-msgstr "Új Jelenet"
+msgstr "Új Scene"
#: editor/editor_node.cpp
msgid "New Inherited Scene.."
-msgstr "Új Örökölt Jelenet.."
+msgstr "Új örökölt Scene"
#: editor/editor_node.cpp
msgid "Open Scene.."
-msgstr "Jelenet Megnyitása.."
+msgstr "Scene megnyitása"
#: editor/editor_node.cpp
msgid "Save Scene"
-msgstr "Jelenet Mentése"
+msgstr "Scene mentés"
#: editor/editor_node.cpp
msgid "Save all Scenes"
-msgstr "Minden Jelenet Mentése"
+msgstr "Minden Scene mentés"
#: editor/editor_node.cpp
msgid "Close Scene"
-msgstr "Jelenet Bezárása"
+msgstr "Scene bezárás"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Open Recent"
@@ -1934,11 +1934,11 @@ msgstr "Mégis"
#: editor/editor_node.cpp
msgid "Revert Scene"
-msgstr "Jelenet Visszaállítása"
+msgstr "Scene visszaállítás"
#: editor/editor_node.cpp
msgid "Miscellaneous project or scene-wide tools."
-msgstr "Egyéb projekt- vagy jelenet-szintű eszközök."
+msgstr "Egyéb projekt- vagy Scene-szintű eszközök."
#: editor/editor_node.cpp
msgid "Project"
@@ -2009,7 +2009,7 @@ msgid ""
"Collision shapes and raycast nodes (for 2D and 3D) will be visible on the "
"running game if this option is turned on."
msgstr ""
-"Az ütközési alakzatok és a fénysugárkövető node-ok (mind 2D-hez és 3D-hez) "
+"Az ütközési alakzatok és a fénysugárkövető Node-ok (mind 2D-hez és 3D-hez) "
"láthatóak lesznek a játék futásakor, ha ez az opció be van kapcsolva."
#: editor/editor_node.cpp
@@ -2026,7 +2026,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Sync Scene Changes"
-msgstr "Jelenet Változtatások Szinkronizálása"
+msgstr "Scene változtatások szinkronizálás"
#: editor/editor_node.cpp
msgid ""
@@ -2125,7 +2125,7 @@ msgstr "Szünetelteti a jelenetet"
#: editor/editor_node.cpp
msgid "Pause Scene"
-msgstr "Jelenet Szüneteltetése"
+msgstr "Scene szüneteltetés"
#: editor/editor_node.cpp
msgid "Stop the scene."
@@ -2137,19 +2137,19 @@ msgstr "Leállítás"
#: editor/editor_node.cpp
msgid "Play the edited scene."
-msgstr "Szerkesztett jelenet futtatása."
+msgstr "Szerkesztett Scene futtatása."
#: editor/editor_node.cpp
msgid "Play Scene"
-msgstr "Jelenet Futtatása"
+msgstr "Scene futtatás"
#: editor/editor_node.cpp
msgid "Play custom scene"
-msgstr "Tetszőleges jelenet futtatása"
+msgstr "Tetszőleges Scene futtatás"
#: editor/editor_node.cpp
msgid "Play Custom Scene"
-msgstr "Tetszőleges Jelenet Futtatása"
+msgstr "Tetszőleges Scene futtatás"
#: editor/editor_node.cpp
msgid "Spins when the editor window repaints!"
@@ -2387,7 +2387,7 @@ msgstr "Írja a logikát a _run() metódusba."
#: editor/editor_run_script.cpp
msgid "There is an edited scene already."
-msgstr "Már létezik szerkesztett jelenet."
+msgstr "Már létezik a szerkesztett Scene."
#: editor/editor_run_script.cpp
msgid "Couldn't instance script:"
@@ -2411,11 +2411,11 @@ msgstr "Alapértelmezett (Ugyanaz, Mint a Szerkesztőnek)"
#: editor/editor_sub_scene.cpp
msgid "Select Node(s) to Import"
-msgstr "Válassza Ki Az Importálandó Node-okat"
+msgstr "Válassza ki az importálandó Node-okat"
#: editor/editor_sub_scene.cpp
msgid "Scene Path:"
-msgstr "Jelenet Elérési Útja:"
+msgstr "Scene elérési Út:"
#: editor/editor_sub_scene.cpp
msgid "Import From Node:"
@@ -2699,7 +2699,7 @@ msgstr "Áthelyezés.."
#: editor/filesystem_dock.cpp
msgid "Open Scene(s)"
-msgstr "Jelenet(ek) Megnyitása"
+msgstr "Scene(k) megnyitás"
#: editor/filesystem_dock.cpp
msgid "Instance"
@@ -2735,8 +2735,7 @@ msgstr "Mappa Kedvencnek jelölése / Kedvenc jelölés visszavonása"
#: editor/filesystem_dock.cpp
msgid "Instance the selected scene(s) as child of the selected node."
-msgstr ""
-"Kiválasztott jelenet(ek) példányosítása a kiválasztott Node gyermekeként."
+msgstr "Kiválasztott Scene(k) példányosítása a kiválasztott Node gyermekeként."
#: editor/filesystem_dock.cpp
msgid ""
@@ -2806,11 +2805,11 @@ msgstr "Importálás Több Jelentként és Anyagokként"
#: editor/import/resource_importer_scene.cpp
#: editor/plugins/cube_grid_theme_editor_plugin.cpp
msgid "Import Scene"
-msgstr "Jelenet Importálása"
+msgstr "Scene importálás"
#: editor/import/resource_importer_scene.cpp
msgid "Importing Scene.."
-msgstr "Jelenet Importálása.."
+msgstr "Scene importálás"
#: editor/import/resource_importer_scene.cpp
msgid "Generating Lightmaps"
@@ -3242,7 +3241,7 @@ msgstr "Az animációs fa érvénytelen."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Animation Node"
-msgstr "Animációs Node"
+msgstr "Animáció Node"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "OneShot Node"
@@ -3266,7 +3265,7 @@ msgstr "Keverés4 Node"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "TimeScale Node"
-msgstr "IdőSkála Node"
+msgstr "Időskála Node"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "TimeSeek Node"
@@ -3282,7 +3281,7 @@ msgstr "Animációk Importálása.."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Edit Node Filters"
-msgstr "Node Szűrők Módosítása"
+msgstr "Node szűrők szerkesztés"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Filters.."
@@ -3290,7 +3289,7 @@ msgstr "Szűrők.."
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "AnimationTree"
-msgstr "AnimationTree"
+msgstr "AnimációFa"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Free"
@@ -3643,15 +3642,15 @@ msgstr "Illesztés szülőhöz"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node anchor"
-msgstr "Illesztés node horgonyhoz"
+msgstr "Illesztés Node horgonyhoz"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node sides"
-msgstr "Illesztés node oldalakhoz"
+msgstr "Illesztés Node oldalakhoz"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to other nodes"
-msgstr "Illesztés más node-okhoz"
+msgstr "Illesztés más Node-okhoz"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to guides"
@@ -3718,6 +3717,16 @@ msgid "Show Guides"
msgstr "Vezetővonalak Megjelenítése"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Rács Megjelenítése"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "Segítők Megjelenítése"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Kijelölés Középre"
@@ -3779,17 +3788,17 @@ msgstr "Rendben"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Cannot instantiate multiple nodes without root."
-msgstr "Nem lehet több node-ot példányosítani gyökér nélkül."
+msgstr "Nem lehet több Node-ot példányosítani gyökér nélkül."
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Create Node"
-msgstr "Node Létrehozása"
+msgstr "Node létrehozás"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Error instancing scene from %s"
-msgstr "Hiba történt a jelenet példányosításkor %s-ből"
+msgstr "Hiba történt a Scene példányosításkor %s-ből"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Change default type"
@@ -3927,8 +3936,8 @@ msgid ""
"No OccluderPolygon2D resource on this node.\n"
"Create and assign one?"
msgstr ""
-"Nincs OccluderPolygon2D erőforrás ezen a node-on.\n"
-"Létrehoz egyet és hozzárendeli a node-hoz?"
+"Nincs OccluderPolygon2D erőforrás ezen a Node-on.\n"
+"Létrehoz egyet és hozzárendeli a Node-hoz?"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Create Occluder Polygon"
@@ -3968,7 +3977,7 @@ msgstr "Statikus Konvex Test Létrehozása"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "This doesn't work on scene root!"
-msgstr "Ez nem hajtható végre a jelenet gyökerén!"
+msgstr "Ez nem hajtható végre a gyökér Scene-en!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Create Trimesh Shape"
@@ -4008,6 +4017,10 @@ msgstr ""
"A Mesh-nek nincsenek felszínei, ami alapján körvonalakat lehetni készíteni!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Körvonalkészítés sikertelen!"
@@ -4313,7 +4326,7 @@ msgstr "Kibocsátási Pontok Létrehozása A Mesh Alapján"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Node"
-msgstr "Kibocsátási Pontok Létrehozása A Node Alapján"
+msgstr "Kibocsátási pontok létrehozása a Node alapján"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emitter"
@@ -4572,7 +4585,7 @@ msgstr "Beillesztés"
#: editor/plugins/resource_preloader_editor_plugin.cpp
msgid "ResourcePreloader"
-msgstr "ResourcePreloader"
+msgstr "ForrásElőtöltö"
#: editor/plugins/script_editor_plugin.cpp
msgid "Clear Recent Files"
@@ -4785,8 +4798,8 @@ msgstr "Hibakereső"
msgid ""
"Built-in scripts can only be edited when the scene they belong to is loaded"
msgstr ""
-"A beépített szkriptek csak akkor szerkeszthetőek, amikor az a jelenet, "
-"amihez tartoznak, éppen be van töltve"
+"A beépített szkriptek csak akkor szerkeszthetőek, amikor az a Scene amihez "
+"tartoznak éppen be van töltve"
#: editor/plugins/script_text_editor.cpp
msgid "Only resources from filesystem can be dropped."
@@ -4927,27 +4940,27 @@ msgstr "Kontextusérzékeny Súgó"
#: editor/plugins/shader_editor_plugin.cpp
msgid "Shader"
-msgstr "Shader"
+msgstr "Árnyaló"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Constant"
-msgstr ""
+msgstr "Skaláris állandó változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Constant"
-msgstr ""
+msgstr "Vec állandó változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change RGB Constant"
-msgstr ""
+msgstr "RGB állandó változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Operator"
-msgstr ""
+msgstr "Skaláris kezelő változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Operator"
-msgstr ""
+msgstr "Vec kezelő változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Scalar Operator"
@@ -4955,7 +4968,7 @@ msgstr ""
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change RGB Operator"
-msgstr ""
+msgstr "RGB kezelő változtatás"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Toggle Rot Only"
@@ -5674,6 +5687,15 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Elem Hozzáadása"
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6149,7 +6171,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6293,10 +6317,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7336,6 +7356,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7978,10 +8002,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/id.po b/editor/translations/id.po
index df1049048..21d333009 100644
--- a/editor/translations/id.po
+++ b/editor/translations/id.po
@@ -11,21 +11,22 @@
# Khairul Hidayat <khairulcyber4rt@gmail.com>, 2016.
# Romi Kusuma Bakti <romikusumab@gmail.com>, 2017.
# Sofyan Sugianto <sofyanartem@gmail.com>, 2017-2018.
+# Tito <ijavadroid@gmail.com>, 2018.
# Tom My <tom.asadinawan@gmail.com>, 2017.
# yursan9 <rizal.sagi@gmail.com>, 2016.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-03-02 01:38+0000\n"
-"Last-Translator: Sofyan Sugianto <sofyanartem@gmail.com>\n"
+"PO-Revision-Date: 2018-04-18 16:38+0000\n"
+"Last-Translator: Tito <ijavadroid@gmail.com>\n"
"Language-Team: Indonesian <https://hosted.weblate.org/projects/godot-engine/"
"godot/id/>\n"
"Language: id\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 2.20-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -1089,9 +1090,8 @@ msgid "[unsaved]"
msgstr "[belum disimpan]"
#: editor/editor_dir_dialog.cpp
-#, fuzzy
msgid "Please select a base directory first"
-msgstr "Pilih direktori kerja terlebih dahulu"
+msgstr "Slahkan pilih direktori kerja terlebih dahulu"
#: editor/editor_dir_dialog.cpp
msgid "Choose a Directory"
@@ -1409,6 +1409,10 @@ msgstr "Bersihkan"
msgid "Clear Output"
msgstr "Bersihkan Luaran"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Error menyimpan resource!"
@@ -3159,7 +3163,7 @@ msgstr ""
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Next (Auto Queue):"
-msgstr ""
+msgstr "Selanjutnya (Antrian Otomatis):"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Cross-Animation Blend Times"
@@ -3182,7 +3186,7 @@ msgstr "Sunting Filter"
#: editor/plugins/animation_tree_editor_plugin.cpp
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Scale:"
-msgstr ""
+msgstr "Skala:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Fade In (s):"
@@ -3202,11 +3206,11 @@ msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Auto Restart:"
-msgstr ""
+msgstr "Mulai Ulang Otomatis:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Restart (s):"
-msgstr ""
+msgstr "Mulai Ulang:"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Random Restart (s):"
@@ -3214,7 +3218,7 @@ msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Start!"
-msgstr ""
+msgstr "Mulai!"
#: editor/plugins/animation_tree_editor_plugin.cpp
#: editor/plugins/multimesh_editor_plugin.cpp
@@ -3303,7 +3307,7 @@ msgstr ""
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Import Animations.."
-msgstr ""
+msgstr "Impor Animasi.."
#: editor/plugins/animation_tree_editor_plugin.cpp
#, fuzzy
@@ -3312,12 +3316,11 @@ msgstr "Sunting Filter Node"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Filters.."
-msgstr ""
+msgstr "Penyaring.."
#: editor/plugins/animation_tree_editor_plugin.cpp
-#, fuzzy
msgid "AnimationTree"
-msgstr "Animasi"
+msgstr "PohonAnimasi"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Free"
@@ -3747,6 +3750,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -4043,6 +4054,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5744,6 +5759,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6244,7 +6267,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6393,11 +6418,6 @@ msgid "Delete Item"
msgstr "Hapus"
#: editor/project_settings_editor.cpp
-#, fuzzy
-msgid "Can't contain '/' or ':'"
-msgstr "Sambungkan Ke Node:"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7484,6 +7504,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -8212,12 +8236,22 @@ msgstr ""
"bekerja."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Hanya satu WorldEnvironment yang diizinkan per scene (atau atur scene-scene "
"yang diacu)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8315,6 +8349,10 @@ msgid "Invalid font size."
msgstr "Ukuran font tidak sah."
#, fuzzy
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Sambungkan Ke Node:"
+
+#, fuzzy
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/is.po b/editor/translations/is.po
index b0b8698f3..eb4f29126 100644
--- a/editor/translations/is.po
+++ b/editor/translations/is.po
@@ -1372,6 +1372,10 @@ msgstr ""
msgid "Clear Output"
msgstr ""
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3592,6 +3596,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3877,6 +3889,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5538,6 +5554,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6013,7 +6037,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6157,10 +6183,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7199,6 +7221,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7835,10 +7861,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/it.po b/editor/translations/it.po
index ba1a09e3a..85f4e665a 100644
--- a/editor/translations/it.po
+++ b/editor/translations/it.po
@@ -9,16 +9,18 @@
# Elia Zanaboni <elia.zanaboni@gmail.com>, 2017.
# Giovanni Solimeno (Crax97) <gsolimeno97@gmail.com>, 2017.
# Marco Melorio <m.melorio@icloud.com>, 2017.
-# Myself <whatamidoing.wt@gmail.com>, 2017.
+# Matteo <matteo.guglielmetti@hotmail.it>, 2018.
+# Myself <whatamidoing.wt@gmail.com>, 2017-2018.
# RealAquilus <JamesHeller@live.it>, 2017.
+# Samuele Zolfanelli <samdazel@gmail.com>, 2018.
# Sean Bone <seanbone@zumguy.com>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2017-12-14 12:48+0000\n"
-"Last-Translator: anonymous <>\n"
+"PO-Revision-Date: 2018-05-02 10:38+0000\n"
+"Last-Translator: Samuele Zolfanelli <samdazel@gmail.com>\n"
"Language-Team: Italian <https://hosted.weblate.org/projects/godot-engine/"
"godot/it/>\n"
"Language: it\n"
@@ -26,7 +28,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 2.18-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -505,9 +507,8 @@ msgid "Connecting Signal:"
msgstr "Connessione Segnali:"
#: editor/connections_dialog.cpp
-#, fuzzy
msgid "Disconnect '%s' from '%s'"
-msgstr "Connetti '%s' a '%s'"
+msgstr "Disconnetti '%s' da '%s'"
#: editor/connections_dialog.cpp
msgid "Connect.."
@@ -525,7 +526,7 @@ msgstr "Segnali"
#: editor/create_dialog.cpp
#, fuzzy
msgid "Change %s Type"
-msgstr "Cambia Tipo"
+msgstr "Cambia Tipo di %s"
#: editor/create_dialog.cpp editor/project_settings_editor.cpp
#: modules/visual_script/visual_script_editor.cpp
@@ -533,9 +534,8 @@ msgid "Change"
msgstr "Cambia"
#: editor/create_dialog.cpp
-#, fuzzy
msgid "Create New %s"
-msgstr "Crea Nuovo"
+msgstr "Crea Nuovo %s"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp
@@ -646,9 +646,8 @@ msgstr ""
"Rimuoverli comunque? (no undo)"
#: editor/dependency_editor.cpp
-#, fuzzy
msgid "Cannot remove:"
-msgstr "Impossibile rimouvere:\n"
+msgstr "Impossibile rimuovere:"
#: editor/dependency_editor.cpp
msgid "Error loading:"
@@ -761,7 +760,7 @@ msgstr "Donatori Oro"
#: editor/editor_about.cpp
msgid "Silver Donors"
-msgstr "Sponsors Argento"
+msgstr "Donatori Argento"
#: editor/editor_about.cpp
msgid "Bronze Donors"
@@ -842,9 +841,8 @@ msgid "Rename Audio Bus"
msgstr "Rinomina Bus Audio"
#: editor/editor_audio_buses.cpp
-#, fuzzy
msgid "Change Audio Bus Volume"
-msgstr "Imposta Bus Audio su Solo"
+msgstr "Cambia il volume del Bus Audio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Solo"
@@ -909,7 +907,7 @@ msgstr "Elimina Effetto"
#: editor/editor_audio_buses.cpp
msgid "Audio"
-msgstr ""
+msgstr "Audio"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus"
@@ -1090,13 +1088,12 @@ msgid "Updating scene.."
msgstr "Aggiornando la scena.."
#: editor/editor_data.cpp
-#, fuzzy
msgid "[empty]"
-msgstr "(vuoto)"
+msgstr "[vuoto]"
#: editor/editor_data.cpp
msgid "[unsaved]"
-msgstr ""
+msgstr "[non salvato]"
#: editor/editor_dir_dialog.cpp
msgid "Please select a base directory first"
@@ -1349,9 +1346,8 @@ msgid "Description"
msgstr "Descrizione"
#: editor/editor_help.cpp
-#, fuzzy
msgid "Online Tutorials:"
-msgstr "Tutorials"
+msgstr "Tutorial online:"
#: editor/editor_help.cpp
#, fuzzy
@@ -1360,7 +1356,7 @@ msgid ""
"$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/"
"url][/color]."
msgstr ""
-"Al momento una descrizione per questo metodo non esiste. Aiutaci [color="
+"Al momento una descrizione per questa classe non esiste. Aiutaci [color="
"$color][url=$url]aggiungendone una[/url][/color]!"
#: editor/editor_help.cpp
@@ -1417,7 +1413,11 @@ msgstr "Rimuovi"
#: editor/editor_log.cpp
#, fuzzy
msgid "Clear Output"
-msgstr "Output"
+msgstr "Pulisci output"
+
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
@@ -2382,9 +2382,8 @@ msgid "Frame #:"
msgstr "Frame #:"
#: editor/editor_profiler.cpp
-#, fuzzy
msgid "Time"
-msgstr "Tempo:"
+msgstr "Tempo"
#: editor/editor_profiler.cpp
#, fuzzy
@@ -2491,7 +2490,7 @@ msgstr "Non é stato trovato version.txt all'interno di templates."
#: editor/export_template_manager.cpp
#, fuzzy
msgid "Error creating path for templates:"
-msgstr "Errore di creazione del percorso per le template:\n"
+msgstr "Errore di creazione del percorso per i template:"
#: editor/export_template_manager.cpp
msgid "Extracting Export Templates"
@@ -2526,9 +2525,8 @@ msgstr "Nessuna risposta."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
-#, fuzzy
msgid "Request Failed."
-msgstr "Rich. Fall."
+msgstr "Richiesta fallita."
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -2572,7 +2570,7 @@ msgstr "Connettendo.."
#: editor/export_template_manager.cpp
#, fuzzy
msgid "Can't Connect"
-msgstr "Impossibile connettersi"
+msgstr "Impossibile connettere."
#: editor/export_template_manager.cpp
msgid "Connected"
@@ -2592,7 +2590,6 @@ msgid "Connection Error"
msgstr "Errore di connessione"
#: editor/export_template_manager.cpp
-#, fuzzy
msgid "SSL Handshake Error"
msgstr "Errore nell'Handshake SSL"
@@ -2651,8 +2648,7 @@ msgstr "Visualizza elementi come una lista"
#, fuzzy
msgid "Status: Import of file failed. Please fix file and reimport manually."
msgstr ""
-"\n"
-"Stato: Importazione file fallita. Si prega di sistemare il file e "
+"Stato: Importazione file fallita. Si prega di riparare il file e "
"reimportarlo manualmente."
#: editor/filesystem_dock.cpp
@@ -2662,7 +2658,7 @@ msgstr "Impossibile spostare/rinominare risorse root."
#: editor/filesystem_dock.cpp
#, fuzzy
msgid "Cannot move a folder into itself."
-msgstr "Impossibile spostare una cartella in se stessa.\n"
+msgstr "Impossibile spostare una cartella in se stessa."
#: editor/filesystem_dock.cpp
#, fuzzy
@@ -2670,14 +2666,12 @@ msgid "Error moving:"
msgstr "Errore spostamento:\n"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Error duplicating:"
-msgstr "Errore in caricamento:"
+msgstr "Errore duplicazione:"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Unable to update dependencies:"
-msgstr "Impossibile aggiornare le dipendenze:\n"
+msgstr "Impossibile aggiornare le dipendenze:"
#: editor/filesystem_dock.cpp
msgid "No name provided"
@@ -2708,14 +2702,12 @@ msgid "Renaming folder:"
msgstr "Rinomina cartella:"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Duplicating file:"
-msgstr "duplica"
+msgstr "Duplicando file:"
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Duplicating folder:"
-msgstr "Rinomina cartella:"
+msgstr "Duplicando cartella:"
#: editor/filesystem_dock.cpp
msgid "Expand all"
@@ -2736,7 +2728,7 @@ msgstr "Sposta in.."
#: editor/filesystem_dock.cpp
#, fuzzy
msgid "Open Scene(s)"
-msgstr "Apri Scena"
+msgstr "Apri Scena/e"
#: editor/filesystem_dock.cpp
msgid "Instance"
@@ -2751,9 +2743,8 @@ msgid "View Owners.."
msgstr "Vedi Proprietari.."
#: editor/filesystem_dock.cpp
-#, fuzzy
msgid "Duplicate.."
-msgstr "duplica"
+msgstr "Duplica.."
#: editor/filesystem_dock.cpp
msgid "Previous Directory"
@@ -2850,14 +2841,13 @@ msgid "Importing Scene.."
msgstr "Importando Scena.."
#: editor/import/resource_importer_scene.cpp
-#, fuzzy
msgid "Generating Lightmaps"
-msgstr "Trasferisci a Lightmap:"
+msgstr "Generando Lightmap"
#: editor/import/resource_importer_scene.cpp
#, fuzzy
msgid "Generating for Mesh: "
-msgstr "Generando AABB"
+msgstr "Generando per Mesh: "
#: editor/import/resource_importer_scene.cpp
msgid "Running Custom Script.."
@@ -3106,15 +3096,16 @@ msgstr "Copia Animazione"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Onion Skinning"
-msgstr ""
+msgstr "Onion Skinning"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Enable Onion Skinning"
msgstr "Attiva Onion Skinning"
#: editor/plugins/animation_player_editor_plugin.cpp
+#, fuzzy
msgid "Directions"
-msgstr ""
+msgstr "Direzioni"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Past"
@@ -3135,12 +3126,12 @@ msgstr "1 Passo"
#: editor/plugins/animation_player_editor_plugin.cpp
#, fuzzy
msgid "2 steps"
-msgstr "2 Passi"
+msgstr "2 passi"
#: editor/plugins/animation_player_editor_plugin.cpp
#, fuzzy
msgid "3 steps"
-msgstr "3 Passi"
+msgstr "3 passi"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Differences Only"
@@ -3148,11 +3139,11 @@ msgstr "Solo Differenze"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Force White Modulate"
-msgstr ""
+msgstr "Forza Modulazione Bianca"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Include Gizmos (3D)"
-msgstr ""
+msgstr "Includi Gizmo (3D)"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Create New Animation"
@@ -3482,21 +3473,28 @@ msgid ""
"Save your scene (for images to be saved in the same dir), or pick a save "
"path from the BakedLightmap properties."
msgstr ""
+"Impossibile determinare un percorso di salvataggio per le immagini di "
+"lightmap.\n"
+"Salva la scena (per salvare le immagini nella stessa directory), o scegli un "
+"percorso di salvataggio nelle proprietà di BackedLightmap."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid ""
"No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake "
"Light' flag is on."
msgstr ""
+"Nessuna mesh da preprocessare. Assicurarsi che contengano un canale UV2 e "
+"che la spunta 'Bake Light' sia abilitata."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid "Failed creating lightmap images, make sure path is writable."
msgstr ""
+"Tentativo di creazione delle immagini di lightmap fallito, assicurarsi che "
+"il percorso dei file sia scrivibile."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
-#, fuzzy
msgid "Bake Lightmaps"
-msgstr "Trasferisci a Lightmap:"
+msgstr "Preprocessa Lightmaps"
#: editor/plugins/camera_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
@@ -3757,6 +3755,16 @@ msgid "Show Guides"
msgstr "Mostra guide"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Visualizza Origine"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 Vista"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Centra Selezione"
@@ -4060,6 +4068,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "La mesh non ha superficie dalla quale creare un'outline!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Impossiblile creare outline!"
@@ -5776,6 +5788,16 @@ msgid "Checked Item"
msgstr "Checked Item"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Aggiungi Elemento"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Checked Item"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Ha"
@@ -6295,8 +6317,10 @@ msgid "Mouse Button"
msgstr "Pulsante Mouse"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Azione invalida (va bene tutto a parte '/' o ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6444,11 +6468,6 @@ msgstr "Elimina Input"
#: editor/project_settings_editor.cpp
#, fuzzy
-msgid "Can't contain '/' or ':'"
-msgstr "Impossibile connetersi all'host:"
-
-#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Already existing"
msgstr "Attiva Persistenza"
@@ -7556,6 +7575,10 @@ msgstr "Impostazioni Snap"
msgid "Pick Distance:"
msgstr "Istanza:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
#, fuzzy
msgid "Generating solution..."
@@ -7821,7 +7844,7 @@ msgstr "Iteratore"
#: modules/visual_script/visual_script_editor.cpp
msgid "While"
-msgstr "Mentre"
+msgstr "While"
#: modules/visual_script/visual_script_editor.cpp
msgid "Return"
@@ -8307,12 +8330,22 @@ msgstr ""
"poter funzionare."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Solamente un WorldEnvironment è consentito per scena (o insieme di scene "
"istanziate)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8412,6 +8445,13 @@ msgstr "Errore caricamento font."
msgid "Invalid font size."
msgstr "Dimensione font Invalida."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Azione invalida (va bene tutto a parte '/' o ':')."
+
+#, fuzzy
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Impossibile connetersi all'host:"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/ja.po b/editor/translations/ja.po
index 905c498a3..e05530b25 100644
--- a/editor/translations/ja.po
+++ b/editor/translations/ja.po
@@ -12,20 +12,20 @@
# Lexi Grafen <shfeedly@gmail.com>, 2017.
# NoahDigital <taku_58@hotmail.com>, 2017.
# Tetsuji Ochiai <ochiaixp@gmail.com>, 2017.
-# Tohru Ike (rokujyouhitoma) <rokujyouhitomajp@gmail.com>, 2017.
+# Tohru Ike (rokujyouhitoma) <rokujyouhitomajp@gmail.com>, 2017-2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-01-06 13:19+0000\n"
-"Last-Translator: NoahDigital <taku_58@hotmail.com>\n"
+"PO-Revision-Date: 2018-04-02 08:38+0000\n"
+"Last-Translator: Tohru Ike (rokujyouhitoma) <rokujyouhitomajp@gmail.com>\n"
"Language-Team: Japanese <https://hosted.weblate.org/projects/godot-engine/"
"godot/ja/>\n"
"Language: ja\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 2.19-dev\n"
+"X-Generator: Weblate 2.20-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -468,7 +468,6 @@ msgstr "追加"
#: editor/plugins/animation_tree_editor_plugin.cpp
#: editor/plugins/theme_editor_plugin.cpp editor/project_manager.cpp
#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Remove"
msgstr "削除"
@@ -1554,6 +1553,10 @@ msgstr "削除"
msgid "Clear Output"
msgstr "出力"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
#, fuzzy
msgid "Error saving resource!"
@@ -4148,6 +4151,16 @@ msgstr "ボーンを表示する"
#: editor/plugins/canvas_item_editor_plugin.cpp
#, fuzzy
+msgid "Show Origin"
+msgstr "原点を見る"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 ビューポート"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
msgid "Center Selection"
msgstr "選択対象を中央に"
@@ -4467,6 +4480,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "メッシュにアウトラインを作成するためのサーフェイスが存在しません"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
#, fuzzy
msgid "Could not create outline!"
msgstr "アウトラインを生成できませんでした!"
@@ -6322,6 +6339,16 @@ msgid "Checked Item"
msgstr "チェック済みアイテム"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "アイテムを追加"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "チェック済みアイテム"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6838,6 +6865,8 @@ msgid ""
"You don't currently have any projects.\n"
"Would you like to explore the official example projects in the Asset Library?"
msgstr ""
+"あなたは現在どのプロジェクトも持っていません。\n"
+"アセットライブラリで公式のサンプルプロジェクトを探しましょうか?"
#: editor/project_settings_editor.cpp
#, fuzzy
@@ -6859,8 +6888,10 @@ msgid "Mouse Button"
msgstr "マウスボタン"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "不正なアクション( '/' と':'は不可です)."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -7016,11 +7047,6 @@ msgstr "入力を消去"
#: editor/project_settings_editor.cpp
#, fuzzy
-msgid "Can't contain '/' or ':'"
-msgstr "ホストに接続できません:"
-
-#: editor/project_settings_editor.cpp
-#, fuzzy
msgid "Already existing"
msgstr "アクション'%s'は既にあります!"
@@ -8194,6 +8220,10 @@ msgstr "Snapの設定"
msgid "Pick Distance:"
msgstr "インスタンス:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
#, fuzzy
msgid "Generating solution..."
@@ -8977,6 +9007,10 @@ msgstr ""
"す。"
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
#, fuzzy
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
@@ -8984,6 +9018,12 @@ msgstr ""
"1 つだけの WorldEnvironment は、シーン (またはインスタンス化されたシーンの"
"セット) ごとに許可されます。"
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -9084,6 +9124,13 @@ msgstr "フォント読み込みエラー。"
msgid "Invalid font size."
msgstr "無効なフォント サイズです。"
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "不正なアクション( '/' と':'は不可です)."
+
+#, fuzzy
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "ホストに接続できません:"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/ko.po b/editor/translations/ko.po
index 28294c730..c3fcfbbb7 100644
--- a/editor/translations/ko.po
+++ b/editor/translations/ko.po
@@ -3,18 +3,19 @@
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
#
-# 박한얼 (volzhs) <volzhs@gmail.com>, 2016-2018.
# Ch <ccwpc@hanmail.net>, 2017.
# paijai 송 (fivejobi) <xotjq237@gmail.com>, 2018.
+# Sun Kim <perplexingsun@gmail.com>, 2018.
# TheRedPlanet <junmo.moon8@gmail.com>, 2018.
# Xavier Cho <mysticfallband@gmail.com>, 2018.
+# 박한얼 (volzhs) <volzhs@gmail.com>, 2016-2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-02-24 08:39+0000\n"
-"Last-Translator: TheRedPlanet <junmo.moon8@gmail.com>\n"
+"PO-Revision-Date: 2018-04-18 15:39+0000\n"
+"Last-Translator: Sun Kim <perplexingsun@gmail.com>\n"
"Language-Team: Korean <https://hosted.weblate.org/projects/godot-engine/"
"godot/ko/>\n"
"Language: ko\n"
@@ -22,7 +23,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 2.20-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -37,9 +38,8 @@ msgid "Anim Change Keyframe Time"
msgstr "애니메이션 키프레임 시간 변경"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Transition"
-msgstr "애니메이션 교체 트랜지션"
+msgstr "애니메이션 트랜지션 변경"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
@@ -164,7 +164,6 @@ msgid "Constant"
msgstr "비선형"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "In"
msgstr "In"
@@ -897,7 +896,7 @@ msgstr "이펙트 삭제"
#: editor/editor_audio_buses.cpp
msgid "Audio"
-msgstr "음성"
+msgstr "오디오"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus"
@@ -1399,6 +1398,10 @@ msgstr "지우기"
msgid "Clear Output"
msgstr "출력 지우기"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "리소스 저장 중 에러!"
@@ -3689,6 +3692,16 @@ msgid "Show Guides"
msgstr "가이드 보기"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "원점 보기"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1개 뷰포트"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "선택 항목 화면 중앙에 표시"
@@ -3978,6 +3991,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "메시에 외곽선을 만들기 위한 서피스가 없습니다!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "외곽선을 만들수 없습니다!"
@@ -4609,7 +4626,7 @@ msgstr "파일"
#: editor/plugins/script_editor_plugin.cpp
msgid "New"
-msgstr "새로운"
+msgstr "새 파일"
#: editor/plugins/script_editor_plugin.cpp
msgid "Save All"
@@ -5644,6 +5661,16 @@ msgid "Checked Item"
msgstr "항목 확인됨"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "항목 추가"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "항목 확인됨"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "가진다"
@@ -5861,7 +5888,7 @@ msgstr "프로젝트의 모든 리소스 내보내기"
#: editor/project_export.cpp
msgid "Export selected scenes (and dependencies)"
-msgstr "선택된 리소스 내보내기 (종속된 리소스 포함)"
+msgstr "선택된 씬 내보내기 (종속된 리소스 포함)"
#: editor/project_export.cpp
msgid "Export selected resources (and dependencies)"
@@ -6135,8 +6162,10 @@ msgid "Mouse Button"
msgstr "마우스 버튼"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "유효하지 않은 액션 ('/' 또는 ':' 문자 사용 불가)."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6279,10 +6308,6 @@ msgid "Delete Item"
msgstr "아이템 삭제"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "'/' 또는 ':' 문자를 포함할 수 없음"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "이미 존재함"
@@ -6801,15 +6826,15 @@ msgid ""
"Click to unlock"
msgstr ""
"노드가 잠겨있습니다.\n"
-"클릭해서 잠금해제 하십시오"
+"클릭하면 잠금 해제됩니다"
#: editor/scene_tree_editor.cpp
msgid ""
"Children are not selectable.\n"
"Click to make selectable"
msgstr ""
-"자식들을 선택 할 수 없습니다.\n"
-"클릭해서 선택 할 수 있게 하십시오"
+"자식들을 선택할 수 없습니다.\n"
+"클릭하면 선택할 수 있게 됩니다"
#: editor/scene_tree_editor.cpp
msgid "Toggle Visibility"
@@ -7336,6 +7361,10 @@ msgstr "그리드맵 설정"
msgid "Pick Distance:"
msgstr "거리 선택:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "솔루션 생성 중..."
@@ -7386,7 +7415,7 @@ msgstr "경고"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "내부 예외 스택 추적의 끝"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7418,7 +7447,8 @@ msgstr "유효하지 않은 시퀀스 출력을 반환한 노드: "
#: modules/visual_script/visual_script.cpp
msgid "Found sequence bit but not the node in the stack, report bug!"
-msgstr "시퀀스 비트를 발견했지만 스택안의 노드에는 없습니다, 버그를 기록!"
+msgstr ""
+"시퀀스 비트를 발견했지만 스택안의 노드에는 없습니다, 버그를 제보하세요!"
#: modules/visual_script/visual_script.cpp
msgid "Stack overflow with stack depth: "
@@ -8033,10 +8063,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "Path 속성은 유효한 Spatial 노드를 가리켜야 합니다."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr "씬마다 WorldEnvironment가 단 하나만 허용됩니다."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8132,6 +8172,12 @@ msgstr "폰트 로딩 에러."
msgid "Invalid font size."
msgstr "유효하지 않은 폰트 크기."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "유효하지 않은 액션 ('/' 또는 ':' 문자 사용 불가)."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "'/' 또는 ':' 문자를 포함할 수 없음"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/lt.po b/editor/translations/lt.po
index 0ad2945bd..6504e570f 100644
--- a/editor/translations/lt.po
+++ b/editor/translations/lt.po
@@ -1380,6 +1380,10 @@ msgstr ""
msgid "Clear Output"
msgstr ""
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3610,6 +3614,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3895,6 +3907,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5557,6 +5573,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6035,7 +6059,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6179,10 +6205,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7221,6 +7243,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7859,10 +7885,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/nb.po b/editor/translations/nb.po
index 8d9ffe7a2..17123dc8f 100644
--- a/editor/translations/nb.po
+++ b/editor/translations/nb.po
@@ -3,7 +3,7 @@
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
#
-# Allan Nordhøy <epost@anotheragency.no>, 2017.
+# Allan Nordhøy <epost@anotheragency.no>, 2017-2018.
# Anonymous <GentleSaucepan@protonmail.com>, 2017.
# flesk <eivindkn@gmail.com>, 2017.
# Jørgen Aarmo Lund <jorgen.aarmo@gmail.com>, 2016.
@@ -14,15 +14,15 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-01-20 08:55+0000\n"
-"Last-Translator: NicolaiF <nico-fre@hotmail.com>\n"
+"PO-Revision-Date: 2018-03-22 03:38+0000\n"
+"Last-Translator: Allan Nordhøy <epost@anotheragency.no>\n"
"Language-Team: Norwegian Bokmål <https://hosted.weblate.org/projects/godot-"
"engine/godot/nb/>\n"
"Language: nb\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 2.19-dev\n"
+"X-Generator: Weblate 2.20-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -1219,15 +1219,15 @@ msgstr "Veksle modus"
#: editor/editor_file_dialog.cpp
msgid "Focus Path"
-msgstr ""
+msgstr "Fokuser Bane"
#: editor/editor_file_dialog.cpp
msgid "Move Favorite Up"
-msgstr "Flytt favoritt oppover"
+msgstr "Flytt Favoritt Oppover"
#: editor/editor_file_dialog.cpp
msgid "Move Favorite Down"
-msgstr "Flytt favoritt nedover"
+msgstr "Flytt Favoritt Nedover"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Go to parent folder"
@@ -1235,7 +1235,7 @@ msgstr "Gå til overnevnt mappe"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Directories & Files:"
-msgstr "Mapper og filer:"
+msgstr "Mapper og Filer:"
#: editor/editor_file_dialog.cpp
msgid "Preview:"
@@ -1252,7 +1252,7 @@ msgstr "Må ha en gyldig filutvidelse."
#: editor/editor_file_system.cpp
msgid "ScanSources"
-msgstr ""
+msgstr "SkannKilder"
#: editor/editor_file_system.cpp
msgid "(Re)Importing Assets"
@@ -1414,6 +1414,10 @@ msgstr "Tøm"
msgid "Clear Output"
msgstr "Output"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Feil ved lagring av ressurs!"
@@ -1640,6 +1644,9 @@ msgid ""
"You can change it later in \"Project Settings\" under the 'application' "
"category."
msgstr ""
+"Valgte scene '%s' er ikke en scenefil, velg en gyldig én?\n"
+"Du kan endre dette senere i \"Prosjekt Instillinger\" under 'applikasjon' "
+"kategorien."
#: editor/editor_node.cpp
msgid "Current scene was never saved, please save it prior to running."
@@ -1655,7 +1662,7 @@ msgstr "Åpne Scene"
#: editor/editor_node.cpp
msgid "Open Base Scene"
-msgstr ""
+msgstr "Åpne Base Scene"
#: editor/editor_node.cpp
msgid "Quick Open Scene.."
@@ -1787,6 +1794,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Unable to load addon script from path: '%s' Script is not in tool mode."
msgstr ""
+"Kunne ikke laste tillegsskript fra sti: '%s' Script er ikke i verktøymodus."
#: editor/editor_node.cpp
msgid ""
@@ -2002,6 +2010,11 @@ msgid ""
"On Android, deploy will use the USB cable for faster performance. This "
"option speeds up testing for games with a large footprint."
msgstr ""
+"Når dette alternativet er aktivert, eksportering eller distribuering vil "
+"produsere en kjørbar fil.\n"
+"Filstystemet vil få prosjektet fra redigerinsverktøyet over nettverket. \n"
+"På Android, distribusjon du vil bruke USB-kabelen for raskere ytelse. Dette "
+"alternativet gjør testing for spill med et stort fotavtrykk raskere."
#: editor/editor_node.cpp
#, fuzzy
@@ -2068,11 +2081,11 @@ msgstr "Redigeringsverktøy"
#: editor/editor_node.cpp editor/settings_config_dialog.cpp
msgid "Editor Settings"
-msgstr ""
+msgstr "Redigeringsverktøy-instillinger"
#: editor/editor_node.cpp
msgid "Editor Layout"
-msgstr ""
+msgstr "Redigeringsverktøy Layout"
#: editor/editor_node.cpp
msgid "Toggle Fullscreen"
@@ -2189,7 +2202,7 @@ msgstr "Last inn en eksisterende ressurs fra disk og rediger den."
#: editor/editor_node.cpp
msgid "Save the currently edited resource."
-msgstr ""
+msgstr "Lagre den nylige redigerte ressursen."
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Save As.."
@@ -2487,7 +2500,7 @@ msgstr "Feil ved laging av sti for mal:\n"
#: editor/export_template_manager.cpp
msgid "Extracting Export Templates"
-msgstr ""
+msgstr "Ekstraherer Eksport Mal"
#: editor/export_template_manager.cpp
msgid "Importing:"
@@ -2851,7 +2864,7 @@ msgstr ""
#: editor/import/resource_importer_scene.cpp
msgid "Generating for Mesh: "
-msgstr ""
+msgstr "Genererer for Maske: "
#: editor/import/resource_importer_scene.cpp
msgid "Running Custom Script.."
@@ -3501,7 +3514,7 @@ msgstr ""
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid "Failed creating lightmap images, make sure path is writable."
-msgstr ""
+msgstr "Laging av lysmapbilder feilet, forsikre om at stien er skrivbar."
#: editor/plugins/baked_lightmap_editor_plugin.cpp
msgid "Bake Lightmaps"
@@ -3760,6 +3773,16 @@ msgstr "Vis veiledere"
#: editor/plugins/canvas_item_editor_plugin.cpp
#, fuzzy
+msgid "Show Origin"
+msgstr "Vis Rutenett"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "Vis hjelpere"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
msgid "Center Selection"
msgstr "Plasser Utvalg I Midten"
@@ -3782,7 +3805,7 @@ msgstr "Sett inn Nøkkel"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Insert Key (Existing Tracks)"
-msgstr ""
+msgstr "Sett inn Nøkkel (Eksisterende Spor)"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Copy Pose"
@@ -3818,11 +3841,11 @@ msgstr "Legger til %s.."
#: editor/plugins/canvas_item_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Ok"
-msgstr ""
+msgstr "Ok"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Cannot instantiate multiple nodes without root."
-msgstr ""
+msgstr "Kan ikke instansiere flere noder uten rot."
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
@@ -4051,6 +4074,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Kunne ikke lage omriss!"
@@ -4222,7 +4249,7 @@ msgstr ""
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Calculating grid size..."
-msgstr ""
+msgstr "Regner ut rutenettstørrelse…"
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Creating heightfield..."
@@ -4283,7 +4310,7 @@ msgstr ""
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Error loading image:"
-msgstr ""
+msgstr "Feil ved innlasting av bilde:"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "No pixels with transparency > 128 in image.."
@@ -4369,7 +4396,7 @@ msgstr ""
#: editor/plugins/particles_editor_plugin.cpp
msgid "Surface Points"
-msgstr ""
+msgstr "Overflatepunkter"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Surface Points+Normal (Directed)"
@@ -4481,11 +4508,11 @@ msgstr "Fjern Funksjon"
#: editor/plugins/path_editor_plugin.cpp
msgid "Split Path"
-msgstr ""
+msgstr "Splitt Sti"
#: editor/plugins/path_editor_plugin.cpp
msgid "Remove Path Point"
-msgstr ""
+msgstr "Fjern Stipunkt"
#: editor/plugins/path_editor_plugin.cpp
#, fuzzy
@@ -4563,7 +4590,7 @@ msgstr ""
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Enable Snap"
-msgstr ""
+msgstr "Aktiver Snap"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Grid"
@@ -4588,28 +4615,28 @@ msgstr "Fjern Ressurs"
#: editor/plugins/resource_preloader_editor_plugin.cpp
msgid "Resource clipboard is empty!"
-msgstr ""
+msgstr "Ressurs-utklippstavle er tom!"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/scene_tree_dock.cpp editor/scene_tree_editor.cpp
msgid "Open in Editor"
-msgstr ""
+msgstr "Åpne i Redigeringsverktøy"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/scene_tree_editor.cpp
msgid "Instance:"
-msgstr ""
+msgstr "Instans:"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp
#: editor/scene_tree_editor.cpp editor/script_editor_debugger.cpp
msgid "Type:"
-msgstr ""
+msgstr "Type:"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Load Resource"
-msgstr ""
+msgstr "Last Ressurs"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4680,35 +4707,33 @@ msgstr "Flytt Ned"
#: editor/plugins/script_editor_plugin.cpp
msgid "Next script"
-msgstr ""
+msgstr "Neste skript"
#: editor/plugins/script_editor_plugin.cpp
msgid "Previous script"
-msgstr ""
+msgstr "Forrige skript"
#: editor/plugins/script_editor_plugin.cpp
msgid "File"
-msgstr ""
+msgstr "Fil"
#: editor/plugins/script_editor_plugin.cpp
msgid "New"
-msgstr ""
+msgstr "Ny"
#: editor/plugins/script_editor_plugin.cpp
msgid "Save All"
-msgstr ""
+msgstr "Lagre Alle"
#: editor/plugins/script_editor_plugin.cpp
msgid "Soft Reload Script"
msgstr ""
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Copy Script Path"
-msgstr "Kopier Sti"
+msgstr "Kopier Skript-Sti"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Show In File System"
msgstr "Vis I Filutforsker"
@@ -4722,35 +4747,35 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
msgid "Reload Theme"
-msgstr ""
+msgstr "Gjeninnlast drakt"
#: editor/plugins/script_editor_plugin.cpp
msgid "Save Theme"
-msgstr ""
+msgstr "Lagre drakt"
#: editor/plugins/script_editor_plugin.cpp
msgid "Save Theme As"
-msgstr ""
+msgstr "Lagre drakt som"
#: editor/plugins/script_editor_plugin.cpp
msgid "Close Docs"
-msgstr ""
+msgstr "Lukk Dokumentasjon"
#: editor/plugins/script_editor_plugin.cpp
msgid "Close All"
-msgstr ""
+msgstr "Lukk Alle"
#: editor/plugins/script_editor_plugin.cpp
msgid "Close Other Tabs"
-msgstr ""
+msgstr "Lukk Andre Faner"
#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp
msgid "Run"
-msgstr ""
+msgstr "Kjør"
#: editor/plugins/script_editor_plugin.cpp
msgid "Toggle Scripts Panel"
-msgstr ""
+msgstr "Veksle skriptpanel"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4764,15 +4789,15 @@ msgstr "Finn neste"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Step Over"
-msgstr ""
+msgstr "Hopp Over"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Step Into"
-msgstr ""
+msgstr "Tre inn i"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Break"
-msgstr ""
+msgstr "Brekk"
#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp
#: editor/script_editor_debugger.cpp
@@ -4781,35 +4806,35 @@ msgstr "Fortsett"
#: editor/plugins/script_editor_plugin.cpp
msgid "Keep Debugger Open"
-msgstr ""
+msgstr "Hold feilretteren åpen"
#: editor/plugins/script_editor_plugin.cpp
msgid "Debug with external editor"
-msgstr ""
+msgstr "Feilrett med ekstern behandler"
#: editor/plugins/script_editor_plugin.cpp
msgid "Open Godot online documentation"
-msgstr ""
+msgstr "Åpne Godots nettbaserte dokumentasjon"
#: editor/plugins/script_editor_plugin.cpp
msgid "Search the class hierarchy."
-msgstr ""
+msgstr "Søk i klasse-hierarkiet."
#: editor/plugins/script_editor_plugin.cpp
msgid "Search the reference documentation."
-msgstr ""
+msgstr "Søk i referanse-dokumentasjonen."
#: editor/plugins/script_editor_plugin.cpp
msgid "Go to previous edited document."
-msgstr ""
+msgstr "Gå til tidligere redigert dokument."
#: editor/plugins/script_editor_plugin.cpp
msgid "Go to next edited document."
-msgstr ""
+msgstr "Gå til neste redigerte dokument."
#: editor/plugins/script_editor_plugin.cpp
msgid "Discard"
-msgstr ""
+msgstr "Forkast"
#: editor/plugins/script_editor_plugin.cpp
msgid "Create Script"
@@ -4823,15 +4848,15 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp
msgid "Reload"
-msgstr ""
+msgstr "Gjeninnlat"
#: editor/plugins/script_editor_plugin.cpp
msgid "Resave"
-msgstr ""
+msgstr "Lagre på nytt"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Debugger"
-msgstr ""
+msgstr "Feilretter"
#: editor/plugins/script_editor_plugin.cpp
msgid ""
@@ -4852,15 +4877,15 @@ msgstr ""
#: editor/plugins/script_text_editor.cpp
msgid "Uppercase"
-msgstr ""
+msgstr "Store versaler"
#: editor/plugins/script_text_editor.cpp
msgid "Lowercase"
-msgstr ""
+msgstr "Små bokstaver"
#: editor/plugins/script_text_editor.cpp
msgid "Capitalize"
-msgstr ""
+msgstr "Store bokstaver"
#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp
#: scene/gui/text_edit.cpp
@@ -4876,7 +4901,7 @@ msgstr "Lim inn"
#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp
#: scene/gui/text_edit.cpp
msgid "Select All"
-msgstr ""
+msgstr "Velg Alle"
#: editor/plugins/script_text_editor.cpp
#, fuzzy
@@ -4885,15 +4910,15 @@ msgstr "Slett Valgte"
#: editor/plugins/script_text_editor.cpp
msgid "Indent Left"
-msgstr ""
+msgstr "Innrykk Venstre"
#: editor/plugins/script_text_editor.cpp
msgid "Indent Right"
-msgstr ""
+msgstr "Innrykk Høyre"
#: editor/plugins/script_text_editor.cpp
msgid "Toggle Comment"
-msgstr ""
+msgstr "Veksle kommentar"
#: editor/plugins/script_text_editor.cpp
#, fuzzy
@@ -4902,15 +4927,15 @@ msgstr "Slett Valgte"
#: editor/plugins/script_text_editor.cpp
msgid "Fold All Lines"
-msgstr ""
+msgstr "Fold sammen alle linjer"
#: editor/plugins/script_text_editor.cpp
msgid "Unfold All Lines"
-msgstr ""
+msgstr "Brett ut alle linjer"
#: editor/plugins/script_text_editor.cpp
msgid "Clone Down"
-msgstr ""
+msgstr "Klon nedover"
#: editor/plugins/script_text_editor.cpp
msgid "Complete Symbol"
@@ -4930,7 +4955,7 @@ msgstr ""
#: editor/plugins/script_text_editor.cpp
msgid "Auto Indent"
-msgstr ""
+msgstr "Automatisk innrykk"
#: editor/plugins/script_text_editor.cpp
#: modules/visual_script/visual_script_editor.cpp
@@ -4951,11 +4976,11 @@ msgstr ""
#: editor/plugins/script_text_editor.cpp
msgid "Convert To Uppercase"
-msgstr ""
+msgstr "Konverter til store versaler"
#: editor/plugins/script_text_editor.cpp
msgid "Convert To Lowercase"
-msgstr ""
+msgstr "Konverter til små versaler"
#: editor/plugins/script_text_editor.cpp
msgid "Find Previous"
@@ -5051,7 +5076,7 @@ msgstr ""
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Comment"
-msgstr ""
+msgstr "Endre Kommentar"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Add/Remove to Color Ramp"
@@ -5135,15 +5160,15 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scaling: "
-msgstr ""
+msgstr "Skalerer: "
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Translating: "
-msgstr ""
+msgstr "Oversetter: "
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotating %s degrees."
-msgstr ""
+msgstr "Roterer %s grader."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Keying is disabled (no key inserted)."
@@ -5168,7 +5193,7 @@ msgstr "Forandre"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Surface Changes"
-msgstr ""
+msgstr "Overflateendringer"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Draw Calls"
@@ -5200,31 +5225,31 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Left"
-msgstr ""
+msgstr "Venstre"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right View."
-msgstr ""
+msgstr "Høyrevisning."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right"
-msgstr ""
+msgstr "Høyre"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Front View."
-msgstr ""
+msgstr "Frontvisning."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Front"
-msgstr ""
+msgstr "Front"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rear View."
-msgstr ""
+msgstr "Bakvisning."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rear"
-msgstr ""
+msgstr "Bak"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Align with view"
@@ -5268,15 +5293,15 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Information"
-msgstr ""
+msgstr "Vis Informasjon"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View FPS"
-msgstr ""
+msgstr "Vis FPS"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Half Resolution"
-msgstr ""
+msgstr "Halver Oppløsning"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Audio Listener"
@@ -5357,27 +5382,27 @@ msgstr "Snap til veiledere"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Bottom View"
-msgstr ""
+msgstr "Bunnvisning"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Top View"
-msgstr ""
+msgstr "Toppvisning"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rear View"
-msgstr ""
+msgstr "Bakvisning"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Front View"
-msgstr ""
+msgstr "Frontvisning"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Left View"
-msgstr ""
+msgstr "Venstrevisning"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right View"
-msgstr ""
+msgstr "Høyrevisning"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Switch Perspective/Orthogonal view"
@@ -5563,15 +5588,15 @@ msgstr ""
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "(empty)"
-msgstr ""
+msgstr "(tom)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Animations"
-msgstr ""
+msgstr "Animasjoner"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Speed (FPS):"
-msgstr ""
+msgstr "Hastighet (FPS):"
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Loop"
@@ -5732,6 +5757,15 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Legg til Element"
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -5741,7 +5775,7 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
msgid "Options"
-msgstr ""
+msgstr "Innstillinger"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Have,Many,Several,Options!"
@@ -5749,15 +5783,15 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 1"
-msgstr ""
+msgstr "Fane 1"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 2"
-msgstr ""
+msgstr "Fane 2"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Tab 3"
-msgstr ""
+msgstr "Fane 3"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Data Type:"
@@ -5765,7 +5799,7 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
msgid "Icon"
-msgstr ""
+msgstr "Ikon"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Style"
@@ -5773,15 +5807,15 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
msgid "Font"
-msgstr ""
+msgstr "Font"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Color"
-msgstr ""
+msgstr "Farge"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme"
-msgstr ""
+msgstr "Tema"
#: editor/plugins/tile_map_editor_plugin.cpp
#, fuzzy
@@ -5822,11 +5856,11 @@ msgstr ""
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Mirror X"
-msgstr ""
+msgstr "Speil X"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Mirror Y"
-msgstr ""
+msgstr "Speil Y"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Paint Tile"
@@ -5838,19 +5872,19 @@ msgstr ""
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 0 degrees"
-msgstr ""
+msgstr "Roter 0 grader"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 90 degrees"
-msgstr ""
+msgstr "Roter 90 grader"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 180 degrees"
-msgstr ""
+msgstr "Roter 180 grader"
#: editor/plugins/tile_map_editor_plugin.cpp
msgid "Rotate 270 degrees"
-msgstr ""
+msgstr "Roter 270 grader"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Could not find tile:"
@@ -5912,11 +5946,11 @@ msgstr ""
#: editor/progress_dialog.cpp scene/gui/dialogs.cpp
msgid "Cancel"
-msgstr ""
+msgstr "Avbryt"
#: editor/project_export.cpp
msgid "Runnable"
-msgstr ""
+msgstr "Kjørbar"
#: editor/project_export.cpp
msgid "Delete patch '%s' from list?"
@@ -5940,19 +5974,19 @@ msgstr ""
#: editor/project_export.cpp
msgid "Resources"
-msgstr ""
+msgstr "Ressurser"
#: editor/project_export.cpp
msgid "Export all resources in the project"
-msgstr ""
+msgstr "Eksporter alle ressurser til prosjektet"
#: editor/project_export.cpp
msgid "Export selected scenes (and dependencies)"
-msgstr ""
+msgstr "Eksporter valgte scener (og avhengigheter)"
#: editor/project_export.cpp
msgid "Export selected resources (and dependencies)"
-msgstr ""
+msgstr "Exporter valgte ressurs (og avhengigheter)"
#: editor/project_export.cpp
msgid "Export Mode:"
@@ -6046,39 +6080,40 @@ msgid ""
"Couldn't load project.godot in project path (error %d). It may be missing or "
"corrupted."
msgstr ""
+"Kunne ikke laste project.godot i prosjekt sti (error %d). Den kan være "
+"manglet eller korruptert."
#: editor/project_manager.cpp
msgid "Couldn't edit project.godot in project path."
-msgstr ""
+msgstr "Kunne ikke endre project.godot i projsektstien."
#: editor/project_manager.cpp
msgid "Couldn't create project.godot in project path."
-msgstr ""
+msgstr "Kunne ikke lage project.godot i prosjektstien."
#: editor/project_manager.cpp
msgid "The following files failed extraction from package:"
-msgstr ""
+msgstr "De følgende filene feilet ekstrahering fra pakke:"
#: editor/project_manager.cpp
msgid "Rename Project"
-msgstr ""
+msgstr "Endre Navn på Prosjekt"
#: editor/project_manager.cpp
msgid "New Game Project"
-msgstr ""
+msgstr "Nytt Spill-Prosjekt"
#: editor/project_manager.cpp
msgid "Import Existing Project"
-msgstr ""
+msgstr "Importer Eksisterende Prosjekt"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Import & Edit"
-msgstr "Importer"
+msgstr "Importer & Endre"
#: editor/project_manager.cpp
msgid "Create New Project"
-msgstr ""
+msgstr "Opprett Nytt Prosjekt"
#: editor/project_manager.cpp
#, fuzzy
@@ -6087,24 +6122,23 @@ msgstr "Opprett skript"
#: editor/project_manager.cpp
msgid "Install Project:"
-msgstr ""
+msgstr "Installer Prosjekt:"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Install & Edit"
-msgstr "Installer"
+msgstr "Installer & Endre"
#: editor/project_manager.cpp
msgid "Project Name:"
-msgstr ""
+msgstr "Prosjektnavn:"
#: editor/project_manager.cpp
msgid "Create folder"
-msgstr ""
+msgstr "Opprett mappe"
#: editor/project_manager.cpp
msgid "Project Path:"
-msgstr ""
+msgstr "Prosjektsti:"
#: editor/project_manager.cpp
msgid "Browse"
@@ -6116,11 +6150,11 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Can't open project"
-msgstr ""
+msgstr "Kan ikke åpne prosjekt"
#: editor/project_manager.cpp
msgid "Are you sure to open more than one project?"
-msgstr ""
+msgstr "Er du sikker på at du vil åpne mer enn ett prosjekt?"
#: editor/project_manager.cpp
msgid ""
@@ -6137,11 +6171,11 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Are you sure to run more than one project?"
-msgstr ""
+msgstr "Er du sikker på at du vil kjøre mer enn ett prosjekt?"
#: editor/project_manager.cpp
msgid "Remove project from the list? (Folder contents will not be modified)"
-msgstr ""
+msgstr "Fjern prosjekt fra listen? (Mappeinnhold vil ikke bli modifisert)"
#: editor/project_manager.cpp
msgid ""
@@ -6154,6 +6188,8 @@ msgid ""
"You are about the scan %s folders for existing Godot projects. Do you "
"confirm?"
msgstr ""
+"Du er i ferd med å skanne %s mapper for eksisterende Godotprosjekter. "
+"Bekrefter du?"
#: editor/project_manager.cpp
msgid "Project Manager"
@@ -6161,19 +6197,19 @@ msgstr "Prosjektleder"
#: editor/project_manager.cpp
msgid "Project List"
-msgstr ""
+msgstr "Prosjektliste"
#: editor/project_manager.cpp
msgid "Scan"
-msgstr ""
+msgstr "Skann"
#: editor/project_manager.cpp
msgid "Select a Folder to Scan"
-msgstr ""
+msgstr "Velg en Mappe å Skanne"
#: editor/project_manager.cpp
msgid "New Project"
-msgstr ""
+msgstr "Nytt Prosjekt"
#: editor/project_manager.cpp
msgid "Templates"
@@ -6181,15 +6217,15 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Exit"
-msgstr ""
+msgstr "Avslutt"
#: editor/project_manager.cpp
msgid "Restart Now"
-msgstr ""
+msgstr "Omstart Nå"
#: editor/project_manager.cpp
msgid "Can't run project"
-msgstr ""
+msgstr "Kan ikke kjøre prosjekt"
#: editor/project_manager.cpp
msgid ""
@@ -6211,10 +6247,12 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Mouse Button"
-msgstr ""
+msgstr "Museknapp"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6360,12 +6398,8 @@ msgid "Delete Item"
msgstr "Slett Valgte"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
-msgstr ""
+msgstr "Eksisterer allerede"
#: editor/project_settings_editor.cpp
msgid "Add Input Action"
@@ -7419,6 +7453,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
#, fuzzy
msgid "Generating solution..."
@@ -8084,10 +8122,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/nl.po b/editor/translations/nl.po
index e54a0c796..9927fd8e8 100644
--- a/editor/translations/nl.po
+++ b/editor/translations/nl.po
@@ -13,6 +13,7 @@
# Ferdinand de Coninck <ferdinand.deconinck@gmail.com>, 2018.
# Jorn Theunissen <jorn-theunissen@hotmail.com>, 2018.
# Maikel <maikel_martens_1@hotmail.com>, 2017.
+# millenniumproof <millenniumproof@gmail.com>, 2018.
# Pieter-Jan Briers <pieterjan.briers@gmail.com>, 2017-2018.
# Robin Arys <robinarys@hotmail.com>, 2017.
# Senno Kaasjager <senno.kaasjager@gmail.com>, 2017.
@@ -24,15 +25,15 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-02-17 19:35+0000\n"
-"Last-Translator: Pieter-Jan Briers <pieterjan.briers@gmail.com>\n"
+"PO-Revision-Date: 2018-04-06 15:35+0000\n"
+"Last-Translator: millenniumproof <millenniumproof@gmail.com>\n"
"Language-Team: Dutch <https://hosted.weblate.org/projects/godot-engine/godot/"
"nl/>\n"
"Language: nl\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 2.19\n"
+"X-Generator: Weblate 2.20\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -754,7 +755,7 @@ msgstr "Gouden Sponsors"
#: editor/editor_about.cpp
msgid "Mini Sponsors"
-msgstr "Mini Sponsors"
+msgstr "Mini Sponsoren"
#: editor/editor_about.cpp
msgid "Gold Donors"
@@ -770,7 +771,7 @@ msgstr "Bronzen Donors"
#: editor/editor_about.cpp
msgid "Donors"
-msgstr "Donors"
+msgstr "Donoren"
#: editor/editor_about.cpp
msgid "License"
@@ -1412,6 +1413,10 @@ msgstr "Leegmaken"
msgid "Clear Output"
msgstr "Maak Uitvoer Leeg"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Error bij het opslaan van resource!"
@@ -3087,14 +3092,12 @@ msgid "Enable Onion Skinning"
msgstr "\"Onion Skinning\" Inschakelen"
#: editor/plugins/animation_player_editor_plugin.cpp
-#, fuzzy
msgid "Directions"
-msgstr "Beschrijving"
+msgstr "Richtingen"
#: editor/plugins/animation_player_editor_plugin.cpp
-#, fuzzy
msgid "Past"
-msgstr "Plakken"
+msgstr "Verleden"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Future"
@@ -3612,9 +3615,8 @@ msgid "Pan Mode"
msgstr "Verschuif Modus"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Toggles snapping"
-msgstr "Breekpunt Aan- of Uitschakelen"
+msgstr "Snappen Aan- of Uitschakelen"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Use Snap"
@@ -3637,9 +3639,8 @@ msgid "Configure Snap..."
msgstr "Configureer Uitlijnen..."
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Snap Relative"
-msgstr "Uitlijn Relatief"
+msgstr "Snap Relatief"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Use Pixel Snap"
@@ -3650,14 +3651,12 @@ msgid "Smart snapping"
msgstr "Slim Uitlijnen"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Snap to parent"
-msgstr "Uitlijnen naar ouder"
+msgstr "Snap naar ouder"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Snap to node anchor"
-msgstr "Uitlijnen naar node anker"
+msgstr "Snap naar node anker"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Snap to node sides"
@@ -3736,6 +3735,16 @@ msgid "Show Guides"
msgstr "Toon hulplijnen"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Raster Weergeven"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "Toon helpers"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Centreer Selectie"
@@ -3772,9 +3781,8 @@ msgid "Drag pivot from mouse position"
msgstr "Versleep draaipunt vanaf muispositie"
#: editor/plugins/canvas_item_editor_plugin.cpp
-#, fuzzy
msgid "Set pivot at mouse position"
-msgstr "Verwijder Signaal"
+msgstr "Plaats pivot bij muispositie"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Multiply grid step by 2"
@@ -3894,9 +3902,8 @@ msgid "Remove point"
msgstr "Punt verwijderen"
#: editor/plugins/curve_editor_plugin.cpp
-#, fuzzy
msgid "Left linear"
-msgstr "Lineair"
+msgstr "Links Lineair"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Right linear"
@@ -3907,9 +3914,8 @@ msgid "Load preset"
msgstr "Laad voorinstelling"
#: editor/plugins/curve_editor_plugin.cpp
-#, fuzzy
msgid "Remove Curve Point"
-msgstr "Verwijder Signaal"
+msgstr "Verwijder Curve Punt"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Toggle Curve Linear Tangent"
@@ -3925,9 +3931,8 @@ msgid "Bake GI Probe"
msgstr "Bak GI Probe"
#: editor/plugins/gradient_editor_plugin.cpp
-#, fuzzy
msgid "Add/Remove Color Ramp Point"
-msgstr "Verwijder/Voeg Kleuren Hellings Punt Toe"
+msgstr "Voeg Toe/Verwijder Kleur Hellingspunt"
#: editor/plugins/gradient_editor_plugin.cpp
#: editor/plugins/shader_graph_editor_plugin.cpp
@@ -3953,7 +3958,7 @@ msgid ""
"Create and assign one?"
msgstr ""
"Geen OccluderPolygon2D resource op deze node.\n"
-"Creëer en wijs een toe?"
+"Creëer en wijs één toe?"
#: editor/plugins/light_occluder_2d_editor_plugin.cpp
msgid "Create Occluder Polygon"
@@ -4012,13 +4017,12 @@ msgid "Create Navigation Mesh"
msgstr "Creëer Navigatie Mesh"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-#, fuzzy
msgid "Contained Mesh is not of type ArrayMesh."
-msgstr "Bevatte Mesh is niet van type ArrayMesh"
+msgstr "Bevatte Mesh is niet van type ArrayMesh."
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "UV Unwrap failed, mesh may not be manifold?"
-msgstr ""
+msgstr "UV Uitpakken is gefaald, wellicht is de mesh niet manifold?"
#: editor/plugins/mesh_instance_editor_plugin.cpp
#, fuzzy
@@ -4038,6 +4042,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Mesh heeft geen oppervlakte om omlijning van te maken!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Kon omlijning niet maken!"
@@ -4058,14 +4066,12 @@ msgid "Create Convex Static Body"
msgstr "Creëer Convex Statisch Lichaam"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-#, fuzzy
msgid "Create Trimesh Collision Sibling"
-msgstr "Creëer Trimesh Botsings Broer"
+msgstr "Creëer Trimesh Botsing Broer"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-#, fuzzy
msgid "Create Convex Collision Sibling"
-msgstr "Creëer Convex Botsings Broer"
+msgstr "Creëer Convex Botsing Broer"
#: editor/plugins/mesh_instance_editor_plugin.cpp
#, fuzzy
@@ -4082,7 +4088,7 @@ msgstr "Geef UV2 Weer"
#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Unwrap UV2 for Lightmap/AO"
-msgstr ""
+msgstr "Pak UV2 uit voor Lichtmap/AO"
#: editor/plugins/mesh_instance_editor_plugin.cpp
#, fuzzy
@@ -4090,9 +4096,8 @@ msgid "Create Outline Mesh"
msgstr "Creëer Omlijning Mesh"
#: editor/plugins/mesh_instance_editor_plugin.cpp
-#, fuzzy
msgid "Outline Size:"
-msgstr "Omlijning Grootte:"
+msgstr "Grootte Omlijning:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "No mesh source specified (and no MultiMesh set in node)."
@@ -4138,7 +4143,7 @@ msgstr "Ouder heeft geen vaste vlakken om te bevolken."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Couldn't map area."
-msgstr ""
+msgstr "Kon het gebied niet mappen."
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Select a Source Mesh:"
@@ -4150,7 +4155,7 @@ msgstr "Selecteer een Doel Oppervlakte:"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Populate Surface"
-msgstr ""
+msgstr "Bevolk Oppervlakte"
#: editor/plugins/multimesh_editor_plugin.cpp
msgid "Populate MultiMesh"
@@ -4221,9 +4226,8 @@ msgid "Creating heightfield..."
msgstr "Hoogteveld aan het creëeren..."
#: editor/plugins/navigation_mesh_generator.cpp
-#, fuzzy
msgid "Marking walkable triangles..."
-msgstr "Lokale wijziging aan het opslaan.."
+msgstr "Markeer loopbare driehoeken.."
#: editor/plugins/navigation_mesh_generator.cpp
#, fuzzy
@@ -4231,9 +4235,8 @@ msgid "Constructing compact heightfield..."
msgstr "Compact hoogteveld aan het bouwen..."
#: editor/plugins/navigation_mesh_generator.cpp
-#, fuzzy
msgid "Eroding walkable area..."
-msgstr "Wandelbaar gebied eroderen..."
+msgstr "Wandelbaar gebied aan het eroderen..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Partitioning..."
@@ -4249,15 +4252,15 @@ msgstr "Polymesh aan het creëeren..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Converting to native navigation mesh..."
-msgstr ""
+msgstr "Naar navigatie mesh aan het converteren..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Navigation Mesh Generator Setup:"
-msgstr ""
+msgstr "Navigatie Mesh Generator Setup:"
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Parsing Geometry..."
-msgstr ""
+msgstr "Geometrie aan het ontleden..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Done!"
@@ -4274,7 +4277,7 @@ msgstr "AABB Genereren"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Can only set point into a ParticlesMaterial process material"
-msgstr ""
+msgstr "Kan punt alleen plaatsen in een PartikelsMateriaal proces materiaal"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Error loading image:"
@@ -4286,250 +4289,246 @@ msgstr "Geen pixels met transparantie > 128 in afbeelding.."
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generate Visibility Rect"
-msgstr ""
+msgstr "Genereer Zichtbaarheid Rechthoek"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Load Emission Mask"
-msgstr ""
+msgstr "Laad Emissie Masker"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Clear Emission Mask"
-msgstr ""
+msgstr "Leeg Emissie Masker"
#: editor/plugins/particles_2d_editor_plugin.cpp
#: editor/plugins/particles_editor_plugin.cpp
msgid "Particles"
-msgstr ""
+msgstr "Partikels"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Generated Point Count:"
-msgstr ""
+msgstr "Telling Gegenereerde Punten:"
#: editor/plugins/particles_2d_editor_plugin.cpp
#: editor/plugins/particles_editor_plugin.cpp
msgid "Generation Time (sec):"
-msgstr ""
+msgstr "Genereer Tijd (sec):"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Emission Mask"
-msgstr ""
+msgstr "Emissie Masker"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Capture from Pixel"
-msgstr ""
+msgstr "Neem uit Pixel"
#: editor/plugins/particles_2d_editor_plugin.cpp
msgid "Emission Colors"
-msgstr ""
+msgstr "Kleuren Emissie"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Node does not contain geometry."
-msgstr ""
+msgstr "Node bevat geen geometrie."
#: editor/plugins/particles_editor_plugin.cpp
msgid "Node does not contain geometry (faces)."
-msgstr ""
+msgstr "Node bevat geen geometrie (vlakken)."
#: editor/plugins/particles_editor_plugin.cpp
msgid "A processor material of type 'ParticlesMaterial' is required."
-msgstr ""
+msgstr "Een processor materiaal of type 'PartikelMateriaal' is nodig."
#: editor/plugins/particles_editor_plugin.cpp
msgid "Faces contain no area!"
-msgstr ""
+msgstr "Vlakken bevatten geen gebied!"
#: editor/plugins/particles_editor_plugin.cpp
msgid "No faces!"
-msgstr ""
+msgstr "Geen vlakken!"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Generate AABB"
-msgstr ""
+msgstr "Genereer AABB"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Mesh"
-msgstr ""
+msgstr "Creëer Emissie Punten Vanuit Mesh"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emission Points From Node"
-msgstr ""
+msgstr "Creëer Emissie Punten Vanuit Node"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Create Emitter"
-msgstr ""
+msgstr "Creëer Emitter"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Emission Points:"
-msgstr ""
+msgstr "Emissie Punten:"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Surface Points"
-msgstr ""
+msgstr "Oppervlakte Punten"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Surface Points+Normal (Directed)"
-msgstr ""
+msgstr "Oppervlakte Punten+Normaal (Gericht)"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Volume"
-msgstr ""
+msgstr "Volume"
#: editor/plugins/particles_editor_plugin.cpp
msgid "Emission Source: "
-msgstr ""
+msgstr "Emissie Bron: "
#: editor/plugins/particles_editor_plugin.cpp
msgid "Generate Visibility AABB"
-msgstr ""
+msgstr "Genereer Zichtbaarheid AABB"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Remove Point from Curve"
-msgstr ""
+msgstr "Verwijder Punt van Curve"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Remove Out-Control from Curve"
-msgstr ""
+msgstr "Verwijder Uit-Control van Curve"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Remove In-Control from Curve"
-msgstr ""
+msgstr "Verwijder In-Controle van Curve"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Add Point to Curve"
-msgstr ""
+msgstr "Voeg Punt toe aan Curve"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Move Point in Curve"
-msgstr ""
+msgstr "Verplaats Punt in Curve"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Move In-Control in Curve"
-msgstr ""
+msgstr "Verplaats In-Control in Curve"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Move Out-Control in Curve"
-msgstr ""
+msgstr "Verplaats Uit-Controle in Curve"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Select Points"
-msgstr ""
+msgstr "Selecteer Punten"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Shift+Drag: Select Control Points"
-msgstr ""
+msgstr "Shift+Drag: Selecteer Controle Punten"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Click: Add Point"
-msgstr ""
+msgstr "Klik: Voeg Punt Toe"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Right Click: Delete Point"
-msgstr ""
+msgstr "Rechter Klik: Verwijder Punt"
#: editor/plugins/path_2d_editor_plugin.cpp
msgid "Select Control Points (Shift+Drag)"
-msgstr ""
+msgstr "Selecteer Controle Punten (Shift+Sleep)"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Add Point (in empty space)"
-msgstr ""
+msgstr "Voeg Punt Toe (in lege ruimte)"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Split Segment (in curve)"
-msgstr ""
+msgstr "Splits Segment (in curve)"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Delete Point"
-msgstr ""
+msgstr "Verwijder Punt"
#: editor/plugins/path_2d_editor_plugin.cpp
#: editor/plugins/path_editor_plugin.cpp
msgid "Close Curve"
-msgstr ""
+msgstr "Sluit Curve"
#: editor/plugins/path_editor_plugin.cpp
msgid "Curve Point #"
-msgstr ""
+msgstr "Curve Punt #"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Set Curve Point Position"
-msgstr "Verwijder Signaal"
+msgstr "Zet Curve Punt Positie"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Set Curve In Position"
-msgstr "Verwijder Signaal"
+msgstr "Zet Curve In Positie"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Set Curve Out Position"
-msgstr "Verwijder Signaal"
+msgstr "Set Curve Uit Positie"
#: editor/plugins/path_editor_plugin.cpp
msgid "Split Path"
-msgstr ""
+msgstr "Splits Pad"
#: editor/plugins/path_editor_plugin.cpp
msgid "Remove Path Point"
-msgstr ""
+msgstr "Verwijder Pad Punt"
#: editor/plugins/path_editor_plugin.cpp
-#, fuzzy
msgid "Remove Out-Control Point"
-msgstr "Verwijder Autoload"
+msgstr "Verwijder Uit-Controle Punt"
#: editor/plugins/path_editor_plugin.cpp
msgid "Remove In-Control Point"
-msgstr ""
+msgstr "Verwijder In-Controle Punt"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Create UV Map"
-msgstr ""
+msgstr "Creëer UV Map"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Transform UV Map"
-msgstr ""
+msgstr "Transformeer UV Map"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Polygon 2D UV Editor"
-msgstr ""
+msgstr "Polygon 2D UV Editor"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Move Point"
-msgstr ""
+msgstr "Beweeg Punt"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Ctrl: Rotate"
-msgstr ""
+msgstr "Ctrl: Roteer"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Shift: Move All"
-msgstr ""
+msgstr "Shift: Beweeg alles"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Shift+Ctrl: Scale"
-msgstr ""
+msgstr "Shift+Ctrl: Schaal"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Move Polygon"
-msgstr ""
+msgstr "Beweeg Polygon"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Rotate Polygon"
-msgstr ""
+msgstr "Roteer Polygon"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Scale Polygon"
-msgstr ""
+msgstr "Schaal Polygon"
#: editor/plugins/polygon_2d_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4541,70 +4540,70 @@ msgstr "Bewerken"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Polygon->UV"
-msgstr ""
+msgstr "Polygon->UV"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "UV->Polygon"
-msgstr ""
+msgstr "UV->Polygon"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Clear UV"
-msgstr ""
+msgstr "Wis UV"
#: editor/plugins/polygon_2d_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Snap"
-msgstr ""
+msgstr "Snap"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Enable Snap"
-msgstr ""
+msgstr "Zet Snap Aan"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Grid"
-msgstr ""
+msgstr "Grid"
#: editor/plugins/resource_preloader_editor_plugin.cpp
msgid "ERROR: Couldn't load resource!"
-msgstr ""
+msgstr "ERROR: Laden van bron mislukt!"
#: editor/plugins/resource_preloader_editor_plugin.cpp
msgid "Add Resource"
-msgstr ""
+msgstr "Voeg Bron Toe"
#: editor/plugins/resource_preloader_editor_plugin.cpp
msgid "Rename Resource"
-msgstr ""
+msgstr "Naam Wijzigen Bron"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Delete Resource"
-msgstr ""
+msgstr "Verwijder Bron"
#: editor/plugins/resource_preloader_editor_plugin.cpp
msgid "Resource clipboard is empty!"
-msgstr ""
+msgstr "Bronnen klembord is leeg!"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/scene_tree_dock.cpp editor/scene_tree_editor.cpp
msgid "Open in Editor"
-msgstr ""
+msgstr "Openen in Editor"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/scene_tree_editor.cpp
msgid "Instance:"
-msgstr ""
+msgstr "Instantie:"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp
#: editor/scene_tree_editor.cpp editor/script_editor_debugger.cpp
msgid "Type:"
-msgstr ""
+msgstr "Type:"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/sprite_frames_editor_plugin.cpp
msgid "Load Resource"
-msgstr ""
+msgstr "Laad Bron"
#: editor/plugins/resource_preloader_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
@@ -4614,249 +4613,246 @@ msgid "Paste"
msgstr "Plakken"
#: editor/plugins/resource_preloader_editor_plugin.cpp
-#, fuzzy
msgid "ResourcePreloader"
-msgstr "Resource"
+msgstr "BronnenPrelader"
#: editor/plugins/script_editor_plugin.cpp
msgid "Clear Recent Files"
-msgstr ""
+msgstr "Wis Recente Bestanden"
#: editor/plugins/script_editor_plugin.cpp
msgid "Close and save changes?"
-msgstr ""
+msgstr "Wijzigingen oplaan en sluiten?"
#: editor/plugins/script_editor_plugin.cpp
msgid "Error while saving theme"
-msgstr ""
+msgstr "Fout bij het opslaan van het thema"
#: editor/plugins/script_editor_plugin.cpp
msgid "Error saving"
-msgstr ""
+msgstr "Fout bij het opslaan"
#: editor/plugins/script_editor_plugin.cpp
msgid "Error importing theme"
-msgstr ""
+msgstr "Fout bij import van thema"
#: editor/plugins/script_editor_plugin.cpp
msgid "Error importing"
-msgstr ""
+msgstr "Fout bij importeren"
#: editor/plugins/script_editor_plugin.cpp
msgid "Import Theme"
-msgstr ""
+msgstr "Importeer Thema"
#: editor/plugins/script_editor_plugin.cpp
msgid "Save Theme As.."
-msgstr ""
+msgstr "Thema Opslaan Als.."
#: editor/plugins/script_editor_plugin.cpp
msgid " Class Reference"
-msgstr ""
+msgstr " Klasse Referentie"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Sort"
-msgstr "Sorteren:"
+msgstr "Sorteren"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp editor/scene_tree_dock.cpp
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Move Up"
-msgstr ""
+msgstr "Plaats Omhoog"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp editor/scene_tree_dock.cpp
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Move Down"
-msgstr ""
+msgstr "Plaats Omlaag"
#: editor/plugins/script_editor_plugin.cpp
msgid "Next script"
-msgstr ""
+msgstr "Volgend script"
#: editor/plugins/script_editor_plugin.cpp
msgid "Previous script"
-msgstr ""
+msgstr "Vorig script"
#: editor/plugins/script_editor_plugin.cpp
msgid "File"
-msgstr ""
+msgstr "Bestand"
#: editor/plugins/script_editor_plugin.cpp
msgid "New"
-msgstr ""
+msgstr "Nieuw"
#: editor/plugins/script_editor_plugin.cpp
msgid "Save All"
-msgstr ""
+msgstr "Alles Opslaan"
#: editor/plugins/script_editor_plugin.cpp
msgid "Soft Reload Script"
-msgstr ""
+msgstr "Zacht Herladen Script"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Copy Script Path"
-msgstr "Kopieer Pad"
+msgstr "Kopieer Script Pad"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Show In File System"
-msgstr "Weergeven in Bestandsbeheer"
+msgstr "Toon in Bestandsbeheer"
#: editor/plugins/script_editor_plugin.cpp
msgid "History Prev"
-msgstr ""
+msgstr "Geschiedenis voorgaande"
#: editor/plugins/script_editor_plugin.cpp
msgid "History Next"
-msgstr ""
+msgstr "Geschiedenis Volgende"
#: editor/plugins/script_editor_plugin.cpp
msgid "Reload Theme"
-msgstr ""
+msgstr "Herlaad Thema"
#: editor/plugins/script_editor_plugin.cpp
msgid "Save Theme"
-msgstr ""
+msgstr "Thema Opslaan"
#: editor/plugins/script_editor_plugin.cpp
msgid "Save Theme As"
-msgstr ""
+msgstr "Thema Opslaan Als"
#: editor/plugins/script_editor_plugin.cpp
msgid "Close Docs"
-msgstr ""
+msgstr "Sluit Docs"
#: editor/plugins/script_editor_plugin.cpp
msgid "Close All"
-msgstr ""
+msgstr "Sluit Alles"
#: editor/plugins/script_editor_plugin.cpp
msgid "Close Other Tabs"
-msgstr ""
+msgstr "Sluit Andere Tabbladen"
#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp
msgid "Run"
msgstr "Starten"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Toggle Scripts Panel"
-msgstr "Toggle Favoriet"
+msgstr "Schakel Scripten Paneel"
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
msgid "Find.."
-msgstr ""
+msgstr "Vind.."
#: editor/plugins/script_editor_plugin.cpp
#: editor/plugins/script_text_editor.cpp
msgid "Find Next"
-msgstr ""
+msgstr "Vind Volgende"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Step Over"
-msgstr ""
+msgstr "Stap Over"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Step Into"
-msgstr ""
+msgstr "Stap In"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Break"
-msgstr ""
+msgstr "Breek"
#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp
#: editor/script_editor_debugger.cpp
msgid "Continue"
-msgstr ""
+msgstr "Vervolg"
#: editor/plugins/script_editor_plugin.cpp
msgid "Keep Debugger Open"
-msgstr ""
+msgstr "Houd Debugger Open"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Debug with external editor"
-msgstr "Afhankelijkheden Editor"
+msgstr "Debug met externe editor"
#: editor/plugins/script_editor_plugin.cpp
msgid "Open Godot online documentation"
-msgstr ""
+msgstr "Open Godot online documentatie"
#: editor/plugins/script_editor_plugin.cpp
msgid "Search the class hierarchy."
-msgstr ""
+msgstr "Zoek in de klasse hiërarchie."
#: editor/plugins/script_editor_plugin.cpp
msgid "Search the reference documentation."
-msgstr ""
+msgstr "Zoek in de referentie documentatie."
#: editor/plugins/script_editor_plugin.cpp
msgid "Go to previous edited document."
-msgstr ""
+msgstr "Ga naar voorgaand bewerkte document."
#: editor/plugins/script_editor_plugin.cpp
msgid "Go to next edited document."
-msgstr ""
+msgstr "Ga naar volgend bewerkte document."
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Discard"
-msgstr "Discreet"
+msgstr "Verwerp"
#: editor/plugins/script_editor_plugin.cpp
msgid "Create Script"
-msgstr ""
+msgstr "Creëer Script"
#: editor/plugins/script_editor_plugin.cpp
msgid ""
"The following files are newer on disk.\n"
"What action should be taken?:"
msgstr ""
+"De volgende bestanden zijn nieuwer op de schijf.\n"
+"Welke aktie moet worden genomen?:"
#: editor/plugins/script_editor_plugin.cpp
msgid "Reload"
-msgstr ""
+msgstr "Herlaad"
#: editor/plugins/script_editor_plugin.cpp
msgid "Resave"
-msgstr ""
+msgstr "Heropslaan"
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Debugger"
-msgstr ""
+msgstr "Debugger"
#: editor/plugins/script_editor_plugin.cpp
msgid ""
"Built-in scripts can only be edited when the scene they belong to is loaded"
msgstr ""
+"Ingebouwde scripts kunnen alleen ge-edit worden wanneer de bijbehorende "
+"scène geladen is"
#: editor/plugins/script_text_editor.cpp
msgid "Only resources from filesystem can be dropped."
-msgstr ""
+msgstr "Alleen bronnen uit bestandssysteem kunnen gedropt worden."
#: editor/plugins/script_text_editor.cpp
msgid "Pick Color"
-msgstr ""
+msgstr "Kies Kleur"
#: editor/plugins/script_text_editor.cpp
msgid "Convert Case"
-msgstr ""
+msgstr "Converteer Hoofdlettergebruik"
#: editor/plugins/script_text_editor.cpp
msgid "Uppercase"
-msgstr ""
+msgstr "Hoofdletters"
#: editor/plugins/script_text_editor.cpp
msgid "Lowercase"
-msgstr ""
+msgstr "Kleine letters"
#: editor/plugins/script_text_editor.cpp
msgid "Capitalize"
-msgstr ""
+msgstr "Maak Hoofdletters"
#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp
#: scene/gui/text_edit.cpp
@@ -4875,58 +4871,56 @@ msgid "Select All"
msgstr "Alles Selecteren"
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Delete Line"
-msgstr "Verwijder"
+msgstr "Verwijder Regel"
#: editor/plugins/script_text_editor.cpp
msgid "Indent Left"
-msgstr ""
+msgstr "Links Inspringen"
#: editor/plugins/script_text_editor.cpp
msgid "Indent Right"
-msgstr ""
+msgstr "Rechts Inspringen"
#: editor/plugins/script_text_editor.cpp
msgid "Toggle Comment"
-msgstr ""
+msgstr "Commentaar Aan/Uit"
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Fold/Unfold Line"
-msgstr "Ga naar Regel"
+msgstr "Vouw/Ontvouw Regel"
#: editor/plugins/script_text_editor.cpp
msgid "Fold All Lines"
-msgstr ""
+msgstr "Vouw Alle Regels"
#: editor/plugins/script_text_editor.cpp
msgid "Unfold All Lines"
-msgstr ""
+msgstr "Ontvouw Alle Regels"
#: editor/plugins/script_text_editor.cpp
msgid "Clone Down"
-msgstr ""
+msgstr "Kloon Omlaag"
#: editor/plugins/script_text_editor.cpp
msgid "Complete Symbol"
-msgstr ""
+msgstr "Voltooi Symbool"
#: editor/plugins/script_text_editor.cpp
msgid "Trim Trailing Whitespace"
-msgstr ""
+msgstr "Trim Navolgende Spaties"
#: editor/plugins/script_text_editor.cpp
msgid "Convert Indent To Spaces"
-msgstr ""
+msgstr "Converteer Indentatie Naar Spaties"
#: editor/plugins/script_text_editor.cpp
msgid "Convert Indent To Tabs"
-msgstr ""
+msgstr "Converteer Indentatie Naar Tabs"
#: editor/plugins/script_text_editor.cpp
msgid "Auto Indent"
-msgstr ""
+msgstr "Auto Indentatie"
#: editor/plugins/script_text_editor.cpp
#: modules/visual_script/visual_script_editor.cpp
@@ -4935,389 +4929,379 @@ msgstr "Breekpunt Aan- of Uitschakelen"
#: editor/plugins/script_text_editor.cpp
msgid "Remove All Breakpoints"
-msgstr ""
+msgstr "Verwijder Alle Breekpunten"
#: editor/plugins/script_text_editor.cpp
msgid "Goto Next Breakpoint"
-msgstr ""
+msgstr "Ga Naar Volgende Breekpunt"
#: editor/plugins/script_text_editor.cpp
msgid "Goto Previous Breakpoint"
-msgstr ""
+msgstr "Ga Naar Vorige Breekpunt"
#: editor/plugins/script_text_editor.cpp
msgid "Convert To Uppercase"
-msgstr ""
+msgstr "Converteer Naar Hoofdletters"
#: editor/plugins/script_text_editor.cpp
-#, fuzzy
msgid "Convert To Lowercase"
-msgstr "Verbind Aan Node:"
+msgstr "Converteer Naar Kleine Letters"
#: editor/plugins/script_text_editor.cpp
msgid "Find Previous"
-msgstr ""
+msgstr "Vind Vorige"
#: editor/plugins/script_text_editor.cpp
msgid "Replace.."
-msgstr ""
+msgstr "Vervang.."
#: editor/plugins/script_text_editor.cpp
msgid "Goto Function.."
-msgstr ""
+msgstr "Ga Naar Functie.."
#: editor/plugins/script_text_editor.cpp
msgid "Goto Line.."
-msgstr ""
+msgstr "Ga Naar Regel.."
#: editor/plugins/script_text_editor.cpp
msgid "Contextual Help"
-msgstr ""
+msgstr "Contextuele Hulp"
#: editor/plugins/shader_editor_plugin.cpp
msgid "Shader"
-msgstr ""
+msgstr "Shader"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Constant"
-msgstr ""
+msgstr "Verander Shalar Constante"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Constant"
-msgstr ""
+msgstr "Verander Vec Constante"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change RGB Constant"
-msgstr ""
+msgstr "Verander RGB Constante"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Operator"
-msgstr ""
+msgstr "Verander Scalar Operator"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Operator"
-msgstr ""
+msgstr "Verander Vec Operator"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Scalar Operator"
-msgstr ""
+msgstr "Verander Vec Scalar Operator"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change RGB Operator"
-msgstr ""
+msgstr "Verander RGB Operator"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Toggle Rot Only"
-msgstr ""
+msgstr "Aan/Uit Alleen Rot"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Function"
-msgstr ""
+msgstr "Verander Scalar Functie"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Function"
-msgstr ""
+msgstr "Verander Vec Functie"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Scalar Uniform"
-msgstr ""
+msgstr "Verander Scalar Uniform"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Vec Uniform"
-msgstr ""
+msgstr "Verander Vec Uniform"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change RGB Uniform"
-msgstr ""
+msgstr "Verander RGB Uniform"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Default Value"
-msgstr ""
+msgstr "Verander Standaardwaarde"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change XForm Uniform"
-msgstr ""
+msgstr "Verander XForm Uniform"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Texture Uniform"
-msgstr ""
+msgstr "Verander Textuur Uniform"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Cubemap Uniform"
-msgstr ""
+msgstr "Verander Cubemap Uniform"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Comment"
-msgstr ""
+msgstr "Verander Commentaar"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Add/Remove to Color Ramp"
-msgstr ""
+msgstr "Voeg Toe/Verwijder van Kleur Helling"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Add/Remove to Curve Map"
-msgstr ""
+msgstr "Voeg Toe/Verwijder van Curve Map"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Modify Curve Map"
-msgstr ""
+msgstr "Wijzig Curve Map"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Change Input Name"
-msgstr ""
+msgstr "Verander Input Naam"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Connect Graph Nodes"
-msgstr ""
+msgstr "Verbind Graaf Knooppunten"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Disconnect Graph Nodes"
-msgstr ""
+msgstr "Ontkoppel Graaf Knooppunten"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Remove Shader Graph Node"
-msgstr ""
+msgstr "Verwijder Shader Graaf Knooppunten"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Move Shader Graph Node"
-msgstr ""
+msgstr "Verplaats Shader Graaf Knooppunten"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Duplicate Graph Node(s)"
-msgstr ""
+msgstr "Dupliceer Graaf Knooppunt(en)"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Delete Shader Graph Node(s)"
-msgstr ""
+msgstr "Verwijder Shader Graaf Knooppunt(en)"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Error: Cyclic Connection Link"
-msgstr ""
+msgstr "Fout: Cyclische Connectie Link"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Error: Missing Input Connections"
-msgstr ""
+msgstr "Fout: Ontbrekende Input Connecties"
#: editor/plugins/shader_graph_editor_plugin.cpp
msgid "Add Shader Graph Node"
-msgstr ""
+msgstr "Voeg Shader Graaf Knooppunt Toe"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Orthogonal"
-msgstr ""
+msgstr "Orthogonaal"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Perspective"
-msgstr ""
+msgstr "Perspectief"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Aborted."
-msgstr ""
+msgstr "Transformatie Afgebroken."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "X-Axis Transform."
-msgstr ""
+msgstr "X-As Transformatie."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Y-Axis Transform."
-msgstr ""
+msgstr "Y-As Transformatie."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Z-Axis Transform."
-msgstr ""
+msgstr "Z-As Transformatie."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Plane Transform."
-msgstr ""
+msgstr "Bekijk Vlak Transformatie."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scaling: "
-msgstr ""
+msgstr "Schaling: "
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Translating: "
-msgstr "Transitie"
+msgstr "Transitie: "
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotating %s degrees."
-msgstr ""
+msgstr "Roteren %s graden."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Keying is disabled (no key inserted)."
-msgstr ""
+msgstr "Key-en is uitgeschakeld (geen key ingevoegd)."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Animation Key Inserted."
-msgstr ""
+msgstr "Animatie Key Ingevoegd."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Objects Drawn"
-msgstr ""
+msgstr "Objecten Getekend"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Material Changes"
-msgstr "Lokale wijziging aan het opslaan.."
+msgstr "Materiaal Wijzigingen"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Shader Changes"
-msgstr "Wijzig"
+msgstr "Shader Wijzigingen"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Surface Changes"
-msgstr ""
+msgstr "Oppervlakte Wijzigingen"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Draw Calls"
-msgstr ""
+msgstr "Teken Aanroepingen"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Vertices"
-msgstr ""
+msgstr "Vertices"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "FPS"
-msgstr ""
+msgstr "FPS"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Top View."
-msgstr ""
+msgstr "Bovenaanzicht."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Bottom View."
-msgstr ""
+msgstr "Onderaanzicht."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Bottom"
-msgstr ""
+msgstr "Onder"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Left View."
-msgstr ""
+msgstr "Linkeraanzicht."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Left"
-msgstr ""
+msgstr "Links"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right View."
-msgstr ""
+msgstr "Rechteraanzicht."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Right"
-msgstr ""
+msgstr "Rechts"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Front View."
-msgstr ""
+msgstr "Vooraanzicht."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Front"
-msgstr ""
+msgstr "Voor"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rear View."
-msgstr ""
+msgstr "Achteraanzicht."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rear"
-msgstr ""
+msgstr "Achter"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Align with view"
-msgstr ""
+msgstr "Uitlijnen met zicht"
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "OK :("
-msgstr ""
+msgstr "OK :("
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "No parent to instance a child at."
-msgstr ""
+msgstr "Geen ouder om kind aan te instantiëren."
#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "This operation requires a single selected node."
-msgstr ""
+msgstr "Deze bewerking vereist één geselecteerde knooppunt."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Display Normal"
-msgstr ""
+msgstr "Weergave Normaalvector"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Display Wireframe"
-msgstr ""
+msgstr "Weergave Wireframe"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Display Overdraw"
-msgstr ""
+msgstr "Weergave Overdraw"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Display Unshaded"
-msgstr ""
+msgstr "Weergave Zonder Shading"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Environment"
-msgstr ""
+msgstr "Bekijk Omgeving"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Gizmos"
-msgstr ""
+msgstr "Bekijk Gizmos"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View Information"
-msgstr ""
+msgstr "Bekijk Informatie"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "View FPS"
-msgstr "Bekijk Bestanden"
+msgstr "Bekijk FPS"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Half Resolution"
-msgstr "Schaal Selectie"
+msgstr "Halve Resolutie"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Audio Listener"
-msgstr ""
+msgstr "Audio Luisteraar"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Doppler Enable"
-msgstr "Inschakelen"
+msgstr "Inschakelen Doppler"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Left"
-msgstr ""
+msgstr "Vrijekijk Links"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Right"
-msgstr ""
+msgstr "Vrijekijk Rechts"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Freelook Forward"
-msgstr "Ga Verder"
+msgstr "Vrijekijk Vooruit"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Freelook Backwards"
-msgstr "Achterwaarts"
+msgstr "Vrijekijk Achteruit"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Up"
-msgstr ""
+msgstr "Vrijekijk Omhoog"
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Freelook Down"
-msgstr "Scrollwiel Omlaag."
+msgstr "Vrijekijk Omlaag"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Freelook Speed Modifier"
-msgstr ""
+msgstr "Vrijekijk Snelheid Modificator"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "XForm Dialog"
@@ -5739,6 +5723,15 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Item Toevoegen"
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6233,7 +6226,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6380,11 +6375,6 @@ msgid "Delete Item"
msgstr "Verwijder"
#: editor/project_settings_editor.cpp
-#, fuzzy
-msgid "Can't contain '/' or ':'"
-msgstr "Kan niet verbinden met host:"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7453,6 +7443,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -8176,12 +8170,22 @@ msgstr ""
"Pad eigenschap moet verwijzen naar een geldige Spatial node om te werken."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Slechts één WorldEnvironment is toegestaan per scene (of set van "
"geïnstantieerde scenes)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8275,6 +8279,10 @@ msgstr "Fout bij het laden van lettertype."
msgid "Invalid font size."
msgstr "Ongeldige lettertype grootte."
+#, fuzzy
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Kan niet verbinden met host:"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/pl.po b/editor/translations/pl.po
index 21618953a..3c8ee72ce 100644
--- a/editor/translations/pl.po
+++ b/editor/translations/pl.po
@@ -8,6 +8,7 @@
# Adrian Węcławski <weclawskiadrian@gmail.com>, 2016.
# aelspire <aelspire@gmail.com>, 2017.
# Daniel Lewan <vision360.daniel@gmail.com>, 2016-2018.
+# Dariusz Król <rexioweb@gmail.com>, 2018.
# heya10 <igor.gielzak@gmail.com>, 2017.
# holistyczny interlokutor <jakubowesmieci@gmail.com>, 2017.
# Kajetan Kuszczyński <kajetanek99@gmail.com>, 2016.
@@ -26,8 +27,8 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-01-22 08:08+0000\n"
-"Last-Translator: Maksymilian Świąć <maksymilian.swiac@gmail.com>\n"
+"PO-Revision-Date: 2018-04-23 15:40+0000\n"
+"Last-Translator: Dariusz Król <rexioweb@gmail.com>\n"
"Language-Team: Polish <https://hosted.weblate.org/projects/godot-engine/"
"godot/pl/>\n"
"Language: pl\n"
@@ -35,7 +36,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
"|| n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 2.19-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -242,7 +243,7 @@ msgstr "Zmień pętlę animacji"
#: editor/animation_editor.cpp
msgid "Anim Create Typed Value Key"
-msgstr ""
+msgstr "Utwórz klucz dla wpisanej wartości"
#: editor/animation_editor.cpp
msgid "Anim Insert"
@@ -1411,6 +1412,10 @@ msgstr "Wyczyść"
msgid "Clear Output"
msgstr "Wyczyść dane wyjściowe"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Błąd podczas zapisu zasobu!"
@@ -3726,6 +3731,16 @@ msgid "Show Guides"
msgstr "Pokaż prowadnice"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Pokaż pozycję początkową"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 widok"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Wyśrodkowywanie na zaznaczeniu"
@@ -4019,6 +4034,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Siatka nie posiada powierzchni z której można utworzyć zarys!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Nie udało się utworzyć zarysu!"
@@ -5730,6 +5749,16 @@ msgid "Checked Item"
msgstr "Zaznaczony element"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Dodaj element"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Zaznaczony element"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Ma"
@@ -6227,8 +6256,10 @@ msgid "Mouse Button"
msgstr "Przycisk myszy"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Nieprawidłowa akcja (wszystko oprócz '/' lub ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6371,10 +6402,6 @@ msgid "Delete Item"
msgstr "Usuń element"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "Nie może zawierać '/ 'lub':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Już istnieje"
@@ -7446,6 +7473,10 @@ msgstr "Ustawienia GridMap"
msgid "Pick Distance:"
msgstr "Wybierz odległość:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "Generowanie solucji..."
@@ -8139,12 +8170,22 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "Pole Path musi wskazywać na węzeł Spatial."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Tylko jeden WorldEnvironment jest dozwolony na scenę (lub zestaw "
"zinstancjonowanych scen)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8243,6 +8284,12 @@ msgstr "Błąd ładowania fonta."
msgid "Invalid font size."
msgstr "Niepoprawny rozmiar fonta."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Nieprawidłowa akcja (wszystko oprócz '/' lub ':')."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Nie może zawierać '/ 'lub':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/pr.po b/editor/translations/pr.po
index 6029e861f..94856aa87 100644
--- a/editor/translations/pr.po
+++ b/editor/translations/pr.po
@@ -1385,6 +1385,10 @@ msgstr ""
msgid "Clear Output"
msgstr ""
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3624,6 +3628,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3913,6 +3925,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5588,6 +5604,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6066,7 +6090,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6212,10 +6238,6 @@ msgid "Delete Item"
msgstr "Yar, Blow th' Selected Down!"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7274,6 +7296,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7939,10 +7965,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/pt_BR.po b/editor/translations/pt_BR.po
index ae67217cf..3ad4798ca 100644
--- a/editor/translations/pt_BR.po
+++ b/editor/translations/pt_BR.po
@@ -4,11 +4,13 @@
# This file is distributed under the same license as the Godot source code.
#
# Allyson Souza <allyson_as@outlook.com>, 2017.
+# Anderson Araujo <anderson.araujoprog@gmail.com>, 2018.
# António Sarmento <antonio.luis.sarmento@gmail.com>, 2016.
# AWK <arthurwk1800@gmail.com>, 2017.
# Francesco Perrotti-Garcia <fpg1503@gmail.com>, 2017.
# George Marques <george@gmarqu.es>, 2016.
# Guilherme Felipe C G Silva <guilhermefelipecgs@gmail.com>, 2017.
+# João Victor Lima <victordevtb@outlook.com>, 2018.
# João Vitor de Oliveira Carlos <lopogax@gmail.com>, 2018.
# Joaquim Ferreira <joaquimferreira1996@bol.com.br>, 2016.
# jonathan railarem <railarem@gmail.com>, 2017.
@@ -19,16 +21,15 @@
# Marcus Correia <marknokalt@live.com>, 2017-2018.
# Michael Alexsander Silva Dias <michaelalexsander@protonmail.com>, 2017-2018.
# Renato Rotenberg <renato.rotenberg@gmail.com>, 2017.
-# Rodolfo R Gomes <rodolforg@gmail.com>, 2017.
+# Rodolfo R Gomes <rodolforg@gmail.com>, 2017-2018.
# Tiago Almeida <thyagoeap@gmail.com>, 2017.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: 2016-05-30\n"
-"PO-Revision-Date: 2018-02-22 20:49+0000\n"
-"Last-Translator: Michael Alexsander Silva Dias <michaelalexsander@protonmail."
-"com>\n"
+"PO-Revision-Date: 2018-04-30 13:39+0000\n"
+"Last-Translator: Rodolfo R Gomes <rodolforg@gmail.com>\n"
"Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
"godot-engine/godot/pt_BR/>\n"
"Language: pt_BR\n"
@@ -36,7 +37,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 2.20-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -44,7 +45,7 @@ msgstr "Desabilitado"
#: editor/animation_editor.cpp
msgid "All Selection"
-msgstr "Toda a Seleção"
+msgstr "Toda a seleção"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Time"
@@ -775,7 +776,7 @@ msgstr "Doadores"
#: editor/editor_about.cpp
msgid "License"
-msgstr "LIcença"
+msgstr "Licença"
#: editor/editor_about.cpp
msgid "Thirdparty License"
@@ -1413,6 +1414,10 @@ msgstr "Limpar"
msgid "Clear Output"
msgstr "Limpar Saída"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Erro ao salvar Recurso!"
@@ -1654,11 +1659,11 @@ msgstr "Abrir Cena Base"
#: editor/editor_node.cpp
msgid "Quick Open Scene.."
-msgstr "Abri Cena Rápidamente..."
+msgstr "Abrir Cena Rapidamente..."
#: editor/editor_node.cpp
msgid "Quick Open Script.."
-msgstr "Abrir Script Rápidamente..."
+msgstr "Abrir Script Rapidamente..."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1977,7 +1982,7 @@ msgstr "Depurar"
#: editor/editor_node.cpp
msgid "Deploy with Remote Debug"
-msgstr "Instalar Depuração Remota"
+msgstr "Distribuir com Depuragem Remota"
#: editor/editor_node.cpp
msgid ""
@@ -1989,7 +1994,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Small Deploy with Network FS"
-msgstr "Instalação Pequena com FS em rede"
+msgstr "Pequeno teste com o sistema de arquivos em rede"
#: editor/editor_node.cpp
msgid ""
@@ -2032,7 +2037,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Sync Scene Changes"
-msgstr "Sincronizar Alterações na Cena"
+msgstr "Sincronizar Mudanças de Cena"
#: editor/editor_node.cpp
msgid ""
@@ -2048,7 +2053,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Sync Script Changes"
-msgstr "Sincronizar Alterações no Script"
+msgstr "Sincronizar Mudanças de Script"
#: editor/editor_node.cpp
msgid ""
@@ -2361,7 +2366,7 @@ msgstr "Inclusivo"
#: editor/editor_profiler.cpp
msgid "Self"
-msgstr "Mesmo"
+msgstr "Self"
#: editor/editor_profiler.cpp
msgid "Frame #:"
@@ -3436,7 +3441,7 @@ msgstr "Oficial"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Testing"
-msgstr "Em teste"
+msgstr "Testando"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Assets ZIP File"
@@ -3721,6 +3726,16 @@ msgid "Show Guides"
msgstr "Mostrar guias"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Ver Origem"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 Viewport"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Centralizar Seleção"
@@ -4010,6 +4025,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Malha não tem superfície para criar contornos dela!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Não se pôde criar contorno!"
@@ -5297,7 +5316,7 @@ msgstr "Modo Escala (R)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Coords"
-msgstr "Coordenadas Locais"
+msgstr "Local Coords"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Space Mode (%s)"
@@ -5681,6 +5700,16 @@ msgid "Checked Item"
msgstr "Item Checado"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Adicionar Item"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Item Checado"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Tem"
@@ -5726,7 +5755,7 @@ msgstr "Fonte"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Color"
-msgstr "Cor"
+msgstr "Color"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Theme"
@@ -6182,8 +6211,10 @@ msgid "Mouse Button"
msgstr "Botão do Mous"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Ação Inválida (qualquer coisa serve, exceto '/' ou ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6326,10 +6357,6 @@ msgid "Delete Item"
msgstr "Excluir Item"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "Não pode conter '/' ou ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Já existe"
@@ -6443,7 +6470,7 @@ msgstr "Remapeamentos por Localidade:"
#: editor/project_settings_editor.cpp
msgid "Locale"
-msgstr "Localidade"
+msgstr "Locale"
#: editor/project_settings_editor.cpp
msgid "Locales Filter"
@@ -6555,7 +6582,7 @@ msgstr "[Vazio]"
#: editor/property_editor.cpp modules/visual_script/visual_script_editor.cpp
msgid "Set"
-msgstr "Definir"
+msgstr "Set"
#: editor/property_editor.cpp
msgid "Properties:"
@@ -6964,7 +6991,7 @@ msgstr "Carregar arquivo de script existente"
#: editor/script_create_dialog.cpp
msgid "Language"
-msgstr "Idioma"
+msgstr "Linguagem"
#: editor/script_create_dialog.cpp
msgid "Inherits"
@@ -7052,7 +7079,7 @@ msgstr "Pilha de Rastreamento (se aplicável):"
#: editor/script_editor_debugger.cpp
msgid "Profiler"
-msgstr "Profiler"
+msgstr "Profilador"
#: editor/script_editor_debugger.cpp
msgid "Monitor"
@@ -7076,11 +7103,11 @@ msgstr "Total:"
#: editor/script_editor_debugger.cpp
msgid "Video Mem"
-msgstr "Memória de Vídeo"
+msgstr "Mem. de Vídeo"
#: editor/script_editor_debugger.cpp
msgid "Resource Path"
-msgstr "Caminho do recurso"
+msgstr "Caminho do Recurso"
#: editor/script_editor_debugger.cpp
msgid "Type"
@@ -7386,6 +7413,10 @@ msgstr "Configurações do GridMap"
msgid "Pick Distance:"
msgstr "Escolha uma Distância:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "Gerando solução..."
@@ -7436,7 +7467,7 @@ msgstr "Avisos"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "Fim da pilha de rastreamento de exceção interna"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7616,27 +7647,27 @@ msgstr "Condição"
#: modules/visual_script/visual_script_editor.cpp
msgid "Sequence"
-msgstr "Sequência"
+msgstr "Sequence"
#: modules/visual_script/visual_script_editor.cpp
msgid "Switch"
-msgstr "Trocar"
+msgstr "Switch"
#: modules/visual_script/visual_script_editor.cpp
msgid "Iterator"
-msgstr "Iterador"
+msgstr "Iterator"
#: modules/visual_script/visual_script_editor.cpp
msgid "While"
-msgstr "Enquanto"
+msgstr "While"
#: modules/visual_script/visual_script_editor.cpp
msgid "Return"
-msgstr "Retornar"
+msgstr "Return"
#: modules/visual_script/visual_script_editor.cpp
msgid "Call"
-msgstr "Chamar"
+msgstr "Call"
#: modules/visual_script/visual_script_editor.cpp
msgid "Get"
@@ -8097,12 +8128,22 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "A propriedade Caminho deve apontar para um nó Spatial para funcionar."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"É permitido apenas um nó WorldEnvironment por cena (ou conjunto de cenas "
"instanciadas)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8199,6 +8240,12 @@ msgstr "Erro ao carregar fonte."
msgid "Invalid font size."
msgstr "Tamanho de fonte inválido."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Ação Inválida (qualquer coisa serve, exceto '/' ou ':')."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Não pode conter '/' ou ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/pt_PT.po b/editor/translations/pt_PT.po
index 9f520ddf4..84e80718d 100644
--- a/editor/translations/pt_PT.po
+++ b/editor/translations/pt_PT.po
@@ -17,15 +17,15 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-02-24 23:41+0000\n"
-"Last-Translator: Paulo Caldeira <paucal@gmail.com>\n"
+"PO-Revision-Date: 2018-04-25 09:40+0000\n"
+"Last-Translator: João Lopes <linux-man@hotmail.com>\n"
"Language-Team: Portuguese (Portugal) <https://hosted.weblate.org/projects/"
"godot-engine/godot/pt_PT/>\n"
"Language: pt_PT\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 2.20-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -830,31 +830,31 @@ msgstr "Adicionar Efeito"
#: editor/editor_audio_buses.cpp
msgid "Rename Audio Bus"
-msgstr "Mudar Barramento de Áudio"
+msgstr "Renomear o barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "Change Audio Bus Volume"
-msgstr "Alterar Volume do canal áudio"
+msgstr "Alterar Volume do barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Solo"
-msgstr "Alternar solo do canal de áudio"
+msgstr "Alternar solo do barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Mute"
-msgstr "Alternar silêncio do canal áudio"
+msgstr "Alternar silêncio do barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Bypass Effects"
-msgstr "Alternar efeitos bypass do canal de áudio"
+msgstr "Alternar efeitos bypass do barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "Select Audio Bus Send"
-msgstr "Selecionar envio do canal áudio"
+msgstr "Selecionar envio do barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus Effect"
-msgstr "Adicionar Efeito de Barramento de Áudio"
+msgstr "Adicionar Efeito de barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "Move Bus Effect"
@@ -866,7 +866,7 @@ msgstr "Apagar Efeito de Barramento"
#: editor/editor_audio_buses.cpp
msgid "Audio Bus, Drag and Drop to rearrange."
-msgstr "Canal áudio, arrastar e largar para reorganizar."
+msgstr "Barramento de áudio, arrastar e largar para reorganizar."
#: editor/editor_audio_buses.cpp
msgid "Solo"
@@ -903,7 +903,7 @@ msgstr "Áudio"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus"
-msgstr "Adicionar Barramento de Áudio"
+msgstr "Adicionar barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "Master bus can't be deleted!"
@@ -911,23 +911,23 @@ msgstr "O barramento principal não pode ser removido!"
#: editor/editor_audio_buses.cpp
msgid "Delete Audio Bus"
-msgstr "Apagar Barramento de Áudio"
+msgstr "Apagar barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "Duplicate Audio Bus"
-msgstr "Barramento de Áudio Duplicado"
+msgstr "Duplicar barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "Reset Bus Volume"
-msgstr "Restabelecer Volume do Barramento"
+msgstr "Restabelecer volume do barramento"
#: editor/editor_audio_buses.cpp
msgid "Move Audio Bus"
-msgstr "Mover Barramento de Áudio"
+msgstr "Mover barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "Save Audio Bus Layout As.."
-msgstr "Guardar Modelo de Barramento de Áudio Como.."
+msgstr "Guardar Modelo do barramento de áudio como.."
#: editor/editor_audio_buses.cpp
msgid "Location for New Layout.."
@@ -935,7 +935,7 @@ msgstr "Localização para o Novo Modelo.."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
-msgstr "Abrir Modelo de Barramento de Áudio"
+msgstr "Abrir Modelo de barramento de áudio"
#: editor/editor_audio_buses.cpp
msgid "There is no 'res://default_bus_layout.tres' file."
@@ -969,7 +969,7 @@ msgstr "Guardar Como"
#: editor/editor_audio_buses.cpp
msgid "Save this Bus Layout to a file."
-msgstr "Guardar este Modelo de Barramento para um Ficheiro."
+msgstr "Guardar este Modelo de Barramento para um ficheiro."
#: editor/editor_audio_buses.cpp editor/import_dock.cpp
msgid "Load Default"
@@ -1405,6 +1405,10 @@ msgstr "Limpar"
msgid "Clear Output"
msgstr "Limpar Saída"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Erro ao guardar recurso!"
@@ -3706,6 +3710,16 @@ msgid "Show Guides"
msgstr "Mostrar guias"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Ver origem"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 Vista"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Centrar seleção"
@@ -3995,6 +4009,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "A Mesh não tem superfície para criar contornos!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Contorno não pode ser criado!"
@@ -5663,6 +5681,16 @@ msgid "Checked Item"
msgstr "Item verificado"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Adicionar item"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Item verificado"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Tem"
@@ -5983,8 +6011,8 @@ msgid ""
"Couldn't load project.godot in project path (error %d). It may be missing or "
"corrupted."
msgstr ""
-"Não foi possível carregar o project.godot no caminho do projeto. Poderá "
-"estar em falta ou corrompido."
+"Não foi possível carregar o project.godot no caminho do projeto (erro %d). "
+"Poderá estar em falta ou corrompido."
#: editor/project_manager.cpp
msgid "Couldn't edit project.godot in project path."
@@ -6161,8 +6189,10 @@ msgid "Mouse Button"
msgstr "Botão do rato"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Ação inválida (tudo menos '/' ou ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6305,10 +6335,6 @@ msgid "Delete Item"
msgstr "Apagar item"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "Não pode conter '/' ou ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Já existe"
@@ -7027,7 +7053,7 @@ msgstr "Erros:"
#: editor/script_editor_debugger.cpp
msgid "Stack Trace (if applicable):"
-msgstr "Rastreamento de pilha (se aplicável):"
+msgstr "Stack Trace (se aplicável):"
#: editor/script_editor_debugger.cpp
msgid "Profiler"
@@ -7059,7 +7085,7 @@ msgstr "Memória Vídeo"
#: editor/script_editor_debugger.cpp
msgid "Resource Path"
-msgstr "Caminho de recurso"
+msgstr "Caminho do recurso"
#: editor/script_editor_debugger.cpp
msgid "Type"
@@ -7075,7 +7101,7 @@ msgstr "Uso"
#: editor/script_editor_debugger.cpp
msgid "Misc"
-msgstr "Misc"
+msgstr "Diversos"
#: editor/script_editor_debugger.cpp
msgid "Clicked Control:"
@@ -7366,6 +7392,10 @@ msgstr "Configurações do GridMap"
msgid "Pick Distance:"
msgstr "Distância de escolha:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "A gerar soluções..."
@@ -7416,7 +7446,7 @@ msgstr "Avisos"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "Fim do stack trace de exceção interna"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7448,12 +7478,11 @@ msgstr "O Nó retornou uma sequência de saída (output) incorreta: "
#: modules/visual_script/visual_script.cpp
msgid "Found sequence bit but not the node in the stack, report bug!"
-msgstr ""
-"A sequência foi encontrada mas não o Nó na pilha (stack), faça report de bug!"
+msgstr "Foi encontrada o bit da sequência mas não o Nó na pilha, relate o bug!"
#: modules/visual_script/visual_script.cpp
msgid "Stack overflow with stack depth: "
-msgstr "Stack overflow com a profundidade da pilha (stack): "
+msgstr "Pilha cheia com a profundidade da pilha: "
#: modules/visual_script/visual_script_editor.cpp
msgid "Change Signal Arguments"
@@ -8078,12 +8107,22 @@ msgstr ""
"válido."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Apenas um WorldEnvironment é permitido por Cena (ou grupo de cenas "
"instanciadas)."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8180,6 +8219,12 @@ msgstr "Erro ao carregar letra."
msgid "Invalid font size."
msgstr "Tamanho de letra inválido."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Ação inválida (tudo menos '/' ou ':')."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Não pode conter '/' ou ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/ro.po b/editor/translations/ro.po
index 1d89dd74e..e5b3fcbad 100644
--- a/editor/translations/ro.po
+++ b/editor/translations/ro.po
@@ -3,13 +3,14 @@
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
#
+# Filip <filipanton@tutanota.com>, 2018.
# TigerxWood <TigerxWood@gmail.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-01-20 08:55+0000\n"
-"Last-Translator: TigerxWood <TigerxWood@gmail.com>\n"
+"PO-Revision-Date: 2018-05-02 18:03+0000\n"
+"Last-Translator: Filip <filipanton@tutanota.com>\n"
"Language-Team: Romanian <https://hosted.weblate.org/projects/godot-engine/"
"godot/ro/>\n"
"Language: ro\n"
@@ -17,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < "
"20)) ? 1 : 2;\n"
-"X-Generator: Weblate 2.19-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -25,15 +26,15 @@ msgstr "Dezactivat"
#: editor/animation_editor.cpp
msgid "All Selection"
-msgstr "Toate selecțiile"
+msgstr "Toată selecția"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Time"
-msgstr "Anim Schimbare timp cadre cheie"
+msgstr "Anim Schimbați Timpul Cadru Cheie"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr "Anim Schimbare tranziție"
+msgstr "Anim Schimbați Tranziție"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
@@ -49,149 +50,149 @@ msgstr "Anim Schimbare apelare"
#: editor/animation_editor.cpp
msgid "Anim Add Track"
-msgstr "Anim Adăugare pistă"
+msgstr "Anim Adăugați Pistă"
#: editor/animation_editor.cpp
msgid "Anim Duplicate Keys"
-msgstr "Anim Clonare chei"
+msgstr "Anim Clonare Chei"
#: editor/animation_editor.cpp
msgid "Move Anim Track Up"
-msgstr "Mutare sus pistă anim"
+msgstr "Mută Pista Anim Sus"
#: editor/animation_editor.cpp
msgid "Move Anim Track Down"
-msgstr "Mutare jos pistă anim"
+msgstr "Mută Pista Anim Jos"
#: editor/animation_editor.cpp
msgid "Remove Anim Track"
-msgstr "Eliminare pistă anim"
+msgstr "Elimină Pista Anim"
#: editor/animation_editor.cpp
msgid "Set Transitions to:"
-msgstr "Setare tranziții la:"
+msgstr "Setează Tranziții la:"
#: editor/animation_editor.cpp
msgid "Anim Track Rename"
-msgstr "Redenumire pistă anim"
+msgstr "Redenumește Pista Anim"
#: editor/animation_editor.cpp
msgid "Anim Track Change Interpolation"
-msgstr ""
+msgstr "Anim Schimbați Interpolarea Pistei"
#: editor/animation_editor.cpp
msgid "Anim Track Change Value Mode"
-msgstr ""
+msgstr "Anim Schimbați Modul Valorii Pistei"
#: editor/animation_editor.cpp
msgid "Anim Track Change Wrap Mode"
-msgstr ""
+msgstr "Anim Schimbați Modul Învelirii Pistei"
#: editor/animation_editor.cpp
msgid "Edit Node Curve"
-msgstr ""
+msgstr "Editați Curba Nodului"
#: editor/animation_editor.cpp
msgid "Edit Selection Curve"
-msgstr ""
+msgstr "Editați Curba Selecției"
#: editor/animation_editor.cpp
msgid "Anim Delete Keys"
-msgstr ""
+msgstr "Anim Ștergeți Cheile"
#: editor/animation_editor.cpp editor/plugins/tile_map_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Duplicate Selection"
-msgstr ""
+msgstr "Duplicați Selecția"
#: editor/animation_editor.cpp
msgid "Duplicate Transposed"
-msgstr ""
+msgstr "Duplicați Transpunerea"
#: editor/animation_editor.cpp
msgid "Remove Selection"
-msgstr ""
+msgstr "Elminați Selecția"
#: editor/animation_editor.cpp
msgid "Continuous"
-msgstr ""
+msgstr "Continuu"
#: editor/animation_editor.cpp
msgid "Discrete"
-msgstr ""
+msgstr "Discret"
#: editor/animation_editor.cpp
msgid "Trigger"
-msgstr ""
+msgstr "Trăgaci"
#: editor/animation_editor.cpp
msgid "Anim Add Key"
-msgstr ""
+msgstr "Anim Adaugați Cheie"
#: editor/animation_editor.cpp
msgid "Anim Move Keys"
-msgstr ""
+msgstr "Anim Mutați Cheie"
#: editor/animation_editor.cpp
msgid "Scale Selection"
-msgstr ""
+msgstr "Scalați Selecția"
#: editor/animation_editor.cpp
msgid "Scale From Cursor"
-msgstr ""
+msgstr "Scalați De La Cursor"
#: editor/animation_editor.cpp
msgid "Goto Next Step"
-msgstr ""
+msgstr "Mergeți la Pasul Următor"
#: editor/animation_editor.cpp
msgid "Goto Prev Step"
-msgstr ""
+msgstr "Mergeți la Pasul Anterior"
#: editor/animation_editor.cpp editor/plugins/curve_editor_plugin.cpp
#: editor/property_editor.cpp
msgid "Linear"
-msgstr ""
+msgstr "Linear"
#: editor/animation_editor.cpp editor/plugins/theme_editor_plugin.cpp
msgid "Constant"
-msgstr ""
+msgstr "Constant"
#: editor/animation_editor.cpp
msgid "In"
-msgstr ""
+msgstr "În"
#: editor/animation_editor.cpp
msgid "Out"
-msgstr ""
+msgstr "Afară"
#: editor/animation_editor.cpp
msgid "In-Out"
-msgstr ""
+msgstr "Înăuntru-Afară"
#: editor/animation_editor.cpp
msgid "Out-In"
-msgstr ""
+msgstr "Afară-Înăuntru"
#: editor/animation_editor.cpp
msgid "Transitions"
-msgstr ""
+msgstr "Tranziții"
#: editor/animation_editor.cpp
msgid "Optimize Animation"
-msgstr ""
+msgstr "Optimizați Animația"
#: editor/animation_editor.cpp
msgid "Clean-Up Animation"
-msgstr ""
+msgstr "Curățați Animația"
#: editor/animation_editor.cpp
msgid "Create NEW track for %s and insert key?"
-msgstr ""
+msgstr "Creați pistă NOUA pentru %s și inserați cheie?"
#: editor/animation_editor.cpp
msgid "Create %d NEW tracks and insert keys?"
-msgstr ""
+msgstr "Creați %d piste NOI și inserați cheie?"
#: editor/animation_editor.cpp editor/create_dialog.cpp
#: editor/editor_audio_buses.cpp editor/plugins/abstract_polygon_2d_editor.cpp
@@ -199,270 +200,272 @@ msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
#: editor/plugins/particles_editor_plugin.cpp editor/script_create_dialog.cpp
msgid "Create"
-msgstr ""
+msgstr "Creați"
#: editor/animation_editor.cpp
msgid "Anim Create & Insert"
-msgstr ""
+msgstr "Anim Creați și Inserați"
#: editor/animation_editor.cpp
msgid "Anim Insert Track & Key"
-msgstr ""
+msgstr "Anim Inserați Pistă și Cheie"
#: editor/animation_editor.cpp
msgid "Anim Insert Key"
-msgstr ""
+msgstr "Anim Inserați Cheie"
#: editor/animation_editor.cpp
msgid "Change Anim Len"
-msgstr ""
+msgstr "Schimbați Lung Anim"
#: editor/animation_editor.cpp
msgid "Change Anim Loop"
-msgstr ""
+msgstr "Schimbați Bucla Anim"
#: editor/animation_editor.cpp
msgid "Anim Create Typed Value Key"
-msgstr ""
+msgstr "Anim Creați Cheie Valoare Typed"
#: editor/animation_editor.cpp
msgid "Anim Insert"
-msgstr ""
+msgstr "Anim Inserați"
#: editor/animation_editor.cpp
msgid "Anim Scale Keys"
-msgstr ""
+msgstr "Anim Scalați Cheile"
#: editor/animation_editor.cpp
msgid "Anim Add Call Track"
-msgstr ""
+msgstr "Anim Adăugați Pistă Chemare"
#: editor/animation_editor.cpp
msgid "Animation zoom."
-msgstr ""
+msgstr "Zoom Animație."
#: editor/animation_editor.cpp
msgid "Length (s):"
-msgstr ""
+msgstr "Lungime (s):"
#: editor/animation_editor.cpp
msgid "Animation length (in seconds)."
-msgstr ""
+msgstr "Lungime Animație (în secunde)."
#: editor/animation_editor.cpp
msgid "Step (s):"
-msgstr ""
+msgstr "Pas (s):"
#: editor/animation_editor.cpp
msgid "Cursor step snap (in seconds)."
-msgstr ""
+msgstr "Pas Bruscare Cursor (în secunde)."
#: editor/animation_editor.cpp
msgid "Enable/Disable looping in animation."
-msgstr ""
+msgstr "Activați / Dezactivați Repetiția în Animație."
#: editor/animation_editor.cpp
msgid "Add new tracks."
-msgstr ""
+msgstr "Adăugați piste noi."
#: editor/animation_editor.cpp
msgid "Move current track up."
-msgstr ""
+msgstr "Mișcați pista curentă sus."
#: editor/animation_editor.cpp
msgid "Move current track down."
-msgstr ""
+msgstr "Mișcați pista curentă jos."
#: editor/animation_editor.cpp
msgid "Remove selected track."
-msgstr ""
+msgstr "Ștergeți pista selectată."
#: editor/animation_editor.cpp
msgid "Track tools"
-msgstr ""
+msgstr "Unelte Pistă"
#: editor/animation_editor.cpp
msgid "Enable editing of individual keys by clicking them."
-msgstr ""
+msgstr "Activați editarea de chei individuale prin clic."
#: editor/animation_editor.cpp
msgid "Anim. Optimizer"
-msgstr ""
+msgstr "Anim. Optimizator"
#: editor/animation_editor.cpp
msgid "Max. Linear Error:"
-msgstr ""
+msgstr "Eroare Lineară Max:"
#: editor/animation_editor.cpp
msgid "Max. Angular Error:"
-msgstr ""
+msgstr "Eroare Angulară Max:"
#: editor/animation_editor.cpp
msgid "Max Optimizable Angle:"
-msgstr ""
+msgstr "Max Unghi Optimizabil:"
#: editor/animation_editor.cpp
msgid "Optimize"
-msgstr ""
+msgstr "Optimizați"
#: editor/animation_editor.cpp
msgid "Select an AnimationPlayer from the Scene Tree to edit animations."
-msgstr ""
+msgstr "Selectați un Animator din Copacul Scenă să editați animații."
#: editor/animation_editor.cpp
msgid "Key"
-msgstr ""
+msgstr "Cheie"
#: editor/animation_editor.cpp
msgid "Transition"
-msgstr ""
+msgstr "Tranziție"
#: editor/animation_editor.cpp
msgid "Scale Ratio:"
-msgstr ""
+msgstr "Proporție Scalare:"
#: editor/animation_editor.cpp
msgid "Call Functions in Which Node?"
-msgstr ""
+msgstr "Chemați Funcții în Care Nod?"
#: editor/animation_editor.cpp
msgid "Remove invalid keys"
-msgstr ""
+msgstr "Ștergeți chei inoperabile"
#: editor/animation_editor.cpp
msgid "Remove unresolved and empty tracks"
-msgstr ""
+msgstr "Ștergeți piste nerezolvate sau goale"
#: editor/animation_editor.cpp
msgid "Clean-up all animations"
-msgstr ""
+msgstr "Curățați toate animațiile"
#: editor/animation_editor.cpp
msgid "Clean-Up Animation(s) (NO UNDO!)"
-msgstr ""
+msgstr "Curățați Animațiile (FĂRĂ ANULARE!)"
#: editor/animation_editor.cpp
msgid "Clean-Up"
-msgstr ""
+msgstr "Curățați"
#: editor/array_property_edit.cpp
msgid "Resize Array"
-msgstr ""
+msgstr "Redimensionați Array-ul"
#: editor/array_property_edit.cpp
msgid "Change Array Value Type"
-msgstr ""
+msgstr "Schimbați Tipul Array-ului"
#: editor/array_property_edit.cpp
msgid "Change Array Value"
-msgstr ""
+msgstr "Schimbați Valoarea Array-ului"
#: editor/code_editor.cpp
msgid "Go to Line"
-msgstr ""
+msgstr "Duceți-vă la Linie"
#: editor/code_editor.cpp
msgid "Line Number:"
-msgstr ""
+msgstr "Linia Numărul:"
#: editor/code_editor.cpp
msgid "No Matches"
-msgstr ""
+msgstr "Nici o Potrivire"
#: editor/code_editor.cpp
msgid "Replaced %d occurrence(s)."
-msgstr ""
+msgstr "Înlocuit %d potriviri."
#: editor/code_editor.cpp
msgid "Match Case"
-msgstr ""
+msgstr "Potrivește Caz-ul"
#: editor/code_editor.cpp
msgid "Whole Words"
-msgstr ""
+msgstr "Cuvinte Complete"
#: editor/code_editor.cpp
msgid "Replace"
-msgstr ""
+msgstr "Înlocuiți"
#: editor/code_editor.cpp
msgid "Replace All"
-msgstr ""
+msgstr "Înlocuiți Tot"
#: editor/code_editor.cpp
msgid "Selection Only"
-msgstr ""
+msgstr "Numai Selecția"
#: editor/code_editor.cpp
msgid "Zoom In"
-msgstr ""
+msgstr "Zoom-ați În"
#: editor/code_editor.cpp
msgid "Zoom Out"
-msgstr ""
+msgstr "Zoom-ați Afară"
#: editor/code_editor.cpp
msgid "Reset Zoom"
-msgstr ""
+msgstr "Resetați Zoom-area"
#: editor/code_editor.cpp editor/script_editor_debugger.cpp
msgid "Line:"
-msgstr ""
+msgstr "Linie:"
#: editor/code_editor.cpp
msgid "Col:"
-msgstr ""
+msgstr "Col:"
#: editor/connections_dialog.cpp
msgid "Method in target Node must be specified!"
-msgstr ""
+msgstr "Metoda din Nod-ul țintă trebuie specificată!"
#: editor/connections_dialog.cpp
msgid ""
"Target method not found! Specify a valid method or attach a script to target "
"Node."
msgstr ""
+"Metoda țintă nu există! Specificați o metodă validă sau atașați un script la "
+"Nod-ul țintă."
#: editor/connections_dialog.cpp
msgid "Connect To Node:"
-msgstr ""
+msgstr "Conectați la Nod:"
#: editor/connections_dialog.cpp editor/editor_autoload_settings.cpp
#: editor/groups_editor.cpp editor/plugins/item_list_editor_plugin.cpp
#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp
msgid "Add"
-msgstr ""
+msgstr "Adăugați"
#: editor/connections_dialog.cpp editor/dependency_editor.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
#: editor/plugins/theme_editor_plugin.cpp editor/project_manager.cpp
#: editor/project_settings_editor.cpp
msgid "Remove"
-msgstr ""
+msgstr "Ștergeți"
#: editor/connections_dialog.cpp
msgid "Add Extra Call Argument:"
-msgstr ""
+msgstr "Adăugați Extra Argument de Chemare:"
#: editor/connections_dialog.cpp
msgid "Extra Call Arguments:"
-msgstr ""
+msgstr "Extra Argumente de Chemare:"
#: editor/connections_dialog.cpp
msgid "Path to Node:"
-msgstr ""
+msgstr "Drum la Nod:"
#: editor/connections_dialog.cpp
msgid "Make Function"
-msgstr ""
+msgstr "Faceți Funcția"
#: editor/connections_dialog.cpp
msgid "Deferred"
-msgstr ""
+msgstr "Amânat(ă)"
#: editor/connections_dialog.cpp
msgid "Oneshot"
-msgstr ""
+msgstr "Oneshot"
#: editor/connections_dialog.cpp editor/dependency_editor.cpp
#: editor/export_template_manager.cpp
@@ -476,143 +479,147 @@ msgstr ""
#: editor/run_settings_dialog.cpp editor/settings_config_dialog.cpp
#: modules/visual_script/visual_script_editor.cpp
msgid "Close"
-msgstr ""
+msgstr "Aproape"
#: editor/connections_dialog.cpp
msgid "Connect"
-msgstr ""
+msgstr "Conectați"
#: editor/connections_dialog.cpp
msgid "Connect '%s' to '%s'"
-msgstr ""
+msgstr "Conectați '%s' la '%s'"
#: editor/connections_dialog.cpp
msgid "Connecting Signal:"
-msgstr ""
+msgstr "Conectați Semnal:"
#: editor/connections_dialog.cpp
msgid "Disconnect '%s' from '%s'"
-msgstr ""
+msgstr "Deconectați '%s' de la '%s'"
#: editor/connections_dialog.cpp
msgid "Connect.."
-msgstr ""
+msgstr "Conectați.."
#: editor/connections_dialog.cpp
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "Disconnect"
-msgstr ""
+msgstr "Deconectați"
#: editor/connections_dialog.cpp editor/editor_help.cpp editor/node_dock.cpp
msgid "Signals"
-msgstr ""
+msgstr "Semnale"
#: editor/create_dialog.cpp
msgid "Change %s Type"
-msgstr ""
+msgstr "Schimbați Tipul %s"
#: editor/create_dialog.cpp editor/project_settings_editor.cpp
#: modules/visual_script/visual_script_editor.cpp
msgid "Change"
-msgstr ""
+msgstr "Schimbați"
#: editor/create_dialog.cpp
msgid "Create New %s"
-msgstr ""
+msgstr "Creați %s Nou"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp
msgid "Favorites:"
-msgstr ""
+msgstr "Favorite:"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
msgid "Recent:"
-msgstr ""
+msgstr "Recent:"
#: editor/create_dialog.cpp editor/editor_node.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
#: editor/quick_open.cpp
msgid "Search:"
-msgstr ""
+msgstr "Cautați:"
#: editor/create_dialog.cpp editor/editor_help.cpp
#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
#: editor/quick_open.cpp
msgid "Matches:"
-msgstr ""
+msgstr "Potriviri:"
#: editor/create_dialog.cpp editor/editor_help.cpp
#: editor/plugins/asset_library_editor_plugin.cpp editor/property_selector.cpp
#: editor/script_editor_debugger.cpp
msgid "Description:"
-msgstr ""
+msgstr "Descripție:"
#: editor/dependency_editor.cpp
msgid "Search Replacement For:"
-msgstr ""
+msgstr "Cautați Înlocuitor Pentru:"
#: editor/dependency_editor.cpp
msgid "Dependencies For:"
-msgstr ""
+msgstr "Dependențe Pentru:"
#: editor/dependency_editor.cpp
msgid ""
"Scene '%s' is currently being edited.\n"
"Changes will not take effect unless reloaded."
msgstr ""
+"Scena '%s' este în proces de editare. \n"
+"Modificările nu vor avea efect dacă nu reîncărcați."
#: editor/dependency_editor.cpp
msgid ""
"Resource '%s' is in use.\n"
"Changes will take effect when reloaded."
msgstr ""
+"Resursa '%s' este în folosință.\n"
+"Modificările vor avea efect după ce reîncărcați."
#: editor/dependency_editor.cpp
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Dependencies"
-msgstr ""
+msgstr "Dependențe"
#: editor/dependency_editor.cpp
msgid "Resource"
-msgstr ""
+msgstr "Resursă"
#: editor/dependency_editor.cpp editor/editor_autoload_settings.cpp
#: editor/project_manager.cpp editor/project_settings_editor.cpp
#: editor/script_create_dialog.cpp
msgid "Path"
-msgstr ""
+msgstr "Cale"
#: editor/dependency_editor.cpp
msgid "Dependencies:"
-msgstr ""
+msgstr "Dependințe:"
#: editor/dependency_editor.cpp
msgid "Fix Broken"
-msgstr ""
+msgstr "Reparați Stricat"
#: editor/dependency_editor.cpp
msgid "Dependency Editor"
-msgstr ""
+msgstr "Editor de Dependență"
#: editor/dependency_editor.cpp
msgid "Search Replacement Resource:"
-msgstr ""
+msgstr "Cautați Înlocuitor Resursă:"
#: editor/dependency_editor.cpp editor/editor_file_dialog.cpp
#: editor/editor_help.cpp editor/editor_node.cpp editor/filesystem_dock.cpp
#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
#: editor/quick_open.cpp scene/gui/file_dialog.cpp
msgid "Open"
-msgstr ""
+msgstr "Deschide"
#: editor/dependency_editor.cpp
msgid "Owners Of:"
-msgstr ""
+msgstr "Stăpâni La:"
#: editor/dependency_editor.cpp
msgid "Remove selected files from the project? (no undo)"
-msgstr ""
+msgstr "Ștergeți fișierele selectate din proiect? (fără anulare)"
#: editor/dependency_editor.cpp
msgid ""
@@ -620,54 +627,57 @@ msgid ""
"work.\n"
"Remove them anyway? (no undo)"
msgstr ""
+"Fișierele în proces de ștergere sunt necesare pentru alte resurse ca ele să "
+"sa funcționeze.\n"
+"Ștergeți oricum? (fără anulare)"
#: editor/dependency_editor.cpp
msgid "Cannot remove:"
-msgstr ""
+msgstr "Nu se poate șterge:"
#: editor/dependency_editor.cpp
msgid "Error loading:"
-msgstr ""
+msgstr "Eroare încărcând:"
#: editor/dependency_editor.cpp
msgid "Scene failed to load due to missing dependencies:"
-msgstr ""
+msgstr "Scena nu a putut fi încărcata deoarece are dependențe în lipsa:"
#: editor/dependency_editor.cpp editor/editor_node.cpp
msgid "Open Anyway"
-msgstr ""
+msgstr "Deschide Oricum"
#: editor/dependency_editor.cpp
msgid "Which action should be taken?"
-msgstr ""
+msgstr "Ce acțiune trebuie să fie luată?"
#: editor/dependency_editor.cpp
msgid "Fix Dependencies"
-msgstr ""
+msgstr "Rezolvă dependențele"
#: editor/dependency_editor.cpp
msgid "Errors loading!"
-msgstr ""
+msgstr "Erori încărcând!"
#: editor/dependency_editor.cpp
msgid "Permanently delete %d item(s)? (No undo!)"
-msgstr ""
+msgstr "Ștergeți permanent %d articol(e)? (Fără anulare!)"
#: editor/dependency_editor.cpp
msgid "Owns"
-msgstr ""
+msgstr "Deține"
#: editor/dependency_editor.cpp
msgid "Resources Without Explicit Ownership:"
-msgstr ""
+msgstr "Resurse Fără Deținere Explicită:"
#: editor/dependency_editor.cpp editor/editor_node.cpp
msgid "Orphan Resource Explorer"
-msgstr ""
+msgstr "Explorator de Resurse Orfane"
#: editor/dependency_editor.cpp
msgid "Delete selected files?"
-msgstr ""
+msgstr "Ştergeți fişierele selectate?"
#: editor/dependency_editor.cpp editor/editor_audio_buses.cpp
#: editor/editor_file_dialog.cpp editor/editor_node.cpp
@@ -675,83 +685,83 @@ msgstr ""
#: editor/project_export.cpp editor/project_settings_editor.cpp
#: editor/scene_tree_dock.cpp
msgid "Delete"
-msgstr ""
+msgstr "Ștergeți"
#: editor/dictionary_property_edit.cpp
msgid "Change Dictionary Key"
-msgstr ""
+msgstr "Schimbați Cheie Dicţionar"
#: editor/dictionary_property_edit.cpp
msgid "Change Dictionary Value"
-msgstr ""
+msgstr "Schimbaţi Valoarea Dicţionar"
#: editor/editor_about.cpp
msgid "Thanks from the Godot community!"
-msgstr ""
+msgstr "Mulțumesc din partea comunităţii Godot!"
#: editor/editor_about.cpp
msgid "Thanks!"
-msgstr ""
+msgstr "Mulţumiri!"
#: editor/editor_about.cpp
msgid "Godot Engine contributors"
-msgstr ""
+msgstr "Contribuabili Motor Godot"
#: editor/editor_about.cpp
msgid "Project Founders"
-msgstr ""
+msgstr "Fondatorii Proiectului"
#: editor/editor_about.cpp
msgid "Lead Developer"
-msgstr ""
+msgstr "Dezvoltator Principal"
#: editor/editor_about.cpp
msgid "Project Manager "
-msgstr ""
+msgstr "Manager de Proiect "
#: editor/editor_about.cpp
msgid "Developers"
-msgstr ""
+msgstr "Dezvoltatori"
#: editor/editor_about.cpp
msgid "Authors"
-msgstr ""
+msgstr "Autori"
#: editor/editor_about.cpp
msgid "Platinum Sponsors"
-msgstr ""
+msgstr "Sponsori Platină"
#: editor/editor_about.cpp
msgid "Gold Sponsors"
-msgstr ""
+msgstr "Sponsori Aur"
#: editor/editor_about.cpp
msgid "Mini Sponsors"
-msgstr ""
+msgstr "Mini Sponsori"
#: editor/editor_about.cpp
msgid "Gold Donors"
-msgstr ""
+msgstr "Donatori de Aur"
#: editor/editor_about.cpp
msgid "Silver Donors"
-msgstr ""
+msgstr "Donatori de Argint"
#: editor/editor_about.cpp
msgid "Bronze Donors"
-msgstr ""
+msgstr "Donatori de Bronz"
#: editor/editor_about.cpp
msgid "Donors"
-msgstr ""
+msgstr "Donatori"
#: editor/editor_about.cpp
msgid "License"
-msgstr ""
+msgstr "Licență"
#: editor/editor_about.cpp
msgid "Thirdparty License"
-msgstr ""
+msgstr "Licenţe Thirdparty"
#: editor/editor_about.cpp
msgid ""
@@ -760,338 +770,347 @@ msgid ""
"is an exhaustive list of all such thirdparty components with their "
"respective copyright statements and license terms."
msgstr ""
+"Motorul Godot se bazează pe un număr de biblioteci thirdparty gratis și "
+"opensource, toate compatibile cu termenii licenţei MIT ai lui. Mai jos este "
+"o listă exhaustivă a tuturor acestor componente de thirdparty cu declaraţii "
+"de autor respective şi termenii licenței."
#: editor/editor_about.cpp
msgid "All Components"
-msgstr ""
+msgstr "Toate Componentele"
#: editor/editor_about.cpp
msgid "Components"
-msgstr ""
+msgstr "Componente"
#: editor/editor_about.cpp
msgid "Licenses"
-msgstr ""
+msgstr "Licențe"
#: editor/editor_asset_installer.cpp editor/project_manager.cpp
msgid "Error opening package file, not in zip format."
-msgstr ""
+msgstr "Eroare la deschiderea fişierului pachet, nu este în format zip."
#: editor/editor_asset_installer.cpp
msgid "Uncompressing Assets"
-msgstr ""
+msgstr "Decompresez Active"
#: editor/editor_asset_installer.cpp editor/project_manager.cpp
msgid "Package Installed Successfully!"
-msgstr ""
+msgstr "Pachet Instalat cu Succes!"
#: editor/editor_asset_installer.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Success!"
-msgstr ""
+msgstr "Succes!"
#: editor/editor_asset_installer.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Install"
-msgstr ""
+msgstr "Instalați"
#: editor/editor_asset_installer.cpp
msgid "Package Installer"
-msgstr ""
+msgstr "Instalator de Pachet"
#: editor/editor_audio_buses.cpp
msgid "Speakers"
-msgstr ""
+msgstr "Difuzoare"
#: editor/editor_audio_buses.cpp
msgid "Add Effect"
-msgstr ""
+msgstr "Adaugă Efect"
#: editor/editor_audio_buses.cpp
msgid "Rename Audio Bus"
-msgstr ""
+msgstr "Redenumiţi Pista Audio"
#: editor/editor_audio_buses.cpp
msgid "Change Audio Bus Volume"
-msgstr ""
+msgstr "Schimbați Volumul Pistei Audio"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Solo"
-msgstr ""
+msgstr "Comutați Pista Audio Singură"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Mute"
-msgstr ""
+msgstr "Comutați Pista Audio Mută"
#: editor/editor_audio_buses.cpp
msgid "Toggle Audio Bus Bypass Effects"
-msgstr ""
+msgstr "Comutați Efecte de Ocolire Pista Audio"
#: editor/editor_audio_buses.cpp
msgid "Select Audio Bus Send"
-msgstr ""
+msgstr "Selectați Pista Audio Trimitere"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus Effect"
-msgstr ""
+msgstr "Adaugați Efect Pista Audio"
#: editor/editor_audio_buses.cpp
msgid "Move Bus Effect"
-msgstr ""
+msgstr "Mutați Pista Efect"
#: editor/editor_audio_buses.cpp
msgid "Delete Bus Effect"
-msgstr ""
+msgstr "Ștergeți Pista Efect"
#: editor/editor_audio_buses.cpp
msgid "Audio Bus, Drag and Drop to rearrange."
-msgstr ""
+msgstr "Pista Audio, Trageți și Plasați pentru a rearanja."
#: editor/editor_audio_buses.cpp
msgid "Solo"
-msgstr ""
+msgstr "Singură"
#: editor/editor_audio_buses.cpp
msgid "Mute"
-msgstr ""
+msgstr "Mut"
#: editor/editor_audio_buses.cpp
msgid "Bypass"
-msgstr ""
+msgstr "Ocolire"
#: editor/editor_audio_buses.cpp
msgid "Bus options"
-msgstr ""
+msgstr "Opțiuni Pistă Audio"
#: editor/editor_audio_buses.cpp editor/filesystem_dock.cpp
#: editor/plugins/tile_map_editor_plugin.cpp editor/scene_tree_dock.cpp
msgid "Duplicate"
-msgstr ""
+msgstr "Duplicați"
#: editor/editor_audio_buses.cpp
msgid "Reset Volume"
-msgstr ""
+msgstr "Resetați Volumul"
#: editor/editor_audio_buses.cpp
msgid "Delete Effect"
-msgstr ""
+msgstr "Ștergeți Efectul"
#: editor/editor_audio_buses.cpp
msgid "Audio"
-msgstr ""
+msgstr "Audio"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus"
-msgstr ""
+msgstr "Adaugă Pistă Audio"
#: editor/editor_audio_buses.cpp
msgid "Master bus can't be deleted!"
-msgstr ""
+msgstr "Imposibil de Șters Pistă Audio Master!"
#: editor/editor_audio_buses.cpp
msgid "Delete Audio Bus"
-msgstr ""
+msgstr "Ştergeţi Pista Audio"
#: editor/editor_audio_buses.cpp
msgid "Duplicate Audio Bus"
-msgstr ""
+msgstr "Duplicați Pista Audio"
#: editor/editor_audio_buses.cpp
msgid "Reset Bus Volume"
-msgstr ""
+msgstr "Resetați Volumul Pistei Audio"
#: editor/editor_audio_buses.cpp
msgid "Move Audio Bus"
-msgstr ""
+msgstr "Mutați Pista Audio"
#: editor/editor_audio_buses.cpp
msgid "Save Audio Bus Layout As.."
-msgstr ""
+msgstr "Salvați Pista Audio Șablon Ca.."
#: editor/editor_audio_buses.cpp
msgid "Location for New Layout.."
-msgstr ""
+msgstr "Locație pentru Noul Șablon..."
#: editor/editor_audio_buses.cpp
msgid "Open Audio Bus Layout"
-msgstr ""
+msgstr "Deschideți Șablon Pistă Audio"
#: editor/editor_audio_buses.cpp
msgid "There is no 'res://default_bus_layout.tres' file."
-msgstr ""
+msgstr "Nu există nici un fişier 'res://default_bus_layout.tres'."
#: editor/editor_audio_buses.cpp
msgid "Invalid file, not an audio bus layout."
-msgstr ""
+msgstr "Fişier nevalid, nu un șablon de pistă audio."
#: editor/editor_audio_buses.cpp
msgid "Add Bus"
-msgstr ""
+msgstr "Adaugați Pistă Audio"
#: editor/editor_audio_buses.cpp
msgid "Create a new Bus Layout."
-msgstr ""
+msgstr "Creaţi un Șablon nou Pistă Audio."
#: editor/editor_audio_buses.cpp editor/property_editor.cpp
#: editor/script_create_dialog.cpp
msgid "Load"
-msgstr ""
+msgstr "Încărcați"
#: editor/editor_audio_buses.cpp
msgid "Load an existing Bus Layout."
-msgstr ""
+msgstr "Încărcaţi un Șablon de Pistă Audio existent."
#: editor/editor_audio_buses.cpp
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Save As"
-msgstr ""
+msgstr "Salvați Ca"
#: editor/editor_audio_buses.cpp
msgid "Save this Bus Layout to a file."
-msgstr ""
+msgstr "Salvaţi acest Șablon Pistă Audio într-un fişier."
#: editor/editor_audio_buses.cpp editor/import_dock.cpp
msgid "Load Default"
-msgstr ""
+msgstr "Încărcați Implicit"
#: editor/editor_audio_buses.cpp
msgid "Load the default Bus Layout."
-msgstr ""
+msgstr "Încărcat Șablonul Pistă Audio implicit."
#: editor/editor_autoload_settings.cpp
msgid "Invalid name."
-msgstr ""
+msgstr "Nume nevalid."
#: editor/editor_autoload_settings.cpp
msgid "Valid characters:"
-msgstr ""
+msgstr "Caractere valide:"
#: editor/editor_autoload_settings.cpp
msgid "Invalid name. Must not collide with an existing engine class name."
msgstr ""
+"Nume nevalid. Nu trebuie să se lovească cu un nume de clasa deja existent în "
+"motor."
#: editor/editor_autoload_settings.cpp
msgid "Invalid name. Must not collide with an existing buit-in type name."
msgstr ""
+"Nume nevalid. Nu trebuie să se lovească cu un nume de tip deja existent în "
+"motor tip."
#: editor/editor_autoload_settings.cpp
msgid "Invalid name. Must not collide with an existing global constant name."
msgstr ""
+"Nume nevalid. Nu trebuie să se lovească cu un nume ce constante globale."
#: editor/editor_autoload_settings.cpp
msgid "Invalid Path."
-msgstr ""
+msgstr "Cale nevalidă."
#: editor/editor_autoload_settings.cpp
msgid "File does not exist."
-msgstr ""
+msgstr "Fișierul nu există."
#: editor/editor_autoload_settings.cpp
msgid "Not in resource path."
-msgstr ""
+msgstr "Nu în calea de resurse."
#: editor/editor_autoload_settings.cpp
msgid "Add AutoLoad"
-msgstr ""
+msgstr "Adaugați AutoLoad"
#: editor/editor_autoload_settings.cpp
msgid "Autoload '%s' already exists!"
-msgstr ""
+msgstr "AutoLoad '%s' există deja!"
#: editor/editor_autoload_settings.cpp
msgid "Rename Autoload"
-msgstr ""
+msgstr "Redenumiţi Autoload"
#: editor/editor_autoload_settings.cpp
msgid "Toggle AutoLoad Globals"
-msgstr ""
+msgstr "Comutați Globale AutoLoad"
#: editor/editor_autoload_settings.cpp
msgid "Move Autoload"
-msgstr ""
+msgstr "Mutați Autoload"
#: editor/editor_autoload_settings.cpp
msgid "Remove Autoload"
-msgstr ""
+msgstr "Eliminați Autoload"
#: editor/editor_autoload_settings.cpp
msgid "Enable"
-msgstr ""
+msgstr "Activați"
#: editor/editor_autoload_settings.cpp
msgid "Rearrange Autoloads"
-msgstr ""
+msgstr "Rearanjați Autoload-urile"
#: editor/editor_autoload_settings.cpp editor/editor_file_dialog.cpp
#: scene/gui/file_dialog.cpp
msgid "Path:"
-msgstr ""
+msgstr "Cale:"
#: editor/editor_autoload_settings.cpp
msgid "Node Name:"
-msgstr ""
+msgstr "Nume Nod:"
#: editor/editor_autoload_settings.cpp editor/editor_profiler.cpp
#: editor/project_manager.cpp editor/settings_config_dialog.cpp
msgid "Name"
-msgstr ""
+msgstr "Nume"
#: editor/editor_autoload_settings.cpp
msgid "Singleton"
-msgstr ""
+msgstr "Singleton"
#: editor/editor_data.cpp
msgid "Updating Scene"
-msgstr ""
+msgstr "Scena se Actualizează"
#: editor/editor_data.cpp
msgid "Storing local changes.."
-msgstr ""
+msgstr "Modificările locale se stochează..."
#: editor/editor_data.cpp
msgid "Updating scene.."
-msgstr ""
+msgstr "Scena se Actualizează.."
#: editor/editor_data.cpp
msgid "[empty]"
-msgstr ""
+msgstr "[gol]"
#: editor/editor_data.cpp
msgid "[unsaved]"
-msgstr ""
+msgstr "[nesalvat]"
#: editor/editor_dir_dialog.cpp
msgid "Please select a base directory first"
-msgstr ""
+msgstr "Vă rugăm să selectaţi mai întâi un director de baza"
#: editor/editor_dir_dialog.cpp
msgid "Choose a Directory"
-msgstr ""
+msgstr "Alegeţi un Director"
#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp
msgid "Create Folder"
-msgstr ""
+msgstr "Creați Director"
#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
#: editor/editor_plugin_settings.cpp editor/filesystem_dock.cpp
#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
#: scene/gui/file_dialog.cpp
msgid "Name:"
-msgstr ""
+msgstr "Nume:"
#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp
msgid "Could not create folder."
-msgstr ""
+msgstr "Directorul nu a putut fi creat."
#: editor/editor_dir_dialog.cpp
msgid "Choose"
-msgstr ""
+msgstr "Alegeți"
#: editor/editor_export.cpp
msgid "Storing File:"
-msgstr ""
+msgstr "Fişierul se Stochează:"
#: editor/editor_export.cpp
msgid "Packing"
@@ -1371,6 +1390,10 @@ msgstr ""
msgid "Clear Output"
msgstr ""
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3590,6 +3613,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3875,6 +3906,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5536,6 +5571,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6011,7 +6054,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6155,10 +6200,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7197,6 +7238,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7833,10 +7878,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/ru.po b/editor/translations/ru.po
index 67e02b9ce..9ddbc965e 100644
--- a/editor/translations/ru.po
+++ b/editor/translations/ru.po
@@ -5,7 +5,9 @@
#
# Artem Varaksa <aymfst@gmail.com>, 2018.
# B10nicMachine <shumik1337@gmail.com>, 2017.
+# Chaosus89 <chaosus89@gmail.com>, 2018.
# DimOkGamer <dimokgamer@gmail.com>, 2016-2017.
+# Forest Swamp <sample1989@mail.ru>, 2018.
# Igor S <scorched@bk.ru>, 2017.
# ijet <my-ijet@mail.ru>, 2017-2018.
# Maxim Kim <habamax@gmail.com>, 2016.
@@ -13,13 +15,14 @@
# outbools <drag4e@yandex.ru>, 2017.
# pitchblack <pitchblack@mail.ru>, 2017.
# Sergey Agarkov <zorgsoft@gmail.com>, 2017.
+# Аркадий Авас <savvot@gmail.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
"POT-Creation-Date: \n"
-"PO-Revision-Date: 2018-02-23 20:40+0000\n"
-"Last-Translator: ijet <my-ijet@mail.ru>\n"
+"PO-Revision-Date: 2018-04-27 16:39+0000\n"
+"Last-Translator: Chaosus89 <chaosus89@gmail.com>\n"
"Language-Team: Russian <https://hosted.weblate.org/projects/godot-engine/"
"godot/ru/>\n"
"Language: ru\n"
@@ -28,7 +31,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 2.20-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -1288,11 +1291,11 @@ msgstr "Краткое описание:"
#: editor/editor_help.cpp
msgid "Members"
-msgstr "Участники"
+msgstr "Свойства"
#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
msgid "Members:"
-msgstr "Участники:"
+msgstr "Свойства:"
#: editor/editor_help.cpp
msgid "Public Methods"
@@ -1358,7 +1361,7 @@ msgstr "Свойства"
#: editor/editor_help.cpp
msgid "Property Description:"
-msgstr "Описание свойства:"
+msgstr "Описание свойств:"
#: editor/editor_help.cpp
msgid ""
@@ -1407,6 +1410,10 @@ msgstr "Очистить"
msgid "Clear Output"
msgstr "Очистить вывод"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Ошибка при сохранении ресурса!"
@@ -2011,7 +2018,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Visible Navigation"
-msgstr "Видимые области навигации"
+msgstr "Видимая навигация"
#: editor/editor_node.cpp
msgid ""
@@ -2352,7 +2359,7 @@ msgstr "Включительно"
#: editor/editor_profiler.cpp
msgid "Self"
-msgstr "Сущность"
+msgstr "Self"
#: editor/editor_profiler.cpp
msgid "Frame #:"
@@ -3711,6 +3718,16 @@ msgid "Show Guides"
msgstr "Показывать направляющие"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Отображать начало координат"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 Окно"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Центрировать на выбранном"
@@ -4000,6 +4017,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Полиcетка не имеет поверхности для создания контура!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Невозможно создать контур!"
@@ -4639,7 +4660,7 @@ msgstr "Сохранить всё"
#: editor/plugins/script_editor_plugin.cpp
msgid "Soft Reload Script"
-msgstr "Мягко перезагрузить скрипты"
+msgstr "Мягко перезагрузить скрипт"
#: editor/plugins/script_editor_plugin.cpp
msgid "Copy Script Path"
@@ -5536,18 +5557,16 @@ msgid "Move (After)"
msgstr "Переместить (после)"
#: editor/plugins/sprite_frames_editor_plugin.cpp
-#, fuzzy
msgid "SpriteFrames"
-msgstr "Кадры cпрайта"
+msgstr "SpriteFrames"
#: editor/plugins/style_box_editor_plugin.cpp
msgid "StyleBox Preview:"
-msgstr "StyleBox предпросмотр:"
+msgstr "Предпросмотр StyleBox:"
#: editor/plugins/style_box_editor_plugin.cpp
-#, fuzzy
msgid "StyleBox"
-msgstr "Стиль"
+msgstr "StyleBox"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Set Region Rect"
@@ -5670,6 +5689,16 @@ msgid "Checked Item"
msgstr "Отмеченный элемент"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Добавить элемент"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Отмеченный элемент"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Имеет"
@@ -5923,7 +5952,7 @@ msgstr "Создать латку"
#: editor/project_export.cpp
msgid "Features"
-msgstr "Свойства"
+msgstr "Особенности"
#: editor/project_export.cpp
msgid "Custom (comma-separated):"
@@ -6149,7 +6178,7 @@ msgstr ""
#: editor/project_settings_editor.cpp
msgid "Key "
-msgstr "Кнопка "
+msgstr "Ключ "
#: editor/project_settings_editor.cpp
msgid "Joy Button"
@@ -6164,8 +6193,10 @@ msgid "Mouse Button"
msgstr "Кнопка мыши"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Недопустимое название действия (подойдёт всё кроме '/' или ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6308,10 +6339,6 @@ msgid "Delete Item"
msgstr "Удалить элемент"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "Не может содержать '/' или ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Уже существует"
@@ -6968,7 +6995,7 @@ msgstr "Встроенный Скрипт"
#: editor/script_create_dialog.cpp
msgid "Attach Node Script"
-msgstr "Добавление скрипта"
+msgstr "Прикрепить скрипт"
#: editor/script_editor_debugger.cpp
msgid "Remote "
@@ -7370,6 +7397,10 @@ msgstr "GridMap Параметры"
msgid "Pick Distance:"
msgstr "Расстояние выбора:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "Генерация решения..."
@@ -7420,7 +7451,7 @@ msgstr "Предупреждения"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "Конец трассировки внутреннего стека исключений"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7977,10 +8008,9 @@ msgstr "ARVROrigin требует дочерний узел ARVRCamera"
#: scene/3d/baked_lightmap.cpp
msgid "%d%%"
-msgstr ""
+msgstr "%d%%"
#: scene/3d/baked_lightmap.cpp
-#, fuzzy
msgid "(Time Left: %d:%02d s)"
msgstr "(Осталось: %d:%02d сек)"
@@ -8080,12 +8110,22 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "Свойство Path должно указывать на действительный Spatial узел."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Только один WorldEnvironment допускается на сцену или совокупность "
"приведённых сцен."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8185,6 +8225,12 @@ msgstr "Ошибка загрузки шрифта."
msgid "Invalid font size."
msgstr "Недопустимый размер шрифта."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Недопустимое название действия (подойдёт всё кроме '/' или ':')."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Не может содержать '/' или ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/sk.po b/editor/translations/sk.po
index c5fa76fb0..16f675df3 100644
--- a/editor/translations/sk.po
+++ b/editor/translations/sk.po
@@ -1385,6 +1385,10 @@ msgstr ""
msgid "Clear Output"
msgstr "Popis:"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3624,6 +3628,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3914,6 +3926,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5590,6 +5606,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6073,7 +6097,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6218,10 +6244,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7278,6 +7300,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7931,10 +7957,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/sl.po b/editor/translations/sl.po
index 04fb6d981..74b469fc4 100644
--- a/editor/translations/sl.po
+++ b/editor/translations/sl.po
@@ -3,16 +3,17 @@
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
#
-# matevž lapajne <sivar.lapajne@gmail.com>, 2016-2017.
-# Matjaž Vitas <matjaz.vitas@gmail.com>, 2017.
+# matevž lapajne <sivar.lapajne@gmail.com>, 2016-2018.
+# Matjaž Vitas <matjaz.vitas@gmail.com>, 2017-2018.
# Miha Komatar <miha.komatar@gmail.com>, 2018.
# Simon Šander <simon.sand3r@gmail.com>, 2017.
+# Yahara Octanis <yaharao55@gmail.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-01-25 22:27+0000\n"
-"Last-Translator: Miha Komatar <miha.komatar@gmail.com>\n"
+"PO-Revision-Date: 2018-05-03 07:41+0000\n"
+"Last-Translator: matevž lapajne <sivar.lapajne@gmail.com>\n"
"Language-Team: Slovenian <https://hosted.weblate.org/projects/godot-engine/"
"godot/sl/>\n"
"Language: sl\n"
@@ -20,7 +21,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
"%100==4 ? 2 : 3;\n"
-"X-Generator: Weblate 2.19-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -28,130 +29,128 @@ msgstr "Onemogočen"
#: editor/animation_editor.cpp
msgid "All Selection"
-msgstr "Vsa izbira"
+msgstr "Celotna izbira"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Time"
-msgstr ""
+msgstr "Animacija Spremeni čas ključne slike"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr "Anim spremeni prehod"
+msgstr "Animacija Spremeni prehod"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
-msgstr ""
+msgstr "Animacija Spremeni transformacijo"
#: editor/animation_editor.cpp
msgid "Anim Change Keyframe Value"
-msgstr ""
+msgstr "Animacija Spremeni vrednost ključne slike"
#: editor/animation_editor.cpp
msgid "Anim Change Call"
-msgstr "Anim izberi klic"
+msgstr "Animacija Spremeni klic"
#: editor/animation_editor.cpp
msgid "Anim Add Track"
-msgstr ""
+msgstr "Animacija Dodaj sled"
#: editor/animation_editor.cpp
msgid "Anim Duplicate Keys"
-msgstr ""
+msgstr "Animacija Podvoji ključe"
#: editor/animation_editor.cpp
msgid "Move Anim Track Up"
-msgstr ""
+msgstr "Premakni animacijsko sled gor"
#: editor/animation_editor.cpp
msgid "Move Anim Track Down"
-msgstr ""
+msgstr "Premakni animacijsko sled dol"
#: editor/animation_editor.cpp
msgid "Remove Anim Track"
-msgstr ""
+msgstr "Odstrani animacijsko sled"
#: editor/animation_editor.cpp
msgid "Set Transitions to:"
-msgstr "Nastavite Prehode na:"
+msgstr "Nastavi prehode na:"
#: editor/animation_editor.cpp
msgid "Anim Track Rename"
-msgstr ""
+msgstr "Animacija Preimenuj sled"
#: editor/animation_editor.cpp
msgid "Anim Track Change Interpolation"
-msgstr ""
+msgstr "Animacija Spremeni interpolacijo sledi"
#: editor/animation_editor.cpp
msgid "Anim Track Change Value Mode"
-msgstr ""
+msgstr "Animacija Spremeni način vrednosti sledi"
#: editor/animation_editor.cpp
msgid "Anim Track Change Wrap Mode"
-msgstr ""
+msgstr "Animacija Spremeni način ovijanja sledi"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Edit Node Curve"
-msgstr "Uredi Node Krivuljo"
+msgstr "Uredi krivuljo vozlišča"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Edit Selection Curve"
-msgstr "Uredi Izbor Krivulje"
+msgstr "Uredi Krivulje izbora"
#: editor/animation_editor.cpp
msgid "Anim Delete Keys"
-msgstr ""
+msgstr "Animacija Izbriši ključe"
#: editor/animation_editor.cpp editor/plugins/tile_map_editor_plugin.cpp
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Duplicate Selection"
-msgstr "Podvoji Izbrano"
+msgstr "Podvoji izbrano"
#: editor/animation_editor.cpp
msgid "Duplicate Transposed"
-msgstr ""
+msgstr "Podvoji transponirano"
#: editor/animation_editor.cpp
msgid "Remove Selection"
-msgstr "Odstrani Izbiro"
+msgstr "Odstrani izbrano"
#: editor/animation_editor.cpp
msgid "Continuous"
-msgstr ""
+msgstr "Neprekinjeno"
#: editor/animation_editor.cpp
msgid "Discrete"
-msgstr ""
+msgstr "Diskretno"
#: editor/animation_editor.cpp
msgid "Trigger"
-msgstr ""
+msgstr "Sprožilec"
#: editor/animation_editor.cpp
msgid "Anim Add Key"
-msgstr ""
+msgstr "Animacija Dodaj ključ"
#: editor/animation_editor.cpp
msgid "Anim Move Keys"
-msgstr ""
+msgstr "Animacija Premakni ključ"
#: editor/animation_editor.cpp
msgid "Scale Selection"
-msgstr "Povečaj Izbiro"
+msgstr "Povečaj izbiro"
#: editor/animation_editor.cpp
msgid "Scale From Cursor"
-msgstr ""
+msgstr "Povečaj iz kazalca"
#: editor/animation_editor.cpp
msgid "Goto Next Step"
-msgstr "Pojdite na Naslednji Korak"
+msgstr "Pojdi na naslednji korak"
#: editor/animation_editor.cpp
msgid "Goto Prev Step"
-msgstr "Pojdite na Predhodni Korak"
+msgstr "Pojdi na prejšnji korak"
#: editor/animation_editor.cpp editor/plugins/curve_editor_plugin.cpp
#: editor/property_editor.cpp
@@ -160,7 +159,7 @@ msgstr "Linearno"
#: editor/animation_editor.cpp editor/plugins/theme_editor_plugin.cpp
msgid "Constant"
-msgstr ""
+msgstr "Konstanta"
#: editor/animation_editor.cpp
msgid "In"
@@ -192,7 +191,7 @@ msgstr "Počisti Animacijo"
#: editor/animation_editor.cpp
msgid "Create NEW track for %s and insert key?"
-msgstr ""
+msgstr "Ustvari NOVI trak za %s in vstavi ključ?"
#: editor/animation_editor.cpp
msgid "Create %d NEW tracks and insert keys?"
@@ -244,7 +243,7 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Animation zoom."
-msgstr "Približaj Animacijo"
+msgstr "Približaj animacijo"
#: editor/animation_editor.cpp
msgid "Length (s):"
@@ -252,7 +251,7 @@ msgstr "Dolžina (s):"
#: editor/animation_editor.cpp
msgid "Animation length (in seconds)."
-msgstr ""
+msgstr "Dolžina animacije (v sekundah)."
#: editor/animation_editor.cpp
msgid "Step (s):"
@@ -443,7 +442,7 @@ msgstr ""
#: editor/plugins/theme_editor_plugin.cpp editor/project_manager.cpp
#: editor/project_settings_editor.cpp
msgid "Remove"
-msgstr ""
+msgstr "Odstrani"
#: editor/connections_dialog.cpp
msgid "Add Extra Call Argument:"
@@ -530,7 +529,7 @@ msgstr "Ustvari"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp
msgid "Favorites:"
-msgstr ""
+msgstr "Priljubljene:"
#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
msgid "Recent:"
@@ -588,7 +587,7 @@ msgstr ""
#: editor/project_manager.cpp editor/project_settings_editor.cpp
#: editor/script_create_dialog.cpp
msgid "Path"
-msgstr ""
+msgstr "Pot"
#: editor/dependency_editor.cpp
msgid "Dependencies:"
@@ -670,7 +669,7 @@ msgstr ""
#: editor/dependency_editor.cpp editor/editor_node.cpp
msgid "Orphan Resource Explorer"
-msgstr ""
+msgstr "Raziskovalec Osamljenih Virov"
#: editor/dependency_editor.cpp
msgid "Delete selected files?"
@@ -714,7 +713,7 @@ msgstr ""
#: editor/editor_about.cpp
msgid "Project Manager "
-msgstr ""
+msgstr "Upravljalnik Projekta "
#: editor/editor_about.cpp
msgid "Developers"
@@ -889,7 +888,7 @@ msgstr "Izbriši Izbrano"
#: editor/editor_audio_buses.cpp
msgid "Audio"
-msgstr ""
+msgstr "Zvok"
#: editor/editor_audio_buses.cpp
msgid "Add Audio Bus"
@@ -1045,7 +1044,7 @@ msgstr ""
#: editor/editor_autoload_settings.cpp editor/editor_profiler.cpp
#: editor/project_manager.cpp editor/settings_config_dialog.cpp
msgid "Name"
-msgstr ""
+msgstr "Ime"
#: editor/editor_autoload_settings.cpp
msgid "Singleton"
@@ -1053,7 +1052,7 @@ msgstr ""
#: editor/editor_data.cpp
msgid "Updating Scene"
-msgstr ""
+msgstr "Posodabljanje Scene"
#: editor/editor_data.cpp
msgid "Storing local changes.."
@@ -1061,11 +1060,11 @@ msgstr ""
#: editor/editor_data.cpp
msgid "Updating scene.."
-msgstr ""
+msgstr "Posodabljanje scene.."
#: editor/editor_data.cpp
msgid "[empty]"
-msgstr ""
+msgstr "[prazen]"
#: editor/editor_data.cpp
msgid "[unsaved]"
@@ -1082,7 +1081,7 @@ msgstr ""
#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp
msgid "Create Folder"
-msgstr ""
+msgstr "Ustvarite Mapo"
#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
#: editor/editor_plugin_settings.cpp editor/filesystem_dock.cpp
@@ -1117,9 +1116,8 @@ msgid "File Exists, Overwrite?"
msgstr ""
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
-#, fuzzy
msgid "Select Current Folder"
-msgstr "Dodaj Setter Lastnost"
+msgstr "Izberite Trenutno Mapo"
#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
msgid "Copy Path"
@@ -1143,7 +1141,7 @@ msgstr ""
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "All Files (*)"
-msgstr ""
+msgstr "Vse Datoteke (*)"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open a File"
@@ -1213,7 +1211,7 @@ msgstr ""
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Directories & Files:"
-msgstr ""
+msgstr "Mape & Datoteke:"
#: editor/editor_file_dialog.cpp
msgid "Preview:"
@@ -1222,7 +1220,7 @@ msgstr ""
#: editor/editor_file_dialog.cpp editor/script_editor_debugger.cpp
#: scene/gui/file_dialog.cpp
msgid "File:"
-msgstr ""
+msgstr "Datoteka:"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Must use a valid extension."
@@ -1373,7 +1371,7 @@ msgstr ""
#: editor/editor_log.cpp
msgid "Output:"
-msgstr ""
+msgstr "Izhod:"
#: editor/editor_log.cpp editor/plugins/animation_tree_editor_plugin.cpp
#: editor/property_editor.cpp editor/script_editor_debugger.cpp
@@ -1386,6 +1384,10 @@ msgstr ""
msgid "Clear Output"
msgstr ""
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -1433,7 +1435,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Saving Scene"
-msgstr ""
+msgstr "Shranjevanje Scene"
#: editor/editor_node.cpp
msgid "Analyzing"
@@ -1594,19 +1596,19 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Open Scene"
-msgstr ""
+msgstr "Odpri Sceno"
#: editor/editor_node.cpp
msgid "Open Base Scene"
-msgstr ""
+msgstr "Odpri Osnovno Sceno"
#: editor/editor_node.cpp
msgid "Quick Open Scene.."
-msgstr ""
+msgstr "Hitro Odpri Sceno.."
#: editor/editor_node.cpp
msgid "Quick Open Script.."
-msgstr ""
+msgstr "Hitro Odpri Skripto.."
#: editor/editor_node.cpp
msgid "Save & Close"
@@ -1618,7 +1620,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Save Scene As.."
-msgstr ""
+msgstr "Shrani Sceno Kot.."
#: editor/editor_node.cpp
msgid "No"
@@ -1670,11 +1672,11 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Quick Run Scene.."
-msgstr ""
+msgstr "Hitro Zaženi Sceno.."
#: editor/editor_node.cpp
msgid "Quit"
-msgstr ""
+msgstr "Zapri"
#: editor/editor_node.cpp
msgid "Exit the editor?"
@@ -1750,7 +1752,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Clear Recent Scenes"
-msgstr ""
+msgstr "Počisti Nedavne Scene"
#: editor/editor_node.cpp
msgid "Save Layout"
@@ -1795,15 +1797,15 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Add a new scene."
-msgstr ""
+msgstr "Dodaj novo Sceno."
#: editor/editor_node.cpp
msgid "Scene"
-msgstr ""
+msgstr "Scena"
#: editor/editor_node.cpp
msgid "Go to previously opened scene."
-msgstr ""
+msgstr "Pojdite na predhodno odprte scene."
#: editor/editor_node.cpp
msgid "Next tab"
@@ -1823,35 +1825,35 @@ msgstr ""
#: editor/editor_node.cpp
msgid "New Scene"
-msgstr ""
+msgstr "Nova Scena"
#: editor/editor_node.cpp
msgid "New Inherited Scene.."
-msgstr ""
+msgstr "Nova Podedovana Scena.."
#: editor/editor_node.cpp
msgid "Open Scene.."
-msgstr ""
+msgstr "Odpri Sceno.."
#: editor/editor_node.cpp
msgid "Save Scene"
-msgstr ""
+msgstr "Shrani Sceno"
#: editor/editor_node.cpp
msgid "Save all Scenes"
-msgstr ""
+msgstr "Shrani vse Scene"
#: editor/editor_node.cpp
msgid "Close Scene"
-msgstr ""
+msgstr "Zapri Sceno"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Open Recent"
-msgstr ""
+msgstr "Odpri Nedavno"
#: editor/editor_node.cpp
msgid "Convert To.."
-msgstr ""
+msgstr "Pretvori V.."
#: editor/editor_node.cpp
msgid "MeshLibrary.."
@@ -1864,16 +1866,16 @@ msgstr ""
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
msgid "Undo"
-msgstr ""
+msgstr "Razveljavi"
#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
#: scene/gui/line_edit.cpp
msgid "Redo"
-msgstr ""
+msgstr "Ponovi"
#: editor/editor_node.cpp
msgid "Revert Scene"
-msgstr ""
+msgstr "Povrni Sceno"
#: editor/editor_node.cpp
msgid "Miscellaneous project or scene-wide tools."
@@ -1881,11 +1883,11 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Project"
-msgstr ""
+msgstr "Projekt"
#: editor/editor_node.cpp
msgid "Project Settings"
-msgstr ""
+msgstr "Nastavitve Projekta"
#: editor/editor_node.cpp
msgid "Run Script"
@@ -1893,23 +1895,23 @@ msgstr ""
#: editor/editor_node.cpp editor/project_export.cpp
msgid "Export"
-msgstr ""
+msgstr "Izvozi"
#: editor/editor_node.cpp
msgid "Tools"
-msgstr ""
+msgstr "Orodja"
#: editor/editor_node.cpp
msgid "Quit to Project List"
-msgstr ""
+msgstr "Zapri na Seznam Projektov"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Debug"
-msgstr ""
+msgstr "Razhroščevalnik"
#: editor/editor_node.cpp
msgid "Deploy with Remote Debug"
-msgstr ""
+msgstr "Uvajanje z Oddaljenim Razhroščevanjem"
#: editor/editor_node.cpp
msgid ""
@@ -1919,7 +1921,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Small Deploy with Network FS"
-msgstr ""
+msgstr "Kratko Uvajanje z Omrežjem FS"
#: editor/editor_node.cpp
msgid ""
@@ -1976,9 +1978,8 @@ msgid ""
msgstr ""
#: editor/editor_node.cpp
-#, fuzzy
msgid "Editor"
-msgstr "Uredi"
+msgstr "Urejevalnik"
#: editor/editor_node.cpp editor/settings_config_dialog.cpp
msgid "Editor Settings"
@@ -1998,7 +1999,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Help"
-msgstr ""
+msgstr "Pomoč"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Classes"
@@ -2130,7 +2131,7 @@ msgstr ""
#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
#: editor/project_manager.cpp
msgid "Import"
-msgstr ""
+msgstr "Uvozi"
#: editor/editor_node.cpp
msgid "Node"
@@ -2138,11 +2139,11 @@ msgstr ""
#: editor/editor_node.cpp
msgid "FileSystem"
-msgstr ""
+msgstr "DatotečniSistem"
#: editor/editor_node.cpp
msgid "Output"
-msgstr ""
+msgstr "Izhod"
#: editor/editor_node.cpp
msgid "Don't Save"
@@ -2198,7 +2199,7 @@ msgstr ""
#: editor/editor_node.cpp editor/project_manager.cpp
msgid "Open Asset Library"
-msgstr ""
+msgstr "Odprite Asset Library"
#: editor/editor_node.cpp
msgid "Open the next Editor"
@@ -2664,12 +2665,12 @@ msgstr ""
#: editor/filesystem_dock.cpp
msgid "Move"
-msgstr ""
+msgstr "Premakni"
#: editor/filesystem_dock.cpp editor/plugins/animation_tree_editor_plugin.cpp
#: editor/project_manager.cpp
msgid "Rename"
-msgstr ""
+msgstr "Preimenuj"
#: editor/groups_editor.cpp
msgid "Add to Group"
@@ -3056,7 +3057,7 @@ msgstr ""
#: editor/plugins/animation_player_editor_plugin.cpp
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Animation"
-msgstr ""
+msgstr "Animacija"
#: editor/plugins/animation_tree_editor_plugin.cpp
msgid "New name:"
@@ -3292,24 +3293,24 @@ msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "first"
-msgstr ""
+msgstr "prvi"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "prev"
-msgstr ""
+msgstr "prejšnji"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "next"
-msgstr ""
+msgstr "naslednji"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "last"
-msgstr ""
+msgstr "zadnji"
#: editor/plugins/asset_library_editor_plugin.cpp
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "All"
-msgstr ""
+msgstr "Vse"
#: editor/plugins/asset_library_editor_plugin.cpp
#: editor/project_settings_editor.cpp
@@ -3318,7 +3319,7 @@ msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Sort:"
-msgstr ""
+msgstr "Razvrsti:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Reverse"
@@ -3327,15 +3328,15 @@ msgstr ""
#: editor/plugins/asset_library_editor_plugin.cpp
#: editor/project_settings_editor.cpp
msgid "Category:"
-msgstr ""
+msgstr "Kategorija:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Site:"
-msgstr ""
+msgstr "Stran:"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Support.."
-msgstr ""
+msgstr "Podpora.."
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Official"
@@ -3461,11 +3462,11 @@ msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Select Mode"
-msgstr ""
+msgstr "Izberite Način"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Drag: Rotate"
-msgstr ""
+msgstr "Povlecite: Zavrti"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Alt+Drag: Move"
@@ -3477,15 +3478,15 @@ msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Alt+RMB: Depth list selection"
-msgstr ""
+msgstr "Alt+RMB: Izbira globine"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Move Mode"
-msgstr ""
+msgstr "Način Premika"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Rotate Mode"
-msgstr ""
+msgstr "Način Vrtenja"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
@@ -3493,6 +3494,8 @@ msgid ""
"Show a list of all objects at the position clicked\n"
"(same as Alt+RMB in select mode)."
msgstr ""
+"Ob kliku prikaži seznam vseh objektov na tem mestu.\n"
+"(isto kot Alt+RMB v načinu izbire)."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Click to change object's rotation pivot."
@@ -3562,12 +3565,12 @@ msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Lock the selected object in place (can't be moved)."
-msgstr ""
+msgstr "Izbrani predmet zaklenite na svoje mesto (ga ni mogoče premakniti)."
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Unlock the selected object (can be moved)."
-msgstr ""
+msgstr "Odklenite izbrani predmet (ga lahko premaknete)."
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Makes sure the object's children are not selectable."
@@ -3600,7 +3603,7 @@ msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/spatial_editor_plugin.cpp
msgid "View"
-msgstr ""
+msgstr "Pogled"
#: editor/plugins/canvas_item_editor_plugin.cpp
#: editor/plugins/polygon_2d_editor_plugin.cpp
@@ -3620,6 +3623,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3909,6 +3920,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -4370,7 +4385,7 @@ msgstr ""
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Ctrl: Rotate"
-msgstr ""
+msgstr "Ctrl: Vrtenje"
#: editor/plugins/polygon_2d_editor_plugin.cpp
msgid "Shift: Move All"
@@ -4597,7 +4612,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp
msgid "Run"
-msgstr ""
+msgstr "Zaženi"
#: editor/plugins/script_editor_plugin.cpp
msgid "Toggle Scripts Panel"
@@ -4682,7 +4697,7 @@ msgstr ""
#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
msgid "Debugger"
-msgstr ""
+msgstr "Razhroščevalnik"
#: editor/plugins/script_editor_plugin.cpp
msgid ""
@@ -5170,9 +5185,8 @@ msgid "XForm Dialog"
msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
-#, fuzzy
msgid "Select Mode (Q)"
-msgstr "Dodaj Setter Lastnost"
+msgstr "Izberite Način (Q)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid ""
@@ -5180,18 +5194,21 @@ msgid ""
"Alt+Drag: Move\n"
"Alt+RMB: Depth list selection"
msgstr ""
+"Povlecite: Zavrtite\n"
+"Alt+Drag: Premaknite\n"
+"Alt+RMB: Izbira globine"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Move Mode (W)"
-msgstr ""
+msgstr "Način Premika (W)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotate Mode (E)"
-msgstr ""
+msgstr "Način Vrtenja (E)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Scale Mode (R)"
-msgstr ""
+msgstr "Način Obsega (R)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Coords"
@@ -5199,11 +5216,11 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Space Mode (%s)"
-msgstr ""
+msgstr "Lokalno prostorski način (%s)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Snap Mode (%s)"
-msgstr ""
+msgstr "Način Zaskoka (%s)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Bottom View"
@@ -5273,15 +5290,15 @@ msgstr "Preklopi na Zaustavitev"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform"
-msgstr ""
+msgstr "Preoblikovanje"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Configure Snap.."
-msgstr ""
+msgstr "Preoblikuj Zaskok.."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Dialog.."
-msgstr ""
+msgstr "Preoblikovanje Dialoga.."
#: editor/plugins/spatial_editor_plugin.cpp
msgid "1 Viewport"
@@ -5326,11 +5343,11 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Snap Settings"
-msgstr ""
+msgstr "Nastavitve Zaskočenja"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Translate Snap:"
-msgstr ""
+msgstr "Prestavi Zaskočenje:"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotate Snap (deg.):"
@@ -5358,7 +5375,7 @@ msgstr ""
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Transform Change"
-msgstr ""
+msgstr "Preoblikovanje Sprememb"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Translate:"
@@ -5466,7 +5483,7 @@ msgstr ""
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "Snap Mode:"
-msgstr ""
+msgstr "Način Postavljanja:"
#: editor/plugins/texture_region_editor_plugin.cpp
msgid "<None>"
@@ -5583,6 +5600,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -5863,11 +5888,11 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Please choose a 'project.godot' file."
-msgstr ""
+msgstr "Izberite datoteko 'projekt.godot'."
#: editor/project_manager.cpp
msgid "Please choose an empty folder."
-msgstr ""
+msgstr "Izberite prazno mapo."
#: editor/project_manager.cpp
msgid "Imported Project"
@@ -5914,11 +5939,11 @@ msgstr "Preimenuj Funkcijo"
#: editor/project_manager.cpp
msgid "New Game Project"
-msgstr ""
+msgstr "Novi Projekt Igre"
#: editor/project_manager.cpp
msgid "Import Existing Project"
-msgstr ""
+msgstr "Uvoz Obstoječega Projekta"
#: editor/project_manager.cpp
msgid "Import & Edit"
@@ -5926,12 +5951,11 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Create New Project"
-msgstr ""
+msgstr "Ustvarite Nov Projekt"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Create & Edit"
-msgstr "Ustvari"
+msgstr "Ustvari & Uredi"
#: editor/project_manager.cpp
msgid "Install Project:"
@@ -5943,19 +5967,19 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Project Name:"
-msgstr ""
+msgstr "Ime Projekta:"
#: editor/project_manager.cpp
msgid "Create folder"
-msgstr ""
+msgstr "Ustvarite mapo"
#: editor/project_manager.cpp
msgid "Project Path:"
-msgstr ""
+msgstr "Pot Projekta:"
#: editor/project_manager.cpp
msgid "Browse"
-msgstr ""
+msgstr "Brskaj"
#: editor/project_manager.cpp
msgid "Unnamed Project"
@@ -6004,32 +6028,31 @@ msgstr ""
#: editor/project_manager.cpp
msgid "Project Manager"
-msgstr ""
+msgstr "Upravljalnik Projekta"
#: editor/project_manager.cpp
msgid "Project List"
-msgstr ""
+msgstr "Seznam Projektov"
#: editor/project_manager.cpp
msgid "Scan"
-msgstr ""
+msgstr "Preglej"
#: editor/project_manager.cpp
msgid "Select a Folder to Scan"
-msgstr ""
+msgstr "Izberite Mapo za Skeniranje"
#: editor/project_manager.cpp
msgid "New Project"
-msgstr ""
+msgstr "Nov Projekt"
#: editor/project_manager.cpp
-#, fuzzy
msgid "Templates"
-msgstr "Odstrani Spremenljivko"
+msgstr "Predloge"
#: editor/project_manager.cpp
msgid "Exit"
-msgstr ""
+msgstr "Izhod"
#: editor/project_manager.cpp
msgid "Restart Now"
@@ -6044,6 +6067,8 @@ msgid ""
"You don't currently have any projects.\n"
"Would you like to explore the official example projects in the Asset Library?"
msgstr ""
+"Trenutno nimate projektov.\n"
+"Želite raziskovati uradne primere projektov v Asset Library?"
#: editor/project_settings_editor.cpp
msgid "Key "
@@ -6062,7 +6087,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6208,10 +6235,6 @@ msgid "Delete Item"
msgstr "Izbriši Izbrano"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -6434,7 +6457,7 @@ msgstr ""
#: editor/property_editor.cpp
msgid "[Empty]"
-msgstr ""
+msgstr "[Prazen]"
#: editor/property_editor.cpp modules/visual_script/visual_script_editor.cpp
msgid "Set"
@@ -7261,6 +7284,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7318,15 +7345,15 @@ msgid ""
"A node yielded without working memory, please read the docs on how to yield "
"properly!"
msgstr ""
-"Node je bil sprejet brez potrebnega pomnilnika, pravilen postopek je opisan "
-"v dokumentaciji!"
+"Vozlišče se je ustavilo brez delovnega spomina! Prosimo preberite si v "
+"dokumentaciji, kako pravilno ustaviti vozlišče."
#: modules/visual_script/visual_script.cpp
msgid ""
"Node yielded, but did not return a function state in the first working "
"memory."
msgstr ""
-"Node pridobljen, vendar se ne vrne v funkcijalno stanje v prvem delavnem "
+"Vozlišče se je ustavilo, vendar ni vrnilo stanje funkcije v prvem delovnem "
"spominu."
#: modules/visual_script/visual_script.cpp
@@ -7334,16 +7361,17 @@ msgid ""
"Return value must be assigned to first element of node working memory! Fix "
"your node please."
msgstr ""
-"Vrnjena vrednost mora biti dodeljena prvemu elementu v node-delavnemu "
-"spominu! Prosim, da popraviš node."
+"Vrnjena vrednost mora biti dodeljena prvemu elementu delovnega spomina "
+"vozlišča! Prosimo popravite vozlišče."
#: modules/visual_script/visual_script.cpp
msgid "Node returned an invalid sequence output: "
-msgstr "Node je vrnil napačno zaporedje na izhodu: "
+msgstr "Vozlišče je vrnilo napačno sekvenco na izhodu: "
#: modules/visual_script/visual_script.cpp
msgid "Found sequence bit but not the node in the stack, report bug!"
-msgstr "Zaporedni bit najden, vendar ne node v skladu, prijavi napako!"
+msgstr ""
+"Sekvenčni bit je bil najden, vozlišče na skladu pa ne; prijavite napako!"
#: modules/visual_script/visual_script.cpp
msgid "Stack overflow with stack depth: "
@@ -7417,7 +7445,7 @@ msgstr ""
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Node"
-msgstr "Dodaj Node"
+msgstr "Dodaj vozlišče"
#: modules/visual_script/visual_script_editor.cpp
#, fuzzy
@@ -7453,13 +7481,13 @@ msgid "Hold Ctrl to drop a Variable Setter."
msgstr ""
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Add Preload Node"
-msgstr "Dodaj Node"
+msgstr "Dodaj prednaloženo vozlišče"
#: modules/visual_script/visual_script_editor.cpp
+#, fuzzy
msgid "Add Node(s) From Tree"
-msgstr "Dodaj Node(e) iz Drevesa"
+msgstr "Dodaj vozlišče(a) iz drevesa"
#: modules/visual_script/visual_script_editor.cpp
msgid "Add Getter Property"
@@ -7594,9 +7622,8 @@ msgid "Delete Selected"
msgstr "Izbriši Izbrano"
#: modules/visual_script/visual_script_editor.cpp
-#, fuzzy
msgid "Find Node Type"
-msgstr "Najdi Node Type"
+msgstr "Poišči tip vozlišča"
#: modules/visual_script/visual_script_editor.cpp
msgid "Copy Nodes"
@@ -7628,15 +7655,15 @@ msgstr "Neveljaven indeks lastnosti imena."
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Base object is not a Node!"
-msgstr "Osnovni objekt ni Node!"
+msgstr "Začetni objekt ni vozlišče!"
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Path does not lead Node!"
-msgstr "Pot ne vodi do Node!"
+msgstr "Pot ne vodi do vozlišča!"
#: modules/visual_script/visual_script_func_nodes.cpp
msgid "Invalid index property name '%s' in node %s."
-msgstr "Neveljaven indeks lastnosti imena '%s' v node %s."
+msgstr "Neveljaven indeks lastnosti imena '%s' v vozlišču %s."
#: modules/visual_script/visual_script_nodes.cpp
msgid ": Invalid argument of type: "
@@ -7656,7 +7683,7 @@ msgstr "VariableSet ni najden v skripti: "
#: modules/visual_script/visual_script_nodes.cpp
msgid "Custom node has no _step() method, can't process graph."
-msgstr "Custom node nima _step() metode, grafa ni mogoče obdelati."
+msgstr "Vozlišče po meri nima metode _step(); grafa ni mogoče obdelati."
#: modules/visual_script/visual_script_nodes.cpp
msgid ""
@@ -7927,10 +7954,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -7945,20 +7982,19 @@ msgstr ""
#: scene/gui/color_picker.cpp
msgid "Raw Mode"
-msgstr ""
+msgstr "Neobdelan način"
#: scene/gui/color_picker.cpp
msgid "Add current color as a preset"
-msgstr "Dodaj trenutno barvo za prednastavitev"
+msgstr "Dodaj trenutno barvo kot prednastavljeno"
#: scene/gui/dialogs.cpp
msgid "Alert!"
msgstr "Opozorilo!"
#: scene/gui/dialogs.cpp
-#, fuzzy
msgid "Please Confirm..."
-msgstr "Potrdite..."
+msgstr "Prosimo Potrdite..."
#: scene/gui/file_dialog.cpp
msgid "Select this Folder"
diff --git a/editor/translations/sr_Cyrl.po b/editor/translations/sr_Cyrl.po
index e411ff230..2c2b1eb00 100644
--- a/editor/translations/sr_Cyrl.po
+++ b/editor/translations/sr_Cyrl.po
@@ -1402,6 +1402,10 @@ msgstr "Обриши"
msgid "Clear Output"
msgstr "Излаз"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Грешка при чувању ресурса!"
@@ -3710,6 +3714,16 @@ msgid "Show Guides"
msgstr "Покажи водиче"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Прикажи центар"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 прозор"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Центрирај одабрано"
@@ -3999,6 +4013,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Мрежа нема површине за прављење ивица!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Неуспех при прављењу ивица!"
@@ -5685,6 +5703,16 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Додај ствар"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "CheckBox Radio1"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Има"
@@ -6180,7 +6208,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6325,10 +6355,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7373,6 +7399,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
#, fuzzy
msgid "Generating solution..."
@@ -8022,10 +8052,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/sr_Latn.po b/editor/translations/sr_Latn.po
new file mode 100644
index 000000000..d7cb85af1
--- /dev/null
+++ b/editor/translations/sr_Latn.po
@@ -0,0 +1,7954 @@
+# Serbian (latin) translation of the Godot Engine editor
+# Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur.
+# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
+# This file is distributed under the same license as the Godot source code.
+#
+# Milos Ponjavusic <brane@branegames.com>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Godot Engine editor\n"
+"PO-Revision-Date: 2018-04-25 14:41+0000\n"
+"Last-Translator: Milos Ponjavusic <brane@branegames.com>\n"
+"Language-Team: Serbian (latin) <https://hosted.weblate.org/projects/godot-"
+"engine/godot/sr_Latn/>\n"
+"Language: sr_Latn\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8-bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 3.0-dev\n"
+
+#: editor/animation_editor.cpp
+msgid "Disabled"
+msgstr "Onemogućeno"
+
+#: editor/animation_editor.cpp
+msgid "All Selection"
+msgstr "Sve sekcije"
+
+#: editor/animation_editor.cpp
+msgid "Anim Change Keyframe Time"
+msgstr "Animacija Promjeni Vrijeme Ključnog Kadra"
+
+#: editor/animation_editor.cpp
+msgid "Anim Change Transition"
+msgstr "Animacija Promjeni Tranziciju"
+
+#: editor/animation_editor.cpp
+msgid "Anim Change Transform"
+msgstr "Animacija Promjeni Transformaciju"
+
+#: editor/animation_editor.cpp
+msgid "Anim Change Keyframe Value"
+msgstr "Animacija Promjeni Vrijednost Ključnog Kadra"
+
+#: editor/animation_editor.cpp
+msgid "Anim Change Call"
+msgstr "Animacija Promjeni Poziv"
+
+#: editor/animation_editor.cpp
+msgid "Anim Add Track"
+msgstr "Animacija Dodaj Kanal"
+
+#: editor/animation_editor.cpp
+msgid "Anim Duplicate Keys"
+msgstr "Animacija Uduplaj Ključeve"
+
+#: editor/animation_editor.cpp
+msgid "Move Anim Track Up"
+msgstr "Pomjeri Kanal Animacije Gore"
+
+#: editor/animation_editor.cpp
+msgid "Move Anim Track Down"
+msgstr "Pomjeri Kanal Animacije Dole"
+
+#: editor/animation_editor.cpp
+msgid "Remove Anim Track"
+msgstr "Odstrani Kanal Animacije"
+
+#: editor/animation_editor.cpp
+msgid "Set Transitions to:"
+msgstr "Postavi tranzicije na:"
+
+#: editor/animation_editor.cpp
+msgid "Anim Track Rename"
+msgstr "Animacija Preimenuj Kanal"
+
+#: editor/animation_editor.cpp
+msgid "Anim Track Change Interpolation"
+msgstr "Animacija Promjeni Interpolaciju Kanala"
+
+#: editor/animation_editor.cpp
+msgid "Anim Track Change Value Mode"
+msgstr "Animacija Promjeni Vrijednosni Režim Kanala"
+
+#: editor/animation_editor.cpp
+msgid "Anim Track Change Wrap Mode"
+msgstr "Animacija Promjeni Režim Omotavanja Kanala"
+
+#: editor/animation_editor.cpp
+msgid "Edit Node Curve"
+msgstr "Izmjeni Krivulju Čvora"
+
+#: editor/animation_editor.cpp
+msgid "Edit Selection Curve"
+msgstr "Izmjeni Selekciju Krivulje"
+
+#: editor/animation_editor.cpp
+msgid "Anim Delete Keys"
+msgstr "Animacija Obriši Ključeve"
+
+#: editor/animation_editor.cpp editor/plugins/tile_map_editor_plugin.cpp
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Duplicate Selection"
+msgstr "Uduplaj Selekciju"
+
+#: editor/animation_editor.cpp
+msgid "Duplicate Transposed"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Remove Selection"
+msgstr "Obriši Selekciju"
+
+#: editor/animation_editor.cpp
+msgid "Continuous"
+msgstr "Neprekidna"
+
+#: editor/animation_editor.cpp
+msgid "Discrete"
+msgstr "Diskretna"
+
+#: editor/animation_editor.cpp
+msgid "Trigger"
+msgstr "Okidač"
+
+#: editor/animation_editor.cpp
+msgid "Anim Add Key"
+msgstr "Animacija Dodaj Ključ"
+
+#: editor/animation_editor.cpp
+msgid "Anim Move Keys"
+msgstr "Animacija Pomjeri Ključeve"
+
+#: editor/animation_editor.cpp
+msgid "Scale Selection"
+msgstr "Skaliraj Selekciju"
+
+#: editor/animation_editor.cpp
+msgid "Scale From Cursor"
+msgstr "Skaliraj od Kursora"
+
+#: editor/animation_editor.cpp
+msgid "Goto Next Step"
+msgstr "Otiđi Na Sljedeći Korak"
+
+#: editor/animation_editor.cpp
+msgid "Goto Prev Step"
+msgstr "Otiđi Na Prethodni Korak"
+
+#: editor/animation_editor.cpp editor/plugins/curve_editor_plugin.cpp
+#: editor/property_editor.cpp
+msgid "Linear"
+msgstr "Linearna"
+
+#: editor/animation_editor.cpp editor/plugins/theme_editor_plugin.cpp
+msgid "Constant"
+msgstr "Kontanta"
+
+#: editor/animation_editor.cpp
+msgid "In"
+msgstr "U"
+
+#: editor/animation_editor.cpp
+msgid "Out"
+msgstr "Izvan"
+
+#: editor/animation_editor.cpp
+msgid "In-Out"
+msgstr "U-Izvan"
+
+#: editor/animation_editor.cpp
+msgid "Out-In"
+msgstr "Izvan-U"
+
+#: editor/animation_editor.cpp
+msgid "Transitions"
+msgstr "Tranzicije"
+
+#: editor/animation_editor.cpp
+msgid "Optimize Animation"
+msgstr "Optimizuj Animaciju"
+
+#: editor/animation_editor.cpp
+msgid "Clean-Up Animation"
+msgstr "Počisti Animaciju"
+
+#: editor/animation_editor.cpp
+msgid "Create NEW track for %s and insert key?"
+msgstr "Napravi Novi kanal za %s i dodaj ključ?"
+
+#: editor/animation_editor.cpp
+msgid "Create %d NEW tracks and insert keys?"
+msgstr "Napravi %d novih kanala i dodaj ključeve?"
+
+#: editor/animation_editor.cpp editor/create_dialog.cpp
+#: editor/editor_audio_buses.cpp editor/plugins/abstract_polygon_2d_editor.cpp
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+#: editor/plugins/particles_editor_plugin.cpp editor/script_create_dialog.cpp
+msgid "Create"
+msgstr "Napravi"
+
+#: editor/animation_editor.cpp
+msgid "Anim Create & Insert"
+msgstr "Animacija Napravi i Dodaj"
+
+#: editor/animation_editor.cpp
+msgid "Anim Insert Track & Key"
+msgstr "Animacija Dodaj kanal i ključ"
+
+#: editor/animation_editor.cpp
+msgid "Anim Insert Key"
+msgstr "Animacija dodaj ključ"
+
+#: editor/animation_editor.cpp
+msgid "Change Anim Len"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Change Anim Loop"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Create Typed Value Key"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Insert"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Scale Keys"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim Add Call Track"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Animation zoom."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Length (s):"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Animation length (in seconds)."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Step (s):"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Cursor step snap (in seconds)."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Enable/Disable looping in animation."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Add new tracks."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Move current track up."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Move current track down."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Remove selected track."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Track tools"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Enable editing of individual keys by clicking them."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Anim. Optimizer"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Max. Linear Error:"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Max. Angular Error:"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Max Optimizable Angle:"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Optimize"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Select an AnimationPlayer from the Scene Tree to edit animations."
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Key"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Transition"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Scale Ratio:"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Call Functions in Which Node?"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Remove invalid keys"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Remove unresolved and empty tracks"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Clean-up all animations"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Clean-Up Animation(s) (NO UNDO!)"
+msgstr ""
+
+#: editor/animation_editor.cpp
+msgid "Clean-Up"
+msgstr ""
+
+#: editor/array_property_edit.cpp
+msgid "Resize Array"
+msgstr ""
+
+#: editor/array_property_edit.cpp
+msgid "Change Array Value Type"
+msgstr ""
+
+#: editor/array_property_edit.cpp
+msgid "Change Array Value"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Go to Line"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Line Number:"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "No Matches"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Replaced %d occurrence(s)."
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Match Case"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Whole Words"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Replace"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Replace All"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Selection Only"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Zoom In"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Zoom Out"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Reset Zoom"
+msgstr ""
+
+#: editor/code_editor.cpp editor/script_editor_debugger.cpp
+msgid "Line:"
+msgstr ""
+
+#: editor/code_editor.cpp
+msgid "Col:"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Method in target Node must be specified!"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid ""
+"Target method not found! Specify a valid method or attach a script to target "
+"Node."
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Connect To Node:"
+msgstr ""
+
+#: editor/connections_dialog.cpp editor/editor_autoload_settings.cpp
+#: editor/groups_editor.cpp editor/plugins/item_list_editor_plugin.cpp
+#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp
+msgid "Add"
+msgstr ""
+
+#: editor/connections_dialog.cpp editor/dependency_editor.cpp
+#: editor/plugins/animation_tree_editor_plugin.cpp
+#: editor/plugins/theme_editor_plugin.cpp editor/project_manager.cpp
+#: editor/project_settings_editor.cpp
+msgid "Remove"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Add Extra Call Argument:"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Extra Call Arguments:"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Path to Node:"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Make Function"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Deferred"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Oneshot"
+msgstr ""
+
+#: editor/connections_dialog.cpp editor/dependency_editor.cpp
+#: editor/export_template_manager.cpp
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/plugins/script_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp editor/project_export.cpp
+#: editor/project_settings_editor.cpp editor/property_editor.cpp
+#: editor/run_settings_dialog.cpp editor/settings_config_dialog.cpp
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Close"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Connect"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Connect '%s' to '%s'"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Connecting Signal:"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Disconnect '%s' from '%s'"
+msgstr ""
+
+#: editor/connections_dialog.cpp
+msgid "Connect.."
+msgstr ""
+
+#: editor/connections_dialog.cpp
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Disconnect"
+msgstr ""
+
+#: editor/connections_dialog.cpp editor/editor_help.cpp editor/node_dock.cpp
+msgid "Signals"
+msgstr ""
+
+#: editor/create_dialog.cpp
+msgid "Change %s Type"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/project_settings_editor.cpp
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change"
+msgstr ""
+
+#: editor/create_dialog.cpp
+msgid "Create New %s"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
+#: editor/filesystem_dock.cpp
+msgid "Favorites:"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/editor_file_dialog.cpp
+msgid "Recent:"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/editor_node.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
+#: editor/quick_open.cpp
+msgid "Search:"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/editor_help.cpp
+#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
+#: editor/quick_open.cpp
+msgid "Matches:"
+msgstr ""
+
+#: editor/create_dialog.cpp editor/editor_help.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp editor/property_selector.cpp
+#: editor/script_editor_debugger.cpp
+msgid "Description:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Search Replacement For:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Dependencies For:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid ""
+"Scene '%s' is currently being edited.\n"
+"Changes will not take effect unless reloaded."
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid ""
+"Resource '%s' is in use.\n"
+"Changes will take effect when reloaded."
+msgstr ""
+
+#: editor/dependency_editor.cpp
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Dependencies"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Resource"
+msgstr ""
+
+#: editor/dependency_editor.cpp editor/editor_autoload_settings.cpp
+#: editor/project_manager.cpp editor/project_settings_editor.cpp
+#: editor/script_create_dialog.cpp
+msgid "Path"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Dependencies:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Fix Broken"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Dependency Editor"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Search Replacement Resource:"
+msgstr ""
+
+#: editor/dependency_editor.cpp editor/editor_file_dialog.cpp
+#: editor/editor_help.cpp editor/editor_node.cpp editor/filesystem_dock.cpp
+#: editor/plugins/script_editor_plugin.cpp editor/property_selector.cpp
+#: editor/quick_open.cpp scene/gui/file_dialog.cpp
+msgid "Open"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Owners Of:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Remove selected files from the project? (no undo)"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid ""
+"The files being removed are required by other resources in order for them to "
+"work.\n"
+"Remove them anyway? (no undo)"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Cannot remove:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Error loading:"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Scene failed to load due to missing dependencies:"
+msgstr ""
+
+#: editor/dependency_editor.cpp editor/editor_node.cpp
+msgid "Open Anyway"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Which action should be taken?"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Fix Dependencies"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Errors loading!"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Permanently delete %d item(s)? (No undo!)"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Owns"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Resources Without Explicit Ownership:"
+msgstr ""
+
+#: editor/dependency_editor.cpp editor/editor_node.cpp
+msgid "Orphan Resource Explorer"
+msgstr ""
+
+#: editor/dependency_editor.cpp
+msgid "Delete selected files?"
+msgstr ""
+
+#: editor/dependency_editor.cpp editor/editor_audio_buses.cpp
+#: editor/editor_file_dialog.cpp editor/editor_node.cpp
+#: editor/filesystem_dock.cpp editor/plugins/item_list_editor_plugin.cpp
+#: editor/project_export.cpp editor/project_settings_editor.cpp
+#: editor/scene_tree_dock.cpp
+msgid "Delete"
+msgstr ""
+
+#: editor/dictionary_property_edit.cpp
+msgid "Change Dictionary Key"
+msgstr ""
+
+#: editor/dictionary_property_edit.cpp
+msgid "Change Dictionary Value"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Thanks from the Godot community!"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Thanks!"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Godot Engine contributors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Project Founders"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Lead Developer"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Project Manager "
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Developers"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Authors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Platinum Sponsors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Gold Sponsors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Mini Sponsors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Gold Donors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Silver Donors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Bronze Donors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Donors"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "License"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Thirdparty License"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid ""
+"Godot Engine relies on a number of thirdparty free and open source "
+"libraries, all compatible with the terms of its MIT license. The following "
+"is an exhaustive list of all such thirdparty components with their "
+"respective copyright statements and license terms."
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "All Components"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Components"
+msgstr ""
+
+#: editor/editor_about.cpp
+msgid "Licenses"
+msgstr ""
+
+#: editor/editor_asset_installer.cpp editor/project_manager.cpp
+msgid "Error opening package file, not in zip format."
+msgstr ""
+
+#: editor/editor_asset_installer.cpp
+msgid "Uncompressing Assets"
+msgstr ""
+
+#: editor/editor_asset_installer.cpp editor/project_manager.cpp
+msgid "Package Installed Successfully!"
+msgstr ""
+
+#: editor/editor_asset_installer.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Success!"
+msgstr ""
+
+#: editor/editor_asset_installer.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Install"
+msgstr ""
+
+#: editor/editor_asset_installer.cpp
+msgid "Package Installer"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Speakers"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Add Effect"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Rename Audio Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Change Audio Bus Volume"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Toggle Audio Bus Solo"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Toggle Audio Bus Mute"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Toggle Audio Bus Bypass Effects"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Select Audio Bus Send"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Add Audio Bus Effect"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Move Bus Effect"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Delete Bus Effect"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Audio Bus, Drag and Drop to rearrange."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Solo"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Mute"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Bypass"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Bus options"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp editor/filesystem_dock.cpp
+#: editor/plugins/tile_map_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "Duplicate"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Reset Volume"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Delete Effect"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Audio"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Add Audio Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Master bus can't be deleted!"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Delete Audio Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Duplicate Audio Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Reset Bus Volume"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Move Audio Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Save Audio Bus Layout As.."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Location for New Layout.."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Open Audio Bus Layout"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "There is no 'res://default_bus_layout.tres' file."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Invalid file, not an audio bus layout."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Add Bus"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Create a new Bus Layout."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp editor/property_editor.cpp
+#: editor/script_create_dialog.cpp
+msgid "Load"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Load an existing Bus Layout."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Save As"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Save this Bus Layout to a file."
+msgstr ""
+
+#: editor/editor_audio_buses.cpp editor/import_dock.cpp
+msgid "Load Default"
+msgstr ""
+
+#: editor/editor_audio_buses.cpp
+msgid "Load the default Bus Layout."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Invalid name."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Valid characters:"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Invalid name. Must not collide with an existing engine class name."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Invalid name. Must not collide with an existing buit-in type name."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Invalid name. Must not collide with an existing global constant name."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Invalid Path."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "File does not exist."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Not in resource path."
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Add AutoLoad"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Autoload '%s' already exists!"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Rename Autoload"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Toggle AutoLoad Globals"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Move Autoload"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Remove Autoload"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Enable"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Rearrange Autoloads"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp editor/editor_file_dialog.cpp
+#: scene/gui/file_dialog.cpp
+msgid "Path:"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Node Name:"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp editor/editor_profiler.cpp
+#: editor/project_manager.cpp editor/settings_config_dialog.cpp
+msgid "Name"
+msgstr ""
+
+#: editor/editor_autoload_settings.cpp
+msgid "Singleton"
+msgstr ""
+
+#: editor/editor_data.cpp
+msgid "Updating Scene"
+msgstr ""
+
+#: editor/editor_data.cpp
+msgid "Storing local changes.."
+msgstr ""
+
+#: editor/editor_data.cpp
+msgid "Updating scene.."
+msgstr ""
+
+#: editor/editor_data.cpp
+msgid "[empty]"
+msgstr ""
+
+#: editor/editor_data.cpp
+msgid "[unsaved]"
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp
+msgid "Please select a base directory first"
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp
+msgid "Choose a Directory"
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
+#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp
+msgid "Create Folder"
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
+#: editor/editor_plugin_settings.cpp editor/filesystem_dock.cpp
+#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
+#: scene/gui/file_dialog.cpp
+msgid "Name:"
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp editor/editor_file_dialog.cpp
+#: editor/filesystem_dock.cpp scene/gui/file_dialog.cpp
+msgid "Could not create folder."
+msgstr ""
+
+#: editor/editor_dir_dialog.cpp
+msgid "Choose"
+msgstr ""
+
+#: editor/editor_export.cpp
+msgid "Storing File:"
+msgstr ""
+
+#: editor/editor_export.cpp
+msgid "Packing"
+msgstr ""
+
+#: editor/editor_export.cpp platform/javascript/export/export.cpp
+msgid "Template file not found:"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "File Exists, Overwrite?"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Select Current Folder"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
+msgid "Copy Path"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
+msgid "Show In File Manager"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp editor/filesystem_dock.cpp
+msgid "New Folder.."
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Refresh"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "All Recognized"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "All Files (*)"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Open a File"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Open File(s)"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Open a Directory"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Open a File or Directory"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp editor/editor_node.cpp
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/script_editor_plugin.cpp scene/gui/file_dialog.cpp
+msgid "Save"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Save a File"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Go Back"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Go Forward"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Go Up"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Toggle Hidden Files"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Toggle Favorite"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Toggle Mode"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Focus Path"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Move Favorite Up"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Move Favorite Down"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Go to parent folder"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Directories & Files:"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp
+msgid "Preview:"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp editor/script_editor_debugger.cpp
+#: scene/gui/file_dialog.cpp
+msgid "File:"
+msgstr ""
+
+#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
+msgid "Must use a valid extension."
+msgstr ""
+
+#: editor/editor_file_system.cpp
+msgid "ScanSources"
+msgstr ""
+
+#: editor/editor_file_system.cpp
+msgid "(Re)Importing Assets"
+msgstr ""
+
+#: editor/editor_help.cpp editor/editor_node.cpp
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Search Help"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Class List:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Search Classes"
+msgstr ""
+
+#: editor/editor_help.cpp editor/plugins/spatial_editor_plugin.cpp
+msgid "Top"
+msgstr ""
+
+#: editor/editor_help.cpp editor/property_editor.cpp
+msgid "Class:"
+msgstr ""
+
+#: editor/editor_help.cpp editor/scene_tree_editor.cpp
+msgid "Inherits:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Inherited by:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Brief Description:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Members"
+msgstr ""
+
+#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
+msgid "Members:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Public Methods"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Public Methods:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "GUI Theme Items"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "GUI Theme Items:"
+msgstr ""
+
+#: editor/editor_help.cpp modules/visual_script/visual_script_editor.cpp
+msgid "Signals:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Enumerations"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Enumerations:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "enum "
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Constants"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Constants:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Description"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Online Tutorials:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid ""
+"There are currently no tutorials for this class, you can [color=$color][url="
+"$url]contribute one[/url][/color] or [color=$color][url=$url2]request one[/"
+"url][/color]."
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Properties"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Property Description:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid ""
+"There is currently no description for this property. Please help us by "
+"[color=$color][url=$url]contributing one[/url][/color]!"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Methods"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Method Description:"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid ""
+"There is currently no description for this method. Please help us by [color="
+"$color][url=$url]contributing one[/url][/color]!"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Search Text"
+msgstr ""
+
+#: editor/editor_help.cpp
+msgid "Find"
+msgstr ""
+
+#: editor/editor_log.cpp
+msgid "Output:"
+msgstr ""
+
+#: editor/editor_log.cpp editor/plugins/animation_tree_editor_plugin.cpp
+#: editor/property_editor.cpp editor/script_editor_debugger.cpp
+#: modules/gdnative/gdnative_library_editor_plugin.cpp scene/gui/line_edit.cpp
+#: scene/gui/text_edit.cpp
+msgid "Clear"
+msgstr ""
+
+#: editor/editor_log.cpp
+msgid "Clear Output"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
+msgid "Error saving resource!"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
+msgid "Save Resource As.."
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/spatial_editor_plugin.cpp
+#: editor/scene_tree_dock.cpp
+msgid "I see.."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Can't open file for writing:"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Requested file format unknown:"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error while saving."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Can't open '%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error while parsing '%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Unexpected end of file '%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Missing '%s' or its dependencies."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error while loading '%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Saving Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Analyzing"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Creating Thumbnail"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "This operation can't be done without a tree root."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Couldn't save scene. Likely dependencies (instances or inheritance) couldn't "
+"be satisfied."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Failed to load resource."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Can't load MeshLibrary for merging!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error saving MeshLibrary!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Can't load TileSet for merging!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error saving TileSet!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Error trying to save layout!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Default editor layout overridden."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Layout name not found!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Restored default layout to base settings."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This resource belongs to a scene that was imported, so it's not editable.\n"
+"Please read the documentation relevant to importing scenes to better "
+"understand this workflow."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This resource belongs to a scene that was instanced or inherited.\n"
+"Changes to it will not be kept when saving the current scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This resource was imported, so it's not editable. Change its settings in the "
+"import panel and then re-import."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This scene was imported, so changes to it will not be kept.\n"
+"Instancing it or inheriting will allow making changes to it.\n"
+"Please read the documentation relevant to importing scenes to better "
+"understand this workflow."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This is a remote object so changes to it will not be kept.\n"
+"Please read the documentation relevant to debugging to better understand "
+"this workflow."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Expand all properties"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Collapse all properties"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Copy Params"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Paste Params"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "Paste Resource"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Copy Resource"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Make Built-In"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Make Sub-Resources Unique"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open in Help"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "There is no defined scene to run."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"No main scene has ever been defined, select one?\n"
+"You can change it later in \"Project Settings\" under the 'application' "
+"category."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Selected scene '%s' does not exist, select a valid one?\n"
+"You can change it later in \"Project Settings\" under the 'application' "
+"category."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Selected scene '%s' is not a scene file, select a valid one?\n"
+"You can change it later in \"Project Settings\" under the 'application' "
+"category."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Current scene was never saved, please save it prior to running."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Could not start subprocess!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open Base Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Quick Open Scene.."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Quick Open Script.."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save & Close"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save changes to '%s' before closing?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save Scene As.."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "No"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Yes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "This scene has never been saved. Save before running?"
+msgstr ""
+
+#: editor/editor_node.cpp editor/scene_tree_dock.cpp
+msgid "This operation can't be done without a scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Export Mesh Library"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "This operation can't be done without a root node."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Export Tile Set"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "This operation can't be done without a selected node."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Current scene not saved. Open anyway?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Can't reload a scene that was never saved."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Revert"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "This action cannot be undone. Revert anyway?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Quick Run Scene.."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Quit"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Exit the editor?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open Project Manager?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save & Quit"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save changes to the following scene(s) before quitting?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save changes the following scene(s) before opening Project Manager?"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"This option is deprecated. Situations where refresh must be forced are now "
+"considered a bug. Please report."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Pick a Main Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Unable to enable addon plugin at: '%s' parsing of config failed."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Unable to find script field for addon plugin at: 'res://addons/%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Unable to load addon script from path: '%s'."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Unable to load addon script from path: '%s' Base type is not EditorPlugin."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Unable to load addon script from path: '%s' Script is not in tool mode."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Scene '%s' was automatically imported, so it can't be modified.\n"
+"To make changes to it, a new inherited scene can be created."
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "Ugh"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Error loading scene, it must be inside the project path. Use 'Import' to "
+"open the scene, then save it inside the project path."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Scene '%s' has broken dependencies:"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Clear Recent Scenes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save Layout"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Delete Layout"
+msgstr ""
+
+#: editor/editor_node.cpp editor/import_dock.cpp
+#: editor/script_create_dialog.cpp
+msgid "Default"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Switch Scene Tab"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "%d more files or folders"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "%d more folders"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "%d more files"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Dock Position"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Distraction Free Mode"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Toggle distraction-free mode."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Add a new scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Go to previously opened scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Next tab"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Previous tab"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Filter Files.."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Operations with scene files."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "New Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "New Inherited Scene.."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open Scene.."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save all Scenes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Close Scene"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
+msgid "Open Recent"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Convert To.."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "MeshLibrary.."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "TileSet.."
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
+#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
+msgid "Undo"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_text_editor.cpp
+#: scene/gui/line_edit.cpp
+msgid "Redo"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Revert Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Miscellaneous project or scene-wide tools."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Project"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Project Settings"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Run Script"
+msgstr ""
+
+#: editor/editor_node.cpp editor/project_export.cpp
+msgid "Export"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Tools"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Quit to Project List"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
+msgid "Debug"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Deploy with Remote Debug"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"When exporting or deploying, the resulting executable will attempt to "
+"connect to the IP of this computer in order to be debugged."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Small Deploy with Network FS"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"When this option is enabled, export or deploy will produce a minimal "
+"executable.\n"
+"The filesystem will be provided from the project by the editor over the "
+"network.\n"
+"On Android, deploy will use the USB cable for faster performance. This "
+"option speeds up testing for games with a large footprint."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Visible Collision Shapes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Collision shapes and raycast nodes (for 2D and 3D) will be visible on the "
+"running game if this option is turned on."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Visible Navigation"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"Navigation meshes and polygons will be visible on the running game if this "
+"option is turned on."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Sync Scene Changes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"When this option is turned on, any changes made to the scene in the editor "
+"will be replicated in the running game.\n"
+"When used remotely on a device, this is more efficient with network "
+"filesystem."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Sync Script Changes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid ""
+"When this option is turned on, any script that is saved will be reloaded on "
+"the running game.\n"
+"When used remotely on a device, this is more efficient with network "
+"filesystem."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Editor"
+msgstr ""
+
+#: editor/editor_node.cpp editor/settings_config_dialog.cpp
+msgid "Editor Settings"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Editor Layout"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Toggle Fullscreen"
+msgstr ""
+
+#: editor/editor_node.cpp editor/project_export.cpp
+msgid "Manage Export Templates"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Help"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
+msgid "Classes"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
+#: editor/plugins/script_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp
+#: editor/plugins/shader_editor_plugin.cpp editor/project_settings_editor.cpp
+msgid "Search"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
+msgid "Online Docs"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Q&A"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Issue Tracker"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
+msgid "Community"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "About"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play the project."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Pause the scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Pause Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Stop the scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Stop"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play the edited scene."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play custom scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Play Custom Scene"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Spins when the editor window repaints!"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Update Always"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Update Changes"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Disable Update Spinner"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Inspector"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Create a new resource in memory and edit it."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Load an existing resource from disk and edit it."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Save the currently edited resource."
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
+msgid "Save As.."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Go to the previous edited object in history."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Go to the next edited object in history."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "History of recently edited objects."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Object properties."
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Changes may be lost!"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/asset_library_editor_plugin.cpp
+#: editor/project_manager.cpp
+msgid "Import"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Node"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "FileSystem"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Output"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Don't Save"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Import Templates From ZIP File"
+msgstr ""
+
+#: editor/editor_node.cpp editor/project_export.cpp
+msgid "Export Project"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Export Library"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Merge With Existing"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Password:"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open & Run a Script"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "New Inherited"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Load Errors"
+msgstr ""
+
+#: editor/editor_node.cpp editor/plugins/tile_map_editor_plugin.cpp
+msgid "Select"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open 2D Editor"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open 3D Editor"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open Script Editor"
+msgstr ""
+
+#: editor/editor_node.cpp editor/project_manager.cpp
+msgid "Open Asset Library"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open the next Editor"
+msgstr ""
+
+#: editor/editor_node.cpp
+msgid "Open the previous Editor"
+msgstr ""
+
+#: editor/editor_plugin.cpp
+msgid "Creating Mesh Previews"
+msgstr ""
+
+#: editor/editor_plugin.cpp
+msgid "Thumbnail.."
+msgstr ""
+
+#: editor/editor_plugin_settings.cpp
+msgid "Installed Plugins:"
+msgstr ""
+
+#: editor/editor_plugin_settings.cpp
+msgid "Update"
+msgstr ""
+
+#: editor/editor_plugin_settings.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Version:"
+msgstr ""
+
+#: editor/editor_plugin_settings.cpp
+msgid "Author:"
+msgstr ""
+
+#: editor/editor_plugin_settings.cpp
+msgid "Status:"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Stop Profiling"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Start Profiling"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Measure:"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Frame Time (sec)"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Average Time (sec)"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Frame %"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Physics Frame %"
+msgstr ""
+
+#: editor/editor_profiler.cpp editor/script_editor_debugger.cpp
+msgid "Time:"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Inclusive"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Self"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Frame #:"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Time"
+msgstr ""
+
+#: editor/editor_profiler.cpp
+msgid "Calls"
+msgstr ""
+
+#: editor/editor_run_native.cpp
+msgid "Select device from the list"
+msgstr ""
+
+#: editor/editor_run_native.cpp
+msgid ""
+"No runnable export preset found for this platform.\n"
+"Please add a runnable preset in the export menu."
+msgstr ""
+
+#: editor/editor_run_script.cpp
+msgid "Write your logic in the _run() method."
+msgstr ""
+
+#: editor/editor_run_script.cpp
+msgid "There is an edited scene already."
+msgstr ""
+
+#: editor/editor_run_script.cpp
+msgid "Couldn't instance script:"
+msgstr ""
+
+#: editor/editor_run_script.cpp
+msgid "Did you forget the 'tool' keyword?"
+msgstr ""
+
+#: editor/editor_run_script.cpp
+msgid "Couldn't run script:"
+msgstr ""
+
+#: editor/editor_run_script.cpp
+msgid "Did you forget the '_run' method?"
+msgstr ""
+
+#: editor/editor_settings.cpp
+msgid "Default (Same as Editor)"
+msgstr ""
+
+#: editor/editor_sub_scene.cpp
+msgid "Select Node(s) to Import"
+msgstr ""
+
+#: editor/editor_sub_scene.cpp
+msgid "Scene Path:"
+msgstr ""
+
+#: editor/editor_sub_scene.cpp
+msgid "Import From Node:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Re-Download"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Uninstall"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "(Installed)"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Download"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "(Missing)"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "(Current)"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Retrieving mirrors, please wait.."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Remove template version '%s'?"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Can't open export templates zip."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Invalid version.txt format inside templates."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "No version.txt found inside templates."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Error creating path for templates:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Extracting Export Templates"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Importing:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid ""
+"No download links found for this version. Direct download is only available "
+"for official releases."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Can't resolve."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Can't connect."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "No response."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Request Failed."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Redirect Loop."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Failed:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Download Complete."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Error requesting url: "
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Connecting to Mirror.."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Disconnected"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Resolving"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Can't Resolve"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Connecting.."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Can't Connect"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Connected"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Requesting.."
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Downloading"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Connection Error"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "SSL Handshake Error"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Current Version:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Installed Versions:"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Install From File"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Remove Template"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Select template file"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Export Template Manager"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Download Templates"
+msgstr ""
+
+#: editor/export_template_manager.cpp
+msgid "Select mirror from list: "
+msgstr ""
+
+#: editor/file_type_cache.cpp
+msgid "Can't open file_type_cache.cch for writing, not saving file type cache!"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Cannot navigate to '%s' as it has not been found in the file system!"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "View items as a grid of thumbnails"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "View items as a list"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Status: Import of file failed. Please fix file and reimport manually."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Cannot move/rename resources root."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Cannot move a folder into itself."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Error moving:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Error duplicating:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Unable to update dependencies:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "No name provided"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Provided name contains invalid characters"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "No name provided."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Name contains invalid characters."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "A file or folder with this name already exists."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Renaming file:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Renaming folder:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Duplicating file:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Duplicating folder:"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Expand all"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Collapse all"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Rename.."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Move To.."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Open Scene(s)"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Instance"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Edit Dependencies.."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "View Owners.."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Duplicate.."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Previous Directory"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Next Directory"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Re-Scan Filesystem"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Toggle folder status as Favorite"
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Instance the selected scene(s) as child of the selected node."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid ""
+"Scanning Files,\n"
+"Please Wait.."
+msgstr ""
+
+#: editor/filesystem_dock.cpp
+msgid "Move"
+msgstr ""
+
+#: editor/filesystem_dock.cpp editor/plugins/animation_tree_editor_plugin.cpp
+#: editor/project_manager.cpp
+msgid "Rename"
+msgstr ""
+
+#: editor/groups_editor.cpp
+msgid "Add to Group"
+msgstr ""
+
+#: editor/groups_editor.cpp
+msgid "Remove from Group"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import as Single Scene"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Animations"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Materials"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Objects"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Objects+Materials"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Objects+Animations"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Materials+Animations"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import with Separate Objects+Materials+Animations"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import as Multiple Scenes"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Import as Multiple Scenes+Materials"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+#: editor/plugins/cube_grid_theme_editor_plugin.cpp
+msgid "Import Scene"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Importing Scene.."
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Generating Lightmaps"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Generating for Mesh: "
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Running Custom Script.."
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Couldn't load post-import script:"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Invalid/broken script for post-import (check console):"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Error running post-import script:"
+msgstr ""
+
+#: editor/import/resource_importer_scene.cpp
+msgid "Saving.."
+msgstr ""
+
+#: editor/import_dock.cpp
+msgid "Set as Default for '%s'"
+msgstr ""
+
+#: editor/import_dock.cpp
+msgid "Clear Default for '%s'"
+msgstr ""
+
+#: editor/import_dock.cpp
+msgid " Files"
+msgstr ""
+
+#: editor/import_dock.cpp
+msgid "Import As:"
+msgstr ""
+
+#: editor/import_dock.cpp editor/property_editor.cpp
+msgid "Preset.."
+msgstr ""
+
+#: editor/import_dock.cpp
+msgid "Reimport"
+msgstr ""
+
+#: editor/multi_node_edit.cpp
+msgid "MultiNode Set"
+msgstr ""
+
+#: editor/node_dock.cpp
+msgid "Groups"
+msgstr ""
+
+#: editor/node_dock.cpp
+msgid "Select a Node to edit Signals and Groups."
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Create Poly"
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+#: editor/plugins/collision_polygon_editor_plugin.cpp
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Edit Poly"
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+msgid "Insert Point"
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+#: editor/plugins/collision_polygon_editor_plugin.cpp
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Edit Poly (Remove Point)"
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+msgid "Remove Poly And Point"
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+msgid "Create a new polygon from scratch"
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+msgid ""
+"Edit existing polygon:\n"
+"LMB: Move Point.\n"
+"Ctrl+LMB: Split Segment.\n"
+"RMB: Erase Point."
+msgstr ""
+
+#: editor/plugins/abstract_polygon_2d_editor.cpp
+msgid "Delete points"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Toggle Autoplay"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "New Animation Name:"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "New Anim"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Change Animation Name:"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Delete Animation?"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Remove Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "ERROR: Invalid animation name!"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "ERROR: Animation name already exists!"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Rename Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Add Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Blend Next Changed"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Change Blend Time"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Load Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Duplicate Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "ERROR: No animation to copy!"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "ERROR: No animation resource on clipboard!"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Pasted Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Paste Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "ERROR: No animation to edit!"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Play selected animation backwards from current pos. (A)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Play selected animation backwards from end. (Shift+A)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Stop animation playback. (S)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Play selected animation from start. (Shift+D)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Play selected animation from current pos. (D)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Animation position (in seconds)."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Scale animation playback globally for the node."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Create new animation in player."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Load animation from disk."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Load an animation from disk."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Save the current animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Display list of animations in player."
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Autoplay on Load"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Edit Target Blend Times"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Animation Tools"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Copy Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Onion Skinning"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Enable Onion Skinning"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Directions"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Past"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Future"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Depth"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "1 step"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "2 steps"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "3 steps"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Differences Only"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Force White Modulate"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Include Gizmos (3D)"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Create New Animation"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Animation Name:"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp editor/property_editor.cpp
+#: editor/script_create_dialog.cpp
+msgid "Error!"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Blend Times:"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Next (Auto Queue):"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+msgid "Cross-Animation Blend Times"
+msgstr ""
+
+#: editor/plugins/animation_player_editor_plugin.cpp
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Animation"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "New name:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Edit Filters"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Scale:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Fade In (s):"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Fade Out (s):"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Mix"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Auto Restart:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Restart (s):"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Random Restart (s):"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Start!"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Amount:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend 0:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend 1:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "X-Fade Time (s):"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Current:"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Add Input"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Clear Auto-Advance"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Set Auto-Advance"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Delete Input"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Animation tree is valid."
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Animation tree is invalid."
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Animation Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "OneShot Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Mix Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend2 Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend3 Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Blend4 Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "TimeScale Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "TimeSeek Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Transition Node"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Import Animations.."
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Edit Node Filters"
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "Filters.."
+msgstr ""
+
+#: editor/plugins/animation_tree_editor_plugin.cpp
+msgid "AnimationTree"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Free"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Contents:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "View Files"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Can't resolve hostname:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Connection error, please try again."
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Can't connect to host:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "No response from host:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Request failed, return code:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Request failed, too many redirects"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Bad download hash, assuming file has been tampered with."
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Expected:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Got:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Failed sha256 hash check"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Asset Download Error:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Fetching:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Resolving.."
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Error making request"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Idle"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Retry"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Download Error"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Download for this asset is already in progress!"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "first"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "prev"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "next"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "last"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "All"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+#: editor/project_settings_editor.cpp
+msgid "Plugins"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Sort:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Reverse"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+#: editor/project_settings_editor.cpp
+msgid "Category:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Site:"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Support.."
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Official"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Testing"
+msgstr ""
+
+#: editor/plugins/asset_library_editor_plugin.cpp
+msgid "Assets ZIP File"
+msgstr ""
+
+#: editor/plugins/baked_lightmap_editor_plugin.cpp
+msgid ""
+"Can't determine a save path for lightmap images.\n"
+"Save your scene (for images to be saved in the same dir), or pick a save "
+"path from the BakedLightmap properties."
+msgstr ""
+
+#: editor/plugins/baked_lightmap_editor_plugin.cpp
+msgid ""
+"No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake "
+"Light' flag is on."
+msgstr ""
+
+#: editor/plugins/baked_lightmap_editor_plugin.cpp
+msgid "Failed creating lightmap images, make sure path is writable."
+msgstr ""
+
+#: editor/plugins/baked_lightmap_editor_plugin.cpp
+msgid "Bake Lightmaps"
+msgstr ""
+
+#: editor/plugins/camera_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Preview"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Configure Snap"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Grid Offset:"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Grid Step:"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Rotation Offset:"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Rotation Step:"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Move Pivot"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Move Action"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Move vertical guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Create new vertical guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Remove vertical guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Move horizontal guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Create new horizontal guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Remove horizontal guide"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Create new horizontal and vertical guides"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Edit IK Chain"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Edit CanvasItem"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Anchors only"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Change Anchors and Margins"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Change Anchors"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Paste Pose"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Select Mode"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Drag: Rotate"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Alt+Drag: Move"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Press 'v' to Change Pivot, 'Shift+v' to Drag Pivot (while moving)."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Alt+RMB: Depth list selection"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Move Mode"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Rotate Mode"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid ""
+"Show a list of all objects at the position clicked\n"
+"(same as Alt+RMB in select mode)."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Click to change object's rotation pivot."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Pan Mode"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Toggles snapping"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Use Snap"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snapping options"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to grid"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Use Rotation Snap"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Configure Snap..."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap Relative"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Use Pixel Snap"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Smart snapping"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to parent"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to node anchor"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to node sides"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to other nodes"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Snap to guides"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Lock the selected object in place (can't be moved)."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Unlock the selected object (can be moved)."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Makes sure the object's children are not selectable."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Restores the object's children's ability to be selected."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Make Bones"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Clear Bones"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Bones"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Make IK Chain"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Clear IK Chain"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Show Grid"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Helpers"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Rulers"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Guides"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Center Selection"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Frame Selection"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Layout"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Insert Keys"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Insert Key"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Insert Key (Existing Tracks)"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Copy Pose"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Clear Pose"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Drag pivot from mouse position"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Set pivot at mouse position"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Multiply grid step by 2"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Divide grid step by 2"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Add %s"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Adding %s..."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "Ok"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Cannot instantiate multiple nodes without root."
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "Create Node"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "Error instancing scene from %s"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Change default type"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid ""
+"Drag & drop + Shift : Add node as sibling\n"
+"Drag & drop + Alt : Change node type"
+msgstr ""
+
+#: editor/plugins/collision_polygon_editor_plugin.cpp
+msgid "Create Poly3D"
+msgstr ""
+
+#: editor/plugins/collision_shape_2d_editor_plugin.cpp
+msgid "Set Handle"
+msgstr ""
+
+#: editor/plugins/cube_grid_theme_editor_plugin.cpp
+msgid "Remove item %d?"
+msgstr ""
+
+#: editor/plugins/cube_grid_theme_editor_plugin.cpp
+#: editor/plugins/theme_editor_plugin.cpp
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Add Item"
+msgstr ""
+
+#: editor/plugins/cube_grid_theme_editor_plugin.cpp
+msgid "Remove Selected Item"
+msgstr ""
+
+#: editor/plugins/cube_grid_theme_editor_plugin.cpp
+msgid "Import from Scene"
+msgstr ""
+
+#: editor/plugins/cube_grid_theme_editor_plugin.cpp
+msgid "Update from Scene"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Flat0"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Flat1"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Ease in"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Ease out"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Smoothstep"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Modify Curve Point"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Modify Curve Tangent"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Load Curve Preset"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Add point"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Remove point"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Left linear"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Right linear"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Load preset"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Remove Curve Point"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Toggle Curve Linear Tangent"
+msgstr ""
+
+#: editor/plugins/curve_editor_plugin.cpp
+msgid "Hold Shift to edit tangents individually"
+msgstr ""
+
+#: editor/plugins/gi_probe_editor_plugin.cpp
+msgid "Bake GI Probe"
+msgstr ""
+
+#: editor/plugins/gradient_editor_plugin.cpp
+msgid "Add/Remove Color Ramp Point"
+msgstr ""
+
+#: editor/plugins/gradient_editor_plugin.cpp
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Modify Color Ramp"
+msgstr ""
+
+#: editor/plugins/item_list_editor_plugin.cpp
+msgid "Item %d"
+msgstr ""
+
+#: editor/plugins/item_list_editor_plugin.cpp
+msgid "Items"
+msgstr ""
+
+#: editor/plugins/item_list_editor_plugin.cpp
+msgid "Item List Editor"
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid ""
+"No OccluderPolygon2D resource on this node.\n"
+"Create and assign one?"
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Create Occluder Polygon"
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Create a new polygon from scratch."
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Edit existing polygon:"
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "LMB: Move Point."
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "Ctrl+LMB: Split Segment."
+msgstr ""
+
+#: editor/plugins/light_occluder_2d_editor_plugin.cpp
+msgid "RMB: Erase Point."
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh is empty!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Static Trimesh Body"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Static Convex Body"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "This doesn't work on scene root!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Trimesh Shape"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Convex Shape"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Navigation Mesh"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Contained Mesh is not of type ArrayMesh."
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "UV Unwrap failed, mesh may not be manifold?"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "No mesh to debug."
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Model has no UV in this layer"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "MeshInstance lacks a Mesh!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh has not surface to create outlines from!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Could not create outline!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Outline"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Trimesh Static Body"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Convex Static Body"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Trimesh Collision Sibling"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Convex Collision Sibling"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Outline Mesh.."
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "View UV1"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "View UV2"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Unwrap UV2 for Lightmap/AO"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Create Outline Mesh"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Outline Size:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "No mesh source specified (and no MultiMesh set in node)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "No mesh source specified (and MultiMesh contains no Mesh)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Mesh source is invalid (invalid path)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Mesh source is invalid (not a MeshInstance)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Mesh source is invalid (contains no Mesh resource)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "No surface source specified."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Surface source is invalid (invalid path)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Surface source is invalid (no geometry)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Surface source is invalid (no faces)."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Parent has no solid faces to populate."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Couldn't map area."
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Select a Source Mesh:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Select a Target Surface:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Populate Surface"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Populate MultiMesh"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Target Surface:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Source Mesh:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "X-Axis"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Y-Axis"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Z-Axis"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Mesh Up Axis:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Random Rotation:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Random Tilt:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Random Scale:"
+msgstr ""
+
+#: editor/plugins/multimesh_editor_plugin.cpp
+msgid "Populate"
+msgstr ""
+
+#: editor/plugins/navigation_mesh_editor_plugin.cpp
+msgid "Bake!"
+msgstr ""
+
+#: editor/plugins/navigation_mesh_editor_plugin.cpp
+msgid "Bake the navigation mesh."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_editor_plugin.cpp
+msgid "Clear the navigation mesh."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Setting up Configuration..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Calculating grid size..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Creating heightfield..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Marking walkable triangles..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Constructing compact heightfield..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Eroding walkable area..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Partitioning..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Creating contours..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Creating polymesh..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Converting to native navigation mesh..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Navigation Mesh Generator Setup:"
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Parsing Geometry..."
+msgstr ""
+
+#: editor/plugins/navigation_mesh_generator.cpp
+msgid "Done!"
+msgstr ""
+
+#: editor/plugins/navigation_polygon_editor_plugin.cpp
+msgid "Create Navigation Polygon"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Generating AABB"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Can only set point into a ParticlesMaterial process material"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Error loading image:"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "No pixels with transparency > 128 in image.."
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Generate Visibility Rect"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Load Emission Mask"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Clear Emission Mask"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Particles"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Generated Point Count:"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Generation Time (sec):"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Emission Mask"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Capture from Pixel"
+msgstr ""
+
+#: editor/plugins/particles_2d_editor_plugin.cpp
+msgid "Emission Colors"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Node does not contain geometry."
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Node does not contain geometry (faces)."
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "A processor material of type 'ParticlesMaterial' is required."
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Faces contain no area!"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "No faces!"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Generate AABB"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Create Emission Points From Mesh"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Create Emission Points From Node"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Create Emitter"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Emission Points:"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Surface Points"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Surface Points+Normal (Directed)"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Volume"
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Emission Source: "
+msgstr ""
+
+#: editor/plugins/particles_editor_plugin.cpp
+msgid "Generate Visibility AABB"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Remove Point from Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Remove Out-Control from Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Remove In-Control from Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Add Point to Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Move Point in Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Move In-Control in Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Move Out-Control in Curve"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Select Points"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Shift+Drag: Select Control Points"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Click: Add Point"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Right Click: Delete Point"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+msgid "Select Control Points (Shift+Drag)"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Add Point (in empty space)"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Split Segment (in curve)"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Delete Point"
+msgstr ""
+
+#: editor/plugins/path_2d_editor_plugin.cpp
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Close Curve"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Curve Point #"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Set Curve Point Position"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Set Curve In Position"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Set Curve Out Position"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Split Path"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Remove Path Point"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Remove Out-Control Point"
+msgstr ""
+
+#: editor/plugins/path_editor_plugin.cpp
+msgid "Remove In-Control Point"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Create UV Map"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Transform UV Map"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Polygon 2D UV Editor"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Move Point"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Ctrl: Rotate"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Shift: Move All"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Shift+Ctrl: Scale"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Move Polygon"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Rotate Polygon"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Scale Polygon"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp
+#: editor/plugins/shader_editor_plugin.cpp editor/project_manager.cpp
+#: editor/project_settings_editor.cpp editor/property_editor.cpp
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Edit"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Polygon->UV"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "UV->Polygon"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Clear UV"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Snap"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Enable Snap"
+msgstr ""
+
+#: editor/plugins/polygon_2d_editor_plugin.cpp
+msgid "Grid"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "ERROR: Couldn't load resource!"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "Add Resource"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "Rename Resource"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Delete Resource"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "Resource clipboard is empty!"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/scene_tree_dock.cpp editor/scene_tree_editor.cpp
+msgid "Open in Editor"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/scene_tree_editor.cpp
+msgid "Instance:"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/plugins/theme_editor_plugin.cpp editor/project_settings_editor.cpp
+#: editor/scene_tree_editor.cpp editor/script_editor_debugger.cpp
+msgid "Type:"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Load Resource"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp editor/property_editor.cpp
+#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
+msgid "Paste"
+msgstr ""
+
+#: editor/plugins/resource_preloader_editor_plugin.cpp
+msgid "ResourcePreloader"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Clear Recent Files"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Close and save changes?"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Error while saving theme"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Error saving"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Error importing theme"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Error importing"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Import Theme"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Save Theme As.."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid " Class Reference"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Sort"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp editor/scene_tree_dock.cpp
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Move Up"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp editor/scene_tree_dock.cpp
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Move Down"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Next script"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Previous script"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "File"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "New"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Save All"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Soft Reload Script"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Copy Script Path"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Show In File System"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "History Prev"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "History Next"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Reload Theme"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Save Theme"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Save Theme As"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Close Docs"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Close All"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Close Other Tabs"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp
+msgid "Run"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Toggle Scripts Panel"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp
+msgid "Find.."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+#: editor/plugins/script_text_editor.cpp
+msgid "Find Next"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
+msgid "Step Over"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
+msgid "Step Into"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
+msgid "Break"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/project_manager.cpp
+#: editor/script_editor_debugger.cpp
+msgid "Continue"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Keep Debugger Open"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Debug with external editor"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Open Godot online documentation"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Search the class hierarchy."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Search the reference documentation."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Go to previous edited document."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Go to next edited document."
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Discard"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Create Script"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid ""
+"The following files are newer on disk.\n"
+"What action should be taken?:"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Reload"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid "Resave"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp editor/script_editor_debugger.cpp
+msgid "Debugger"
+msgstr ""
+
+#: editor/plugins/script_editor_plugin.cpp
+msgid ""
+"Built-in scripts can only be edited when the scene they belong to is loaded"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Only resources from filesystem can be dropped."
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Pick Color"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Convert Case"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Uppercase"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Lowercase"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Capitalize"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp
+#: scene/gui/text_edit.cpp
+msgid "Cut"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+#: editor/plugins/sprite_frames_editor_plugin.cpp editor/property_editor.cpp
+#: scene/gui/line_edit.cpp scene/gui/text_edit.cpp
+msgid "Copy"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp scene/gui/line_edit.cpp
+#: scene/gui/text_edit.cpp
+msgid "Select All"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Delete Line"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Indent Left"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Indent Right"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Toggle Comment"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Fold/Unfold Line"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Fold All Lines"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Unfold All Lines"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Clone Down"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Complete Symbol"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Trim Trailing Whitespace"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Convert Indent To Spaces"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Convert Indent To Tabs"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Auto Indent"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Toggle Breakpoint"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Remove All Breakpoints"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Goto Next Breakpoint"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Goto Previous Breakpoint"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Convert To Uppercase"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Convert To Lowercase"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Find Previous"
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Replace.."
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Goto Function.."
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Goto Line.."
+msgstr ""
+
+#: editor/plugins/script_text_editor.cpp
+msgid "Contextual Help"
+msgstr ""
+
+#: editor/plugins/shader_editor_plugin.cpp
+msgid "Shader"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Scalar Constant"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Vec Constant"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change RGB Constant"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Scalar Operator"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Vec Operator"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Vec Scalar Operator"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change RGB Operator"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Toggle Rot Only"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Scalar Function"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Vec Function"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Scalar Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Vec Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change RGB Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Default Value"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change XForm Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Texture Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Cubemap Uniform"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Comment"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Add/Remove to Color Ramp"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Add/Remove to Curve Map"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Modify Curve Map"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Change Input Name"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Connect Graph Nodes"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Disconnect Graph Nodes"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Remove Shader Graph Node"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Move Shader Graph Node"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Duplicate Graph Node(s)"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Delete Shader Graph Node(s)"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Error: Cyclic Connection Link"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Error: Missing Input Connections"
+msgstr ""
+
+#: editor/plugins/shader_graph_editor_plugin.cpp
+msgid "Add Shader Graph Node"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Orthogonal"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Perspective"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Transform Aborted."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "X-Axis Transform."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Y-Axis Transform."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Z-Axis Transform."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Plane Transform."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Scaling: "
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Translating: "
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rotating %s degrees."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Keying is disabled (no key inserted)."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Animation Key Inserted."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Objects Drawn"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Material Changes"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Shader Changes"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Surface Changes"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Draw Calls"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Vertices"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "FPS"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Top View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Bottom View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Bottom"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Left View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Left"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Right View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Right"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Front View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Front"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rear View."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rear"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Align with view"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "OK :("
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "No parent to instance a child at."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp editor/scene_tree_dock.cpp
+msgid "This operation requires a single selected node."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Display Normal"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Display Wireframe"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Display Overdraw"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Display Unshaded"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Environment"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Gizmos"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Information"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View FPS"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Half Resolution"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Audio Listener"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Doppler Enable"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Left"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Right"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Forward"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Backwards"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Up"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Down"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Freelook Speed Modifier"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "XForm Dialog"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Select Mode (Q)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid ""
+"Drag: Rotate\n"
+"Alt+Drag: Move\n"
+"Alt+RMB: Depth list selection"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Move Mode (W)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rotate Mode (E)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Scale Mode (R)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Local Coords"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Local Space Mode (%s)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Snap Mode (%s)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Bottom View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Top View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rear View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Front View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Left View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Right View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Switch Perspective/Orthogonal view"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Insert Animation Key"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Focus Origin"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Focus Selection"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Align Selection With View"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Tool Select"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Tool Move"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Tool Rotate"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Tool Scale"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Toggle Freelook"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Transform"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Configure Snap.."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Transform Dialog.."
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "1 Viewport"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "2 Viewports"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "2 Viewports (Alt)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "3 Viewports"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "3 Viewports (Alt)"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "4 Viewports"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Origin"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Grid"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Settings"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Skeleton Gizmo visibility"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Snap Settings"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Translate Snap:"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rotate Snap (deg.):"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Scale Snap (%):"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Viewport Settings"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Perspective FOV (deg.):"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Z-Near:"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "View Z-Far:"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Transform Change"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Translate:"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Rotate (deg.):"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Scale (ratio):"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Transform Type"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Pre"
+msgstr ""
+
+#: editor/plugins/spatial_editor_plugin.cpp
+msgid "Post"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "ERROR: Couldn't load frame resource!"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Add Frame"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Resource clipboard is empty or not a texture!"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Paste Frame"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Add Empty"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Change Animation Loop"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Change Animation FPS"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "(empty)"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Animations"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Speed (FPS):"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Loop"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Animation Frames"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Insert Empty (Before)"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Insert Empty (After)"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Move (Before)"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "Move (After)"
+msgstr ""
+
+#: editor/plugins/sprite_frames_editor_plugin.cpp
+msgid "SpriteFrames"
+msgstr ""
+
+#: editor/plugins/style_box_editor_plugin.cpp
+msgid "StyleBox Preview:"
+msgstr ""
+
+#: editor/plugins/style_box_editor_plugin.cpp
+msgid "StyleBox"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Set Region Rect"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Snap Mode:"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "<None>"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Pixel Snap"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Grid Snap"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Auto Slice"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Offset:"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Step:"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Separation:"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Texture Region"
+msgstr ""
+
+#: editor/plugins/texture_region_editor_plugin.cpp
+msgid "Texture Region Editor"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Can't save theme to file:"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Add All Items"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Add All"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Remove Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Remove All Items"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Remove All"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Edit theme.."
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Theme editing menu."
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Add Class Items"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Remove Class Items"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Create Empty Template"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Create Empty Editor Template"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Create From Current Editor Theme"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "CheckBox Radio1"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "CheckBox Radio2"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Check Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Has"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Many"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp editor/project_export.cpp
+msgid "Options"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Have,Many,Several,Options!"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Tab 1"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Tab 2"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Tab 3"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Data Type:"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Icon"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Style"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Font"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Color"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Theme"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Erase Selection"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Paint TileMap"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Line Draw"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Rectangle Paint"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Bucket Fill"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Erase TileMap"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Erase selection"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Find tile"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Transpose"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Mirror X"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Mirror Y"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Paint Tile"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Pick Tile"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Rotate 0 degrees"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Rotate 90 degrees"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Rotate 180 degrees"
+msgstr ""
+
+#: editor/plugins/tile_map_editor_plugin.cpp
+msgid "Rotate 270 degrees"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Could not find tile:"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Item name or ID:"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Create from scene?"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Merge from scene?"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Tile Set"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Create from Scene"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Merge from Scene"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp editor/script_editor_debugger.cpp
+msgid "Error"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Autotiles"
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid ""
+"Select sub-tile to use as icon, this will be also used on invalid autotile "
+"bindings."
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid ""
+"LMB: set bit on.\n"
+"RMB: set bit off."
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Select current edited sub-tile."
+msgstr ""
+
+#: editor/plugins/tile_set_editor_plugin.cpp
+msgid "Select sub-tile to change its priority."
+msgstr ""
+
+#: editor/progress_dialog.cpp scene/gui/dialogs.cpp
+msgid "Cancel"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Runnable"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Delete patch '%s' from list?"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Delete preset '%s'?"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export templates for this platform are missing/corrupted: "
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Presets"
+msgstr ""
+
+#: editor/project_export.cpp editor/project_settings_editor.cpp
+msgid "Add.."
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Resources"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export all resources in the project"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export selected scenes (and dependencies)"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export selected resources (and dependencies)"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export Mode:"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Resources to export:"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid ""
+"Filters to export non-resource files (comma separated, e.g: *.json, *.txt)"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid ""
+"Filters to exclude files from project (comma separated, e.g: *.json, *.txt)"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Patches"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Make Patch"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Features"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Custom (comma-separated):"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Feature List:"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export PCK/Zip"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export templates for this platform are missing:"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export templates for this platform are missing/corrupted:"
+msgstr ""
+
+#: editor/project_export.cpp
+msgid "Export With Debug"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "The path does not exist."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Please choose a 'project.godot' file."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Please choose an empty folder."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Imported Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Couldn't create folder."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "There is already a folder in this path with the specified name."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "It would be a good idea to name your project."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Invalid project path (changed anything?)."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid ""
+"Couldn't load project.godot in project path (error %d). It may be missing or "
+"corrupted."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Couldn't edit project.godot in project path."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Couldn't create project.godot in project path."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "The following files failed extraction from package:"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Rename Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "New Game Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Import Existing Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Import & Edit"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Create New Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Create & Edit"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Install Project:"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Install & Edit"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Project Name:"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Create folder"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Project Path:"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Browse"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Unnamed Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Can't open project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Are you sure to open more than one project?"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid ""
+"Can't run project: no main scene defined.\n"
+"Please edit the project and set the main scene in \"Project Settings\" under "
+"the \"Application\" category."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid ""
+"Can't run project: Assets need to be imported.\n"
+"Please edit the project to trigger the initial import."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Are you sure to run more than one project?"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Remove project from the list? (Folder contents will not be modified)"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid ""
+"Language changed.\n"
+"The UI will update next time the editor or project manager starts."
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid ""
+"You are about the scan %s folders for existing Godot projects. Do you "
+"confirm?"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Project Manager"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Project List"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Scan"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Select a Folder to Scan"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "New Project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Templates"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Exit"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Restart Now"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid "Can't run project"
+msgstr ""
+
+#: editor/project_manager.cpp
+msgid ""
+"You don't currently have any projects.\n"
+"Would you like to explore the official example projects in the Asset Library?"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Key "
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Joy Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Joy Axis"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Mouse Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Action '%s' already exists!"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Rename Input Action Event"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Input Action Event"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
+msgid "Shift+"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
+msgid "Alt+"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
+msgid "Control+"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
+msgid "Press a Key.."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Mouse Button Index:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Left Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Right Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Middle Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Wheel Up Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Wheel Down Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Button 6"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Button 7"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Button 8"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Button 9"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Joypad Axis Index:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Axis"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Joypad Button Index:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Erase Input Action"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Erase Input Action Event"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Event"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Device"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Button"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Left Button."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Right Button."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Middle Button."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Wheel Up."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Wheel Down."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Global Property"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Select a setting item first!"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "No property '%s' exists."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Setting '%s' is internal, and it can't be deleted."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Delete Item"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Already existing"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Input Action"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Error saving settings."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Settings saved OK."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Override for Feature"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Translation"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Remove Translation"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Add Remapped Path"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Resource Remap Add Remap"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Change Resource Remap Language"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Remove Resource Remap"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Remove Resource Remap Option"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Changed Locale Filter"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Changed Locale Filter Mode"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Project Settings (project.godot)"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
+msgid "General"
+msgstr ""
+
+#: editor/project_settings_editor.cpp editor/property_editor.cpp
+msgid "Property:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Override For.."
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Input Map"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Action:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Device:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Index:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Localization"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Translations"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Translations:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Remaps"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Resources:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Remaps by Locale:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Locale"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Locales Filter"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Show all locales"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Show only selected locales"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Filter mode:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "Locales:"
+msgstr ""
+
+#: editor/project_settings_editor.cpp
+msgid "AutoLoad"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Pick a Viewport"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Ease In"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Ease Out"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Zero"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Easing In-Out"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Easing Out-In"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "File.."
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Dir.."
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Assign"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Select Node"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "New Script"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "New %s"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Make Unique"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Show in File System"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Convert To %s"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Error loading file: Not a resource!"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Selected node is not a Viewport!"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Pick a Node"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Bit %d, val %d."
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "On"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "[Empty]"
+msgstr ""
+
+#: editor/property_editor.cpp modules/visual_script/visual_script_editor.cpp
+msgid "Set"
+msgstr ""
+
+#: editor/property_editor.cpp
+msgid "Properties:"
+msgstr ""
+
+#: editor/property_selector.cpp
+msgid "Select Property"
+msgstr ""
+
+#: editor/property_selector.cpp
+msgid "Select Virtual Method"
+msgstr ""
+
+#: editor/property_selector.cpp
+msgid "Select Method"
+msgstr ""
+
+#: editor/pvrtc_compress.cpp
+msgid "Could not execute PVRTC tool:"
+msgstr ""
+
+#: editor/pvrtc_compress.cpp
+msgid "Can't load back converted image using PVRTC tool:"
+msgstr ""
+
+#: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp
+msgid "Reparent Node"
+msgstr ""
+
+#: editor/reparent_dialog.cpp
+msgid "Reparent Location (Select new Parent):"
+msgstr ""
+
+#: editor/reparent_dialog.cpp
+msgid "Keep Global Transform"
+msgstr ""
+
+#: editor/reparent_dialog.cpp editor/scene_tree_dock.cpp
+msgid "Reparent"
+msgstr ""
+
+#: editor/run_settings_dialog.cpp
+msgid "Run Mode:"
+msgstr ""
+
+#: editor/run_settings_dialog.cpp
+msgid "Current Scene"
+msgstr ""
+
+#: editor/run_settings_dialog.cpp
+msgid "Main Scene"
+msgstr ""
+
+#: editor/run_settings_dialog.cpp
+msgid "Main Scene Arguments:"
+msgstr ""
+
+#: editor/run_settings_dialog.cpp
+msgid "Scene Run Settings"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp editor/script_create_dialog.cpp
+#: scene/gui/dialogs.cpp
+msgid "OK"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "No parent to instance the scenes at."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Error loading scene from %s"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid ""
+"Cannot instance the scene '%s' because the current scene exists within one "
+"of its nodes."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Instance Scene(s)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "This operation can't be done on the tree root."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Move Node In Parent"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Move Nodes In Parent"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Duplicate Node(s)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Delete Node(s)?"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Can not perform with the root node."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "This operation can't be done on instanced scenes."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Save New Scene As.."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Editable Children"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Load As Placeholder"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Discard Instancing"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Makes Sense!"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Can't operate on nodes from a foreign scene!"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Can't operate on nodes the current scene inherits from!"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Remove Node(s)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid ""
+"Couldn't save new scene. Likely dependencies (instances) couldn't be "
+"satisfied."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Error saving scene."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Error duplicating scene to save it."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Sub-Resources"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Clear Inheritance"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Delete Node(s)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Add Child Node"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Instance Child Scene"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Change Type"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Attach Script"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Clear Script"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Merge From Scene"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Save Branch as Scene"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Copy Node Path"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Delete (No Confirm)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Add/Create a New Node"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid ""
+"Instance a scene file as a Node. Creates an inherited scene if no root node "
+"exists."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Filter nodes"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Attach a new or existing script for the selected node."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Clear a script for the selected node."
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Remote"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Local"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Clear Inheritance? (No Undo!)"
+msgstr ""
+
+#: editor/scene_tree_dock.cpp
+msgid "Clear!"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Toggle Spatial Visible"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Toggle CanvasItem Visible"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Node configuration warning:"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid ""
+"Node has connection(s) and group(s)\n"
+"Click to show signals dock."
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid ""
+"Node has connections.\n"
+"Click to show signals dock."
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid ""
+"Node is in group(s).\n"
+"Click to show groups dock."
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Open script"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid ""
+"Node is locked.\n"
+"Click to unlock"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid ""
+"Children are not selectable.\n"
+"Click to make selectable"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Toggle Visibility"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Invalid node name, the following characters are not allowed:"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Rename Node"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Scene Tree (Nodes):"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Node Configuration Warning!"
+msgstr ""
+
+#: editor/scene_tree_editor.cpp
+msgid "Select a Node"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Error loading template '%s'"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Error - Could not create script in filesystem."
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Error loading script from %s"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "N/A"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Path is empty"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Path is not local"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Invalid base path"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Directory of the same name exists"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "File exists, will be reused"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Invalid extension"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Wrong extension chosen"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Invalid Path"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Invalid class name"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Invalid inherited parent name or path"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Script valid"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Allowed: a-z, A-Z, 0-9 and _"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Built-in script (into scene file)"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Create new script file"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Load existing script file"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Language"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Inherits"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Class Name"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Template"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Built-in Script"
+msgstr ""
+
+#: editor/script_create_dialog.cpp
+msgid "Attach Node Script"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Remote "
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Bytes:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Warning"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Error:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Source:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Function:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Pick one or more items from the list to display the graph."
+msgstr ""
+
+#: editor/script_editor_debugger.cpp modules/mono/editor/mono_bottom_panel.cpp
+msgid "Errors"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Child Process Connected"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Copy Error"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Inspect Previous Instance"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Inspect Next Instance"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Stack Frames"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Variable"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Errors:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Stack Trace (if applicable):"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Profiler"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Monitor"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Value"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Monitors"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "List of Video Memory Usage by Resource:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Total:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Video Mem"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Resource Path"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Type"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Format"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Usage"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Misc"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Clicked Control:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Clicked Control Type:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Live Edit Root:"
+msgstr ""
+
+#: editor/script_editor_debugger.cpp
+msgid "Set From Tree"
+msgstr ""
+
+#: editor/settings_config_dialog.cpp
+msgid "Shortcuts"
+msgstr ""
+
+#: editor/settings_config_dialog.cpp
+msgid "Binding"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Light Radius"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change AudioStreamPlayer3D Emission Angle"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Camera FOV"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Camera Size"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Sphere Shape Radius"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Box Shape Extents"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Capsule Shape Radius"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Capsule Shape Height"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Ray Shape Length"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Notifier Extents"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Particles AABB"
+msgstr ""
+
+#: editor/spatial_editor_gizmos.cpp
+msgid "Change Probe Extents"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Select the dynamic library for this entry"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Select dependencies of the library for this entry"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Remove current entry"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Double click to create a new entry"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Platform:"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Platform"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Dynamic Library"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "Add an architecture entry"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_editor_plugin.cpp
+msgid "GDNativeLibrary"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_singleton_editor.cpp
+msgid "Library"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_singleton_editor.cpp
+msgid "Status"
+msgstr ""
+
+#: modules/gdnative/gdnative_library_singleton_editor.cpp
+msgid "Libraries: "
+msgstr ""
+
+#: modules/gdnative/register_types.cpp
+msgid "GDNative"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+#: modules/visual_script/visual_script_builtin_funcs.cpp
+msgid "Invalid type argument to convert(), use TYPE_* constants."
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp modules/mono/glue/glue_header.h
+#: modules/visual_script/visual_script_builtin_funcs.cpp
+msgid "Not enough bytes for decoding bytes, or invalid format."
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "step argument is zero!"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Not a script with an instance"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Not based on a script"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Not based on a resource file"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Invalid instance dictionary format (missing @path)"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Invalid instance dictionary format (can't load script at @path)"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Invalid instance dictionary format (invalid script at @path)"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Invalid instance dictionary (invalid subclasses)"
+msgstr ""
+
+#: modules/gdscript/gdscript_functions.cpp
+msgid "Object can't provide a length."
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Next Plane"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Previous Plane"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Plane:"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Next Floor"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Previous Floor"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Floor:"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "GridMap Delete Selection"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "GridMap Duplicate Selection"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Grid Map"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Snap View"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Clip Disabled"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Clip Above"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Clip Below"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Edit X Axis"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Edit Y Axis"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Edit Z Axis"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Rotate X"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Rotate Y"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Rotate Z"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Back Rotate X"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Back Rotate Y"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Back Rotate Z"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Cursor Clear Rotation"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Create Area"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Create Exterior Connector"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Erase Area"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Clear Selection"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "GridMap Settings"
+msgstr ""
+
+#: modules/gridmap/grid_map_editor_plugin.cpp
+msgid "Pick Distance:"
+msgstr ""
+
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Generating solution..."
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Generating C# project..."
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Failed to create solution."
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Failed to save solution."
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Done"
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Failed to create C# project."
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Mono"
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "About C# support"
+msgstr ""
+
+#: modules/mono/editor/godotsharp_editor.cpp
+msgid "Create C# solution"
+msgstr ""
+
+#: modules/mono/editor/mono_bottom_panel.cpp
+msgid "Builds"
+msgstr ""
+
+#: modules/mono/editor/mono_bottom_panel.cpp
+msgid "Build Project"
+msgstr ""
+
+#: modules/mono/editor/mono_bottom_panel.cpp
+msgid "Warnings"
+msgstr ""
+
+#: modules/mono/mono_gd/gd_mono_utils.cpp
+msgid "End of inner exception stack trace"
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid ""
+"A node yielded without working memory, please read the docs on how to yield "
+"properly!"
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid ""
+"Node yielded, but did not return a function state in the first working "
+"memory."
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid ""
+"Return value must be assigned to first element of node working memory! Fix "
+"your node please."
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid "Node returned an invalid sequence output: "
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid "Found sequence bit but not the node in the stack, report bug!"
+msgstr ""
+
+#: modules/visual_script/visual_script.cpp
+msgid "Stack overflow with stack depth: "
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Signal Arguments"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Argument Type"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Argument name"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Set Variable Default Value"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Set Variable Type"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Functions:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Variables:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Name is not a valid identifier:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Name already in use by another func/var/signal:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Rename Function"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Rename Variable"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Rename Signal"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Function"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Variable"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Signal"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Expression"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Node"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Remove VisualScript Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Duplicate VisualScript Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold %s to drop a Getter. Hold Shift to drop a generic signature."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold Ctrl to drop a Getter. Hold Shift to drop a generic signature."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold %s to drop a simple reference to the node."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold Ctrl to drop a simple reference to the node."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold %s to drop a Variable Setter."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Hold Ctrl to drop a Variable Setter."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Preload Node"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Node(s) From Tree"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Getter Property"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Add Setter Property"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Base Type"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Move Node(s)"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Remove VisualScript Node"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Connect Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Condition"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Sequence"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Switch"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Iterator"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "While"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Return"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Call"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Get"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Script already has function '%s'"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Change Input Value"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Can't copy the function node."
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Clipboard is empty!"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Paste VisualScript Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Remove Function"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Edit Variable"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Remove Variable"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Edit Signal"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Remove Signal"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Editing Variable:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Editing Signal:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Base Type:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Available Nodes:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Select or create a function to edit graph"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Edit Signal Arguments:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Edit Variable:"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Delete Selected"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Find Node Type"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Copy Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Cut Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_editor.cpp
+msgid "Paste Nodes"
+msgstr ""
+
+#: modules/visual_script/visual_script_flow_control.cpp
+msgid "Input type not iterable: "
+msgstr ""
+
+#: modules/visual_script/visual_script_flow_control.cpp
+msgid "Iterator became invalid"
+msgstr ""
+
+#: modules/visual_script/visual_script_flow_control.cpp
+msgid "Iterator became invalid: "
+msgstr ""
+
+#: modules/visual_script/visual_script_func_nodes.cpp
+msgid "Invalid index property name."
+msgstr ""
+
+#: modules/visual_script/visual_script_func_nodes.cpp
+msgid "Base object is not a Node!"
+msgstr ""
+
+#: modules/visual_script/visual_script_func_nodes.cpp
+msgid "Path does not lead Node!"
+msgstr ""
+
+#: modules/visual_script/visual_script_func_nodes.cpp
+msgid "Invalid index property name '%s' in node %s."
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid ": Invalid argument of type: "
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid ": Invalid arguments: "
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid "VariableGet not found in script: "
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid "VariableSet not found in script: "
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid "Custom node has no _step() method, can't process graph."
+msgstr ""
+
+#: modules/visual_script/visual_script_nodes.cpp
+msgid ""
+"Invalid return value from _step(), must be integer (seq out), or string "
+"(error)."
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Run in Browser"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Run exported HTML in the system's default browser."
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Could not write file:"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Could not open template for export:"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Invalid export template:"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Could not read custom HTML shell:"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Could not read boot splash image file:"
+msgstr ""
+
+#: platform/javascript/export/export.cpp
+msgid "Using default boot splash image."
+msgstr ""
+
+#: scene/2d/animated_sprite.cpp
+msgid ""
+"A SpriteFrames resource must be created or set in the 'Frames' property in "
+"order for AnimatedSprite to display frames."
+msgstr ""
+
+#: scene/2d/canvas_modulate.cpp
+msgid ""
+"Only one visible CanvasModulate is allowed per scene (or set of instanced "
+"scenes). The first created one will work, while the rest will be ignored."
+msgstr ""
+
+#: scene/2d/collision_object_2d.cpp
+msgid ""
+"This node has no children shapes, so it can't interact with the space.\n"
+"Consider adding CollisionShape2D or CollisionPolygon2D children nodes to "
+"define its shape."
+msgstr ""
+
+#: scene/2d/collision_polygon_2d.cpp
+msgid ""
+"CollisionPolygon2D only serves to provide a collision shape to a "
+"CollisionObject2D derived node. Please only use it as a child of Area2D, "
+"StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."
+msgstr ""
+
+#: scene/2d/collision_polygon_2d.cpp
+msgid "An empty CollisionPolygon2D has no effect on collision."
+msgstr ""
+
+#: scene/2d/collision_shape_2d.cpp
+msgid ""
+"CollisionShape2D only serves to provide a collision shape to a "
+"CollisionObject2D derived node. Please only use it as a child of Area2D, "
+"StaticBody2D, RigidBody2D, KinematicBody2D, etc. to give them a shape."
+msgstr ""
+
+#: scene/2d/collision_shape_2d.cpp
+msgid ""
+"A shape must be provided for CollisionShape2D to function. Please create a "
+"shape resource for it!"
+msgstr ""
+
+#: scene/2d/light_2d.cpp
+msgid ""
+"A texture with the shape of the light must be supplied to the 'texture' "
+"property."
+msgstr ""
+
+#: scene/2d/light_occluder_2d.cpp
+msgid ""
+"An occluder polygon must be set (or drawn) for this occluder to take effect."
+msgstr ""
+
+#: scene/2d/light_occluder_2d.cpp
+msgid "The occluder polygon for this occluder is empty. Please draw a polygon!"
+msgstr ""
+
+#: scene/2d/navigation_polygon.cpp
+msgid ""
+"A NavigationPolygon resource must be set or created for this node to work. "
+"Please set a property or draw a polygon."
+msgstr ""
+
+#: scene/2d/navigation_polygon.cpp
+msgid ""
+"NavigationPolygonInstance must be a child or grandchild to a Navigation2D "
+"node. It only provides navigation data."
+msgstr ""
+
+#: scene/2d/parallax_layer.cpp
+msgid ""
+"ParallaxLayer node only works when set as child of a ParallaxBackground node."
+msgstr ""
+
+#: scene/2d/particles_2d.cpp scene/3d/particles.cpp
+msgid ""
+"A material to process the particles is not assigned, so no behavior is "
+"imprinted."
+msgstr ""
+
+#: scene/2d/path_2d.cpp
+msgid "PathFollow2D only works when set as a child of a Path2D node."
+msgstr ""
+
+#: scene/2d/physics_body_2d.cpp
+msgid ""
+"Size changes to RigidBody2D (in character or rigid modes) will be overridden "
+"by the physics engine when running.\n"
+"Change the size in children collision shapes instead."
+msgstr ""
+
+#: scene/2d/remote_transform_2d.cpp
+msgid "Path property must point to a valid Node2D node to work."
+msgstr ""
+
+#: scene/2d/visibility_notifier_2d.cpp
+msgid ""
+"VisibilityEnable2D works best when used with the edited scene root directly "
+"as parent."
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid "ARVRCamera must have an ARVROrigin node as its parent"
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid "ARVRController must have an ARVROrigin node as its parent"
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid ""
+"The controller id must not be 0 or this controller will not be bound to an "
+"actual controller"
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid "ARVRAnchor must have an ARVROrigin node as its parent"
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid ""
+"The anchor id must not be 0 or this anchor will not be bound to an actual "
+"anchor"
+msgstr ""
+
+#: scene/3d/arvr_nodes.cpp
+msgid "ARVROrigin requires an ARVRCamera child node"
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp
+msgid "%d%%"
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp
+msgid "(Time Left: %d:%02d s)"
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp
+msgid "Plotting Meshes: "
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp
+msgid "Plotting Lights:"
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp scene/3d/gi_probe.cpp
+msgid "Finishing Plot"
+msgstr ""
+
+#: scene/3d/baked_lightmap.cpp
+msgid "Lighting Meshes: "
+msgstr ""
+
+#: scene/3d/collision_object.cpp
+msgid ""
+"This node has no children shapes, so it can't interact with the space.\n"
+"Consider adding CollisionShape or CollisionPolygon children nodes to define "
+"its shape."
+msgstr ""
+
+#: scene/3d/collision_polygon.cpp
+msgid ""
+"CollisionPolygon only serves to provide a collision shape to a "
+"CollisionObject derived node. Please only use it as a child of Area, "
+"StaticBody, RigidBody, KinematicBody, etc. to give them a shape."
+msgstr ""
+
+#: scene/3d/collision_polygon.cpp
+msgid "An empty CollisionPolygon has no effect on collision."
+msgstr ""
+
+#: scene/3d/collision_shape.cpp
+msgid ""
+"CollisionShape only serves to provide a collision shape to a CollisionObject "
+"derived node. Please only use it as a child of Area, StaticBody, RigidBody, "
+"KinematicBody, etc. to give them a shape."
+msgstr ""
+
+#: scene/3d/collision_shape.cpp
+msgid ""
+"A shape must be provided for CollisionShape to function. Please create a "
+"shape resource for it!"
+msgstr ""
+
+#: scene/3d/gi_probe.cpp
+msgid "Plotting Meshes"
+msgstr ""
+
+#: scene/3d/navigation_mesh.cpp
+msgid "A NavigationMesh resource must be set or created for this node to work."
+msgstr ""
+
+#: scene/3d/navigation_mesh.cpp
+msgid ""
+"NavigationMeshInstance must be a child or grandchild to a Navigation node. "
+"It only provides navigation data."
+msgstr ""
+
+#: scene/3d/particles.cpp
+msgid ""
+"Nothing is visible because meshes have not been assigned to draw passes."
+msgstr ""
+
+#: scene/3d/physics_body.cpp
+msgid ""
+"Size changes to RigidBody (in character or rigid modes) will be overridden "
+"by the physics engine when running.\n"
+"Change the size in children collision shapes instead."
+msgstr ""
+
+#: scene/3d/remote_transform.cpp
+msgid "Path property must point to a valid Spatial node to work."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
+#: scene/3d/sprite_3d.cpp
+msgid ""
+"A SpriteFrames resource must be created or set in the 'Frames' property in "
+"order for AnimatedSprite3D to display frames."
+msgstr ""
+
+#: scene/3d/vehicle_body.cpp
+msgid ""
+"VehicleWheel serves to provide a wheel system to a VehicleBody. Please use "
+"it as a child of a VehicleBody."
+msgstr ""
+
+#: scene/gui/color_picker.cpp
+msgid "Raw Mode"
+msgstr ""
+
+#: scene/gui/color_picker.cpp
+msgid "Add current color as a preset"
+msgstr ""
+
+#: scene/gui/dialogs.cpp
+msgid "Alert!"
+msgstr ""
+
+#: scene/gui/dialogs.cpp
+msgid "Please Confirm..."
+msgstr ""
+
+#: scene/gui/file_dialog.cpp
+msgid "Select this Folder"
+msgstr ""
+
+#: scene/gui/popup.cpp
+msgid ""
+"Popups will hide by default unless you call popup() or any of the popup*() "
+"functions. Making them visible for editing is fine though, but they will "
+"hide upon running."
+msgstr ""
+
+#: scene/gui/scroll_container.cpp
+msgid ""
+"ScrollContainer is intended to work with a single child control.\n"
+"Use a container as child (VBox,HBox,etc), or a Control and set the custom "
+"minimum size manually."
+msgstr ""
+
+#: scene/gui/tree.cpp
+msgid "(Other)"
+msgstr ""
+
+#: scene/main/scene_tree.cpp
+msgid ""
+"Default Environment as specified in Project Settings (Rendering -> "
+"Environment -> Default Environment) could not be loaded."
+msgstr ""
+
+#: scene/main/viewport.cpp
+msgid ""
+"This viewport is not set as render target. If you intend for it to display "
+"its contents directly to the screen, make it a child of a Control so it can "
+"obtain a size. Otherwise, make it a RenderTarget and assign its internal "
+"texture to some node for display."
+msgstr ""
+
+#: scene/resources/dynamic_font.cpp
+msgid "Error initializing FreeType."
+msgstr ""
+
+#: scene/resources/dynamic_font.cpp
+msgid "Unknown font format."
+msgstr ""
+
+#: scene/resources/dynamic_font.cpp
+msgid "Error loading font."
+msgstr ""
+
+#: scene/resources/dynamic_font.cpp
+msgid "Invalid font size."
+msgstr ""
diff --git a/editor/translations/sv.po b/editor/translations/sv.po
index 64a268a7b..4a861d1b7 100644
--- a/editor/translations/sv.po
+++ b/editor/translations/sv.po
@@ -1568,6 +1568,10 @@ msgstr "Rensa"
msgid "Clear Output"
msgstr "Output:"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
#, fuzzy
msgid "Error saving resource!"
@@ -3989,6 +3993,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -4284,6 +4296,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -6032,6 +6048,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6547,7 +6571,9 @@ msgid "Mouse Button"
msgstr "Musknapp"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6694,10 +6720,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7816,6 +7838,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
#, fuzzy
msgid "Generating solution..."
@@ -8517,10 +8543,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/ta.po b/editor/translations/ta.po
index 4be5e8b83..e7269ffa0 100644
--- a/editor/translations/ta.po
+++ b/editor/translations/ta.po
@@ -1372,6 +1372,10 @@ msgstr ""
msgid "Clear Output"
msgstr ""
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3592,6 +3596,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3877,6 +3889,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5538,6 +5554,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6013,7 +6037,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6157,10 +6183,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7199,6 +7221,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7835,10 +7861,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/th.po b/editor/translations/th.po
index b31fba186..74e2270f2 100644
--- a/editor/translations/th.po
+++ b/editor/translations/th.po
@@ -9,7 +9,7 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-02-25 04:41+0000\n"
+"PO-Revision-Date: 2018-03-10 03:46+0000\n"
"Last-Translator: Poommetee Ketson <poommetee@protonmail.com>\n"
"Language-Team: Thai <https://hosted.weblate.org/projects/godot-engine/godot/"
"th/>\n"
@@ -1382,6 +1382,10 @@ msgstr "ลบ"
msgid "Clear Output"
msgstr "ลบข้อความ"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "บันทึกรีซอร์สผิดพลาด!"
@@ -3641,6 +3645,16 @@ msgid "Show Guides"
msgstr "แสดงเส้นนำ"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "แสดงจุดกำเนิด"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 มุมมอง"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "ให้สิ่งที่เลือกอยู่กลางจอ"
@@ -3930,6 +3944,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Mesh ไม่มีพื้นผิวให้สร้างเส้นขอบได้!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "สร้างเส้นรอบรูปไม่ได้!"
@@ -5596,6 +5614,16 @@ msgid "Checked Item"
msgstr "ไอเทมมีเครื่องหมาย"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "เพิ่มไอเทม"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "ไอเทมมีเครื่องหมาย"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "มี"
@@ -6082,8 +6110,10 @@ msgid "Mouse Button"
msgstr "ปุ่มเมาส์"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "ใช้ชื่อนี้ไม่ได้ (มี '/' หรือ ':')"
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6226,10 +6256,6 @@ msgid "Delete Item"
msgstr "ลบไอเทม"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "ต้องไม่มี '/' หรือ ':'"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "มีอยู่ก่อนแล้ว"
@@ -7278,6 +7304,10 @@ msgstr "การตั้งค่า GridMap"
msgid "Pick Distance:"
msgstr "ระยะการเลือก:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "กำลังสร้าง solution..."
@@ -7328,7 +7358,7 @@ msgstr "คำเตือน"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "สิ้นสุดสแตคข้อผิดพลาดภายใน"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7940,10 +7970,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "ต้องแก้ไข Path ให้ชี้ไปยังโหนด Spatial จึงจะทำงานได้"
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr "จะมี WorldEnvironment ได้เพียงโหนดเดียวในฉาก (หรือกลุ่มของฉากที่เป็นอินสแตนซ์)"
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8034,6 +8074,12 @@ msgstr "ผิดพลาดขณะโหลดฟอนต์"
msgid "Invalid font size."
msgstr "ขนาดฟอนต์ผิดพลาด"
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "ใช้ชื่อนี้ไม่ได้ (มี '/' หรือ ':')"
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "ต้องไม่มี '/' หรือ ':'"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/tr.po b/editor/translations/tr.po
index bb485dc60..5e4a18ce2 100644
--- a/editor/translations/tr.po
+++ b/editor/translations/tr.po
@@ -9,6 +9,7 @@
# Enescan Yerlikaya <enescanyerlikaya@gmail.com>, 2017.
# Fatih Mert Doğancan <fatihmertdogancan@hotmail.com>, 2017.
# hubbyist <hub@legrud.net>, 2017.
+# H.Hüseyin CİHANGİR <hashusfb@gmail.com>, 2018.
# M. Yavuz Uzun <myavuzuzun@yandex.com>, 2016.
# Orkun Turan <holygatestudio@yandex.com>, 2016-2017.
# razah <icnikerazah@gmail.com>, 2017-2018.
@@ -18,15 +19,15 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-01-24 23:40+0000\n"
-"Last-Translator: razah <icnikerazah@gmail.com>\n"
+"PO-Revision-Date: 2018-04-19 12:41+0000\n"
+"Last-Translator: H.Hüseyin CİHANGİR <hashusfb@gmail.com>\n"
"Language-Team: Turkish <https://hosted.weblate.org/projects/godot-engine/"
"godot/tr/>\n"
"Language: tr\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 2.19-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -1401,6 +1402,10 @@ msgstr "Temizle"
msgid "Clear Output"
msgstr "Çıktıyı Temizle"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Kaynak kaydedilirken hata!"
@@ -1961,8 +1966,9 @@ msgid "Debug"
msgstr "Hata Ayıklama"
#: editor/editor_node.cpp
+#, fuzzy
msgid "Deploy with Remote Debug"
-msgstr "Uzaktan Hata Ayıklama ile Dağıt"
+msgstr "Uzaktan Hata Ayıklama ile Aç"
#: editor/editor_node.cpp
msgid ""
@@ -2017,7 +2023,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Sync Scene Changes"
-msgstr "Sahne Değişikliklerini Eşzamanla"
+msgstr "Sahne Değişikliklerini Eş Zamanla"
#: editor/editor_node.cpp
msgid ""
@@ -2033,7 +2039,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Sync Script Changes"
-msgstr "Betik Değişikliklerini Eşzamanla"
+msgstr "Betik Değişikliklerini Eş Zamanla"
#: editor/editor_node.cpp
msgid ""
@@ -3704,6 +3710,16 @@ msgid "Show Guides"
msgstr "Kılavuzları göster"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Başlatım Görünümü"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 Görüntükapısı"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "İçre Seçimi"
@@ -3993,6 +4009,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Örüntü anahat oluşturmak için bir yüzeye sahip değil!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Anahat oluşturulamadı!"
@@ -5277,7 +5297,7 @@ msgstr "Ölçek Biçimi (R)"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Coords"
-msgstr "Yerel Koordlar"
+msgstr "Yerel Kordinatlar"
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Local Space Mode (%s)"
@@ -5661,6 +5681,16 @@ msgid "Checked Item"
msgstr "Denetlenen Öğe"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Öğe Ekle"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Denetlenen Öğe"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Var"
@@ -5975,11 +6005,11 @@ msgid "Invalid project path (changed anything?)."
msgstr "Geçersiz proje yolu (bir şey değişti mi?)."
#: editor/project_manager.cpp
-#, fuzzy
msgid ""
"Couldn't load project.godot in project path (error %d). It may be missing or "
"corrupted."
-msgstr "proje yolundaki proje.godot düzenlenemedi."
+msgstr ""
+"Proje yolundaki proje.godot düzenlenemedi.Eksik veya bozulmuş olabilir."
#: editor/project_manager.cpp
msgid "Couldn't edit project.godot in project path."
@@ -6157,8 +6187,10 @@ msgid "Mouse Button"
msgstr "Fare Düğmesi"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Geçersiz işlem (her şey ancak şu '/' ya da şuna ':' gider)."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6301,10 +6333,6 @@ msgid "Delete Item"
msgstr "Öğeyi Sil"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "'/' veya ':' içeremez"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Zaten mevcut"
@@ -6442,7 +6470,7 @@ msgstr "Yereller:"
#: editor/project_settings_editor.cpp
msgid "AutoLoad"
-msgstr "KendindenYükle"
+msgstr "Otomatik Yükle"
#: editor/property_editor.cpp
msgid "Pick a Viewport"
@@ -7050,7 +7078,7 @@ msgstr "Toplam:"
#: editor/script_editor_debugger.cpp
msgid "Video Mem"
-msgstr "İzleti Belleği"
+msgstr "Görüntü Belleği"
#: editor/script_editor_debugger.cpp
msgid "Resource Path"
@@ -7070,7 +7098,7 @@ msgstr "Kullanım"
#: editor/script_editor_debugger.cpp
msgid "Misc"
-msgstr "Türlü"
+msgstr "Çeşitli"
#: editor/script_editor_debugger.cpp
msgid "Clicked Control:"
@@ -7244,18 +7272,16 @@ msgid "Object can't provide a length."
msgstr "Nesne bir uzunluk sağlayamaz."
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Next Plane"
msgstr "Sonraki sekme"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Previous Plane"
msgstr "Önceki sekme"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Plane:"
-msgstr ""
+msgstr "Sekme:"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Next Floor"
@@ -7361,6 +7387,10 @@ msgstr "IzgaraHaritası Ayarları"
msgid "Pick Distance:"
msgstr "Uzaklık Seç:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "solü oluşturuluyor..."
@@ -7390,8 +7420,9 @@ msgid "Mono"
msgstr "Tekli"
#: modules/mono/editor/godotsharp_editor.cpp
+#, fuzzy
msgid "About C# support"
-msgstr ""
+msgstr "C# hakkında destek"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Create C# solution"
@@ -7595,7 +7626,7 @@ msgstr "Dizi"
#: modules/visual_script/visual_script_editor.cpp
msgid "Switch"
-msgstr "Değiştir"
+msgstr "Anahtar"
#: modules/visual_script/visual_script_editor.cpp
msgid "Iterator"
@@ -7967,8 +7998,9 @@ msgid "%d%%"
msgstr ""
#: scene/3d/baked_lightmap.cpp
+#, fuzzy
msgid "(Time Left: %d:%02d s)"
-msgstr ""
+msgstr "(Kalan Zaman:%d:%02d s)"
#: scene/3d/baked_lightmap.cpp
msgid "Plotting Meshes: "
@@ -8069,12 +8101,22 @@ msgstr ""
"Yol özelliği, çalışmak için geçerli bir Spatial düğümüne işaret etmelidir."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"Her sahne başına (ya da örneklenmiş sahneler dizisine) sadece bir tane "
"WorldEnvironment 'a izin verilir."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8171,6 +8213,12 @@ msgstr "Yazıtipi yükleme hatası."
msgid "Invalid font size."
msgstr "Geçersiz yazıtipi boyutu."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Geçersiz işlem (her şey ancak şu '/' ya da şuna ':' gider)."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "'/' veya ':' içeremez"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/uk.po b/editor/translations/uk.po
index 685812f2d..45138cd5d 100644
--- a/editor/translations/uk.po
+++ b/editor/translations/uk.po
@@ -4,16 +4,17 @@
# This file is distributed under the same license as the Godot source code.
#
# Aleksandr <XpycT.TOP@gmail.com>, 2017.
+# Yuri Chornoivan <yurchor@ukr.net>, 2018.
# Андрій Бандура <andriykopanytsia@gmail.com>, 2018.
# Гидеон Теон <t.kudely94@gmail.com>, 2017.
+# Максим Якимчук <xpinovo@gmail.com>, 2018.
# Марс Ямбар <mjambarmeta@gmail.com>, 2017-2018.
# Олександр Пилипчук <pilipchukap@rambler.ru>, 2018.
-# Yuri Chornoivan <yurchor@ukr.net>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Ukrainian (Godot Engine)\n"
-"PO-Revision-Date: 2018-02-22 18:08+0000\n"
+"PO-Revision-Date: 2018-04-20 18:42+0000\n"
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
"Language-Team: Ukrainian <https://hosted.weblate.org/projects/godot-engine/"
"godot/uk/>\n"
@@ -22,7 +23,7 @@ msgstr ""
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 2.20-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -300,11 +301,11 @@ msgstr "Максимальна лінійна похибка:"
#: editor/animation_editor.cpp
msgid "Max. Angular Error:"
-msgstr "Макс. Кутові похибки:"
+msgstr "Макс. кутова похибка:"
#: editor/animation_editor.cpp
msgid "Max Optimizable Angle:"
-msgstr "Максимальний оптимізований кут:"
+msgstr "Макс. оптимізований кут:"
#: editor/animation_editor.cpp
msgid "Optimize"
@@ -1400,6 +1401,10 @@ msgstr "Очистити"
msgid "Clear Output"
msgstr "Очистити вивід"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "Помилка збереження ресурсу!"
@@ -1961,7 +1966,7 @@ msgstr "Вийти в список проектів"
#: editor/editor_node.cpp editor/plugins/script_editor_plugin.cpp
msgid "Debug"
-msgstr "Налагодження"
+msgstr "Діагностика"
#: editor/editor_node.cpp
msgid "Deploy with Remote Debug"
@@ -2103,7 +2108,7 @@ msgstr "Спільнота"
#: editor/editor_node.cpp
msgid "About"
-msgstr "Про"
+msgstr "Про програму"
#: editor/editor_node.cpp
msgid "Play the project."
@@ -3394,7 +3399,7 @@ msgstr "Все"
#: editor/plugins/asset_library_editor_plugin.cpp
#: editor/project_settings_editor.cpp
msgid "Plugins"
-msgstr "Плагіни"
+msgstr "Плаґіни"
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Sort:"
@@ -3708,6 +3713,16 @@ msgid "Show Guides"
msgstr "Показати напрямні"
#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "Перегляд центра"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1 панель перегляду"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr "Центрувати на вибраному"
@@ -3998,6 +4013,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Сітка не має поверхні, щоб створити контури!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "Не вдалося створити контур!"
@@ -5667,6 +5686,16 @@ msgid "Checked Item"
msgstr "Позначений елемент"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "Додати елемент"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "Позначений елемент"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "Має"
@@ -6166,8 +6195,10 @@ msgid "Mouse Button"
msgstr "Кнопка миші"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Некоректна дія (можна усе, окрім «/» або «:»)."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6310,10 +6341,6 @@ msgid "Delete Item"
msgstr "Вилучити елемент"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "Не може містити «/» або «:»"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "Вже існує"
@@ -6539,7 +6566,7 @@ msgstr "[Порожньо]"
#: editor/property_editor.cpp modules/visual_script/visual_script_editor.cpp
msgid "Set"
-msgstr "Встановити"
+msgstr "Множина"
#: editor/property_editor.cpp
msgid "Properties:"
@@ -7372,6 +7399,10 @@ msgstr "Параметри GridMap"
msgid "Pick Distance:"
msgstr "Відстань вибору:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr "Створення розв'язку..."
@@ -7422,7 +7453,7 @@ msgstr "Попередження"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "Кінець трасування стека для внутрішнього виключення"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -8082,12 +8113,22 @@ msgstr ""
"коректний вузол Spatial."
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
"У сцені (або наборі екземплярів сцен) може бути лише один елемент "
"WorldEnvironment."
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8185,6 +8226,12 @@ msgstr "Помилка завантаження шрифту."
msgid "Invalid font size."
msgstr "Некоректний розмір шрифту."
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Некоректна дія (можна усе, окрім «/» або «:»)."
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "Не може містити «/» або «:»"
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/ur_PK.po b/editor/translations/ur_PK.po
index 96fbfbeee..4f03e8a38 100644
--- a/editor/translations/ur_PK.po
+++ b/editor/translations/ur_PK.po
@@ -1382,6 +1382,10 @@ msgstr ""
msgid "Clear Output"
msgstr "سب سکریپشن بنائیں"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3611,6 +3615,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3900,6 +3912,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5573,6 +5589,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6053,7 +6077,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6198,10 +6224,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7255,6 +7277,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7897,10 +7923,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/vi.po b/editor/translations/vi.po
index c39ccc4db..d6284d640 100644
--- a/editor/translations/vi.po
+++ b/editor/translations/vi.po
@@ -3,6 +3,7 @@
# Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md)
# This file is distributed under the same license as the Godot source code.
#
+# 01lifeleft <01lifeleft@gmail.com>, 2018.
# Dlean Jeans <dleanjeans@gmail.com>, 2018.
# Hai Le <dark.hades.1102@gmail.com>, 2017.
# Nguyễn Tuấn Anh <anhnt.fami@gmail.com>, 2017.
@@ -11,15 +12,15 @@
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2018-01-24 12:35+0000\n"
-"Last-Translator: Dlean Jeans <dleanjeans@gmail.com>\n"
+"PO-Revision-Date: 2018-04-18 15:44+0000\n"
+"Last-Translator: 01lifeleft <01lifeleft@gmail.com>\n"
"Language-Team: Vietnamese <https://hosted.weblate.org/projects/godot-engine/"
"godot/vi/>\n"
"Language: vi\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 2.19-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -79,7 +80,7 @@ msgstr "Đổi tên Anim Track"
#: editor/animation_editor.cpp
msgid "Anim Track Change Interpolation"
-msgstr ""
+msgstr "Đổi phép nội suy Anim Track"
#: editor/animation_editor.cpp
msgid "Anim Track Change Value Mode"
@@ -112,31 +113,31 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Remove Selection"
-msgstr ""
+msgstr "Bỏ lựa chọn"
#: editor/animation_editor.cpp
msgid "Continuous"
-msgstr ""
+msgstr "Liên tục"
#: editor/animation_editor.cpp
msgid "Discrete"
-msgstr ""
+msgstr "Đứt đoạn"
#: editor/animation_editor.cpp
msgid "Trigger"
-msgstr ""
+msgstr "Kích hoạt"
#: editor/animation_editor.cpp
msgid "Anim Add Key"
-msgstr ""
+msgstr "Thêm Key Anim"
#: editor/animation_editor.cpp
msgid "Anim Move Keys"
-msgstr ""
+msgstr "Di chuyển các Key Anim"
#: editor/animation_editor.cpp
msgid "Scale Selection"
-msgstr ""
+msgstr "Chọn Scale"
#: editor/animation_editor.cpp
msgid "Scale From Cursor"
@@ -165,15 +166,15 @@ msgstr "Trong"
#: editor/animation_editor.cpp
msgid "Out"
-msgstr ""
+msgstr "Ngoài"
#: editor/animation_editor.cpp
msgid "In-Out"
-msgstr ""
+msgstr "Trong-Ngoài"
#: editor/animation_editor.cpp
msgid "Out-In"
-msgstr ""
+msgstr "Ngoài-Trong"
#: editor/animation_editor.cpp
msgid "Transitions"
@@ -185,7 +186,7 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Clean-Up Animation"
-msgstr ""
+msgstr "Dọn dẹp Animation"
#: editor/animation_editor.cpp
msgid "Create NEW track for %s and insert key?"
@@ -205,7 +206,7 @@ msgstr "Tạo"
#: editor/animation_editor.cpp
msgid "Anim Create & Insert"
-msgstr ""
+msgstr "Tạo & Chèn Anim"
#: editor/animation_editor.cpp
msgid "Anim Insert Track & Key"
@@ -221,7 +222,7 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Change Anim Loop"
-msgstr ""
+msgstr "Đổi vòng lặp Anim"
#: editor/animation_editor.cpp
msgid "Anim Create Typed Value Key"
@@ -229,7 +230,7 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Anim Insert"
-msgstr ""
+msgstr "Chèn Anim"
#: editor/animation_editor.cpp
msgid "Anim Scale Keys"
@@ -249,7 +250,7 @@ msgstr "Độ dài (giây):"
#: editor/animation_editor.cpp
msgid "Animation length (in seconds)."
-msgstr ""
+msgstr "Độ dài Animation (giây)."
#: editor/animation_editor.cpp
msgid "Step (s):"
@@ -261,7 +262,7 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Enable/Disable looping in animation."
-msgstr ""
+msgstr "Mở/Tắt lặp animation."
#: editor/animation_editor.cpp
msgid "Add new tracks."
@@ -1373,6 +1374,10 @@ msgstr ""
msgid "Clear Output"
msgstr ""
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -3596,6 +3601,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3881,6 +3894,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5542,6 +5559,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6019,7 +6044,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6163,10 +6190,6 @@ msgid "Delete Item"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7112,7 +7135,7 @@ msgstr ""
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Floor:"
-msgstr ""
+msgstr "Tầng:"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "GridMap Delete Selection"
@@ -7206,6 +7229,10 @@ msgstr ""
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7842,10 +7869,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
diff --git a/editor/translations/zh_CN.po b/editor/translations/zh_CN.po
index 9c55ece6d..45d2d8150 100644
--- a/editor/translations/zh_CN.po
+++ b/editor/translations/zh_CN.po
@@ -10,23 +10,26 @@
# 吴亮弟 <wu@liangdi.me>, 2017.
# ageazrael <ageazrael@gmail.com>, 2016.
# Bruce Guo <guoboism@hotmail.com>, 2016.
-# dragonandy <dragonandy@foxmail.com>, 2017.
+# dragonandy <dragonandy@foxmail.com>, 2017-2018.
# Geequlim <geequlim@gmail.com>, 2016-2018.
# lalalaring <783482203@qq.com>, 2017.
# Luo Jun <vipsbpig@gmail.com>, 2016-2017.
# oberon-tonya <360119124@qq.com>, 2016.
# Qichunren <whyruby@gmail.com>, 2017.
-# sersoong <seraphim945@qq.com>, 2017.
+# seanfy <everxiao@qq.com>, 2018.
+# sersoong <seraphim945@qq.com>, 2017-2018.
# wanfang liu <wanfang.liu@gmail.com>, 2016.
# WeiXiong Huang <wx_Huang@sina.com>, 2017.
# Youmu <konpaku.w@gmail.com>, 2017.
+# yuetian <18829280955@163.com>, 2018.
+# Zae Chao <zae.vito@live.com>, 2018.
#
msgid ""
msgstr ""
"Project-Id-Version: Chinese (Simplified) (Godot Engine)\n"
"POT-Creation-Date: 2018-01-20 12:15+0200\n"
-"PO-Revision-Date: 2018-02-03 05:35+0000\n"
-"Last-Translator: 360119124 <360119124@qq.com>\n"
+"PO-Revision-Date: 2018-05-03 08:59+0000\n"
+"Last-Translator: Geequlim <geequlim@gmail.com>\n"
"Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/"
"godot-engine/godot/zh_Hans/>\n"
"Language: zh_CN\n"
@@ -34,7 +37,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 2.19-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -328,7 +331,7 @@ msgstr "在场景树中选择一个AnimationPlayer来编辑动画。"
#: editor/animation_editor.cpp
msgid "Key"
-msgstr "键"
+msgstr "关键帧"
#: editor/animation_editor.cpp
msgid "Transition"
@@ -807,7 +810,7 @@ msgstr "软件包安装成功!"
#: editor/editor_asset_installer.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Success!"
-msgstr "完成!"
+msgstr "成功!"
#: editor/editor_asset_installer.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -1154,7 +1157,7 @@ msgstr "所有文件(*)"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open a File"
-msgstr "打开文件"
+msgstr "打开单个文件"
#: editor/editor_file_dialog.cpp scene/gui/file_dialog.cpp
msgid "Open File(s)"
@@ -1383,7 +1386,7 @@ msgstr "查找"
#: editor/editor_log.cpp
msgid "Output:"
-msgstr "输出:"
+msgstr "日志:"
#: editor/editor_log.cpp editor/plugins/animation_tree_editor_plugin.cpp
#: editor/property_editor.cpp editor/script_editor_debugger.cpp
@@ -1396,6 +1399,10 @@ msgstr "清除"
msgid "Clear Output"
msgstr "清空输出"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr "保存资源出错!"
@@ -1668,7 +1675,7 @@ msgstr "导出网格库(Mesh Library)"
#: editor/editor_node.cpp
msgid "This operation can't be done without a root node."
-msgstr "此操作必须有一个根节点(root node)才能执行。"
+msgstr "需要有根节点才能完成此操作。"
#: editor/editor_node.cpp
msgid "Export Tile Set"
@@ -1824,7 +1831,7 @@ msgstr "切换无干扰模式。"
#: editor/editor_node.cpp
msgid "Add a new scene."
-msgstr "新建场景"
+msgstr "添加新场景。"
#: editor/editor_node.cpp
msgid "Scene"
@@ -1926,7 +1933,7 @@ msgstr "导出"
#: editor/editor_node.cpp
msgid "Tools"
-msgstr "工具"
+msgstr "工具(tools)"
#: editor/editor_node.cpp
msgid "Quit to Project List"
@@ -1938,7 +1945,7 @@ msgstr "调试"
#: editor/editor_node.cpp
msgid "Deploy with Remote Debug"
-msgstr "部署远程调试"
+msgstr "使用远程调试部署"
#: editor/editor_node.cpp
msgid ""
@@ -1949,7 +1956,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Small Deploy with Network FS"
-msgstr "发布基于网络文件系统的最小应用包"
+msgstr "小型部署与网络文件系统"
#: editor/editor_node.cpp
msgid ""
@@ -1967,7 +1974,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Visible Collision Shapes"
-msgstr "碰撞区域可见"
+msgstr "可见碰撞区域"
#: editor/editor_node.cpp
msgid ""
@@ -1977,7 +1984,7 @@ msgstr "如果启用此项,节点的碰撞区域和raycast将在游戏运行
#: editor/editor_node.cpp
msgid "Visible Navigation"
-msgstr "Navigation可见"
+msgstr "可见导航"
#: editor/editor_node.cpp
msgid ""
@@ -2179,7 +2186,7 @@ msgstr "文件系统"
#: editor/editor_node.cpp
msgid "Output"
-msgstr "输出"
+msgstr "日志"
#: editor/editor_node.cpp
msgid "Don't Save"
@@ -2404,7 +2411,7 @@ msgstr "(当前)"
#: editor/export_template_manager.cpp
msgid "Retrieving mirrors, please wait.."
-msgstr "正在链接镜像地址,请稍后.."
+msgstr "检索镜像,请等待..."
#: editor/export_template_manager.cpp
msgid "Remove template version '%s'?"
@@ -2438,7 +2445,7 @@ msgstr "导入:"
msgid ""
"No download links found for this version. Direct download is only available "
"for official releases."
-msgstr "当前版本没有下载链接。只能通过官方发布下载。"
+msgstr "当前版本没有下载链接。直链下载只提供官方正式版。"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
@@ -2472,7 +2479,7 @@ msgstr "失败:"
#: editor/export_template_manager.cpp
msgid "Download Complete."
-msgstr "下载完成"
+msgstr "下载完成。"
#: editor/export_template_manager.cpp
msgid "Error requesting url: "
@@ -2480,7 +2487,7 @@ msgstr "请求链接错误: "
#: editor/export_template_manager.cpp
msgid "Connecting to Mirror.."
-msgstr "正在连接镜像……"
+msgstr "正在连接镜像网站。。"
#: editor/export_template_manager.cpp
msgid "Disconnected"
@@ -2510,7 +2517,7 @@ msgstr "已连接"
#: editor/export_template_manager.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Requesting.."
-msgstr "正在请求……"
+msgstr "正在请求。。"
#: editor/export_template_manager.cpp
msgid "Downloading"
@@ -2719,7 +2726,7 @@ msgstr "导入为独立场景"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Animations"
-msgstr "导入独立的动画"
+msgstr "与独立的动画一同导入"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Materials"
@@ -2735,15 +2742,15 @@ msgstr "导入独立物体 + 材质"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Objects+Animations"
-msgstr "导入独立物体+动画"
+msgstr "导入独立的物体和动画"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Materials+Animations"
-msgstr "导入独立材质+动画"
+msgstr "导入独立的材质和动画"
#: editor/import/resource_importer_scene.cpp
msgid "Import with Separate Objects+Materials+Animations"
-msgstr "导入独立物体+材质+动画"
+msgstr "导入独立的物体、材质和动画"
#: editor/import/resource_importer_scene.cpp
msgid "Import as Multiple Scenes"
@@ -2768,7 +2775,7 @@ msgstr "正在生成光照贴图"
#: editor/import/resource_importer_scene.cpp
msgid "Generating for Mesh: "
-msgstr "正在生成Mesh"
+msgstr "正在生成Mesh: "
#: editor/import/resource_importer_scene.cpp
msgid "Running Custom Script.."
@@ -3056,7 +3063,7 @@ msgstr "仅不同"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Force White Modulate"
-msgstr ""
+msgstr "强制用白色调和"
#: editor/plugins/animation_player_editor_plugin.cpp
msgid "Include Gizmos (3D)"
@@ -3651,7 +3658,17 @@ msgstr "显示标尺"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Show Guides"
-msgstr "显示标尺 "
+msgstr "显示引导"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Origin"
+msgstr "显示原点"
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+#, fuzzy
+msgid "Show Viewport"
+msgstr "1个视口"
#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
@@ -3770,13 +3787,12 @@ msgid "Update from Scene"
msgstr "从场景中更新"
#: editor/plugins/curve_editor_plugin.cpp
-#, fuzzy
msgid "Flat0"
msgstr "Flat0"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Flat1"
-msgstr ""
+msgstr "Flat1"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Ease in"
@@ -3788,7 +3804,7 @@ msgstr "渐出"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Smoothstep"
-msgstr "圆滑级别 "
+msgstr "平滑插值"
#: editor/plugins/curve_editor_plugin.cpp
msgid "Modify Curve Point"
@@ -3944,6 +3960,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr "Mesh(网格)没有表面来创建轮廓(outlines)!"
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr "无法创建轮廓(outlines)!"
@@ -4125,7 +4145,7 @@ msgstr "标记可移动三角形..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Constructing compact heightfield..."
-msgstr "构建紧凑高度图... "
+msgstr "构建紧凑高度图..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Eroding walkable area..."
@@ -4133,11 +4153,11 @@ msgstr "正在计算可行区域..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Partitioning..."
-msgstr "分区中... "
+msgstr "分区中..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Creating contours..."
-msgstr "正在创建轮廓... "
+msgstr "正在创建轮廓..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Creating polymesh..."
@@ -4145,7 +4165,7 @@ msgstr "创建多边形网格..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Converting to native navigation mesh..."
-msgstr "转换为导航网格(mesh)... "
+msgstr "转换为导航网格(mesh)..."
#: editor/plugins/navigation_mesh_generator.cpp
msgid "Navigation Mesh Generator Setup:"
@@ -4361,7 +4381,7 @@ msgstr "设置曲线的顶点坐标"
#: editor/plugins/path_editor_plugin.cpp
msgid "Set Curve In Position"
-msgstr "设置曲线的内控制点"
+msgstr "设置的曲线初始位置(Pos)"
#: editor/plugins/path_editor_plugin.cpp
msgid "Set Curve Out Position"
@@ -4590,9 +4610,8 @@ msgid "Copy Script Path"
msgstr "拷贝脚本路径"
#: editor/plugins/script_editor_plugin.cpp
-#, fuzzy
msgid "Show In File System"
-msgstr "在资源管理器中展示"
+msgstr "在资源管理器中显示"
#: editor/plugins/script_editor_plugin.cpp
msgid "History Prev"
@@ -5021,7 +5040,7 @@ msgstr "缩放: "
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Translating: "
-msgstr "语言:"
+msgstr "移动: "
#: editor/plugins/spatial_editor_plugin.cpp
msgid "Rotating %s degrees."
@@ -5588,7 +5607,7 @@ msgstr "创建空编辑器主题模板"
#: editor/plugins/theme_editor_plugin.cpp
msgid "Create From Current Editor Theme"
-msgstr "从当前编辑器主题创建"
+msgstr "从当前编辑器主题模板创建"
#: editor/plugins/theme_editor_plugin.cpp
msgid "CheckBox Radio1"
@@ -5611,6 +5630,16 @@ msgid "Checked Item"
msgstr "已选项目(Checked Item)"
#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Radio Item"
+msgstr "添加项目"
+
+#: editor/plugins/theme_editor_plugin.cpp
+#, fuzzy
+msgid "Checked Radio Item"
+msgstr "已选项目(Checked Item)"
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr "有(Has)"
@@ -5778,8 +5807,8 @@ msgid ""
"LMB: set bit on.\n"
"RMB: set bit off."
msgstr ""
-"鼠标左键: 启用bit.\n"
-"鼠标右键: 禁用bit"
+"鼠标左键: 启用该bit。\n"
+"鼠标右键: 禁用该bit。"
#: editor/plugins/tile_set_editor_plugin.cpp
msgid "Select current edited sub-tile."
@@ -5799,7 +5828,7 @@ msgstr "可执行的"
#: editor/project_export.cpp
msgid "Delete patch '%s' from list?"
-msgstr "确认要从列表中删除Patch '%s' 吗?"
+msgstr "从列表中删除补丁''%s'?"
#: editor/project_export.cpp
msgid "Delete preset '%s'?"
@@ -5920,11 +5949,11 @@ msgid "Invalid project path (changed anything?)."
msgstr "项目路径非法(被外部修改?)。"
#: editor/project_manager.cpp
-#, fuzzy
msgid ""
"Couldn't load project.godot in project path (error %d). It may be missing or "
"corrupted."
-msgstr "无法在项目目录下编辑project.godot文件。"
+msgstr ""
+"无法在项目路径中加载 project.godot 文件(错误 %d)。该文件可能缺失或已损坏。"
#: editor/project_manager.cpp
msgid "Couldn't edit project.godot in project path."
@@ -6098,8 +6127,10 @@ msgid "Mouse Button"
msgstr "鼠标按键"
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
-msgstr "Action名非法(不得包含'/'或':')。"
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
+msgstr ""
#: editor/project_settings_editor.cpp
msgid "Action '%s' already exists!"
@@ -6231,7 +6262,7 @@ msgstr "请先选择一个设置项目 !"
#: editor/project_settings_editor.cpp
msgid "No property '%s' exists."
-msgstr "属性 '%s' 不存在"
+msgstr "不存在属性 '%s'。"
#: editor/project_settings_editor.cpp
msgid "Setting '%s' is internal, and it can't be deleted."
@@ -6242,10 +6273,6 @@ msgid "Delete Item"
msgstr "删除条目"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr "不能包含 \"/\" 或 \":\""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr "已经存在"
@@ -6307,7 +6334,7 @@ msgstr "项目设置(project.godot)"
#: editor/project_settings_editor.cpp editor/settings_config_dialog.cpp
msgid "General"
-msgstr "一般"
+msgstr "常规"
#: editor/project_settings_editor.cpp editor/property_editor.cpp
msgid "Property:"
@@ -6466,13 +6493,12 @@ msgid "On"
msgstr "启用"
#: editor/property_editor.cpp
-#, fuzzy
msgid "[Empty]"
-msgstr "添加空白帧"
+msgstr "[空]"
#: editor/property_editor.cpp modules/visual_script/visual_script_editor.cpp
msgid "Set"
-msgstr "设置"
+msgstr "Set"
#: editor/property_editor.cpp
msgid "Properties:"
@@ -6831,7 +6857,7 @@ msgstr "存在同名目录"
#: editor/script_create_dialog.cpp
msgid "File exists, will be reused"
-msgstr "文件存在,将被重用"
+msgstr "文件已存在, 将被重用"
#: editor/script_create_dialog.cpp
msgid "Invalid extension"
@@ -6898,7 +6924,6 @@ msgid "Attach Node Script"
msgstr "设置节点的脚本"
#: editor/script_editor_debugger.cpp
-#, fuzzy
msgid "Remote "
msgstr "远程 "
@@ -6968,7 +6993,7 @@ msgstr "性能分析"
#: editor/script_editor_debugger.cpp
msgid "Monitor"
-msgstr "显示"
+msgstr "监视"
#: editor/script_editor_debugger.cpp
msgid "Value"
@@ -7008,7 +7033,7 @@ msgstr "用量"
#: editor/script_editor_debugger.cpp
msgid "Misc"
-msgstr "杂项"
+msgstr "其他"
#: editor/script_editor_debugger.cpp
msgid "Clicked Control:"
@@ -7181,18 +7206,16 @@ msgid "Object can't provide a length."
msgstr "对象无法提供长度。"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Next Plane"
-msgstr "下一项"
+msgstr "下一个平面"
#: modules/gridmap/grid_map_editor_plugin.cpp
-#, fuzzy
msgid "Previous Plane"
-msgstr "上一个目录"
+msgstr "上一个平面"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Plane:"
-msgstr ""
+msgstr "平面:"
#: modules/gridmap/grid_map_editor_plugin.cpp
msgid "Next Floor"
@@ -7298,9 +7321,13 @@ msgstr "GridMap设置"
msgid "Pick Distance:"
msgstr "拾取距离:"
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
-msgstr "正在创生成决方案... "
+msgstr "正在创生成决方案..."
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating C# project..."
@@ -7328,7 +7355,7 @@ msgstr "Mono"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "About C# support"
-msgstr ""
+msgstr "关于C#支持"
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Create C# solution"
@@ -7348,7 +7375,7 @@ msgstr "警告"
#: modules/mono/mono_gd/gd_mono_utils.cpp
msgid "End of inner exception stack trace"
-msgstr ""
+msgstr "内部异常堆栈追朔结束"
#: modules/visual_script/visual_script.cpp
msgid ""
@@ -7515,11 +7542,11 @@ msgstr "连接节点"
#: modules/visual_script/visual_script_editor.cpp
msgid "Condition"
-msgstr "条件(Condition)"
+msgstr "条件"
#: modules/visual_script/visual_script_editor.cpp
msgid "Sequence"
-msgstr "序列(Sequence)"
+msgstr "序列"
#: modules/visual_script/visual_script_editor.cpp
msgid "Switch"
@@ -7868,11 +7895,11 @@ msgstr "ARVROrigin 必须拥有 ARVRCamera 子节点"
#: scene/3d/baked_lightmap.cpp
msgid "%d%%"
-msgstr ""
+msgstr "%d%%"
#: scene/3d/baked_lightmap.cpp
msgid "(Time Left: %d:%02d s)"
-msgstr ""
+msgstr "(剩余时间:%d:%02d 秒)"
#: scene/3d/baked_lightmap.cpp
msgid "Plotting Meshes: "
@@ -7964,10 +7991,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr "path属性必须指向一个合法的Spatial节点才能正常工作。"
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr "每个场景中只允许有一个WorldEnvironment类型的节点。"
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8059,6 +8096,12 @@ msgstr "加载字体出错。"
msgid "Invalid font size."
msgstr "字体大小非法。"
+#~ msgid "Invalid action (anything goes but '/' or ':')."
+#~ msgstr "Action名非法(不得包含'/'或':')。"
+
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "不能包含 \"/\" 或 \":\""
+
#~ msgid ""
#~ "Invalid version.txt format inside templates. Revision is not a valid "
#~ "identifier."
diff --git a/editor/translations/zh_HK.po b/editor/translations/zh_HK.po
index 6e2e5ac50..f4c6a3978 100644
--- a/editor/translations/zh_HK.po
+++ b/editor/translations/zh_HK.po
@@ -1471,6 +1471,10 @@ msgstr "清空"
msgid "Clear Output"
msgstr "下一個腳本"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
#, fuzzy
msgid "Error saving resource!"
@@ -3793,6 +3797,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -4085,6 +4097,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5783,6 +5799,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6278,7 +6302,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6425,11 +6451,6 @@ msgid "Delete Item"
msgstr "刪除"
#: editor/project_settings_editor.cpp
-#, fuzzy
-msgid "Can't contain '/' or ':'"
-msgstr "不能連到主機:"
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -7508,6 +7529,10 @@ msgstr "設定"
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -8171,10 +8196,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8257,6 +8292,10 @@ msgid "Invalid font size."
msgstr "無效字型"
#, fuzzy
+#~ msgid "Can't contain '/' or ':'"
+#~ msgstr "不能連到主機:"
+
+#, fuzzy
#~ msgid "Can't write file."
#~ msgstr "無法新增資料夾"
diff --git a/editor/translations/zh_TW.po b/editor/translations/zh_TW.po
index 2d67ef98c..ccbd45bf3 100644
--- a/editor/translations/zh_TW.po
+++ b/editor/translations/zh_TW.po
@@ -4,24 +4,27 @@
# This file is distributed under the same license as the Godot source code.
#
# Allen H <w84miracle@gmail.com>, 2017.
+# Billy SU <g4691821@gmail.com>, 2018.
# Chao Yu <casd82@gmail.com>, 2017.
# Cliffs Dover <bottle@dancingbottle.com>, 2017.
+# Kisaragi Hiu <mail@kisaragi-hiu.com>, 2018.
# Matt <chchwy@gmail.com>, 2017.
# popcade <popcade@gmail.com>, 2016.
+# Qing <icinriiq@gmail.com>, 2018.
# Sam Pan <sampan66@gmail.com>, 2016.
#
msgid ""
msgstr ""
"Project-Id-Version: Godot Engine editor\n"
-"PO-Revision-Date: 2017-11-24 10:45+0000\n"
-"Last-Translator: Matt <chchwy@gmail.com>\n"
+"PO-Revision-Date: 2018-04-24 09:35+0000\n"
+"Last-Translator: Qing <icinriiq@gmail.com>\n"
"Language-Team: Chinese (Traditional) <https://hosted.weblate.org/projects/"
"godot-engine/godot/zh_Hant/>\n"
"Language: zh_TW\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8-bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 2.18-dev\n"
+"X-Generator: Weblate 3.0-dev\n"
#: editor/animation_editor.cpp
msgid "Disabled"
@@ -38,20 +41,20 @@ msgstr "動畫更改座標"
#: editor/animation_editor.cpp
msgid "Anim Change Transition"
-msgstr ""
+msgstr "動畫更改轉場效果"
#: editor/animation_editor.cpp
msgid "Anim Change Transform"
msgstr "動畫更改座標"
#: editor/animation_editor.cpp
-#, fuzzy
msgid "Anim Change Keyframe Value"
-msgstr "動畫更改座標"
+msgstr "動畫更改關鍵幀數值"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Change Call"
-msgstr ""
+msgstr "動畫改變呼叫"
#: editor/animation_editor.cpp
msgid "Anim Add Track"
@@ -90,8 +93,9 @@ msgid "Anim Track Change Value Mode"
msgstr "動畫軌改變模式"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Track Change Wrap Mode"
-msgstr ""
+msgstr "動畫軌道更改環繞模式"
#: editor/animation_editor.cpp
msgid "Edit Node Curve"
@@ -112,7 +116,7 @@ msgstr "複製所選"
#: editor/animation_editor.cpp
msgid "Duplicate Transposed"
-msgstr ""
+msgstr "重複轉置"
#: editor/animation_editor.cpp
msgid "Remove Selection"
@@ -177,7 +181,7 @@ msgstr "進出"
#: editor/animation_editor.cpp
msgid "Out-In"
-msgstr ""
+msgstr "外-內"
#: editor/animation_editor.cpp
msgid "Transitions"
@@ -212,28 +216,30 @@ msgid "Anim Create & Insert"
msgstr "動畫建立與插入"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Insert Track & Key"
-msgstr ""
+msgstr "動畫新增軌跡與按鍵"
#: editor/animation_editor.cpp
msgid "Anim Insert Key"
-msgstr ""
+msgstr "動畫新增按鍵"
#: editor/animation_editor.cpp
msgid "Change Anim Len"
-msgstr ""
+msgstr "變更動畫長度"
#: editor/animation_editor.cpp
msgid "Change Anim Loop"
-msgstr ""
+msgstr "變更動畫迴圈"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim Create Typed Value Key"
-msgstr ""
+msgstr "動畫新增具類別之鍵值"
#: editor/animation_editor.cpp
msgid "Anim Insert"
-msgstr ""
+msgstr "插入動畫"
#: editor/animation_editor.cpp
msgid "Anim Scale Keys"
@@ -241,19 +247,19 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Anim Add Call Track"
-msgstr ""
+msgstr "動畫新增呼叫軌跡"
#: editor/animation_editor.cpp
msgid "Animation zoom."
-msgstr ""
+msgstr "動畫空間。"
#: editor/animation_editor.cpp
msgid "Length (s):"
-msgstr ""
+msgstr "長度(s):"
#: editor/animation_editor.cpp
msgid "Animation length (in seconds)."
-msgstr "動畫長度 (秒)"
+msgstr "動畫長度 (秒)。"
#: editor/animation_editor.cpp
msgid "Step (s):"
@@ -265,75 +271,77 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Enable/Disable looping in animation."
-msgstr ""
+msgstr "啟用/停用 動畫迴圈。"
#: editor/animation_editor.cpp
msgid "Add new tracks."
-msgstr ""
+msgstr "新增動畫軌。"
#: editor/animation_editor.cpp
msgid "Move current track up."
-msgstr ""
+msgstr "上移當前動畫軌。"
#: editor/animation_editor.cpp
msgid "Move current track down."
-msgstr ""
+msgstr "下移當前動畫軌。"
#: editor/animation_editor.cpp
msgid "Remove selected track."
-msgstr ""
+msgstr "移除選擇的動畫軌。"
#: editor/animation_editor.cpp
msgid "Track tools"
-msgstr ""
+msgstr "軌跡工具"
#: editor/animation_editor.cpp
msgid "Enable editing of individual keys by clicking them."
-msgstr ""
+msgstr "啟用可使用點擊來更改各個鍵。"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Anim. Optimizer"
-msgstr ""
+msgstr "動畫. 最佳化"
#: editor/animation_editor.cpp
msgid "Max. Linear Error:"
-msgstr ""
+msgstr "最大線性錯誤:"
#: editor/animation_editor.cpp
msgid "Max. Angular Error:"
-msgstr ""
+msgstr "最大角度錯誤:"
#: editor/animation_editor.cpp
msgid "Max Optimizable Angle:"
-msgstr ""
+msgstr "最大可優化角度:"
#: editor/animation_editor.cpp
msgid "Optimize"
-msgstr ""
+msgstr "最佳化"
#: editor/animation_editor.cpp
+#, fuzzy
msgid "Select an AnimationPlayer from the Scene Tree to edit animations."
-msgstr ""
+msgstr "從場景樹中選擇一個 AnimationPlayer 來編輯動畫。"
#: editor/animation_editor.cpp
msgid "Key"
-msgstr ""
+msgstr "鍵"
#: editor/animation_editor.cpp
msgid "Transition"
-msgstr ""
+msgstr "轉場"
#: editor/animation_editor.cpp
msgid "Scale Ratio:"
-msgstr ""
+msgstr "縮放比例:"
#: editor/animation_editor.cpp
msgid "Call Functions in Which Node?"
-msgstr ""
+msgstr "呼叫哪個節點裡的函式?"
#: editor/animation_editor.cpp
msgid "Remove invalid keys"
-msgstr ""
+msgstr "移除無效按鍵"
#: editor/animation_editor.cpp
msgid "Remove unresolved and empty tracks"
@@ -341,27 +349,27 @@ msgstr ""
#: editor/animation_editor.cpp
msgid "Clean-up all animations"
-msgstr ""
+msgstr "清除所有動畫"
#: editor/animation_editor.cpp
msgid "Clean-Up Animation(s) (NO UNDO!)"
-msgstr ""
+msgstr "清除動畫 (無法復原!)"
#: editor/animation_editor.cpp
msgid "Clean-Up"
-msgstr ""
+msgstr "清除"
#: editor/array_property_edit.cpp
msgid "Resize Array"
-msgstr ""
+msgstr "調整陣列大小"
#: editor/array_property_edit.cpp
msgid "Change Array Value Type"
-msgstr ""
+msgstr "調整陣列資料型態"
#: editor/array_property_edit.cpp
msgid "Change Array Value"
-msgstr ""
+msgstr "調整陣列資料"
#: editor/code_editor.cpp
msgid "Go to Line"
@@ -385,7 +393,7 @@ msgstr "符合大小寫"
#: editor/code_editor.cpp
msgid "Whole Words"
-msgstr ""
+msgstr "整個字"
#: editor/code_editor.cpp
msgid "Replace"
@@ -421,17 +429,17 @@ msgstr "列:"
#: editor/connections_dialog.cpp
msgid "Method in target Node must be specified!"
-msgstr ""
+msgstr "需指定在目標節點上的方法!"
#: editor/connections_dialog.cpp
msgid ""
"Target method not found! Specify a valid method or attach a script to target "
"Node."
-msgstr ""
+msgstr "找不到目標方法! 指定一個對的方法或附加一個腳本到目標的節點上。"
#: editor/connections_dialog.cpp
msgid "Connect To Node:"
-msgstr ""
+msgstr "連接到節點:"
#: editor/connections_dialog.cpp editor/editor_autoload_settings.cpp
#: editor/groups_editor.cpp editor/plugins/item_list_editor_plugin.cpp
@@ -448,15 +456,15 @@ msgstr "移除"
#: editor/connections_dialog.cpp
msgid "Add Extra Call Argument:"
-msgstr ""
+msgstr "新增額外呼叫參數:"
#: editor/connections_dialog.cpp
msgid "Extra Call Arguments:"
-msgstr ""
+msgstr "額外呼叫參數:"
#: editor/connections_dialog.cpp
msgid "Path to Node:"
-msgstr ""
+msgstr "節點路徑:"
#: editor/connections_dialog.cpp
msgid "Make Function"
@@ -469,7 +477,7 @@ msgstr "延遲"
#: editor/connections_dialog.cpp
msgid "Oneshot"
-msgstr ""
+msgstr "一次性"
#: editor/connections_dialog.cpp editor/dependency_editor.cpp
#: editor/export_template_manager.cpp
@@ -483,23 +491,23 @@ msgstr ""
#: editor/run_settings_dialog.cpp editor/settings_config_dialog.cpp
#: modules/visual_script/visual_script_editor.cpp
msgid "Close"
-msgstr ""
+msgstr "關閉"
#: editor/connections_dialog.cpp
msgid "Connect"
-msgstr ""
+msgstr "連接"
#: editor/connections_dialog.cpp
msgid "Connect '%s' to '%s'"
-msgstr ""
+msgstr "連接 '%s' 到 '%s'"
#: editor/connections_dialog.cpp
msgid "Connecting Signal:"
-msgstr ""
+msgstr "連結訊號:"
#: editor/connections_dialog.cpp
msgid "Disconnect '%s' from '%s'"
-msgstr ""
+msgstr "將 '%s' 從 '%s' 中斷連接"
#: editor/connections_dialog.cpp
msgid "Connect.."
@@ -512,7 +520,7 @@ msgstr "斷線"
#: editor/connections_dialog.cpp editor/editor_help.cpp editor/node_dock.cpp
msgid "Signals"
-msgstr ""
+msgstr "信號"
#: editor/create_dialog.cpp
#, fuzzy
@@ -522,7 +530,7 @@ msgstr "變更鏡頭尺寸"
#: editor/create_dialog.cpp editor/project_settings_editor.cpp
#: modules/visual_script/visual_script_editor.cpp
msgid "Change"
-msgstr ""
+msgstr "更換"
#: editor/create_dialog.cpp
#, fuzzy
@@ -559,11 +567,11 @@ msgstr "描述:"
#: editor/dependency_editor.cpp
msgid "Search Replacement For:"
-msgstr ""
+msgstr "尋找取代目標:"
#: editor/dependency_editor.cpp
msgid "Dependencies For:"
-msgstr ""
+msgstr "相依於:"
#: editor/dependency_editor.cpp
msgid ""
@@ -578,15 +586,17 @@ msgid ""
"Resource '%s' is in use.\n"
"Changes will take effect when reloaded."
msgstr ""
+"'%s' 資源正在使用中。\n"
+"變更會在重新載入時套用。"
#: editor/dependency_editor.cpp
#: modules/gdnative/gdnative_library_editor_plugin.cpp
msgid "Dependencies"
-msgstr ""
+msgstr "相依"
#: editor/dependency_editor.cpp
msgid "Resource"
-msgstr ""
+msgstr "資源"
#: editor/dependency_editor.cpp editor/editor_autoload_settings.cpp
#: editor/project_manager.cpp editor/project_settings_editor.cpp
@@ -596,15 +606,15 @@ msgstr "路徑"
#: editor/dependency_editor.cpp
msgid "Dependencies:"
-msgstr ""
+msgstr "相依:"
#: editor/dependency_editor.cpp
msgid "Fix Broken"
-msgstr ""
+msgstr "修復損壞的"
#: editor/dependency_editor.cpp
msgid "Dependency Editor"
-msgstr ""
+msgstr "相依性編輯器"
#: editor/dependency_editor.cpp
msgid "Search Replacement Resource:"
@@ -619,7 +629,7 @@ msgstr "開啟"
#: editor/dependency_editor.cpp
msgid "Owners Of:"
-msgstr ""
+msgstr "擁有者:"
#: editor/dependency_editor.cpp
msgid "Remove selected files from the project? (no undo)"
@@ -636,7 +646,7 @@ msgstr ""
#: editor/dependency_editor.cpp
msgid "Cannot remove:"
-msgstr ""
+msgstr "無法移除:"
#: editor/dependency_editor.cpp
msgid "Error loading:"
@@ -656,7 +666,7 @@ msgstr "該執行什麼操作呢?"
#: editor/dependency_editor.cpp
msgid "Fix Dependencies"
-msgstr ""
+msgstr "修復相依性"
#: editor/dependency_editor.cpp
msgid "Errors loading!"
@@ -704,7 +714,7 @@ msgstr ""
#: editor/editor_about.cpp
msgid "Thanks!"
-msgstr ""
+msgstr "謝謝!"
#: editor/editor_about.cpp
msgid "Godot Engine contributors"
@@ -725,7 +735,7 @@ msgstr "專案創始人"
#: editor/editor_about.cpp
msgid "Developers"
-msgstr ""
+msgstr "開發者"
#: editor/editor_about.cpp
msgid "Authors"
@@ -761,7 +771,7 @@ msgstr ""
#: editor/editor_about.cpp
msgid "License"
-msgstr ""
+msgstr "授權"
#: editor/editor_about.cpp
msgid "Thirdparty License"
@@ -785,7 +795,7 @@ msgstr ""
#: editor/editor_about.cpp
msgid "Licenses"
-msgstr ""
+msgstr "授權"
#: editor/editor_asset_installer.cpp editor/project_manager.cpp
msgid "Error opening package file, not in zip format."
@@ -808,7 +818,7 @@ msgstr ""
#: editor/editor_asset_installer.cpp
#: editor/plugins/asset_library_editor_plugin.cpp
msgid "Install"
-msgstr ""
+msgstr "安裝"
#: editor/editor_asset_installer.cpp
msgid "Package Installer"
@@ -869,7 +879,7 @@ msgstr ""
#: editor/editor_audio_buses.cpp
msgid "Mute"
-msgstr ""
+msgstr "靜音"
#: editor/editor_audio_buses.cpp
msgid "Bypass"
@@ -981,11 +991,11 @@ msgstr ""
#: editor/editor_autoload_settings.cpp
msgid "Invalid name."
-msgstr ""
+msgstr "不能使用的名稱。"
#: editor/editor_autoload_settings.cpp
msgid "Valid characters:"
-msgstr ""
+msgstr "合法字元:"
#: editor/editor_autoload_settings.cpp
msgid "Invalid name. Must not collide with an existing engine class name."
@@ -1059,7 +1069,7 @@ msgstr "名稱"
#: editor/editor_autoload_settings.cpp
msgid "Singleton"
-msgstr ""
+msgstr "單例"
#: editor/editor_data.cpp
msgid "Updating Scene"
@@ -1196,7 +1206,7 @@ msgstr "往上"
#: editor/editor_file_dialog.cpp
msgid "Toggle Hidden Files"
-msgstr ""
+msgstr "切換顯示隱藏檔案"
#: editor/editor_file_dialog.cpp
msgid "Toggle Favorite"
@@ -1396,6 +1406,10 @@ msgstr "清除"
msgid "Clear Output"
msgstr "輸出:"
+#: editor/editor_node.cpp
+msgid "Project export failed with error code %d."
+msgstr ""
+
#: editor/editor_node.cpp editor/plugins/animation_player_editor_plugin.cpp
msgid "Error saving resource!"
msgstr ""
@@ -1536,7 +1550,7 @@ msgstr ""
#: editor/editor_node.cpp
msgid "Expand all properties"
-msgstr ""
+msgstr "展開所有屬性"
#: editor/editor_node.cpp
msgid "Collapse all properties"
@@ -1653,17 +1667,17 @@ msgid "Export Mesh Library"
msgstr ""
#: editor/editor_node.cpp
-#, fuzzy
msgid "This operation can't be done without a root node."
-msgstr "此操作無法復原, 確定要還原嗎?"
+msgstr "此操作無法在沒有根節點的情況下進行。"
#: editor/editor_node.cpp
msgid "Export Tile Set"
msgstr ""
#: editor/editor_node.cpp
+#, fuzzy
msgid "This operation can't be done without a selected node."
-msgstr ""
+msgstr "此操作需要有選擇著節點才能進行。"
#: editor/editor_node.cpp
msgid "Current scene not saved. Open anyway?"
@@ -2152,8 +2166,9 @@ msgid "Import"
msgstr ""
#: editor/editor_node.cpp
+#, fuzzy
msgid "Node"
-msgstr ""
+msgstr "節點"
#: editor/editor_node.cpp
msgid "FileSystem"
@@ -3652,6 +3667,14 @@ msgid "Show Guides"
msgstr ""
#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Origin"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
+msgid "Show Viewport"
+msgstr ""
+
+#: editor/plugins/canvas_item_editor_plugin.cpp
msgid "Center Selection"
msgstr ""
@@ -3942,6 +3965,10 @@ msgid "Mesh has not surface to create outlines from!"
msgstr ""
#: editor/plugins/mesh_instance_editor_plugin.cpp
+msgid "Mesh primitive type is not PRIMITIVE_TRIANGLES!"
+msgstr ""
+
+#: editor/plugins/mesh_instance_editor_plugin.cpp
msgid "Could not create outline!"
msgstr ""
@@ -5625,6 +5652,14 @@ msgid "Checked Item"
msgstr ""
#: editor/plugins/theme_editor_plugin.cpp
+msgid "Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
+msgid "Checked Radio Item"
+msgstr ""
+
+#: editor/plugins/theme_editor_plugin.cpp
msgid "Has"
msgstr ""
@@ -6112,7 +6147,9 @@ msgid "Mouse Button"
msgstr ""
#: editor/project_settings_editor.cpp
-msgid "Invalid action (anything goes but '/' or ':')."
+msgid ""
+"Invalid action name. it cannot be empty nor contain '/', ':', '=', '\\' or "
+"'\"'"
msgstr ""
#: editor/project_settings_editor.cpp
@@ -6258,10 +6295,6 @@ msgid "Delete Item"
msgstr "刪除"
#: editor/project_settings_editor.cpp
-msgid "Can't contain '/' or ':'"
-msgstr ""
-
-#: editor/project_settings_editor.cpp
msgid "Already existing"
msgstr ""
@@ -6800,7 +6833,7 @@ msgstr ""
#: editor/scene_tree_editor.cpp
msgid "Scene Tree (Nodes):"
-msgstr ""
+msgstr "場景樹 (節點):"
#: editor/scene_tree_editor.cpp
msgid "Node Configuration Warning!"
@@ -7334,6 +7367,10 @@ msgstr "專案設定"
msgid "Pick Distance:"
msgstr ""
+#: modules/mono/csharp_script.cpp
+msgid "Class name can't be a reserved keyword"
+msgstr ""
+
#: modules/mono/editor/godotsharp_editor.cpp
msgid "Generating solution..."
msgstr ""
@@ -7987,10 +8024,20 @@ msgid "Path property must point to a valid Spatial node to work."
msgstr ""
#: scene/3d/scenario_fx.cpp
+msgid "WorldEnvironment needs an Environment resource."
+msgstr ""
+
+#: scene/3d/scenario_fx.cpp
msgid ""
"Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."
msgstr ""
+#: scene/3d/scenario_fx.cpp
+msgid ""
+"This WorldEnvironment is ignored. Either add a Camera (for 3D scenes) or set "
+"this environment's Background Mode to Canvas (for 2D scenes)."
+msgstr ""
+
#: scene/3d/sprite_3d.cpp
msgid ""
"A SpriteFrames resource must be created or set in the 'Frames' property in "
@@ -8009,20 +8056,19 @@ msgstr ""
#: scene/gui/color_picker.cpp
msgid "Add current color as a preset"
-msgstr ""
+msgstr "將目前顏色設為預設"
#: scene/gui/dialogs.cpp
msgid "Alert!"
-msgstr ""
+msgstr "警告!"
#: scene/gui/dialogs.cpp
msgid "Please Confirm..."
-msgstr ""
+msgstr "請確認..."
#: scene/gui/file_dialog.cpp
-#, fuzzy
msgid "Select this Folder"
-msgstr "僅選擇區域"
+msgstr "選擇此資料夾"
#: scene/gui/popup.cpp
msgid ""
@@ -8040,13 +8086,15 @@ msgstr ""
#: scene/gui/tree.cpp
msgid "(Other)"
-msgstr ""
+msgstr "(其它)"
#: scene/main/scene_tree.cpp
msgid ""
"Default Environment as specified in Project Settings (Rendering -> "
"Environment -> Default Environment) could not be loaded."
msgstr ""
+"在專案設定中的預設環境(Rendering -> Environment -> Default Environment)不能被"
+"載入"
#: scene/main/viewport.cpp
msgid ""
diff --git a/main/SCsub b/main/SCsub
index 9eddc4f5b..e2bf03234 100644
--- a/main/SCsub
+++ b/main/SCsub
@@ -121,7 +121,7 @@ def make_default_controller_mappings(target, source, env):
g.write("\t\"{}\",\n".format(mapping))
g.write("#endif\n")
- g.write("};\n")
+ g.write("\tNULL\n};\n")
g.close()
env.main_sources = []
diff --git a/main/input_default.cpp b/main/input_default.cpp
index c3bc83b2d..29d30110e 100644
--- a/main/input_default.cpp
+++ b/main/input_default.cpp
@@ -127,6 +127,14 @@ bool InputDefault::is_action_just_released(const StringName &p_action) const {
}
}
+float InputDefault::get_action_strength(const StringName &p_action) const {
+ const Map<StringName, Action>::Element *E = action_state.find(p_action);
+ if (!E)
+ return 0.0f;
+
+ return E->get().strength;
+}
+
float InputDefault::get_joy_axis(int p_device, int p_axis) const {
_THREAD_SAFE_METHOD_
@@ -186,8 +194,6 @@ void InputDefault::joy_connection_changed(int p_idx, bool p_connected, String p_
Joypad js;
js.name = p_connected ? p_name : "";
js.uid = p_connected ? p_guid : "";
- js.mapping = -1;
- js.hat_current = 0;
if (p_connected) {
@@ -250,6 +256,11 @@ Vector3 InputDefault::get_gyroscope() const {
void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) {
+ _parse_input_event_impl(p_event, false);
+}
+
+void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) {
+
_THREAD_SAFE_METHOD_
Ref<InputEventKey> k = p_event;
@@ -273,25 +284,30 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) {
mouse_button_mask &= ~(1 << (mb->get_button_index() - 1));
}
- if (main_loop && emulate_touch && mb->get_button_index() == 1) {
+ Point2 pos = mb->get_global_position();
+ if (mouse_pos != pos) {
+ set_mouse_position(pos);
+ }
+
+ if (main_loop && emulate_touch_from_mouse && !p_is_emulated && mb->get_button_index() == 1) {
Ref<InputEventScreenTouch> touch_event;
touch_event.instance();
touch_event->set_pressed(mb->is_pressed());
touch_event->set_position(mb->get_position());
main_loop->input_event(touch_event);
}
-
- Point2 pos = mb->get_global_position();
- if (mouse_pos != pos) {
- set_mouse_position(pos);
- }
}
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
- if (main_loop && emulate_touch && mm->get_button_mask() & 1) {
+ Point2 pos = mm->get_global_position();
+ if (mouse_pos != pos) {
+ set_mouse_position(pos);
+ }
+
+ if (main_loop && emulate_touch_from_mouse && !p_is_emulated && mm->get_button_mask() & 1) {
Ref<InputEventScreenDrag> drag_event;
drag_event.instance();
@@ -303,6 +319,58 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) {
}
}
+ if (emulate_mouse_from_touch) {
+
+ Ref<InputEventScreenTouch> st = p_event;
+
+ if (st.is_valid()) {
+ bool translate = false;
+ if (st->is_pressed()) {
+ if (mouse_from_touch_index == -1) {
+ translate = true;
+ mouse_from_touch_index = st->get_index();
+ }
+ } else {
+ if (st->get_index() == mouse_from_touch_index) {
+ translate = true;
+ mouse_from_touch_index = -1;
+ }
+ }
+
+ if (translate) {
+ Ref<InputEventMouseButton> button_event;
+ button_event.instance();
+
+ button_event->set_position(st->get_position());
+ button_event->set_global_position(st->get_position());
+ button_event->set_pressed(st->is_pressed());
+ button_event->set_button_index(BUTTON_LEFT);
+ if (st->is_pressed()) {
+ button_event->set_button_mask(mouse_button_mask | (1 << BUTTON_LEFT - 1));
+ } else {
+ button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1));
+ }
+
+ _parse_input_event_impl(button_event, true);
+ }
+ }
+
+ Ref<InputEventScreenDrag> sd = p_event;
+
+ if (sd.is_valid() && sd->get_index() == mouse_from_touch_index) {
+ Ref<InputEventMouseMotion> motion_event;
+ motion_event.instance();
+
+ motion_event->set_position(sd->get_position());
+ motion_event->set_global_position(sd->get_position());
+ motion_event->set_relative(sd->get_relative());
+ motion_event->set_speed(sd->get_speed());
+ motion_event->set_button_mask(mouse_button_mask);
+
+ _parse_input_event_impl(motion_event, true);
+ }
+ }
+
Ref<InputEventJoypadButton> jb = p_event;
if (jb.is_valid()) {
@@ -330,16 +398,18 @@ void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) {
}
}
- if (!p_event->is_echo()) {
- for (const Map<StringName, InputMap::Action>::Element *E = InputMap::get_singleton()->get_action_map().front(); E; E = E->next()) {
+ for (const Map<StringName, InputMap::Action>::Element *E = InputMap::get_singleton()->get_action_map().front(); E; E = E->next()) {
+ if (InputMap::get_singleton()->event_is_action(p_event, E->key())) {
- if (InputMap::get_singleton()->event_is_action(p_event, E->key()) && is_action_pressed(E->key()) != p_event->is_pressed()) {
+ // Save the action's state
+ if (!p_event->is_echo() && is_action_pressed(E->key()) != p_event->is_action_pressed(E->key())) {
Action action;
action.physics_frame = Engine::get_singleton()->get_physics_frames();
action.idle_frame = Engine::get_singleton()->get_idle_frames();
- action.pressed = p_event->is_pressed();
+ action.pressed = p_event->is_action_pressed(E->key());
action_state[E->key()] = action;
}
+ action_state[E->key()].strength = p_event->get_action_strength(E->key());
}
}
@@ -413,10 +483,6 @@ void InputDefault::set_mouse_position(const Point2 &p_posf) {
mouse_speed_track.update(p_posf - mouse_pos);
mouse_pos = p_posf;
- if (custom_cursor.is_valid()) {
- //removed, please insist that we implement hardware cursors
- // VisualServer::get_singleton()->cursor_set_pos(get_mouse_position());
- }
}
Point2 InputDefault::get_mouse_position() const {
@@ -489,25 +555,59 @@ void InputDefault::action_release(const StringName &p_action) {
action_state[p_action] = action;
}
-void InputDefault::set_emulate_touch(bool p_emulate) {
+void InputDefault::set_emulate_touch_from_mouse(bool p_emulate) {
- emulate_touch = p_emulate;
+ emulate_touch_from_mouse = p_emulate;
}
-bool InputDefault::is_emulating_touchscreen() const {
+bool InputDefault::is_emulating_touch_from_mouse() const {
- return emulate_touch;
+ return emulate_touch_from_mouse;
+}
+
+// Calling this whenever the game window is focused helps unstucking the "touch mouse"
+// if the OS or its abstraction class hasn't properly reported that touch pointers raised
+void InputDefault::ensure_touch_mouse_raised() {
+
+ if (mouse_from_touch_index != -1) {
+ mouse_from_touch_index = -1;
+
+ Ref<InputEventMouseButton> button_event;
+ button_event.instance();
+
+ button_event->set_position(mouse_pos);
+ button_event->set_global_position(mouse_pos);
+ button_event->set_pressed(false);
+ button_event->set_button_index(BUTTON_LEFT);
+ button_event->set_button_mask(mouse_button_mask & ~(1 << BUTTON_LEFT - 1));
+
+ _parse_input_event_impl(button_event, true);
+ }
+}
+
+void InputDefault::set_emulate_mouse_from_touch(bool p_emulate) {
+
+ emulate_mouse_from_touch = p_emulate;
+}
+
+bool InputDefault::is_emulating_mouse_from_touch() const {
+
+ return emulate_mouse_from_touch;
+}
+
+Input::CursorShape InputDefault::get_default_cursor_shape() {
+ return default_shape;
+}
+
+void InputDefault::set_default_cursor_shape(CursorShape p_shape) {
+ default_shape = p_shape;
+ OS::get_singleton()->set_cursor_shape((OS::CursorShape)p_shape);
}
void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
if (Engine::get_singleton()->is_editor_hint())
return;
- if (custom_cursor == p_cursor)
- return;
-
- custom_cursor = p_cursor;
-
OS::get_singleton()->set_custom_mouse_cursor(p_cursor, (OS::CursorShape)p_shape, p_hotspot);
}
@@ -529,7 +629,9 @@ void InputDefault::set_mouse_in_window(bool p_in_window) {
InputDefault::InputDefault() {
mouse_button_mask = 0;
- emulate_touch = false;
+ emulate_touch_from_mouse = false;
+ emulate_mouse_from_touch = false;
+ mouse_from_touch_index = -1;
main_loop = NULL;
hat_map_default[HAT_UP].type = TYPE_BUTTON;
@@ -787,12 +889,12 @@ InputDefault::JoyEvent InputDefault::_find_to_event(String p_to) {
JoyEvent ret;
ret.type = -1;
+ ret.index = 0;
int i = 0;
while (buttons[i]) {
if (p_to == buttons[i]) {
- //printf("mapping button %s\n", buttons[i]);
ret.type = TYPE_BUTTON;
ret.index = i;
ret.value = 0;
diff --git a/main/input_default.h b/main/input_default.h
index 0479fdc0f..2e3cae852 100644
--- a/main/input_default.h
+++ b/main/input_default.h
@@ -55,11 +55,15 @@ class InputDefault : public Input {
uint64_t physics_frame;
uint64_t idle_frame;
bool pressed;
+ float strength;
};
Map<StringName, Action> action_state;
- bool emulate_touch;
+ bool emulate_touch_from_mouse;
+ bool emulate_mouse_from_touch;
+
+ int mouse_from_touch_index;
struct VibrationInfo {
float weak_magnitude;
@@ -96,7 +100,6 @@ class InputDefault : public Input {
int hat_current;
Joypad() {
-
for (int i = 0; i < JOY_AXIS_MAX; i++) {
last_axis[i] = 0.0f;
@@ -109,13 +112,14 @@ class InputDefault : public Input {
last_hat = HAT_MASK_CENTER;
filter = 0.01f;
mapping = -1;
+ hat_current = 0;
}
};
SpeedTrack mouse_speed_track;
Map<int, Joypad> joy_names;
int fallback_mapping;
- RES custom_cursor;
+ CursorShape default_shape = CURSOR_ARROW;
public:
enum HatMask {
@@ -175,6 +179,8 @@ private:
void _axis_event(int p_device, int p_axis, float p_value);
float _handle_deadzone(int p_device, int p_axis, float p_value);
+ void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated);
+
public:
virtual bool is_key_pressed(int p_scancode) const;
virtual bool is_mouse_button_pressed(int p_button) const;
@@ -182,6 +188,7 @@ public:
virtual bool is_action_pressed(const StringName &p_action) const;
virtual bool is_action_just_pressed(const StringName &p_action) const;
virtual bool is_action_just_released(const StringName &p_action) const;
+ virtual float get_action_strength(const StringName &p_action) const;
virtual float get_joy_axis(int p_device, int p_axis) const;
String get_joy_name(int p_idx);
@@ -223,9 +230,15 @@ public:
void iteration(float p_step);
- void set_emulate_touch(bool p_emulate);
- virtual bool is_emulating_touchscreen() const;
+ void set_emulate_touch_from_mouse(bool p_emulate);
+ virtual bool is_emulating_touch_from_mouse() const;
+ void ensure_touch_mouse_raised();
+
+ void set_emulate_mouse_from_touch(bool p_emulate);
+ virtual bool is_emulating_mouse_from_touch() const;
+ virtual CursorShape get_default_cursor_shape();
+ virtual void set_default_cursor_shape(CursorShape p_shape);
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
virtual void set_mouse_in_window(bool p_in_window);
diff --git a/main/main.cpp b/main/main.cpp
index a59ca3da3..ffc02d882 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -1141,13 +1141,16 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
GLOBAL_DEF("application/config/icon", String());
ProjectSettings::get_singleton()->set_custom_property_info("application/config/icon", PropertyInfo(Variant::STRING, "application/config/icon", PROPERTY_HINT_FILE, "*.png,*.webp"));
- if (bool(GLOBAL_DEF("display/window/handheld/emulate_touchscreen", false))) {
- if (!OS::get_singleton()->has_touchscreen_ui_hint() && Input::get_singleton() && !(editor || project_manager)) {
- //only if no touchscreen ui hint, set emulation
- InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton());
- if (id)
- id->set_emulate_touch(true);
+ InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton());
+ if (id) {
+ if (bool(GLOBAL_DEF("input/pointing_devices/emulate_touch_from_mouse", false)) && !(editor || project_manager)) {
+ if (!OS::get_singleton()->has_touchscreen_ui_hint()) {
+ //only if no touchscreen ui hint, set emulation
+ id->set_emulate_touch_from_mouse(true);
+ }
}
+
+ id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF("input/pointing_devices/emulate_mouse_from_touch", true)));
}
MAIN_PRINT("Main: Load Remaps");
@@ -1312,7 +1315,7 @@ bool Main::start() {
DocData docsrc;
Map<String, String> doc_data_classes;
Set<String> checked_paths;
- print_line("Loading docs..");
+ print_line("Loading docs...");
for (int i = 0; i < _doc_data_class_path_count; i++) {
String path = doc_tool.plus_file(_doc_data_class_paths[i].path);
@@ -1330,14 +1333,14 @@ bool Main::start() {
checked_paths.insert(index_path);
print_line("Loading docs from: " + index_path);
- print_line("Merging docs..");
+ print_line("Merging docs...");
doc.merge_from(docsrc);
for (Set<String>::Element *E = checked_paths.front(); E; E = E->next()) {
print_line("Erasing old docs at: " + E->get());
DocData::erase_classes(E->get());
}
- print_line("Generating new docs..");
+ print_line("Generating new docs...");
doc.save_classes(index_path, doc_data_classes);
return false;
@@ -1512,7 +1515,7 @@ bool Main::start() {
bool snap_controls = GLOBAL_DEF("gui/common/snap_controls_to_pixels", true);
sml->get_root()->set_snap_controls_to_pixels(snap_controls);
- bool font_oversampling = GLOBAL_DEF("rendering/quality/dynamic_fonts/use_oversampling", false);
+ bool font_oversampling = GLOBAL_DEF("rendering/quality/dynamic_fonts/use_oversampling", true);
sml->set_use_font_oversampling(font_oversampling);
} else {
@@ -1525,7 +1528,7 @@ bool Main::start() {
sml->set_auto_accept_quit(GLOBAL_DEF("application/config/auto_accept_quit", true));
sml->set_quit_on_go_back(GLOBAL_DEF("application/config/quit_on_go_back", true));
GLOBAL_DEF("gui/common/snap_controls_to_pixels", true);
- GLOBAL_DEF("rendering/quality/dynamic_fonts/use_oversampling", false);
+ GLOBAL_DEF("rendering/quality/dynamic_fonts/use_oversampling", true);
}
String local_game_path;
diff --git a/main/tests/test_gui.cpp b/main/tests/test_gui.cpp
index b7b6c2169..305b74971 100644
--- a/main/tests/test_gui.cpp
+++ b/main/tests/test_gui.cpp
@@ -185,6 +185,10 @@ public:
popup->add_item("Popup");
popup->add_check_item("Check Popup");
popup->set_item_checked(4, true);
+ popup->add_separator();
+ popup->add_radio_check_item("Option A");
+ popup->set_item_checked(6, true);
+ popup->add_radio_check_item("Option B");
OptionButton *options = memnew(OptionButton);
diff --git a/main/tests/test_oa_hash_map.cpp b/main/tests/test_oa_hash_map.cpp
index ac65fdf19..0e34faace 100644
--- a/main/tests/test_oa_hash_map.cpp
+++ b/main/tests/test_oa_hash_map.cpp
@@ -49,7 +49,7 @@ MainLoop *test() {
map.set(42, 11880);
int value;
- map.lookup(42, &value);
+ map.lookup(42, value);
OS::get_singleton()->print("capacity %d\n", map.get_capacity());
OS::get_singleton()->print("elements %d\n", map.get_num_elements());
@@ -72,7 +72,7 @@ MainLoop *test() {
uint32_t num_elems = 0;
for (int i = 0; i < 500; i++) {
int tmp;
- if (map.lookup(i, &tmp))
+ if (map.lookup(i, tmp) && tmp == i * 2)
num_elems++;
}
@@ -88,7 +88,7 @@ MainLoop *test() {
map.set("Godot rocks", 42);
for (OAHashMap<String, int>::Iterator it = map.iter(); it.valid; it = map.next_iter(it)) {
- OS::get_singleton()->print("map[\"%s\"] = %d\n", it.key->utf8().get_data(), *it.data);
+ OS::get_singleton()->print("map[\"%s\"] = %d\n", it.key->utf8().get_data(), *it.value);
}
}
diff --git a/methods.py b/methods.py
index 792417866..7cdc16007 100644
--- a/methods.py
+++ b/methods.py
@@ -93,7 +93,6 @@ def include_file_in_legacygl_header(filename, header_data, depth):
enumbase = ifdefline[:ifdefline.find("_EN_")]
ifdefline = ifdefline.replace("_EN_", "_")
line = line.replace("_EN_", "_")
-# print(enumbase+":"+ifdefline);
if (enumbase not in header_data.enums):
header_data.enums[enumbase] = []
if (ifdefline not in header_data.enums[enumbase]):
@@ -192,9 +191,6 @@ def include_file_in_legacygl_header(filename, header_data, depth):
line = line.replace("\r", "")
line = line.replace("\n", "")
- # line=line.replace("\\","\\\\")
- # line=line.replace("\"","\\\"")
- # line=line+"\\n\\"
if (header_data.reading == "vertex"):
header_data.vertex_lines += [line]
@@ -224,7 +220,6 @@ def build_legacygl_header(filename, include, class_suffix, output_attribs, gles2
out_file_base = out_file
out_file_base = out_file_base[out_file_base.rfind("/") + 1:]
out_file_base = out_file_base[out_file_base.rfind("\\") + 1:]
-# print("out file "+out_file+" base " +out_file_base)
out_file_ifdef = out_file_base.replace(".", "_").upper()
fd.write("#ifndef " + out_file_ifdef + class_suffix + "_120\n")
fd.write("#define " + out_file_ifdef + class_suffix + "_120\n")
@@ -262,10 +257,6 @@ def build_legacygl_header(filename, include, class_suffix, output_attribs, gles2
fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, int16_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n")
fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, uint32_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n")
fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, int32_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n")
- #fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, uint64_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n");
- #fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, int64_t p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n");
- #fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, unsigned long p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n");
- #fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, long p_value) { _FU glUniform1i(get_uniform(p_uniform),p_value); }\n\n");
fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Color& p_color) { _FU GLfloat col[4]={p_color.r,p_color.g,p_color.b,p_color.a}; glUniform4fv(get_uniform(p_uniform),1,col); }\n\n")
fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Vector2& p_vec2) { _FU GLfloat vec2[2]={p_vec2.x,p_vec2.y}; glUniform2fv(get_uniform(p_uniform),1,vec2); }\n\n")
fd.write("\t_FORCE_INLINE_ void set_uniform(Uniforms p_uniform, const Vector3& p_vec3) { _FU GLfloat vec3[3]={p_vec3.x,p_vec3.y,p_vec3.z}; glUniform3fv(get_uniform(p_uniform),1,vec3); }\n\n")
@@ -367,10 +358,8 @@ def build_legacygl_header(filename, include, class_suffix, output_attribs, gles2
x = header_data.enums[xv]
bits = 1
amt = len(x)
-# print(x)
while(2**bits < amt):
bits += 1
-# print("amount: "+str(amt)+" bits "+str(bits));
strs = "{"
for i in range(amt):
strs += "\"#define " + x[i] + "\\n\","
@@ -658,7 +647,6 @@ def win32_spawn(sh, escape, cmd, args, env):
newargs = ' '.join(args[1:])
cmdline = cmd + " " + newargs
startupinfo = subprocess.STARTUPINFO()
- #startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
for e in env:
if type(env[e]) != type(""):
env[e] = str(env[e])
@@ -998,7 +986,6 @@ def detect_visual_c_compiler_version(tools_env):
# and for VS 2017 and newer we check VCTOOLSINSTALLDIR:
if 'VCTOOLSINSTALLDIR' in tools_env:
- # print("Checking VCTOOLSINSTALLDIR")
# Newer versions have a different path available
vc_amd64_compiler_detection_index = tools_env["PATH"].upper().find(tools_env['VCTOOLSINSTALLDIR'].upper() + "BIN\\HOSTX64\\X64;")
@@ -1027,13 +1014,6 @@ def detect_visual_c_compiler_version(tools_env):
vc_chosen_compiler_index = vc_x86_amd64_compiler_detection_index
vc_chosen_compiler_str = "x86_amd64"
- # debug help
- # print(vc_amd64_compiler_detection_index)
- # print(vc_amd64_x86_compiler_detection_index)
- # print(vc_x86_compiler_detection_index)
- # print(vc_x86_amd64_compiler_detection_index)
- # print("chosen "+str(vc_chosen_compiler_index)+ " | "+str(vc_chosen_compiler_str))
-
return vc_chosen_compiler_str
def find_visual_c_batch_file(env):
@@ -1066,7 +1046,6 @@ def generate_vs_project(env, num_jobs):
'call "' + batch_file + '" !plat!']
result = " ^& ".join(common_build_prefix + [commands])
- # print("Building commandline: ", result)
return result
env.AddToVSProject(env.core_sources)
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
index ab15e35f6..a9a40c050 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
@@ -22,14 +22,6 @@
1FF4C1851F584E3F00A41E41 /* GameKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1841F584E3F00A41E41 /* GameKit.framework */; };
1FF4C1871F584E5600A41E41 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1861F584E5600A41E41 /* StoreKit.framework */; };
1FF4C1871F584E7600A41E41 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1881F584E7600A41E41 /* StoreKit.framework */; };
- D07CD43F1C5D573600B7FB28 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */; };
- D07CD4411C5D573600B7FB28 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */; };
- D07CD4421C5D573600B7FB28 /* Default-Portrait-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */; };
- D07CD4441C5D573600B7FB28 /* Default-Landscape-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */; };
- D07CD4451C5D573600B7FB28 /* Default-Landscape@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */; };
- D07CD4461C5D573600B7FB28 /* Default-Landscape-1366h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */; };
- D07CD4471C5D573600B7FB28 /* Default-Portrait@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */; };
- D07CD4481C5D573600B7FB28 /* Default-Portrait-1366h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */; };
D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D07CD44D1C5D589C00B7FB28 /* Images.xcassets */; };
D0BCFE3818AEBDA2004A7AAE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCFE3718AEBDA2004A7AAE /* Foundation.framework */; };
D0BCFE3A18AEBDA2004A7AAE /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCFE3918AEBDA2004A7AAE /* CoreGraphics.framework */; };
@@ -57,14 +49,6 @@
1FF4C1881F584E7600A41E41 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
1FF4C1881F584E6300A41E41 /* $binary.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = $binary.entitlements; sourceTree = "<group>"; };
1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dummy.cpp; sourceTree = "<group>"; };
- D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
- D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = "<group>"; };
- D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait-736h@3x.png"; sourceTree = "<group>"; };
- D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape-736h@3x.png"; sourceTree = "<group>"; };
- D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x.png"; sourceTree = "<group>"; };
- D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape-1366h@2x.png"; sourceTree = "<group>"; };
- D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x.png"; sourceTree = "<group>"; };
- D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait-1366h@2x.png"; sourceTree = "<group>"; };
D07CD44D1C5D589C00B7FB28 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
D0BCFE3418AEBDA2004A7AAE /* $binary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = $binary.app; sourceTree = BUILT_PRODUCTS_DIR; };
D0BCFE3718AEBDA2004A7AAE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -159,14 +143,6 @@
isa = PBXGroup;
children = (
1FF4C1881F584E6300A41E41 /* $binary.entitlements */,
- D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */,
- D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */,
- D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */,
- D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */,
- D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */,
- D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */,
- D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */,
- D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */,
D07CD44D1C5D589C00B7FB28 /* Images.xcassets */,
D0BCFE4218AEBDA2004A7AAE /* Supporting Files */,
1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */,
@@ -254,14 +230,6 @@
1F1575721F582BE20003B888 /* dylibs in Resources */,
D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */,
D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */,
- D07CD4471C5D573600B7FB28 /* Default-Portrait@2x.png in Resources */,
- D07CD4461C5D573600B7FB28 /* Default-Landscape-1366h@2x.png in Resources */,
- D07CD4411C5D573600B7FB28 /* Default-667h@2x.png in Resources */,
- D07CD43F1C5D573600B7FB28 /* Default-568h@2x.png in Resources */,
- D07CD4451C5D573600B7FB28 /* Default-Landscape@2x.png in Resources */,
- D07CD4441C5D573600B7FB28 /* Default-Landscape-736h@3x.png in Resources */,
- D07CD4421C5D573600B7FB28 /* Default-Portrait-736h@3x.png in Resources */,
- D07CD4481C5D573600B7FB28 /* Default-Portrait-1366h@2x.png in Resources */,
D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */,
$additional_pbx_resources_build
);
@@ -377,6 +345,7 @@
buildSettings = {
ARCHS = "$godot_archs";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_ENTITLEMENTS = $binary/$binary.entitlements;
CODE_SIGN_IDENTITY = "$code_sign_identity_debug";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_debug";
@@ -401,6 +370,7 @@
buildSettings = {
ARCHS = "$godot_archs";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CODE_SIGN_ENTITLEMENTS = $binary/$binary.entitlements;
CODE_SIGN_IDENTITY = "$code_sign_identity_release";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_release";
diff --git a/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png
deleted file mode 100644
index 1d5e47266..000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png
deleted file mode 100644
index b51598fed..000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png
deleted file mode 100644
index ec5b4f788..000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png
deleted file mode 100644
index 2a025b745..000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png
deleted file mode 100644
index 7099f3e18..000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png
deleted file mode 100644
index a6d054ba2..000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png
deleted file mode 100644
index 8c44edbcc..000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png
deleted file mode 100644
index a6d054ba2..000000000
--- a/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png
+++ /dev/null
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 000000000..ce81d7602
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,102 @@
+{
+ "images" : [
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "2436h",
+ "filename" : "Default-Portrait-X.png",
+ "minimum-system-version" : "11.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "2436h",
+ "filename" : "Default-Landscape-X.png",
+ "minimum-system-version" : "11.0",
+ "orientation" : "landscape",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-Portrait-736h@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "736h",
+ "filename" : "Default-Landscape-736h@3x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "landscape",
+ "scale" : "3x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "667h",
+ "filename" : "Default-667h@2x.png",
+ "minimum-system-version" : "8.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "iphone",
+ "filename" : "Default-480h@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "extent" : "full-screen",
+ "idiom" : "iphone",
+ "subtype" : "retina4",
+ "filename" : "Default-568h@2x.png",
+ "minimum-system-version" : "7.0",
+ "orientation" : "portrait",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "1x"
+ },
+ {
+ "orientation" : "portrait",
+ "idiom" : "ipad",
+ "filename" : "Default-Portrait@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ },
+ {
+ "orientation" : "landscape",
+ "idiom" : "ipad",
+ "filename" : "Default-Landscape@2x.png",
+ "extent" : "full-screen",
+ "minimum-system-version" : "7.0",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+} \ No newline at end of file
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png
new file mode 100644
index 000000000..6b9b10daa
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
new file mode 100644
index 000000000..50497496b
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png
new file mode 100644
index 000000000..1c489de69
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png
new file mode 100644
index 000000000..d82dfce93
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
new file mode 100644
index 000000000..5120595df
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png
new file mode 100644
index 000000000..cf6cf1347
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
new file mode 100644
index 000000000..4eb167ae1
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png
new file mode 100644
index 000000000..a9f951dea
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png
new file mode 100644
index 000000000..06d16412e
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png
new file mode 100644
index 000000000..d70cab17b
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png
Binary files differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
new file mode 100644
index 000000000..f93497107
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
Binary files differ
diff --git a/misc/dist/linux/godot.6 b/misc/dist/linux/godot.6
index 86abdf6ef..26ebe01af 100644
--- a/misc/dist/linux/godot.6
+++ b/misc/dist/linux/godot.6
@@ -59,7 +59,7 @@ Password for remote filesystem.
Audio driver ('PulseAudio', 'ALSA').
.TP
\fB\-\-video\-driver\fR <driver>
-Video driver ('GLES3').
+Video driver ('GLES3', 'GLES2').
.SS "Display options:"
.TP
\fB\-f\fR, \fB\-\-fullscreen\fR
diff --git a/misc/travis/scons-local-osx.sh b/misc/travis/scons-local-osx.sh
index d9d7d98b3..fe7b43aad 100755
--- a/misc/travis/scons-local-osx.sh
+++ b/misc/travis/scons-local-osx.sh
@@ -5,10 +5,10 @@ echo "Download and install Scons local package ..."
echo
echo "Downloading sources ..."
-curl -L -O http://prdownloads.sourceforge.net/scons/scons-local-3.0.0.zip # latest version available here: http://scons.org/pages/download.html
+curl -L -O https://downloads.sourceforge.net/scons/scons-local-3.0.1.zip # latest version available here: http://scons.org/pages/download.html
echo "Extracting to build directory ..."
-unzip -qq -n scons-local-3.0.0.zip -d $TRAVIS_BUILD_DIR/scons-local
+unzip -qq -n scons-local-3.0.1.zip -d $TRAVIS_BUILD_DIR/scons-local
echo "Installing symlinks ..."
ln -s $TRAVIS_BUILD_DIR/scons-local/scons.py /usr/local/bin/scons
diff --git a/modules/bullet/area_bullet.cpp b/modules/bullet/area_bullet.cpp
index 648919e61..bfb452d10 100644
--- a/modules/bullet/area_bullet.cpp
+++ b/modules/bullet/area_bullet.cpp
@@ -68,7 +68,9 @@ AreaBullet::AreaBullet() :
}
AreaBullet::~AreaBullet() {
- remove_all_overlapping_instantly();
+ // signal are handled by godot, so just clear without notify
+ for (int i = overlappingObjects.size() - 1; 0 <= i; --i)
+ overlappingObjects[i].object->on_exit_area(this);
}
void AreaBullet::dispatch_callbacks() {
@@ -121,23 +123,21 @@ void AreaBullet::scratch() {
isScratched = true;
}
-void AreaBullet::remove_all_overlapping_instantly() {
- CollisionObjectBullet *supportObject;
+void AreaBullet::clear_overlaps(bool p_notify) {
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- supportObject = overlappingObjects[i].object;
- call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED);
- supportObject->on_exit_area(this);
+ if (p_notify)
+ call_event(overlappingObjects[i].object, PhysicsServer::AREA_BODY_REMOVED);
+ overlappingObjects[i].object->on_exit_area(this);
}
overlappingObjects.clear();
}
-void AreaBullet::remove_overlapping_instantly(CollisionObjectBullet *p_object) {
- CollisionObjectBullet *supportObject;
+void AreaBullet::remove_overlap(CollisionObjectBullet *p_object, bool p_notify) {
for (int i = overlappingObjects.size() - 1; 0 <= i; --i) {
- supportObject = overlappingObjects[i].object;
- if (supportObject == p_object) {
- call_event(supportObject, PhysicsServer::AREA_BODY_REMOVED);
- supportObject->on_exit_area(this);
+ if (overlappingObjects[i].object == p_object) {
+ if (p_notify)
+ call_event(overlappingObjects[i].object, PhysicsServer::AREA_BODY_REMOVED);
+ overlappingObjects[i].object->on_exit_area(this);
overlappingObjects.remove(i);
break;
}
diff --git a/modules/bullet/area_bullet.h b/modules/bullet/area_bullet.h
index 78136d574..b2046c684 100644
--- a/modules/bullet/area_bullet.h
+++ b/modules/bullet/area_bullet.h
@@ -150,9 +150,9 @@ public:
void set_on_state_change(ObjectID p_id, const StringName &p_method, const Variant &p_udata = Variant());
void scratch();
- void remove_all_overlapping_instantly();
+ void clear_overlaps(bool p_notify);
// Dispatch the callbacks and removes from overlapping list
- void remove_overlapping_instantly(CollisionObjectBullet *p_object);
+ void remove_overlap(CollisionObjectBullet *p_object, bool p_notify);
virtual void on_collision_filters_change();
virtual void on_collision_checker_start() {}
diff --git a/modules/bullet/collision_object_bullet.cpp b/modules/bullet/collision_object_bullet.cpp
index 34aff68a4..57e4db708 100644
--- a/modules/bullet/collision_object_bullet.cpp
+++ b/modules/bullet/collision_object_bullet.cpp
@@ -49,7 +49,7 @@
CollisionObjectBullet::ShapeWrapper::~ShapeWrapper() {}
void CollisionObjectBullet::ShapeWrapper::set_transform(const Transform &p_transform) {
- G_TO_B(p_transform.get_basis().get_scale(), scale);
+ G_TO_B(p_transform.get_basis().get_scale_abs(), scale);
G_TO_B(p_transform, transform);
UNSCALE_BT_BASIS(transform);
}
@@ -68,12 +68,10 @@ CollisionObjectBullet::CollisionObjectBullet(Type p_type) :
force_shape_reset(false) {}
CollisionObjectBullet::~CollisionObjectBullet() {
- // Remove all overlapping
+ // Remove all overlapping, notify is not required since godot take care of it
for (int i = areasOverlapped.size() - 1; 0 <= i; --i) {
- areasOverlapped[i]->remove_overlapping_instantly(this);
+ areasOverlapped[i]->remove_overlap(this, /*Notify*/ false);
}
- // not required
- // areasOverlapped.clear();
destroyBulletCollisionObject();
}
@@ -160,7 +158,7 @@ int CollisionObjectBullet::get_godot_object_flags() const {
void CollisionObjectBullet::set_transform(const Transform &p_global_transform) {
- set_body_scale(p_global_transform.basis.get_scale());
+ set_body_scale(p_global_transform.basis.get_scale_abs());
btTransform bt_transform;
G_TO_B(p_global_transform, bt_transform);
diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp
index 72c982bb0..197550d68 100644
--- a/modules/bullet/godot_result_callbacks.cpp
+++ b/modules/bullet/godot_result_callbacks.cpp
@@ -63,6 +63,9 @@ bool GodotClosestRayResultCallback::needsCollision(btBroadphaseProxy *proxy0) co
}
bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ if (count >= m_resultMax)
+ return false;
+
const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
@@ -70,6 +73,7 @@ bool GodotAllConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) con
if (m_exclude->has(gObj->get_self())) {
return false;
}
+
return true;
} else {
return false;
@@ -87,7 +91,7 @@ btScalar GodotAllConvexResultCallback::addSingleResult(btCollisionWorld::LocalCo
result.collider = 0 == result.collider_id ? NULL : ObjectDB::get_instance(result.collider_id);
++count;
- return count < m_resultMax;
+ return 1; // not used by bullet
}
bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
@@ -106,7 +110,7 @@ bool GodotKinClosestConvexResultCallback::needsCollision(btBroadphaseProxy *prox
if (gObj->getType() == CollisionObjectBullet::TYPE_AREA)
return false;
- if (m_self_object->has_collision_exception(gObj))
+ if (m_self_object->has_collision_exception(gObj) || gObj->has_collision_exception(m_self_object))
return false;
}
return true;
@@ -168,10 +172,7 @@ btScalar GodotAllContactResultCallback::addSingleResult(btManifoldPoint &cp, con
result.shape = cp.m_index0;
}
- if (colObj)
- result.collider_id = colObj->get_instance_id();
- else
- result.collider_id = 0;
+ result.collider_id = colObj->get_instance_id();
result.collider = 0 == result.collider_id ? NULL : ObjectDB::get_instance(result.collider_id);
result.rid = colObj->get_self();
++m_count;
@@ -181,6 +182,9 @@ btScalar GodotAllContactResultCallback::addSingleResult(btManifoldPoint &cp, con
}
bool GodotContactPairContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
+ if (m_count >= m_resultMax)
+ return false;
+
const bool needs = GodotFilterCallback::test_collision_filters(m_collisionFilterGroup, m_collisionFilterMask, proxy0->m_collisionFilterGroup, proxy0->m_collisionFilterMask);
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
@@ -206,7 +210,7 @@ btScalar GodotContactPairContactResultCallback::addSingleResult(btManifoldPoint
++m_count;
- return m_count < m_resultMax;
+ return 1; // Not used by bullet
}
bool GodotRestInfoContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) const {
@@ -243,29 +247,23 @@ btScalar GodotRestInfoContactResultCallback::addSingleResult(btManifoldPoint &cp
m_rest_info_collision_object = colObj0Wrap->getCollisionObject();
}
- if (colObj)
- m_result->collider_id = colObj->get_instance_id();
- else
- m_result->collider_id = 0;
+ m_result->collider_id = colObj->get_instance_id();
m_result->rid = colObj->get_self();
m_collided = true;
}
- return cp.getDistance();
+ return 1; // Not used by bullet
}
void GodotDeepPenetrationContactResultCallback::addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth) {
- // Has penetration
- if (m_penetration_distance < ABS(depth)) {
+ if (m_penetration_distance > depth) { // Has penetration?
bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject();
-
m_penetration_distance = depth;
- m_pointCollisionObject = (isSwapped ? m_body0Wrap : m_body1Wrap)->getCollisionObject();
- m_other_compound_shape_index = isSwapped ? m_index1 : m_index0;
+ m_other_compound_shape_index = isSwapped ? m_index0 : m_index1;
m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld;
- m_pointWorld = isSwapped ? (pointInWorldOnB + normalOnBInWorld * depth) : pointInWorldOnB;
+ m_pointWorld = isSwapped ? (pointInWorldOnB + (normalOnBInWorld * depth)) : pointInWorldOnB;
}
}
diff --git a/modules/bullet/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h
index 60493d478..363051f24 100644
--- a/modules/bullet/godot_result_callbacks.h
+++ b/modules/bullet/godot_result_callbacks.h
@@ -185,21 +185,18 @@ struct GodotDeepPenetrationContactResultCallback : public btManifoldResult {
btVector3 m_pointWorld;
btScalar m_penetration_distance;
int m_other_compound_shape_index;
- const btCollisionObject *m_pointCollisionObject;
GodotDeepPenetrationContactResultCallback(const btCollisionObjectWrapper *body0Wrap, const btCollisionObjectWrapper *body1Wrap) :
btManifoldResult(body0Wrap, body1Wrap),
- m_pointCollisionObject(NULL),
m_penetration_distance(0),
m_other_compound_shape_index(0) {}
void reset() {
- m_pointCollisionObject = NULL;
m_penetration_distance = 0;
}
bool hasHit() {
- return m_pointCollisionObject;
+ return m_penetration_distance < 0;
}
virtual void addContactPoint(const btVector3 &normalOnBInWorld, const btVector3 &pointInWorldOnB, btScalar depth);
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index 5d8d391bd..76d961446 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -125,14 +125,13 @@ btScaledBvhTriangleMeshShape *ShapeBullet::create_shape_concave(btBvhTriangleMes
}
}
-btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) {
+btHeightfieldTerrainShape *ShapeBullet::create_shape_height_field(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height) {
const btScalar ignoredHeightScale(1);
- const btScalar fieldHeight(500); // Meters
const int YAxis = 1; // 0=X, 1=Y, 2=Z
const bool flipQuadEdges = false;
const void *heightsPtr = p_heights.read().ptr();
- return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, -fieldHeight, fieldHeight, YAxis, PHY_FLOAT, flipQuadEdges));
+ return bulletnew(btHeightfieldTerrainShape(p_width, p_depth, heightsPtr, ignoredHeightScale, p_min_height, p_max_height, YAxis, PHY_FLOAT, flipQuadEdges));
}
btRayShape *ShapeBullet::create_shape_ray(real_t p_length, bool p_slips_on_slope) {
@@ -337,10 +336,10 @@ void ConcavePolygonShapeBullet::setup(PoolVector<Vector3> p_faces) {
int src_face_count = faces.size();
if (0 < src_face_count) {
- btTriangleMesh *shapeInterface = bulletnew(btTriangleMesh);
-
// It counts the faces and assert the array contains the correct number of vertices.
ERR_FAIL_COND(src_face_count % 3);
+
+ btTriangleMesh *shapeInterface = bulletnew(btTriangleMesh);
src_face_count /= 3;
PoolVector<Vector3>::Read r = p_faces.read();
const Vector3 *facesr = r.ptr();
@@ -387,19 +386,44 @@ void HeightMapShapeBullet::set_data(const Variant &p_data) {
Dictionary d = p_data;
ERR_FAIL_COND(!d.has("width"));
ERR_FAIL_COND(!d.has("depth"));
- ERR_FAIL_COND(!d.has("cell_size"));
ERR_FAIL_COND(!d.has("heights"));
+ real_t l_min_height = 0.0;
+ real_t l_max_height = 0.0;
+
+ // If specified, min and max height will be used as precomputed values
+ if (d.has("min_height"))
+ l_min_height = d["min_height"];
+ if (d.has("max_height"))
+ l_max_height = d["max_height"];
+
+ ERR_FAIL_COND(l_min_height > l_max_height);
+
int l_width = d["width"];
int l_depth = d["depth"];
- real_t l_cell_size = d["cell_size"];
PoolVector<real_t> l_heights = d["heights"];
ERR_FAIL_COND(l_width <= 0);
ERR_FAIL_COND(l_depth <= 0);
- ERR_FAIL_COND(l_cell_size <= CMP_EPSILON);
- ERR_FAIL_COND(l_heights.size() != (width * depth));
- setup(heights, width, depth, cell_size);
+ ERR_FAIL_COND(l_heights.size() != (l_width * l_depth));
+
+ // Compute min and max heights if not specified.
+ if (!d.has("min_height") && !d.has("max_height")) {
+
+ PoolVector<real_t>::Read r = heights.read();
+ int heights_size = heights.size();
+
+ for (int i = 0; i < heights_size; ++i) {
+ real_t h = r[i];
+
+ if (h < l_min_height)
+ l_min_height = h;
+ else if (h > l_max_height)
+ l_max_height = h;
+ }
+ }
+
+ setup(l_heights, l_width, l_depth, l_min_height, l_max_height);
}
Variant HeightMapShapeBullet::get_data() const {
@@ -410,8 +434,14 @@ PhysicsServer::ShapeType HeightMapShapeBullet::get_type() const {
return PhysicsServer::SHAPE_HEIGHTMAP;
}
-void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size) {
+void HeightMapShapeBullet::setup(PoolVector<real_t> &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
+
{ // Copy
+
+ // TODO If Godot supported 16-bit integer image format, we could share the same memory block for heightfields
+ // without having to copy anything, optimizing memory and loading performance (Bullet only reads and doesn't take ownership of the data).
+
const int heights_size = p_heights.size();
heights.resize(heights_size);
PoolVector<real_t>::Read p_heights_r = p_heights.read();
@@ -420,14 +450,16 @@ void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int
heights_w[i] = p_heights_r[i];
}
}
+
width = p_width;
depth = p_depth;
- cell_size = p_cell_size;
+ min_height = p_min_height;
+ max_height = p_max_height;
notifyShapeChanged();
}
btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, cell_size));
+ btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, min_height, max_height));
cs->setLocalScaling(p_implicit_scale);
prepare(cs);
cs->setMargin(p_margin);
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index 2acba90e3..abeea0f9c 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -85,7 +85,7 @@ 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(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size);
+ static class btHeightfieldTerrainShape *create_shape_height_field(PoolVector<real_t> &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);
};
@@ -199,7 +199,8 @@ public:
PoolVector<real_t> heights;
int width;
int depth;
- real_t cell_size;
+ real_t min_height;
+ real_t max_height;
HeightMapShapeBullet();
@@ -209,7 +210,7 @@ public:
virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
private:
- void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_cell_size);
+ void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
};
class RayShapeBullet : public ShapeBullet {
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 8c15758e0..3a1f5d78d 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -122,7 +122,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
- btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin);
+ btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
bulletdelete(btShape);
ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
@@ -202,7 +202,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
- btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale(), p_margin);
+ btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
bulletdelete(btShape);
ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
@@ -234,7 +234,7 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
- btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale(), p_margin);
+ btCollisionShape *btShape = shape->create_bt_shape(p_shape_xform.basis.get_scale_abs(), p_margin);
if (!btShape->isConvex()) {
bulletdelete(btShape);
ERR_PRINTS("The shape is not a convex shape, then is not supported: shape type: " + itos(shape->get_type()));
@@ -660,7 +660,10 @@ void SpaceBullet::check_ghost_overlaps() {
// For each overlapping
for (i = ghostOverlaps.size() - 1; 0 <= i; --i) {
- if (!(ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_RIGID_BODY || ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA))
+ if (ghostOverlaps[i]->getUserIndex() == CollisionObjectBullet::TYPE_AREA) {
+ if (!static_cast<AreaBullet *>(ghostOverlaps[i]->getUserPointer())->is_monitorable())
+ continue;
+ } else if (ghostOverlaps[i]->getUserIndex() != CollisionObjectBullet::TYPE_RIGID_BODY)
continue;
otherObject = static_cast<RigidCollisionObjectBullet *>(ghostOverlaps[i]->getUserPointer());
@@ -993,7 +996,7 @@ public:
}
void reset() {
- result_collision_objects.empty();
+ result_collision_objects.clear();
}
};
@@ -1028,7 +1031,10 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran
for (int i = recover_broad_result.result_collision_objects.size() - 1; 0 <= i; --i) {
btCollisionObject *otherObject = recover_broad_result.result_collision_objects[i];
- if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object()) || (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()))
+ if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) {
+ otherObject->activate(); // Force activation of hitten rigid, soft body
+ continue;
+ } else if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object()))
continue;
if (otherObject->getCollisionShape()->isCompound()) {
@@ -1117,7 +1123,7 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC
dispatcher->freeCollisionAlgorithm(algorithm);
if (contactPointResult.hasHit()) {
- r_delta_recover_movement += contactPointResult.m_pointNormalWorld * (contactPointResult.m_penetration_distance * p_recover_movement_scale);
+ r_delta_recover_movement += contactPointResult.m_pointNormalWorld * (contactPointResult.m_penetration_distance * -1 * p_recover_movement_scale);
if (r_recover_result) {
if (contactPointResult.m_penetration_distance < r_recover_result->penetration_distance) {
diff --git a/modules/csg/SCsub b/modules/csg/SCsub
new file mode 100644
index 000000000..57c504efd
--- /dev/null
+++ b/modules/csg/SCsub
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+Import('env')
+Import('env_modules')
+
+env_csg = env_modules.Clone()
+
+# Godot's own source files
+env_csg.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/csg/config.py b/modules/csg/config.py
new file mode 100644
index 000000000..5f133eba9
--- /dev/null
+++ b/modules/csg/config.py
@@ -0,0 +1,5 @@
+def can_build(platform):
+ return True
+
+def configure(env):
+ pass
diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp
new file mode 100644
index 000000000..c1fe11d6a
--- /dev/null
+++ b/modules/csg/csg.cpp
@@ -0,0 +1,1488 @@
+#include "csg.h"
+#include "face3.h"
+#include "geometry.h"
+#include "os/os.h"
+#include "sort.h"
+#include "thirdparty/misc/triangulator.h"
+
+void CSGBrush::clear() {
+ faces.clear();
+}
+
+void CSGBrush::build_from_faces(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uvs, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials, const PoolVector<bool> &p_invert_faces) {
+
+ clear();
+
+ int vc = p_vertices.size();
+
+ ERR_FAIL_COND((vc % 3) != 0)
+
+ PoolVector<Vector3>::Read rv = p_vertices.read();
+ int uvc = p_uvs.size();
+ PoolVector<Vector2>::Read ruv = p_uvs.read();
+ int sc = p_smooth.size();
+ PoolVector<bool>::Read rs = p_smooth.read();
+ int mc = p_materials.size();
+ PoolVector<Ref<Material> >::Read rm = p_materials.read();
+ int ic = p_invert_faces.size();
+ PoolVector<bool>::Read ri = p_invert_faces.read();
+
+ Map<Ref<Material>, int> material_map;
+
+ faces.resize(p_vertices.size() / 3);
+
+ for (int i = 0; i < faces.size(); i++) {
+ Face &f = faces[i];
+ f.vertices[0] = rv[i * 3 + 0];
+ f.vertices[1] = rv[i * 3 + 1];
+ f.vertices[2] = rv[i * 3 + 2];
+ if (uvc == vc) {
+ f.uvs[0] = ruv[i * 3 + 0];
+ f.uvs[1] = ruv[i * 3 + 1];
+ f.uvs[2] = ruv[i * 3 + 2];
+ }
+ if (sc == vc / 3) {
+ f.smooth = rs[i];
+ } else {
+ f.smooth = false;
+ }
+
+ if (ic == vc / 3) {
+ f.invert = ri[i];
+ } else {
+ f.invert = false;
+ }
+
+ if (mc == vc / 3) {
+ Ref<Material> mat = rm[i];
+ if (mat.is_valid()) {
+ const Map<Ref<Material>, int>::Element *E = material_map.find(mat);
+ if (E) {
+ f.material = E->get();
+ } else {
+ f.material = material_map.size();
+ material_map[mat] = f.material;
+ }
+ } else {
+ f.material = -1;
+ }
+ }
+ }
+
+ materials.resize(material_map.size());
+ for (Map<Ref<Material>, int>::Element *E = material_map.front(); E; E = E->next()) {
+ materials[E->get()] = E->key();
+ }
+
+ _regen_face_aabbs();
+}
+
+void CSGBrush::_regen_face_aabbs() {
+
+ for (int i = 0; i < faces.size(); i++) {
+
+ faces[i].aabb.position = faces[i].vertices[0];
+ faces[i].aabb.expand_to(faces[i].vertices[1]);
+ faces[i].aabb.expand_to(faces[i].vertices[2]);
+ faces[i].aabb.grow_by(faces[i].aabb.get_longest_axis_size() * 0.001); //make it a tad bigger to avoid num precision erros
+ }
+}
+
+void CSGBrush::copy_from(const CSGBrush &p_brush, const Transform &p_xform) {
+
+ faces = p_brush.faces;
+ materials = p_brush.materials;
+
+ for (int i = 0; i < faces.size(); i++) {
+ for (int j = 0; j < 3; j++) {
+ faces[i].vertices[j] = p_xform.xform(p_brush.faces[i].vertices[j]);
+ }
+ }
+
+ _regen_face_aabbs();
+}
+
+////////////////////////
+
+void CSGBrushOperation::BuildPoly::create(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B) {
+
+ //creates the initial face that will be used for clipping against the other faces
+
+ Vector3 va[3] = {
+ p_brush->faces[p_face].vertices[0],
+ p_brush->faces[p_face].vertices[1],
+ p_brush->faces[p_face].vertices[2],
+ };
+
+ plane = Plane(va[0], va[1], va[2]);
+
+ to_world.origin = va[0];
+
+ to_world.basis.set_axis(2, plane.normal);
+ to_world.basis.set_axis(0, (va[1] - va[2]).normalized());
+ to_world.basis.set_axis(1, to_world.basis.get_axis(0).cross(to_world.basis.get_axis(2)).normalized());
+
+ to_poly = to_world.affine_inverse();
+
+ face_index = p_face;
+
+ for (int i = 0; i < 3; i++) {
+
+ Point p;
+ Vector3 localp = to_poly.xform(va[i]);
+ p.point.x = localp.x;
+ p.point.y = localp.y;
+ p.uv = p_brush->faces[p_face].uvs[i];
+
+ points.push_back(p);
+
+ ///edge
+
+ Edge e;
+ e.points[0] = i;
+ e.points[1] = (i + 1) % 3;
+ e.outer = true;
+ edges.push_back(e);
+ }
+
+ smooth = p_brush->faces[p_face].smooth;
+ invert = p_brush->faces[p_face].invert;
+
+ if (p_brush->faces[p_face].material != -1) {
+ material = p_brush->materials[p_brush->faces[p_face].material];
+ }
+
+ base_edges = 3;
+}
+
+static Vector2 interpolate_uv(const Vector2 &p_vertex_a, const Vector2 &p_vertex_b, const Vector2 &p_vertex_c, const Vector2 &p_uv_a, const Vector2 &p_uv_c) {
+
+ float len_a_c = (p_vertex_c - p_vertex_a).length();
+ if (len_a_c < CMP_EPSILON) {
+ return p_uv_a;
+ }
+
+ float len_a_b = (p_vertex_b - p_vertex_a).length();
+
+ float c = len_a_b / len_a_c;
+
+ return p_uv_a.linear_interpolate(p_uv_c, c);
+}
+
+static Vector2 interpolate_triangle_uv(const Vector2 &p_pos, const Vector2 *p_vtx, const Vector2 *p_uv) {
+
+ if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2) {
+ return p_uv[0];
+ }
+ if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2) {
+ return p_uv[1];
+ }
+ if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2) {
+ return p_uv[2];
+ }
+
+ Vector2 v0 = p_vtx[1] - p_vtx[0];
+ Vector2 v1 = p_vtx[2] - p_vtx[0];
+ Vector2 v2 = p_pos - p_vtx[0];
+
+ float d00 = v0.dot(v0);
+ float d01 = v0.dot(v1);
+ float d11 = v1.dot(v1);
+ float d20 = v2.dot(v0);
+ float d21 = v2.dot(v1);
+ float denom = (d00 * d11 - d01 * d01);
+ if (denom == 0) {
+ return p_uv[0];
+ }
+ float v = (d11 * d20 - d01 * d21) / denom;
+ float w = (d00 * d21 - d01 * d20) / denom;
+ float u = 1.0f - v - w;
+
+ return p_uv[0] * u + p_uv[1] * v + p_uv[2] * w;
+}
+
+void CSGBrushOperation::BuildPoly::_clip_segment(const CSGBrush *p_brush, int p_face, const Vector2 *segment, MeshMerge &mesh_merge, bool p_for_B) {
+
+ //keep track of what was inserted
+ Vector<int> inserted_points;
+
+ //keep track of point indices for what was inserted, allowing reuse of points.
+ int segment_idx[2] = { -1, -1 };
+
+ //check if edge and poly share a vertex, of so, assign it to segment_idx
+ for (int i = 0; i < points.size(); i++) {
+ for (int j = 0; j < 2; j++) {
+ if (segment[j].distance_to(points[i].point) < CMP_EPSILON) {
+ segment_idx[j] = i;
+ inserted_points.push_back(i);
+ break;
+ }
+ }
+ }
+
+ //check if both segment points are shared with other vertices
+ if (segment_idx[0] != -1 && segment_idx[1] != -1) {
+
+ if (segment_idx[0] == segment_idx[1]) {
+ return; //segment was too tiny, both mapped to same point
+ }
+
+ bool found = false;
+
+ //check if the segment already exists
+ for (int i = 0; i < edges.size(); i++) {
+
+ if (
+ (edges[i].points[0] == segment_idx[0] && edges[i].points[1] == segment_idx[1]) ||
+ (edges[i].points[0] == segment_idx[1] && edges[i].points[1] == segment_idx[0])) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ //it does already exist, do nothing
+ return;
+ }
+
+ //directly add the new segment
+ Edge new_edge;
+ new_edge.points[0] = segment_idx[0];
+ new_edge.points[1] = segment_idx[1];
+ edges.push_back(new_edge);
+ return;
+ }
+
+ //check edge by edge against the segment points to see if intersects
+
+ for (int i = 0; i < base_edges; i++) {
+
+ //if a point is shared with one of the edge points, then this edge must not be tested, as it will result in a numerical precision error.
+ bool edge_valid = true;
+ for (int j = 0; j < 2; j++) {
+
+ if (edges[i].points[0] == segment_idx[0] || edges[i].points[1] == segment_idx[1] || edges[i].points[0] == segment_idx[1] || edges[i].points[1] == segment_idx[0]) {
+ edge_valid = false; //segment has this point, cant check against this
+ break;
+ }
+ }
+
+ if (!edge_valid) //already hit a point in this edge, so dont test it
+ continue;
+
+ //see if either points are within the edge isntead of crossing it
+ Vector2 res;
+ bool found = false;
+ int assign_segment_id = -1;
+
+ for (int j = 0; j < 2; j++) {
+
+ Vector2 edgeseg[2] = { points[edges[i].points[0]].point, points[edges[i].points[1]].point };
+ Vector2 closest = Geometry::get_closest_point_to_segment_2d(segment[j], edgeseg);
+
+ if (closest.distance_to(segment[j]) < CMP_EPSILON) {
+ //point rest of this edge
+ res = closest;
+ found = true;
+ assign_segment_id = j;
+ }
+ }
+
+ //test if the point crosses the edge
+ if (!found && Geometry::segment_intersects_segment_2d(segment[0], segment[1], points[edges[i].points[0]].point, points[edges[i].points[1]].point, &res)) {
+ //point does cross the edge
+ found = true;
+ }
+
+ //check whether an intersection against the segment happened
+ if (found) {
+
+ //It did! so first, must slice the segment
+ Point new_point;
+ new_point.point = res;
+ //make sure to interpolate UV too
+ new_point.uv = interpolate_uv(points[edges[i].points[0]].point, new_point.point, points[edges[i].points[1]].point, points[edges[i].points[0]].uv, points[edges[i].points[1]].uv);
+
+ int point_idx = points.size();
+ points.push_back(new_point);
+
+ //split the edge in 2
+ Edge new_edge;
+ new_edge.points[0] = edges[i].points[0];
+ new_edge.points[1] = point_idx;
+ new_edge.outer = edges[i].outer;
+ edges[i].points[0] = point_idx;
+ edges.insert(i, new_edge);
+ i++; //skip newly inserted edge
+ base_edges++; //will need an extra one in the base triangle
+ if (assign_segment_id >= 0) {
+ //point did split a segment, so make sure to remember this
+ segment_idx[assign_segment_id] = point_idx;
+ }
+ inserted_points.push_back(point_idx);
+ }
+ }
+
+ //final step: after cutting the original triangle, try to see if we can still insert
+ //this segment
+
+ //if already inserted two points, just use them for a segment
+
+ if (inserted_points.size() >= 2) { //should never be >2 on non-manifold geometry, but cope with error
+ //two points were inserted, create the new edge
+ Edge new_edge;
+ new_edge.points[0] = inserted_points[0];
+ new_edge.points[1] = inserted_points[1];
+ edges.push_back(new_edge);
+ return;
+ }
+
+ // One or no points were inserted (besides splitting), so try to see if extra points can be placed inside the triangle.
+ // This needs to be done here, after the previous tests were exhausted
+ for (int i = 0; i < 2; i++) {
+
+ if (segment_idx[i] != -1)
+ continue; //already assigned to something, so skip
+
+ //check whether one of the segment endpoints is inside the triangle. If it is, this points needs to be inserted
+ if (Geometry::is_point_in_triangle(segment[i], points[0].point, points[1].point, points[2].point)) {
+
+ Point new_point;
+ new_point.point = segment[i];
+
+ Vector2 point3[3] = { points[0].point, points[1].point, points[2].point };
+ Vector2 uv3[3] = { points[0].uv, points[1].uv, points[2].uv };
+
+ new_point.uv = interpolate_triangle_uv(new_point.point, point3, uv3);
+
+ int point_idx = points.size();
+ points.push_back(new_point);
+ inserted_points.push_back(point_idx);
+ }
+ }
+
+ //check again whether two points were inserted, if so then create the new edge
+ if (inserted_points.size() >= 2) { //should never be >2 on non-manifold geometry, but cope with error
+ Edge new_edge;
+ new_edge.points[0] = inserted_points[0];
+ new_edge.points[1] = inserted_points[1];
+ edges.push_back(new_edge);
+ }
+}
+
+void CSGBrushOperation::BuildPoly::clip(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B) {
+
+ //Clip function.. find triangle points that will be mapped to the plane and form a segment
+
+ Vector2 segment[3]; //2D
+
+ int src_points = 0;
+
+ for (int i = 0; i < 3; i++) {
+ Vector3 p = p_brush->faces[p_face].vertices[i];
+ if (plane.has_point(p)) {
+ Vector3 pp = plane.project(p);
+ pp = to_poly.xform(pp);
+ segment[src_points++] = Vector2(pp.x, pp.y);
+ } else {
+ Vector3 q = p_brush->faces[p_face].vertices[(i + 1) % 3];
+ if (plane.has_point(q))
+ continue; //next point is in plane, will be added eventually
+ if (plane.is_point_over(p) == plane.is_point_over(q))
+ continue; // both on same side of the plane, don't add
+
+ Vector3 res;
+ if (plane.intersects_segment(p, q, &res)) {
+ res = to_poly.xform(res);
+ segment[src_points++] = Vector2(res.x, res.y);
+ }
+ }
+ }
+
+ //all above or all below, nothing to do. Should not happen though since a precheck was done before.
+ if (src_points == 0)
+ return;
+
+ //just one point in plane is not worth doing anything
+ if (src_points == 1)
+ return;
+
+ //transform A points to 2D
+
+ if (segment[0].distance_to(segment[1]) < CMP_EPSILON)
+ return; //too small
+
+ _clip_segment(p_brush, p_face, segment, mesh_merge, p_for_B);
+}
+
+void CSGBrushOperation::_collision_callback(const CSGBrush *A, int p_face_a, Map<int, BuildPoly> &build_polys_a, const CSGBrush *B, int p_face_b, Map<int, BuildPoly> &build_polys_b, MeshMerge &mesh_merge) {
+
+ //construct a frame of reference for both transforms, in order to do intersection test
+ Vector3 va[3] = {
+ A->faces[p_face_a].vertices[0],
+ A->faces[p_face_a].vertices[1],
+ A->faces[p_face_a].vertices[2],
+ };
+ Vector3 vb[3] = {
+ B->faces[p_face_b].vertices[0],
+ B->faces[p_face_b].vertices[1],
+ B->faces[p_face_b].vertices[2],
+ };
+
+ {
+ //check if either is a degenerate
+ if (va[0].distance_to(va[1]) < CMP_EPSILON || va[0].distance_to(va[2]) < CMP_EPSILON || va[1].distance_to(va[2]) < CMP_EPSILON)
+ return;
+
+ if (vb[0].distance_to(vb[1]) < CMP_EPSILON || vb[0].distance_to(vb[2]) < CMP_EPSILON || vb[1].distance_to(vb[2]) < CMP_EPSILON)
+ return;
+ }
+
+ {
+ //check if points are the same
+ int equal_count = 0;
+
+ for (int i = 0; i < 3; i++) {
+
+ for (int j = 0; j < 3; j++) {
+ if (va[i].distance_to(vb[j]) < mesh_merge.vertex_snap) {
+ equal_count++;
+ break;
+ }
+ }
+ }
+
+ //if 2 or 3 points are the same, there is no point in doing anything. They can't
+ //be clipped either, so add both.
+ if (equal_count == 2 || equal_count == 3) {
+ return;
+ }
+ }
+
+ // do a quick pre-check for no-intersection using the SAT theorem
+
+ {
+
+ //b under or over a plane
+ int over_count = 0, in_plane_count = 0, under_count = 0;
+ Plane plane_a(va[0], va[1], va[2]);
+ if (plane_a.normal == Vector3()) {
+ return; //degenerate
+ }
+
+ for (int i = 0; i < 3; i++) {
+ if (plane_a.has_point(vb[i]))
+ in_plane_count++;
+ else if (plane_a.is_point_over(vb[i]))
+ over_count++;
+ else
+ under_count++;
+ }
+
+ if (over_count == 0 || under_count == 0)
+ return; //no intersection, something needs to be under AND over
+
+ //a under or over b plane
+ over_count = 0;
+ under_count = 0;
+ in_plane_count = 0;
+
+ Plane plane_b(vb[0], vb[1], vb[2]);
+ if (plane_b.normal == Vector3())
+ return; //degenerate
+
+ for (int i = 0; i < 3; i++) {
+ if (plane_b.has_point(va[i]))
+ in_plane_count++;
+ else if (plane_b.is_point_over(va[i]))
+ over_count++;
+ else
+ under_count++;
+ }
+
+ if (over_count == 0 || under_count == 0)
+ return; //no intersection, something needs to be under AND over
+
+ //edge pairs (cross product combinations), see SAT theorem
+
+ for (int i = 0; i < 3; i++) {
+
+ Vector3 axis_a = (va[i] - va[(i + 1) % 3]).normalized();
+
+ for (int j = 0; j < 3; j++) {
+
+ Vector3 axis_b = (vb[j] - vb[(j + 1) % 3]).normalized();
+
+ Vector3 sep_axis = axis_a.cross(axis_b);
+ if (sep_axis == Vector3())
+ continue; //colineal
+ sep_axis.normalize();
+
+ real_t min_a = 1e20, max_a = -1e20;
+ real_t min_b = 1e20, max_b = -1e20;
+
+ for (int k = 0; k < 3; k++) {
+ real_t d = sep_axis.dot(va[k]);
+ min_a = MIN(min_a, d);
+ max_a = MAX(max_a, d);
+ d = sep_axis.dot(vb[k]);
+ min_b = MIN(min_b, d);
+ max_b = MAX(max_b, d);
+ }
+
+ min_b -= (max_a - min_a) * 0.5;
+ max_b += (max_a - min_a) * 0.5;
+
+ real_t dmin = min_b - (min_a + max_a) * 0.5;
+ real_t dmax = max_b - (min_a + max_a) * 0.5;
+
+ if (dmin > CMP_EPSILON || dmax < -CMP_EPSILON) {
+ return; //does not contain zero, so they don't overlap
+ }
+ }
+ }
+ }
+
+ //if we are still here, it means they most likely intersect, so create BuildPolys if they dont existy
+
+ BuildPoly *poly_a = NULL;
+
+ if (!build_polys_a.has(p_face_a)) {
+
+ BuildPoly bp;
+ bp.create(A, p_face_a, mesh_merge, false);
+ build_polys_a[p_face_a] = bp;
+ }
+
+ poly_a = &build_polys_a[p_face_a];
+
+ BuildPoly *poly_b = NULL;
+
+ if (!build_polys_b.has(p_face_b)) {
+
+ BuildPoly bp;
+ bp.create(B, p_face_b, mesh_merge, true);
+ build_polys_b[p_face_b] = bp;
+ }
+
+ poly_b = &build_polys_b[p_face_b];
+
+ //clip each other, this could be improved by using vertex unique IDs (more vertices may be shared instead of using snap)
+ poly_a->clip(B, p_face_b, mesh_merge, false);
+ poly_b->clip(A, p_face_a, mesh_merge, true);
+}
+
+void CSGBrushOperation::_add_poly_points(const BuildPoly &p_poly, int p_edge, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<bool> &edge_process, Vector<PolyPoints> &r_poly) {
+
+ //this function follows the polygon points counter clockwise and adds them. It creates lists of unique polygons
+ //every time an unused edge is found, it's pushed to a stack and continues from there.
+
+ List<EdgeSort> edge_stack;
+
+ {
+ EdgeSort es;
+ es.angle = 0; //wont be checked here
+ es.edge = p_edge;
+ es.prev_point = p_from_point;
+ es.edge_point = p_to_point;
+
+ edge_stack.push_back(es);
+ }
+
+ //attempt to empty the stack.
+ while (edge_stack.size()) {
+
+ EdgeSort e = edge_stack.front()->get();
+ edge_stack.pop_front();
+
+ if (edge_process[e.edge]) {
+ //nothing to do here
+ continue;
+ }
+
+ Vector<int> points;
+ points.push_back(e.prev_point);
+
+ int prev_point = e.prev_point;
+ int to_point = e.edge_point;
+ int current_edge = e.edge;
+
+ edge_process[e.edge] = true; //mark as processed
+
+ int limit = p_poly.points.size() * 4; //avoid infinite recursion
+
+ while (to_point != e.prev_point && limit) {
+
+ Vector2 segment[2] = { p_poly.points[prev_point].point, p_poly.points[to_point].point };
+
+ //construct a basis transform from the segment, which will be used to check the angle
+ Transform2D t2d;
+ t2d[0] = (segment[1] - segment[0]).normalized(); //use as Y
+ t2d[1] = Vector2(-t2d[0].y, t2d[0].x); // use as tangent
+ t2d[2] = segment[1]; //origin
+
+ if (t2d.basis_determinant() == 0)
+ break; //abort poly
+
+ t2d.affine_invert();
+
+ //push all edges found here, they will be sorted by minimum angle later.
+ Vector<EdgeSort> next_edges;
+
+ for (int i = 0; i < vertex_process[to_point].size(); i++) {
+
+ int edge = vertex_process[to_point][i];
+ int opposite_point = p_poly.edges[edge].points[0] == to_point ? p_poly.edges[edge].points[1] : p_poly.edges[edge].points[0];
+ if (opposite_point == prev_point)
+ continue; //not going back
+
+ EdgeSort e;
+ Vector2 local_vec = t2d.xform(p_poly.points[opposite_point].point);
+ e.angle = -local_vec.angle(); //negate so we can sort by minimum angle
+ e.edge = edge;
+ e.edge_point = opposite_point;
+ e.prev_point = to_point;
+
+ next_edges.push_back(e);
+ }
+
+ //finally, sort by minimum angle
+ next_edges.sort();
+
+ int next_point = -1;
+ int next_edge = -1;
+
+ for (int i = 0; i < next_edges.size(); i++) {
+
+ if (i == 0) {
+ //minimum angle found is the next point
+ next_point = next_edges[i].edge_point;
+ next_edge = next_edges[i].edge;
+
+ } else {
+ //the rest are pushed to the stack IF they were not processed yet.
+ if (!edge_process[next_edges[i].edge]) {
+ edge_stack.push_back(next_edges[i]);
+ }
+ }
+ }
+
+ if (next_edge == -1) {
+ //did not find anything, may be a dead-end edge (this should normally not happen)
+ //just flip the direction and go back
+ next_point = prev_point;
+ next_edge = current_edge;
+ }
+
+ points.push_back(to_point);
+
+ prev_point = to_point;
+ to_point = next_point;
+ edge_process[next_edge] = true; //mark this edge as processed
+ current_edge = next_edge;
+
+ limit--;
+ }
+
+ //if more than 2 points were added to the polygon, add it to the list of polygons.
+ if (points.size() > 2) {
+ PolyPoints pp;
+ pp.points = points;
+ r_poly.push_back(pp);
+ }
+ }
+}
+
+void CSGBrushOperation::_add_poly_outline(const BuildPoly &p_poly, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<int> &r_outline) {
+
+ //this is the opposite of the function above. It adds polygon outlines instead.
+ //this is used for triangulating holes.
+ //no stack is used here because only the bigger outline is interesting.
+
+ r_outline.push_back(p_from_point);
+
+ int prev_point = p_from_point;
+ int to_point = p_to_point;
+
+ int limit = p_poly.points.size() * 4; //avoid infinite recursion
+
+ while (to_point != p_from_point && limit) {
+
+ Vector2 segment[2] = { p_poly.points[prev_point].point, p_poly.points[to_point].point };
+ //again create a transform to compute the angle.
+ Transform2D t2d;
+ t2d[0] = (segment[1] - segment[0]).normalized(); //use as Y
+ t2d[1] = Vector2(-t2d[0].y, t2d[0].x); // use as tangent
+ t2d[2] = segment[1]; //origin
+
+ if (t2d.basis_determinant() == 0)
+ break; //abort poly
+
+ t2d.affine_invert();
+
+ float max_angle;
+ int next_point_angle = -1;
+
+ for (int i = 0; i < vertex_process[to_point].size(); i++) {
+
+ int edge = vertex_process[to_point][i];
+ int opposite_point = p_poly.edges[edge].points[0] == to_point ? p_poly.edges[edge].points[1] : p_poly.edges[edge].points[0];
+ if (opposite_point == prev_point)
+ continue; //not going back
+
+ float angle = -t2d.xform(p_poly.points[opposite_point].point).angle();
+ if (next_point_angle == -1 || angle > max_angle) { //same as before but use greater to check.
+ max_angle = angle;
+ next_point_angle = opposite_point;
+ }
+ }
+
+ if (next_point_angle == -1) {
+ //go back because no route found
+ next_point_angle = prev_point;
+ }
+
+ r_outline.push_back(to_point);
+ prev_point = to_point;
+ to_point = next_point_angle;
+
+ limit--;
+ }
+}
+
+void CSGBrushOperation::_merge_poly(MeshMerge &mesh, int p_face_idx, const BuildPoly &p_poly, bool p_from_b) {
+
+ //finally, merge the 2D polygon back to 3D
+
+ Vector<Vector<int> > vertex_process;
+ Vector<bool> edge_process;
+
+ vertex_process.resize(p_poly.points.size());
+ edge_process.resize(p_poly.edges.size());
+
+ //none processed by default
+ for (int i = 0; i < edge_process.size(); i++) {
+ edge_process[i] = false;
+ }
+
+ //put edges in points, so points can go through them
+ for (int i = 0; i < p_poly.edges.size(); i++) {
+ vertex_process[p_poly.edges[i].points[0]].push_back(i);
+ vertex_process[p_poly.edges[i].points[1]].push_back(i);
+ }
+
+ Vector<PolyPoints> polys;
+
+ //process points that were not processed
+ for (int i = 0; i < edge_process.size(); i++) {
+ if (edge_process[i] == true)
+ continue; //already processed
+
+ int intersect_poly = -1;
+
+ if (i > 0) {
+ //this is disconnected, so it's clearly a hole. lets find where it belongs
+ Vector2 ref_point = p_poly.points[p_poly.edges[i].points[0]].point;
+
+ for (int j = 0; j < polys.size(); j++) {
+
+ //find a point outside poly
+ Vector2 out_point(-1e20, -1e20);
+
+ const PolyPoints &pp = polys[j];
+
+ for (int k = 0; k < pp.points.size(); k++) {
+ Vector2 p = p_poly.points[pp.points[k]].point;
+ out_point.x = MAX(out_point.x, p.x);
+ out_point.y = MAX(out_point.y, p.y);
+ }
+
+ out_point += Vector2(0.12341234, 0.4123412); // move to a random place to avoid direct edge-point chances
+
+ int intersections = 0;
+
+ for (int k = 0; k < pp.points.size(); k++) {
+ Vector2 p1 = p_poly.points[pp.points[k]].point;
+ Vector2 p2 = p_poly.points[pp.points[(k + 1) % pp.points.size()]].point;
+
+ if (Geometry::segment_intersects_segment_2d(ref_point, out_point, p1, p2, NULL)) {
+ intersections++;
+ }
+ }
+
+ if (intersections % 2 == 1) {
+ //hole is inside this poly
+ intersect_poly = j;
+ break;
+ }
+ }
+ }
+
+ if (intersect_poly != -1) {
+ //must add this as a hole
+ Vector<int> outline;
+ _add_poly_outline(p_poly, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, outline);
+
+ if (outline.size() > 1) {
+ polys[intersect_poly].holes.push_back(outline);
+ }
+ }
+ _add_poly_points(p_poly, i, p_poly.edges[i].points[0], p_poly.edges[i].points[1], vertex_process, edge_process, polys);
+ }
+
+ //get rid of holes, not the most optiomal way, but also not a common case at all to be inoptimal
+ for (int i = 0; i < polys.size(); i++) {
+
+ if (!polys[i].holes.size())
+ continue;
+
+ //repeat until no more holes are left to be merged
+ while (polys[i].holes.size()) {
+
+ //try to merge a hole with the outline
+ bool added_hole = false;
+
+ for (int j = 0; j < polys[i].holes.size(); j++) {
+
+ //try hole vertices
+ int with_outline_vertex = -1;
+ int from_hole_vertex = -1;
+
+ bool found = false;
+
+ for (int k = 0; k < polys[i].holes[j].size(); k++) {
+
+ int from_idx = polys[i].holes[j][k];
+ Vector2 from = p_poly.points[from_idx].point;
+
+ //try a segment from hole vertex to outline vertices
+ from_hole_vertex = k;
+
+ bool valid = true;
+
+ for (int l = 0; l < polys[i].points.size(); l++) {
+
+ int to_idx = polys[i].points[l];
+ Vector2 to = p_poly.points[to_idx].point;
+ with_outline_vertex = l;
+
+ //try agaisnt outline (other points) first
+
+ valid = true;
+
+ for (int m = 0; m < polys[i].points.size(); m++) {
+
+ int m_next = (m + 1) % polys[i].points.size();
+ if (m == with_outline_vertex || m_next == with_outline_vertex) //do not test with edges that share this point
+ continue;
+
+ if (Geometry::segment_intersects_segment_2d(from, to, p_poly.points[polys[i].points[m]].point, p_poly.points[polys[i].points[m_next]].point, NULL)) {
+ valid = false;
+ break;
+ }
+ }
+
+ if (!valid)
+ continue;
+
+ //try agaisnt all holes including self
+
+ for (int m = 0; m < polys[i].holes.size(); m++) {
+
+ for (int n = 0; n < polys[i].holes[m].size(); n++) {
+
+ int n_next = (n + 1) % polys[i].holes[m].size();
+ if (m == j && (n == from_hole_vertex || n_next == from_hole_vertex)) //contains vertex being tested from current hole, skip
+ continue;
+
+ if (Geometry::segment_intersects_segment_2d(from, to, p_poly.points[polys[i].holes[m][n]].point, p_poly.points[polys[i].holes[m][n_next]].point, NULL)) {
+ valid = false;
+ break;
+ }
+ }
+
+ if (!valid)
+ break;
+ }
+
+ if (valid) //all passed! exit loop
+ break;
+ else
+ continue; //something went wrong, go on.
+ }
+
+ if (valid) {
+ found = true; //if in the end this was valid, use it
+ break;
+ }
+ }
+
+ if (found) {
+
+ //hook this hole with outline, and remove from list of holes
+
+ //duplicate point
+ int insert_at = with_outline_vertex;
+ polys[i].points.insert(insert_at, polys[i].points[insert_at]);
+ insert_at++;
+ //insert all others, outline should be backwards (must check)
+ int holesize = polys[i].holes[j].size();
+ for (int k = 0; k <= holesize; k++) {
+ int idx = (from_hole_vertex + k) % holesize;
+ polys[i].points.insert(insert_at, polys[i].holes[j][idx]);
+ insert_at++;
+ }
+
+ added_hole = true;
+ polys[i].holes.remove(j);
+ break; //got rid of hole, break and continue
+ }
+ }
+
+ ERR_BREAK(!added_hole);
+ }
+ }
+
+ //triangulate polygons
+
+ for (int i = 0; i < polys.size(); i++) {
+
+ Vector<Vector2> vertices;
+ vertices.resize(polys[i].points.size());
+ for (int j = 0; j < vertices.size(); j++) {
+ vertices[j] = p_poly.points[polys[i].points[j]].point;
+ }
+
+ Vector<int> indices = Geometry::triangulate_polygon(vertices);
+
+ for (int j = 0; j < indices.size(); j += 3) {
+
+ //obtain the vertex
+
+ Vector3 face[3];
+ Vector2 uv[3];
+ float cp = Geometry::vec2_cross(p_poly.points[polys[i].points[indices[j + 0]]].point, p_poly.points[polys[i].points[indices[j + 1]]].point, p_poly.points[polys[i].points[indices[j + 2]]].point);
+ if (Math::abs(cp) < CMP_EPSILON)
+ continue;
+
+ for (int k = 0; k < 3; k++) {
+
+ Vector2 p = p_poly.points[polys[i].points[indices[j + k]]].point;
+ face[k] = p_poly.to_world.xform(Vector3(p.x, p.y, 0));
+ uv[k] = p_poly.points[polys[i].points[indices[j + k]]].uv;
+ }
+
+ mesh.add_face(face[0], face[1], face[2], uv[0], uv[1], uv[2], p_poly.smooth, p_poly.invert, p_poly.material, p_from_b);
+ }
+ }
+}
+
+//use a limit to speed up bvh and limit the depth
+#define BVH_LIMIT 8
+
+int CSGBrushOperation::MeshMerge::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc) {
+
+ if (p_depth > max_depth) {
+ max_depth = p_depth;
+ }
+
+ if (p_size <= BVH_LIMIT) {
+
+ for (int i = 0; i < p_size - 1; i++) {
+ p_bb[p_from + i]->next = p_bb[p_from + i + 1] - p_bvh;
+ }
+ return p_bb[p_from] - p_bvh;
+ } else if (p_size == 0) {
+
+ return -1;
+ }
+
+ AABB aabb;
+ aabb = p_bb[p_from]->aabb;
+ for (int i = 1; i < p_size; i++) {
+
+ aabb.merge_with(p_bb[p_from + i]->aabb);
+ }
+
+ int li = aabb.get_longest_axis_index();
+
+ switch (li) {
+
+ case Vector3::AXIS_X: {
+ SortArray<BVH *, BVHCmpX> sort_x;
+ sort_x.nth_element(0, p_size, p_size / 2, &p_bb[p_from]);
+ //sort_x.sort(&p_bb[p_from],p_size);
+ } break;
+ case Vector3::AXIS_Y: {
+ SortArray<BVH *, BVHCmpY> sort_y;
+ sort_y.nth_element(0, p_size, p_size / 2, &p_bb[p_from]);
+ //sort_y.sort(&p_bb[p_from],p_size);
+ } break;
+ case Vector3::AXIS_Z: {
+ SortArray<BVH *, BVHCmpZ> sort_z;
+ sort_z.nth_element(0, p_size, p_size / 2, &p_bb[p_from]);
+ //sort_z.sort(&p_bb[p_from],p_size);
+
+ } break;
+ }
+
+ int left = _create_bvh(p_bvh, p_bb, p_from, p_size / 2, p_depth + 1, max_depth, max_alloc);
+ int right = _create_bvh(p_bvh, p_bb, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, max_depth, max_alloc);
+
+ int index = max_alloc++;
+ BVH *_new = &p_bvh[index];
+ _new->aabb = aabb;
+ _new->center = aabb.position + aabb.size * 0.5;
+ _new->face = -1;
+ _new->left = left;
+ _new->right = right;
+ _new->next = -1;
+
+ return index;
+}
+
+int CSGBrushOperation::MeshMerge::_bvh_count_intersections(BVH *bvhptr, int p_max_depth, int p_bvh_first, const Vector3 &p_begin, const Vector3 &p_end, int p_exclude) const {
+
+ uint32_t *stack = (uint32_t *)alloca(sizeof(int) * p_max_depth);
+
+ enum {
+ TEST_AABB_BIT = 0,
+ VISIT_LEFT_BIT = 1,
+ VISIT_RIGHT_BIT = 2,
+ VISIT_DONE_BIT = 3,
+ VISITED_BIT_SHIFT = 29,
+ NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1,
+ VISITED_BIT_MASK = ~NODE_IDX_MASK,
+
+ };
+
+ int intersections = 0;
+
+ int level = 0;
+
+ const Vector3 *vertexptr = points.ptr();
+ const Face *facesptr = faces.ptr();
+ AABB segment_aabb;
+ segment_aabb.position = p_begin;
+ segment_aabb.expand_to(p_end);
+
+ int pos = p_bvh_first;
+
+ stack[0] = pos;
+ while (true) {
+
+ uint32_t node = stack[level] & NODE_IDX_MASK;
+ const BVH &b = bvhptr[node];
+ bool done = false;
+
+ switch (stack[level] >> VISITED_BIT_SHIFT) {
+ case TEST_AABB_BIT: {
+
+ if (b.face >= 0) {
+
+ const BVH *bp = &b;
+
+ while (bp) {
+
+ bool valid = segment_aabb.intersects(bp->aabb) && bp->aabb.intersects_segment(p_begin, p_end);
+
+ if (valid && p_exclude != bp->face) {
+ const Face &s = facesptr[bp->face];
+ Face3 f3(vertexptr[s.points[0]], vertexptr[s.points[1]], vertexptr[s.points[2]]);
+
+ Vector3 res;
+
+ if (f3.intersects_segment(p_begin, p_end, &res)) {
+ intersections++;
+ }
+ }
+ if (bp->next != -1) {
+ bp = &bvhptr[bp->next];
+ } else {
+ bp = NULL;
+ }
+ }
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+
+ } else {
+
+ bool valid = segment_aabb.intersects(b.aabb) && b.aabb.intersects_segment(p_begin, p_end);
+
+ if (!valid) {
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+
+ } else {
+ stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node;
+ }
+ }
+ continue;
+ }
+ case VISIT_LEFT_BIT: {
+
+ stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node;
+ stack[level + 1] = b.left | TEST_AABB_BIT;
+ level++;
+ continue;
+ }
+ case VISIT_RIGHT_BIT: {
+
+ stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
+ stack[level + 1] = b.right | TEST_AABB_BIT;
+ level++;
+ continue;
+ }
+ case VISIT_DONE_BIT: {
+
+ if (level == 0) {
+ done = true;
+ break;
+ } else
+ level--;
+ continue;
+ }
+ }
+
+ if (done)
+ break;
+ }
+
+ return intersections;
+}
+
+void CSGBrushOperation::MeshMerge::mark_inside_faces() {
+
+ // mark faces that are inside. This helps later do the boolean ops when merging.
+ // this approach is very brute force (with a bunch of optimizatios, such as BVH and pre AABB intersection test)
+
+ AABB aabb;
+
+ for (int i = 0; i < points.size(); i++) {
+ if (i == 0) {
+ aabb.position = points[i];
+ } else {
+ aabb.expand_to(points[i]);
+ }
+ }
+
+ float max_distance = aabb.size.length() * 1.2;
+
+ Vector<BVH> bvhvec;
+ bvhvec.resize(faces.size() * 3); //will never be larger than this (todo make better)
+ BVH *bvh = bvhvec.ptrw();
+
+ AABB faces_a;
+ AABB faces_b;
+
+ bool first_a = true;
+ bool first_b = true;
+
+ for (int i = 0; i < faces.size(); i++) {
+ bvh[i].left = -1;
+ bvh[i].right = -1;
+ bvh[i].face = i;
+ bvh[i].aabb.position = points[faces[i].points[0]];
+ bvh[i].aabb.expand_to(points[faces[i].points[1]]);
+ bvh[i].aabb.expand_to(points[faces[i].points[2]]);
+ bvh[i].center = bvh[i].aabb.position + bvh[i].aabb.size * 0.5;
+ bvh[i].next = -1;
+ if (faces[i].from_b) {
+ if (first_b) {
+ faces_b = bvh[i].aabb;
+ first_b = false;
+ } else {
+ faces_b.merge_with(bvh[i].aabb);
+ }
+ } else {
+ if (first_a) {
+ faces_a = bvh[i].aabb;
+ first_a = false;
+ } else {
+ faces_a.merge_with(bvh[i].aabb);
+ }
+ }
+ }
+
+ AABB intersection_aabb = faces_a.intersection(faces_b);
+ intersection_aabb.grow_by(intersection_aabb.get_longest_axis_size() * 0.01); //grow a little, avoid numerical error
+
+ if (intersection_aabb.size == Vector3()) //AABB do not intersect, so neither do shapes.
+ return;
+
+ Vector<BVH *> bvhtrvec;
+ bvhtrvec.resize(faces.size());
+ BVH **bvhptr = bvhtrvec.ptrw();
+ for (int i = 0; i < faces.size(); i++) {
+
+ bvhptr[i] = &bvh[i];
+ }
+
+ int max_depth = 0;
+ int max_alloc = faces.size();
+ _create_bvh(bvh, bvhptr, 0, faces.size(), 1, max_depth, max_alloc);
+
+ for (int i = 0; i < faces.size(); i++) {
+
+ if (!intersection_aabb.intersects(bvh[i].aabb))
+ continue; //not in AABB intersection, so not in face intersection
+ Vector3 center = points[faces[i].points[0]];
+ center += points[faces[i].points[1]];
+ center += points[faces[i].points[2]];
+ center /= 3.0;
+
+ Plane plane(points[faces[i].points[0]], points[faces[i].points[1]], points[faces[i].points[2]]);
+ Vector3 target = center + plane.normal * max_distance + Vector3(0.0001234, 0.000512, 0.00013423); //reduce chance of edge hits by doing a small increment
+
+ int intersections = _bvh_count_intersections(bvh, max_depth, max_alloc - 1, center, target, i);
+
+ if (intersections & 1) {
+ faces[i].inside = true;
+ }
+ }
+}
+
+void CSGBrushOperation::MeshMerge::add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector2 &p_uv_a, const Vector2 &p_uv_b, const Vector2 &p_uv_c, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b) {
+
+ Vector3 src_points[3] = { p_a, p_b, p_c };
+ Vector2 src_uvs[3] = { p_uv_a, p_uv_b, p_uv_c };
+ int indices[3];
+ for (int i = 0; i < 3; i++) {
+
+ VertexKey vk;
+ vk.x = int((double(src_points[i].x) + double(vertex_snap) * 0.31234) / double(vertex_snap));
+ vk.y = int((double(src_points[i].y) + double(vertex_snap) * 0.31234) / double(vertex_snap));
+ vk.z = int((double(src_points[i].z) + double(vertex_snap) * 0.31234) / double(vertex_snap));
+
+ int res;
+ if (snap_cache.lookup(vk, res)) {
+ indices[i] = res;
+ } else {
+ indices[i] = points.size();
+ points.push_back(src_points[i]);
+ snap_cache.set(vk, indices[i]);
+ }
+ }
+
+ if (indices[0] == indices[2] || indices[0] == indices[1] || indices[1] == indices[2])
+ return; //not adding degenerate
+
+ MeshMerge::Face face;
+ face.from_b = p_from_b;
+ face.inside = false;
+ face.smooth = p_smooth;
+ face.invert = p_invert;
+ if (p_material.is_valid()) {
+ if (!materials.has(p_material)) {
+ face.material_idx = materials.size();
+ materials[p_material] = face.material_idx;
+ } else {
+ face.material_idx = materials[p_material];
+ }
+ } else {
+ face.material_idx = -1;
+ }
+
+ for (int k = 0; k < 3; k++) {
+
+ face.points[k] = indices[k];
+ face.uvs[k] = src_uvs[k];
+ ;
+ }
+
+ faces.push_back(face);
+}
+
+void CSGBrushOperation::merge_brushes(Operation p_operation, const CSGBrush &p_A, const CSGBrush &p_B, CSGBrush &result, float p_snap) {
+
+ CallbackData cd;
+ cd.self = this;
+ cd.A = &p_A;
+ cd.B = &p_B;
+
+ MeshMerge mesh_merge;
+ mesh_merge.vertex_snap = p_snap;
+
+ //check intersections between faces. Use AABB to speed up precheck
+ //this generates list of buildpolys and clips them.
+ //this was originally BVH optimized, but its not really worth it.
+ for (int i = 0; i < p_A.faces.size(); i++) {
+ cd.face_a = i;
+ for (int j = 0; j < p_B.faces.size(); j++) {
+ if (p_A.faces[i].aabb.intersects(p_B.faces[j].aabb)) {
+ _collision_callback(&p_A, i, cd.build_polys_A, &p_B, j, cd.build_polys_B, mesh_merge);
+ }
+ }
+ }
+
+ //merge the already cliped polys back to 3D
+ for (Map<int, BuildPoly>::Element *E = cd.build_polys_A.front(); E; E = E->next()) {
+ _merge_poly(mesh_merge, E->key(), E->get(), false);
+ }
+
+ for (Map<int, BuildPoly>::Element *E = cd.build_polys_B.front(); E; E = E->next()) {
+ _merge_poly(mesh_merge, E->key(), E->get(), true);
+ }
+
+ //merge the non clipped faces back
+
+ for (int i = 0; i < p_A.faces.size(); i++) {
+
+ if (cd.build_polys_A.has(i))
+ continue; //made from buildpoly, skipping
+
+ Vector3 points[3];
+ Vector2 uvs[3];
+ for (int j = 0; j < 3; j++) {
+ points[j] = p_A.faces[i].vertices[j];
+ uvs[j] = p_A.faces[i].uvs[j];
+ }
+ Ref<Material> material;
+ if (p_A.faces[i].material != -1) {
+ material = p_A.materials[p_A.faces[i].material];
+ }
+ mesh_merge.add_face(points[0], points[1], points[2], uvs[0], uvs[1], uvs[2], p_A.faces[i].smooth, p_A.faces[i].invert, material, false);
+ }
+
+ for (int i = 0; i < p_B.faces.size(); i++) {
+
+ if (cd.build_polys_B.has(i))
+ continue; //made from buildpoly, skipping
+
+ Vector3 points[3];
+ Vector2 uvs[3];
+ for (int j = 0; j < 3; j++) {
+ points[j] = p_B.faces[i].vertices[j];
+ uvs[j] = p_B.faces[i].uvs[j];
+ }
+ Ref<Material> material;
+ if (p_B.faces[i].material != -1) {
+ material = p_B.materials[p_B.faces[i].material];
+ }
+ mesh_merge.add_face(points[0], points[1], points[2], uvs[0], uvs[1], uvs[2], p_B.faces[i].smooth, p_B.faces[i].invert, material, true);
+ }
+
+ //mark faces that ended up inside the intersection
+ mesh_merge.mark_inside_faces();
+
+ //regen new brush to start filling it again
+ result.clear();
+
+ switch (p_operation) {
+
+ case OPERATION_UNION: {
+
+ int outside_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (mesh_merge.faces[i].inside)
+ continue;
+
+ outside_count++;
+ }
+
+ result.faces.resize(outside_count);
+
+ outside_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (mesh_merge.faces[i].inside)
+ continue;
+ for (int j = 0; j < 3; j++) {
+ result.faces[outside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ result.faces[outside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
+ }
+
+ result.faces[outside_count].smooth = mesh_merge.faces[i].smooth;
+ result.faces[outside_count].invert = mesh_merge.faces[i].invert;
+ result.faces[outside_count].material = mesh_merge.faces[i].material_idx;
+ outside_count++;
+ }
+
+ result._regen_face_aabbs();
+
+ } break;
+ case OPERATION_INTERSECTION: {
+
+ int inside_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (!mesh_merge.faces[i].inside)
+ continue;
+
+ inside_count++;
+ }
+
+ result.faces.resize(inside_count);
+
+ inside_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (!mesh_merge.faces[i].inside)
+ continue;
+ for (int j = 0; j < 3; j++) {
+ result.faces[inside_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ result.faces[inside_count].uvs[j] = mesh_merge.faces[i].uvs[j];
+ }
+
+ result.faces[inside_count].smooth = mesh_merge.faces[i].smooth;
+ result.faces[inside_count].invert = mesh_merge.faces[i].invert;
+ result.faces[inside_count].material = mesh_merge.faces[i].material_idx;
+ inside_count++;
+ }
+
+ result._regen_face_aabbs();
+
+ } break;
+ case OPERATION_SUBSTRACTION: {
+
+ int face_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+ if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside)
+ continue;
+ if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside)
+ continue;
+
+ face_count++;
+ }
+
+ result.faces.resize(face_count);
+
+ face_count = 0;
+
+ for (int i = 0; i < mesh_merge.faces.size(); i++) {
+
+ if (mesh_merge.faces[i].from_b && !mesh_merge.faces[i].inside)
+ continue;
+ if (!mesh_merge.faces[i].from_b && mesh_merge.faces[i].inside)
+ continue;
+
+ for (int j = 0; j < 3; j++) {
+ result.faces[face_count].vertices[j] = mesh_merge.points[mesh_merge.faces[i].points[j]];
+ result.faces[face_count].uvs[j] = mesh_merge.faces[i].uvs[j];
+ }
+
+ if (mesh_merge.faces[i].from_b) {
+ //invert facing of insides of B
+ SWAP(result.faces[face_count].vertices[1], result.faces[face_count].vertices[2]);
+ SWAP(result.faces[face_count].uvs[1], result.faces[face_count].uvs[2]);
+ }
+
+ result.faces[face_count].smooth = mesh_merge.faces[i].smooth;
+ result.faces[face_count].invert = mesh_merge.faces[i].invert;
+ result.faces[face_count].material = mesh_merge.faces[i].material_idx;
+ face_count++;
+ }
+
+ result._regen_face_aabbs();
+
+ } break;
+ }
+
+ //updatelist of materials
+ result.materials.resize(mesh_merge.materials.size());
+ for (const Map<Ref<Material>, int>::Element *E = mesh_merge.materials.front(); E; E = E->next()) {
+ result.materials[E->get()] = E->key();
+ }
+}
diff --git a/modules/csg/csg.h b/modules/csg/csg.h
new file mode 100644
index 000000000..bb67e1fb3
--- /dev/null
+++ b/modules/csg/csg.h
@@ -0,0 +1,206 @@
+#ifndef CSG_H
+#define CSG_H
+
+#include "aabb.h"
+#include "dvector.h"
+#include "map.h"
+#include "math_2d.h"
+#include "oa_hash_map.h"
+#include "plane.h"
+#include "scene/resources/material.h"
+#include "transform.h"
+#include "vector3.h"
+
+struct CSGBrush {
+
+ struct Face {
+
+ Vector3 vertices[3];
+ Vector2 uvs[3];
+ AABB aabb;
+ bool smooth;
+ bool invert;
+ int material;
+ };
+
+ Vector<Face> faces;
+ Vector<Ref<Material> > materials;
+
+ void _regen_face_aabbs();
+ //create a brush from faces
+ void build_from_faces(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uvs, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials, const PoolVector<bool> &p_invert_faces);
+ void copy_from(const CSGBrush &p_brush, const Transform &p_xform);
+
+ void clear();
+};
+
+struct CSGBrushOperation {
+
+ enum Operation {
+ OPERATION_UNION,
+ OPERATION_INTERSECTION,
+ OPERATION_SUBSTRACTION,
+
+ };
+
+ struct MeshMerge {
+
+ struct BVH {
+ int face;
+ int left;
+ int right;
+ int next;
+ Vector3 center;
+ AABB aabb;
+ };
+
+ struct BVHCmpX {
+
+ bool operator()(const BVH *p_left, const BVH *p_right) const {
+
+ return p_left->center.x < p_right->center.x;
+ }
+ };
+
+ struct BVHCmpY {
+
+ bool operator()(const BVH *p_left, const BVH *p_right) const {
+
+ return p_left->center.y < p_right->center.y;
+ }
+ };
+ struct BVHCmpZ {
+
+ bool operator()(const BVH *p_left, const BVH *p_right) const {
+
+ return p_left->center.z < p_right->center.z;
+ }
+ };
+
+ int _bvh_count_intersections(BVH *bvhptr, int p_max_depth, int p_bvh_first, const Vector3 &p_begin, const Vector3 &p_end, int p_exclude) const;
+ int _create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc);
+
+ struct VertexKey {
+ int32_t x, y, z;
+ _FORCE_INLINE_ bool operator<(const VertexKey &p_key) const {
+ if (x == p_key.x) {
+ if (y == p_key.y) {
+ return z < p_key.z;
+ } else {
+ return y < p_key.y;
+ }
+ } else {
+ return x < p_key.x;
+ }
+ }
+
+ _FORCE_INLINE_ bool operator==(const VertexKey &p_key) const {
+ return (x == p_key.x && y == p_key.y && z == p_key.z);
+ }
+ };
+
+ struct VertexKeyHash {
+ static _FORCE_INLINE_ uint32_t hash(const VertexKey &p_vk) {
+ uint32_t h = hash_djb2_one_32(p_vk.x);
+ h = hash_djb2_one_32(p_vk.y, h);
+ h = hash_djb2_one_32(p_vk.z, h);
+ return h;
+ }
+ };
+
+ OAHashMap<VertexKey, int, VertexKeyHash> snap_cache;
+
+ Vector<Vector3> points;
+
+ struct Face {
+ bool from_b;
+ bool inside;
+ int points[3];
+ Vector2 uvs[3];
+ bool smooth;
+ bool invert;
+ int material_idx;
+ };
+
+ Vector<Face> faces;
+
+ Map<Ref<Material>, int> materials;
+
+ Map<Vector3, int> vertex_map;
+ void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector2 &p_uv_a, const Vector2 &p_uv_b, const Vector2 &p_uv_c, bool p_smooth, bool p_invert, const Ref<Material> &p_material, bool p_from_b);
+ // void add_face(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, bool p_from_b);
+
+ float vertex_snap;
+ void mark_inside_faces();
+ };
+
+ struct BuildPoly {
+
+ Plane plane;
+ Transform to_poly;
+ Transform to_world;
+ int face_index;
+
+ struct Point {
+ Vector2 point;
+ Vector2 uv;
+ };
+
+ Vector<Point> points;
+
+ struct Edge {
+ bool outer;
+ int points[2];
+ Edge() {
+ outer = false;
+ }
+ };
+
+ Vector<Edge> edges;
+ Ref<Material> material;
+ bool smooth;
+ bool invert;
+
+ int base_edges; //edges from original triangle, even if split
+
+ void _clip_segment(const CSGBrush *p_brush, int p_face, const Vector2 *segment, MeshMerge &mesh_merge, bool p_for_B);
+
+ void create(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B);
+ void clip(const CSGBrush *p_brush, int p_face, MeshMerge &mesh_merge, bool p_for_B);
+ };
+
+ struct PolyPoints {
+
+ Vector<int> points;
+
+ Vector<Vector<int> > holes;
+ };
+
+ struct EdgeSort {
+ int edge;
+ int prev_point;
+ int edge_point;
+ float angle;
+ bool operator<(const EdgeSort &p_edge) const { return angle < p_edge.angle; }
+ };
+
+ struct CallbackData {
+ const CSGBrush *A;
+ const CSGBrush *B;
+ int face_a;
+ CSGBrushOperation *self;
+ Map<int, BuildPoly> build_polys_A;
+ Map<int, BuildPoly> build_polys_B;
+ };
+
+ void _add_poly_points(const BuildPoly &p_poly, int p_edge, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<bool> &edge_process, Vector<PolyPoints> &r_poly);
+ void _add_poly_outline(const BuildPoly &p_poly, int p_from_point, int p_to_point, const Vector<Vector<int> > &vertex_process, Vector<int> &r_outline);
+ void _merge_poly(MeshMerge &mesh, int p_face_idx, const BuildPoly &p_poly, bool p_from_b);
+
+ void _collision_callback(const CSGBrush *A, int p_face_a, Map<int, BuildPoly> &build_polys_a, const CSGBrush *B, int p_face_b, Map<int, BuildPoly> &build_polys_b, MeshMerge &mesh_merge);
+
+ static void _collision_callbacks(void *ud, int p_face_b);
+ void merge_brushes(Operation p_operation, const CSGBrush &p_A, const CSGBrush &p_B, CSGBrush &result, float p_snap = 0.001);
+};
+
+#endif // CSG_H
diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp
new file mode 100644
index 000000000..06cbaab3b
--- /dev/null
+++ b/modules/csg/csg_gizmos.cpp
@@ -0,0 +1,315 @@
+#include "csg_gizmos.h"
+
+///////////
+
+String CSGShapeSpatialGizmo::get_handle_name(int p_idx) const {
+
+ if (Object::cast_to<CSGSphere>(cs)) {
+
+ return "Radius";
+ }
+
+ if (Object::cast_to<CSGBox>(cs)) {
+
+ static const char *hname[3] = { "Width", "Height", "Depth" };
+ return hname[p_idx];
+ }
+
+ if (Object::cast_to<CSGCylinder>(cs)) {
+
+ return p_idx == 0 ? "Radius" : "Height";
+ }
+
+ if (Object::cast_to<CSGTorus>(cs)) {
+
+ return p_idx == 0 ? "InnerRadius" : "OuterRadius";
+ }
+
+ return "";
+}
+Variant CSGShapeSpatialGizmo::get_handle_value(int p_idx) const {
+
+ if (Object::cast_to<CSGSphere>(cs)) {
+
+ CSGSphere *s = Object::cast_to<CSGSphere>(cs);
+ return s->get_radius();
+ }
+
+ if (Object::cast_to<CSGBox>(cs)) {
+
+ CSGBox *s = Object::cast_to<CSGBox>(cs);
+ switch (p_idx) {
+ case 0: return s->get_width();
+ case 1: return s->get_height();
+ case 2: return s->get_depth();
+ }
+ }
+
+ if (Object::cast_to<CSGCylinder>(cs)) {
+
+ CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
+ return p_idx == 0 ? s->get_radius() : s->get_height();
+ }
+
+ if (Object::cast_to<CSGTorus>(cs)) {
+
+ CSGTorus *s = Object::cast_to<CSGTorus>(cs);
+ return p_idx == 0 ? s->get_inner_radius() : s->get_outer_radius();
+ }
+
+ return Variant();
+}
+void CSGShapeSpatialGizmo::set_handle(int p_idx, Camera *p_camera, const Point2 &p_point) {
+
+ Transform gt = cs->get_global_transform();
+ gt.orthonormalize();
+ Transform gi = gt.affine_inverse();
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) };
+
+ if (Object::cast_to<CSGSphere>(cs)) {
+
+ CSGSphere *s = Object::cast_to<CSGSphere>(cs);
+
+ Vector3 ra, rb;
+ Geometry::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
+ float d = ra.x;
+ if (d < 0.001)
+ d = 0.001;
+
+ s->set_radius(d);
+ }
+
+ if (Object::cast_to<CSGBox>(cs)) {
+
+ CSGBox *s = Object::cast_to<CSGBox>(cs);
+
+ Vector3 axis;
+ axis[p_idx] = 1.0;
+ Vector3 ra, rb;
+ Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = ra[p_idx];
+ if (d < 0.001)
+ d = 0.001;
+
+ switch (p_idx) {
+ case 0: s->set_width(d); break;
+ case 1: s->set_height(d); break;
+ case 2: s->set_depth(d); break;
+ }
+ }
+
+ if (Object::cast_to<CSGCylinder>(cs)) {
+
+ CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
+
+ Vector3 axis;
+ axis[p_idx == 0 ? 0 : 1] = 1.0;
+ Vector3 ra, rb;
+ Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = axis.dot(ra);
+
+ if (d < 0.001)
+ d = 0.001;
+
+ if (p_idx == 0)
+ s->set_radius(d);
+ else if (p_idx == 1)
+ s->set_height(d * 2.0);
+ }
+
+ if (Object::cast_to<CSGTorus>(cs)) {
+
+ CSGTorus *s = Object::cast_to<CSGTorus>(cs);
+
+ Vector3 axis;
+ axis[0] = 1.0;
+ Vector3 ra, rb;
+ Geometry::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = axis.dot(ra);
+
+ if (d < 0.001)
+ d = 0.001;
+
+ if (p_idx == 0)
+ s->set_inner_radius(d);
+ else if (p_idx == 1)
+ s->set_outer_radius(d);
+ }
+}
+void CSGShapeSpatialGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) {
+
+ if (Object::cast_to<CSGSphere>(cs)) {
+ CSGSphere *s = Object::cast_to<CSGSphere>(cs);
+ if (p_cancel) {
+ s->set_radius(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Sphere Shape Radius"));
+ ur->add_do_method(s, "set_radius", s->get_radius());
+ ur->add_undo_method(s, "set_radius", p_restore);
+ ur->commit_action();
+ }
+
+ if (Object::cast_to<CSGBox>(cs)) {
+ CSGBox *s = Object::cast_to<CSGBox>(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;
+ }
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::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;
+ 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->commit_action();
+ }
+
+ if (Object::cast_to<CSGCylinder>(cs)) {
+ CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
+ if (p_cancel) {
+ if (p_idx == 0)
+ s->set_radius(p_restore);
+ else
+ s->set_height(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ if (p_idx == 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);
+ } else {
+ ur->create_action(TTR("Change Cylinder Height"));
+ ur->add_do_method(s, "set_height", s->get_height());
+ ur->add_undo_method(s, "set_height", p_restore);
+ }
+
+ ur->commit_action();
+ }
+
+ if (Object::cast_to<CSGTorus>(cs)) {
+ CSGTorus *s = Object::cast_to<CSGTorus>(cs);
+ if (p_cancel) {
+ if (p_idx == 0)
+ s->set_inner_radius(p_restore);
+ else
+ s->set_outer_radius(p_restore);
+ return;
+ }
+
+ UndoRedo *ur = SpatialEditor::get_singleton()->get_undo_redo();
+ if (p_idx == 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);
+ } else {
+ ur->create_action(TTR("Change Torus Outer Radius"));
+ ur->add_do_method(s, "set_outer_radius", s->get_outer_radius());
+ ur->add_undo_method(s, "set_outer_radius", p_restore);
+ }
+
+ ur->commit_action();
+ }
+}
+void CSGShapeSpatialGizmo::redraw() {
+
+ clear();
+
+ Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/csg");
+ Ref<Material> material = create_material("shape_material", gizmo_color);
+
+ PoolVector<Vector3> faces = cs->get_brush_faces();
+
+ Vector<Vector3> lines;
+ lines.resize(faces.size() * 2);
+ {
+ PoolVector<Vector3>::Read r = faces.read();
+
+ for (int i = 0; i < lines.size(); i += 6) {
+ int f = i / 6;
+ for (int j = 0; j < 3; j++) {
+ int j_n = (j + 1) % 3;
+ lines[i + j * 2 + 0] = r[f * 3 + j];
+ lines[i + j * 2 + 1] = r[f * 3 + j_n];
+ }
+ }
+ }
+
+ add_lines(lines, material);
+ add_collision_segments(lines);
+
+ if (Object::cast_to<CSGSphere>(cs)) {
+ CSGSphere *s = Object::cast_to<CSGSphere>(cs);
+
+ float r = s->get_radius();
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(r, 0, 0));
+ add_handles(handles);
+ }
+
+ if (Object::cast_to<CSGBox>(cs)) {
+ CSGBox *s = Object::cast_to<CSGBox>(cs);
+
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(s->get_width(), 0, 0));
+ handles.push_back(Vector3(0, s->get_height(), 0));
+ handles.push_back(Vector3(0, 0, s->get_depth()));
+ add_handles(handles);
+ }
+
+ if (Object::cast_to<CSGCylinder>(cs)) {
+ CSGCylinder *s = Object::cast_to<CSGCylinder>(cs);
+
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(s->get_radius(), 0, 0));
+ handles.push_back(Vector3(0, s->get_height() * 0.5, 0));
+ add_handles(handles);
+ }
+
+ if (Object::cast_to<CSGTorus>(cs)) {
+ CSGTorus *s = Object::cast_to<CSGTorus>(cs);
+
+ Vector<Vector3> handles;
+ handles.push_back(Vector3(s->get_inner_radius(), 0, 0));
+ handles.push_back(Vector3(s->get_outer_radius(), 0, 0));
+ add_handles(handles);
+ }
+}
+CSGShapeSpatialGizmo::CSGShapeSpatialGizmo(CSGShape *p_cs) {
+
+ cs = p_cs;
+ set_spatial_node(p_cs);
+}
+
+Ref<SpatialEditorGizmo> EditorPluginCSG::create_spatial_gizmo(Spatial *p_spatial) {
+ if (Object::cast_to<CSGSphere>(p_spatial) || Object::cast_to<CSGBox>(p_spatial) || Object::cast_to<CSGCylinder>(p_spatial) || Object::cast_to<CSGTorus>(p_spatial) || Object::cast_to<CSGMesh>(p_spatial) || Object::cast_to<CSGPolygon>(p_spatial)) {
+ Ref<CSGShapeSpatialGizmo> csg = memnew(CSGShapeSpatialGizmo(Object::cast_to<CSGShape>(p_spatial)));
+ return csg;
+ }
+
+ return Ref<SpatialEditorGizmo>();
+}
+
+EditorPluginCSG::EditorPluginCSG(EditorNode *p_editor) {
+
+ EDITOR_DEF("editors/3d_gizmos/gizmo_colors/csg", Color(0.2, 0.5, 1, 0.1));
+}
diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h
new file mode 100644
index 000000000..b5e394eca
--- /dev/null
+++ b/modules/csg/csg_gizmos.h
@@ -0,0 +1,30 @@
+#ifndef CSG_GIZMOS_H
+#define CSG_GIZMOS_H
+
+#include "csg_shape.h"
+#include "editor/editor_plugin.h"
+#include "editor/spatial_editor_gizmos.h"
+
+class CSGShapeSpatialGizmo : public EditorSpatialGizmo {
+
+ GDCLASS(CSGShapeSpatialGizmo, EditorSpatialGizmo);
+
+ CSGShape *cs;
+
+public:
+ virtual String get_handle_name(int p_idx) const;
+ virtual Variant get_handle_value(int p_idx) const;
+ virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point);
+ virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false);
+ void redraw();
+ CSGShapeSpatialGizmo(CSGShape *p_cs = NULL);
+};
+
+class EditorPluginCSG : public EditorPlugin {
+ GDCLASS(EditorPluginCSG, EditorPlugin)
+public:
+ virtual Ref<SpatialEditorGizmo> create_spatial_gizmo(Spatial *p_spatial);
+ EditorPluginCSG(EditorNode *p_editor);
+};
+
+#endif // CSG_GIZMOS_H
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
new file mode 100644
index 000000000..1f2f12f54
--- /dev/null
+++ b/modules/csg/csg_shape.cpp
@@ -0,0 +1,2125 @@
+#include "csg_shape.h"
+#include "scene/3d/path.h"
+
+void CSGShape::set_use_collision(bool p_enable) {
+
+ if (use_collision == p_enable)
+ return;
+
+ use_collision = p_enable;
+
+ if (!is_inside_tree() || !is_root_shape())
+ return;
+
+ if (use_collision) {
+ root_collision_shape.instance();
+ root_collision_instance = PhysicsServer::get_singleton()->body_create(PhysicsServer::BODY_MODE_STATIC);
+ PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform());
+ PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
+ PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space());
+ _make_dirty(); //force update
+ } else {
+ PhysicsServer::get_singleton()->free(root_collision_instance);
+ root_collision_instance = RID();
+ root_collision_shape.unref();
+ }
+}
+
+bool CSGShape::is_using_collision() const {
+ return use_collision;
+}
+
+bool CSGShape::is_root_shape() const {
+
+ return !parent;
+}
+
+void CSGShape::set_snap(float p_snap) {
+ snap = p_snap;
+}
+
+float CSGShape::get_snap() const {
+ return snap;
+}
+
+void CSGShape::_make_dirty() {
+
+ if (!is_inside_tree())
+ return;
+
+ if (dirty) {
+ return;
+ }
+
+ dirty = true;
+
+ if (parent) {
+ parent->_make_dirty();
+ } else {
+ //only parent will do
+ call_deferred("_update_shape");
+ }
+}
+
+CSGBrush *CSGShape::_get_brush() {
+
+ if (dirty) {
+ if (brush) {
+ memdelete(brush);
+ }
+ brush = NULL;
+
+ CSGBrush *n = _build_brush();
+
+ for (int i = 0; i < get_child_count(); i++) {
+
+ CSGShape *child = Object::cast_to<CSGShape>(get_child(i));
+ if (!child)
+ continue;
+ if (!child->is_visible_in_tree())
+ continue;
+
+ CSGBrush *n2 = child->_get_brush();
+ if (!n2)
+ continue;
+ if (!n) {
+ n = memnew(CSGBrush);
+
+ n->copy_from(*n2, child->get_transform());
+
+ } else {
+
+ CSGBrush *nn = memnew(CSGBrush);
+ CSGBrush *nn2 = memnew(CSGBrush);
+ nn2->copy_from(*n2, child->get_transform());
+
+ CSGBrushOperation bop;
+
+ switch (child->get_operation()) {
+ case CSGShape::OPERATION_UNION: bop.merge_brushes(CSGBrushOperation::OPERATION_UNION, *n, *nn2, *nn, snap); break;
+ case CSGShape::OPERATION_INTERSECTION: bop.merge_brushes(CSGBrushOperation::OPERATION_INTERSECTION, *n, *nn2, *nn, snap); break;
+ case CSGShape::OPERATION_SUBTRACTION: bop.merge_brushes(CSGBrushOperation::OPERATION_SUBSTRACTION, *n, *nn2, *nn, snap); break;
+ }
+ memdelete(n);
+ memdelete(nn2);
+ n = nn;
+ }
+ }
+
+ if (n) {
+ AABB aabb;
+ for (int i = 0; i < n->faces.size(); i++) {
+ for (int j = 0; j < 3; j++) {
+ if (i == 0 && j == 0)
+ aabb.position = n->faces[i].vertices[j];
+ else
+ aabb.expand_to(n->faces[i].vertices[j]);
+ }
+ }
+ node_aabb = aabb;
+ } else {
+ node_aabb = AABB();
+ }
+
+ brush = n;
+
+ dirty = false;
+ }
+
+ return brush;
+}
+
+void CSGShape::_update_shape() {
+
+ //print_line("updating shape for " + String(get_path()));
+ set_base(RID());
+ root_mesh.unref(); //byebye root mesh
+
+ CSGBrush *n = _get_brush();
+ ERR_FAIL_COND(!n);
+
+ OAHashMap<Vector3, Vector3> vec_map;
+
+ Vector<int> face_count;
+ face_count.resize(n->materials.size() + 1);
+ for (int i = 0; i < face_count.size(); i++) {
+ face_count[i] = 0;
+ }
+
+ for (int i = 0; i < n->faces.size(); i++) {
+ int mat = n->faces[i].material;
+ ERR_CONTINUE(mat < -1 || mat >= face_count.size());
+ int idx = mat == -1 ? face_count.size() - 1 : mat;
+ if (n->faces[i].smooth) {
+
+ Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]);
+
+ for (int j = 0; j < 3; j++) {
+ Vector3 v = n->faces[i].vertices[j];
+ Vector3 add;
+ if (vec_map.lookup(v, add)) {
+ add += p.normal;
+ } else {
+ add = p.normal;
+ }
+ vec_map.set(v, add);
+ }
+ }
+
+ face_count[idx]++;
+ }
+
+ Vector<ShapeUpdateSurface> surfaces;
+
+ surfaces.resize(face_count.size());
+
+ //create arrays
+ for (int i = 0; i < surfaces.size(); i++) {
+
+ surfaces[i].vertices.resize(face_count[i] * 3);
+ surfaces[i].normals.resize(face_count[i] * 3);
+ surfaces[i].uvs.resize(face_count[i] * 3);
+ surfaces[i].last_added = 0;
+
+ if (i != surfaces.size() - 1) {
+ surfaces[i].material = n->materials[i];
+ }
+
+ surfaces[i].verticesw = surfaces[i].vertices.write();
+ surfaces[i].normalsw = surfaces[i].normals.write();
+ surfaces[i].uvsw = surfaces[i].uvs.write();
+ }
+
+ //fill arrays
+ PoolVector<Vector3> physics_faces;
+ bool fill_physics_faces = false;
+ if (root_collision_shape.is_valid()) {
+ physics_faces.resize(n->faces.size() * 3);
+ fill_physics_faces = true;
+ }
+
+ {
+ PoolVector<Vector3>::Write physicsw;
+
+ if (fill_physics_faces) {
+ physicsw = physics_faces.write();
+ }
+
+ for (int i = 0; i < n->faces.size(); i++) {
+
+ int order[3] = { 0, 1, 2 };
+
+ if (n->faces[i].invert) {
+ SWAP(order[1], order[2]);
+ }
+
+ if (fill_physics_faces) {
+ physicsw[i * 3 + 0] = n->faces[i].vertices[order[0]];
+ physicsw[i * 3 + 1] = n->faces[i].vertices[order[1]];
+ physicsw[i * 3 + 2] = n->faces[i].vertices[order[2]];
+ }
+
+ int mat = n->faces[i].material;
+ ERR_CONTINUE(mat < -1 || mat >= face_count.size());
+ int idx = mat == -1 ? face_count.size() - 1 : mat;
+
+ int last = surfaces[idx].last_added;
+
+ Plane p(n->faces[i].vertices[0], n->faces[i].vertices[1], n->faces[i].vertices[2]);
+
+ for (int j = 0; j < 3; j++) {
+
+ Vector3 v = n->faces[i].vertices[j];
+
+ Vector3 normal = p.normal;
+
+ if (n->faces[i].smooth && vec_map.lookup(v, normal)) {
+ normal.normalize();
+ }
+
+ if (n->faces[i].invert) {
+
+ normal = -normal;
+ }
+
+ surfaces[idx].verticesw[last + order[j]] = v;
+ surfaces[idx].uvsw[last + order[j]] = n->faces[i].uvs[j];
+ surfaces[idx].normalsw[last + order[j]] = normal;
+ }
+
+ surfaces[idx].last_added += 3;
+ }
+ }
+
+ root_mesh.instance();
+ //create surfaces
+
+ for (int i = 0; i < surfaces.size(); i++) {
+
+ surfaces[i].verticesw = PoolVector<Vector3>::Write();
+ surfaces[i].normalsw = PoolVector<Vector3>::Write();
+ surfaces[i].uvsw = PoolVector<Vector2>::Write();
+
+ if (surfaces[i].last_added == 0)
+ continue;
+
+ Array array;
+ array.resize(Mesh::ARRAY_MAX);
+
+ array[Mesh::ARRAY_VERTEX] = surfaces[i].vertices;
+ array[Mesh::ARRAY_NORMAL] = surfaces[i].normals;
+ array[Mesh::ARRAY_TEX_UV] = surfaces[i].uvs;
+
+ int idx = root_mesh->get_surface_count();
+ root_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array);
+ root_mesh->surface_set_material(idx, surfaces[i].material);
+ }
+
+ if (root_collision_shape.is_valid()) {
+ root_collision_shape->set_faces(physics_faces);
+ }
+
+ set_base(root_mesh->get_rid());
+}
+AABB CSGShape::get_aabb() const {
+ return node_aabb;
+}
+
+PoolVector<Vector3> CSGShape::get_brush_faces() {
+ ERR_FAIL_COND_V(!is_inside_tree(), PoolVector<Vector3>());
+ CSGBrush *b = _get_brush();
+ if (!b) {
+ return PoolVector<Vector3>();
+ }
+
+ PoolVector<Vector3> faces;
+ int fc = b->faces.size();
+ faces.resize(fc * 3);
+ {
+ PoolVector<Vector3>::Write w = faces.write();
+ for (int i = 0; i < fc; i++) {
+ w[i * 3 + 0] = b->faces[i].vertices[0];
+ w[i * 3 + 1] = b->faces[i].vertices[1];
+ w[i * 3 + 2] = b->faces[i].vertices[2];
+ }
+ }
+
+ return faces;
+}
+
+PoolVector<Face3> CSGShape::get_faces(uint32_t p_usage_flags) const {
+
+ return PoolVector<Face3>();
+}
+
+void CSGShape::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+
+ Node *parentn = get_parent();
+ if (parentn) {
+ parent = Object::cast_to<CSGShape>(parentn);
+ }
+
+ if (use_collision && is_root_shape()) {
+ root_collision_shape.instance();
+ root_collision_instance = PhysicsServer::get_singleton()->body_create(PhysicsServer::BODY_MODE_STATIC);
+ PhysicsServer::get_singleton()->body_set_state(root_collision_instance, PhysicsServer::BODY_STATE_TRANSFORM, get_global_transform());
+ PhysicsServer::get_singleton()->body_add_shape(root_collision_instance, root_collision_shape->get_rid());
+ PhysicsServer::get_singleton()->body_set_space(root_collision_instance, get_world()->get_space());
+ }
+
+ _make_dirty();
+ }
+
+ if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
+
+ //print_line("local xform changed");
+ if (parent) {
+ parent->_make_dirty();
+ }
+ }
+
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ if (parent)
+ parent->_make_dirty();
+ parent = NULL;
+
+ if (use_collision && is_root_shape()) {
+ PhysicsServer::get_singleton()->free(root_collision_instance);
+ root_collision_instance = RID();
+ root_collision_shape.unref();
+ }
+ _make_dirty();
+ }
+}
+
+void CSGShape::set_operation(Operation p_operation) {
+
+ operation = p_operation;
+ _make_dirty();
+}
+
+CSGShape::Operation CSGShape::get_operation() const {
+ return operation;
+}
+
+void CSGShape::_validate_property(PropertyInfo &property) const {
+ if (is_inside_tree() && property.name.begins_with("use_collision") && !is_root_shape()) {
+ //hide collision if not root
+ property.usage = PROPERTY_USAGE_NOEDITOR;
+ }
+}
+
+void CSGShape::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_update_shape"), &CSGShape::_update_shape);
+ ClassDB::bind_method(D_METHOD("is_root_shape"), &CSGShape::is_root_shape);
+
+ ClassDB::bind_method(D_METHOD("set_operation", "operation"), &CSGShape::set_operation);
+ ClassDB::bind_method(D_METHOD("get_operation"), &CSGShape::get_operation);
+
+ ClassDB::bind_method(D_METHOD("set_use_collision", "operation"), &CSGShape::set_use_collision);
+ ClassDB::bind_method(D_METHOD("is_using_collision"), &CSGShape::is_using_collision);
+
+ ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CSGShape::set_snap);
+ ClassDB::bind_method(D_METHOD("get_snap"), &CSGShape::get_snap);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_collision"), "set_use_collision", "is_using_collision");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_RANGE, "0.0001,1,0.001"), "set_snap", "get_snap");
+
+ BIND_CONSTANT(OPERATION_UNION);
+ BIND_CONSTANT(OPERATION_INTERSECTION);
+ BIND_CONSTANT(OPERATION_SUBTRACTION);
+}
+
+CSGShape::CSGShape() {
+ brush = NULL;
+ set_notify_local_transform(true);
+ dirty = false;
+ parent = NULL;
+ use_collision = false;
+ operation = OPERATION_UNION;
+ snap = 0.001;
+}
+
+CSGShape::~CSGShape() {
+ if (brush) {
+ memdelete(brush);
+ brush = NULL;
+ }
+}
+//////////////////////////////////
+
+CSGBrush *CSGCombiner::_build_brush() {
+
+ return NULL; //does not build anything
+}
+
+CSGCombiner::CSGCombiner() {
+}
+
+/////////////////////
+
+CSGBrush *CSGPrimitive::_create_brush_from_arrays(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uv, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials) {
+
+ CSGBrush *brush = memnew(CSGBrush);
+
+ PoolVector<bool> invert;
+ invert.resize(p_vertices.size() / 3);
+ {
+ int ic = invert.size();
+ PoolVector<bool>::Write w = invert.write();
+ for (int i = 0; i < ic; i++) {
+ w[i] = invert_faces;
+ }
+ }
+ brush->build_from_faces(p_vertices, p_uv, p_smooth, p_materials, invert);
+
+ return brush;
+}
+
+void CSGPrimitive::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_invert_faces", "invert_faces"), &CSGPrimitive::set_invert_faces);
+ ClassDB::bind_method(D_METHOD("is_inverting_faces"), &CSGPrimitive::is_inverting_faces);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_faces"), "set_invert_faces", "is_inverting_faces");
+}
+
+void CSGPrimitive::set_invert_faces(bool p_invert) {
+ if (invert_faces == p_invert)
+ return;
+
+ invert_faces = p_invert;
+
+ _make_dirty();
+}
+
+bool CSGPrimitive::is_inverting_faces() {
+ return invert_faces;
+}
+
+CSGPrimitive::CSGPrimitive() {
+ invert_faces = false;
+}
+
+/////////////////////
+
+CSGBrush *CSGMesh::_build_brush() {
+
+ if (!mesh.is_valid())
+ return NULL;
+
+ PoolVector<Vector3> vertices;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<Vector2> uvs;
+
+ for (int i = 0; i < mesh->get_surface_count(); i++) {
+
+ if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) {
+ continue;
+ }
+
+ Array arrays = mesh->surface_get_arrays(i);
+
+ PoolVector<Vector3> avertices = arrays[Mesh::ARRAY_VERTEX];
+ if (avertices.size() == 0)
+ continue;
+
+ PoolVector<Vector3>::Read vr = avertices.read();
+
+ PoolVector<Vector3> anormals = arrays[Mesh::ARRAY_NORMAL];
+ PoolVector<Vector3>::Read nr;
+ bool nr_used = false;
+ if (anormals.size()) {
+ nr = anormals.read();
+ nr_used = true;
+ }
+
+ PoolVector<Vector2> auvs = arrays[Mesh::ARRAY_TEX_UV];
+ PoolVector<Vector2>::Read uvr;
+ bool uvr_used = false;
+ if (auvs.size()) {
+ uvr = auvs.read();
+ uvr_used = true;
+ }
+
+ Ref<Material> mat = mesh->surface_get_material(i);
+
+ PoolVector<int> aindices = arrays[Mesh::ARRAY_INDEX];
+ if (aindices.size()) {
+ int as = vertices.size();
+ int is = aindices.size();
+
+ vertices.resize(as + is);
+ smooth.resize((as + is) / 3);
+ materials.resize((as + is) / 3);
+ uvs.resize(as + is);
+
+ PoolVector<Vector3>::Write vw = vertices.write();
+ PoolVector<bool>::Write sw = smooth.write();
+ PoolVector<Vector2>::Write uvw = uvs.write();
+ PoolVector<Ref<Material> >::Write mw = materials.write();
+
+ PoolVector<int>::Read ir = aindices.read();
+
+ for (int j = 0; j < is; j += 3) {
+
+ Vector3 vertex[3];
+ Vector3 normal[3];
+ Vector2 uv[3];
+
+ for (int k = 0; k < 3; k++) {
+ int idx = ir[j + k];
+ vertex[k] = vr[idx];
+ if (nr_used) {
+ normal[k] = nr[idx];
+ }
+ if (uvr_used) {
+ uv[k] = uvr[idx];
+ }
+ }
+
+ bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON;
+
+ vw[as + j + 0] = vertex[0];
+ vw[as + j + 1] = vertex[1];
+ vw[as + j + 2] = vertex[2];
+
+ uvw[as + j + 0] = uv[0];
+ uvw[as + j + 1] = uv[1];
+ uvw[as + j + 2] = uv[2];
+
+ sw[j / 3] = !flat;
+ mw[j / 3] = mat;
+ }
+ } else {
+ int is = vertices.size();
+ int as = avertices.size();
+
+ vertices.resize(as + is);
+ smooth.resize((as + is) / 3);
+ uvs.resize(as + is);
+ materials.resize((as + is) / 3);
+
+ PoolVector<Vector3>::Write vw = vertices.write();
+ PoolVector<bool>::Write sw = smooth.write();
+ PoolVector<Vector2>::Write uvw = uvs.write();
+ PoolVector<Ref<Material> >::Write mw = materials.write();
+
+ for (int j = 0; j < is; j += 3) {
+
+ Vector3 vertex[3];
+ Vector3 normal[3];
+ Vector2 uv[3];
+
+ for (int k = 0; k < 3; k++) {
+ vertex[k] = vr[j + k];
+ if (nr_used) {
+ normal[k] = nr[j + k];
+ }
+ if (uvr_used) {
+ uv[k] = uvr[j + k];
+ }
+ }
+
+ bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON;
+
+ vw[as + j + 0] = vertex[0];
+ vw[as + j + 1] = vertex[1];
+ vw[as + j + 2] = vertex[2];
+
+ uvw[as + j + 0] = uv[0];
+ uvw[as + j + 1] = uv[1];
+ uvw[as + j + 2] = uv[2];
+
+ sw[j / 3] = !flat;
+ mw[j / 3] = mat;
+ }
+ }
+ }
+
+ //print_line("total vertices? " + itos(vertices.size()));
+ if (vertices.size() == 0)
+ return NULL;
+
+ return _create_brush_from_arrays(vertices, uvs, smooth, materials);
+}
+
+void CSGMesh::_mesh_changed() {
+ _make_dirty();
+ update_gizmo();
+}
+
+void CSGMesh::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_mesh", "mesh"), &CSGMesh::set_mesh);
+ ClassDB::bind_method(D_METHOD("get_mesh"), &CSGMesh::get_mesh);
+
+ ClassDB::bind_method(D_METHOD("_mesh_changed"), &CSGMesh::_mesh_changed);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_mesh", "get_mesh");
+}
+
+void CSGMesh::set_mesh(const Ref<Mesh> &p_mesh) {
+
+ if (mesh == p_mesh)
+ return;
+ if (mesh.is_valid()) {
+ mesh->disconnect("changed", this, "_mesh_changed");
+ }
+ mesh = p_mesh;
+
+ if (mesh.is_valid()) {
+ mesh->connect("changed", this, "_mesh_changed");
+ }
+
+ _make_dirty();
+}
+
+Ref<Mesh> CSGMesh::get_mesh() {
+ return mesh;
+}
+
+////////////////////////////////
+
+CSGBrush *CSGSphere::_build_brush() {
+
+ // set our bounding box
+
+ CSGBrush *brush = memnew(CSGBrush);
+
+ int face_count = rings * radial_segments * 2 - radial_segments * 2;
+
+ bool invert_val = is_inverting_faces();
+ Ref<Material> material = get_material();
+
+ PoolVector<Vector3> faces;
+ PoolVector<Vector2> uvs;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<bool> invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+
+ PoolVector<Vector3>::Write facesw = faces.write();
+ PoolVector<Vector2>::Write uvsw = uvs.write();
+ PoolVector<bool>::Write smoothw = smooth.write();
+ PoolVector<Ref<Material> >::Write materialsw = materials.write();
+ PoolVector<bool>::Write invertw = invert.write();
+
+ 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;
+
+ 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
+ };
+
+ Vector2 u[4] = {
+ Vector2(v1, u0),
+ Vector2(v1, u1),
+ Vector2(v0, u1),
+ Vector2(v0, u0),
+
+ };
+
+ if (i < rings) {
+
+ //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;
+
+ face++;
+ }
+
+ if (i > 1) {
+ //face 2
+ facesw[face * 3 + 0] = v[2];
+ facesw[face * 3 + 1] = v[3];
+ facesw[face * 3 + 2] = v[0];
+
+ uvsw[face * 3 + 0] = u[2];
+ uvsw[face * 3 + 1] = u[3];
+ uvsw[face * 3 + 2] = u[0];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ }
+ }
+ }
+
+ if (face != face_count) {
+ ERR_PRINT("Face mismatch bug! fix code");
+ }
+ }
+
+ brush->build_from_faces(faces, uvs, smooth, materials, invert);
+
+ return brush;
+}
+
+void CSGSphere::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGSphere::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &CSGSphere::get_radius);
+
+ ClassDB::bind_method(D_METHOD("set_radial_segments", "radial_segments"), &CSGSphere::set_radial_segments);
+ ClassDB::bind_method(D_METHOD("get_radial_segments"), &CSGSphere::get_radial_segments);
+ ClassDB::bind_method(D_METHOD("set_rings", "rings"), &CSGSphere::set_rings);
+ ClassDB::bind_method(D_METHOD("get_rings"), &CSGSphere::get_rings);
+
+ ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGSphere::set_smooth_faces);
+ ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGSphere::get_smooth_faces);
+
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGSphere::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGSphere::get_material);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,100.0,0.001"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "radial_segments", PROPERTY_HINT_RANGE, "1,100,1"), "set_radial_segments", "get_radial_segments");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "rings", PROPERTY_HINT_RANGE, "1,100,1"), "set_rings", "get_rings");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+}
+
+void CSGSphere::set_radius(const float p_radius) {
+ ERR_FAIL_COND(p_radius <= 0);
+ radius = p_radius;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGSphere::get_radius() const {
+ return radius;
+}
+
+void CSGSphere::set_radial_segments(const int p_radial_segments) {
+ radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGSphere::get_radial_segments() const {
+ return radial_segments;
+}
+
+void CSGSphere::set_rings(const int p_rings) {
+ rings = p_rings > 1 ? p_rings : 1;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGSphere::get_rings() const {
+ return rings;
+}
+
+void CSGSphere::set_smooth_faces(const bool p_smooth_faces) {
+ smooth_faces = p_smooth_faces;
+ _make_dirty();
+}
+
+bool CSGSphere::get_smooth_faces() const {
+ return smooth_faces;
+}
+
+void CSGSphere::set_material(const Ref<Material> &p_material) {
+
+ material = p_material;
+ _make_dirty();
+}
+
+Ref<Material> CSGSphere::get_material() const {
+
+ return material;
+}
+
+CSGSphere::CSGSphere() {
+ // defaults
+ radius = 1.0;
+ radial_segments = 12;
+ rings = 6;
+ smooth_faces = true;
+}
+
+///////////////
+
+CSGBrush *CSGBox::_build_brush() {
+
+ // set our bounding box
+
+ CSGBrush *brush = memnew(CSGBrush);
+
+ int face_count = 12; //it's a cube..
+
+ bool invert_val = is_inverting_faces();
+ Ref<Material> material = get_material();
+
+ PoolVector<Vector3> faces;
+ PoolVector<Vector2> uvs;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<bool> invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+
+ PoolVector<Vector3>::Write facesw = faces.write();
+ PoolVector<Vector2>::Write uvsw = uvs.write();
+ PoolVector<bool>::Write smoothw = smooth.write();
+ PoolVector<Ref<Material> >::Write materialsw = materials.write();
+ PoolVector<bool>::Write invertw = invert.write();
+
+ int face = 0;
+
+ Vector3 vertex_mul(width, height, depth);
+
+ {
+
+ for (int i = 0; i < 6; i++) {
+
+ Vector3 face_points[4];
+ float uv_points[8] = { 0, 0, 0, 1, 1, 1, 1, 0 };
+
+ for (int j = 0; j < 4; j++) {
+
+ float v[3];
+ v[0] = 1.0;
+ v[1] = 1 - 2 * ((j >> 1) & 1);
+ v[2] = v[1] * (1 - 2 * (j & 1));
+
+ for (int k = 0; k < 3; k++) {
+
+ if (i < 3)
+ face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ else
+ face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ }
+ }
+
+ Vector2 u[4];
+ for (int j = 0; j < 4; j++) {
+ u[j] = Vector2(uv_points[j * 2 + 0], uv_points[j * 2 + 1]);
+ }
+
+ //face 1
+ facesw[face * 3 + 0] = face_points[0] * vertex_mul;
+ facesw[face * 3 + 1] = face_points[1] * vertex_mul;
+ facesw[face * 3 + 2] = face_points[2] * vertex_mul;
+
+ uvsw[face * 3 + 0] = u[0];
+ uvsw[face * 3 + 1] = u[1];
+ uvsw[face * 3 + 2] = u[2];
+
+ smoothw[face] = false;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ //face 1
+ 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;
+
+ uvsw[face * 3 + 0] = u[2];
+ uvsw[face * 3 + 1] = u[3];
+ uvsw[face * 3 + 2] = u[0];
+
+ smoothw[face] = false;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ }
+ }
+
+ if (face != face_count) {
+ ERR_PRINT("Face mismatch bug! fix code");
+ }
+ }
+
+ brush->build_from_faces(faces, uvs, smooth, materials, invert);
+
+ return brush;
+}
+
+void CSGBox::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_width", "width"), &CSGBox::set_width);
+ ClassDB::bind_method(D_METHOD("get_width"), &CSGBox::get_width);
+
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGBox::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &CSGBox::get_height);
+
+ ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGBox::set_depth);
+ ClassDB::bind_method(D_METHOD("get_depth"), &CSGBox::get_depth);
+
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGBox::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGBox::get_material);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "width", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_width", "get_width");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_height", "get_height");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_depth", "get_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+}
+
+void CSGBox::set_width(const float p_width) {
+ width = p_width;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGBox::get_width() const {
+ return width;
+}
+
+void CSGBox::set_height(const float p_height) {
+ height = p_height;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGBox::get_height() const {
+ return height;
+}
+
+void CSGBox::set_depth(const float p_depth) {
+ depth = p_depth;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGBox::get_depth() const {
+ return depth;
+}
+
+void CSGBox::set_material(const Ref<Material> &p_material) {
+
+ material = p_material;
+ _make_dirty();
+ update_gizmo();
+}
+
+Ref<Material> CSGBox::get_material() const {
+
+ return material;
+}
+
+CSGBox::CSGBox() {
+ // defaults
+ width = 1.0;
+ height = 1.0;
+ depth = 1.0;
+}
+
+///////////////
+
+CSGBrush *CSGCylinder::_build_brush() {
+
+ // set our bounding box
+
+ CSGBrush *brush = memnew(CSGBrush);
+
+ int face_count = sides * (cone ? 1 : 2) + sides + (cone ? 0 : sides);
+
+ bool invert_val = is_inverting_faces();
+ Ref<Material> material = get_material();
+
+ PoolVector<Vector3> faces;
+ PoolVector<Vector2> uvs;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<bool> invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+
+ PoolVector<Vector3>::Write facesw = faces.write();
+ PoolVector<Vector2>::Write uvsw = uvs.write();
+ PoolVector<bool>::Write smoothw = smooth.write();
+ PoolVector<Ref<Material> >::Write materialsw = materials.write();
+ PoolVector<bool>::Write invertw = invert.write();
+
+ int face = 0;
+
+ Vector3 vertex_mul(radius, height * 0.5, radius);
+
+ {
+
+ for (int i = 0; i < sides; i++) {
+
+ 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;
+
+ Vector3 base(Math::cos(ang), 0, Math::sin(ang));
+ Vector3 base_n(Math::cos(ang_n), 0, Math::sin(ang_n));
+
+ Vector3 face_points[4] = {
+ base + Vector3(0, -1, 0),
+ base_n + Vector3(0, -1, 0),
+ base_n * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0),
+ base * (cone ? 0.0 : 1.0) + Vector3(0, 1, 0),
+ };
+
+ Vector2 u[4] = {
+ Vector2(inc, 0),
+ Vector2(inc_n, 0),
+ Vector2(inc_n, 1),
+ Vector2(inc, 1),
+ };
+
+ //side face 1
+ facesw[face * 3 + 0] = face_points[0] * vertex_mul;
+ facesw[face * 3 + 1] = face_points[1] * vertex_mul;
+ facesw[face * 3 + 2] = face_points[2] * vertex_mul;
+
+ 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;
+
+ face++;
+
+ if (!cone) {
+ //side 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;
+
+ 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;
+ face++;
+ }
+
+ //bottom face 1
+ facesw[face * 3 + 0] = face_points[1] * vertex_mul;
+ facesw[face * 3 + 1] = face_points[0] * vertex_mul;
+ facesw[face * 3 + 2] = Vector3(0, -1, 0) * vertex_mul;
+
+ uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5);
+ uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5);
+ uvsw[face * 3 + 2] = Vector2(0.5, 0.5);
+
+ smoothw[face] = false;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+ face++;
+
+ if (!cone) {
+ //top face 1
+ facesw[face * 3 + 0] = face_points[3] * vertex_mul;
+ facesw[face * 3 + 1] = face_points[2] * vertex_mul;
+ facesw[face * 3 + 2] = Vector3(0, 1, 0) * vertex_mul;
+
+ uvsw[face * 3 + 0] = Vector2(face_points[1].x, face_points[1].y) * 0.5 + Vector2(0.5, 0.5);
+ uvsw[face * 3 + 1] = Vector2(face_points[0].x, face_points[0].y) * 0.5 + Vector2(0.5, 0.5);
+ uvsw[face * 3 + 2] = Vector2(0.5, 0.5);
+
+ smoothw[face] = false;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+ face++;
+ }
+ }
+ }
+
+ if (face != face_count) {
+ ERR_PRINT("Face mismatch bug! fix code");
+ }
+ }
+
+ brush->build_from_faces(faces, uvs, smooth, materials, invert);
+
+ return brush;
+}
+
+void CSGCylinder::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_radius", "radius"), &CSGCylinder::set_radius);
+ ClassDB::bind_method(D_METHOD("get_radius"), &CSGCylinder::get_radius);
+
+ ClassDB::bind_method(D_METHOD("set_height", "height"), &CSGCylinder::set_height);
+ ClassDB::bind_method(D_METHOD("get_height"), &CSGCylinder::get_height);
+
+ ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGCylinder::set_sides);
+ ClassDB::bind_method(D_METHOD("get_sides"), &CSGCylinder::get_sides);
+
+ ClassDB::bind_method(D_METHOD("set_cone", "cone"), &CSGCylinder::set_cone);
+ ClassDB::bind_method(D_METHOD("is_cone"), &CSGCylinder::is_cone);
+
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGCylinder::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGCylinder::get_material);
+
+ ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGCylinder::set_smooth_faces);
+ ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGCylinder::get_smooth_faces);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_radius", "get_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "height", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "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, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+}
+
+void CSGCylinder::set_radius(const float p_radius) {
+ radius = p_radius;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGCylinder::get_radius() const {
+ return radius;
+}
+
+void CSGCylinder::set_height(const float p_height) {
+ height = p_height;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGCylinder::get_height() const {
+ return height;
+}
+
+void CSGCylinder::set_sides(const int p_sides) {
+ ERR_FAIL_COND(p_sides < 3);
+ sides = p_sides;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGCylinder::get_sides() const {
+ return sides;
+}
+
+void CSGCylinder::set_cone(const bool p_cone) {
+ cone = p_cone;
+ _make_dirty();
+ update_gizmo();
+}
+
+bool CSGCylinder::is_cone() const {
+ return cone;
+}
+
+void CSGCylinder::set_smooth_faces(const bool p_smooth_faces) {
+ smooth_faces = p_smooth_faces;
+ _make_dirty();
+}
+
+bool CSGCylinder::get_smooth_faces() const {
+ return smooth_faces;
+}
+
+void CSGCylinder::set_material(const Ref<Material> &p_material) {
+
+ material = p_material;
+ _make_dirty();
+}
+
+Ref<Material> CSGCylinder::get_material() const {
+
+ return material;
+}
+
+CSGCylinder::CSGCylinder() {
+ // defaults
+ radius = 1.0;
+ height = 1.0;
+ sides = 8;
+ cone = false;
+ smooth_faces = true;
+}
+
+///////////////
+
+CSGBrush *CSGTorus::_build_brush() {
+
+ // set our bounding box
+
+ float min_radius = inner_radius;
+ float max_radius = outer_radius;
+
+ if (min_radius == max_radius)
+ return NULL; //sorry, can't
+
+ if (min_radius > max_radius) {
+ SWAP(min_radius, max_radius);
+ }
+
+ float radius = (max_radius - min_radius) * 0.5;
+
+ CSGBrush *brush = memnew(CSGBrush);
+
+ int face_count = ring_sides * sides * 2;
+
+ bool invert_val = is_inverting_faces();
+ Ref<Material> material = get_material();
+
+ PoolVector<Vector3> faces;
+ PoolVector<Vector2> uvs;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<bool> invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ {
+
+ PoolVector<Vector3>::Write facesw = faces.write();
+ PoolVector<Vector2>::Write uvsw = uvs.write();
+ PoolVector<bool>::Write smoothw = smooth.write();
+ PoolVector<Ref<Material> >::Write materialsw = materials.write();
+ PoolVector<bool>::Write invertw = invert.write();
+
+ int face = 0;
+
+ {
+
+ for (int i = 0; i < sides; i++) {
+
+ 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;
+
+ Vector3 normali = Vector3(Math::cos(angi), 0, Math::sin(angi));
+ Vector3 normali_n = Vector3(Math::cos(angi_n), 0, Math::sin(angi_n));
+
+ for (int j = 0; j < ring_sides; j++) {
+
+ 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;
+
+ 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);
+
+ Vector3 face_points[4] = {
+ Vector3(normali.x * normalj.x, normalj.y, normali.z * normalj.x),
+ Vector3(normali.x * normalj_n.x, normalj_n.y, normali.z * normalj_n.x),
+ Vector3(normali_n.x * normalj_n.x, normalj_n.y, normali_n.z * normalj_n.x),
+ Vector3(normali_n.x * normalj.x, normalj.y, normali_n.z * normalj.x)
+ };
+
+ Vector2 u[4] = {
+ Vector2(inci, incj),
+ Vector2(inci, incj_n),
+ Vector2(inci_n, incj_n),
+ Vector2(inci_n, incj),
+ };
+
+ // face 1
+ facesw[face * 3 + 0] = face_points[0];
+ facesw[face * 3 + 1] = face_points[2];
+ facesw[face * 3 + 2] = face_points[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] = face_points[3];
+ facesw[face * 3 + 1] = face_points[2];
+ facesw[face * 3 + 2] = face_points[0];
+
+ uvsw[face * 3 + 0] = u[3];
+ uvsw[face * 3 + 1] = u[2];
+ uvsw[face * 3 + 2] = u[0];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+ face++;
+ }
+ }
+ }
+
+ if (face != face_count) {
+ ERR_PRINT("Face mismatch bug! fix code");
+ }
+ }
+
+ brush->build_from_faces(faces, uvs, smooth, materials, invert);
+
+ return brush;
+}
+
+void CSGTorus::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_inner_radius", "radius"), &CSGTorus::set_inner_radius);
+ ClassDB::bind_method(D_METHOD("get_inner_radius"), &CSGTorus::get_inner_radius);
+
+ ClassDB::bind_method(D_METHOD("set_outer_radius", "radius"), &CSGTorus::set_outer_radius);
+ ClassDB::bind_method(D_METHOD("get_outer_radius"), &CSGTorus::get_outer_radius);
+
+ ClassDB::bind_method(D_METHOD("set_sides", "sides"), &CSGTorus::set_sides);
+ ClassDB::bind_method(D_METHOD("get_sides"), &CSGTorus::get_sides);
+
+ ClassDB::bind_method(D_METHOD("set_ring_sides", "sides"), &CSGTorus::set_ring_sides);
+ ClassDB::bind_method(D_METHOD("get_ring_sides"), &CSGTorus::get_ring_sides);
+
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGTorus::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGTorus::get_material);
+
+ ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGTorus::set_smooth_faces);
+ ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGTorus::get_smooth_faces);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "inner_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_inner_radius", "get_inner_radius");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "outer_radius", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "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, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+}
+
+void CSGTorus::set_inner_radius(const float p_inner_radius) {
+ inner_radius = p_inner_radius;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGTorus::get_inner_radius() const {
+ return inner_radius;
+}
+
+void CSGTorus::set_outer_radius(const float p_outer_radius) {
+ outer_radius = p_outer_radius;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGTorus::get_outer_radius() const {
+ return outer_radius;
+}
+
+void CSGTorus::set_sides(const int p_sides) {
+ ERR_FAIL_COND(p_sides < 3);
+ sides = p_sides;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGTorus::get_sides() const {
+ return sides;
+}
+
+void CSGTorus::set_ring_sides(const int p_ring_sides) {
+ ERR_FAIL_COND(p_ring_sides < 3);
+ ring_sides = p_ring_sides;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGTorus::get_ring_sides() const {
+ return ring_sides;
+}
+
+void CSGTorus::set_smooth_faces(const bool p_smooth_faces) {
+ smooth_faces = p_smooth_faces;
+ _make_dirty();
+}
+
+bool CSGTorus::get_smooth_faces() const {
+ return smooth_faces;
+}
+
+void CSGTorus::set_material(const Ref<Material> &p_material) {
+
+ material = p_material;
+ _make_dirty();
+}
+
+Ref<Material> CSGTorus::get_material() const {
+
+ return material;
+}
+
+CSGTorus::CSGTorus() {
+ // defaults
+ inner_radius = 2.0;
+ outer_radius = 3.0;
+ sides = 8;
+ ring_sides = 6;
+ smooth_faces = true;
+}
+
+///////////////
+
+CSGBrush *CSGPolygon::_build_brush() {
+
+ // set our bounding box
+
+ if (polygon.size() < 3)
+ return NULL;
+
+ Vector<Point2> final_polygon = polygon;
+
+ if (Triangulate::get_area(final_polygon) > 0) {
+ final_polygon.invert();
+ }
+
+ Vector<int> triangles = Geometry::triangulate_polygon(final_polygon);
+
+ if (triangles.size() < 3)
+ return NULL;
+
+ Path *path = NULL;
+ Ref<Curve3D> curve;
+
+ if (mode == MODE_PATH) {
+ if (!has_node(path_node))
+ return NULL;
+ Node *n = get_node(path_node);
+ if (!n)
+ return NULL;
+ path = Object::cast_to<Path>(n);
+ if (!path)
+ return NULL;
+
+ if (path != path_cache) {
+ if (path_cache) {
+ path_cache->disconnect("tree_exited", this, "_path_exited");
+ path_cache->disconnect("curve_changed", this, "_path_changed");
+ path_cache = NULL;
+ }
+
+ path_cache = path;
+
+ if (path_cache) {
+ path_cache->connect("tree_exited", this, "_path_exited");
+ path_cache->connect("curve_changed", this, "_path_changed");
+ path_cache = NULL;
+ }
+ }
+ curve = path->get_curve();
+ if (curve.is_null())
+ return NULL;
+ if (curve->get_baked_length() <= 0)
+ return NULL;
+ }
+ CSGBrush *brush = memnew(CSGBrush);
+
+ int face_count;
+
+ switch (mode) {
+ case MODE_DEPTH: face_count = triangles.size() * 2 / 3 + (final_polygon.size()) * 2; break;
+ case MODE_SPIN: face_count = (spin_degrees < 360 ? triangles.size() * 2 / 3 : 0) + (final_polygon.size()) * 2 * spin_sides; break;
+ case MODE_PATH: {
+ float bl = curve->get_baked_length();
+ int splits = MAX(2, Math::ceil(bl / path_interval));
+ face_count = triangles.size() * 2 / 3 + splits * final_polygon.size() * 2;
+ } break;
+ }
+
+ bool invert_val = is_inverting_faces();
+ Ref<Material> material = get_material();
+
+ PoolVector<Vector3> faces;
+ PoolVector<Vector2> uvs;
+ PoolVector<bool> smooth;
+ PoolVector<Ref<Material> > materials;
+ PoolVector<bool> invert;
+
+ faces.resize(face_count * 3);
+ uvs.resize(face_count * 3);
+
+ smooth.resize(face_count);
+ materials.resize(face_count);
+ invert.resize(face_count);
+
+ AABB aabb; //must be computed
+ {
+
+ PoolVector<Vector3>::Write facesw = faces.write();
+ PoolVector<Vector2>::Write uvsw = uvs.write();
+ PoolVector<bool>::Write smoothw = smooth.write();
+ PoolVector<Ref<Material> >::Write materialsw = materials.write();
+ PoolVector<bool>::Write invertw = invert.write();
+
+ int face = 0;
+
+ 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;
+ }
+
+ 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;
+
+ face++;
+
+ // face 2
+ facesw[face * 3 + 0] = v[2];
+ facesw[face * 3 + 1] = v[3];
+ facesw[face * 3 + 2] = v[0];
+
+ uvsw[face * 3 + 0] = u[2];
+ uvsw[face * 3 + 1] = u[3];
+ uvsw[face * 3 + 2] = u[0];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ }
+
+ } 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;
+ }
+
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_val;
+ 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;
+ }
+
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_val;
+ face++;
+ }
+ }
+ }
+ } break;
+ case MODE_PATH: {
+
+ float bl = curve->get_baked_length();
+ int splits = MAX(2, Math::ceil(bl / path_interval));
+
+ Transform 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;
+
+ 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();
+
+ } else {
+ local_dir = lookat_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
+
+ xf = path_to_this * xf;
+
+ if (i > 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();
+
+ 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)),
+ };
+
+ 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;
+
+ face++;
+
+ // face 2
+ facesw[face * 3 + 0] = v[2];
+ facesw[face * 3 + 1] = v[3];
+ facesw[face * 3 + 2] = v[0];
+
+ uvsw[face * 3 + 0] = u[2];
+ uvsw[face * 3 + 1] = u[3];
+ uvsw[face * 3 + 2] = u[0];
+
+ smoothw[face] = smooth_faces;
+ invertw[face] = invert_val;
+ materialsw[face] = material;
+
+ face++;
+ }
+ }
+
+ if (i == 0) {
+
+ 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);
+ }
+
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_val;
+ face++;
+ }
+ }
+
+ if (i == splits) {
+
+ 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);
+ }
+
+ smoothw[face] = false;
+ materialsw[face] = material;
+ invertw[face] = invert_val;
+ face++;
+ }
+ }
+
+ prev_xf = xf;
+ }
+
+ } break;
+ }
+
+ 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]);
+ }
+ }
+ }
+
+ brush->build_from_faces(faces, uvs, smooth, materials, invert);
+
+ return brush;
+}
+
+void CSGPolygon::_notification(int p_what) {
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ if (path_cache) {
+ path_cache->disconnect("tree_exited", this, "_path_exited");
+ path_cache->disconnect("curve_changed", this, "_path_changed");
+ path_cache = NULL;
+ }
+ }
+}
+
+void CSGPolygon::_validate_property(PropertyInfo &property) const {
+ if (property.name.begins_with("spin") && mode != MODE_SPIN) {
+ property.usage = 0;
+ }
+ if (property.name.begins_with("path") && mode != MODE_PATH) {
+ property.usage = 0;
+ }
+ if (property.name == "depth" && mode != MODE_DEPTH) {
+ property.usage = 0;
+ }
+
+ CSGShape::_validate_property(property);
+}
+
+void CSGPolygon::_path_changed() {
+ _make_dirty();
+ update_gizmo();
+}
+
+void CSGPolygon::_path_exited() {
+ path_cache = NULL;
+}
+
+void CSGPolygon::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &CSGPolygon::set_polygon);
+ ClassDB::bind_method(D_METHOD("get_polygon"), &CSGPolygon::get_polygon);
+
+ ClassDB::bind_method(D_METHOD("set_mode", "mode"), &CSGPolygon::set_mode);
+ ClassDB::bind_method(D_METHOD("get_mode"), &CSGPolygon::get_mode);
+
+ ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CSGPolygon::set_depth);
+ ClassDB::bind_method(D_METHOD("get_depth"), &CSGPolygon::get_depth);
+
+ ClassDB::bind_method(D_METHOD("set_spin_degrees", "degrees"), &CSGPolygon::set_spin_degrees);
+ ClassDB::bind_method(D_METHOD("get_spin_degrees"), &CSGPolygon::get_spin_degrees);
+
+ ClassDB::bind_method(D_METHOD("set_spin_sides", "spin_sides"), &CSGPolygon::set_spin_sides);
+ ClassDB::bind_method(D_METHOD("get_spin_sides"), &CSGPolygon::get_spin_sides);
+
+ ClassDB::bind_method(D_METHOD("set_path_node", "path"), &CSGPolygon::set_path_node);
+ ClassDB::bind_method(D_METHOD("get_path_node"), &CSGPolygon::get_path_node);
+
+ ClassDB::bind_method(D_METHOD("set_path_interval", "distance"), &CSGPolygon::set_path_interval);
+ ClassDB::bind_method(D_METHOD("get_path_interval"), &CSGPolygon::get_path_interval);
+
+ ClassDB::bind_method(D_METHOD("set_path_rotation", "mode"), &CSGPolygon::set_path_rotation);
+ ClassDB::bind_method(D_METHOD("get_path_rotation"), &CSGPolygon::get_path_rotation);
+
+ ClassDB::bind_method(D_METHOD("set_material", "material"), &CSGPolygon::set_material);
+ ClassDB::bind_method(D_METHOD("get_material"), &CSGPolygon::get_material);
+
+ ClassDB::bind_method(D_METHOD("set_smooth_faces", "smooth_faces"), &CSGPolygon::set_smooth_faces);
+ ClassDB::bind_method(D_METHOD("get_smooth_faces"), &CSGPolygon::get_smooth_faces);
+
+ ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CSGPolygon::_is_editable_3d_polygon);
+ ClassDB::bind_method(D_METHOD("_has_editable_3d_polygon_no_depth"), &CSGPolygon::_has_editable_3d_polygon_no_depth);
+
+ ClassDB::bind_method(D_METHOD("_path_exited"), &CSGPolygon::_path_exited);
+ ClassDB::bind_method(D_METHOD("_path_changed"), &CSGPolygon::_path_changed);
+
+ ADD_PROPERTY(PropertyInfo(Variant::POOL_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::REAL, "depth", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_depth", "get_depth");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "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"), "set_path_node", "get_path_node");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_interval", PROPERTY_HINT_RANGE, "0.001,1000.0,0.001"), "set_path_interval", "get_path_interval");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "path_rotation", PROPERTY_HINT_ENUM, "Polygon,Path,PathFollow"), "set_path_rotation", "get_path_rotation");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "smooth_faces"), "set_smooth_faces", "get_smooth_faces");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+
+ BIND_ENUM_CONSTANT(MODE_DEPTH);
+ BIND_ENUM_CONSTANT(MODE_SPIN);
+ BIND_ENUM_CONSTANT(MODE_PATH);
+
+ BIND_ENUM_CONSTANT(PATH_ROTATION_POLYGON);
+ BIND_ENUM_CONSTANT(PATH_ROTATION_PATH);
+ BIND_ENUM_CONSTANT(PATH_ROTATION_PATH_FOLLOW);
+}
+
+void CSGPolygon::set_polygon(const Vector<Vector2> &p_polygon) {
+ polygon = p_polygon;
+ _make_dirty();
+ update_gizmo();
+}
+
+Vector<Vector2> CSGPolygon::get_polygon() const {
+ return polygon;
+}
+
+void CSGPolygon::set_mode(Mode p_mode) {
+ mode = p_mode;
+ _make_dirty();
+ update_gizmo();
+ _change_notify();
+}
+
+CSGPolygon::Mode CSGPolygon::get_mode() const {
+ return mode;
+}
+
+void CSGPolygon::set_depth(const float p_depth) {
+ ERR_FAIL_COND(p_depth < 0.001);
+ depth = p_depth;
+ _make_dirty();
+ update_gizmo();
+}
+
+float CSGPolygon::get_depth() const {
+ return depth;
+}
+
+void CSGPolygon::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();
+}
+
+float CSGPolygon::get_spin_degrees() const {
+ return spin_degrees;
+}
+
+void CSGPolygon::set_spin_sides(const int p_spin_sides) {
+ ERR_FAIL_COND(p_spin_sides < 3);
+ spin_sides = p_spin_sides;
+ _make_dirty();
+ update_gizmo();
+}
+
+int CSGPolygon::get_spin_sides() const {
+ return spin_sides;
+}
+
+void CSGPolygon::set_path_node(const NodePath &p_path) {
+ path_node = p_path;
+ _make_dirty();
+ update_gizmo();
+}
+
+NodePath CSGPolygon::get_path_node() const {
+ return path_node;
+}
+
+void CSGPolygon::set_path_interval(float p_interval) {
+ ERR_FAIL_COND(p_interval < 0.001);
+ path_interval = p_interval;
+ _make_dirty();
+ update_gizmo();
+}
+float CSGPolygon::get_path_interval() const {
+ return path_interval;
+}
+
+void CSGPolygon::set_path_rotation(PathRotation p_rotation) {
+ path_rotation = p_rotation;
+ _make_dirty();
+ update_gizmo();
+}
+
+CSGPolygon::PathRotation CSGPolygon::get_path_rotation() const {
+ return path_rotation;
+}
+
+void CSGPolygon::set_smooth_faces(const bool p_smooth_faces) {
+ smooth_faces = p_smooth_faces;
+ _make_dirty();
+}
+
+bool CSGPolygon::get_smooth_faces() const {
+ return smooth_faces;
+}
+
+void CSGPolygon::set_material(const Ref<Material> &p_material) {
+
+ material = p_material;
+ _make_dirty();
+}
+
+Ref<Material> CSGPolygon::get_material() const {
+
+ return material;
+}
+
+bool CSGPolygon::_is_editable_3d_polygon() const {
+ return true;
+}
+
+bool CSGPolygon::_has_editable_3d_polygon_no_depth() const {
+ return true;
+}
+
+CSGPolygon::CSGPolygon() {
+ // defaults
+ mode = MODE_DEPTH;
+ polygon.push_back(Vector2(0, 0));
+ polygon.push_back(Vector2(0, 1));
+ polygon.push_back(Vector2(1, 1));
+ polygon.push_back(Vector2(1, 0));
+ depth = 1.0;
+ spin_degrees = 360;
+ spin_sides = 8;
+ smooth_faces = false;
+ path_interval = 1;
+ path_rotation = PATH_ROTATION_PATH;
+ path_cache = NULL;
+}
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
new file mode 100644
index 000000000..c1d2cce60
--- /dev/null
+++ b/modules/csg/csg_shape.h
@@ -0,0 +1,360 @@
+#ifndef CSG_SHAPE_H
+#define CSG_SHAPE_H
+
+#define CSGJS_HEADER_ONLY
+
+#include "csg.h"
+#include "scene/3d/visual_instance.h"
+#include "scene/resources/concave_polygon_shape.h"
+
+class CSGShape : public VisualInstance {
+ GDCLASS(CSGShape, VisualInstance);
+
+public:
+ enum Operation {
+ OPERATION_UNION,
+ OPERATION_INTERSECTION,
+ OPERATION_SUBTRACTION,
+
+ };
+
+private:
+ Operation operation;
+ CSGShape *parent;
+
+ CSGBrush *brush;
+
+ AABB node_aabb;
+
+ bool dirty;
+ float snap;
+
+ bool use_collision;
+ Ref<ConcavePolygonShape> root_collision_shape;
+ RID root_collision_instance;
+
+ Ref<ArrayMesh> root_mesh;
+
+ struct Vector3Hasher {
+ _ALWAYS_INLINE_ uint32_t hash(const Vector3 &p_vec3) const {
+ uint32_t h = hash_djb2_one_float(p_vec3.x);
+ h = hash_djb2_one_float(p_vec3.y, h);
+ h = hash_djb2_one_float(p_vec3.z, h);
+ return h;
+ }
+ };
+
+ struct ShapeUpdateSurface {
+ PoolVector<Vector3> vertices;
+ PoolVector<Vector3> normals;
+ PoolVector<Vector2> uvs;
+ Ref<Material> material;
+ int last_added;
+
+ PoolVector<Vector3>::Write verticesw;
+ PoolVector<Vector3>::Write normalsw;
+ PoolVector<Vector2>::Write uvsw;
+ };
+
+ void _update_shape();
+
+protected:
+ void _notification(int p_what);
+ virtual CSGBrush *_build_brush() = 0;
+ void _make_dirty();
+
+ static void _bind_methods();
+
+ friend class CSGCombiner;
+ CSGBrush *_get_brush();
+
+ virtual void _validate_property(PropertyInfo &property) const;
+
+public:
+ void set_operation(Operation p_operation);
+ Operation get_operation() const;
+
+ virtual PoolVector<Vector3> get_brush_faces();
+
+ virtual AABB get_aabb() const;
+ virtual PoolVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ void set_use_collision(bool p_enable);
+ bool is_using_collision() const;
+
+ void set_snap(float p_snap);
+ float get_snap() const;
+
+ bool is_root_shape() const;
+ CSGShape();
+ ~CSGShape();
+};
+
+VARIANT_ENUM_CAST(CSGShape::Operation)
+
+class CSGCombiner : public CSGShape {
+ GDCLASS(CSGCombiner, CSGShape)
+private:
+ virtual CSGBrush *_build_brush();
+
+public:
+ CSGCombiner();
+};
+
+class CSGPrimitive : public CSGShape {
+ GDCLASS(CSGPrimitive, CSGShape)
+
+private:
+ bool invert_faces;
+
+protected:
+ CSGBrush *_create_brush_from_arrays(const PoolVector<Vector3> &p_vertices, const PoolVector<Vector2> &p_uv, const PoolVector<bool> &p_smooth, const PoolVector<Ref<Material> > &p_materials);
+ static void _bind_methods();
+
+public:
+ void set_invert_faces(bool p_invert);
+ bool is_inverting_faces();
+
+ CSGPrimitive();
+};
+
+class CSGMesh : public CSGPrimitive {
+ GDCLASS(CSGMesh, CSGPrimitive)
+
+ virtual CSGBrush *_build_brush();
+
+ Ref<Mesh> mesh;
+
+ void _mesh_changed();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_mesh(const Ref<Mesh> &p_mesh);
+ Ref<Mesh> get_mesh();
+};
+
+class CSGSphere : public CSGPrimitive {
+
+ GDCLASS(CSGSphere, CSGPrimitive)
+ virtual CSGBrush *_build_brush();
+
+ Ref<Material> material;
+ bool smooth_faces;
+ float radius;
+ int radial_segments;
+ int rings;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_radius(const float p_radius);
+ float get_radius() const;
+
+ void set_radial_segments(const int p_radial_segments);
+ int get_radial_segments() const;
+
+ void set_rings(const int p_rings);
+ int get_rings() const;
+
+ void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
+
+ void set_smooth_faces(bool p_smooth_faces);
+ bool get_smooth_faces() const;
+
+ CSGSphere();
+};
+
+class CSGBox : public CSGPrimitive {
+
+ GDCLASS(CSGBox, CSGPrimitive)
+ virtual CSGBrush *_build_brush();
+
+ Ref<Material> material;
+ float width;
+ float height;
+ float depth;
+
+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_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
+
+ CSGBox();
+};
+
+class CSGCylinder : public CSGPrimitive {
+
+ GDCLASS(CSGCylinder, CSGPrimitive)
+ virtual CSGBrush *_build_brush();
+
+ Ref<Material> material;
+ float radius;
+ float height;
+ int sides;
+ bool cone;
+ bool smooth_faces;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_radius(const float p_radius);
+ float get_radius() const;
+
+ void set_height(const float p_height);
+ float get_height() const;
+
+ void set_sides(const int p_sides);
+ int get_sides() const;
+
+ void set_cone(const bool p_cone);
+ bool is_cone() const;
+
+ void set_smooth_faces(bool p_smooth_faces);
+ bool get_smooth_faces() const;
+
+ void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
+
+ CSGCylinder();
+};
+
+class CSGTorus : public CSGPrimitive {
+
+ GDCLASS(CSGTorus, CSGPrimitive)
+ virtual CSGBrush *_build_brush();
+
+ Ref<Material> material;
+ float inner_radius;
+ float outer_radius;
+ int sides;
+ int ring_sides;
+ bool smooth_faces;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void set_inner_radius(const float p_inner_radius);
+ float get_inner_radius() const;
+
+ void set_outer_radius(const float p_outer_radius);
+ float get_outer_radius() const;
+
+ void set_sides(const int p_sides);
+ int get_sides() const;
+
+ void set_ring_sides(const int p_ring_sides);
+ int get_ring_sides() const;
+
+ void set_smooth_faces(bool p_smooth_faces);
+ bool get_smooth_faces() const;
+
+ void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
+
+ CSGTorus();
+};
+
+class CSGPolygon : public CSGPrimitive {
+
+ GDCLASS(CSGPolygon, CSGPrimitive)
+
+public:
+ enum Mode {
+ MODE_DEPTH,
+ MODE_SPIN,
+ MODE_PATH
+ };
+
+ enum PathRotation {
+ PATH_ROTATION_POLYGON,
+ PATH_ROTATION_PATH,
+ PATH_ROTATION_PATH_FOLLOW,
+ };
+
+private:
+ virtual CSGBrush *_build_brush();
+
+ Vector<Vector2> polygon;
+ Ref<Material> material;
+
+ Mode mode;
+
+ float depth;
+
+ float spin_degrees;
+ int spin_sides;
+
+ NodePath path_node;
+ float path_interval;
+ PathRotation path_rotation;
+
+ Node *path_cache;
+
+ bool smooth_faces;
+
+ bool _is_editable_3d_polygon() const;
+ bool _has_editable_3d_polygon_no_depth() const;
+
+ void _path_changed();
+ void _path_exited();
+
+protected:
+ static void _bind_methods();
+ virtual void _validate_property(PropertyInfo &property) const;
+ void _notification(int p_what);
+
+public:
+ void set_polygon(const Vector<Vector2> &p_polygon);
+ Vector<Vector2> get_polygon() const;
+
+ void set_mode(Mode p_mode);
+ Mode get_mode() const;
+
+ void set_depth(float p_depth);
+ float get_depth() const;
+
+ void set_spin_degrees(float p_spin_degrees);
+ float get_spin_degrees() const;
+
+ void set_spin_sides(int p_sides);
+ int get_spin_sides() const;
+
+ void set_path_node(const NodePath &p_path);
+ NodePath get_path_node() const;
+
+ void set_path_interval(float p_interval);
+ float get_path_interval() const;
+
+ void set_path_rotation(PathRotation p_rotation);
+ PathRotation get_path_rotation() const;
+
+ void set_smooth_faces(bool p_smooth_faces);
+ bool get_smooth_faces() const;
+
+ void set_material(const Ref<Material> &p_material);
+ Ref<Material> get_material() const;
+
+ CSGPolygon();
+};
+
+VARIANT_ENUM_CAST(CSGPolygon::Mode)
+VARIANT_ENUM_CAST(CSGPolygon::PathRotation)
+
+#endif // CSG_SHAPE_H
diff --git a/modules/csg/register_types.cpp b/modules/csg/register_types.cpp
new file mode 100644
index 000000000..020724ee5
--- /dev/null
+++ b/modules/csg/register_types.cpp
@@ -0,0 +1,59 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (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 "csg_shape.h"
+#include "csg_gizmos.h"
+
+void register_csg_types() {
+
+#ifndef _3D_DISABLED
+
+ ClassDB::register_virtual_class<CSGShape>();
+ ClassDB::register_virtual_class<CSGPrimitive>();
+ ClassDB::register_class<CSGMesh>();
+ ClassDB::register_class<CSGSphere>();
+ ClassDB::register_class<CSGBox>();
+ ClassDB::register_class<CSGCylinder>();
+ ClassDB::register_class<CSGTorus>();
+ ClassDB::register_class<CSGPolygon>();
+ ClassDB::register_class<CSGCombiner>();
+
+#ifdef TOOLS_ENABLED
+ EditorPlugins::add_by_type<EditorPluginCSG>();
+#endif
+#endif
+
+}
+
+void unregister_csg_types() {
+
+}
diff --git a/modules/csg/register_types.h b/modules/csg/register_types.h
new file mode 100644
index 000000000..49490d31d
--- /dev/null
+++ b/modules/csg/register_types.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (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. */
+/*************************************************************************/
+
+void register_csg_types();
+void unregister_csg_types();
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp
index f3f4acd76..bd76c766a 100644
--- a/modules/enet/networked_multiplayer_enet.cpp
+++ b/modules/enet/networked_multiplayer_enet.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "networked_multiplayer_enet.h"
+#include "io/ip.h"
#include "io/marshalls.h"
#include "os/os.h"
@@ -57,6 +58,10 @@ int NetworkedMultiplayerENet::get_packet_peer() const {
Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int p_in_bandwidth, int p_out_bandwidth) {
ERR_FAIL_COND_V(active, ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_max_clients < 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_in_bandwidth < 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_out_bandwidth < 0, ERR_INVALID_PARAMETER);
ENetAddress address;
@@ -79,8 +84,8 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
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 */,
SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
- p_in_bandwidth /* assume any amount of incoming bandwidth */,
- p_out_bandwidth /* assume any amount of outgoing bandwidth */);
+ p_in_bandwidth /* limit incoming bandwith if > 0 */,
+ p_out_bandwidth /* limit outgoing bandwith if > 0 */);
ERR_FAIL_COND_V(!host, ERR_CANT_CREATE);
@@ -92,35 +97,76 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
connection_status = CONNECTION_CONNECTED;
return OK;
}
-Error NetworkedMultiplayerENet::create_client(const IP_Address &p_ip, int p_port, int p_in_bandwidth, int p_out_bandwidth) {
+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(active, ERR_ALREADY_IN_USE);
+ ERR_FAIL_COND_V(p_port < 0 || p_port > 65535, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_client_port < 0 || p_client_port > 65535, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_in_bandwidth < 0, ERR_INVALID_PARAMETER);
+ ERR_FAIL_COND_V(p_out_bandwidth < 0, ERR_INVALID_PARAMETER);
- host = enet_host_create(NULL /* create a client host */,
- 1 /* only allow 1 outgoing connection */,
- SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
- p_in_bandwidth /* 56K modem with 56 Kbps downstream bandwidth */,
- p_out_bandwidth /* 56K modem with 14 Kbps upstream bandwidth */);
+ 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(!bind_ip.is_ipv4(), ERR_INVALID_PARAMETER);
+ 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 */,
+ SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
+ p_in_bandwidth /* limit incoming bandwith if > 0 */,
+ p_out_bandwidth /* limit outgoing bandwith if > 0 */);
+ } else {
+ host = enet_host_create(NULL /* create a client host */,
+ 1 /* only allow 1 outgoing connection */,
+ SYSCH_MAX /* allow up to SYSCH_MAX channels to be used */,
+ p_in_bandwidth /* limit incoming bandwith if > 0 */,
+ p_out_bandwidth /* limit outgoing bandwith if > 0 */);
+ }
ERR_FAIL_COND_V(!host, ERR_CANT_CREATE);
_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(!ip.is_valid(), ERR_CANT_RESOLVE);
+ }
+
ENetAddress address;
#ifdef GODOT_ENET
- enet_address_set_ip(&address, p_ip.get_ipv6(), 16);
+ enet_address_set_ip(&address, ip.get_ipv6(), 16);
#else
- ERR_FAIL_COND_V(!p_ip.is_ipv4(), ERR_INVALID_PARAMETER);
- address.host = *(uint32_t *)p_ip.get_ipv4();
+ ERR_FAIL_COND_V(!ip.is_ipv4(), ERR_INVALID_PARAMETER);
+ address.host = *(uint32_t *)ip.get_ipv4();
#endif
address.port = p_port;
- //enet_address_set_host (& address, "localhost");
- //address.port = p_port;
-
unique_id = _gen_unique_id();
- /* Initiate the connection, allocating the enough channels */
+ // Initiate connection, allocating enough channels
ENetPeer *peer = enet_host_connect(host, &address, SYSCH_MAX, unique_id);
if (peer == NULL) {
@@ -128,7 +174,7 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address &p_ip, int p_port
ERR_FAIL_COND_V(!peer, ERR_CANT_CREATE);
}
- //technically safe to ignore the peer or anything else.
+ // Technically safe to ignore the peer or anything else.
connection_status = CONNECTION_CONNECTING;
active = true;
@@ -148,13 +194,13 @@ void NetworkedMultiplayerENet::poll() {
/* Wait up to 1000 milliseconds for an event. */
while (true) {
- if (!host || !active) //might have been disconnected while emitting a notification
+ if (!host || !active) // Might have been disconnected while emitting a notification
return;
int ret = enet_host_service(host, &event, 1);
if (ret < 0) {
- //error, do something?
+ // Error, do something?
break;
} else if (ret == 0) {
break;
@@ -162,7 +208,7 @@ void NetworkedMultiplayerENet::poll() {
switch (event.type) {
case ENET_EVENT_TYPE_CONNECT: {
- /* Store any relevant client information here. */
+ // Store any relevant client information here.
if (server && refuse_connections) {
enet_peer_reset(event.peer);
@@ -172,7 +218,7 @@ void NetworkedMultiplayerENet::poll() {
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
+ if (*new_id == 0) { // Data zero is sent by server (enet won't let you configure this). Server is always 1.
*new_id = 1;
}
@@ -180,22 +226,22 @@ void NetworkedMultiplayerENet::poll() {
peer_map[*new_id] = event.peer;
- connection_status = CONNECTION_CONNECTED; //if connecting, this means it connected t something!
+ connection_status = CONNECTION_CONNECTED; // If connecting, this means it connected to something!
emit_signal("peer_connected", *new_id);
if (server) {
- //someone connected, let it know of all the peers available
+ // 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
+ // Send existing peers to new peer
ENetPacket *packet = enet_packet_create(NULL, 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
+ // Send the new peer to existing peers
packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_ADD_PEER, &packet->data[0]);
encode_uint32(*new_id, &packet->data[4]);
@@ -209,7 +255,7 @@ void NetworkedMultiplayerENet::poll() {
} break;
case ENET_EVENT_TYPE_DISCONNECT: {
- /* Reset the peer's client information. */
+ // Reset the peer's client information.
int *id = (int *)event.peer->data;
@@ -220,12 +266,12 @@ void NetworkedMultiplayerENet::poll() {
} else {
if (server) {
- //someone disconnected, let it know to everyone else
+ // Someone disconnected, notify everyone else
for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
if (E->key() == *id)
continue;
- //send the new peer to existing peers
+
ENetPacket *packet = enet_packet_create(NULL, 8, ENET_PACKET_FLAG_RELIABLE);
encode_uint32(SYSMSG_REMOVE_PEER, &packet->data[0]);
encode_uint32(*id, &packet->data[4]);
@@ -246,7 +292,7 @@ void NetworkedMultiplayerENet::poll() {
case ENET_EVENT_TYPE_RECEIVE: {
if (event.channelID == SYSCH_CONFIG) {
- //some config message
+ // Some config message
ERR_CONTINUE(event.packet->dataLength < 8);
// Only server can send config messages
@@ -292,13 +338,13 @@ void NetworkedMultiplayerENet::poll() {
packet.from = *id;
if (target == 0) {
- //re-send the everyone but sender :|
+ // Re-send to everyone but sender :|
incoming_packets.push_back(packet);
- //and make copies for sending
+ // 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
+ if (uint32_t(E->key()) == source) // Do not resend to self
continue;
ENetPacket *packet2 = enet_packet_create(packet.packet->data, packet.packet->dataLength, flags);
@@ -307,12 +353,12 @@ void NetworkedMultiplayerENet::poll() {
}
} else if (target < 0) {
- //to all but one
+ // To all but one
- //and make copies for sending
+ // 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
+ 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, flags);
@@ -321,18 +367,18 @@ void NetworkedMultiplayerENet::poll() {
}
if (-target != 1) {
- //server is not excluded
+ // Server is not excluded
incoming_packets.push_back(packet);
} else {
- //server is excluded, erase packet
+ // Server is excluded, erase packet
enet_packet_destroy(packet.packet);
}
} else if (target == 1) {
- //to myself and only myself
+ // To myself and only myself
incoming_packets.push_back(packet);
} else {
- //to someone else, specifically
+ // To someone else, specifically
ERR_CONTINUE(!peer_map.has(target));
enet_peer_send(peer_map[target], event.channelID, packet.packet);
}
@@ -341,14 +387,14 @@ void NetworkedMultiplayerENet::poll() {
incoming_packets.push_back(packet);
}
- //destroy packet later..
+ // Destroy packet later
} else {
ERR_CONTINUE(true);
}
} break;
case ENET_EVENT_TYPE_NONE: {
- //do nothing
+ // Do nothing
} break;
}
}
@@ -360,10 +406,10 @@ bool NetworkedMultiplayerENet::is_server() const {
return server;
}
-void NetworkedMultiplayerENet::close_connection() {
+void NetworkedMultiplayerENet::close_connection(uint32_t wait_usec) {
- if (!active)
- return;
+ ERR_FAIL_COND(!active);
+ ERR_FAIL_COND(wait_usec < 0);
_pop_current_packet();
@@ -377,20 +423,54 @@ void NetworkedMultiplayerENet::close_connection() {
if (peers_disconnected) {
enet_host_flush(host);
- OS::get_singleton()->delay_usec(100); //wait 100ms for disconnection packets to send
+
+ 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();
- unique_id = 1; //server is 1
+ unique_id = 1; // Server is 1
connection_status = CONNECTION_DISCONNECTED;
}
+void NetworkedMultiplayerENet::disconnect_peer(int p_peer, bool now) {
+
+ ERR_FAIL_COND(!active);
+ ERR_FAIL_COND(!is_server());
+ ERR_FAIL_COND(!peer_map.has(p_peer))
+
+ if (now) {
+ 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()
+
+ for (Map<int, ENetPeer *>::Element *E = peer_map.front(); E; E = E->next()) {
+
+ if (E->key() == p_peer)
+ continue;
+
+ ENetPacket *packet = enet_packet_create(NULL, 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);
+ }
+
+ 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(incoming_packets.size() == 0, ERR_UNAVAILABLE);
@@ -405,6 +485,7 @@ Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer, int &r_buff
return OK;
}
+
Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
ERR_FAIL_COND_V(!active, ERR_UNCONFIGURED);
@@ -440,9 +521,9 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
}
ENetPacket *packet = enet_packet_create(NULL, p_buffer_size + 12, packet_flags);
- encode_uint32(unique_id, &packet->data[0]); //source ID
- encode_uint32(target_peer, &packet->data[4]); //dest ID
- encode_uint32(packet_flags, &packet->data[8]); //dest ID
+ encode_uint32(unique_id, &packet->data[0]); // Source ID
+ encode_uint32(target_peer, &packet->data[4]); // Dest ID
+ encode_uint32(packet_flags, &packet->data[8]); // Dest ID
copymem(&packet->data[12], p_buffer, p_buffer_size);
if (server) {
@@ -450,14 +531,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
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
+ // 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
+ if (F->key() == exclude) // Exclude packet
continue;
ENetPacket *packet2 = enet_packet_create(packet->data, packet->dataLength, packet_flags);
@@ -465,14 +546,14 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
enet_peer_send(F->get(), channel, packet2);
}
- enet_packet_destroy(packet); //original packet no longer needed
+ 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_peer_send(peer_map[1], channel, packet); // Send to server for broadcast
}
enet_host_flush(host);
@@ -482,7 +563,7 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer, int p_buffer
int NetworkedMultiplayerENet::get_max_packet_size() const {
- return 1 << 24; //anything is good
+ return 1 << 24; // Anything is good
}
void NetworkedMultiplayerENet::_pop_current_packet() {
@@ -511,16 +592,12 @@ uint32_t NetworkedMultiplayerENet::_gen_unique_id() const {
(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)OS::get_singleton()->get_unique_id().hash64(), hash );
- */
hash = hash_djb2_one_32(
- (uint32_t)((uint64_t)this), hash); //rely on aslr heap
+ (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
+ (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
+ hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
}
return hash;
@@ -596,7 +673,7 @@ size_t NetworkedMultiplayerENet::enet_compress(void *context, const ENetBuffer *
return 0;
if (ret > int(outLimit))
- return 0; //do not bother
+ return 0; // Do not bother
copymem(outData, enet->dst_compressor_mem.ptr(), ret);
@@ -651,17 +728,48 @@ void NetworkedMultiplayerENet::_setup_compressor() {
void NetworkedMultiplayerENet::enet_compressor_destroy(void *context) {
- //do none
+ // Nothing to do
+}
+
+IP_Address NetworkedMultiplayerENet::get_peer_address(int p_peer_id) const {
+
+ ERR_FAIL_COND_V(!peer_map.has(p_peer_id), IP_Address());
+ ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, IP_Address());
+ ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, IP_Address());
+
+ 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(!peer_map.has(p_peer_id), 0);
+ ERR_FAIL_COND_V(!is_server() && p_peer_id != 1, 0);
+ ERR_FAIL_COND_V(peer_map[p_peer_id] == NULL, 0);
+#ifdef GODOT_ENET
+ return peer_map[p_peer_id]->address.port;
+#else
+ return peer_map[p_peer_id]->address.port;
+#endif
}
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", "ip", "port", "in_bandwidth", "out_bandwidth"), &NetworkedMultiplayerENet::create_client, DEFVAL(0), DEFVAL(0));
- ClassDB::bind_method(D_METHOD("close_connection"), &NetworkedMultiplayerENet::close_connection);
+ 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("get_peer_address"), &NetworkedMultiplayerENet::get_peer_address);
+ ClassDB::bind_method(D_METHOD("get_peer_port"), &NetworkedMultiplayerENet::get_peer_port);
ADD_PROPERTY(PropertyInfo(Variant::INT, "compression_mode", PROPERTY_HINT_ENUM, "None,Range Coder,FastLZ,ZLib,ZStd"), "set_compression_mode", "get_compression_mode");
@@ -696,7 +804,7 @@ NetworkedMultiplayerENet::~NetworkedMultiplayerENet() {
close_connection();
}
-// sets IP for ENet to bind when using create_server
+// 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(!p_ip.is_valid() && !p_ip.is_wildcard());
diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h
index 440e9b540..d481f5d49 100644
--- a/modules/enet/networked_multiplayer_enet.h
+++ b/modules/enet/networked_multiplayer_enet.h
@@ -115,10 +115,15 @@ public:
virtual int get_packet_peer() const;
+ 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 IP_Address &p_ip, int p_port, 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 close_connection();
+ void disconnect_peer(int p_peer, bool now = false);
virtual void poll();
diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub
index acfb83bc1..8654ef3d8 100644
--- a/modules/gdnative/SCsub
+++ b/modules/gdnative/SCsub
@@ -275,8 +275,8 @@ if ARGUMENTS.get('gdnative_wrapper', False):
if gd_wrapper_env['use_lto']:
if not env.msvc:
- gd_wrapper_env.Append(CCFLAGS=['--no-lto'])
- gd_wrapper_env.Append(LINKFLAGS=['--no-lto'])
+ gd_wrapper_env.Append(CCFLAGS=['-fno-lto'])
+ gd_wrapper_env.Append(LINKFLAGS=['-fno-lto'])
else:
gd_wrapper_env.Append(CCFLAGS=['/GL-'])
gd_wrapper_env.Append(LINKFLAGS=['/LTCG:OFF'])
diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml
index 308d8defc..754a6d251 100644
--- a/modules/gdnative/doc_classes/GDNativeLibrary.xml
+++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml
@@ -9,12 +9,6 @@
<demos>
</demos>
<methods>
- <method name="get_config_file">
- <return type="ConfigFile">
- </return>
- <description>
- </description>
- </method>
<method name="get_current_dependencies" qualifiers="const">
<return type="PoolStringArray">
</return>
@@ -29,6 +23,8 @@
</method>
</methods>
<members>
+ <member name="config_file" type="ConfigFile" setter="set_config_file" getter="get_config_file">
+ </member>
<member name="load_once" type="bool" setter="set_load_once" getter="should_load_once">
</member>
<member name="reloadable" type="bool" setter="set_reloadable" getter="is_reloadable">
diff --git a/modules/gdnative/gdnative.cpp b/modules/gdnative/gdnative.cpp
index 42c3028f2..897588385 100644
--- a/modules/gdnative/gdnative.cpp
+++ b/modules/gdnative/gdnative.cpp
@@ -66,8 +66,169 @@ GDNativeLibrary::GDNativeLibrary() {
GDNativeLibrary::~GDNativeLibrary() {
}
+bool GDNativeLibrary::_set(const StringName &p_name, const Variant &p_property) {
+
+ String name = p_name;
+
+ if (name.begins_with("entry/")) {
+ String key = name.substr(6, name.length() - 6);
+
+ config_file->set_value("entry", key, p_property);
+
+ set_config_file(config_file);
+
+ return true;
+ }
+
+ if (name.begins_with("dependency/")) {
+ String key = name.substr(11, name.length() - 11);
+
+ config_file->set_value("dependencies", key, p_property);
+
+ set_config_file(config_file);
+
+ return true;
+ }
+
+ return false;
+}
+
+bool GDNativeLibrary::_get(const StringName &p_name, Variant &r_property) const {
+ String name = p_name;
+
+ if (name.begins_with("entry/")) {
+ String key = name.substr(6, name.length() - 6);
+
+ r_property = config_file->get_value("entry", key);
+
+ return true;
+ }
+
+ if (name.begins_with("dependency/")) {
+ String key = name.substr(11, name.length() - 11);
+
+ r_property = config_file->get_value("dependencies", key);
+
+ return true;
+ }
+
+ return false;
+}
+
+void GDNativeLibrary::_get_property_list(List<PropertyInfo> *p_list) const {
+ // set entries
+ List<String> entry_key_list;
+
+ if (config_file->has_section("entry"))
+ 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();
+
+ PropertyInfo prop;
+
+ prop.type = Variant::STRING;
+ prop.name = "entry/" + key;
+
+ p_list->push_back(prop);
+ }
+
+ // set dependencies
+ List<String> dependency_key_list;
+
+ if (config_file->has_section("dependencies"))
+ 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();
+
+ PropertyInfo prop;
+
+ prop.type = Variant::STRING;
+ prop.name = "dependency/" + key;
+
+ p_list->push_back(prop);
+ }
+}
+
+void GDNativeLibrary::set_config_file(Ref<ConfigFile> p_config_file) {
+
+ 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));
+ set_reloadable(p_config_file->get_value("general", "reloadable", default_reloadable));
+
+ String entry_lib_path;
+ {
+
+ List<String> entry_keys;
+
+ if (p_config_file->has_section("entry"))
+ 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();
+
+ Vector<String> tags = key.split(".");
+
+ bool skip = false;
+ for (int i = 0; i < tags.size(); i++) {
+ bool has_feature = OS::get_singleton()->has_feature(tags[i]);
+
+ if (!has_feature) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (skip) {
+ continue;
+ }
+
+ entry_lib_path = p_config_file->get_value("entry", key);
+ break;
+ }
+ }
+
+ Vector<String> dependency_paths;
+ {
+
+ List<String> dependency_keys;
+
+ if (p_config_file->has_section("dependencies"))
+ 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();
+
+ Vector<String> tags = key.split(".");
+
+ bool skip = false;
+ for (int i = 0; i < tags.size(); i++) {
+ bool has_feature = OS::get_singleton()->has_feature(tags[i]);
+
+ if (!has_feature) {
+ skip = true;
+ break;
+ }
+ }
+
+ if (skip) {
+ continue;
+ }
+
+ dependency_paths = p_config_file->get_value("dependencies", key);
+ break;
+ }
+ }
+
+ current_library_path = entry_lib_path;
+ current_dependencies = dependency_paths;
+}
+
void GDNativeLibrary::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_config_file"), &GDNativeLibrary::get_config_file);
+ ClassDB::bind_method(D_METHOD("set_config_file", "config_file"), &GDNativeLibrary::set_config_file);
ClassDB::bind_method(D_METHOD("get_current_library_path"), &GDNativeLibrary::get_current_library_path);
ClassDB::bind_method(D_METHOD("get_current_dependencies"), &GDNativeLibrary::get_current_dependencies);
@@ -82,6 +243,8 @@ 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_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "config_file", PROPERTY_HINT_RESOURCE_TYPE, "ConfigFile"), "set_config_file", "get_config_file");
+
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "load_once"), "set_load_once", "should_load_once");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "singleton"), "set_singleton", "is_singleton");
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "symbol_prefix"), "set_symbol_prefix", "get_symbol_prefix");
@@ -337,73 +500,7 @@ RES GDNativeLibraryResourceLoader::load(const String &p_path, const String &p_or
*r_error = err;
}
- lib->set_singleton(config->get_value("general", "singleton", default_singleton));
- lib->set_load_once(config->get_value("general", "load_once", default_load_once));
- lib->set_symbol_prefix(config->get_value("general", "symbol_prefix", default_symbol_prefix));
- lib->set_reloadable(config->get_value("general", "reloadable", default_reloadable));
-
- String entry_lib_path;
- {
-
- 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();
-
- Vector<String> tags = key.split(".");
-
- bool skip = false;
- for (int i = 0; i < tags.size(); i++) {
- bool has_feature = OS::get_singleton()->has_feature(tags[i]);
-
- if (!has_feature) {
- skip = true;
- break;
- }
- }
-
- if (skip) {
- continue;
- }
-
- entry_lib_path = config->get_value("entry", key);
- break;
- }
- }
-
- Vector<String> dependency_paths;
- {
-
- 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();
-
- Vector<String> tags = key.split(".");
-
- bool skip = false;
- for (int i = 0; i < tags.size(); i++) {
- bool has_feature = OS::get_singleton()->has_feature(tags[i]);
-
- if (!has_feature) {
- skip = true;
- break;
- }
- }
-
- if (skip) {
- continue;
- }
-
- dependency_paths = config->get_value("dependencies", key);
- break;
- }
- }
-
- lib->current_library_path = entry_lib_path;
- lib->current_dependencies = dependency_paths;
+ lib->set_config_file(config);
return lib;
}
diff --git a/modules/gdnative/gdnative.h b/modules/gdnative/gdnative.h
index 3298ea950..b17bb94f1 100644
--- a/modules/gdnative/gdnative.h
+++ b/modules/gdnative/gdnative.h
@@ -66,8 +66,14 @@ public:
GDNativeLibrary();
~GDNativeLibrary();
+ virtual bool _set(const StringName &p_name, const Variant &p_property);
+ virtual bool _get(const StringName &p_name, Variant &r_property) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
_FORCE_INLINE_ Ref<ConfigFile> get_config_file() { return config_file; }
+ void set_config_file(Ref<ConfigFile> p_config_file);
+
// things that change per-platform
// so there are no setters for this
_FORCE_INLINE_ String get_current_library_path() const {
diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json
index a8919f713..f41c3859b 100644
--- a/modules/gdnative/gdnative_api.json
+++ b/modules/gdnative/gdnative_api.json
@@ -5822,6 +5822,23 @@
]
},
{
+ "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": [
diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h
index 747328bc4..cfbe16fa7 100644
--- a/modules/gdnative/include/nativescript/godot_nativescript.h
+++ b/modules/gdnative/include/nativescript/godot_nativescript.h
@@ -214,16 +214,19 @@ void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle,
// type tag API
+void GDAPI godot_nativescript_set_global_type_tag(int p_idx, const char *p_name, const void *p_type_tag);
+const void GDAPI *godot_nativescript_get_global_type_tag(int p_idx, const char *p_name);
+
void GDAPI godot_nativescript_set_type_tag(void *p_gdnative_handle, const char *p_name, const void *p_type_tag);
const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object);
// instance binding API
typedef struct {
- void *(*alloc_instance_binding_data)(void *, godot_object *);
- void (*free_instance_binding_data)(void *, void *);
+ GDCALLINGCONV void *(*alloc_instance_binding_data)(void *, const void *, godot_object *);
+ GDCALLINGCONV void (*free_instance_binding_data)(void *, void *);
void *data;
- void (*free_func)(void *);
+ GDCALLINGCONV void (*free_func)(void *);
} godot_instance_binding_functions;
int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_instance_binding_functions p_binding_functions);
diff --git a/modules/gdnative/nativescript/godot_nativescript.cpp b/modules/gdnative/nativescript/godot_nativescript.cpp
index aea595d0f..ace2ecac5 100644
--- a/modules/gdnative/nativescript/godot_nativescript.cpp
+++ b/modules/gdnative/nativescript/godot_nativescript.cpp
@@ -313,6 +313,14 @@ void GDAPI godot_nativescript_set_signal_documentation(void *p_gdnative_handle,
signal->get().documentation = *(String *)&p_documentation;
}
+void GDAPI godot_nativescript_set_global_type_tag(int p_idx, const char *p_name, const void *p_type_tag) {
+ NativeScriptLanguage::get_singleton()->set_global_type_tag(p_idx, StringName(p_name), p_type_tag);
+}
+
+const void GDAPI *godot_nativescript_get_global_type_tag(int p_idx, const char *p_name) {
+ return NativeScriptLanguage::get_singleton()->get_global_type_tag(p_idx, StringName(p_name));
+}
+
void GDAPI godot_nativescript_set_type_tag(void *p_gdnative_handle, const char *p_name, const void *p_type_tag) {
String *s = (String *)p_gdnative_handle;
@@ -331,13 +339,11 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object)
const Object *o = (Object *)p_object;
if (!o->get_script_instance()) {
- ERR_EXPLAIN("Attempted to get type tag on an object without a script!");
- ERR_FAIL_V(NULL);
+ return NULL;
} else {
NativeScript *script = Object::cast_to<NativeScript>(o->get_script_instance()->get_script().ptr());
if (!script) {
- ERR_EXPLAIN("Attempted to get type tag on an object without a nativescript attached");
- ERR_FAIL_V(NULL);
+ return NULL;
}
if (script->get_script_desc())
@@ -347,10 +353,6 @@ const void GDAPI *godot_nativescript_get_type_tag(const godot_object *p_object)
return NULL;
}
-#ifdef __cplusplus
-}
-#endif
-
int GDAPI godot_nativescript_register_instance_binding_data_functions(godot_instance_binding_functions p_binding_functions) {
return NativeScriptLanguage::get_singleton()->register_binding_functions(p_binding_functions);
}
@@ -362,3 +364,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);
}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 5806ee3f3..cf8977f3e 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -55,12 +55,6 @@
#include "editor/editor_node.h"
#endif
-//
-//
-// Script stuff
-//
-//
-
void NativeScript::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_class_name", "class_name"), &NativeScript::set_class_name);
ClassDB::bind_method(D_METHOD("get_class_name"), &NativeScript::get_class_name);
@@ -363,14 +357,13 @@ void NativeScript::get_script_property_list(List<PropertyInfo> *p_list) const {
NativeScriptDesc *script_data = get_script_desc();
Set<StringName> existing_properties;
+ List<PropertyInfo>::Element *original_back = p_list->back();
while (script_data) {
- List<PropertyInfo>::Element *insert_position = p_list->front();
- bool insert_before = true;
+ List<PropertyInfo>::Element *insert_position = original_back;
for (OrderedHashMap<StringName, NativeScriptDesc::Property>::Element E = script_data->properties.front(); E; E = E.next()) {
if (!existing_properties.has(E.key())) {
- insert_position = insert_before ? p_list->insert_before(insert_position, E.get().info) : p_list->insert_after(insert_position, E.get().info);
- insert_before = false;
+ insert_position = p_list->insert_after(insert_position, E.get().info);
existing_properties.insert(E.key());
}
}
@@ -528,12 +521,6 @@ NativeScript::~NativeScript() {
#endif
}
- //
- //
- // ScriptInstance stuff
- //
- //
-
#define GET_SCRIPT_DESC() script->get_script_desc()
void NativeScriptInstance::_ml_call_reversed(NativeScriptDesc *script_data, const StringName &p_method, const Variant **p_args, int p_argcount) {
@@ -872,15 +859,12 @@ NativeScriptInstance::~NativeScriptInstance() {
}
}
-//
-//
-// ScriptingLanguage stuff
-//
-//
-
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();
@@ -916,18 +900,6 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) {
gdn = E->get();
}
- if (gdn.is_valid() && gdn->get_library().is_valid()) {
- Ref<GDNativeLibrary> lib = gdn->get_library();
- void *terminate_fn;
- Error err = gdn->get_symbol(lib->get_symbol_prefix() + _terminate_call_name, terminate_fn, true);
-
- if (err == OK) {
- void (*terminate)(void *) = (void (*)(void *))terminate_fn;
-
- terminate((void *)&lib_path);
- }
- }
-
for (Map<StringName, NativeScriptDesc>::Element *C = classes.front(); C; C = C->next()) {
// free property stuff first
@@ -952,6 +924,27 @@ void NativeScriptLanguage::_unload_stuff(bool p_reload) {
if (C->get().destroy_func.free_func)
C->get().destroy_func.free_func(C->get().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();
+
+ library_classes.erase(lib_path);
+
+ if (gdn.is_valid() && gdn->get_library().is_valid()) {
+ Ref<GDNativeLibrary> lib = gdn->get_library();
+ void *terminate_fn;
+ Error err = gdn->get_symbol(lib->get_symbol_prefix() + _terminate_call_name, terminate_fn, true);
+
+ if (err == OK) {
+ void (*terminate)(void *) = (void (*)(void *))terminate_fn;
+
+ terminate((void *)&lib_path);
+ }
+ }
}
}
@@ -1183,8 +1176,11 @@ void *NativeScriptLanguage::get_instance_binding_data(int p_idx, Object *p_objec
}
if (!(*binding_data)[p_idx]) {
+
+ const void *global_type_tag = global_type_tags[p_idx].get(p_object->get_class_name());
+
// no binding data yet, soooooo alloc new one \o/
- (*binding_data)[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, (godot_object *)p_object);
+ (*binding_data)[p_idx] = binding_functions[p_idx].second.alloc_instance_binding_data(binding_functions[p_idx].second.data, global_type_tag, (godot_object *)p_object);
}
return (*binding_data)[p_idx];
@@ -1226,6 +1222,27 @@ void NativeScriptLanguage::free_instance_binding_data(void *p_data) {
delete &binding_data;
}
+void NativeScriptLanguage::set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag) {
+ if (!global_type_tags.has(p_idx)) {
+ global_type_tags.insert(p_idx, HashMap<StringName, const void *>());
+ }
+
+ HashMap<StringName, const void *> &tags = global_type_tags[p_idx];
+
+ tags.set(p_class_name, p_type_tag);
+}
+
+const void *NativeScriptLanguage::get_global_type_tag(int p_idx, StringName p_class_name) const {
+ if (!global_type_tags.has(p_idx))
+ return NULL;
+
+ const HashMap<StringName, const void *> &tags = global_type_tags[p_idx];
+
+ const void *tag = tags.get(p_class_name);
+
+ return tag;
+}
+
#ifndef NO_THREADS
void NativeScriptLanguage::defer_init_library(Ref<GDNativeLibrary> lib, NativeScript *script) {
MutexLock lock(mutex);
@@ -1363,6 +1380,7 @@ void NativeReloadNode::_notification(int p_what) {
MutexLock lock(NSL->mutex);
#endif
NSL->_unload_stuff(true);
+
for (Map<String, Ref<GDNative> >::Element *L = NSL->library_gdnatives.front(); L; L = L->next()) {
Ref<GDNative> gdn = L->get();
@@ -1376,7 +1394,6 @@ void NativeReloadNode::_notification(int p_what) {
}
gdn->terminate();
- NSL->library_classes.erase(L->key());
}
unloaded = true;
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 17b6ddc74..68a8126a3 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -36,6 +36,7 @@
#include "core/self_list.h"
#include "io/resource_loader.h"
#include "io/resource_saver.h"
+#include "oa_hash_map.h"
#include "ordered_hash_map.h"
#include "os/thread_safe.h"
#include "scene/main/node.h"
@@ -240,6 +241,8 @@ private:
Vector<Pair<bool, godot_instance_binding_functions> > binding_functions;
Set<Vector<void *> *> binding_instances;
+ Map<int, HashMap<StringName, const void *> > global_type_tags;
+
public:
// These two maps must only be touched on the main thread
Map<String, Map<StringName, NativeScriptDesc> > library_classes;
@@ -323,6 +326,9 @@ public:
virtual void *alloc_instance_binding_data(Object *p_object);
virtual void free_instance_binding_data(void *p_data);
+
+ void set_global_type_tag(int p_idx, StringName p_class_name, const void *p_type_tag);
+ const void *get_global_type_tag(int p_idx, StringName p_class_name) const;
};
inline NativeScriptDesc *NativeScript::get_script_desc() const {
diff --git a/modules/gdnative/pluginscript/register_types.cpp b/modules/gdnative/pluginscript/register_types.cpp
index 8888f9e15..924abf29d 100644
--- a/modules/gdnative/pluginscript/register_types.cpp
+++ b/modules/gdnative/pluginscript/register_types.cpp
@@ -64,7 +64,7 @@ static Error _check_language_desc(const godot_pluginscript_language_desc *desc)
// desc->make_function is not mandatory
// desc->complete_code is not mandatory
// desc->auto_indent_code is not mandatory
- // desc->add_global_constant is not mandatory
+ ERR_FAIL_COND_V(!desc->add_global_constant, ERR_BUG);
// desc->debug_get_error is not mandatory
// desc->debug_get_stack_level_count is not mandatory
// desc->debug_get_stack_level_line is not mandatory
@@ -78,7 +78,7 @@ static Error _check_language_desc(const godot_pluginscript_language_desc *desc)
// desc->profiling_stop is not mandatory
// desc->profiling_get_accumulated_data is not mandatory
// desc->profiling_get_frame_data is not mandatory
- // desc->frame is not mandatory
+ // desc->profiling_frame is not mandatory
ERR_FAIL_COND_V(!desc->script_desc.init, ERR_BUG);
ERR_FAIL_COND_V(!desc->script_desc.finish, ERR_BUG);
diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp
index 5f72dca86..87e127649 100644
--- a/modules/gdscript/gdscript_editor.cpp
+++ b/modules/gdscript/gdscript_editor.cpp
@@ -61,7 +61,7 @@ Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const Str
"# var b = \"textvar\"\n\n" +
"func _ready():\n" +
"%TS%# Called when the node is added to the scene for the first time.\n" +
- "%TS%# Initialization here\n" +
+ "%TS%# Initialization here.\n" +
"%TS%pass\n\n" +
"#func _process(delta):\n" +
"#%TS%# Called every frame. Delta is time since last frame.\n" +
@@ -430,6 +430,9 @@ struct GDScriptCompletionIdentifier {
Ref<GDScript> script;
Variant::Type type;
Variant value; //im case there is a value, also return it
+
+ GDScriptCompletionIdentifier() :
+ type(Variant::NIL) {}
};
static GDScriptCompletionIdentifier _get_type_from_variant(const Variant &p_variant, bool p_allow_gdnative_class = false) {
@@ -551,9 +554,7 @@ static Ref<Reference> _get_parent_class(GDScriptCompletionContext &context) {
static GDScriptCompletionIdentifier _get_native_class(GDScriptCompletionContext &context) {
- //eeh...
GDScriptCompletionIdentifier id;
- id.type = Variant::NIL;
REF pc = _get_parent_class(context);
if (!pc.is_valid()) {
@@ -1333,13 +1334,23 @@ static void _find_identifiers_in_block(GDScriptCompletionContext &context, int p
for (int i = 0; i < context.block->statements.size(); i++) {
- if (context.block->statements[i]->line > p_line)
+ GDScriptParser::Node *statement = context.block->statements[i];
+ if (statement->line > p_line)
continue;
- if (context.block->statements[i]->type == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
+ GDScriptParser::BlockNode::Type statementType = statement->type;
+ if (statementType == GDScriptParser::BlockNode::TYPE_LOCAL_VAR) {
- const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(context.block->statements[i]);
+ const GDScriptParser::LocalVarNode *lv = static_cast<const GDScriptParser::LocalVarNode *>(statement);
result.insert(lv->name.operator String());
+ } else if (statementType == GDScriptParser::BlockNode::TYPE_CONTROL_FLOW) {
+
+ const GDScriptParser::ControlFlowNode *cf = static_cast<const GDScriptParser::ControlFlowNode *>(statement);
+ if (cf->cf_type == GDScriptParser::ControlFlowNode::CF_FOR) {
+
+ const GDScriptParser::IdentifierNode *id = static_cast<const GDScriptParser::IdentifierNode *>(cf->arguments[0]);
+ result.insert(id->name.operator String());
+ }
}
}
}
@@ -1511,6 +1522,13 @@ static void _find_identifiers(GDScriptCompletionContext &context, int p_line, bo
result.insert(_type_names[i]);
}
+ List<String> reserved_words;
+ GDScriptLanguage::get_singleton()->get_reserved_words(&reserved_words);
+
+ for (List<String>::Element *E = reserved_words.front(); E; E = E->next()) {
+ result.insert(E->get());
+ }
+
//autoload singletons
List<PropertyInfo> props;
ProjectSettings::get_singleton()->get_property_list(&props);
@@ -2631,6 +2649,18 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
context.function = p.get_completion_function();
context.base = p_owner;
context.base_path = p_base_path;
+
+ if (context._class && context._class->extends_class.size() > 0) {
+ bool success = false;
+ ClassDB::get_integer_constant(context._class->extends_class[0], p_symbol, &success);
+ if (success) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name = context._class->extends_class[0];
+ r_result.class_member = p_symbol;
+ return OK;
+ }
+ }
+
bool isfunction = false;
switch (p.get_completion_type()) {
@@ -2850,7 +2880,24 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
return OK;
}
} else {
- r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ /*
+ // Because get_integer_constant_enum and get_integer_constant dont work on @GlobalScope
+ // 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) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
+ r_result.class_name = "@GlobalScope";
+ r_result.class_member = enumName;
+ return OK;
+ }
+ else {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_CONSTANT;
+ r_result.class_name = "@GlobalScope";
+ r_result.class_member = p_symbol;
+ return OK;
+ }*/
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_TBD_GLOBALSCOPE;
r_result.class_name = "@GlobalScope";
r_result.class_member = p_symbol;
return OK;
@@ -2913,6 +2960,14 @@ Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symbol
return OK;
}
+ StringName enumName = ClassDB::get_integer_constant_enum(t.obj_type, p_symbol, true);
+ if (enumName != StringName()) {
+ r_result.type = ScriptLanguage::LookupResult::RESULT_CLASS_ENUM;
+ r_result.class_name = t.obj_type;
+ r_result.class_member = enumName;
+ return OK;
+ }
+
bool success;
ClassDB::get_integer_constant(t.obj_type, p_symbol, &success);
if (success) {
diff --git a/modules/gdscript/gdscript_function.cpp b/modules/gdscript/gdscript_function.cpp
index f83bec0c7..1c5b8187c 100644
--- a/modules/gdscript/gdscript_function.cpp
+++ b/modules/gdscript/gdscript_function.cpp
@@ -86,7 +86,7 @@ Variant *GDScriptFunction::_get_variant(int p_address, GDScriptInstance *p_insta
o = o->_owner;
}
- ERR_EXPLAIN("GDScriptCompiler bug..");
+ ERR_EXPLAIN("GDScriptCompiler bug...");
ERR_FAIL_V(NULL);
} break;
case ADDR_TYPE_LOCAL_CONSTANT: {
@@ -1311,9 +1311,9 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
GDScriptLanguage::get_singleton()->script_frame_time += time_taken - function_call_time;
}
-#endif
if (ScriptDebugger::get_singleton())
GDScriptLanguage::get_singleton()->exit_function();
+#endif
if (_stack_size) {
//free stack
diff --git a/modules/gdscript/gdscript_highlighter.cpp b/modules/gdscript/gdscript_highlighter.cpp
new file mode 100644
index 000000000..4e89851bf
--- /dev/null
+++ b/modules/gdscript/gdscript_highlighter.cpp
@@ -0,0 +1,262 @@
+/*************************************************************************/
+/* gdscript_highlighter.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (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_highlighter.h"
+#include "scene/gui/text_edit.h"
+
+inline bool _is_symbol(CharType c) {
+
+ return is_symbol(c);
+}
+
+static bool _is_text_char(CharType c) {
+
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
+}
+
+static bool _is_whitespace(CharType c) {
+ return c == '\t' || c == ' ';
+}
+
+static bool _is_char(CharType c) {
+
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
+}
+
+static bool _is_number(CharType c) {
+ return (c >= '0' && c <= '9');
+}
+
+static bool _is_hex_symbol(CharType c) {
+ return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+Map<int, TextEdit::HighlighterInfo> GDScriptSyntaxHighlighter::_get_line_syntax_highlighting(int p_line) {
+ Map<int, TextEdit::HighlighterInfo> color_map;
+
+ bool prev_is_char = false;
+ bool prev_is_number = false;
+ bool in_keyword = false;
+ bool in_word = false;
+ bool in_function_name = false;
+ bool in_member_variable = false;
+ bool is_hex_notation = false;
+ Color keyword_color;
+ Color color;
+
+ int in_region = text_editor->_is_line_in_region(p_line);
+ int deregion = 0;
+
+ const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text_editor->_get_line_color_region_info(p_line);
+ const String &str = text_editor->get_line(p_line);
+ Color prev_color;
+ for (int j = 0; j < str.length(); j++) {
+ TextEdit::HighlighterInfo highlighter_info;
+
+ if (deregion > 0) {
+ deregion--;
+ if (deregion == 0) {
+ in_region = -1;
+ }
+ }
+
+ if (deregion != 0) {
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ continue;
+ }
+
+ color = font_color;
+
+ bool is_char = _is_text_char(str[j]);
+ bool is_symbol = _is_symbol(str[j]);
+ bool is_number = _is_number(str[j]);
+
+ // allow ABCDEF in hex notation
+ if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+ is_number = true;
+ } else {
+ is_hex_notation = false;
+ }
+
+ // check for dot or underscore or 'x' for hex notation in floating point number
+ if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) {
+ is_number = true;
+ is_symbol = false;
+ is_char = false;
+
+ if (str[j] == 'x' && str[j - 1] == '0') {
+ is_hex_notation = true;
+ }
+ }
+
+ if (!in_word && _is_char(str[j]) && !is_number) {
+ in_word = true;
+ }
+
+ if ((in_keyword || in_word) && !is_hex_notation) {
+ is_number = false;
+ }
+
+ if (is_symbol && str[j] != '.' && in_word) {
+ in_word = false;
+ }
+
+ if (is_symbol && cri_map.has(j)) {
+ const TextEdit::Text::ColorRegionInfo &cri = cri_map[j];
+
+ if (in_region == -1) {
+ if (!cri.end) {
+ in_region = cri.region;
+ }
+ } else {
+ TextEdit::ColorRegion cr = text_editor->_get_color_region(cri.region);
+ if (in_region == cri.region && !cr.line_only) { //ignore otherwise
+ if (cri.end || cr.eq) {
+ deregion = cr.eq ? cr.begin_key.length() : cr.end_key.length();
+ }
+ }
+ }
+ }
+
+ if (!is_char) {
+ in_keyword = false;
+ }
+
+ if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
+
+ int to = j;
+ while (to < str.length() && _is_text_char(str[to]))
+ to++;
+
+ String word = str.substr(j, to - j);
+ Color col = Color();
+ if (text_editor->has_keyword_color(word)) {
+ col = text_editor->get_keyword_color(word);
+ } else if (text_editor->has_member_color(word)) {
+ col = text_editor->get_member_color(word);
+ for (int k = j - 1; k >= 0; k--) {
+ if (str[k] == '.') {
+ col = Color(); //member indexing not allowed
+ break;
+ } else if (str[k] > 32) {
+ break;
+ }
+ }
+ }
+
+ if (col != Color()) {
+ in_keyword = true;
+ keyword_color = col;
+ }
+ }
+
+ if (!in_function_name && in_word && !in_keyword) {
+
+ int k = j;
+ while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k++;
+ }
+
+ // check for space between name and bracket
+ while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
+ k++;
+ }
+
+ if (str[k] == '(') {
+ in_function_name = true;
+ }
+ }
+
+ if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
+ int k = j;
+ while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k--;
+ }
+
+ if (str[k] == '.') {
+ in_member_variable = true;
+ }
+ }
+
+ if (is_symbol) {
+ in_function_name = false;
+ in_member_variable = false;
+ }
+
+ if (in_region >= 0)
+ color = text_editor->_get_color_region(in_region).color;
+ else if (in_keyword)
+ color = keyword_color;
+ else if (in_member_variable)
+ color = member_color;
+ else if (in_function_name)
+ color = function_color;
+ else if (is_symbol)
+ color = symbol_color;
+ else if (is_number)
+ color = number_color;
+
+ prev_is_char = is_char;
+ prev_is_number = is_number;
+
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ }
+ return color_map;
+}
+
+String GDScriptSyntaxHighlighter::get_name() {
+ return "GDScript";
+}
+
+List<String> GDScriptSyntaxHighlighter::get_supported_languages() {
+ List<String> languages;
+ languages.push_back("GDScript");
+ return languages;
+}
+
+void GDScriptSyntaxHighlighter::_update_cache() {
+ font_color = text_editor->get_color("font_color");
+ symbol_color = text_editor->get_color("symbol_color");
+ function_color = text_editor->get_color("function_color");
+ number_color = text_editor->get_color("number_color");
+ member_color = text_editor->get_color("member_variable_color");
+}
+
+SyntaxHighlighter *GDScriptSyntaxHighlighter::create() {
+ return memnew(GDScriptSyntaxHighlighter);
+}
diff --git a/modules/gdscript/gdscript_highlighter.h b/modules/gdscript/gdscript_highlighter.h
new file mode 100644
index 000000000..ef1bdd410
--- /dev/null
+++ b/modules/gdscript/gdscript_highlighter.h
@@ -0,0 +1,56 @@
+/*************************************************************************/
+/* gdscript_highlighter.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2018 Godot Engine contributors (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_HIGHLIGHTER_H
+#define GDSCRIPT_HIGHLIGHTER_H
+
+#include "scene/gui/text_edit.h"
+
+class GDScriptSyntaxHighlighter : public SyntaxHighlighter {
+private:
+ // colours
+ Color font_color;
+ Color symbol_color;
+ Color function_color;
+ Color built_in_type_color;
+ Color number_color;
+ Color member_color;
+
+public:
+ static SyntaxHighlighter *create();
+
+ virtual void _update_cache();
+ virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line);
+
+ virtual String get_name();
+ virtual List<String> get_supported_languages();
+};
+
+#endif // GDSCRIPT_HIGHLIGHTER_H
diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp
index 435bc327d..e7b0700e7 100644
--- a/modules/gdscript/gdscript_parser.cpp
+++ b/modules/gdscript/gdscript_parser.cpp
@@ -970,7 +970,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
}
if (!expr) {
- ERR_EXPLAIN("GDScriptParser bug, couldn't figure out what expression is..");
+ ERR_EXPLAIN("GDScriptParser bug, couldn't figure out what expression is...");
ERR_FAIL_COND_V(!expr, NULL);
}
@@ -1305,7 +1305,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
expr_pos++;
if (expr_pos == expression.size()) {
//can happen..
- _set_error("Unexpected end of expression..");
+ _set_error("Unexpected end of expression...");
return NULL;
}
}
@@ -1324,7 +1324,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
} else if (is_ternary) {
if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -1343,7 +1343,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (expression[next_op - 1].is_op) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -1380,7 +1380,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
} else {
if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -1390,7 +1390,7 @@ GDScriptParser::Node *GDScriptParser::_parse_expression(Node *p_parent, bool p_s
if (expression[next_op - 1].is_op) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -3440,6 +3440,22 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
if (tokenizer->get_token() == GDScriptTokenizer::TK_PARENTHESIS_OPEN) {
tokenizer->advance();
+
+ String hint_prefix = "";
+ bool is_arrayed = false;
+
+ while (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE &&
+ tokenizer->get_token_type() == Variant::ARRAY &&
+ tokenizer->get_token(1) == GDScriptTokenizer::TK_COMMA) {
+ tokenizer->advance(); // Array
+ tokenizer->advance(); // Comma
+ if (is_arrayed) {
+ hint_prefix += itos(Variant::ARRAY) + ":";
+ } else {
+ is_arrayed = true;
+ }
+ }
+
if (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE) {
Variant::Type type = tokenizer->get_token_type();
@@ -3455,28 +3471,6 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
current_export.usage |= PROPERTY_USAGE_SCRIPT_VARIABLE;
tokenizer->advance();
- String hint_prefix = "";
-
- if (type == Variant::ARRAY && tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
-
- while (tokenizer->get_token() == GDScriptTokenizer::TK_BUILT_IN_TYPE) {
- type = tokenizer->get_token_type();
-
- tokenizer->advance();
-
- if (type == Variant::ARRAY) {
- hint_prefix += itos(Variant::ARRAY) + ":";
- if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
- tokenizer->advance();
- }
- } else {
- hint_prefix += itos(type);
- break;
- }
- }
- }
-
if (tokenizer->get_token() == GDScriptTokenizer::TK_COMMA) {
// hint expected next!
tokenizer->advance();
@@ -3830,13 +3824,6 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
} break;
}
}
- if (current_export.type == Variant::ARRAY && !hint_prefix.empty()) {
- if (current_export.hint) {
- hint_prefix += "/" + itos(current_export.hint);
- }
- current_export.hint_string = hint_prefix + ":" + current_export.hint_string;
- current_export.hint = PROPERTY_HINT_NONE;
- }
} else {
@@ -3923,6 +3910,16 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
return;
}
+ if (is_arrayed) {
+ hint_prefix += itos(current_export.type);
+ if (current_export.hint) {
+ hint_prefix += "/" + itos(current_export.hint);
+ }
+ current_export.hint_string = hint_prefix + ":" + current_export.hint_string;
+ current_export.hint = PROPERTY_HINT_TYPE_STRING;
+ current_export.type = Variant::ARRAY;
+ }
+
tokenizer->advance();
}
@@ -4090,7 +4087,8 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
member._export.type=Variant::DICTIONARY;
- } else*/ {
+ } else*/
+ {
if (subexpr->type != Node::TYPE_CONSTANT) {
diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp
index 95efcda80..85c94c359 100644
--- a/modules/gdscript/register_types.cpp
+++ b/modules/gdscript/register_types.cpp
@@ -31,6 +31,7 @@
#include "register_types.h"
#include "gdscript.h"
+#include "gdscript_highlighter.h"
#include "gdscript_tokenizer.h"
#include "io/file_access_encrypted.h"
#include "io/resource_loader.h"
@@ -92,6 +93,7 @@ void register_gdscript_types() {
ResourceSaver::add_resource_format_saver(resource_saver_gd);
#ifdef TOOLS_ENABLED
+ ScriptEditor::register_create_syntax_highlighter_function(GDScriptSyntaxHighlighter::create);
EditorNode::add_init_callback(_editor_init);
#endif
}
diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp
index 869492232..4b96824dc 100644
--- a/modules/gridmap/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/grid_map_editor_plugin.cpp
@@ -1046,14 +1046,14 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
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_check_item(TTR("Clip Disabled"), MENU_OPTION_CLIP_DISABLED);
+ 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_check_item(TTR("Clip Above"), MENU_OPTION_CLIP_ABOVE);
- options->get_popup()->add_check_item(TTR("Clip Below"), MENU_OPTION_CLIP_BELOW);
+ 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_check_item(TTR("Edit X Axis"), MENU_OPTION_X_AXIS, KEY_Z);
- options->get_popup()->add_check_item(TTR("Edit Y Axis"), MENU_OPTION_Y_AXIS, KEY_X);
- options->get_popup()->add_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);
@@ -1154,9 +1154,9 @@ GridMapEditor::GridMapEditor(EditorNode *p_editor) {
for (int k = 0; k < 3; k++) {
if (i < 3)
- face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[j][(i + k) % 3] = v[k];
else
- face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[3 - j][(i + k) % 3] = -v[k];
}
}
diff --git a/modules/mbedtls/stream_peer_mbed_tls.cpp b/modules/mbedtls/stream_peer_mbed_tls.cpp
index 4135eb40f..a63e53ec1 100755
--- a/modules/mbedtls/stream_peer_mbed_tls.cpp
+++ b/modules/mbedtls/stream_peer_mbed_tls.cpp
@@ -293,28 +293,10 @@ void StreamPeerMbedTLS::initialize_ssl() {
mbedtls_debug_set_threshold(1);
#endif
- String certs_path = GLOBAL_DEF("network/ssl/certificates", "");
- ProjectSettings::get_singleton()->set_custom_property_info("network/ssl/certificates", PropertyInfo(Variant::STRING, "network/ssl/certificates", PROPERTY_HINT_FILE, "*.crt"));
+ PoolByteArray cert_array = StreamPeerSSL::get_project_cert_array();
- if (certs_path != "") {
-
- FileAccess *f = FileAccess::open(certs_path, FileAccess::READ);
- if (f) {
- PoolByteArray arr;
- int flen = f->get_len();
- arr.resize(flen + 1);
- {
- PoolByteArray::Write w = arr.write();
- f->get_buffer(w.ptr(), flen);
- w[flen] = 0; //end f string
- }
-
- memdelete(f);
-
- _load_certs(arr);
- print_line("Loaded certs from '" + certs_path);
- }
- }
+ if (cert_array.size() > 0)
+ _load_certs(cert_array);
available = true;
}
diff --git a/modules/mbedtls/stream_peer_mbed_tls.h b/modules/mbedtls/stream_peer_mbed_tls.h
index ce17614d8..2b96a194a 100755
--- a/modules/mbedtls/stream_peer_mbed_tls.h
+++ b/modules/mbedtls/stream_peer_mbed_tls.h
@@ -32,8 +32,6 @@
#define STREAM_PEER_OPEN_SSL_H
#include "io/stream_peer_ssl.h"
-#include "os/file_access.h"
-#include "project_settings.h"
#include "mbedtls/config.h"
#include "mbedtls/ctr_drbg.h"
diff --git a/modules/mono/config.py b/modules/mono/config.py
index 831d849ea..18d9c6779 100644
--- a/modules/mono/config.py
+++ b/modules/mono/config.py
@@ -59,9 +59,6 @@ def configure(env):
mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
if env['platform'] == 'windows':
- if mono_static:
- raise RuntimeError('mono-static: Not supported on Windows')
-
if bits == '32':
if os.getenv('MONO32_PREFIX'):
mono_root = os.getenv('MONO32_PREFIX')
@@ -81,24 +78,41 @@ def configure(env):
env.Append(LIBPATH=mono_lib_path)
env.Append(CPPPATH=os.path.join(mono_root, 'include', 'mono-2.0'))
- mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
+ if mono_static:
+ lib_suffix = Environment()['LIBSUFFIX']
+ mono_static_lib_name = 'libmono-static-sgen'
- if not mono_lib_name:
- raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
+ if not os.path.isfile(os.path.join(mono_lib_path, mono_static_lib_name + lib_suffix)):
+ raise RuntimeError('Could not find static mono library in: ' + mono_lib_path)
- if os.getenv('VCINSTALLDIR'):
- env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
+ if env.msvc:
+ env.Append(LINKFLAGS=mono_static_lib_name + lib_suffix)
+
+ env.Append(LINKFLAGS='Mincore' + lib_suffix)
+ env.Append(LINKFLAGS='msvcrt' + lib_suffix)
+ env.Append(LINKFLAGS='LIBCMT' + lib_suffix)
+ env.Append(LINKFLAGS='Psapi' + lib_suffix)
+ else:
+ env.Append(LIBS=mono_static_lib_name)
else:
- env.Append(LIBS=mono_lib_name)
+ mono_lib_name = find_file_in_dir(mono_lib_path, mono_lib_names, extension='.lib')
+
+ if not mono_lib_name:
+ raise RuntimeError('Could not find mono library in: ' + mono_lib_path)
+
+ if env.msvc:
+ env.Append(LINKFLAGS=mono_lib_name + Environment()['LIBSUFFIX'])
+ else:
+ env.Append(LIBS=mono_lib_name)
- mono_bin_path = os.path.join(mono_root, 'bin')
+ mono_bin_path = os.path.join(mono_root, 'bin')
- mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll')
+ mono_dll_name = find_file_in_dir(mono_bin_path, mono_lib_names, extension='.dll')
- if not mono_dll_name:
- raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
+ if not mono_dll_name:
+ raise RuntimeError('Could not find mono shared library in: ' + mono_bin_path)
- copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll')
+ copy_file(mono_bin_path, 'bin', mono_dll_name + '.dll')
copy_file(os.path.join(mono_lib_path, 'mono', '4.5'), assemblies_output_dir, 'mscorlib.dll')
else:
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 1d0afa7f2..bbe245951 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -305,7 +305,7 @@ Ref<Script> CSharpLanguage::get_template(const String &p_class_name, const Strin
" public override void _Ready()\n"
" {\n"
" // Called every time the node is added to the scene.\n"
- " // Initialization here\n"
+ " // Initialization here.\n"
" \n"
" }\n"
"\n"
@@ -1954,8 +1954,12 @@ Variant CSharpScript::_new(const Variant **p_args, int p_argcount, Variant::Call
ScriptInstance *CSharpScript::instance_create(Object *p_this) {
- if (!valid)
- return NULL;
+ if (!script_class) {
+ ERR_EXPLAIN("Cannot find class " + name + " for script " + get_path());
+ ERR_FAIL_V(NULL);
+ }
+
+ ERR_FAIL_COND_V(!valid, NULL);
if (!tool && !ScriptServer::is_scripting_enabled()) {
#ifdef TOOLS_ENABLED
@@ -2045,20 +2049,15 @@ Error CSharpScript::reload(bool p_keep_state) {
if (project_assembly) {
script_class = project_assembly->get_object_derived_class(name);
- if (!script_class) {
- ERR_PRINTS("Cannot find class " + name + " for script " + get_path());
- }
+ valid = script_class != NULL;
+
+ if (script_class) {
#ifdef DEBUG_ENABLED
- else if (OS::get_singleton()->is_stdout_verbose()) {
OS::get_singleton()->print(String("Found class " + script_class->get_namespace() + "." +
script_class->get_name() + " for script " + get_path() + "\n")
.utf8());
- }
#endif
- valid = script_class != NULL;
-
- if (script_class) {
tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute));
native = GDMonoUtils::get_class_native_base(script_class);
@@ -2288,7 +2287,9 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p
CRASH_COND(mono_domain_get() == NULL);
#endif
-#else
+#endif
+
+#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() && mono_domain_get() == NULL) {
CRASH_COND(Thread::get_caller_id() == Thread::get_main_id());
@@ -2297,14 +2298,20 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p
// because this may be called by one of the editor's worker threads.
// Attach this thread temporarily to reload the script.
- MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN);
- CRASH_COND(mono_thread == NULL);
+ if (SCRIPTS_DOMAIN) {
+ MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN);
+ CRASH_COND(mono_thread == NULL);
+ script->reload();
+ mono_thread_detach(mono_thread);
+ }
+
+ } else { // just reload it normally
+#endif
script->reload();
- mono_thread_detach(mono_thread);
- } else // just reload it normally
+#ifdef TOOLS_ENABLED
+ }
#endif
- script->reload();
if (r_error)
*r_error = OK;
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index a210b8e48..4c598d4f3 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -248,14 +248,14 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
if (imethod.is_virtual)
continue;
- const TypeInterface *return_type = _get_type_by_name_or_placeholder(imethod.return_type);
+ const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type);
String im_sig;
String im_unique_sig;
if (p_itype.is_object_type) {
im_sig += "IntPtr " CS_PARAM_METHODBIND ", ";
- im_unique_sig += imethod.return_type.operator String() + ",IntPtr,IntPtr";
+ im_unique_sig += imethod.return_type.cname.operator String() + ",IntPtr,IntPtr";
}
im_sig += "IntPtr " CS_PARAM_INSTANCE;
@@ -263,7 +263,7 @@ 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_by_name_or_placeholder(F->get().type);
+ const TypeInterface *arg_type = _get_type_or_placeholder(F->get().type);
im_sig += ", ";
im_sig += arg_type->im_type_in;
@@ -730,7 +730,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
}
output.push_back(INDENT1 "public ");
- bool is_abstract = !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled'
+ bool is_abstract = itype.is_object_type && !ClassDB::can_instance(itype.name) && ClassDB::is_class_enabled(itype.name); // can_instance returns true if there's a constructor and the class is not 'disabled'
output.push_back(itype.is_singleton ? "static class " : (is_abstract ? "abstract class " : "class "));
output.push_back(itype.proxy_name);
@@ -1069,12 +1069,12 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
}
if (getter && setter) {
- ERR_FAIL_COND_V(getter->return_type != setter->arguments.back()->get().type, ERR_BUG);
+ ERR_FAIL_COND_V(getter->return_type.cname != setter->arguments.back()->get().type.cname, ERR_BUG);
}
- StringName proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
+ const TypeReference &proptype_name = getter ? getter->return_type : setter->arguments.back()->get().type;
- const TypeInterface *prop_itype = _get_type_by_name_or_null(proptype_name);
+ const TypeInterface *prop_itype = _get_type_or_null(proptype_name);
ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found
String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(p_iprop.cname));
@@ -1122,9 +1122,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.push_back(getter->proxy_name + "(");
if (p_iprop.index != -1) {
const ArgumentInterface &idx_arg = getter->arguments.front()->get();
- if (idx_arg.type != name_cache.type_int) {
+ if (idx_arg.type.cname != name_cache.type_int) {
// Assume the index parameter is an enum
- const TypeInterface *idx_arg_type = _get_type_by_name_or_null(idx_arg.type);
+ const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
CRASH_COND(idx_arg_type == NULL);
p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index));
} else {
@@ -1139,9 +1139,9 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.push_back(setter->proxy_name + "(");
if (p_iprop.index != -1) {
const ArgumentInterface &idx_arg = setter->arguments.front()->get();
- if (idx_arg.type != name_cache.type_int) {
+ if (idx_arg.type.cname != name_cache.type_int) {
// Assume the index parameter is an enum
- const TypeInterface *idx_arg_type = _get_type_by_name_or_null(idx_arg.type);
+ const TypeInterface *idx_arg_type = _get_type_or_null(idx_arg.type);
CRASH_COND(idx_arg_type == NULL);
p_output.push_back("(" + idx_arg_type->proxy_name + ")" + itos(p_iprop.index) + ", ");
} else {
@@ -1158,7 +1158,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, List<String> &p_output) {
- const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type);
+ const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
String method_bind_field = "method_bind_" + itos(p_method_bind_count);
@@ -1175,7 +1175,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// 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 TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
+ const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
// Add the current arguments to the signature
// If the argument has a default value which is not a constant, we will make it Nullable
@@ -1328,21 +1328,19 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
const InternalCall *im_icall = match->value();
String im_call = im_icall->editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS;
- im_call += "." + im_icall->name + "(" + icall_params + ");\n";
+ im_call += "." + im_icall->name + "(" + icall_params + ")";
if (p_imethod.arguments.size())
p_output.push_back(cs_in_statements);
if (return_type->cname == name_cache.type_void) {
- p_output.push_back(im_call);
+ p_output.push_back(im_call + ";\n");
} else if (return_type->cs_out.empty()) {
- p_output.push_back("return " + im_call);
+ p_output.push_back("return " + im_call + ";\n");
} else {
- p_output.push_back(return_type->im_type_out);
- p_output.push_back(" " LOCAL_RET " = ");
- p_output.push_back(im_call);
p_output.push_back(INDENT3);
- p_output.push_back(sformat(return_type->cs_out, LOCAL_RET) + "\n");
+ p_output.push_back(sformat(return_type->cs_out, im_call, return_type->cs_type, return_type->im_type_out));
+ p_output.push_back("\n");
}
p_output.push_back(CLOSE_BLOCK_L2);
@@ -1540,9 +1538,9 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
if (p_imethod.is_virtual)
return OK; // Ignore
- bool ret_void = p_imethod.return_type == name_cache.type_void;
+ bool ret_void = p_imethod.return_type.cname == name_cache.type_void;
- const TypeInterface *return_type = _get_type_by_name_or_placeholder(p_imethod.return_type);
+ const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
String argc_str = itos(p_imethod.arguments.size());
@@ -1554,7 +1552,7 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
int i = 0;
for (const List<ArgumentInterface>::Element *F = p_imethod.arguments.front(); F; F = F->next()) {
const ArgumentInterface &iarg = F->get();
- const TypeInterface *arg_type = _get_type_by_name_or_placeholder(iarg.type);
+ const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
String c_param_name = "arg" + itos(i + 1);
@@ -1695,42 +1693,49 @@ Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInte
return OK;
}
-const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_null(const StringName &p_cname) {
+const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(const TypeReference &p_typeref) {
- const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_cname);
+ const Map<StringName, TypeInterface>::Element *builtin_type_match = builtin_types.find(p_typeref.cname);
if (builtin_type_match)
return &builtin_type_match->get();
- const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_cname);
+ const OrderedHashMap<StringName, TypeInterface>::Element obj_type_match = obj_types.find(p_typeref.cname);
if (obj_type_match)
return &obj_type_match.get();
- const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_cname);
+ if (p_typeref.is_enum) {
+ const Map<StringName, TypeInterface>::Element *enum_match = enum_types.find(p_typeref.cname);
- if (enum_match)
- return &enum_match->get();
+ if (enum_match)
+ return &enum_match->get();
+
+ // Enum not found. Most likely because none of its constants were bound, so it's empty. That's fine. Use int instead.
+ const Map<StringName, TypeInterface>::Element *int_match = builtin_types.find(name_cache.type_int);
+ ERR_FAIL_NULL_V(int_match, NULL);
+ return &int_match->get();
+ }
return NULL;
}
-const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_by_name_or_placeholder(const StringName &p_cname) {
+const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) {
- const TypeInterface *found = _get_type_by_name_or_null(p_cname);
+ const TypeInterface *found = _get_type_or_null(p_typeref);
if (found)
return found;
- ERR_PRINTS(String() + "Type not found. Creating placeholder: " + p_cname.operator String());
+ ERR_PRINTS(String() + "Type not found. Creating placeholder: " + p_typeref.cname.operator String());
- const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_cname);
+ const Map<StringName, TypeInterface>::Element *match = placeholder_types.find(p_typeref.cname);
if (match)
return &match->get();
TypeInterface placeholder;
- TypeInterface::create_placeholder_type(placeholder, p_cname);
+ TypeInterface::create_placeholder_type(placeholder, p_typeref.cname);
return &placeholder_types.insert(placeholder.cname, placeholder)->get();
}
@@ -1874,7 +1879,7 @@ void BindingsGenerator::_populate_object_type_interfaces() {
// The method Object.free is registered as a virtual method, but without the virtual flag.
// This is because this method is not supposed to be overridden, but called.
// We assume the return type is void.
- imethod.return_type = name_cache.type_void;
+ imethod.return_type.cname = name_cache.type_void;
// Actually, more methods like this may be added in the future,
// which could actually will return something different.
@@ -1889,21 +1894,22 @@ void BindingsGenerator::_populate_object_type_interfaces() {
} else {
ERR_PRINTS("Missing MethodBind for non-virtual method: " + itype.name + "." + imethod.name);
}
- } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { // TODO redundant?
- imethod.return_type = return_info.class_name;
+ } else if (return_info.type == Variant::INT && return_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ imethod.return_type.cname = return_info.class_name;
+ imethod.return_type.is_enum = true;
} else if (return_info.class_name != StringName()) {
- imethod.return_type = return_info.class_name;
+ imethod.return_type.cname = return_info.class_name;
} else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- imethod.return_type = return_info.hint_string;
+ imethod.return_type.cname = return_info.hint_string;
} else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
- imethod.return_type = name_cache.type_Variant;
+ imethod.return_type.cname = name_cache.type_Variant;
} else if (return_info.type == Variant::NIL) {
- imethod.return_type = name_cache.type_void;
+ imethod.return_type.cname = name_cache.type_void;
} else {
- imethod.return_type = Variant::get_type_name(return_info.type);
+ imethod.return_type.cname = Variant::get_type_name(return_info.type);
}
- if (!itype.requires_collections && imethod.return_type == name_cache.type_Dictionary)
+ if (!itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary)
itype.requires_collections = true;
for (int i = 0; i < argc; i++) {
@@ -1912,21 +1918,22 @@ void BindingsGenerator::_populate_object_type_interfaces() {
ArgumentInterface iarg;
iarg.name = arginfo.name;
- if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) { // TODO redundant?
- iarg.type = arginfo.class_name;
+ if (arginfo.type == Variant::INT && arginfo.usage & PROPERTY_USAGE_CLASS_IS_ENUM) {
+ iarg.type.cname = arginfo.class_name;
+ iarg.type.is_enum = true;
} else if (arginfo.class_name != StringName()) {
- iarg.type = arginfo.class_name;
+ iarg.type.cname = arginfo.class_name;
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- iarg.type = arginfo.hint_string;
+ iarg.type.cname = arginfo.hint_string;
} else if (arginfo.type == Variant::NIL) {
- iarg.type = name_cache.type_Variant;
+ iarg.type.cname = name_cache.type_Variant;
} else {
- iarg.type = Variant::get_type_name(arginfo.type);
+ iarg.type.cname = Variant::get_type_name(arginfo.type);
}
iarg.name = escape_csharp_keyword(snake_to_camel_case(iarg.name));
- if (!itype.requires_collections && iarg.type == name_cache.type_Dictionary)
+ if (!itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary)
itype.requires_collections = true;
if (m && m->has_default_argument(i)) {
@@ -1938,7 +1945,7 @@ void BindingsGenerator::_populate_object_type_interfaces() {
if (imethod.is_vararg) {
ArgumentInterface ivararg;
- ivararg.type = name_cache.type_VarArg;
+ ivararg.type.cname = name_cache.type_VarArg;
ivararg.name = "@args";
imethod.add_argument(ivararg);
}
@@ -2023,17 +2030,11 @@ void BindingsGenerator::_populate_object_type_interfaces() {
itype.enums.push_back(ienum);
TypeInterface enum_itype;
+ enum_itype.is_enum = true;
enum_itype.name = itype.name + "." + String(*k);
enum_itype.cname = StringName(enum_itype.name);
enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name;
- enum_itype.c_arg_in = "&%s";
- enum_itype.c_type = "int";
- enum_itype.c_type_in = "int";
- enum_itype.c_type_out = "int";
- enum_itype.cs_type = enum_itype.proxy_name;
- enum_itype.im_type_in = enum_itype.proxy_name;
- enum_itype.im_type_out = enum_itype.proxy_name;
- enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[enum_itype.proxy_name];
+ TypeInterface::postsetup_enum_type(enum_itype);
enum_types.insert(enum_itype.cname, enum_itype);
}
@@ -2068,7 +2069,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg
switch (p_val.get_type()) {
case Variant::NIL:
- if (ClassDB::class_exists(r_iarg.type)) {
+ if (ClassDB::class_exists(r_iarg.type.cname)) {
// Object type
r_iarg.default_argument = "null";
} else {
@@ -2081,7 +2082,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg
r_iarg.default_argument = bool(p_val) ? "true" : "false";
break;
case Variant::INT:
- if (r_iarg.type != name_cache.type_int) {
+ if (r_iarg.type.cname != name_cache.type_int) {
r_iarg.default_argument = "(%s)" + r_iarg.default_argument;
}
break;
@@ -2142,7 +2143,7 @@ void BindingsGenerator::_default_argument_from_variant(const Variant &p_val, Arg
default: {}
}
- if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type == name_cache.type_Variant && r_iarg.default_argument != "null")
+ if (r_iarg.def_param_mode == ArgumentInterface::CONSTANT && r_iarg.type.cname == name_cache.type_Variant && r_iarg.default_argument != "null")
r_iarg.def_param_mode = ArgumentInterface::NULLABLE_REF;
}
@@ -2161,7 +2162,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_arg_in = "&%s_in"; \
itype.c_type_in = m_type_in; \
itype.cs_in = "ref %s"; \
- itype.cs_out = "return (" #m_type ")%0;"; \
+ itype.cs_out = "return (%1)%0;"; \
itype.im_type_out = "object"; \
builtin_types.insert(itype.cname, itype); \
}
@@ -2256,7 +2257,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)";
- itype.cs_out = "return new NodePath(%0);";
+ itype.cs_out = "return new %1(%0);";
itype.im_type_in = "IntPtr";
itype.im_type_out = "IntPtr";
_populate_builtin_type(itype, Variant::NODE_PATH);
@@ -2279,7 +2280,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)";
- itype.cs_out = "return new RID(%0);";
+ itype.cs_out = "return new %1(%0);";
itype.im_type_in = "IntPtr";
itype.im_type_out = "IntPtr";
_populate_builtin_type(itype, Variant::_RID);
@@ -2408,11 +2409,11 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant::
iarg.name = pi.name;
if (pi.type == Variant::NIL)
- iarg.type = name_cache.type_Variant;
+ iarg.type.cname = name_cache.type_Variant;
else
- iarg.type = Variant::get_type_name(pi.type);
+ iarg.type.cname = Variant::get_type_name(pi.type);
- if (!r_itype.requires_collections && iarg.type == name_cache.type_Dictionary)
+ if (!r_itype.requires_collections && iarg.type.cname == name_cache.type_Dictionary)
r_itype.requires_collections = true;
if ((mi.default_arguments.size() - mi.arguments.size() + i) >= 0)
@@ -2423,12 +2424,12 @@ void BindingsGenerator::_populate_builtin_type(TypeInterface &r_itype, Variant::
if (mi.return_val.type == Variant::NIL) {
if (mi.return_val.name != "")
- imethod.return_type = name_cache.type_Variant;
+ imethod.return_type.cname = name_cache.type_Variant;
} else {
- imethod.return_type = Variant::get_type_name(mi.return_val.type);
+ imethod.return_type.cname = Variant::get_type_name(mi.return_val.type);
}
- if (!r_itype.requires_collections && imethod.return_type == name_cache.type_Dictionary)
+ if (!r_itype.requires_collections && imethod.return_type.cname == name_cache.type_Dictionary)
r_itype.requires_collections = true;
if (r_itype.class_doc) {
@@ -2494,13 +2495,11 @@ void BindingsGenerator::_populate_global_constants() {
EnumInterface &ienum = E->get();
TypeInterface enum_itype;
- enum_itype = TypeInterface::create_value_type(ienum.cname);
- enum_itype.c_arg_in = "&%s";
- enum_itype.c_type = "int";
- enum_itype.c_type_in = "int";
- enum_itype.c_type_out = "int";
- enum_itype.im_type_in = enum_itype.name;
- enum_itype.im_type_out = enum_itype.name;
+ enum_itype.is_enum = true;
+ enum_itype.name = ienum.cname.operator String();
+ enum_itype.cname = ienum.cname;
+ enum_itype.proxy_name = enum_itype.name;
+ TypeInterface::postsetup_enum_type(enum_itype);
enum_types.insert(enum_itype.cname, enum_itype);
ienum.prefix = _determine_enum_prefix(ienum);
@@ -2521,15 +2520,13 @@ void BindingsGenerator::_populate_global_constants() {
hardcoded_enums.push_back("Vector3.Axis");
for (List<StringName>::Element *E = hardcoded_enums.front(); E; E = E->next()) {
// These enums are not generated and must be written manually (e.g.: Vector3.Axis)
- // Here, we are assuming core types do not begin with underscore
+ // Here, we assume core types do not begin with underscore
TypeInterface enum_itype;
- enum_itype = TypeInterface::create_value_type(E->get());
- enum_itype.c_arg_in = "&%s";
- enum_itype.c_type = "int";
- enum_itype.c_type_in = "int";
- enum_itype.c_type_out = "int";
- enum_itype.im_type_in = enum_itype.name;
- enum_itype.im_type_out = enum_itype.name;
+ enum_itype.is_enum = true;
+ enum_itype.name = E->get().operator String();
+ enum_itype.cname = E->get();
+ enum_itype.proxy_name = enum_itype.name;
+ TypeInterface::postsetup_enum_type(enum_itype);
enum_types.insert(enum_itype.cname, enum_itype);
}
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index f6194139a..5b33a0e53 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -81,6 +81,15 @@ class BindingsGenerator {
const DocData::PropertyDoc *prop_doc;
};
+ struct TypeReference {
+ StringName cname;
+ bool is_enum;
+
+ TypeReference() {
+ is_enum = false;
+ }
+ };
+
struct ArgumentInterface {
enum DefaultParamMode {
CONSTANT,
@@ -88,7 +97,8 @@ class BindingsGenerator {
NULLABLE_REF
};
- StringName type;
+ TypeReference type;
+
String name;
String default_argument;
DefaultParamMode def_param_mode;
@@ -110,7 +120,7 @@ class BindingsGenerator {
/**
* [TypeInterface::name] of the return type
*/
- StringName return_type;
+ TypeReference return_type;
/**
* Determines if the method has a variable number of arguments (VarArg)
@@ -146,7 +156,7 @@ class BindingsGenerator {
}
MethodInterface() {
- return_type = BindingsGenerator::get_singleton()->name_cache.type_void;
+ return_type.cname = BindingsGenerator::get_singleton()->name_cache.type_void;
is_vararg = false;
is_virtual = false;
requires_object_call = false;
@@ -175,6 +185,7 @@ class BindingsGenerator {
ClassDB::APIType api_type;
+ bool is_enum;
bool is_object_type;
bool is_singleton;
bool is_reference;
@@ -276,7 +287,9 @@ class BindingsGenerator {
* One or more statements that determine how a variable of this type is returned from a method.
* It must contain the return statement(s).
* Formatting elements:
- * %0 or %s: name of the variable to be returned
+ * %0: internal method call statement
+ * %1: [cs_type] of the return type
+ * %2: [im_type_out] of the return type
*/
String cs_out;
@@ -293,8 +306,6 @@ class BindingsGenerator {
/**
* Type used for the return type of internal call methods.
- * If [cs_out] is not empty and the method return type is not void,
- * it is also used for the type of the return variable.
*/
String im_type_out;
@@ -379,10 +390,24 @@ class BindingsGenerator {
r_itype.im_type_out = r_itype.proxy_name;
}
+ static void postsetup_enum_type(TypeInterface &r_enum_itype) {
+ r_enum_itype.c_arg_in = "&%s";
+ r_enum_itype.c_type = "int";
+ r_enum_itype.c_type_in = "int";
+ r_enum_itype.c_type_out = "int";
+ r_enum_itype.cs_type = r_enum_itype.proxy_name;
+ r_enum_itype.cs_in = "(int)%s";
+ r_enum_itype.cs_out = "return (%1)%0;";
+ r_enum_itype.im_type_in = "int";
+ r_enum_itype.im_type_out = "int";
+ r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name];
+ }
+
TypeInterface() {
api_type = ClassDB::API_NONE;
+ is_enum = false;
is_object_type = false;
is_singleton = false;
is_reference = false;
@@ -492,6 +517,8 @@ class BindingsGenerator {
return "Ref";
else if (p_type.is_object_type)
return "Obj";
+ else if (p_type.is_enum)
+ return "int";
return p_type.name;
}
@@ -501,8 +528,8 @@ class BindingsGenerator {
void _generate_header_icalls();
void _generate_method_icalls(const TypeInterface &p_itype);
- const TypeInterface *_get_type_by_name_or_null(const StringName &p_cname);
- const TypeInterface *_get_type_by_name_or_placeholder(const StringName &p_cname);
+ const TypeInterface *_get_type_or_null(const TypeReference &p_typeref);
+ const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref);
void _default_argument_from_variant(const Variant &p_val, ArgumentInterface &r_iarg);
void _populate_builtin_type(TypeInterface &r_itype, Variant::Type vtype);
diff --git a/modules/mono/editor/godotsharp_builds.cpp b/modules/mono/editor/godotsharp_builds.cpp
index ad07f043b..2f2b5768d 100644
--- a/modules/mono/editor/godotsharp_builds.cpp
+++ b/modules/mono/editor/godotsharp_builds.cpp
@@ -525,11 +525,10 @@ void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
}
}
-GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) {
-
- build_info = p_build_info;
- build_tab = NULL;
- exit_callback = p_callback;
- exited = true;
- exit_code = -1;
+GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) :
+ build_info(p_build_info),
+ build_tab(NULL),
+ exit_callback(p_callback),
+ exited(true),
+ exit_code(-1) {
}
diff --git a/modules/mono/editor/mono_bottom_panel.cpp b/modules/mono/editor/mono_bottom_panel.cpp
index 32aec2a3b..1b5a30383 100644
--- a/modules/mono/editor/mono_bottom_panel.cpp
+++ b/modules/mono/editor/mono_bottom_panel.cpp
@@ -407,9 +407,14 @@ void MonoBuildTab::stop_build() {
void MonoBuildTab::_issue_activated(int p_idx) {
- ERR_FAIL_INDEX(p_idx, issues.size());
+ ERR_FAIL_INDEX(p_idx, issues_list->get_item_count());
- const BuildIssue &issue = issues[p_idx];
+ // Get correct issue idx from issue list
+ int issue_idx = this->issues_list->get_item_metadata(p_idx);
+
+ ERR_FAIL_INDEX(issue_idx, issues.size());
+
+ const BuildIssue &issue = issues[issue_idx];
if (issue.project_file.empty() && issue.file.empty())
return;
@@ -437,21 +442,16 @@ void MonoBuildTab::_bind_methods() {
ClassDB::bind_method("_issue_activated", &MonoBuildTab::_issue_activated);
}
-MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) {
-
- build_info = p_build_info;
- logs_dir = p_logs_dir;
-
- build_exited = false;
-
- issues_list = memnew(ItemList);
+MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) :
+ build_info(p_build_info),
+ logs_dir(p_logs_dir),
+ build_exited(false),
+ issues_list(memnew(ItemList)),
+ error_count(0),
+ warning_count(0),
+ errors_visible(true),
+ warnings_visible(true) {
issues_list->set_v_size_flags(SIZE_EXPAND_FILL);
issues_list->connect("item_activated", this, "_issue_activated");
add_child(issues_list);
-
- error_count = 0;
- warning_count = 0;
-
- errors_visible = true;
- warnings_visible = true;
}
diff --git a/modules/mono/glue/cs_files/AABB.cs b/modules/mono/glue/cs_files/AABB.cs
index 25458c93e..39f2d2ed5 100644
--- a/modules/mono/glue/cs_files/AABB.cs
+++ b/modules/mono/glue/cs_files/AABB.cs
@@ -1,12 +1,10 @@
-using System;
-
// file: core/math/aabb.h
// commit: 7ad14e7a3e6f87ddc450f7e34621eb5200808451
// file: core/math/aabb.cpp
// commit: bd282ff43f23fe845f29a3e25c8efc01bd65ffb0
// file: core/variant_call.cpp
// commit: 5ad9be4c24e9d7dc5672fdc42cea896622fe5685
-
+using System;
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
@@ -51,12 +49,12 @@ namespace Godot
Vector3 dst_min = with.position;
Vector3 dst_max = 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 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;
}
public AABB Expand(Vector3 to_point)
@@ -113,7 +111,7 @@ namespace Godot
public Vector3 GetLongestAxis()
{
- Vector3 axis = new Vector3(1f, 0f, 0f);
+ var axis = new Vector3(1f, 0f, 0f);
real_t max_size = size.x;
if (size.y > max_size)
@@ -125,7 +123,6 @@ namespace Godot
if (size.z > max_size)
{
axis = new Vector3(0f, 0f, 1f);
- max_size = size.z;
}
return axis;
@@ -133,7 +130,7 @@ namespace Godot
public Vector3.Axis GetLongestAxisIndex()
{
- Vector3.Axis axis = Vector3.Axis.X;
+ var axis = Vector3.Axis.X;
real_t max_size = size.x;
if (size.y > max_size)
@@ -145,7 +142,6 @@ namespace Godot
if (size.z > max_size)
{
axis = Vector3.Axis.Z;
- max_size = size.z;
}
return axis;
@@ -166,7 +162,7 @@ namespace Godot
public Vector3 GetShortestAxis()
{
- Vector3 axis = new Vector3(1f, 0f, 0f);
+ var axis = new Vector3(1f, 0f, 0f);
real_t max_size = size.x;
if (size.y < max_size)
@@ -178,7 +174,6 @@ namespace Godot
if (size.z < max_size)
{
axis = new Vector3(0f, 0f, 1f);
- max_size = size.z;
}
return axis;
@@ -186,7 +181,7 @@ namespace Godot
public Vector3.Axis GetShortestAxisIndex()
{
- Vector3.Axis axis = Vector3.Axis.X;
+ var axis = Vector3.Axis.X;
real_t max_size = size.x;
if (size.y < max_size)
@@ -198,7 +193,6 @@ namespace Godot
if (size.z < max_size)
{
axis = Vector3.Axis.Z;
- max_size = size.z;
}
return axis;
@@ -223,14 +217,14 @@ namespace Godot
Vector3 ofs = position + half_extents;
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 ? -half_extents.x : half_extents.x,
+ dir.y > 0f ? -half_extents.y : half_extents.y,
+ dir.z > 0f ? -half_extents.z : half_extents.z);
}
public AABB Grow(real_t by)
{
- AABB res = this;
+ var res = this;
res.position.x -= by;
res.position.y -= by;
@@ -283,48 +277,42 @@ namespace Godot
{
return new AABB();
}
- else
- {
- 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 = 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;
if (src_min.y > dst_max.y || src_max.y < dst_min.y)
{
return new AABB();
}
- else
- {
- 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 = 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;
if (src_min.z > dst_max.z || src_max.z < dst_min.z)
{
return new AABB();
}
- else
- {
- 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 = 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;
return new AABB(min, max - min);
}
public bool Intersects(AABB with)
{
- if (position.x >= (with.position.x + with.size.x))
+ if (position.x >= with.position.x + with.size.x)
return false;
- if ((position.x + size.x) <= with.position.x)
+ if (position.x + size.x <= with.position.x)
return false;
- if (position.y >= (with.position.y + with.size.y))
+ if (position.y >= with.position.y + with.size.y)
return false;
- if ((position.y + size.y) <= with.position.y)
+ if (position.y + size.y <= with.position.y)
return false;
- if (position.z >= (with.position.z + with.size.z))
+ if (position.z >= with.position.z + with.size.z)
return false;
- if ((position.z + size.z) <= with.position.z)
+ if (position.z + size.z <= with.position.z)
return false;
return true;
@@ -341,7 +329,7 @@ namespace Godot
new Vector3(position.x + size.x, position.y, position.z),
new Vector3(position.x + size.x, position.y, position.z + size.z),
new Vector3(position.x + size.x, position.y + size.y, position.z),
- new Vector3(position.x + size.x, position.y + size.y, position.z + size.z),
+ new Vector3(position.x + size.x, position.y + size.y, position.z + size.z)
};
bool over = false;
@@ -408,19 +396,19 @@ namespace Godot
{
Vector3 beg_1 = position;
Vector3 beg_2 = with.position;
- Vector3 end_1 = new Vector3(size.x, size.y, size.z) + beg_1;
- Vector3 end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2;
+ var end_1 = new Vector3(size.x, size.y, size.z) + beg_1;
+ var end_2 = new Vector3(with.size.x, with.size.y, with.size.z) + beg_2;
- Vector3 min = new Vector3(
- (beg_1.x < beg_2.x) ? beg_1.x : beg_2.x,
- (beg_1.y < beg_2.y) ? beg_1.y : beg_2.y,
- (beg_1.z < beg_2.z) ? beg_1.z : beg_2.z
+ var min = new Vector3(
+ beg_1.x < beg_2.x ? beg_1.x : beg_2.x,
+ beg_1.y < beg_2.y ? beg_1.y : beg_2.y,
+ beg_1.z < beg_2.z ? beg_1.z : beg_2.z
);
- Vector3 max = new Vector3(
- (end_1.x > end_2.x) ? end_1.x : end_2.x,
- (end_1.y > end_2.y) ? end_1.y : end_2.y,
- (end_1.z > end_2.z) ? end_1.z : end_2.z
+ var max = new Vector3(
+ end_1.x > end_2.x ? end_1.x : end_2.x,
+ end_1.y > end_2.y ? end_1.y : end_2.y,
+ end_1.z > end_2.z ? end_1.z : end_2.z
);
return new AABB(min, max - min);
@@ -467,8 +455,8 @@ namespace Godot
{
return String.Format("{0} - {1}", new object[]
{
- this.position.ToString(),
- this.size.ToString()
+ position.ToString(),
+ size.ToString()
});
}
@@ -476,8 +464,8 @@ namespace Godot
{
return String.Format("{0} - {1}", new object[]
{
- this.position.ToString(format),
- this.size.ToString(format)
+ position.ToString(format),
+ size.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Basis.cs b/modules/mono/glue/cs_files/Basis.cs
index 89b3e94c3..929b13d70 100644
--- a/modules/mono/glue/cs_files/Basis.cs
+++ b/modules/mono/glue/cs_files/Basis.cs
@@ -1,6 +1,5 @@
using System;
using System.Runtime.InteropServices;
-
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
@@ -19,8 +18,7 @@ namespace Godot
new Vector3(0f, 0f, 1f)
);
- private static readonly Basis[] orthoBases = new Basis[24]
- {
+ private static readonly Basis[] orthoBases = {
new Basis(1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f),
new Basis(0f, -1f, 0f, 1f, 0f, 0f, 0f, 0f, 1f),
new Basis(-1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f),
@@ -49,20 +47,20 @@ namespace Godot
public Vector3 x
{
- get => GetAxis(0);
- set => SetAxis(0, value);
+ get { return GetAxis(0); }
+ set { SetAxis(0, value); }
}
public Vector3 y
{
- get => GetAxis(1);
- set => SetAxis(1, value);
+ get { return GetAxis(1); }
+ set { SetAxis(1, value); }
}
public Vector3 z
{
- get => GetAxis(2);
- set => SetAxis(2, value);
+ get { return GetAxis(2); }
+ set { SetAxis(2, value); }
}
private Vector3 _x;
@@ -188,7 +186,7 @@ namespace Godot
public Vector3 GetEuler()
{
- Basis m = this.Orthonormalized();
+ Basis m = Orthonormalized();
Vector3 euler;
euler.z = 0.0f;
@@ -206,13 +204,13 @@ namespace Godot
}
else
{
- euler.x = Mathf.PI * 0.5f;
+ euler.x = Mathf.Pi * 0.5f;
euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]);
}
}
else
{
- euler.x = -Mathf.PI * 0.5f;
+ euler.x = -Mathf.Pi * 0.5f;
euler.y = -Mathf.Atan2(-m[0, 1], m[0, 0]);
}
@@ -221,7 +219,7 @@ namespace Godot
public int GetOrthogonalIndex()
{
- Basis orth = this;
+ var orth = this;
for (int i = 0; i < 3; i++)
{
@@ -251,10 +249,9 @@ namespace Godot
public Basis Inverse()
{
- Basis inv = this;
+ var inv = this;
- real_t[] co = new real_t[3]
- {
+ real_t[] co = {
inv[1, 1] * inv[2, 2] - inv[1, 2] * inv[2, 1],
inv[1, 2] * inv[2, 0] - inv[1, 0] * inv[2, 2],
inv[1, 0] * inv[2, 1] - inv[1, 1] * inv[2, 0]
@@ -297,12 +294,12 @@ namespace Godot
Vector3 zAxis = GetAxis(2);
xAxis.Normalize();
- yAxis = (yAxis - xAxis * (xAxis.Dot(yAxis)));
+ yAxis = yAxis - xAxis * xAxis.Dot(yAxis);
yAxis.Normalize();
- zAxis = (zAxis - xAxis * (xAxis.Dot(zAxis)) - yAxis * (yAxis.Dot(zAxis)));
+ zAxis = zAxis - xAxis * xAxis.Dot(zAxis) - yAxis * yAxis.Dot(zAxis);
zAxis.Normalize();
- return Basis.CreateFromAxes(xAxis, yAxis, zAxis);
+ return CreateFromAxes(xAxis, yAxis, zAxis);
}
public Basis Rotated(Vector3 axis, real_t phi)
@@ -312,7 +309,7 @@ namespace Godot
public Basis Scaled(Vector3 scale)
{
- Basis m = this;
+ var m = this;
m[0, 0] *= scale.x;
m[0, 1] *= scale.x;
@@ -344,7 +341,7 @@ namespace Godot
public Basis Transposed()
{
- Basis tr = this;
+ var tr = this;
real_t temp = this[0, 1];
this[0, 1] = this[1, 0];
@@ -375,9 +372,9 @@ namespace Godot
{
return new Vector3
(
- (this[0, 0] * v.x) + (this[1, 0] * v.y) + (this[2, 0] * v.z),
- (this[0, 1] * v.x) + (this[1, 1] * v.y) + (this[2, 1] * v.z),
- (this[0, 2] * v.x) + (this[1, 2] * v.y) + (this[2, 2] * v.z)
+ this[0, 0] * v.x + this[1, 0] * v.y + this[2, 0] * v.z,
+ this[0, 1] * v.x + this[1, 1] * v.y + this[2, 1] * v.z,
+ this[0, 2] * v.x + this[1, 2] * v.y + this[2, 2] * v.z
);
}
@@ -393,34 +390,38 @@ namespace Godot
(_y[0] - _x[1]) * inv_s,
s * 0.25f
);
- } else if (_x[0] > _y[1] && _x[0] > _z[2]) {
- real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f;
- real_t inv_s = 1f / s;
- return new Quat(
- s * 0.25f,
- (_x[1] + _y[0]) * inv_s,
- (_x[2] + _z[0]) * inv_s,
- (_z[1] - _y[2]) * inv_s
- );
- } else if (_y[1] > _z[2]) {
- real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f;
- real_t inv_s = 1f / s;
- return new Quat(
- (_x[1] + _y[0]) * inv_s,
- s * 0.25f,
- (_y[2] + _z[1]) * inv_s,
- (_x[2] - _z[0]) * inv_s
- );
- } else {
- real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f;
- real_t inv_s = 1f / s;
- return new Quat(
- (_x[2] + _z[0]) * inv_s,
- (_y[2] + _z[1]) * inv_s,
- s * 0.25f,
- (_y[0] - _x[1]) * inv_s
- );
}
+
+ if (_x[0] > _y[1] && _x[0] > _z[2]) {
+ real_t s = Mathf.Sqrt(_x[0] - _y[1] - _z[2] + 1.0f) * 2f;
+ real_t inv_s = 1f / s;
+ return new Quat(
+ s * 0.25f,
+ (_x[1] + _y[0]) * inv_s,
+ (_x[2] + _z[0]) * inv_s,
+ (_z[1] - _y[2]) * inv_s
+ );
+ }
+
+ if (_y[1] > _z[2]) {
+ real_t s = Mathf.Sqrt(-_x[0] + _y[1] - _z[2] + 1.0f) * 2f;
+ real_t inv_s = 1f / s;
+ return new Quat(
+ (_x[1] + _y[0]) * inv_s,
+ s * 0.25f,
+ (_y[2] + _z[1]) * inv_s,
+ (_x[2] - _z[0]) * inv_s
+ );
+ } else {
+ real_t s = Mathf.Sqrt(-_x[0] - _y[1] + _z[2] + 1.0f) * 2f;
+ real_t inv_s = 1f / s;
+ return new Quat(
+ (_x[2] + _z[0]) * inv_s,
+ (_y[2] + _z[1]) * inv_s,
+ s * 0.25f,
+ (_y[0] - _x[1]) * inv_s
+ );
+ }
}
public Basis(Quat quat)
@@ -440,33 +441,33 @@ namespace Godot
real_t yz = quat.y * zs;
real_t zz = quat.z * zs;
- this._x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy);
- this._y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx);
- this._z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy));
+ _x = new Vector3(1.0f - (yy + zz), xy - wz, xz + wy);
+ _y = new Vector3(xy + wz, 1.0f - (xx + zz), yz - wx);
+ _z = new Vector3(xz - wy, yz + wx, 1.0f - (xx + yy));
}
public Basis(Vector3 axis, real_t phi)
{
- Vector3 axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
+ var axis_sq = new Vector3(axis.x * axis.x, axis.y * axis.y, axis.z * axis.z);
- real_t cosine = Mathf.Cos( (real_t)phi);
- real_t sine = Mathf.Sin( (real_t)phi);
+ real_t cosine = Mathf.Cos( phi);
+ real_t sine = Mathf.Sin( phi);
- this._x = new Vector3
+ _x = new Vector3
(
axis_sq.x + cosine * (1.0f - axis_sq.x),
axis.x * axis.y * (1.0f - cosine) - axis.z * sine,
axis.z * axis.x * (1.0f - cosine) + axis.y * sine
);
- this._y = new Vector3
+ _y = new Vector3
(
axis.x * axis.y * (1.0f - cosine) + axis.z * sine,
axis_sq.y + cosine * (1.0f - axis_sq.y),
axis.y * axis.z * (1.0f - cosine) - axis.x * sine
);
- this._z = new Vector3
+ _z = new Vector3
(
axis.z * axis.x * (1.0f - cosine) - axis.y * sine,
axis.y * axis.z * (1.0f - cosine) + axis.x * sine,
@@ -476,16 +477,16 @@ namespace Godot
public Basis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis)
{
- this._x = xAxis;
- this._y = yAxis;
- this._z = zAxis;
+ _x = xAxis;
+ _y = yAxis;
+ _z = zAxis;
}
public Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz)
{
- this._x = new Vector3(xx, xy, xz);
- this._y = new Vector3(yx, yy, yz);
- this._z = new Vector3(zx, zy, zz);
+ _x = new Vector3(xx, xy, xz);
+ _y = new Vector3(yx, yy, yz);
+ _z = new Vector3(zx, zy, zz);
}
public static Basis operator *(Basis left, Basis right)
@@ -532,9 +533,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this._x.ToString(),
- this._y.ToString(),
- this._z.ToString()
+ _x.ToString(),
+ _y.ToString(),
+ _z.ToString()
});
}
@@ -542,9 +543,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this._x.ToString(format),
- this._y.ToString(format),
- this._z.ToString(format)
+ _x.ToString(format),
+ _y.ToString(format),
+ _z.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs
index aa42c487c..af94bb616 100644
--- a/modules/mono/glue/cs_files/Color.cs
+++ b/modules/mono/glue/cs_files/Color.cs
@@ -45,8 +45,8 @@ namespace Godot
{
get
{
- float max = (float) Mathf.Max(r, (float) Mathf.Max(g, b));
- float min = (float) Mathf.Min(r, (float) Mathf.Min(g, b));
+ float max = Math.Max(r, Math.Max(g, b));
+ float min = Math.Min(r, Math.Min(g, b));
float delta = max - min;
@@ -79,8 +79,8 @@ namespace Godot
{
get
{
- float max = (float) Mathf.Max(r, (float) Mathf.Max(g, b));
- float min = (float) Mathf.Min(r, (float) Mathf.Min(g, b));
+ float max = Math.Max(r, Math.Max(g, b));
+ float min = Math.Min(r, Math.Min(g, b));
float delta = max - min;
@@ -96,7 +96,7 @@ namespace Godot
{
get
{
- return (float) Mathf.Max(r, (float) Mathf.Max(g, b));
+ return Math.Max(r, Math.Max(g, b));
}
set
{
@@ -104,7 +104,7 @@ namespace Godot
}
}
- private static readonly Color black = new Color(0f, 0f, 0f, 1.0f);
+ private static readonly Color black = new Color(0f, 0f, 0f);
public Color Black
{
@@ -180,7 +180,7 @@ namespace Godot
hue += 1.0f;
}
- saturation = (max == 0) ? 0 : 1f - (1f * min / max);
+ saturation = max == 0 ? 0 : 1f - 1f * min / max;
value = max / 255f;
}
@@ -232,12 +232,10 @@ namespace Godot
{
return new Color(0, 0, 0, 0);
}
- else
- {
- 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;
}
@@ -265,14 +263,14 @@ namespace Godot
);
}
- public Color LinearInterpolate(Color b, float t)
+ public Color LinearInterpolate(Color c, float t)
{
- Color res = this;
+ var res = this;
- res.r += (t * (b.r - this.r));
- res.g += (t * (b.g - this.g));
- res.b += (t * (b.b - this.b));
- res.a += (t * (b.a - this.a));
+ res.r += t * (c.r - r);
+ res.g += t * (c.g - g);
+ res.b += t * (c.b - b);
+ res.a += t * (c.a - a);
return res;
}
@@ -305,7 +303,7 @@ namespace Godot
public string ToHtml(bool include_alpha = true)
{
- String txt = string.Empty;
+ var txt = string.Empty;
txt += _to_hex(r);
txt += _to_hex(g);
@@ -328,13 +326,13 @@ namespace Godot
public Color(int rgba)
{
- this.a = (rgba & 0xFF) / 255.0f;
+ a = (rgba & 0xFF) / 255.0f;
rgba >>= 8;
- this.b = (rgba & 0xFF) / 255.0f;
+ b = (rgba & 0xFF) / 255.0f;
rgba >>= 8;
- this.g = (rgba & 0xFF) / 255.0f;
+ g = (rgba & 0xFF) / 255.0f;
rgba >>= 8;
- this.r = (rgba & 0xFF) / 255.0f;
+ r = (rgba & 0xFF) / 255.0f;
}
private static int _parse_col(string str, int ofs)
@@ -344,7 +342,7 @@ namespace Godot
for (int i = 0; i < 2; i++)
{
int c = str[i + ofs];
- int v = 0;
+ int v;
if (c >= '0' && c <= '9')
{
@@ -376,9 +374,9 @@ namespace Godot
private String _to_hex(float val)
{
- int v = (int) Mathf.Clamp(val * 255.0f, 0, 255);
+ var v = (int) Mathf.Clamp(val * 255.0f, 0, 255);
- string ret = string.Empty;
+ var ret = string.Empty;
for (int i = 0; i < 2; i++)
{
@@ -405,7 +403,7 @@ namespace Godot
if (color[0] == '#')
color = color.Substring(1, color.Length - 1);
- bool alpha = false;
+ bool alpha;
if (color.Length == 8)
alpha = true;
@@ -434,7 +432,7 @@ namespace Godot
public static Color Color8(byte r8, byte g8, byte b8, byte a8)
{
- return new Color((float)r8 / 255f, (float)g8 / 255f, (float)b8 / 255f, (float)a8 / 255f);
+ return new Color(r8 / 255f, g8 / 255f, b8 / 255f, a8 / 255f);
}
public Color(string rgba)
@@ -451,7 +449,7 @@ namespace Godot
if (rgba[0] == '#')
rgba = rgba.Substring(1);
- bool alpha = false;
+ bool alpha;
if (rgba.Length == 8)
{
@@ -513,14 +511,11 @@ namespace Godot
if (left.g == right.g)
{
if (left.b == right.b)
- return (left.a < right.a);
- else
- return (left.b < right.b);
- }
- else
- {
- return left.g < right.g;
+ return left.a < right.a;
+ return left.b < right.b;
}
+
+ return left.g < right.g;
}
return left.r < right.r;
@@ -533,14 +528,11 @@ namespace Godot
if (left.g == right.g)
{
if (left.b == right.b)
- return (left.a > right.a);
- else
- return (left.b > right.b);
- }
- else
- {
- return left.g > right.g;
+ return left.a > right.a;
+ return left.b > right.b;
}
+
+ return left.g > right.g;
}
return left.r > right.r;
@@ -568,24 +560,12 @@ namespace Godot
public override string ToString()
{
- return String.Format("{0},{1},{2},{3}", new object[]
- {
- this.r.ToString(),
- this.g.ToString(),
- this.b.ToString(),
- this.a.ToString()
- });
+ return String.Format("{0},{1},{2},{3}", r.ToString(), g.ToString(), b.ToString(), a.ToString());
}
public string ToString(string format)
{
- return String.Format("{0},{1},{2},{3}", new object[]
- {
- this.r.ToString(format),
- this.g.ToString(format),
- this.b.ToString(format),
- this.a.ToString(format)
- });
+ return String.Format("{0},{1},{2},{3}", r.ToString(format), g.ToString(format), b.ToString(format), a.ToString(format));
}
}
}
diff --git a/modules/mono/glue/cs_files/DebuggingUtils.cs b/modules/mono/glue/cs_files/DebuggingUtils.cs
index ffaaf0083..b27816084 100644
--- a/modules/mono/glue/cs_files/DebuggingUtils.cs
+++ b/modules/mono/glue/cs_files/DebuggingUtils.cs
@@ -14,7 +14,7 @@ namespace Godot
else if (type == typeof(void))
sb.Append("void");
else
- sb.Append(type.ToString());
+ sb.Append(type);
sb.Append(" ");
}
@@ -32,7 +32,7 @@ namespace Godot
return;
}
- StringBuilder sb = new StringBuilder();
+ var sb = new StringBuilder();
if (methodBase is MethodInfo)
sb.AppendTypeName(((MethodInfo)methodBase).ReturnType);
diff --git a/modules/mono/glue/cs_files/GD.cs b/modules/mono/glue/cs_files/GD.cs
index 1ee7e7d21..ec1534cb9 100644
--- a/modules/mono/glue/cs_files/GD.cs
+++ b/modules/mono/glue/cs_files/GD.cs
@@ -91,7 +91,7 @@ namespace Godot
public static int[] Range(int length)
{
- int[] ret = new int[length];
+ var ret = new int[length];
for (int i = 0; i < length; i++)
{
@@ -106,7 +106,7 @@ namespace Godot
if (to < from)
return new int[0];
- int[] ret = new int[to - from];
+ var ret = new int[to - from];
for (int i = from; i < to; i++)
{
@@ -124,14 +124,14 @@ namespace Godot
return new int[0];
// Calculate count
- int count = 0;
+ int count;
if (increment > 0)
- count = ((to - from - 1) / increment) + 1;
+ count = (to - from - 1) / increment + 1;
else
- count = ((from - to - 1) / -increment) + 1;
+ count = (from - to - 1) / -increment + 1;
- int[] ret = new int[count];
+ var ret = new int[count];
if (increment > 0)
{
diff --git a/modules/mono/glue/cs_files/GodotMethodAttribute.cs b/modules/mono/glue/cs_files/GodotMethodAttribute.cs
index 21333c8da..55848769d 100644
--- a/modules/mono/glue/cs_files/GodotMethodAttribute.cs
+++ b/modules/mono/glue/cs_files/GodotMethodAttribute.cs
@@ -2,7 +2,7 @@ using System;
namespace Godot
{
- [AttributeUsage(AttributeTargets.Method, Inherited = true)]
+ [AttributeUsage(AttributeTargets.Method)]
internal class GodotMethodAttribute : Attribute
{
private string methodName;
diff --git a/modules/mono/glue/cs_files/GodotSynchronizationContext.cs b/modules/mono/glue/cs_files/GodotSynchronizationContext.cs
index eb4d0bed1..da3c7bac8 100644
--- a/modules/mono/glue/cs_files/GodotSynchronizationContext.cs
+++ b/modules/mono/glue/cs_files/GodotSynchronizationContext.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
diff --git a/modules/mono/glue/cs_files/GodotTaskScheduler.cs b/modules/mono/glue/cs_files/GodotTaskScheduler.cs
index f587645a4..6bf25a89d 100644
--- a/modules/mono/glue/cs_files/GodotTaskScheduler.cs
+++ b/modules/mono/glue/cs_files/GodotTaskScheduler.cs
@@ -36,7 +36,7 @@ namespace Godot
TryDequeue(task);
}
- return base.TryExecuteTask(task);
+ return TryExecuteTask(task);
}
protected sealed override bool TryDequeue(Task task)
diff --git a/modules/mono/glue/cs_files/IAwaiter.cs b/modules/mono/glue/cs_files/IAwaiter.cs
index 73c71b563..b5aa1a538 100644
--- a/modules/mono/glue/cs_files/IAwaiter.cs
+++ b/modules/mono/glue/cs_files/IAwaiter.cs
@@ -1,4 +1,3 @@
-using System;
using System.Runtime.CompilerServices;
namespace Godot
diff --git a/modules/mono/glue/cs_files/MarshalUtils.cs b/modules/mono/glue/cs_files/MarshalUtils.cs
index 2bdfb95c5..ff4477cc6 100644
--- a/modules/mono/glue/cs_files/MarshalUtils.cs
+++ b/modules/mono/glue/cs_files/MarshalUtils.cs
@@ -7,7 +7,7 @@ namespace Godot
{
private static Dictionary<object, object> ArraysToDictionary(object[] keys, object[] values)
{
- Dictionary<object, object> ret = new Dictionary<object, object>();
+ var ret = new Dictionary<object, object>();
for (int i = 0; i < keys.Length; i++)
{
@@ -19,11 +19,11 @@ namespace Godot
private static void DictionaryToArrays(Dictionary<object, object> from, out object[] keysTo, out object[] valuesTo)
{
- Dictionary<object, object>.KeyCollection keys = from.Keys;
+ var keys = from.Keys;
keysTo = new object[keys.Count];
keys.CopyTo(keysTo, 0);
- Dictionary<object, object>.ValueCollection values = from.Values;
+ var values = from.Values;
valuesTo = new object[values.Count];
values.CopyTo(valuesTo, 0);
}
diff --git a/modules/mono/glue/cs_files/Mathf.cs b/modules/mono/glue/cs_files/Mathf.cs
index 8b9c264d0..0d20a1256 100644
--- a/modules/mono/glue/cs_files/Mathf.cs
+++ b/modules/mono/glue/cs_files/Mathf.cs
@@ -1,5 +1,4 @@
using System;
-
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
@@ -8,16 +7,14 @@ using real_t = System.Single;
namespace Godot
{
- public static class Mathf
+ public static partial class Mathf
{
- // Define constants with Decimal precision and cast down to double or float.
- public const real_t PI = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979
+ // Define constants with Decimal precision and cast down to double or float.
- #if REAL_T_IS_DOUBLE
- public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used.
- #else
- public const real_t Epsilon = 1e-06f;
- #endif
+ public const real_t Tau = (real_t) 6.2831853071795864769252867666M; // 6.2831855f and 6.28318530717959
+ public const real_t Pi = (real_t) 3.1415926535897932384626433833M; // 3.1415927f and 3.14159265358979
+ public const real_t Inf = real_t.PositiveInfinity;
+ 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
@@ -27,6 +24,11 @@ namespace Godot
return Math.Abs(s);
}
+ public static int Abs(int s)
+ {
+ return Math.Abs(s);
+ }
+
public static real_t Acos(real_t s)
{
return (real_t)Math.Acos(s);
@@ -57,18 +59,14 @@ namespace Godot
return (real_t)Math.Ceiling(s);
}
- public static real_t Clamp(real_t val, real_t min, real_t max)
+ public static int Clamp(int value, int min, int max)
{
- if (val < min)
- {
- return min;
- }
- else if (val > max)
- {
- return max;
- }
+ return value < min ? min : value > max ? max : value;
+ }
- return val;
+ public static real_t Clamp(real_t value, real_t min, real_t max)
+ {
+ return value < min ? min : value > max ? max : value;
}
public static real_t Cos(real_t s)
@@ -116,7 +114,8 @@ namespace Godot
return Pow(s, curve);
}
- else if (curve < 0f)
+
+ if (curve < 0f)
{
if (s < 0.5f)
{
@@ -145,10 +144,23 @@ namespace Godot
{
return x % y;
}
- else
- {
- return y - (-x % y);
- }
+
+ return y - -x % y;
+ }
+
+ public static real_t InverseLerp(real_t from, real_t to, real_t weight)
+ {
+ return (Clamp(weight, 0f, 1f) - from) / (to - from);
+ }
+
+ public static bool IsInf(real_t s)
+ {
+ return real_t.IsInfinity(s);
+ }
+
+ public static bool IsNaN(real_t s)
+ {
+ return real_t.IsNaN(s);
}
public static real_t Lerp(real_t from, real_t to, real_t weight)
@@ -163,34 +175,34 @@ namespace Godot
public static int Max(int a, int b)
{
- return (a > b) ? a : b;
+ return a > b ? a : b;
}
public static real_t Max(real_t a, real_t b)
{
- return (a > b) ? a : b;
+ return a > b ? a : b;
}
public static int Min(int a, int b)
{
- return (a < b) ? a : b;
+ return a < b ? a : b;
}
public static real_t Min(real_t a, real_t b)
{
- return (a < b) ? a : b;
+ return a < b ? a : b;
}
- public static int NearestPo2(int val)
+ public static int NearestPo2(int value)
{
- val--;
- val |= val >> 1;
- val |= val >> 2;
- val |= val >> 4;
- val |= val >> 8;
- val |= val >> 16;
- val++;
- return val;
+ value--;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ value++;
+ return value;
}
public static Vector2 Polar2Cartesian(real_t r, real_t th)
@@ -213,14 +225,14 @@ namespace Godot
return (real_t)Math.Round(s);
}
- public static int RoundToInt(real_t s)
+ public static int Sign(int s)
{
- return (int)Math.Round(s);
+ return s < 0 ? -1 : 1;
}
public static real_t Sign(real_t s)
{
- return (s < 0f) ? -1f : 1f;
+ return s < 0f ? -1f : 1f;
}
public static real_t Sin(real_t s)
@@ -257,5 +269,17 @@ namespace Godot
{
return (real_t)Math.Tanh(s);
}
+
+ public static int Wrap(int value, int min, int max)
+ {
+ int rng = max - min;
+ return min + ((value - min) % rng + rng) % rng;
+ }
+
+ public static real_t Wrap(real_t value, real_t min, real_t max)
+ {
+ real_t rng = max - min;
+ return min + ((value - min) % rng + rng) % rng;
+ }
}
}
diff --git a/modules/mono/glue/cs_files/MathfEx.cs b/modules/mono/glue/cs_files/MathfEx.cs
new file mode 100644
index 000000000..739b7fb56
--- /dev/null
+++ b/modules/mono/glue/cs_files/MathfEx.cs
@@ -0,0 +1,39 @@
+using System;
+
+#if REAL_T_IS_DOUBLE
+using real_t = System.Double;
+#else
+using real_t = System.Single;
+#endif
+
+namespace Godot
+{
+ public static partial class Mathf
+ {
+ // Define constants with Decimal precision and cast down to double or float.
+
+ public const real_t E = (real_t) 2.7182818284590452353602874714M; // 2.7182817f and 2.718281828459045
+ public const real_t Sqrt2 = (real_t) 1.4142135623730950488016887242M; // 1.4142136f and 1.414213562373095
+
+#if REAL_T_IS_DOUBLE
+ public const real_t Epsilon = 1e-14; // Epsilon size should depend on the precision used.
+#else
+ public const real_t Epsilon = 1e-06f;
+#endif
+
+ public static int CeilToInt(real_t s)
+ {
+ return (int)Math.Ceiling(s);
+ }
+
+ public static int FloorToInt(real_t s)
+ {
+ return (int)Math.Floor(s);
+ }
+
+ public static int RoundToInt(real_t s)
+ {
+ return (int)Math.Round(s);
+ }
+ }
+} \ No newline at end of file
diff --git a/modules/mono/glue/cs_files/Plane.cs b/modules/mono/glue/cs_files/Plane.cs
index 0f74f3b66..8b9252202 100644
--- a/modules/mono/glue/cs_files/Plane.cs
+++ b/modules/mono/glue/cs_files/Plane.cs
@@ -1,5 +1,4 @@
using System;
-
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
@@ -81,9 +80,9 @@ namespace Godot
if (Mathf.Abs(denom) <= Mathf.Epsilon)
return new Vector3();
- Vector3 result = (b.normal.Cross(c.normal) * this.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;
}
@@ -114,7 +113,7 @@ namespace Godot
real_t dist = (normal.Dot(begin) - d) / den;
- if (dist < -Mathf.Epsilon || dist > (1.0f + Mathf.Epsilon))
+ if (dist < -Mathf.Epsilon || dist > 1.0f + Mathf.Epsilon)
return new Vector3();
return begin + segment * -dist;
@@ -198,8 +197,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- this.normal.ToString(),
- this.d.ToString()
+ normal.ToString(),
+ d.ToString()
});
}
@@ -207,8 +206,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- this.normal.ToString(format),
- this.d.ToString(format)
+ normal.ToString(format),
+ d.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Quat.cs b/modules/mono/glue/cs_files/Quat.cs
index 0cf3e00dd..c69c55d99 100644
--- a/modules/mono/glue/cs_files/Quat.cs
+++ b/modules/mono/glue/cs_files/Quat.cs
@@ -1,6 +1,5 @@
using System;
using System.Runtime.InteropServices;
-
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
@@ -106,10 +105,10 @@ namespace Godot
}
public void Set(Quat q)
{
- this.x = q.x;
- this.y = q.y;
- this.z = q.z;
- this.w = q.w;
+ x = q.x;
+ y = q.y;
+ z = q.z;
+ w = q.w;
}
public Quat Slerp(Quat b, real_t t)
@@ -117,7 +116,7 @@ namespace Godot
// Calculate cosine
real_t cosom = x * b.x + y * b.y + z * b.z + w * b.w;
- real_t[] to1 = new real_t[4];
+ var to1 = new real_t[4];
// Adjust signs if necessary
if (cosom < 0.0)
@@ -138,7 +137,7 @@ namespace Godot
real_t sinom, scale0, scale1;
// Calculate coefficients
- if ((1.0 - cosom) > Mathf.Epsilon)
+ if (1.0 - cosom > Mathf.Epsilon)
{
// Standard case (Slerp)
real_t omega = Mathf.Acos(cosom);
@@ -165,7 +164,7 @@ namespace Godot
public Quat Slerpni(Quat b, real_t t)
{
- real_t dot = this.Dot(b);
+ real_t dot = Dot(b);
if (Mathf.Abs(dot) > 0.9999f)
{
@@ -179,17 +178,17 @@ namespace Godot
return new Quat
(
- invFactor * this.x + newFactor * b.x,
- invFactor * this.y + newFactor * b.y,
- invFactor * this.z + newFactor * b.z,
- invFactor * this.w + newFactor * b.w
+ invFactor * x + newFactor * b.x,
+ invFactor * y + newFactor * b.y,
+ invFactor * z + newFactor * b.z,
+ invFactor * w + newFactor * b.w
);
}
public Vector3 Xform(Vector3 v)
{
Quat q = this * v;
- q *= this.Inverse();
+ q *= Inverse();
return new Vector3(q.x, q.y, q.z);
}
@@ -203,10 +202,10 @@ namespace Godot
}
public Quat(Quat q)
{
- this.x = q.x;
- this.y = q.y;
- this.z = q.z;
- this.w = q.w;
+ x = q.x;
+ y = q.y;
+ z = q.z;
+ w = q.w;
}
public Quat(Vector3 axis, real_t angle)
@@ -327,24 +326,12 @@ namespace Godot
public override string ToString()
{
- return String.Format("({0}, {1}, {2}, {3})", new object[]
- {
- this.x.ToString(),
- this.y.ToString(),
- this.z.ToString(),
- this.w.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})", new object[]
- {
- this.x.ToString(format),
- this.y.ToString(format),
- this.z.ToString(format),
- this.w.ToString(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/cs_files/Rect2.cs b/modules/mono/glue/cs_files/Rect2.cs
index decee35f8..6f1665657 100644
--- a/modules/mono/glue/cs_files/Rect2.cs
+++ b/modules/mono/glue/cs_files/Rect2.cs
@@ -1,6 +1,5 @@
using System;
using System.Runtime.InteropServices;
-
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
@@ -39,7 +38,7 @@ namespace Godot
public Rect2 Clip(Rect2 b)
{
- Rect2 newRect = b;
+ var newRect = b;
if (!Intersects(newRect))
return new Rect2();
@@ -58,14 +57,14 @@ namespace Godot
public bool Encloses(Rect2 b)
{
- return (b.position.x >= position.x) && (b.position.y >= position.y) &&
- ((b.position.x + b.size.x) < (position.x + size.x)) &&
- ((b.position.y + b.size.y) < (position.y + size.y));
+ return b.position.x >= position.x && b.position.y >= position.y &&
+ b.position.x + b.size.x < position.x + size.x &&
+ b.position.y + b.size.y < position.y + size.y;
}
public Rect2 Expand(Vector2 to)
{
- Rect2 expanded = this;
+ var expanded = this;
Vector2 begin = expanded.position;
Vector2 end = expanded.position + expanded.size;
@@ -93,7 +92,7 @@ namespace Godot
public Rect2 Grow(real_t by)
{
- Rect2 g = this;
+ var g = this;
g.position.x -= by;
g.position.y -= by;
@@ -105,7 +104,7 @@ namespace Godot
public Rect2 GrowIndividual(real_t left, real_t top, real_t right, real_t bottom)
{
- Rect2 g = this;
+ var g = this;
g.position.x -= left;
g.position.y -= top;
@@ -117,12 +116,12 @@ namespace Godot
public Rect2 GrowMargin(Margin margin, real_t by)
{
- Rect2 g = this;
+ var g = this;
- g.GrowIndividual((Margin.Left == margin) ? by : 0,
- (Margin.Top == margin) ? by : 0,
- (Margin.Right == margin) ? by : 0,
- (Margin.Bottom == margin) ? by : 0);
+ g.GrowIndividual(Margin.Left == margin ? by : 0,
+ Margin.Top == margin ? by : 0,
+ Margin.Right == margin ? by : 0,
+ Margin.Bottom == margin ? by : 0);
return g;
}
@@ -139,9 +138,9 @@ namespace Godot
if (point.y < position.y)
return false;
- if (point.x >= (position.x + size.x))
+ if (point.x >= position.x + size.x)
return false;
- if (point.y >= (position.y + size.y))
+ if (point.y >= position.y + size.y)
return false;
return true;
@@ -149,13 +148,13 @@ namespace Godot
public bool Intersects(Rect2 b)
{
- if (position.x > (b.position.x + b.size.x))
+ if (position.x > b.position.x + b.size.x)
return false;
- if ((position.x + size.x) < b.position.x)
+ if (position.x + size.x < b.position.x)
return false;
- if (position.y > (b.position.y + b.size.y))
+ if (position.y > b.position.y + b.size.y)
return false;
- if ((position.y + size.y) < b.position.y)
+ if (position.y + size.y < b.position.y)
return false;
return true;
@@ -185,17 +184,17 @@ namespace Godot
public Rect2(Vector2 position, real_t width, real_t height)
{
this.position = position;
- this.size = new Vector2(width, height);
+ size = new Vector2(width, height);
}
public Rect2(real_t x, real_t y, Vector2 size)
{
- this.position = new Vector2(x, y);
+ position = new Vector2(x, y);
this.size = size;
}
public Rect2(real_t x, real_t y, real_t width, real_t height)
{
- this.position = new Vector2(x, y);
- this.size = new Vector2(width, height);
+ position = new Vector2(x, y);
+ size = new Vector2(width, height);
}
public static bool operator ==(Rect2 left, Rect2 right)
@@ -232,8 +231,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- this.position.ToString(),
- this.size.ToString()
+ position.ToString(),
+ size.ToString()
});
}
@@ -241,8 +240,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- this.position.ToString(format),
- this.size.ToString(format)
+ position.ToString(format),
+ size.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/SignalAttribute.cs b/modules/mono/glue/cs_files/SignalAttribute.cs
index d8a6cabb8..3957387be 100644
--- a/modules/mono/glue/cs_files/SignalAttribute.cs
+++ b/modules/mono/glue/cs_files/SignalAttribute.cs
@@ -5,8 +5,5 @@ namespace Godot
[AttributeUsage(AttributeTargets.Delegate)]
public class SignalAttribute : Attribute
{
- public SignalAttribute()
- {
- }
}
}
diff --git a/modules/mono/glue/cs_files/SignalAwaiter.cs b/modules/mono/glue/cs_files/SignalAwaiter.cs
index 19ccc26e7..c06f6b05c 100644
--- a/modules/mono/glue/cs_files/SignalAwaiter.cs
+++ b/modules/mono/glue/cs_files/SignalAwaiter.cs
@@ -4,15 +4,15 @@ namespace Godot
{
public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]>
{
- private bool completed = false;
- private object[] result = null;
- private Action action = null;
+ private bool completed;
+ private object[] result;
+ private Action action;
- public SignalAwaiter(Godot.Object source, string signal, Godot.Object target)
+ public SignalAwaiter(Object source, string signal, Object target)
{
NativeCalls.godot_icall_Object_connect_signal_awaiter(
- Godot.Object.GetPtr(source),
- signal, Godot.Object.GetPtr(target), this
+ Object.GetPtr(source),
+ signal, Object.GetPtr(target), this
);
}
diff --git a/modules/mono/glue/cs_files/StringExtensions.cs b/modules/mono/glue/cs_files/StringExtensions.cs
index cbc337ab1..21090fb68 100644
--- a/modules/mono/glue/cs_files/StringExtensions.cs
+++ b/modules/mono/glue/cs_files/StringExtensions.cs
@@ -1,4 +1,5 @@
//using System;
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -43,11 +44,9 @@ namespace Godot
{
return instance.Substring(prev, i - prev);
}
- else
- {
- count++;
- prev = i + 1;
- }
+
+ count++;
+ prev = i + 1;
}
i++;
@@ -83,7 +82,7 @@ namespace Godot
// </summary>
public static string[] Bigrams(this string instance)
{
- string[] b = new string[instance.Length - 1];
+ var b = new string[instance.Length - 1];
for (int i = 0; i < b.Length; i++)
{
@@ -98,7 +97,7 @@ namespace Godot
// </summary>
public static string CEscape(this string instance)
{
- StringBuilder sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(string.Copy(instance));
sb.Replace("\\", "\\\\");
sb.Replace("\a", "\\a");
@@ -120,7 +119,7 @@ namespace Godot
// </summary>
public static string CUnescape(this string instance)
{
- StringBuilder sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(string.Copy(instance));
sb.Replace("\\a", "\a");
sb.Replace("\\b", "\b");
@@ -143,7 +142,7 @@ namespace Godot
public static string Capitalize(this string instance)
{
string aux = instance.Replace("_", " ").ToLower();
- string cap = string.Empty;
+ var cap = string.Empty;
for (int i = 0; i < aux.GetSliceCount(" "); i++)
{
@@ -178,13 +177,13 @@ namespace Godot
{
if (to[to_idx] == 0 && instance[instance_idx] == 0)
return 0; // We're equal
- else if (instance[instance_idx] == 0)
+ if (instance[instance_idx] == 0)
return -1; // If this is empty, and the other one is not, then we're less... I think?
- else if (to[to_idx] == 0)
+ if (to[to_idx] == 0)
return 1; // Otherwise the other one is smaller...
- else if (instance[instance_idx] < to[to_idx]) // More than
+ if (instance[instance_idx] < to[to_idx]) // More than
return -1;
- else if (instance[instance_idx] > to[to_idx]) // Less than
+ if (instance[instance_idx] > to[to_idx]) // Less than
return 1;
instance_idx++;
@@ -260,12 +259,12 @@ namespace Godot
{
int basepos = instance.Find("://");
- string rs = string.Empty;
- string @base = string.Empty;
+ string rs;
+ var @base = string.Empty;
if (basepos != -1)
{
- int end = basepos + 3;
+ var end = basepos + 3;
rs = instance.Substring(end, instance.Length);
@base = instance.Substring(0, end);
}
@@ -312,8 +311,8 @@ namespace Godot
int hashv = 5381;
int c;
- while ((c = (int)instance[index++]) != 0)
- hashv = ((hashv << 5) + hashv) + c; // hash * 33 + c
+ while ((c = instance[index++]) != 0)
+ hashv = (hashv << 5) + hashv + c; // hash * 33 + c
return hashv;
}
@@ -379,7 +378,7 @@ namespace Godot
while (instance[src] != 0 && text[tgt] != 0)
{
- bool match = false;
+ bool match;
if (case_insensitive)
{
@@ -455,7 +454,10 @@ namespace Godot
return false; // Don't start with number plz
}
- bool valid_char = (instance[i] >= '0' && instance[i] <= '9') || (instance[i] >= 'a' && instance[i] <= 'z') || (instance[i] >= 'A' && instance[i] <= 'Z') || instance[i] == '_';
+ bool valid_char = instance[i] >= '0' &&
+ instance[i] <= '9' || instance[i] >= 'a' &&
+ instance[i] <= 'z' || instance[i] >= 'A' &&
+ instance[i] <= 'Z' || instance[i] == '_';
if (!valid_char)
return false;
@@ -502,7 +504,7 @@ namespace Godot
// </summary>
public static string JsonEscape(this string instance)
{
- StringBuilder sb = new StringBuilder(string.Copy(instance));
+ var sb = new StringBuilder(string.Copy(instance));
sb.Replace("\\", "\\\\");
sb.Replace("\b", "\\b");
@@ -551,7 +553,7 @@ namespace Godot
case '\0':
return instance[0] == 0;
case '*':
- return ExprMatch(expr + 1, instance, caseSensitive) || (instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive));
+ return ExprMatch(expr + 1, instance, caseSensitive) || instance[0] != 0 && ExprMatch(expr, instance + 1, caseSensitive);
case '?':
return instance[0] != 0 && instance[0] != '.' && ExprMatch(expr + 1, instance + 1, caseSensitive);
default:
@@ -610,13 +612,13 @@ namespace Godot
{
if (to[to_idx] == 0 && instance[instance_idx] == 0)
return 0; // We're equal
- else if (instance[instance_idx] == 0)
+ if (instance[instance_idx] == 0)
return -1; // If this is empty, and the other one is not, then we're less... I think?
- else if (to[to_idx] == 0)
+ if (to[to_idx] == 0)
return 1; // Otherwise the other one is smaller..
- else if (char.ToUpper(instance[instance_idx]) < char.ToUpper(to[to_idx])) // More than
+ if (char.ToUpper(instance[instance_idx]) < char.ToUpper(to[to_idx])) // More than
return -1;
- else if (char.ToUpper(instance[instance_idx]) > char.ToUpper(to[to_idx])) // Less than
+ if (char.ToUpper(instance[instance_idx]) > char.ToUpper(to[to_idx])) // Less than
return 1;
instance_idx++;
@@ -724,8 +726,7 @@ namespace Godot
{
if (instance.Length > 0 && instance[instance.Length - 1] == '/')
return instance + file;
- else
- return instance + "/" + file;
+ return instance + "/" + file;
}
// <summary>
@@ -771,7 +772,7 @@ namespace Godot
if (pos < 0)
return string.Empty;
- return instance.Substring(pos, (instance.Length - pos));
+ return instance.Substring(pos, instance.Length - pos);
}
public static byte[] Sha256Buffer(this string instance)
@@ -824,7 +825,7 @@ namespace Godot
}
}
- return (2.0f * inter) / sum;
+ return 2.0f * inter / sum;
}
// <summary>
@@ -832,7 +833,7 @@ namespace Godot
// </summary>
public static string[] Split(this string instance, string divisor, bool allow_empty = true)
{
- return instance.Split(new string[] { divisor }, StringSplitOptions.RemoveEmptyEntries);
+ return instance.Split(new[] { divisor }, StringSplitOptions.RemoveEmptyEntries);
}
// <summary>
@@ -840,7 +841,7 @@ namespace Godot
// </summary>
public static float[] SplitFloats(this string instance, string divisor, bool allow_empty = true)
{
- List<float> ret = new List<float>();
+ var ret = new List<float>();
int from = 0;
int len = instance.Length;
@@ -849,7 +850,7 @@ namespace Godot
int end = instance.Find(divisor, from);
if (end < 0)
end = len;
- if (allow_empty || (end > from))
+ if (allow_empty || end > from)
ret.Add(float.Parse(instance.Substring(from)));
if (end == len)
break;
@@ -878,13 +879,10 @@ namespace Godot
{
if (right)
return instance.Trim(non_printable);
- else
- return instance.TrimStart(non_printable);
- }
- else
- {
- return instance.TrimEnd(non_printable);
+ return instance.TrimStart(non_printable);
}
+
+ return instance.TrimEnd(non_printable);
}
// <summary>
diff --git a/modules/mono/glue/cs_files/Transform.cs b/modules/mono/glue/cs_files/Transform.cs
index ce26c6070..d1b247a55 100644
--- a/modules/mono/glue/cs_files/Transform.cs
+++ b/modules/mono/glue/cs_files/Transform.cs
@@ -1,6 +1,5 @@
using System;
using System.Runtime.InteropServices;
-
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
@@ -29,7 +28,7 @@ namespace Godot
public Transform LookingAt(Vector3 target, Vector3 up)
{
- Transform t = this;
+ var t = this;
t.SetLookAt(origin, target, up);
return t;
}
@@ -98,22 +97,22 @@ namespace Godot
return new Vector3
(
- (basis[0, 0] * vInv.x) + (basis[1, 0] * vInv.y) + (basis[2, 0] * vInv.z),
- (basis[0, 1] * vInv.x) + (basis[1, 1] * vInv.y) + (basis[2, 1] * vInv.z),
- (basis[0, 2] * vInv.x) + (basis[1, 2] * vInv.y) + (basis[2, 2] * vInv.z)
+ basis[0, 0] * vInv.x + basis[1, 0] * vInv.y + basis[2, 0] * vInv.z,
+ basis[0, 1] * vInv.x + basis[1, 1] * vInv.y + basis[2, 1] * vInv.z,
+ basis[0, 2] * vInv.x + basis[1, 2] * vInv.y + basis[2, 2] * vInv.z
);
}
// Constructors
public Transform(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis, Vector3 origin)
{
- this.basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis);
+ basis = Basis.CreateFromAxes(xAxis, yAxis, zAxis);
this.origin = origin;
}
public Transform(Quat quat, Vector3 origin)
{
- this.basis = new Basis(quat);
+ basis = new Basis(quat);
this.origin = origin;
}
@@ -164,8 +163,8 @@ namespace Godot
{
return String.Format("{0} - {1}", new object[]
{
- this.basis.ToString(),
- this.origin.ToString()
+ basis.ToString(),
+ origin.ToString()
});
}
@@ -173,8 +172,8 @@ namespace Godot
{
return String.Format("{0} - {1}", new object[]
{
- this.basis.ToString(format),
- this.origin.ToString(format)
+ basis.ToString(format),
+ origin.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Transform2D.cs b/modules/mono/glue/cs_files/Transform2D.cs
index 836cca129..ff5259178 100644
--- a/modules/mono/glue/cs_files/Transform2D.cs
+++ b/modules/mono/glue/cs_files/Transform2D.cs
@@ -1,6 +1,5 @@
using System;
using System.Runtime.InteropServices;
-
#if REAL_T_IS_DOUBLE
using real_t = System.Double;
#else
@@ -111,7 +110,7 @@ namespace Godot
public Transform2D AffineInverse()
{
- Transform2D inv = this;
+ var inv = this;
real_t det = this[0, 0] * this[1, 1] - this[1, 0] * this[0, 1];
@@ -158,15 +157,15 @@ namespace Godot
Vector2 s2 = m.Scale;
// Slerp rotation
- Vector2 v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1));
- Vector2 v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2));
+ var v1 = new Vector2(Mathf.Cos(r1), Mathf.Sin(r1));
+ var v2 = new Vector2(Mathf.Cos(r2), Mathf.Sin(r2));
real_t dot = v1.Dot(v2);
// Clamp dot to [-1, 1]
- dot = (dot < -1.0f) ? -1.0f : ((dot > 1.0f) ? 1.0f : dot);
+ dot = dot < -1.0f ? -1.0f : (dot > 1.0f ? 1.0f : dot);
- Vector2 v = new Vector2();
+ Vector2 v;
if (dot > 0.9995f)
{
@@ -185,7 +184,7 @@ namespace Godot
Vector2 p2 = m.Origin;
// Construct matrix
- Transform2D res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c));
+ var res = new Transform2D(Mathf.Atan2(v.y, v.x), p1.LinearInterpolate(p2, c));
Vector2 scale = s1.LinearInterpolate(s2, c);
res.x *= scale;
res.y *= scale;
@@ -195,7 +194,7 @@ namespace Godot
public Transform2D Inverse()
{
- Transform2D inv = this;
+ var inv = this;
// Swap
real_t temp = inv.x.y;
@@ -209,13 +208,13 @@ namespace Godot
public Transform2D Orthonormalized()
{
- Transform2D on = this;
+ var 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;
@@ -231,7 +230,7 @@ namespace Godot
public Transform2D Scaled(Vector2 scale)
{
- Transform2D copy = this;
+ var copy = this;
copy.x *= scale;
copy.y *= scale;
copy.o *= scale;
@@ -250,7 +249,7 @@ namespace Godot
public Transform2D Translated(Vector2 offset)
{
- Transform2D copy = this;
+ var copy = this;
copy.o += copy.BasisXform(offset);
return copy;
}
@@ -269,22 +268,22 @@ namespace Godot
// Constructors
public Transform2D(Vector2 xAxis, Vector2 yAxis, Vector2 origin)
{
- this.x = xAxis;
- this.y = yAxis;
- this.o = origin;
+ x = xAxis;
+ y = yAxis;
+ o = origin;
}
public Transform2D(real_t xx, real_t xy, real_t yx, real_t yy, real_t ox, real_t oy)
{
- this.x = new Vector2(xx, xy);
- this.y = new Vector2(yx, yy);
- this.o = new Vector2(ox, oy);
+ x = new Vector2(xx, xy);
+ y = new Vector2(yx, yy);
+ o = new Vector2(ox, oy);
}
public Transform2D(real_t rot, Vector2 pos)
{
- real_t cr = Mathf.Cos( (real_t)rot);
- real_t sr = Mathf.Sin( (real_t)rot);
+ real_t cr = Mathf.Cos(rot);
+ real_t sr = Mathf.Sin(rot);
x.x = cr;
y.y = cr;
x.y = -sr;
@@ -345,9 +344,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this.x.ToString(),
- this.y.ToString(),
- this.o.ToString()
+ x.ToString(),
+ y.ToString(),
+ o.ToString()
});
}
@@ -355,9 +354,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this.x.ToString(format),
- this.y.ToString(format),
- this.o.ToString(format)
+ x.ToString(format),
+ y.ToString(format),
+ o.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Vector2.cs b/modules/mono/glue/cs_files/Vector2.cs
index 6fbe37461..9bc40cf8d 100644
--- a/modules/mono/glue/cs_files/Vector2.cs
+++ b/modules/mono/glue/cs_files/Vector2.cs
@@ -1,13 +1,11 @@
-using System;
-using System.Runtime.InteropServices;
-
// 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
@@ -99,10 +97,15 @@ namespace Godot
return -Reflect(n);
}
+ public Vector2 Ceil()
+ {
+ return new Vector2(Mathf.Ceil(x), Mathf.Ceil(y));
+ }
+
public Vector2 Clamped(real_t length)
{
- Vector2 v = this;
- real_t l = this.Length();
+ var v = this;
+ real_t l = Length();
if (l > 0 && length < l)
{
@@ -115,15 +118,15 @@ namespace Godot
public Vector2 CubicInterpolate(Vector2 b, Vector2 preA, Vector2 postB, real_t t)
{
- Vector2 p0 = preA;
- Vector2 p1 = this;
- Vector2 p2 = b;
- Vector2 p3 = postB;
+ var p0 = preA;
+ var p1 = this;
+ var p2 = b;
+ var p3 = postB;
real_t t2 = t * t;
real_t t3 = t2 * t;
- return 0.5f * ((p1 * 2.0f) +
+ 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);
@@ -166,17 +169,17 @@ namespace Godot
public Vector2 LinearInterpolate(Vector2 b, real_t t)
{
- Vector2 res = this;
+ var res = this;
- res.x += (t * (b.x - x));
- res.y += (t * (b.y - y));
+ res.x += t * (b.x - x);
+ res.y += t * (b.y - y);
return res;
}
public Vector2 Normalized()
{
- Vector2 result = this;
+ var result = this;
result.Normalize();
return result;
}
@@ -192,6 +195,11 @@ namespace Godot
return new Vector2(Mathf.Cos(rads), Mathf.Sin(rads)) * Length();
}
+ public Vector2 Round()
+ {
+ return new Vector2(Mathf.Round(x), Mathf.Round(y));
+ }
+
public void Set(real_t x, real_t y)
{
this.x = x;
@@ -199,8 +207,8 @@ namespace Godot
}
public void Set(Vector2 v)
{
- this.x = v.x;
- this.y = v.y;
+ x = v.x;
+ y = v.y;
}
public Vector2 Slide(Vector2 n)
@@ -244,8 +252,8 @@ namespace Godot
}
public Vector2(Vector2 v)
{
- this.x = v.x;
- this.y = v.y;
+ x = v.x;
+ y = v.y;
}
public static Vector2 operator +(Vector2 left, Vector2 right)
@@ -320,10 +328,8 @@ namespace Godot
{
return left.y < right.y;
}
- else
- {
- return left.x < right.x;
- }
+
+ return left.x < right.x;
}
public static bool operator >(Vector2 left, Vector2 right)
@@ -332,10 +338,8 @@ namespace Godot
{
return left.y > right.y;
}
- else
- {
- return left.x > right.x;
- }
+
+ return left.x > right.x;
}
public static bool operator <=(Vector2 left, Vector2 right)
@@ -344,10 +348,8 @@ namespace Godot
{
return left.y <= right.y;
}
- else
- {
- return left.x <= right.x;
- }
+
+ return left.x <= right.x;
}
public static bool operator >=(Vector2 left, Vector2 right)
@@ -356,10 +358,8 @@ namespace Godot
{
return left.y >= right.y;
}
- else
- {
- return left.x >= right.x;
- }
+
+ return left.x >= right.x;
}
public override bool Equals(object obj)
@@ -386,8 +386,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- this.x.ToString(),
- this.y.ToString()
+ x.ToString(),
+ y.ToString()
});
}
@@ -395,8 +395,8 @@ namespace Godot
{
return String.Format("({0}, {1})", new object[]
{
- this.x.ToString(format),
- this.y.ToString(format)
+ x.ToString(format),
+ y.ToString(format)
});
}
}
diff --git a/modules/mono/glue/cs_files/Vector3.cs b/modules/mono/glue/cs_files/Vector3.cs
index 285736d7b..57e4494f7 100644
--- a/modules/mono/glue/cs_files/Vector3.cs
+++ b/modules/mono/glue/cs_files/Vector3.cs
@@ -1,13 +1,11 @@
-using System;
-using System.Runtime.InteropServices;
-
// 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
@@ -67,7 +65,7 @@ namespace Godot
internal void Normalize()
{
- real_t length = this.Length();
+ real_t length = Length();
if (length == 0f)
{
@@ -105,24 +103,24 @@ 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
);
}
public Vector3 CubicInterpolate(Vector3 b, Vector3 preA, Vector3 postB, real_t t)
{
- Vector3 p0 = preA;
- Vector3 p1 = this;
- Vector3 p2 = b;
- Vector3 p3 = postB;
+ var p0 = preA;
+ var p1 = this;
+ var p2 = b;
+ var p3 = postB;
real_t t2 = t * t;
real_t t3 = t2 * t;
return 0.5f * (
- (p1 * 2.0f) + (-p0 + p2) * t +
+ 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
);
@@ -180,9 +178,9 @@ namespace Godot
{
return new Vector3
(
- x + (t * (b.x - x)),
- y + (t * (b.y - y)),
- z + (t * (b.z - z))
+ x + t * (b.x - x),
+ y + t * (b.y - y),
+ z + t * (b.z - z)
);
}
@@ -198,7 +196,7 @@ namespace Godot
public Vector3 Normalized()
{
- Vector3 v = this;
+ var v = this;
v.Normalize();
return v;
}
@@ -221,6 +219,11 @@ namespace Godot
return 2.0f * n * Dot(n) - this;
}
+ public Vector3 Round()
+ {
+ return new Vector3(Mathf.Round(x), Mathf.Round(y), Mathf.Round(z));
+ }
+
public Vector3 Rotated(Vector3 axis, real_t phi)
{
return new Basis(axis, phi).Xform(this);
@@ -234,9 +237,9 @@ namespace Godot
}
public void Set(Vector3 v)
{
- this.x = v.x;
- this.y = v.y;
- this.z = v.z;
+ x = v.x;
+ y = v.y;
+ z = v.z;
}
public Vector3 Slide(Vector3 n)
@@ -294,9 +297,9 @@ namespace Godot
}
public Vector3(Vector3 v)
{
- this.x = v.x;
- this.y = v.y;
- this.z = v.z;
+ x = v.x;
+ y = v.y;
+ z = v.z;
}
public static Vector3 operator +(Vector3 left, Vector3 right)
@@ -379,8 +382,7 @@ namespace Godot
{
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;
@@ -392,8 +394,7 @@ namespace Godot
{
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;
@@ -405,8 +406,7 @@ namespace Godot
{
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;
@@ -418,8 +418,7 @@ namespace Godot
{
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;
@@ -449,9 +448,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this.x.ToString(),
- this.y.ToString(),
- this.z.ToString()
+ x.ToString(),
+ y.ToString(),
+ z.ToString()
});
}
@@ -459,9 +458,9 @@ namespace Godot
{
return String.Format("({0}, {1}, {2})", new object[]
{
- this.x.ToString(format),
- this.y.ToString(format),
- this.z.ToString(format)
+ x.ToString(format),
+ y.ToString(format),
+ z.ToString(format)
});
}
}
diff --git a/modules/mono/mono_reg_utils.py b/modules/mono/mono_reg_utils.py
index 8ddddb3a2..9c188d07a 100644
--- a/modules/mono/mono_reg_utils.py
+++ b/modules/mono/mono_reg_utils.py
@@ -75,7 +75,7 @@ def find_msbuild_tools_path_reg():
vswhere = os.getenv('PROGRAMFILES')
vswhere += r'\Microsoft Visual Studio\Installer\vswhere.exe'
- vswhere_args = ['-latest', '-requires', 'Microsoft.Component.MSBuild']
+ vswhere_args = ['-latest', '-products', '*', '-requires', 'Microsoft.Component.MSBuild']
try:
lines = subprocess.check_output([vswhere] + vswhere_args).splitlines()
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index 9bb8da8ac..7b23cd757 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -174,6 +174,8 @@ String find_msbuild_tools_path() {
List<String> vswhere_args;
vswhere_args.push_back("-latest");
+ vswhere_args.push_back("-products");
+ vswhere_args.push_back("*");
vswhere_args.push_back("-requires");
vswhere_args.push_back("Microsoft.Component.MSBuild");
diff --git a/modules/pvr/texture_loader_pvr.cpp b/modules/pvr/texture_loader_pvr.cpp
index 76c0b969d..8174bccdb 100644
--- a/modules/pvr/texture_loader_pvr.cpp
+++ b/modules/pvr/texture_loader_pvr.cpp
@@ -536,8 +536,8 @@ static void decompress_pvrtc(PVRTCBlock *p_comp_img, const int p_2bit, const int
int p_x, p_y;
- int p_modulation[8][16];
- int p_modulation_modes[8][16];
+ int p_modulation[8][16] = { { 0 } };
+ int p_modulation_modes[8][16] = { { 0 } };
int Mod, DoPT;
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 58c6d73ab..9e6307c0b 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -261,14 +261,12 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
/* look for further theora headers */
while (theora_p && (theora_p < 3) && (ret = ogg_stream_packetout(&to, &op))) {
if (ret < 0) {
- fprintf(stderr, "Error parsing Theora stream headers; "
- "corrupt stream?\n");
+ fprintf(stderr, "Error parsing Theora stream headers; corrupt stream?\n");
clear();
return;
}
if (!th_decode_headerin(&ti, &tc, &ts, &op)) {
- fprintf(stderr, "Error parsing Theora stream headers; "
- "corrupt stream?\n");
+ fprintf(stderr, "Error parsing Theora stream headers; corrupt stream?\n");
clear();
return;
}
@@ -312,9 +310,15 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
td = th_decode_alloc(&ti, ts);
px_fmt = ti.pixel_fmt;
switch (ti.pixel_fmt) {
- case TH_PF_420: printf(" 4:2:0 video\n"); break;
- case TH_PF_422: printf(" 4:2:2 video\n"); break;
- case TH_PF_444: printf(" 4:4:4 video\n"); break;
+ case TH_PF_420:
+ //printf(" 4:2:0 video\n");
+ break;
+ case TH_PF_422:
+ //printf(" 4:2:2 video\n");
+ break;
+ case TH_PF_444:
+ //printf(" 4:4:4 video\n");
+ break;
case TH_PF_RSVD:
default:
printf(" video\n (UNKNOWN Chroma sampling!)\n");
@@ -519,7 +523,7 @@ void VideoStreamPlaybackTheora::update(float p_delta) {
#else
if (file && /*!videobuf_ready && */ no_theora && theora_eos) {
#endif
- printf("video done, stopping\n");
+ //printf("video done, stopping\n");
stop();
return;
};
diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
index 0929b227d..399ba8ef5 100644
--- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
+++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml
@@ -80,7 +80,7 @@
Return the natural logarithm of the input. Note that this is not the typical base-10 logarithm function calculators use.
</constant>
<constant name="MATH_EXP" value="20" enum="BuiltinFunc">
- Return [b]e[/b] raised to the power of the input. [b]e[/b] sometimes called "Euler's number" is a mathematical constant whose value is approximately 2.71828.
+ Return the mathematical constant [b]e[/b] raised to the specified power of the input. [b]e[/b] has an approximate value of 2.71828.
</constant>
<constant name="MATH_ISNAN" value="21" enum="BuiltinFunc">
Return whether the input is NaN (Not a Number) or not. NaN is usually produced by dividing 0 by 0, though other ways exist.
diff --git a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
index df439f879..d456e880b 100644
--- a/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
+++ b/modules/visual_script/doc_classes/VisualScriptMathConstant.xml
@@ -4,7 +4,7 @@
Commonly used mathematical constants.
</brief_description>
<description>
- Provides common math constants, such as Pi or Euler's constant, on an output Data port.
+ Provides common math constants, such as Pi, on an output Data port.
[b]Input Ports:[/b]
none
[b]Output Ports:[/b]
@@ -35,7 +35,7 @@
Tau: [code]6.283185[/code]
</constant>
<constant name="MATH_CONSTANT_E" value="4" enum="MathConstant">
- Natural log: [code]2.718282[/code]
+ Mathematical constant [code]e[/code], the natural log base: [code]2.718282[/code]
</constant>
<constant name="MATH_CONSTANT_SQRT2" value="5" enum="MathConstant">
Square root of two: [code]1.414214[/code]
diff --git a/modules/visual_script/register_types.cpp b/modules/visual_script/register_types.cpp
index 2809cff36..11401c046 100644
--- a/modules/visual_script/register_types.cpp
+++ b/modules/visual_script/register_types.cpp
@@ -112,7 +112,9 @@ void register_visual_script_types() {
register_visual_script_expression_node();
#ifdef TOOLS_ENABLED
+ ClassDB::set_current_api(ClassDB::API_EDITOR);
ClassDB::register_class<_VisualScriptEditor>();
+ ClassDB::set_current_api(ClassDB::API_CORE);
vs_editor_singleton = memnew(_VisualScriptEditor);
Engine::get_singleton()->add_singleton(Engine::Singleton("VisualScriptEditor", _VisualScriptEditor::get_singleton()));
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index ef680547c..03bc4c114 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -2028,6 +2028,7 @@ void VisualScriptInstance::create(const Ref<VisualScript> &p_script, Object *p_o
function.flow_stack_size = 0;
function.pass_stack_size = 0;
function.node_count = 0;
+
Map<StringName, int> local_var_indices;
if (function.node < 0) {
diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h
index 69bb52217..dad9c6831 100644
--- a/modules/visual_script/visual_script.h
+++ b/modules/visual_script/visual_script.h
@@ -374,12 +374,10 @@ class VisualScriptInstance : public ScriptInstance {
int node;
int max_stack;
int trash_pos;
- int return_pos;
int flow_stack_size;
int pass_stack_size;
int node_count;
int argument_count;
- bool valid;
};
Map<StringName, Function> functions;
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index 69503e631..eb10c5e99 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -3236,6 +3236,12 @@ void VisualScriptEditor::_member_option(int p_option) {
}
}
+void VisualScriptEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+}
+
+void VisualScriptEditor::set_syntax_highlighter(SyntaxHighlighter *p_highlighter) {
+}
+
void VisualScriptEditor::_bind_methods() {
ClassDB::bind_method("_member_button", &VisualScriptEditor::_member_button);
diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h
index 80bbf142d..72b5e0922 100644
--- a/modules/visual_script/visual_script_editor.h
+++ b/modules/visual_script/visual_script_editor.h
@@ -246,6 +246,9 @@ protected:
static void _bind_methods();
public:
+ virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+ virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter);
+
virtual void apply_code();
virtual Ref<Script> get_edited_script() const;
virtual Vector<String> get_functions();
diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp
index 16de04e4c..d5f9d2134 100644
--- a/modules/visual_script/visual_script_expression.cpp
+++ b/modules/visual_script/visual_script_expression.cpp
@@ -455,7 +455,7 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
break;
}
- if (cchar == '-' || (cchar >= '0' && cchar <= '9')) {
+ if (cchar >= '0' && cchar <= '9') {
//a number
String num;
@@ -466,11 +466,6 @@ Error VisualScriptExpression::_get_token(Token &r_token) {
#define READING_DONE 4
int reading = READING_INT;
- if (cchar == '-') {
- num += '-';
- cchar = GET_CHAR();
- }
-
CharType c = cchar;
bool exp_sign = false;
bool exp_beg = false;
@@ -1146,7 +1141,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
expr_pos++;
if (expr_pos == expression.size()) {
//can happen..
- _set_error("Unexpected end of expression..");
+ _set_error("Unexpected end of expression...");
return NULL;
}
}
@@ -1166,7 +1161,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
} else {
if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -1175,7 +1170,7 @@ VisualScriptExpression::ENode *VisualScriptExpression::_parse_expression() {
if (expression[next_op - 1].is_op) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
diff --git a/modules/webm/libvpx/SCsub b/modules/webm/libvpx/SCsub
index 0ebafa022..b09c232b3 100644
--- a/modules/webm/libvpx/SCsub
+++ b/modules/webm/libvpx/SCsub
@@ -340,7 +340,7 @@ if webm_simd_optimizations == False:
env_libvpx.add_source_files(env.modules_sources, libvpx_sources)
if webm_cpu_x86:
- is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"])
+ is_clang_or_gcc = ('gcc' in env["CC"]) or ('clang' in env["CC"]) or ("OSXCROSS_ROOT" in os.environ)
env_libvpx_mmx = env_libvpx.Clone()
if cpu_bits == '32' and is_clang_or_gcc:
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index d3330d683..e0b987b4d 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -148,12 +148,14 @@ void EMWSPeer::close() {
IP_Address EMWSPeer::get_connected_host() const {
- return IP_Address();
+ ERR_EXPLAIN("Not supported in HTML5 export");
+ ERR_FAIL_V(IP_Address());
};
uint16_t EMWSPeer::get_connected_port() const {
- return 1025;
+ ERR_EXPLAIN("Not supported in HTML5 export");
+ ERR_FAIL_V(0);
};
EMWSPeer::EMWSPeer() {
diff --git a/modules/websocket/emws_server.cpp b/modules/websocket/emws_server.cpp
index c9ddae0c8..3eb93e415 100644
--- a/modules/websocket/emws_server.cpp
+++ b/modules/websocket/emws_server.cpp
@@ -58,6 +58,19 @@ PoolVector<String> EMWSServer::get_protocols() const {
return out;
}
+IP_Address EMWSServer::get_peer_address(int p_peer_id) const {
+
+ return IP_Address();
+}
+
+int EMWSServer::get_peer_port(int p_peer_id) const {
+
+ return 0;
+}
+
+void EMWSServer::disconnect_peer(int p_peer_id) {
+}
+
EMWSServer::EMWSServer() {
}
diff --git a/modules/websocket/emws_server.h b/modules/websocket/emws_server.h
index aa089ea40..9ec4ce72c 100644
--- a/modules/websocket/emws_server.h
+++ b/modules/websocket/emws_server.h
@@ -46,6 +46,9 @@ 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;
+ int get_peer_port(int p_peer_id) const;
+ void disconnect_peer(int p_peer_id);
virtual void poll();
virtual PoolVector<String> get_protocols() const;
diff --git a/modules/websocket/lws_client.cpp b/modules/websocket/lws_client.cpp
index bebf342f8..2220c9adf 100644
--- a/modules/websocket/lws_client.cpp
+++ b/modules/websocket/lws_client.cpp
@@ -31,6 +31,7 @@
#include "lws_client.h"
#include "core/io/ip.h"
+#include "core/io/stream_peer_ssl.h"
Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
@@ -64,6 +65,9 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
info.uid = -1;
//info.ws_ping_pong_interval = 5;
info.user = _lws_ref;
+#if defined(LWS_OPENSSL_SUPPORT)
+ info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
+#endif
context = lws_create_context(&info);
if (context == NULL) {
@@ -87,7 +91,14 @@ Error LWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port,
i.host = hbuf;
i.path = pbuf;
i.port = p_port;
- i.ssl_connection = p_ssl;
+
+ if (p_ssl) {
+ i.ssl_connection = LCCSCF_USE_SSL;
+ if (!verify_ssl)
+ i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
+ } else {
+ i.ssl_connection = 0;
+ }
lws_client_connect_via_info(&i);
return OK;
@@ -104,6 +115,13 @@ int LWSClient::_handle_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
LWSPeer::PeerData *peer_data = (LWSPeer::PeerData *)user;
switch (reason) {
+ case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS: {
+ PoolByteArray arr = StreamPeerSSL::get_project_cert_array();
+ if (arr.size() > 0)
+ SSL_CTX_add_client_CA((SSL_CTX *)user, d2i_X509(NULL, &arr.read()[0], arr.size()));
+ else if (verify_ssl)
+ WARN_PRINTS("No CA cert specified in project settings, SSL will not work");
+ } break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
peer->set_wsi(wsi);
diff --git a/modules/websocket/lws_peer.cpp b/modules/websocket/lws_peer.cpp
index ba45d7688..3855a39ae 100644
--- a/modules/websocket/lws_peer.cpp
+++ b/modules/websocket/lws_peer.cpp
@@ -32,6 +32,14 @@
#include "lws_peer.h"
#include "core/io/ip.h"
+// Needed for socket_helpers on Android at least. UNIXes has it, just include if not windows
+#if !defined(WINDOWS_ENABLED)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+
+#include "drivers/unix/socket_helpers.h"
+
void LWSPeer::set_wsi(struct lws *p_wsi) {
wsi = p_wsi;
};
@@ -178,12 +186,44 @@ void LWSPeer::close() {
IP_Address LWSPeer::get_connected_host() const {
- return IP_Address();
+ ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address());
+
+ IP_Address ip;
+ int port = 0;
+
+ socklen_t len = 0;
+ struct sockaddr_storage addr;
+
+ int fd = lws_get_socket_fd(wsi);
+ ERR_FAIL_COND_V(fd == -1, IP_Address());
+
+ int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
+ ERR_FAIL_COND_V(ret != 0, IP_Address());
+
+ _set_ip_addr_port(ip, port, &addr);
+
+ return ip;
};
uint16_t LWSPeer::get_connected_port() const {
- return 1025;
+ ERR_FAIL_COND_V(!is_connected_to_host(), 0);
+
+ IP_Address ip;
+ int port = 0;
+
+ socklen_t len = 0;
+ struct sockaddr_storage addr;
+
+ int fd = lws_get_socket_fd(wsi);
+ ERR_FAIL_COND_V(fd == -1, 0);
+
+ int ret = getpeername(fd, (struct sockaddr *)&addr, &len);
+ ERR_FAIL_COND_V(ret != 0, 0);
+
+ _set_ip_addr_port(ip, port, &addr);
+
+ return port;
};
LWSPeer::LWSPeer() {
diff --git a/modules/websocket/lws_server.cpp b/modules/websocket/lws_server.cpp
index 94fe4231a..8d13dc7a9 100644
--- a/modules/websocket/lws_server.cpp
+++ b/modules/websocket/lws_server.cpp
@@ -164,6 +164,24 @@ Ref<WebSocketPeer> LWSServer::get_peer(int p_id) const {
return _peer_map[p_id];
}
+IP_Address LWSServer::get_peer_address(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address());
+
+ return _peer_map[p_peer_id]->get_connected_host();
+}
+
+int LWSServer::get_peer_port(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), 0);
+
+ return _peer_map[p_peer_id]->get_connected_port();
+}
+
+void LWSServer::disconnect_peer(int p_peer_id) {
+ ERR_FAIL_COND(!has_peer(p_peer_id));
+
+ get_peer(p_peer_id)->close();
+}
+
LWSServer::LWSServer() {
context = NULL;
_lws_ref = NULL;
diff --git a/modules/websocket/lws_server.h b/modules/websocket/lws_server.h
index de8f59e5a..9e3fb9b77 100644
--- a/modules/websocket/lws_server.h
+++ b/modules/websocket/lws_server.h
@@ -52,6 +52,9 @@ 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;
+ int get_peer_port(int p_peer_id) const;
+ void disconnect_peer(int p_peer_id);
virtual void poll() { _lws_poll(); }
LWSServer();
diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp
index 591d9510c..770116308 100644
--- a/modules/websocket/websocket_client.cpp
+++ b/modules/websocket/websocket_client.cpp
@@ -32,6 +32,8 @@
GDCINULL(WebSocketClient);
WebSocketClient::WebSocketClient() {
+
+ verify_ssl = true;
}
WebSocketClient::~WebSocketClient() {
@@ -72,6 +74,16 @@ Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protoco
return connect_to_host(host, path, port, ssl, p_protocols);
}
+void WebSocketClient::set_verify_ssl_enabled(bool p_verify_ssl) {
+
+ verify_ssl = p_verify_ssl;
+}
+
+bool WebSocketClient::is_verify_ssl_enabled() const {
+
+ return verify_ssl;
+}
+
bool WebSocketClient::is_server() const {
return false;
@@ -116,6 +128,10 @@ void WebSocketClient::_on_error() {
void WebSocketClient::_bind_methods() {
ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("disconnect_from_host"), &WebSocketClient::disconnect_from_host);
+ 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_PROPERTYNZ(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled");
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 5c863559b..6165f37d4 100644
--- a/modules/websocket/websocket_client.h
+++ b/modules/websocket/websocket_client.h
@@ -41,12 +41,16 @@ class WebSocketClient : public WebSocketMultiplayerPeer {
protected:
Ref<WebSocketPeer> _peer;
+ bool verify_ssl;
static void _bind_methods();
public:
Error connect_to_url(String p_url, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false);
+ void set_verify_ssl_enabled(bool p_verify_ssl);
+ bool is_verify_ssl_enabled() const;
+
virtual void poll() = 0;
virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0;
virtual void disconnect_from_host() = 0;
diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp
index 632404784..61f783e37 100644
--- a/modules/websocket/websocket_peer.cpp
+++ b/modules/websocket/websocket_peer.cpp
@@ -43,6 +43,8 @@ void WebSocketPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_connected_to_host"), &WebSocketPeer::is_connected_to_host);
ClassDB::bind_method(D_METHOD("was_string_packet"), &WebSocketPeer::was_string_packet);
ClassDB::bind_method(D_METHOD("close"), &WebSocketPeer::close);
+ ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketPeer::get_connected_host);
+ ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketPeer::get_connected_port);
BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp
index 5746f61e1..2693b26e4 100644
--- a/modules/websocket/websocket_server.cpp
+++ b/modules/websocket/websocket_server.cpp
@@ -44,6 +44,9 @@ void WebSocketServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(PoolVector<String>()), DEFVAL(false));
ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop);
ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer);
+ ClassDB::bind_method(D_METHOD("get_peer_address"), &WebSocketServer::get_peer_address);
+ ClassDB::bind_method(D_METHOD("get_peer_port"), &WebSocketServer::get_peer_port);
+ ClassDB::bind_method(D_METHOD("disconnect_peer"), &WebSocketServer::disconnect_peer);
ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol")));
diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h
index 360ff9e6d..64935f8a5 100644
--- a/modules/websocket/websocket_server.h
+++ b/modules/websocket/websocket_server.h
@@ -52,6 +52,10 @@ public:
virtual bool is_server() const;
ConnectionStatus get_connection_status() const;
+ virtual IP_Address 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) = 0;
+
void _on_peer_packet(int32_t p_peer_id);
void _on_connect(int32_t p_peer_id, String p_protocol);
void _on_disconnect(int32_t p_peer_id);
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 6d9f0a7e9..6ed03d7ae 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -1153,7 +1153,7 @@ public:
String package_name = p_preset->get("package/unique_name");
if (remove_prev) {
- ep.step("Uninstalling..", 1);
+ ep.step("Uninstalling...", 1);
print_line("Uninstalling previous version: " + devices[p_device].name);
@@ -1232,7 +1232,7 @@ public:
}
}
- ep.step("Running on Device..", 3);
+ ep.step("Running on Device...", 3);
args.clear();
args.push_back("-s");
args.push_back(devices[p_device].id);
@@ -1323,6 +1323,8 @@ public:
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) {
+ ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
+
String src_apk;
EditorProgress ep("export", "Exporting for Android", 105);
@@ -1490,7 +1492,7 @@ public:
ret = unzGoToNextFile(pkg);
}
- ep.step("Adding Files..", 1);
+ ep.step("Adding Files...", 1);
Error err = OK;
Vector<String> cl = cmdline.strip_edges().split(" ");
for (int i = 0; i < cl.size(); i++) {
@@ -1624,14 +1626,14 @@ public:
password = EditorSettings::get_singleton()->get("export/android/debug_keystore_pass");
user = EditorSettings::get_singleton()->get("export/android/debug_keystore_user");
- ep.step("Signing Debug APK..", 103);
+ ep.step("Signing Debug APK...", 103);
} else {
keystore = release_keystore;
password = release_password;
user = release_username;
- ep.step("Signing Release APK..", 103);
+ ep.step("Signing Release APK...", 103);
}
if (!FileAccess::exists(keystore)) {
@@ -1663,7 +1665,7 @@ public:
return ERR_CANT_CREATE;
}
- ep.step("Verifying APK..", 104);
+ ep.step("Verifying APK...", 104);
args.clear();
args.push_back("-verify");
@@ -1683,7 +1685,7 @@ public:
static const int ZIP_ALIGNMENT = 4;
- ep.step("Aligning APK..", 105);
+ ep.step("Aligning APK...", 105);
unzFile tmp_unaligned = unzOpen2(unaligned_path.utf8().get_data(), &io);
if (!tmp_unaligned) {
diff --git a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java
index d72c59037..bde422164 100644
--- a/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java
+++ b/platform/android/java/src/org/godotengine/godot/GodotPaymentV3.java
@@ -101,12 +101,12 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
GodotLib.calldeferred(purchaseCallbackId, "consume_not_required", new Object[] {});
}
- public void callbackFailConsume() {
- GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[] {});
+ public void callbackFailConsume(String message) {
+ GodotLib.calldeferred(purchaseCallbackId, "consume_fail", new Object[] { message });
}
- public void callbackFail() {
- GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[] {});
+ public void callbackFail(String message) {
+ GodotLib.calldeferred(purchaseCallbackId, "purchase_fail", new Object[] { message });
}
public void callbackCancel() {
@@ -165,11 +165,11 @@ public class GodotPaymentV3 extends Godot.SingletonBase {
}
public void callbackDisconnected() {
- GodotLib.calldeferred(purchaseCallbackId, "iap_disconnected", new Object[]{});
+ GodotLib.calldeferred(purchaseCallbackId, "iap_disconnected", new Object[] {});
}
public void callbackConnected() {
- GodotLib.calldeferred(purchaseCallbackId, "iap_connected", new Object[]{});
+ GodotLib.calldeferred(purchaseCallbackId, "iap_connected", new Object[] {});
}
// true if connected, false otherwise
diff --git a/platform/android/java/src/org/godotengine/godot/GodotView.java b/platform/android/java/src/org/godotengine/godot/GodotView.java
index 0222758c2..23723c269 100644
--- a/platform/android/java/src/org/godotengine/godot/GodotView.java
+++ b/platform/android/java/src/org/godotengine/godot/GodotView.java
@@ -261,7 +261,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
};
int source = event.getSource();
- if ((source & InputDevice.SOURCE_JOYSTICK) != 0 || (source & InputDevice.SOURCE_DPAD) != 0 || (source & InputDevice.SOURCE_GAMEPAD) != 0) {
+ if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
final int button = get_godot_button(keyCode);
final int device = find_joy_device(event.getDeviceId());
@@ -302,7 +302,7 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener {
int source = event.getSource();
//Log.e(TAG, String.format("Key down! source %d, device %d, joystick %d, %d, %d", event.getDeviceId(), source, (source & InputDevice.SOURCE_JOYSTICK), (source & InputDevice.SOURCE_DPAD), (source & InputDevice.SOURCE_GAMEPAD)));
- if ((source & InputDevice.SOURCE_JOYSTICK) != 0 || (source & InputDevice.SOURCE_DPAD) != 0 || (source & InputDevice.SOURCE_GAMEPAD) != 0) {
+ if ((source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK || (source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD || (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
if (event.getRepeatCount() > 0) // ignore key echo
return true;
diff --git a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java
index 441a31135..b7bf2362c 100644
--- a/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java
+++ b/platform/android/java/src/org/godotengine/godot/payments/PaymentsManager.java
@@ -116,7 +116,7 @@ public class PaymentsManager {
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail();
+ godotPaymentV3.callbackFail(message);
}
@Override
@@ -148,7 +148,7 @@ public class PaymentsManager {
@Override
protected void error(String message) {
Log.d("godot", "consumeUnconsumedPurchases :" + message);
- godotPaymentV3.callbackFailConsume();
+ godotPaymentV3.callbackFailConsume(message);
}
@Override
@@ -222,7 +222,7 @@ public class PaymentsManager {
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail();
+ godotPaymentV3.callbackFail(message);
}
}
.consume(sku);
@@ -231,7 +231,7 @@ public class PaymentsManager {
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail();
+ godotPaymentV3.callbackFail(message);
}
@Override
@@ -258,7 +258,7 @@ public class PaymentsManager {
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail();
+ godotPaymentV3.callbackFail(message);
}
}
.consume(sku);
@@ -266,7 +266,7 @@ public class PaymentsManager {
@Override
protected void error(String message) {
- godotPaymentV3.callbackFail();
+ godotPaymentV3.callbackFail(message);
}
@Override
@@ -291,7 +291,7 @@ public class PaymentsManager {
@Override
protected void error(String message) {
- godotPaymentV3.callbackFailConsume();
+ godotPaymentV3.callbackFailConsume(message);
}
}
.consume(sku);
diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp
index 9baf58c3e..579c06f76 100644
--- a/platform/android/java_glue.cpp
+++ b/platform/android/java_glue.cpp
@@ -936,6 +936,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) {
+ if (step == 0)
+ return;
+
os_android->main_loop_request_go_back();
}
@@ -976,6 +979,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions) {
+ if (step == 0)
+ return;
+
Vector<OS_Android::TouchPos> points;
for (int i = 0; i < count; i++) {
@@ -1250,6 +1256,8 @@ static unsigned int android_get_keysym(unsigned int p_code) {
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed) {
+ if (step == 0)
+ return;
OS_Android::JoypadEvent jevent;
jevent.device = p_device;
@@ -1261,6 +1269,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value) {
+ if (step == 0)
+ return;
OS_Android::JoypadEvent jevent;
jevent.device = p_device;
@@ -1272,6 +1282,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env,
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y) {
+ if (step == 0)
+ return;
+
OS_Android::JoypadEvent jevent;
jevent.device = p_device;
jevent.type = OS_Android::JOY_EVENT_HAT;
@@ -1301,6 +1314,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged(
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed) {
+ if (step == 0)
+ return;
Ref<InputEventKey> ievent;
ievent.instance();
@@ -1344,14 +1359,18 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj) {
- if (os_android && step > 0)
- os_android->main_loop_focusin();
+ if (step == 0)
+ return;
+
+ os_android->main_loop_focusin();
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj) {
- if (os_android && step > 0)
- os_android->main_loop_focusout();
+ if (step == 0)
+ return;
+
+ os_android->main_loop_focusout();
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj) {
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 5557c1de4..fc41adeb7 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -330,17 +330,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos>
if (touch.size()) {
//end all if exist
- {
- Ref<InputEventMouseButton> ev;
- ev.instance();
- ev->set_button_index(BUTTON_LEFT);
- ev->set_button_mask(BUTTON_MASK_LEFT);
- ev->set_pressed(false);
- ev->set_position(touch[0].pos);
- ev->set_global_position(touch[0].pos);
- input->parse_input_event(ev);
- }
-
for (int i = 0; i < touch.size(); i++) {
Ref<InputEventScreenTouch> ev;
@@ -358,21 +347,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos>
touch[i].pos = p_points[i].pos;
}
- {
- //send mouse
- Ref<InputEventMouseButton> ev;
- ev.instance();
- // ev.type = Ref<InputEvent>::MOUSE_BUTTON;
- ev->set_button_index(BUTTON_LEFT);
- ev->set_button_mask(BUTTON_MASK_LEFT);
- ev->set_pressed(true);
- ev->set_position(touch[0].pos);
- ev->set_global_position(touch[0].pos);
- input->set_mouse_position(Point2(touch[0].pos.x, touch[0].pos.y));
- last_mouse = touch[0].pos;
- input->parse_input_event(ev);
- }
-
//send touch
for (int i = 0; i < touch.size(); i++) {
@@ -387,19 +361,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos>
} break;
case 1: { //motion
- if (p_points.size()) {
- //send mouse, should look for point 0?
- Ref<InputEventMouseMotion> ev;
- ev.instance();
- ev->set_button_mask(BUTTON_MASK_LEFT);
- ev->set_position(p_points[0].pos);
- input->set_mouse_position(Point2(ev->get_position().x, ev->get_position().y));
- ev->set_speed(input->get_last_mouse_speed());
- ev->set_relative(p_points[0].pos - last_mouse);
- last_mouse = p_points[0].pos;
- input->parse_input_event(ev);
- }
-
ERR_FAIL_COND(touch.size() != p_points.size());
for (int i = 0; i < touch.size(); i++) {
@@ -432,16 +393,6 @@ void OS_Android::process_touch(int p_what, int p_pointer, const Vector<TouchPos>
if (touch.size()) {
//end all if exist
- Ref<InputEventMouseButton> ev;
- ev.instance();
- ev->set_button_index(BUTTON_LEFT);
- ev->set_button_mask(BUTTON_MASK_LEFT);
- ev->set_pressed(false);
- ev->set_position(touch[0].pos);
- ev->set_global_position(touch[0].pos);
- input->set_mouse_position(Point2(touch[0].pos.x, touch[0].pos.y));
- input->parse_input_event(ev);
-
for (int i = 0; i < touch.size(); i++) {
Ref<InputEventScreenTouch> ev;
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index 12181b368..d2457e538 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -93,7 +93,6 @@ public:
private:
Vector<TouchPos> touch;
- Point2 last_mouse;
GFXInitFunc gfx_init_func;
void *gfx_init_ud;
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index 6b5f30dc4..b96bec16b 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+import os
Import('env')
@@ -21,6 +22,10 @@ ios_lib = env_ios.add_library('iphone', iphone_lib)
def combine_libs(target=None, source=None, env=None):
lib_path = target[0].srcnode().abspath
- env.Execute('$IPHONEPATH/usr/bin/libtool -static -o "' + lib_path + '" ' + ' '.join([('"' + lib.srcnode().abspath + '"') for lib in source]))
+ if ("OSXCROSS_IOS" in os.environ):
+ libtool = '$IPHONEPATH/usr/bin/${ios_triple}libtool'
+ else:
+ libtool = "$IPHONEPATH/usr/bin/libtool"
+ env.Execute(libtool + ' -static -o "' + lib_path + '" ' + ' '.join([('"' + lib.srcnode().abspath + '"') for lib in source]))
combine_command = env_ios.Command('#bin/libgodot' + env_ios['LIBSUFFIX'], [ios_lib] + env_ios['LIBS'], combine_libs)
diff --git a/platform/iphone/app_delegate.h b/platform/iphone/app_delegate.h
index f14864b5b..c34b5053d 100644
--- a/platform/iphone/app_delegate.h
+++ b/platform/iphone/app_delegate.h
@@ -37,6 +37,7 @@
@interface AppDelegate : NSObject <UIApplicationDelegate, GLViewDelegate> {
//@property (strong, nonatomic) UIWindow *window;
ViewController *view_controller;
+ bool is_focus_out;
};
@property(strong, nonatomic) UIWindow *window;
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index 5c3799ab0..7ed1328b2 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -79,6 +79,7 @@ static ViewController *mainViewController = nil;
}
NSMutableDictionary *ios_joysticks = nil;
+NSMutableArray *pending_ios_joysticks = nil;
- (GCControllerPlayerIndex)getFreePlayerIndex {
bool have_player_1 = false;
@@ -115,6 +116,66 @@ NSMutableDictionary *ios_joysticks = nil;
};
};
+void _ios_add_joystick(GCController *controller, AppDelegate *delegate) {
+ // get a new id for our controller
+ int joy_id = OSIPhone::get_singleton()->get_unused_joy_id();
+ if (joy_id != -1) {
+ // assign our player index
+ if (controller.playerIndex == GCControllerPlayerIndexUnset) {
+ controller.playerIndex = [delegate getFreePlayerIndex];
+ };
+
+ // tell Godot about our new controller
+ OSIPhone::get_singleton()->joy_connection_changed(
+ joy_id, true, [controller.vendorName UTF8String]);
+
+ // add it to our dictionary, this will retain our controllers
+ [ios_joysticks setObject:controller
+ forKey:[NSNumber numberWithInt:joy_id]];
+
+ // set our input handler
+ [delegate setControllerInputHandler:controller];
+ } else {
+ printf("Couldn't retrieve new joy id\n");
+ };
+}
+
+static void on_focus_out(ViewController *view_controller, bool *is_focus_out) {
+ if (!*is_focus_out) {
+ *is_focus_out = true;
+ if (OS::get_singleton()->get_main_loop())
+ OS::get_singleton()->get_main_loop()->notification(
+ MainLoop::NOTIFICATION_WM_FOCUS_OUT);
+
+ [view_controller.view stopAnimation];
+ if (OS::get_singleton()->native_video_is_playing()) {
+ OSIPhone::get_singleton()->native_video_focus_out();
+ }
+
+ AudioDriverCoreAudio *audio = dynamic_cast<AudioDriverCoreAudio *>(AudioDriverCoreAudio::get_singleton());
+ if (audio)
+ audio->stop();
+ }
+}
+
+static void on_focus_in(ViewController *view_controller, bool *is_focus_out) {
+ if (*is_focus_out) {
+ *is_focus_out = false;
+ if (OS::get_singleton()->get_main_loop())
+ OS::get_singleton()->get_main_loop()->notification(
+ MainLoop::NOTIFICATION_WM_FOCUS_IN);
+
+ [view_controller.view startAnimation];
+ if (OSIPhone::get_singleton()->native_video_is_playing()) {
+ OSIPhone::get_singleton()->native_video_unpause();
+ }
+
+ AudioDriverCoreAudio *audio = dynamic_cast<AudioDriverCoreAudio *>(AudioDriverCoreAudio::get_singleton());
+ if (audio)
+ audio->start();
+ }
+}
+
- (void)controllerWasConnected:(NSNotification *)notification {
// create our dictionary if we don't have one yet
if (ios_joysticks == nil) {
@@ -127,28 +188,12 @@ NSMutableDictionary *ios_joysticks = nil;
printf("Couldn't retrieve new controller\n");
} else if ([[ios_joysticks allKeysForObject:controller] count] != 0) {
printf("Controller is already registered\n");
+ } else if (frame_count > 1) {
+ _ios_add_joystick(controller, self);
} else {
- // get a new id for our controller
- int joy_id = OSIPhone::get_singleton()->get_unused_joy_id();
- if (joy_id != -1) {
- // assign our player index
- if (controller.playerIndex == GCControllerPlayerIndexUnset) {
- controller.playerIndex = [self getFreePlayerIndex];
- };
-
- // tell Godot about our new controller
- OSIPhone::get_singleton()->joy_connection_changed(
- joy_id, true, [controller.vendorName UTF8String]);
-
- // add it to our dictionary, this will retain our controllers
- [ios_joysticks setObject:controller
- forKey:[NSNumber numberWithInt:joy_id]];
-
- // set our input handler
- [self setControllerInputHandler:controller];
- } else {
- printf("Couldn't retrieve new joy id\n");
- };
+ if (pending_ios_joysticks == nil)
+ pending_ios_joysticks = [[NSMutableArray alloc] init];
+ [pending_ios_joysticks addObject:controller];
};
};
@@ -352,6 +397,27 @@ NSMutableDictionary *ios_joysticks = nil;
[ios_joysticks dealloc];
ios_joysticks = nil;
};
+
+ if (pending_ios_joysticks != nil) {
+ [pending_ios_joysticks dealloc];
+ pending_ios_joysticks = nil;
+ };
+};
+
+OS::VideoMode _get_video_mode() {
+ int backingWidth;
+ int backingHeight;
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
+ GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
+ glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
+ GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+
+ OS::VideoMode vm;
+ vm.fullscreen = true;
+ vm.width = backingWidth;
+ vm.height = backingHeight;
+ vm.resizable = false;
+ return vm;
};
static int frame_count = 0;
@@ -360,19 +426,7 @@ static int frame_count = 0;
switch (frame_count) {
case 0: {
- int backingWidth;
- int backingHeight;
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
-
- OS::VideoMode vm;
- vm.fullscreen = true;
- vm.width = backingWidth;
- vm.height = backingHeight;
- vm.resizable = false;
- OS::get_singleton()->set_video_mode(vm);
+ OS::get_singleton()->set_video_mode(_get_video_mode());
if (!OS::get_singleton()) {
exit(0);
@@ -410,6 +464,14 @@ static int frame_count = 0;
Main::setup2();
++frame_count;
+ if (pending_ios_joysticks != nil) {
+ for (GCController *controller in pending_ios_joysticks) {
+ _ios_add_joystick(controller, self);
+ }
+ [pending_ios_joysticks dealloc];
+ pending_ios_joysticks = nil;
+ }
+
// this might be necessary before here
NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
for (NSString *key in dict) {
@@ -543,6 +605,8 @@ static int frame_count = 0;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
CGRect rect = [[UIScreen mainScreen] bounds];
+ is_focus_out = false;
+
[application setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone];
// disable idle timer
// application.idleTimerDisabled = YES;
@@ -562,18 +626,13 @@ static int frame_count = 0;
//[glView setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
// UIViewAutoresizingFlexibleWidth];
- int backingWidth;
- int backingHeight;
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
- glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
- GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+ OS::VideoMode vm = _get_video_mode();
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
- int err = iphone_main(backingWidth, backingHeight, gargc, gargv, String::utf8([documentsDirectory UTF8String]));
+ int err = iphone_main(vm.width, vm.height, gargc, gargv, String::utf8([documentsDirectory UTF8String]));
if (err != 0) {
// bail, things did not go very well for us, should probably output a message on screen with our error code...
exit(0);
@@ -607,6 +666,12 @@ static int frame_count = 0;
[self initGameControllers];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(onAudioInterruption:)
+ name:AVAudioSessionInterruptionNotification
+ object:[AVAudioSession sharedInstance]];
+
// OSIPhone::screen_width = rect.size.width - rect.origin.x;
// OSIPhone::screen_height = rect.size.height - rect.origin.y;
@@ -618,6 +683,18 @@ static int frame_count = 0;
return TRUE;
};
+- (void)onAudioInterruption:(NSNotification *)notification {
+ if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
+ if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
+ NSLog(@"Audio interruption began");
+ on_focus_out(view_controller, &is_focus_out);
+ } else if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]) {
+ NSLog(@"Audio interruption ended");
+ on_focus_in(view_controller, &is_focus_out);
+ }
+ }
+};
+
- (void)applicationWillTerminate:(UIApplication *)application {
[self deinitGameControllers];
@@ -635,14 +712,7 @@ static int frame_count = 0;
- (void)applicationDidEnterBackground:(UIApplication *)application {
///@TODO maybe add pause motionManager? and where would we unpause it?
- if (OS::get_singleton()->get_main_loop())
- OS::get_singleton()->get_main_loop()->notification(
- MainLoop::NOTIFICATION_WM_FOCUS_OUT);
-
- [view_controller.view stopAnimation];
- if (OS::get_singleton()->native_video_is_playing()) {
- OSIPhone::get_singleton()->native_video_focus_out();
- };
+ on_focus_out(view_controller, &is_focus_out);
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
@@ -657,19 +727,7 @@ static int frame_count = 0;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
- if (OS::get_singleton()->get_main_loop())
- OS::get_singleton()->get_main_loop()->notification(
- MainLoop::NOTIFICATION_WM_FOCUS_IN);
-
- [view_controller.view
- startAnimation]; // FIXME: resume seems to be recommended elsewhere
- if (OSIPhone::get_singleton()->native_video_is_playing()) {
- OSIPhone::get_singleton()->native_video_unpause();
- };
-
- // Fixed audio can not resume if it is interrupted cause by an incoming phone call
- if (AudioDriverCoreAudio::get_singleton() != NULL)
- AudioDriverCoreAudio::get_singleton()->start();
+ on_focus_in(view_controller, &is_focus_out);
}
- (void)dealloc {
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index e3119814f..4c1e02baf 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -147,6 +147,26 @@ Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_ge
return archs;
}
+struct LoadingScreenInfo {
+ const char *preset_key;
+ const char *export_name;
+};
+
+static const LoadingScreenInfo loading_screen_infos[] = {
+ { "landscape_launch_screens/iphone_2436x1125", "Default-Landscape-X.png" },
+ { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png" },
+ { "landscape_launch_screens/ipad_1024x768", "Default-Landscape.png" },
+ { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png" },
+
+ { "portrait_launch_screens/iphone_640x960", "Default-480h@2x.png" },
+ { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png" },
+ { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png" },
+ { "portrait_launch_screens/iphone_1125x2436", "Default-Portrait-X.png" },
+ { "portrait_launch_screens/ipad_768x1024", "Default-Portrait.png" },
+ { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png" },
+ { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png" }
+};
+
void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) {
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), ""));
@@ -172,6 +192,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPhone/iPod Touch with retina display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "png"), "")); // App Store
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPhone with retina HD display
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad with retina display
@@ -179,15 +200,9 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "png"), "")); // Spotlight
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "png"), "")); // Spotlight on devices with retina display
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/iphone_2208x1242", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6 Plus, 6s Plus, 7 Plus
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/ipad_2732x2048", PROPERTY_HINT_FILE, "png"), "")); // 12.9-inch iPad Pro
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/ipad_2048x1536", PROPERTY_HINT_FILE, "png"), "")); // Other iPads
-
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_640x1136", PROPERTY_HINT_FILE, "png"), "")); // iPhone 5, 5s, SE
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_750x1334", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6, 6s, 7
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_1242x2208", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6 Plus, 6s Plus, 7 Plus
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/ipad_2048x2732", PROPERTY_HINT_FILE, "png"), "")); // 12.9-inch iPad Pro
- r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/ipad_1536x2048", PROPERTY_HINT_FILE, "png"), "")); // Other iPads
+ for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
+ r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "png"), ""));
+ }
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
@@ -313,6 +328,7 @@ static const IconInfo icon_infos[] = {
{ "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true },
{ "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", false },
+ { "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", false },
{ "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false },
@@ -380,23 +396,6 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
return OK;
}
-struct LoadingScreenInfo {
- const char *preset_key;
- const char *export_name;
-};
-
-static const LoadingScreenInfo loading_screen_infos[] = {
- { "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png" },
- { "landscape_launch_screens/ipad_2732x2048", "Default-Landscape-1366h@2x.png" },
- { "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png" },
-
- { "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png" },
- { "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png" },
- { "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png" },
- { "portrait_launch_screens/ipad_2048x2732", "Default-Portrait-1366h@2x.png" },
- { "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png" }
-};
-
Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {
DirAccess *da = DirAccess::open(p_dest_dir);
ERR_FAIL_COND_V(!da, ERR_CANT_OPEN);
@@ -404,12 +403,14 @@ Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPre
for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
LoadingScreenInfo info = loading_screen_infos[i];
String loading_screen_file = p_preset->get(info.preset_key);
- Error err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
- if (err) {
- memdelete(da);
- String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path: " + loading_screen_file;
- ERR_PRINT(err_str.utf8().get_data());
- return err;
+ if (loading_screen_file.size() > 0) {
+ Error err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
+ if (err) {
+ memdelete(da);
+ String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path: " + loading_screen_file;
+ ERR_PRINT(err_str.utf8().get_data());
+ return err;
+ }
}
}
memdelete(da);
@@ -887,7 +888,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
if (err)
return err;
- err = _export_loading_screens(p_preset, dest_dir + binary_name + "/");
+ err = _export_loading_screens(p_preset, dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/");
if (err)
return err;
diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h
index 85376ebc0..0d101eb69 100644
--- a/platform/iphone/gl_view.h
+++ b/platform/iphone/gl_view.h
@@ -83,6 +83,8 @@
@property(strong, nonatomic) MPMoviePlayerController *moviePlayerController;
@property(strong, nonatomic) UIWindow *backgroundWindow;
+@property(nonatomic) UITextAutocorrectionType autocorrectionType;
+
- (void)startAnimation;
- (void)stopAnimation;
- (void)drawView;
diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm
index 69116c64c..478a3125a 100644
--- a/platform/iphone/gl_view.mm
+++ b/platform/iphone/gl_view.mm
@@ -78,6 +78,16 @@ void _hide_keyboard() {
keyboard_text = "";
};
+Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height) {
+ UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, 0, 0);
+ if (_instance != nil && [_instance respondsToSelector:@selector(safeAreaInsets)]) {
+ insets = [_instance safeAreaInsets];
+ }
+ ERR_FAIL_COND_V(insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0,
+ Rect2(0, 0, p_window_width, p_window_height));
+ return Rect2(insets.left, insets.top, p_window_width - insets.right - insets.left, p_window_height - insets.bottom - insets.top);
+}
+
bool _play_video(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) {
p_path = ProjectSettings::get_singleton()->globalize_path(p_path);
@@ -326,9 +336,7 @@ static void clear_touches() {
// Generate IDs for a framebuffer object and a color renderbuffer
UIScreen *mainscr = [UIScreen mainScreen];
printf("******** screen size %i, %i\n", (int)mainscr.currentMode.size.width, (int)mainscr.currentMode.size.height);
- float minPointSize = MIN(mainscr.bounds.size.width, mainscr.bounds.size.height);
- float minScreenSize = MIN(mainscr.currentMode.size.width, mainscr.currentMode.size.height);
- self.contentScaleFactor = minScreenSize / minPointSize;
+ self.contentScaleFactor = mainscr.nativeScale;
glGenFramebuffersOES(1, &viewFramebuffer);
glGenRenderbuffersOES(1, &viewRenderbuffer);
@@ -489,7 +497,7 @@ static void clear_touches() {
int tid = get_touch_id(touch);
ERR_FAIL_COND(tid == -1);
CGPoint touchPoint = [touch locationInView:self];
- OSIPhone::get_singleton()->mouse_button(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1, tid == 0);
+ OSIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, true, touch.tapCount > 1);
};
};
}
@@ -506,10 +514,9 @@ static void clear_touches() {
continue;
int tid = get_touch_id(touch);
ERR_FAIL_COND(tid == -1);
- int first = get_first_id(touch);
CGPoint touchPoint = [touch locationInView:self];
CGPoint prev_point = [touch previousLocationInView:self];
- OSIPhone::get_singleton()->mouse_move(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, first == tid);
+ OSIPhone::get_singleton()->touch_drag(tid, prev_point.x * self.contentScaleFactor, prev_point.y * self.contentScaleFactor, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor);
};
};
}
@@ -525,9 +532,9 @@ static void clear_touches() {
continue;
int tid = get_touch_id(touch);
ERR_FAIL_COND(tid == -1);
- int rem = remove_touch(touch);
+ remove_touch(touch);
CGPoint touchPoint = [touch locationInView:self];
- OSIPhone::get_singleton()->mouse_button(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false, rem == 0);
+ OSIPhone::get_singleton()->touch_press(tid, touchPoint.x * self.contentScaleFactor, touchPoint.y * self.contentScaleFactor, false, false);
};
};
}
@@ -627,6 +634,7 @@ static void clear_touches() {
}
init_touches();
self.multipleTouchEnabled = YES;
+ self.autocorrectionType = UITextAutocorrectionTypeNo;
printf("******** adding observer for sound routing changes\n");
[[NSNotificationCenter defaultCenter]
diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm
index 6fa189e91..2cdd477ed 100644
--- a/platform/iphone/in_app_store.mm
+++ b/platform/iphone/in_app_store.mm
@@ -238,6 +238,7 @@ Error InAppStore::restore_purchases() {
ret["type"] = "purchase";
ret["result"] = "error";
ret["product_id"] = pid;
+ ret["error"] = String::utf8([transaction.error.localizedDescription UTF8String]);
InAppStore::get_singleton()->_post_event(ret);
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
} break;
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index c4c59431f..f618c80a7 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -190,7 +190,7 @@ void OSIPhone::key(uint32_t p_key, bool p_pressed) {
queue_event(ev);
};
-void OSIPhone::mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick, bool p_use_as_mouse) {
+void OSIPhone::touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick) {
if (!GLOBAL_DEF("debug/disable_touch", false)) {
Ref<InputEventScreenTouch> ev;
@@ -202,28 +202,10 @@ void OSIPhone::mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_
queue_event(ev);
};
- mouse_list.pressed[p_idx] = p_pressed;
-
- if (p_use_as_mouse) {
-
- Ref<InputEventMouseButton> ev;
- ev.instance();
-
- ev->set_position(Vector2(p_x, p_y));
- ev->set_global_position(Vector2(p_x, p_y));
-
- //mouse_list.pressed[p_idx] = p_pressed;
-
- input->set_mouse_position(ev->get_position());
- ev->set_button_index(BUTTON_LEFT);
- ev->set_doubleclick(p_doubleclick);
- ev->set_pressed(p_pressed);
-
- queue_event(ev);
- };
+ touch_list.pressed[p_idx] = p_pressed;
};
-void OSIPhone::mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, bool p_use_as_mouse) {
+void OSIPhone::touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y) {
if (!GLOBAL_DEF("debug/disable_touch", false)) {
@@ -234,21 +216,6 @@ void OSIPhone::mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_
ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y));
queue_event(ev);
};
-
- if (p_use_as_mouse) {
- Ref<InputEventMouseMotion> ev;
- ev.instance();
-
- ev->set_position(Vector2(p_x, p_y));
- ev->set_global_position(Vector2(p_x, p_y));
- ev->set_relative(Vector2(p_x - p_prev_x, p_y - p_prev_y));
-
- input->set_mouse_position(ev->get_position());
- ev->set_speed(input->get_last_mouse_speed());
- ev->set_button_mask(BUTTON_LEFT); // pressed
-
- queue_event(ev);
- };
};
void OSIPhone::queue_event(const Ref<InputEvent> &p_event) {
@@ -262,10 +229,10 @@ void OSIPhone::touches_cancelled() {
for (int i = 0; i < MAX_MOUSE_COUNT; i++) {
- if (mouse_list.pressed[i]) {
+ if (touch_list.pressed[i]) {
// send a mouse_up outside the screen
- mouse_button(i, -1, -1, false, false, false);
+ touch_press(i, -1, -1, false, false);
};
};
};
@@ -376,7 +343,7 @@ Point2 OSIPhone::get_mouse_position() const {
int OSIPhone::get_mouse_button_state() const {
- return mouse_list.pressed[0];
+ return 0;
};
void OSIPhone::set_window_title(const String &p_title){};
@@ -504,6 +471,12 @@ Size2 OSIPhone::get_window_size() const {
return Vector2(video_mode.width, video_mode.height);
}
+extern Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height);
+
+Rect2 OSIPhone::get_window_safe_area() const {
+ return _get_ios_window_safe_area(video_mode.width, video_mode.height);
+}
+
bool OSIPhone::has_touchscreen_ui_hint() const {
return true;
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 2e4458aee..7d73a6fe5 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -106,7 +106,7 @@ private:
};
};
- MouseList mouse_list;
+ MouseList touch_list;
Vector3 last_accel;
@@ -127,8 +127,8 @@ public:
uint8_t get_orientations() const;
- void mouse_button(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick, bool p_use_as_mouse);
- void mouse_move(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y, bool p_use_as_mouse);
+ void touch_press(int p_idx, int p_x, int p_y, bool p_pressed, bool p_doubleclick);
+ void touch_drag(int p_idx, int p_prev_x, int p_prev_y, int p_x, int p_y);
void touches_cancelled();
void key(uint32_t p_key, bool p_pressed);
void set_virtual_keyboard_height(int p_height);
@@ -177,6 +177,7 @@ public:
virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
virtual Size2 get_window_size() const;
+ virtual Rect2 get_window_safe_area() const;
virtual bool has_touchscreen_ui_hint() const;
diff --git a/platform/iphone/power_iphone.cpp b/platform/iphone/power_iphone.cpp
index 95a9aa970..7f9dadc36 100644
--- a/platform/iphone/power_iphone.cpp
+++ b/platform/iphone/power_iphone.cpp
@@ -30,7 +30,7 @@
#include "power_iphone.h"
-bool OS::PowerState::UpdatePowerInfo() {
+bool PowerIphone::UpdatePowerInfo() {
return false;
}
diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py
index 851f4ecb4..a48cb872e 100644
--- a/platform/javascript/detect.py
+++ b/platform/javascript/detect.py
@@ -12,7 +12,7 @@ def get_name():
def can_build():
- return 'EMSCRIPTEN_ROOT' in os.environ or 'EMSCRIPTEN' in os.environ
+ return 'EM_CONFIG' in os.environ or os.path.exists(os.path.expanduser('~/.emscripten'))
def get_opts():
@@ -67,10 +67,20 @@ def configure(env):
## Compiler configuration
env['ENV'] = os.environ
- if 'EMSCRIPTEN_ROOT' in os.environ:
- env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT'])
- elif 'EMSCRIPTEN' in os.environ:
- env.PrependENVPath('PATH', os.environ['EMSCRIPTEN'])
+
+ em_config_file = os.getenv('EM_CONFIG') or os.path.expanduser('~/.emscripten')
+ if not os.path.exists(em_config_file):
+ raise RuntimeError("Emscripten configuration file '%s' does not exist" % em_config_file)
+ with open(em_config_file) as f:
+ em_config = {}
+ try:
+ # Emscripten configuration file is a Python file with simple assignments.
+ exec(f.read(), em_config)
+ except StandardError as e:
+ raise RuntimeError("Emscripten configuration file '%s' is invalid:\n%s" % (em_config_file, e))
+ if 'EMSCRIPTEN_ROOT' not in em_config:
+ raise RuntimeError("'EMSCRIPTEN_ROOT' missing in Emscripten configuration file '%s'" % em_config_file)
+ env.PrependENVPath('PATH', em_config['EMSCRIPTEN_ROOT'])
env['CC'] = 'emcc'
env['CXX'] = 'em++'
@@ -129,13 +139,6 @@ def configure(env):
# This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1.
env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1'])
- # engine.js uses FS but is not currently evaluated by Emscripten, so export FS.
- # TODO: Getting rid of this export is desirable.
- extra_exports = [
- 'FS',
- ]
- env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="%s"' % repr(extra_exports)])
-
env.Append(LINKFLAGS=['-s', 'INVOKE_RUN=0'])
# TODO: Reevaluate usage of this setting now that engine.js manages engine runtime.
diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js
index e6fb48d0d..e4839af43 100644
--- a/platform/javascript/engine.js
+++ b/platform/javascript/engine.js
@@ -1,3 +1,5 @@
+ exposedLibs['PATH'] = PATH;
+ exposedLibs['FS'] = FS;
return Module;
},
};
@@ -12,6 +14,13 @@
var loadingFiles = {};
+ function getPathLeaf(path) {
+
+ while (path.endsWith('/'))
+ path = path.slice(0, -1);
+ return path.slice(path.lastIndexOf('/') + 1);
+ }
+
function getBasePath(path) {
if (path.endsWith('/'))
@@ -23,14 +32,15 @@
function getBaseName(path) {
- path = getBasePath(path);
- return path.slice(path.lastIndexOf('/') + 1);
+ return getPathLeaf(getBasePath(path));
}
Engine = function Engine() {
this.rtenv = null;
+ var LIBS = {};
+
var initPromise = null;
var unloadAfterInit = true;
@@ -80,11 +90,11 @@
return new Promise(function(resolve, reject) {
rtenvProps.onRuntimeInitialized = resolve;
rtenvProps.onAbort = reject;
- rtenvProps.engine.rtenv = Engine.RuntimeEnvironment(rtenvProps);
+ rtenvProps.engine.rtenv = Engine.RuntimeEnvironment(rtenvProps, LIBS);
});
}
- this.preloadFile = function(pathOrBuffer, bufferFilename) {
+ this.preloadFile = function(pathOrBuffer, destPath) {
if (pathOrBuffer instanceof ArrayBuffer) {
pathOrBuffer = new Uint8Array(pathOrBuffer);
@@ -93,14 +103,14 @@
}
if (pathOrBuffer instanceof Uint8Array) {
preloadedFiles.push({
- name: bufferFilename,
+ path: destPath,
buffer: pathOrBuffer
});
return Promise.resolve();
} else if (typeof pathOrBuffer === 'string') {
return loadPromise(pathOrBuffer, preloadProgressTracker).then(function(xhr) {
preloadedFiles.push({
- name: pathOrBuffer,
+ path: destPath || pathOrBuffer,
buffer: xhr.response
});
});
@@ -119,7 +129,12 @@
this.startGame = function(mainPack) {
executableName = getBaseName(mainPack);
- return Promise.all([this.init(getBasePath(mainPack)), this.preloadFile(mainPack)]).then(
+ return Promise.all([
+ // Load from directory,
+ this.init(getBasePath(mainPack)),
+ // ...but write to root where the engine expects it.
+ this.preloadFile(mainPack, getPathLeaf(mainPack))
+ ]).then(
Function.prototype.apply.bind(synchronousStart, this, [])
);
};
@@ -163,7 +178,16 @@
this.rtenv.thisProgram = executableName || getBaseName(basePath);
preloadedFiles.forEach(function(file) {
- this.rtenv.FS.createDataFile('/', file.name, new Uint8Array(file.buffer), true, true, true);
+ var dir = LIBS.PATH.dirname(file.path);
+ try {
+ LIBS.FS.stat(dir);
+ } catch (e) {
+ if (e.code !== 'ENOENT') {
+ throw e;
+ }
+ LIBS.FS.mkdirTree(dir);
+ }
+ LIBS.FS.createDataFile('/', file.path, new Uint8Array(file.buffer), true, true, true);
}, this);
preloadedFiles = null;
diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp
index 118a77835..8d90e01ae 100644
--- a/platform/javascript/http_client_javascript.cpp
+++ b/platform/javascript/http_client_javascript.cpp
@@ -88,7 +88,7 @@ Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Ve
ERR_FAIL_COND_V(port < 0, ERR_UNCONFIGURED);
ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER);
- String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + "/" + p_url;
+ String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + p_url;
godot_xhr_reset(xhr_id);
godot_xhr_open(xhr_id, _methods[p_method], url.utf8().get_data(),
username.empty() ? NULL : username.utf8().get_data(),
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index a275fb792..1b5463e40 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -285,23 +285,6 @@ static EM_BOOL _touchpress_callback(int event_type, const EmscriptenTouchEvent *
_input->parse_input_event(ev);
}
-
- if (touch_event->touches[lowest_id_index].isChanged) {
-
- Ref<InputEventMouseButton> ev_mouse;
- ev_mouse.instance();
- ev_mouse->set_button_mask(_input->get_mouse_button_mask());
- dom2godot_mod(touch_event, ev_mouse);
-
- const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index];
- ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY));
- ev_mouse->set_global_position(ev_mouse->get_position());
-
- ev_mouse->set_button_index(BUTTON_LEFT);
- ev_mouse->set_pressed(event_type == EMSCRIPTEN_EVENT_TOUCHSTART);
-
- _input->parse_input_event(ev_mouse);
- }
return true;
}
@@ -327,24 +310,6 @@ static EM_BOOL _touchmove_callback(int event_type, const EmscriptenTouchEvent *t
_input->parse_input_event(ev);
}
-
- if (touch_event->touches[lowest_id_index].isChanged) {
-
- Ref<InputEventMouseMotion> ev_mouse;
- ev_mouse.instance();
- dom2godot_mod(touch_event, ev_mouse);
- ev_mouse->set_button_mask(_input->get_mouse_button_mask());
-
- const EmscriptenTouchPoint &first_touch = touch_event->touches[lowest_id_index];
- ev_mouse->set_position(Point2(first_touch.canvasX, first_touch.canvasY));
- ev_mouse->set_global_position(ev_mouse->get_position());
-
- ev_mouse->set_relative(ev_mouse->get_position() - _input->get_mouse_position());
- _input->set_mouse_position(ev_mouse->get_position());
- ev_mouse->set_speed(_input->get_last_mouse_speed());
-
- _input->parse_input_event(ev_mouse);
- }
return true;
}
diff --git a/platform/javascript/pre.js b/platform/javascript/pre.js
index 311aa44fd..02194bc75 100644
--- a/platform/javascript/pre.js
+++ b/platform/javascript/pre.js
@@ -1,2 +1,2 @@
var Engine = {
- RuntimeEnvironment: function(Module) {
+ RuntimeEnvironment: function(Module, exposedLibs) {
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index e51a8082f..1e9631fae 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -93,6 +93,7 @@ def configure(env):
env['AR'] = basecmd + "ar"
env['RANLIB'] = basecmd + "ranlib"
env['AS'] = basecmd + "as"
+ env.Append(CCFLAGS=['-D__MACPORTS__']) #hack to fix libvpx MM256_BROADCASTSI128_SI256 define
if (env["CXX"] == "clang++"):
env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND'])
diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm
index 6ccbaf896..64116fa1e 100644
--- a/platform/osx/godot_main_osx.mm
+++ b/platform/osx/godot_main_osx.mm
@@ -101,5 +101,5 @@ int main(int argc, char **argv) {
Main::cleanup();
- return 0;
+ return os.get_exit_code();
};
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index ef23d6114..fbefd41bb 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -192,6 +192,7 @@ static Vector2 get_mouse_pos(NSEvent *event) {
// Note: called before main loop init!
char *utfs = strdup([filename UTF8String]);
OS_OSX::singleton->open_with_filename.parse_utf8(utfs);
+ free(utfs);
return YES;
}
@@ -838,6 +839,108 @@ static int translateKey(unsigned int key) {
return table[key];
}
+struct _KeyCodeMap {
+ UniChar kchar;
+ int kcode;
+};
+
+static const _KeyCodeMap _keycodes[55] = {
+ { '`', KEY_QUOTELEFT },
+ { '~', KEY_ASCIITILDE },
+ { '0', KEY_KP_0 },
+ { '1', KEY_KP_1 },
+ { '2', KEY_KP_2 },
+ { '3', KEY_KP_3 },
+ { '4', KEY_KP_4 },
+ { '5', KEY_KP_5 },
+ { '6', KEY_KP_6 },
+ { '7', KEY_KP_7 },
+ { '8', KEY_KP_8 },
+ { '9', KEY_KP_9 },
+ { '-', KEY_MINUS },
+ { '_', KEY_UNDERSCORE },
+ { '=', KEY_EQUAL },
+ { '+', KEY_PLUS },
+ { 'q', KEY_Q },
+ { 'w', KEY_W },
+ { 'e', KEY_E },
+ { 'r', KEY_R },
+ { 't', KEY_T },
+ { 'y', KEY_Y },
+ { 'u', KEY_U },
+ { 'i', KEY_I },
+ { 'o', KEY_O },
+ { 'p', KEY_P },
+ { '[', KEY_BRACERIGHT },
+ { ']', KEY_BRACELEFT },
+ { '{', KEY_BRACERIGHT },
+ { '}', KEY_BRACELEFT },
+ { 'a', KEY_A },
+ { 's', KEY_S },
+ { 'd', KEY_D },
+ { 'f', KEY_F },
+ { 'g', KEY_G },
+ { 'h', KEY_H },
+ { 'j', KEY_J },
+ { 'k', KEY_K },
+ { 'l', KEY_L },
+ { ';', KEY_SEMICOLON },
+ { ':', KEY_COLON },
+ { '\'', KEY_APOSTROPHE },
+ { '\"', KEY_QUOTEDBL },
+ { '\\', KEY_BACKSLASH },
+ { '#', KEY_NUMBERSIGN },
+ { 'z', KEY_Z },
+ { 'x', KEY_X },
+ { 'c', KEY_C },
+ { 'v', KEY_V },
+ { 'b', KEY_B },
+ { 'n', KEY_N },
+ { 'm', KEY_M },
+ { ',', KEY_COMMA },
+ { '.', KEY_PERIOD },
+ { '/', KEY_SLASH }
+};
+
+static int remapKey(unsigned int key) {
+
+ TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
+ if (!currentKeyboard)
+ return translateKey(key);
+
+ CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
+ if (!layoutData)
+ return nil;
+
+ const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
+
+ UInt32 keysDown = 0;
+ UniChar chars[4];
+ UniCharCount realLength;
+
+ OSStatus err = UCKeyTranslate(keyboardLayout,
+ key,
+ kUCKeyActionDisplay,
+ 0,
+ LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysBit,
+ &keysDown,
+ sizeof(chars) / sizeof(chars[0]),
+ &realLength,
+ chars);
+
+ if (err != noErr) {
+ return translateKey(key);
+ }
+
+ for (unsigned int i = 0; i < 55; i++) {
+ if (_keycodes[i].kchar == chars[0]) {
+ return _keycodes[i].kcode;
+ }
+ }
+ return translateKey(key);
+}
+
- (void)keyDown:(NSEvent *)event {
//disable raw input in IME mode
@@ -847,7 +950,7 @@ static int translateKey(unsigned int key) {
ke.osx_state = [event modifierFlags];
ke.pressed = true;
ke.echo = [event isARepeat];
- ke.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode]));
+ ke.scancode = remapKey([event keyCode]);
ke.unicode = 0;
push_to_key_event_buffer(ke);
@@ -900,7 +1003,7 @@ static int translateKey(unsigned int key) {
}
ke.osx_state = mod;
- ke.scancode = latin_keyboard_keycode_convert(translateKey(key));
+ ke.scancode = remapKey(key);
ke.unicode = 0;
push_to_key_event_buffer(ke);
@@ -916,7 +1019,7 @@ static int translateKey(unsigned int key) {
ke.osx_state = [event modifierFlags];
ke.pressed = false;
ke.echo = false;
- ke.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode]));
+ ke.scancode = remapKey([event keyCode]);
ke.unicode = 0;
push_to_key_event_buffer(ke);
@@ -1371,6 +1474,11 @@ void OS_OSX::set_cursor_shape(CursorShape p_shape) {
if (cursor_shape == p_shape)
return;
+ if (mouse_mode != MOUSE_MODE_VISIBLE) {
+ cursor_shape = p_shape;
+ return;
+ }
+
if (cursors[p_shape] != NULL) {
[cursors[p_shape] set];
} else {
@@ -1404,9 +1512,7 @@ void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
Ref<Texture> texture = p_cursor;
Ref<Image> image = texture->get_data();
- int image_size = 32 * 32;
-
- ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32);
+ ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256);
NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
diff --git a/platform/uwp/app.cpp b/platform/uwp/app.cpp
index 5ff62b38f..c18aa3640 100644
--- a/platform/uwp/app.cpp
+++ b/platform/uwp/app.cpp
@@ -85,8 +85,7 @@ App::App() :
mWindowHeight(0),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
- mEglSurface(EGL_NO_SURFACE),
- number_of_contacts(0) {
+ mEglSurface(EGL_NO_SURFACE) {
}
// The first method called when the IFrameworkView is being created.
@@ -271,48 +270,44 @@ void App::pointer_event(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Cor
last_touch_y[screen_touch->get_index()] = pos.Y;
os->input_event(screen_touch);
- if (number_of_contacts > 1)
- return;
-
- }; // fallthrought of sorts
+ } else {
- Ref<InputEventMouseButton> mouse_button;
- mouse_button.instance();
- mouse_button->set_device(0);
- mouse_button->set_pressed(p_pressed);
- mouse_button->set_button_index(but);
- mouse_button->set_position(Vector2(pos.X, pos.Y));
- mouse_button->set_global_position(Vector2(pos.X, pos.Y));
+ Ref<InputEventMouseButton> mouse_button;
+ mouse_button.instance();
+ mouse_button->set_device(0);
+ mouse_button->set_pressed(p_pressed);
+ mouse_button->set_button_index(but);
+ mouse_button->set_position(Vector2(pos.X, pos.Y));
+ mouse_button->set_global_position(Vector2(pos.X, pos.Y));
- if (p_is_wheel) {
- if (point->Properties->MouseWheelDelta > 0) {
- mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_UP);
- } else if (point->Properties->MouseWheelDelta < 0) {
- mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_LEFT : BUTTON_WHEEL_DOWN);
+ if (p_is_wheel) {
+ if (point->Properties->MouseWheelDelta > 0) {
+ mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_UP);
+ } else if (point->Properties->MouseWheelDelta < 0) {
+ mouse_button->set_button_index(point->Properties->IsHorizontalMouseWheel ? BUTTON_WHEEL_LEFT : BUTTON_WHEEL_DOWN);
+ }
}
- }
-
- last_touch_x[31] = pos.X;
- last_touch_y[31] = pos.Y;
- os->input_event(mouse_button);
+ last_touch_x[31] = pos.X;
+ last_touch_y[31] = pos.Y;
- if (p_is_wheel) {
- // Send release for mouse wheel
- mouse_button->set_pressed(false);
os->input_event(mouse_button);
+
+ if (p_is_wheel) {
+ // Send release for mouse wheel
+ mouse_button->set_pressed(false);
+ os->input_event(mouse_button);
+ }
}
};
void App::OnPointerPressed(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) {
- number_of_contacts++;
pointer_event(sender, args, true);
};
void App::OnPointerReleased(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Core::PointerEventArgs ^ args) {
- number_of_contacts--;
pointer_event(sender, args, false);
};
@@ -351,7 +346,7 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co
Windows::UI::Input::PointerPoint ^ point = args->CurrentPoint;
Windows::Foundation::Point pos = _get_pixel_position(window, point->Position, os);
- if (point->IsInContact && _is_touch(point)) {
+ if (_is_touch(point)) {
Ref<InputEventScreenDrag> screen_drag;
screen_drag.instance();
@@ -361,25 +356,23 @@ void App::OnPointerMoved(Windows::UI::Core::CoreWindow ^ sender, Windows::UI::Co
screen_drag->set_relative(Vector2(screen_drag->get_position().x - last_touch_x[screen_drag->get_index()], screen_drag->get_position().y - last_touch_y[screen_drag->get_index()]));
os->input_event(screen_drag);
- if (number_of_contacts > 1)
- return;
-
- }; // fallthrought of sorts
+ } else {
- // In case the mouse grabbed, MouseMoved will handle this
- if (os->get_mouse_mode() == OS::MouseMode::MOUSE_MODE_CAPTURED)
- return;
+ // In case the mouse grabbed, MouseMoved will handle this
+ if (os->get_mouse_mode() == OS::MouseMode::MOUSE_MODE_CAPTURED)
+ return;
- Ref<InputEventMouseMotion> mouse_motion;
- mouse_motion.instance();
- mouse_motion->set_device(0);
- mouse_motion->set_position(Vector2(pos.X, pos.Y));
- mouse_motion->set_global_position(Vector2(pos.X, pos.Y));
- mouse_motion->set_relative(Vector2(pos.X - last_touch_x[31], pos.Y - last_touch_y[31]));
+ Ref<InputEventMouseMotion> mouse_motion;
+ mouse_motion.instance();
+ mouse_motion->set_device(0);
+ mouse_motion->set_position(Vector2(pos.X, pos.Y));
+ mouse_motion->set_global_position(Vector2(pos.X, pos.Y));
+ mouse_motion->set_relative(Vector2(pos.X - last_touch_x[31], pos.Y - last_touch_y[31]));
- last_mouse_pos = pos;
+ last_mouse_pos = pos;
- os->input_event(mouse_motion);
+ os->input_event(mouse_motion);
+ }
}
void App::OnMouseMoved(MouseDevice ^ mouse_device, MouseEventArgs ^ args) {
diff --git a/platform/uwp/app.h b/platform/uwp/app.h
index c23270b8b..5f69f2cb0 100644
--- a/platform/uwp/app.h
+++ b/platform/uwp/app.h
@@ -107,7 +107,6 @@ namespace GodotUWP
int last_touch_x[32]; // 20 fingers, index 31 reserved for the mouse
int last_touch_y[32];
- int number_of_contacts;
Windows::Foundation::Point last_mouse_pos;
};
}
diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py
index 3ee195e4f..0e7b125dc 100644
--- a/platform/uwp/detect.py
+++ b/platform/uwp/detect.py
@@ -43,6 +43,8 @@ def get_flags():
def configure(env):
+ env.msvc = True
+
if (env["bits"] != "default"):
print("Error: bits argument is disabled for MSVC")
print("""
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index 7d7bee922..3c537b3b5 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -122,6 +122,14 @@ class AppxPackager {
Vector<BlockHash> hashes;
uLong file_crc32;
ZPOS64_T zip_offset;
+
+ FileMeta() :
+ lfh_size(0),
+ compressed(false),
+ compressed_size(0),
+ uncompressed_size(0),
+ file_crc32(0),
+ zip_offset(0) {}
};
String progress_task;
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 13fe781ff..bed5781ae 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -188,6 +188,10 @@ void OS_Windows::initialize_core() {
ticks_start = 0;
ticks_start = get_ticks_usec();
+ // set minimum resolution for periodic timers, otherwise Sleep(n) may wait at least as
+ // long as the windows scheduler resolution (~16-30ms) even for calls like Sleep(1)
+ timeBeginPeriod(1);
+
process_map = memnew((Map<ProcessID, ProcessInfo>));
IP_Unix::make_default();
@@ -342,6 +346,14 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
} break;
case WM_MOUSEMOVE: {
+ if (input->is_emulating_mouse_from_touch()) {
+ // Universal translation enabled; ignore OS translation
+ LPARAM extra = GetMessageExtraInfo();
+ if (IsPenEvent(extra)) {
+ break;
+ }
+ }
+
if (outside) {
//mouse enter
@@ -367,18 +379,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
// Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
if (!window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
break;
- /*
- LPARAM extra = GetMessageExtraInfo();
- if (IsPenEvent(extra)) {
-
- int idx = extra & 0x7f;
- _drag_event(idx, uMsg, wParam, lParam);
- if (idx != 0) {
- return 0;
- };
- // fallthrough for mouse event
- };
- */
Ref<InputEventMouseMotion> mm;
mm.instance();
@@ -448,18 +448,13 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
/*case WM_XBUTTONDOWN:
case WM_XBUTTONUP: */ {
- /*
- LPARAM extra = GetMessageExtraInfo();
- if (IsPenEvent(extra)) {
-
- int idx = extra & 0x7f;
- _touch_event(idx, uMsg, wParam, lParam);
- if (idx != 0) {
- return 0;
- };
- // fallthrough for mouse event
- };
- */
+ if (input->is_emulating_mouse_from_touch()) {
+ // Universal translation enabled; ignore OS translation
+ LPARAM extra = GetMessageExtraInfo();
+ if (IsPenEvent(extra)) {
+ break;
+ }
+ }
Ref<InputEventMouseButton> mb;
mb.instance();
@@ -1261,6 +1256,8 @@ void OS_Windows::finalize() {
void OS_Windows::finalize_core() {
+ timeEndPeriod(1);
+
memdelete(process_map);
TCPServerWinsock::cleanup();
@@ -1299,7 +1296,9 @@ void OS_Windows::set_mouse_mode(MouseMode p_mode) {
if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) {
hCursor = SetCursor(NULL);
} else {
- SetCursor(hCursor);
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ set_cursor_shape(c);
}
}
@@ -1863,6 +1862,11 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) {
if (cursor_shape == p_shape)
return;
+ if (mouse_mode != MOUSE_MODE_VISIBLE) {
+ cursor_shape = p_shape;
+ return;
+ }
+
static const LPCTSTR win_cursors[CURSOR_MAX] = {
IDC_ARROW,
IDC_IBEAM,
@@ -1888,6 +1892,7 @@ void OS_Windows::set_cursor_shape(CursorShape p_shape) {
} else {
SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));
}
+
cursor_shape = p_shape;
}
@@ -1896,26 +1901,25 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap
Ref<Texture> texture = p_cursor;
Ref<Image> image = texture->get_data();
- UINT image_size = 32 * 32;
+ UINT image_size = texture->get_width() * texture->get_height();
UINT size = sizeof(UINT) * image_size;
- ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32);
+ ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256);
// Create the BITMAP with alpha channel
COLORREF *buffer = (COLORREF *)malloc(sizeof(COLORREF) * image_size);
image->lock();
for (UINT index = 0; index < image_size; index++) {
- int column_index = floor(index / 32);
- int row_index = index % 32;
+ int row_index = floor(index / texture->get_width());
+ int column_index = index % texture->get_width();
- Color pcColor = image->get_pixel(row_index, column_index);
- *(buffer + index) = image->get_pixel(row_index, column_index).to_argb32();
+ *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32();
}
image->unlock();
// Using 4 channels, so 4 * 8 bits
- HBITMAP bitmap = CreateBitmap(32, 32, 1, 4 * 8, buffer);
+ HBITMAP bitmap = CreateBitmap(texture->get_width(), texture->get_height(), 1, 4 * 8, buffer);
COLORREF clrTransparent = -1;
// Create the AND and XOR masks for the bitmap
diff --git a/platform/x11/detect.py b/platform/x11/detect.py
index da2b0701b..5820a926e 100644
--- a/platform/x11/detect.py
+++ b/platform/x11/detect.py
@@ -234,10 +234,10 @@ def configure(env):
print("ALSA libraries not found, disabling driver")
if env['pulseaudio']:
- if (os.system("pkg-config --exists libpulse-simple") == 0): # 0 means found
+ if (os.system("pkg-config --exists libpulse") == 0): # 0 means found
print("Enabling PulseAudio")
env.Append(CPPFLAGS=["-DPULSEAUDIO_ENABLED"])
- env.ParseConfig('pkg-config --cflags --libs libpulse-simple')
+ env.ParseConfig('pkg-config --cflags --libs libpulse')
else:
print("PulseAudio development libraries not found, disabling driver")
diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp
index c06c7516d..336068cb1 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/x11/os_x11.cpp
@@ -634,7 +634,7 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) {
bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED);
if (showCursor) {
- XUndefineCursor(x11_display, x11_window); // show cursor
+ XDefineCursor(x11_display, x11_window, cursors[current_cursor]); // show cursor
} else {
XDefineCursor(x11_display, x11_window, null_cursor); // hide cursor
}
@@ -1690,6 +1690,11 @@ void OS_X11::process_xevents() {
if (touch.state.has(index)) // Defensive
break;
touch.state[index] = pos;
+ if (touch.state.size() == 1) {
+ // X11 may send a motion event when a touch gesture begins, that would result
+ // in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out
+ touch.mouse_pos_to_filter = pos;
+ }
input->parse_input_event(st);
} else {
if (!touch.state.has(index)) // Defensive
@@ -1896,6 +1901,18 @@ void OS_X11::process_xevents() {
// to be able to send relative motion events.
Point2i pos(event.xmotion.x, event.xmotion.y);
+ // Avoidance of spurious mouse motion (see handling of touch)
+ bool filter = false;
+ // Adding some tolerance to match better Point2i to Vector2
+ if (touch.state.size() && Vector2(pos).distance_squared_to(touch.mouse_pos_to_filter) < 2) {
+ filter = true;
+ }
+ // Invalidate to avoid filtering a possible legitimate similar event coming later
+ touch.mouse_pos_to_filter = Vector2(1e10, 1e10);
+ if (filter) {
+ break;
+ }
+
if (mouse_mode == MOUSE_MODE_CAPTURED) {
if (pos == Point2i(current_videomode.width / 2, current_videomode.height / 2)) {
@@ -2375,11 +2392,11 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
Ref<Texture> texture = p_cursor;
Ref<Image> image = texture->get_data();
- ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32);
+ ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256);
// Create the cursor structure
XcursorImage *cursor_image = XcursorImageCreate(texture->get_width(), texture->get_height());
- XcursorUInt image_size = 32 * 32;
+ XcursorUInt image_size = texture->get_width() * texture->get_height();
XcursorDim size = sizeof(XcursorPixel) * image_size;
cursor_image->version = 1;
@@ -2393,10 +2410,10 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
image->lock();
for (XcursorPixel index = 0; index < image_size; index++) {
- int column_index = floor(index / 32);
- int row_index = index % 32;
+ int row_index = floor(index / texture->get_width());
+ int column_index = index % texture->get_width();
- *(cursor_image->pixels + index) = image->get_pixel(row_index, column_index).to_argb32();
+ *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32();
}
image->unlock();
diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h
index 610dba071..0a39da77d 100644
--- a/platform/x11/os_x11.h
+++ b/platform/x11/os_x11.h
@@ -127,6 +127,7 @@ class OS_X11 : public OS_Unix {
Vector<int> devices;
XIEventMask event_mask;
Map<int, Vector2> state;
+ Vector2 mouse_pos_to_filter;
} touch;
#endif
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 824f50495..447bd9a09 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -80,6 +80,10 @@ Rect2 AnimatedSprite::_edit_get_rect() const {
return Rect2(ofs, s);
}
+bool AnimatedSprite::_edit_use_rect() const {
+ return true;
+}
+
void SpriteFrames::add_frame(const StringName &p_anim, const Ref<Texture> &p_frame, int p_at_pos) {
Map<StringName, Anim>::Element *E = animations.find(p_anim);
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
index a38adf792..c7606d88a 100644
--- a/scene/2d/animated_sprite.h
+++ b/scene/2d/animated_sprite.h
@@ -157,6 +157,7 @@ public:
virtual Point2 _edit_get_pivot() const;
virtual bool _edit_use_pivot() const;
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void set_sprite_frames(const Ref<SpriteFrames> &p_frames);
Ref<SpriteFrames> get_sprite_frames() const;
diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp
index 446b367be..caa1adebd 100644
--- a/scene/2d/back_buffer_copy.cpp
+++ b/scene/2d/back_buffer_copy.cpp
@@ -55,6 +55,10 @@ Rect2 BackBufferCopy::_edit_get_rect() const {
return rect;
}
+bool BackBufferCopy::_edit_use_rect() const {
+ return true;
+}
+
void BackBufferCopy::set_rect(const Rect2 &p_rect) {
rect = p_rect;
diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h
index b786c2b82..752d56de2 100644
--- a/scene/2d/back_buffer_copy.h
+++ b/scene/2d/back_buffer_copy.h
@@ -54,6 +54,7 @@ protected:
public:
Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void set_rect(const Rect2 &p_rect);
Rect2 get_rect() const;
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index 5cca5705a..27bdeda4a 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -94,6 +94,7 @@ void CanvasItemMaterial::_update_shader() {
case BLEND_MODE_SUB: code += "blend_sub"; break;
case BLEND_MODE_MUL: code += "blend_mul"; break;
case BLEND_MODE_PREMULT_ALPHA: code += "blend_premul_alpha"; break;
+ case BLEND_MODE_DISABLED: code += "blend_disabled"; break;
}
switch (light_mode) {
@@ -245,6 +246,14 @@ CanvasItemMaterial::~CanvasItemMaterial() {
///////////////////////////////////////////////////////////////////
+bool CanvasItem::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
+ if (_edit_use_rect()) {
+ return _edit_get_rect().has_point(p_point);
+ } else {
+ return p_point.length() < p_tolerance;
+ }
+}
+
bool CanvasItem::is_visible_in_tree() const {
if (!is_inside_tree())
@@ -263,7 +272,8 @@ bool CanvasItem::is_visible_in_tree() const {
void CanvasItem::_propagate_visibility_changed(bool p_visible) {
- notification(NOTIFICATION_VISIBILITY_CHANGED);
+ if (!first_draw)
+ notification(NOTIFICATION_VISIBILITY_CHANGED);
if (p_visible)
update(); //todo optimize
@@ -412,7 +422,7 @@ void CanvasItem::_enter_canvas() {
RID canvas;
if (canvas_layer)
- canvas = canvas_layer->get_world_2d()->get_canvas();
+ canvas = canvas_layer->get_canvas();
else
canvas = get_viewport()->find_world_2d()->get_canvas();
@@ -821,6 +831,12 @@ float CanvasItem::draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const
void CanvasItem::_notify_transform(CanvasItem *p_node) {
+ /* This check exists to avoid re-propagating the transform
+ * notification down the tree on dirty nodes. It provides
+ * optimization by avoiding redundancy (nodes are dirty, will get the
+ * notification anyway).
+ */
+
if (/*p_node->xform_change.in_list() &&*/ p_node->global_invalid) {
return; //nothing to do
}
@@ -854,7 +870,7 @@ RID CanvasItem::get_canvas() const {
ERR_FAIL_COND_V(!is_inside_tree(), RID());
if (canvas_layer)
- return canvas_layer->get_world_2d()->get_canvas();
+ return canvas_layer->get_canvas();
else
return get_viewport()->find_world_2d()->get_canvas();
}
@@ -875,9 +891,7 @@ Ref<World2D> CanvasItem::get_world_2d() const {
CanvasItem *tl = get_toplevel();
- if (tl->canvas_layer) {
- return tl->canvas_layer->get_world_2d();
- } else if (tl->get_viewport()) {
+ if (tl->get_viewport()) {
return tl->get_viewport()->find_world_2d();
} else {
return Ref<World2D>();
@@ -976,7 +990,8 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("_edit_set_position", "position"), &CanvasItem::_edit_set_position);
ClassDB::bind_method(D_METHOD("_edit_get_position"), &CanvasItem::_edit_get_position);
- ClassDB::bind_method(D_METHOD("_edit_use_position"), &CanvasItem::_edit_use_position);
+ ClassDB::bind_method(D_METHOD("_edit_set_scale", "scale"), &CanvasItem::_edit_set_scale);
+ ClassDB::bind_method(D_METHOD("_edit_get_scale"), &CanvasItem::_edit_get_scale);
ClassDB::bind_method(D_METHOD("_edit_set_rect", "rect"), &CanvasItem::_edit_set_rect);
ClassDB::bind_method(D_METHOD("_edit_get_rect"), &CanvasItem::_edit_get_rect);
ClassDB::bind_method(D_METHOD("_edit_use_rect"), &CanvasItem::_edit_use_rect);
@@ -1091,6 +1106,7 @@ void CanvasItem::_bind_methods() {
BIND_ENUM_CONSTANT(BLEND_MODE_SUB);
BIND_ENUM_CONSTANT(BLEND_MODE_MUL);
BIND_ENUM_CONSTANT(BLEND_MODE_PREMULT_ALPHA);
+ BIND_ENUM_CONSTANT(BLEND_MODE_DISABLED);
BIND_CONSTANT(NOTIFICATION_TRANSFORM_CHANGED);
BIND_CONSTANT(NOTIFICATION_DRAW);
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index 980fcb410..10d5082df 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -54,7 +54,8 @@ public:
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
- BLEND_MODE_PREMULT_ALPHA
+ BLEND_MODE_PREMULT_ALPHA,
+ BLEND_MODE_DISABLED
};
enum LightMode {
@@ -145,7 +146,8 @@ public:
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MUL,
- BLEND_MODE_PREMULT_ALPHA
+ BLEND_MODE_PREMULT_ALPHA,
+ BLEND_MODE_DISABLED
};
private:
@@ -220,30 +222,46 @@ public:
/* EDITOR */
+ // Save and restore a CanvasItem state
virtual void _edit_set_state(const Dictionary &p_state){};
virtual Dictionary _edit_get_state() const { return Dictionary(); };
- // Used to move/select the node
- virtual void _edit_set_position(const Point2 &p_position){};
- virtual Point2 _edit_get_position() const { return Point2(); };
- virtual bool _edit_use_position() const { return false; };
+ // Used to move the node
+ virtual void _edit_set_position(const Point2 &p_position) = 0;
+ virtual Point2 _edit_get_position() const = 0;
- // Used to resize/move/select the node
+ // Used to scale the node
+ virtual void _edit_set_scale(const Size2 &p_scale) = 0;
+ virtual Size2 _edit_get_scale() const = 0;
+
+ // Used to resize/move the node
virtual void _edit_set_rect(const Rect2 &p_rect){};
- virtual Rect2 _edit_get_rect() const { return Rect2(-32, -32, 64, 64); };
- virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const { return _edit_get_rect().has_point(p_point); }
- Rect2 _edit_get_item_and_children_rect() const;
+ virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); };
virtual bool _edit_use_rect() const { return false; };
+ Rect2 _edit_get_item_and_children_rect() const;
+
+ // used to select the node
+ virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
+
// Used to rotate the node
- virtual void _edit_set_rotation(float p_rotation){};
- virtual float _edit_get_rotation() const { return 0.0; };
- virtual bool _edit_use_rotation() const { return false; };
+ virtual void
+ _edit_set_rotation(float p_rotation){};
+ virtual float _edit_get_rotation() const {
+ return 0.0;
+ };
+ virtual bool _edit_use_rotation() const {
+ return false;
+ };
// Used to set a pivot
virtual void _edit_set_pivot(const Point2 &p_pivot){};
- virtual Point2 _edit_get_pivot() const { return Point2(); };
- virtual bool _edit_use_pivot() const { return false; };
+ virtual Point2 _edit_get_pivot() const {
+ return Point2();
+ };
+ virtual bool _edit_use_pivot() const {
+ return false;
+ };
virtual Size2 _edit_get_minimum_size() const;
@@ -308,7 +326,9 @@ public:
virtual Transform2D get_global_transform_with_canvas() const;
CanvasItem *get_toplevel() const;
- _FORCE_INLINE_ RID get_canvas_item() const { return canvas_item; }
+ _FORCE_INLINE_ RID get_canvas_item() const {
+ return canvas_item;
+ }
void set_block_transform_notify(bool p_enable);
bool is_block_transform_notify_enabled() const;
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index 7e2026d22..9d2a83fda 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -264,6 +264,10 @@ Rect2 CollisionPolygon2D::_edit_get_rect() const {
return aabb;
}
+bool CollisionPolygon2D::_edit_use_rect() const {
+ return true;
+}
+
bool CollisionPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
return Geometry::is_point_in_polygon(p_point, Variant(polygon));
diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h
index 4dafe7d1d..412a92329 100644
--- a/scene/2d/collision_polygon_2d.h
+++ b/scene/2d/collision_polygon_2d.h
@@ -73,6 +73,7 @@ public:
Vector<Point2> get_polygon() const;
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
virtual String get_configuration_warning() const;
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index 0eeb6dafe..83ef4df8f 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -173,11 +173,6 @@ Ref<Shape2D> CollisionShape2D::get_shape() const {
return shape;
}
-Rect2 CollisionShape2D::_edit_get_rect() const {
-
- return rect;
-}
-
bool CollisionShape2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
if (!shape.is_valid())
diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h
index cdff59582..ed2c09d53 100644
--- a/scene/2d/collision_shape_2d.h
+++ b/scene/2d/collision_shape_2d.h
@@ -54,7 +54,6 @@ protected:
static void _bind_methods();
public:
- virtual Rect2 _edit_get_rect() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
void set_shape(const Ref<Shape2D> &p_shape);
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 1220ff299..9a44eb31b 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -65,6 +65,10 @@ Rect2 Light2D::_edit_get_rect() const {
return Rect2(texture_offset - s / 2.0, s);
}
+bool Light2D::_edit_use_rect() const {
+ return true;
+}
+
void Light2D::_update_light_visibility() {
if (!is_inside_tree())
diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h
index 16d8c485d..543805e32 100644
--- a/scene/2d/light_2d.h
+++ b/scene/2d/light_2d.h
@@ -92,6 +92,7 @@ public:
virtual Point2 _edit_get_pivot() const;
virtual bool _edit_use_pivot() const;
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void set_enabled(bool p_enabled);
bool is_enabled() const;
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index d4481583f..c9e5d0f1b 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -107,12 +107,12 @@ OccluderPolygon2D::~OccluderPolygon2D() {
VS::get_singleton()->free(occ_polygon);
}
-#ifdef DEBUG_ENABLED
void LightOccluder2D::_poly_changed() {
+#ifdef DEBUG_ENABLED
update();
-}
#endif
+}
void LightOccluder2D::_notification(int p_what) {
@@ -221,9 +221,7 @@ void LightOccluder2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_occluder_light_mask", "mask"), &LightOccluder2D::set_occluder_light_mask);
ClassDB::bind_method(D_METHOD("get_occluder_light_mask"), &LightOccluder2D::get_occluder_light_mask);
-#ifdef DEBUG_ENABLED
ClassDB::bind_method("_poly_changed", &LightOccluder2D::_poly_changed);
-#endif
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "OccluderPolygon2D"), "set_occluder_polygon", "get_occluder_polygon");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_occluder_light_mask", "get_occluder_light_mask");
diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h
index 5e16351a6..d59c9100b 100644
--- a/scene/2d/light_occluder_2d.h
+++ b/scene/2d/light_occluder_2d.h
@@ -78,9 +78,7 @@ class LightOccluder2D : public Node2D {
int mask;
Ref<OccluderPolygon2D> occluder_polygon;
-#ifdef DEBUG_ENABLED
void _poly_changed();
-#endif
protected:
void _notification(int p_what);
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index ba4a5c557..3e61dd05f 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -62,6 +62,10 @@ Rect2 Line2D::_edit_get_rect() const {
return aabb;
}
+bool Line2D::_edit_use_rect() const {
+ return true;
+}
+
bool Line2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
const real_t d = _width / 2 + p_tolerance;
@@ -266,7 +270,8 @@ void Line2D::_draw() {
lb.indices,
lb.vertices,
lb.colors,
- lb.uvs,
+ lb.uvs, Vector<int>(), Vector<float>(),
+
texture_rid);
// DEBUG
diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h
index 0eba02455..24c48982c 100644
--- a/scene/2d/line_2d.h
+++ b/scene/2d/line_2d.h
@@ -59,6 +59,7 @@ public:
Line2D();
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
void set_points(const PoolVector<Vector2> &p_points);
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
index 95e24505b..3813bd96f 100644
--- a/scene/2d/node_2d.cpp
+++ b/scene/2d/node_2d.cpp
@@ -58,16 +58,39 @@ void Node2D::_edit_set_state(const Dictionary &p_state) {
}
void Node2D::_edit_set_position(const Point2 &p_position) {
- pos = p_position;
- _update_transform();
- _change_notify("position");
+ set_position(p_position);
}
Point2 Node2D::_edit_get_position() const {
return pos;
}
+void Node2D::_edit_set_scale(const Size2 &p_scale) {
+ set_scale(p_scale);
+}
+
+Size2 Node2D::_edit_get_scale() const {
+ return _scale;
+}
+
+void Node2D::_edit_set_rotation(float p_rotation) {
+ angle = p_rotation;
+ _update_transform();
+ _change_notify("rotation");
+ _change_notify("rotation_degrees");
+}
+
+float Node2D::_edit_get_rotation() const {
+ return angle;
+}
+
+bool Node2D::_edit_use_rotation() const {
+ return true;
+}
+
void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) {
+ ERR_FAIL_COND(!_edit_use_rect());
+
Rect2 r = _edit_get_rect();
Vector2 zero_offset;
@@ -83,7 +106,7 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) {
if (r.size.y != 0)
new_scale.y = p_edit_rect.size.y / r.size.y;
- Point2 new_pos = p_edit_rect.position + p_edit_rect.size * zero_offset; //p_edit_rect.pos - r.pos;
+ Point2 new_pos = p_edit_rect.position + p_edit_rect.size * zero_offset;
Transform2D postxf;
postxf.set_rotation_and_scale(angle, _scale);
@@ -97,25 +120,6 @@ void Node2D::_edit_set_rect(const Rect2 &p_edit_rect) {
_change_notify("position");
}
-bool Node2D::_edit_use_rect() const {
- return true;
-}
-
-void Node2D::_edit_set_rotation(float p_rotation) {
- angle = p_rotation;
- _update_transform();
- _change_notify("rotation");
- _change_notify("rotation_degrees");
-}
-
-float Node2D::_edit_get_rotation() const {
- return angle;
-}
-
-bool Node2D::_edit_use_rotation() const {
- return true;
-}
-
void Node2D::_update_xform_values() {
pos = _mat.elements[2];
@@ -444,10 +448,10 @@ void Node2D::_bind_methods() {
ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "", 0), "set_transform", "get_transform");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", 0), "set_global_position", "get_global_position");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_rotation", PROPERTY_HINT_NONE, "", 0), "set_global_rotation", "get_global_rotation");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_rotation_degrees", PROPERTY_HINT_NONE, "", 0), "set_global_rotation_degrees", "get_global_rotation_degrees");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "global_scale", PROPERTY_HINT_NONE, "", 0), "set_global_scale", "get_global_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_scale", PROPERTY_HINT_NONE, "", 0), "set_global_scale", "get_global_scale");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_transform", PROPERTY_HINT_NONE, "", 0), "set_global_transform", "get_global_transform");
ADD_GROUP("Z Index", "");
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
index f817b214f..725686cdf 100644
--- a/scene/2d/node_2d.h
+++ b/scene/2d/node_2d.h
@@ -62,12 +62,16 @@ public:
virtual void _edit_set_position(const Point2 &p_position);
virtual Point2 _edit_get_position() const;
- virtual void _edit_set_rect(const Rect2 &p_edit_rect);
- virtual bool _edit_use_rect() const;
+
+ virtual void _edit_set_scale(const Size2 &p_scale);
+ virtual Size2 _edit_get_scale() const;
+
virtual void _edit_set_rotation(float p_rotation);
virtual float _edit_get_rotation() const;
virtual bool _edit_use_rotation() const;
+ virtual void _edit_set_rect(const Rect2 &p_edit_rect);
+
void set_position(const Point2 &p_pos);
void set_rotation(float p_radians);
void set_rotation_degrees(float p_degrees);
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index 050f98b02..584c2f2c8 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -72,7 +72,7 @@ void ParallaxLayer::_update_mirroring() {
ParallaxBackground *pb = Object::cast_to<ParallaxBackground>(get_parent());
if (pb) {
- RID c = pb->get_world_2d()->get_canvas();
+ RID c = pb->get_canvas();
RID ci = get_canvas_item();
Point2 mirrorScale = mirroring * get_scale();
VisualServer::get_singleton()->canvas_set_item_mirroring(c, ci, mirrorScale);
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index 052a0ac02..7377591f7 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -57,6 +57,10 @@ Rect2 Path2D::_edit_get_rect() const {
return aabb;
}
+bool Path2D::_edit_use_rect() const {
+ return true;
+}
+
bool Path2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
for (int i = 0; i < curve->get_point_count(); i++) {
@@ -92,7 +96,7 @@ void Path2D::_notification(int p_what) {
#else
const float line_width = 2;
#endif
- const Color color = Color(0.5, 0.6, 1.0, 0.7);
+ const Color color = Color(1.0, 1.0, 1.0, 1.0);
for (int i = 0; i < curve->get_point_count(); i++) {
@@ -147,6 +151,7 @@ void Path2D::_bind_methods() {
Path2D::Path2D() {
set_curve(Ref<Curve2D>(memnew(Curve2D))); //create one by default
+ set_self_modulate(Color(0.5, 0.6, 1.0, 0.7));
}
/////////////////////////////////////////////////////////////////////////////////
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index 735d289d7..64696442c 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -48,6 +48,7 @@ protected:
public:
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
void set_curve(const Ref<Curve2D> &p_curve);
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index 2cb1e86f5..4d6ebc81c 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -30,7 +30,7 @@
#include "polygon_2d.h"
#include "core/math/geometry.h"
-
+#include "skeleton_2d.h"
Dictionary Polygon2D::_edit_get_state() const {
Dictionary state = Node2D::_edit_get_state();
state["offset"] = offset;
@@ -73,6 +73,10 @@ Rect2 Polygon2D::_edit_get_rect() const {
return item_rect;
}
+bool Polygon2D::_edit_use_rect() const {
+ return true;
+}
+
bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
return Geometry::is_point_in_polygon(p_point - get_offset(), Variant(polygon));
@@ -87,8 +91,20 @@ void Polygon2D::_notification(int p_what) {
if (polygon.size() < 3)
return;
+ Skeleton2D *skeleton_node = NULL;
+ if (has_node(skeleton)) {
+ skeleton_node = Object::cast_to<Skeleton2D>(get_node(skeleton));
+ }
+
+ if (skeleton_node)
+ VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), skeleton_node->get_skeleton());
+ else
+ VS::get_singleton()->canvas_item_attach_skeleton(get_canvas_item(), RID());
+
Vector<Vector2> points;
Vector<Vector2> uvs;
+ Vector<int> bones;
+ Vector<float> weights;
points.resize(polygon.size());
@@ -176,6 +192,70 @@ void Polygon2D::_notification(int p_what) {
}
}
+ if (!invert && bone_weights.size()) {
+ //a skeleton is set! fill indices and weights
+ int vc = points.size();
+ bones.resize(vc * 4);
+ weights.resize(vc * 4);
+
+ int *bonesw = bones.ptrw();
+ float *weightsw = weights.ptrw();
+
+ for (int i = 0; i < vc * 4; i++) {
+ bonesw[i] = 0;
+ weightsw[i] = 0;
+ }
+
+ for (int i = 0; i < bone_weights.size(); i++) {
+ if (bone_weights[i].weights.size() != points.size()) {
+ continue; //different number of vertices, sorry not using.
+ }
+ if (!skeleton_node->has_node(bone_weights[i].path)) {
+ continue; //node does not exist
+ }
+ Bone2D *bone = Object::cast_to<Bone2D>(skeleton_node->get_node(bone_weights[i].path));
+ if (!bone) {
+ continue;
+ }
+
+ int bone_index = bone->get_index_in_skeleton();
+ PoolVector<float>::Read r = bone_weights[i].weights.read();
+ for (int j = 0; j < vc; j++) {
+ if (r[j] == 0.0)
+ continue; //weight is unpainted, skip
+ //find an index with a weight
+ for (int k = 0; k < 4; k++) {
+ if (weightsw[j * 4 + k] < r[j]) {
+ //this is less than this weight, insert weight!
+ for (int l = 3; l > k; l--) {
+ weightsw[j * 4 + l] = weightsw[j * 4 + l - 1];
+ bonesw[j * 4 + l] = bonesw[j * 4 + l - 1];
+ }
+ weightsw[j * 4 + k] = r[j];
+ bonesw[j * 4 + k] = bone_index;
+ break;
+ }
+ }
+ }
+ }
+
+ //normalize the weights
+ for (int i = 0; i < vc; i++) {
+ float tw = 0;
+ for (int j = 0; j < 4; j++) {
+ tw += weightsw[i * 4 + j];
+ }
+ if (tw == 0)
+ continue; //unpainted, do nothing
+
+ //normalize
+ for (int j = 0; j < 4; j++) {
+ weightsw[i * 4 + j] /= tw;
+ // print_line("point " + itos(i) + " idx " + itos(j) + " index: " + itos(bonesw[i * 4 + j]) + " weight: " + rtos(weightsw[i * 4 + j]));
+ }
+ }
+ }
+
Vector<Color> colors;
int color_len = vertex_colors.size();
colors.resize(len);
@@ -193,7 +273,8 @@ void Polygon2D::_notification(int p_what) {
// VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID());
if (invert || splits.size() == 0) {
- VS::get_singleton()->canvas_item_add_polygon(get_canvas_item(), points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID(), RID(), antialiased);
+ Vector<int> indices = Geometry::triangulate_polygon(points);
+ VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID());
} else {
//use splits
Vector<int> loop;
@@ -264,7 +345,7 @@ void Polygon2D::_notification(int p_what) {
//print_line("loops: " + itos(loops.size()) + " indices: " + itos(indices.size()));
- VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, texture.is_valid() ? texture->get_rid() : RID());
+ VS::get_singleton()->canvas_item_add_triangle_array(get_canvas_item(), indices, points, colors, uvs, bones, weights, texture.is_valid() ? texture->get_rid() : RID());
}
} break;
@@ -426,6 +507,74 @@ Vector2 Polygon2D::get_offset() const {
return offset;
}
+void Polygon2D::add_bone(const NodePath &p_path, const PoolVector<float> &p_weights) {
+
+ Bone bone;
+ bone.path = p_path;
+ bone.weights = p_weights;
+ bone_weights.push_back(bone);
+}
+int Polygon2D::get_bone_count() const {
+ return bone_weights.size();
+}
+NodePath Polygon2D::get_bone_path(int p_index) const {
+ ERR_FAIL_INDEX_V(p_index, bone_weights.size(), NodePath());
+ return bone_weights[p_index].path;
+}
+PoolVector<float> Polygon2D::get_bone_weights(int p_index) const {
+
+ ERR_FAIL_INDEX_V(p_index, bone_weights.size(), PoolVector<float>());
+ return bone_weights[p_index].weights;
+}
+void Polygon2D::erase_bone(int p_idx) {
+
+ ERR_FAIL_INDEX(p_idx, bone_weights.size());
+ bone_weights.remove(p_idx);
+}
+
+void Polygon2D::clear_bones() {
+ bone_weights.clear();
+}
+
+void Polygon2D::set_bone_weights(int p_index, const PoolVector<float> &p_weights) {
+ ERR_FAIL_INDEX(p_index, bone_weights.size());
+ bone_weights[p_index].weights = p_weights;
+ update();
+}
+void Polygon2D::set_bone_path(int p_index, const NodePath &p_path) {
+ ERR_FAIL_INDEX(p_index, bone_weights.size());
+ bone_weights[p_index].path = p_path;
+ update();
+}
+
+Array Polygon2D::_get_bones() const {
+ Array bones;
+ for (int i = 0; i < get_bone_count(); i++) {
+ bones.push_back(get_bone_path(i));
+ bones.push_back(get_bone_weights(i));
+ }
+ return bones;
+}
+void Polygon2D::_set_bones(const Array &p_bones) {
+
+ ERR_FAIL_COND(p_bones.size() & 1);
+ clear_bones();
+ for (int i = 0; i < p_bones.size(); i += 2) {
+ add_bone(p_bones[i], p_bones[i + 1]);
+ }
+}
+
+void Polygon2D::set_skeleton(const NodePath &p_skeleton) {
+ if (skeleton == p_skeleton)
+ return;
+ skeleton = p_skeleton;
+ update();
+}
+
+NodePath Polygon2D::get_skeleton() const {
+ return skeleton;
+}
+
void Polygon2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_polygon", "polygon"), &Polygon2D::set_polygon);
@@ -470,6 +619,21 @@ void Polygon2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &Polygon2D::set_offset);
ClassDB::bind_method(D_METHOD("get_offset"), &Polygon2D::get_offset);
+ ClassDB::bind_method(D_METHOD("add_bone", "path", "weights"), &Polygon2D::add_bone);
+ ClassDB::bind_method(D_METHOD("get_bone_count"), &Polygon2D::get_bone_count);
+ ClassDB::bind_method(D_METHOD("get_bone_path", "index"), &Polygon2D::get_bone_path);
+ ClassDB::bind_method(D_METHOD("get_bone_weights", "index"), &Polygon2D::get_bone_weights);
+ ClassDB::bind_method(D_METHOD("erase_bone", "index"), &Polygon2D::erase_bone);
+ ClassDB::bind_method(D_METHOD("clear_bones"), &Polygon2D::clear_bones);
+ ClassDB::bind_method(D_METHOD("set_bone_path", "index", "path"), &Polygon2D::set_bone_path);
+ ClassDB::bind_method(D_METHOD("set_bone_weights", "index", "weights"), &Polygon2D::set_bone_weights);
+
+ ClassDB::bind_method(D_METHOD("set_skeleton", "skeleton"), &Polygon2D::set_skeleton);
+ ClassDB::bind_method(D_METHOD("get_skeleton"), &Polygon2D::get_skeleton);
+
+ ClassDB::bind_method(D_METHOD("_set_bones", "bones"), &Polygon2D::_set_bones);
+ ClassDB::bind_method(D_METHOD("_get_bones"), &Polygon2D::_get_bones);
+
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv");
ADD_PROPERTY(PropertyInfo(Variant::POOL_INT_ARRAY, "splits"), "set_splits", "get_splits");
@@ -484,10 +648,14 @@ void Polygon2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_scale"), "set_texture_scale", "get_texture_scale");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation_degrees", PROPERTY_HINT_RANGE, "-1440,1440,0.1"), "set_texture_rotation_degrees", "get_texture_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "texture_rotation", PROPERTY_HINT_NONE, "", 0), "set_texture_rotation", "get_texture_rotation");
+ ADD_GROUP("Skeleton", "");
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "skeleton"), "set_skeleton", "get_skeleton");
ADD_GROUP("Invert", "invert_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "invert_enable"), "set_invert", "get_invert");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "invert_border", PROPERTY_HINT_RANGE, "0.1,16384,0.1"), "set_invert_border", "get_invert_border");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "_set_bones", "_get_bones");
}
Polygon2D::Polygon2D() {
diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h
index 3a2417754..575f71d74 100644
--- a/scene/2d/polygon_2d.h
+++ b/scene/2d/polygon_2d.h
@@ -42,6 +42,13 @@ class Polygon2D : public Node2D {
PoolVector<Color> vertex_colors;
PoolVector<int> splits;
+ struct Bone {
+ NodePath path;
+ PoolVector<float> weights;
+ };
+
+ Vector<Bone> bone_weights;
+
Color color;
Ref<Texture> texture;
Size2 tex_scale;
@@ -56,6 +63,11 @@ class Polygon2D : public Node2D {
mutable bool rect_cache_dirty;
mutable Rect2 item_rect;
+ NodePath skeleton;
+
+ Array _get_bones() const;
+ void _set_bones(const Array &p_bones);
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -68,6 +80,7 @@ public:
virtual Point2 _edit_get_pivot() const;
virtual bool _edit_use_pivot() const;
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
@@ -113,6 +126,18 @@ public:
void set_offset(const Vector2 &p_offset);
Vector2 get_offset() const;
+ void add_bone(const NodePath &p_path = NodePath(), const PoolVector<float> &p_weights = PoolVector<float>());
+ int get_bone_count() const;
+ NodePath get_bone_path(int p_index) const;
+ PoolVector<float> get_bone_weights(int p_index) const;
+ void erase_bone(int p_idx);
+ void clear_bones();
+ void set_bone_weights(int p_index, const PoolVector<float> &p_weights);
+ void set_bone_path(int p_index, const NodePath &p_path);
+
+ void set_skeleton(const NodePath &p_skeleton);
+ NodePath get_skeleton() const;
+
Polygon2D();
};
diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp
index 37f6aaa2d..64d23719e 100644
--- a/scene/2d/position_2d.cpp
+++ b/scene/2d/position_2d.cpp
@@ -44,6 +44,10 @@ Rect2 Position2D::_edit_get_rect() const {
return Rect2(Point2(-10, -10), Size2(20, 20));
}
+bool Position2D::_edit_use_rect() const {
+ return false;
+}
+
void Position2D::_notification(int p_what) {
switch (p_what) {
diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h
index 6f6a34c45..bff474ccc 100644
--- a/scene/2d/position_2d.h
+++ b/scene/2d/position_2d.h
@@ -44,6 +44,7 @@ protected:
public:
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
Position2D();
};
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 5a563143a..255d2d38d 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -101,7 +101,7 @@ void RayCast2D::set_enabled(bool p_enabled) {
enabled = p_enabled;
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint())
- set_physics_process(p_enabled);
+ set_physics_process_internal(p_enabled);
if (!p_enabled)
collided = false;
}
@@ -141,9 +141,9 @@ void RayCast2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (enabled && !Engine::get_singleton()->is_editor_hint())
- set_physics_process(true);
+ set_physics_process_internal(true);
else
- set_physics_process(false);
+ set_physics_process_internal(false);
if (Object::cast_to<CollisionObject2D>(get_parent())) {
if (exclude_parent_body)
@@ -155,7 +155,7 @@ void RayCast2D::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
if (enabled)
- set_physics_process(false);
+ set_physics_process_internal(false);
} break;
@@ -183,7 +183,7 @@ void RayCast2D::_notification(int p_what) {
} break;
- case NOTIFICATION_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!enabled)
break;
diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp
index b6cb734ce..9480f1817 100644
--- a/scene/2d/screen_button.cpp
+++ b/scene/2d/screen_button.cpp
@@ -335,6 +335,10 @@ Rect2 TouchScreenButton::_edit_get_rect() const {
return Rect2(Size2(), texture->get_size());
}
+bool TouchScreenButton::_edit_use_rect() const {
+ return true;
+}
+
void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) {
visibility = p_mode;
update();
diff --git a/scene/2d/screen_button.h b/scene/2d/screen_button.h
index e6f2a2f3c..b2fafcc93 100644
--- a/scene/2d/screen_button.h
+++ b/scene/2d/screen_button.h
@@ -104,6 +104,7 @@ public:
bool is_pressed() const;
Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
TouchScreenButton();
};
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 705e82bcb..2363c791f 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -12,6 +12,8 @@ void Bone2D::_notification(int p_what) {
break;
if (!Object::cast_to<Bone2D>(parent))
break; //skeletons must be chained to Bone2Ds.
+
+ parent = parent->get_parent();
}
if (skeleton) {
@@ -26,6 +28,11 @@ void Bone2D::_notification(int p_what) {
skeleton->_make_transform_dirty();
}
}
+ if (p_what == NOTIFICATION_MOVED_IN_PARENT) {
+ if (skeleton) {
+ skeleton->_make_bone_setup_dirty();
+ }
+ }
if (p_what == NOTIFICATION_EXIT_TREE) {
if (skeleton) {
@@ -46,12 +53,22 @@ void Bone2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rest", "rest"), &Bone2D::set_rest);
ClassDB::bind_method(D_METHOD("get_rest"), &Bone2D::get_rest);
ClassDB::bind_method(D_METHOD("apply_rest"), &Bone2D::apply_rest);
+ ClassDB::bind_method(D_METHOD("get_skeleton_rest"), &Bone2D::get_skeleton_rest);
+ ClassDB::bind_method(D_METHOD("get_index_in_skeleton"), &Bone2D::get_index_in_skeleton);
+
+ ClassDB::bind_method(D_METHOD("set_default_length", "default_length"), &Bone2D::set_default_length);
+ ClassDB::bind_method(D_METHOD("get_default_length"), &Bone2D::get_default_length);
+
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D,"rest"),"set_rest","get_rest");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"default_length",PROPERTY_HINT_RANGE,"1,1024,1"),"set_default_length","get_default_length");
}
void Bone2D::set_rest(const Transform2D &p_rest) {
rest = p_rest;
if (skeleton)
skeleton->_make_bone_setup_dirty();
+
+ update_configuration_warning();
}
Transform2D Bone2D::get_rest() const {
@@ -71,22 +88,56 @@ void Bone2D::apply_rest() {
set_transform(rest);
}
+void Bone2D::set_default_length(float p_length) {
+
+ default_length=p_length;
+
+}
+
+float Bone2D::get_default_length() const {
+ return default_length;
+}
+
+int Bone2D::get_index_in_skeleton() const {
+ ERR_FAIL_COND_V(!skeleton,-1);
+ skeleton->_update_bone_setup();
+ return skeleton_index;
+}
String Bone2D::get_configuration_warning() const {
+
+ String warning = Node2D::get_configuration_warning();
if (!skeleton) {
+ if (warning!=String()) {
+ warning+="\n";
+ }
if (parent_bone) {
- return TTR("This Bone2D chain should end at a Skeleton2D node.");
+ warning+=TTR("This Bone2D chain should end at a Skeleton2D node.");
} else {
- return TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node.");
+ warning+=TTR("A Bone2D only works with a Skeleton2D or another Bone2D as parent node.");
+ }
+ }
+
+ if (rest==Transform2D(0,0,0,0,0,0)) {
+ if (warning!=String()) {
+ warning+="\n";
}
+ warning+=TTR("This bone lacks a proper REST pose. Go to the Skeleton2D node and set one.");
+
}
- return Node2D::get_configuration_warning();
+ return warning;
}
Bone2D::Bone2D() {
skeleton = NULL;
parent_bone = NULL;
+ skeleton_index=-1;
+ default_length=16;
set_notify_local_transform(true);
+ //this is a clever hack so the bone knows no rest has been set yet, allowing to show an error.
+ for(int i=0;i<3;i++) {
+ rest[i]=Vector2(0,0);
+ }
}
//////////////////////////////////////
@@ -112,7 +163,14 @@ void Skeleton2D::_update_bone_setup() {
bones.sort(); //sorty so they are always in the same order/index
for (int i = 0; i < bones.size(); i++) {
- bones[i].rest_inverse = bones[i].bone->get_skeleton_rest(); //bind pose
+ bones[i].rest_inverse = bones[i].bone->get_skeleton_rest().affine_inverse(); //bind pose
+ bones[i].bone->skeleton_index=i;
+ Bone2D *parent_bone = Object::cast_to<Bone2D>(bones[i].bone->get_parent());
+ if (parent_bone) {
+ bones[i].parent_index=parent_bone->skeleton_index;
+ } else {
+ bones[i].parent_index=-1;
+ }
}
transform_dirty = true;
@@ -140,13 +198,20 @@ void Skeleton2D::_update_transform() {
transform_dirty = false;
- Transform2D global_xform = get_global_transform();
- Transform2D global_xform_inverse = global_xform.affine_inverse();
+ for (int i = 0; i < bones.size(); i++) {
+
+ ERR_CONTINUE(bones[i].parent_index>=i);
+ if (bones[i].parent_index>=0) {
+ bones[i].accum_transform = bones[bones[i].parent_index].accum_transform * bones[i].bone->get_transform();
+ } else {
+ bones[i].accum_transform = bones[i].bone->get_transform();
+ }
+ }
for (int i = 0; i < bones.size(); i++) {
- Transform2D final_xform = bones[i].rest_inverse * bones[i].bone->get_relative_transform_to_parent(this);
- VS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, global_xform * (final_xform * global_xform_inverse));
+ Transform2D final_xform = bones[i].accum_transform * bones[i].rest_inverse;
+ VS::get_singleton()->skeleton_bone_set_transform_2d(skeleton, i, final_xform);
}
}
@@ -177,10 +242,12 @@ void Skeleton2D::_notification(int p_what) {
_update_bone_setup();
if (transform_dirty)
_update_transform();
+
+ request_ready();
}
if (p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- _make_transform_dirty();
+ VS::get_singleton()->skeleton_set_base_transform_2d(skeleton,get_global_transform());
}
}
@@ -201,6 +268,7 @@ void Skeleton2D::_bind_methods() {
Skeleton2D::Skeleton2D() {
bone_setup_dirty = true;
transform_dirty = true;
+
skeleton = VS::get_singleton()->skeleton_create();
}
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index 49199f684..cd270dac8 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -11,6 +11,10 @@ class Bone2D : public Node2D {
Bone2D *parent_bone;
Skeleton2D *skeleton;
Transform2D rest;
+ float default_length;
+
+friend class Skeleton2D;
+ int skeleton_index;
protected:
void _notification(int p_what);
@@ -24,6 +28,11 @@ public:
String get_configuration_warning() const;
+ void set_default_length(float p_length);
+ float get_default_length() const;
+
+ int get_index_in_skeleton() const;
+
Bone2D();
};
@@ -37,6 +46,8 @@ class Skeleton2D : public Node2D {
return p_bone.bone->is_greater_than(bone);
}
Bone2D *bone;
+ int parent_index;
+ Transform2D accum_transform;
Transform2D rest_inverse;
};
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index 67f016ae7..bc39368c8 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -58,6 +58,14 @@ bool Sprite::_edit_use_pivot() const {
return true;
}
+Rect2 Sprite::_edit_get_rect() const {
+ return get_rect();
+}
+
+bool Sprite::_edit_use_rect() const {
+ return true;
+}
+
void Sprite::_get_rects(Rect2 &r_src_rect, Rect2 &r_dst_rect, bool &r_filter_clip) const {
Size2 s;
@@ -323,7 +331,31 @@ bool Sprite::_edit_is_selected_on_click(const Point2 &p_point, double p_toleranc
}
ERR_FAIL_COND_V(image.is_null(), false);
+ if (image->is_compressed()) {
+ return dst_rect.has_point(p_point);
+ }
+ bool is_repeat = texture->get_flags() & Texture::FLAG_REPEAT;
+ bool is_mirrored_repeat = texture->get_flags() & Texture::FLAG_MIRRORED_REPEAT;
+ if (is_repeat) {
+ int mirror_x = 0;
+ int mirror_y = 0;
+ if (is_mirrored_repeat) {
+ mirror_x = (int)(q.x / texture->get_size().width);
+ mirror_y = (int)(q.y / texture->get_size().height);
+ }
+ q.x = Math::fmod(q.x, texture->get_size().width);
+ q.y = Math::fmod(q.y, texture->get_size().height);
+ if (mirror_x % 2 == 1) {
+ q.x = texture->get_size().width - q.x - 1;
+ }
+ if (mirror_y % 2 == 1) {
+ q.y = texture->get_size().height - q.y - 1;
+ }
+ } else {
+ q.x = MIN(q.x, texture->get_size().width - 1);
+ q.y = MIN(q.y, texture->get_size().height - 1);
+ }
image->lock();
const Color c = image->get_pixel((int)q.x, (int)q.y);
image->unlock();
diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h
index abd04515e..609ad8bb3 100644
--- a/scene/2d/sprite.h
+++ b/scene/2d/sprite.h
@@ -74,7 +74,9 @@ public:
virtual Point2 _edit_get_pivot() const;
virtual bool _edit_use_pivot() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
- virtual Rect2 _edit_get_rect() const { return get_rect(); }
+
+ virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void set_texture(const Ref<Texture> &p_texture);
Ref<Texture> get_texture() const;
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index b602839b9..3d3f43d5c 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -265,12 +265,18 @@ void TileMap::_update_dirty_quadrants() {
SceneTree *st = SceneTree::get_singleton();
Color debug_collision_color;
+ Color debug_navigation_color;
bool debug_shapes = st && st->is_debugging_collisions_hint();
if (debug_shapes) {
debug_collision_color = st->get_debug_collisions_color();
}
+ bool debug_navigation = st && st->is_debugging_navigation_hint();
+ if (debug_navigation) {
+ debug_navigation_color = st->get_debug_navigation_color();
+ }
+
while (dirty_quadrant_list.first()) {
Quadrant &q = *dirty_quadrant_list.first()->self();
@@ -298,6 +304,7 @@ void TileMap::_update_dirty_quadrants() {
}
q.occluder_instances.clear();
Ref<ShaderMaterial> prev_material;
+ int prev_z_index;
RID prev_canvas_item;
RID prev_debug_canvas_item;
@@ -318,11 +325,12 @@ void TileMap::_update_dirty_quadrants() {
continue;
Ref<ShaderMaterial> mat = tile_set->tile_get_material(c.id);
+ int z_index = tile_set->tile_get_z_index(c.id);
RID canvas_item;
RID debug_canvas_item;
- if (prev_canvas_item == RID() || prev_material != mat) {
+ if (prev_canvas_item == RID() || prev_material != mat || prev_z_index != z_index) {
canvas_item = vs->canvas_item_create();
if (mat.is_valid())
@@ -333,6 +341,7 @@ void TileMap::_update_dirty_quadrants() {
xform.set_origin(q.pos);
vs->canvas_item_set_transform(canvas_item, xform);
vs->canvas_item_set_light_mask(canvas_item, get_light_mask());
+ vs->canvas_item_set_z_index(canvas_item, z_index);
q.canvas_items.push_back(canvas_item);
@@ -348,6 +357,7 @@ void TileMap::_update_dirty_quadrants() {
prev_canvas_item = canvas_item;
prev_material = mat;
+ prev_z_index = z_index;
} else {
canvas_item = prev_canvas_item;
@@ -497,6 +507,55 @@ void TileMap::_update_dirty_quadrants() {
np.id = pid;
np.xform = xform;
q.navpoly_ids[E->key()] = np;
+
+ if (debug_navigation) {
+ RID debug_navigation_item = vs->canvas_item_create();
+ vs->canvas_item_set_parent(debug_navigation_item, canvas_item);
+ vs->canvas_item_set_z_as_relative_to_parent(debug_navigation_item, false);
+ vs->canvas_item_set_z_index(debug_navigation_item, VS::CANVAS_ITEM_Z_MAX - 2); // Display one below collision debug
+
+ if (debug_navigation_item.is_valid()) {
+ PoolVector<Vector2> navigation_polygon_vertices = navpoly->get_vertices();
+ int vsize = navigation_polygon_vertices.size();
+
+ if (vsize > 2) {
+ Vector<Color> colors;
+ Vector<Vector2> vertices;
+ vertices.resize(vsize);
+ colors.resize(vsize);
+ {
+ PoolVector<Vector2>::Read vr = navigation_polygon_vertices.read();
+ for (int i = 0; i < vsize; i++) {
+ vertices[i] = vr[i];
+ colors[i] = debug_navigation_color;
+ }
+ }
+
+ Vector<int> indices;
+
+ for (int i = 0; i < navpoly->get_polygon_count(); i++) {
+ Vector<int> polygon = navpoly->get_polygon(i);
+
+ for (int j = 2; j < polygon.size(); j++) {
+
+ int kofs[3] = { 0, j - 1, j };
+ for (int k = 0; k < 3; k++) {
+
+ int idx = polygon[kofs[k]];
+ ERR_FAIL_INDEX(idx, vsize);
+ indices.push_back(idx);
+ }
+ }
+ }
+ Transform2D navxform;
+ navxform.set_origin(offset.floor());
+ _fix_cell_transform(navxform, c, npoly_ofs + center_ofs, s);
+
+ vs->canvas_item_set_transform(debug_navigation_item, navxform);
+ vs->canvas_item_add_triangle_array(debug_navigation_item, indices, vertices, colors);
+ }
+ }
+ }
}
}
@@ -1064,6 +1123,10 @@ Rect2 TileMap::_edit_get_rect() const {
return rect_cache;
}
+bool TileMap::_edit_use_rect() const {
+ return true;
+}
+
void TileMap::set_collision_layer(uint32_t p_layer) {
collision_layer = p_layer;
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 587bd3b68..07947004b 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -245,6 +245,7 @@ public:
int get_cellv(const Vector2 &p_pos) const;
Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void make_bitmask_area_dirty(const Vector2 &p_pos);
void update_bitmask_area(const Vector2 &p_pos);
diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp
index 4b38534d9..ddca97e60 100644
--- a/scene/2d/visibility_notifier_2d.cpp
+++ b/scene/2d/visibility_notifier_2d.cpp
@@ -89,6 +89,10 @@ Rect2 VisibilityNotifier2D::_edit_get_rect() const {
return rect;
}
+bool VisibilityNotifier2D::_edit_use_rect() const {
+ return true;
+}
+
Rect2 VisibilityNotifier2D::get_rect() const {
return rect;
diff --git a/scene/2d/visibility_notifier_2d.h b/scene/2d/visibility_notifier_2d.h
index 93a35a709..c4e12dfa2 100644
--- a/scene/2d/visibility_notifier_2d.h
+++ b/scene/2d/visibility_notifier_2d.h
@@ -56,6 +56,7 @@ protected:
public:
virtual Rect2 _edit_get_rect() const;
+ virtual bool _edit_use_rect() const;
void set_rect(const Rect2 &p_rect);
Rect2 get_rect() const;
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
index 109bf3adc..1b506e0c4 100644
--- a/scene/3d/camera.h
+++ b/scene/3d/camera.h
@@ -132,9 +132,9 @@ public:
virtual Transform get_camera_transform() const;
- Vector3 project_ray_normal(const Point2 &p_pos) const;
+ virtual Vector3 project_ray_normal(const Point2 &p_pos) const;
virtual Vector3 project_ray_origin(const Point2 &p_pos) const;
- Vector3 project_local_ray_normal(const Point2 &p_pos) const;
+ virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const;
virtual Point2 unproject_position(const Vector3 &p_pos) const;
bool is_position_behind(const Vector3 &p_pos) const;
virtual Vector3 project_position(const Point2 &p_point) const;
diff --git a/scene/3d/collision_polygon.cpp b/scene/3d/collision_polygon.cpp
index 3a77360bc..379dd21c3 100644
--- a/scene/3d/collision_polygon.cpp
+++ b/scene/3d/collision_polygon.cpp
@@ -173,6 +173,9 @@ String CollisionPolygon::get_configuration_warning() const {
return String();
}
+bool CollisionPolygon::_is_editable_3d_polygon() const {
+ return true;
+}
void CollisionPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_depth", "depth"), &CollisionPolygon::set_depth);
@@ -184,6 +187,8 @@ void CollisionPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon::set_disabled);
ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon::is_disabled);
+ ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CollisionPolygon::_is_editable_3d_polygon);
+
ADD_PROPERTY(PropertyInfo(Variant::REAL, "depth"), "set_depth", "get_depth");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
diff --git a/scene/3d/collision_polygon.h b/scene/3d/collision_polygon.h
index 971c67f1a..f1f137c9c 100644
--- a/scene/3d/collision_polygon.h
+++ b/scene/3d/collision_polygon.h
@@ -53,6 +53,8 @@ protected:
void _update_in_shape_owner(bool p_xform_only = false);
+ bool _is_editable_3d_polygon() const;
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
index 8617bbc2f..a39ac5a8f 100644
--- a/scene/3d/particles.cpp
+++ b/scene/3d/particles.cpp
@@ -849,9 +849,9 @@ void ParticlesMaterial::_update_shader() {
code += " vec4(1.250, -1.050, -0.203, 0.0),\n";
code += " vec4(0.000, 0.000, 0.000, 0.0)) * hue_rot_s;\n";
if (color_ramp.is_valid()) {
- code += " COLOR = textureLod(color_ramp,vec2(CUSTOM.y,0.0),0.0) * hue_rot_mat;\n";
+ code += " COLOR = hue_rot_mat * textureLod(color_ramp,vec2(CUSTOM.y,0.0),0.0);\n";
} else {
- code += " COLOR = color_value * hue_rot_mat;\n";
+ code += " COLOR = hue_rot_mat * color_value;\n";
}
if (emission_color_texture.is_valid() && emission_shape >= EMISSION_SHAPE_POINTS) {
code += " COLOR*= texelFetch(emission_texture_color,emission_tex_ofs,0);\n";
@@ -1028,8 +1028,6 @@ void ParticlesMaterial::set_param(Parameter p_param, float p_value) {
case PARAM_ANIM_OFFSET: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset, p_value);
} break;
- case PARAM_MAX: {
- };
}
}
float ParticlesMaterial::get_param(Parameter p_param) const {
@@ -1082,8 +1080,6 @@ void ParticlesMaterial::set_param_randomness(Parameter p_param, float p_value) {
case PARAM_ANIM_OFFSET: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_random, p_value);
} break;
- case PARAM_MAX: {
- };
}
}
float ParticlesMaterial::get_param_randomness(Parameter p_param) const {
@@ -1160,8 +1156,6 @@ void ParticlesMaterial::set_param_texture(Parameter p_param, const Ref<Texture>
case PARAM_ANIM_OFFSET: {
VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->anim_offset_texture, p_texture);
} break;
- case PARAM_MAX: {
- };
}
_queue_shader_change();
@@ -1233,28 +1227,19 @@ void ParticlesMaterial::set_emission_box_extents(Vector3 p_extents) {
void ParticlesMaterial::set_emission_point_texture(const Ref<Texture> &p_points) {
emission_point_texture = p_points;
- RID texture;
- if (p_points.is_valid())
- texture = p_points->get_rid();
- VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, texture);
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_points, p_points);
}
void ParticlesMaterial::set_emission_normal_texture(const Ref<Texture> &p_normals) {
emission_normal_texture = p_normals;
- RID texture;
- if (p_normals.is_valid())
- texture = p_normals->get_rid();
- VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, texture);
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_normal, p_normals);
}
void ParticlesMaterial::set_emission_color_texture(const Ref<Texture> &p_colors) {
emission_color_texture = p_colors;
- RID texture;
- if (p_colors.is_valid())
- texture = p_colors->get_rid();
- VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, texture);
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_color, p_colors);
_queue_shader_change();
}
@@ -1316,10 +1301,7 @@ void ParticlesMaterial::set_trail_size_modifier(const Ref<CurveTexture> &p_trail
curve->ensure_default_setup();
}
- RID texture;
- if (p_trail_size_modifier.is_valid())
- texture = p_trail_size_modifier->get_rid();
- VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_size_modifier, texture);
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_size_modifier, curve);
_queue_shader_change();
}
@@ -1331,10 +1313,7 @@ Ref<CurveTexture> ParticlesMaterial::get_trail_size_modifier() const {
void ParticlesMaterial::set_trail_color_modifier(const Ref<GradientTexture> &p_trail_color_modifier) {
trail_color_modifier = p_trail_color_modifier;
- RID texture;
- if (p_trail_color_modifier.is_valid())
- texture = p_trail_color_modifier->get_rid();
- VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_color_modifier, texture);
+ VisualServer::get_singleton()->material_set_param(_get_material(), shader_names->trail_color_modifier, p_trail_color_modifier);
_queue_shader_change();
}
diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp
index 7ac7f74bb..57d79c960 100644
--- a/scene/3d/path.cpp
+++ b/scene/3d/path.cpp
@@ -40,6 +40,9 @@ void Path::_curve_changed() {
if (is_inside_tree() && Engine::get_singleton()->is_editor_hint())
update_gizmo();
+ if (is_inside_tree()) {
+ emit_signal("curve_changed");
+ }
}
void Path::set_curve(const Ref<Curve3D> &p_curve) {
@@ -68,6 +71,8 @@ void Path::_bind_methods() {
ClassDB::bind_method(D_METHOD("_curve_changed"), &Path::_curve_changed);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve3D"), "set_curve", "get_curve");
+
+ ADD_SIGNAL(MethodInfo("curve_changed"));
}
Path::Path() {
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index ff4a807de..452879998 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -34,6 +34,10 @@
#include "method_bind_ext.gen.inc"
#include "scene/scene_string_names.h"
+#ifdef TOOLS_ENABLED
+#include "editor/plugins/spatial_editor_plugin.h"
+#endif
+
void PhysicsBody::_notification(int p_what) {
/*
@@ -781,7 +785,7 @@ String RigidBody::get_configuration_warning() const {
String warning = CollisionObject::get_configuration_warning();
- if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(0).length() - 1.0) > 0.05)) {
+ if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) {
if (warning != String()) {
warning += "\n";
}
@@ -1266,3 +1270,1108 @@ KinematicCollision::KinematicCollision() {
collision.local_shape = 0;
owner = NULL;
}
+
+///////////////////////////////////////
+
+bool PhysicalBone::JointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ return false;
+}
+
+bool PhysicalBone::JointData::_get(const StringName &p_name, Variant &r_ret) const {
+ return false;
+}
+
+void PhysicalBone::JointData::_get_property_list(List<PropertyInfo> *p_list) const {
+}
+
+bool PhysicalBone::PinJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ if (JointData::_set(p_name, p_value, j)) {
+ return true;
+ }
+
+ if ("joint_constraints/bias" == p_name) {
+ bias = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_BIAS, bias);
+
+ } else if ("joint_constraints/damping" == p_name) {
+ damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_DAMPING, damping);
+
+ } else if ("joint_constraints/impulse_clamp" == p_name) {
+ impulse_clamp = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->pin_joint_set_param(j, PhysicsServer::PIN_JOINT_IMPULSE_CLAMP, impulse_clamp);
+
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool PhysicalBone::PinJointData::_get(const StringName &p_name, Variant &r_ret) const {
+ if (JointData::_get(p_name, r_ret)) {
+ return true;
+ }
+
+ if ("joint_constraints/bias" == p_name) {
+ r_ret = bias;
+ } else if ("joint_constraints/damping" == p_name) {
+ r_ret = damping;
+ } else if ("joint_constraints/impulse_clamp" == p_name) {
+ r_ret = impulse_clamp;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicalBone::PinJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+ JointData::_get_property_list(p_list);
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/damping", PROPERTY_HINT_RANGE, "0.01,8.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/impulse_clamp", PROPERTY_HINT_RANGE, "0.0,64.0,0.01"));
+}
+
+bool PhysicalBone::ConeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ if (JointData::_set(p_name, p_value, j)) {
+ return true;
+ }
+
+ if ("joint_constraints/swing_span" == p_name) {
+ swing_span = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN, swing_span);
+
+ } else if ("joint_constraints/twist_span" == p_name) {
+ twist_span = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN, twist_span);
+
+ } else if ("joint_constraints/bias" == p_name) {
+ bias = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_BIAS, bias);
+
+ } else if ("joint_constraints/softness" == p_name) {
+ softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_SOFTNESS, softness);
+
+ } else if ("joint_constraints/relaxation" == p_name) {
+ relaxation = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer::CONE_TWIST_JOINT_RELAXATION, relaxation);
+
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool PhysicalBone::ConeJointData::_get(const StringName &p_name, Variant &r_ret) const {
+ if (JointData::_get(p_name, r_ret)) {
+ return true;
+ }
+
+ if ("joint_constraints/swing_span" == p_name) {
+ r_ret = Math::rad2deg(swing_span);
+ } else if ("joint_constraints/twist_span" == p_name) {
+ r_ret = Math::rad2deg(twist_span);
+ } else if ("joint_constraints/bias" == p_name) {
+ r_ret = bias;
+ } else if ("joint_constraints/softness" == p_name) {
+ r_ret = softness;
+ } else if ("joint_constraints/relaxation" == p_name) {
+ r_ret = relaxation;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicalBone::ConeJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+ JointData::_get_property_list(p_list);
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/swing_span", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/twist_span", PROPERTY_HINT_RANGE, "-40000,40000,0.1"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/bias", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/relaxation", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+}
+
+bool PhysicalBone::HingeJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ if (JointData::_set(p_name, p_value, j)) {
+ return true;
+ }
+
+ if ("joint_constraints/angular_limit_enabled" == p_name) {
+ angular_limit_enabled = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_flag(j, PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT, angular_limit_enabled);
+
+ } else if ("joint_constraints/angular_limit_upper" == p_name) {
+ angular_limit_upper = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_UPPER, angular_limit_upper);
+
+ } else if ("joint_constraints/angular_limit_lower" == p_name) {
+ angular_limit_lower = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_LOWER, angular_limit_lower);
+
+ } else if ("joint_constraints/angular_limit_bias" == p_name) {
+ angular_limit_bias = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_BIAS, angular_limit_bias);
+
+ } else if ("joint_constraints/angular_limit_softness" == p_name) {
+ angular_limit_softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS, angular_limit_softness);
+
+ } else if ("joint_constraints/angular_limit_relaxation" == p_name) {
+ angular_limit_relaxation = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_param(j, PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION, angular_limit_relaxation);
+
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool PhysicalBone::HingeJointData::_get(const StringName &p_name, Variant &r_ret) const {
+ if (JointData::_get(p_name, r_ret)) {
+ return true;
+ }
+
+ if ("joint_constraints/angular_limit_enabled" == p_name) {
+ r_ret = angular_limit_enabled;
+ } else if ("joint_constraints/angular_limit_upper" == p_name) {
+ r_ret = Math::rad2deg(angular_limit_upper);
+ } else if ("joint_constraints/angular_limit_lower" == p_name) {
+ r_ret = Math::rad2deg(angular_limit_lower);
+ } else if ("joint_constraints/angular_limit_bias" == p_name) {
+ r_ret = angular_limit_bias;
+ } else if ("joint_constraints/angular_limit_softness" == p_name) {
+ r_ret = angular_limit_softness;
+ } else if ("joint_constraints/angular_limit_relaxation" == p_name) {
+ r_ret = angular_limit_relaxation;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicalBone::HingeJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+ JointData::_get_property_list(p_list);
+
+ p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/angular_limit_enabled"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_bias", PROPERTY_HINT_RANGE, "0.01,0.99,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+}
+
+bool PhysicalBone::SliderJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ if (JointData::_set(p_name, p_value, j)) {
+ return true;
+ }
+
+ if ("joint_constraints/linear_limit_upper" == p_name) {
+ linear_limit_upper = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER, linear_limit_upper);
+
+ } else if ("joint_constraints/linear_limit_lower" == p_name) {
+ linear_limit_lower = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER, linear_limit_lower);
+
+ } else if ("joint_constraints/linear_limit_softness" == p_name) {
+ linear_limit_softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS, linear_limit_softness);
+
+ } else if ("joint_constraints/linear_limit_restitution" == p_name) {
+ linear_limit_restitution = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION, linear_limit_restitution);
+
+ } else if ("joint_constraints/linear_limit_damping" == p_name) {
+ linear_limit_damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING, linear_limit_restitution);
+
+ } else if ("joint_constraints/angular_limit_upper" == p_name) {
+ angular_limit_upper = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER, angular_limit_upper);
+
+ } else if ("joint_constraints/angular_limit_lower" == p_name) {
+ angular_limit_lower = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER, angular_limit_lower);
+
+ } else if ("joint_constraints/angular_limit_softness" == p_name) {
+ angular_limit_softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, angular_limit_softness);
+
+ } else if ("joint_constraints/angular_limit_restitution" == p_name) {
+ angular_limit_restitution = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, angular_limit_softness);
+
+ } else if ("joint_constraints/angular_limit_damping" == p_name) {
+ angular_limit_damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(j, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING, angular_limit_damping);
+
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool PhysicalBone::SliderJointData::_get(const StringName &p_name, Variant &r_ret) const {
+ if (JointData::_get(p_name, r_ret)) {
+ return true;
+ }
+
+ if ("joint_constraints/linear_limit_upper" == p_name) {
+ r_ret = linear_limit_upper;
+ } else if ("joint_constraints/linear_limit_lower" == p_name) {
+ r_ret = linear_limit_lower;
+ } else if ("joint_constraints/linear_limit_softness" == p_name) {
+ r_ret = linear_limit_softness;
+ } else if ("joint_constraints/linear_limit_restitution" == p_name) {
+ r_ret = linear_limit_restitution;
+ } else if ("joint_constraints/linear_limit_damping" == p_name) {
+ r_ret = linear_limit_damping;
+ } else if ("joint_constraints/angular_limit_upper" == p_name) {
+ r_ret = Math::rad2deg(angular_limit_upper);
+ } else if ("joint_constraints/angular_limit_lower" == p_name) {
+ r_ret = Math::rad2deg(angular_limit_lower);
+ } else if ("joint_constraints/angular_limit_softness" == p_name) {
+ r_ret = angular_limit_softness;
+ } else if ("joint_constraints/angular_limit_restitution" == p_name) {
+ r_ret = angular_limit_restitution;
+ } else if ("joint_constraints/angular_limit_damping" == p_name) {
+ r_ret = angular_limit_damping;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicalBone::SliderJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+ JointData::_get_property_list(p_list);
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_upper"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_lower"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/linear_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01"));
+
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_restitution", PROPERTY_HINT_RANGE, "0.01,16.0,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/angular_limit_damping", PROPERTY_HINT_RANGE, "0,16.0,0.01"));
+}
+
+bool PhysicalBone::SixDOFJointData::_set(const StringName &p_name, const Variant &p_value, RID j) {
+ if (JointData::_set(p_name, p_value, j)) {
+ return true;
+ }
+
+ String path = p_name;
+
+ Vector3::Axis axis;
+ {
+ const String axis_s = path.get_slicec('/', 1);
+ if ("x" == axis_s) {
+ axis = Vector3::AXIS_X;
+ } else if ("y" == axis_s) {
+ axis = Vector3::AXIS_Y;
+ } else if ("z" == axis_s) {
+ axis = Vector3::AXIS_Z;
+ } else {
+ return false;
+ }
+ }
+
+ String var_name = path.get_slicec('/', 2);
+
+ if ("linear_limit_enabled" == var_name) {
+ axis_data[axis].linear_limit_enabled = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j, axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, axis_data[axis].linear_limit_enabled);
+
+ } else if ("linear_limit_upper" == var_name) {
+ axis_data[axis].linear_limit_upper = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT, axis_data[axis].linear_limit_upper);
+
+ } else if ("linear_limit_lower" == var_name) {
+ axis_data[axis].linear_limit_lower = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT, axis_data[axis].linear_limit_lower);
+
+ } else if ("linear_limit_softness" == var_name) {
+ axis_data[axis].linear_limit_softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, axis_data[axis].linear_limit_softness);
+
+ } else if ("linear_restitution" == var_name) {
+ axis_data[axis].linear_restitution = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION, axis_data[axis].linear_restitution);
+
+ } else if ("linear_damping" == var_name) {
+ axis_data[axis].linear_damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING, axis_data[axis].linear_damping);
+
+ } else if ("angular_limit_enabled" == var_name) {
+ axis_data[axis].angular_limit_enabled = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j, axis, PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, axis_data[axis].angular_limit_enabled);
+
+ } else if ("angular_limit_upper" == var_name) {
+ axis_data[axis].angular_limit_upper = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT, axis_data[axis].angular_limit_upper);
+
+ } else if ("angular_limit_lower" == var_name) {
+ axis_data[axis].angular_limit_lower = Math::deg2rad(real_t(p_value));
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT, axis_data[axis].angular_limit_lower);
+
+ } else if ("angular_limit_softness" == var_name) {
+ axis_data[axis].angular_limit_softness = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS, axis_data[axis].angular_limit_softness);
+
+ } else if ("angular_restitution" == var_name) {
+ axis_data[axis].angular_restitution = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION, axis_data[axis].angular_restitution);
+
+ } else if ("angular_damping" == var_name) {
+ axis_data[axis].angular_damping = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING, axis_data[axis].angular_damping);
+
+ } else if ("erp" == var_name) {
+ axis_data[axis].erp = p_value;
+ if (j.is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j, axis, PhysicsServer::G6DOF_JOINT_ANGULAR_ERP, axis_data[axis].erp);
+
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool PhysicalBone::SixDOFJointData::_get(const StringName &p_name, Variant &r_ret) const {
+ if (JointData::_get(p_name, r_ret)) {
+ return true;
+ }
+
+ String path = p_name;
+
+ int axis;
+ {
+ const String axis_s = path.get_slicec('/', 1);
+ if ("x" == axis_s) {
+ axis = 0;
+ } else if ("y" == axis_s) {
+ axis = 1;
+ } else if ("z" == axis_s) {
+ axis = 2;
+ } else {
+ return false;
+ }
+ }
+
+ String var_name = path.get_slicec('/', 2);
+
+ if ("linear_limit_enabled" == var_name) {
+ r_ret = axis_data[axis].linear_limit_enabled;
+ } else if ("linear_limit_upper" == var_name) {
+ r_ret = axis_data[axis].linear_limit_upper;
+ } else if ("linear_limit_lower" == var_name) {
+ r_ret = axis_data[axis].linear_limit_lower;
+ } else if ("linear_limit_softness" == var_name) {
+ r_ret = axis_data[axis].linear_limit_softness;
+ } else if ("linear_restitution" == var_name) {
+ r_ret = axis_data[axis].linear_restitution;
+ } else if ("linear_damping" == var_name) {
+ r_ret = axis_data[axis].linear_damping;
+ } else if ("angular_limit_enabled" == var_name) {
+ r_ret = axis_data[axis].angular_limit_enabled;
+ } else if ("angular_limit_upper" == var_name) {
+ r_ret = Math::rad2deg(axis_data[axis].angular_limit_upper);
+ } else if ("angular_limit_lower" == var_name) {
+ r_ret = Math::rad2deg(axis_data[axis].angular_limit_lower);
+ } else if ("angular_limit_softness" == var_name) {
+ r_ret = axis_data[axis].angular_limit_softness;
+ } else if ("angular_restitution" == var_name) {
+ r_ret = axis_data[axis].angular_restitution;
+ } else if ("angular_damping" == var_name) {
+ r_ret = axis_data[axis].angular_damping;
+ } else if ("erp" == var_name) {
+ r_ret = axis_data[axis].erp;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void PhysicalBone::SixDOFJointData::_get_property_list(List<PropertyInfo> *p_list) const {
+ const StringName axis_names[] = { "x", "y", "z" };
+ for (int i = 0; i < 3; ++i) {
+ p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/linear_limit_enabled"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_upper"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_lower"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/linear_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "joint_constraints/" + axis_names[i] + "/angular_limit_enabled"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_upper", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_lower", PROPERTY_HINT_RANGE, "-180,180,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_limit_softness", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_restitution", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/angular_damping", PROPERTY_HINT_RANGE, "0.01,16,0.01"));
+ p_list->push_back(PropertyInfo(Variant::REAL, "joint_constraints/" + axis_names[i] + "/erp"));
+ }
+}
+
+bool PhysicalBone::_set(const StringName &p_name, const Variant &p_value) {
+ if (p_name == "bone_name") {
+ set_bone_name(p_value);
+ return true;
+ }
+
+ if (joint_data) {
+ if (joint_data->_set(p_name, p_value)) {
+#ifdef TOOLS_ENABLED
+ if (get_gizmo().is_valid())
+ get_gizmo()->redraw();
+#endif
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool PhysicalBone::_get(const StringName &p_name, Variant &r_ret) const {
+ if (p_name == "bone_name") {
+ r_ret = get_bone_name();
+ return true;
+ }
+
+ if (joint_data) {
+ return joint_data->_get(p_name, r_ret);
+ }
+
+ return false;
+}
+
+void PhysicalBone::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ Skeleton *parent = find_skeleton_parent(get_parent());
+
+ if (parent) {
+
+ String names;
+ for (int i = 0; i < parent->get_bone_count(); i++) {
+ if (i > 0)
+ names += ",";
+ names += parent->get_bone_name(i);
+ }
+
+ p_list->push_back(PropertyInfo(Variant::STRING, "bone_name", PROPERTY_HINT_ENUM, names));
+ } else {
+
+ p_list->push_back(PropertyInfo(Variant::STRING, "bone_name"));
+ }
+
+ if (joint_data) {
+ joint_data->_get_property_list(p_list);
+ }
+}
+
+void PhysicalBone::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ parent_skeleton = find_skeleton_parent(get_parent());
+ update_bone_id();
+ reset_to_rest_position();
+ break;
+ case NOTIFICATION_EXIT_TREE:
+ if (parent_skeleton) {
+ if (-1 != bone_id) {
+ parent_skeleton->unbind_physical_bone_from_bone(bone_id);
+ }
+ }
+ parent_skeleton = NULL;
+ update_bone_id();
+ break;
+ case NOTIFICATION_TRANSFORM_CHANGED:
+ if (Engine::get_singleton()->is_editor_hint()) {
+
+ update_offset();
+ }
+ break;
+ }
+}
+
+void PhysicalBone::_direct_state_changed(Object *p_state) {
+
+ if (!simulate_physics) {
+ return;
+ }
+
+ /// Update bone transform
+
+ PhysicsDirectBodyState *state;
+
+#ifdef DEBUG_ENABLED
+ state = Object::cast_to<PhysicsDirectBodyState>(p_state);
+#else
+ state = (PhysicsDirectBodyState *)p_state; //trust it
+#endif
+
+ Transform global_transform(state->get_transform());
+
+ set_ignore_transform_notification(true);
+ set_global_transform(global_transform);
+ set_ignore_transform_notification(false);
+
+ // Update skeleton
+ if (parent_skeleton) {
+ if (-1 != bone_id) {
+ parent_skeleton->set_bone_global_pose(bone_id, parent_skeleton->get_global_transform().affine_inverse() * (global_transform * body_offset_inverse));
+ }
+ }
+}
+
+void PhysicalBone::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_direct_state_changed"), &PhysicalBone::_direct_state_changed);
+
+ ClassDB::bind_method(D_METHOD("set_joint_type", "joint_type"), &PhysicalBone::set_joint_type);
+ ClassDB::bind_method(D_METHOD("get_joint_type"), &PhysicalBone::get_joint_type);
+
+ ClassDB::bind_method(D_METHOD("set_joint_offset", "offset"), &PhysicalBone::set_joint_offset);
+ ClassDB::bind_method(D_METHOD("get_joint_offset"), &PhysicalBone::get_joint_offset);
+
+ ClassDB::bind_method(D_METHOD("set_body_offset", "offset"), &PhysicalBone::set_body_offset);
+ ClassDB::bind_method(D_METHOD("get_body_offset"), &PhysicalBone::get_body_offset);
+
+ ClassDB::bind_method(D_METHOD("set_static_body", "simulate"), &PhysicalBone::set_static_body);
+ ClassDB::bind_method(D_METHOD("is_static_body"), &PhysicalBone::is_static_body);
+
+ ClassDB::bind_method(D_METHOD("set_simulate_physics", "simulate"), &PhysicalBone::set_simulate_physics);
+ ClassDB::bind_method(D_METHOD("get_simulate_physics"), &PhysicalBone::get_simulate_physics);
+
+ ClassDB::bind_method(D_METHOD("is_simulating_physics"), &PhysicalBone::is_simulating_physics);
+
+ ClassDB::bind_method(D_METHOD("set_mass", "mass"), &PhysicalBone::set_mass);
+ ClassDB::bind_method(D_METHOD("get_mass"), &PhysicalBone::get_mass);
+
+ ClassDB::bind_method(D_METHOD("set_weight", "weight"), &PhysicalBone::set_weight);
+ ClassDB::bind_method(D_METHOD("get_weight"), &PhysicalBone::get_weight);
+
+ ClassDB::bind_method(D_METHOD("set_friction", "friction"), &PhysicalBone::set_friction);
+ ClassDB::bind_method(D_METHOD("get_friction"), &PhysicalBone::get_friction);
+
+ ClassDB::bind_method(D_METHOD("set_bounce", "bounce"), &PhysicalBone::set_bounce);
+ ClassDB::bind_method(D_METHOD("get_bounce"), &PhysicalBone::get_bounce);
+
+ ClassDB::bind_method(D_METHOD("set_gravity_scale", "gravity_scale"), &PhysicalBone::set_gravity_scale);
+ ClassDB::bind_method(D_METHOD("get_gravity_scale"), &PhysicalBone::get_gravity_scale);
+
+ ADD_GROUP("Joint", "joint_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "joint_type", PROPERTY_HINT_ENUM, "None,PinJoint,ConeJoint,HingeJoint,SliderJoint,6DOFJoint"), "set_joint_type", "get_joint_type");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "joint_offset"), "set_joint_offset", "get_joint_offset");
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "simulate_physics"), "set_simulate_physics", "get_simulate_physics");
+ ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "body_offset"), "set_body_offset", "get_body_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "static_body"), "set_static_body", "is_static_body");
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_weight", "get_weight");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale");
+}
+
+Skeleton *PhysicalBone::find_skeleton_parent(Node *p_parent) {
+ if (!p_parent) {
+ return NULL;
+ }
+ Skeleton *s = Object::cast_to<Skeleton>(p_parent);
+ return s ? s : find_skeleton_parent(p_parent->get_parent());
+}
+
+void PhysicalBone::_fix_joint_offset() {
+ // Clamp joint origin to bone origin
+ if (parent_skeleton) {
+ joint_offset.origin = body_offset.affine_inverse().origin;
+ }
+}
+
+void PhysicalBone::_reload_joint() {
+
+ if (joint.is_valid()) {
+ PhysicsServer::get_singleton()->free(joint);
+ joint = RID();
+ }
+
+ if (!parent_skeleton) {
+ return;
+ }
+
+ PhysicalBone *body_a = parent_skeleton->get_physical_bone_parent(bone_id);
+ if (!body_a) {
+ return;
+ }
+
+ Transform joint_transf = get_global_transform() * joint_offset;
+ Transform local_a = body_a->get_global_transform().affine_inverse() * joint_transf;
+ local_a.orthonormalize();
+
+ switch (get_joint_type()) {
+ case JOINT_TYPE_PIN: {
+
+ joint = PhysicsServer::get_singleton()->joint_create_pin(body_a->get_rid(), local_a.origin, get_rid(), joint_offset.origin);
+ const PinJointData *pjd(static_cast<const PinJointData *>(joint_data));
+ PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_BIAS, pjd->bias);
+ PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_DAMPING, pjd->damping);
+ PhysicsServer::get_singleton()->pin_joint_set_param(joint, PhysicsServer::PIN_JOINT_IMPULSE_CLAMP, pjd->impulse_clamp);
+
+ } break;
+ case JOINT_TYPE_CONE: {
+
+ joint = PhysicsServer::get_singleton()->joint_create_cone_twist(body_a->get_rid(), local_a, get_rid(), joint_offset);
+ const ConeJointData *cjd(static_cast<const ConeJointData *>(joint_data));
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_SWING_SPAN, cjd->swing_span);
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_TWIST_SPAN, cjd->twist_span);
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_BIAS, cjd->bias);
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_SOFTNESS, cjd->softness);
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer::CONE_TWIST_JOINT_RELAXATION, cjd->relaxation);
+
+ } break;
+ case JOINT_TYPE_HINGE: {
+
+ joint = PhysicsServer::get_singleton()->joint_create_hinge(body_a->get_rid(), local_a, get_rid(), joint_offset);
+ const HingeJointData *hjd(static_cast<const HingeJointData *>(joint_data));
+ PhysicsServer::get_singleton()->hinge_joint_set_flag(joint, PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT, hjd->angular_limit_enabled);
+ PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_UPPER, hjd->angular_limit_upper);
+ PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_LOWER, hjd->angular_limit_lower);
+ PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_BIAS, hjd->angular_limit_bias);
+ PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS, hjd->angular_limit_softness);
+ PhysicsServer::get_singleton()->hinge_joint_set_param(joint, PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION, hjd->angular_limit_relaxation);
+
+ } break;
+ case JOINT_TYPE_SLIDER: {
+
+ joint = PhysicsServer::get_singleton()->joint_create_slider(body_a->get_rid(), local_a, get_rid(), joint_offset);
+ const SliderJointData *sjd(static_cast<const SliderJointData *>(joint_data));
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER, sjd->linear_limit_upper);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER, sjd->linear_limit_lower);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS, sjd->linear_limit_softness);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION, sjd->linear_limit_restitution);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING, sjd->linear_limit_restitution);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER, sjd->angular_limit_upper);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER, sjd->angular_limit_lower);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, sjd->angular_limit_softness);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS, sjd->angular_limit_softness);
+ PhysicsServer::get_singleton()->slider_joint_set_param(joint, PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING, sjd->angular_limit_damping);
+
+ } break;
+ case JOINT_TYPE_6DOF: {
+
+ joint = PhysicsServer::get_singleton()->joint_create_generic_6dof(body_a->get_rid(), local_a, get_rid(), joint_offset);
+ const SixDOFJointData *g6dofjd(static_cast<const SixDOFJointData *>(joint_data));
+ for (int axis = 0; axis < 3; ++axis) {
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, g6dofjd->axis_data[axis].linear_limit_enabled);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT, g6dofjd->axis_data[axis].linear_limit_upper);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT, g6dofjd->axis_data[axis].linear_limit_lower);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, g6dofjd->axis_data[axis].linear_limit_softness);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION, g6dofjd->axis_data[axis].linear_restitution);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING, g6dofjd->axis_data[axis].linear_damping);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT, g6dofjd->axis_data[axis].angular_limit_enabled);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT, g6dofjd->axis_data[axis].angular_limit_upper);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT, g6dofjd->axis_data[axis].angular_limit_lower);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS, g6dofjd->axis_data[axis].angular_limit_softness);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION, g6dofjd->axis_data[axis].angular_restitution);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING, g6dofjd->axis_data[axis].angular_damping);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(joint, static_cast<Vector3::Axis>(axis), PhysicsServer::G6DOF_JOINT_ANGULAR_ERP, g6dofjd->axis_data[axis].erp);
+ }
+
+ } break;
+ }
+}
+
+void PhysicalBone::_on_bone_parent_changed() {
+ _reload_joint();
+}
+
+void PhysicalBone::_set_gizmo_move_joint(bool p_move_joint) {
+#ifdef TOOLS_ENABLED
+ gizmo_move_joint = p_move_joint;
+ SpatialEditor::get_singleton()->update_transform_gizmo();
+#endif
+}
+
+#ifdef TOOLS_ENABLED
+Transform PhysicalBone::get_global_gizmo_transform() const {
+ return gizmo_move_joint ? get_global_transform() * joint_offset : get_global_transform();
+}
+
+Transform PhysicalBone::get_local_gizmo_transform() const {
+ return gizmo_move_joint ? get_transform() * joint_offset : get_transform();
+}
+#endif
+
+const PhysicalBone::JointData *PhysicalBone::get_joint_data() const {
+ return joint_data;
+}
+
+Skeleton *PhysicalBone::find_skeleton_parent() {
+ return find_skeleton_parent(this);
+}
+
+void PhysicalBone::set_joint_type(JointType p_joint_type) {
+
+ if (p_joint_type == get_joint_type())
+ return;
+
+ memdelete(joint_data);
+ joint_data = NULL;
+ switch (p_joint_type) {
+ case JOINT_TYPE_PIN:
+ joint_data = memnew(PinJointData);
+ break;
+ case JOINT_TYPE_CONE:
+ joint_data = memnew(ConeJointData);
+ break;
+ case JOINT_TYPE_HINGE:
+ joint_data = memnew(HingeJointData);
+ break;
+ case JOINT_TYPE_SLIDER:
+ joint_data = memnew(SliderJointData);
+ break;
+ case JOINT_TYPE_6DOF:
+ joint_data = memnew(SixDOFJointData);
+ break;
+ }
+
+ _reload_joint();
+
+#ifdef TOOLS_ENABLED
+ _change_notify();
+ if (get_gizmo().is_valid())
+ get_gizmo()->redraw();
+#endif
+}
+
+PhysicalBone::JointType PhysicalBone::get_joint_type() const {
+ return joint_data ? joint_data->get_joint_type() : JOINT_TYPE_NONE;
+}
+
+void PhysicalBone::set_joint_offset(const Transform &p_offset) {
+ joint_offset = p_offset;
+
+ _fix_joint_offset();
+
+ set_ignore_transform_notification(true);
+ reset_to_rest_position();
+ set_ignore_transform_notification(false);
+
+#ifdef TOOLS_ENABLED
+ if (get_gizmo().is_valid())
+ get_gizmo()->redraw();
+#endif
+}
+
+const Transform &PhysicalBone::get_body_offset() const {
+ return body_offset;
+}
+
+void PhysicalBone::set_body_offset(const Transform &p_offset) {
+ body_offset = p_offset;
+ body_offset_inverse = body_offset.affine_inverse();
+
+ _fix_joint_offset();
+
+ set_ignore_transform_notification(true);
+ reset_to_rest_position();
+ set_ignore_transform_notification(false);
+
+#ifdef TOOLS_ENABLED
+ if (get_gizmo().is_valid())
+ get_gizmo()->redraw();
+#endif
+}
+
+const Transform &PhysicalBone::get_joint_offset() const {
+ return joint_offset;
+}
+
+void PhysicalBone::set_static_body(bool p_static) {
+
+ static_body = p_static;
+
+ set_as_toplevel(!static_body);
+
+ _reset_physics_simulation_state();
+}
+
+bool PhysicalBone::is_static_body() {
+ return static_body;
+}
+
+void PhysicalBone::set_simulate_physics(bool p_simulate) {
+ if (simulate_physics == p_simulate) {
+ return;
+ }
+
+ simulate_physics = p_simulate;
+ _reset_physics_simulation_state();
+}
+
+bool PhysicalBone::get_simulate_physics() {
+ return simulate_physics;
+}
+
+bool PhysicalBone::is_simulating_physics() {
+ return _internal_simulate_physics && !_internal_static_body;
+}
+
+void PhysicalBone::set_bone_name(const String &p_name) {
+
+ bone_name = p_name;
+ bone_id = -1;
+
+ update_bone_id();
+ reset_to_rest_position();
+}
+
+const String &PhysicalBone::get_bone_name() const {
+
+ return bone_name;
+}
+
+void PhysicalBone::set_mass(real_t p_mass) {
+
+ ERR_FAIL_COND(p_mass <= 0);
+ mass = p_mass;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_MASS, mass);
+}
+
+real_t PhysicalBone::get_mass() const {
+
+ return mass;
+}
+
+void PhysicalBone::set_weight(real_t p_weight) {
+
+ set_mass(p_weight / real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8)));
+}
+
+real_t PhysicalBone::get_weight() const {
+
+ return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8));
+}
+
+void PhysicalBone::set_friction(real_t p_friction) {
+
+ ERR_FAIL_COND(p_friction < 0 || p_friction > 1);
+
+ friction = p_friction;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_FRICTION, friction);
+}
+
+real_t PhysicalBone::get_friction() const {
+
+ return friction;
+}
+
+void PhysicalBone::set_bounce(real_t p_bounce) {
+
+ ERR_FAIL_COND(p_bounce < 0 || p_bounce > 1);
+
+ bounce = p_bounce;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_BOUNCE, bounce);
+}
+real_t PhysicalBone::get_bounce() const {
+
+ return bounce;
+}
+
+void PhysicalBone::set_gravity_scale(real_t p_gravity_scale) {
+
+ gravity_scale = p_gravity_scale;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(), PhysicsServer::BODY_PARAM_GRAVITY_SCALE, gravity_scale);
+}
+
+real_t PhysicalBone::get_gravity_scale() const {
+
+ return gravity_scale;
+}
+
+PhysicalBone::PhysicalBone() :
+ PhysicsBody(PhysicsServer::BODY_MODE_STATIC),
+#ifdef TOOLS_ENABLED
+ gizmo_move_joint(false),
+#endif
+ joint_data(NULL),
+ static_body(false),
+ simulate_physics(false),
+ _internal_static_body(!static_body),
+ _internal_simulate_physics(simulate_physics),
+ bone_id(-1),
+ parent_skeleton(NULL),
+ bone_name(""),
+ bounce(0),
+ mass(1),
+ friction(1),
+ gravity_scale(1) {
+
+ set_static_body(static_body);
+ _reset_physics_simulation_state();
+}
+
+PhysicalBone::~PhysicalBone() {
+ memdelete(joint_data);
+}
+
+void PhysicalBone::update_bone_id() {
+ if (!parent_skeleton) {
+ return;
+ }
+
+ const int new_bone_id = parent_skeleton->find_bone(bone_name);
+
+ if (new_bone_id != bone_id) {
+ if (-1 != bone_id) {
+ // Assert the unbind from old node
+ parent_skeleton->unbind_physical_bone_from_bone(bone_id);
+ parent_skeleton->unbind_child_node_from_bone(bone_id, this);
+ }
+
+ bone_id = new_bone_id;
+
+ parent_skeleton->bind_physical_bone_to_bone(bone_id, this);
+
+ _fix_joint_offset();
+ _internal_static_body = !static_body; // Force staticness reset
+ _reset_staticness_state();
+ }
+}
+
+void PhysicalBone::update_offset() {
+#ifdef TOOLS_ENABLED
+ if (parent_skeleton) {
+
+ Transform bone_transform(parent_skeleton->get_global_transform());
+ if (-1 != bone_id)
+ bone_transform *= parent_skeleton->get_bone_global_pose(bone_id);
+
+ if (gizmo_move_joint) {
+ bone_transform *= body_offset;
+ set_joint_offset(bone_transform.affine_inverse() * get_global_transform());
+ } else {
+ set_body_offset(bone_transform.affine_inverse() * get_global_transform());
+ }
+ }
+#endif
+}
+
+void PhysicalBone::reset_to_rest_position() {
+ if (parent_skeleton) {
+ if (-1 == bone_id) {
+ set_global_transform(parent_skeleton->get_global_transform() * body_offset);
+ } else {
+ set_global_transform(parent_skeleton->get_global_transform() * parent_skeleton->get_bone_global_pose(bone_id) * body_offset);
+ }
+ }
+}
+
+void PhysicalBone::_reset_physics_simulation_state() {
+ if (simulate_physics && !static_body) {
+ _start_physics_simulation();
+ } else {
+ _stop_physics_simulation();
+ }
+
+ _reset_staticness_state();
+}
+
+void PhysicalBone::_reset_staticness_state() {
+
+ if (parent_skeleton && -1 != bone_id) {
+ if (static_body && simulate_physics) { // With this check I'm sure the position of this body is updated only when it's necessary
+
+ if (_internal_static_body) {
+ return;
+ }
+
+ parent_skeleton->bind_child_node_to_bone(bone_id, this);
+ _internal_static_body = true;
+ } else {
+
+ if (!_internal_static_body) {
+ return;
+ }
+
+ parent_skeleton->unbind_child_node_from_bone(bone_id, this);
+ _internal_static_body = false;
+ }
+ }
+}
+
+void PhysicalBone::_start_physics_simulation() {
+ if (_internal_simulate_physics || !parent_skeleton) {
+ return;
+ }
+ reset_to_rest_position();
+ PhysicsServer::get_singleton()->body_set_mode(get_rid(), PhysicsServer::BODY_MODE_RIGID);
+ PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer());
+ PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask());
+ PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed");
+ parent_skeleton->set_bone_ignore_animation(bone_id, true);
+ _internal_simulate_physics = true;
+}
+
+void PhysicalBone::_stop_physics_simulation() {
+ if (!_internal_simulate_physics || !parent_skeleton) {
+ return;
+ }
+ PhysicsServer::get_singleton()->body_set_mode(get_rid(), PhysicsServer::BODY_MODE_STATIC);
+ PhysicsServer::get_singleton()->body_set_collision_layer(get_rid(), 0);
+ PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(), 0);
+ PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(), NULL, "");
+ parent_skeleton->set_bone_ignore_animation(bone_id, false);
+ _internal_simulate_physics = false;
+}
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index ffdc9ab30..17d2769c7 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -33,6 +33,7 @@
#include "scene/3d/collision_object.h"
#include "servers/physics_server.h"
+#include "skeleton.h"
#include "vset.h"
class PhysicsBody : public CollisionObject {
@@ -342,4 +343,267 @@ public:
KinematicCollision();
};
+class PhysicalBone : public PhysicsBody {
+
+ GDCLASS(PhysicalBone, PhysicsBody);
+
+public:
+ enum JointType {
+ JOINT_TYPE_NONE,
+ JOINT_TYPE_PIN,
+ JOINT_TYPE_CONE,
+ JOINT_TYPE_HINGE,
+ JOINT_TYPE_SLIDER,
+ JOINT_TYPE_6DOF
+ };
+
+ struct JointData {
+ virtual JointType get_joint_type() { return JOINT_TYPE_NONE; }
+
+ /// "j" is used to set the parameter inside the PhysicsServer
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+ };
+
+ struct PinJointData : public JointData {
+ virtual JointType get_joint_type() { return JOINT_TYPE_PIN; }
+
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ real_t bias;
+ real_t damping;
+ real_t impulse_clamp;
+
+ PinJointData() :
+ bias(0.3),
+ damping(1.),
+ impulse_clamp(0) {}
+ };
+
+ struct ConeJointData : public JointData {
+ virtual JointType get_joint_type() { return JOINT_TYPE_CONE; }
+
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ real_t swing_span;
+ real_t twist_span;
+ real_t bias;
+ real_t softness;
+ real_t relaxation;
+
+ ConeJointData() :
+ swing_span(Math_PI * 0.25),
+ twist_span(Math_PI),
+ bias(0.3),
+ softness(0.8),
+ relaxation(1.) {}
+ };
+
+ struct HingeJointData : public JointData {
+ virtual JointType get_joint_type() { return JOINT_TYPE_HINGE; }
+
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ bool angular_limit_enabled;
+ real_t angular_limit_upper;
+ real_t angular_limit_lower;
+ real_t angular_limit_bias;
+ real_t angular_limit_softness;
+ real_t angular_limit_relaxation;
+
+ HingeJointData() :
+ angular_limit_enabled(false),
+ angular_limit_upper(Math_PI * 0.5),
+ angular_limit_lower(-Math_PI * 0.5),
+ angular_limit_bias(0.3),
+ angular_limit_softness(0.9),
+ angular_limit_relaxation(1.) {}
+ };
+
+ struct SliderJointData : public JointData {
+ virtual JointType get_joint_type() { return JOINT_TYPE_SLIDER; }
+
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ real_t linear_limit_upper;
+ real_t linear_limit_lower;
+ real_t linear_limit_softness;
+ real_t linear_limit_restitution;
+ real_t linear_limit_damping;
+ real_t angular_limit_upper;
+ real_t angular_limit_lower;
+ real_t angular_limit_softness;
+ real_t angular_limit_restitution;
+ real_t angular_limit_damping;
+
+ SliderJointData() :
+ linear_limit_upper(1.),
+ linear_limit_lower(-1.),
+ linear_limit_softness(1.),
+ linear_limit_restitution(0.7),
+ linear_limit_damping(1.),
+ angular_limit_upper(0),
+ angular_limit_lower(0),
+ angular_limit_softness(1.),
+ angular_limit_restitution(0.7),
+ angular_limit_damping(1.) {}
+ };
+
+ struct SixDOFJointData : public JointData {
+ struct SixDOFAxisData {
+ bool linear_limit_enabled;
+ real_t linear_limit_upper;
+ real_t linear_limit_lower;
+ real_t linear_limit_softness;
+ real_t linear_restitution;
+ real_t linear_damping;
+ bool angular_limit_enabled;
+ real_t angular_limit_upper;
+ real_t angular_limit_lower;
+ real_t angular_limit_softness;
+ real_t angular_restitution;
+ real_t angular_damping;
+ real_t erp;
+
+ SixDOFAxisData() :
+ linear_limit_enabled(true),
+ linear_limit_upper(0),
+ linear_limit_lower(0),
+ linear_limit_softness(0.7),
+ linear_restitution(0.5),
+ linear_damping(1.),
+ angular_limit_enabled(true),
+ angular_limit_upper(0),
+ angular_limit_lower(0),
+ angular_limit_softness(0.5),
+ angular_restitution(0),
+ angular_damping(1.),
+ erp(0.5) {}
+ };
+
+ virtual JointType get_joint_type() { return JOINT_TYPE_6DOF; }
+
+ virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID());
+ virtual bool _get(const StringName &p_name, Variant &r_ret) const;
+ virtual void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ SixDOFAxisData axis_data[3];
+
+ SixDOFJointData() {}
+ };
+
+private:
+#ifdef TOOLS_ENABLED
+ // if false gizmo move body
+ bool gizmo_move_joint;
+#endif
+
+ JointData *joint_data;
+ Transform joint_offset;
+ RID joint;
+
+ Skeleton *parent_skeleton;
+ Transform body_offset;
+ Transform body_offset_inverse;
+ bool static_body;
+ bool _internal_static_body;
+ bool simulate_physics;
+ bool _internal_simulate_physics;
+ int bone_id;
+
+ String bone_name;
+ real_t bounce;
+ real_t mass;
+ real_t friction;
+ real_t gravity_scale;
+
+protected:
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+ void _notification(int p_what);
+ void _direct_state_changed(Object *p_state);
+
+ static void _bind_methods();
+
+private:
+ static Skeleton *find_skeleton_parent(Node *p_parent);
+ void _fix_joint_offset();
+ void _reload_joint();
+
+public:
+ void _on_bone_parent_changed();
+ void _set_gizmo_move_joint(bool p_move_joint);
+
+public:
+#ifdef TOOLS_ENABLED
+ virtual Transform get_global_gizmo_transform() const;
+ virtual Transform get_local_gizmo_transform() const;
+#endif
+
+ const JointData *get_joint_data() const;
+ Skeleton *find_skeleton_parent();
+
+ int get_bone_id() const { return bone_id; }
+
+ void set_joint_type(JointType p_joint_type);
+ JointType get_joint_type() const;
+
+ void set_joint_offset(const Transform &p_offset);
+ const Transform &get_joint_offset() const;
+
+ void set_body_offset(const Transform &p_offset);
+ const Transform &get_body_offset() const;
+
+ void set_static_body(bool p_static);
+ bool is_static_body();
+
+ void set_simulate_physics(bool p_simulate);
+ bool get_simulate_physics();
+ bool is_simulating_physics();
+
+ void set_bone_name(const String &p_name);
+ const String &get_bone_name() const;
+
+ void set_mass(real_t p_mass);
+ real_t get_mass() const;
+
+ void set_weight(real_t p_weight);
+ real_t get_weight() const;
+
+ void set_friction(real_t p_friction);
+ real_t get_friction() const;
+
+ void set_bounce(real_t p_bounce);
+ real_t get_bounce() const;
+
+ void set_gravity_scale(real_t p_gravity_scale);
+ real_t get_gravity_scale() const;
+
+ PhysicalBone();
+ ~PhysicalBone();
+
+private:
+ void update_bone_id();
+ void update_offset();
+ void reset_to_rest_position();
+
+ void _reset_physics_simulation_state();
+ void _reset_staticness_state();
+
+ void _start_physics_simulation();
+ void _stop_physics_simulation();
+};
+
+VARIANT_ENUM_CAST(PhysicalBone::JointType);
+
#endif // PHYSICS_BODY__H
diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp
index dd5ae8a99..7f83e2c3e 100644
--- a/scene/3d/ray_cast.cpp
+++ b/scene/3d/ray_cast.cpp
@@ -103,7 +103,7 @@ void RayCast::set_enabled(bool p_enabled) {
enabled = p_enabled;
if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint())
- set_physics_process(p_enabled);
+ set_physics_process_internal(p_enabled);
if (!p_enabled)
collided = false;
@@ -150,12 +150,12 @@ void RayCast::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
if (enabled && !Engine::get_singleton()->is_editor_hint()) {
- set_physics_process(true);
+ set_physics_process_internal(true);
if (get_tree()->is_debugging_collisions_hint())
_update_debug_shape();
} else
- set_physics_process(false);
+ set_physics_process_internal(false);
if (Object::cast_to<CollisionObject>(get_parent())) {
if (exclude_parent_body)
@@ -168,14 +168,14 @@ void RayCast::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
if (enabled) {
- set_physics_process(false);
+ set_physics_process_internal(false);
}
if (debug_shape)
_clear_debug_shape();
} break;
- case NOTIFICATION_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (!enabled)
break;
diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp
index d3a13c741..a7eb54c85 100644
--- a/scene/3d/skeleton.cpp
+++ b/scene/3d/skeleton.cpp
@@ -33,6 +33,7 @@
#include "message_queue.h"
#include "core/project_settings.h"
+#include "scene/3d/physics_body.h"
#include "scene/resources/surface_tool.h"
bool Skeleton::_set(const StringName &p_path, const Variant &p_value) {
@@ -377,6 +378,17 @@ void Skeleton::unparent_bone_and_rest(int p_bone) {
_make_dirty();
}
+void Skeleton::set_bone_ignore_animation(int p_bone, bool p_ignore) {
+ ERR_FAIL_INDEX(p_bone, bones.size());
+ bones[p_bone].ignore_animation = p_ignore;
+}
+
+bool Skeleton::is_bone_ignore_animation(int p_bone) const {
+
+ ERR_FAIL_INDEX_V(p_bone, bones.size(), false);
+ return bones[p_bone].ignore_animation;
+}
+
void Skeleton::set_bone_disable_rest(int p_bone, bool p_disable) {
ERR_FAIL_INDEX(p_bone, bones.size());
@@ -522,6 +534,103 @@ void Skeleton::localize_rests() {
}
}
+void _notify_physical_bones_simulation(bool start, Node *p_node) {
+
+ for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
+ _notify_physical_bones_simulation(start, p_node->get_child(i));
+ }
+
+ PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node);
+ if (pb) {
+ pb->set_simulate_physics(start);
+ }
+}
+
+void Skeleton::bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone) {
+ ERR_FAIL_INDEX(p_bone, bones.size());
+ ERR_FAIL_COND(bones[p_bone].physical_bone);
+ ERR_FAIL_COND(!p_physical_bone);
+ bones[p_bone].physical_bone = p_physical_bone;
+
+ _rebuild_physical_bones_cache();
+}
+
+void Skeleton::unbind_physical_bone_from_bone(int p_bone) {
+ ERR_FAIL_INDEX(p_bone, bones.size());
+ bones[p_bone].physical_bone = NULL;
+
+ _rebuild_physical_bones_cache();
+}
+
+PhysicalBone *Skeleton::get_physical_bone(int p_bone) {
+ ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL);
+
+ return bones[p_bone].physical_bone;
+}
+
+PhysicalBone *Skeleton::get_physical_bone_parent(int p_bone) {
+ ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL);
+
+ if (bones[p_bone].cache_parent_physical_bone) {
+ return bones[p_bone].cache_parent_physical_bone;
+ }
+
+ return _get_physical_bone_parent(p_bone);
+}
+
+PhysicalBone *Skeleton::_get_physical_bone_parent(int p_bone) {
+ ERR_FAIL_INDEX_V(p_bone, bones.size(), NULL);
+
+ const int parent_bone = bones[p_bone].parent;
+ if (0 > parent_bone) {
+ return NULL;
+ }
+
+ PhysicalBone *pb = bones[parent_bone].physical_bone;
+ if (pb) {
+ return pb;
+ } else {
+ return get_physical_bone_parent(parent_bone);
+ }
+}
+
+void Skeleton::_rebuild_physical_bones_cache() {
+ const int b_size = bones.size();
+ for (int i = 0; i < b_size; ++i) {
+ bones[i].cache_parent_physical_bone = _get_physical_bone_parent(i);
+ if (bones[i].physical_bone)
+ bones[i].physical_bone->_on_bone_parent_changed();
+ }
+}
+
+void Skeleton::physical_bones_simulation(bool start) {
+ _notify_physical_bones_simulation(start, this);
+}
+
+void _physical_bones_add_remove_collision_exception(bool p_add, Node *p_node, RID p_exception) {
+
+ for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
+ _physical_bones_add_remove_collision_exception(p_add, p_node->get_child(i), p_exception);
+ }
+
+ CollisionObject *co = Object::cast_to<CollisionObject>(p_node);
+ if (co) {
+ if (p_add) {
+ PhysicsServer::get_singleton()->body_add_collision_exception(co->get_rid(), p_exception);
+ } else {
+ PhysicsServer::get_singleton()->body_remove_collision_exception(co->get_rid(), p_exception);
+ }
+ }
+}
+
+void Skeleton::physical_bones_add_collision_exception(RID p_exception) {
+ _physical_bones_add_remove_collision_exception(true, this, p_exception);
+}
+
+void Skeleton::physical_bones_remove_collision_exception(RID p_exception) {
+ _physical_bones_add_remove_collision_exception(false, this, p_exception);
+}
+
void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton::add_bone);
@@ -558,6 +667,10 @@ void Skeleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_bone_transform", "bone_idx"), &Skeleton::get_bone_transform);
+ ClassDB::bind_method(D_METHOD("physical_bones_simulation", "start"), &Skeleton::physical_bones_simulation);
+ ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton::physical_bones_add_collision_exception);
+ ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton::physical_bones_remove_collision_exception);
+
BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON);
}
diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h
index d69367005..f0e71c8b4 100644
--- a/scene/3d/skeleton.h
+++ b/scene/3d/skeleton.h
@@ -37,6 +37,8 @@
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
+
+class PhysicalBone;
class Skeleton : public Spatial {
GDCLASS(Skeleton, Spatial);
@@ -48,6 +50,8 @@ class Skeleton : public Spatial {
bool enabled;
int parent;
+ bool ignore_animation;
+
bool disable_rest;
Transform rest;
Transform rest_global_inverse;
@@ -60,13 +64,19 @@ class Skeleton : public Spatial {
Transform transform_final;
+ PhysicalBone *physical_bone;
+ PhysicalBone *cache_parent_physical_bone;
+
List<uint32_t> nodes_bound;
Bone() {
parent = -1;
enabled = true;
+ ignore_animation = false;
custom_pose_enable = false;
disable_rest = false;
+ physical_bone = NULL;
+ cache_parent_physical_bone = NULL;
}
};
@@ -118,6 +128,9 @@ public:
void unparent_bone_and_rest(int p_bone);
+ void set_bone_ignore_animation(int p_bone, bool p_ignore);
+ bool is_bone_ignore_animation(int p_bone) const;
+
void set_bone_disable_rest(int p_bone, bool p_disable);
bool is_bone_rest_disabled(int p_bone) const;
@@ -149,6 +162,25 @@ public:
void localize_rests(); // used for loaders and tools
+ // Physical bone API
+
+ void bind_physical_bone_to_bone(int p_bone, PhysicalBone *p_physical_bone);
+ void unbind_physical_bone_from_bone(int p_bone);
+
+ PhysicalBone *get_physical_bone(int p_bone);
+ PhysicalBone *get_physical_bone_parent(int p_bone);
+
+private:
+ /// This is a slow API os it's cached
+ PhysicalBone *_get_physical_bone_parent(int p_bone);
+ void _rebuild_physical_bones_cache();
+
+public:
+ void physical_bones_simulation(bool start);
+ void physical_bones_add_collision_exception(RID p_exception);
+ void physical_bones_remove_collision_exception(RID p_exception);
+
+public:
Skeleton();
~Skeleton();
};
diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp
index f8a5c7f40..748aa8aad 100644
--- a/scene/3d/spatial.cpp
+++ b/scene/3d/spatial.cpp
@@ -85,9 +85,7 @@ void Spatial::_notify_dirty() {
}
void Spatial::_update_local_transform() const {
- data.local_transform.basis = Basis();
- data.local_transform.basis.scale(data.scale);
- data.local_transform.basis.rotate(data.rotation);
+ data.local_transform.basis.set_euler_scale(data.rotation, data.scale);
data.dirty &= ~DIRTY_LOCAL;
}
@@ -288,6 +286,16 @@ Transform Spatial::get_global_transform() const {
return data.global_transform;
}
+#ifdef TOOLS_ENABLED
+Transform Spatial::get_global_gizmo_transform() const {
+ return get_global_transform();
+}
+
+Transform Spatial::get_local_gizmo_transform() const {
+ return get_transform();
+}
+#endif
+
Spatial *Spatial::get_parent_spatial() const {
return data.parent;
@@ -432,10 +440,9 @@ Ref<SpatialGizmo> Spatial::get_gizmo() const {
#endif
}
-#ifdef TOOLS_ENABLED
-
void Spatial::_update_gizmo() {
+#ifdef TOOLS_ENABLED
if (!is_inside_world())
return;
data.gizmo_dirty = false;
@@ -447,8 +454,10 @@ void Spatial::_update_gizmo() {
data.gizmo->clear();
}
}
+#endif
}
+#ifdef TOOLS_ENABLED
void Spatial::set_disable_gizmo(bool p_enabled) {
data.gizmo_disabled = p_enabled;
@@ -728,9 +737,7 @@ void Spatial::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &Spatial::is_set_as_toplevel);
ClassDB::bind_method(D_METHOD("get_world"), &Spatial::get_world);
-#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("_update_gizmo"), &Spatial::_update_gizmo);
-#endif
ClassDB::bind_method(D_METHOD("update_gizmo"), &Spatial::update_gizmo);
ClassDB::bind_method(D_METHOD("set_gizmo", "gizmo"), &Spatial::set_gizmo);
@@ -792,9 +799,7 @@ void Spatial::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale");
ADD_GROUP("Visibility", "");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
-#ifdef TOOLS_ENABLED
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "SpatialGizmo", 0), "set_gizmo", "get_gizmo");
-#endif
ADD_SIGNAL(MethodInfo("visibility_changed"));
}
diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h
index 518bba9a5..a43bed3e4 100644
--- a/scene/3d/spatial.h
+++ b/scene/3d/spatial.h
@@ -100,10 +100,8 @@ class Spatial : public Node {
#endif
} data;
-#ifdef TOOLS_ENABLED
void _update_gizmo();
-#endif
void _notify_dirty();
void _propagate_transform_changed(Spatial *p_origin);
@@ -147,6 +145,11 @@ public:
Transform get_transform() const;
Transform get_global_transform() const;
+#ifdef TOOLS_ENABLED
+ virtual Transform get_global_gizmo_transform() const;
+ virtual Transform get_local_gizmo_transform() const;
+#endif
+
void set_as_toplevel(bool p_enabled);
bool is_set_as_toplevel() const;
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
index ed3bde950..b72665aa2 100644
--- a/scene/3d/vehicle_body.cpp
+++ b/scene/3d/vehicle_body.cpp
@@ -524,7 +524,7 @@ void VehicleBody::_update_suspension(PhysicsDirectBodyState *s) {
//bilateral constraint between two dynamic objects
void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1,
- PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse) {
+ PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence) {
real_t normalLenSqr = normal.length_squared();
//ERR_FAIL_COND( normalLenSqr < real_t(1.1));
@@ -582,8 +582,12 @@ void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vec
rel_vel = normal.dot(vel);
- //TODO: move this into proper structure
- real_t contactDamping = real_t(0.4);
+ // !BAS! We had this set to 0.4, in bullet its 0.2
+ // real_t contactDamping = real_t(0.2);
+
+ // !BAS! But seeing we apply this frame by frame, makes more sense to me to make this time based
+ // keeping in mind our anti roll factor
+ real_t contactDamping = s->get_step() / p_rollInfluence;
#define ONLY_USE_LINEAR_MASS
#ifdef ONLY_USE_LINEAR_MASS
real_t massTerm = real_t(1.) / ((1.0 / mass) + b2invmass);
@@ -704,7 +708,7 @@ void VehicleBody::_update_friction(PhysicsDirectBodyState *s) {
_resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS,
wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS,
- m_axle[i], m_sideImpulse[i]);
+ m_axle[i], m_sideImpulse[i], wheelInfo.m_rollInfluence);
m_sideImpulse[i] *= sideFrictionStiffness2;
}
diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h
index 7810a42e8..1ac3693cc 100644
--- a/scene/3d/vehicle_body.h
+++ b/scene/3d/vehicle_body.h
@@ -168,7 +168,7 @@ class VehicleBody : public RigidBody {
btVehicleWheelContactPoint(PhysicsDirectBodyState *s, PhysicsBody *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse);
};
- void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse);
+ void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3 &pos1, PhysicsBody *body2, const Vector3 &pos2, const Vector3 &normal, real_t &impulse, real_t p_rollInfluence);
real_t _calc_rolling_friction(btVehicleWheelContactPoint &contactPoint);
void _update_friction(PhysicsDirectBodyState *s);
diff --git a/scene/3d/voxel_light_baker.cpp b/scene/3d/voxel_light_baker.cpp
index d389b69ef..13700e0bd 100644
--- a/scene/3d/voxel_light_baker.cpp
+++ b/scene/3d/voxel_light_baker.cpp
@@ -2338,9 +2338,9 @@ Ref<MultiMesh> VoxelLightBaker::create_debug_multimesh(DebugMode p_mode) {
for (int k = 0; k < 3; k++) {
if (i < 3)
- face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[j][(i + k) % 3] = v[k];
else
- face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[3 - j][(i + k) % 3] = -v[k];
}
}
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 04e7d5cc1..c0d1e62e0 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -182,8 +182,8 @@ void AnimationPlayer::_notification(int p_what) {
if (!processing) {
//make sure that a previous process state was not saved
//only process if "processing" is set
- set_physics_process(false);
- set_process(false);
+ set_physics_process_internal(false);
+ set_process_internal(false);
}
//_set_process(false);
clear_caches();
@@ -246,8 +246,9 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim) {
if (a->track_get_path(i).get_subname_count() == 1 && Object::cast_to<Skeleton>(child)) {
- bone_idx = Object::cast_to<Skeleton>(child)->find_bone(a->track_get_path(i).get_subname(0));
- if (bone_idx == -1) {
+ Skeleton *sk = Object::cast_to<Skeleton>(child);
+ bone_idx = sk->find_bone(a->track_get_path(i).get_subname(0));
+ if (bone_idx == -1 || sk->is_bone_ignore_animation(bone_idx)) {
continue;
}
@@ -579,20 +580,16 @@ void AnimationPlayer::_animation_process2(float p_delta) {
}
void AnimationPlayer::_animation_update_transforms() {
+ {
+ Transform t;
+ for (int i = 0; i < cache_update_size; i++) {
- for (int i = 0; i < cache_update_size; i++) {
+ TrackNodeCache *nc = cache_update[i];
- TrackNodeCache *nc = cache_update[i];
+ ERR_CONTINUE(nc->accum_pass != accum_pass);
- ERR_CONTINUE(nc->accum_pass != accum_pass);
-
- if (nc->spatial) {
-
- Transform t;
t.origin = nc->loc_accum;
- t.basis = nc->rot_accum;
- t.basis.scale(nc->scale_accum);
-
+ t.basis.set_quat_scale(nc->rot_accum, nc->scale_accum);
if (nc->skeleton && nc->bone_idx >= 0) {
nc->skeleton->set_bone_pose(nc->bone_idx, t);
@@ -1025,6 +1022,13 @@ float AnimationPlayer::get_speed_scale() const {
return speed_scale;
}
+float AnimationPlayer::get_playing_speed() const {
+
+ if (!playing) {
+ return 0;
+ }
+ return speed_scale * playback.current.speed_scale;
+}
void AnimationPlayer::seek(float p_time, bool p_update) {
@@ -1203,7 +1207,7 @@ NodePath AnimationPlayer::get_root() const {
void AnimationPlayer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
String pf = p_function;
- if (p_function == "play" || p_function == "remove_animation" || p_function == "has_animation" || p_function == "queue") {
+ if (p_function == "play" || p_function == "play_backwards" || p_function == "remove_animation" || p_function == "has_animation" || p_function == "queue") {
List<StringName> al;
get_animation_list(&al);
for (List<StringName>::Element *E = al.front(); E; E = E->next()) {
@@ -1316,6 +1320,7 @@ void AnimationPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_speed_scale", "speed"), &AnimationPlayer::set_speed_scale);
ClassDB::bind_method(D_METHOD("get_speed_scale"), &AnimationPlayer::get_speed_scale);
+ ClassDB::bind_method(D_METHOD("get_playing_speed"), &AnimationPlayer::get_playing_speed);
ClassDB::bind_method(D_METHOD("set_autoplay", "name"), &AnimationPlayer::set_autoplay);
ClassDB::bind_method(D_METHOD("get_autoplay"), &AnimationPlayer::get_autoplay);
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index ef758bac4..af2022dda 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -293,6 +293,7 @@ public:
void set_speed_scale(float p_speed);
float get_speed_scale() const;
+ float get_playing_speed() const;
void set_autoplay(const String &p_name);
String get_autoplay() const;
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index 89f0e43a8..ce5b372d7 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -813,7 +813,11 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
t.value = t.object->get_indexed(t.subpath);
t.value.zero();
- t.skip = false;
+ if (t.skeleton) {
+ t.skip = t.skeleton->is_bone_ignore_animation(t.bone_idx);
+ } else {
+ t.skip = false;
+ }
}
/* STEP 2 PROCESS ANIMATIONS */
@@ -895,13 +899,12 @@ void AnimationTreePlayer::_process_animation(float p_delta) {
}
Transform xform;
- xform.basis = t.rot;
xform.origin = t.loc;
t.scale.x += 1.0;
t.scale.y += 1.0;
t.scale.z += 1.0;
- xform.basis.scale(t.scale);
+ xform.basis.set_quat_scale(t.rot, t.scale);
if (t.bone_idx >= 0) {
if (t.skeleton)
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 562dd155f..dbfb96697 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -252,7 +252,7 @@ void BaseButton::_notification(int p_what) {
status.hovering = false;
update();
}
- if (p_what == NOTIFICATION_DRAG_BEGIN) {
+ if (p_what == NOTIFICATION_DRAG_BEGIN || p_what == NOTIFICATION_SCROLL_BEGIN) {
if (status.press_attempt) {
status.press_attempt = false;
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 31be18612..6f34f3e49 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -660,6 +660,11 @@ void ColorPickerButton::_color_changed(const Color &p_color) {
emit_signal("color_changed", p_color);
}
+void ColorPickerButton::_modal_closed() {
+
+ emit_signal("popup_closed");
+}
+
void ColorPickerButton::pressed() {
popup->set_position(get_global_position() - picker->get_combined_minimum_size());
@@ -722,8 +727,10 @@ void ColorPickerButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_edit_alpha", "show"), &ColorPickerButton::set_edit_alpha);
ClassDB::bind_method(D_METHOD("is_editing_alpha"), &ColorPickerButton::is_editing_alpha);
ClassDB::bind_method(D_METHOD("_color_changed"), &ColorPickerButton::_color_changed);
+ ClassDB::bind_method(D_METHOD("_modal_closed"), &ColorPickerButton::_modal_closed);
ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color")));
+ ADD_SIGNAL(MethodInfo("popup_closed"));
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
}
@@ -735,5 +742,7 @@ ColorPickerButton::ColorPickerButton() {
popup->add_child(picker);
picker->connect("color_changed", this, "_color_changed");
+ popup->connect("modal_closed", this, "_modal_closed");
+
add_child(popup);
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 40ded4fff..7d1a554ad 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -120,6 +120,8 @@ class ColorPickerButton : public Button {
ColorPicker *picker;
void _color_changed(const Color &p_color);
+ void _modal_closed();
+
virtual void pressed();
protected:
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index a5883863c..b7c1d35fd 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -94,6 +94,14 @@ Point2 Control::_edit_get_position() const {
return get_position();
};
+void Control::_edit_set_scale(const Size2 &p_scale) {
+ set_scale(p_scale);
+}
+
+Size2 Control::_edit_get_scale() const {
+ return data.scale;
+}
+
void Control::_edit_set_rect(const Rect2 &p_edit_rect) {
set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)));
set_size(p_edit_rect.size.snapped(Vector2(1, 1)));
@@ -1281,22 +1289,24 @@ void Control::_size_changed() {
Size2 minimum_size = get_combined_minimum_size();
- if (data.h_grow == GROW_DIRECTION_BEGIN) {
- if (minimum_size.width > new_size_cache.width) {
- new_pos_cache.x = new_pos_cache.x + new_size_cache.width - minimum_size.width;
- new_size_cache.width = minimum_size.width;
+ if (minimum_size.width > new_size_cache.width) {
+ if (data.h_grow == GROW_DIRECTION_BEGIN) {
+ new_pos_cache.x += new_size_cache.width - minimum_size.width;
+ } else if (data.h_grow == GROW_DIRECTION_BOTH) {
+ new_pos_cache.x += 0.5 * (new_size_cache.width - minimum_size.width);
}
- } else {
- new_size_cache.width = MAX(minimum_size.width, new_size_cache.width);
+
+ new_size_cache.width = minimum_size.width;
}
- if (data.v_grow == GROW_DIRECTION_BEGIN) {
- if (minimum_size.height > new_size_cache.height) {
- new_pos_cache.y = new_pos_cache.y + new_size_cache.height - minimum_size.height;
- new_size_cache.height = minimum_size.height;
+ if (minimum_size.height > new_size_cache.height) {
+ if (data.v_grow == GROW_DIRECTION_BEGIN) {
+ new_pos_cache.y += new_size_cache.height - minimum_size.height;
+ } else if (data.v_grow == GROW_DIRECTION_BOTH) {
+ new_pos_cache.y += 0.5 * (new_size_cache.height - minimum_size.height);
}
- } else {
- new_size_cache.height = MAX(minimum_size.height, new_size_cache.height);
+
+ new_size_cache.height = minimum_size.height;
}
// We use a little workaround to avoid flickering when moving the pivot with _edit_set_pivot()
@@ -2838,8 +2848,8 @@ void Control::_bind_methods() {
ADD_PROPERTYINZ(PropertyInfo(Variant::INT, "margin_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_margin", "get_margin", MARGIN_BOTTOM);
ADD_GROUP("Grow Direction", "grow_");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End"), "set_h_grow_direction", "get_h_grow_direction");
- ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End"), "set_v_grow_direction", "get_v_grow_direction");
+ ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_h_grow_direction", "get_h_grow_direction");
+ ADD_PROPERTYNO(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
ADD_GROUP("Rect", "rect_");
ADD_PROPERTYNZ(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
@@ -2886,6 +2896,8 @@ void Control::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_FOCUS_EXIT);
BIND_CONSTANT(NOTIFICATION_THEME_CHANGED);
BIND_CONSTANT(NOTIFICATION_MODAL_CLOSE);
+ BIND_CONSTANT(NOTIFICATION_SCROLL_BEGIN);
+ BIND_CONSTANT(NOTIFICATION_SCROLL_END);
BIND_ENUM_CONSTANT(CURSOR_ARROW);
BIND_ENUM_CONSTANT(CURSOR_IBEAM);
@@ -2939,6 +2951,7 @@ void Control::_bind_methods() {
BIND_ENUM_CONSTANT(GROW_DIRECTION_BEGIN);
BIND_ENUM_CONSTANT(GROW_DIRECTION_END);
+ BIND_ENUM_CONSTANT(GROW_DIRECTION_BOTH);
BIND_ENUM_CONSTANT(ANCHOR_BEGIN);
BIND_ENUM_CONSTANT(ANCHOR_END);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 51325f27b..b5453e60f 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -60,7 +60,8 @@ public:
enum GrowDirection {
GROW_DIRECTION_BEGIN,
- GROW_DIRECTION_END
+ GROW_DIRECTION_END,
+ GROW_DIRECTION_BOTH
};
enum FocusMode {
@@ -271,6 +272,8 @@ public:
NOTIFICATION_FOCUS_EXIT = 44,
NOTIFICATION_THEME_CHANGED = 45,
NOTIFICATION_MODAL_CLOSE = 46,
+ NOTIFICATION_SCROLL_BEGIN = 47,
+ NOTIFICATION_SCROLL_END = 48,
};
@@ -280,6 +283,9 @@ public:
virtual void _edit_set_position(const Point2 &p_position);
virtual Point2 _edit_get_position() const;
+ virtual void _edit_set_scale(const Size2 &p_scale);
+ virtual Size2 _edit_get_scale() const;
+
virtual void _edit_set_rect(const Rect2 &p_edit_rect);
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_use_rect() const;
diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp
index b401abd43..278e4123d 100644
--- a/scene/gui/grid_container.cpp
+++ b/scene/gui/grid_container.cpp
@@ -36,6 +36,8 @@ void GridContainer::_notification(int p_what) {
case NOTIFICATION_SORT_CHILDREN: {
+ int valid_controls_index;
+
Map<int, int> col_minw; // max of min_width of all controls in each col (indexed by col)
Map<int, int> row_minh; // max of min_height of all controls in each row (indexed by row)
Set<int> col_expanded; // columns which have the SIZE_EXPAND flag set
@@ -47,13 +49,15 @@ void GridContainer::_notification(int p_what) {
int max_row = get_child_count() / columns;
// Compute the per-column/per-row data
+ valid_controls_index = 0;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
if (!c || !c->is_visible_in_tree())
continue;
- int row = i / columns;
- int col = i % columns;
+ int row = valid_controls_index / columns;
+ int col = valid_controls_index % columns;
+ valid_controls_index++;
Size2i ms = c->get_combined_minimum_size();
if (col_minw.has(col))
@@ -136,12 +140,14 @@ void GridContainer::_notification(int p_what) {
int col_ofs = 0;
int row_ofs = 0;
+ valid_controls_index = 0;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
if (!c || !c->is_visible_in_tree())
continue;
- int row = i / columns;
- int col = i % columns;
+ int row = valid_controls_index / columns;
+ int col = valid_controls_index % columns;
+ valid_controls_index++;
if (col == 0) {
col_ofs = 0;
@@ -178,6 +184,8 @@ void GridContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_columns", "columns"), &GridContainer::set_columns);
ClassDB::bind_method(D_METHOD("get_columns"), &GridContainer::get_columns);
+ ClassDB::bind_method(D_METHOD("get_child_control_at_cell", "row", "column"),
+ &GridContainer::get_child_control_at_cell);
ADD_PROPERTY(PropertyInfo(Variant::INT, "columns", PROPERTY_HINT_RANGE, "1,1024,1"), "set_columns", "get_columns");
}
@@ -190,17 +198,19 @@ Size2 GridContainer::get_minimum_size() const {
int hsep = get_constant("hseparation");
int vsep = get_constant("vseparation");
- int idx = 0;
int max_row = 0;
int max_col = 0;
+ int valid_controls_index = 0;
for (int i = 0; i < get_child_count(); i++) {
Control *c = Object::cast_to<Control>(get_child(i));
if (!c || !c->is_visible_in_tree())
continue;
- int row = idx / columns;
- int col = idx % columns;
+ int row = valid_controls_index / columns;
+ int col = valid_controls_index % columns;
+ valid_controls_index++;
+
Size2i ms = c->get_combined_minimum_size();
if (col_minw.has(col))
col_minw[col] = MAX(col_minw[col], ms.width);
@@ -213,7 +223,6 @@ Size2 GridContainer::get_minimum_size() const {
row_minh[row] = ms.height;
max_col = MAX(col, max_col);
max_row = MAX(row, max_row);
- idx++;
}
Size2 ms;
@@ -232,6 +241,21 @@ Size2 GridContainer::get_minimum_size() const {
return ms;
}
+Control *GridContainer::get_child_control_at_cell(int row, int column) {
+ Control *c;
+ int grid_index = row * columns + column;
+ for (int i = 0; i < get_child_count(); i++) {
+ c = Object::cast_to<Control>(get_child(i));
+ if (!c || !c->is_visible_in_tree())
+ continue;
+
+ if (grid_index == i) {
+ break;
+ }
+ }
+ return c;
+}
+
GridContainer::GridContainer() {
set_mouse_filter(MOUSE_FILTER_PASS);
diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h
index 243d06f03..7e3470dc8 100644
--- a/scene/gui/grid_container.h
+++ b/scene/gui/grid_container.h
@@ -47,6 +47,7 @@ public:
void set_columns(int p_columns);
int get_columns() const;
virtual Size2 get_minimum_size() const;
+ Control *get_child_control_at_cell(int row, int column);
GridContainer();
};
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index cc17e6bcd..511dc248a 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -37,6 +37,7 @@ void ItemList::add_item(const String &p_item, const Ref<Texture> &p_texture, boo
Item item;
item.icon = p_texture;
item.icon_region = Rect2i();
+ item.icon_modulate = Color(1, 1, 1, 1);
item.text = p_item;
item.selectable = p_selectable;
item.selected = false;
@@ -54,6 +55,7 @@ void ItemList::add_icon_item(const Ref<Texture> &p_item, bool p_selectable) {
Item item;
item.icon = p_item;
item.icon_region = Rect2i();
+ item.icon_modulate = Color(1, 1, 1, 1);
//item.text=p_item;
item.selectable = p_selectable;
item.selected = false;
@@ -138,6 +140,21 @@ Rect2 ItemList::get_item_icon_region(int p_idx) const {
return items[p_idx].icon_region;
}
+void ItemList::set_item_icon_modulate(int p_idx, const Color &p_modulate) {
+
+ ERR_FAIL_INDEX(p_idx, items.size());
+
+ items[p_idx].icon_modulate = p_modulate;
+ update();
+}
+
+Color ItemList::get_item_icon_modulate(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx, items.size(), Color());
+
+ return items[p_idx].icon_modulate;
+}
+
void ItemList::set_item_custom_bg_color(int p_idx, const Color &p_custom_bg_color) {
ERR_FAIL_INDEX(p_idx, items.size());
@@ -661,7 +678,7 @@ void ItemList::_gui_input(const Ref<InputEvent> &p_event) {
search_string = ""; //any mousepress cancels
- if (current % current_columns != (current_columns - 1)) {
+ if (current % current_columns != (current_columns - 1) && current + 1 < items.size()) {
set_current(current + 1);
ensure_current_is_visible();
if (select_mode == SELECT_SINGLE) {
@@ -1045,7 +1062,7 @@ void ItemList::_notification(int p_what) {
draw_rect.size = adj.size;
}
- Color modulate = Color(1, 1, 1, 1);
+ Color modulate = items[i].icon_modulate;
if (items[i].disabled)
modulate.a *= 0.5;
@@ -1389,6 +1406,9 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_icon_region", "idx", "rect"), &ItemList::set_item_icon_region);
ClassDB::bind_method(D_METHOD("get_item_icon_region", "idx"), &ItemList::get_item_icon_region);
+ ClassDB::bind_method(D_METHOD("set_item_icon_modulate", "idx", "modulate"), &ItemList::set_item_icon_modulate);
+ ClassDB::bind_method(D_METHOD("get_item_icon_modulate", "idx"), &ItemList::get_item_icon_modulate);
+
ClassDB::bind_method(D_METHOD("set_item_selectable", "idx", "selectable"), &ItemList::set_item_selectable);
ClassDB::bind_method(D_METHOD("is_item_selectable", "idx"), &ItemList::is_item_selectable);
@@ -1414,7 +1434,7 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_selected", "idx"), &ItemList::is_selected);
ClassDB::bind_method(D_METHOD("get_selected_items"), &ItemList::get_selected_items);
- ClassDB::bind_method(D_METHOD("move_item", "p_from_idx", "p_to_idx"), &ItemList::move_item);
+ ClassDB::bind_method(D_METHOD("move_item", "from_idx", "to_idx"), &ItemList::move_item);
ClassDB::bind_method(D_METHOD("get_item_count"), &ItemList::get_item_count);
ClassDB::bind_method(D_METHOD("remove_item", "idx"), &ItemList::remove_item);
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index 0fa0dd415..58771c177 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -54,6 +54,7 @@ private:
Ref<Texture> icon;
Rect2i icon_region;
+ Color icon_modulate;
Ref<Texture> tag_icon;
String text;
bool selectable;
@@ -135,6 +136,9 @@ public:
void set_item_icon_region(int p_idx, const Rect2 &p_region);
Rect2 get_item_icon_region(int p_idx) const;
+ void set_item_icon_modulate(int p_idx, const Color &p_modulate);
+ Color get_item_icon_modulate(int p_idx) const;
+
void set_item_selectable(int p_idx, bool p_selectable);
bool is_item_selectable(int p_idx) const;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 5c0e8fefc..1ceb3f0a8 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -215,6 +215,12 @@ void LineEdit::_gui_input(Ref<InputEvent> p_event) {
case (KEY_A): { //Select All
select();
} break;
+ case (KEY_LEFT): { // Go to start of text - like HOME key
+ set_cursor_position(0);
+ } break;
+ case (KEY_RIGHT): { // Go to end of text - like END key
+ set_cursor_position(text.length());
+ } break;
default: { handled = false; }
}
@@ -602,6 +608,7 @@ void LineEdit::_notification(int p_what) {
}
int x_ofs = 0;
+ int cached_text_width = text.empty() ? cached_placeholder_width : cached_width;
switch (align) {
@@ -615,15 +622,15 @@ void LineEdit::_notification(int p_what) {
if (window_pos != 0)
x_ofs = style->get_offset().x;
else
- x_ofs = int(size.width - (cached_width)) / 2;
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - (cached_text_width)) / 2);
} break;
case ALIGN_RIGHT: {
- x_ofs = int(size.width - style->get_offset().x - (cached_width));
+ x_ofs = MAX(style->get_margin(MARGIN_LEFT), int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_text_width)));
} break;
}
- int ofs_max = width - style->get_minimum_size().width;
+ int ofs_max = width - style->get_margin(MARGIN_RIGHT);
int char_ofs = window_pos;
int y_area = height - style->get_minimum_size().height;
@@ -662,8 +669,8 @@ void LineEdit::_notification(int p_what) {
if (ofs >= ime_text.length())
break;
- CharType cchar = (pass && !text.empty()) ? '*' : ime_text[ofs];
- CharType next = (pass && !text.empty()) ? '*' : ime_text[ofs + 1];
+ CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs];
+ CharType next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1];
int im_char_width = font->get_char_size(cchar, next).width;
if ((x_ofs + im_char_width) > ofs_max)
@@ -684,8 +691,8 @@ void LineEdit::_notification(int p_what) {
}
}
- CharType cchar = (pass && !text.empty()) ? '*' : t[char_ofs];
- CharType next = (pass && !text.empty()) ? '*' : t[char_ofs + 1];
+ CharType cchar = (pass && !text.empty()) ? secret_character[0] : t[char_ofs];
+ CharType next = (pass && !text.empty()) ? secret_character[0] : t[char_ofs + 1];
int char_width = font->get_char_size(cchar, next).width;
// end of widget, break!
@@ -716,8 +723,8 @@ void LineEdit::_notification(int p_what) {
if (ofs >= ime_text.length())
break;
- CharType cchar = (pass && !text.empty()) ? '*' : ime_text[ofs];
- CharType next = (pass && !text.empty()) ? '*' : ime_text[ofs + 1];
+ CharType cchar = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs];
+ CharType next = (pass && !text.empty()) ? secret_character[0] : ime_text[ofs + 1];
int im_char_width = font->get_char_size(cchar, next).width;
if ((x_ofs + im_char_width) > ofs_max)
@@ -881,7 +888,7 @@ void LineEdit::set_cursor_at_pixel_pos(int p_x) {
} break;
case ALIGN_RIGHT: {
- pixel_ofs = int(size.width - style->get_offset().x - (cached_width));
+ pixel_ofs = int(size.width - style->get_margin(MARGIN_RIGHT) - (cached_width));
} break;
}
@@ -1014,6 +1021,15 @@ String LineEdit::get_text() const {
void LineEdit::set_placeholder(String p_text) {
placeholder = tr(p_text);
+ if ((max_length <= 0) || (placeholder.length() <= max_length)) {
+ Ref<Font> font = get_font("font");
+ cached_placeholder_width = 0;
+ if (font != NULL) {
+ for (int i = 0; i < placeholder.length(); i++) {
+ cached_placeholder_width += font->get_char_size(placeholder[i]).width;
+ }
+ }
+ }
update();
}
@@ -1214,6 +1230,7 @@ void LineEdit::select_all() {
selection.enabled = true;
update();
}
+
void LineEdit::set_editable(bool p_editable) {
editable = p_editable;
@@ -1230,11 +1247,27 @@ void LineEdit::set_secret(bool p_secret) {
pass = p_secret;
update();
}
+
bool LineEdit::is_secret() const {
return pass;
}
+void LineEdit::set_secret_character(const String &p_string) {
+
+ // An empty string as the secret character would crash the engine
+ // It also wouldn't make sense to use multiple characters as the secret character
+ ERR_EXPLAIN("Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given)");
+ ERR_FAIL_COND(p_string.length() != 1);
+
+ secret_character = p_string;
+ update();
+}
+
+String LineEdit::get_secret_character() const {
+ return secret_character;
+}
+
void LineEdit::select(int p_from, int p_to) {
if (p_from == 0 && p_to == 0) {
@@ -1316,12 +1349,12 @@ PopupMenu *LineEdit::get_menu() const {
return menu;
}
-#ifdef TOOLS_ENABLED
void LineEdit::_editor_settings_changed() {
+#ifdef TOOLS_ENABLED
cursor_set_blink_enabled(EDITOR_DEF("text_editor/cursor/caret_blink", false));
cursor_set_blink_speed(EDITOR_DEF("text_editor/cursor/caret_blink_speed", 0.65));
-}
#endif
+}
void LineEdit::set_expand_to_text_length(bool p_enabled) {
@@ -1390,9 +1423,7 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_text_changed"), &LineEdit::_text_changed);
ClassDB::bind_method(D_METHOD("_toggle_draw_caret"), &LineEdit::_toggle_draw_caret);
-#ifdef TOOLS_ENABLED
ClassDB::bind_method("_editor_settings_changed", &LineEdit::_editor_settings_changed);
-#endif
ClassDB::bind_method(D_METHOD("set_align", "align"), &LineEdit::set_align);
ClassDB::bind_method(D_METHOD("get_align"), &LineEdit::get_align);
@@ -1423,6 +1454,8 @@ void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_editable"), &LineEdit::is_editable);
ClassDB::bind_method(D_METHOD("set_secret", "enabled"), &LineEdit::set_secret);
ClassDB::bind_method(D_METHOD("is_secret"), &LineEdit::is_secret);
+ ClassDB::bind_method(D_METHOD("set_secret_character", "character"), &LineEdit::set_secret_character);
+ ClassDB::bind_method(D_METHOD("get_secret_character"), &LineEdit::get_secret_character);
ClassDB::bind_method(D_METHOD("menu_option", "option"), &LineEdit::menu_option);
ClassDB::bind_method(D_METHOD("get_menu"), &LineEdit::get_menu);
ClassDB::bind_method(D_METHOD("set_context_menu_enabled", "enable"), &LineEdit::set_context_menu_enabled);
@@ -1450,6 +1483,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "max_length"), "set_max_length", "get_max_length");
ADD_PROPERTYNO(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character");
ADD_PROPERTYNZ(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length", "get_expand_to_text_length");
ADD_PROPERTY(PropertyInfo(Variant::INT, "focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_focus_mode", "get_focus_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
@@ -1458,7 +1492,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "placeholder_alpha", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_placeholder_alpha", "get_placeholder_alpha");
ADD_GROUP("Caret", "caret_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.1"), "cursor_set_blink_speed", "cursor_get_blink_speed");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "caret_position"), "set_cursor_position", "get_cursor_position");
}
@@ -1468,11 +1502,13 @@ LineEdit::LineEdit() {
_create_undo_state();
align = ALIGN_LEFT;
cached_width = 0;
+ cached_placeholder_width = 0;
cursor_pos = 0;
window_pos = 0;
window_has_focus = true;
max_length = 0;
pass = false;
+ secret_character = "*";
text_changed_dirty = false;
placeholder_alpha = 0.6;
@@ -1493,15 +1529,15 @@ LineEdit::LineEdit() {
context_menu_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
- menu->add_item(TTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X);
- menu->add_item(TTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C);
- menu->add_item(TTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V);
+ menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X);
+ menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C);
+ menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V);
menu->add_separator();
- menu->add_item(TTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A);
- menu->add_item(TTR("Clear"), MENU_CLEAR);
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A);
+ menu->add_item(RTR("Clear"), MENU_CLEAR);
menu->add_separator();
- menu->add_item(TTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
- menu->add_item(TTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z);
+ menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
+ menu->add_item(RTR("Redo"), MENU_REDO, KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Z);
menu->connect("id_pressed", this, "menu_option");
expand_to_text_length = false;
}
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index e3ad3b17f..304faed9b 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -72,6 +72,7 @@ private:
String undo_text;
String text;
String placeholder;
+ String secret_character;
float placeholder_alpha;
String ime_text;
Point2 ime_selection;
@@ -84,6 +85,7 @@ private:
int max_length; // 0 for no maximum
int cached_width;
+ int cached_placeholder_width;
struct Selection {
@@ -133,9 +135,7 @@ private:
void clear_internal();
void changed_internal();
-#ifdef TOOLS_ENABLED
void _editor_settings_changed();
-#endif
void _gui_input(Ref<InputEvent> p_event);
void _notification(int p_what);
@@ -193,6 +193,9 @@ public:
void set_secret(bool p_secret);
bool is_secret() const;
+ void set_secret_character(const String &p_string);
+ String get_secret_character() const;
+
virtual Size2 get_minimum_size() const;
void set_expand_to_text_length(bool p_enabled);
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 2e74faa61..87cf4dc33 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -59,7 +59,6 @@ void MenuButton::pressed() {
popup->set_size(Size2(size.width, 0));
popup->set_parent_rect(Rect2(Point2(gp - popup->get_global_position()), get_size()));
popup->popup();
- popup->set_invalidate_click_until_motion();
}
void MenuButton::_gui_input(Ref<InputEvent> p_event) {
@@ -109,7 +108,6 @@ MenuButton::MenuButton() {
add_child(popup);
popup->set_as_toplevel(true);
popup->set_pass_on_modal_close_click(false);
- connect("button_up", popup, "call_deferred", make_binds("grab_click_focus"));
set_process_unhandled_key_input(true);
set_action_mode(ACTION_MODE_BUTTON_PRESS);
}
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 6e53f11b9..a9402d640 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -112,13 +112,13 @@ void OptionButton::pressed() {
void OptionButton::add_icon_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID) {
- popup->add_icon_check_item(p_icon, p_label, p_ID);
+ popup->add_icon_radio_check_item(p_icon, p_label, p_ID);
if (popup->get_item_count() == 1)
select(0);
}
void OptionButton::add_item(const String &p_label, int p_ID) {
- popup->add_check_item(p_label, p_ID);
+ popup->add_radio_check_item(p_label, p_ID);
if (popup->get_item_count() == 1)
select(0);
}
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 747230e69..fd2466407 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -55,7 +55,7 @@ Size2 PopupMenu::get_minimum_size() const {
float max_w = 0;
int font_h = font->get_height();
- int check_w = get_icon("checked")->get_width();
+ int check_w = MAX(get_icon("checked")->get_width(), get_icon("radio_checked")->get_width());
int accel_max_w = 0;
for (int i = 0; i < items.size(); i++) {
@@ -74,7 +74,7 @@ Size2 PopupMenu::get_minimum_size() const {
size.width += items[i].h_ofs;
- if (items[i].checkable) {
+ if (items[i].checkable_type) {
size.width += check_w + hseparation;
}
@@ -284,7 +284,8 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
if (b->is_pressed())
return;
- switch (b->get_button_index()) {
+ int button_idx = b->get_button_index();
+ switch (button_idx) {
case BUTTON_WHEEL_DOWN: {
@@ -298,30 +299,37 @@ void PopupMenu::_gui_input(const Ref<InputEvent> &p_event) {
_scroll(b->get_factor(), b->get_position());
}
} break;
- case BUTTON_LEFT: {
+ default: {
+ // Allow activating item by releasing the LMB or any that was down when the popup appeared
+ if (button_idx == BUTTON_LEFT || (initial_button_mask & (1 << (button_idx - 1)))) {
- int over = _get_mouse_over(b->get_position());
+ bool was_during_grabbed_click = during_grabbed_click;
+ during_grabbed_click = false;
- if (invalidated_click) {
- invalidated_click = false;
- break;
- }
- if (over < 0) {
- hide();
- break; //non-activable
- }
+ int over = _get_mouse_over(b->get_position());
- if (items[over].separator || items[over].disabled)
- break;
+ if (invalidated_click) {
+ invalidated_click = false;
+ break;
+ }
+ if (over < 0) {
+ if (!was_during_grabbed_click) {
+ hide();
+ }
+ break; //non-activable
+ }
- if (items[over].submenu != "") {
+ if (items[over].separator || items[over].disabled)
+ break;
- _activate_submenu(over);
- return;
- }
- activate_item(over);
+ if (items[over].submenu != "") {
- } break;
+ _activate_submenu(over);
+ return;
+ }
+ activate_item(over);
+ }
+ }
}
//update();
@@ -408,8 +416,9 @@ void PopupMenu::_notification(int p_what) {
Ref<StyleBox> style = get_stylebox("panel");
Ref<StyleBox> hover = get_stylebox("hover");
Ref<Font> font = get_font("font");
- Ref<Texture> check = get_icon("checked");
- Ref<Texture> uncheck = get_icon("unchecked");
+ // In Item::checkable_type enum order (less the non-checkable member)
+ Ref<Texture> check[] = { get_icon("checked"), get_icon("radio_checked") };
+ Ref<Texture> uncheck[] = { get_icon("unchecked"), get_icon("radio_unchecked") };
Ref<Texture> submenu = get_icon("submenu");
Ref<StyleBox> separator = get_stylebox("separator");
@@ -452,14 +461,10 @@ void PopupMenu::_notification(int p_what) {
separator->draw(ci, Rect2(item_ofs + Point2(0, Math::floor((h - sep_h) / 2.0)), Size2(get_size().width - style->get_minimum_size().width, sep_h)));
}
- if (items[i].checkable) {
-
- if (items[i].checked)
- check->draw(ci, item_ofs + Point2(0, Math::floor((h - check->get_height()) / 2.0)));
- else
- uncheck->draw(ci, item_ofs + Point2(0, Math::floor((h - check->get_height()) / 2.0)));
-
- item_ofs.x += check->get_width() + hseparation;
+ if (items[i].checkable_type) {
+ Texture *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
+ icon->draw(ci, item_ofs + Point2(0, Math::floor((h - icon->get_height()) / 2.0)));
+ item_ofs.x += icon->get_width() + hseparation;
}
if (!items[i].icon.is_null()) {
@@ -503,6 +508,11 @@ void PopupMenu::_notification(int p_what) {
update();
}
} break;
+ case NOTIFICATION_POST_POPUP: {
+
+ initial_button_mask = Input::get_singleton()->get_mouse_button_mask();
+ during_grabbed_click = (bool)initial_button_mask;
+ } break;
case NOTIFICATION_POPUP_HIDE: {
if (mouse_over >= 0) {
@@ -554,10 +564,11 @@ void PopupMenu::add_icon_check_item(const Ref<Texture> &p_icon, const String &p_
item.xl_text = tr(p_label);
item.accel = p_accel;
item.ID = p_ID;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
}
+
void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
Item item;
@@ -565,11 +576,25 @@ void PopupMenu::add_check_item(const String &p_label, int p_ID, uint32_t p_accel
item.xl_text = tr(p_label);
item.accel = p_accel;
item.ID = p_ID;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
}
+void PopupMenu::add_radio_check_item(const String &p_label, int p_ID, uint32_t p_accel) {
+
+ add_check_item(p_label, p_ID, p_accel);
+ items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ update();
+}
+
+void PopupMenu::add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID, uint32_t p_accel) {
+
+ add_icon_check_item(p_icon, p_label, p_ID, p_accel);
+ items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ update();
+}
+
void PopupMenu::add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
@@ -598,6 +623,7 @@ void PopupMenu::add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_g
items.push_back(item);
update();
}
+
void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
ERR_FAIL_COND(p_shortcut.is_null());
@@ -607,7 +633,7 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<Sh
Item item;
item.ID = p_ID;
item.shortcut = p_shortcut;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
item.icon = p_icon;
item.shortcut_is_global = p_global;
items.push_back(item);
@@ -624,11 +650,18 @@ void PopupMenu::add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bo
item.ID = p_ID;
item.shortcut = p_shortcut;
item.shortcut_is_global = p_global;
- item.checkable = true;
+ item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX;
items.push_back(item);
update();
}
+void PopupMenu::add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID, bool p_global) {
+
+ add_check_shortcut(p_shortcut, p_ID, p_global);
+ items[items.size() - 1].checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON;
+ update();
+}
+
void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID, uint32_t p_accel) {
Item item;
@@ -636,7 +669,6 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
item.xl_text = tr(p_label);
item.accel = p_accel;
item.ID = p_ID;
- item.checkable = false;
item.max_states = p_max_states;
item.state = p_default_state;
items.push_back(item);
@@ -811,7 +843,14 @@ bool PopupMenu::is_item_separator(int p_idx) const {
void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) {
ERR_FAIL_INDEX(p_idx, items.size());
- items[p_idx].checkable = p_checkable;
+ items[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE;
+ update();
+}
+
+void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) {
+
+ ERR_FAIL_INDEX(p_idx, items.size());
+ items[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE;
update();
}
@@ -867,7 +906,12 @@ void PopupMenu::toggle_item_multistate(int p_idx) {
bool PopupMenu::is_item_checkable(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, items.size(), false);
- return items[p_idx].checkable;
+ return items[p_idx].checkable_type;
+}
+
+bool PopupMenu::is_item_radio_checkable(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx, items.size(), false);
+ return items[p_idx].checkable_type == Item::CHECKABLE_TYPE_RADIO_BUTTON;
}
int PopupMenu::get_item_count() const {
@@ -941,7 +985,7 @@ void PopupMenu::activate_item(int p_item) {
// We close all parents that are chained together,
// with hide_on_item_selection enabled
- if (items[p_item].checkable) {
+ if (items[p_item].checkable_type) {
if (!hide_on_checkable_item_selection || !pop->is_hide_on_checkable_item_selection())
break;
} else if (0 < items[p_item].max_states) {
@@ -958,7 +1002,7 @@ void PopupMenu::activate_item(int p_item) {
// Hides popup by default; unless otherwise specified
// by using set_hide_on_item_selection and set_hide_on_checkable_item_selection
- if (items[p_item].checkable) {
+ if (items[p_item].checkable_type) {
if (!hide_on_checkable_item_selection)
return;
} else if (0 < items[p_item].max_states) {
@@ -1010,7 +1054,9 @@ Array PopupMenu::_get_items() const {
items.push_back(get_item_text(i));
items.push_back(get_item_icon(i));
- items.push_back(is_item_checkable(i));
+ // For compatibility, use false/true for no/checkbox and integers for other values
+ int ct = this->items[i].checkable_type;
+ items.push_back(Variant(ct <= Item::CHECKABLE_TYPE_CHECK_BOX ? is_item_checkable(i) : ct));
items.push_back(is_item_checked(i));
items.push_back(is_item_disabled(i));
@@ -1053,7 +1099,9 @@ void PopupMenu::_set_items(const Array &p_items) {
String text = p_items[i + 0];
Ref<Texture> icon = p_items[i + 1];
+ // For compatibility, use false/true for no/checkbox and integers for other values
bool checkable = p_items[i + 2];
+ bool radio_checkable = (int)p_items[i + 2] == Item::CHECKABLE_TYPE_RADIO_BUTTON;
bool checked = p_items[i + 3];
bool disabled = p_items[i + 4];
@@ -1066,7 +1114,13 @@ void PopupMenu::_set_items(const Array &p_items) {
int idx = get_item_count();
add_item(text, id);
set_item_icon(idx, icon);
- set_item_as_checkable(idx, checkable);
+ if (checkable) {
+ if (radio_checkable) {
+ set_item_as_radio_checkable(idx, true);
+ } else {
+ set_item_as_checkable(idx, true);
+ }
+ }
set_item_checked(idx, checked);
set_item_disabled(idx, disabled);
set_item_id(idx, id);
@@ -1147,12 +1201,14 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_item", "label", "id", "accel"), &PopupMenu::add_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_icon_check_item", "texture", "label", "id", "accel"), &PopupMenu::add_icon_check_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_check_item", "label", "id", "accel"), &PopupMenu::add_check_item, DEFVAL(-1), DEFVAL(0));
+ ClassDB::bind_method(D_METHOD("add_radio_check_item", "label", "id", "accel"), &PopupMenu::add_radio_check_item, DEFVAL(-1), DEFVAL(0));
ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("add_icon_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_shortcut", "shortcut", "id", "global"), &PopupMenu::add_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_icon_check_shortcut", "texture", "shortcut", "id", "global"), &PopupMenu::add_icon_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("add_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_check_shortcut, DEFVAL(-1), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_radio_check_shortcut", "shortcut", "id", "global"), &PopupMenu::add_radio_check_shortcut, DEFVAL(-1), DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &PopupMenu::set_item_text);
ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &PopupMenu::set_item_icon);
@@ -1164,6 +1220,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_item_submenu", "idx", "submenu"), &PopupMenu::set_item_submenu);
ClassDB::bind_method(D_METHOD("set_item_as_separator", "idx", "enable"), &PopupMenu::set_item_as_separator);
ClassDB::bind_method(D_METHOD("set_item_as_checkable", "idx", "enable"), &PopupMenu::set_item_as_checkable);
+ ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "idx", "enable"), &PopupMenu::set_item_as_radio_checkable);
ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &PopupMenu::set_item_tooltip);
ClassDB::bind_method(D_METHOD("set_item_shortcut", "idx", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_item_multistate", "idx", "state"), &PopupMenu::set_item_multistate);
@@ -1182,6 +1239,7 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_submenu", "idx"), &PopupMenu::get_item_submenu);
ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &PopupMenu::is_item_separator);
ClassDB::bind_method(D_METHOD("is_item_checkable", "idx"), &PopupMenu::is_item_checkable);
+ ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "idx"), &PopupMenu::is_item_radio_checkable);
ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &PopupMenu::get_item_tooltip);
ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut);
@@ -1216,15 +1274,20 @@ void PopupMenu::_bind_methods() {
ADD_SIGNAL(MethodInfo("index_pressed", PropertyInfo(Variant::INT, "index")));
}
-void PopupMenu::set_invalidate_click_until_motion() {
+void PopupMenu::popup(const Rect2 &p_bounds) {
+
+ grab_click_focus();
moved = Vector2();
invalidated_click = true;
+ Popup::popup(p_bounds);
}
PopupMenu::PopupMenu() {
mouse_over = -1;
submenu_over = -1;
+ initial_button_mask = 0;
+ during_grabbed_click = false;
set_focus_mode(FOCUS_ALL);
set_as_toplevel(true);
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 60f36e95e..fde91bd84 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -46,7 +46,11 @@ class PopupMenu : public Popup {
String text;
String xl_text;
bool checked;
- bool checkable;
+ enum {
+ CHECKABLE_TYPE_NONE,
+ CHECKABLE_TYPE_CHECK_BOX,
+ CHECKABLE_TYPE_RADIO_BUTTON,
+ } checkable_type;
int max_states;
int state;
bool separator;
@@ -63,7 +67,7 @@ class PopupMenu : public Popup {
Item() {
checked = false;
- checkable = false;
+ checkable_type = CHECKABLE_TYPE_NONE;
separator = false;
max_states = 0;
state = 0;
@@ -78,6 +82,8 @@ class PopupMenu : public Popup {
Timer *submenu_timer;
List<Rect2> autohide_areas;
Vector<Item> items;
+ int initial_button_mask;
+ bool during_grabbed_click;
int mouse_over;
int submenu_over;
Rect2 parent_rect;
@@ -115,12 +121,15 @@ public:
void add_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_icon_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
+ void add_radio_check_item(const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
+ void add_icon_radio_check_item(const Ref<Texture> &p_icon, const String &p_label, int p_ID = -1, uint32_t p_accel = 0);
void add_submenu_item(const String &p_label, const String &p_submenu, int p_ID = -1);
void add_icon_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_icon_check_shortcut(const Ref<Texture> &p_icon, const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
+ void add_radio_check_shortcut(const Ref<ShortCut> &p_shortcut, int p_ID = -1, bool p_global = false);
void add_multistate_item(const String &p_label, int p_max_states, int p_default_state, int p_ID = -1, uint32_t p_accel = 0);
@@ -134,6 +143,7 @@ public:
void set_item_submenu(int p_idx, const String &p_submenu);
void set_item_as_separator(int p_idx, bool p_separator);
void set_item_as_checkable(int p_idx, bool p_checkable);
+ void set_item_as_radio_checkable(int p_idx, bool p_radio_checkable);
void set_item_tooltip(int p_idx, const String &p_tooltip);
void set_item_shortcut(int p_idx, const Ref<ShortCut> &p_shortcut, bool p_global = false);
void set_item_h_offset(int p_idx, int p_offset);
@@ -154,6 +164,7 @@ public:
String get_item_submenu(int p_idx) const;
bool is_item_separator(int p_idx) const;
bool is_item_checkable(int p_idx) const;
+ bool is_item_radio_checkable(int p_idx) const;
String get_item_tooltip(int p_idx) const;
Ref<ShortCut> get_item_shortcut(int p_idx) const;
int get_item_state(int p_idx) const;
@@ -178,7 +189,6 @@ public:
void add_autohide_area(const Rect2 &p_area);
void clear_autohide_areas();
- void set_invalidate_click_until_motion();
void set_hide_on_item_selection(bool p_enabled);
bool is_hide_on_item_selection() const;
@@ -188,6 +198,8 @@ public:
void set_hide_on_multistate_item_selection(bool p_enabled);
bool is_hide_on_multistate_item_selection() const;
+ virtual void popup(const Rect2 &p_bounds = Rect2());
+
PopupMenu();
~PopupMenu();
};
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index ae07d5e67..34114ae7d 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -90,7 +90,7 @@ Rect2 RichTextLabel::_get_text_rect() {
Ref<StyleBox> style = get_stylebox("normal");
return Rect2(style->get_offset(), get_size() - style->get_minimum_size());
}
-int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) {
+int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) {
RID ci;
if (r_outside)
@@ -269,10 +269,12 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
int descent = font->get_descent();
Color color;
+ Color font_color_shadow;
bool underline = false;
if (p_mode == PROCESS_DRAW) {
color = _find_color(text, p_base_color);
+ font_color_shadow = _find_color(text, p_font_color_shadow);
underline = _find_underline(text);
if (_find_meta(text, &meta)) {
@@ -284,7 +286,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
}
rchar = 0;
-
while (*c) {
int end = 0;
@@ -297,7 +298,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1;
line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1;
}
-
while (c[end] != 0 && !(end && c[end - 1] == ' ' && c[end] != ' ')) {
int cw = font->get_char_size(c[end], c[end + 1]).width;
@@ -314,7 +314,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
end++;
}
-
CHECK_HEIGHT(fh);
ENSURE_WIDTH(w);
@@ -376,16 +375,30 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
if (c[i] == '\t')
visible = false;
- if (selected) {
+ if (visible) {
+ if (selected) {
+ cw = font->get_char_size(c[i], c[i + 1]).x;
+ draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
+ }
- cw = font->get_char_size(c[i], c[i + 1]).x;
- draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
- if (visible)
- font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
+ if (p_font_color_shadow.a > 0) {
+ float x_ofs_shadow = align_ofs + pofs;
+ float y_ofs_shadow = y + lh - line_descent;
+ float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, c[i], c[i + 1], p_font_color_shadow);
+
+ if (p_shadow_as_outline) {
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
+ }
+ x_ofs_shadow += move;
+ }
- } else {
- if (visible)
+ if (selected) {
+ font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
+ } else {
cw = font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color);
+ }
}
p_char_count++;
@@ -464,6 +477,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
int vseparation = get_constant("table_vseparation");
Color ccolor = _find_color(table, p_base_color);
Vector2 draw_ofs = Point2(wofs, y);
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
if (p_mode == PROCESS_CACHE) {
@@ -487,7 +503,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
for (int i = 0; i < frame->lines.size(); i++) {
- _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color());
+ _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs);
table->columns[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width);
table->columns[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width);
}
@@ -516,6 +532,39 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
table->total_width += table->columns[i].width + hseparation;
}
+ //resize to max_width if needed and distribute the remaining space
+ bool table_need_fit = true;
+ while (table_need_fit) {
+ table_need_fit = false;
+ //fit slim
+ for (int i = 0; i < table->columns.size(); i++) {
+ if (!table->columns[i].expand)
+ continue;
+ int dif = table->columns[i].width - table->columns[i].max_width;
+ if (dif > 0) {
+ table_need_fit = true;
+ table->columns[i].width = table->columns[i].max_width;
+ table->total_width -= dif;
+ total_ratio -= table->columns[i].expand_ratio;
+ }
+ }
+ //grow
+ remaining_width = available_width - table->total_width;
+ if (remaining_width > 0 && total_ratio > 0) {
+ for (int i = 0; i < table->columns.size(); i++) {
+ if (table->columns[i].expand) {
+ int dif = table->columns[i].max_width - table->columns[i].width;
+ if (dif > 0) {
+ int slice = table->columns[i].expand_ratio * remaining_width / total_ratio;
+ int incr = MIN(dif, slice);
+ table->columns[i].width += incr;
+ table->total_width += incr;
+ }
+ }
+ }
+ }
+ }
+
//compute caches properly again with the right width
idx = 0;
for (List<Item *>::Element *E = table->subitems.front(); E; E = E->next()) {
@@ -527,7 +576,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
for (int i = 0; i < frame->lines.size(); i++) {
int ly = 0;
- _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color());
+ _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs);
frame->lines[i].height_cache = ly; //actual height
frame->lines[i].height_accum_cache = ly; //actual height
}
@@ -560,9 +609,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
if (visible) {
if (p_mode == PROCESS_DRAW) {
- nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor);
+ nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs);
} else if (p_mode == PROCESS_POINTER) {
- _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, p_click_pos, r_click_item, r_click_char, r_outside);
+ _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs, p_click_pos, r_click_item, r_click_char, r_outside);
if (r_click_item && *r_click_item) {
RETURN; // exit early
}
@@ -643,9 +692,7 @@ void RichTextLabel::_scroll_changed(double) {
void RichTextLabel::_update_scroll() {
- int total_height = 0;
- if (main->lines.size())
- total_height = main->lines[main->lines.size() - 1].height_accum_cache + get_stylebox("normal")->get_minimum_size().height;
+ int total_height = get_content_height();
bool exceeds = total_height > get_size().height && scroll_active;
@@ -734,12 +781,18 @@ void RichTextLabel::_notification(int p_what) {
int y = (main->lines[from_line].height_accum_cache - main->lines[from_line].height_cache) - ofs;
Ref<Font> base_font = get_font("normal_font");
Color base_color = get_color("default_color");
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
+
+ float x_ofs = 0;
visible_line_count = 0;
while (y < size.height && from_line < main->lines.size()) {
- visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, Point2i(), NULL, NULL, NULL, total_chars);
+ visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), NULL, NULL, NULL, total_chars);
total_chars += main->lines[from_line].char_count;
+
from_line++;
}
}
@@ -754,6 +807,9 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
Size2 size = get_size();
Rect2 text_rect = _get_text_rect();
int ofs = vscroll->get_value();
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
//todo, change to binary search
int from_line = 0;
@@ -774,7 +830,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
while (y < text_rect.get_size().height && from_line < p_frame->lines.size()) {
- _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, p_click, r_click_item, r_click_char, r_outside);
+ _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, p_click, r_click_item, r_click_char, r_outside);
if (r_click_item && *r_click_item)
return;
from_line++;
@@ -834,9 +890,9 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
// Erase previous selection.
if (selection.active) {
selection.from = NULL;
- selection.from_char = NULL;
+ selection.from_char = '\0';
selection.to = NULL;
- selection.to_char = NULL;
+ selection.to_char = '\0';
selection.active = false;
update();
@@ -1149,13 +1205,16 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
//validate invalid lines
Size2 size = get_size();
Rect2 text_rect = _get_text_rect();
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
Ref<Font> base_font = get_font("normal_font");
for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) {
int y = 0;
- _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color());
+ _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color(), font_color_shadow, use_outline, shadow_ofs);
p_frame->lines[i].height_cache = y;
p_frame->lines[i].height_accum_cache = y;
@@ -1997,6 +2056,13 @@ float RichTextLabel::get_percent_visible() const {
return percent_visible;
}
+int RichTextLabel::get_content_height() {
+ int total_height = 0;
+ if (main->lines.size())
+ total_height = main->lines[main->lines.size() - 1].height_accum_cache + get_stylebox("normal")->get_minimum_size().height;
+ return total_height;
+}
+
void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &RichTextLabel::_gui_input);
@@ -2063,6 +2129,8 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line_count"), &RichTextLabel::get_line_count);
ClassDB::bind_method(D_METHOD("get_visible_line_count"), &RichTextLabel::get_visible_line_count);
+ ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height);
+
ADD_GROUP("BBCode", "bbcode_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bbcode_text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode");
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 83938cff6..e054ce393 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -270,7 +270,7 @@ private:
int visible_characters;
float percent_visible;
- int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Point2i &p_click_pos = Point2i(), Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL, int p_char_count = 0);
+ int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL, int p_char_count = 0);
void _find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL);
Ref<Font> _find_font(Item *p_item);
@@ -340,6 +340,8 @@ public:
int get_line_count() const;
int get_visible_line_count() const;
+ int get_content_height();
+
VScrollBar *get_v_scroll() { return vscroll; }
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const;
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index e1cabd3f8..6ec67aca6 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -114,7 +114,7 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) {
if (smooth_scroll_enabled) {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
} else {
set_value(target_scroll);
}
@@ -138,7 +138,7 @@ void ScrollBar::_gui_input(Ref<InputEvent> p_event) {
if (smooth_scroll_enabled) {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
} else {
set_value(target_scroll);
}
@@ -322,7 +322,7 @@ void ScrollBar::_notification(int p_what) {
drag_slave = NULL;
}
- if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
if (scrolling) {
if (get_value() != target_scroll) {
@@ -337,7 +337,7 @@ void ScrollBar::_notification(int p_what) {
}
} else {
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
}
} else if (drag_slave_touching) {
@@ -397,7 +397,7 @@ void ScrollBar::_notification(int p_what) {
}
if (turnoff) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_slave_touching = false;
drag_slave_touching_deaccel = false;
}
@@ -566,7 +566,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) {
if (mb->is_pressed()) {
if (drag_slave_touching) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_slave_touching_deaccel = false;
drag_slave_touching = false;
drag_slave_speed = Vector2();
@@ -586,7 +586,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) {
drag_slave_touching_deaccel = false;
time_since_motion = 0;
if (drag_slave_touching) {
- set_physics_process(true);
+ set_physics_process_internal(true);
time_since_motion = 0;
}
}
@@ -598,7 +598,7 @@ void ScrollBar::_drag_slave_input(const Ref<InputEvent> &p_input) {
if (drag_slave_speed == Vector2()) {
drag_slave_touching_deaccel = false;
drag_slave_touching = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
} else {
drag_slave_touching_deaccel = true;
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 33b3d4648..a1dcf3b00 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -68,13 +68,19 @@ Size2 ScrollContainer::get_minimum_size() const {
};
void ScrollContainer::_cancel_drag() {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching_deaccel = false;
drag_touching = false;
drag_speed = Vector2();
drag_accum = Vector2();
last_drag_accum = Vector2();
drag_from = Vector2();
+
+ if (beyond_deadzone) {
+ emit_signal("scroll_ended");
+ propagate_notification(NOTIFICATION_SCROLL_END);
+ beyond_deadzone = false;
+ }
}
void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
@@ -122,13 +128,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (mb->is_pressed()) {
if (drag_touching) {
- set_physics_process(false);
- drag_touching_deaccel = false;
- drag_touching = false;
- drag_speed = Vector2();
- drag_accum = Vector2();
- last_drag_accum = Vector2();
- drag_from = Vector2();
+ _cancel_drag();
}
if (true) {
@@ -138,9 +138,10 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
drag_from = Vector2(h_scroll->get_value(), v_scroll->get_value());
drag_touching = OS::get_singleton()->has_touchscreen_ui_hint();
drag_touching_deaccel = false;
+ beyond_deadzone = false;
time_since_motion = 0;
if (drag_touching) {
- set_physics_process(true);
+ set_physics_process_internal(true);
time_since_motion = 0;
}
}
@@ -149,9 +150,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
if (drag_touching) {
if (drag_speed == Vector2()) {
- drag_touching_deaccel = false;
- drag_touching = false;
- set_physics_process(false);
+ _cancel_drag();
} else {
drag_touching_deaccel = true;
@@ -168,17 +167,27 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y);
drag_accum -= motion;
- Vector2 diff = drag_from + drag_accum;
- if (scroll_h)
- h_scroll->set_value(diff.x);
- else
- drag_accum.x = 0;
- if (scroll_v)
- v_scroll->set_value(diff.y);
- else
- drag_accum.y = 0;
- time_since_motion = 0;
+ if (beyond_deadzone || scroll_h && Math::abs(drag_accum.x) > deadzone || scroll_v && Math::abs(drag_accum.y) > deadzone) {
+ if (!beyond_deadzone) {
+ propagate_notification(NOTIFICATION_SCROLL_BEGIN);
+ emit_signal("scroll_started");
+
+ beyond_deadzone = true;
+ // resetting drag_accum here ensures smooth scrolling after reaching deadzone
+ drag_accum = -motion;
+ }
+ Vector2 diff = drag_from + drag_accum;
+ if (scroll_h)
+ h_scroll->set_value(diff.x);
+ else
+ drag_accum.x = 0;
+ if (scroll_v)
+ v_scroll->set_value(diff.y);
+ else
+ drag_accum.y = 0;
+ time_since_motion = 0;
+ }
}
}
@@ -269,7 +278,7 @@ void ScrollContainer::_notification(int p_what) {
update_scrollbars();
}
- if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
if (drag_touching) {
@@ -323,9 +332,7 @@ void ScrollContainer::_notification(int p_what) {
drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y);
if (turnoff_h && turnoff_v) {
- set_physics_process(false);
- drag_touching = false;
- drag_touching_deaccel = false;
+ _cancel_drag();
}
} else {
@@ -430,6 +437,14 @@ void ScrollContainer::set_h_scroll(int p_pos) {
_cancel_drag();
}
+int ScrollContainer::get_deadzone() const {
+ return deadzone;
+}
+
+void ScrollContainer::set_deadzone(int p_deadzone) {
+ deadzone = p_deadzone;
+}
+
String ScrollContainer::get_configuration_warning() const {
int found = 0;
@@ -466,12 +481,20 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_h_scroll"), &ScrollContainer::get_h_scroll);
ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &ScrollContainer::set_v_scroll);
ClassDB::bind_method(D_METHOD("get_v_scroll"), &ScrollContainer::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("set_deadzone", "deadzone"), &ScrollContainer::set_deadzone);
+ ClassDB::bind_method(D_METHOD("get_deadzone"), &ScrollContainer::get_deadzone);
+
+ ADD_SIGNAL(MethodInfo("scroll_started"));
+ ADD_SIGNAL(MethodInfo("scroll_ended"));
ADD_GROUP("Scroll", "scroll_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical"), "set_v_scroll", "get_v_scroll");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone");
+
+ GLOBAL_DEF("gui/common/default_scroll_deadzone", 0);
};
ScrollContainer::ScrollContainer() {
@@ -490,8 +513,11 @@ ScrollContainer::ScrollContainer() {
drag_speed = Vector2();
drag_touching = false;
drag_touching_deaccel = false;
+ beyond_deadzone = false;
scroll_h = true;
scroll_v = true;
+ deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone");
+
set_clip_contents(true);
};
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 6e3387918..3fe1ed447 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -56,10 +56,13 @@ class ScrollContainer : public Container {
bool drag_touching;
bool drag_touching_deaccel;
bool click_handled;
+ bool beyond_deadzone;
bool scroll_h;
bool scroll_v;
+ int deadzone;
+
void _cancel_drag();
protected:
@@ -86,6 +89,9 @@ public:
void set_enable_v_scroll(bool p_enable);
bool is_v_scroll_enabled() const;
+ int get_deadzone() const;
+ void set_deadzone(int p_deadzone);
+
virtual bool clips_input() const;
virtual String get_configuration_warning() const;
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 6e85ce5eb..0363dd44c 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -31,6 +31,9 @@
#include "tab_container.h"
#include "message_queue.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/label.h"
+#include "scene/gui/texture_rect.h"
int TabContainer::_get_top_margin() const {
@@ -492,6 +495,141 @@ void TabContainer::_update_current_tab() {
set_current_tab(current);
}
+Variant TabContainer::get_drag_data(const Point2 &p_point) {
+
+ if (!drag_to_rearrange_enabled)
+ return Variant();
+
+ int tab_over = get_tab_idx_at_point(p_point);
+
+ if (tab_over < 0)
+ return Variant();
+
+ HBoxContainer *drag_preview = memnew(HBoxContainer);
+
+ Ref<Texture> icon = get_tab_icon(tab_over);
+ if (!icon.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(icon);
+ drag_preview->add_child(tf);
+ }
+ Label *label = memnew(Label(get_tab_title(tab_over)));
+ drag_preview->add_child(label);
+ set_drag_preview(drag_preview);
+
+ Dictionary drag_data;
+ drag_data["type"] = "tabc_element";
+ drag_data["tabc_element"] = tab_over;
+ drag_data["from_path"] = get_path();
+ return drag_data;
+}
+
+bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+
+ if (!drag_to_rearrange_enabled)
+ return false;
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return false;
+
+ if (String(d["type"]) == "tabc_element") {
+
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ return true;
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between other TabContainers
+ Node *from_node = get_node(from_path);
+ TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
+ if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) {
+
+ if (!drag_to_rearrange_enabled)
+ return;
+
+ int hover_now = get_tab_idx_at_point(p_point);
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return;
+
+ if (String(d["type"]) == "tabc_element") {
+
+ int tab_from_id = d["tabc_element"];
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_child(get_tab_control(tab_from_id), hover_now);
+ set_current_tab(hover_now);
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between TabContainers
+ Node *from_node = get_node(from_path);
+ TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
+ if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ Control *moving_tabc = from_tabc->get_tab_control(tab_from_id);
+ from_tabc->remove_child(moving_tabc);
+ add_child(moving_tabc);
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_child(moving_tabc, hover_now);
+ set_current_tab(hover_now);
+ emit_signal("tab_changed", hover_now);
+ }
+ }
+ }
+ update();
+}
+
+int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const {
+
+ if (get_tab_count() == 0)
+ return -1;
+
+ // must be on tabs in the tab header area.
+ if (p_point.x < tabs_ofs_cache || p_point.y > _get_top_margin())
+ return -1;
+
+ Size2 size = get_size();
+ int right_ofs = 0;
+
+ if (popup) {
+ Ref<Texture> menu = get_icon("menu");
+ right_ofs += menu->get_width();
+ }
+ if (buttons_visible_cache) {
+ Ref<Texture> increment = get_icon("increment");
+ Ref<Texture> decrement = get_icon("decrement");
+ right_ofs += increment->get_width() + decrement->get_width();
+ }
+ if (p_point.x > size.width - right_ofs) {
+ return -1;
+ }
+
+ // get the tab at the point
+ Vector<Control *> tabs = _get_tabs();
+ int px = p_point.x;
+ px -= tabs_ofs_cache;
+ for (int i = first_tab_cache; i <= last_tab_cache; i++) {
+ int tab_width = _get_tab_width(i);
+ if (px < tab_width) {
+ return i;
+ }
+ px -= tab_width;
+ }
+ return -1;
+}
+
void TabContainer::set_tab_align(TabAlign p_align) {
ERR_FAIL_INDEX(p_align, 3);
@@ -500,6 +638,7 @@ void TabContainer::set_tab_align(TabAlign p_align) {
_change_notify("tab_align");
}
+
TabContainer::TabAlign TabContainer::get_tab_align() const {
return align;
@@ -643,6 +782,21 @@ Popup *TabContainer::get_popup() const {
return popup;
}
+void TabContainer::set_drag_to_rearrange_enabled(bool p_enabled) {
+ drag_to_rearrange_enabled = p_enabled;
+}
+
+bool TabContainer::get_drag_to_rearrange_enabled() const {
+ return drag_to_rearrange_enabled;
+}
+void TabContainer::set_tabs_rearrange_group(int p_group_id) {
+ tabs_rearrange_group = p_group_id;
+}
+
+int TabContainer::get_tabs_rearrange_group() const {
+ return tabs_rearrange_group;
+}
+
void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &TabContainer::_gui_input);
@@ -664,6 +818,10 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabContainer::get_tab_disabled);
ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup);
ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup);
+ ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabContainer::set_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabContainer::get_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabContainer::set_tabs_rearrange_group);
+ ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabContainer::get_tabs_rearrange_group);
ClassDB::bind_method(D_METHOD("_child_renamed_callback"), &TabContainer::_child_renamed_callback);
ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed);
@@ -676,6 +834,7 @@ void TabContainer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
BIND_ENUM_CONSTANT(ALIGN_LEFT);
BIND_ENUM_CONSTANT(ALIGN_CENTER);
@@ -694,4 +853,6 @@ TabContainer::TabContainer() {
align = ALIGN_CENTER;
tabs_visible = true;
popup = NULL;
+ drag_to_rearrange_enabled = false;
+ tabs_rearrange_group = -1;
}
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 4bc6e0014..1afe5f754 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -58,6 +58,8 @@ private:
Control *_get_tab(int p_idx) const;
int _get_top_margin() const;
Popup *popup;
+ bool drag_to_rearrange_enabled;
+ int tabs_rearrange_group;
Vector<Control *> _get_tabs() const;
int _get_tab_width(int p_index) const;
@@ -71,6 +73,11 @@ protected:
virtual void add_child_notify(Node *p_child);
virtual void remove_child_notify(Node *p_child);
+ Variant get_drag_data(const Point2 &p_point);
+ bool can_drop_data(const Point2 &p_point, const Variant &p_data) const;
+ void drop_data(const Point2 &p_point, const Variant &p_data);
+ int get_tab_idx_at_point(const Point2 &p_point) const;
+
static void _bind_methods();
public:
@@ -104,6 +111,11 @@ public:
void set_popup(Node *p_popup);
Popup *get_popup() const;
+ void set_drag_to_rearrange_enabled(bool p_enabled);
+ bool get_drag_to_rearrange_enabled() const;
+ void set_tabs_rearrange_group(int p_group_id);
+ int get_tabs_rearrange_group() const;
+
TabContainer();
};
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index dee32aef7..b114264de 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -31,6 +31,9 @@
#include "tabs.h"
#include "message_queue.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/label.h"
+#include "scene/gui/texture_rect.h"
Size2 Tabs::get_minimum_size() const {
@@ -624,20 +627,105 @@ void Tabs::remove_tab(int p_idx) {
Variant Tabs::get_drag_data(const Point2 &p_point) {
- return get_tab_idx_at_point(p_point);
+ if (!drag_to_rearrange_enabled)
+ return Variant();
+
+ int tab_over = get_tab_idx_at_point(p_point);
+
+ if (tab_over < 0)
+ return Variant();
+
+ HBoxContainer *drag_preview = memnew(HBoxContainer);
+
+ if (!tabs[tab_over].icon.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(tabs[tab_over].icon);
+ drag_preview->add_child(tf);
+ }
+ Label *label = memnew(Label(tabs[tab_over].text));
+ drag_preview->add_child(label);
+ if (!tabs[tab_over].right_button.is_null()) {
+ TextureRect *tf = memnew(TextureRect);
+ tf->set_texture(tabs[tab_over].right_button);
+ drag_preview->add_child(tf);
+ }
+ set_drag_preview(drag_preview);
+
+ Dictionary drag_data;
+ drag_data["type"] = "tab_element";
+ drag_data["tab_element"] = tab_over;
+ drag_data["from_path"] = get_path();
+ return drag_data;
}
bool Tabs::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
- return get_tab_idx_at_point(p_point) > -1;
+ if (!drag_to_rearrange_enabled)
+ return false;
+
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return false;
+
+ if (String(d["type"]) == "tab_element") {
+
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ return true;
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between other Tabs
+ Node *from_node = get_node(from_path);
+ Tabs *from_tabs = Object::cast_to<Tabs>(from_node);
+ if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ return true;
+ }
+ }
+ }
+ return false;
}
void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) {
+ if (!drag_to_rearrange_enabled)
+ return;
+
int hover_now = get_tab_idx_at_point(p_point);
- ERR_FAIL_INDEX(hover_now, tabs.size());
- emit_signal("reposition_active_tab_request", hover_now);
+ Dictionary d = p_data;
+ if (!d.has("type"))
+ return;
+
+ if (String(d["type"]) == "tab_element") {
+
+ int tab_from_id = d["tab_element"];
+ NodePath from_path = d["from_path"];
+ NodePath to_path = get_path();
+ if (from_path == to_path) {
+ if (hover_now < 0)
+ hover_now = get_tab_count() - 1;
+ move_tab(tab_from_id, hover_now);
+ emit_signal("reposition_active_tab_request", hover_now);
+ set_current_tab(hover_now);
+ } else if (get_tabs_rearrange_group() != -1) {
+ // drag and drop between Tabs
+ Node *from_node = get_node(from_path);
+ Tabs *from_tabs = Object::cast_to<Tabs>(from_node);
+ if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ if (tab_from_id >= from_tabs->get_tab_count())
+ return;
+ Tab moving_tab = from_tabs->tabs[tab_from_id];
+ if (hover_now < 0)
+ hover_now = get_tab_count();
+ tabs.insert(hover_now, moving_tab);
+ from_tabs->remove_tab(tab_from_id);
+ set_current_tab(hover_now);
+ emit_signal("tab_changed", hover_now);
+ _update_cache();
+ }
+ }
+ }
+ update();
}
int Tabs::get_tab_idx_at_point(const Point2 &p_point) const {
@@ -817,6 +905,21 @@ bool Tabs::get_scrolling_enabled() const {
return scrolling_enabled;
}
+void Tabs::set_drag_to_rearrange_enabled(bool p_enabled) {
+ drag_to_rearrange_enabled = p_enabled;
+}
+
+bool Tabs::get_drag_to_rearrange_enabled() const {
+ return drag_to_rearrange_enabled;
+}
+void Tabs::set_tabs_rearrange_group(int p_group_id) {
+ tabs_rearrange_group = p_group_id;
+}
+
+int Tabs::get_tabs_rearrange_group() const {
+ return tabs_rearrange_group;
+}
+
void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input);
@@ -842,6 +945,10 @@ void Tabs::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &Tabs::get_tab_close_display_policy);
ClassDB::bind_method(D_METHOD("set_scrolling_enabled", "enabled"), &Tabs::set_scrolling_enabled);
ClassDB::bind_method(D_METHOD("get_scrolling_enabled"), &Tabs::get_scrolling_enabled);
+ ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &Tabs::set_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &Tabs::get_drag_to_rearrange_enabled);
+ ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &Tabs::set_tabs_rearrange_group);
+ ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &Tabs::get_tabs_rearrange_group);
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab")));
@@ -854,6 +961,7 @@ void Tabs::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
BIND_ENUM_CONSTANT(ALIGN_LEFT);
BIND_ENUM_CONSTANT(ALIGN_CENTER);
@@ -884,4 +992,6 @@ Tabs::Tabs() {
scrolling_enabled = true;
buttons_visible = false;
hover = -1;
+ drag_to_rearrange_enabled = false;
+ tabs_rearrange_group = -1;
}
diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h
index 246b3cba6..3b38e7f2c 100644
--- a/scene/gui/tabs.h
+++ b/scene/gui/tabs.h
@@ -90,6 +90,8 @@ private:
int hover; // hovered tab
int min_width;
bool scrolling_enabled;
+ bool drag_to_rearrange_enabled;
+ int tabs_rearrange_group;
int get_tab_width(int p_idx) const;
void _ensure_no_over_offset();
@@ -143,6 +145,11 @@ public:
void set_scrolling_enabled(bool p_enabled);
bool get_scrolling_enabled() const;
+ void set_drag_to_rearrange_enabled(bool p_enabled);
+ bool get_drag_to_rearrange_enabled() const;
+ void set_tabs_rearrange_group(int p_group_id);
+ int get_tabs_rearrange_group() const;
+
void ensure_tab_visible(int p_idx);
void set_min_width(int p_width);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 48bd733e8..d7f0c16d7 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -184,6 +184,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
cri.region = j;
text[p_line].region_info[i] = cri;
i += lr - 1;
+
break;
}
@@ -211,6 +212,7 @@ void TextEdit::Text::_update_line_cache(int p_line) const {
cri.region = j;
text[p_line].region_info[i] = cri;
i += lr - 1;
+
break;
}
}
@@ -537,7 +539,7 @@ void TextEdit::_notification(int p_what) {
draw_caret = false;
update();
} break;
- case NOTIFICATION_PHYSICS_PROCESS: {
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (scrolling && v_scroll->get_value() != target_v_scroll) {
double target_y = target_v_scroll - v_scroll->get_value();
double dist = sqrt(target_y * target_y);
@@ -546,17 +548,16 @@ void TextEdit::_notification(int p_what) {
if (Math::abs(vel) >= dist) {
v_scroll->set_value(target_v_scroll);
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
} else {
v_scroll->set_value(v_scroll->get_value() + vel);
}
} else {
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
}
} break;
case NOTIFICATION_DRAW: {
-
if ((!has_focus() && !menu->has_focus()) || !window_has_focus) {
draw_caret = false;
}
@@ -619,44 +620,10 @@ void TextEdit::_notification(int p_what) {
Color color = cache.font_color;
color.a *= readonly_alpha;
- int in_region = -1;
-
if (syntax_coloring) {
-
if (cache.background_color.a > 0.01) {
-
VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2i(), get_size()), cache.background_color);
}
- //compute actual region to start (may be inside say, a comment).
- //slow in very large documents :( but ok for source!
-
- for (int i = 0; i < cursor.line_ofs; i++) {
-
- const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(i);
-
- if (in_region >= 0 && color_regions[in_region].line_only) {
- in_region = -1; //reset regions that end at end of line
- }
-
- for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) {
-
- const Text::ColorRegionInfo &cri = E->get();
-
- if (in_region == -1) {
-
- if (!cri.end) {
-
- in_region = cri.region;
- }
- } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
-
- if (cri.end || color_regions[cri.region].eq) {
-
- in_region = -1;
- }
- }
- }
- }
}
int brace_open_match_line = -1;
@@ -804,7 +771,6 @@ void TextEdit::_notification(int p_what) {
}
}
- int deregion = 0; //force it to clear inrgion
Point2 cursor_pos;
// get the highlighted words
@@ -848,19 +814,12 @@ void TextEdit::_notification(int p_what) {
if (smooth_scroll_enabled)
ofs_y -= ((v_scroll->get_value() - get_line_scroll_pos()) * get_row_height());
- bool prev_is_char = false;
- bool prev_is_number = false;
- bool in_keyword = false;
bool underlined = false;
- bool in_word = false;
- bool in_function_name = false;
- bool in_member_variable = false;
- bool is_hex_notation = false;
- Color keyword_color;
// check if line contains highlighted word
int highlighted_text_col = -1;
int search_text_col = -1;
+ int highlighted_word_col = -1;
if (!search_text.empty())
search_text_col = _get_column_pos_of_word(search_text, str, search_flags, 0);
@@ -868,7 +827,11 @@ void TextEdit::_notification(int p_what) {
if (highlighted_text.length() != 0 && highlighted_text != search_text)
highlighted_text_col = _get_column_pos_of_word(highlighted_text, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
- const Map<int, Text::ColorRegionInfo> &cri_map = text.get_color_region_info(line);
+ if (select_identifiers_enabled && highlighted_word.length() != 0) {
+ if (_is_char(highlighted_word[0])) {
+ highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
+ }
+ }
if (text.is_marked(line)) {
@@ -936,170 +899,28 @@ void TextEdit::_notification(int p_what) {
cache.font->draw(ci, Point2(cache.style_normal->get_margin(MARGIN_LEFT) + cache.breakpoint_gutter_width + ofs_x, ofs_y + cache.font->get_ascent()), fc, cache.line_number_color);
}
- //loop through characters in one line
- for (int j = 0; j < str.length(); j++) {
-
- //look for keyword
- if (deregion > 0) {
- deregion--;
- if (deregion == 0)
- in_region = -1;
- }
- if (syntax_coloring && deregion == 0) {
-
- color = cache.font_color; //reset
- color.a *= readonly_alpha;
- //find keyword
- bool is_char = _is_text_char(str[j]);
- bool is_symbol = _is_symbol(str[j]);
- bool is_number = _is_number(str[j]);
-
- if (j == 0 && in_region >= 0 && color_regions[in_region].line_only) {
- in_region = -1; //reset regions that end at end of line
- }
-
- // allow ABCDEF in hex notation
- if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
- is_number = true;
- } else {
- is_hex_notation = false;
- }
-
- // check for dot or underscore or 'x' for hex notation in floating point number
- if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) {
- is_number = true;
- is_symbol = false;
- is_char = false;
-
- if (str[j] == 'x' && str[j - 1] == '0') {
- is_hex_notation = true;
- }
- }
-
- if (!in_word && _is_char(str[j]) && !is_number) {
- in_word = true;
- }
-
- if ((in_keyword || in_word) && !is_hex_notation) {
- is_number = false;
- }
-
- if (is_symbol && str[j] != '.' && in_word) {
- in_word = false;
- }
-
- if (is_symbol && cri_map.has(j)) {
-
- const Text::ColorRegionInfo &cri = cri_map[j];
-
- if (in_region == -1) {
-
- if (!cri.end) {
-
- in_region = cri.region;
- }
- } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
-
- if (cri.end || color_regions[cri.region].eq) {
-
- deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length();
- }
- }
- }
-
- if (!is_char) {
- in_keyword = false;
- underlined = false;
- }
-
- if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
-
- int to = j;
- while (to < str.length() && _is_text_char(str[to]))
- to++;
-
- uint32_t hash = String::hash(&str[j], to - j);
- StrRange range(&str[j], to - j);
-
- const Color *col = keywords.custom_getptr(range, hash);
-
- if (!col) {
- col = member_keywords.custom_getptr(range, hash);
-
- if (col) {
- for (int k = j - 1; k >= 0; k--) {
- if (str[k] == '.') {
- col = NULL; //member indexing not allowed
- break;
- } else if (str[k] > 32) {
- break;
- }
- }
- }
- }
-
- if (col) {
-
- in_keyword = true;
- keyword_color = *col;
- }
-
- if (select_identifiers_enabled && highlighted_word != String()) {
- if (highlighted_word == range) {
- underlined = true;
- }
- }
- }
-
- if (!in_function_name && in_word && !in_keyword) {
-
- int k = j;
- while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
- k++;
- }
-
- // check for space between name and bracket
- while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
- k++;
- }
-
- if (str[k] == '(') {
- in_function_name = true;
- }
- }
+ //loop through characters in one line
+ Map<int, HighlighterInfo> color_map;
+ if (syntax_coloring) {
+ color_map = _get_line_syntax_highlighting(line);
+ }
- if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
- int k = j;
- while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
- k--;
- }
+ // ensure we at least use the font color
+ Color current_color = cache.font_color;
+ if (readonly) {
+ current_color.a *= readonly_alpha;
+ }
+ for (int j = 0; j < str.length(); j++) {
- if (str[k] == '.') {
- in_member_variable = true;
+ if (syntax_coloring) {
+ if (color_map.has(j)) {
+ current_color = color_map[j].color;
+ if (readonly) {
+ current_color.a *= readonly_alpha;
}
}
-
- if (is_symbol) {
- in_function_name = false;
- in_member_variable = false;
- }
-
- if (in_region >= 0)
- color = color_regions[in_region].color;
- else if (in_keyword)
- color = keyword_color;
- else if (in_member_variable)
- color = cache.member_variable_color;
- else if (in_function_name)
- color = cache.function_color;
- else if (is_symbol)
- color = cache.symbol_color;
- else if (is_number)
- color = cache.number_color;
-
- prev_is_char = is_char;
- prev_is_number = is_number;
+ color = current_color;
}
int char_w;
@@ -1206,6 +1027,13 @@ void TextEdit::_notification(int p_what) {
}
}
+ if (highlighted_word_col != -1) {
+ if (j > highlighted_word_col + highlighted_word.length()) {
+ highlighted_word_col = _get_column_pos_of_word(highlighted_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, j);
+ }
+ underlined = (j >= highlighted_word_col && j < highlighted_word_col + highlighted_word.length());
+ }
+
if (brace_matching_enabled) {
if ((brace_open_match_line == line && brace_open_match_column == j) ||
(cursor.column == j && cursor.line == line && (brace_open_matching || brace_open_mismatch))) {
@@ -1512,6 +1340,7 @@ void TextEdit::_notification(int p_what) {
OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos + Point2(0, get_row_height()));
OS::get_singleton()->set_ime_intermediate_text_callback(_ime_text_callback, this);
}
+
} break;
case NOTIFICATION_FOCUS_ENTER: {
@@ -1528,7 +1357,6 @@ void TextEdit::_notification(int p_what) {
if (raised_from_completion) {
VisualServer::get_singleton()->canvas_item_set_z_index(get_canvas_item(), 1);
}
-
} break;
case NOTIFICATION_FOCUS_EXIT: {
@@ -2582,6 +2410,12 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
cursor_set_line(line);
cursor_set_column(column);
+#ifdef APPLE_STYLE_KEYS
+ } else if (k->get_command()) {
+ int cursor_current_column = cursor.column;
+ cursor.column = 0;
+ _remove_text(cursor.line, 0, cursor.line, cursor_current_column);
+#endif
} else {
if (cursor.line > 0 && is_line_hidden(cursor.line - 1))
unfold_line(cursor.line - 1);
@@ -2856,7 +2690,11 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
next_line = line;
next_column = column;
-
+#ifdef APPLE_STYLE_KEYS
+ } else if (k->get_command()) {
+ next_column = curline_len;
+ next_line = cursor.line;
+#endif
} else {
next_column = cursor.column < curline_len ? (cursor.column + 1) : 0;
}
@@ -3005,13 +2843,64 @@ void TextEdit::_gui_input(const Ref<InputEvent> &p_gui_input) {
} break;
case KEY_A: {
+#ifndef APPLE_STYLE_KEYS
if (!k->get_command() || k->get_shift() || k->get_alt()) {
scancode_handled = false;
break;
}
-
select_all();
+#else
+ if (k->get_alt()) {
+ scancode_handled = false;
+ break;
+ }
+ if (!k->get_shift() && k->get_command())
+ select_all();
+ else if (k->get_control()) {
+ if (k->get_shift())
+ _pre_shift_selection();
+
+ int current_line_whitespace_len = 0;
+ while (current_line_whitespace_len < text[cursor.line].length()) {
+ CharType c = text[cursor.line][current_line_whitespace_len];
+ if (c != '\t' && c != ' ')
+ break;
+ current_line_whitespace_len++;
+ }
+ if (cursor_get_column() == current_line_whitespace_len)
+ cursor_set_column(0);
+ else
+ cursor_set_column(current_line_whitespace_len);
+
+ if (k->get_shift())
+ _post_shift_selection();
+ else if (k->get_command() || k->get_control())
+ deselect();
+ }
+ } break;
+ case KEY_E: {
+
+ if (!k->get_control() || k->get_command() || k->get_alt()) {
+ scancode_handled = false;
+ break;
+ }
+
+ if (k->get_shift())
+ _pre_shift_selection();
+
+ if (k->get_command())
+ cursor_set_line(text.size() - 1, true, false);
+ cursor_set_column(text[cursor.line].length());
+
+ if (k->get_shift())
+ _post_shift_selection();
+ else if (k->get_command() || k->get_control())
+ deselect();
+
+ _cancel_completion();
+ completion_hint = "";
+#endif
} break;
case KEY_X: {
if (readonly) {
@@ -3199,7 +3088,7 @@ void TextEdit::_scroll_up(real_t p_delta) {
v_scroll->set_value(target_v_scroll);
} else {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
}
} else {
v_scroll->set_value(target_v_scroll);
@@ -3232,7 +3121,7 @@ void TextEdit::_scroll_down(real_t p_delta) {
v_scroll->set_value(target_v_scroll);
} else {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
}
} else {
v_scroll->set_value(target_v_scroll);
@@ -3356,6 +3245,7 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
text_changed_dirty = true;
}
+ _line_edited_from(p_line);
}
String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) const {
@@ -3406,6 +3296,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
MessageQueue::get_singleton()->push_call(this, "_text_changed_emit");
text_changed_dirty = true;
}
+ _line_edited_from(p_from_line);
}
void TextEdit::_insert_text(int p_line, int p_char, const String &p_text, int *r_end_line, int *r_end_char) {
@@ -3528,6 +3419,13 @@ void TextEdit::_insert_text_at_cursor(const String &p_text) {
update();
}
+void TextEdit::_line_edited_from(int p_line) {
+ int cache_size = color_region_cache.size();
+ for (int i = p_line; i < cache_size; i++) {
+ color_region_cache.erase(i);
+ }
+}
+
int TextEdit::get_char_count() {
int totalsize = 0;
@@ -4150,12 +4048,88 @@ void TextEdit::_update_caches() {
cache.can_fold_icon = get_icon("GuiTreeArrowDown", "EditorIcons");
cache.folded_eol_icon = get_icon("GuiEllipsis", "EditorIcons");
text.set_font(cache.font);
+
+ if (syntax_highlighter) {
+ syntax_highlighter->_update_cache();
+ }
+}
+
+SyntaxHighlighter *TextEdit::_get_syntax_highlighting() {
+ return syntax_highlighter;
+}
+
+void TextEdit::_set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter) {
+ syntax_highlighter = p_syntax_highlighter;
+ if (syntax_highlighter) {
+ syntax_highlighter->set_text_editor(this);
+ syntax_highlighter->_update_cache();
+ }
+ update();
+}
+
+int TextEdit::_is_line_in_region(int p_line) {
+
+ // do we have in cache?
+ if (color_region_cache.has(p_line)) {
+ return color_region_cache[p_line];
+ }
+
+ // if not find the closest line we have
+ int previous_line = p_line - 1;
+ for (previous_line; previous_line > -1; previous_line--) {
+ if (color_region_cache.has(p_line)) {
+ break;
+ }
+ }
+
+ // calculate up to line we need and update the cache along the way.
+ int in_region = color_region_cache[previous_line];
+ if (previous_line == -1) {
+ in_region = -1;
+ }
+ for (int i = previous_line; i < p_line; i++) {
+ const Map<int, Text::ColorRegionInfo> &cri_map = _get_line_color_region_info(i);
+ for (const Map<int, Text::ColorRegionInfo>::Element *E = cri_map.front(); E; E = E->next()) {
+ const Text::ColorRegionInfo &cri = E->get();
+ if (in_region == -1) {
+ if (!cri.end) {
+ in_region = cri.region;
+ }
+ } else if (in_region == cri.region && !_get_color_region(cri.region).line_only) {
+ if (cri.end || _get_color_region(cri.region).eq) {
+ in_region = -1;
+ }
+ }
+ }
+
+ if (in_region >= 0 && _get_color_region(in_region).line_only) {
+ in_region = -1;
+ }
+
+ color_region_cache[i + 1] = in_region;
+ }
+ return in_region;
+}
+
+TextEdit::ColorRegion TextEdit::_get_color_region(int p_region) const {
+ if (p_region < 0 || p_region >= color_regions.size()) {
+ return ColorRegion();
+ }
+ return color_regions[p_region];
+}
+
+Map<int, TextEdit::Text::ColorRegionInfo> TextEdit::_get_line_color_region_info(int p_line) const {
+ if (p_line < 0 || p_line > text.size() - 1) {
+ return Map<int, Text::ColorRegionInfo>();
+ }
+ return text.get_color_region_info(p_line);
}
void TextEdit::clear_colors() {
keywords.clear();
color_regions.clear();
+ color_region_cache.clear();
text.clear_caches();
}
@@ -4165,6 +4139,14 @@ void TextEdit::add_keyword_color(const String &p_keyword, const Color &p_color)
update();
}
+bool TextEdit::has_keyword_color(String p_keyword) const {
+ return keywords.has(p_keyword);
+}
+
+Color TextEdit::get_keyword_color(String p_keyword) const {
+ return keywords[p_keyword];
+}
+
void TextEdit::add_color_region(const String &p_begin_key, const String &p_end_key, const Color &p_color, bool p_line_only) {
color_regions.push_back(ColorRegion(p_begin_key, p_end_key, p_color, p_line_only));
@@ -4177,6 +4159,14 @@ void TextEdit::add_member_keyword(const String &p_keyword, const Color &p_color)
update();
}
+bool TextEdit::has_member_color(String p_member) const {
+ return member_keywords.has(p_member);
+}
+
+Color TextEdit::get_member_color(String p_member) const {
+ return member_keywords[p_member];
+}
+
void TextEdit::clear_member_keywords() {
member_keywords.clear();
update();
@@ -5049,6 +5039,11 @@ void TextEdit::set_indent_size(const int p_size) {
update();
}
+int TextEdit::get_indent_size() {
+
+ return indent_size;
+}
+
void TextEdit::set_draw_tabs(bool p_draw) {
draw_tabs = p_draw;
@@ -5691,6 +5686,8 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_v_scroll_speed"), &TextEdit::get_v_scroll_speed);
ClassDB::bind_method(D_METHOD("add_keyword_color", "keyword", "color"), &TextEdit::add_keyword_color);
+ ClassDB::bind_method(D_METHOD("has_keyword_color", "keyword"), &TextEdit::has_keyword_color);
+ ClassDB::bind_method(D_METHOD("get_keyword_color", "keyword"), &TextEdit::get_keyword_color);
ClassDB::bind_method(D_METHOD("add_color_region", "begin_key", "end_key", "color", "line_only"), &TextEdit::add_color_region, DEFVAL(false));
ClassDB::bind_method(D_METHOD("clear_colors"), &TextEdit::clear_colors);
ClassDB::bind_method(D_METHOD("menu_option", "option"), &TextEdit::menu_option);
@@ -5713,7 +5710,7 @@ void TextEdit::_bind_methods() {
ADD_GROUP("Caret", "caret_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_block_mode"), "cursor_set_block_mode", "cursor_is_block_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "cursor_set_blink_enabled", "cursor_get_blink_enabled");
- ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.1"), "cursor_set_blink_speed", "cursor_get_blink_speed");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "caret_blink_speed", PROPERTY_HINT_RANGE, "0.1,10,0.01"), "cursor_set_blink_speed", "cursor_get_blink_speed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_moving_by_right_click"), "set_right_click_moves_caret", "is_right_click_moving_caret");
ADD_SIGNAL(MethodInfo("cursor_changed"));
@@ -5744,6 +5741,7 @@ TextEdit::TextEdit() {
clear();
wrap = false;
set_focus_mode(FOCUS_ALL);
+ syntax_highlighter = NULL;
_update_caches();
cache.size = Size2(1, 1);
cache.row_height = 1;
@@ -5757,7 +5755,7 @@ TextEdit::TextEdit() {
indent_size = 4;
text.set_indent_size(indent_size);
text.clear();
- //text.insert(1,"Mongolia..");
+ //text.insert(1,"Mongolia...");
//text.insert(2,"PAIS GENEROSO!!");
text.set_color_regions(&color_regions);
@@ -5847,16 +5845,213 @@ TextEdit::TextEdit() {
context_menu_enabled = true;
menu = memnew(PopupMenu);
add_child(menu);
- menu->add_item(TTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X);
- menu->add_item(TTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C);
- menu->add_item(TTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V);
+ menu->add_item(RTR("Cut"), MENU_CUT, KEY_MASK_CMD | KEY_X);
+ menu->add_item(RTR("Copy"), MENU_COPY, KEY_MASK_CMD | KEY_C);
+ menu->add_item(RTR("Paste"), MENU_PASTE, KEY_MASK_CMD | KEY_V);
menu->add_separator();
- menu->add_item(TTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A);
- menu->add_item(TTR("Clear"), MENU_CLEAR);
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL, KEY_MASK_CMD | KEY_A);
+ menu->add_item(RTR("Clear"), MENU_CLEAR);
menu->add_separator();
- menu->add_item(TTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
+ menu->add_item(RTR("Undo"), MENU_UNDO, KEY_MASK_CMD | KEY_Z);
menu->connect("id_pressed", this, "menu_option");
}
TextEdit::~TextEdit() {
}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Map<int, TextEdit::HighlighterInfo> TextEdit::_get_line_syntax_highlighting(int p_line) {
+ if (syntax_highlighter != NULL) {
+ return syntax_highlighter->_get_line_syntax_highlighting(p_line);
+ }
+
+ Map<int, HighlighterInfo> color_map;
+
+ bool prev_is_char = false;
+ bool prev_is_number = false;
+ bool in_keyword = false;
+ bool in_word = false;
+ bool in_function_name = false;
+ bool in_member_variable = false;
+ bool is_hex_notation = false;
+ Color keyword_color;
+ Color color;
+
+ int in_region = _is_line_in_region(p_line);
+ int deregion = 0;
+
+ const Map<int, TextEdit::Text::ColorRegionInfo> cri_map = text.get_color_region_info(p_line);
+ const String &str = text[p_line];
+ Color prev_color;
+ for (int j = 0; j < str.length(); j++) {
+ HighlighterInfo highlighter_info;
+
+ if (deregion > 0) {
+ deregion--;
+ if (deregion == 0) {
+ in_region = -1;
+ }
+ }
+
+ if (deregion != 0) {
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ continue;
+ }
+
+ color = cache.font_color;
+
+ bool is_char = _is_text_char(str[j]);
+ bool is_symbol = _is_symbol(str[j]);
+ bool is_number = _is_number(str[j]);
+
+ // allow ABCDEF in hex notation
+ if (is_hex_notation && (_is_hex_symbol(str[j]) || is_number)) {
+ is_number = true;
+ } else {
+ is_hex_notation = false;
+ }
+
+ // check for dot or underscore or 'x' for hex notation in floating point number
+ if ((str[j] == '.' || str[j] == 'x' || str[j] == '_') && !in_word && prev_is_number && !is_number) {
+ is_number = true;
+ is_symbol = false;
+ is_char = false;
+
+ if (str[j] == 'x' && str[j - 1] == '0') {
+ is_hex_notation = true;
+ }
+ }
+
+ if (!in_word && _is_char(str[j]) && !is_number) {
+ in_word = true;
+ }
+
+ if ((in_keyword || in_word) && !is_hex_notation) {
+ is_number = false;
+ }
+
+ if (is_symbol && str[j] != '.' && in_word) {
+ in_word = false;
+ }
+
+ if (is_symbol && cri_map.has(j)) {
+ const TextEdit::Text::ColorRegionInfo &cri = cri_map[j];
+
+ if (in_region == -1) {
+ if (!cri.end) {
+ in_region = cri.region;
+ }
+ } else if (in_region == cri.region && !color_regions[cri.region].line_only) { //ignore otherwise
+ if (cri.end || color_regions[cri.region].eq) {
+ deregion = color_regions[cri.region].eq ? color_regions[cri.region].begin_key.length() : color_regions[cri.region].end_key.length();
+ }
+ }
+ }
+
+ if (!is_char) {
+ in_keyword = false;
+ }
+
+ if (in_region == -1 && !in_keyword && is_char && !prev_is_char) {
+
+ int to = j;
+ while (to < str.length() && _is_text_char(str[to]))
+ to++;
+
+ uint32_t hash = String::hash(&str[j], to - j);
+ StrRange range(&str[j], to - j);
+
+ const Color *col = keywords.custom_getptr(range, hash);
+
+ if (!col) {
+ col = member_keywords.custom_getptr(range, hash);
+
+ if (col) {
+ for (int k = j - 1; k >= 0; k--) {
+ if (str[k] == '.') {
+ col = NULL; //member indexing not allowed
+ break;
+ } else if (str[k] > 32) {
+ break;
+ }
+ }
+ }
+ }
+
+ if (col) {
+ in_keyword = true;
+ keyword_color = *col;
+ }
+ }
+
+ if (!in_function_name && in_word && !in_keyword) {
+
+ int k = j;
+ while (k < str.length() && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k++;
+ }
+
+ // check for space between name and bracket
+ while (k < str.length() && (str[k] == '\t' || str[k] == ' ')) {
+ k++;
+ }
+
+ if (str[k] == '(') {
+ in_function_name = true;
+ }
+ }
+
+ if (!in_function_name && !in_member_variable && !in_keyword && !is_number && in_word) {
+ int k = j;
+ while (k > 0 && !_is_symbol(str[k]) && str[k] != '\t' && str[k] != ' ') {
+ k--;
+ }
+
+ if (str[k] == '.') {
+ in_member_variable = true;
+ }
+ }
+
+ if (is_symbol) {
+ in_function_name = false;
+ in_member_variable = false;
+ }
+
+ if (in_region >= 0)
+ color = color_regions[in_region].color;
+ else if (in_keyword)
+ color = keyword_color;
+ else if (in_member_variable)
+ color = cache.member_variable_color;
+ else if (in_function_name)
+ color = cache.function_color;
+ else if (is_symbol)
+ color = cache.symbol_color;
+ else if (is_number)
+ color = cache.number_color;
+
+ prev_is_char = is_char;
+ prev_is_number = is_number;
+
+ if (color != prev_color) {
+ prev_color = color;
+ highlighter_info.color = color;
+ color_map[j] = highlighter_info;
+ }
+ }
+
+ return color_map;
+}
+
+void SyntaxHighlighter::set_text_editor(TextEdit *p_text_editor) {
+ text_editor = p_text_editor;
+}
+
+TextEdit *SyntaxHighlighter::get_text_editor() {
+ return text_editor;
+}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 8ac3b9fce..60c6ab492 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -36,10 +36,82 @@
#include "scene/gui/scroll_bar.h"
#include "scene/main/timer.h"
+class SyntaxHighlighter;
+
class TextEdit : public Control {
- GDCLASS(TextEdit, Control);
+ GDCLASS(TextEdit, Control)
+
+public:
+ struct HighlighterInfo {
+ Color color;
+ };
+
+ struct ColorRegion {
+
+ Color color;
+ String begin_key;
+ String end_key;
+ bool line_only;
+ bool eq;
+ ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) {
+ begin_key = p_begin_key;
+ end_key = p_end_key;
+ color = p_color;
+ line_only = p_line_only || p_end_key == "";
+ eq = begin_key == end_key;
+ }
+ };
+
+ class Text {
+ public:
+ struct ColorRegionInfo {
+
+ int region;
+ bool end;
+ };
+
+ struct Line {
+ int width_cache : 24;
+ bool marked : 1;
+ bool breakpoint : 1;
+ bool hidden : 1;
+ Map<int, ColorRegionInfo> region_info;
+ String data;
+ };
+
+ private:
+ const Vector<ColorRegion> *color_regions;
+ mutable Vector<Line> text;
+ Ref<Font> font;
+ int indent_size;
+
+ void _update_line_cache(int p_line) const;
+
+ public:
+ void set_indent_size(int p_indent_size);
+ void set_font(const Ref<Font> &p_font);
+ void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; }
+ int get_line_width(int p_line) const;
+ int get_max_width(bool p_exclude_hidden = false) const;
+ const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const;
+ void set(int p_line, const String &p_text);
+ void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; }
+ bool is_marked(int p_line) const { return text[p_line].marked; }
+ void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; }
+ bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
+ void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; }
+ bool is_hidden(int p_line) const { return text[p_line].hidden; }
+ void insert(int p_at, const String &p_text);
+ void remove(int p_at);
+ int size() const { return text.size(); }
+ void clear();
+ void clear_caches();
+ _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
+ Text() { indent_size = 4; }
+ };
+private:
struct Cursor {
int last_fit_x;
int line, column; ///< cursor
@@ -115,69 +187,7 @@ class TextEdit : public Control {
Size2 size;
} cache;
- struct ColorRegion {
-
- Color color;
- String begin_key;
- String end_key;
- bool line_only;
- bool eq;
- ColorRegion(const String &p_begin_key = "", const String &p_end_key = "", const Color &p_color = Color(), bool p_line_only = false) {
- begin_key = p_begin_key;
- end_key = p_end_key;
- color = p_color;
- line_only = p_line_only || p_end_key == "";
- eq = begin_key == end_key;
- }
- };
-
- class Text {
- public:
- struct ColorRegionInfo {
-
- int region;
- bool end;
- };
-
- struct Line {
- int width_cache : 24;
- bool marked : 1;
- bool breakpoint : 1;
- bool hidden : 1;
- Map<int, ColorRegionInfo> region_info;
- String data;
- };
-
- private:
- const Vector<ColorRegion> *color_regions;
- mutable Vector<Line> text;
- Ref<Font> font;
- int indent_size;
-
- void _update_line_cache(int p_line) const;
-
- public:
- void set_indent_size(int p_indent_size);
- void set_font(const Ref<Font> &p_font);
- void set_color_regions(const Vector<ColorRegion> *p_regions) { color_regions = p_regions; }
- int get_line_width(int p_line) const;
- int get_max_width(bool p_exclude_hidden = false) const;
- const Map<int, ColorRegionInfo> &get_color_region_info(int p_line) const;
- void set(int p_line, const String &p_text);
- void set_marked(int p_line, bool p_marked) { text[p_line].marked = p_marked; }
- bool is_marked(int p_line) const { return text[p_line].marked; }
- void set_breakpoint(int p_line, bool p_breakpoint) { text[p_line].breakpoint = p_breakpoint; }
- bool is_breakpoint(int p_line) const { return text[p_line].breakpoint; }
- void set_hidden(int p_line, bool p_hidden) { text[p_line].hidden = p_hidden; }
- bool is_hidden(int p_line) const { return text[p_line].hidden; }
- void insert(int p_at, const String &p_text);
- void remove(int p_at);
- int size() const { return text.size(); }
- void clear();
- void clear_caches();
- _FORCE_INLINE_ const String &operator[](int p_line) const { return text[p_line].data; }
- Text() { indent_size = 4; }
- };
+ Map<int, int> color_region_cache;
struct TextOperation {
@@ -209,9 +219,12 @@ class TextEdit : public Control {
void _do_text_op(const TextOperation &p_op, bool p_reverse);
//syntax coloring
+ SyntaxHighlighter *syntax_highlighter;
HashMap<String, Color> keywords;
HashMap<String, Color> member_keywords;
+ Map<int, HighlighterInfo> _get_line_syntax_highlighting(int p_line);
+
Vector<ColorRegion> color_regions;
Set<String> completion_prefixes;
@@ -355,6 +368,7 @@ class TextEdit : public Control {
void _update_caches();
void _cursor_changed_emit();
void _text_changed_emit();
+ void _line_edited_from(int p_line);
void _push_current_op();
@@ -391,6 +405,13 @@ protected:
static void _bind_methods();
public:
+ SyntaxHighlighter *_get_syntax_highlighting();
+ void _set_syntax_highlighting(SyntaxHighlighter *p_syntax_highlighter);
+
+ int _is_line_in_region(int p_line);
+ ColorRegion _get_color_region(int p_region) const;
+ Map<int, Text::ColorRegionInfo> _get_line_color_region_info(int p_line) const;
+
enum MenuItems {
MENU_CUT,
MENU_COPY,
@@ -536,6 +557,7 @@ public:
void set_indent_using_spaces(const bool p_use_spaces);
bool is_indent_using_spaces() const;
void set_indent_size(const int p_size);
+ int get_indent_size();
void set_draw_tabs(bool p_draw);
bool is_drawing_tabs() const;
void set_override_selected_font_color(bool p_override_selected_font_color);
@@ -545,10 +567,15 @@ public:
bool is_insert_mode() const;
void add_keyword_color(const String &p_keyword, const Color &p_color);
+ bool has_keyword_color(String p_keyword) const;
+ Color get_keyword_color(String p_keyword) const;
+
void add_color_region(const String &p_begin_key = String(), const String &p_end_key = String(), const Color &p_color = Color(), bool p_line_only = false);
void clear_colors();
void add_member_keyword(const String &p_keyword, const Color &p_color);
+ bool has_member_color(String p_member) const;
+ Color get_member_color(String p_member) const;
void clear_member_keywords();
int get_v_scroll() const;
@@ -621,4 +648,19 @@ public:
VARIANT_ENUM_CAST(TextEdit::MenuItems);
VARIANT_ENUM_CAST(TextEdit::SearchFlags);
+class SyntaxHighlighter {
+protected:
+ TextEdit *text_editor;
+
+public:
+ virtual void _update_cache() = 0;
+ virtual Map<int, TextEdit::HighlighterInfo> _get_line_syntax_highlighting(int p_line) = 0;
+
+ virtual String get_name() = 0;
+ virtual List<String> get_supported_languages() = 0;
+
+ void set_text_editor(TextEdit *p_text_editor);
+ TextEdit *get_text_editor();
+};
+
#endif // TEXT_EDIT_H
diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp
index 4b3ba6df3..82d983184 100644
--- a/scene/gui/texture_progress.cpp
+++ b/scene/gui/texture_progress.cpp
@@ -106,6 +106,33 @@ Ref<Texture> TextureProgress::get_progress_texture() const {
return progress;
}
+void TextureProgress::set_tint_under(const Color &p_tint) {
+ tint_under = p_tint;
+ update();
+}
+
+Color TextureProgress::get_tint_under() const {
+ return tint_under;
+}
+
+void TextureProgress::set_tint_progress(const Color &p_tint) {
+ tint_progress = p_tint;
+ update();
+}
+
+Color TextureProgress::get_tint_progress() const {
+ return tint_progress;
+}
+
+void TextureProgress::set_tint_over(const Color &p_tint) {
+ tint_over = p_tint;
+ update();
+}
+
+Color TextureProgress::get_tint_over() const {
+ return tint_over;
+}
+
Point2 TextureProgress::unit_val_to_uv(float val) {
if (progress.is_null())
return Point2();
@@ -170,7 +197,7 @@ Point2 TextureProgress::get_relative_center() {
return p;
}
-void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio) {
+void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate) {
Vector2 texture_size = p_texture->get_size();
Vector2 topleft = Vector2(stretch_margin[MARGIN_LEFT], stretch_margin[MARGIN_TOP]);
Vector2 bottomright = Vector2(stretch_margin[MARGIN_RIGHT], stretch_margin[MARGIN_BOTTOM]);
@@ -240,7 +267,7 @@ void TextureProgress::draw_nine_patch_stretched(const Ref<Texture> &p_texture, F
}
RID ci = get_canvas_item();
- VS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright);
+ VS::get_singleton()->canvas_item_add_nine_patch(ci, dst_rect, src_rect, p_texture->get_rid(), topleft, bottomright, VS::NINE_PATCH_STRETCH, VS::NINE_PATCH_STRETCH, true, p_modulate);
}
void TextureProgress::_notification(int p_what) {
@@ -251,42 +278,42 @@ void TextureProgress::_notification(int p_what) {
if (nine_patch_stretch && (mode == FILL_LEFT_TO_RIGHT || mode == FILL_RIGHT_TO_LEFT || mode == FILL_TOP_TO_BOTTOM || mode == FILL_BOTTOM_TO_TOP)) {
if (under.is_valid()) {
- draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0);
+ draw_nine_patch_stretched(under, FILL_LEFT_TO_RIGHT, 1.0, tint_under);
}
if (progress.is_valid()) {
- draw_nine_patch_stretched(progress, mode, get_as_ratio());
+ draw_nine_patch_stretched(progress, mode, get_as_ratio(), tint_progress);
}
if (over.is_valid()) {
- draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0);
+ draw_nine_patch_stretched(over, FILL_LEFT_TO_RIGHT, 1.0, tint_over);
}
} else {
if (under.is_valid())
- draw_texture(under, Point2());
+ draw_texture(under, Point2(), tint_under);
if (progress.is_valid()) {
Size2 s = progress->get_size();
switch (mode) {
case FILL_LEFT_TO_RIGHT: {
Rect2 region = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_RIGHT_TO_LEFT: {
Rect2 region = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_TOP_TO_BOTTOM: {
Rect2 region = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio()));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_BOTTOM_TO_TOP: {
Rect2 region = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio()));
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} break;
case FILL_CLOCKWISE:
case FILL_COUNTER_CLOCKWISE: {
float val = get_as_ratio() * rad_max_degrees / 360;
if (val == 1) {
Rect2 region = Rect2(Point2(), s);
- draw_texture_rect_region(progress, region, region);
+ draw_texture_rect_region(progress, region, region, tint_progress);
} else if (val != 0) {
Array pts;
float direction = mode == FILL_CLOCKWISE ? 1 : -1;
@@ -311,7 +338,9 @@ void TextureProgress::_notification(int p_what) {
uvs.push_back(uv);
points.push_back(Point2(uv.x * s.x, uv.y * s.y));
}
- draw_polygon(points, Vector<Color>(), uvs, progress);
+ Vector<Color> colors;
+ colors.push_back(tint_progress);
+ draw_polygon(points, colors, uvs, progress);
}
if (Engine::get_singleton()->is_editor_hint()) {
Point2 p = progress->get_size();
@@ -323,11 +352,11 @@ void TextureProgress::_notification(int p_what) {
}
} break;
default:
- draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)));
+ draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress);
}
}
if (over.is_valid())
- draw_texture(over, Point2());
+ draw_texture(over, Point2(), tint_over);
}
} break;
@@ -389,6 +418,15 @@ void TextureProgress::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &TextureProgress::set_fill_mode);
ClassDB::bind_method(D_METHOD("get_fill_mode"), &TextureProgress::get_fill_mode);
+ ClassDB::bind_method(D_METHOD("set_tint_under", "tint"), &TextureProgress::set_tint_under);
+ ClassDB::bind_method(D_METHOD("get_tint_under"), &TextureProgress::get_tint_under);
+
+ ClassDB::bind_method(D_METHOD("set_tint_progress", "tint"), &TextureProgress::set_tint_progress);
+ ClassDB::bind_method(D_METHOD("get_tint_progress"), &TextureProgress::get_tint_progress);
+
+ ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgress::set_tint_over);
+ ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgress::get_tint_over);
+
ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgress::set_radial_initial_angle);
ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgress::get_radial_initial_angle);
@@ -409,6 +447,10 @@ void TextureProgress::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_over_texture", "get_over_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_progress_texture", "get_progress_texture");
ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise"), "set_fill_mode", "get_fill_mode");
+ ADD_GROUP("Tint", "tint_");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_under", "get_tint_under");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_over", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_over", "get_tint_over");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_progress", PROPERTY_HINT_COLOR_NO_ALPHA), "set_tint_progress", "get_tint_progress");
ADD_GROUP("Radial Fill", "radial_");
ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_initial_angle", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_radial_initial_angle", "get_radial_initial_angle");
ADD_PROPERTYNZ(PropertyInfo(Variant::REAL, "radial_fill_degrees", PROPERTY_HINT_RANGE, "0.0,360.0,0.1,slider"), "set_fill_degrees", "get_fill_degrees");
@@ -440,4 +482,6 @@ TextureProgress::TextureProgress() {
stretch_margin[MARGIN_RIGHT] = 0;
stretch_margin[MARGIN_BOTTOM] = 0;
stretch_margin[MARGIN_TOP] = 0;
+
+ tint_under = tint_progress = tint_over = Color(1, 1, 1);
}
diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress.h
index 77c3980e2..34158b5db 100644
--- a/scene/gui/texture_progress.h
+++ b/scene/gui/texture_progress.h
@@ -82,6 +82,15 @@ public:
void set_nine_patch_stretch(bool p_stretch);
bool get_nine_patch_stretch() const;
+ void set_tint_under(const Color &p_tint);
+ Color get_tint_under() const;
+
+ void set_tint_progress(const Color &p_tint);
+ Color get_tint_progress() const;
+
+ void set_tint_over(const Color &p_tint);
+ Color get_tint_over() const;
+
Size2 get_minimum_size() const;
TextureProgress();
@@ -93,10 +102,11 @@ private:
Point2 rad_center_off;
bool nine_patch_stretch;
int stretch_margin[4];
+ Color tint_under, tint_progress, tint_over;
Point2 unit_val_to_uv(float val);
Point2 get_relative_center();
- void draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio);
+ void draw_nine_patch_stretched(const Ref<Texture> &p_texture, FillMode p_mode, double p_ratio, const Color &p_modulate);
};
VARIANT_ENUM_CAST(TextureProgress::FillMode);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index e7f63997f..1d2761276 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -2545,7 +2545,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
if (drag_speed == 0) {
drag_touching_deaccel = false;
drag_touching = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
} else {
drag_touching_deaccel = true;
@@ -2611,7 +2611,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
break;
if (drag_touching) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching_deaccel = false;
drag_touching = false;
drag_speed = 0;
@@ -2626,7 +2626,7 @@ void Tree::_gui_input(Ref<InputEvent> p_event) {
drag_touching = OS::get_singleton()->has_touchscreen_ui_hint();
drag_touching_deaccel = false;
if (drag_touching) {
- set_physics_process(true);
+ set_physics_process_internal(true);
}
if (b->get_button_index() == BUTTON_LEFT) {
@@ -2829,7 +2829,7 @@ void Tree::_notification(int p_what) {
drop_mode_flags = 0;
scrolling = false;
- set_physics_process(false);
+ set_physics_process_internal(false);
update();
}
if (p_what == NOTIFICATION_DRAG_BEGIN) {
@@ -2837,10 +2837,10 @@ void Tree::_notification(int p_what) {
single_select_defer = NULL;
if (cache.scroll_speed > 0 && get_rect().has_point(get_viewport()->get_mouse_position() - get_global_position())) {
scrolling = true;
- set_physics_process(true);
+ set_physics_process_internal(true);
}
}
- if (p_what == NOTIFICATION_PHYSICS_PROCESS) {
+ if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
if (drag_touching) {
@@ -2853,7 +2853,7 @@ void Tree::_notification(int p_what) {
if (pos < 0) {
pos = 0;
turnoff = true;
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching = false;
drag_touching_deaccel = false;
}
@@ -2873,7 +2873,7 @@ void Tree::_notification(int p_what) {
drag_speed = sgn * val;
if (turnoff) {
- set_physics_process(false);
+ set_physics_process_internal(false);
drag_touching = false;
drag_touching_deaccel = false;
}
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp
index 4eee0126d..88e184753 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_player.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "video_player.h"
+#include "scene/scene_string_names.h"
#include "os/os.h"
#include "servers/audio_server.h"
@@ -159,11 +160,7 @@ void VideoPlayer::_notification(int p_notification) {
bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus);
- if (stream.is_null())
- return;
- if (paused)
- return;
- if (!playback->is_playing())
+ if (stream.is_null() || paused || !playback->is_playing())
return;
double audio_time = USEC_TO_SEC(OS::get_singleton()->get_ticks_usec());
@@ -174,7 +171,11 @@ void VideoPlayer::_notification(int p_notification) {
if (delta == 0)
return;
- playback->update(delta);
+ playback->update(delta); // playback->is_playing() returns false in the last video frame
+
+ if (!playback->is_playing()) {
+ emit_signal(SceneStringNames::get_singleton()->finished);
+ }
} break;
@@ -467,6 +468,8 @@ void VideoPlayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_video_texture"), &VideoPlayer::get_video_texture);
+ ADD_SIGNAL(MethodInfo("finished"));
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_track", PROPERTY_HINT_RANGE, "0,128,1"), "set_audio_track", "get_audio_track");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VideoStream"), "set_stream", "get_stream");
//ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), "set_loop", "has_loop") ;
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index 31d45d8e4..841421095 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -35,7 +35,7 @@ void CanvasLayer::set_layer(int p_xform) {
layer = p_xform;
if (viewport.is_valid())
- VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas->get_canvas(), layer);
+ VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer);
}
int CanvasLayer::get_layer() const {
@@ -48,7 +48,7 @@ void CanvasLayer::set_transform(const Transform2D &p_xform) {
transform = p_xform;
locrotscale_dirty = true;
if (viewport.is_valid())
- VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform);
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
}
Transform2D CanvasLayer::get_transform() const {
@@ -61,7 +61,7 @@ void CanvasLayer::_update_xform() {
transform.set_rotation_and_scale(rot, scale);
transform.set_origin(ofs);
if (viewport.is_valid())
- VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform);
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
}
void CanvasLayer::_update_locrotscale() {
@@ -133,11 +133,6 @@ Vector2 CanvasLayer::get_scale() const {
return scale;
}
-Ref<World2D> CanvasLayer::get_world_2d() const {
-
- return canvas;
-}
-
void CanvasLayer::_notification(int p_what) {
switch (p_what) {
@@ -153,14 +148,14 @@ void CanvasLayer::_notification(int p_what) {
ERR_FAIL_COND(!vp);
viewport = vp->get_viewport_rid();
- VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas->get_canvas());
- VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas->get_canvas(), layer);
- VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform);
+ VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
+ VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer);
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
} break;
case NOTIFICATION_EXIT_TREE: {
- VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas->get_canvas());
+ VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
viewport = RID();
} break;
@@ -184,7 +179,7 @@ RID CanvasLayer::get_viewport() const {
void CanvasLayer::set_custom_viewport(Node *p_viewport) {
ERR_FAIL_NULL(p_viewport);
if (is_inside_tree()) {
- VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas->get_canvas());
+ VisualServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
viewport = RID();
}
@@ -205,9 +200,9 @@ void CanvasLayer::set_custom_viewport(Node *p_viewport) {
viewport = vp->get_viewport_rid();
- VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas->get_canvas());
- VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas->get_canvas(), layer);
- VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas->get_canvas(), transform);
+ VisualServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
+ VisualServer::get_singleton()->viewport_set_canvas_layer(viewport, canvas, layer);
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
}
}
@@ -225,6 +220,10 @@ int CanvasLayer::get_sort_index() {
return sort_index++;
}
+RID CanvasLayer::get_canvas() const {
+
+ return canvas;
+}
void CanvasLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer);
@@ -248,7 +247,7 @@ void CanvasLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_custom_viewport", "viewport"), &CanvasLayer::set_custom_viewport);
ClassDB::bind_method(D_METHOD("get_custom_viewport"), &CanvasLayer::get_custom_viewport);
- ClassDB::bind_method(D_METHOD("get_world_2d"), &CanvasLayer::get_world_2d);
+ ClassDB::bind_method(D_METHOD("get_canvas"), &CanvasLayer::get_canvas);
//ClassDB::bind_method(D_METHOD("get_viewport"),&CanvasLayer::get_viewport);
ADD_PROPERTY(PropertyInfo(Variant::INT, "layer", PROPERTY_HINT_RANGE, "-128,128,1"), "set_layer", "get_layer");
@@ -268,8 +267,13 @@ CanvasLayer::CanvasLayer() {
rot = 0;
locrotscale_dirty = false;
layer = 1;
- canvas = Ref<World2D>(memnew(World2D));
+ canvas = VS::get_singleton()->canvas_create();
custom_viewport = NULL;
custom_viewport_id = 0;
sort_index = 0;
}
+
+CanvasLayer::~CanvasLayer() {
+
+ VS::get_singleton()->free(canvas);
+}
diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h
index c3352a6db..aae23fbb1 100644
--- a/scene/main/canvas_layer.h
+++ b/scene/main/canvas_layer.h
@@ -45,7 +45,7 @@ class CanvasLayer : public Node {
real_t rot;
int layer;
Transform2D transform;
- Ref<World2D> canvas;
+ RID canvas;
ObjectID custom_viewport_id; // to check validity
Viewport *custom_viewport;
@@ -81,8 +81,6 @@ public:
void set_scale(const Size2 &p_scale);
Size2 get_scale() const;
- Ref<World2D> get_world_2d() const;
-
Size2 get_viewport_size() const;
RID get_viewport() const;
@@ -93,7 +91,10 @@ public:
void reset_sort_index();
int get_sort_index();
+ RID get_canvas() const;
+
CanvasLayer();
+ ~CanvasLayer();
};
#endif // CANVAS_LAYER_H
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 28b454057..a1d79e735 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -477,7 +477,7 @@ bool Node::is_network_master() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
- return get_tree()->get_network_unique_id() == data.network_master;
+ return get_multiplayer_api()->get_network_unique_id() == data.network_master;
}
/***** RPC CONFIG ********/
@@ -667,200 +667,16 @@ Variant Node::_rpc_unreliable_id_bind(const Variant **p_args, int p_argcount, Va
}
void Node::rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) {
-
ERR_FAIL_COND(!is_inside_tree());
-
- bool skip_rpc = false;
- bool call_local_native = false;
- bool call_local_script = false;
-
- if (p_peer_id == 0 || p_peer_id == get_tree()->get_network_unique_id() || (p_peer_id < 0 && p_peer_id != -get_tree()->get_network_unique_id())) {
- //check that send mode can use local call
-
- Map<StringName, RPCMode>::Element *E = data.rpc_methods.find(p_method);
- if (E) {
-
- switch (E->get()) {
-
- case RPC_MODE_DISABLED: {
- //do nothing
- } break;
- case RPC_MODE_REMOTE: {
- //do nothing also, no need to call local
- } break;
- case RPC_MODE_SYNC: {
- //call it, sync always results in call
- call_local_native = true;
- } break;
- case RPC_MODE_MASTER: {
- call_local_native = is_network_master();
- if (call_local_native) {
- skip_rpc = true; //no other master so..
- }
- } break;
- case RPC_MODE_SLAVE: {
- call_local_native = !is_network_master();
- } break;
- }
- }
-
- if (call_local_native) {
- // done below
- } else if (get_script_instance()) {
- //attempt with script
- ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rpc_mode(p_method);
-
- switch (rpc_mode) {
-
- case ScriptInstance::RPC_MODE_DISABLED: {
- //do nothing
- } break;
- case ScriptInstance::RPC_MODE_REMOTE: {
- //do nothing also, no need to call local
- } break;
- case ScriptInstance::RPC_MODE_SYNC: {
- //call it, sync always results in call
- call_local_script = true;
- } break;
- case ScriptInstance::RPC_MODE_MASTER: {
- call_local_script = is_network_master();
- if (call_local_script) {
- skip_rpc = true; //no other master so..
- }
- } break;
- case ScriptInstance::RPC_MODE_SLAVE: {
- call_local_script = !is_network_master();
- } break;
- }
- }
- }
-
- if (!skip_rpc) {
- get_tree()->_rpc(this, p_peer_id, p_unreliable, false, p_method, p_arg, p_argcount);
- }
-
- if (call_local_native) {
- Variant::CallError ce;
- call(p_method, p_arg, p_argcount, ce);
- if (ce.error != Variant::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(this, p_method, p_arg, p_argcount, ce);
- error = "rpc() aborted in local call: - " + error;
- ERR_PRINTS(error);
- return;
- }
- }
-
- if (call_local_script) {
- Variant::CallError ce;
- ce.error = Variant::CallError::CALL_OK;
- get_script_instance()->call(p_method, p_arg, p_argcount, ce);
- if (ce.error != Variant::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(this, p_method, p_arg, p_argcount, ce);
- error = "rpc() aborted in script local call: - " + error;
- ERR_PRINTS(error);
- return;
- }
- }
+ get_multiplayer_api()->rpcp(this, p_peer_id, p_unreliable, p_method, p_arg, p_argcount);
}
-/******** RSET *********/
-
void Node::rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value) {
-
ERR_FAIL_COND(!is_inside_tree());
-
- bool skip_rset = false;
-
- if (p_peer_id == 0 || p_peer_id == get_tree()->get_network_unique_id() || (p_peer_id < 0 && p_peer_id != -get_tree()->get_network_unique_id())) {
- //check that send mode can use local call
-
- bool set_local = false;
-
- Map<StringName, RPCMode>::Element *E = data.rpc_properties.find(p_property);
- if (E) {
-
- switch (E->get()) {
-
- case RPC_MODE_DISABLED: {
- //do nothing
- } break;
- case RPC_MODE_REMOTE: {
- //do nothing also, no need to call local
- } break;
- case RPC_MODE_SYNC: {
- //call it, sync always results in call
- set_local = true;
- } break;
- case RPC_MODE_MASTER: {
- set_local = is_network_master();
- if (set_local) {
- skip_rset = true;
- }
-
- } break;
- case RPC_MODE_SLAVE: {
- set_local = !is_network_master();
- } break;
- }
- }
-
- if (set_local) {
- bool valid;
- set(p_property, p_value, &valid);
-
- if (!valid) {
- String error = "rset() aborted in local set, property not found: - " + String(p_property);
- ERR_PRINTS(error);
- return;
- }
- } else if (get_script_instance()) {
- //attempt with script
- ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rset_mode(p_property);
-
- switch (rpc_mode) {
-
- case ScriptInstance::RPC_MODE_DISABLED: {
- //do nothing
- } break;
- case ScriptInstance::RPC_MODE_REMOTE: {
- //do nothing also, no need to call local
- } break;
- case ScriptInstance::RPC_MODE_SYNC: {
- //call it, sync always results in call
- set_local = true;
- } break;
- case ScriptInstance::RPC_MODE_MASTER: {
- set_local = is_network_master();
- if (set_local) {
- skip_rset = true;
- }
- } break;
- case ScriptInstance::RPC_MODE_SLAVE: {
- set_local = !is_network_master();
- } break;
- }
-
- if (set_local) {
-
- bool valid = get_script_instance()->set(p_property, p_value);
-
- if (!valid) {
- String error = "rset() aborted in local script set, property not found: - " + String(p_property);
- ERR_PRINTS(error);
- return;
- }
- }
- }
- }
-
- if (skip_rset)
- return;
-
- const Variant *vptr = &p_value;
-
- get_tree()->_rpc(this, p_peer_id, p_unreliable, true, p_property, &vptr, 1);
+ get_multiplayer_api()->rsetp(this, p_peer_id, p_unreliable, p_property, p_value);
}
+/******** RSET *********/
void Node::rset(const StringName &p_property, const Variant &p_value) {
rsetp(0, false, p_property, p_value);
@@ -882,6 +698,30 @@ void Node::rset_unreliable_id(int p_peer_id, const StringName &p_property, const
}
//////////// end of rpc
+Ref<MultiplayerAPI> Node::get_multiplayer_api() const {
+ if (multiplayer_api.is_valid())
+ return multiplayer_api;
+ if (!is_inside_tree())
+ return Ref<MultiplayerAPI>();
+ return get_tree()->get_multiplayer_api();
+}
+
+Ref<MultiplayerAPI> Node::get_custom_multiplayer_api() const {
+ return multiplayer_api;
+}
+
+void Node::set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) {
+
+ multiplayer_api = p_multiplayer_api;
+}
+
+const Map<StringName, Node::RPCMode>::Element *Node::get_node_rpc_mode(const StringName &p_method) {
+ return data.rpc_methods.find(p_method);
+}
+
+const Map<StringName, Node::RPCMode>::Element *Node::get_node_rset_mode(const StringName &p_property) {
+ return data.rpc_properties.find(p_property);
+}
bool Node::can_call_rpc(const StringName &p_method, int p_from) const {
@@ -1139,9 +979,23 @@ void Node::_set_name_nocheck(const StringName &p_name) {
data.name = p_name;
}
+String Node::invalid_character = ". : @ / \"";
+
+bool Node::_validate_node_name(String &p_name) {
+ String name = p_name;
+ Vector<String> chars = Node::invalid_character.split(" ");
+ for (int i = 0; i < chars.size(); i++) {
+ name = name.replace(chars[i], "");
+ }
+ bool is_valid = name == p_name;
+ p_name = name;
+ return is_valid;
+}
+
void Node::set_name(const String &p_name) {
- String name = p_name.replace(":", "").replace("/", "").replace("@", "");
+ String name = p_name;
+ _validate_node_name(name);
ERR_FAIL_COND(name == "");
data.name = name;
@@ -1868,11 +1722,18 @@ bool Node::has_persistent_groups() const {
return false;
}
-void Node::_print_tree(const Node *p_node) {
+void Node::_print_tree_pretty(const String prefix, const bool last) {
- print_line(String(p_node->get_path_to(this)));
- for (int i = 0; i < data.children.size(); i++)
- data.children[i]->_print_tree(p_node);
+ String new_prefix = last ? String::utf8(" ┖╴") : String::utf8(" ┠╴");
+ print_line(prefix + new_prefix + String(get_name()));
+ for (int i = 0; i < data.children.size(); i++) {
+ new_prefix = last ? String::utf8(" ") : String::utf8(" ┃ ");
+ data.children[i]->_print_tree_pretty(prefix + new_prefix, i == data.children.size() - 1);
+ }
+}
+
+void Node::print_tree_pretty() {
+ _print_tree_pretty("", true);
}
void Node::print_tree() {
@@ -1880,6 +1741,12 @@ void Node::print_tree() {
_print_tree(this);
}
+void Node::_print_tree(const Node *p_node) {
+ print_line(String(p_node->get_path_to(this)));
+ for (int i = 0; i < data.children.size(); i++)
+ data.children[i]->_print_tree(p_node);
+}
+
void Node::_propagate_reverse_notification(int p_notification) {
data.blocked++;
@@ -2163,13 +2030,7 @@ Node *Node::_duplicate(int p_flags, Map<const Node *, Node *> *r_duplimap) const
if (name == script_property_name)
continue;
- Variant value = N->get()->get(name);
- // Duplicate dictionaries and arrays, mainly needed for __meta__
- if (value.get_type() == Variant::DICTIONARY) {
- value = Dictionary(value).duplicate();
- } else if (value.get_type() == Variant::ARRAY) {
- value = Array(value).duplicate();
- }
+ Variant value = N->get()->get(name).duplicate(true);
if (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE) {
@@ -2313,13 +2174,7 @@ void Node::_duplicate_and_reown(Node *p_new_parent, const Map<Node *, Node *> &p
continue;
String name = E->get().name;
- Variant value = get(name);
- // Duplicate dictionaries and arrays, mainly needed for __meta__
- if (value.get_type() == Variant::DICTIONARY) {
- value = Dictionary(value).duplicate();
- } else if (value.get_type() == Variant::ARRAY) {
- value = Array(value).duplicate();
- }
+ Variant value = get(name).duplicate(true);
node->set(name, value);
}
@@ -2726,18 +2581,21 @@ Array Node::_get_children() const {
return arr;
}
-#ifdef TOOLS_ENABLED
void Node::set_import_path(const NodePath &p_import_path) {
+#ifdef TOOLS_ENABLED
data.import_path = p_import_path;
+#endif
}
NodePath Node::get_import_path() const {
+#ifdef TOOLS_ENABLED
return data.import_path;
-}
-
+#else
+ return NodePath();
#endif
+}
static void _add_nodes_to_options(const Node *p_base, const Node *p_node, List<String> *r_options) {
@@ -2840,6 +2698,7 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_and_skip"), &Node::remove_and_skip);
ClassDB::bind_method(D_METHOD("get_index"), &Node::get_index);
ClassDB::bind_method(D_METHOD("print_tree"), &Node::print_tree);
+ ClassDB::bind_method(D_METHOD("print_tree_pretty"), &Node::print_tree_pretty);
ClassDB::bind_method(D_METHOD("set_filename", "filename"), &Node::set_filename);
ClassDB::bind_method(D_METHOD("get_filename"), &Node::get_filename);
ClassDB::bind_method(D_METHOD("propagate_notification", "what"), &Node::propagate_notification);
@@ -2889,16 +2748,16 @@ void Node::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_network_master"), &Node::is_network_master);
+ ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &Node::get_multiplayer_api);
+ ClassDB::bind_method(D_METHOD("get_custom_multiplayer_api"), &Node::get_custom_multiplayer_api);
+ ClassDB::bind_method(D_METHOD("set_custom_multiplayer_api", "api"), &Node::set_custom_multiplayer_api);
ClassDB::bind_method(D_METHOD("rpc_config", "method", "mode"), &Node::rpc_config);
ClassDB::bind_method(D_METHOD("rset_config", "property", "mode"), &Node::rset_config);
-#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path);
ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path);
ADD_PROPERTYNZ(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path");
-#endif
-
{
MethodInfo mi;
@@ -2970,6 +2829,8 @@ void Node::_bind_methods() {
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, "", 0), "set_name", "get_name");
ADD_PROPERTYNZ(PropertyInfo(Variant::STRING, "filename", PROPERTY_HINT_NONE, "", 0), "set_filename", "get_filename");
ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "owner", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_owner", "get_owner");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "", "get_multiplayer_api");
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT, "custom_multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer_api", "get_custom_multiplayer_api");
BIND_VMETHOD(MethodInfo("_process", PropertyInfo(Variant::REAL, "delta")));
BIND_VMETHOD(MethodInfo("_physics_process", PropertyInfo(Variant::REAL, "delta")));
diff --git a/scene/main/node.h b/scene/main/node.h
index 341869f7b..b5a956116 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -151,6 +151,9 @@ private:
NAME_CASING_SNAKE_CASE
};
+ Ref<MultiplayerAPI> multiplayer_api;
+
+ void _print_tree_pretty(const String prefix, const bool last);
void _print_tree(const Node *p_node);
Node *_get_node(const NodePath &p_path) const;
@@ -187,6 +190,12 @@ private:
void _set_tree(SceneTree *p_tree);
+#ifdef TOOLS_ENABLED
+ friend class SceneTreeEditor;
+#endif
+ static String invalid_character;
+ static bool _validate_node_name(String &p_name);
+
protected:
void _block() { data.blocked++; }
void _unblock() { data.blocked--; }
@@ -287,6 +296,7 @@ public:
int get_index() const;
void print_tree();
+ void print_tree_pretty();
void set_filename(const String &p_filename);
String get_filename() const;
@@ -370,10 +380,8 @@ public:
void force_parent_owned() { data.parent_owned = true; } //hack to avoid duplicate nodes
-#ifdef TOOLS_ENABLED
void set_import_path(const NodePath &p_import_path); //path used when imported, used by scene editors to keep tracking
NodePath get_import_path() const;
-#endif
bool is_owned_by_parent() const;
@@ -403,15 +411,20 @@ public:
void rpc_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_LIST); //rpc call, honors RPCMode
void rpc_unreliable_id(int p_peer_id, const StringName &p_method, VARIANT_ARG_LIST); //rpc call, honors RPCMode
- void rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
-
void rset(const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode
void rset_unreliable(const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode
void rset_id(int p_peer_id, const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode
void rset_unreliable_id(int p_peer_id, const StringName &p_property, const Variant &p_value); //remote set call, honors RPCMode
+ void rpcp(int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount);
void rsetp(int p_peer_id, bool p_unreliable, const StringName &p_property, const Variant &p_value);
+ Ref<MultiplayerAPI> get_multiplayer_api() const;
+ Ref<MultiplayerAPI> get_custom_multiplayer_api() const;
+ void set_custom_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api);
+ const Map<StringName, RPCMode>::Element *get_node_rpc_mode(const StringName &p_method);
+ const Map<StringName, RPCMode>::Element *get_node_rset_mode(const StringName &p_property);
+
bool can_call_rpc(const StringName &p_method, int p_from) const;
bool can_call_rset(const StringName &p_property, int p_from) const;
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 037331dec..011087b48 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -33,6 +33,7 @@
#include "editor/editor_node.h"
#include "io/marshalls.h"
#include "io/resource_loader.h"
+#include "main/input_default.h"
#include "message_queue.h"
#include "node.h"
#include "os/keyboard.h"
@@ -484,7 +485,7 @@ bool SceneTree::idle(float p_time) {
idle_process_time = p_time;
- _network_poll();
+ multiplayer_api->poll();
emit_signal("idle_frame");
@@ -620,6 +621,13 @@ void SceneTree::_notification(int p_notification) {
case NOTIFICATION_WM_FOCUS_IN:
case NOTIFICATION_WM_FOCUS_OUT: {
+ if (p_notification == NOTIFICATION_WM_FOCUS_IN) {
+ InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton());
+ if (id) {
+ id->ensure_touch_mouse_raised();
+ }
+ }
+
get_root()->propagate_notification(p_notification);
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
@@ -1197,16 +1205,20 @@ void SceneTree::set_screen_stretch(StretchMode p_mode, StretchAspect p_aspect, c
_update_root_rect();
}
-#ifdef TOOLS_ENABLED
void SceneTree::set_edited_scene_root(Node *p_node) {
+#ifdef TOOLS_ENABLED
edited_scene_root = p_node;
+#endif
}
Node *SceneTree::get_edited_scene_root() const {
+#ifdef TOOLS_ENABLED
return edited_scene_root;
-}
+#else
+ return NULL;
#endif
+}
void SceneTree::set_current_scene(Node *p_scene) {
@@ -1633,16 +1645,11 @@ Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec, bool p_process_pa
void SceneTree::_network_peer_connected(int p_id) {
- connected_peers.insert(p_id);
- path_get_cache.insert(p_id, PathGetCache());
-
emit_signal("network_peer_connected", p_id);
}
void SceneTree::_network_peer_disconnected(int p_id) {
- connected_peers.erase(p_id);
- path_get_cache.erase(p_id); //I no longer need your cache, sorry
emit_signal("network_peer_disconnected", p_id);
}
@@ -1661,471 +1668,70 @@ void SceneTree::_server_disconnected() {
emit_signal("server_disconnected");
}
-void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer) {
- if (network_peer.is_valid()) {
- network_peer->disconnect("peer_connected", this, "_network_peer_connected");
- network_peer->disconnect("peer_disconnected", this, "_network_peer_disconnected");
- network_peer->disconnect("connection_succeeded", this, "_connected_to_server");
- network_peer->disconnect("connection_failed", this, "_connection_failed");
- network_peer->disconnect("server_disconnected", this, "_server_disconnected");
- connected_peers.clear();
- path_get_cache.clear();
- path_send_cache.clear();
- last_send_cache_id = 1;
+Ref<MultiplayerAPI> SceneTree::get_multiplayer_api() const {
+ return multiplayer_api;
+}
+
+void SceneTree::set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api) {
+ ERR_FAIL_COND(!p_multiplayer_api.is_valid());
+
+ if (multiplayer_api.is_valid()) {
+ multiplayer_api->disconnect("network_peer_connected", this, "_network_peer_connected");
+ multiplayer_api->disconnect("network_peer_disconnected", this, "_network_peer_disconnected");
+ multiplayer_api->disconnect("connected_to_server", this, "_connected_to_server");
+ multiplayer_api->disconnect("connection_failed", this, "_connection_failed");
+ multiplayer_api->disconnect("server_disconnected", this, "_server_disconnected");
}
- ERR_EXPLAIN("Supplied NetworkedNetworkPeer must be connecting or connected.");
- ERR_FAIL_COND(p_network_peer.is_valid() && p_network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED);
+ multiplayer_api = p_multiplayer_api;
+ multiplayer_api->set_root_node(root);
+
+ multiplayer_api->connect("network_peer_connected", this, "_network_peer_connected");
+ multiplayer_api->connect("network_peer_disconnected", this, "_network_peer_disconnected");
+ multiplayer_api->connect("connected_to_server", this, "_connected_to_server");
+ multiplayer_api->connect("connection_failed", this, "_connection_failed");
+ multiplayer_api->connect("server_disconnected", this, "_server_disconnected");
+}
- network_peer = p_network_peer;
+void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer) {
- if (network_peer.is_valid()) {
- network_peer->connect("peer_connected", this, "_network_peer_connected");
- network_peer->connect("peer_disconnected", this, "_network_peer_disconnected");
- network_peer->connect("connection_succeeded", this, "_connected_to_server");
- network_peer->connect("connection_failed", this, "_connection_failed");
- network_peer->connect("server_disconnected", this, "_server_disconnected");
- }
+ multiplayer_api->set_network_peer(p_network_peer);
}
Ref<NetworkedMultiplayerPeer> SceneTree::get_network_peer() const {
- return network_peer;
+ return multiplayer_api->get_network_peer();
}
bool SceneTree::is_network_server() const {
- ERR_FAIL_COND_V(!network_peer.is_valid(), false);
- return network_peer->is_server();
+ return multiplayer_api->is_network_server();
}
bool SceneTree::has_network_peer() const {
- return network_peer.is_valid();
+ return multiplayer_api->has_network_peer();
}
int SceneTree::get_network_unique_id() const {
- ERR_FAIL_COND_V(!network_peer.is_valid(), 0);
- return network_peer->get_unique_id();
+ return multiplayer_api->get_network_unique_id();
}
Vector<int> SceneTree::get_network_connected_peers() const {
- ERR_FAIL_COND_V(!network_peer.is_valid(), Vector<int>());
- Vector<int> ret;
- for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
- ret.push_back(E->get());
- }
-
- return ret;
+ return multiplayer_api->get_network_connected_peers();
}
int SceneTree::get_rpc_sender_id() const {
- return rpc_sender_id;
+ return multiplayer_api->get_rpc_sender_id();
}
void SceneTree::set_refuse_new_network_connections(bool p_refuse) {
- ERR_FAIL_COND(!network_peer.is_valid());
- network_peer->set_refuse_new_connections(p_refuse);
+ multiplayer_api->set_refuse_new_network_connections(p_refuse);
}
bool SceneTree::is_refusing_new_network_connections() const {
-
- ERR_FAIL_COND_V(!network_peer.is_valid(), false);
-
- return network_peer->is_refusing_new_connections();
-}
-
-void SceneTree::_rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount) {
-
- if (network_peer.is_null()) {
- ERR_EXPLAIN("Attempt to remote call/set when networking is not active in SceneTree.");
- ERR_FAIL();
- }
-
- if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING) {
- ERR_EXPLAIN("Attempt to remote call/set when networking is not connected yet in SceneTree.");
- ERR_FAIL();
- }
-
- if (network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) {
- ERR_EXPLAIN("Attempt to remote call/set when networking is disconnected.");
- ERR_FAIL();
- }
-
- if (p_argcount > 255) {
- ERR_EXPLAIN("Too many arguments >255.");
- ERR_FAIL();
- }
-
- if (p_to != 0 && !connected_peers.has(ABS(p_to))) {
- if (p_to == get_network_unique_id()) {
- ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: " + itos(get_network_unique_id()));
- } else {
- ERR_EXPLAIN("Attempt to remote call unexisting ID: " + itos(p_to));
- }
-
- ERR_FAIL();
- }
-
- NodePath from_path = p_from->get_path();
- ERR_FAIL_COND(from_path.is_empty());
-
- //see if the path is cached
- PathSentCache *psc = path_send_cache.getptr(from_path);
- if (!psc) {
- //path is not cached, create
- path_send_cache[from_path] = PathSentCache();
- psc = path_send_cache.getptr(from_path);
- psc->id = last_send_cache_id++;
- }
-
- //create base packet, lots of hardcode because it must be tight
-
- int ofs = 0;
-
-#define MAKE_ROOM(m_amount) \
- if (packet_cache.size() < m_amount) packet_cache.resize(m_amount);
-
- //encode type
- MAKE_ROOM(1);
- packet_cache[0] = p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
- ofs += 1;
-
- //encode ID
- MAKE_ROOM(ofs + 4);
- encode_uint32(psc->id, &packet_cache[ofs]);
- ofs += 4;
-
- //encode function name
- CharString name = String(p_name).utf8();
- int len = encode_cstring(name.get_data(), NULL);
- MAKE_ROOM(ofs + len);
- encode_cstring(name.get_data(), &packet_cache[ofs]);
- ofs += len;
-
- if (p_set) {
- //set argument
- Error err = encode_variant(*p_arg[0], NULL, len);
- ERR_FAIL_COND(err != OK);
- MAKE_ROOM(ofs + len);
- encode_variant(*p_arg[0], &packet_cache[ofs], len);
- ofs += len;
-
- } else {
- //call arguments
- MAKE_ROOM(ofs + 1);
- packet_cache[ofs] = p_argcount;
- ofs += 1;
- for (int i = 0; i < p_argcount; i++) {
- Error err = encode_variant(*p_arg[i], NULL, len);
- ERR_FAIL_COND(err != OK);
- MAKE_ROOM(ofs + len);
- encode_variant(*p_arg[i], &packet_cache[ofs], len);
- ofs += len;
- }
- }
-
- //see if all peers have cached path (is so, call can be fast)
- bool has_all_peers = true;
-
- List<int> peers_to_add; //if one is missing, take note to add it
-
- for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
-
- if (p_to < 0 && E->get() == -p_to)
- continue; //continue, excluded
-
- if (p_to > 0 && E->get() != p_to)
- continue; //continue, not for this peer
-
- Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
-
- if (!F || F->get() == false) {
- //path was not cached, or was cached but is unconfirmed
- if (!F) {
- //not cached at all, take note
- peers_to_add.push_back(E->get());
- }
-
- has_all_peers = false;
- }
- }
-
- //those that need to be added, send a message for this
-
- for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) {
-
- //encode function name
- CharString pname = String(from_path).utf8();
- int len = encode_cstring(pname.get_data(), NULL);
-
- Vector<uint8_t> packet;
-
- packet.resize(1 + 4 + len);
- packet[0] = NETWORK_COMMAND_SIMPLIFY_PATH;
- encode_uint32(psc->id, &packet[1]);
- encode_cstring(pname.get_data(), &packet[5]);
-
- network_peer->set_target_peer(E->get()); //to all of you
- network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
- network_peer->put_packet(packet.ptr(), packet.size());
-
- psc->confirmed_peers.insert(E->get(), false); //insert into confirmed, but as false since it was not confirmed
- }
-
- //take chance and set transfer mode, since all send methods will use it
- network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
-
- if (has_all_peers) {
-
- //they all have verified paths, so send fast
- network_peer->set_target_peer(p_to); //to all of you
- network_peer->put_packet(packet_cache.ptr(), ofs); //a message with love
- } else {
- //not all verified path, so send one by one
-
- //apend path at the end, since we will need it for some packets
- CharString pname = String(from_path).utf8();
- int path_len = encode_cstring(pname.get_data(), NULL);
- MAKE_ROOM(ofs + path_len);
- encode_cstring(pname.get_data(), &packet_cache[ofs]);
-
- for (Set<int>::Element *E = connected_peers.front(); E; E = E->next()) {
-
- if (p_to < 0 && E->get() == -p_to)
- continue; //continue, excluded
-
- if (p_to > 0 && E->get() != p_to)
- continue; //continue, not for this peer
-
- Map<int, bool>::Element *F = psc->confirmed_peers.find(E->get());
- ERR_CONTINUE(!F); //should never happen
-
- network_peer->set_target_peer(E->get()); //to this one specifically
-
- if (F->get() == true) {
- //this one confirmed path, so use id
- encode_uint32(psc->id, &packet_cache[1]);
- network_peer->put_packet(packet_cache.ptr(), ofs);
- } else {
- //this one did not confirm path yet, so use entire path (sorry!)
- encode_uint32(0x80000000 | ofs, &packet_cache[1]); //offset to path and flag
- network_peer->put_packet(packet_cache.ptr(), ofs + path_len);
- }
- }
- }
-}
-
-void SceneTree::_network_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
-
- ERR_FAIL_COND(p_packet_len < 5);
-
- uint8_t packet_type = p_packet[0];
-
- switch (packet_type) {
-
- case NETWORK_COMMAND_REMOTE_CALL:
- case NETWORK_COMMAND_REMOTE_SET: {
-
- ERR_FAIL_COND(p_packet_len < 5);
- uint32_t target = decode_uint32(&p_packet[1]);
-
- Node *node = NULL;
-
- if (target & 0x80000000) {
- //use full path (not cached yet)
-
- int ofs = target & 0x7FFFFFFF;
- ERR_FAIL_COND(ofs >= p_packet_len);
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[ofs], p_packet_len - ofs);
-
- NodePath np = paths;
-
- node = get_root()->get_node(np);
- if (node == NULL) {
- ERR_EXPLAIN("Failed to get path from RPC: " + String(np));
- ERR_FAIL_COND(node == NULL);
- }
- } else {
- //use cached path
- int id = target;
-
- Map<int, PathGetCache>::Element *E = path_get_cache.find(p_from);
- ERR_FAIL_COND(!E);
-
- Map<int, PathGetCache::NodeInfo>::Element *F = E->get().nodes.find(id);
- ERR_FAIL_COND(!F);
-
- PathGetCache::NodeInfo *ni = &F->get();
- //do proper caching later
-
- node = get_root()->get_node(ni->path);
- if (node == NULL) {
- ERR_EXPLAIN("Failed to get cached path from RPC: " + String(ni->path));
- ERR_FAIL_COND(node == NULL);
- }
- }
-
- ERR_FAIL_COND(p_packet_len < 6);
-
- //detect cstring end
- int len_end = 5;
- for (; len_end < p_packet_len; len_end++) {
- if (p_packet[len_end] == 0) {
- break;
- }
- }
-
- ERR_FAIL_COND(len_end >= p_packet_len);
-
- StringName name = String::utf8((const char *)&p_packet[5]);
-
- if (packet_type == NETWORK_COMMAND_REMOTE_CALL) {
-
- if (!node->can_call_rpc(name, p_from))
- return;
-
- int ofs = len_end + 1;
-
- ERR_FAIL_COND(ofs >= p_packet_len);
-
- int argc = p_packet[ofs];
- Vector<Variant> args;
- Vector<const Variant *> argp;
- args.resize(argc);
- argp.resize(argc);
-
- ofs++;
-
- for (int i = 0; i < argc; i++) {
-
- ERR_FAIL_COND(ofs >= p_packet_len);
- int vlen;
- Error err = decode_variant(args[i], &p_packet[ofs], p_packet_len - ofs, &vlen);
- ERR_FAIL_COND(err != OK);
- //args[i]=p_packet[3+i];
- argp[i] = &args[i];
- ofs += vlen;
- }
-
- Variant::CallError ce;
-
- node->call(name, (const Variant **)argp.ptr(), argc, ce);
- if (ce.error != Variant::CallError::CALL_OK) {
- String error = Variant::get_call_error_text(node, name, (const Variant **)argp.ptr(), argc, ce);
- error = "RPC - " + error;
- ERR_PRINTS(error);
- }
-
- } else {
-
- if (!node->can_call_rset(name, p_from))
- return;
-
- int ofs = len_end + 1;
-
- ERR_FAIL_COND(ofs >= p_packet_len);
-
- Variant value;
- decode_variant(value, &p_packet[ofs], p_packet_len - ofs);
-
- bool valid;
-
- node->set(name, value, &valid);
- if (!valid) {
- String error = "Error setting remote property '" + String(name) + "', not found in object of type " + node->get_class();
- ERR_PRINTS(error);
- }
- }
-
- } break;
- case NETWORK_COMMAND_SIMPLIFY_PATH: {
-
- ERR_FAIL_COND(p_packet_len < 5);
- int id = decode_uint32(&p_packet[1]);
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[5], p_packet_len - 5);
-
- NodePath path = paths;
-
- if (!path_get_cache.has(p_from)) {
- path_get_cache[p_from] = PathGetCache();
- }
-
- PathGetCache::NodeInfo ni;
- ni.path = path;
- ni.instance = 0;
-
- path_get_cache[p_from].nodes[id] = ni;
-
- {
- //send ack
-
- //encode path
- CharString pname = String(path).utf8();
- int len = encode_cstring(pname.get_data(), NULL);
-
- Vector<uint8_t> packet;
-
- packet.resize(1 + len);
- packet[0] = NETWORK_COMMAND_CONFIRM_PATH;
- encode_cstring(pname.get_data(), &packet[1]);
-
- network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
- network_peer->set_target_peer(p_from);
- network_peer->put_packet(packet.ptr(), packet.size());
- }
- } break;
- case NETWORK_COMMAND_CONFIRM_PATH: {
-
- String paths;
- paths.parse_utf8((const char *)&p_packet[1], p_packet_len - 1);
-
- NodePath path = paths;
-
- PathSentCache *psc = path_send_cache.getptr(path);
- ERR_FAIL_COND(!psc);
-
- Map<int, bool>::Element *E = psc->confirmed_peers.find(p_from);
- ERR_FAIL_COND(!E);
- E->get() = true;
- } break;
- }
-}
-
-void SceneTree::_network_poll() {
-
- if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED)
- return;
-
- network_peer->poll();
-
- if (!network_peer.is_valid()) //it's possible that polling might have resulted in a disconnection, so check here
- return;
-
- while (network_peer->get_available_packet_count()) {
-
- int sender = network_peer->get_packet_peer();
- const uint8_t *packet;
- int len;
-
- Error err = network_peer->get_packet(&packet, len);
- if (err != OK) {
- ERR_PRINT("Error getting packet!");
- }
-
- rpc_sender_id = sender;
- _network_process_packet(sender, packet, len);
- rpc_sender_id = 0;
-
- if (!network_peer.is_valid()) {
- break; //it's also possible that a packet or RPC caused a disconnection, so also check here
- }
- }
+ return multiplayer_api->is_refusing_new_network_connections();
}
void SceneTree::_bind_methods() {
@@ -2143,10 +1749,8 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_debug_navigation_hint", "enable"), &SceneTree::set_debug_navigation_hint);
ClassDB::bind_method(D_METHOD("is_debugging_navigation_hint"), &SceneTree::is_debugging_navigation_hint);
-#ifdef TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("set_edited_scene_root", "scene"), &SceneTree::set_edited_scene_root);
ClassDB::bind_method(D_METHOD("get_edited_scene_root"), &SceneTree::get_edited_scene_root);
-#endif
ClassDB::bind_method(D_METHOD("set_pause", "enable"), &SceneTree::set_pause);
ClassDB::bind_method(D_METHOD("is_paused"), &SceneTree::is_paused);
@@ -2196,6 +1800,8 @@ void SceneTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("_change_scene"), &SceneTree::_change_scene);
+ ClassDB::bind_method(D_METHOD("set_multiplayer_api", "multiplayer_api"), &SceneTree::set_multiplayer_api);
+ ClassDB::bind_method(D_METHOD("get_multiplayer_api"), &SceneTree::get_multiplayer_api);
ClassDB::bind_method(D_METHOD("set_network_peer", "peer"), &SceneTree::set_network_peer);
ClassDB::bind_method(D_METHOD("get_network_peer"), &SceneTree::get_network_peer);
ClassDB::bind_method(D_METHOD("is_network_server"), &SceneTree::is_network_server);
@@ -2219,12 +1825,11 @@ void SceneTree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused"), "set_pause", "is_paused");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_font_oversampling"), "set_use_font_oversampling", "is_using_font_oversampling");
-#ifdef TOOLS_ENABLED
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "edited_scene_root", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_edited_scene_root", "get_edited_scene_root");
-#endif
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "current_scene", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "set_current_scene", "get_current_scene");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", 0), "set_network_peer", "get_network_peer");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root", PROPERTY_HINT_RESOURCE_TYPE, "Node", 0), "", "get_root");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_api", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_multiplayer_api", "get_multiplayer_api");
ADD_SIGNAL(MethodInfo("tree_changed"));
ADD_SIGNAL(MethodInfo("node_added", PropertyInfo(Variant::OBJECT, "node")));
@@ -2320,7 +1925,6 @@ SceneTree::SceneTree() {
call_lock = 0;
root_lock = 0;
node_count = 0;
- rpc_sender_id = 0;
//create with mainloop
@@ -2329,6 +1933,9 @@ SceneTree::SceneTree() {
if (!root->get_world().is_valid())
root->set_world(Ref<World>(memnew(World)));
+ // Initialize network state
+ set_multiplayer_api(Ref<MultiplayerAPI>(memnew(MultiplayerAPI)));
+
//root->set_world_2d( Ref<World2D>( memnew( World2D )));
root->set_as_audio_listener(true);
root->set_as_audio_listener_2d(true);
@@ -2423,8 +2030,6 @@ SceneTree::SceneTree() {
live_edit_root = NodePath("/root");
- last_send_cache_id = 1;
-
#endif
use_font_oversampling = false;
diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h
index c5357762e..9c06e4ded 100644
--- a/scene/main/scene_tree.h
+++ b/scene/main/scene_tree.h
@@ -31,7 +31,7 @@
#ifndef SCENE_MAIN_LOOP_H
#define SCENE_MAIN_LOOP_H
-#include "io/networked_multiplayer_peer.h"
+#include "io/multiplayer_api.h"
#include "os/main_loop.h"
#include "os/thread_safe.h"
#include "scene/resources/mesh.h"
@@ -185,16 +185,8 @@ private:
///network///
- enum NetworkCommands {
- NETWORK_COMMAND_REMOTE_CALL,
- NETWORK_COMMAND_REMOTE_SET,
- NETWORK_COMMAND_SIMPLIFY_PATH,
- NETWORK_COMMAND_CONFIRM_PATH,
- };
-
- Ref<NetworkedMultiplayerPeer> network_peer;
+ Ref<MultiplayerAPI> multiplayer_api;
- Set<int> connected_peers;
void _network_peer_connected(int p_id);
void _network_peer_disconnected(int p_id);
@@ -202,39 +194,9 @@ private:
void _connection_failed();
void _server_disconnected();
- int rpc_sender_id;
-
- //path sent caches
- struct PathSentCache {
- Map<int, bool> confirmed_peers;
- int id;
- };
-
- HashMap<NodePath, PathSentCache> path_send_cache;
- int last_send_cache_id;
-
- //path get caches
- struct PathGetCache {
- struct NodeInfo {
- NodePath path;
- ObjectID instance;
- };
-
- Map<int, NodeInfo> nodes;
- };
-
- Map<int, PathGetCache> path_get_cache;
-
- Vector<uint8_t> packet_cache;
-
- void _network_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
- void _network_poll();
-
static SceneTree *singleton;
friend class Node;
- void _rpc(Node *p_from, int p_to, bool p_unreliable, bool p_set, const StringName &p_name, const Variant **p_arg, int p_argcount);
-
void tree_changed();
void node_added(Node *p_node);
void node_removed(Node *p_node);
@@ -428,10 +390,8 @@ public:
//void change_scene(const String& p_path);
//Node *get_loaded_scene();
-#ifdef TOOLS_ENABLED
void set_edited_scene_root(Node *p_node);
Node *get_edited_scene_root() const;
-#endif
void set_current_scene(Node *p_scene);
Node *get_current_scene() const;
@@ -450,6 +410,8 @@ public:
//network API
+ Ref<MultiplayerAPI> get_multiplayer_api() const;
+ void set_multiplayer_api(Ref<MultiplayerAPI> p_multiplayer_api);
void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_network_peer);
Ref<NetworkedMultiplayerPeer> get_network_peer() const;
bool is_network_server() const;
diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp
index ad2cdbfd0..c285694df 100755
--- a/scene/main/timer.cpp
+++ b/scene/main/timer.cpp
@@ -107,7 +107,10 @@ bool Timer::has_autostart() const {
return autostart;
}
-void Timer::start() {
+void Timer::start(float p_time) {
+ if (p_time > 0) {
+ set_wait_time(p_time);
+ }
time_left = wait_time;
_set_process(true);
}
@@ -185,7 +188,7 @@ void Timer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_autostart", "enable"), &Timer::set_autostart);
ClassDB::bind_method(D_METHOD("has_autostart"), &Timer::has_autostart);
- ClassDB::bind_method(D_METHOD("start"), &Timer::start);
+ ClassDB::bind_method(D_METHOD("start", "time_sec"), &Timer::start, DEFVAL(-1));
ClassDB::bind_method(D_METHOD("stop"), &Timer::stop);
ClassDB::bind_method(D_METHOD("set_paused", "paused"), &Timer::set_paused);
diff --git a/scene/main/timer.h b/scene/main/timer.h
index 410d98540..2f42252a7 100755
--- a/scene/main/timer.h
+++ b/scene/main/timer.h
@@ -64,7 +64,7 @@ public:
void set_autostart(bool p_start);
bool has_autostart() const;
- void start();
+ void start(float p_time = -1);
void stop();
void set_paused(bool p_paused);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 08fbf4446..11b663e41 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -181,6 +181,7 @@ public:
Viewport::GUI::GUI() {
mouse_focus = NULL;
+ mouse_click_grabber = NULL;
mouse_focus_button = -1;
key_focus = NULL;
mouse_over = NULL;
@@ -1813,7 +1814,7 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
}
if (!over) {
- OS::get_singleton()->set_cursor_shape(OS::CURSOR_ARROW);
+ OS::get_singleton()->set_cursor_shape((OS::CursorShape)Input::get_singleton()->get_default_cursor_shape());
return;
}
@@ -2278,7 +2279,7 @@ List<Control *>::Element *Viewport::_gui_show_modal(Control *p_control) {
else
p_control->_modal_set_prev_focus_owner(0);
- if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus)) {
+ if (gui.mouse_focus && !p_control->is_a_parent_of(gui.mouse_focus) && !gui.mouse_click_grabber) {
Ref<InputEventMouseButton> mb;
mb.instance();
mb->set_position(gui.mouse_focus->get_local_mouse_position());
@@ -2300,9 +2301,22 @@ Control *Viewport::_gui_get_focus_owner() {
void Viewport::_gui_grab_click_focus(Control *p_control) {
+ gui.mouse_click_grabber = p_control;
+ call_deferred("_post_gui_grab_click_focus");
+}
+
+void Viewport::_post_gui_grab_click_focus() {
+
+ Control *focus_grabber = gui.mouse_click_grabber;
+ if (!focus_grabber) {
+ // Redundant grab requests were made
+ return;
+ }
+ gui.mouse_click_grabber = NULL;
+
if (gui.mouse_focus) {
- if (gui.mouse_focus == p_control)
+ if (gui.mouse_focus == focus_grabber)
return;
Ref<InputEventMouseButton> mb;
mb.instance();
@@ -2313,9 +2327,9 @@ void Viewport::_gui_grab_click_focus(Control *p_control) {
mb->set_position(click);
mb->set_button_index(gui.mouse_focus_button);
mb->set_pressed(false);
- gui.mouse_focus->call_deferred(SceneStringNames::get_singleton()->_gui_input, mb);
+ gui.mouse_focus->call_multilevel(SceneStringNames::get_singleton()->_gui_input, mb);
- gui.mouse_focus = p_control;
+ gui.mouse_focus = focus_grabber;
gui.focus_inv_xform = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse();
click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
mb->set_position(click);
@@ -2463,6 +2477,16 @@ bool Viewport::is_3d_disabled() const {
return disable_3d;
}
+void Viewport::set_keep_3d_linear(bool p_keep_3d_linear) {
+ keep_3d_linear = p_keep_3d_linear;
+ VS::get_singleton()->viewport_set_keep_3d_linear(viewport, keep_3d_linear);
+}
+
+bool Viewport::get_keep_3d_linear() const {
+
+ return keep_3d_linear;
+}
+
Variant Viewport::gui_get_drag_data() const {
return gui.drag_data;
}
@@ -2646,8 +2670,12 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_disable_3d", "disable"), &Viewport::set_disable_3d);
ClassDB::bind_method(D_METHOD("is_3d_disabled"), &Viewport::is_3d_disabled);
+ ClassDB::bind_method(D_METHOD("set_keep_3d_linear", "keep_3d_linear"), &Viewport::set_keep_3d_linear);
+ ClassDB::bind_method(D_METHOD("get_keep_3d_linear"), &Viewport::get_keep_3d_linear);
+
ClassDB::bind_method(D_METHOD("_gui_show_tooltip"), &Viewport::_gui_show_tooltip);
ClassDB::bind_method(D_METHOD("_gui_remove_focus"), &Viewport::_gui_remove_focus);
+ ClassDB::bind_method(D_METHOD("_post_gui_grab_click_focus"), &Viewport::_post_gui_grab_click_focus);
ClassDB::bind_method(D_METHOD("set_shadow_atlas_size", "size"), &Viewport::set_shadow_atlas_size);
ClassDB::bind_method(D_METHOD("get_shadow_atlas_size"), &Viewport::get_shadow_atlas_size);
@@ -2669,6 +2697,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x"), "set_msaa", "get_msaa");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hdr"), "set_hdr", "get_hdr");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_3d_linear"), "set_keep_3d_linear", "get_keep_3d_linear");
ADD_PROPERTY(PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_ENUM, "2D,2D No-Sampling,3D,3D No-Effects"), "set_usage", "get_usage");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
ADD_GROUP("Render Target", "render_target_");
@@ -2791,6 +2820,7 @@ Viewport::Viewport() {
disable_input = false;
disable_3d = false;
+ keep_3d_linear = false;
//window tooltip
gui.tooltip_timer = -1;
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 07bbd3f1f..162a902c8 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -226,6 +226,7 @@ private:
void _update_global_transform();
bool disable_3d;
+ bool keep_3d_linear;
UpdateMode update_mode;
RID texture_rid;
uint32_t texture_flags;
@@ -248,6 +249,7 @@ private:
bool key_event_accepted;
Control *mouse_focus;
+ Control *mouse_click_grabber;
int mouse_focus_button;
Control *key_focus;
Control *mouse_over;
@@ -323,6 +325,7 @@ private:
bool _gui_control_has_focus(const Control *p_control);
void _gui_control_grab_focus(Control *p_control);
void _gui_grab_click_focus(Control *p_control);
+ void _post_gui_grab_click_focus();
void _gui_accept_event();
Control *_gui_get_focus_owner();
@@ -431,6 +434,9 @@ public:
void set_disable_3d(bool p_disable);
bool is_3d_disabled() const;
+ void set_keep_3d_linear(bool p_keep_3d_linear);
+ bool get_keep_3d_linear() const;
+
void set_attach_to_screen_rect(const Rect2 &p_rect);
Rect2 get_attach_to_screen_rect() const;
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 19d8f09eb..7533fa5f6 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -58,6 +58,7 @@
#include "scene/2d/ray_cast_2d.h"
#include "scene/2d/remote_transform_2d.h"
#include "scene/2d/screen_button.h"
+#include "scene/2d/skeleton_2d.h"
#include "scene/2d/sprite.h"
#include "scene/2d/tile_map.h"
#include "scene/2d/visibility_notifier_2d.h"
@@ -389,6 +390,7 @@ void register_scene_types() {
ClassDB::register_class<RigidBody>();
ClassDB::register_class<KinematicCollision>();
ClassDB::register_class<KinematicBody>();
+ ClassDB::register_class<PhysicalBone>();
ClassDB::register_class<VehicleBody>();
ClassDB::register_class<VehicleWheel>();
@@ -449,6 +451,8 @@ void register_scene_types() {
ClassDB::register_class<VisibilityNotifier2D>();
ClassDB::register_class<VisibilityEnabler2D>();
ClassDB::register_class<Polygon2D>();
+ ClassDB::register_class<Skeleton2D>();
+ ClassDB::register_class<Bone2D>();
ClassDB::register_class<Light2D>();
ClassDB::register_class<LightOccluder2D>();
ClassDB::register_class<OccluderPolygon2D>();
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 5fd6f6c74..4ec1e8973 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -806,6 +806,87 @@ float Curve2D::get_bake_interval() const {
return bake_interval;
}
+Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve2D");
+ ERR_FAIL_COND_V(pc == 0, Vector2());
+ }
+
+ if (pc == 1)
+ return baked_point_cache.get(0);
+
+ PoolVector2Array::Read r = baked_point_cache.read();
+
+ Vector2 nearest;
+ float nearest_dist = -1.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector2 origin = r[i];
+ Vector2 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector2 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = proj;
+ nearest_dist = dist;
+ }
+ }
+
+ return nearest;
+}
+
+float Curve2D::get_closest_offset(const Vector2 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve2D");
+ ERR_FAIL_COND_V(pc == 0, 0.0f);
+ }
+
+ if (pc == 1)
+ return 0.0f;
+
+ PoolVector2Array::Read r = baked_point_cache.read();
+
+ float nearest = 0.0f;
+ float nearest_dist = -1.0f;
+ float offset = 0.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector2 origin = r[i];
+ Vector2 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector2 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = offset + d;
+ nearest_dist = dist;
+ }
+
+ offset += bake_interval;
+ }
+
+ return nearest;
+}
+
Dictionary Curve2D::_get_data() const {
Dictionary dc;
@@ -909,6 +990,8 @@ void Curve2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve2D::get_baked_length);
ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve2D::interpolate_baked, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve2D::get_baked_points);
+ ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve2D::get_closest_point);
+ ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve2D::get_closest_offset);
ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve2D::tessellate, DEFVAL(5), DEFVAL(4));
ClassDB::bind_method(D_METHOD("_get_data"), &Curve2D::_get_data);
@@ -1276,6 +1359,87 @@ PoolRealArray Curve3D::get_baked_tilts() const {
return baked_tilt_cache;
}
+Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve3D");
+ ERR_FAIL_COND_V(pc == 0, Vector3());
+ }
+
+ if (pc == 1)
+ return baked_point_cache.get(0);
+
+ PoolVector3Array::Read r = baked_point_cache.read();
+
+ Vector3 nearest;
+ float nearest_dist = -1.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector3 origin = r[i];
+ Vector3 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector3 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = proj;
+ nearest_dist = dist;
+ }
+ }
+
+ return nearest;
+}
+
+float Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve3D");
+ ERR_FAIL_COND_V(pc == 0, 0.0f);
+ }
+
+ if (pc == 1)
+ return 0.0f;
+
+ PoolVector3Array::Read r = baked_point_cache.read();
+
+ float nearest = 0.0f;
+ float nearest_dist = -1.0f;
+ float offset = 0.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector3 origin = r[i];
+ Vector3 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector3 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = offset + d;
+ nearest_dist = dist;
+ }
+
+ offset += bake_interval;
+ }
+
+ return nearest;
+}
+
void Curve3D::set_bake_interval(float p_tolerance) {
bake_interval = p_tolerance;
@@ -1404,6 +1568,8 @@ void Curve3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve3D::interpolate_baked, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve3D::get_baked_points);
ClassDB::bind_method(D_METHOD("get_baked_tilts"), &Curve3D::get_baked_tilts);
+ ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve3D::get_closest_point);
+ ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve3D::get_closest_offset);
ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve3D::tessellate, DEFVAL(5), DEFVAL(4));
ClassDB::bind_method(D_METHOD("_get_data"), &Curve3D::_get_data);
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 4f55e4055..492eb05d1 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -199,6 +199,8 @@ public:
float get_baked_length() const;
Vector2 interpolate_baked(float p_offset, bool p_cubic = false) const;
PoolVector2Array get_baked_points() const; //useful for going through
+ Vector2 get_closest_point(const Vector2 &p_to_point) const;
+ float get_closest_offset(const Vector2 &p_to_point) const;
PoolVector2Array tessellate(int p_max_stages = 5, float p_tolerance = 4) const; //useful for display
@@ -268,6 +270,8 @@ public:
float interpolate_baked_tilt(float p_offset) const;
PoolVector3Array get_baked_points() const; //useful for going through
PoolRealArray get_baked_tilts() const; //useful for going through
+ Vector3 get_closest_point(const Vector3 &p_to_point) const;
+ float get_closest_offset(const Vector3 &p_to_point) const;
PoolVector3Array tessellate(int p_max_stages = 5, float p_tolerance = 4) const; //useful for display
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 3e244aa8f..2e652a00f 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -582,6 +582,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("checked", "PopupMenu", make_icon(checked_png));
theme->set_icon("unchecked", "PopupMenu", make_icon(unchecked_png));
+ theme->set_icon("radio_checked", "PopupMenu", make_icon(radio_checked_png));
+ theme->set_icon("radio_unchecked", "PopupMenu", make_icon(radio_unchecked_png));
theme->set_icon("submenu", "PopupMenu", make_icon(submenu_png));
theme->set_font("font", "PopupMenu", default_font);
@@ -816,6 +818,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color_selected", "RichTextLabel", font_color_selection);
theme->set_color("selection_color", "RichTextLabel", Color(0.1, 0.1, 1, 0.8));
+ theme->set_color("font_color_shadow", "RichTextLabel", Color(0, 0, 0, 0));
+
+ theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * scale);
+ theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * scale);
+ theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * scale);
+
theme->set_constant("line_separation", "RichTextLabel", 1 * scale);
theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale);
theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale);
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index a83ef198f..1282ce767 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -387,10 +387,12 @@ void SpatialMaterial::_update_shader() {
if (flags[FLAG_USE_VERTEX_LIGHTING]) {
code += ",vertex_lighting";
}
-
if (flags[FLAG_TRIPLANAR_USE_WORLD] && (flags[FLAG_UV1_USE_TRIPLANAR] || flags[FLAG_UV2_USE_TRIPLANAR])) {
code += ",world_vertex_coords";
}
+ if (flags[FLAG_DONT_RECEIVE_SHADOWS]) {
+ code += ",shadows_disabled";
+ }
code += ";\n";
code += "uniform vec4 albedo : hint_color;\n";
@@ -1849,6 +1851,7 @@ void SpatialMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_world_triplanar"), "set_flag", "get_flag", FLAG_TRIPLANAR_USE_WORLD);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_fixed_size"), "set_flag", "get_flag", FLAG_FIXED_SIZE);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_albedo_tex_force_srgb"), "set_flag", "get_flag", FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flags_do_not_receive_shadows"), "set_flag", "get_flag", FLAG_DONT_RECEIVE_SHADOWS);
ADD_GROUP("Vertex Color", "vertex_color");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_use_as_albedo"), "set_flag", "get_flag", FLAG_ALBEDO_FROM_VERTEX_COLOR);
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "vertex_color_is_srgb"), "set_flag", "get_flag", FLAG_SRGB_VERTEX_COLOR);
@@ -2038,6 +2041,7 @@ void SpatialMaterial::_bind_methods() {
BIND_ENUM_CONSTANT(FLAG_USE_ALPHA_SCISSOR);
BIND_ENUM_CONSTANT(FLAG_TRIPLANAR_USE_WORLD);
BIND_ENUM_CONSTANT(FLAG_ALBEDO_TEXTURE_FORCE_SRGB);
+ BIND_ENUM_CONSTANT(FLAG_DONT_RECEIVE_SHADOWS);
BIND_ENUM_CONSTANT(FLAG_MAX);
BIND_ENUM_CONSTANT(DIFFUSE_BURLEY);
diff --git a/scene/resources/material.h b/scene/resources/material.h
index 2c297cda4..ce733bfb8 100644
--- a/scene/resources/material.h
+++ b/scene/resources/material.h
@@ -188,6 +188,7 @@ public:
FLAG_EMISSION_ON_UV2,
FLAG_USE_ALPHA_SCISSOR,
FLAG_ALBEDO_TEXTURE_FORCE_SRGB,
+ FLAG_DONT_RECEIVE_SHADOWS,
FLAG_MAX
};
@@ -236,7 +237,7 @@ private:
uint64_t blend_mode : 2;
uint64_t depth_draw_mode : 2;
uint64_t cull_mode : 2;
- uint64_t flags : 14;
+ uint64_t flags : 15;
uint64_t detail_blend_mode : 2;
uint64_t diffuse_mode : 3;
uint64_t specular_mode : 2;
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 949ba12a4..d87644381 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -315,6 +315,8 @@ Ref<Mesh> Mesh::create_outline(float p_margin) const {
}
}
+ ERR_FAIL_COND_V(arrays.size() != ARRAY_MAX, Ref<ArrayMesh>());
+
{
PoolVector<int>::Write ir;
PoolVector<int> indices = arrays[ARRAY_INDEX];
@@ -910,6 +912,7 @@ void ArrayMesh::surface_set_material(int p_idx, const Ref<Material> &p_material)
VisualServer::get_singleton()->mesh_surface_set_material(mesh, p_idx, p_material.is_null() ? RID() : p_material->get_rid());
_change_notify("material");
+ emit_changed();
}
void ArrayMesh::surface_set_name(int p_idx, const String &p_name) {
@@ -917,6 +920,7 @@ void ArrayMesh::surface_set_name(int p_idx, const String &p_name) {
ERR_FAIL_INDEX(p_idx, surfaces.size());
surfaces[p_idx].name = p_name;
+ emit_changed();
}
String ArrayMesh::surface_get_name(int p_idx) const {
@@ -929,6 +933,7 @@ void ArrayMesh::surface_update_region(int p_surface, int p_offset, const PoolVec
ERR_FAIL_INDEX(p_surface, surfaces.size());
VS::get_singleton()->mesh_surface_update_region(mesh, p_surface, p_offset, p_data);
+ emit_changed();
}
void ArrayMesh::surface_set_custom_aabb(int p_idx, const AABB &p_aabb) {
@@ -936,6 +941,7 @@ void ArrayMesh::surface_set_custom_aabb(int p_idx, const AABB &p_aabb) {
ERR_FAIL_INDEX(p_idx, surfaces.size());
surfaces[p_idx].aabb = p_aabb;
// set custom aabb too?
+ emit_changed();
}
Ref<Material> ArrayMesh::surface_get_material(int p_idx) const {
@@ -984,6 +990,7 @@ void ArrayMesh::set_custom_aabb(const AABB &p_custom) {
custom_aabb = p_custom;
VS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb);
+ emit_changed();
}
AABB ArrayMesh::get_custom_aabb() const {
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 3df9285bb..846f6e356 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -249,6 +249,8 @@ Node *SceneState::instance(GenEditState p_edit_state) const {
//must make a copy, because this res is local to scene
}
}
+ } else if (p_edit_state == GEN_EDIT_STATE_INSTANCE) {
+ value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor
}
node->set(snames[nprops[j].name], value, &valid);
}
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 94c54c91d..ad63422aa 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -65,6 +65,8 @@ void PrimitiveMesh::_update() const {
pending_request = false;
_clear_triangle_mesh();
+
+ const_cast<PrimitiveMesh *>(this)->emit_changed();
}
void PrimitiveMesh::_request_update() {
@@ -164,7 +166,11 @@ void PrimitiveMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mesh_arrays"), &PrimitiveMesh::get_mesh_arrays);
+ ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &PrimitiveMesh::set_custom_aabb);
+ ClassDB::bind_method(D_METHOD("get_custom_aabb"), &PrimitiveMesh::get_custom_aabb);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb");
}
void PrimitiveMesh::set_material(const Ref<Material> &p_material) {
@@ -185,6 +191,18 @@ Array PrimitiveMesh::get_mesh_arrays() const {
return surface_get_arrays(0);
}
+void PrimitiveMesh::set_custom_aabb(const AABB &p_custom) {
+
+ custom_aabb = p_custom;
+ VS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb);
+ emit_changed();
+}
+
+AABB PrimitiveMesh::get_custom_aabb() const {
+
+ return custom_aabb;
+}
+
PrimitiveMesh::PrimitiveMesh() {
// defaults
mesh = VisualServer::get_singleton()->mesh_create();
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index 94a7a055a..23d1671d5 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -48,6 +48,7 @@ class PrimitiveMesh : public Mesh {
private:
RID mesh;
mutable AABB aabb;
+ AABB custom_aabb;
Ref<Material> material;
@@ -81,6 +82,9 @@ public:
Array get_mesh_arrays() const;
+ void set_custom_aabb(const AABB &p_custom);
+ AABB get_custom_aabb() const;
+
PrimitiveMesh();
~PrimitiveMesh();
};
diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp
index 030b822f3..597866eb7 100644
--- a/scene/resources/scene_format_text.cpp
+++ b/scene/resources/scene_format_text.cpp
@@ -1672,7 +1672,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
f->store_string(vars);
}
- f->store_line("]\n");
+ f->store_line("]");
for (int j = 0; j < state->get_node_property_count(i); j++) {
@@ -1682,10 +1682,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
f->store_string(_valprop(String(state->get_node_property_name(i, j))) + " = " + vars + "\n");
}
- if (state->get_node_property_count(i)) {
- //add space
- f->store_line(String());
- }
+ f->store_line(String());
}
for (int i = 0; i < state->get_connection_count(); i++) {
@@ -1708,14 +1705,12 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const RES &p_r
f->store_string(" binds= " + vars);
}
- f->store_line("]\n");
+ f->store_line("]");
}
- f->store_line(String());
-
Vector<NodePath> editable_instances = state->get_editable_instances();
for (int i = 0; i < editable_instances.size(); i++) {
- f->store_line("[editable path=\"" + editable_instances[i].operator String() + "\"]");
+ f->store_line("\n[editable path=\"" + editable_instances[i].operator String() + "\"]");
}
}
diff --git a/scene/resources/style_box.cpp b/scene/resources/style_box.cpp
index e87fd7baf..7da65ac98 100644
--- a/scene/resources/style_box.cpp
+++ b/scene/resources/style_box.cpp
@@ -106,7 +106,11 @@ void StyleBoxTexture::set_texture(Ref<Texture> p_texture) {
if (texture == p_texture)
return;
texture = p_texture;
- region_rect = Rect2(Point2(), texture->get_size());
+ if (p_texture.is_null()) {
+ region_rect = Rect2(0, 0, 0, 0);
+ } else {
+ region_rect = Rect2(Point2(), texture->get_size());
+ }
emit_signal("texture_changed");
emit_changed();
_change_notify("texture");
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 07c1036a1..5a42873d7 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -861,7 +861,7 @@ void SurfaceTool::generate_tangents() {
}
}
-void SurfaceTool::generate_normals() {
+void SurfaceTool::generate_normals(bool p_flip) {
ERR_FAIL_COND(primitive != Mesh::PRIMITIVE_TRIANGLES);
@@ -887,7 +887,11 @@ void SurfaceTool::generate_normals() {
ERR_FAIL_COND(!v[2]);
E = v[2]->next();
- Vector3 normal = Plane(v[0]->get().vertex, v[1]->get().vertex, v[2]->get().vertex).normal;
+ Vector3 normal;
+ if (!p_flip)
+ normal = Plane(v[0]->get().vertex, v[1]->get().vertex, v[2]->get().vertex).normal;
+ else
+ normal = Plane(v[2]->get().vertex, v[1]->get().vertex, v[0]->get().vertex).normal;
if (smooth) {
@@ -980,7 +984,7 @@ void SurfaceTool::_bind_methods() {
ClassDB::bind_method(D_METHOD("index"), &SurfaceTool::index);
ClassDB::bind_method(D_METHOD("deindex"), &SurfaceTool::deindex);
- ClassDB::bind_method(D_METHOD("generate_normals"), &SurfaceTool::generate_normals);
+ ClassDB::bind_method(D_METHOD("generate_normals", "flip"), &SurfaceTool::generate_normals, DEFVAL(false));
ClassDB::bind_method(D_METHOD("generate_tangents"), &SurfaceTool::generate_tangents);
ClassDB::bind_method(D_METHOD("add_to_format", "flags"), &SurfaceTool::add_to_format);
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index 7a9aa349b..459d39938 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -116,7 +116,7 @@ public:
void index();
void deindex();
- void generate_normals();
+ void generate_normals(bool p_flip = false);
void generate_tangents();
void add_to_format(int p_flags) { format |= p_flags; }
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 4463f98b0..42d64376f 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -142,6 +142,8 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) {
tile_set_navigation_polygon(id, p_value);
else if (what == "navigation_offset")
tile_set_navigation_polygon_offset(id, p_value);
+ else if (what == "z_index")
+ tile_set_z_index(id, p_value);
else
return false;
@@ -239,6 +241,8 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = tile_get_navigation_polygon(id);
else if (what == "navigation_offset")
r_ret = tile_get_navigation_polygon_offset(id);
+ else if (what == "z_index")
+ r_ret = tile_get_z_index(id);
else
return false;
@@ -278,6 +282,7 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::OBJECT, pre + "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D", PROPERTY_USAGE_EDITOR));
p_list->push_back(PropertyInfo(Variant::BOOL, pre + "shape_one_way", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
p_list->push_back(PropertyInfo(Variant::ARRAY, pre + "shapes", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR));
+ p_list->push_back(PropertyInfo(Variant::INT, pre + "z_index", PROPERTY_HINT_RANGE, itos(VS::CANVAS_ITEM_Z_MIN) + "," + itos(VS::CANVAS_ITEM_Z_MAX) + ",1"));
}
}
@@ -347,6 +352,7 @@ void TileSet::tile_set_modulate(int p_id, const Color &p_modulate) {
ERR_FAIL_COND(!tile_map.has(p_id));
tile_map[p_id].modulate = p_modulate;
emit_changed();
+ _change_notify("modulate");
}
Color TileSet::tile_get_modulate(int p_id) const {
@@ -747,6 +753,19 @@ Vector<TileSet::ShapeData> TileSet::tile_get_shapes(int p_id) const {
return tile_map[p_id].shapes_data;
}
+int TileSet::tile_get_z_index(int p_id) const {
+
+ ERR_FAIL_COND_V(!tile_map.has(p_id), 0);
+ return tile_map[p_id].z_index;
+}
+
+void TileSet::tile_set_z_index(int p_id, int p_z_index) {
+
+ ERR_FAIL_COND(!tile_map.has(p_id));
+ tile_map[p_id].z_index = p_z_index;
+ emit_changed();
+}
+
void TileSet::_tile_set_shapes(int p_id, const Array &p_shapes) {
ERR_FAIL_COND(!tile_map.has(p_id));
@@ -918,6 +937,8 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("tile_get_shape_count", "id"), &TileSet::tile_get_shape_count);
ClassDB::bind_method(D_METHOD("tile_set_shapes", "id", "shapes"), &TileSet::_tile_set_shapes);
ClassDB::bind_method(D_METHOD("tile_get_shapes", "id"), &TileSet::_tile_get_shapes);
+ ClassDB::bind_method(D_METHOD("tile_set_tile_mode", "id", "tilemode"), &TileSet::tile_set_tile_mode);
+ ClassDB::bind_method(D_METHOD("tile_get_tile_mode", "id"), &TileSet::tile_get_tile_mode);
ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon", "id", "navigation_polygon"), &TileSet::tile_set_navigation_polygon);
ClassDB::bind_method(D_METHOD("tile_get_navigation_polygon", "id"), &TileSet::tile_get_navigation_polygon);
ClassDB::bind_method(D_METHOD("tile_set_navigation_polygon_offset", "id", "navigation_polygon_offset"), &TileSet::tile_set_navigation_polygon_offset);
@@ -926,6 +947,8 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("tile_get_light_occluder", "id"), &TileSet::tile_get_light_occluder);
ClassDB::bind_method(D_METHOD("tile_set_occluder_offset", "id", "occluder_offset"), &TileSet::tile_set_occluder_offset);
ClassDB::bind_method(D_METHOD("tile_get_occluder_offset", "id"), &TileSet::tile_get_occluder_offset);
+ ClassDB::bind_method(D_METHOD("tile_set_z_index", "id", "z_index"), &TileSet::tile_set_z_index);
+ ClassDB::bind_method(D_METHOD("tile_get_z_index", "id"), &TileSet::tile_get_z_index);
ClassDB::bind_method(D_METHOD("remove_tile", "id"), &TileSet::remove_tile);
ClassDB::bind_method(D_METHOD("clear"), &TileSet::clear);
@@ -947,6 +970,10 @@ void TileSet::_bind_methods() {
BIND_ENUM_CONSTANT(BIND_BOTTOMLEFT);
BIND_ENUM_CONSTANT(BIND_BOTTOM);
BIND_ENUM_CONSTANT(BIND_BOTTOMRIGHT);
+
+ BIND_ENUM_CONSTANT(SINGLE_TILE);
+ BIND_ENUM_CONSTANT(AUTO_TILE);
+ BIND_ENUM_CONSTANT(ANIMATED_TILE);
}
TileSet::TileSet() {
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 46f34b625..d5704ac9a 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -113,11 +113,13 @@ private:
Color modulate;
TileMode tile_mode;
AutotileData autotile_data;
+ int z_index;
// Default modulate for back-compat
explicit TileData() :
tile_mode(SINGLE_TILE),
- modulate(1, 1, 1) {}
+ modulate(1, 1, 1),
+ z_index(0) {}
};
Map<int, TileData> tile_map;
@@ -220,6 +222,9 @@ public:
Ref<NavigationPolygon> autotile_get_navigation_polygon(int p_id, const Vector2 &p_coord) const;
const Map<Vector2, Ref<NavigationPolygon> > &autotile_get_navigation_map(int p_id) const;
+ void tile_set_z_index(int p_id, int p_z_index);
+ int tile_get_z_index(int p_id) const;
+
void remove_tile(int p_id);
bool has_tile(int p_id) const;
@@ -238,5 +243,6 @@ public:
VARIANT_ENUM_CAST(TileSet::AutotileBindings);
VARIANT_ENUM_CAST(TileSet::BitmaskMode);
+VARIANT_ENUM_CAST(TileSet::TileMode);
#endif // TILE_SET_H
diff --git a/servers/arvr_server.cpp b/servers/arvr_server.cpp
index f9d402fe7..f48bedbda 100644
--- a/servers/arvr_server.cpp
+++ b/servers/arvr_server.cpp
@@ -58,6 +58,8 @@ void ARVRServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_primary_interface"), &ARVRServer::get_primary_interface);
ClassDB::bind_method(D_METHOD("set_primary_interface", "interface"), &ARVRServer::set_primary_interface);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "primary_interface"), "set_primary_interface", "get_primary_interface");
+
ClassDB::bind_method(D_METHOD("get_last_process_usec"), &ARVRServer::get_last_process_usec);
ClassDB::bind_method(D_METHOD("get_last_commit_usec"), &ARVRServer::get_last_commit_usec);
ClassDB::bind_method(D_METHOD("get_last_frame_usec"), &ARVRServer::get_last_frame_usec);
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index c3b0de6d9..b08e41301 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -101,6 +101,18 @@ int AudioDriver::get_total_channels_by_speaker_mode(AudioDriver::SpeakerMode p_m
ERR_FAIL_V(2);
}
+Array AudioDriver::get_device_list() {
+ Array list;
+
+ list.push_back("Default");
+
+ return list;
+}
+
+String AudioDriver::get_device() {
+ return "Default";
+}
+
AudioDriver::AudioDriver() {
_last_mix_time = 0;
@@ -1108,6 +1120,21 @@ Ref<AudioBusLayout> AudioServer::generate_bus_layout() const {
return state;
}
+Array AudioServer::get_device_list() {
+
+ return AudioDriver::get_singleton()->get_device_list();
+}
+
+String AudioServer::get_device() {
+
+ return AudioDriver::get_singleton()->get_device();
+}
+
+void AudioServer::set_device(String device) {
+
+ AudioDriver::get_singleton()->set_device(device);
+}
+
void AudioServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_bus_count", "amount"), &AudioServer::set_bus_count);
@@ -1154,6 +1181,9 @@ void AudioServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_speaker_mode"), &AudioServer::get_speaker_mode);
ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioServer::get_mix_rate);
+ ClassDB::bind_method(D_METHOD("get_device_list"), &AudioServer::get_device_list);
+ ClassDB::bind_method(D_METHOD("get_device"), &AudioServer::get_device);
+ ClassDB::bind_method(D_METHOD("set_device"), &AudioServer::set_device);
ClassDB::bind_method(D_METHOD("set_bus_layout", "bus_layout"), &AudioServer::set_bus_layout);
ClassDB::bind_method(D_METHOD("generate_bus_layout"), &AudioServer::generate_bus_layout);
diff --git a/servers/audio_server.h b/servers/audio_server.h
index a8be48b4c..af2668b69 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -70,6 +70,9 @@ public:
virtual void start() = 0;
virtual int get_mix_rate() const = 0;
virtual SpeakerMode get_speaker_mode() const = 0;
+ virtual Array get_device_list();
+ virtual String get_device();
+ virtual void set_device(String device) {}
virtual void lock() = 0;
virtual void unlock() = 0;
virtual void finish() = 0;
@@ -300,6 +303,10 @@ public:
void set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout);
Ref<AudioBusLayout> generate_bus_layout() const;
+ Array get_device_list();
+ String get_device();
+ void set_device(String device);
+
AudioServer();
virtual ~AudioServer();
};
diff --git a/servers/physics/body_pair_sw.cpp b/servers/physics/body_pair_sw.cpp
index 882d201f6..2a6a9e08a 100644
--- a/servers/physics/body_pair_sw.cpp
+++ b/servers/physics/body_pair_sw.cpp
@@ -78,6 +78,7 @@ void BodyPairSW::contact_added_callback(const Vector3 &p_point_A, const Vector3
contact.local_A = local_A;
contact.local_B = local_B;
contact.normal = (p_point_A - p_point_B).normalized();
+ contact.mass_normal = 0; // will be computed in setup()
// attempt to determine if the contact will be reused
real_t contact_recycle_radius = space->get_contact_recycle_radius();
diff --git a/servers/physics/collision_object_sw.h b/servers/physics/collision_object_sw.h
index f5d32e56a..dee28bb6d 100644
--- a/servers/physics/collision_object_sw.h
+++ b/servers/physics/collision_object_sw.h
@@ -37,7 +37,8 @@
#include "shape_sw.h"
#ifdef DEBUG_ENABLED
-#define MAX_OBJECT_DISTANCE 10000000.0
+#define MAX_OBJECT_DISTANCE 3.1622776601683791e+18
+
#define MAX_OBJECT_DISTANCE_X2 (MAX_OBJECT_DISTANCE * MAX_OBJECT_DISTANCE)
#endif
diff --git a/servers/physics_2d/body_pair_2d_sw.cpp b/servers/physics_2d/body_pair_2d_sw.cpp
index f51882b5e..61c0e0063 100644
--- a/servers/physics_2d/body_pair_2d_sw.cpp
+++ b/servers/physics_2d/body_pair_2d_sw.cpp
@@ -62,6 +62,7 @@ void BodyPair2DSW::_contact_added_callback(const Vector2 &p_point_A, const Vecto
contact.local_B = local_B;
contact.reused = true;
contact.normal = (p_point_A - p_point_B).normalized();
+ contact.mass_normal = 0; // will be computed in setup()
// attempt to determine if the contact will be reused
diff --git a/servers/physics_2d/collision_object_2d_sw.cpp b/servers/physics_2d/collision_object_2d_sw.cpp
index ce06aa9a2..23084a424 100644
--- a/servers/physics_2d/collision_object_2d_sw.cpp
+++ b/servers/physics_2d/collision_object_2d_sw.cpp
@@ -73,6 +73,27 @@ void CollisionObject2DSW::set_shape_transform(int p_index, const Transform2D &p_
_shapes_changed();
}
+void CollisionObject2DSW::set_shape_as_disabled(int p_idx, bool p_disabled) {
+ ERR_FAIL_INDEX(p_idx, shapes.size());
+
+ CollisionObject2DSW::Shape &shape = shapes[p_idx];
+ if (shape.disabled == p_disabled)
+ return;
+
+ shape.disabled = p_disabled;
+
+ if (!space)
+ return;
+
+ if (p_disabled && shape.bpid != 0) {
+ space->get_broadphase()->remove(shape.bpid);
+ shape.bpid = 0;
+ _update_shapes();
+ } else if (!p_disabled && shape.bpid == 0) {
+ _update_shapes(); // automatically adds shape with bpid == 0
+ }
+}
+
void CollisionObject2DSW::remove_shape(Shape2DSW *p_shape) {
//remove a shape, all the times it appears
@@ -139,6 +160,10 @@ void CollisionObject2DSW::_update_shapes() {
for (int i = 0; i < shapes.size(); i++) {
Shape &s = shapes[i];
+
+ if (s.disabled)
+ continue;
+
if (s.bpid == 0) {
s.bpid = space->get_broadphase()->create(this, i);
space->get_broadphase()->set_static(s.bpid, _static);
@@ -163,6 +188,9 @@ void CollisionObject2DSW::_update_shapes_with_motion(const Vector2 &p_motion) {
for (int i = 0; i < shapes.size(); i++) {
Shape &s = shapes[i];
+ if (s.disabled)
+ continue;
+
if (s.bpid == 0) {
s.bpid = space->get_broadphase()->create(this, i);
space->get_broadphase()->set_static(s.bpid, _static);
diff --git a/servers/physics_2d/collision_object_2d_sw.h b/servers/physics_2d/collision_object_2d_sw.h
index 5f25c2715..ab3e219ac 100644
--- a/servers/physics_2d/collision_object_2d_sw.h
+++ b/servers/physics_2d/collision_object_2d_sw.h
@@ -136,10 +136,7 @@ public:
_FORCE_INLINE_ Transform2D get_inv_transform() const { return inv_transform; }
_FORCE_INLINE_ Space2DSW *get_space() const { return space; }
- _FORCE_INLINE_ void set_shape_as_disabled(int p_idx, bool p_disabled) {
- ERR_FAIL_INDEX(p_idx, shapes.size());
- shapes[p_idx].disabled = p_disabled;
- }
+ void set_shape_as_disabled(int p_idx, bool p_disabled);
_FORCE_INLINE_ bool is_shape_set_as_disabled(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, shapes.size(), false);
return shapes[p_idx].disabled;
diff --git a/servers/physics_2d/shape_2d_sw.cpp b/servers/physics_2d/shape_2d_sw.cpp
index 433942708..2b0eab599 100644
--- a/servers/physics_2d/shape_2d_sw.cpp
+++ b/servers/physics_2d/shape_2d_sw.cpp
@@ -1011,6 +1011,10 @@ void ConcavePolygonShape2DSW::cull(const Rect2 &p_local_aabb, Callback p_callbac
stack[i]=0;
*/
+ if (segments.size() == 0 || points.size() == 0 || bvh.size() == 0) {
+ return;
+ }
+
int level = 0;
const Segment *segmentptr = &segments[0];
diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp
index c29093d1a..503ca0af4 100644
--- a/servers/physics_2d/space_2d_sw.cpp
+++ b/servers/physics_2d/space_2d_sw.cpp
@@ -33,6 +33,7 @@
#include "collision_solver_2d_sw.h"
#include "pair.h"
#include "physics_2d_server_sw.h"
+
_FORCE_INLINE_ static bool _can_collide_with(CollisionObject2DSW *p_object, uint32_t p_collision_mask) {
return p_object->get_collision_layer() & p_collision_mask;
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
index 8bf112b0d..8d8e9e693 100644
--- a/servers/visual/rasterizer.h
+++ b/servers/visual/rasterizer.h
@@ -175,6 +175,7 @@ public:
virtual RID texture_create() = 0;
virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = VS::TEXTURE_FLAGS_DEFAULT) = 0;
virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) = 0;
+ virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) = 0;
virtual Ref<Image> texture_get_data(RID p_texture, VS::CubeMapSide p_cube_side = VS::CUBEMAP_LEFT) const = 0;
virtual void texture_set_flags(RID p_texture, uint32_t p_flags) = 0;
virtual uint32_t texture_get_flags(RID p_texture) const = 0;
@@ -325,6 +326,7 @@ public:
virtual Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const = 0;
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0;
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0;
+ virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
/* Light API */
@@ -513,6 +515,7 @@ public:
RENDER_TARGET_NO_3D,
RENDER_TARGET_NO_SAMPLING,
RENDER_TARGET_HDR,
+ RENDER_TARGET_KEEP_3D_LINEAR,
RENDER_TARGET_FLAG_MAX
};
@@ -735,6 +738,8 @@ public:
Vector<Point2> points;
Vector<Point2> uvs;
Vector<Color> colors;
+ Vector<int> bones;
+ Vector<float> weights;
RID texture;
RID normal_map;
int count;
@@ -812,6 +817,8 @@ public:
mutable bool rect_dirty;
mutable Rect2 rect;
RID material;
+ RID skeleton;
+
Item *next;
struct CopyBackBuffer {
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
index d5a9bfb60..d399c548f 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -1492,11 +1492,6 @@ const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
{ "abs", TYPE_IVEC3, { TYPE_IVEC3, TYPE_VOID } },
{ "abs", TYPE_IVEC4, { TYPE_IVEC4, TYPE_VOID } },
- { "abs", TYPE_UINT, { TYPE_UINT, TYPE_VOID } },
- { "abs", TYPE_UVEC2, { TYPE_UVEC2, TYPE_VOID } },
- { "abs", TYPE_UVEC3, { TYPE_UVEC3, TYPE_VOID } },
- { "abs", TYPE_UVEC4, { TYPE_UVEC4, TYPE_VOID } },
-
{ "sign", TYPE_FLOAT, { TYPE_FLOAT, TYPE_VOID } },
{ "sign", TYPE_VEC2, { TYPE_VEC2, TYPE_VOID } },
{ "sign", TYPE_VEC3, { TYPE_VEC3, TYPE_VOID } },
@@ -2212,6 +2207,37 @@ ShaderLanguage::DataType ShaderLanguage::get_scalar_type(DataType p_type) {
return scalar_types[p_type];
}
+int ShaderLanguage::get_cardinality(DataType p_type) {
+ static const int cardinality_table[] = {
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 1,
+ 2,
+ 3,
+ 4,
+ 1,
+ 2,
+ 3,
+ 4,
+ 1,
+ 2,
+ 3,
+ 4,
+ 2,
+ 3,
+ 4,
+ 1,
+ 1,
+ 1,
+ 1,
+ };
+
+ return cardinality_table[p_type];
+}
+
bool ShaderLanguage::_get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier) {
identifier = StringName();
@@ -2682,7 +2708,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return NULL;
}
- bool index_valid = false;
DataType member_type = TYPE_VOID;
switch (expr->get_datatype()) {
@@ -2701,7 +2726,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
_set_error("Only integer constants are allowed as index at the moment");
return NULL;
}
- index_valid = true;
+
switch (expr->get_datatype()) {
case TYPE_BVEC2: member_type = TYPE_BOOL; break;
case TYPE_VEC2: member_type = TYPE_FLOAT; break;
@@ -2726,7 +2751,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
_set_error("Only integer constants are allowed as index at the moment");
return NULL;
}
- index_valid = true;
+
switch (expr->get_datatype()) {
case TYPE_BVEC3: member_type = TYPE_BOOL; break;
case TYPE_VEC3: member_type = TYPE_FLOAT; break;
@@ -2750,7 +2775,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
_set_error("Only integer constants are allowed as index at the moment");
return NULL;
}
- index_valid = true;
+
switch (expr->get_datatype()) {
case TYPE_BVEC4: member_type = TYPE_BOOL; break;
case TYPE_VEC4: member_type = TYPE_FLOAT; break;
@@ -2765,11 +2790,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
}
- if (!index_valid) {
- _set_error("Invalid index");
- return NULL;
- }
-
OperatorNode *op = alloc_node<OperatorNode>();
op->op = OP_INDEX;
op->return_cache = member_type;
@@ -2971,7 +2991,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expr_pos++;
if (expr_pos == expression.size()) {
//can happen..
- _set_error("Unexpected end of expression..");
+ _set_error("Unexpected end of expression...");
return NULL;
}
}
@@ -3008,12 +3028,12 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (is_ternary) {
if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
if (next_op + 2 >= expression.size() || !expression[next_op + 2].is_op || expression[next_op + 2].op != OP_SELECT_ELSE) {
- _set_error("Mising matching ':' for select operator");
+ _set_error("Missing matching ':' for select operator");
return NULL;
}
@@ -3044,7 +3064,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else {
if (next_op < 1 || next_op >= (expression.size() - 1)) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -3053,7 +3073,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (expression[next_op - 1].is_op) {
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
ERR_FAIL_V(NULL);
}
@@ -3069,7 +3089,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
// can be followed by a unary op in a valid combination,
// due to how precedence works, unaries will always disappear first
- _set_error("Parser bug..");
+ _set_error("Parser bug...");
}
op->arguments.push_back(expression[next_op - 1].node); //expression goes as left
@@ -3122,9 +3142,18 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
if (get_scalar_type(cn->datatype) == base) {
- for (int j = 0; j < cn->values.size(); j++) {
- values.push_back(cn->values[j]);
- }
+ int cardinality = get_cardinality(op->arguments[i]->get_datatype());
+ if (cn->values.size() == cardinality) {
+
+ for (int j = 0; j < cn->values.size(); j++) {
+ values.push_back(cn->values[j]);
+ }
+ } else if (cn->values.size() == 1) {
+
+ for (int j = 0; j < cardinality; j++) {
+ values.push_back(cn->values[0]);
+ }
+ } // else: should be filtered by the parser as it's an invalid constructor
} else if (get_scalar_type(cn->datatype) == cn->datatype) {
ConstantNode::Value v;
@@ -3218,7 +3247,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
precision = get_token_precision(tk.type);
tk = _get_token();
if (!is_token_nonvoid_datatype(tk.type)) {
- _set_error("Expected datatype after precission");
+ _set_error("Expected datatype after precision");
return ERR_PARSE_ERROR;
}
}
@@ -3516,7 +3545,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
if (!p_can_break) {
//all is good
- _set_error("Contiuning is not allowed here");
+ _set_error("Continuing is not allowed here");
}
ControlFlowNode *flow = alloc_node<ControlFlowNode>();
@@ -3667,7 +3696,8 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
_set_error("void datatype not allowed here");
return ERR_PARSE_ERROR;
}
- if (!uniform && type < TYPE_FLOAT && type > TYPE_VEC4) { // FIXME: always false! should it be || instead?
+
+ if (!uniform && (type < TYPE_FLOAT || type > TYPE_VEC4)) {
_set_error("Invalid type for varying, only float,vec2,vec3,vec4 allowed.");
return ERR_PARSE_ERROR;
}
diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h
index e8cdf1f89..720511e18 100644
--- a/servers/visual/shader_language.h
+++ b/servers/visual/shader_language.h
@@ -425,6 +425,7 @@ public:
FunctionNode() {
type = TYPE_FUNCTION;
+ return_type = TYPE_VOID;
return_precision = PRECISION_DEFAULT;
can_discard = false;
}
@@ -532,6 +533,7 @@ public:
static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value = NULL);
static DataType get_scalar_type(DataType p_type);
+ static int get_cardinality(DataType p_type);
static bool is_scalar_type(DataType p_type);
static bool is_sampler_type(DataType p_type);
diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp
index 904264933..95193f7a8 100644
--- a/servers/visual/shader_types.cpp
+++ b/servers/visual/shader_types.cpp
@@ -173,6 +173,8 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_SPATIAL].modes.insert("skip_vertex_transform");
shader_modes[VS::SHADER_SPATIAL].modes.insert("world_vertex_coords");
+ shader_modes[VS::SHADER_SPATIAL].modes.insert("shadows_disabled");
+
shader_modes[VS::SHADER_SPATIAL].modes.insert("vertex_lighting");
/************ CANVAS ITEM **************************/
@@ -207,7 +209,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_CANVAS_ITEM].functions["fragment"].built_ins["SCREEN_TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["fragment"].can_discard = true;
- shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["POSITION"] = constt(ShaderLanguage::TYPE_VEC2);
+ shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["NORMAL"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["UV"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["COLOR"] = constt(ShaderLanguage::TYPE_VEC4);
@@ -218,9 +220,8 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT_HEIGHT"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT_COLOR"] = ShaderLanguage::TYPE_VEC4;
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT_UV"] = ShaderLanguage::TYPE_VEC2;
- shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT_SHADOW"] = ShaderLanguage::TYPE_VEC4;
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["LIGHT"] = ShaderLanguage::TYPE_VEC4;
- shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["SHADOW"] = ShaderLanguage::TYPE_VEC4;
+ shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["SHADOW_COLOR"] = ShaderLanguage::TYPE_VEC4;
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[VS::SHADER_CANVAS_ITEM].functions["light"].can_discard = true;
@@ -232,6 +233,7 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_sub");
shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_mul");
shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_premul_alpha");
+ shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("blend_disabled");
shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("unshaded");
shader_modes[VS::SHADER_CANVAS_ITEM].modes.insert("light_only");
@@ -254,7 +256,6 @@ ShaderTypes::ShaderTypes() {
shader_modes[VS::SHADER_PARTICLES].functions["vertex"].built_ins["RANDOM_SEED"] = constt(ShaderLanguage::TYPE_UINT);
shader_modes[VS::SHADER_PARTICLES].functions["vertex"].can_discard = false;
- shader_modes[VS::SHADER_PARTICLES].modes.insert("billboard");
shader_modes[VS::SHADER_PARTICLES].modes.insert("disable_force");
shader_modes[VS::SHADER_PARTICLES].modes.insert("disable_velocity");
shader_modes[VS::SHADER_PARTICLES].modes.insert("keep_data");
diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp
index dd8d07f00..6439ba850 100644
--- a/servers/visual/visual_server_canvas.cpp
+++ b/servers/visual/visual_server_canvas.cpp
@@ -694,7 +694,7 @@ void VisualServerCanvas::canvas_item_add_polygon(RID p_item, const Vector<Point2
canvas_item->commands.push_back(polygon);
}
-void VisualServerCanvas::canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, int p_count, RID p_normal_map) {
+void VisualServerCanvas::canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, const Vector<int> &p_bones, const Vector<float> &p_weights, RID p_texture, int p_count, RID p_normal_map) {
Item *canvas_item = canvas_item_owner.getornull(p_item);
ERR_FAIL_COND(!canvas_item);
@@ -702,6 +702,8 @@ void VisualServerCanvas::canvas_item_add_triangle_array(RID p_item, const Vector
int ps = p_points.size();
ERR_FAIL_COND(!p_colors.empty() && p_colors.size() != ps && p_colors.size() != 1);
ERR_FAIL_COND(!p_uvs.empty() && p_uvs.size() != ps);
+ ERR_FAIL_COND(!p_bones.empty() && p_bones.size() != ps * 4);
+ ERR_FAIL_COND(!p_weights.empty() && p_weights.size() != ps * 4);
Vector<int> indices = p_indices;
@@ -726,6 +728,8 @@ void VisualServerCanvas::canvas_item_add_triangle_array(RID p_item, const Vector
polygon->points = p_points;
polygon->uvs = p_uvs;
polygon->colors = p_colors;
+ polygon->bones = p_bones;
+ polygon->weights = p_weights;
polygon->indices = indices;
polygon->count = count;
polygon->antialiased = false;
diff --git a/servers/visual/visual_server_canvas.h b/servers/visual/visual_server_canvas.h
index 6a5a7623b..4d9398a17 100644
--- a/servers/visual/visual_server_canvas.h
+++ b/servers/visual/visual_server_canvas.h
@@ -51,8 +51,6 @@ public:
Vector<Item *> child_items;
- RID skeleton;
-
Item() {
children_order_dirty = true;
E = NULL;
@@ -183,7 +181,7 @@ public:
void canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, VS::NinePatchAxisMode p_x_axis_mode = VS::NINE_PATCH_STRETCH, VS::NinePatchAxisMode p_y_axis_mode = VS::NINE_PATCH_STRETCH, bool p_draw_center = true, const Color &p_modulate = Color(1, 1, 1), RID p_normal_map = RID());
void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0, RID p_normal_map = RID());
void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), RID p_normal_map = RID(), bool p_antialiased = false);
- void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID());
+ void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID());
void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID());
void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID());
void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal, int p_h_frames, int p_v_frames);
diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h
index a94e110b5..8f19de9f8 100644
--- a/servers/visual/visual_server_raster.h
+++ b/servers/visual/visual_server_raster.h
@@ -148,6 +148,7 @@ public:
BIND0R(RID, texture_create)
BIND5(texture_allocate, RID, int, int, Image::Format, uint32_t)
BIND3(texture_set_data, RID, const Ref<Image> &, CubeMapSide)
+ BIND10(texture_set_data_partial, RID, const Ref<Image> &, int, int, int, int, int, int, int, CubeMapSide)
BIND2RC(Ref<Image>, texture_get_data, RID, CubeMapSide)
BIND2(texture_set_flags, RID, uint32_t)
BIND1RC(uint32_t, texture_get_flags, RID)
@@ -285,6 +286,7 @@ public:
BIND2RC(Transform, skeleton_bone_get_transform, RID, int)
BIND3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &)
BIND2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int)
+ BIND2(skeleton_set_base_transform_2d, RID, const Transform2D &)
/* Light API */
@@ -450,6 +452,7 @@ public:
BIND2(viewport_set_hide_canvas, RID, bool)
BIND2(viewport_set_disable_environment, RID, bool)
BIND2(viewport_set_disable_3d, RID, bool)
+ BIND2(viewport_set_keep_3d_linear, RID, bool)
BIND2(viewport_attach_camera, RID, RID)
BIND2(viewport_set_scenario, RID, RID)
@@ -580,7 +583,7 @@ public:
BIND11(canvas_item_add_nine_patch, RID, const Rect2 &, const Rect2 &, RID, const Vector2 &, const Vector2 &, NinePatchAxisMode, NinePatchAxisMode, bool, const Color &, RID)
BIND7(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float, RID)
BIND7(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, RID, bool)
- BIND8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID)
+ BIND10(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, const Vector<int> &, const Vector<float> &, RID, int, RID)
BIND4(canvas_item_add_mesh, RID, const RID &, RID, RID)
BIND4(canvas_item_add_multimesh, RID, RID, RID, RID)
BIND6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
diff --git a/servers/visual/visual_server_viewport.cpp b/servers/visual/visual_server_viewport.cpp
index 83e05f6f2..dcc270ca5 100644
--- a/servers/visual/visual_server_viewport.cpp
+++ b/servers/visual/visual_server_viewport.cpp
@@ -178,11 +178,14 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E
VSG::rasterizer->restore_render_target();
if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().layer > scenario_canvas_max_layer) {
+ Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface();
- if (can_draw_3d) {
- VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
- } else {
+ if (!can_draw_3d) {
VSG::scene->render_empty_scene(p_viewport->scenario, p_viewport->shadow_atlas);
+ } else if (p_viewport->use_arvr && arvr_interface.is_valid()) {
+ VSG::scene->render_camera(arvr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
+ } else {
+ VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
}
scenario_draw_canvas_bg = false;
}
@@ -210,11 +213,14 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E
i++;
if (scenario_draw_canvas_bg && E->key().layer >= scenario_canvas_max_layer) {
+ Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface();
- if (can_draw_3d) {
- VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
- } else {
+ if (!can_draw_3d) {
VSG::scene->render_empty_scene(p_viewport->scenario, p_viewport->shadow_atlas);
+ } else if (p_viewport->use_arvr && arvr_interface.is_valid()) {
+ VSG::scene->render_camera(arvr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
+ } else {
+ VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
}
scenario_draw_canvas_bg = false;
@@ -222,11 +228,14 @@ void VisualServerViewport::_draw_viewport(Viewport *p_viewport, ARVRInterface::E
}
if (scenario_draw_canvas_bg) {
+ Ref<ARVRInterface> arvr_interface = ARVRServer::get_singleton()->get_primary_interface();
- if (can_draw_3d) {
- VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
- } else {
+ if (!can_draw_3d) {
VSG::scene->render_empty_scene(p_viewport->scenario, p_viewport->shadow_atlas);
+ } else if (p_viewport->use_arvr && arvr_interface.is_valid()) {
+ VSG::scene->render_camera(arvr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
+ } else {
+ VSG::scene->render_camera(p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas);
}
scenario_draw_canvas_bg = false;
@@ -453,6 +462,15 @@ void VisualServerViewport::viewport_set_disable_3d(RID p_viewport, bool p_disabl
//this should be just for disabling rendering of 3D, to actually disable it, set usage
}
+void VisualServerViewport::viewport_set_keep_3d_linear(RID p_viewport, bool p_keep_3d_linear) {
+
+ Viewport *viewport = viewport_owner.getornull(p_viewport);
+ ERR_FAIL_COND(!viewport);
+
+ viewport->keep_3d_linear = p_keep_3d_linear;
+ VSG::storage->render_target_set_flag(viewport->render_target, RasterizerStorage::RENDER_TARGET_KEEP_3D_LINEAR, p_keep_3d_linear);
+}
+
void VisualServerViewport::viewport_attach_camera(RID p_viewport, RID p_camera) {
Viewport *viewport = viewport_owner.getornull(p_viewport);
diff --git a/servers/visual/visual_server_viewport.h b/servers/visual/visual_server_viewport.h
index 1d28cf22a..c0c83c045 100644
--- a/servers/visual/visual_server_viewport.h
+++ b/servers/visual/visual_server_viewport.h
@@ -64,6 +64,7 @@ public:
bool disable_environment;
bool disable_3d;
bool disable_3d_by_usage;
+ bool keep_3d_linear;
RID shadow_atlas;
int shadow_atlas_size;
@@ -110,6 +111,7 @@ public:
shadow_atlas_size = 0;
disable_3d = false;
disable_3d_by_usage = false;
+ keep_3d_linear = false;
debug_draw = VS::VIEWPORT_DEBUG_DRAW_DISABLED;
for (int i = 0; i < VS::VIEWPORT_RENDER_INFO_MAX; i++) {
render_info[i] = 0;
@@ -164,6 +166,7 @@ public:
void viewport_set_hide_canvas(RID p_viewport, bool p_hide);
void viewport_set_disable_environment(RID p_viewport, bool p_disable);
void viewport_set_disable_3d(RID p_viewport, bool p_disable);
+ void viewport_set_keep_3d_linear(RID p_viewport, bool p_keep_3d_linear);
void viewport_attach_camera(RID p_viewport, RID p_camera);
void viewport_set_scenario(RID p_viewport, RID p_scenario);
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
index b91f80d13..19bb58f3a 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -84,6 +84,7 @@ public:
FUNCRID(texture)
FUNC5(texture_allocate, RID, int, int, Image::Format, uint32_t)
FUNC3(texture_set_data, RID, const Ref<Image> &, CubeMapSide)
+ FUNC10(texture_set_data_partial, RID, const Ref<Image> &, int, int, int, int, int, int, int, CubeMapSide)
FUNC2RC(Ref<Image>, texture_get_data, RID, CubeMapSide)
FUNC2(texture_set_flags, RID, uint32_t)
FUNC1RC(uint32_t, texture_get_flags, RID)
@@ -221,6 +222,7 @@ public:
FUNC2RC(Transform, skeleton_bone_get_transform, RID, int)
FUNC3(skeleton_bone_set_transform_2d, RID, int, const Transform2D &)
FUNC2RC(Transform2D, skeleton_bone_get_transform_2d, RID, int)
+ FUNC2(skeleton_set_base_transform_2d, RID, const Transform2D &)
/* Light API */
@@ -377,6 +379,7 @@ public:
FUNC2(viewport_set_hide_canvas, RID, bool)
FUNC2(viewport_set_disable_environment, RID, bool)
FUNC2(viewport_set_disable_3d, RID, bool)
+ FUNC2(viewport_set_keep_3d_linear, RID, bool)
FUNC2(viewport_attach_camera, RID, RID)
FUNC2(viewport_set_scenario, RID, RID)
@@ -498,7 +501,7 @@ public:
FUNC11(canvas_item_add_nine_patch, RID, const Rect2 &, const Rect2 &, RID, const Vector2 &, const Vector2 &, NinePatchAxisMode, NinePatchAxisMode, bool, const Color &, RID)
FUNC7(canvas_item_add_primitive, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, float, RID)
FUNC7(canvas_item_add_polygon, RID, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, RID, bool)
- FUNC8(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, RID, int, RID)
+ FUNC10(canvas_item_add_triangle_array, RID, const Vector<int> &, const Vector<Point2> &, const Vector<Color> &, const Vector<Point2> &, const Vector<int> &, const Vector<float> &, RID, int, RID)
FUNC4(canvas_item_add_mesh, RID, const RID &, RID, RID)
FUNC4(canvas_item_add_multimesh, RID, RID, RID, RID)
FUNC6(canvas_item_add_particles, RID, RID, RID, RID, int, int)
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
index f8640720c..f8b34f7df 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -214,9 +214,9 @@ RID VisualServer::_make_test_cube() {
for (int k = 0; k < 3; k++) {
if (i < 3)
- face_points[j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[j][(i + k) % 3] = v[k];
else
- face_points[3 - j][(i + k) % 3] = v[k] * (i >= 3 ? -1 : 1);
+ face_points[3 - j][(i + k) % 3] = -v[k];
}
normal_points[j] = Vector3();
normal_points[j][i % 3] = (i >= 3 ? -1 : 1);
@@ -1528,6 +1528,7 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("texture_create_from_image", "image", "flags"), &VisualServer::texture_create_from_image, DEFVAL(TEXTURE_FLAGS_DEFAULT));
ClassDB::bind_method(D_METHOD("texture_allocate", "texture", "width", "height", "format", "flags"), &VisualServer::texture_allocate, DEFVAL(TEXTURE_FLAGS_DEFAULT));
ClassDB::bind_method(D_METHOD("texture_set_data", "texture", "image", "cube_side"), &VisualServer::texture_set_data, DEFVAL(CUBEMAP_LEFT));
+ ClassDB::bind_method(D_METHOD("texture_set_data_partial", "texture", "image", "src_x", "src_y", "src_w", "src_h", "dst_x", "dst_y", "dst_mip", "cube_side"), &VisualServer::texture_set_data_partial, DEFVAL(CUBEMAP_LEFT));
ClassDB::bind_method(D_METHOD("texture_get_data", "texture", "cube_side"), &VisualServer::texture_get_data, DEFVAL(CUBEMAP_LEFT));
ClassDB::bind_method(D_METHOD("texture_set_flags", "texture", "flags"), &VisualServer::texture_set_flags);
ClassDB::bind_method(D_METHOD("texture_get_flags", "texture"), &VisualServer::texture_get_flags);
diff --git a/servers/visual_server.h b/servers/visual_server.h
index 537588fd6..65d0f07a4 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -109,6 +109,7 @@ public:
RID texture_create_from_image(const Ref<Image> &p_image, uint32_t p_flags = TEXTURE_FLAGS_DEFAULT); // helper
virtual void texture_allocate(RID p_texture, int p_width, int p_height, Image::Format p_format, uint32_t p_flags = TEXTURE_FLAGS_DEFAULT) = 0;
virtual void texture_set_data(RID p_texture, const Ref<Image> &p_image, CubeMapSide p_cube_side = CUBEMAP_LEFT) = 0;
+ virtual void texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, CubeMapSide p_cube_side = CUBEMAP_LEFT) = 0;
virtual Ref<Image> texture_get_data(RID p_texture, CubeMapSide p_cube_side = CUBEMAP_LEFT) const = 0;
virtual void texture_set_flags(RID p_texture, uint32_t p_flags) = 0;
virtual uint32_t texture_get_flags(RID p_texture) const = 0;
@@ -351,6 +352,7 @@ public:
virtual Transform skeleton_bone_get_transform(RID p_skeleton, int p_bone) const = 0;
virtual void skeleton_bone_set_transform_2d(RID p_skeleton, int p_bone, const Transform2D &p_transform) = 0;
virtual Transform2D skeleton_bone_get_transform_2d(RID p_skeleton, int p_bone) const = 0;
+ virtual void skeleton_set_base_transform_2d(RID p_skeleton, const Transform2D &p_base_transform) = 0;
/* Light API */
@@ -591,6 +593,7 @@ public:
virtual void viewport_set_hide_canvas(RID p_viewport, bool p_hide) = 0;
virtual void viewport_set_disable_environment(RID p_viewport, bool p_disable) = 0;
virtual void viewport_set_disable_3d(RID p_viewport, bool p_disable) = 0;
+ virtual void viewport_set_keep_3d_linear(RID p_viewport, bool p_disable) = 0;
virtual void viewport_attach_camera(RID p_viewport, RID p_camera) = 0;
virtual void viewport_set_scenario(RID p_viewport, RID p_scenario) = 0;
@@ -845,7 +848,7 @@ public:
virtual void canvas_item_add_nine_patch(RID p_item, const Rect2 &p_rect, const Rect2 &p_source, RID p_texture, const Vector2 &p_topleft, const Vector2 &p_bottomright, NinePatchAxisMode p_x_axis_mode = NINE_PATCH_STRETCH, NinePatchAxisMode p_y_axis_mode = NINE_PATCH_STRETCH, bool p_draw_center = true, const Color &p_modulate = Color(1, 1, 1), RID p_normal_map = RID()) = 0;
virtual void canvas_item_add_primitive(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, RID p_texture, float p_width = 1.0, RID p_normal_map = RID()) = 0;
virtual void canvas_item_add_polygon(RID p_item, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), RID p_normal_map = RID(), bool p_antialiased = false) = 0;
- virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID()) = 0;
+ virtual void canvas_item_add_triangle_array(RID p_item, const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>(), RID p_texture = RID(), int p_count = -1, RID p_normal_map = RID()) = 0;
virtual void canvas_item_add_mesh(RID p_item, const RID &p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0;
virtual void canvas_item_add_multimesh(RID p_item, RID p_mesh, RID p_texture = RID(), RID p_normal_map = RID()) = 0;
virtual void canvas_item_add_particles(RID p_item, RID p_particles, RID p_texture, RID p_normal_map, int p_h_frames, int p_v_frames) = 0;
diff --git a/thirdparty/README.md b/thirdparty/README.md
index d3fa0e466..06084641c 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -89,7 +89,7 @@ Use UI font variant if available, because it has tight vertical metrics and good
### Hack Regular
- Upstream: https://github.com/source-foundry/Hack
-- Version: 3.000
+- Version: 3.003
- License: MIT + Bitstream Vera License
### DroidSans*.ttf
@@ -115,7 +115,7 @@ Files extracted from upstream source:
## glad
- Upstream: https://github.com/Dav1dde/glad
-- Version: 0.1.16a0
+- Version: 0.1.20a0
- License: MIT
The files we package are automatically generated.
@@ -234,31 +234,35 @@ changes are marked with `// -- GODOT --` comments.
## libwebsockets
- Upstream: https://github.com/warmcat/libwebsockets
-- Version: 2.4.1
+- Version: 2.4.2
- License: LGPLv2.1 + static linking exception
File extracted from upstream source:
- Everything in `lib/` except `minilex.c`, `http2/`, `event-libs/`.
- From `misc/` exclude `lws-genhash.c`, `lws-ring.c`, `romfs.{c,h}`, `smtp.c`.
- From `plat/` exclude `lws-plat-{esp*,optee}.c`.
- - From `server/` exclude `access-log.c`, `cgi.c`, `daemonize.c`, `lws-spa.c`,
+ - From `server/` exclude `access-log.c`, `cgi.c`, `daemonize.c`, `lws-spa.c`,
`peer-limits.c`, `rewrite.c`
- Also copy `win32helpers/` from `win32port/`
- `mbedtls_wrapper/include/platform/ssl_port.h` has a small change to check for OSX and FreeBSD (missing `malloc.h`).
The bug is fixed in upstream master via `LWS_HAVE_MALLOC_H`, but not in the 2.4.1 branch (as the file structure has changed).
+- You might need to apply the patch in `thirdparty/lws/mbedtls_verify.diff` (port of PR 1215) to future `2.4.x` releases if it does not get cherry picked.
-Important: `lws_config.h` and `lws_config_private.h` contains custom
+Important: `lws_config.h` and `lws_config_private.h` contains custom
Godot build configurations, check them out when updating.
## mbedTLS
- Upstream: https://tls.mbed.org/
-- Version: 2.7.0
+- Version: 2.8.0
- License: Apache 2.0
-File extracted from upstream release tarball `mbedtls-2.7.0-apache.tgz`:
-- All `*.h` from `include/mbedtls/` to `thirdparty/include/mbedtls/`
-- All `*.c` from `library/` to `thirdparty/library/`
+File extracted from upstream release tarball `mbedtls-2.8.0-apache.tgz`:
+- All `*.h` from `include/mbedtls/` to `thirdparty/mbedtls/include/mbedtls/`
+- All `*.c` from `library/` to `thirdparty/mbedtls/library/`
+- In file `thirdparty/mbedtls/library/net_sockets.c` mbedTLS overrides the `_WIN32_WINNT` define.
+ Be sure to check the Godot addition to only redfine it when undefined or `< 0x0501` (PRed upstream).
+- Applied the patch in `thirdparty/mbedtls/1453.diff` (PR 1453). Soon to be merged upstream. Check it out at next update.
## minizip
diff --git a/thirdparty/fonts/Hack_Regular.ttf b/thirdparty/fonts/Hack_Regular.ttf
index f34270081..92a90cb06 100644
--- a/thirdparty/fonts/Hack_Regular.ttf
+++ b/thirdparty/fonts/Hack_Regular.ttf
Binary files differ
diff --git a/thirdparty/fonts/LICENSE_Hack.md b/thirdparty/fonts/LICENSE_Hack.md
index ddd23a2b8..08927e504 100644
--- a/thirdparty/fonts/LICENSE_Hack.md
+++ b/thirdparty/fonts/LICENSE_Hack.md
@@ -1,4 +1,4 @@
-The work in the Hack project is Copyright 2017 Source Foundry Authors and licensed under the MIT License
+The work in the Hack project is Copyright 2018 Source Foundry Authors and licensed under the MIT License
The work in the DejaVu project was committed to the public domain.
@@ -6,7 +6,7 @@ Bitstream Vera Sans Mono Copyright 2003 Bitstream Inc. and licensed under the Bi
### MIT License
-Copyright (c) 2017 Source Foundry Authors
+Copyright (c) 2018 Source Foundry Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/thirdparty/fonts/NotoSansUI_Bold.ttf b/thirdparty/fonts/NotoSansUI_Bold.ttf
new file mode 100644
index 000000000..810450aa8
--- /dev/null
+++ b/thirdparty/fonts/NotoSansUI_Bold.ttf
Binary files differ
diff --git a/thirdparty/glad/KHR/khrplatform.h b/thirdparty/glad/KHR/khrplatform.h
index 1ad3554a7..975bbffed 100644
--- a/thirdparty/glad/KHR/khrplatform.h
+++ b/thirdparty/glad/KHR/khrplatform.h
@@ -2,7 +2,7 @@
#define __khrplatform_h_
/*
-** Copyright (c) 2008-2009 The Khronos Group Inc.
+** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
@@ -26,18 +26,16 @@
/* Khronos platform-specific types and definitions.
*
- * $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $
+ * The master copy of khrplatform.h is maintained in the Khronos EGL
+ * Registry repository at https://github.com/KhronosGroup/EGL-Registry
+ * The last semantic modification to khrplatform.h was at commit ID:
+ * 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
- * Please submit changes by sending them to the public Khronos Bugzilla
- * (http://khronos.org/bugzilla) by filing a bug against product
- * "Khronos (general)" component "Registry".
- *
- * A predefined template which fills in some of the bug fields can be
- * reached using http://tinyurl.com/khrplatform-h-bugreport, but you
- * must create a Bugzilla login first.
+ * Please submit changes by filing pull requests or issues on
+ * the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
diff --git a/thirdparty/glad/glad.c b/thirdparty/glad/glad.c
index c2aaea214..8f8b2189e 100644
--- a/thirdparty/glad/glad.c
+++ b/thirdparty/glad/glad.c
@@ -1,6 +1,6 @@
/*
- OpenGL loader generated by glad 0.1.18a0 on Mon Mar 5 18:43:52 2018.
+ OpenGL loader generated by glad 0.1.20a0 on Fri May 4 21:44:11 2018.
Language/Generator: C/C++
Specification: gl
@@ -27,21 +27,40 @@
static void* get_proc(const char *namez);
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
#include <windows.h>
static HMODULE libGL;
typedef void* (APIENTRYP PFNWGLGETPROCADDRESSPROC_PRIVATE)(const char*);
static PFNWGLGETPROCADDRESSPROC_PRIVATE gladGetProcAddressPtr;
+#ifdef _MSC_VER
+#ifdef __has_include
+ #if __has_include(<winapifamily.h>)
+ #define HAVE_WINAPIFAMILY 1
+ #endif
+#elif _MSC_VER >= 1700 && !_USING_V110_SDK71_
+ #define HAVE_WINAPIFAMILY 1
+#endif
+#endif
+
+#ifdef HAVE_WINAPIFAMILY
+ #include <winapifamily.h>
+ #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
+ #define IS_UWP 1
+ #endif
+#endif
+
static
int open_gl(void) {
+#ifndef IS_UWP
libGL = LoadLibraryW(L"opengl32.dll");
if(libGL != NULL) {
gladGetProcAddressPtr = (PFNWGLGETPROCADDRESSPROC_PRIVATE)GetProcAddress(
libGL, "wglGetProcAddress");
return gladGetProcAddressPtr != NULL;
}
+#endif
return 0;
}
@@ -50,7 +69,7 @@ static
void close_gl(void) {
if(libGL != NULL) {
FreeLibrary((HMODULE) libGL);
- libGL = NULL;
+ libGL = NULL;
}
}
#else
@@ -113,7 +132,7 @@ void* get_proc(const char *namez) {
}
#endif
if(result == NULL) {
-#ifdef _WIN32
+#if defined(_WIN32) || defined(__CYGWIN__)
result = (void*)GetProcAddress((HMODULE) libGL, namez);
#else
result = dlsym(libGL, namez);
diff --git a/thirdparty/glad/glad/glad.h b/thirdparty/glad/glad/glad.h
index 9de720fbc..4eebad4f2 100644
--- a/thirdparty/glad/glad/glad.h
+++ b/thirdparty/glad/glad/glad.h
@@ -1,6 +1,6 @@
/*
- OpenGL loader generated by glad 0.1.18a0 on Mon Mar 5 18:43:52 2018.
+ OpenGL loader generated by glad 0.1.20a0 on Fri May 4 21:44:11 2018.
Language/Generator: C/C++
Specification: gl
@@ -1581,7 +1581,7 @@ GLAPI PFNGLGETTEXLEVELPARAMETERIVPROC glad_glGetTexLevelParameteriv;
typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC)(GLenum cap);
GLAPI PFNGLISENABLEDPROC glad_glIsEnabled;
#define glIsEnabled glad_glIsEnabled
-typedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble near, GLdouble far);
+typedef void (APIENTRYP PFNGLDEPTHRANGEPROC)(GLdouble n, GLdouble f);
GLAPI PFNGLDEPTHRANGEPROC glad_glDepthRange;
#define glDepthRange glad_glDepthRange
typedef void (APIENTRYP PFNGLVIEWPORTPROC)(GLint x, GLint y, GLsizei width, GLsizei height);
diff --git a/thirdparty/lws/client/client.c b/thirdparty/lws/client/client.c
index 20450aa92..ded4e4bf0 100644
--- a/thirdparty/lws/client/client.c
+++ b/thirdparty/lws/client/client.c
@@ -258,9 +258,10 @@ start_ws_handshake:
#ifdef LWS_OPENSSL_SUPPORT
/* we can retry this... just cook the SSL BIO the first time */
- if (wsi->use_ssl && !wsi->ssl) {
- if (lws_ssl_client_bio_create(wsi))
- return -1;
+ if (wsi->use_ssl && !wsi->ssl &&
+ lws_ssl_client_bio_create(wsi) < 0) {
+ cce = "bio_create failed";
+ goto bail3;
}
if (wsi->use_ssl) {
@@ -727,9 +728,10 @@ lws_client_interpret_server_handshake(struct lws *wsi)
return 0;
}
- if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
- lwsl_info("no ACCEPT\n");
- cce = "HS: ACCEPT missing";
+ if (p && !strncmp(p, "401", 3)) {
+ lwsl_warn(
+ "lws_client_handshake: got bad HTTP response '%s'\n", p);
+ cce = "HS: ws upgrade unauthorized";
goto bail3;
}
@@ -740,6 +742,12 @@ lws_client_interpret_server_handshake(struct lws *wsi)
goto bail3;
}
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_ACCEPT) == 0) {
+ lwsl_info("no ACCEPT\n");
+ cce = "HS: ACCEPT missing";
+ goto bail3;
+ }
+
p = lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE);
if (!p) {
lwsl_info("no UPGRADE\n");
diff --git a/thirdparty/lws/client/ssl-client.c b/thirdparty/lws/client/ssl-client.c
index b69fd2da3..962c6e3cb 100644
--- a/thirdparty/lws/client/ssl-client.c
+++ b/thirdparty/lws/client/ssl-client.c
@@ -176,11 +176,7 @@ lws_ssl_client_bio_create(struct lws *wsi)
#endif
#else
#if defined(LWS_WITH_MBEDTLS)
- if (wsi->vhost->x509_client_CA)
- SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
- else
- SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback);
-
+ SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
#else
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
SSL_set_tlsext_host_name(wsi->ssl, hostname);
@@ -284,9 +280,13 @@ some_wait:
char *p = (char *)&pt->serv_buf[0];
char *sb = p;
- lwsl_err("ssl hs1 error, X509_V_ERR = %d: %s\n",
- n, ERR_error_string(n, sb));
+ lwsl_err("ssl hs1 error, X509_V_ERR = %d: errno %d: %s\n",
+ n, errno, ERR_error_string(n, sb));
lws_ssl_elaborate_error();
+#if defined(LWS_WITH_MBEDTLS)
+ if (n == SSL_ERROR_SYSCALL)
+ return -1;
+#endif
}
n = -1;
diff --git a/thirdparty/lws/context.c b/thirdparty/lws/context.c
index f67476b1e..9f221f50f 100644
--- a/thirdparty/lws/context.c
+++ b/thirdparty/lws/context.c
@@ -1621,7 +1621,7 @@ lws_context_destroy2(struct lws_context *context)
lws_check_deferred_free(context, 1);
#if LWS_MAX_SMP > 1
- pthread_mutex_destroy(&context->lock, NULL);
+ pthread_mutex_destroy(&context->lock);
#endif
lws_free(context);
diff --git a/thirdparty/lws/libwebsockets.c b/thirdparty/lws/libwebsockets.c
index 50f975d21..8fe085404 100644
--- a/thirdparty/lws/libwebsockets.c
+++ b/thirdparty/lws/libwebsockets.c
@@ -482,8 +482,9 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
goto just_kill_connection;
- if (wsi->mode == LWSCM_HTTP_SERVING ||
- wsi->mode == LWSCM_HTTP2_SERVING) {
+ if (!wsi->told_user_closed &&
+ (wsi->mode == LWSCM_HTTP_SERVING ||
+ wsi->mode == LWSCM_HTTP2_SERVING)) {
if (wsi->user_space)
wsi->vhost->protocols->callback(wsi,
LWS_CALLBACK_HTTP_DROP_PROTOCOL,
@@ -583,7 +584,7 @@ just_kill_connection:
lws_remove_child_from_any_parent(wsi);
n = 0;
- if (wsi->user_space) {
+ if (!wsi->told_user_closed && wsi->user_space) {
lwsl_debug("%s: %p: DROP_PROTOCOL %s\n", __func__, wsi,
wsi->protocol->name);
wsi->protocol->callback(wsi,
@@ -656,8 +657,10 @@ just_kill_connection:
__func__, wsi, (int)(long)wsi->desc.sockfd,
wsi->state);
if (!wsi->socket_is_permanently_unusable &&
- lws_sockfd_valid(wsi->desc.sockfd))
+ lws_sockfd_valid(wsi->desc.sockfd)) {
+ wsi->socket_is_permanently_unusable = 1;
n = shutdown(wsi->desc.sockfd, SHUT_WR);
+ }
}
if (n)
lwsl_debug("closing: shutdown (state %d) ret %d\n",
diff --git a/thirdparty/lws/libwebsockets.h b/thirdparty/lws/libwebsockets.h
index ef996c5d7..460c73260 100644
--- a/thirdparty/lws/libwebsockets.h
+++ b/thirdparty/lws/libwebsockets.h
@@ -1073,7 +1073,7 @@ enum lws_callback_reasons {
LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22,
/**< if configured for
* including OpenSSL support, this callback allows your user code
- * to load extra certifcates into the server which allow it to
+ * to load extra certificates into the server which allow it to
* verify the validity of certificates returned by clients. user
* is the server's OpenSSL SSL_CTX* */
LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23,
@@ -4013,9 +4013,6 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
#if !defined(LWS_SIZEOFPTR)
#define LWS_SIZEOFPTR (sizeof (void *))
#endif
-#if !defined(u_int64_t)
-#define u_int64_t unsigned long long
-#endif
#if defined(__x86_64__)
#define _LWS_PAD_SIZE 16 /* Intel recommended for best performance */
@@ -4808,7 +4805,7 @@ LWS_VISIBLE LWS_EXTERN unsigned long
lws_now_secs(void);
/**
- * lws_get_context - Allow geting lws_context from a Websocket connection
+ * lws_get_context - Allow getting lws_context from a Websocket connection
* instance
*
* With this function, users can access context in the callback function.
diff --git a/thirdparty/lws/lws_config.h b/thirdparty/lws/lws_config.h
index 3a918747b..6005d94ec 100644
--- a/thirdparty/lws/lws_config.h
+++ b/thirdparty/lws/lws_config.h
@@ -1,5 +1,10 @@
/* lws_config.h Generated from lws_config.h.in */
-#include "lws_config_private.h"
+
+/* GODOT ADDITION */
+#ifndef DEBUG_ENABLED
+#define LWS_WITH_NO_LOGS
+#endif
+/* END GODOT ADDITION */
#ifndef NDEBUG
#ifndef _DEBUG
@@ -25,54 +30,45 @@
/* #undef LWS_WITH_PLUGINS */
/* #undef LWS_WITH_NO_LOGS */
-#ifndef DEBUG_ENABLED
-#define LWS_WITH_NO_LOGS
-#endif
/* The Libwebsocket version */
-#define LWS_LIBRARY_VERSION "2.4.1"
+#define LWS_LIBRARY_VERSION "2.4.2"
#define LWS_LIBRARY_VERSION_MAJOR 2
#define LWS_LIBRARY_VERSION_MINOR 4
-#define LWS_LIBRARY_VERSION_PATCH 1
+#define LWS_LIBRARY_VERSION_PATCH 2
/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */
#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH
/* The current git commit hash that we're building from */
-#define LWS_BUILD_HASH "55f97b7806e07db2d4c8a158172cd309d0faf450"
+#define LWS_BUILD_HASH "8964ce9db75a98e463dfafd2e89f2bc8a95ec6ed"
/* Build with OpenSSL support */
#define LWS_OPENSSL_SUPPORT
/* The client should load and trust CA root certs it finds in the OS */
-#define LWS_SSL_CLIENT_USE_OS_CA_CERTS
+/* #undef LWS_SSL_CLIENT_USE_OS_CA_CERTS */
/* Sets the path where the client certs should be installed. */
-#define LWS_OPENSSL_CLIENT_CERTS "../share"
+/* #undef LWS_OPENSSL_CLIENT_CERTS "../share" */
/* Turn off websocket extensions */
/* #undef LWS_NO_EXTENSIONS */
/* Enable libev io loop */
/* #undef LWS_WITH_LIBEV */
-#undef LWS_WITH_LIBEV
/* Enable libuv io loop */
/* #undef LWS_WITH_LIBUV */
-#undef LWS_WITH_LIBUV
/* Enable libevent io loop */
/* #undef LWS_WITH_LIBEVENT */
-#undef LWS_WITH_LIBEVENT
/* Build with support for ipv6 */
/* #undef LWS_WITH_IPV6 */
/* Build with support for UNIX domain socket */
/* #undef LWS_WITH_UNIX_SOCK */
-#ifdef WINDOWS_ENABLED
-#undef LWS_USE_UNIX_SOCK
-#endif
/* Build with support for HTTP2 */
/* #undef LWS_WITH_HTTP2 */
@@ -100,7 +96,7 @@
/* SSL server using ECDH certificate */
/* #undef LWS_SSL_SERVER_WITH_ECDH_CERT */
-#define LWS_HAVE_SSL_CTX_set1_param
+/* #undef LWS_HAVE_SSL_CTX_set1_param */
#define LWS_HAVE_X509_VERIFY_PARAM_set1_host
/* #undef LWS_HAVE_RSA_SET0_KEY */
@@ -110,7 +106,7 @@
/* #undef LWS_WITH_CGI */
/* whether the Openssl is recent enough, and / or built with, ecdh */
-#define LWS_HAVE_OPENSSL_ECDH_H
+/* #undef LWS_HAVE_OPENSSL_ECDH_H */
/* HTTP Proxy support */
/* #undef LWS_WITH_HTTP_PROXY */
@@ -157,9 +153,9 @@
/* OpenSSL various APIs */
-/* #undef LWS_HAVE_TLS_CLIENT_METHOD */
-#define LWS_HAVE_TLSV1_2_CLIENT_METHOD
-#define LWS_HAVE_SSL_SET_INFO_CALLBACK
+#define LWS_HAVE_TLS_CLIENT_METHOD
+/* #undef LWS_HAVE_TLSV1_2_CLIENT_METHOD */
+/* #undef LWS_HAVE_SSL_SET_INFO_CALLBACK */
#define LWS_HAS_INTPTR_T
diff --git a/thirdparty/lws/mbedtls_verify.diff b/thirdparty/lws/mbedtls_verify.diff
new file mode 100644
index 000000000..d320645d6
--- /dev/null
+++ b/thirdparty/lws/mbedtls_verify.diff
@@ -0,0 +1,74 @@
+diff --git a/thirdparty/lws/client/ssl-client.c b/thirdparty/lws/client/ssl-client.c
+index 6626e0844..962c6e3cb 100644
+--- a/thirdparty/lws/client/ssl-client.c
++++ b/thirdparty/lws/client/ssl-client.c
+@@ -176,11 +176,7 @@ lws_ssl_client_bio_create(struct lws *wsi)
+ #endif
+ #else
+ #if defined(LWS_WITH_MBEDTLS)
+- if (wsi->vhost->x509_client_CA)
+- SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
+- else
+- SSL_set_verify(wsi->ssl, SSL_VERIFY_NONE, OpenSSL_client_verify_callback);
+-
++ SSL_set_verify(wsi->ssl, SSL_VERIFY_PEER, OpenSSL_client_verify_callback);
+ #else
+ #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ SSL_set_tlsext_host_name(wsi->ssl, hostname);
+diff --git a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
+index 63504919c..4e3d61109 100644
+--- a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
++++ b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
+@@ -218,7 +218,7 @@ static int ssl_pm_reload_crt(SSL *ssl)
+ struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm;
+
+ if (ssl->verify_mode == SSL_VERIFY_PEER)
+- mode = MBEDTLS_SSL_VERIFY_REQUIRED;
++ mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
+ else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+ mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
+ else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE)
+@@ -712,11 +712,39 @@ long ssl_pm_get_verify_result(const SSL *ssl)
+ struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
+
+ ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
+- if (ret) {
+- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret);
++
++ if (!ret)
++ return X509_V_OK;
++
++ if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
++ (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
++ // Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification
++ verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
++
++ else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
++ verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
++
++ else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
++ (ret & MBEDTLS_X509_BADCRL_BAD_KEY))
++ verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
++
++ else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
++ (ret & MBEDTLS_X509_BADCRL_BAD_MD))
++ verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
++
++ else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
++ (ret & MBEDTLS_X509_BADCRL_FUTURE))
++ verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
++
++ else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
++ (ret & MBEDTLS_X509_BADCRL_EXPIRED))
++ verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
++
++ else
+ verify_result = X509_V_ERR_UNSPECIFIED;
+- } else
+- verify_result = X509_V_OK;
++
++ SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
++ "mbedtls_ssl_get_verify_result() return 0x%x", ret);
+
+ return verify_result;
+ }
diff --git a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h b/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h
index 45198bc97..2ca438c42 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h
+++ b/thirdparty/lws/mbedtls_wrapper/include/internal/ssl_types.h
@@ -215,6 +215,7 @@ struct ssl_st
int (*verify_callback) (int ok, X509_STORE_CTX *ctx);
int rwstate;
+ int interrupted_remaining_write;
long verify_result;
diff --git a/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h b/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h
index 2ffd7e754..eca68f20d 100644
--- a/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h
+++ b/thirdparty/lws/mbedtls_wrapper/include/platform/ssl_port.h
@@ -25,11 +25,13 @@
*/
#include "string.h"
-#if defined(__APPLE__) || defined(__FreeBSD__)
+/* GODOT ADDITION */
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)
#include <stdlib.h>
#else
#include "malloc.h"
#endif
+/* END GODOT ADDITION */
void *ssl_mem_zalloc(size_t size);
diff --git a/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c b/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c
index 187fc9f00..d8fdd06fa 100644
--- a/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c
+++ b/thirdparty/lws/mbedtls_wrapper/library/ssl_lib.c
@@ -142,9 +142,9 @@ int SSL_get_error(const SSL *ssl, int ret_code)
ret = SSL_ERROR_NONE;
else if (ret_code < 0)
{
- if (SSL_want_read(ssl))
+ if (ssl->err == SSL_ERROR_WANT_READ || SSL_want_read(ssl))
ret = SSL_ERROR_WANT_READ;
- else if (SSL_want_write(ssl))
+ else if (ssl->err == SSL_ERROR_WANT_WRITE || SSL_want_write(ssl))
ret = SSL_ERROR_WANT_WRITE;
else
ret = SSL_ERROR_SYSCALL; //unknown
@@ -457,7 +457,7 @@ int SSL_read(SSL *ssl, void *buffer, int len)
int SSL_write(SSL *ssl, const void *buffer, int len)
{
int ret;
- int send_bytes;
+ int send_bytes, bytes;
const unsigned char *pbuf;
SSL_ASSERT1(ssl);
@@ -470,25 +470,36 @@ int SSL_write(SSL *ssl, const void *buffer, int len)
pbuf = (const unsigned char *)buffer;
do {
- int bytes;
-
if (send_bytes > SSL_SEND_DATA_MAX_LENGTH)
bytes = SSL_SEND_DATA_MAX_LENGTH;
else
bytes = send_bytes;
+ if (ssl->interrupted_remaining_write) {
+ bytes = ssl->interrupted_remaining_write;
+ ssl->interrupted_remaining_write = 0;
+ }
+
ret = SSL_METHOD_CALL(send, ssl, pbuf, bytes);
+ //printf("%s: ssl_pm said %d for %d requested (cum %d)\n", __func__, ret, bytes, len -send_bytes);
+ /* the return is a NEGATIVE OpenSSL error code, or the length sent */
if (ret > 0) {
pbuf += ret;
send_bytes -= ret;
- }
- } while (ret > 0 && send_bytes);
+ } else
+ ssl->interrupted_remaining_write = bytes;
+ } while (ret > 0 && send_bytes && ret == bytes);
if (ret >= 0) {
ret = len - send_bytes;
- ssl->rwstate = SSL_NOTHING;
- } else
- ret = -1;
+ if (!ret)
+ ssl->rwstate = SSL_NOTHING;
+ } else {
+ if (send_bytes == len)
+ ret = -1;
+ else
+ ret = len - send_bytes;
+ }
return ret;
}
diff --git a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
index 536733fba..4e3d61109 100644
--- a/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
+++ b/thirdparty/lws/mbedtls_wrapper/platform/ssl_pm.c
@@ -218,7 +218,7 @@ static int ssl_pm_reload_crt(SSL *ssl)
struct x509_pm *crt_pm = (struct x509_pm *)ssl->cert->x509->x509_pm;
if (ssl->verify_mode == SSL_VERIFY_PEER)
- mode = MBEDTLS_SSL_VERIFY_REQUIRED;
+ mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
else if (ssl->verify_mode == SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
mode = MBEDTLS_SSL_VERIFY_OPTIONAL;
else if (ssl->verify_mode == SSL_VERIFY_CLIENT_ONCE)
@@ -360,17 +360,52 @@ int ssl_pm_read(SSL *ssl, void *buffer, int len)
return ret;
}
+/*
+ * This returns -1, or the length sent.
+ * If -1, then you need to find out if the error was
+ * fatal or recoverable using SSL_get_error()
+ */
int ssl_pm_send(SSL *ssl, const void *buffer, int len)
{
int ret;
struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
ret = mbedtls_ssl_write(&ssl_pm->ssl, buffer, len);
+ /*
+ * We can get a positive number, which may be less than len... that
+ * much was sent successfully and you can call again to send more.
+ *
+ * We can get a negative mbedtls error code... if WANT_WRITE or WANT_READ,
+ * it's nonfatal and means it should be retried as-is. If something else,
+ * it's fatal actually.
+ *
+ * If this function returns something other than a positive value or
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, the ssl context becomes unusable, and
+ * you should either free it or call mbedtls_ssl_session_reset() on it
+ * before re-using it for a new connection; the current connection must
+ * be closed.
+ *
+ * When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ, it must be
+ * called later with the same arguments, until it returns a positive value.
+ */
+
if (ret < 0) {
- if (ret == MBEDTLS_ERR_NET_CONN_RESET)
+ SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret);
+ switch (ret) {
+ case MBEDTLS_ERR_NET_CONN_RESET:
ssl->err = SSL_ERROR_SYSCALL;
- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_write() return -0x%x", -ret);
- ret = -1;
+ break;
+ case MBEDTLS_ERR_SSL_WANT_WRITE:
+ ssl->err = SSL_ERROR_WANT_WRITE;
+ break;
+ case MBEDTLS_ERR_SSL_WANT_READ:
+ ssl->err = SSL_ERROR_WANT_READ;
+ break;
+ default:
+ break;
+ }
+
+ ret = -1;
}
return ret;
@@ -677,11 +712,39 @@ long ssl_pm_get_verify_result(const SSL *ssl)
struct ssl_pm *ssl_pm = (struct ssl_pm *)ssl->ssl_pm;
ret = mbedtls_ssl_get_verify_result(&ssl_pm->ssl);
- if (ret) {
- SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL, "mbedtls_ssl_get_verify_result() return 0x%x", ret);
+
+ if (!ret)
+ return X509_V_OK;
+
+ if (ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED ||
+ (ret & MBEDTLS_X509_BADCRL_NOT_TRUSTED))
+ // Allows us to use LCCSCF_ALLOW_SELFSIGNED to skip verification
+ verify_result = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+
+ else if (ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
+ verify_result = X509_V_ERR_HOSTNAME_MISMATCH;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_BAD_KEY) ||
+ (ret & MBEDTLS_X509_BADCRL_BAD_KEY))
+ verify_result = X509_V_ERR_CA_KEY_TOO_SMALL;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_BAD_MD) ||
+ (ret & MBEDTLS_X509_BADCRL_BAD_MD))
+ verify_result = X509_V_ERR_CA_MD_TOO_WEAK;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_FUTURE) ||
+ (ret & MBEDTLS_X509_BADCRL_FUTURE))
+ verify_result = X509_V_ERR_CERT_NOT_YET_VALID;
+
+ else if ((ret & MBEDTLS_X509_BADCERT_EXPIRED) ||
+ (ret & MBEDTLS_X509_BADCRL_EXPIRED))
+ verify_result = X509_V_ERR_CERT_HAS_EXPIRED;
+
+ else
verify_result = X509_V_ERR_UNSPECIFIED;
- } else
- verify_result = X509_V_OK;
+
+ SSL_DEBUG(SSL_PLATFORM_ERROR_LEVEL,
+ "mbedtls_ssl_get_verify_result() return 0x%x", ret);
return verify_result;
}
diff --git a/thirdparty/lws/misc/lejp.c b/thirdparty/lws/misc/lejp.c
index 5407c90f9..38efa8b12 100644
--- a/thirdparty/lws/misc/lejp.c
+++ b/thirdparty/lws/misc/lejp.c
@@ -444,7 +444,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
goto append_npos;
}
if (c == '.') {
- if (ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) {
+ if (!ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
diff --git a/thirdparty/lws/misc/sha-1.c b/thirdparty/lws/misc/sha-1.c
index 9353fbefe..50205a010 100644
--- a/thirdparty/lws/misc/sha-1.c
+++ b/thirdparty/lws/misc/sha-1.c
@@ -45,7 +45,7 @@ struct sha1_ctxt {
} h;
union {
unsigned char b8[8];
- u_int64_t b64[1];
+ uint64_t b64[1];
} c;
union {
unsigned char b8[64];
diff --git a/thirdparty/lws/output.c b/thirdparty/lws/output.c
index ed4752490..375ff3ef9 100644
--- a/thirdparty/lws/output.c
+++ b/thirdparty/lws/output.c
@@ -270,9 +270,12 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
if (wsi->state != LWSS_ESTABLISHED &&
((wsi->state != LWSS_RETURNED_CLOSE_ALREADY &&
+ wsi->state != LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION &&
wsi->state != LWSS_AWAITING_CLOSE_ACK) ||
- wp != LWS_WRITE_CLOSE))
+ wp != LWS_WRITE_CLOSE)) {
+ lwsl_debug("binning\n");
return 0;
+ }
/* if we are continuing a frame that already had its header done */
@@ -507,7 +510,7 @@ send_raw:
(wp & 0x1f) == LWS_WRITE_HTTP_FINAL) &&
wsi->u.http.tx_content_length) {
wsi->u.http.tx_content_remain -= len;
- lwsl_info("%s: content_remain = %llu\n", __func__,
+ lwsl_info("%s: wsi %p: tx_content_remain = %llu\n", __func__, wsi,
(unsigned long long)wsi->u.http.tx_content_remain);
if (!wsi->u.http.tx_content_remain) {
lwsl_info("%s: selecting final write mode\n", __func__);
@@ -639,6 +642,9 @@ LWS_VISIBLE int lws_serve_http_file_fragment(struct lws *wsi)
poss = context->pt_serv_buf_size - n - LWS_H2_FRAME_HEADER_LENGTH;
+ if (poss > wsi->u.http.tx_content_remain)
+ poss = wsi->u.http.tx_content_remain;
+
/*
* if there is a hint about how much we will do well to send at one time,
* restrict ourselves to only trying to send that.
diff --git a/thirdparty/lws/pollfd.c b/thirdparty/lws/pollfd.c
index 4d6704d41..54a4a8605 100644
--- a/thirdparty/lws/pollfd.c
+++ b/thirdparty/lws/pollfd.c
@@ -537,9 +537,14 @@ LWS_VISIBLE int
lws_callback_on_writable_all_protocol(const struct lws_context *context,
const struct lws_protocols *protocol)
{
- struct lws_vhost *vhost = context->vhost_list;
+ struct lws_vhost *vhost;
int n;
+ if (!context)
+ return 0;
+
+ vhost = context->vhost_list;
+
while (vhost) {
for (n = 0; n < vhost->count_protocols; n++)
if (protocol->callback ==
diff --git a/thirdparty/lws/private-libwebsockets.h b/thirdparty/lws/private-libwebsockets.h
index 4f0b37433..535fa0be5 100644
--- a/thirdparty/lws/private-libwebsockets.h
+++ b/thirdparty/lws/private-libwebsockets.h
@@ -356,9 +356,6 @@ esp8266_tcp_stream_bind(lws_sockfd_type fd, int port, struct lws *wsi);
#ifndef BYTE_ORDER
#define BYTE_ORDER LITTLE_ENDIAN
#endif
-#ifndef u_int64_t
-typedef unsigned __int64 u_int64_t;
-#endif
#undef __P
#ifndef __P
@@ -1633,7 +1630,6 @@ struct lws_h2_netconn {
unsigned int pad_length:1;
unsigned int collected_priority:1;
unsigned int is_first_header_char:1;
- unsigned int seen_nonpseudoheader:1;
unsigned int zero_huff_padding:1;
unsigned int last_action_dyntable_resize:1;
@@ -1922,6 +1918,7 @@ struct lws {
unsigned int hdr_parsing_completed:1;
unsigned int http2_substream:1;
unsigned int upgraded_to_http2:1;
+ unsigned int seen_nonpseudoheader:1;
unsigned int listener:1;
unsigned int user_space_externally_allocated:1;
unsigned int socket_is_permanently_unusable:1;
diff --git a/thirdparty/lws/server/ssl-server.c b/thirdparty/lws/server/ssl-server.c
index a9516f223..c4362824b 100644
--- a/thirdparty/lws/server/ssl-server.c
+++ b/thirdparty/lws/server/ssl-server.c
@@ -155,7 +155,7 @@ lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg)
*/
vh = context->vhost_list;
while (vh) {
- if (!vh->being_destroyed && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
+ if (!vh->being_destroyed && ssl && vh->ssl_ctx == SSL_get_SSL_CTX(ssl))
break;
vh = vh->vhost_next;
}
diff --git a/thirdparty/lws/service.c b/thirdparty/lws/service.c
index 6748e30bd..8cf455e2c 100644
--- a/thirdparty/lws/service.c
+++ b/thirdparty/lws/service.c
@@ -1073,6 +1073,8 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
c = lws_token_to_string(m);
if (!c)
break;
+ if (!(*c))
+ break;
len = lws_hdr_total_length(wsi, m);
if (!len || len > sizeof(buf) - 1) {
@@ -1090,6 +1092,11 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
m++;
} while (1);
+ /* explicitly detach the ah */
+
+ lws_header_table_force_to_detachable_state(wsi);
+ lws_header_table_detach(wsi, 0);
+
/* ... and then drop the connection */
if (wsi->desc.sockfd == our_fd)
@@ -1098,7 +1105,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS);
- ah = ah->next;
+ ah = pt->ah_list;
}
#ifdef LWS_WITH_CGI
@@ -1644,6 +1651,14 @@ drain:
break;
}
#endif
+ /*
+ * something went wrong with parsing the handshake, and
+ * we ended up back in the event loop without completing it
+ */
+ case LWSCM_PRE_WS_SERVING_ACCEPT:
+ wsi->socket_is_permanently_unusable = 1;
+ goto close_and_handled;
+
default:
#ifdef LWS_NO_CLIENT
break;
diff --git a/thirdparty/lws/ssl.c b/thirdparty/lws/ssl.c
index 0a647b469..4ff3088ab 100644
--- a/thirdparty/lws/ssl.c
+++ b/thirdparty/lws/ssl.c
@@ -20,11 +20,7 @@
*/
#include "private-libwebsockets.h"
-
-/* workaround for mingw */
-#if !defined(ECONNABORTED)
-#define ECONNABORTED 103
-#endif
+#include <errno.h>
int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf,
lws_filepos_t *amount)
@@ -463,7 +459,7 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
lwsl_debug("%p: SSL_read says %d\n", wsi, n);
/* manpage: returning 0 means connection shut down */
- if (!n) {
+ if (!n || (n == -1 && errno == ENOTCONN)) {
wsi->socket_is_permanently_unusable = 1;
return LWS_SSL_CAPABLE_ERROR;
@@ -476,12 +472,12 @@ lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, int len)
m == SSL_ERROR_SYSCALL)
return LWS_SSL_CAPABLE_ERROR;
- if (SSL_want_read(wsi->ssl)) {
+ if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->ssl)) {
lwsl_debug("%s: WANT_READ\n", __func__);
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
- if (SSL_want_write(wsi->ssl)) {
+ if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->ssl)) {
lwsl_debug("%s: WANT_WRITE\n", __func__);
lwsl_debug("%p: LWS_SSL_CAPABLE_MORE_SERVICE\n", wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
@@ -885,6 +881,7 @@ go_again:
failed:
lws_stats_atomic_bump(wsi->context, pt,
LWSSTATS_C_SSL_CONNECTIONS_FAILED, 1);
+ wsi->socket_is_permanently_unusable = 1;
lwsl_info("SSL_accept failed socket %u: %s\n", wsi->desc.sockfd,
lws_ssl_get_error_string(m, n, buf, sizeof(buf)));
lws_ssl_elaborate_error();
@@ -903,7 +900,7 @@ accepted:
/* adapt our vhost to match the SNI SSL_CTX that was chosen */
vh = context->vhost_list;
while (vh) {
- if (!vh->being_destroyed &&
+ if (!vh->being_destroyed && wsi->ssl &&
vh->ssl_ctx == SSL_get_SSL_CTX(wsi->ssl)) {
lwsl_info("setting wsi to vh %s\n", vh->name);
wsi->vhost = vh;
diff --git a/thirdparty/mbedtls/1453.diff b/thirdparty/mbedtls/1453.diff
new file mode 100644
index 000000000..acc3654cd
--- /dev/null
+++ b/thirdparty/mbedtls/1453.diff
@@ -0,0 +1,120 @@
+diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c
+index 67900c46c8..cefe882d2a 100644
+--- a/thirdparty/mbedtls/library/entropy_poll.c
++++ b/thirdparty/mbedtls/library/entropy_poll.c
+@@ -54,28 +54,43 @@
+ #define _WIN32_WINNT 0x0400
+ #endif
+ #include <windows.h>
+-#include <wincrypt.h>
++#include <bcrypt.h>
++#if defined(_MSC_VER) && _MSC_VER <= 1600
++/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and
++ * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants.
++ * These constants are guaranteed to be the same, though, so we suppress the
++ * warning when including intsafe.h.
++ */
++#pragma warning( push )
++#pragma warning( disable : 4005 )
++#endif
++#include <intsafe.h>
++#if defined(_MSC_VER) && _MSC_VER <= 1600
++#pragma warning( pop )
++#endif
+
+ int mbedtls_platform_entropy_poll( void *data, unsigned char *output, size_t len,
+ size_t *olen )
+ {
+- HCRYPTPROV provider;
++ ULONG len_as_ulong = 0;
+ ((void) data);
+ *olen = 0;
+
+- if( CryptAcquireContext( &provider, NULL, NULL,
+- PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) == FALSE )
++ /*
++ * BCryptGenRandom takes ULONG for size, which is smaller than size_t on
++ * 64-bit Windows platforms. Ensure len's value can be safely converted into
++ * a ULONG.
++ */
++ if ( FAILED( SizeTToULong( len, &len_as_ulong ) ) )
+ {
+ return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );
+ }
+
+- if( CryptGenRandom( provider, (DWORD) len, output ) == FALSE )
++ if ( !BCRYPT_SUCCESS( BCryptGenRandom( NULL, output, len_as_ulong, BCRYPT_USE_SYSTEM_PREFERRED_RNG ) ) )
+ {
+- CryptReleaseContext( provider, 0 );
+ return( MBEDTLS_ERR_ENTROPY_SOURCE_FAILED );
+ }
+
+- CryptReleaseContext( provider, 0 );
+ *olen = len;
+
+ return( 0 );
+diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c
+index afff4e18bf..7960fa1a1a 100644
+--- a/thirdparty/mbedtls/library/x509_crt.c
++++ b/thirdparty/mbedtls/library/x509_crt.c
+@@ -64,6 +64,19 @@
+
+ #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
+ #include <windows.h>
++#if defined(_MSC_VER) && _MSC_VER <= 1600
++/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and
++ * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants.
++ * These constants are guaranteed to be the same, though, so we suppress the
++ * warning when including intsafe.h.
++ */
++#pragma warning( push )
++#pragma warning( disable : 4005 )
++#endif
++#include <intsafe.h>
++#if defined(_MSC_VER) && _MSC_VER <= 1600
++#pragma warning( pop )
++#endif
+ #else
+ #include <time.h>
+ #endif
+@@ -1130,6 +1143,7 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+ char filename[MAX_PATH];
+ char *p;
+ size_t len = strlen( path );
++ int lengthAsInt = 0;
+
+ WIN32_FIND_DATAW file_data;
+ HANDLE hFind;
+@@ -1144,7 +1158,18 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+ p = filename + len;
+ filename[len++] = '*';
+
+- w_ret = MultiByteToWideChar( CP_ACP, 0, filename, (int)len, szDir,
++ if ( FAILED ( SizeTToInt( len, &lengthAsInt ) ) )
++ return( MBEDTLS_ERR_X509_FILE_IO_ERROR );
++
++ /*
++ * Note this function uses the code page CP_ACP, and assumes the incoming
++ * string is encoded in ANSI, before translating it into Unicode. If the
++ * incoming string were changed to be UTF-8, then the length check needs to
++ * change to check the number of characters, not the number of bytes, in the
++ * incoming string are less than MAX_PATH to avoid a buffer overrun with
++ * MultiByteToWideChar().
++ */
++ w_ret = MultiByteToWideChar( CP_ACP, 0, filename, lengthAsInt, szDir,
+ MAX_PATH - 3 );
+ if( w_ret == 0 )
+ return( MBEDTLS_ERR_X509_BAD_INPUT_DATA );
+@@ -1161,8 +1186,11 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
+ if( file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
+ continue;
+
++ if ( FAILED( SizeTToInt( wcslen( file_data.cFileName ), &lengthAsInt ) ) )
++ return( MBEDTLS_ERR_X509_FILE_IO_ERROR );
++
+ w_ret = WideCharToMultiByte( CP_ACP, 0, file_data.cFileName,
+- lstrlenW( file_data.cFileName ),
++ lengthAsInt,
+ p, (int) len - 1,
+ NULL, NULL );
+ if( w_ret == 0 )
diff --git a/thirdparty/mbedtls/include/mbedtls/asn1.h b/thirdparty/mbedtls/include/mbedtls/asn1.h
index fde328a12..96c1c9a8a 100644
--- a/thirdparty/mbedtls/include/mbedtls/asn1.h
+++ b/thirdparty/mbedtls/include/mbedtls/asn1.h
@@ -88,6 +88,21 @@
#define MBEDTLS_ASN1_PRIMITIVE 0x00
#define MBEDTLS_ASN1_CONSTRUCTED 0x20
#define MBEDTLS_ASN1_CONTEXT_SPECIFIC 0x80
+
+/*
+ * Bit masks for each of the components of an ASN.1 tag as specified in
+ * ITU X.690 (08/2015), section 8.1 "General rules for encoding",
+ * paragraph 8.1.2.2:
+ *
+ * Bit 8 7 6 5 1
+ * +-------+-----+------------+
+ * | Class | P/C | Tag number |
+ * +-------+-----+------------+
+ */
+#define MBEDTLS_ASN1_TAG_CLASS_MASK 0xC0
+#define MBEDTLS_ASN1_TAG_PC_MASK 0x20
+#define MBEDTLS_ASN1_TAG_VALUE_MASK 0x1F
+
/* \} name */
/* \} addtogroup asn1_module */
diff --git a/thirdparty/mbedtls/include/mbedtls/ccm.h b/thirdparty/mbedtls/include/mbedtls/ccm.h
index 5a9ee4a1c..630b7fdf6 100644
--- a/thirdparty/mbedtls/include/mbedtls/ccm.h
+++ b/thirdparty/mbedtls/include/mbedtls/ccm.h
@@ -105,7 +105,7 @@ void mbedtls_ccm_free( mbedtls_ccm_context *ctx );
* Must be at least \p length Bytes wide.
* \param tag The buffer holding the tag.
* \param tag_len The length of the tag to generate in Bytes:
- * 4, 6, 8, 10, 14 or 16.
+ * 4, 6, 8, 10, 12, 14 or 16.
*
* \note The tag is written to a separate buffer. To concatenate
* the \p tag with the \p output, as done in <em>RFC-3610:
@@ -131,10 +131,13 @@ int mbedtls_ccm_encrypt_and_tag( mbedtls_ccm_context *ctx, size_t length,
* \param iv_len The length of the IV in Bytes: 7, 8, 9, 10, 11, 12, or 13.
* \param add The additional data field.
* \param add_len The length of additional data in Bytes.
+ * Must be less than 2^16 - 2^8.
* \param input The buffer holding the input data.
* \param output The buffer holding the output data.
+ * Must be at least \p length Bytes wide.
* \param tag The buffer holding the tag.
* \param tag_len The length of the tag in Bytes.
+ * 4, 6, 8, 10, 12, 14 or 16.
*
* \return 0 if successful and authenticated, or
* #MBEDTLS_ERR_CCM_AUTH_FAILED if the tag does not match.
diff --git a/thirdparty/mbedtls/include/mbedtls/check_config.h b/thirdparty/mbedtls/include/mbedtls/check_config.h
index 1143aa268..be8033296 100644
--- a/thirdparty/mbedtls/include/mbedtls/check_config.h
+++ b/thirdparty/mbedtls/include/mbedtls/check_config.h
@@ -78,6 +78,10 @@
#error "MBEDTLS_DHM_C defined, but not all prerequisites"
#endif
+#if defined(MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT) && !defined(MBEDTLS_SSL_TRUNCATED_HMAC)
+#error "MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT defined, but not all prerequisites"
+#endif
+
#if defined(MBEDTLS_CMAC_C) && \
!defined(MBEDTLS_AES_C) && !defined(MBEDTLS_DES_C)
#error "MBEDTLS_CMAC_C defined, but not all prerequisites"
diff --git a/thirdparty/mbedtls/include/mbedtls/config.h b/thirdparty/mbedtls/include/mbedtls/config.h
index 79eedffdd..b5905ef9d 100644
--- a/thirdparty/mbedtls/include/mbedtls/config.h
+++ b/thirdparty/mbedtls/include/mbedtls/config.h
@@ -1049,7 +1049,8 @@
/**
* \def MBEDTLS_RSA_NO_CRT
*
- * Do not use the Chinese Remainder Theorem for the RSA private operation.
+ * Do not use the Chinese Remainder Theorem
+ * for the RSA private operation.
*
* Uncomment this macro to disable the use of CRT in RSA.
*
@@ -1412,6 +1413,30 @@
#define MBEDTLS_SSL_TRUNCATED_HMAC
/**
+ * \def MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT
+ *
+ * Fallback to old (pre-2.7), non-conforming implementation of the truncated
+ * HMAC extension which also truncates the HMAC key. Note that this option is
+ * only meant for a transitory upgrade period and is likely to be removed in
+ * a future version of the library.
+ *
+ * \warning The old implementation is non-compliant and has a security weakness
+ * (2^80 brute force attack on the HMAC key used for a single,
+ * uninterrupted connection). This should only be enabled temporarily
+ * when (1) the use of truncated HMAC is essential in order to save
+ * bandwidth, and (2) the peer is an Mbed TLS stack that doesn't use
+ * the fixed implementation yet (pre-2.7).
+ *
+ * \deprecated This option is deprecated and will likely be removed in a
+ * future version of Mbed TLS.
+ *
+ * Uncomment to fallback to old, non-compliant truncated HMAC implementation.
+ *
+ * Requires: MBEDTLS_SSL_TRUNCATED_HMAC
+ */
+//#define MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT
+
+/**
* \def MBEDTLS_THREADING_ALT
*
* Provide your own alternate threading implementation.
@@ -1517,6 +1542,9 @@
*
* \note Currently compression can't be used with DTLS.
*
+ * \deprecated This feature is deprecated and will be removed
+ * in the next major revision of the library.
+ *
* Used in: library/ssl_tls.c
* library/ssl_cli.c
* library/ssl_srv.c
diff --git a/thirdparty/mbedtls/include/mbedtls/dhm.h b/thirdparty/mbedtls/include/mbedtls/dhm.h
index da2e66b11..00fafd8d1 100644
--- a/thirdparty/mbedtls/include/mbedtls/dhm.h
+++ b/thirdparty/mbedtls/include/mbedtls/dhm.h
@@ -372,7 +372,7 @@ MBEDTLS_DEPRECATED typedef char const * mbedtls_deprecated_constant_t;
* in <em>RFC-5114: Additional Diffie-Hellman Groups for Use with
* IETF Standards</em>.
*/
-#define MBEDTLS_DHM_RFC5114_MODP_P \
+#define MBEDTLS_DHM_RFC5114_MODP_2048_P \
MBEDTLS_DEPRECATED_STRING_CONSTANT( \
"AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1" \
"B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15" \
diff --git a/thirdparty/mbedtls/include/mbedtls/md2.h b/thirdparty/mbedtls/include/mbedtls/md2.h
index 2ff3f171a..0fd8b5afc 100644
--- a/thirdparty/mbedtls/include/mbedtls/md2.h
+++ b/thirdparty/mbedtls/include/mbedtls/md2.h
@@ -39,11 +39,6 @@
#define MBEDTLS_ERR_MD2_HW_ACCEL_FAILED -0x002B /**< MD2 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
-
#if !defined(MBEDTLS_MD2_ALT)
// Regular implementation
//
@@ -187,11 +182,7 @@ int mbedtls_internal_md2_process( mbedtls_md2_context *ctx );
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md2_starts(
- mbedtls_md2_context *ctx )
-{
- mbedtls_md2_starts_ret( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_md2_starts( mbedtls_md2_context *ctx );
/**
* \brief MD2 process buffer
@@ -207,13 +198,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md2_starts(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md2_update(
- mbedtls_md2_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_md2_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_md2_update( mbedtls_md2_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief MD2 final digest
@@ -228,12 +215,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md2_update(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md2_finish(
- mbedtls_md2_context *ctx,
- unsigned char output[16] )
-{
- mbedtls_md2_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md2_finish( mbedtls_md2_context *ctx,
+ unsigned char output[16] );
/**
* \brief MD2 process data block (internal use only)
@@ -247,11 +230,7 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md2_finish(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md2_process(
- mbedtls_md2_context *ctx )
-{
- mbedtls_internal_md2_process( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_md2_process( mbedtls_md2_context *ctx );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -304,12 +283,9 @@ int mbedtls_md2_ret( const unsigned char *input,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md2( const unsigned char *input,
- size_t ilen,
- unsigned char output[16] )
-{
- mbedtls_md2_ret( input, ilen, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md2( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/md4.h b/thirdparty/mbedtls/include/mbedtls/md4.h
index a2ab57f07..23fa95e46 100644
--- a/thirdparty/mbedtls/include/mbedtls/md4.h
+++ b/thirdparty/mbedtls/include/mbedtls/md4.h
@@ -40,11 +40,6 @@
#define MBEDTLS_ERR_MD4_HW_ACCEL_FAILED -0x002D /**< MD4 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
-
#if !defined(MBEDTLS_MD4_ALT)
// Regular implementation
//
@@ -188,11 +183,7 @@ int mbedtls_internal_md4_process( mbedtls_md4_context *ctx,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md4_starts(
- mbedtls_md4_context *ctx )
-{
- mbedtls_md4_starts_ret( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_md4_starts( mbedtls_md4_context *ctx );
/**
* \brief MD4 process buffer
@@ -208,13 +199,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md4_starts(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md4_update(
- mbedtls_md4_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_md4_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_md4_update( mbedtls_md4_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief MD4 final digest
@@ -229,12 +216,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md4_update(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md4_finish(
- mbedtls_md4_context *ctx,
- unsigned char output[16] )
-{
- mbedtls_md4_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md4_finish( mbedtls_md4_context *ctx,
+ unsigned char output[16] );
/**
* \brief MD4 process data block (internal use only)
@@ -249,12 +232,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md4_finish(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md4_process(
- mbedtls_md4_context *ctx,
- const unsigned char data[64] )
-{
- mbedtls_internal_md4_process( ctx, data );
-}
+MBEDTLS_DEPRECATED void mbedtls_md4_process( mbedtls_md4_context *ctx,
+ const unsigned char data[64] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -309,12 +288,9 @@ int mbedtls_md4_ret( const unsigned char *input,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md4( const unsigned char *input,
- size_t ilen,
- unsigned char output[16] )
-{
- mbedtls_md4_ret( input, ilen, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md4( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/md5.h b/thirdparty/mbedtls/include/mbedtls/md5.h
index d49391f81..06ea4c5d4 100644
--- a/thirdparty/mbedtls/include/mbedtls/md5.h
+++ b/thirdparty/mbedtls/include/mbedtls/md5.h
@@ -43,11 +43,6 @@
// Regular implementation
//
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -188,11 +183,7 @@ int mbedtls_internal_md5_process( mbedtls_md5_context *ctx,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md5_starts(
- mbedtls_md5_context *ctx )
-{
- mbedtls_md5_starts_ret( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_md5_starts( mbedtls_md5_context *ctx );
/**
* \brief MD5 process buffer
@@ -208,13 +199,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md5_starts(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md5_update(
- mbedtls_md5_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_md5_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_md5_update( mbedtls_md5_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief MD5 final digest
@@ -229,12 +216,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md5_update(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md5_finish(
- mbedtls_md5_context *ctx,
- unsigned char output[16] )
-{
- mbedtls_md5_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md5_finish( mbedtls_md5_context *ctx,
+ unsigned char output[16] );
/**
* \brief MD5 process data block (internal use only)
@@ -249,12 +232,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_md5_finish(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md5_process(
- mbedtls_md5_context *ctx,
- const unsigned char data[64] )
-{
- mbedtls_internal_md5_process( ctx, data );
-}
+MBEDTLS_DEPRECATED void mbedtls_md5_process( mbedtls_md5_context *ctx,
+ const unsigned char data[64] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -309,12 +288,9 @@ int mbedtls_md5_ret( const unsigned char *input,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_md5( const unsigned char *input,
- size_t ilen,
- unsigned char output[16] )
-{
- mbedtls_md5_ret( input, ilen, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_md5( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/oid.h b/thirdparty/mbedtls/include/mbedtls/oid.h
index bf2ef5ece..408645ece 100644
--- a/thirdparty/mbedtls/include/mbedtls/oid.h
+++ b/thirdparty/mbedtls/include/mbedtls/oid.h
@@ -228,6 +228,14 @@
#define MBEDTLS_OID_HMAC_SHA1 MBEDTLS_OID_RSA_COMPANY "\x02\x07" /**< id-hmacWithSHA1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 7 } */
+#define MBEDTLS_OID_HMAC_SHA224 MBEDTLS_OID_RSA_COMPANY "\x02\x08" /**< id-hmacWithSHA224 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 8 } */
+
+#define MBEDTLS_OID_HMAC_SHA256 MBEDTLS_OID_RSA_COMPANY "\x02\x09" /**< id-hmacWithSHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 9 } */
+
+#define MBEDTLS_OID_HMAC_SHA384 MBEDTLS_OID_RSA_COMPANY "\x02\x0A" /**< id-hmacWithSHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 10 } */
+
+#define MBEDTLS_OID_HMAC_SHA512 MBEDTLS_OID_RSA_COMPANY "\x02\x0B" /**< id-hmacWithSHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2) 11 } */
+
/*
* Encryption algorithms
*/
@@ -514,6 +522,16 @@ int mbedtls_oid_get_oid_by_sig_alg( mbedtls_pk_type_t pk_alg, mbedtls_md_type_t
* \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
*/
int mbedtls_oid_get_md_alg( const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_alg );
+
+/**
+ * \brief Translate hmac algorithm OID into md_type
+ *
+ * \param oid OID to use
+ * \param md_hmac place to store message hmac algorithm
+ *
+ * \return 0 if successful, or MBEDTLS_ERR_OID_NOT_FOUND
+ */
+int mbedtls_oid_get_md_hmac( const mbedtls_asn1_buf *oid, mbedtls_md_type_t *md_hmac );
#endif /* MBEDTLS_MD_C */
/**
diff --git a/thirdparty/mbedtls/include/mbedtls/ripemd160.h b/thirdparty/mbedtls/include/mbedtls/ripemd160.h
index c21868b18..3a8b50a62 100644
--- a/thirdparty/mbedtls/include/mbedtls/ripemd160.h
+++ b/thirdparty/mbedtls/include/mbedtls/ripemd160.h
@@ -35,11 +35,6 @@
#define MBEDTLS_ERR_RIPEMD160_HW_ACCEL_FAILED -0x0031 /**< RIPEMD160 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
-
#if !defined(MBEDTLS_RIPEMD160_ALT)
// Regular implementation
//
@@ -139,11 +134,8 @@ int mbedtls_internal_ripemd160_process( mbedtls_ripemd160_context *ctx,
*
* \param ctx context to be initialized
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_starts(
- mbedtls_ripemd160_context *ctx )
-{
- mbedtls_ripemd160_starts_ret( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_ripemd160_starts(
+ mbedtls_ripemd160_context *ctx );
/**
* \brief RIPEMD-160 process buffer
@@ -154,13 +146,10 @@ MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_starts(
* \param input buffer holding the data
* \param ilen length of the input data
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_update(
+MBEDTLS_DEPRECATED void mbedtls_ripemd160_update(
mbedtls_ripemd160_context *ctx,
const unsigned char *input,
- size_t ilen )
-{
- mbedtls_ripemd160_update_ret( ctx, input, ilen );
-}
+ size_t ilen );
/**
* \brief RIPEMD-160 final digest
@@ -170,12 +159,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_update(
* \param ctx RIPEMD-160 context
* \param output RIPEMD-160 checksum result
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_finish(
+MBEDTLS_DEPRECATED void mbedtls_ripemd160_finish(
mbedtls_ripemd160_context *ctx,
- unsigned char output[20] )
-{
- mbedtls_ripemd160_finish_ret( ctx, output );
-}
+ unsigned char output[20] );
/**
* \brief RIPEMD-160 process data block (internal use only)
@@ -185,12 +171,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_finish(
* \param ctx RIPEMD-160 context
* \param data buffer holding one block of data
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160_process(
+MBEDTLS_DEPRECATED void mbedtls_ripemd160_process(
mbedtls_ripemd160_context *ctx,
- const unsigned char data[64] )
-{
- mbedtls_internal_ripemd160_process( ctx, data );
-}
+ const unsigned char data[64] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -235,13 +218,9 @@ int mbedtls_ripemd160_ret( const unsigned char *input,
* \param ilen length of the input data
* \param output RIPEMD-160 checksum result
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_ripemd160(
- const unsigned char *input,
- size_t ilen,
- unsigned char output[20] )
-{
- mbedtls_ripemd160_ret( input, ilen, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_ripemd160( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[20] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/rsa.h b/thirdparty/mbedtls/include/mbedtls/rsa.h
index fb2f77f94..5548f3c12 100644
--- a/thirdparty/mbedtls/include/mbedtls/rsa.h
+++ b/thirdparty/mbedtls/include/mbedtls/rsa.h
@@ -518,6 +518,18 @@ int mbedtls_rsa_public( mbedtls_rsa_context *ctx,
*
* \note The input and output buffers must be large
* enough. For example, 128 Bytes if RSA-1024 is used.
+ *
+ * \note Blinding is used if and only if a PRNG is provided.
+ *
+ * \note If blinding is used, both the base of exponentation
+ * and the exponent are blinded, providing protection
+ * against some side-channel attacks.
+ *
+ * \warning It is deprecated and a security risk to not provide
+ * a PRNG here and thereby prevent the use of blinding.
+ * Future versions of the library may enforce the presence
+ * of a PRNG.
+ *
*/
int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
diff --git a/thirdparty/mbedtls/include/mbedtls/sha1.h b/thirdparty/mbedtls/include/mbedtls/sha1.h
index e4f865021..05540cde1 100644
--- a/thirdparty/mbedtls/include/mbedtls/sha1.h
+++ b/thirdparty/mbedtls/include/mbedtls/sha1.h
@@ -39,11 +39,6 @@
#define MBEDTLS_ERR_SHA1_HW_ACCEL_FAILED -0x0035 /**< SHA-1 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
-
#if !defined(MBEDTLS_SHA1_ALT)
// Regular implementation
//
@@ -190,11 +185,7 @@ int mbedtls_internal_sha1_process( mbedtls_sha1_context *ctx,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha1_starts(
- mbedtls_sha1_context *ctx )
-{
- mbedtls_sha1_starts_ret( ctx );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha1_starts( mbedtls_sha1_context *ctx );
/**
* \brief SHA-1 process buffer
@@ -210,13 +201,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha1_starts(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha1_update(
- mbedtls_sha1_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_sha1_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha1_update( mbedtls_sha1_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief SHA-1 final digest
@@ -231,12 +218,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha1_update(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha1_finish(
- mbedtls_sha1_context *ctx,
- unsigned char output[20] )
-{
- mbedtls_sha1_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha1_finish( mbedtls_sha1_context *ctx,
+ unsigned char output[20] );
/**
* \brief SHA-1 process data block (internal use only)
@@ -251,12 +234,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha1_finish(
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha1_process(
- mbedtls_sha1_context *ctx,
- const unsigned char data[64] )
-{
- mbedtls_internal_sha1_process( ctx, data );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha1_process( mbedtls_sha1_context *ctx,
+ const unsigned char data[64] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -317,12 +296,9 @@ int mbedtls_sha1_ret( const unsigned char *input,
* stronger message digests instead.
*
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha1( const unsigned char *input,
- size_t ilen,
- unsigned char output[20] )
-{
- mbedtls_sha1_ret( input, ilen, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha1( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[20] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/sha256.h b/thirdparty/mbedtls/include/mbedtls/sha256.h
index a2b6e1164..ffb16c277 100644
--- a/thirdparty/mbedtls/include/mbedtls/sha256.h
+++ b/thirdparty/mbedtls/include/mbedtls/sha256.h
@@ -35,10 +35,6 @@
#define MBEDTLS_ERR_SHA256_HW_ACCEL_FAILED -0x0037 /**< SHA-256 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
#if !defined(MBEDTLS_SHA256_ALT)
// Regular implementation
//
@@ -156,12 +152,8 @@ int mbedtls_internal_sha256_process( mbedtls_sha256_context *ctx,
* <ul><li>0: Use SHA-256.</li>
* <li>1: Use SHA-224.</li></ul>
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha256_starts(
- mbedtls_sha256_context *ctx,
- int is224 )
-{
- mbedtls_sha256_starts_ret( ctx, is224 );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha256_starts( mbedtls_sha256_context *ctx,
+ int is224 );
/**
* \brief This function feeds an input buffer into an ongoing
@@ -173,13 +165,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha256_starts(
* \param input The buffer holding the data.
* \param ilen The length of the input data.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha256_update(
- mbedtls_sha256_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_sha256_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha256_update( mbedtls_sha256_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief This function finishes the SHA-256 operation, and writes
@@ -190,12 +178,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha256_update(
* \param ctx The SHA-256 context.
* \param output The SHA-224or SHA-256 checksum result.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha256_finish(
- mbedtls_sha256_context *ctx,
- unsigned char output[32] )
-{
- mbedtls_sha256_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha256_finish( mbedtls_sha256_context *ctx,
+ unsigned char output[32] );
/**
* \brief This function processes a single data block within
@@ -207,12 +191,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha256_finish(
* \param ctx The SHA-256 context.
* \param data The buffer holding one block of data.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha256_process(
- mbedtls_sha256_context *ctx,
- const unsigned char data[64] )
-{
- mbedtls_internal_sha256_process( ctx, data );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha256_process( mbedtls_sha256_context *ctx,
+ const unsigned char data[64] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -276,14 +256,10 @@ int mbedtls_sha256_ret( const unsigned char *input,
* <ul><li>0: Use SHA-256.</li>
* <li>1: Use SHA-224.</li></ul>
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha256(
- const unsigned char *input,
- size_t ilen,
- unsigned char output[32],
- int is224 )
-{
- mbedtls_sha256_ret( input, ilen, output, is224 );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha256( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[32],
+ int is224 );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/sha512.h b/thirdparty/mbedtls/include/mbedtls/sha512.h
index 52ae204d4..8404a2d59 100644
--- a/thirdparty/mbedtls/include/mbedtls/sha512.h
+++ b/thirdparty/mbedtls/include/mbedtls/sha512.h
@@ -35,10 +35,6 @@
#define MBEDTLS_ERR_SHA512_HW_ACCEL_FAILED -0x0039 /**< SHA-512 hardware accelerator failed */
-#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
- !defined(inline) && !defined(__cplusplus)
-#define inline __inline
-#endif
#if !defined(MBEDTLS_SHA512_ALT)
// Regular implementation
//
@@ -156,12 +152,8 @@ int mbedtls_internal_sha512_process( mbedtls_sha512_context *ctx,
* <ul><li>0: Use SHA-512.</li>
* <li>1: Use SHA-384.</li></ul>
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha512_starts(
- mbedtls_sha512_context *ctx,
- int is384 )
-{
- mbedtls_sha512_starts_ret( ctx, is384 );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha512_starts( mbedtls_sha512_context *ctx,
+ int is384 );
/**
* \brief This function feeds an input buffer into an ongoing
@@ -173,13 +165,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha512_starts(
* \param input The buffer holding the data.
* \param ilen The length of the input data.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha512_update(
- mbedtls_sha512_context *ctx,
- const unsigned char *input,
- size_t ilen )
-{
- mbedtls_sha512_update_ret( ctx, input, ilen );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha512_update( mbedtls_sha512_context *ctx,
+ const unsigned char *input,
+ size_t ilen );
/**
* \brief This function finishes the SHA-512 operation, and writes
@@ -190,12 +178,8 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha512_update(
* \param ctx The SHA-512 context.
* \param output The SHA-384 or SHA-512 checksum result.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha512_finish(
- mbedtls_sha512_context *ctx,
- unsigned char output[64] )
-{
- mbedtls_sha512_finish_ret( ctx, output );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha512_finish( mbedtls_sha512_context *ctx,
+ unsigned char output[64] );
/**
* \brief This function processes a single data block within
@@ -207,12 +191,9 @@ MBEDTLS_DEPRECATED static inline void mbedtls_sha512_finish(
* \param ctx The SHA-512 context.
* \param data The buffer holding one block of data.
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha512_process(
+MBEDTLS_DEPRECATED void mbedtls_sha512_process(
mbedtls_sha512_context *ctx,
- const unsigned char data[128] )
-{
- mbedtls_internal_sha512_process( ctx, data );
-}
+ const unsigned char data[128] );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
@@ -278,14 +259,10 @@ int mbedtls_sha512_ret( const unsigned char *input,
* <ul><li>0: Use SHA-512.</li>
* <li>1: Use SHA-384.</li></ul>
*/
-MBEDTLS_DEPRECATED static inline void mbedtls_sha512(
- const unsigned char *input,
- size_t ilen,
- unsigned char output[64],
- int is384 )
-{
- mbedtls_sha512_ret( input, ilen, output, is384 );
-}
+MBEDTLS_DEPRECATED void mbedtls_sha512( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[64],
+ int is384 );
#undef MBEDTLS_DEPRECATED
#endif /* !MBEDTLS_DEPRECATED_REMOVED */
diff --git a/thirdparty/mbedtls/include/mbedtls/ssl.h b/thirdparty/mbedtls/include/mbedtls/ssl.h
index 51e843ae2..dffc16219 100644
--- a/thirdparty/mbedtls/include/mbedtls/ssl.h
+++ b/thirdparty/mbedtls/include/mbedtls/ssl.h
@@ -49,6 +49,15 @@
#endif
#if defined(MBEDTLS_ZLIB_SUPPORT)
+
+#if defined(MBEDTLS_DEPRECATED_WARNING)
+#warning "Record compression support via MBEDTLS_ZLIB_SUPPORT is deprecated and will be removed in the next major revision of the library"
+#endif
+
+#if defined(MBEDTLS_DEPRECATED_REMOVED)
+#error "Record compression support via MBEDTLS_ZLIB_SUPPORT is deprecated and cannot be used if MBEDTLS_DEPRECATED_REMOVED is set"
+#endif
+
#include "zlib.h"
#endif
@@ -971,8 +980,13 @@ void mbedtls_ssl_init( mbedtls_ssl_context *ssl );
* \note No copy of the configuration context is made, it can be
* shared by many mbedtls_ssl_context structures.
*
- * \warning Modifying the conf structure after it has been used in this
- * function is unsupported!
+ * \warning The conf structure will be accessed during the session.
+ * It must not be modified or freed as long as the session
+ * is active.
+ *
+ * \warning This function must be called exactly once per context.
+ * Calling mbedtls_ssl_setup again is not supported, even
+ * if no session is active.
*
* \param ssl SSL context
* \param conf SSL configuration to use
diff --git a/thirdparty/mbedtls/include/mbedtls/ssl_internal.h b/thirdparty/mbedtls/include/mbedtls/ssl_internal.h
index 9f583a877..60b431a0f 100644
--- a/thirdparty/mbedtls/include/mbedtls/ssl_internal.h
+++ b/thirdparty/mbedtls/include/mbedtls/ssl_internal.h
@@ -71,6 +71,9 @@
#endif /* MBEDTLS_SSL_PROTO_TLS1 */
#endif /* MBEDTLS_SSL_PROTO_SSL3 */
+#define MBEDTLS_SSL_MIN_VALID_MINOR_VERSION MBEDTLS_SSL_MINOR_VERSION_1
+#define MBEDTLS_SSL_MIN_VALID_MAJOR_VERSION MBEDTLS_SSL_MAJOR_VERSION_3
+
/* Determine maximum supported version */
#define MBEDTLS_SSL_MAX_MAJOR_VERSION MBEDTLS_SSL_MAJOR_VERSION_3
diff --git a/thirdparty/mbedtls/include/mbedtls/version.h b/thirdparty/mbedtls/include/mbedtls/version.h
index 961be59c3..c3ee649f5 100644
--- a/thirdparty/mbedtls/include/mbedtls/version.h
+++ b/thirdparty/mbedtls/include/mbedtls/version.h
@@ -39,7 +39,7 @@
* Major, Minor, Patchlevel
*/
#define MBEDTLS_VERSION_MAJOR 2
-#define MBEDTLS_VERSION_MINOR 7
+#define MBEDTLS_VERSION_MINOR 8
#define MBEDTLS_VERSION_PATCH 0
/**
@@ -47,9 +47,9 @@
* MMNNPP00
* Major version | Minor version | Patch version
*/
-#define MBEDTLS_VERSION_NUMBER 0x02070000
-#define MBEDTLS_VERSION_STRING "2.7.0"
-#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.7.0"
+#define MBEDTLS_VERSION_NUMBER 0x02080000
+#define MBEDTLS_VERSION_STRING "2.8.0"
+#define MBEDTLS_VERSION_STRING_FULL "mbed TLS 2.8.0"
#if defined(MBEDTLS_VERSION_C)
diff --git a/thirdparty/mbedtls/library/aes.c b/thirdparty/mbedtls/library/aes.c
index dba4a5f57..3d2eac82d 100644
--- a/thirdparty/mbedtls/library/aes.c
+++ b/thirdparty/mbedtls/library/aes.c
@@ -765,12 +765,14 @@ int mbedtls_internal_aes_encrypt( mbedtls_aes_context *ctx,
}
#endif /* !MBEDTLS_AES_ENCRYPT_ALT */
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
void mbedtls_aes_encrypt( mbedtls_aes_context *ctx,
const unsigned char input[16],
unsigned char output[16] )
{
mbedtls_internal_aes_encrypt( ctx, input, output );
}
+#endif /* !MBEDTLS_DEPRECATED_REMOVED */
/*
* AES-ECB block decryption
@@ -831,12 +833,14 @@ int mbedtls_internal_aes_decrypt( mbedtls_aes_context *ctx,
}
#endif /* !MBEDTLS_AES_DECRYPT_ALT */
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
void mbedtls_aes_decrypt( mbedtls_aes_context *ctx,
const unsigned char input[16],
unsigned char output[16] )
{
mbedtls_internal_aes_decrypt( ctx, input, output );
}
+#endif /* !MBEDTLS_DEPRECATED_REMOVED */
/*
* AES-ECB block encryption/decryption
diff --git a/thirdparty/mbedtls/library/bignum.c b/thirdparty/mbedtls/library/bignum.c
index d27c130bc..9f13da442 100644
--- a/thirdparty/mbedtls/library/bignum.c
+++ b/thirdparty/mbedtls/library/bignum.c
@@ -1623,7 +1623,7 @@ int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi
mbedtls_mpi RR, T, W[ 2 << MBEDTLS_MPI_WINDOW_SIZE ], Apos;
int neg;
- if( mbedtls_mpi_cmp_int( N, 0 ) < 0 || ( N->p[0] & 1 ) == 0 )
+ if( mbedtls_mpi_cmp_int( N, 0 ) <= 0 || ( N->p[0] & 1 ) == 0 )
return( MBEDTLS_ERR_MPI_BAD_INPUT_DATA );
if( mbedtls_mpi_cmp_int( E, 0 ) < 0 )
diff --git a/thirdparty/mbedtls/library/ctr_drbg.c b/thirdparty/mbedtls/library/ctr_drbg.c
index c2310cb57..ff532a013 100644
--- a/thirdparty/mbedtls/library/ctr_drbg.c
+++ b/thirdparty/mbedtls/library/ctr_drbg.c
@@ -19,7 +19,7 @@
* This file is part of mbed TLS (https://tls.mbed.org)
*/
/*
- * The NIST SP 800-90 DRBGs are described in the following publucation.
+ * The NIST SP 800-90 DRBGs are described in the following publication.
*
* http://csrc.nist.gov/publications/nistpubs/800-90/SP800-90revised_March2007.pdf
*/
diff --git a/thirdparty/mbedtls/library/debug.c b/thirdparty/mbedtls/library/debug.c
index f9229b360..db3924ac5 100644
--- a/thirdparty/mbedtls/library/debug.c
+++ b/thirdparty/mbedtls/library/debug.c
@@ -91,7 +91,7 @@ void mbedtls_debug_print_msg( const mbedtls_ssl_context *ssl, int level,
va_start( argp, format );
#if defined(_WIN32)
-#if defined(_TRUNCATE)
+#if defined(_TRUNCATE) && !defined(__MINGW32__)
ret = _vsnprintf_s( str, DEBUG_BUF_SIZE, _TRUNCATE, format, argp );
#else
ret = _vsnprintf( str, DEBUG_BUF_SIZE, format, argp );
diff --git a/thirdparty/mbedtls/library/entropy_poll.c b/thirdparty/mbedtls/library/entropy_poll.c
index 5e8a090b3..ed350735d 100644
--- a/thirdparty/mbedtls/library/entropy_poll.c
+++ b/thirdparty/mbedtls/library/entropy_poll.c
@@ -55,16 +55,17 @@
#endif
#include <windows.h>
#include <bcrypt.h>
-#if _MSC_VER <= 1600
-/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and <intsafe.h> are included, as they
- * redefine a number of <TYPE>_MAX constants. These constants are guaranteed to be the same, though, so
- * we suppress the warning when including intsafe.h.
+#if defined(_MSC_VER) && _MSC_VER <= 1600
+/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and
+ * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants.
+ * These constants are guaranteed to be the same, though, so we suppress the
+ * warning when including intsafe.h.
*/
#pragma warning( push )
#pragma warning( disable : 4005 )
#endif
#include <intsafe.h>
-#if _MSC_VER <= 1600
+#if defined(_MSC_VER) && _MSC_VER <= 1600
#pragma warning( pop )
#endif
@@ -76,8 +77,9 @@ int mbedtls_platform_entropy_poll( void *data, unsigned char *output, size_t len
*olen = 0;
/*
- * BCryptGenRandom takes ULONG for size, which is smaller than size_t on 64-bit platforms.
- * Ensure len's value can be safely converted into a ULONG.
+ * BCryptGenRandom takes ULONG for size, which is smaller than size_t on
+ * 64-bit Windows platforms. Ensure len's value can be safely converted into
+ * a ULONG.
*/
if ( FAILED( SizeTToULong( len, &len_as_ulong ) ) )
{
diff --git a/thirdparty/mbedtls/library/md2.c b/thirdparty/mbedtls/library/md2.c
index 5028e8c58..b88aa406a 100644
--- a/thirdparty/mbedtls/library/md2.c
+++ b/thirdparty/mbedtls/library/md2.c
@@ -115,6 +115,13 @@ int mbedtls_md2_starts_ret( mbedtls_md2_context *ctx )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md2_starts( mbedtls_md2_context *ctx )
+{
+ mbedtls_md2_starts_ret( ctx );
+}
+#endif
+
#if !defined(MBEDTLS_MD2_PROCESS_ALT)
int mbedtls_internal_md2_process( mbedtls_md2_context *ctx )
{
@@ -151,6 +158,13 @@ int mbedtls_internal_md2_process( mbedtls_md2_context *ctx )
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md2_process( mbedtls_md2_context *ctx )
+{
+ mbedtls_internal_md2_process( ctx );
+}
+#endif
#endif /* !MBEDTLS_MD2_PROCESS_ALT */
/*
@@ -187,6 +201,15 @@ int mbedtls_md2_update_ret( mbedtls_md2_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md2_update( mbedtls_md2_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_md2_update_ret( ctx, input, ilen );
+}
+#endif
+
/*
* MD2 final digest
*/
@@ -214,6 +237,14 @@ int mbedtls_md2_finish_ret( mbedtls_md2_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md2_finish( mbedtls_md2_context *ctx,
+ unsigned char output[16] )
+{
+ mbedtls_md2_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_MD2_ALT */
/*
@@ -243,6 +274,15 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md2( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] )
+{
+ mbedtls_md2_ret( input, ilen, output );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
diff --git a/thirdparty/mbedtls/library/md4.c b/thirdparty/mbedtls/library/md4.c
index 34a4b0e24..ba704f58e 100644
--- a/thirdparty/mbedtls/library/md4.c
+++ b/thirdparty/mbedtls/library/md4.c
@@ -111,6 +111,13 @@ int mbedtls_md4_starts_ret( mbedtls_md4_context *ctx )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md4_starts( mbedtls_md4_context *ctx )
+{
+ mbedtls_md4_starts_ret( ctx );
+}
+#endif
+
#if !defined(MBEDTLS_MD4_PROCESS_ALT)
int mbedtls_internal_md4_process( mbedtls_md4_context *ctx,
const unsigned char data[64] )
@@ -217,6 +224,14 @@ int mbedtls_internal_md4_process( mbedtls_md4_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md4_process( mbedtls_md4_context *ctx,
+ const unsigned char data[64] )
+{
+ mbedtls_internal_md4_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_MD4_PROCESS_ALT */
/*
@@ -273,6 +288,15 @@ int mbedtls_md4_update_ret( mbedtls_md4_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md4_update( mbedtls_md4_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_md4_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char md4_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -318,6 +342,14 @@ int mbedtls_md4_finish_ret( mbedtls_md4_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md4_finish( mbedtls_md4_context *ctx,
+ unsigned char output[16] )
+{
+ mbedtls_md4_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_MD4_ALT */
/*
@@ -347,6 +379,15 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md4( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] )
+{
+ mbedtls_md4_ret( input, ilen, output );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
diff --git a/thirdparty/mbedtls/library/md5.c b/thirdparty/mbedtls/library/md5.c
index 8872dc467..8440ebffc 100644
--- a/thirdparty/mbedtls/library/md5.c
+++ b/thirdparty/mbedtls/library/md5.c
@@ -110,6 +110,13 @@ int mbedtls_md5_starts_ret( mbedtls_md5_context *ctx )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md5_starts( mbedtls_md5_context *ctx )
+{
+ mbedtls_md5_starts_ret( ctx );
+}
+#endif
+
#if !defined(MBEDTLS_MD5_PROCESS_ALT)
int mbedtls_internal_md5_process( mbedtls_md5_context *ctx,
const unsigned char data[64] )
@@ -236,6 +243,14 @@ int mbedtls_internal_md5_process( mbedtls_md5_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md5_process( mbedtls_md5_context *ctx,
+ const unsigned char data[64] )
+{
+ mbedtls_internal_md5_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_MD5_PROCESS_ALT */
/*
@@ -289,6 +304,15 @@ int mbedtls_md5_update_ret( mbedtls_md5_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md5_update( mbedtls_md5_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_md5_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char md5_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -332,6 +356,14 @@ int mbedtls_md5_finish_ret( mbedtls_md5_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md5_finish( mbedtls_md5_context *ctx,
+ unsigned char output[16] )
+{
+ mbedtls_md5_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_MD5_ALT */
/*
@@ -361,6 +393,15 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_md5( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[16] )
+{
+ mbedtls_md5_ret( input, ilen, output );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
* RFC 1321 test vectors
diff --git a/thirdparty/mbedtls/library/memory_buffer_alloc.c b/thirdparty/mbedtls/library/memory_buffer_alloc.c
index 545d5a2c3..821ae2c70 100644
--- a/thirdparty/mbedtls/library/memory_buffer_alloc.c
+++ b/thirdparty/mbedtls/library/memory_buffer_alloc.c
@@ -113,7 +113,7 @@ static void debug_header( memory_header *hdr )
#endif
}
-static void debug_chain()
+static void debug_chain( void )
{
memory_header *cur = heap.first;
@@ -180,11 +180,11 @@ static int verify_header( memory_header *hdr )
return( 0 );
}
-static int verify_chain()
+static int verify_chain( void )
{
- memory_header *prv = heap.first, *cur = heap.first->next;
+ memory_header *prv = heap.first, *cur;
- if( verify_header( heap.first ) != 0 )
+ if( prv == NULL || verify_header( prv ) != 0 )
{
#if defined(MBEDTLS_MEMORY_DEBUG)
mbedtls_fprintf( stderr, "FATAL: verification of first header "
@@ -202,6 +202,8 @@ static int verify_chain()
return( 1 );
}
+ cur = heap.first->next;
+
while( cur != NULL )
{
if( verify_header( cur ) != 0 )
@@ -245,7 +247,9 @@ static void *buffer_alloc_calloc( size_t n, size_t size )
original_len = len = n * size;
- if( n != 0 && len / n != size )
+ if( n == 0 || size == 0 || len / n != size )
+ return( NULL );
+ else if( len > (size_t)-MBEDTLS_MEMORY_ALIGN_MULTIPLE )
return( NULL );
if( len % MBEDTLS_MEMORY_ALIGN_MULTIPLE )
@@ -386,7 +390,7 @@ static void buffer_alloc_free( void *ptr )
if( ptr == NULL || heap.buf == NULL || heap.first == NULL )
return;
- if( p < heap.buf || p > heap.buf + heap.len )
+ if( p < heap.buf || p >= heap.buf + heap.len )
{
#if defined(MBEDTLS_MEMORY_DEBUG)
mbedtls_fprintf( stderr, "FATAL: mbedtls_free() outside of managed "
@@ -500,13 +504,13 @@ void mbedtls_memory_buffer_set_verify( int verify )
heap.verify = verify;
}
-int mbedtls_memory_buffer_alloc_verify()
+int mbedtls_memory_buffer_alloc_verify( void )
{
return verify_chain();
}
#if defined(MBEDTLS_MEMORY_DEBUG)
-void mbedtls_memory_buffer_alloc_status()
+void mbedtls_memory_buffer_alloc_status( void )
{
mbedtls_fprintf( stderr,
"Current use: %zu blocks / %zu bytes, max: %zu blocks / "
@@ -570,8 +574,7 @@ static void buffer_alloc_free_mutexed( void *ptr )
void mbedtls_memory_buffer_alloc_init( unsigned char *buf, size_t len )
{
- memset( &heap, 0, sizeof(buffer_alloc_ctx) );
- memset( buf, 0, len );
+ memset( &heap, 0, sizeof( buffer_alloc_ctx ) );
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_init( &heap.mutex );
@@ -581,26 +584,30 @@ void mbedtls_memory_buffer_alloc_init( unsigned char *buf, size_t len )
mbedtls_platform_set_calloc_free( buffer_alloc_calloc, buffer_alloc_free );
#endif
- if( (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE )
+ if( len < sizeof( memory_header ) + MBEDTLS_MEMORY_ALIGN_MULTIPLE )
+ return;
+ else if( (size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE )
{
/* Adjust len first since buf is used in the computation */
len -= MBEDTLS_MEMORY_ALIGN_MULTIPLE
- - (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE;
+ - (size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE;
buf += MBEDTLS_MEMORY_ALIGN_MULTIPLE
- - (size_t) buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE;
+ - (size_t)buf % MBEDTLS_MEMORY_ALIGN_MULTIPLE;
}
+ memset( buf, 0, len );
+
heap.buf = buf;
heap.len = len;
- heap.first = (memory_header *) buf;
- heap.first->size = len - sizeof(memory_header);
+ heap.first = (memory_header *)buf;
+ heap.first->size = len - sizeof( memory_header );
heap.first->magic1 = MAGIC1;
heap.first->magic2 = MAGIC2;
heap.first_free = heap.first;
}
-void mbedtls_memory_buffer_alloc_free()
+void mbedtls_memory_buffer_alloc_free( void )
{
#if defined(MBEDTLS_THREADING_C)
mbedtls_mutex_free( &heap.mutex );
@@ -620,7 +627,7 @@ static int check_pointer( void *p )
return( 0 );
}
-static int check_all_free( )
+static int check_all_free( void )
{
if(
#if defined(MBEDTLS_MEMORY_DEBUG)
diff --git a/thirdparty/mbedtls/library/net_sockets.c b/thirdparty/mbedtls/library/net_sockets.c
index 754049005..2fb548caa 100644
--- a/thirdparty/mbedtls/library/net_sockets.c
+++ b/thirdparty/mbedtls/library/net_sockets.c
@@ -45,11 +45,14 @@
#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \
!defined(EFI32)
-#ifdef _WIN32_WINNT
+/* GODOT ADDITION */
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0501)
#undef _WIN32_WINNT
-#endif
/* Enables getaddrinfo() & Co */
-#define _WIN32_WINNT 0x0601
+#define _WIN32_WINNT 0x0501
+#endif
+/* END GODOT ADDITION */
+
#include <ws2tcpip.h>
#include <winsock2.h>
diff --git a/thirdparty/mbedtls/library/oid.c b/thirdparty/mbedtls/library/oid.c
index f13826ed7..edea950f8 100644
--- a/thirdparty/mbedtls/library/oid.c
+++ b/thirdparty/mbedtls/library/oid.c
@@ -625,6 +625,51 @@ static const oid_md_alg_t oid_md_alg[] =
FN_OID_TYPED_FROM_ASN1(oid_md_alg_t, md_alg, oid_md_alg)
FN_OID_GET_ATTR1(mbedtls_oid_get_md_alg, oid_md_alg_t, md_alg, mbedtls_md_type_t, md_alg)
FN_OID_GET_OID_BY_ATTR1(mbedtls_oid_get_oid_by_md, oid_md_alg_t, oid_md_alg, mbedtls_md_type_t, md_alg)
+
+/*
+ * For HMAC digestAlgorithm
+ */
+typedef struct {
+ mbedtls_oid_descriptor_t descriptor;
+ mbedtls_md_type_t md_hmac;
+} oid_md_hmac_t;
+
+static const oid_md_hmac_t oid_md_hmac[] =
+{
+#if defined(MBEDTLS_SHA1_C)
+ {
+ { ADD_LEN( MBEDTLS_OID_HMAC_SHA1 ), "hmacSHA1", "HMAC-SHA-1" },
+ MBEDTLS_MD_SHA1,
+ },
+#endif /* MBEDTLS_SHA1_C */
+#if defined(MBEDTLS_SHA256_C)
+ {
+ { ADD_LEN( MBEDTLS_OID_HMAC_SHA224 ), "hmacSHA224", "HMAC-SHA-224" },
+ MBEDTLS_MD_SHA224,
+ },
+ {
+ { ADD_LEN( MBEDTLS_OID_HMAC_SHA256 ), "hmacSHA256", "HMAC-SHA-256" },
+ MBEDTLS_MD_SHA256,
+ },
+#endif /* MBEDTLS_SHA256_C */
+#if defined(MBEDTLS_SHA512_C)
+ {
+ { ADD_LEN( MBEDTLS_OID_HMAC_SHA384 ), "hmacSHA384", "HMAC-SHA-384" },
+ MBEDTLS_MD_SHA384,
+ },
+ {
+ { ADD_LEN( MBEDTLS_OID_HMAC_SHA512 ), "hmacSHA512", "HMAC-SHA-512" },
+ MBEDTLS_MD_SHA512,
+ },
+#endif /* MBEDTLS_SHA512_C */
+ {
+ { NULL, 0, NULL, NULL },
+ MBEDTLS_MD_NONE,
+ },
+};
+
+FN_OID_TYPED_FROM_ASN1(oid_md_hmac_t, md_hmac, oid_md_hmac)
+FN_OID_GET_ATTR1(mbedtls_oid_get_md_hmac, oid_md_hmac_t, md_hmac, mbedtls_md_type_t, md_hmac)
#endif /* MBEDTLS_MD_C */
#if defined(MBEDTLS_PKCS12_C)
diff --git a/thirdparty/mbedtls/library/pem.c b/thirdparty/mbedtls/library/pem.c
index c09651f4a..ac86d7e47 100644
--- a/thirdparty/mbedtls/library/pem.c
+++ b/thirdparty/mbedtls/library/pem.c
@@ -442,7 +442,7 @@ int mbedtls_pem_write_buffer( const char *header, const char *footer,
unsigned char *buf, size_t buf_len, size_t *olen )
{
int ret;
- unsigned char *encode_buf, *c, *p = buf;
+ unsigned char *encode_buf = NULL, *c, *p = buf;
size_t len = 0, use_len, add_len = 0;
mbedtls_base64_encode( NULL, 0, &use_len, der_data, der_len );
@@ -454,7 +454,8 @@ int mbedtls_pem_write_buffer( const char *header, const char *footer,
return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL );
}
- if( ( encode_buf = mbedtls_calloc( 1, use_len ) ) == NULL )
+ if( use_len != 0 &&
+ ( ( encode_buf = mbedtls_calloc( 1, use_len ) ) == NULL ) )
return( MBEDTLS_ERR_PEM_ALLOC_FAILED );
if( ( ret = mbedtls_base64_encode( encode_buf, use_len, &use_len, der_data,
diff --git a/thirdparty/mbedtls/library/pkcs5.c b/thirdparty/mbedtls/library/pkcs5.c
index e28d5a847..95f44fa98 100644
--- a/thirdparty/mbedtls/library/pkcs5.c
+++ b/thirdparty/mbedtls/library/pkcs5.c
@@ -96,11 +96,9 @@ static int pkcs5_parse_pbkdf2_params( const mbedtls_asn1_buf *params,
if( ( ret = mbedtls_asn1_get_alg_null( &p, end, &prf_alg_oid ) ) != 0 )
return( MBEDTLS_ERR_PKCS5_INVALID_FORMAT + ret );
- if( MBEDTLS_OID_CMP( MBEDTLS_OID_HMAC_SHA1, &prf_alg_oid ) != 0 )
+ if( mbedtls_oid_get_md_hmac( &prf_alg_oid, md_type ) != 0 )
return( MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE );
- *md_type = MBEDTLS_MD_SHA1;
-
if( p != end )
return( MBEDTLS_ERR_PKCS5_INVALID_FORMAT +
MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
diff --git a/thirdparty/mbedtls/library/pkparse.c b/thirdparty/mbedtls/library/pkparse.c
index b4def4f91..9022db2f9 100644
--- a/thirdparty/mbedtls/library/pkparse.c
+++ b/thirdparty/mbedtls/library/pkparse.c
@@ -181,6 +181,10 @@ static int pk_get_ecparams( unsigned char **p, const unsigned char *end,
{
int ret;
+ if ( end - *p < 1 )
+ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT +
+ MBEDTLS_ERR_ASN1_OUT_OF_DATA );
+
/* Tag may be either OID or SEQUENCE */
params->tag = **p;
if( params->tag != MBEDTLS_ASN1_OID
@@ -1277,6 +1281,9 @@ int mbedtls_pk_parse_key( mbedtls_pk_context *pk,
{
unsigned char *key_copy;
+ if( keylen == 0 )
+ return( MBEDTLS_ERR_PK_KEY_INVALID_FORMAT );
+
if( ( key_copy = mbedtls_calloc( 1, keylen ) ) == NULL )
return( MBEDTLS_ERR_PK_ALLOC_FAILED );
@@ -1348,11 +1355,45 @@ int mbedtls_pk_parse_public_key( mbedtls_pk_context *ctx,
{
int ret;
unsigned char *p;
+#if defined(MBEDTLS_RSA_C)
+ const mbedtls_pk_info_t *pk_info;
+#endif
#if defined(MBEDTLS_PEM_PARSE_C)
size_t len;
mbedtls_pem_context pem;
mbedtls_pem_init( &pem );
+#if defined(MBEDTLS_RSA_C)
+ /* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */
+ if( keylen == 0 || key[keylen - 1] != '\0' )
+ ret = MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT;
+ else
+ ret = mbedtls_pem_read_buffer( &pem,
+ "-----BEGIN RSA PUBLIC KEY-----",
+ "-----END RSA PUBLIC KEY-----",
+ key, NULL, 0, &len );
+
+ if( ret == 0 )
+ {
+ p = pem.buf;
+ if( ( pk_info = mbedtls_pk_info_from_type( MBEDTLS_PK_RSA ) ) == NULL )
+ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
+
+ if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
+ return( ret );
+
+ if ( ( ret = pk_get_rsapubkey( &p, p + pem.buflen, mbedtls_pk_rsa( *ctx ) ) ) != 0 )
+ mbedtls_pk_free( ctx );
+
+ mbedtls_pem_free( &pem );
+ return( ret );
+ }
+ else if( ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT )
+ {
+ mbedtls_pem_free( &pem );
+ return( ret );
+ }
+#endif /* MBEDTLS_RSA_C */
/* Avoid calling mbedtls_pem_read_buffer() on non-null-terminated string */
if( keylen == 0 || key[keylen - 1] != '\0' )
@@ -1368,23 +1409,43 @@ int mbedtls_pk_parse_public_key( mbedtls_pk_context *ctx,
/*
* Was PEM encoded
*/
- key = pem.buf;
- keylen = pem.buflen;
+ p = pem.buf;
+
+ ret = mbedtls_pk_parse_subpubkey( &p, p + pem.buflen, ctx );
+ mbedtls_pem_free( &pem );
+ return( ret );
}
else if( ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT )
{
mbedtls_pem_free( &pem );
return( ret );
}
+ mbedtls_pem_free( &pem );
#endif /* MBEDTLS_PEM_PARSE_C */
+
+#if defined(MBEDTLS_RSA_C)
+ if( ( pk_info = mbedtls_pk_info_from_type( MBEDTLS_PK_RSA ) ) == NULL )
+ return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG );
+
+ if( ( ret = mbedtls_pk_setup( ctx, pk_info ) ) != 0 )
+ return( ret );
+
+ p = (unsigned char *)key;
+ ret = pk_get_rsapubkey( &p, p + keylen, mbedtls_pk_rsa( *ctx ) );
+ if( ret == 0 )
+ {
+ return( ret );
+ }
+ mbedtls_pk_free( ctx );
+ if( ret != ( MBEDTLS_ERR_PK_INVALID_PUBKEY + MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ) )
+ {
+ return( ret );
+ }
+#endif /* MBEDTLS_RSA_C */
p = (unsigned char *) key;
ret = mbedtls_pk_parse_subpubkey( &p, p + keylen, ctx );
-#if defined(MBEDTLS_PEM_PARSE_C)
- mbedtls_pem_free( &pem );
-#endif
-
return( ret );
}
diff --git a/thirdparty/mbedtls/library/platform.c b/thirdparty/mbedtls/library/platform.c
index 76df7fac1..a295f9b9a 100644
--- a/thirdparty/mbedtls/library/platform.c
+++ b/thirdparty/mbedtls/library/platform.c
@@ -82,7 +82,7 @@ int mbedtls_platform_win32_snprintf( char *s, size_t n, const char *fmt, ... )
return( -1 );
va_start( argp, fmt );
-#if defined(_TRUNCATE)
+#if defined(_TRUNCATE) && !defined(__MINGW32__)
ret = _vsnprintf_s( s, n, _TRUNCATE, fmt, argp );
#else
ret = _vsnprintf( s, n, fmt, argp );
diff --git a/thirdparty/mbedtls/library/ripemd160.c b/thirdparty/mbedtls/library/ripemd160.c
index b85b117c6..2ba48b7fd 100644
--- a/thirdparty/mbedtls/library/ripemd160.c
+++ b/thirdparty/mbedtls/library/ripemd160.c
@@ -112,6 +112,13 @@ int mbedtls_ripemd160_starts_ret( mbedtls_ripemd160_context *ctx )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_ripemd160_starts( mbedtls_ripemd160_context *ctx )
+{
+ mbedtls_ripemd160_starts_ret( ctx );
+}
+#endif
+
#if !defined(MBEDTLS_RIPEMD160_PROCESS_ALT)
/*
* Process one block
@@ -295,6 +302,14 @@ int mbedtls_internal_ripemd160_process( mbedtls_ripemd160_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_ripemd160_process( mbedtls_ripemd160_context *ctx,
+ const unsigned char data[64] )
+{
+ mbedtls_internal_ripemd160_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_RIPEMD160_PROCESS_ALT */
/*
@@ -349,6 +364,15 @@ int mbedtls_ripemd160_update_ret( mbedtls_ripemd160_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_ripemd160_update( mbedtls_ripemd160_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_ripemd160_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char ripemd160_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -395,6 +419,14 @@ int mbedtls_ripemd160_finish_ret( mbedtls_ripemd160_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_ripemd160_finish( mbedtls_ripemd160_context *ctx,
+ unsigned char output[20] )
+{
+ mbedtls_ripemd160_finish_ret( ctx, output );
+}
+#endif
+
#endif /* ! MBEDTLS_RIPEMD160_ALT */
/*
@@ -424,6 +456,15 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_ripemd160( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[20] )
+{
+ mbedtls_ripemd160_ret( input, ilen, output );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
* Test vectors from the RIPEMD-160 paper and
diff --git a/thirdparty/mbedtls/library/rsa.c b/thirdparty/mbedtls/library/rsa.c
index 6526978e2..c9f7ba91b 100644
--- a/thirdparty/mbedtls/library/rsa.c
+++ b/thirdparty/mbedtls/library/rsa.c
@@ -773,16 +773,38 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
{
int ret;
size_t olen;
- mbedtls_mpi T, T1, T2;
+
+ /* Temporary holding the result */
+ mbedtls_mpi T;
+
+ /* Temporaries holding P-1, Q-1 and the
+ * exponent blinding factor, respectively. */
mbedtls_mpi P1, Q1, R;
-#if defined(MBEDTLS_RSA_NO_CRT)
- mbedtls_mpi D_blind;
- mbedtls_mpi *D = &ctx->D;
-#else
+
+#if !defined(MBEDTLS_RSA_NO_CRT)
+ /* Temporaries holding the results mod p resp. mod q. */
+ mbedtls_mpi TP, TQ;
+
+ /* Temporaries holding the blinded exponents for
+ * the mod p resp. mod q computation (if used). */
mbedtls_mpi DP_blind, DQ_blind;
+
+ /* Pointers to actual exponents to be used - either the unblinded
+ * or the blinded ones, depending on the presence of a PRNG. */
mbedtls_mpi *DP = &ctx->DP;
mbedtls_mpi *DQ = &ctx->DQ;
-#endif
+#else
+ /* Temporary holding the blinded exponent (if used). */
+ mbedtls_mpi D_blind;
+
+ /* Pointer to actual exponent to be used - either the unblinded
+ * or the blinded one, depending on the presence of a PRNG. */
+ mbedtls_mpi *D = &ctx->D;
+#endif /* MBEDTLS_RSA_NO_CRT */
+
+ /* Temporaries holding the initial input and the double
+ * checked result; should be the same in the end. */
+ mbedtls_mpi I, C;
if( rsa_check_context( ctx, 1 /* private key checks */,
f_rng != NULL /* blinding y/n */ ) != 0 )
@@ -790,8 +812,17 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
}
- mbedtls_mpi_init( &T ); mbedtls_mpi_init( &T1 ); mbedtls_mpi_init( &T2 );
- mbedtls_mpi_init( &P1 ); mbedtls_mpi_init( &Q1 ); mbedtls_mpi_init( &R );
+#if defined(MBEDTLS_THREADING_C)
+ if( ( ret = mbedtls_mutex_lock( &ctx->mutex ) ) != 0 )
+ return( ret );
+#endif
+
+ /* MPI Initialization */
+ mbedtls_mpi_init( &T );
+
+ mbedtls_mpi_init( &P1 );
+ mbedtls_mpi_init( &Q1 );
+ mbedtls_mpi_init( &R );
if( f_rng != NULL )
{
@@ -803,12 +834,15 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
#endif
}
-
-#if defined(MBEDTLS_THREADING_C)
- if( ( ret = mbedtls_mutex_lock( &ctx->mutex ) ) != 0 )
- return( ret );
+#if !defined(MBEDTLS_RSA_NO_CRT)
+ mbedtls_mpi_init( &TP ); mbedtls_mpi_init( &TQ );
#endif
+ mbedtls_mpi_init( &I );
+ mbedtls_mpi_init( &C );
+
+ /* End of MPI initialization */
+
MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( &T, input, ctx->len ) );
if( mbedtls_mpi_cmp_mpi( &T, &ctx->N ) >= 0 )
{
@@ -816,6 +850,8 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
goto cleanup;
}
+ MBEDTLS_MPI_CHK( mbedtls_mpi_copy( &I, &T ) );
+
if( f_rng != NULL )
{
/*
@@ -874,24 +910,25 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
/*
* Faster decryption using the CRT
*
- * T1 = input ^ dP mod P
- * T2 = input ^ dQ mod Q
+ * TP = input ^ dP mod P
+ * TQ = input ^ dQ mod Q
*/
- MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &T1, &T, DP, &ctx->P, &ctx->RP ) );
- MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &T2, &T, DQ, &ctx->Q, &ctx->RQ ) );
+
+ MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &TP, &T, DP, &ctx->P, &ctx->RP ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &TQ, &T, DQ, &ctx->Q, &ctx->RQ ) );
/*
- * T = (T1 - T2) * (Q^-1 mod P) mod P
+ * T = (TP - TQ) * (Q^-1 mod P) mod P
*/
- MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &T, &T1, &T2 ) );
- MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &T1, &T, &ctx->QP ) );
- MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &T, &T1, &ctx->P ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( &T, &TP, &TQ ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &TP, &T, &ctx->QP ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &T, &TP, &ctx->P ) );
/*
- * T = T2 + T * Q
+ * T = TQ + T * Q
*/
- MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &T1, &T, &ctx->Q ) );
- MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &T, &T2, &T1 ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &TP, &T, &ctx->Q ) );
+ MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &T, &TQ, &TP ) );
#endif /* MBEDTLS_RSA_NO_CRT */
if( f_rng != NULL )
@@ -904,6 +941,15 @@ int mbedtls_rsa_private( mbedtls_rsa_context *ctx,
MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &T, &T, &ctx->N ) );
}
+ /* Verify the result to prevent glitching attacks. */
+ MBEDTLS_MPI_CHK( mbedtls_mpi_exp_mod( &C, &T, &ctx->E,
+ &ctx->N, &ctx->RN ) );
+ if( mbedtls_mpi_cmp_mpi( &C, &I ) != 0 )
+ {
+ ret = MBEDTLS_ERR_RSA_VERIFY_FAILED;
+ goto cleanup;
+ }
+
olen = ctx->len;
MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &T, output, olen ) );
@@ -913,8 +959,9 @@ cleanup:
return( MBEDTLS_ERR_THREADING_MUTEX_ERROR );
#endif
- mbedtls_mpi_free( &T ); mbedtls_mpi_free( &T1 ); mbedtls_mpi_free( &T2 );
- mbedtls_mpi_free( &P1 ); mbedtls_mpi_free( &Q1 ); mbedtls_mpi_free( &R );
+ mbedtls_mpi_free( &P1 );
+ mbedtls_mpi_free( &Q1 );
+ mbedtls_mpi_free( &R );
if( f_rng != NULL )
{
@@ -926,6 +973,15 @@ cleanup:
#endif
}
+ mbedtls_mpi_free( &T );
+
+#if !defined(MBEDTLS_RSA_NO_CRT)
+ mbedtls_mpi_free( &TP ); mbedtls_mpi_free( &TQ );
+#endif
+
+ mbedtls_mpi_free( &C );
+ mbedtls_mpi_free( &I );
+
if( ret != 0 )
return( MBEDTLS_ERR_RSA_PRIVATE_FAILED + ret );
@@ -2222,7 +2278,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( verbose != 0 )
@@ -2237,7 +2294,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( verbose != 0 )
@@ -2250,7 +2308,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( memcmp( rsa_decrypted, rsa_plaintext, len ) != 0 )
@@ -2258,7 +2317,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( verbose != 0 )
@@ -2283,7 +2343,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( verbose != 0 )
@@ -2296,7 +2357,8 @@ int mbedtls_rsa_self_test( int verbose )
if( verbose != 0 )
mbedtls_printf( "failed\n" );
- return( 1 );
+ ret = 1;
+ goto cleanup;
}
if( verbose != 0 )
diff --git a/thirdparty/mbedtls/library/sha1.c b/thirdparty/mbedtls/library/sha1.c
index 8432eba8b..1f29a0fbf 100644
--- a/thirdparty/mbedtls/library/sha1.c
+++ b/thirdparty/mbedtls/library/sha1.c
@@ -111,6 +111,13 @@ int mbedtls_sha1_starts_ret( mbedtls_sha1_context *ctx )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha1_starts( mbedtls_sha1_context *ctx )
+{
+ mbedtls_sha1_starts_ret( ctx );
+}
+#endif
+
#if !defined(MBEDTLS_SHA1_PROCESS_ALT)
int mbedtls_internal_sha1_process( mbedtls_sha1_context *ctx,
const unsigned char data[64] )
@@ -270,6 +277,14 @@ int mbedtls_internal_sha1_process( mbedtls_sha1_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha1_process( mbedtls_sha1_context *ctx,
+ const unsigned char data[64] )
+{
+ mbedtls_internal_sha1_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_SHA1_PROCESS_ALT */
/*
@@ -322,6 +337,15 @@ int mbedtls_sha1_update_ret( mbedtls_sha1_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha1_update( mbedtls_sha1_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_sha1_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char sha1_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -365,6 +389,14 @@ int mbedtls_sha1_finish_ret( mbedtls_sha1_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha1_finish( mbedtls_sha1_context *ctx,
+ unsigned char output[20] )
+{
+ mbedtls_sha1_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_SHA1_ALT */
/*
@@ -394,6 +426,15 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha1( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[20] )
+{
+ mbedtls_sha1_ret( input, ilen, output );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
* FIPS-180-1 test vectors
diff --git a/thirdparty/mbedtls/library/sha256.c b/thirdparty/mbedtls/library/sha256.c
index abcd64d13..f39bcbab6 100644
--- a/thirdparty/mbedtls/library/sha256.c
+++ b/thirdparty/mbedtls/library/sha256.c
@@ -135,6 +135,14 @@ int mbedtls_sha256_starts_ret( mbedtls_sha256_context *ctx, int is224 )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha256_starts( mbedtls_sha256_context *ctx,
+ int is224 )
+{
+ mbedtls_sha256_starts_ret( ctx, is224 );
+}
+#endif
+
#if !defined(MBEDTLS_SHA256_PROCESS_ALT)
static const uint32_t K[] =
{
@@ -238,6 +246,14 @@ int mbedtls_internal_sha256_process( mbedtls_sha256_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha256_process( mbedtls_sha256_context *ctx,
+ const unsigned char data[64] )
+{
+ mbedtls_internal_sha256_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_SHA256_PROCESS_ALT */
/*
@@ -290,6 +306,15 @@ int mbedtls_sha256_update_ret( mbedtls_sha256_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha256_update( mbedtls_sha256_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_sha256_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char sha256_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -339,6 +364,14 @@ int mbedtls_sha256_finish_ret( mbedtls_sha256_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha256_finish( mbedtls_sha256_context *ctx,
+ unsigned char output[32] )
+{
+ mbedtls_sha256_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_SHA256_ALT */
/*
@@ -369,6 +402,16 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha256( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[32],
+ int is224 )
+{
+ mbedtls_sha256_ret( input, ilen, output, is224 );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
* FIPS-180-2 test vectors
diff --git a/thirdparty/mbedtls/library/sha512.c b/thirdparty/mbedtls/library/sha512.c
index c99b6da95..97cee07c5 100644
--- a/thirdparty/mbedtls/library/sha512.c
+++ b/thirdparty/mbedtls/library/sha512.c
@@ -149,6 +149,14 @@ int mbedtls_sha512_starts_ret( mbedtls_sha512_context *ctx, int is384 )
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha512_starts( mbedtls_sha512_context *ctx,
+ int is384 )
+{
+ mbedtls_sha512_starts_ret( ctx, is384 );
+}
+#endif
+
#if !defined(MBEDTLS_SHA512_PROCESS_ALT)
/*
@@ -269,6 +277,14 @@ int mbedtls_internal_sha512_process( mbedtls_sha512_context *ctx,
return( 0 );
}
+
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha512_process( mbedtls_sha512_context *ctx,
+ const unsigned char data[128] )
+{
+ mbedtls_internal_sha512_process( ctx, data );
+}
+#endif
#endif /* !MBEDTLS_SHA512_PROCESS_ALT */
/*
@@ -320,6 +336,15 @@ int mbedtls_sha512_update_ret( mbedtls_sha512_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha512_update( mbedtls_sha512_context *ctx,
+ const unsigned char *input,
+ size_t ilen )
+{
+ mbedtls_sha512_update_ret( ctx, input, ilen );
+}
+#endif
+
static const unsigned char sha512_padding[128] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -375,6 +400,14 @@ int mbedtls_sha512_finish_ret( mbedtls_sha512_context *ctx,
return( 0 );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha512_finish( mbedtls_sha512_context *ctx,
+ unsigned char output[64] )
+{
+ mbedtls_sha512_finish_ret( ctx, output );
+}
+#endif
+
#endif /* !MBEDTLS_SHA512_ALT */
/*
@@ -405,6 +438,16 @@ exit:
return( ret );
}
+#if !defined(MBEDTLS_DEPRECATED_REMOVED)
+void mbedtls_sha512( const unsigned char *input,
+ size_t ilen,
+ unsigned char output[64],
+ int is384 )
+{
+ mbedtls_sha512_ret( input, ilen, output, is384 );
+}
+#endif
+
#if defined(MBEDTLS_SELF_TEST)
/*
diff --git a/thirdparty/mbedtls/library/ssl_cli.c b/thirdparty/mbedtls/library/ssl_cli.c
index 2534346a4..88864b813 100644
--- a/thirdparty/mbedtls/library/ssl_cli.c
+++ b/thirdparty/mbedtls/library/ssl_cli.c
@@ -902,6 +902,8 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl )
*p++ = (unsigned char)( ciphersuites[i] );
}
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "client hello, got %d ciphersuites (excluding SCSVs)", n ) );
+
/*
* Add TLS_EMPTY_RENEGOTIATION_INFO_SCSV
*/
@@ -909,6 +911,7 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl )
if( ssl->renego_status == MBEDTLS_SSL_INITIAL_HANDSHAKE )
#endif
{
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "adding EMPTY_RENEGOTIATION_INFO_SCSV" ) );
*p++ = (unsigned char)( MBEDTLS_SSL_EMPTY_RENEGOTIATION_INFO >> 8 );
*p++ = (unsigned char)( MBEDTLS_SSL_EMPTY_RENEGOTIATION_INFO );
n++;
@@ -928,8 +931,6 @@ static int ssl_write_client_hello( mbedtls_ssl_context *ssl )
*q++ = (unsigned char)( n >> 7 );
*q++ = (unsigned char)( n << 1 );
- MBEDTLS_SSL_DEBUG_MSG( 3, ( "client hello, got %d ciphersuites", n ) );
-
#if defined(MBEDTLS_ZLIB_SUPPORT)
offer_compress = 1;
#else
@@ -2057,10 +2058,16 @@ static int ssl_parse_server_psk_hint( mbedtls_ssl_context *ssl,
*
* opaque psk_identity_hint<0..2^16-1>;
*/
+ if( (*p) > end - 2 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message "
+ "(psk_identity_hint length)" ) );
+ return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE );
+ }
len = (*p)[0] << 8 | (*p)[1];
*p += 2;
- if( (*p) + len > end )
+ if( (*p) > end - len )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message "
"(psk_identity_hint length)" ) );
@@ -2478,10 +2485,18 @@ static int ssl_parse_server_key_exchange( mbedtls_ssl_context *ssl )
/*
* Read signature
*/
+
+ if( p > end - 2 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) );
+ mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
+ MBEDTLS_SSL_ALERT_MSG_DECODE_ERROR );
+ return( MBEDTLS_ERR_SSL_BAD_HS_SERVER_KEY_EXCHANGE );
+ }
sig_len = ( p[0] << 8 ) | p[1];
p += 2;
- if( end != p + sig_len )
+ if( p != end - sig_len )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad server key exchange message" ) );
mbedtls_ssl_send_alert_message( ssl, MBEDTLS_SSL_ALERT_LEVEL_FATAL,
diff --git a/thirdparty/mbedtls/library/ssl_tls.c b/thirdparty/mbedtls/library/ssl_tls.c
index 617dedb1b..236e52d76 100644
--- a/thirdparty/mbedtls/library/ssl_tls.c
+++ b/thirdparty/mbedtls/library/ssl_tls.c
@@ -501,6 +501,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
unsigned char *key2;
unsigned char *mac_enc;
unsigned char *mac_dec;
+ size_t mac_key_len;
size_t iv_copy_len;
const mbedtls_cipher_info_t *cipher_info;
const mbedtls_md_info_t *md_info;
@@ -692,6 +693,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
cipher_info->mode == MBEDTLS_MODE_CCM )
{
transform->maclen = 0;
+ mac_key_len = 0;
transform->ivlen = 12;
transform->fixed_ivlen = 4;
@@ -712,7 +714,8 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
}
/* Get MAC length */
- transform->maclen = mbedtls_md_get_size( md_info );
+ mac_key_len = mbedtls_md_get_size( md_info );
+ transform->maclen = mac_key_len;
#if defined(MBEDTLS_SSL_TRUNCATED_HMAC)
/*
@@ -721,7 +724,16 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
* so we only need to adjust the length here.
*/
if( session->trunc_hmac == MBEDTLS_SSL_TRUNC_HMAC_ENABLED )
+ {
transform->maclen = MBEDTLS_SSL_TRUNCATED_HMAC_LEN;
+
+#if defined(MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT)
+ /* Fall back to old, non-compliant version of the truncated
+ * HMAC implementation which also truncates the key
+ * (Mbed TLS versions from 1.3 to 2.6.0) */
+ mac_key_len = transform->maclen;
+#endif
+ }
#endif /* MBEDTLS_SSL_TRUNCATED_HMAC */
/* IV length */
@@ -783,11 +795,11 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
#if defined(MBEDTLS_SSL_CLI_C)
if( ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT )
{
- key1 = keyblk + transform->maclen * 2;
- key2 = keyblk + transform->maclen * 2 + transform->keylen;
+ key1 = keyblk + mac_key_len * 2;
+ key2 = keyblk + mac_key_len * 2 + transform->keylen;
mac_enc = keyblk;
- mac_dec = keyblk + transform->maclen;
+ mac_dec = keyblk + mac_key_len;
/*
* This is not used in TLS v1.1.
@@ -803,10 +815,10 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
#if defined(MBEDTLS_SSL_SRV_C)
if( ssl->conf->endpoint == MBEDTLS_SSL_IS_SERVER )
{
- key1 = keyblk + transform->maclen * 2 + transform->keylen;
- key2 = keyblk + transform->maclen * 2;
+ key1 = keyblk + mac_key_len * 2 + transform->keylen;
+ key2 = keyblk + mac_key_len * 2;
- mac_enc = keyblk + transform->maclen;
+ mac_enc = keyblk + mac_key_len;
mac_dec = keyblk;
/*
@@ -828,14 +840,14 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
#if defined(MBEDTLS_SSL_PROTO_SSL3)
if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 )
{
- if( transform->maclen > sizeof transform->mac_enc )
+ if( mac_key_len > sizeof transform->mac_enc )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
- memcpy( transform->mac_enc, mac_enc, transform->maclen );
- memcpy( transform->mac_dec, mac_dec, transform->maclen );
+ memcpy( transform->mac_enc, mac_enc, mac_key_len );
+ memcpy( transform->mac_dec, mac_dec, mac_key_len );
}
else
#endif /* MBEDTLS_SSL_PROTO_SSL3 */
@@ -843,8 +855,8 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
defined(MBEDTLS_SSL_PROTO_TLS1_2)
if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 )
{
- mbedtls_md_hmac_starts( &transform->md_ctx_enc, mac_enc, transform->maclen );
- mbedtls_md_hmac_starts( &transform->md_ctx_dec, mac_dec, transform->maclen );
+ mbedtls_md_hmac_starts( &transform->md_ctx_enc, mac_enc, mac_key_len );
+ mbedtls_md_hmac_starts( &transform->md_ctx_dec, mac_dec, mac_key_len );
}
else
#endif
@@ -864,7 +876,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
transform->iv_enc, transform->iv_dec,
iv_copy_len,
mac_enc, mac_dec,
- transform->maclen ) ) != 0 )
+ mac_key_len ) ) != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_hw_record_init", ret );
return( MBEDTLS_ERR_SSL_HW_ACCEL_FAILED );
@@ -877,7 +889,7 @@ int mbedtls_ssl_derive_keys( mbedtls_ssl_context *ssl )
{
ssl->conf->f_export_keys( ssl->conf->p_export_keys,
session->master, keyblk,
- transform->maclen, transform->keylen,
+ mac_key_len, transform->keylen,
iv_copy_len );
}
#endif
@@ -7673,8 +7685,14 @@ int mbedtls_ssl_config_defaults( mbedtls_ssl_config *conf,
* Default
*/
default:
- conf->min_major_ver = MBEDTLS_SSL_MAJOR_VERSION_3;
- conf->min_minor_ver = MBEDTLS_SSL_MINOR_VERSION_1; /* TLS 1.0 */
+ conf->min_major_ver = ( MBEDTLS_SSL_MIN_MAJOR_VERSION >
+ MBEDTLS_SSL_MIN_VALID_MAJOR_VERSION ) ?
+ MBEDTLS_SSL_MIN_MAJOR_VERSION :
+ MBEDTLS_SSL_MIN_VALID_MAJOR_VERSION;
+ conf->min_minor_ver = ( MBEDTLS_SSL_MIN_MINOR_VERSION >
+ MBEDTLS_SSL_MIN_VALID_MINOR_VERSION ) ?
+ MBEDTLS_SSL_MIN_MINOR_VERSION :
+ MBEDTLS_SSL_MIN_VALID_MINOR_VERSION;
conf->max_major_ver = MBEDTLS_SSL_MAX_MAJOR_VERSION;
conf->max_minor_ver = MBEDTLS_SSL_MAX_MINOR_VERSION;
diff --git a/thirdparty/mbedtls/library/version.c b/thirdparty/mbedtls/library/version.c
index 6ca80d469..fd9675088 100644
--- a/thirdparty/mbedtls/library/version.c
+++ b/thirdparty/mbedtls/library/version.c
@@ -30,7 +30,7 @@
#include "mbedtls/version.h"
#include <string.h>
-unsigned int mbedtls_version_get_number()
+unsigned int mbedtls_version_get_number( void )
{
return( MBEDTLS_VERSION_NUMBER );
}
diff --git a/thirdparty/mbedtls/library/version_features.c b/thirdparty/mbedtls/library/version_features.c
index 72afec2da..da47e3d75 100644
--- a/thirdparty/mbedtls/library/version_features.c
+++ b/thirdparty/mbedtls/library/version_features.c
@@ -468,6 +468,9 @@ static const char *features[] = {
#if defined(MBEDTLS_SSL_TRUNCATED_HMAC)
"MBEDTLS_SSL_TRUNCATED_HMAC",
#endif /* MBEDTLS_SSL_TRUNCATED_HMAC */
+#if defined(MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT)
+ "MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT",
+#endif /* MBEDTLS_SSL_TRUNCATED_HMAC_COMPAT */
#if defined(MBEDTLS_THREADING_ALT)
"MBEDTLS_THREADING_ALT",
#endif /* MBEDTLS_THREADING_ALT */
diff --git a/thirdparty/mbedtls/library/x509_crl.c b/thirdparty/mbedtls/library/x509_crl.c
index 55d12acd0..b0f39d428 100644
--- a/thirdparty/mbedtls/library/x509_crl.c
+++ b/thirdparty/mbedtls/library/x509_crl.c
@@ -95,17 +95,23 @@ static int x509_crl_get_version( unsigned char **p,
}
/*
- * X.509 CRL v2 extensions (no extensions parsed yet.)
+ * X.509 CRL v2 extensions
+ *
+ * We currently don't parse any extension's content, but we do check that the
+ * list of extensions is well-formed and abort on critical extensions (that
+ * are unsupported as we don't support any extension so far)
*/
static int x509_get_crl_ext( unsigned char **p,
const unsigned char *end,
mbedtls_x509_buf *ext )
{
int ret;
- size_t len = 0;
- /* Get explicit tag */
- if( ( ret = mbedtls_x509_get_ext( p, end, ext, 0) ) != 0 )
+ /*
+ * crlExtensions [0] EXPLICIT Extensions OPTIONAL
+ * -- if present, version MUST be v2
+ */
+ if( ( ret = mbedtls_x509_get_ext( p, end, ext, 0 ) ) != 0 )
{
if( ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG )
return( 0 );
@@ -115,11 +121,54 @@ static int x509_get_crl_ext( unsigned char **p,
while( *p < end )
{
+ /*
+ * Extension ::= SEQUENCE {
+ * extnID OBJECT IDENTIFIER,
+ * critical BOOLEAN DEFAULT FALSE,
+ * extnValue OCTET STRING }
+ */
+ int is_critical = 0;
+ const unsigned char *end_ext_data;
+ size_t len;
+
+ /* Get enclosing sequence tag */
if( ( ret = mbedtls_asn1_get_tag( p, end, &len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+ end_ext_data = *p + len;
+
+ /* Get OID (currently ignored) */
+ if( ( ret = mbedtls_asn1_get_tag( p, end_ext_data, &len,
+ MBEDTLS_ASN1_OID ) ) != 0 )
+ {
+ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+ }
+ *p += len;
+
+ /* Get optional critical */
+ if( ( ret = mbedtls_asn1_get_bool( p, end_ext_data,
+ &is_critical ) ) != 0 &&
+ ( ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ) )
+ {
+ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+ }
+
+ /* Data should be octet string type */
+ if( ( ret = mbedtls_asn1_get_tag( p, end_ext_data, &len,
+ MBEDTLS_ASN1_OCTET_STRING ) ) != 0 )
+ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
+
+ /* Ignore data so far and just check its length */
*p += len;
+ if( *p != end_ext_data )
+ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
+ MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
+
+ /* Abort on (unsupported) critical extensions */
+ if( is_critical )
+ return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
+ MBEDTLS_ERR_ASN1_UNEXPECTED_TAG );
}
if( *p != end )
@@ -257,7 +306,7 @@ int mbedtls_x509_crl_parse_der( mbedtls_x509_crl *chain,
{
int ret;
size_t len;
- unsigned char *p, *end;
+ unsigned char *p = NULL, *end = NULL;
mbedtls_x509_buf sig_params1, sig_params2, sig_oid2;
mbedtls_x509_crl *crl = chain;
@@ -294,7 +343,11 @@ int mbedtls_x509_crl_parse_der( mbedtls_x509_crl *chain,
/*
* Copy raw DER-encoded CRL
*/
- if( ( p = mbedtls_calloc( 1, buflen ) ) == NULL )
+ if( buflen == 0 )
+ return( MBEDTLS_ERR_X509_INVALID_FORMAT );
+
+ p = mbedtls_calloc( 1, buflen );
+ if( p == NULL )
return( MBEDTLS_ERR_X509_ALLOC_FAILED );
memcpy( p, buf, buflen );
diff --git a/thirdparty/mbedtls/library/x509_crt.c b/thirdparty/mbedtls/library/x509_crt.c
index 6c592fdb5..2a5dbb878 100644
--- a/thirdparty/mbedtls/library/x509_crt.c
+++ b/thirdparty/mbedtls/library/x509_crt.c
@@ -62,16 +62,17 @@
#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
#include <windows.h>
-#if _MSC_VER <= 1600
-/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and <intsafe.h> are included, as they
- * redefine a number of <TYPE>_MAX constants. These constants are guaranteed to be the same, though, so
- * we suppress the warning when including intsafe.h.
+#if defined(_MSC_VER) && _MSC_VER <= 1600
+/* Visual Studio 2010 and earlier issue a warning when both <stdint.h> and
+ * <intsafe.h> are included, as they redefine a number of <TYPE>_MAX constants.
+ * These constants are guaranteed to be the same, though, so we suppress the
+ * warning when including intsafe.h.
*/
#pragma warning( push )
#pragma warning( disable : 4005 )
#endif
#include <intsafe.h>
-#if _MSC_VER <= 1600
+#if defined(_MSC_VER) && _MSC_VER <= 1600
#pragma warning( pop )
#endif
#else
@@ -145,7 +146,8 @@ const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_suiteb =
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ),
/* Only ECDSA */
- MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ),
+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECDSA ) |
+ MBEDTLS_X509_ID_FLAG( MBEDTLS_PK_ECKEY ),
#if defined(MBEDTLS_ECP_C)
/* Only NIST P-256 and P-384 */
MBEDTLS_X509_ID_FLAG( MBEDTLS_ECP_DP_SECP256R1 ) |
@@ -484,9 +486,12 @@ static int x509_get_subject_alt_name( unsigned char **p,
if( ( ret = mbedtls_asn1_get_len( p, end, &tag_len ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
- if( ( tag & MBEDTLS_ASN1_CONTEXT_SPECIFIC ) != MBEDTLS_ASN1_CONTEXT_SPECIFIC )
+ if( ( tag & MBEDTLS_ASN1_TAG_CLASS_MASK ) !=
+ MBEDTLS_ASN1_CONTEXT_SPECIFIC )
+ {
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
MBEDTLS_ERR_ASN1_UNEXPECTED_TAG );
+ }
/* Skip everything but DNS name */
if( tag != ( MBEDTLS_ASN1_CONTEXT_SPECIFIC | 2 ) )
@@ -1140,6 +1145,14 @@ int mbedtls_x509_crt_parse_path( mbedtls_x509_crt *chain, const char *path )
if ( FAILED ( SizeTToInt( len, &lengthAsInt ) ) )
return( MBEDTLS_ERR_X509_FILE_IO_ERROR );
+ /*
+ * Note this function uses the code page CP_ACP, and assumes the incoming
+ * string is encoded in ANSI, before translating it into Unicode. If the
+ * incoming string were changed to be UTF-8, then the length check needs to
+ * change to check the number of characters, not the number of bytes, in the
+ * incoming string are less than MAX_PATH to avoid a buffer overrun with
+ * MultiByteToWideChar().
+ */
w_ret = MultiByteToWideChar( CP_ACP, 0, filename, lengthAsInt, szDir,
MAX_PATH - 3 );
if( w_ret == 0 )