Przeglądaj źródła

Merge pull request #45892 from akien-mga/3.2-cherrypicks

Cherry-picks for the 3.2 branch (future 3.2.4) - 22nd batch
Rémi Verschelde 4 lat temu
rodzic
commit
9a89782996

+ 5 - 1
.mailmap

@@ -6,6 +6,7 @@ Anish Bhobe <[email protected]>
 Anutrix <[email protected]>
 Anutrix <[email protected]>
 Aren Villanueva <[email protected]> <[email protected]>
 Aren Villanueva <[email protected]> <[email protected]>
 Ariel Manzur <[email protected]>
 Ariel Manzur <[email protected]>
+Ariel Manzur <[email protected]> <[email protected]>
 Ariel Manzur <[email protected]> <[email protected]>
 Ariel Manzur <[email protected]> <[email protected]>
 Ariel Manzur <[email protected]> <[email protected]>
 Ariel Manzur <[email protected]> <[email protected]>
 Ariel Manzur <[email protected]> <[email protected]>
 Ariel Manzur <[email protected]> <[email protected]>
@@ -25,6 +26,7 @@ Daniel J. Ramirez <[email protected]>
 Dominik 'dreamsComeTrue' Jasiński <[email protected]>
 Dominik 'dreamsComeTrue' Jasiński <[email protected]>
 Emmanuel Barroga <[email protected]>
 Emmanuel Barroga <[email protected]>
 Eric M <[email protected]>
 Eric M <[email protected]>
+Eric Rybicki <[email protected]> <[email protected]>
 Erik Selecký <[email protected]>
 Erik Selecký <[email protected]>
 Erik Selecký <[email protected]> <[email protected]>
 Erik Selecký <[email protected]> <[email protected]>
 Fabian <[email protected]>
 Fabian <[email protected]>
@@ -84,6 +86,7 @@ Mateo Kuruk Miccino <[email protected]>
 Max Hilbrunner <[email protected]>
 Max Hilbrunner <[email protected]>
 Max Hilbrunner <[email protected]> <[email protected]>
 Max Hilbrunner <[email protected]> <[email protected]>
 Michael Alexsander <[email protected]>
 Michael Alexsander <[email protected]>
+Nathan Franke <[email protected]> <[email protected]>
 Nathan Lovato <[email protected]>
 Nathan Lovato <[email protected]>
 Nathan Warden <[email protected]> <[email protected]>
 Nathan Warden <[email protected]> <[email protected]>
 Nils ANDRÉ-CHANG <[email protected]>
 Nils ANDRÉ-CHANG <[email protected]>
@@ -97,7 +100,7 @@ Pieter-Jan Briers <[email protected]>
 Pieter-Jan Briers <[email protected]> <[email protected]>
 Pieter-Jan Briers <[email protected]> <[email protected]>
 Poommetee Ketson <[email protected]>
 Poommetee Ketson <[email protected]>
 Przemysław Gołąb (n-pigeon) <[email protected]>
 Przemysław Gołąb (n-pigeon) <[email protected]>
-Rafał Mikrut <[email protected]>
+Rafał Mikrut <mikrutrafal@protonmail.com> <mikrutrafal[email protected]>
 Ralf Hölzemer <[email protected]> <[email protected]>
 Ralf Hölzemer <[email protected]> <[email protected]>
 Ralf Hölzemer <[email protected]> <[email protected]>
 Ralf Hölzemer <[email protected]> <[email protected]>
 Ramesh Ravone <[email protected]>
 Ramesh Ravone <[email protected]>
@@ -123,5 +126,6 @@ Wilhem Barbier <[email protected]> <[email protected]>
 Wilhem Barbier <[email protected]> <[email protected]>
 Wilhem Barbier <[email protected]> <[email protected]>
 Will Nations <[email protected]>
 Will Nations <[email protected]>
 yg2f <[email protected]>
 yg2f <[email protected]>
+Yuri Sizov <[email protected]> <[email protected]>
 Zak Stam <[email protected]>
 Zak Stam <[email protected]>
 Zher Huei Lee <[email protected]>
 Zher Huei Lee <[email protected]>

+ 3 - 0
AUTHORS.md

