瀏覽代碼

Merge pull request #69400 from akien-mga/3.x-cherrypicks

Cherry-picks for the 3.x branch (future 3.6) - 6th batch
Rémi Verschelde 2 年之前
父節點
當前提交
87296b3ea5
共有 41 個文件被更改,包括 177 次插入112 次删除
  1. 1 0
      core/image.cpp
  2. 1 1
      core/os/dir_access.cpp
  3. 0 5
      core/os/midi_driver.cpp
  4. 1 0
      core/project_settings.cpp
  5. 1 1
      doc/classes/AudioStreamGeneratorPlayback.xml
  6. 1 1
      doc/classes/InputEventMIDI.xml
  7. 1 0
      doc/classes/PopupMenu.xml
  8. 4 4
      drivers/gles2/shaders/tonemap.glsl
  9. 4 4
      drivers/gles3/shaders/tonemap.glsl
  10. 1 1
      drivers/windows/dir_access_windows.cpp
  11. 2 0
      editor/collada/collada.cpp
  12. 6 1
      editor/editor_file_system.cpp
  13. 1 1
      editor/editor_node.cpp
  14. 1 2
      editor/find_in_files.cpp
  15. 6 0
      editor/import/editor_import_collada.cpp
  16. 9 8
      editor/plugins/script_text_editor.cpp
  17. 2 1
      editor/plugins/spatial_editor_plugin.cpp
  18. 7 3
      editor/plugins/sprite_frames_editor_plugin.cpp
  19. 18 17
      editor/project_manager.cpp
  20. 15 1
      editor/script_editor_debugger.cpp
  21. 7 1
      modules/tga/image_loader_tga.cpp
  22. 5 1
      platform/android/android_keys_utils.h
  23. 1 1
      platform/android/java/lib/src/org/godotengine/godot/Godot.java
  24. 3 3
      platform/iphone/export/export.cpp
  25. 5 4
      platform/osx/export/export.cpp
  26. 4 2
      platform/osx/os_osx.mm
  27. 2 5
      platform/windows/os_windows.cpp
  28. 4 1
      platform/x11/detect.py
  29. 4 1
      platform/x11/detect_prime.cpp
  30. 9 7
      scene/3d/skeleton.cpp
  31. 5 2
      scene/gui/color_picker.cpp
  32. 5 1
      scene/main/node.cpp
  33. 10 6
      scene/property_utils.cpp
  34. 2 1
      scene/resources/primitive_meshes.cpp
  35. 1 1
      thirdparty/README.md
  36. 11 14
      thirdparty/recastnavigation/Recast/Include/Recast.h
  37. 3 3
      thirdparty/recastnavigation/Recast/Include/RecastAlloc.h
  38. 5 0
      thirdparty/recastnavigation/Recast/Source/Recast.cpp
  39. 0 2
      thirdparty/recastnavigation/Recast/Source/RecastMesh.cpp
  40. 1 1
      thirdparty/recastnavigation/Recast/Source/RecastMeshDetail.cpp
  41. 8 4
      thirdparty/recastnavigation/Recast/Source/RecastRasterization.cpp

+ 1 - 0
core/image.cpp

@@ -3098,6 +3098,7 @@ Ref<Image> Image::rgbe_to_srgb() {
 void Image::bumpmap_to_normalmap(float bump_scale) {
 	ERR_FAIL_COND(!_can_modify(format));
 	ERR_FAIL_COND_MSG(write_lock.ptr(), "Cannot modify image when it is locked.");
+	clear_mipmaps();
 	convert(Image::FORMAT_RF);
 
 	PoolVector<uint8_t> result_image; //rgba output

+ 1 - 1
core/os/dir_access.cpp

@@ -301,7 +301,7 @@ Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
 	const size_t copy_buffer_limit = 65536; // 64 KB
 
 	fsrc->seek_end(0);
-	int size = fsrc->get_position();
+	uint64_t size = fsrc->get_position();
 	fsrc->seek(0);
 	err = OK;
 	size_t buffer_size = MIN(size * sizeof(uint8_t), copy_buffer_limit);

+ 0 - 5
core/os/midi_driver.cpp

@@ -87,11 +87,6 @@ void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_
 			if (length >= 2 + param_position) {
 				event->set_pitch(data[param_position]);
 				event->set_velocity(data[param_position + 1]);
-
-				if (event->get_message() == MIDI_MESSAGE_NOTE_ON && event->get_velocity() == 0) {
-					// https://www.midi.org/forum/228-writing-midi-software-send-note-off,-or-zero-velocity-note-on
-					event->set_message(MIDI_MESSAGE_NOTE_OFF);
-				}
 			}
 			break;
 

+ 1 - 0
core/project_settings.cpp

@@ -1049,6 +1049,7 @@ ProjectSettings::ProjectSettings() {
 	// Initialization of engine variables should be done in the setup() method,
 	// so that the values can be overridden from project.godot or project.binary.
 
+	CRASH_COND_MSG(singleton != nullptr, "Instantiating a new ProjectSettings singleton is not supported.");
 	singleton = this;
 	last_order = NO_BUILTIN_ORDER_BASE;
 	last_builtin_order = 0;

+ 1 - 1
doc/classes/AudioStreamGeneratorPlayback.xml

@@ -27,7 +27,7 @@
 		<method name="get_frames_available" qualifiers="const">
 			<return type="int" />
 			<description>
-				Returns the number of audio data frames left to play. If this returned number reaches [code]0[/code], the audio will stop playing until frames are added again. Therefore, make sure your script can always generate and push new audio frames fast enough to avoid audio cracking.
+				Returns the number of frames that can be pushed to the audio sample data buffer without overflowing it. If the result is [code]0[/code], the buffer is full.
 			</description>
 		</method>
 		<method name="get_skips" qualifiers="const">

+ 1 - 1
doc/classes/InputEventMIDI.xml

@@ -42,7 +42,7 @@
 			The pressure of the MIDI signal. This value ranges from 0 to 127. For many devices, this value is always zero.
 		</member>
 		<member name="velocity" type="int" setter="set_velocity" getter="get_velocity" default="0">
-			The velocity of the MIDI signal. This value ranges from 0 to 127. For a piano, this corresponds to how quickly the key was pressed, and is rarely above about 110 in practice.
+			The velocity of the MIDI signal. This value ranges from 0 to 127. For a piano, this corresponds to how quickly the key was pressed, and is rarely above about 110 in practice. Note that some MIDI devices may send a [constant MIDI_MESSAGE_NOTE_ON] message with zero velocity and expect this to be treated the same as a [constant MIDI_MESSAGE_NOTE_OFF] message, but device implementations vary so Godot reports event data exactly as received.
 		</member>
 	</members>
 	<constants>

+ 1 - 0
doc/classes/PopupMenu.xml

@@ -493,6 +493,7 @@
 			<argument index="0" name="id" type="int" />
 			<description>
 				Emitted when an item of some [code]id[/code] is pressed or its accelerator is activated.
+				[b]Note:[/b] If [code]id[/code] is negative (either explicitly or due to overflow), this will return the correponding index instead.
 			</description>
 		</signal>
 		<signal name="index_pressed">

+ 4 - 4
drivers/gles2/shaders/tonemap.glsl

@@ -237,10 +237,10 @@ vec4 apply_fxaa(vec4 color, vec2 uv_interp, vec2 pixel_size) {
 	const float FXAA_SPAN_MAX = 8.0;
 	const vec3 luma = vec3(0.299, 0.587, 0.114);
 
-	vec4 rgbNW = texture2DLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0);
-	vec4 rgbNE = texture2DLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0);
-	vec4 rgbSW = texture2DLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0);
-	vec4 rgbSE = texture2DLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0);
+	vec4 rgbNW = textureLod(source, uv_interp + vec2(-0.5, -0.5) * pixel_size, 0.0);
+	vec4 rgbNE = textureLod(source, uv_interp + vec2(0.5, -0.5) * pixel_size, 0.0);
+	vec4 rgbSW = textureLod(source, uv_interp + vec2(-0.5, 0.5) * pixel_size, 0.0);
+	vec4 rgbSE = textureLod(source, uv_interp + vec2(0.5, 0.5) * pixel_size, 0.0);
 	vec3 rgbM = color.rgb;
 
 #ifdef DISABLE_ALPHA

