diff options
27 files changed, 693 insertions, 297 deletions
| diff --git a/core/error_macros.h b/core/error_macros.h index 46a1623115..d7366be453 100644 --- a/core/error_macros.h +++ b/core/error_macros.h @@ -285,7 +285,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * If it is null, the current function returns.   */  #define ERR_FAIL_NULL(m_param)                                                                          \ -	if (unlikely(!m_param)) {                                                                           \ +	if (unlikely(m_param == nullptr)) {                                                                 \  		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \  		return;                                                                                         \  	} else                                                                                              \ @@ -296,7 +296,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * If it is null, prints `m_msg` and the current function returns.   */  #define ERR_FAIL_NULL_MSG(m_param, m_msg)                                                                                 \ -	if (unlikely(!m_param)) {                                                                                             \ +	if (unlikely(m_param == nullptr)) {                                                                                   \  		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \  		return;                                                                                                           \  	} else                                                                                                                \ @@ -310,7 +310,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * If it is null, the current function returns `m_retval`.   */  #define ERR_FAIL_NULL_V(m_param, m_retval)                                                              \ -	if (unlikely(!m_param)) {                                                                           \ +	if (unlikely(m_param == nullptr)) {                                                                 \  		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \  		return m_retval;                                                                                \  	} else                                                                                              \ @@ -321,7 +321,7 @@ void _err_print_index_error(const char *p_function, const char *p_file, int p_li   * If it is null, prints `m_msg` and the current function returns `m_retval`.   */  #define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg)                                                                     \ -	if (unlikely(!m_param)) {                                                                                             \ +	if (unlikely(m_param == nullptr)) {                                                                                   \  		_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", DEBUG_STR(m_msg)); \  		return m_retval;                                                                                                  \  	} else                                                                                                                \ diff --git a/doc/classes/Skeleton3D.xml b/doc/classes/Skeleton3D.xml index 640cbe84f5..183fd5396f 100644 --- a/doc/classes/Skeleton3D.xml +++ b/doc/classes/Skeleton3D.xml @@ -31,6 +31,16 @@  				[i]Deprecated soon.[/i]  			</description>  		</method> +		<method name="bone_transform_to_world_transform"> +			<return type="Transform"> +			</return> +			<argument index="0" name="bone_transform" type="Transform"> +			</argument> +			<description> +				Takes the given bone pose/transform and converts it to a world transform, relative to the [Skeleton3D] node. +				This is useful for using the bone transform in calculations with transforms from [Node3D]-based nodes. +			</description> +		</method>  		<method name="clear_bones">  			<return type="void">  			</return> @@ -42,6 +52,7 @@  			<return type="void">  			</return>  			<description> +				Removes the global pose override on all bones in the skeleton.  			</description>  		</method>  		<method name="find_bone" qualifiers="const"> @@ -136,12 +147,14 @@  			<argument index="0" name="bone_idx" type="int">  			</argument>  			<description> +				Returns whether the bone rest for the bone at [code]bone_idx[/code] is disabled.  			</description>  		</method>  		<method name="localize_rests">  			<return type="void">  			</return>  			<description> +				Returns all bones in the skeleton to their rest poses.  			</description>  		</method>  		<method name="physical_bones_add_collision_exception"> @@ -150,6 +163,8 @@  			<argument index="0" name="exception" type="RID">  			</argument>  			<description> +				Adds a collision exception to the physical bone. +				Works just like the [RigidBody3D] node.  			</description>  		</method>  		<method name="physical_bones_remove_collision_exception"> @@ -158,6 +173,8 @@  			<argument index="0" name="exception" type="RID">  			</argument>  			<description> +				Removes a collision exception to the physical bone. +				Works just like the [RigidBody3D] node.  			</description>  		</method>  		<method name="physical_bones_start_simulation"> @@ -166,12 +183,15 @@  			<argument index="0" name="bones" type="StringName[]" default="[  ]">  			</argument>  			<description> +				Tells the [PhysicalBone3D] nodes in the Skeleton to start simulating and reacting to the physics world. +				Optionally, a list of bone names can be passed-in, allowing only the passed-in bones to be simulated.  			</description>  		</method>  		<method name="physical_bones_stop_simulation">  			<return type="void">  			</return>  			<description> +				Tells the [PhysicalBone3D] nodes in the Skeleton to stop simulating.  			</description>  		</method>  		<method name="register_skin"> @@ -180,6 +200,7 @@  			<argument index="0" name="skin" type="Skin">  			</argument>  			<description> +				Binds the given Skin to the Skeleton.  			</description>  		</method>  		<method name="set_bone_custom_pose"> @@ -190,6 +211,8 @@  			<argument index="1" name="custom_pose" type="Transform">  			</argument>  			<description> +				Sets the custom pose transform, [code]custom_pose[/code], for the bone at [code]bone_idx[/code]. This pose is an addition to the bone rest pose. +				[b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.  			</description>  		</method>  		<method name="set_bone_disable_rest"> @@ -200,6 +223,7 @@  			<argument index="1" name="disable" type="bool">  			</argument>  			<description> +				Disables the rest pose for the bone at [code]bone_idx[/code] if [code]true[/code], enables the bone rest if [code]false[/code].  			</description>  		</method>  		<method name="set_bone_global_pose_override"> @@ -214,6 +238,9 @@  			<argument index="3" name="persistent" type="bool" default="false">  			</argument>  			<description> +				Sets the global pose transform, [code]pose[/code], for the bone at [code]bone_idx[/code]. +				[code]amount[/code] is the interpolation strengh that will be used when applying the pose, and [code]persistent[/code] determines if the applied pose will remain. +				[b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.  			</description>  		</method>  		<method name="set_bone_parent"> @@ -237,6 +264,7 @@  			</argument>  			<description>  				Returns the pose transform for bone [code]bone_idx[/code]. +				[b]Note[/b]: The pose transform needs to be in bone space. Use [method world_transform_to_bone_transform] to convert a world transform, like one you can get from a [Node3D], to bone space.  			</description>  		</method>  		<method name="set_bone_rest"> @@ -267,6 +295,17 @@  			<argument index="0" name="bone_idx" type="int">  			</argument>  			<description> +				Unparents the bone at [code]bone_idx[/code] and sets its rest position to that of it's parent prior to being reset. +			</description> +		</method> +		<method name="world_transform_to_bone_transform"> +			<return type="Transform"> +			</return> +			<argument index="0" name="world_transform" type="Transform"> +			</argument> +			<description> +				Takes the given world transform, relative to the [Skeleton3D], and converts it to a bone pose/transform. +				This is useful for using setting bone poses using transforms from [Node3D]-based nodes.  			</description>  		</method>  	</methods> diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index bad2203423..f50ac6d580 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -243,6 +243,9 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview  	if (p_overview) {  		class_desc->push_cell();  		class_desc->push_align(RichTextLabel::ALIGN_RIGHT); +	} else { +		static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; +		class_desc->add_text(String(prefix));  	}  	_add_type(p_method.return_type, p_method.return_enum); @@ -378,7 +381,6 @@ void EditorHelp::_update_doc() {  		class_desc->push_color(title_color);  		class_desc->push_font(doc_font);  		class_desc->add_text(TTR("Inherits:") + " "); -		class_desc->pop();  		String inherits = cd.inherits; @@ -393,6 +395,7 @@ void EditorHelp::_update_doc() {  		}  		class_desc->pop(); +		class_desc->pop();  		class_desc->add_newline();  	} @@ -401,13 +404,12 @@ void EditorHelp::_update_doc() {  		bool found = false;  		bool prev = false; +		class_desc->push_font(doc_font);  		for (Map<String, DocData::ClassDoc>::Element *E = doc->class_list.front(); E; E = E->next()) {  			if (E->get().inherits == cd.name) {  				if (!found) {  					class_desc->push_color(title_color); -					class_desc->push_font(doc_font);  					class_desc->add_text(TTR("Inherited by:") + " "); -					class_desc->pop();  					found = true;  				} @@ -419,6 +421,7 @@ void EditorHelp::_update_doc() {  				prev = true;  			}  		} +		class_desc->pop();  		if (found) {  			class_desc->pop(); @@ -758,6 +761,8 @@ void EditorHelp::_update_doc() {  			signal_line[cd.signals[i].name] = class_desc->get_line_count() - 2; //gets overridden if description  			class_desc->push_font(doc_code_font); // monofont  			class_desc->push_color(headline_color); +			static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; +			class_desc->add_text(String(prefix));  			_add_text(cd.signals[i].name);  			class_desc->pop();  			class_desc->push_color(symbol_color); @@ -835,10 +840,10 @@ void EditorHelp::_update_doc() {  			for (Map<String, Vector<DocData::ConstantDoc>>::Element *E = enums.front(); E; E = E->next()) {  				enum_line[E->key()] = class_desc->get_line_count() - 2; +				class_desc->push_font(doc_code_font);  				class_desc->push_color(title_color);  				class_desc->add_text("enum  ");  				class_desc->pop(); -				class_desc->push_font(doc_code_font);  				String e = E->key();  				if ((e.get_slice_count(".") > 1) && (e.get_slice(".", 0) == edited_class)) {  					e = e.get_slice(".", 1); @@ -851,6 +856,8 @@ void EditorHelp::_update_doc() {  				class_desc->push_color(symbol_color);  				class_desc->add_text(":");  				class_desc->pop(); + +				class_desc->add_newline();  				class_desc->add_newline();  				class_desc->push_indent(1); @@ -869,6 +876,8 @@ void EditorHelp::_update_doc() {  					class_desc->push_font(doc_code_font);  					class_desc->push_color(headline_color); +					static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; +					class_desc->add_text(String(prefix));  					_add_text(enum_list[i].name);  					class_desc->pop();  					class_desc->push_color(symbol_color); @@ -880,14 +889,15 @@ void EditorHelp::_update_doc() {  					class_desc->pop();  					if (enum_list[i].description != "") {  						class_desc->push_font(doc_font); -						//class_desc->add_text("  "); -						class_desc->push_indent(1);  						class_desc->push_color(comment_color); +						static const CharType dash[6] = { ' ', ' ', 0x2013 /* en dash */, ' ', ' ', 0 }; +						class_desc->add_text(String(dash));  						_add_text(DTR(enum_list[i].description));  						class_desc->pop();  						class_desc->pop(); -						class_desc->pop(); // indent -						class_desc->add_newline(); +						if (DTR(enum_list[i].description).find("\n") > 0) { +							class_desc->add_newline(); +						}  					}  					class_desc->add_newline(); @@ -931,6 +941,9 @@ void EditorHelp::_update_doc() {  						class_desc->add_text(String(prefix));  						class_desc->pop();  					} +				} else { +					static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; +					class_desc->add_text(String(prefix));  				}  				class_desc->push_color(headline_color); @@ -946,13 +959,15 @@ void EditorHelp::_update_doc() {  				class_desc->pop();  				if (constants[i].description != "") {  					class_desc->push_font(doc_font); -					class_desc->push_indent(1);  					class_desc->push_color(comment_color); +					static const CharType dash[6] = { ' ', ' ', 0x2013 /* en dash */, ' ', ' ', 0 }; +					class_desc->add_text(String(dash));  					_add_text(DTR(constants[i].description));  					class_desc->pop();  					class_desc->pop(); -					class_desc->pop(); // indent -					class_desc->add_newline(); +					if (DTR(constants[i].description).find("\n") > 0) { +						class_desc->add_newline(); +					}  				}  				class_desc->add_newline(); @@ -987,6 +1002,9 @@ void EditorHelp::_update_doc() {  			class_desc->push_cell();  			class_desc->push_font(doc_code_font); +			static const CharType prefix[3] = { 0x25CF /* filled circle */, ' ', 0 }; +			class_desc->add_text(String(prefix)); +  			_add_type(cd.properties[i].type, cd.properties[i].enumeration);  			class_desc->add_text(" ");  			class_desc->pop(); // font @@ -1015,6 +1033,11 @@ void EditorHelp::_update_doc() {  			class_desc->pop(); // font  			class_desc->pop(); // cell +			Map<String, DocData::MethodDoc> method_map; +			for (int j = 0; j < methods.size(); j++) { +				method_map[methods[j].name] = methods[j]; +			} +  			if (cd.properties[i].setter != "") {  				class_desc->push_cell();  				class_desc->pop(); // cell @@ -1022,7 +1045,14 @@ void EditorHelp::_update_doc() {  				class_desc->push_cell();  				class_desc->push_font(doc_code_font);  				class_desc->push_color(text_color); -				class_desc->add_text(cd.properties[i].setter + TTR("(value)")); +				if (method_map[cd.properties[i].setter].arguments.size() > 1) { +					// Setters with additional arguments are exposed in the method list, so we link them here for quick access. +					class_desc->push_meta("@method " + cd.properties[i].setter); +					class_desc->add_text(cd.properties[i].setter + TTR("(value)")); +					class_desc->pop(); +				} else { +					class_desc->add_text(cd.properties[i].setter + TTR("(value)")); +				}  				class_desc->pop(); // color  				class_desc->push_color(comment_color);  				class_desc->add_text(" setter"); @@ -1039,7 +1069,14 @@ void EditorHelp::_update_doc() {  				class_desc->push_cell();  				class_desc->push_font(doc_code_font);  				class_desc->push_color(text_color); -				class_desc->add_text(cd.properties[i].getter + "()"); +				if (method_map[cd.properties[i].getter].arguments.size() > 0) { +					// Getters with additional arguments are exposed in the method list, so we link them here for quick access. +					class_desc->push_meta("@method " + cd.properties[i].getter); +					class_desc->add_text(cd.properties[i].getter + "()"); +					class_desc->pop(); +				} else { +					class_desc->add_text(cd.properties[i].getter + "()"); +				}  				class_desc->pop(); //color  				class_desc->push_color(comment_color);  				class_desc->add_text(" getter"); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index cfa0c7a063..74267452e6 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1283,14 +1283,25 @@ void EditorPropertyVector3::_value_changed(double val, const String &p_name) {  }  void EditorPropertyVector3::update_property() { -	Vector3 val = get_edited_object()->get(get_edited_property()); +	update_using_vector(get_edited_object()->get(get_edited_property())); +} + +void EditorPropertyVector3::update_using_vector(Vector3 p_vector) {  	setting = true; -	spin[0]->set_value(val.x); -	spin[1]->set_value(val.y); -	spin[2]->set_value(val.z); +	spin[0]->set_value(p_vector.x); +	spin[1]->set_value(p_vector.y); +	spin[2]->set_value(p_vector.z);  	setting = false;  } +Vector3 EditorPropertyVector3::get_vector() { +	Vector3 v3; +	v3.x = spin[0]->get_value(); +	v3.y = spin[1]->get_value(); +	v3.z = spin[2]->get_value(); +	return v3; +} +  void EditorPropertyVector3::_notification(int p_what) {  	if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {  		Color base = get_theme_color("accent_color", "Editor"); @@ -1434,7 +1445,7 @@ EditorPropertyVector2i::EditorPropertyVector2i(bool p_force_wide) {  	setting = false;  } -///////////////////// RECT2 ///////////////////////// +///////////////////// RECT2i /////////////////////////  void EditorPropertyRect2i::_value_changed(double val, const String &p_name) {  	if (setting) { @@ -1520,7 +1531,7 @@ EditorPropertyRect2i::EditorPropertyRect2i(bool p_force_wide) {  	setting = false;  } -///////////////////// VECTOR3 ///////////////////////// +///////////////////// VECTOR3i /////////////////////////  void EditorPropertyVector3i::_value_changed(double val, const String &p_name) {  	if (setting) { @@ -2029,21 +2040,23 @@ void EditorPropertyTransform::_value_changed(double val, const String &p_name) {  }  void EditorPropertyTransform::update_property() { -	Transform val = get_edited_object()->get(get_edited_property()); -	setting = true; -	spin[0]->set_value(val.basis[0][0]); -	spin[1]->set_value(val.basis[1][0]); -	spin[2]->set_value(val.basis[2][0]); -	spin[3]->set_value(val.basis[0][1]); -	spin[4]->set_value(val.basis[1][1]); -	spin[5]->set_value(val.basis[2][1]); -	spin[6]->set_value(val.basis[0][2]); -	spin[7]->set_value(val.basis[1][2]); -	spin[8]->set_value(val.basis[2][2]); -	spin[9]->set_value(val.origin[0]); -	spin[10]->set_value(val.origin[1]); -	spin[11]->set_value(val.origin[2]); +	update_using_transform(get_edited_object()->get(get_edited_property())); +} +void EditorPropertyTransform::update_using_transform(Transform p_transform) { +	setting = true; +	spin[0]->set_value(p_transform.basis[0][0]); +	spin[1]->set_value(p_transform.basis[1][0]); +	spin[2]->set_value(p_transform.basis[2][0]); +	spin[3]->set_value(p_transform.basis[0][1]); +	spin[4]->set_value(p_transform.basis[1][1]); +	spin[5]->set_value(p_transform.basis[2][1]); +	spin[6]->set_value(p_transform.basis[0][2]); +	spin[7]->set_value(p_transform.basis[1][2]); +	spin[8]->set_value(p_transform.basis[2][2]); +	spin[9]->set_value(p_transform.origin[0]); +	spin[10]->set_value(p_transform.origin[1]); +	spin[11]->set_value(p_transform.origin[2]);  	setting = false;  } diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 61c11f4534..35e6c10d90 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -392,6 +392,8 @@ protected:  public:  	virtual void update_property(); +	virtual void update_using_vector(Vector3 p_vector); +	virtual Vector3 get_vector();  	void setup(double p_min, double p_max, double p_step, bool p_no_slider);  	EditorPropertyVector3(bool p_force_wide = false);  }; @@ -536,6 +538,7 @@ protected:  public:  	virtual void update_property(); +	virtual void update_using_transform(Transform p_transform);  	void setup(double p_min, double p_max, double p_step, bool p_no_slider);  	EditorPropertyTransform();  }; diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 2586f17fe1..52da8dea19 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -62,125 +62,56 @@ void BoneTransformEditor::create_editors() {  	enabled_checkbox->set_visible(toggle_enabled);  	section->get_vbox()->add_child(enabled_checkbox); -	Label *l1 = memnew(Label(TTR("Translation"))); -	section->get_vbox()->add_child(l1); - -	translation_grid = memnew(GridContainer()); -	translation_grid->set_columns(TRANSLATION_COMPONENTS); -	section->get_vbox()->add_child(translation_grid); - -	Label *l2 = memnew(Label(TTR("Rotation Degrees"))); -	section->get_vbox()->add_child(l2); - -	rotation_grid = memnew(GridContainer()); -	rotation_grid->set_columns(ROTATION_DEGREES_COMPONENTS); -	section->get_vbox()->add_child(rotation_grid); - -	Label *l3 = memnew(Label(TTR("Scale"))); -	section->get_vbox()->add_child(l3); - -	scale_grid = memnew(GridContainer()); -	scale_grid->set_columns(SCALE_COMPONENTS); -	section->get_vbox()->add_child(scale_grid); - -	Label *l4 = memnew(Label(TTR("Transform"))); -	section->get_vbox()->add_child(l4); - -	transform_grid = memnew(GridContainer()); -	transform_grid->set_columns(TRANSFORM_CONTROL_COMPONENTS); -	section->get_vbox()->add_child(transform_grid); - -	static const char *desc[TRANSFORM_COMPONENTS] = { "x", "y", "z", "x", "y", "z", "x", "y", "z", "x", "y", "z" }; - -	for (int i = 0; i < TRANSFORM_CONTROL_COMPONENTS; ++i) { -		translation_slider[i] = memnew(EditorSpinSlider()); -		translation_slider[i]->set_label(desc[i]); -		setup_spinner(translation_slider[i], false); -		translation_grid->add_child(translation_slider[i]); - -		rotation_slider[i] = memnew(EditorSpinSlider()); -		rotation_slider[i]->set_label(desc[i]); -		setup_spinner(rotation_slider[i], false); -		rotation_grid->add_child(rotation_slider[i]); - -		scale_slider[i] = memnew(EditorSpinSlider()); -		scale_slider[i]->set_label(desc[i]); -		setup_spinner(scale_slider[i], false); -		scale_grid->add_child(scale_slider[i]); -	} - -	for (int i = 0; i < TRANSFORM_COMPONENTS; ++i) { -		transform_slider[i] = memnew(EditorSpinSlider()); -		transform_slider[i]->set_label(desc[i]); -		setup_spinner(transform_slider[i], true); -		transform_grid->add_child(transform_slider[i]); -	} -} - -void BoneTransformEditor::setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner) { -	spinner->set_flat(true); -	spinner->set_min(-10000); -	spinner->set_max(10000); -	spinner->set_step(0.001f); -	spinner->set_hide_slider(true); -	spinner->set_allow_greater(true); -	spinner->set_allow_lesser(true); -	spinner->set_h_size_flags(SIZE_EXPAND_FILL); - -	spinner->connect_compat("value_changed", this, "_value_changed", varray(is_transform_spinner)); +	// Translation property +	translation_property = memnew(EditorPropertyVector3()); +	translation_property->setup(-10000, 10000, 0.001f, true); +	translation_property->set_label("Translation"); +	translation_property->set_use_folding(true); +	translation_property->set_read_only(false); +	translation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3)); +	section->get_vbox()->add_child(translation_property); + +	// Rotation property +	rotation_property = memnew(EditorPropertyVector3()); +	rotation_property->setup(-10000, 10000, 0.001f, true); +	rotation_property->set_label("Rotation Degrees"); +	rotation_property->set_use_folding(true); +	rotation_property->set_read_only(false); +	rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3)); +	section->get_vbox()->add_child(rotation_property); + +	// Scale property +	scale_property = memnew(EditorPropertyVector3()); +	scale_property->setup(-10000, 10000, 0.001f, true); +	scale_property->set_label("Scale"); +	scale_property->set_use_folding(true); +	scale_property->set_read_only(false); +	scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3)); +	section->get_vbox()->add_child(scale_property); + +	// Transform/Matrix section +	transform_section = memnew(EditorInspectorSection); +	transform_section->setup("trf_properties_transform", "Matrix", this, section_color, true); +	section->get_vbox()->add_child(transform_section); + +	// Transform/Matrix property +	transform_property = memnew(EditorPropertyTransform()); +	transform_property->setup(-10000, 10000, 0.001f, true); +	transform_property->set_label("Transform"); +	transform_property->set_use_folding(true); +	transform_property->set_read_only(false); +	transform_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_transform)); +	transform_section->get_vbox()->add_child(transform_property);  }  void BoneTransformEditor::_notification(int p_what) {  	switch (p_what) {  		case NOTIFICATION_ENTER_TREE: {  			create_editors(); -			key_button->connect_compat("pressed", this, "_key_button_pressed"); -			enabled_checkbox->connect_compat("toggled", this, "_checkbox_toggled"); +			key_button->connect("pressed", callable_mp(this, &BoneTransformEditor::_key_button_pressed)); +			enabled_checkbox->connect("toggled", callable_mp(this, &BoneTransformEditor::_checkbox_toggled));  			[[fallthrough]];  		} -		case NOTIFICATION_THEME_CHANGED: { -			const Color base = get_theme_color("accent_color", "Editor"); -			const Color bg_color = get_theme_color("property_color", "Editor"); -			const Color bg_lbl_color(bg_color.r, bg_color.g, bg_color.b, 0.5); - -			for (int i = 0; i < TRANSLATION_COMPONENTS; i++) { -				Color c = base; -				c.set_hsv(float(i % TRANSLATION_COMPONENTS) / TRANSLATION_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); -				if (!translation_slider[i]) { -					continue; -				} -				translation_slider[i]->set_custom_label_color(true, c); -			} - -			for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) { -				Color c = base; -				c.set_hsv(float(i % ROTATION_DEGREES_COMPONENTS) / ROTATION_DEGREES_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); -				if (!rotation_slider[i]) { -					continue; -				} -				rotation_slider[i]->set_custom_label_color(true, c); -			} - -			for (int i = 0; i < SCALE_COMPONENTS; i++) { -				Color c = base; -				c.set_hsv(float(i % SCALE_COMPONENTS) / SCALE_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); -				if (!scale_slider[i]) { -					continue; -				} -				scale_slider[i]->set_custom_label_color(true, c); -			} - -			for (int i = 0; i < TRANSFORM_COMPONENTS; i++) { -				Color c = base; -				c.set_hsv(float(i % TRANSFORM_COMPONENTS) / TRANSFORM_COMPONENTS + 0.05, c.get_s() * 0.75, c.get_v()); -				if (!transform_slider[i]) { -					continue; -				} -				transform_slider[i]->set_custom_label_color(true, c); -			} - -			break; -		}  		case NOTIFICATION_SORT_CHILDREN: {  			const Ref<Font> font = get_theme_font("font", "Tree"); @@ -189,8 +120,8 @@ void BoneTransformEditor::_notification(int p_what) {  			buffer.y += font->get_height();  			buffer.y += get_theme_constant("vseparation", "Tree"); -			const float vector_height = translation_grid->get_size().y; -			const float transform_height = transform_grid->get_size().y; +			const float vector_height = translation_property->get_size().y; +			const float transform_height = transform_property->get_size().y;  			const float button_height = key_button->get_size().y;  			const float width = get_size().x - get_theme_constant("inspector_margin", "Editor"); @@ -202,10 +133,10 @@ void BoneTransformEditor::_notification(int p_what) {  			}  			if (section->get_vbox()->is_visible()) { -				input_rects.push_back(Rect2(translation_grid->get_position() + buffer, Size2(width, vector_height))); -				input_rects.push_back(Rect2(rotation_grid->get_position() + buffer, Size2(width, vector_height))); -				input_rects.push_back(Rect2(scale_grid->get_position() + buffer, Size2(width, vector_height))); -				input_rects.push_back(Rect2(transform_grid->get_position() + buffer, Size2(width, transform_height))); +				input_rects.push_back(Rect2(translation_property->get_position() + buffer, Size2(width, vector_height))); +				input_rects.push_back(Rect2(rotation_property->get_position() + buffer, Size2(width, vector_height))); +				input_rects.push_back(Rect2(scale_property->get_position() + buffer, Size2(width, vector_height))); +				input_rects.push_back(Rect2(transform_property->get_position() + buffer, Size2(width, transform_height)));  			} else {  				const int32_t start = input_rects.size();  				const int32_t empty_input_rect_elements = 4; @@ -234,49 +165,53 @@ void BoneTransformEditor::_notification(int p_what) {  	}  } -void BoneTransformEditor::_value_changed(const double p_value, const bool p_from_transform) { +void BoneTransformEditor::_value_changed(const double p_value) {  	if (updating)  		return; -	if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") { -		const Transform tform = compute_transform(p_from_transform); +	Transform tform = compute_transform_from_vector3s(); +	_change_transform(tform); +} +void BoneTransformEditor::_value_changed_vector3(const String p_property_name, const Vector3 p_vector, const StringName p_edited_property_name, const bool p_boolean) { +	if (updating) +		return; +	Transform tform = compute_transform_from_vector3s(); +	_change_transform(tform); +} + +Transform BoneTransformEditor::compute_transform_from_vector3s() const { +	// Convert rotation from degrees to radians. +	Vector3 prop_rotation = rotation_property->get_vector(); +	prop_rotation.x = Math::deg2rad(prop_rotation.x); +	prop_rotation.y = Math::deg2rad(prop_rotation.y); +	prop_rotation.z = Math::deg2rad(prop_rotation.z); + +	return Transform( +			Basis(prop_rotation, scale_property->get_vector()), +			translation_property->get_vector()); +} + +void BoneTransformEditor::_value_changed_transform(const String p_property_name, const Transform p_transform, const StringName p_edited_property_name, const bool p_boolean) { +	if (updating) +		return; +	_change_transform(p_transform); +} + +void BoneTransformEditor::_change_transform(Transform p_new_transform) { +	if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") {  		undo_redo->create_action(TTR("Set Custom Bone Pose Transform"), UndoRedo::MERGE_ENDS);  		undo_redo->add_undo_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), skeleton->get_bone_custom_pose(property.get_slicec('/', 1).to_int())); -		undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), tform); +		undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), p_new_transform);  		undo_redo->commit_action();  	} else if (property.get_slicec('/', 0) == "bones") { -		const Transform tform = compute_transform(p_from_transform); -  		undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);  		undo_redo->add_undo_property(skeleton, property, skeleton->get(property)); -		undo_redo->add_do_property(skeleton, property, tform); +		undo_redo->add_do_property(skeleton, property, p_new_transform);  		undo_redo->commit_action();  	}  } -Transform BoneTransformEditor::compute_transform(const bool p_from_transform) const { -	// Last modified was a raw transform column... -	if (p_from_transform) { -		Transform tform; - -		for (int i = 0; i < BASIS_COMPONENTS; ++i) { -			tform.basis[i / BASIS_SPLIT_COMPONENTS][i % BASIS_SPLIT_COMPONENTS] = transform_slider[i]->get_value(); -		} - -		for (int i = 0; i < TRANSLATION_COMPONENTS; ++i) { -			tform.origin[i] = transform_slider[i + BASIS_COMPONENTS]->get_value(); -		} - -		return tform; -	} - -	return Transform( -			Basis(Vector3(Math::deg2rad(rotation_slider[0]->get_value()), Math::deg2rad(rotation_slider[1]->get_value()), Math::deg2rad(rotation_slider[2]->get_value())), -					Vector3(scale_slider[0]->get_value(), scale_slider[1]->get_value(), scale_slider[2]->get_value())), -			Vector3(translation_slider[0]->get_value(), translation_slider[1]->get_value(), translation_slider[2]->get_value())); -} -  void BoneTransformEditor::update_enabled_checkbox() {  	if (enabled_checkbox) {  		const String path = "bones/" + property.get_slicec('/', 1) + "/enabled"; @@ -285,12 +220,6 @@ void BoneTransformEditor::update_enabled_checkbox() {  	}  } -void BoneTransformEditor::_bind_methods() { -	ClassDB::bind_method(D_METHOD("_value_changed", "value"), &BoneTransformEditor::_value_changed); -	ClassDB::bind_method(D_METHOD("_key_button_pressed"), &BoneTransformEditor::_key_button_pressed); -	ClassDB::bind_method(D_METHOD("_checkbox_toggled", "toggled"), &BoneTransformEditor::_checkbox_toggled); -} -  void BoneTransformEditor::_update_properties() {  	if (updating)  		return; @@ -318,47 +247,22 @@ void BoneTransformEditor::_update_custom_pose_properties() {  }  void BoneTransformEditor::_update_transform_properties(Transform tform) { -	Quat rot = tform.get_basis(); -	Vector3 rot_rad = rot.get_euler(); -	Vector3 rot_degrees = Vector3(Math::rad2deg(rot_rad.x), Math::rad2deg(rot_rad.y), Math::rad2deg(rot_rad.z)); -	Vector3 tr = tform.get_origin(); +	Basis rotation_basis = tform.get_basis(); +	Vector3 rotation_radians = rotation_basis.get_rotation_euler(); +	Vector3 rotation_degrees = Vector3(Math::rad2deg(rotation_radians.x), Math::rad2deg(rotation_radians.y), Math::rad2deg(rotation_radians.z)); +	Vector3 translation = tform.get_origin();  	Vector3 scale = tform.basis.get_scale(); -	for (int i = 0; i < TRANSLATION_COMPONENTS; i++) { -		translation_slider[i]->set_value(tr[i]); -	} - -	for (int i = 0; i < ROTATION_DEGREES_COMPONENTS; i++) { -		rotation_slider[i]->set_value(rot_degrees[i]); -	} - -	for (int i = 0; i < SCALE_COMPONENTS; i++) { -		scale_slider[i]->set_value(scale[i]); -	} - -	transform_slider[0]->set_value(tform.get_basis()[Vector3::AXIS_X].x); -	transform_slider[1]->set_value(tform.get_basis()[Vector3::AXIS_X].y); -	transform_slider[2]->set_value(tform.get_basis()[Vector3::AXIS_X].z); -	transform_slider[3]->set_value(tform.get_basis()[Vector3::AXIS_Y].x); -	transform_slider[4]->set_value(tform.get_basis()[Vector3::AXIS_Y].y); -	transform_slider[5]->set_value(tform.get_basis()[Vector3::AXIS_Y].z); -	transform_slider[6]->set_value(tform.get_basis()[Vector3::AXIS_Z].x); -	transform_slider[7]->set_value(tform.get_basis()[Vector3::AXIS_Z].y); -	transform_slider[8]->set_value(tform.get_basis()[Vector3::AXIS_Z].z); - -	for (int i = 0; i < TRANSLATION_COMPONENTS; i++) { -		transform_slider[BASIS_COMPONENTS + i]->set_value(tform.get_origin()[i]); -	} +	translation_property->update_using_vector(translation); +	rotation_property->update_using_vector(rotation_degrees); +	scale_property->update_using_vector(scale); +	transform_property->update_using_transform(tform);  	update_enabled_checkbox();  	updating = false;  }  BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) : -		translation_slider(), -		rotation_slider(), -		scale_slider(), -		transform_slider(),  		skeleton(p_skeleton),  		key_button(nullptr),  		enabled_checkbox(nullptr), @@ -397,7 +301,7 @@ void BoneTransformEditor::_key_button_pressed() {  		return;  	// Need to normalize the basis before you key it -	Transform tform = compute_transform(true); +	Transform tform = compute_transform_from_vector3s();  	tform.orthonormalize();  	AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(skeleton, name, tform);  } @@ -627,8 +531,7 @@ void Skeleton3DEditor::update_joint_tree() {  	items.insert(-1, root);  	const Vector<int> &joint_porder = skeleton->get_bone_process_orders(); - -	Ref<Texture> bone_icon = get_theme_icon("Skeleton3D", "EditorIcons"); +	Ref<Texture> bone_icon = get_theme_icon("BoneAttachment3D", "EditorIcons");  	for (int i = 0; i < joint_porder.size(); ++i) {  		const int b_idx = joint_porder[i]; @@ -714,11 +617,11 @@ void Skeleton3DEditor::_notification(int p_what) {  			update_joint_tree();  			update_editors(); -			get_tree()->connect_compat("node_removed", this, "_node_removed", Vector<Variant>(), Object::CONNECT_ONESHOT); -			joint_tree->connect_compat("item_selected", this, "_joint_tree_selection_changed"); -			joint_tree->connect_compat("item_rmb_selected", this, "_joint_tree_rmb_select"); +			get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Vector<Variant>(), Object::CONNECT_ONESHOT); +			joint_tree->connect("item_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_selection_changed)); +			joint_tree->connect("item_rmb_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_rmb_select));  #ifdef TOOLS_ENABLED -			skeleton->connect_compat("pose_updated", this, "_update_properties"); +			skeleton->connect("pose_updated", callable_mp(this, &Skeleton3DEditor::_update_properties));  #endif // TOOLS_ENABLED  			break; diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h index 8b0639ed92..a5b8ce80a9 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.h +++ b/editor/plugins/skeleton_3d_editor_plugin.h @@ -41,30 +41,19 @@ class PhysicalBone3D;  class Skeleton3DEditorPlugin;  class Button;  class CheckBox; +class EditorPropertyTransform; +class EditorPropertyVector3;  class BoneTransformEditor : public VBoxContainer {  	GDCLASS(BoneTransformEditor, VBoxContainer); -	static const int32_t TRANSLATION_COMPONENTS = 3; -	static const int32_t ROTATION_DEGREES_COMPONENTS = 3; -	static const int32_t SCALE_COMPONENTS = 3; -	static const int32_t BASIS_COMPONENTS = 9; -	static const int32_t BASIS_SPLIT_COMPONENTS = 3; -	static const int32_t TRANSFORM_COMPONENTS = 12; -	static const int32_t TRANSFORM_SPLIT_COMPONENTS = 3; -	static const int32_t TRANSFORM_CONTROL_COMPONENTS = 3; -  	EditorInspectorSection *section; -	GridContainer *translation_grid; -	GridContainer *rotation_grid; -	GridContainer *scale_grid; -	GridContainer *transform_grid; - -	EditorSpinSlider *translation_slider[TRANSLATION_COMPONENTS]; -	EditorSpinSlider *rotation_slider[ROTATION_DEGREES_COMPONENTS]; -	EditorSpinSlider *scale_slider[SCALE_COMPONENTS]; -	EditorSpinSlider *transform_slider[TRANSFORM_COMPONENTS]; +	EditorPropertyVector3 *translation_property; +	EditorPropertyVector3 *rotation_property; +	EditorPropertyVector3 *scale_property; +	EditorInspectorSection *transform_section; +	EditorPropertyTransform *transform_property;  	Rect2 background_rects[5]; @@ -83,17 +72,22 @@ class BoneTransformEditor : public VBoxContainer {  	String label;  	void create_editors(); -	void setup_spinner(EditorSpinSlider *spinner, const bool is_transform_spinner); -	void _value_changed(const double p_value, const bool p_from_transform); - -	Transform compute_transform(const bool p_from_transform) const; +	// Called when one of the EditorSpinSliders are changed. +	void _value_changed(const double p_value); +	// Called when the one of the EditorPropertyVector3 are updated. +	void _value_changed_vector3(const String p_property_name, const Vector3 p_vector, const StringName p_edited_property_name, const bool p_boolean); +	// Called when the transform_property is updated. +	void _value_changed_transform(const String p_property_name, const Transform p_transform, const StringName p_edited_property_name, const bool p_boolean); +	// Changes the transform to the given transform and updates the UI accordingly. +	void _change_transform(Transform p_new_transform); +	// Creates a Transform using the EditorPropertyVector3 properties. +	Transform compute_transform_from_vector3s() const;  	void update_enabled_checkbox();  protected:  	void _notification(int p_what); -	static void _bind_methods();  public:  	BoneTransformEditor(Skeleton3D *p_skeleton); diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 50d8289fd1..3a5db3687b 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -3093,6 +3093,14 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co  								return OK;  							}  						} + +						for (int i = 0; i < base_type.class_type->subclasses.size(); i++) { +							if (base_type.class_type->subclasses[i]->name == p_symbol) { +								r_result.type = ScriptLanguage::LookupResult::RESULT_SCRIPT_LOCATION; +								r_result.location = base_type.class_type->subclasses[i]->line; +								return OK; +							} +						}  					}  					base_type = base_type.class_type->base_type;  				} diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index a06ecb1ea8..d2867cb77a 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -2821,6 +2821,8 @@ void GDScriptParser::_parse_block(BlockNode *p_block, bool p_static) {  #ifdef DEBUG_ENABLED +	pending_newline = -1; // reset for the new block +  	NewLineNode *nl = alloc_node<NewLineNode>();  	nl->line = tokenizer->get_token_line(); diff --git a/modules/mono/build_scripts/godot_tools_build.py b/modules/mono/build_scripts/godot_tools_build.py index 7391e8790d..3bbbf29d3b 100644 --- a/modules/mono/build_scripts/godot_tools_build.py +++ b/modules/mono/build_scripts/godot_tools_build.py @@ -15,7 +15,9 @@ def build_godot_tools(source, target, env):      from .solution_builder import build_solution -    build_solution(env, solution_path, build_config) +    extra_msbuild_args = ["/p:GodotPlatform=" + env["platform"]] + +    build_solution(env, solution_path, build_config, extra_msbuild_args)      # No need to copy targets. The GodotTools csproj takes care of copying them. diff --git a/modules/mono/build_scripts/mono_configure.py b/modules/mono/build_scripts/mono_configure.py index 23f01b3cca..80e3b59325 100644 --- a/modules/mono/build_scripts/mono_configure.py +++ b/modules/mono/build_scripts/mono_configure.py @@ -191,17 +191,16 @@ def configure(env, env_mono):                  env.Append(LIBS=["psapi"])                  env.Append(LIBS=["version"])          else: -            mono_lib_name = find_name_in_dir_files( -                mono_lib_path, mono_lib_names, prefixes=["", "lib"], extensions=lib_suffixes -            ) +            mono_lib_file = find_file_in_dir(mono_lib_path, mono_lib_names, extensions=lib_suffixes) -            if not mono_lib_name: +            if not mono_lib_file:                  raise RuntimeError("Could not find mono library in: " + mono_lib_path)              if env.msvc: -                env.Append(LINKFLAGS=mono_lib_name + ".lib") +                env.Append(LINKFLAGS=mono_lib_file)              else: -                env.Append(LIBS=[mono_lib_name]) +                mono_lib_file_path = os.path.join(mono_lib_path, mono_lib_file) +                env.Append(LINKFLAGS=mono_lib_file_path)              mono_bin_path = os.path.join(mono_root, "bin") diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs index d069651dd3..572c541412 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Client.cs @@ -19,9 +19,12 @@ namespace GodotTools.IdeMessaging          private readonly string identity;          private string MetaFilePath { get; } +        private DateTime? metaFileModifiedTime;          private GodotIdeMetadata godotIdeMetadata;          private readonly FileSystemWatcher fsWatcher; +        public string GodotEditorExecutablePath => godotIdeMetadata.EditorExecutablePath; +          private readonly IMessageHandler messageHandler;          private Peer peer; @@ -123,7 +126,7 @@ namespace GodotTools.IdeMessaging              MetaFilePath = Path.Combine(projectMetadataDir, GodotIdeMetadata.DefaultFileName);              // FileSystemWatcher requires an existing directory -            if (!File.Exists(projectMetadataDir)) +            if (!Directory.Exists(projectMetadataDir))                  Directory.CreateDirectory(projectMetadataDir);              fsWatcher = new FileSystemWatcher(projectMetadataDir, GodotIdeMetadata.DefaultFileName); @@ -142,6 +145,13 @@ namespace GodotTools.IdeMessaging                  if (!File.Exists(MetaFilePath))                      return; +                var lastWriteTime = File.GetLastWriteTime(MetaFilePath); + +                if (lastWriteTime == metaFileModifiedTime) +                    return; + +                metaFileModifiedTime = lastWriteTime; +                  var metadata = ReadMetadataFile();                  if (metadata != null && metadata != godotIdeMetadata) @@ -173,6 +183,13 @@ namespace GodotTools.IdeMessaging                  if (IsConnected || !File.Exists(MetaFilePath))                      return; +                var lastWriteTime = File.GetLastWriteTime(MetaFilePath); + +                if (lastWriteTime == metaFileModifiedTime) +                    return; + +                metaFileModifiedTime = lastWriteTime; +                  var metadata = ReadMetadataFile();                  if (metadata != null) @@ -185,7 +202,8 @@ namespace GodotTools.IdeMessaging          private GodotIdeMetadata? ReadMetadataFile()          { -            using (var reader = File.OpenText(MetaFilePath)) +            using (var fileStream = new FileStream(MetaFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) +            using (var reader = new StreamReader(fileStream))              {                  string portStr = reader.ReadLine(); @@ -272,6 +290,7 @@ namespace GodotTools.IdeMessaging          // ReSharper disable once UnusedMember.Global          public async void Start()          { +            fsWatcher.Created += OnMetaFileChanged;              fsWatcher.Changed += OnMetaFileChanged;              fsWatcher.Deleted += OnMetaFileDeleted;              fsWatcher.EnableRaisingEvents = true; diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj index 67815959a6..dad6b9ae7a 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/GodotTools.IdeMessaging.csproj @@ -4,7 +4,7 @@      <TargetFramework>netstandard2.0</TargetFramework>      <LangVersion>7.2</LangVersion>      <PackageId>GodotTools.IdeMessaging</PackageId> -    <Version>1.1.0</Version> +    <Version>1.1.1</Version>      <AssemblyVersion>$(Version)</AssemblyVersion>      <Authors>Godot Engine contributors</Authors>      <Company /> diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs index a4e86d6177..10d7e1898e 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Peer.cs @@ -105,49 +105,45 @@ namespace GodotTools.IdeMessaging                      try                      { -                        try +                        if (msg.Kind == MessageKind.Request)                          { -                            if (msg.Kind == MessageKind.Request) -                            { -                                var responseContent = await messageHandler.HandleRequest(this, msg.Id, msg.Content, Logger); -                                await WriteMessage(new Message(MessageKind.Response, msg.Id, responseContent)); -                            } -                            else if (msg.Kind == MessageKind.Response) -                            { -                                ResponseAwaiter responseAwaiter; +                            var responseContent = await messageHandler.HandleRequest(this, msg.Id, msg.Content, Logger); +                            await WriteMessage(new Message(MessageKind.Response, msg.Id, responseContent)); +                        } +                        else if (msg.Kind == MessageKind.Response) +                        { +                            ResponseAwaiter responseAwaiter; -                                using (await requestsSem.UseAsync()) +                            using (await requestsSem.UseAsync()) +                            { +                                if (!requestAwaiterQueues.TryGetValue(msg.Id, out var queue) || queue.Count <= 0)                                  { -                                    if (!requestAwaiterQueues.TryGetValue(msg.Id, out var queue) || queue.Count <= 0) -                                    { -                                        Logger.LogError($"Received unexpected response: {msg.Id}"); -                                        return; -                                    } - -                                    responseAwaiter = queue.Dequeue(); +                                    Logger.LogError($"Received unexpected response: {msg.Id}"); +                                    return;                                  } -                                responseAwaiter.SetResult(msg.Content); -                            } -                            else -                            { -                                throw new IndexOutOfRangeException($"Invalid message kind {msg.Kind}"); +                                responseAwaiter = queue.Dequeue();                              } + +                            responseAwaiter.SetResult(msg.Content);                          } -                        catch (Exception e) +                        else                          { -                            Logger.LogError($"Message handler for '{msg}' failed with exception", e); +                            throw new IndexOutOfRangeException($"Invalid message kind {msg.Kind}");                          }                      }                      catch (Exception e)                      { -                        Logger.LogError($"Exception thrown from message handler. Message: {msg}", e); +                        Logger.LogError($"Message handler for '{msg}' failed with exception", e);                      }                  }              }              catch (Exception e)              { -                Logger.LogError("Unhandled exception in the peer loop", e); +                if (!IsDisposed || !(e is SocketException || e.InnerException is SocketException)) +                { +                    Logger.LogError("Unhandled exception in the peer loop", e); +                }              }          } diff --git a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs index 1dd4f852e5..e93db9377b 100644 --- a/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs +++ b/modules/mono/editor/GodotTools/GodotTools.IdeMessaging/Requests/Requests.cs @@ -67,6 +67,19 @@ namespace GodotTools.IdeMessaging.Requests      {      } +    public sealed class StopPlayRequest : Request +    { +        public new const string Id = "StopPlay"; + +        public StopPlayRequest() : base(Id) +        { +        } +    } + +    public sealed class StopPlayResponse : Response +    { +    } +      public sealed class DebugPlayRequest : Request      {          public string DebuggerHost { get; set; } diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj new file mode 100644 index 0000000000..5b3ed0b1b7 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/GodotTools.OpenVisualStudio.csproj @@ -0,0 +1,12 @@ +<Project Sdk="Microsoft.NET.Sdk"> +    <PropertyGroup> +        <ProjectGuid>{EAFFF236-FA96-4A4D-BD23-0E51EF988277}</ProjectGuid> +        <OutputType>Exe</OutputType> +        <TargetFramework>net472</TargetFramework> +        <LangVersion>7.2</LangVersion> +    </PropertyGroup> +    <ItemGroup> +        <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" /> +        <PackageReference Include="EnvDTE" Version="8.0.2" /> +    </ItemGroup> +</Project> diff --git a/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs new file mode 100644 index 0000000000..affb2a47e7 --- /dev/null +++ b/modules/mono/editor/GodotTools/GodotTools.OpenVisualStudio/Program.cs @@ -0,0 +1,270 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text.RegularExpressions; +using EnvDTE; + +namespace GodotTools.OpenVisualStudio +{ +    internal static class Program +    { +        [DllImport("ole32.dll")] +        private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable pprot); + +        [DllImport("ole32.dll")] +        private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc); + +        [DllImport("user32.dll")] +        private static extern bool SetForegroundWindow(IntPtr hWnd); + +        private static void ShowHelp() +        { +            Console.WriteLine("Opens the file(s) in a Visual Studio instance that is editing the specified solution."); +            Console.WriteLine("If an existing instance for the solution is not found, a new one is created."); +            Console.WriteLine(); +            Console.WriteLine("Usage:"); +            Console.WriteLine(@"  GodotTools.OpenVisualStudio.exe solution [file[;line[;col]]...]"); +            Console.WriteLine(); +            Console.WriteLine("Lines and columns begin at one. Zero or lower will result in an error."); +            Console.WriteLine("If a line is specified but a column is not, the line is selected in the text editor."); +        } + +        // STAThread needed, otherwise CoRegisterMessageFilter may return CO_E_NOT_SUPPORTED. +        [STAThread] +        private static int Main(string[] args) +        { +            if (args.Length == 0 || args[0] == "--help" || args[0] == "-h") +            { +                ShowHelp(); +                return 0; +            } + +            string solutionFile = NormalizePath(args[0]); + +            var dte = FindInstanceEditingSolution(solutionFile); + +            if (dte == null) +            { +                // Open a new instance + +                var visualStudioDteType = Type.GetTypeFromProgID("VisualStudio.DTE.16.0", throwOnError: true); +                dte = (DTE)Activator.CreateInstance(visualStudioDteType); + +                dte.UserControl = true; + +                try +                { +                    dte.Solution.Open(solutionFile); +                } +                catch (ArgumentException) +                { +                    Console.Error.WriteLine("Solution.Open: Invalid path or file not found"); +                    return 1; +                } + +                dte.MainWindow.Visible = true; +            } + +            MessageFilter.Register(); + +            try +            { +                // Open files + +                for (int i = 1; i < args.Length; i++) +                { +                    // Both the line number and the column begin at one + +                    string[] fileArgumentParts = args[i].Split(';'); + +                    string filePath = NormalizePath(fileArgumentParts[0]); + +                    try +                    { +                        dte.ItemOperations.OpenFile(filePath); +                    } +                    catch (ArgumentException) +                    { +                        Console.Error.WriteLine("ItemOperations.OpenFile: Invalid path or file not found"); +                        return 1; +                    } + +                    if (fileArgumentParts.Length > 1) +                    { +                        if (int.TryParse(fileArgumentParts[1], out int line)) +                        { +                            var textSelection = (TextSelection)dte.ActiveDocument.Selection; + +                            if (fileArgumentParts.Length > 2) +                            { +                                if (int.TryParse(fileArgumentParts[2], out int column)) +                                { +                                    textSelection.MoveToLineAndOffset(line, column); +                                } +                                else +                                { +                                    Console.Error.WriteLine("The column part of the argument must be a valid integer"); +                                    return 1; +                                } +                            } +                            else +                            { +                                textSelection.GotoLine(line, Select: true); +                            } +                        } +                        else +                        { +                            Console.Error.WriteLine("The line part of the argument must be a valid integer"); +                            return 1; +                        } +                    } +                } +            } +            finally +            { +                var mainWindow = dte.MainWindow; +                mainWindow.Activate(); +                SetForegroundWindow(new IntPtr(mainWindow.HWnd)); + +                MessageFilter.Revoke(); +            } + +            return 0; +        } + +        private static DTE FindInstanceEditingSolution(string solutionPath) +        { +            if (GetRunningObjectTable(0, out IRunningObjectTable pprot) != 0) +                return null; + +            try +            { +                pprot.EnumRunning(out IEnumMoniker ppenumMoniker); +                ppenumMoniker.Reset(); + +                var moniker = new IMoniker[1]; + +                while (ppenumMoniker.Next(1, moniker, IntPtr.Zero) == 0) +                { +                    string ppszDisplayName; + +                    CreateBindCtx(0, out IBindCtx ppbc); + +                    try +                    { +                        moniker[0].GetDisplayName(ppbc, null, out ppszDisplayName); +                    } +                    finally +                    { +                        Marshal.ReleaseComObject(ppbc); +                    } + +                    if (ppszDisplayName == null) +                        continue; + +                    // The digits after the colon are the process ID +                    if (!Regex.IsMatch(ppszDisplayName, "!VisualStudio.DTE.16.0:[0-9]")) +                        continue; + +                    if (pprot.GetObject(moniker[0], out object ppunkObject) == 0) +                    { +                        if (ppunkObject is DTE dte && dte.Solution.FullName.Length > 0) +                        { +                            if (NormalizePath(dte.Solution.FullName) == solutionPath) +                                return dte; +                        } +                    } +                } +            } +            finally +            { +                Marshal.ReleaseComObject(pprot); +            } + +            return null; +        } + +        static string NormalizePath(string path) +        { +            return new Uri(Path.GetFullPath(path)).LocalPath +                .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) +                .ToUpperInvariant(); +        } + +        #region MessageFilter. See: http: //msdn.microsoft.com/en-us/library/ms228772.aspx + +        private class MessageFilter : IOleMessageFilter +        { +            // Class containing the IOleMessageFilter +            // thread error-handling functions + +            private static IOleMessageFilter _oldFilter; + +            // Start the filter +            public static void Register() +            { +                IOleMessageFilter newFilter = new MessageFilter(); +                int ret = CoRegisterMessageFilter(newFilter, out _oldFilter); +                if (ret != 0) +                    Console.Error.WriteLine($"CoRegisterMessageFilter failed with error code: {ret}"); +            } + +            // Done with the filter, close it +            public static void Revoke() +            { +                int ret = CoRegisterMessageFilter(_oldFilter, out _); +                if (ret != 0) +                    Console.Error.WriteLine($"CoRegisterMessageFilter failed with error code: {ret}"); +            } + +            // +            // IOleMessageFilter functions +            // Handle incoming thread requests +            int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo) +            { +                // Return the flag SERVERCALL_ISHANDLED +                return 0; +            } + +            // Thread call was rejected, so try again. +            int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType) +            { +                if (dwRejectType == 2) +                    // flag = SERVERCALL_RETRYLATER +                { +                    // Retry the thread call immediately if return >= 0 & < 100 +                    return 99; +                } + +                // Too busy; cancel call +                return -1; +            } + +            int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType) +            { +                // Return the flag PENDINGMSG_WAITDEFPROCESS +                return 2; +            } + +            // Implement the IOleMessageFilter interface +            [DllImport("ole32.dll")] +            private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter); +        } + +        [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +        private interface IOleMessageFilter +        { +            [PreserveSig] +            int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo); + +            [PreserveSig] +            int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType); + +            [PreserveSig] +            int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType); +        } + +        #endregion +    } +} diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs index fb2beb6995..679d5bb444 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs @@ -12,6 +12,11 @@ namespace GodotTools.ProjectEditor          private const string CoreApiProjectName = "GodotSharp";          private const string EditorApiProjectName = "GodotSharpEditor"; +        public const string CSharpProjectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"; +        public const string GodotProjectTypeGuid = "{8F3E2DF0-C35C-4265-82FC-BEA011F4A7ED}"; + +        public static readonly string GodotDefaultProjectTypeGuids = $"{GodotProjectTypeGuid};{CSharpProjectTypeGuid}"; +          public static string GenGameProject(string dir, string name, IEnumerable<string> compileItems)          {              string path = Path.Combine(dir, name + ".csproj"); @@ -19,6 +24,7 @@ namespace GodotTools.ProjectEditor              ProjectPropertyGroupElement mainGroup;              var root = CreateLibraryProject(name, "Debug", out mainGroup); +            mainGroup.SetProperty("ProjectTypeGuids", GodotDefaultProjectTypeGuids);              mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));              mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));              mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)")); diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs index 069a1edaa3..8774b4ee31 100644 --- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs +++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs @@ -165,6 +165,21 @@ namespace GodotTools.ProjectEditor              return result.ToArray();          } +        public static void EnsureHasProjectTypeGuids(MSBuildProject project) +        { +            var root = project.Root; + +            bool found = root.PropertyGroups.Any(pg => +                string.IsNullOrEmpty(pg.Condition) && pg.Properties.Any(p => p.Name == "ProjectTypeGuids")); + +            if (found) +                return; + +            root.AddProperty("ProjectTypeGuids", ProjectGenerator.GodotDefaultProjectTypeGuids); + +            project.HasUnsavedChanges = true; +        } +          ///  Simple function to make sure the Api assembly references are configured correctly          public static void FixApiHintPath(MSBuildProject project)          { diff --git a/modules/mono/editor/GodotTools/GodotTools.sln b/modules/mono/editor/GodotTools/GodotTools.sln index f6147eb5bb..ba5379e562 100644 --- a/modules/mono/editor/GodotTools/GodotTools.sln +++ b/modules/mono/editor/GodotTools/GodotTools.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.BuildLogger", "G  EndProject  Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.IdeMessaging", "GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj", "{92600954-25F0-4291-8E11-1FEE9FC4BE20}"  EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotTools.OpenVisualStudio", "GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj", "{EAFFF236-FA96-4A4D-BD23-0E51EF988277}" +EndProject  Global  	GlobalSection(SolutionConfigurationPlatforms) = preSolution  		Debug|Any CPU = Debug|Any CPU @@ -37,5 +39,9 @@ Global  		{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Debug|Any CPU.Build.0 = Debug|Any CPU  		{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Release|Any CPU.ActiveCfg = Release|Any CPU  		{92600954-25F0-4291-8E11-1FEE9FC4BE20}.Release|Any CPU.Build.0 = Release|Any CPU +		{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.ActiveCfg = Debug|Any CPU +		{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Debug|Any CPU.Build.0 = Debug|Any CPU +		{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.ActiveCfg = Release|Any CPU +		{EAFFF236-FA96-4A4D-BD23-0E51EF988277}.Release|Any CPU.Build.0 = Release|Any CPU  	EndGlobalSection  EndGlobal diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index c874025be0..403e25781d 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -6,6 +6,7 @@ using System;  using System.Collections.Generic;  using System.Diagnostics.CodeAnalysis;  using System.IO; +using System.Linq;  using GodotTools.Ides;  using GodotTools.Ides.Rider;  using GodotTools.Internals; @@ -238,7 +239,31 @@ namespace GodotTools                      // Not an error. Tells the caller to fallback to the global external editor settings or the built-in editor.                      return Error.Unavailable;                  case ExternalEditorId.VisualStudio: -                    throw new NotSupportedException(); +                { +                    string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath); + +                    var args = new List<string> +                    { +                        GodotSharpDirs.ProjectSlnPath, +                        line >= 0 ? $"{scriptPath};{line + 1};{col + 1}" : scriptPath +                    }; + +                    string command = Path.Combine(GodotSharpDirs.DataEditorToolsDir, "GodotTools.OpenVisualStudio.exe"); + +                    try +                    { +                        if (Godot.OS.IsStdoutVerbose()) +                            Console.WriteLine($"Running: \"{command}\" {string.Join(" ", args.Select(a => $"\"{a}\""))}"); + +                        OS.RunProcess(command, args); +                    } +                    catch (Exception e) +                    { +                        GD.PushError($"Error when trying to run code editor: VisualStudio. Exception message: '{e.Message}'"); +                    } + +                    break; +                }                  case ExternalEditorId.VisualStudioForMac:                      goto case ExternalEditorId.MonoDevelop;                  case ExternalEditorId.Rider: @@ -458,6 +483,9 @@ namespace GodotTools                      // Apply the other fixes only after configurations have been migrated +                    // Make sure the existing project has the ProjectTypeGuids property (for VisualStudio) +                    ProjectUtils.EnsureHasProjectTypeGuids(msbuildProject); +                      // Make sure the existing project has Api assembly references configured correctly                      ProjectUtils.FixApiHintPath(msbuildProject); @@ -501,7 +529,8 @@ namespace GodotTools              if (OS.IsWindows)              { -                settingsHintStr += $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" + +                settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudio}" + +                                   $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +                                     $",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +                                     $",JetBrains Rider:{(int)ExternalEditorId.Rider}";              } diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj index ba527ca3b5..3f14629b11 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj +++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj @@ -33,5 +33,7 @@      <ProjectReference Include="..\GodotTools.IdeMessaging\GodotTools.IdeMessaging.csproj" />      <ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj" />      <ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj" /> +    <!-- Include it if this is an SCons build targeting Windows, or if it's not an SCons build but we're on Windows --> +    <ProjectReference Include="..\GodotTools.OpenVisualStudio\GodotTools.OpenVisualStudio.csproj" Condition=" '$(GodotPlatform)' == 'windows' Or ( '$(GodotPlatform)' == '' And '$(OS)' == 'Windows_NT' ) " />    </ItemGroup>  </Project> diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs index 32f264d100..98e8d13be0 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs @@ -307,6 +307,11 @@ namespace GodotTools.Ides                          var request = JsonConvert.DeserializeObject<DebugPlayRequest>(content.Body);                          return await HandleDebugPlay(request);                      }, +                    [StopPlayRequest.Id] = async (peer, content) => +                    { +                        var request = JsonConvert.DeserializeObject<StopPlayRequest>(content.Body); +                        return await HandleStopPlay(request); +                    },                      [ReloadScriptsRequest.Id] = async (peer, content) =>                      {                          _ = JsonConvert.DeserializeObject<ReloadScriptsRequest>(content.Body); @@ -343,6 +348,12 @@ namespace GodotTools.Ides                  return Task.FromResult<Response>(new DebugPlayResponse());              } +            private static Task<Response> HandleStopPlay(StopPlayRequest request) +            { +                DispatchToMainThread(Internal.EditorRunStop); +                return Task.FromResult<Response>(new StopPlayResponse()); +            } +              private static Task<Response> HandleReloadScripts()              {                  DispatchToMainThread(Internal.ScriptEditorDebugger_ReloadScripts); diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp index 1b4fe68582..e0cf916a01 100644 --- a/modules/mono/utils/mono_reg_utils.cpp +++ b/modules/mono/utils/mono_reg_utils.cpp @@ -75,7 +75,6 @@ LONG _RegKeyQueryString(HKEY hKey, const String &p_value_name, String &r_value)  	if (res == ERROR_MORE_DATA) {  		// dwBufferSize now contains the actual size -		Vector<WCHAR> buffer;  		buffer.resize(dwBufferSize);  		res = RegQueryValueExW(hKey, p_value_name.c_str(), 0, nullptr, (LPBYTE)buffer.ptr(), &dwBufferSize);  	} diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index a09424fa17..6723ca04b9 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -67,6 +67,8 @@ SkinReference::~SkinReference() {  	RS::get_singleton()->free(skeleton);  } +/////////////////////////////////////// +  bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {  	String path = p_path; @@ -853,6 +855,15 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) {  	return skin_ref;  } +// helper functions +Transform Skeleton3D::bone_transform_to_world_transform(Transform p_bone_transform) { +	return get_global_transform() * p_bone_transform; +} + +Transform Skeleton3D::world_transform_to_bone_transform(Transform p_world_transform) { +	return get_global_transform().affine_inverse() * p_world_transform; +} +  void Skeleton3D::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_bone_process_orders"), &Skeleton3D::get_bone_process_orders);  	ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone); @@ -892,6 +903,9 @@ void Skeleton3D::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose);  	ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose); +	ClassDB::bind_method(D_METHOD("bone_transform_to_world_transform", "bone_transform"), &Skeleton3D::bone_transform_to_world_transform); +	ClassDB::bind_method(D_METHOD("world_transform_to_bone_transform", "world_transform"), &Skeleton3D::world_transform_to_bone_transform); +  #ifndef _3D_DISABLED  	ClassDB::bind_method(D_METHOD("set_animate_physical_bones"), &Skeleton3D::set_animate_physical_bones); diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index 66706a9450..a21891a32e 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -71,10 +71,6 @@ class Skeleton3D : public Node3D {  private:  	friend class SkinReference; -	Set<SkinReference *> skin_bindings; - -	void _skin_changed(); -  	struct Bone {  		String name; @@ -116,6 +112,10 @@ private:  		}  	}; +	Set<SkinReference *> skin_bindings; + +	void _skin_changed(); +  	bool animate_physical_bones;  	Vector<Bone> bones;  	Vector<int> process_order; @@ -200,6 +200,10 @@ public:  	Ref<SkinReference> register_skin(const Ref<Skin> &p_skin); +	// Helper functions +	Transform bone_transform_to_world_transform(Transform p_transform); +	Transform world_transform_to_bone_transform(Transform p_transform); +  #ifndef _3D_DISABLED  	// Physical bone API @@ -213,7 +217,7 @@ public:  	PhysicalBone3D *get_physical_bone_parent(int p_bone);  private: -	/// This is a slow API os it's cached +	/// This is a slow API, so it's cached  	PhysicalBone3D *_get_physical_bone_parent(int p_bone);  	void _rebuild_physical_bones_cache(); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 77d4dee21e..8236f9a9e3 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1268,7 +1268,7 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui  		String class_name = vsnode->get_class_name();  		if (class_name == "VisualShaderNodeCustom") { -			class_name = vsnode->get_script_instance()->get_script()->get_language()->get_global_class_name(vsnode->get_script_instance()->get_script()->get_path()); +			class_name = vsnode->get_script_instance()->get_script()->get_path();  		}  		if (!r_classes.has(class_name)) {  			global_code_per_node += vsnode->generate_global_per_node(get_mode(), type, node); |