@@ -114,6 +114,7 @@ name is available.
     Leon Krause (leonkrause)
     Leon Krause (leonkrause)
     Liz Haas (27thLiz)
     Liz Haas (27thLiz)
     Lucien Menassol (Kanabenki)
     Lucien Menassol (Kanabenki)
+    Lyuma
     m4nu3lf
     m4nu3lf
     Maganty Rushyendra (mrushyendra)
     Maganty Rushyendra (mrushyendra)
     Marcel Admiraal (madmiraal)
     Marcel Admiraal (madmiraal)
@@ -138,6 +139,7 @@ name is available.
     MichiRecRoom (LikeLakers2)
     MichiRecRoom (LikeLakers2)
     mrezai
     mrezai
     muiroc
     muiroc
+    Nathan Franke (nathanfranke)
     Nathan Lovato (NathanLovato)
     Nathan Lovato (NathanLovato)
     Nathan Warden (NathanWarden)
     Nathan Warden (NathanWarden)
     Nils André-Chang (NilsIrl)
     Nils André-Chang (NilsIrl)
@@ -191,6 +193,7 @@ name is available.
     Xavier Cho (mysticfall)
     Xavier Cho (mysticfall)
     yg2f (SuperUserNameMan)
     yg2f (SuperUserNameMan)
     Yuri Roubinsky (Chaosus)
     Yuri Roubinsky (Chaosus)
+    Yuri Sizov (pycbouh)
     Zak Stam (zaksnet)
     Zak Stam (zaksnet)
     Zher Huei Lee (leezh)
     Zher Huei Lee (leezh)
     ZuBsPaCe
     ZuBsPaCe

+ 12 - 8
core/math/math_funcs.h

@@ -233,15 +233,19 @@ public:
 	static _ALWAYS_INLINE_ double range_lerp(double p_value, double p_istart, double p_istop, double p_ostart, double p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); }
 	static _ALWAYS_INLINE_ double range_lerp(double p_value, double p_istart, double p_istop, double p_ostart, double p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); }
 	static _ALWAYS_INLINE_ float range_lerp(float p_value, float p_istart, float p_istop, float p_ostart, float p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); }
 	static _ALWAYS_INLINE_ float range_lerp(float p_value, float p_istart, float p_istop, float p_ostart, float p_ostop) { return Math::lerp(p_ostart, p_ostop, Math::inverse_lerp(p_istart, p_istop, p_value)); }
 
 
-	static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_weight) {
-		if (is_equal_approx(p_from, p_to)) return p_from;
-		double x = CLAMP((p_weight - p_from) / (p_to - p_from), 0.0, 1.0);
-		return x * x * (3.0 - 2.0 * x);
+	static _ALWAYS_INLINE_ double smoothstep(double p_from, double p_to, double p_s) {
+		if (is_equal_approx(p_from, p_to)) {
+			return p_from;
+		}
+		double s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0, 1.0);
+		return s * s * (3.0 - 2.0 * s);
 	}
 	}
-	static _ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_weight) {
-		if (is_equal_approx(p_from, p_to)) return p_from;
-		float x = CLAMP((p_weight - p_from) / (p_to - p_from), 0.0f, 1.0f);
-		return x * x * (3.0f - 2.0f * x);
+	static _ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_s) {
+		if (is_equal_approx(p_from, p_to)) {
+			return p_from;
+		}
+		float s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0f, 1.0f);
+		return s * s * (3.0f - 2.0f * s);
 	}
 	}
 	static _ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; }
 	static _ALWAYS_INLINE_ double move_toward(double p_from, double p_to, double p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; }
 	static _ALWAYS_INLINE_ float move_toward(float p_from, float p_to, float p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; }
 	static _ALWAYS_INLINE_ float move_toward(float p_from, float p_to, float p_delta) { return abs(p_to - p_from) <= p_delta ? p_to : p_from + SGN(p_to - p_from) * p_delta; }

+ 26 - 17
core/ustring.cpp