+ 4 - 4
drivers/gles3/shaders/tonemap.glsl

@@ -325,10 +325,10 @@ vec4 apply_fxaa(vec4 color, float exposure, vec2 uv_interp, vec2 pixel_size) {
 	const float FXAA_SPAN_MAX = 8.0;
 	const vec3 luma = vec3(0.299, 0.587, 0.114);
 
-	vec4 rgbNW = textureLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0);
-	vec4 rgbNE = textureLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0);
-	vec4 rgbSW = textureLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0);
-	vec4 rgbSE = textureLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0);
+	vec4 rgbNW = textureLod(source, uv_interp + vec2(-0.5, -0.5) * pixel_size, 0.0);
+	vec4 rgbNE = textureLod(source, uv_interp + vec2(0.5, -0.5) * pixel_size, 0.0);
+	vec4 rgbSW = textureLod(source, uv_interp + vec2(-0.5, 0.5) * pixel_size, 0.0);
+	vec4 rgbSE = textureLod(source, uv_interp + vec2(0.5, 0.5) * pixel_size, 0.0);
 	vec3 rgbM = color.rgb;
 
 #ifdef DISABLE_ALPHA

+ 1 - 1
drivers/windows/dir_access_windows.cpp

@@ -154,7 +154,7 @@ Error DirAccessWindows::make_dir(String p_dir) {
 	if (p_dir.is_rel_path())
 		p_dir = current_dir.plus_file(p_dir);
 
-	p_dir = p_dir.replace("/", "\\");
+	p_dir = p_dir.simplify_path().replace("/", "\\");
 
 	bool success;
 	int err;

+ 2 - 0
editor/collada/collada.cpp

@@ -845,6 +845,8 @@ void Collada::_parse_curve_geometry(XMLParser &parser, String p_id, String p_nam
 
 	CurveData &curvedata = state.curve_data_map[p_id];
 	curvedata.name = p_name;
+	String closed = parser.get_attribute_value_safe("closed").to_lower();
+	curvedata.closed = closed == "true" || closed == "1";
 
 	COLLADA_PRINT("curve name: " + p_name);
 

+ 6 - 1
editor/editor_file_system.cpp

@@ -549,7 +549,12 @@ bool EditorFileSystem::_update_scan_actions() {
 				if (_test_for_reimport(full_path, false)) {
 					//must reimport
 					reimports.push_back(full_path);
-					reimports.append_array(_get_dependencies(full_path));
+					Vector<String> dependencies = _get_dependencies(full_path);
+					for (int i = 0; i < dependencies.size(); i++) {
+						if (import_extensions.has(dependencies[i].get_extension())) {
+							reimports.push_back(dependencies[i]);
+						}
+					}
 				} else {
 					//must not reimport, all was good
 					//update modified times, to avoid reimport

+ 1 - 1
editor/editor_node.cpp

@@ -3834,7 +3834,7 @@ void EditorNode::_quick_opened() {
 		List<String> scene_extensions;
 		ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions);
 
-		if (open_scene_dialog || scene_extensions.find(files[i].get_extension())) {
+		if (open_scene_dialog || scene_extensions.find(files[i].get_extension().to_lower())) {
 			open_request(res_path);
 		} else {
 			load_resource(res_path);

+ 1 - 2
editor/find_in_files.cpp

@@ -431,8 +431,7 @@ void FindInFilesDialog::set_find_in_files_mode(FindInFilesMode p_mode) {
 }
 
 String FindInFilesDialog::get_search_text() const {
-	String text = _search_text_line_edit->get_text();
-	return text.strip_edges();
+	return _search_text_line_edit->get_text();
 }
 
 String FindInFilesDialog::get_replace_text() const {

+ 6 - 0
editor/import/editor_import_collada.cpp

@@ -1074,6 +1074,12 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, uint32_t p_use_com
 						c->set_point_tilt(i, tilts->array[i]);
 					}
 				}
+				if (cd.closed && pc > 1) {
+					Vector3 pos = c->get_point_position(0);
+					Vector3 in = c->get_point_in(0);
+					Vector3 out = c->get_point_out(0);
+					c->add_point(pos, in, out, -1);
+				}
 
 				curve_cache[ng->source] = c;
 				path->set_curve(c);

+ 9 - 8
editor/plugins/script_text_editor.cpp

@@ -1478,16 +1478,17 @@ bool ScriptTextEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_
 }
 
 static Node *_find_script_node(Node *p_edited_scene, Node *p_current_node, const Ref<Script> &script) {
-	if (p_edited_scene != p_current_node && p_current_node->get_owner() != p_edited_scene) {
-		return nullptr;
-	}
-
-	Ref<Script> scr = p_current_node->get_script();
-
-	if (scr.is_valid() && scr == script) {
-		return p_current_node;
+	// Check scripts only for the nodes belonging to the edited scene.
+	if (p_current_node == p_edited_scene || p_current_node->get_owner() == p_edited_scene) {
+		Ref<Script> scr = p_current_node->get_script();
+		if (scr.is_valid() && scr == script) {
+			return p_current_node;
+		}
 	}
 
+	// Traverse all children, even the ones not owned by the edited scene as they
+	// can still have child nodes added within the edited scene and thus owned by
+	// it (e.g. nodes added to subscene's root or to its editable children).
 	for (int i = 0; i < p_current_node->get_child_count(); i++) {
 		Node *n = _find_script_node(p_edited_scene, p_current_node->get_child(i), script);
 		if (n) {

+ 2 - 1
editor/plugins/spatial_editor_plugin.cpp

@@ -4151,7 +4151,8 @@ bool SpatialEditorViewport::can_drop_data_fw(const Point2 &p_point, const Varian
 			ResourceLoader::get_recognized_extensions_for_type("Mesh", &mesh_extensions);
 
 			for (int i = 0; i < files.size(); i++) {
-				if (mesh_extensions.find(files[i].get_extension()) || scene_extensions.find(files[i].get_extension())) {
+				String extension = files[i].get_extension().to_lower();
+				if (mesh_extensions.find(extension) || scene_extensions.find(extension)) {
 					RES res = ResourceLoader::load(files[i]);
 					if (res.is_null()) {
 						continue;

+ 7 - 3
editor/plugins/sprite_frames_editor_plugin.cpp

@@ -67,14 +67,18 @@ int SpriteFramesEditor::_sheet_preview_position_to_frame_index(const Point2 &p_p
 	const Size2i block_size = frame_size + separation;
 	const Point2i position = p_position / sheet_zoom - offset;
 
-	if (position.x % block_size.x > frame_size.x || position.y % block_size.y > frame_size.y) {
+	if (position.x < 0 || position.y < 0) {
+		return -1; // Out of bounds.
+	}
+
+	if (position.x % block_size.x >= frame_size.x || position.y % block_size.y >= frame_size.y) {
 		return -1; // Gap between frames.
 	}
 
 	const Point2i frame = position / block_size;
 	const Size2i frame_count = _get_frame_count();
-	if (frame.x < 0 || frame.y < 0 || frame.x >= frame_count.x || frame.y >= frame_count.y) {
-		return -1; // Out of bound.
+	if (frame.x >= frame_count.x || frame.y >= frame_count.y) {
+		return -1; // Out of bounds.
 	}
 
 	return frame_count.x * frame.y + frame.x;

+ 18 - 17
editor/project_manager.cpp

@@ -431,17 +431,17 @@ private:
 				return;
 			}
 
-			ProjectSettings *current = memnew(ProjectSettings);
-
-			int err = current->setup(dir2, "");
+			// Load project.godot as ConfigFile to set the new name.
+			ConfigFile cfg;
+			String project_godot = dir2.plus_file("project.godot");
+			Error err = cfg.load(project_godot);
 			if (err != OK) {
-				set_message(vformat(TTR("Couldn't load project.godot in project path (error %d). It may be missing or corrupted."), err), MESSAGE_ERROR);
+				set_message(vformat(TTR("Couldn't load project at '%s' (error %d). It may be missing or corrupted."), project_godot, err), MESSAGE_ERROR);
 			} else {
-				ProjectSettings::CustomMap edited_settings;
-				edited_settings["application/config/name"] = project_name->get_text().strip_edges();
-
-				if (current->save_custom(dir2.plus_file("project.godot"), edited_settings, Vector<String>(), true) != OK) {
-					set_message(TTR("Couldn't edit project.godot in project path."), MESSAGE_ERROR);
+				cfg.set_value("application", "config/name", project_name->get_text().strip_edges());
+				err = cfg.save(project_godot);
+				if (err != OK) {
+					set_message(vformat(TTR("Couldn't save project at '%s' (error %d)."), project_godot, err), MESSAGE_ERROR);
 				}
 			}
 
@@ -689,18 +689,19 @@ public:
 			rasterizer_container->hide();
 			get_ok()->set_disabled(false);
 
-			ProjectSettings *current = memnew(ProjectSettings);
-
-			int err = current->setup(project_path->get_text(), "");
+			// Fetch current name from project.godot to prefill the text input.
+			ConfigFile cfg;
+			String project_godot = project_path->get_text().plus_file("project.godot");
+			Error err = cfg.load(project_godot);
 			if (err != OK) {
-				set_message(vformat(TTR("Couldn't load project.godot in project path (error %d). It may be missing or corrupted."), err), MESSAGE_ERROR);
+				set_message(vformat(TTR("Couldn't load project at '%s' (error %d). It may be missing or corrupted."), project_godot, err), MESSAGE_ERROR);
 				status_rect->show();
 				msg->show();
 				get_ok()->set_disabled(true);
-			} else if (current->has_setting("application/config/name")) {
-				String proj = current->get("application/config/name");
-				project_name->set_text(proj);
-				_text_changed(proj);
+			} else {
+				String cur_name = cfg.get_value("application", "config/name", "");
+				project_name->set_text(cur_name);
+				_text_changed(cur_name);
 			}
 
 			project_name->call_deferred("grab_focus");

+ 15 - 1
editor/script_editor_debugger.cpp

@@ -2288,8 +2288,22 @@ void ScriptEditorDebugger::_item_menu_id_pressed(int p_option) {
 				ti = ti->get_parent();
 			}
 
-			// We only need the first child here (C++ source stack trace).
+			// Find the child with the "C++ Source".
+			// It's not at a fixed position as "C++ Error" may come first.
 			TreeItem *ci = ti->get_children();
+			const String cpp_source = "<" + TTR("C++ Source") + ">";
+			while (ci) {
+				if (ci->get_text(0) == cpp_source) {
+					break;
+				}
+				ci = ci->get_next();
+			}
+
+			if (!ci) {
+				WARN_PRINT("No C++ source reference is available for this error.");
+				return;
+			}
+
 			// Parse back the `file:line @ method()` string.
 			const Vector<String> file_line_number = ci->get_text(1).split("@")[0].strip_edges().split(":");
 			ERR_FAIL_COND_MSG(file_line_number.size() < 2, "Incorrect C++ source stack trace file:line format (please report).");

+ 7 - 1
modules/tga/image_loader_tga.cpp

@@ -265,14 +265,21 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
 		err = FAILED;
 	}
 
+	uint64_t color_map_size;
 	if (has_color_map) {
 		if (tga_header.color_map_length > 256 || (tga_header.color_map_depth != 24) || tga_header.color_map_type != 1) {
 			err = FAILED;
 		}
+		color_map_size = tga_header.color_map_length * (tga_header.color_map_depth >> 3);
 	} else {
 		if (tga_header.color_map_type) {
 			err = FAILED;
 		}
+		color_map_size = 0;
+	}
+
+	if ((src_image_len - f->get_position()) < (tga_header.id_length + color_map_size)) {
+		err = FAILED; // TGA data appears to be truncated (fewer bytes than expected).
 	}
 
 	if (tga_header.image_width <= 0 || tga_header.image_height <= 0) {
@@ -289,7 +296,6 @@ Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force
 		PoolVector<uint8_t> palette;
 
 		if (has_color_map) {
-			size_t color_map_size = tga_header.color_map_length * (tga_header.color_map_depth >> 3);
 			err = palette.resize(color_map_size);
 			if (err == OK) {
 				PoolVector<uint8_t>::Write palette_w = palette.write();

+ 5 - 1
platform/android/android_keys_utils.h

@@ -43,7 +43,6 @@ struct AndroidGodotCodePair {
 
 static AndroidGodotCodePair android_godot_code_pairs[] = {
 	{ AKEYCODE_UNKNOWN, KEY_UNKNOWN }, // (0) Unknown key code.
-	{ AKEYCODE_HOME, KEY_HOME }, // (3) Home key.
 	{ AKEYCODE_BACK, KEY_BACK }, // (4) Back key.
 	{ AKEYCODE_0, KEY_0 }, // (7) '0' key.
 	{ AKEYCODE_1, KEY_1 }, // (8) '1' key.
@@ -63,6 +62,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
 	{ AKEYCODE_DPAD_RIGHT, KEY_RIGHT }, // (22) Directional Pad Right key.
 	{ AKEYCODE_VOLUME_UP, KEY_VOLUMEUP }, // (24) Volume Up key.
 	{ AKEYCODE_VOLUME_DOWN, KEY_VOLUMEDOWN }, // (25) Volume Down key.
+	{ AKEYCODE_POWER, KEY_STANDBY }, // (26) Power key.
 	{ AKEYCODE_CLEAR, KEY_CLEAR }, // (28) Clear key.
 	{ AKEYCODE_A, KEY_A }, // (29) 'A' key.
 	{ AKEYCODE_B, KEY_B }, // (30) 'B' key.
@@ -98,6 +98,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
 	{ AKEYCODE_SHIFT_RIGHT, KEY_SHIFT }, // (60) Right Shift modifier key.
 	{ AKEYCODE_TAB, KEY_TAB }, // (61) Tab key.
 	{ AKEYCODE_SPACE, KEY_SPACE }, // (62) Space key.
+	{ AKEYCODE_ENVELOPE, KEY_LAUNCHMAIL }, // (65) Envelope special function key.
 	{ AKEYCODE_ENTER, KEY_ENTER }, // (66) Enter key.
 	{ AKEYCODE_DEL, KEY_BACKSPACE }, // (67) Backspace key.
 	{ AKEYCODE_GRAVE, KEY_QUOTELEFT }, // (68) '`' (backtick) key.
@@ -114,6 +115,7 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
 	{ AKEYCODE_MENU, KEY_MENU }, // (82) Menu key.
 	{ AKEYCODE_SEARCH, KEY_SEARCH }, // (84) Search key.
 	{ AKEYCODE_MEDIA_STOP, KEY_MEDIASTOP }, // (86) Stop media key.
+	{ AKEYCODE_MEDIA_NEXT, KEY_MEDIANEXT }, // (87) Play Next media key.
 	{ AKEYCODE_MEDIA_PREVIOUS, KEY_MEDIAPREVIOUS }, // (88) Play Previous media key.
 	{ AKEYCODE_PAGE_UP, KEY_PAGEUP }, // (92) Page Up key.
 	{ AKEYCODE_PAGE_DOWN, KEY_PAGEDOWN }, // (93) Page Down key.
@@ -127,6 +129,8 @@ static AndroidGodotCodePair android_godot_code_pairs[] = {
 	{ AKEYCODE_META_RIGHT, KEY_META }, // (118) Right Meta modifier key.
 	{ AKEYCODE_SYSRQ, KEY_PRINT }, // (120) System Request / Print Screen key.
 	{ AKEYCODE_BREAK, KEY_PAUSE }, // (121) Break / Pause key.
+	{ AKEYCODE_MOVE_HOME, KEY_HOME }, // (122) Home Movement key.
+	{ AKEYCODE_MOVE_END, KEY_END }, // (123) End Movement key.
 	{ AKEYCODE_INSERT, KEY_INSERT }, // (124) Insert key.
 	{ AKEYCODE_FORWARD, KEY_FORWARD }, // (125) Forward key.
 	{ AKEYCODE_MEDIA_PLAY, KEY_MEDIAPLAY }, // (126) Play media key.

+ 1 - 1
platform/android/java/lib/src/org/godotengine/godot/Godot.java

@@ -392,7 +392,7 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
 			for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) {
 				plugin.onRegisterPluginWithGodotNative();
 			}
-			setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
+			setKeepScreenOn(Boolean.parseBoolean(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on")));
 
 			// The Godot Android plugins are setup on completion of GodotLib.setup
 			mainThreadHandler.post(() -> {

+ 3 - 3
platform/iphone/export/export.cpp

@@ -947,7 +947,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
 		Ref<Image> image;
 		String image_path = p_dest_dir.plus_file("[email protected]");
 		image.instance();
-		Error err = image->load(custom_launch_image_2x);
+		Error err = ImageLoader::load_image(custom_launch_image_2x, image);
 
 		if (err) {
 			image.unref();
@@ -961,7 +961,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
 		image.unref();
 		image_path = p_dest_dir.plus_file("[email protected]");
 		image.instance();
-		err = image->load(custom_launch_image_3x);
+		err = ImageLoader::load_image(custom_launch_image_3x, image);
 
 		if (err) {
 			image.unref();
@@ -978,7 +978,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
 
 		if (!splash_path.empty()) {
 			splash.instance();
-			const Error err = splash->load(splash_path);
+			const Error err = ImageLoader::load_image(splash_path, splash);
 			if (err) {
 				splash.unref();
 			}

+ 5 - 4
platform/osx/export/export.cpp

@@ -31,6 +31,7 @@
 #include "export.h"
 #include "codesign.h"
 
+#include "core/io/image_loader.h"
 #include "core/io/marshalls.h"
 #include "core/io/resource_saver.h"
 #include "core/io/zip_io.h"
@@ -813,7 +814,7 @@ Error EditorExportPlatformOSX::_code_sign(const Ref<EditorExportPreset> &p_prese
 			String str;
 			int exitcode = 0;
 
-			Error err = OS::get_singleton()->execute("codesign", args, true, NULL, &str, NULL, true);
+			Error err = OS::get_singleton()->execute("codesign", args, true, NULL, &str, &exitcode, true);
 			if (err != OK) {
 				add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("Could not start codesign executable, make sure Xcode command line tools are installed."));
 				return err;
@@ -1180,8 +1181,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
 				} else {
 					Ref<Image> icon;
 					icon.instance();
-					icon->load(iconpath);
-					if (!icon->empty()) {
+					err = ImageLoader::load_image(iconpath, icon);
+					if (err == OK && !icon->empty()) {
 						_make_icon(p_preset, icon, data);
 					}
 				}
@@ -1719,8 +1720,8 @@ bool EditorExportPlatformOSX::has_valid_project_configuration(const Ref<EditorEx
 				if (p_preset->get("notarization/apple_id_name") != "") {
 					if (p_preset->get("notarization/apple_id_password") == "") {
 						err += TTR("Notarization: Apple ID password not specified.") + "\n";
+						valid = false;
 					}
-					valid = false;
 				}
 				if (p_preset->get("notarization/api_uuid") != "") {
 					if (p_preset->get("notarization/api_key") == "") {

+ 4 - 2
platform/osx/os_osx.mm

@@ -137,7 +137,7 @@ static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
 	// special case handling of command-period, which is traditionally a special
 	// shortcut in macOS and doesn't arrive at our regular keyDown handler.
 	if ([event type] == NSEventTypeKeyDown) {
-		if (([event modifierFlags] & NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
+		if ((([event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask) == NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
 			Ref<InputEventKey> k;
 			k.instance();
 
@@ -195,7 +195,9 @@ static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
 
 - (void)applicationDidFinishLaunching:(NSNotification *)notice {
 	NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
-	if (nsappname == nil || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO)) {
+	NSString *nsbundleid_env = [NSString stringWithUTF8String:getenv("__CFBundleIdentifier")];
+	NSString *nsbundleid = [[NSBundle mainBundle] bundleIdentifier];
+	if (nsappname == nil || isatty(STDOUT_FILENO) || isatty(STDIN_FILENO) || isatty(STDERR_FILENO) || ![nsbundleid isEqualToString:nsbundleid_env]) {
 		// If the executable is started from terminal or is not bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
 		[self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
 	}

+ 2 - 5
platform/windows/os_windows.cpp

@@ -4003,11 +4003,8 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) {
 	//
 	// NOTE: The engine does not use ANSI escape codes to color error/warning messages; it uses Windows API calls instead.
 	// Therefore, error/warning messages are still colored on Windows versions older than 10.
-	HANDLE stdoutHandle;
-	stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
-	DWORD outMode = 0;
-	outMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
-
+	HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
+	DWORD outMode = ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
 	if (!SetConsoleMode(stdoutHandle, outMode)) {
 		// Windows 8.1 or below, or Windows 10 prior to Anniversary Update.
 		print_verbose("Can't set the ENABLE_VIRTUAL_TERMINAL_PROCESSING Windows console mode.");

+ 4 - 1
platform/x11/detect.py

@@ -400,7 +400,10 @@ def configure(env):
     if platform.system() == "Linux":
         env.Append(LIBS=["dl"])
 
-    if platform.system().find("BSD") >= 0:
+    if not env["execinfo"] and platform.libc_ver()[0] != "glibc":
+        # The default crash handler depends on glibc, so if the host uses
+        # a different libc (BSD libc, musl), fall back to libexecinfo.
+        print("Note: Using `execinfo=yes` for the crash handler as required on platforms where glibc is missing.")
         env["execinfo"] = True
 
     if env["execinfo"]:

+ 4 - 1
platform/x11/detect_prime.cpp

@@ -203,7 +203,10 @@ int detect_prime() {
 				print_verbose("Couldn't write vendor/renderer string.");
 			}
 			close(fdset[1]);
-			exit(0);
+
+			// The function quick_exit() is used because exit() will call destructors on static objects copied by fork().
+			// These objects will be freed anyway when the process finishes execution.
+			quick_exit(0);
 		}
 	}
 

+ 9 - 7
scene/3d/skeleton.cpp

@@ -707,10 +707,14 @@ void _pb_start_simulation(const Skeleton *p_skeleton, Node *p_node, const Vector
 	PhysicalBone *pb = Object::cast_to<PhysicalBone>(p_node);
 	if (pb) {
 		bool sim = false;
-		for (int i = p_sim_bones.size() - 1; 0 <= i; --i) {
-			if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
-				sim = true;
-				break;
+		if (p_sim_bones.empty()) { // If no bones is specified, activate ragdoll on full body.
+			sim = true;
+		} else {
+			for (int i = p_sim_bones.size() - 1; 0 <= i; --i) {
+				if (p_sim_bones[i] == pb->get_bone_id() || p_skeleton->is_bone_parent_of(pb->get_bone_id(), p_sim_bones[i])) {
+					sim = true;
+					break;
+				}
 			}
 		}
 
@@ -725,9 +729,7 @@ void _pb_start_simulation(const Skeleton *p_skeleton, Node *p_node, const Vector
 
 void Skeleton::physical_bones_start_simulation_on(const Array &p_bones) {
 	Vector<int> sim_bones;
-	if (p_bones.size() <= 0) {
-		sim_bones.push_back(0); // if no bones is specified, activate ragdoll on full body
-	} else {
+	if (p_bones.size() > 0) {
 		sim_bones.resize(p_bones.size());
 		int c = 0;
 		for (int i = sim_bones.size() - 1; 0 <= i; --i) {

+ 5 - 2
scene/gui/color_picker.cpp

@@ -195,12 +195,15 @@ void ColorPicker::_html_entered(const String &p_html) {
 		return;
 	}
 
-	float last_alpha = color.a;
+	Color previous_color = color;
 	color = Color::html(p_html);
 	if (!is_editing_alpha()) {
-		color.a = last_alpha;
+		color.a = previous_color.a;
 	}
 
+	if (color == previous_color) {
+		return;
+	}
 	if (!is_inside_tree()) {
 		return;
 	}

+ 5 - 1
scene/main/node.cpp

@@ -2887,10 +2887,14 @@ void Node::print_stray_nodes() {
 }
 
 void Node::queue_delete() {
+	// There are users which instantiate multiple scene trees for their games.
+	// Use the node's own tree to handle its deletion when relevant.
 	if (is_inside_tree()) {
 		get_tree()->queue_delete(this);
 	} else {
-		SceneTree::get_singleton()->queue_delete(this);
+		SceneTree *tree = SceneTree::get_singleton();
+		ERR_FAIL_NULL_MSG(tree, "Can't queue free a node when no SceneTree is available.");
+		tree->queue_delete(this);
 	}
 }
 

+ 10 - 6
scene/property_utils.cpp

@@ -167,8 +167,10 @@ static bool _collect_inheritance_chain(const Ref<SceneState> &p_state, const Nod
 		state = state->get_base_scene_state();
 	}
 
-	for (int i = inheritance_states.size() - 1; i >= 0; --i) {
-		r_states_stack.push_back(inheritance_states[i]);
+	if (inheritance_states.size() > 0) {
+		for (int i = inheritance_states.size() - 1; i >= 0; --i) {
+			r_states_stack.push_back(inheritance_states[i]);
+		}
 	}
 
 	return found;
@@ -212,10 +214,12 @@ Vector<SceneState::PackState> PropertyUtils::get_node_states_stack(const Node *p
 	{
 		states_stack_ret.resize(states_stack.size());
 		_FastPackState *ps = states_stack.ptr();
-		for (int i = states_stack.size() - 1; i >= 0; --i) {
-			states_stack_ret.write[i].state.reference_ptr(ps->state);
-			states_stack_ret.write[i].node = ps->node;
-			++ps;
+		if (states_stack.size() > 0) {
+			for (int i = states_stack.size() - 1; i >= 0; --i) {
+				states_stack_ret.write[i].state.reference_ptr(ps->state);
+				states_stack_ret.write[i].node = ps->node;
+				++ps;
+			}
 		}
 	}
 	return states_stack_ret;

+ 2 - 1
scene/resources/primitive_meshes.cpp

@@ -774,6 +774,7 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
 
 	thisrow = 0;
 	prevrow = 0;
+	const real_t side_normal_y = (bottom_radius - top_radius) / height;
 	for (j = 0; j <= (rings + 1); j++) {
 		v = j;
 		v /= (rings + 1);
@@ -792,7 +793,7 @@ void CylinderMesh::create_mesh_array(Array &p_arr, float top_radius, float botto
 
 			Vector3 p = Vector3(x * radius, y, z * radius);
 			points.push_back(p);
-			normals.push_back(Vector3(x, 0.0, z));
+			normals.push_back(Vector3(x, side_normal_y, z).normalized());
 			ADD_TANGENT(z, 0.0, -x, 1.0)
 			uvs.push_back(Vector2(u, v * 0.5));
 			point++;

+ 1 - 1
thirdparty/README.md

@@ -494,7 +494,7 @@ Files extracted from upstream source:
 ## recastnavigation
 
 - Upstream: https://github.com/recastnavigation/recastnavigation
-- Version: git (5a870d427e47abd4a8e4ce58a95582ec049434d5, 2022)
+- Version: git (4fef0446609b23d6ac180ed822817571525528a1, 2022)
 - License: zlib
 
 Files extracted from upstream source:

+ 11 - 14
thirdparty/recastnavigation/Recast/Include/Recast.h

@@ -22,13 +22,16 @@
 /// The value of PI used by Recast.
 static const float RC_PI = 3.14159265f;
 
+/// Used to ignore unused function parameters and silence any compiler warnings.
+template<class T> void rcIgnoreUnused(const T&) { }
+
 /// Recast log categories.
 /// @see rcContext
 enum rcLogCategory
 {
 	RC_LOG_PROGRESS = 1,	///< A progress log entry.
 	RC_LOG_WARNING,			///< A warning log entry.
-	RC_LOG_ERROR,			///< An error log entry.
+	RC_LOG_ERROR			///< An error log entry.
 };
 
 /// Recast performance timer categories.
@@ -101,7 +104,6 @@ enum rcTimerLabel
 class rcContext
 {
 public:
-
 	/// Contructor.
 	///  @param[in]		state	TRUE if the logging and performance timers should be enabled.  [Default: true]
 	inline rcContext(bool state = true) : m_logEnabled(state), m_timerEnabled(state) {}
@@ -140,31 +142,30 @@ public:
 	inline int getAccumulatedTime(const rcTimerLabel label) const { return m_timerEnabled ? doGetAccumulatedTime(label) : -1; }
 
 protected:
-
 	/// Clears all log entries.
-	virtual void doResetLog() {}
+	virtual void doResetLog();
 
 	/// Logs a message.
 	///  @param[in]		category	The category of the message.
 	///  @param[in]		msg			The formatted message.
 	///  @param[in]		len			The length of the formatted message.
-	virtual void doLog(const rcLogCategory /*category*/, const char* /*msg*/, const int /*len*/) {}
+	virtual void doLog(const rcLogCategory category, const char* msg, const int len) { rcIgnoreUnused(category); rcIgnoreUnused(msg); rcIgnoreUnused(len); }
 
 	/// Clears all timers. (Resets all to unused.)
 	virtual void doResetTimers() {}
 
 	/// Starts the specified performance timer.
 	///  @param[in]		label	The category of timer.
-	virtual void doStartTimer(const rcTimerLabel /*label*/) {}
+	virtual void doStartTimer(const rcTimerLabel label) { rcIgnoreUnused(label); }
 
 	/// Stops the specified performance timer.
 	///  @param[in]		label	The category of the timer.
-	virtual void doStopTimer(const rcTimerLabel /*label*/) {}
+	virtual void doStopTimer(const rcTimerLabel label) { rcIgnoreUnused(label); }
 
 	/// Returns the total accumulated time of the specified performance timer.
 	///  @param[in]		label	The category of the timer.
 	///  @return The accumulated time of the timer, or -1 if timers are disabled or the timer has never been started.
-	virtual int doGetAccumulatedTime(const rcTimerLabel /*label*/) const { return -1; }
+	virtual int doGetAccumulatedTime(const rcTimerLabel label) const { rcIgnoreUnused(label); return -1; }
 	
 	/// True if logging is enabled.
 	bool m_logEnabled;
@@ -564,7 +565,7 @@ static const int RC_AREA_BORDER = 0x20000;
 enum rcBuildContoursFlags
 {
 	RC_CONTOUR_TESS_WALL_EDGES = 0x01,	///< Tessellate solid (impassable) edges during contour simplification.
-	RC_CONTOUR_TESS_AREA_EDGES = 0x02,	///< Tessellate edges between areas during contour simplification.
+	RC_CONTOUR_TESS_AREA_EDGES = 0x02	///< Tessellate edges between areas during contour simplification.
 };
 
 /// Applied to the region id field of contour vertices in order to extract the region id.
@@ -595,11 +596,6 @@ static const int RC_NOT_CONNECTED = 0x3f;
 /// @name General helper functions
 /// @{
 
-/// Used to ignore a function parameter.  VS complains about unused parameters
-/// and this silences the warning.
-///  @param [in] _ Unused parameter
-template<class T> void rcIgnoreUnused(const T&) { }
-
 /// Swaps the values of the two parameters.
 ///  @param[in,out]	a	Value A
 ///  @param[in,out]	b	Value B
@@ -996,6 +992,7 @@ void rcMarkConvexPolyArea(rcContext* ctx, const float* verts, const int nverts,
 ///  @ingroup recast
 ///  @param[in]		verts		The vertices of the polygon [Form: (x, y, z) * @p nverts]
 ///  @param[in]		nverts		The number of vertices in the polygon.
+///  @param[in]		offset		How much to offset the polygon by. [Units: wu]
 ///  @param[out]	outVerts	The offset vertices (should hold up to 2 * @p nverts) [Form: (x, y, z) * return value]
 ///  @param[in]		maxOutVerts	The max number of vertices that can be stored to @p outVerts.
 ///  @returns Number of vertices in the offset polygon or 0 if too few vertices in @p outVerts.

+ 3 - 3
thirdparty/recastnavigation/Recast/Include/RecastAlloc.h

@@ -112,7 +112,7 @@ class rcVectorBase {
 	typedef rcSizeType size_type;
 	typedef T value_type;
 
-	rcVectorBase() : m_size(0), m_cap(0), m_data(0) {};
+	rcVectorBase() : m_size(0), m_cap(0), m_data(0) {}
 	rcVectorBase(const rcVectorBase<T, H>& other) : m_size(0), m_cap(0), m_data(0) { assign(other.begin(), other.end()); }
 	explicit rcVectorBase(rcSizeType count) : m_size(0), m_cap(0), m_data(0) { resize(count); }
 	rcVectorBase(rcSizeType count, const T& value) : m_size(0), m_cap(0), m_data(0) { resize(count, value); }
@@ -142,8 +142,8 @@ class rcVectorBase {
 
 	const T& front() const { rcAssert(m_size); return m_data[0]; }
 	T& front() { rcAssert(m_size); return m_data[0]; }
-	const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; };
-	T& back() { rcAssert(m_size); return m_data[m_size - 1]; };
+	const T& back() const { rcAssert(m_size); return m_data[m_size - 1]; }
+	T& back() { rcAssert(m_size); return m_data[m_size - 1]; }
 	const T* data() const { return m_data; }
 	T* data() { return m_data; }
 

+ 5 - 0
thirdparty/recastnavigation/Recast/Source/Recast.cpp

@@ -94,6 +94,11 @@ void rcContext::log(const rcLogCategory category, const char* format, ...)
 	doLog(category, msg, len);
 }
 
+void rcContext::doResetLog()
+{
+	// Defined out of line to fix the weak v-tables warning
+}
+
 rcHeightfield* rcAllocHeightfield()
 {
 	return rcNew<rcHeightfield>(RC_ALLOC_PERM);

+ 0 - 2
thirdparty/recastnavigation/Recast/Source/RecastMesh.cpp

@@ -566,7 +566,6 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
 	const int nvp = mesh.nvp;
 	
 	// Count number of polygons to remove.
-	int numRemovedVerts = 0;
 	int numTouchedVerts = 0;
 	int numRemainingEdges = 0;
 	for (int i = 0; i < mesh.npolys; ++i)
@@ -586,7 +585,6 @@ static bool canRemoveVertex(rcContext* ctx, rcPolyMesh& mesh, const unsigned sho
 		}
 		if (numRemoved)
 		{
-			numRemovedVerts += numRemoved;
 			numRemainingEdges += numVerts-(numRemoved+1);
 		}
 	}

+ 1 - 1
thirdparty/recastnavigation/Recast/Source/RecastMeshDetail.cpp

@@ -284,7 +284,7 @@ static unsigned short getHeight(const float fx, const float fy, const float fz,
 enum EdgeValues
 {
 	EV_UNDEF = -1,
-	EV_HULL = -2,
+	EV_HULL = -2
 };
 
 static int findEdge(const int* edges, int nedges, int s, int t)

+ 8 - 4
thirdparty/recastnavigation/Recast/Source/RecastRasterization.cpp

@@ -264,7 +264,8 @@ static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
 	// Calculate the footprint of the triangle on the grid's y-axis
 	int y0 = (int)((tmin[2] - bmin[2])*ics);
 	int y1 = (int)((tmax[2] - bmin[2])*ics);
-	y0 = rcClamp(y0, 0, h-1);
+	// use -1 rather than 0 to cut the polygon properly at the start of the tile
+	y0 = rcClamp(y0, -1, h-1);
 	y1 = rcClamp(y1, 0, h-1);
 	
 	// Clip the triangle into all grid cells it touches.
@@ -283,7 +284,7 @@ static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
 		dividePoly(in, nvIn, inrow, &nvrow, p1, &nvIn, cz+cs, 2);
 		rcSwap(in, p1);
 		if (nvrow < 3) continue;
-		
+		if (y < 0) continue;
 		// find the horizontal bounds in the row
 		float minX = inrow[0], maxX = inrow[0];
 		for (int i=1; i<nvrow; ++i)
@@ -293,7 +294,10 @@ static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
 		}
 		int x0 = (int)((minX - bmin[0])*ics);
 		int x1 = (int)((maxX - bmin[0])*ics);
-		x0 = rcClamp(x0, 0, w-1);
+		if (x1 < 0 || x0 >= w) {
+			continue;
+		}
+		x0 = rcClamp(x0, -1, w-1);
 		x1 = rcClamp(x1, 0, w-1);
 
 		int nv, nv2 = nvrow;
@@ -305,7 +309,7 @@ static bool rasterizeTri(const float* v0, const float* v1, const float* v2,
 			dividePoly(inrow, nv2, p1, &nv, p2, &nv2, cx+cs, 0);
 			rcSwap(inrow, p2);
 			if (nv < 3) continue;
-			
+			if (x < 0) continue;
 			// Calculate min and max of the span.
 			float smin = p1[1], smax = p1[1];
 			for (int i = 1; i < nv; ++i)