@@ -2710,40 +2710,49 @@ int String::rfindn(const String &p_str, int p_from) const {
 }
 }
 
 
 bool String::ends_with(const String &p_string) const {
 bool String::ends_with(const String &p_string) const {
-
 	int l = p_string.length();
 	int l = p_string.length();
+	if (l > length()) {
+		return false;
+	}
+
 	if (l == 0) {
 	if (l == 0) {
 		return true;
 		return true;
 	}
 	}
 
 
-	int pos = find_last(p_string);
-	if (pos == -1)
-		return false;
-	return pos + l == length();
+	const CharType *p = &p_string[0];
+	const CharType *s = &operator[](length() - l);
+
+	for (int i = 0; i < l; i++) {
+		if (p[i] != s[i]) {
+			return false;
+		}
+	}
+
+	return true;
 }
 }
 
 
 bool String::begins_with(const String &p_string) const {
 bool String::begins_with(const String &p_string) const {
-
-	if (p_string.length() > length())
+	int l = p_string.length();
+	if (l > length()) {
 		return false;
 		return false;
+	}
 
 
-	int l = p_string.length();
-	if (l == 0)
+	if (l == 0) {
 		return true;
 		return true;
+	}
 
 
-	const CharType *src = &p_string[0];
-	const CharType *str = &operator[](0);
-
-	int i = 0;
-	for (; i < l; i++) {
+	const CharType *p = &p_string[0];
+	const CharType *s = &operator[](0);
 
 
-		if (src[i] != str[i])
+	for (int i = 0; i < l; i++) {
+		if (p[i] != s[i]) {
 			return false;
 			return false;
+		}
 	}
 	}
 
 
-	// only if i == l the p_string matches the beginning
-	return i == l;
+	return true;
 }
 }
+
 bool String::begins_with(const char *p_string) const {
 bool String::begins_with(const char *p_string) const {
 
 
 	int l = length();
 	int l = length();

+ 3 - 4
doc/classes/EditorImportPlugin.xml

@@ -15,7 +15,7 @@
 		    return "my.special.plugin"
 		    return "my.special.plugin"
 
 
 		func get_visible_name():
 		func get_visible_name():
-		    return "Special Mesh Importer"
+		    return "Special Mesh"
 
 
 		func get_recognized_extensions():
 		func get_recognized_extensions():
 		    return ["special", "spec"]
 		    return ["special", "spec"]
@@ -44,8 +44,7 @@
 		    # Fill the Mesh with data read in "file", left as an exercise to the reader
 		    # Fill the Mesh with data read in "file", left as an exercise to the reader
 
 
 		    var filename = save_path + "." + get_save_extension()
 		    var filename = save_path + "." + get_save_extension()
-		    ResourceSaver.save(filename, mesh)
-		    return OK
+		    return ResourceSaver.save(filename, mesh)
 		[/codeblock]
 		[/codeblock]
 	</description>
 	</description>
 	<tutorials>
 	<tutorials>
@@ -143,7 +142,7 @@
 			<return type="String">
 			<return type="String">
 			</return>
 			</return>
 			<description>
 			<description>
-				Gets the name to display in the import window.
+				Gets the name to display in the import window. You should choose this name as a continuation to "Import as", e.g. "Import as Special Mesh".
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="import" qualifiers="virtual">
 		<method name="import" qualifiers="virtual">

+ 7 - 0
doc/classes/GraphNode.xml

@@ -226,6 +226,13 @@
 				Emitted when the GraphNode is moved.
 				Emitted when the GraphNode is moved.
 			</description>
 			</description>
 		</signal>
 		</signal>
+		<signal name="slot_updated">
+			<argument index="0" name="idx" type="int">
+			</argument>
+			<description>
+				Emitted when any GraphNode's slot is updated.
+			</description>
+		</signal>
 		<signal name="raise_request">
 		<signal name="raise_request">
 			<description>
 			<description>
 				Emitted when the GraphNode is requested to be displayed over other ones. Happens on focusing (clicking into) the GraphNode.
 				Emitted when the GraphNode is requested to be displayed over other ones. Happens on focusing (clicking into) the GraphNode.

+ 15 - 3
editor/filesystem_dock.cpp

@@ -1294,14 +1294,26 @@ void FileSystemDock::_make_scene_confirm() {
 void FileSystemDock::_file_removed(String p_file) {
 void FileSystemDock::_file_removed(String p_file) {
 	emit_signal("file_removed", p_file);
 	emit_signal("file_removed", p_file);
 
 
-	path = "res://";
+	// Find the closest parent directory available, in case multiple items were deleted along the same path.
+	path = p_file.get_base_dir();
+	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+	while (!da->dir_exists(path)) {
+		path = path.get_base_dir();
+	}
+
 	current_path->set_text(path);
 	current_path->set_text(path);
 }
 }
 
 
 void FileSystemDock::_folder_removed(String p_folder) {
 void FileSystemDock::_folder_removed(String p_folder) {
 	emit_signal("folder_removed", p_folder);
 	emit_signal("folder_removed", p_folder);
 
 
-	path = "res://";
+	// Find the closest parent directory available, in case multiple items were deleted along the same path.
+	path = p_folder.get_base_dir();
+	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
+	while (!da->dir_exists(path)) {
+		path = path.get_base_dir();
+	}
+
 	current_path->set_text(path);
 	current_path->set_text(path);
 }
 }
 
 
@@ -1469,7 +1481,7 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool overw
 		print_verbose("FileSystem: saving moved scenes.");
 		print_verbose("FileSystem: saving moved scenes.");
 		_save_scenes_after_move(file_renames);
 		_save_scenes_after_move(file_renames);
 
 
-		path = "res://";
+		path = p_to_path;
 		current_path->set_text(path);
 		current_path->set_text(path);
 	}
 	}
 }
 }

+ 6 - 4
misc/dist/html/editor.html

@@ -262,9 +262,7 @@
 				return;
 				return;
 			}
 			}
 			Promise.all([
 			Promise.all([
-				deleteDB("/home/web_user/projects"),
-				deleteDB("/home/web_user/.config"),
-				deleteDB("/home/web_user/.cache"),
+				deleteDB("/home/web_user"),
 			]).then(function(results) {
 			]).then(function(results) {
 				alert("Done.");
 				alert("Done.");
 			}).catch(function (err) {
 			}).catch(function (err) {
@@ -286,6 +284,10 @@
 			tabs.forEach(function (elem) {
 			tabs.forEach(function (elem) {
 				if (elem.id == 'tab-' + name) {
 				if (elem.id == 'tab-' + name) {
 					elem.style.display = 'block';
 					elem.style.display = 'block';
+					if (name == 'editor' || name == 'game') {
+						const canvas = document.getElementById(name + '-canvas');
+						canvas.focus();
+					}
 				} else {
 				} else {
 					elem.style.display = 'none';
 					elem.style.display = 'none';
 				}
 				}
@@ -327,7 +329,7 @@
 		function startEditor(zip) {
 		function startEditor(zip) {
 
 
 			const INDETERMINATE_STATUS_STEP_MS = 100;
 			const INDETERMINATE_STATUS_STEP_MS = 100;
-			const persistentPaths = ['/home/web_user/'];
+			const persistentPaths = ['/home/web_user'];
 
 
 			var editorCanvas = document.getElementById('editor-canvas');
 			var editorCanvas = document.getElementById('editor-canvas');
 			var gameCanvas = document.getElementById('game-canvas');
 			var gameCanvas = document.getElementById('game-canvas');

+ 20 - 8
modules/gdscript/doc_classes/@GDScript.xml

@@ -320,7 +320,7 @@
 			</argument>
 			</argument>
 			<description>
 			<description>
 				The natural exponential function. It raises the mathematical constant [b]e[/b] to the power of [code]s[/code] and returns it.
 				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.
+				[b]e[/b] has an approximate value of 2.71828, and can be obtained with [code]exp(1)[/code].
 				For exponents to other bases use the method [method pow].
 				For exponents to other bases use the method [method pow].
 				[codeblock]
 				[codeblock]
 				a = exp(2) # Approximately 7.39
 				a = exp(2) # Approximately 7.39
@@ -501,6 +501,8 @@
 			</argument>
 			</argument>
 			<description>
 			<description>
 				Returns [code]true[/code] if [code]a[/code] and [code]b[/code] are approximately equal to each other.
 				Returns [code]true[/code] if [code]a[/code] and [code]b[/code] are approximately equal to each other.
+				Here, approximately equal means that [code]a[/code] and [code]b[/code] are within a small internal epsilon of each other, which scales with the magnitude of the numbers.
+				Infinity values of the same sign are considered equal.
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="is_inf">
 		<method name="is_inf">
@@ -638,6 +640,7 @@
 				[codeblock]
 				[codeblock]
 				log(10) # Returns 2.302585
 				log(10) # Returns 2.302585
 				[/codeblock]
 				[/codeblock]
+				[b]Note:[/b] The logarithm of [code]0[/code] returns [code]-inf[/code], while negative values return [code]-nan[/code].
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="max">
 		<method name="max">
@@ -683,7 +686,9 @@
 				Moves [code]from[/code] toward [code]to[/code] by the [code]delta[/code] value.
 				Moves [code]from[/code] toward [code]to[/code] by the [code]delta[/code] value.
 				Use a negative [code]delta[/code] value to move away.
 				Use a negative [code]delta[/code] value to move away.
 				[codeblock]
 				[codeblock]
+				move_toward(5, 10, 4) # Returns 9
 				move_toward(10, 5, 4) # Returns 6
 				move_toward(10, 5, 4) # Returns 6
+				move_toward(10, 5, -1.5) # Returns 11.5
 				[/codeblock]
 				[/codeblock]
 			</description>
 			</description>
 		</method>
 		</method>
@@ -693,12 +698,17 @@
 			<argument index="0" name="value" type="int">
 			<argument index="0" name="value" type="int">
 			</argument>
 			</argument>
 			<description>
 			<description>
-				Returns the nearest larger power of 2 for integer [code]value[/code].
+				Returns the nearest equal or larger power of 2 for integer [code]value[/code].
+				In other words, returns the smallest value [code]a[/code] where [code]a = pow(2, n)[/code] such that [code]value &lt;= a[/code] for some non-negative integer [code]n[/code].
 				[codeblock]
 				[codeblock]
 				nearest_po2(3) # Returns 4
 				nearest_po2(3) # Returns 4
 				nearest_po2(4) # Returns 4
 				nearest_po2(4) # Returns 4
 				nearest_po2(5) # Returns 8
 				nearest_po2(5) # Returns 8
+
+				nearest_po2(0) # Returns 0 (this may not be what you expect)
+				nearest_po2(-1) # Returns 0 (this may not be what you expect)
 				[/codeblock]
 				[/codeblock]
+				[b]WARNING:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for non-positive values of [code]value[/code] (in reality, 1 is the smallest integer power of 2).
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="ord">
 		<method name="ord">
@@ -1078,15 +1088,17 @@
 			</argument>
 			</argument>
 			<argument index="1" name="to" type="float">
 			<argument index="1" name="to" type="float">
 			</argument>
 			</argument>
-			<argument index="2" name="weight" type="float">
+			<argument index="2" name="s" type="float">
 			</argument>
 			</argument>
 			<description>
 			<description>
-				Returns a number smoothly interpolated between the [code]from[/code] and [code]to[/code], based on the [code]weight[/code]. Similar to [method lerp], but interpolates faster at the beginning and slower at the end.
+				Returns the result of smoothly interpolating the value of [code]s[/code] between [code]0[/code] and [code]1[/code], based on the where [code]s[/code] lies with respect to the edges [code]from[/code] and [code]to[/code].
+				The return value is [code]0[/code] if [code]s &lt;= from[/code], and [code]1[/code] if [code]s &gt;= to[/code]. If [code]s[/code] lies between [code]from[/code] and [code]to[/code], the returned value follows an S-shaped curve that maps [code]s[/code] between [code]0[/code] and [code]1[/code].
+				This S-shaped curve is the cubic Hermite interpolator, given by [code]f(s) = 3*s^2 - 2*s^3[/code].
 				[codeblock]
 				[codeblock]
 				smoothstep(0, 2, -5.0) # Returns 0.0
 				smoothstep(0, 2, -5.0) # Returns 0.0
-				smoothstep(0, 2, 0.5)  # Returns 0.15625
-				smoothstep(0, 2, 1.0)  # Returns 0.5
-				smoothstep(0, 2, 2.0)  # Returns 1.0
+				smoothstep(0, 2, 0.5) # Returns 0.15625
+				smoothstep(0, 2, 1.0) # Returns 0.5
+				smoothstep(0, 2, 2.0) # Returns 1.0
 				[/codeblock]
 				[/codeblock]
 			</description>
 			</description>
 		</method>
 		</method>
@@ -1100,7 +1112,7 @@
 				[codeblock]
 				[codeblock]
 				sqrt(9) # Returns 3
 				sqrt(9) # Returns 3
 				[/codeblock]
 				[/codeblock]
-				If you need negative inputs, use [code]System.Numerics.Complex[/code] in C#.
+				[b]Note:[/b]Negative values of [code]s[/code] return NaN. If you need negative inputs, use [code]System.Numerics.Complex[/code] in C#.
 			</description>
 			</description>
 		</method>
 		</method>
 		<method name="step_decimals">
 		<method name="step_decimals">

+ 1 - 1
modules/gdscript/gdscript_functions.cpp

@@ -1715,7 +1715,7 @@ MethodInfo GDScriptFunctions::get_info(Function p_func) {
 			return mi;
 			return mi;
 		} break;
 		} break;
 		case MATH_SMOOTHSTEP: {
 		case MATH_SMOOTHSTEP: {
-			MethodInfo mi("smoothstep", PropertyInfo(Variant::REAL, "from"), PropertyInfo(Variant::REAL, "to"), PropertyInfo(Variant::REAL, "weight"));
+			MethodInfo mi("smoothstep", PropertyInfo(Variant::REAL, "from"), PropertyInfo(Variant::REAL, "to"), PropertyInfo(Variant::REAL, "s"));
 			mi.return_val.type = Variant::REAL;
 			mi.return_val.type = Variant::REAL;
 			return mi;
 			return mi;
 		} break;
 		} break;

+ 12 - 0
scene/gui/graph_edit.cpp

@@ -396,6 +396,15 @@ void GraphEdit::_graph_node_moved(Node *p_gn) {
 	connections_layer->update();
 	connections_layer->update();
 }
 }
 
 
+void GraphEdit::_graph_node_slot_updated(int p_index, Node *p_gn) {
+	GraphNode *gn = Object::cast_to<GraphNode>(p_gn);
+	ERR_FAIL_COND(!gn);
+	top_layer->update();
+	minimap->update();
+	update();
+	connections_layer->update();
+}
+
 void GraphEdit::add_child_notify(Node *p_child) {
 void GraphEdit::add_child_notify(Node *p_child) {
 
 
 	Control::add_child_notify(p_child);
 	Control::add_child_notify(p_child);
@@ -406,6 +415,7 @@ void GraphEdit::add_child_notify(Node *p_child) {
 	if (gn) {
 	if (gn) {
 		gn->set_scale(Vector2(zoom, zoom));
 		gn->set_scale(Vector2(zoom, zoom));
 		gn->connect("offset_changed", this, "_graph_node_moved", varray(gn));
 		gn->connect("offset_changed", this, "_graph_node_moved", varray(gn));
+		gn->connect("slot_updated", this, "_graph_node_slot_updated", varray(gn));
 		gn->connect("raise_request", this, "_graph_node_raised", varray(gn));
 		gn->connect("raise_request", this, "_graph_node_raised", varray(gn));
 		gn->connect("item_rect_changed", connections_layer, "update");
 		gn->connect("item_rect_changed", connections_layer, "update");
 		gn->connect("item_rect_changed", minimap, "update");
 		gn->connect("item_rect_changed", minimap, "update");
@@ -432,6 +442,7 @@ void GraphEdit::remove_child_notify(Node *p_child) {
 	GraphNode *gn = Object::cast_to<GraphNode>(p_child);
 	GraphNode *gn = Object::cast_to<GraphNode>(p_child);
 	if (gn) {
 	if (gn) {
 		gn->disconnect("offset_changed", this, "_graph_node_moved");
 		gn->disconnect("offset_changed", this, "_graph_node_moved");
+		gn->disconnect("slot_updated", this, "_graph_node_slot_updated");
 		gn->disconnect("raise_request", this, "_graph_node_raised");
 		gn->disconnect("raise_request", this, "_graph_node_raised");
 
 
 		// In case of the whole GraphEdit being destroyed these references can already be freed.
 		// In case of the whole GraphEdit being destroyed these references can already be freed.
@@ -1659,6 +1670,7 @@ void GraphEdit::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("_graph_node_moved"), &GraphEdit::_graph_node_moved);
 	ClassDB::bind_method(D_METHOD("_graph_node_moved"), &GraphEdit::_graph_node_moved);
 	ClassDB::bind_method(D_METHOD("_graph_node_raised"), &GraphEdit::_graph_node_raised);
 	ClassDB::bind_method(D_METHOD("_graph_node_raised"), &GraphEdit::_graph_node_raised);
+	ClassDB::bind_method(D_METHOD("_graph_node_slot_updated"), &GraphEdit::_graph_node_slot_updated);
 
 
 	ClassDB::bind_method(D_METHOD("_top_layer_input"), &GraphEdit::_top_layer_input);
 	ClassDB::bind_method(D_METHOD("_top_layer_input"), &GraphEdit::_top_layer_input);
 	ClassDB::bind_method(D_METHOD("_top_layer_draw"), &GraphEdit::_top_layer_draw);
 	ClassDB::bind_method(D_METHOD("_top_layer_draw"), &GraphEdit::_top_layer_draw);

+ 1 - 0
scene/gui/graph_edit.h

@@ -164,6 +164,7 @@ private:
 
 
 	void _graph_node_raised(Node *p_gn);
 	void _graph_node_raised(Node *p_gn);
 	void _graph_node_moved(Node *p_gn);
 	void _graph_node_moved(Node *p_gn);
+	void _graph_node_slot_updated(int p_index, Node *p_gn);
 
 
 	void _update_scroll();
 	void _update_scroll();
 	void _scroll_moved(double);
 	void _scroll_moved(double);

+ 3 - 0
scene/gui/graph_node.cpp

@@ -312,6 +312,8 @@ void GraphNode::set_slot(int p_idx, bool p_enable_left, int p_type_left, const C
 	slot_info[p_idx] = s;
 	slot_info[p_idx] = s;
 	update();
 	update();
 	connpos_dirty = true;
 	connpos_dirty = true;
+
+	emit_signal("slot_updated", p_idx);
 }
 }
 
 
 void GraphNode::clear_slot(int p_idx) {
 void GraphNode::clear_slot(int p_idx) {
@@ -720,6 +722,7 @@ void GraphNode::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "overlay", PROPERTY_HINT_ENUM, "Disabled,Breakpoint,Position"), "set_overlay", "get_overlay");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "overlay", PROPERTY_HINT_ENUM, "Disabled,Breakpoint,Position"), "set_overlay", "get_overlay");
 
 
 	ADD_SIGNAL(MethodInfo("offset_changed"));
 	ADD_SIGNAL(MethodInfo("offset_changed"));
+	ADD_SIGNAL(MethodInfo("slot_updated", PropertyInfo(Variant::INT, "idx")));
 	ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to")));
 	ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to")));
 	ADD_SIGNAL(MethodInfo("raise_request"));
 	ADD_SIGNAL(MethodInfo("raise_request"));
 	ADD_SIGNAL(MethodInfo("close_request"));
 	ADD_SIGNAL(MethodInfo("close_request"));

+ 6 - 0
scene/main/viewport.cpp

@@ -416,6 +416,12 @@ void Viewport::_notification(int p_what) {
 				int point_count = PhysicsServer::get_singleton()->space_get_contact_count(find_world()->get_space());
 				int point_count = PhysicsServer::get_singleton()->space_get_contact_count(find_world()->get_space());
 
 
 				VS::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, point_count);
 				VS::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, point_count);
+
+				for (int i = 0; i < point_count; i++) {
+					Transform point_transform;
+					point_transform.origin = points[i];
+					VS::get_singleton()->multimesh_instance_set_transform(contact_3d_debug_multimesh, i, point_transform);
+				}
 			}
 			}
 
 
 			if (!GLOBAL_GET("physics/common/enable_pause_aware_picking")) {
 			if (!GLOBAL_GET("physics/common/enable_pause_aware_picking")) {