diff options
39 files changed, 2067 insertions, 30 deletions
diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp index 238897c2b1..30fa434fad 100644 --- a/core/string/node_path.cpp +++ b/core/string/node_path.cpp @@ -199,6 +199,21 @@ Vector<StringName> NodePath::get_subnames() const {  	return Vector<StringName>();  } +StringName NodePath::get_concatenated_names() const { +	ERR_FAIL_COND_V(!data, StringName()); + +	if (!data->concatenated_path) { +		int pc = data->path.size(); +		String concatenated; +		const StringName *sn = data->path.ptr(); +		for (int i = 0; i < pc; i++) { +			concatenated += i == 0 ? sn[i].operator String() : "/" + sn[i]; +		} +		data->concatenated_path = concatenated; +	} +	return data->concatenated_path; +} +  StringName NodePath::get_concatenated_subnames() const {  	ERR_FAIL_COND_V(!data, StringName()); diff --git a/core/string/node_path.h b/core/string/node_path.h index 53976bd524..2bce33e21e 100644 --- a/core/string/node_path.h +++ b/core/string/node_path.h @@ -39,6 +39,7 @@ class NodePath {  		SafeRefCount refcount;  		Vector<StringName> path;  		Vector<StringName> subpath; +		StringName concatenated_path;  		StringName concatenated_subpath;  		bool absolute;  		bool has_slashes; @@ -59,6 +60,7 @@ public:  	StringName get_subname(int p_idx) const;  	Vector<StringName> get_names() const;  	Vector<StringName> get_subnames() const; +	StringName get_concatenated_names() const;  	StringName get_concatenated_subnames() const;  	NodePath rel_path_to(const NodePath &p_np) const; diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index c14de74af7..8e16a767cf 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1806,6 +1806,7 @@ static void _register_variant_builtin_methods() {  	bind_method(NodePath, get_subname_count, sarray(), varray());  	bind_method(NodePath, hash, sarray(), varray());  	bind_method(NodePath, get_subname, sarray("idx"), varray()); +	bind_method(NodePath, get_concatenated_names, sarray(), varray());  	bind_method(NodePath, get_concatenated_subnames, sarray(), varray());  	bind_method(NodePath, get_as_property_path, sarray(), varray());  	bind_method(NodePath, is_empty, sarray(), varray()); diff --git a/doc/classes/BoneMap.xml b/doc/classes/BoneMap.xml new file mode 100644 index 0000000000..371cb4fa93 --- /dev/null +++ b/doc/classes/BoneMap.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="BoneMap" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +	<brief_description> +		Bone map for retargeting. +	</brief_description> +	<description> +		This class contains a hashmap that uses a list of bone names in [SkeletonProfile] as key names. +		By assigning the actual [Skeleton3D] bone name as the key value, it maps the [Skeleton3D] to the [SkeletonProfile]. +	</description> +	<tutorials> +	</tutorials> +	<methods> +		<method name="find_profile_bone_name" qualifiers="const"> +			<return type="StringName" /> +			<argument index="0" name="skeleton_bone_name" type="StringName" /> +			<description> +				Returns a profile bone name having [code]skeleton_bone_name[/code]. If not found, an empty [StringName] will be returned. +				In the retargeting process, the returned bone name is the bone name of the target skeleton. +			</description> +		</method> +		<method name="get_skeleton_bone_name" qualifiers="const"> +			<return type="StringName" /> +			<argument index="0" name="profile_bone_name" type="StringName" /> +			<description> +				Returns a skeleton bone name is mapped to [code]profile_bone_name[/code]. +				In the retargeting process, the returned bone name is the bone name of the source skeleton. +			</description> +		</method> +		<method name="set_skeleton_bone_name"> +			<return type="void" /> +			<argument index="0" name="profile_bone_name" type="StringName" /> +			<argument index="1" name="skeleton_bone_name" type="StringName" /> +			<description> +				Maps a skeleton bone name to [code]profile_bone_name[/code]. +				In the retargeting process, the setting bone name is the bone name of the source skeleton. +			</description> +		</method> +	</methods> +	<members> +		<member name="profile" type="SkeletonProfile" setter="set_profile" getter="get_profile"> +			A [SkeletonProfile] of the mapping target. Key names in the [BoneMap] are synchronized with it. +		</member> +	</members> +	<signals> +		<signal name="bone_map_updated"> +			<description> +				This signal is emitted when change the key value in the [BoneMap]. This is used to validate mapping and to update [BoneMap] editor. +			</description> +		</signal> +		<signal name="profile_updated"> +			<description> +				This signal is emitted when change the value in profile or change the reference of profile. This is used to update key names in the [BoneMap] and to redraw the [BoneMap] editor. +			</description> +		</signal> +	</signals> +</class> diff --git a/doc/classes/EditorScenePostImportPlugin.xml b/doc/classes/EditorScenePostImportPlugin.xml index 0fdbd5db1e..93fd5e46ba 100644 --- a/doc/classes/EditorScenePostImportPlugin.xml +++ b/doc/classes/EditorScenePostImportPlugin.xml @@ -114,7 +114,9 @@  		</constant>  		<constant name="INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE" value="5" enum="InternalImportCategory">  		</constant> -		<constant name="INTERNAL_IMPORT_CATEGORY_MAX" value="6" enum="InternalImportCategory"> +		<constant name="INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE" value="6" enum="InternalImportCategory"> +		</constant> +		<constant name="INTERNAL_IMPORT_CATEGORY_MAX" value="7" enum="InternalImportCategory">  		</constant>  	</constants>  </class> diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index ac434af4fa..ff2afd595a 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -281,6 +281,13 @@  		<member name="basis" type="Basis" setter="set_basis" getter="get_basis">  			Direct access to the 3x3 basis of the [Transform3D] property.  		</member> +		<member name="global_position" type="Vector3" setter="set_global_position" getter="get_global_position"> +			Global position of this node. This is equivalent to [code]global_transform.origin[/code]. +		</member> +		<member name="global_rotation" type="Vector3" setter="set_global_rotation" getter="get_global_rotation"> +			Rotation part of the global transformation in radians, specified in terms of YXZ-Euler angles in the format (X angle, Y angle, Z angle). +			[b]Note:[/b] In the mathematical sense, rotation is a matrix and not a vector. The three Euler angles, which are the three independent parameters of the Euler-angle parametrization of the rotation matrix, are stored in a [Vector3] data structure not because the rotation is a vector, but only because [Vector3] exists as a convenient data-structure to store 3 floating-point numbers. Therefore, applying affine operations on the rotation "vector" is not meaningful. +		</member>  		<member name="global_transform" type="Transform3D" setter="set_global_transform" getter="get_global_transform">  			World3D space (global) [Transform3D] of this node.  		</member> diff --git a/doc/classes/NodePath.xml b/doc/classes/NodePath.xml index 00c5dcaa3d..d9e0680a38 100644 --- a/doc/classes/NodePath.xml +++ b/doc/classes/NodePath.xml @@ -87,6 +87,12 @@  				[/codeblocks]  			</description>  		</method> +		<method name="get_concatenated_names" qualifiers="const"> +			<return type="StringName" /> +			<description> +				Returns all paths concatenated with a slash character ([code]/[/code]) as separator without subnames. +			</description> +		</method>  		<method name="get_concatenated_subnames" qualifiers="const">  			<return type="StringName" />  			<description> diff --git a/doc/classes/SkeletonProfile.xml b/doc/classes/SkeletonProfile.xml new file mode 100644 index 0000000000..55a2ea6759 --- /dev/null +++ b/doc/classes/SkeletonProfile.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="SkeletonProfile" inherits="Resource" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +	<brief_description> +		Profile of a virtual skeleton used as a target for retargeting. +	</brief_description> +	<description> +		This resource is used in [EditorScenePostImport]. Some parameters are referring to bones in [Skeleton3D], [Skin], [Animation], and some other nodes are rewritten based on the parameters of [SkeletonProfile]. +	</description> +	<tutorials> +	</tutorials> +	<methods> +		<method name="get_bone_name" qualifiers="const"> +			<return type="StringName" /> +			<argument index="0" name="bone_idx" type="int" /> +			<description> +				Returns the name of the bone at [code]bone_idx[/code] that will be the key name in the [BoneMap]. +				In the retargeting process, the returned bone name is the bone name of the target skeleton. +			</description> +		</method> +		<method name="get_group" qualifiers="const"> +			<return type="StringName" /> +			<argument index="0" name="bone_idx" type="int" /> +			<description> +				Returns the group of the bone at [code]bone_idx[/code]. +			</description> +		</method> +		<method name="get_group_name" qualifiers="const"> +			<return type="StringName" /> +			<argument index="0" name="group_idx" type="int" /> +			<description> +				Returns the name of the group at [code]group_idx[/code] that will be the drawing group in the [BoneMap] editor. +			</description> +		</method> +		<method name="get_handle_offset" qualifiers="const"> +			<return type="Vector2" /> +			<argument index="0" name="bone_idx" type="int" /> +			<description> +				Returns the offset of the bone at [code]bone_idx[/code] that will be the button position in the [BoneMap] editor. +				This is the offset with origin at the top left corner of the square. +			</description> +		</method> +		<method name="get_texture" qualifiers="const"> +			<return type="Texture2D" /> +			<argument index="0" name="group_idx" type="int" /> +			<description> +				Returns the texture of the group at [code]group_idx[/code] that will be the drawing group background image in the [BoneMap] editor. +			</description> +		</method> +		<method name="set_bone_name"> +			<return type="void" /> +			<argument index="0" name="bone_idx" type="int" /> +			<argument index="1" name="bone_name" type="StringName" /> +			<description> +				Sets the name of the bone at [code]bone_idx[/code] that will be the key name in the [BoneMap]. +				In the retargeting process, the setting bone name is the bone name of the target skeleton. +			</description> +		</method> +		<method name="set_group"> +			<return type="void" /> +			<argument index="0" name="bone_idx" type="int" /> +			<argument index="1" name="group" type="StringName" /> +			<description> +				Sets the group of the bone at [code]bone_idx[/code]. +			</description> +		</method> +		<method name="set_group_name"> +			<return type="void" /> +			<argument index="0" name="group_idx" type="int" /> +			<argument index="1" name="group_name" type="StringName" /> +			<description> +				Sets the name of the group at [code]group_idx[/code] that will be the drawing group in the [BoneMap] editor. +			</description> +		</method> +		<method name="set_handle_offset"> +			<return type="void" /> +			<argument index="0" name="bone_idx" type="int" /> +			<argument index="1" name="handle_offset" type="Vector2" /> +			<description> +				Sets the offset of the bone at [code]bone_idx[/code] that will be the button position in the [BoneMap] editor. +				This is the offset with origin at the top left corner of the square. +			</description> +		</method> +		<method name="set_texture"> +			<return type="void" /> +			<argument index="0" name="group_idx" type="int" /> +			<argument index="1" name="texture" type="Texture2D" /> +			<description> +				Sets the texture of the group at [code]group_idx[/code] that will be the drawing group background image in the [BoneMap] editor. +			</description> +		</method> +	</methods> +	<members> +		<member name="bone_size" type="int" setter="set_bone_size" getter="get_bone_size" default="0"> +		</member> +		<member name="group_size" type="int" setter="set_group_size" getter="get_group_size" default="0"> +		</member> +	</members> +	<signals> +		<signal name="profile_updated"> +			<description> +				This signal is emitted when change the value in profile. This is used to update key name in the [BoneMap] and to redraw the [BoneMap] editor. +				[b]Note[/b]: This signal is not connected directly to editor to simplify the reference, instead it is passed on to editor through the [BoneMap]. +			</description> +		</signal> +	</signals> +</class> diff --git a/doc/classes/SkeletonProfileHumanoid.xml b/doc/classes/SkeletonProfileHumanoid.xml new file mode 100644 index 0000000000..065184244e --- /dev/null +++ b/doc/classes/SkeletonProfileHumanoid.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="SkeletonProfileHumanoid" inherits="SkeletonProfile" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> +	<brief_description> +	</brief_description> +	<description> +		A [SkeletonProfile] as a preset that is optimized for the human form. This exists for standardization, so all parameters are read-only. +	</description> +	<tutorials> +	</tutorials> +	<members> +		<member name="bone_size" type="int" setter="set_bone_size" getter="get_bone_size" overrides="SkeletonProfile" default="56" /> +		<member name="group_size" type="int" setter="set_group_size" getter="get_group_size" overrides="SkeletonProfile" default="4" /> +	</members> +</class> diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index e3caaf93c6..7697bbfdf4 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -134,6 +134,7 @@  #include "editor/plugins/audio_stream_editor_plugin.h"  #include "editor/plugins/audio_stream_randomizer_editor_plugin.h"  #include "editor/plugins/bit_map_editor_plugin.h" +#include "editor/plugins/bone_map_editor_plugin.h"  #include "editor/plugins/camera_3d_editor_plugin.h"  #include "editor/plugins/canvas_item_editor_plugin.h"  #include "editor/plugins/collision_polygon_2d_editor_plugin.h" @@ -7144,6 +7145,7 @@ EditorNode::EditorNode() {  	add_editor_plugin(memnew(GradientTexture2DEditorPlugin));  	add_editor_plugin(memnew(BitMapEditorPlugin));  	add_editor_plugin(memnew(RayCast2DEditorPlugin)); +	add_editor_plugin(memnew(BoneMapEditorPlugin));  	for (int i = 0; i < EditorPlugins::get_plugin_count(); i++) {  		add_editor_plugin(EditorPlugins::create(i)); diff --git a/editor/icons/BoneMapHumanBody.svg b/editor/icons/BoneMapHumanBody.svg new file mode 100644 index 0000000000..2c2c5db1f6 --- /dev/null +++ b/editor/icons/BoneMapHumanBody.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m926.5 217.162c-11.5-2-26.03 4.547-37.5 6.5-15.723 2.678-25.238 3.24-33.333 5.167-1.227.292-3.103.763-5.792.958 0 0-.019.16-.052.437-36.819.994-106.823-6.062-138.156-2.062-23.816 3.041-86.334-5.667-105.667-6-13.911-.239-59.292-4.583-71.75-2.5-.667-4.083-1.5-10.75.95-17.468 14.881-7.246 27.229-21.569 35.341-38.467.922 4.424 6.252 4.929 12.459-14.231 5.662-17.478 2.324-22.254-2.313-22.525.172-2.056.279-4.105.313-6.142.788-48.041-15-78.667-69-78.667s-69.787 30.626-69 78.667c.033 2.036.141 4.086.313 6.142-4.637.271-7.975 5.048-2.313 22.525 6.207 19.16 11.537 18.655 12.459 14.231 8.113 16.897 20.461 31.221 35.342 38.467 2.449 6.718 1.617 13.385.949 17.468-12.457-2.083-57.838 2.261-71.75 2.5-19.332.333-81.85 9.041-105.666 6-31.333-4-101.337 3.056-138.156 2.062-.033-.276-.053-.437-.053-.437-2.689-.195-4.564-.666-5.791-.958-8.096-1.927-17.611-2.489-33.334-5.167-11.469-1.953-26-8.5-37.5-6.5-3.367.586 6 9.834 15.5 12.334 13.635 3.588 25.25 10.666 36 13.166-2.25 3.75-15.59 7.063-23 12-5.336 3.557 6.5 6.5 12 5 20.842-5.684 22.973.389 37.514-9.019 30.078 4.078 102.537 20.514 122.154 14.186 12.457-4.018 100.332 7.083 142.332 5.833 6.039-.18 1.656 65.563 2 73.5 3 69-16.842 133.135-18.666 169.667-1.92 38.42-3.42 57.919 7.666 131.333 6.967 46.126-2.521 82.079-2 94 6 137 29 172 4 221-14 27.44 67.449 26.958 65 9-3.012-22.092-12.666-22.333-10.666-46.333 1.896-22.768 16.049-151.298 8.666-206.667-2-15 0-26 2-66 2.355-47.101 7-88 14-123 7 35 11.645 75.899 14 123 2 40 4 51 2 66-7.383 55.369 6.77 183.899 8.667 206.667 2 24-7.654 24.241-10.667 46.333-2.449 17.958 79 18.44 65-9-25-49-2-84 4-221 .522-11.921-8.966-47.874-2-94 11.086-73.414 9.586-92.913 7.667-131.333-1.824-36.532-21.667-100.667-18.667-169.667.345-7.938-4.039-73.68 2-73.5 42 1.25 129.876-9.852 142.333-5.833 19.616 6.328 92.076-10.107 122.153-14.186 14.541 9.407 16.673 3.335 37.514 9.019 5.5 1.5 17.336-1.443 12-5-7.409-4.937-20.75-8.25-23-12 10.75-2.5 22.366-9.578 36.001-13.166 9.5-2.5 18.866-11.748 15.499-12.334z" fill="#b2b2b2"/></svg> diff --git a/editor/icons/BoneMapHumanFace.svg b/editor/icons/BoneMapHumanFace.svg new file mode 100644 index 0000000000..6cb21140bc --- /dev/null +++ b/editor/icons/BoneMapHumanFace.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m788.105 552.967c17.995-57.892 31.896-124.566 30.875-198.071-3.758-270.403-249.846-251.479-295.568-244.947-359.868 51.409-219.047 452.358-220.453 496.426-4.899 153.499 83.686 170.991 161.665 215.554 2.646 1.512 7.259 1.786 13.313 1.111 7.223 25.179 11.762 59.035 9.548 75.638-3.266 24.495 209.021 24.495 209.021 0 0-62.883 12.233-124.363 33.827-188.89 7.143-2.284 16.054-7.601 25.963-16.95 13.681-12.908 34.839-21.774 45.726-63.145 15.615-59.338 3.869-76.074-13.917-76.726z" fill="#b2b2b2"/></svg> diff --git a/editor/icons/BoneMapHumanLeftHand.svg b/editor/icons/BoneMapHumanLeftHand.svg new file mode 100644 index 0000000000..08c68bb4be --- /dev/null +++ b/editor/icons/BoneMapHumanLeftHand.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m703.906 786.098c7.046-66.929 28.135-153.363 18.529-260.192-1.143-12.71-4.5-48.282-4.46-82.732.025-21.174-2.111-48.505-1.975-64.174.167-19.333-.428-41.584-.625-55.755-1.052-75.44-13.225-85.827-30.813-85.827-17.246 0-26.77 14.266-27.062 84.582-.061 14.42.5 51 .5 58.5 0 17.508-.333 34.167 0 53.5.447 25.955-4.279 68-9 68-3.902 0-8.099-39.299-9.575-76.999-.756-19.326-3.219-58.336-2.6-70.102 1.759-33.413.474-58.914 1.537-90.165 3.183-93.607-13.016-111.729-34.695-111.729-21.973 0-35.979 57.688-34.849 114.224.128 6.394-1.165 50.739.188 89.859.754 21.811-1.07 49.627-1.683 69.67-1.095 35.768-5.755 63.896-8.869 63.896-2.641 0-4.135-32.584-5.456-65.706-.859-21.557-4.468-58.477-3.664-83.616 1.886-59.012-1.139-110.226-1.063-121.501.635-94.955-14.66-123.101-36.052-123.101-21.476 0-37.188 30.192-36.6 123.343.067 10.53-2.62 99.926-1.759 121.816.865 21.992-2.773 65.062-3.517 84.818-1.299 34.521-6.49 63.947-9.124 63.947-3.281 0-10.794-25.638-11.724-60.965-.587-22.275 1.231-50.99.624-70.688-1.257-40.707-3.175-64.631-3.877-99.708-1.945-97.182-16.352-106.289-38.142-106.289-17.957 0-32.453 28.673-32.657 115.03-.065 27.702-2.429 62.626-.315 94.329.805 12.081-.622 42.512-1.875 73.894-.799 20.007-1.102 47.501-1.137 63.775-.17 78.595-26.712 133.424-36.555 131.308-30.333-6.521-51.648-43.918-71.219-117.307-10.551-39.566-36.667-71.149-69.9-77.813-25.9-5.193-19.783 46.161-1.319 125.293 8.65 37.068 27.909 86.227 39.566 122.655 31.653 98.917 125.574 188.563 160.903 228.546 17.146 19.403 236.894 19.403 264.59 0 11.525-8.07 43.087-101.557 45.724-126.616z" fill="#b2b2b2"/></svg> diff --git a/editor/icons/BoneMapHumanRightHand.svg b/editor/icons/BoneMapHumanRightHand.svg new file mode 100644 index 0000000000..4e40af35d8 --- /dev/null +++ b/editor/icons/BoneMapHumanRightHand.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m320.094 786.098c-7.046-66.929-28.135-153.363-18.529-260.192 1.143-12.71 4.5-48.282 4.46-82.732-.025-21.174 2.111-48.505 1.975-64.174-.167-19.333.428-41.584.625-55.755 1.052-75.44 13.225-85.827 30.813-85.827 17.246 0 26.77 14.266 27.062 84.582.061 14.42-.5 51-.5 58.5 0 17.508.333 34.167 0 53.5-.447 25.955 4.279 68 9 68 3.902 0 8.099-39.299 9.575-76.999.756-19.326 3.219-58.336 2.6-70.102-1.759-33.413-.474-58.914-1.537-90.165-3.183-93.607 13.016-111.729 34.695-111.729 21.973 0 35.979 57.688 34.849 114.224-.128 6.394 1.165 50.739-.188 89.859-.754 21.811 1.07 49.627 1.683 69.67 1.095 35.768 5.755 63.896 8.869 63.896 2.641 0 4.135-32.584 5.456-65.706.859-21.557 4.468-58.477 3.664-83.616-1.886-59.012 1.139-110.226 1.063-121.501-.635-94.955 14.66-123.101 36.052-123.101 21.476 0 37.188 30.192 36.6 123.343-.067 10.53 2.62 99.926 1.759 121.816-.865 21.992 2.773 65.062 3.517 84.818 1.299 34.521 6.49 63.947 9.124 63.947 3.281 0 10.794-25.638 11.724-60.965.587-22.275-1.231-50.99-.624-70.688 1.257-40.707 3.176-64.631 3.877-99.708 1.945-97.182 16.352-106.289 38.142-106.289 17.957 0 32.453 28.673 32.657 115.03.065 27.702 2.429 62.626.314 94.329-.805 12.081.622 42.512 1.875 73.894.799 20.007 1.102 47.501 1.137 63.775.171 78.595 26.713 133.424 36.556 131.308 30.333-6.521 51.648-43.918 71.219-117.307 10.551-39.566 36.667-71.149 69.9-77.813 25.9-5.193 19.783 46.161 1.318 125.293-8.649 37.068-27.909 86.227-39.566 122.655-31.652 98.917-125.573 188.563-160.902 228.546-17.146 19.403-236.894 19.403-264.59 0-11.525-8.07-43.087-101.557-45.724-126.616z" fill="#b2b2b2"/></svg> diff --git a/editor/icons/BoneMapperHandle.svg b/editor/icons/BoneMapperHandle.svg new file mode 100644 index 0000000000..8c7d7e1d70 --- /dev/null +++ b/editor/icons/BoneMapperHandle.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 12 12" height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="6" fill-opacity=".2941" r="5"/><circle cx="6" cy="6" fill="#fff" r="4"/></svg> diff --git a/editor/icons/BoneMapperHandleCircle.svg b/editor/icons/BoneMapperHandleCircle.svg new file mode 100644 index 0000000000..ecf97669b8 --- /dev/null +++ b/editor/icons/BoneMapperHandleCircle.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 12 12" height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="6" fill="#fff" r="3"/></svg> diff --git a/editor/icons/BoneMapperHandleSelected.svg b/editor/icons/BoneMapperHandleSelected.svg new file mode 100644 index 0000000000..729a443f6e --- /dev/null +++ b/editor/icons/BoneMapperHandleSelected.svg @@ -0,0 +1 @@ +<svg enable-background="new -506.5 517.5 12 12" height="12" viewBox="-506.5 517.5 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><circle cx="-500.5" cy="523.5" fill-opacity=".2941" r="5"/><g fill="#fff"><circle cx="-500.5" cy="523.5" r="4"/><path d="m-499.5 517.5h5v5h-1v-4h-4z"/><path d="m-494.5 524.5v5h-5v-1h4v-4z"/><path d="m-501.5 529.5h-5v-5h1v4h4z"/><path d="m-506.5 522.5v-5h5v1h-4v4z"/></g></svg> diff --git a/editor/import/post_import_plugin_skeleton_renamer.cpp b/editor/import/post_import_plugin_skeleton_renamer.cpp new file mode 100644 index 0000000000..b0c4bc8c30 --- /dev/null +++ b/editor/import/post_import_plugin_skeleton_renamer.cpp @@ -0,0 +1,144 @@ +/*************************************************************************/ +/*  post_import_plugin_skeleton_renamer.cpp                              */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#include "post_import_plugin_skeleton_renamer.h" + +#include "editor/import/scene_import_settings.h" +#include "scene/3d/importer_mesh_instance_3d.h" +#include "scene/3d/skeleton_3d.h" +#include "scene/animation/animation_player.h" +#include "scene/resources/bone_map.h" + +void PostImportPluginSkeletonRenamer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) { +	if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) { +		r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/bone_renamer/rename_bones"), true)); +	} +} + +void PostImportPluginSkeletonRenamer::internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) { +	if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) { +		// Prepare objects. +		Object *map = p_options["retarget/bone_map"].get_validated_object(); +		if (!map || !bool(p_options["retarget/bone_renamer/rename_bones"])) { +			return; +		} +		BoneMap *bone_map = Object::cast_to<BoneMap>(map); +		Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_node); + +		// Rename bones in Skeleton3D. +		{ +			int len = skeleton->get_bone_count(); +			for (int i = 0; i < len; i++) { +				StringName bn = bone_map->find_profile_bone_name(skeleton->get_bone_name(i)); +				if (bn) { +					skeleton->set_bone_name(i, bn); +				} +			} +		} + +		// Rename bones in Skin. +		{ +			TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D"); +			while (nodes.size()) { +				ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back()); +				Ref<Skin> skin = mi->get_skin(); +				if (skin.is_valid()) { +					Node *node = mi->get_node(mi->get_skeleton_path()); +					if (node) { +						Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node); +						if (mesh_skeleton && node == skeleton) { +							int len = skin->get_bind_count(); +							for (int i = 0; i < len; i++) { +								StringName bn = bone_map->find_profile_bone_name(skin->get_bind_name(i)); +								if (bn) { +									skin->set_bind_name(i, bn); +								} +							} +						} +					} +				} +			} +		} + +		// Rename bones in AnimationPlayer. +		{ +			TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer"); +			while (nodes.size()) { +				AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); +				List<StringName> anims; +				ap->get_animation_list(&anims); +				for (const StringName &name : anims) { +					Ref<Animation> anim = ap->get_animation(name); +					int len = anim->get_track_count(); +					for (int i = 0; i < len; i++) { +						if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) { +							continue; +						} +						String track_path = String(anim->track_get_path(i).get_concatenated_names()); +						Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); +						if (node) { +							Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); +							if (track_skeleton && track_skeleton == skeleton) { +								StringName bn = bone_map->find_profile_bone_name(anim->track_get_path(i).get_subname(0)); +								if (bn) { +									anim->track_set_path(i, track_path + ":" + bn); +								} +							} +						} +					} +				} +			} +		} + +		// Rename bones in all Nodes by calling method. +		{ +			Vector<Variant> vargs; +			vargs.push_back(p_base_scene); +			vargs.push_back(skeleton); +			vargs.push_back(bone_map); +			const Variant **argptrs = (const Variant **)alloca(sizeof(const Variant **) * vargs.size()); +			const Variant *args = vargs.ptr(); +			uint32_t argcount = vargs.size(); +			for (uint32_t i = 0; i < argcount; i++) { +				argptrs[i] = &args[i]; +			} + +			TypedArray<Node> nodes = p_base_scene->find_children("*"); +			while (nodes.size()) { +				Node *nd = Object::cast_to<Node>(nodes.pop_back()); +				Callable::CallError ce; +				nd->callp("_notify_skeleton_bones_renamed", argptrs, argcount, ce); +			} +		} +	} +} + +PostImportPluginSkeletonRenamer::PostImportPluginSkeletonRenamer() { +} diff --git a/editor/import/post_import_plugin_skeleton_renamer.h b/editor/import/post_import_plugin_skeleton_renamer.h new file mode 100644 index 0000000000..73cbabd1c5 --- /dev/null +++ b/editor/import/post_import_plugin_skeleton_renamer.h @@ -0,0 +1,46 @@ +/*************************************************************************/ +/*  post_import_plugin_skeleton_renamer.h                                */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#ifndef POST_IMPORT_PLUGIN_SKELETON_RENAMER_H +#define POST_IMPORT_PLUGIN_SKELETON_RENAMER_H + +#include "resource_importer_scene.h" + +class PostImportPluginSkeletonRenamer : public EditorScenePostImportPlugin { +	GDCLASS(PostImportPluginSkeletonRenamer, EditorScenePostImportPlugin); + +public: +	virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) override; +	virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) override; + +	PostImportPluginSkeletonRenamer(); +}; + +#endif // POST_IMPORT_PLUGIN_SKELETON_RENAMER_H diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 171ef5bf4c..a9c43e573f 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -232,6 +232,7 @@ void EditorScenePostImportPlugin::_bind_methods() {  	BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MATERIAL);  	BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION);  	BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE); +	BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE);  	BIND_ENUM_CONSTANT(INTERNAL_IMPORT_CATEGORY_MAX);  } @@ -766,6 +767,27 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<  	}  	{ +		//make sure this is unique +		node_settings = node_settings.duplicate(true); +		//fill node settings for this node with default values +		List<ImportOption> iopts; +		if (Object::cast_to<ImporterMeshInstance3D>(p_node)) { +			get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE, &iopts); +		} else if (Object::cast_to<AnimationPlayer>(p_node)) { +			get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts); +		} else if (Object::cast_to<Skeleton3D>(p_node)) { +			get_internal_import_options(INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, &iopts); +		} else { +			get_internal_import_options(INTERNAL_IMPORT_CATEGORY_NODE, &iopts); +		} +		for (const ImportOption &E : iopts) { +			if (!node_settings.has(E.option.name)) { +				node_settings[E.option.name] = E.default_value; +			} +		} +	} + +	{  		ObjectID node_id = p_node->get_instance_id();  		for (int i = 0; i < post_importer_plugins.size(); i++) {  			post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_NODE, p_root, p_node, Ref<Resource>(), node_settings); @@ -785,6 +807,16 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<  		}  	} +	if (Object::cast_to<Skeleton3D>(p_node)) { +		ObjectID node_id = p_node->get_instance_id(); +		for (int i = 0; i < post_importer_plugins.size(); i++) { +			post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE, p_root, p_node, Ref<Resource>(), node_settings); +			if (ObjectDB::get_instance(node_id) == nullptr) { //may have been erased, so do not continue +				break; +			} +		} +	} +  	if (Object::cast_to<ImporterMeshInstance3D>(p_node)) {  		ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(p_node); @@ -799,6 +831,16 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<  						if (!mat_id.is_empty() && p_material_data.has(mat_id)) {  							Dictionary matdata = p_material_data[mat_id]; +							{ +								//fill node settings for this node with default values +								List<ImportOption> iopts; +								get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MATERIAL, &iopts); +								for (const ImportOption &E : iopts) { +									if (!matdata.has(E.option.name)) { +										matdata[E.option.name] = E.default_value; +									} +								} +							}  							for (int j = 0; j < post_importer_plugins.size(); j++) {  								post_importer_plugins.write[j]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL, p_root, p_node, mat, matdata); @@ -966,19 +1008,6 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap<  	if (Object::cast_to<AnimationPlayer>(p_node)) {  		AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node); -		{ -			//make sure this is unique -			node_settings = node_settings.duplicate(true); -			//fill node settings for this node with default values -			List<ImportOption> iopts; -			get_internal_import_options(INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, &iopts); -			for (const ImportOption &E : iopts) { -				if (!node_settings.has(E.option.name)) { -					node_settings[E.option.name] = E.default_value; -				} -			} -		} -  		for (int i = 0; i < post_importer_plugins.size(); i++) {  			post_importer_plugins.write[i]->internal_process(EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, p_root, p_node, Ref<Resource>(), node_settings);  		} @@ -1385,6 +1414,10 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p  				r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "slice_" + itos(i + 1) + "/save_to_file/keep_custom_tracks"), false));  			}  		} break; +		case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: { +			r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); +			r_options->push_back(ImportOption(PropertyInfo(Variant::OBJECT, "retarget/bone_map", PROPERTY_HINT_RESOURCE_TYPE, "BoneMap", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), Variant())); +		} break;  		default: {  		}  	} @@ -1499,6 +1532,12 @@ bool ResourceImporterScene::get_internal_option_visibility(InternalImportCategor  				}  			}  		} break; +		case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: { +			const bool use_retarget = p_options["retarget/bone_map"].get_validated_object() != nullptr; +			if (p_option != "retarget/bone_map" && p_option.begins_with("retarget/")) { +				return use_retarget; +			} +		} break;  		default: {  		}  	} @@ -1534,6 +1573,8 @@ bool ResourceImporterScene::get_internal_option_update_view_required(InternalImp  		} break;  		case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: {  		} break; +		case INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE: { +		} break;  		default: {  		}  	} @@ -1622,6 +1663,16 @@ void ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_m  				if (!mesh_id.is_empty() && p_mesh_data.has(mesh_id)) {  					Dictionary mesh_settings = p_mesh_data[mesh_id]; +					{ +						//fill node settings for this node with default values +						List<ImportOption> iopts; +						get_internal_import_options(INTERNAL_IMPORT_CATEGORY_MESH, &iopts); +						for (const ImportOption &E : iopts) { +							if (!mesh_settings.has(E.option.name)) { +								mesh_settings[E.option.name] = E.default_value; +							} +						} +					}  					if (mesh_settings.has("generate/shadow_meshes")) {  						int shadow_meshes = mesh_settings["generate/shadow_meshes"]; diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index 16cf3d651d..c143e86bd4 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -106,6 +106,7 @@ public:  		INTERNAL_IMPORT_CATEGORY_MATERIAL,  		INTERNAL_IMPORT_CATEGORY_ANIMATION,  		INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, +		INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE,  		INTERNAL_IMPORT_CATEGORY_MAX  	}; @@ -259,6 +260,7 @@ public:  		INTERNAL_IMPORT_CATEGORY_MATERIAL = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MATERIAL,  		INTERNAL_IMPORT_CATEGORY_ANIMATION = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION,  		INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE, +		INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE,  		INTERNAL_IMPORT_CATEGORY_MAX = EditorScenePostImportPlugin::INTERNAL_IMPORT_CATEGORY_MAX  	}; diff --git a/editor/import/scene_import_settings.cpp b/editor/import/scene_import_settings.cpp index 99d1658405..af145c22b4 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/scene_import_settings.cpp @@ -339,6 +339,8 @@ void SceneImportSettings::_fill_scene(Node *p_node, TreeItem *p_parent_item) {  				category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_MESH_3D_NODE;  			} else if (Object::cast_to<AnimationPlayer>(p_node)) {  				category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE; +			} else if (Object::cast_to<Skeleton3D>(p_node)) { +				category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE;  			} else {  				category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;  			} @@ -617,6 +619,13 @@ SceneImportSettings *SceneImportSettings::get_singleton() {  	return singleton;  } +Node *SceneImportSettings::get_selected_node() { +	if (selected_id == "") { +		return nullptr; +	} +	return node_map[selected_id].node; +} +  void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {  	selecting = true;  	scene_import_settings_data->hide_options = false; @@ -657,6 +666,8 @@ void SceneImportSettings::_select(Tree *p_from, String p_type, String p_id) {  				scene_import_settings_data->hide_options = editing_animation;  			} else if (Object::cast_to<AnimationPlayer>(nd.node)) {  				scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE; +			} else if (Object::cast_to<Skeleton3D>(nd.node)) { +				scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE;  			} else {  				scene_import_settings_data->category = ResourceImporterScene::INTERNAL_IMPORT_CATEGORY_NODE;  				scene_import_settings_data->hide_options = editing_animation; diff --git a/editor/import/scene_import_settings.h b/editor/import/scene_import_settings.h index 81d13166ab..b5cf82f64b 100644 --- a/editor/import/scene_import_settings.h +++ b/editor/import/scene_import_settings.h @@ -201,6 +201,7 @@ public:  	void update_view();  	void open_settings(const String &p_path, bool p_for_animation = false);  	static SceneImportSettings *get_singleton(); +	Node *get_selected_node();  	SceneImportSettings();  	~SceneImportSettings();  }; diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp new file mode 100644 index 0000000000..fffadae3eb --- /dev/null +++ b/editor/plugins/bone_map_editor_plugin.cpp @@ -0,0 +1,439 @@ +/*************************************************************************/ +/*  bone_map_editor_plugin.cpp                                           */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#include "bone_map_editor_plugin.h" + +#include "editor/editor_scale.h" +#include "editor/import/post_import_plugin_skeleton_renamer.h" +#include "editor/import/scene_import_settings.h" + +void BoneMapperButton::fetch_textures() { +	if (selected) { +		set_normal_texture(get_theme_icon(SNAME("BoneMapperHandleSelected"), SNAME("EditorIcons"))); +	} else { +		set_normal_texture(get_theme_icon(SNAME("BoneMapperHandle"), SNAME("EditorIcons"))); +	} +	set_offset(SIDE_LEFT, 0); +	set_offset(SIDE_RIGHT, 0); +	set_offset(SIDE_TOP, 0); +	set_offset(SIDE_BOTTOM, 0); + +	circle = memnew(TextureRect); +	circle->set_texture(get_theme_icon(SNAME("BoneMapperHandleCircle"), SNAME("EditorIcons"))); +	add_child(circle); +	set_state(BONE_MAP_STATE_UNSET); +} + +StringName BoneMapperButton::get_profile_bone_name() const { +	return profile_bone_name; +} + +void BoneMapperButton::set_state(BoneMapState p_state) { +	switch (p_state) { +		case BONE_MAP_STATE_UNSET: { +			circle->set_modulate(EditorSettings::get_singleton()->get("editors/bone_mapper/handle_colors/unset")); +		} break; +		case BONE_MAP_STATE_SET: { +			circle->set_modulate(EditorSettings::get_singleton()->get("editors/bone_mapper/handle_colors/set")); +		} break; +		case BONE_MAP_STATE_ERROR: { +			circle->set_modulate(EditorSettings::get_singleton()->get("editors/bone_mapper/handle_colors/error")); +		} break; +		default: { +		} break; +	} +} + +void BoneMapperButton::_notification(int p_what) { +	switch (p_what) { +		case NOTIFICATION_ENTER_TREE: { +			fetch_textures(); +		} break; +	} +} + +BoneMapperButton::BoneMapperButton(const StringName p_profile_bone_name, bool p_selected) { +	profile_bone_name = p_profile_bone_name; +	selected = p_selected; +} + +BoneMapperButton::~BoneMapperButton() { +} + +void BoneMapperItem::create_editor() { +	skeleton_bone_selector = memnew(EditorPropertyTextEnum); +	skeleton_bone_selector->setup(skeleton_bone_names); +	skeleton_bone_selector->set_label(profile_bone_name); +	skeleton_bone_selector->set_selectable(false); +	skeleton_bone_selector->set_object_and_property(bone_map.ptr(), "bone_map/" + String(profile_bone_name)); +	skeleton_bone_selector->update_property(); +	skeleton_bone_selector->connect("property_changed", callable_mp(this, &BoneMapperItem::_value_changed)); +	add_child(skeleton_bone_selector); +} + +void BoneMapperItem::_update_property() { +	if (skeleton_bone_selector->get_edited_object() && skeleton_bone_selector->get_edited_property()) { +		skeleton_bone_selector->update_property(); +	} +} + +void BoneMapperItem::_value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { +	bone_map->set(p_property, p_value); +} + +void BoneMapperItem::_notification(int p_what) { +	switch (p_what) { +		case NOTIFICATION_ENTER_TREE: { +			create_editor(); +			bone_map->connect("bone_map_updated", callable_mp(this, &BoneMapperItem::_update_property)); +		} break; +		case NOTIFICATION_EXIT_TREE: { +			if (!bone_map.is_null() && bone_map->is_connected("bone_map_updated", callable_mp(this, &BoneMapperItem::_update_property))) { +				bone_map->disconnect("bone_map_updated", callable_mp(this, &BoneMapperItem::_update_property)); +			} +		} break; +	} +} + +void BoneMapperItem::_bind_methods() { +} + +BoneMapperItem::BoneMapperItem(Ref<BoneMap> &p_bone_map, PackedStringArray p_skeleton_bone_names, const StringName &p_profile_bone_name) { +	bone_map = p_bone_map; +	skeleton_bone_names = p_skeleton_bone_names; +	profile_bone_name = p_profile_bone_name; +} + +BoneMapperItem::~BoneMapperItem() { +} + +void BoneMapper::create_editor() { +	profile_group_selector = memnew(EditorPropertyEnum); +	profile_group_selector->set_label("Group"); +	profile_group_selector->set_selectable(false); +	profile_group_selector->set_object_and_property(this, "current_group_idx"); +	profile_group_selector->update_property(); +	profile_group_selector->connect("property_changed", callable_mp(this, &BoneMapper::_value_changed)); +	add_child(profile_group_selector); + +	bone_mapper_field = memnew(AspectRatioContainer); +	bone_mapper_field->set_stretch_mode(AspectRatioContainer::STRETCH_FIT); +	bone_mapper_field->set_custom_minimum_size(Vector2(0, 256.0) * EDSCALE); +	bone_mapper_field->set_h_size_flags(Control::SIZE_FILL); +	add_child(bone_mapper_field); + +	profile_bg = memnew(ColorRect); +	profile_bg->set_color(Color(0, 0, 0, 1)); +	profile_bg->set_h_size_flags(Control::SIZE_FILL); +	profile_bg->set_v_size_flags(Control::SIZE_FILL); +	bone_mapper_field->add_child(profile_bg); + +	profile_texture = memnew(TextureRect); +	profile_texture->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); +	profile_texture->set_ignore_texture_size(true); +	profile_texture->set_h_size_flags(Control::SIZE_FILL); +	profile_texture->set_v_size_flags(Control::SIZE_FILL); +	bone_mapper_field->add_child(profile_texture); + +	mapper_item_vbox = memnew(VBoxContainer); +	add_child(mapper_item_vbox); + +	separator = memnew(HSeparator); +	add_child(separator); + +	recreate_items(); +} + +void BoneMapper::update_group_idx() { +	if (!bone_map->get_profile().is_valid()) { +		return; +	} + +	PackedStringArray group_names; +	int len = bone_map->get_profile()->get_group_size(); +	for (int i = 0; i < len; i++) { +		group_names.push_back(bone_map->get_profile()->get_group_name(i)); +	} +	if (current_group_idx >= len) { +		current_group_idx = 0; +	} +	if (len > 0) { +		profile_group_selector->setup(group_names); +		profile_group_selector->update_property(); +		profile_group_selector->set_read_only(false); +	} +} + +void BoneMapper::set_current_group_idx(int p_group_idx) { +	current_group_idx = p_group_idx; +	recreate_editor(); +} + +int BoneMapper::get_current_group_idx() const { +	return current_group_idx; +} + +void BoneMapper::set_current_bone_idx(int p_bone_idx) { +	current_bone_idx = p_bone_idx; +	recreate_editor(); +} + +int BoneMapper::get_current_bone_idx() const { +	return current_bone_idx; +} + +void BoneMapper::recreate_editor() { +	// Clear buttons. +	int len = bone_mapper_buttons.size(); +	for (int i = 0; i < len; i++) { +		profile_texture->remove_child(bone_mapper_buttons[i]); +		memdelete(bone_mapper_buttons[i]); +	} +	bone_mapper_buttons.clear(); + +	// Organize mapper items. +	len = bone_mapper_items.size(); +	for (int i = 0; i < len; i++) { +		bone_mapper_items[i]->set_visible(current_bone_idx == i); +	} + +	Ref<SkeletonProfile> profile = bone_map->get_profile(); +	if (profile.is_valid()) { +		SkeletonProfileHumanoid *hmn = Object::cast_to<SkeletonProfileHumanoid>(profile.ptr()); +		if (hmn) { +			StringName hmn_group_name = profile->get_group_name(current_group_idx); +			if (hmn_group_name == "Body") { +				profile_texture->set_texture(get_theme_icon(SNAME("BoneMapHumanBody"), SNAME("EditorIcons"))); +			} else if (hmn_group_name == "Face") { +				profile_texture->set_texture(get_theme_icon(SNAME("BoneMapHumanFace"), SNAME("EditorIcons"))); +			} else if (hmn_group_name == "LeftHand") { +				profile_texture->set_texture(get_theme_icon(SNAME("BoneMapHumanLeftHand"), SNAME("EditorIcons"))); +			} else if (hmn_group_name == "RightHand") { +				profile_texture->set_texture(get_theme_icon(SNAME("BoneMapHumanRightHand"), SNAME("EditorIcons"))); +			} +		} else { +			profile_texture->set_texture(profile->get_texture(current_group_idx)); +		} +	} else { +		profile_texture->set_texture(Ref<Texture2D>()); +	} + +	if (!profile.is_valid()) { +		return; +	} + +	for (int i = 0; i < len; i++) { +		if (profile->get_group(i) == profile->get_group_name(current_group_idx)) { +			BoneMapperButton *mb = memnew(BoneMapperButton(profile->get_bone_name(i), current_bone_idx == i)); +			mb->connect("pressed", callable_mp(this, &BoneMapper::set_current_bone_idx), varray(i), CONNECT_DEFERRED); +			mb->set_h_grow_direction(GROW_DIRECTION_BOTH); +			mb->set_v_grow_direction(GROW_DIRECTION_BOTH); +			Vector2 vc = profile->get_handle_offset(i); +			bone_mapper_buttons.push_back(mb); +			profile_texture->add_child(mb); +			mb->set_anchor(SIDE_LEFT, vc.x); +			mb->set_anchor(SIDE_RIGHT, vc.x); +			mb->set_anchor(SIDE_TOP, vc.y); +			mb->set_anchor(SIDE_BOTTOM, vc.y); +		} +	} + +	_update_state(); +} + +void BoneMapper::clear_items() { +	// Clear items. +	int len = bone_mapper_items.size(); +	for (int i = 0; i < len; i++) { +		mapper_item_vbox->remove_child(bone_mapper_items[i]); +		memdelete(bone_mapper_items[i]); +	} +	bone_mapper_items.clear(); +} + +void BoneMapper::recreate_items() { +	clear_items(); +	// Create items by profile. +	Ref<SkeletonProfile> profile = bone_map->get_profile(); +	if (profile.is_valid()) { +		PackedStringArray skeleton_bone_names; +		skeleton_bone_names.push_back(String()); + +		int len = skeleton->get_bone_count(); +		for (int i = 0; i < len; i++) { +			skeleton_bone_names.push_back(skeleton->get_bone_name(i)); +		} + +		len = profile->get_bone_size(); +		for (int i = 0; i < len; i++) { +			StringName bn = profile->get_bone_name(i); +			bone_mapper_items.append(memnew(BoneMapperItem(bone_map, skeleton_bone_names, bn))); +			mapper_item_vbox->add_child(bone_mapper_items[i]); +		} +	} + +	update_group_idx(); +	recreate_editor(); +} + +void BoneMapper::_update_state() { +	int len = bone_mapper_buttons.size(); +	for (int i = 0; i < len; i++) { +		StringName sbn = bone_map->get_skeleton_bone_name(bone_mapper_buttons[i]->get_profile_bone_name()); +		if (skeleton->find_bone(sbn) >= 0) { +			if (bone_map->get_skeleton_bone_name_count(sbn) == 1) { +				bone_mapper_buttons[i]->set_state(BoneMapperButton::BONE_MAP_STATE_SET); +			} else { +				bone_mapper_buttons[i]->set_state(BoneMapperButton::BONE_MAP_STATE_ERROR); +			} +		} else { +			bone_mapper_buttons[i]->set_state(BoneMapperButton::BONE_MAP_STATE_UNSET); +		} +	} +} + +void BoneMapper::_value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { +	set(p_property, p_value); +	recreate_editor(); +} + +void BoneMapper::_bind_methods() { +	ClassDB::bind_method(D_METHOD("set_current_group_idx", "current_group_idx"), &BoneMapper::set_current_group_idx); +	ClassDB::bind_method(D_METHOD("get_current_group_idx"), &BoneMapper::get_current_group_idx); +	ClassDB::bind_method(D_METHOD("set_current_bone_idx", "current_bone_idx"), &BoneMapper::set_current_bone_idx); +	ClassDB::bind_method(D_METHOD("get_current_bone_idx"), &BoneMapper::get_current_bone_idx); +	ADD_PROPERTY(PropertyInfo(Variant::INT, "current_group_idx"), "set_current_group_idx", "get_current_group_idx"); +	ADD_PROPERTY(PropertyInfo(Variant::INT, "current_bone_idx"), "set_current_bone_idx", "get_current_bone_idx"); +} + +void BoneMapper::_notification(int p_what) { +	switch (p_what) { +		case NOTIFICATION_ENTER_TREE: { +			create_editor(); +			bone_map->connect("bone_map_updated", callable_mp(this, &BoneMapper::_update_state)); +			bone_map->connect("profile_updated", callable_mp(this, &BoneMapper::recreate_items)); +		} break; +		case NOTIFICATION_EXIT_TREE: { +			clear_items(); +			if (!bone_map.is_null()) { +				if (bone_map->is_connected("bone_map_updated", callable_mp(this, &BoneMapper::_update_state))) { +					bone_map->disconnect("bone_map_updated", callable_mp(this, &BoneMapper::_update_state)); +				} +				if (bone_map->is_connected("profile_updated", callable_mp(this, &BoneMapper::recreate_items))) { +					bone_map->disconnect("profile_updated", callable_mp(this, &BoneMapper::recreate_items)); +				} +			} +		} +	} +} + +BoneMapper::BoneMapper(Skeleton3D *p_skeleton, Ref<BoneMap> &p_bone_map) { +	skeleton = p_skeleton; +	bone_map = p_bone_map; +} + +BoneMapper::~BoneMapper() { +} + +void BoneMapEditor::create_editors() { +	if (!skeleton) { +		return; +	} +	bone_mapper = memnew(BoneMapper(skeleton, bone_map)); +	add_child(bone_mapper); +} + +void BoneMapEditor::fetch_objects() { +	// Hackey... but it may be the easist way to get a selected object from "ImporterScene". +	SceneImportSettings *si = SceneImportSettings::get_singleton(); +	if (!si) { +		return; +	} +	Node *selected = si->get_selected_node(); +	if (selected) { +		Skeleton3D *sk = Object::cast_to<Skeleton3D>(selected); +		if (!sk) { +			return; +		} +		skeleton = sk; +	} else { +		// Editor should not exist. +		skeleton = nullptr; +	} +} + +void BoneMapEditor::_notification(int p_what) { +	switch (p_what) { +		case NOTIFICATION_ENTER_TREE: { +			fetch_objects(); +			create_editors(); +		} break; +		case NOTIFICATION_EXIT_TREE: { +			remove_child(bone_mapper); +			bone_mapper->queue_delete(); +		} +	} +} + +BoneMapEditor::BoneMapEditor(Ref<BoneMap> &p_bone_map) { +	bone_map = p_bone_map; +} + +BoneMapEditor::~BoneMapEditor() { +} + +bool EditorInspectorPluginBoneMap::can_handle(Object *p_object) { +	return Object::cast_to<BoneMap>(p_object) != nullptr; +} + +void EditorInspectorPluginBoneMap::parse_begin(Object *p_object) { +	BoneMap *bm = Object::cast_to<BoneMap>(p_object); +	if (!bm) { +		return; +	} +	Ref<BoneMap> r(bm); +	editor = memnew(BoneMapEditor(r)); +	add_custom_control(editor); +} + +BoneMapEditorPlugin::BoneMapEditorPlugin() { +	// Register properties in editor settings. +	EDITOR_DEF("editors/bone_mapper/handle_colors/set", Color(0.1, 0.6, 0.25)); +	EDITOR_DEF("editors/bone_mapper/handle_colors/error", Color(0.8, 0.2, 0.2)); +	EDITOR_DEF("editors/bone_mapper/handle_colors/unset", Color(0.3, 0.3, 0.3)); + +	Ref<EditorInspectorPluginBoneMap> inspector_plugin; +	inspector_plugin.instantiate(); +	add_inspector_plugin(inspector_plugin); + +	Ref<PostImportPluginSkeletonRenamer> post_import_plugin_renamer; +	post_import_plugin_renamer.instantiate(); +	add_scene_post_import_plugin(post_import_plugin_renamer); +} diff --git a/editor/plugins/bone_map_editor_plugin.h b/editor/plugins/bone_map_editor_plugin.h new file mode 100644 index 0000000000..0ec9f74373 --- /dev/null +++ b/editor/plugins/bone_map_editor_plugin.h @@ -0,0 +1,176 @@ +/*************************************************************************/ +/*  bone_map_editor_plugin.h                                             */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#ifndef BONE_MAP_EDITOR_H +#define BONE_MAP_EDITOR_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "editor/editor_properties.h" +#include "scene/3d/skeleton_3d.h" +#include "scene/gui/color_rect.h" +#include "scene/gui/dialogs.h" +#include "scene/resources/bone_map.h" +#include "scene/resources/texture.h" + +class BoneMapperButton : public TextureButton { +	GDCLASS(BoneMapperButton, TextureButton); + +public: +	enum BoneMapState { +		BONE_MAP_STATE_UNSET, +		BONE_MAP_STATE_SET, +		BONE_MAP_STATE_ERROR +	}; + +private: +	StringName profile_bone_name; +	bool selected = false; + +	TextureRect *circle; + +	void fetch_textures(); + +protected: +	void _notification(int p_what); + +public: +	StringName get_profile_bone_name() const; +	void set_state(BoneMapState p_state); + +	BoneMapperButton(const StringName p_profile_bone_name, bool p_selected); +	~BoneMapperButton(); +}; + +class BoneMapperItem : public VBoxContainer { +	GDCLASS(BoneMapperItem, VBoxContainer); + +	int button_id = -1; +	StringName profile_bone_name; + +	PackedStringArray skeleton_bone_names; +	Ref<BoneMap> bone_map; + +	EditorPropertyTextEnum *skeleton_bone_selector; + +	void _update_property(); + +protected: +	void _notification(int p_what); +	static void _bind_methods(); +	virtual void _value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing); +	virtual void create_editor(); + +public: +	void assign_button_id(int p_button_id); + +	BoneMapperItem(Ref<BoneMap> &p_bone_map, PackedStringArray p_skeleton_bone_names, const StringName &p_profile_bone_name = StringName()); +	~BoneMapperItem(); +}; + +class BoneMapper : public VBoxContainer { +	GDCLASS(BoneMapper, VBoxContainer); + +	Skeleton3D *skeleton; +	Ref<BoneMap> bone_map; + +	Vector<BoneMapperItem *> bone_mapper_items; + +	VBoxContainer *mapper_item_vbox; +	HSeparator *separator; + +	int current_group_idx = 0; +	int current_bone_idx = -1; + +	AspectRatioContainer *bone_mapper_field; +	EditorPropertyEnum *profile_group_selector; +	ColorRect *profile_bg; +	TextureRect *profile_texture; +	Vector<BoneMapperButton *> bone_mapper_buttons; + +	void create_editor(); +	void recreate_editor(); +	void clear_items(); +	void recreate_items(); +	void update_group_idx(); +	void _update_state(); + +protected: +	void _notification(int p_what); +	static void _bind_methods(); +	virtual void _value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing); + +public: +	void set_current_group_idx(int p_group_idx); +	int get_current_group_idx() const; +	void set_current_bone_idx(int p_bone_idx); +	int get_current_bone_idx() const; + +	BoneMapper(Skeleton3D *p_skeleton, Ref<BoneMap> &p_bone_map); +	~BoneMapper(); +}; + +class BoneMapEditor : public VBoxContainer { +	GDCLASS(BoneMapEditor, VBoxContainer); + +	Skeleton3D *skeleton; +	Ref<BoneMap> bone_map; +	BoneMapper *bone_mapper; + +	void fetch_objects(); +	void clear_editors(); +	void create_editors(); + +protected: +	void _notification(int p_what); + +public: +	BoneMapEditor(Ref<BoneMap> &p_bone_map); +	~BoneMapEditor(); +}; + +class EditorInspectorPluginBoneMap : public EditorInspectorPlugin { +	GDCLASS(EditorInspectorPluginBoneMap, EditorInspectorPlugin); +	BoneMapEditor *editor; + +public: +	virtual bool can_handle(Object *p_object) override; +	virtual void parse_begin(Object *p_object) override; +}; + +class BoneMapEditorPlugin : public EditorPlugin { +	GDCLASS(BoneMapEditorPlugin, EditorPlugin); + +public: +	virtual String get_name() const override { return "BoneMap"; } +	BoneMapEditorPlugin(); +}; + +#endif // BONE_MAP_EDITOR_H diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs index 40fb5f8788..9ae01016cb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NodePath.cs @@ -170,6 +170,21 @@ namespace Godot          }          /// <summary> +        /// Returns all names concatenated with a slash character (<c>/</c>). +        /// </summary> +        /// <example> +        /// <code> +        /// var nodepath = new NodePath("Path2D/PathFollow2D/Sprite2D:texture:load_path"); +        /// GD.Print(nodepath.GetConcatenatedNames()); // Path2D/PathFollow2D/Sprite2D +        /// </code> +        /// </example> +        /// <returns>The names concatenated with <c>/</c>.</returns> +        public string GetConcatenatedNames() +        { +            return godot_icall_NodePath_get_concatenated_names(GetPtr(this)); +        } + +        /// <summary>          /// Returns all subnames concatenated with a colon character (<c>:</c>)          /// as separator, i.e. the right side of the first colon in a node path.          /// </summary> @@ -269,6 +284,9 @@ namespace Godot          private static extern IntPtr godot_icall_NodePath_get_as_property_path(IntPtr ptr);          [MethodImpl(MethodImplOptions.InternalCall)] +        private static extern string godot_icall_NodePath_get_concatenated_names(IntPtr ptr); + +        [MethodImpl(MethodImplOptions.InternalCall)]          private static extern string godot_icall_NodePath_get_concatenated_subnames(IntPtr ptr);          [MethodImpl(MethodImplOptions.InternalCall)] diff --git a/modules/mono/glue/nodepath_glue.cpp b/modules/mono/glue/nodepath_glue.cpp index 0ea9814b1a..16e1509eb0 100644 --- a/modules/mono/glue/nodepath_glue.cpp +++ b/modules/mono/glue/nodepath_glue.cpp @@ -68,6 +68,10 @@ MonoString *godot_icall_NodePath_get_subname(NodePath *p_ptr, uint32_t p_idx) {  	return GDMonoMarshal::mono_string_from_godot(p_ptr->get_subname(p_idx));  } +MonoString *godot_icall_NodePath_get_concatenated_names(NodePath *p_ptr) { +	return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_names()); +} +  MonoString *godot_icall_NodePath_get_concatenated_subnames(NodePath *p_ptr) {  	return GDMonoMarshal::mono_string_from_godot(p_ptr->get_concatenated_subnames());  } @@ -85,6 +89,7 @@ void godot_register_nodepath_icalls() {  	GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_Dtor", godot_icall_NodePath_Dtor);  	GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_operator_String", godot_icall_NodePath_operator_String);  	GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_as_property_path", godot_icall_NodePath_get_as_property_path); +	GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_names", godot_icall_NodePath_get_concatenated_names);  	GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_concatenated_subnames", godot_icall_NodePath_get_concatenated_subnames);  	GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name", godot_icall_NodePath_get_name);  	GDMonoUtils::add_internal_call("Godot.NodePath::godot_icall_NodePath_get_name_count", godot_icall_NodePath_get_name_count); diff --git a/platform/osx/export/export_plugin.cpp b/platform/osx/export/export_plugin.cpp index 7010709123..00a7e54131 100644 --- a/platform/osx/export/export_plugin.cpp +++ b/platform/osx/export/export_plugin.cpp @@ -1086,6 +1086,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p  				Ref<FileAccess> f = FileAccess::open(file, FileAccess::WRITE);  				if (f.is_valid()) {  					f->store_buffer(data.ptr(), data.size()); +					f.unref();  					if (is_execute) {  						// chmod with 0755 if the file is executable.  						FileAccess::set_unix_permissions(file, 0755); diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index d0aeffb166..fbd5b5b65b 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -376,6 +376,24 @@ void BoneAttachment3D::on_bone_pose_update(int p_bone_index) {  		}  	}  } +#ifdef TOOLS_ENABLED +void BoneAttachment3D::_notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Ref<BoneMap> p_bone_map) { +	const Skeleton3D *parent = nullptr; +	if (use_external_skeleton) { +		if (external_skeleton_node_cache.is_valid()) { +			parent = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); +		} +	} else { +		parent = Object::cast_to<Skeleton3D>(get_parent()); +	} +	if (parent && parent == p_skeleton) { +		StringName bn = p_bone_map->find_profile_bone_name(bone_name); +		if (bn) { +			set_bone_name(bn); +		} +	} +} +#endif // TOOLS_ENABLED  BoneAttachment3D::BoneAttachment3D() {  } @@ -398,6 +416,9 @@ void BoneAttachment3D::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_use_external_skeleton"), &BoneAttachment3D::get_use_external_skeleton);  	ClassDB::bind_method(D_METHOD("set_external_skeleton", "external_skeleton"), &BoneAttachment3D::set_external_skeleton);  	ClassDB::bind_method(D_METHOD("get_external_skeleton"), &BoneAttachment3D::get_external_skeleton); +#ifdef TOOLS_ENABLED +	ClassDB::bind_method(D_METHOD("_notify_skeleton_bones_renamed"), &BoneAttachment3D::_notify_skeleton_bones_renamed); +#endif // TOOLS_ENABLED  	ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bone_name"), "set_bone_name", "get_bone_name");  	ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_idx"), "set_bone_idx", "get_bone_idx"); diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index 395dfde1d7..137360b141 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -32,6 +32,9 @@  #define BONE_ATTACHMENT_H  #include "scene/3d/skeleton_3d.h" +#ifdef TOOLS_ENABLED +#include "scene/resources/bone_map.h" +#endif // TOOLS_ENABLED  class BoneAttachment3D : public Node3D {  	GDCLASS(BoneAttachment3D, Node3D); @@ -68,6 +71,9 @@ protected:  	void _notification(int p_what);  	static void _bind_methods(); +#ifdef TOOLS_ENABLED +	virtual void _notify_skeleton_bones_renamed(Node *p_base_scene, Skeleton3D *p_skeleton, Ref<BoneMap> p_bone_map); +#endif // TOOLS_ENABLED  public:  	virtual TypedArray<String> get_configuration_warnings() const override; diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 4c00250162..04b1081516 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -237,6 +237,28 @@ void Node3D::set_quaternion(const Quaternion &p_quaternion) {  	}  } +Vector3 Node3D::get_global_position() const { +	return get_global_transform().get_origin(); +} + +void Node3D::set_global_position(const Vector3 &p_position) { +	Transform3D transform = get_global_transform(); +	transform.set_origin(p_position); +	set_global_transform(transform); +} + +Vector3 Node3D::get_global_rotation() const { +	return get_global_transform().get_basis().get_euler(); +} + +void Node3D::set_global_rotation(const Vector3 &p_euler_rad) { +	Transform3D transform = get_global_transform(); +	Basis new_basis = transform.get_basis(); +	new_basis.set_euler(p_euler_rad); +	transform.set_basis(new_basis); +	set_global_transform(transform); +} +  void Node3D::set_transform(const Transform3D &p_transform) {  	data.local_transform = p_transform;  	data.dirty = DIRTY_EULER_ROTATION_AND_SCALE; // Make rot/scale dirty. @@ -950,8 +972,14 @@ void Node3D::_bind_methods() {  	ClassDB::bind_method(D_METHOD("get_quaternion"), &Node3D::get_quaternion);  	ClassDB::bind_method(D_METHOD("set_basis", "basis"), &Node3D::set_basis);  	ClassDB::bind_method(D_METHOD("get_basis"), &Node3D::get_basis); +  	ClassDB::bind_method(D_METHOD("set_global_transform", "global"), &Node3D::set_global_transform);  	ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform); +	ClassDB::bind_method(D_METHOD("set_global_position", "position"), &Node3D::set_global_position); +	ClassDB::bind_method(D_METHOD("get_global_position"), &Node3D::get_global_position); +	ClassDB::bind_method(D_METHOD("set_global_rotation", "radians"), &Node3D::set_global_rotation); +	ClassDB::bind_method(D_METHOD("get_global_rotation"), &Node3D::get_global_rotation); +  	ClassDB::bind_method(D_METHOD("get_parent_node_3d"), &Node3D::get_parent_node_3d);  	ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Node3D::set_ignore_transform_notification);  	ClassDB::bind_method(D_METHOD("set_as_top_level", "enable"), &Node3D::set_as_top_level); @@ -1034,6 +1062,9 @@ void Node3D::_bind_methods() {  	ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_edit_mode", PROPERTY_HINT_ENUM, "Euler,Quaternion,Basis"), "set_rotation_edit_mode", "get_rotation_edit_mode");  	ADD_PROPERTY(PropertyInfo(Variant::INT, "rotation_order", PROPERTY_HINT_ENUM, "XYZ,XZY,YXZ,YZX,ZXY,ZYX"), "set_rotation_order", "get_rotation_order");  	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level"); + +	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "global_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_position", "get_global_position"); +	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "global_rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_rotation", "get_global_rotation");  	ADD_GROUP("Visibility", "");  	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");  	ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent"); diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index cfd88585e4..b1e129798d 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -182,12 +182,18 @@ public:  	void set_rotation(const Vector3 &p_euler_rad);  	void set_scale(const Vector3 &p_scale); +	void set_global_position(const Vector3 &p_position); +	void set_global_rotation(const Vector3 &p_euler_rad); +  	Vector3 get_position() const;  	RotationOrder get_rotation_order() const;  	Vector3 get_rotation() const;  	Vector3 get_scale() const; +	Vector3 get_global_position() const; +	Vector3 get_global_rotation() const; +  	void set_transform(const Transform3D &p_transform);  	void set_basis(const Basis &p_basis);  	void set_quaternion(const Quaternion &p_quaternion); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 545ff68b72..b4701637a4 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -439,9 +439,9 @@ void Node::set_physics_process(bool p_process) {  	data.physics_process = p_process;  	if (data.physics_process) { -		add_to_group("physics_process", false); +		add_to_group(SNAME("_physics_process"), false);  	} else { -		remove_from_group("physics_process"); +		remove_from_group(SNAME("_physics_process"));  	}  } @@ -457,9 +457,9 @@ void Node::set_physics_process_internal(bool p_process_internal) {  	data.physics_process_internal = p_process_internal;  	if (data.physics_process_internal) { -		add_to_group("physics_process_internal", false); +		add_to_group(SNAME("_physics_process_internal"), false);  	} else { -		remove_from_group("physics_process_internal"); +		remove_from_group(SNAME("_physics_process_internal"));  	}  } @@ -770,9 +770,9 @@ void Node::set_process(bool p_process) {  	data.process = p_process;  	if (data.process) { -		add_to_group("process", false); +		add_to_group(SNAME("_process"), false);  	} else { -		remove_from_group("process"); +		remove_from_group(SNAME("_process"));  	}  } @@ -788,9 +788,9 @@ void Node::set_process_internal(bool p_process_internal) {  	data.process_internal = p_process_internal;  	if (data.process_internal) { -		add_to_group("process_internal", false); +		add_to_group(SNAME("_process_internal"), false);  	} else { -		remove_from_group("process_internal"); +		remove_from_group(SNAME("_process_internal"));  	}  } @@ -807,19 +807,19 @@ void Node::set_process_priority(int p_priority) {  	}  	if (is_processing()) { -		data.tree->make_group_changed("process"); +		data.tree->make_group_changed(SNAME("_process"));  	}  	if (is_processing_internal()) { -		data.tree->make_group_changed("process_internal"); +		data.tree->make_group_changed(SNAME("_process_internal"));  	}  	if (is_physics_processing()) { -		data.tree->make_group_changed("physics_process"); +		data.tree->make_group_changed(SNAME("_physics_process"));  	}  	if (is_physics_processing_internal()) { -		data.tree->make_group_changed("physics_process_internal"); +		data.tree->make_group_changed(SNAME("_physics_process_internal"));  	}  } diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 18f69ecc82..a76c00efcb 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -412,9 +412,9 @@ bool SceneTree::physics_process(double p_time) {  	emit_signal(SNAME("physics_frame")); -	_notify_group_pause(SNAME("physics_process_internal"), Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS); +	_notify_group_pause(SNAME("_physics_process_internal"), Node::NOTIFICATION_INTERNAL_PHYSICS_PROCESS);  	call_group(SNAME("_picking_viewports"), SNAME("_process_picking")); -	_notify_group_pause(SNAME("physics_process"), Node::NOTIFICATION_PHYSICS_PROCESS); +	_notify_group_pause(SNAME("_physics_process"), Node::NOTIFICATION_PHYSICS_PROCESS);  	_flush_ugc();  	MessageQueue::get_singleton()->flush(); //small little hack @@ -449,8 +449,8 @@ bool SceneTree::process(double p_time) {  	flush_transform_notifications(); -	_notify_group_pause(SNAME("process_internal"), Node::NOTIFICATION_INTERNAL_PROCESS); -	_notify_group_pause(SNAME("process"), Node::NOTIFICATION_PROCESS); +	_notify_group_pause(SNAME("_process_internal"), Node::NOTIFICATION_INTERNAL_PROCESS); +	_notify_group_pause(SNAME("_process"), Node::NOTIFICATION_PROCESS);  	_flush_ugc();  	MessageQueue::get_singleton()->flush(); //small little hack diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index f70d57291f..4d0d6111ec 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -143,6 +143,7 @@  #include "scene/resources/animation_library.h"  #include "scene/resources/audio_stream_sample.h"  #include "scene/resources/bit_map.h" +#include "scene/resources/bone_map.h"  #include "scene/resources/box_shape_3d.h"  #include "scene/resources/camera_effects.h"  #include "scene/resources/capsule_shape_2d.h" @@ -189,6 +190,7 @@  #include "scene/resources/skeleton_modification_3d_twoboneik.h"  #include "scene/resources/skeleton_modification_stack_2d.h"  #include "scene/resources/skeleton_modification_stack_3d.h" +#include "scene/resources/skeleton_profile.h"  #include "scene/resources/sky.h"  #include "scene/resources/sky_material.h"  #include "scene/resources/sphere_shape_3d.h" @@ -871,6 +873,10 @@ void register_scene_types() {  	GDREGISTER_CLASS(BitMap);  	GDREGISTER_CLASS(Gradient); +	GDREGISTER_CLASS(SkeletonProfile); +	GDREGISTER_CLASS(SkeletonProfileHumanoid); +	GDREGISTER_CLASS(BoneMap); +  	OS::get_singleton()->yield(); // may take time to init  	GDREGISTER_CLASS(AudioStreamPlayer); diff --git a/scene/resources/bone_map.cpp b/scene/resources/bone_map.cpp new file mode 100644 index 0000000000..ce030934fa --- /dev/null +++ b/scene/resources/bone_map.cpp @@ -0,0 +1,172 @@ +/*************************************************************************/ +/*  bone_map.cpp                                                         */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#include "bone_map.h" + +bool BoneMap::_set(const StringName &p_path, const Variant &p_value) { +	String path = p_path; +	if (path.begins_with("bone_map/")) { +		String which = path.get_slicec('/', 1); +		set_skeleton_bone_name(which, p_value); +		return true; +	} +	return true; +} + +bool BoneMap::_get(const StringName &p_path, Variant &r_ret) const { +	String path = p_path; +	if (path.begins_with("bone_map/")) { +		String which = path.get_slicec('/', 1); +		r_ret = get_skeleton_bone_name(which); +		return true; +	} +	return true; +} + +Ref<SkeletonProfile> BoneMap::get_profile() const { +	return profile; +} + +void BoneMap::set_profile(const Ref<SkeletonProfile> &p_profile) { +	bool is_changed = profile != p_profile; +	if (is_changed) { +		if (!profile.is_null() && profile->is_connected("profile_updated", callable_mp(this, &BoneMap::_update_profile))) { +			profile->disconnect("profile_updated", callable_mp(this, &BoneMap::_update_profile)); +		} +		profile = p_profile; +		if (!profile.is_null()) { +			profile->connect("profile_updated", callable_mp(this, &BoneMap::_update_profile)); +		} +		_update_profile(); +	} +	notify_property_list_changed(); +} + +StringName BoneMap::get_skeleton_bone_name(StringName p_profile_bone_name) const { +	ERR_FAIL_COND_V(!bone_map.has(p_profile_bone_name), StringName()); +	return bone_map.get(p_profile_bone_name); +} + +void BoneMap::set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name) { +	ERR_FAIL_COND(!bone_map.has(p_profile_bone_name)); +	bone_map.insert(p_profile_bone_name, p_skeleton_bone_name); +	emit_signal("bone_map_updated"); +} + +StringName BoneMap::find_profile_bone_name(StringName p_skeleton_bone_name) const { +	StringName profile_bone_name = StringName(); +	HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); +	while (E) { +		if (E->value == p_skeleton_bone_name) { +			profile_bone_name = E->key; +			break; +		} +		++E; +	} +	return profile_bone_name; +} + +int BoneMap::get_skeleton_bone_name_count(const StringName p_skeleton_bone_name) const { +	int count = 0; +	HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); +	while (E) { +		if (E->value == p_skeleton_bone_name) { +			++count; +		} +		++E; +	} +	return count; +} + +void BoneMap::_update_profile() { +	_validate_bone_map(); +	emit_signal("profile_updated"); +} + +void BoneMap::_validate_bone_map() { +	Ref<SkeletonProfile> current_profile = get_profile(); +	if (current_profile.is_valid()) { +		// Insert missing profile bones into bone map. +		int len = current_profile->get_bone_size(); +		StringName profile_bone_name; +		for (int i = 0; i < len; i++) { +			profile_bone_name = current_profile->get_bone_name(i); +			if (!bone_map.has(profile_bone_name)) { +				bone_map.insert(profile_bone_name, StringName()); +			} +		} +		// Remove bones that do not exist in the profile from the map. +		Vector<StringName> delete_bones; +		StringName k; +		HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); +		while (E) { +			k = E->key; +			if (!current_profile->has_bone(k)) { +				delete_bones.push_back(k); +			} +			++E; +		} +		len = delete_bones.size(); +		for (int i = 0; i < len; i++) { +			bone_map.erase(delete_bones[i]); +		} +	} else { +		bone_map.clear(); +	} +	emit_signal("retarget_option_updated"); +} + +void BoneMap::_bind_methods() { +	ClassDB::bind_method(D_METHOD("get_profile"), &BoneMap::get_profile); +	ClassDB::bind_method(D_METHOD("set_profile", "profile"), &BoneMap::set_profile); + +	ClassDB::bind_method(D_METHOD("get_skeleton_bone_name", "profile_bone_name"), &BoneMap::get_skeleton_bone_name); +	ClassDB::bind_method(D_METHOD("set_skeleton_bone_name", "profile_bone_name", "skeleton_bone_name"), &BoneMap::set_skeleton_bone_name); + +	ClassDB::bind_method(D_METHOD("find_profile_bone_name", "skeleton_bone_name"), &BoneMap::find_profile_bone_name); + +	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "profile", PROPERTY_HINT_RESOURCE_TYPE, "SkeletonProfile"), "set_profile", "get_profile"); + +	ADD_SIGNAL(MethodInfo("bone_map_updated")); +	ADD_SIGNAL(MethodInfo("profile_updated")); +} + +void BoneMap::_validate_property(PropertyInfo &property) const { +	// +} + +BoneMap::BoneMap() { +	_validate_bone_map(); +} + +BoneMap::~BoneMap() { +} + +////////////////////////////////////// diff --git a/scene/resources/bone_map.h b/scene/resources/bone_map.h new file mode 100644 index 0000000000..4b7928015d --- /dev/null +++ b/scene/resources/bone_map.h @@ -0,0 +1,69 @@ +/*************************************************************************/ +/*  bone_map.h                                                           */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#ifndef BONE_MAP_H +#define BONE_MAP_H + +#include "skeleton_profile.h" + +class BoneMap : public Resource { +	GDCLASS(BoneMap, Resource); + +	Ref<SkeletonProfile> profile; +	HashMap<StringName, StringName> bone_map; + +	void _update_profile(); +	void _validate_bone_map(); + +protected: +	bool _get(const StringName &p_path, Variant &r_ret) const; +	bool _set(const StringName &p_path, const Variant &p_value); +	virtual void _validate_property(PropertyInfo &property) const override; +	static void _bind_methods(); + +public: +	int get_profile_type() const; +	void set_profile_type(const int p_profile_type); + +	Ref<SkeletonProfile> get_profile() const; +	void set_profile(const Ref<SkeletonProfile> &p_profile); + +	int get_skeleton_bone_name_count(const StringName p_skeleton_bone_name) const; + +	StringName get_skeleton_bone_name(StringName p_profile_bone_name) const; +	void set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name); + +	StringName find_profile_bone_name(StringName p_skeleton_bone_name) const; + +	BoneMap(); +	~BoneMap(); +}; + +#endif // BONE_MAP_H diff --git a/scene/resources/skeleton_profile.cpp b/scene/resources/skeleton_profile.cpp new file mode 100644 index 0000000000..05d48f9545 --- /dev/null +++ b/scene/resources/skeleton_profile.cpp @@ -0,0 +1,514 @@ +/*************************************************************************/ +/*  skeleton_profile.cpp                                                 */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#include "skeleton_profile.h" + +bool SkeletonProfile::_set(const StringName &p_path, const Variant &p_value) { +	ERR_FAIL_COND_V(is_read_only, false); +	String path = p_path; + +	if (path.begins_with("group/")) { +		int which = path.get_slicec('/', 1).to_int(); +		String what = path.get_slicec('/', 2); +		ERR_FAIL_INDEX_V(which, groups.size(), false); + +		if (what == "group_name") { +			set_group_name(which, p_value); +		} else if (what == "texture") { +			set_texture(which, p_value); +		} +		return true; +	} + +	if (path.begins_with("bone/")) { +		int which = path.get_slicec('/', 1).to_int(); +		String what = path.get_slicec('/', 2); +		ERR_FAIL_INDEX_V(which, bones.size(), false); + +		if (what == "bone_name") { +			set_bone_name(which, p_value); +		} else if (what == "handle_offset") { +			set_handle_offset(which, p_value); +		} else if (what == "group") { +			set_group(which, p_value); +		} +		return true; +	} +	return true; +} + +bool SkeletonProfile::_get(const StringName &p_path, Variant &r_ret) const { +	String path = p_path; + +	if (path.begins_with("group/")) { +		int which = path.get_slicec('/', 1).to_int(); +		String what = path.get_slicec('/', 2); +		ERR_FAIL_INDEX_V(which, groups.size(), false); + +		if (what == "group_name") { +			r_ret = get_group_name(which); +		} else if (what == "texture") { +			r_ret = get_texture(which); +		} +		return true; +	} + +	if (path.begins_with("bone/")) { +		int which = path.get_slicec('/', 1).to_int(); +		String what = path.get_slicec('/', 2); +		ERR_FAIL_INDEX_V(which, bones.size(), false); + +		if (what == "bone_name") { +			r_ret = get_bone_name(which); +		} else if (what == "handle_offset") { +			r_ret = get_handle_offset(which); +		} else if (what == "group") { +			r_ret = get_group(which); +		} +		return true; +	} +	return true; +} + +void SkeletonProfile::_validate_property(PropertyInfo &property) const { +	if (is_read_only) { +		if (property.name == ("group_size") || property.name == ("bone_size")) { +			property.usage = PROPERTY_USAGE_NO_EDITOR; +			return; +		} +	} +} + +void SkeletonProfile::_get_property_list(List<PropertyInfo> *p_list) const { +	if (is_read_only) { +		return; +	} +	String group_names = ""; +	for (int i = 0; i < groups.size(); i++) { +		String path = "group/" + itos(i) + "/"; +		p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "group_name")); +		p_list->push_back(PropertyInfo(Variant::OBJECT, path + "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D")); +		if (i > 0) { +			group_names = group_names + ","; +		} +		group_names = group_names + groups[i].group_name; +	} +	for (int i = 0; i < bones.size(); i++) { +		String path = "bone/" + itos(i) + "/"; +		p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "bone_name")); +		p_list->push_back(PropertyInfo(Variant::VECTOR2, path + "handle_offset")); +		p_list->push_back(PropertyInfo(Variant::STRING_NAME, path + "group", PROPERTY_HINT_ENUM, group_names)); +	} +} + +int SkeletonProfile::get_group_size() { +	return groups.size(); +} + +void SkeletonProfile::set_group_size(int p_size) { +	if (is_read_only) { +		return; +	} +	ERR_FAIL_COND(p_size < 0); +	groups.resize(p_size); +	emit_signal("profile_updated"); +	notify_property_list_changed(); +} + +StringName SkeletonProfile::get_group_name(int p_group_idx) const { +	ERR_FAIL_INDEX_V(p_group_idx, groups.size(), StringName()); +	return groups[p_group_idx].group_name; +} + +void SkeletonProfile::set_group_name(int p_group_idx, const StringName p_group_name) { +	if (is_read_only) { +		return; +	} +	ERR_FAIL_INDEX(p_group_idx, groups.size()); +	groups.write[p_group_idx].group_name = p_group_name; +	emit_signal("profile_updated"); +} + +Ref<Texture2D> SkeletonProfile::get_texture(int p_group_idx) const { +	ERR_FAIL_INDEX_V(p_group_idx, groups.size(), Ref<Texture2D>()); +	return groups[p_group_idx].texture; +} + +void SkeletonProfile::set_texture(int p_group_idx, const Ref<Texture2D> &p_texture) { +	if (is_read_only) { +		return; +	} +	ERR_FAIL_INDEX(p_group_idx, groups.size()); +	groups.write[p_group_idx].texture = p_texture; +	emit_signal("profile_updated"); +} + +int SkeletonProfile::get_bone_size() { +	return bones.size(); +} + +void SkeletonProfile::set_bone_size(int p_size) { +	if (is_read_only) { +		return; +	} +	ERR_FAIL_COND(p_size < 0); +	bones.resize(p_size); +	emit_signal("profile_updated"); +	notify_property_list_changed(); +} + +StringName SkeletonProfile::get_bone_name(int p_bone_idx) const { +	ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName()); +	return bones[p_bone_idx].bone_name; +} + +void SkeletonProfile::set_bone_name(int p_bone_idx, const StringName p_bone_name) { +	if (is_read_only) { +		return; +	} +	ERR_FAIL_INDEX(p_bone_idx, bones.size()); +	bones.write[p_bone_idx].bone_name = p_bone_name; +	emit_signal("profile_updated"); +} + +Vector2 SkeletonProfile::get_handle_offset(int p_bone_idx) const { +	ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), Vector2()); +	return bones[p_bone_idx].handle_offset; +} + +void SkeletonProfile::set_handle_offset(int p_bone_idx, const Vector2 p_handle_offset) { +	if (is_read_only) { +		return; +	} +	ERR_FAIL_INDEX(p_bone_idx, bones.size()); +	bones.write[p_bone_idx].handle_offset = p_handle_offset; +	emit_signal("profile_updated"); +} + +StringName SkeletonProfile::get_group(int p_bone_idx) const { +	ERR_FAIL_INDEX_V(p_bone_idx, bones.size(), StringName()); +	return bones[p_bone_idx].group; +} + +void SkeletonProfile::set_group(int p_bone_idx, const StringName p_group) { +	if (is_read_only) { +		return; +	} +	ERR_FAIL_INDEX(p_bone_idx, bones.size()); +	bones.write[p_bone_idx].group = p_group; +	emit_signal("profile_updated"); +} + +bool SkeletonProfile::has_bone(StringName p_bone_name) { +	bool is_found = false; +	for (int i = 0; i < bones.size(); i++) { +		if (bones[i].bone_name == p_bone_name) { +			is_found = true; +			break; +		} +	} +	return is_found; +} + +void SkeletonProfile::_bind_methods() { +	ClassDB::bind_method(D_METHOD("set_group_size", "size"), &SkeletonProfile::set_group_size); +	ClassDB::bind_method(D_METHOD("get_group_size"), &SkeletonProfile::get_group_size); + +	ClassDB::bind_method(D_METHOD("get_group_name", "group_idx"), &SkeletonProfile::get_group_name); +	ClassDB::bind_method(D_METHOD("set_group_name", "group_idx", "group_name"), &SkeletonProfile::set_group_name); + +	ClassDB::bind_method(D_METHOD("get_texture", "group_idx"), &SkeletonProfile::get_texture); +	ClassDB::bind_method(D_METHOD("set_texture", "group_idx", "texture"), &SkeletonProfile::set_texture); + +	ClassDB::bind_method(D_METHOD("set_bone_size", "size"), &SkeletonProfile::set_bone_size); +	ClassDB::bind_method(D_METHOD("get_bone_size"), &SkeletonProfile::get_bone_size); + +	ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &SkeletonProfile::get_bone_name); +	ClassDB::bind_method(D_METHOD("set_bone_name", "bone_idx", "bone_name"), &SkeletonProfile::set_bone_name); + +	ClassDB::bind_method(D_METHOD("get_handle_offset", "bone_idx"), &SkeletonProfile::get_handle_offset); +	ClassDB::bind_method(D_METHOD("set_handle_offset", "bone_idx", "handle_offset"), &SkeletonProfile::set_handle_offset); + +	ClassDB::bind_method(D_METHOD("get_group", "bone_idx"), &SkeletonProfile::get_group); +	ClassDB::bind_method(D_METHOD("set_group", "bone_idx", "group"), &SkeletonProfile::set_group); + +	ADD_PROPERTY(PropertyInfo(Variant::INT, "group_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Groups,group/"), "set_group_size", "get_group_size"); +	ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_size", PROPERTY_HINT_RANGE, "0,100,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ARRAY, "Bones,bone/"), "set_bone_size", "get_bone_size"); + +	ADD_SIGNAL(MethodInfo("profile_updated")); +} + +SkeletonProfile::SkeletonProfile() { +} + +SkeletonProfile::~SkeletonProfile() { +} + +SkeletonProfileHumanoid::SkeletonProfileHumanoid() { +	is_read_only = true; + +	groups.resize(4); + +	groups.write[0].group_name = "Body"; +	groups.write[1].group_name = "Face"; +	groups.write[2].group_name = "LeftHand"; +	groups.write[3].group_name = "RightHand"; + +	bones.resize(56); + +	bones.write[0].bone_name = "Root"; +	bones.write[0].handle_offset = Vector2(0.5, 0.91); +	bones.write[0].group = "Body"; + +	bones.write[1].bone_name = "Hips"; +	bones.write[1].handle_offset = Vector2(0.5, 0.5); +	bones.write[1].group = "Body"; + +	bones.write[2].bone_name = "Spine"; +	bones.write[2].handle_offset = Vector2(0.5, 0.43); +	bones.write[2].group = "Body"; + +	bones.write[3].bone_name = "Chest"; +	bones.write[3].handle_offset = Vector2(0.5, 0.36); +	bones.write[3].group = "Body"; + +	bones.write[4].bone_name = "UpperChest"; +	bones.write[4].handle_offset = Vector2(0.5, 0.29); +	bones.write[4].group = "Body"; + +	bones.write[5].bone_name = "Neck"; +	bones.write[5].handle_offset = Vector2(0.5, 0.23); +	bones.write[5].group = "Body"; + +	bones.write[6].bone_name = "Head"; +	bones.write[6].handle_offset = Vector2(0.5, 0.18); +	bones.write[6].group = "Body"; + +	bones.write[7].bone_name = "LeftEye"; +	bones.write[7].handle_offset = Vector2(0.6, 0.46); +	bones.write[7].group = "Face"; + +	bones.write[8].bone_name = "RightEye"; +	bones.write[8].handle_offset = Vector2(0.37, 0.46); +	bones.write[8].group = "Face"; + +	bones.write[9].bone_name = "Jaw"; +	bones.write[9].handle_offset = Vector2(0.46, 0.75); +	bones.write[9].group = "Face"; + +	bones.write[10].bone_name = "LeftShoulder"; +	bones.write[10].handle_offset = Vector2(0.55, 0.235); +	bones.write[10].group = "Body"; + +	bones.write[11].bone_name = "LeftUpperArm"; +	bones.write[11].handle_offset = Vector2(0.6, 0.24); +	bones.write[11].group = "Body"; + +	bones.write[12].bone_name = "LeftLowerArm"; +	bones.write[12].handle_offset = Vector2(0.7, 0.24); +	bones.write[12].group = "Body"; + +	bones.write[13].bone_name = "LeftHand"; +	bones.write[13].handle_offset = Vector2(0.82, 0.235); +	bones.write[13].group = "Body"; + +	bones.write[14].bone_name = "LeftThumbProximal"; +	bones.write[14].handle_offset = Vector2(0.4, 0.8); +	bones.write[14].group = "LeftHand"; + +	bones.write[15].bone_name = "LeftThumbIntermediate"; +	bones.write[15].handle_offset = Vector2(0.3, 0.69); +	bones.write[15].group = "LeftHand"; + +	bones.write[16].bone_name = "LeftThumbDistal"; +	bones.write[16].handle_offset = Vector2(0.23, 0.555); +	bones.write[16].group = "LeftHand"; + +	bones.write[17].bone_name = "LeftIndexProximal"; +	bones.write[17].handle_offset = Vector2(0.413, 0.52); +	bones.write[17].group = "LeftHand"; + +	bones.write[18].bone_name = "LeftIndexIntermediate"; +	bones.write[18].handle_offset = Vector2(0.403, 0.36); +	bones.write[18].group = "LeftHand"; + +	bones.write[19].bone_name = "LeftIndexDistal"; +	bones.write[19].handle_offset = Vector2(0.403, 0.255); +	bones.write[19].group = "LeftHand"; + +	bones.write[20].bone_name = "LeftMiddleProximal"; +	bones.write[20].handle_offset = Vector2(0.5, 0.51); +	bones.write[20].group = "LeftHand"; + +	bones.write[21].bone_name = "LeftMiddleIntermediate"; +	bones.write[21].handle_offset = Vector2(0.5, 0.345); +	bones.write[21].group = "LeftHand"; + +	bones.write[22].bone_name = "LeftMiddleDistal"; +	bones.write[22].handle_offset = Vector2(0.5, 0.22); +	bones.write[22].group = "LeftHand"; + +	bones.write[23].bone_name = "LeftRingProximal"; +	bones.write[23].handle_offset = Vector2(0.586, 0.52); +	bones.write[23].group = "LeftHand"; + +	bones.write[24].bone_name = "LeftRingIntermediate"; +	bones.write[24].handle_offset = Vector2(0.59, 0.36); +	bones.write[24].group = "LeftHand"; + +	bones.write[25].bone_name = "LeftRingDistal"; +	bones.write[25].handle_offset = Vector2(0.591, 0.25); +	bones.write[25].group = "LeftHand"; + +	bones.write[26].bone_name = "LeftLittleProximal"; +	bones.write[26].handle_offset = Vector2(0.663, 0.543); +	bones.write[26].group = "LeftHand"; + +	bones.write[27].bone_name = "LeftLittleIntermediate"; +	bones.write[27].handle_offset = Vector2(0.672, 0.415); +	bones.write[27].group = "LeftHand"; + +	bones.write[28].bone_name = "LeftLittleDistal"; +	bones.write[28].handle_offset = Vector2(0.672, 0.32); +	bones.write[28].group = "LeftHand"; + +	bones.write[29].bone_name = "RightShoulder"; +	bones.write[29].handle_offset = Vector2(0.45, 0.235); +	bones.write[29].group = "Body"; + +	bones.write[30].bone_name = "RightUpperArm"; +	bones.write[30].handle_offset = Vector2(0.4, 0.24); +	bones.write[30].group = "Body"; + +	bones.write[31].bone_name = "RightLowerArm"; +	bones.write[31].handle_offset = Vector2(0.3, 0.24); +	bones.write[31].group = "Body"; + +	bones.write[32].bone_name = "RightHand"; +	bones.write[32].handle_offset = Vector2(0.18, 0.235); +	bones.write[32].group = "Body"; + +	bones.write[33].bone_name = "RightThumbProximal"; +	bones.write[33].handle_offset = Vector2(0.6, 0.8); +	bones.write[33].group = "RightHand"; + +	bones.write[34].bone_name = "RightThumbIntermediate"; +	bones.write[34].handle_offset = Vector2(0.7, 0.69); +	bones.write[34].group = "RightHand"; + +	bones.write[35].bone_name = "RightThumbDistal"; +	bones.write[35].handle_offset = Vector2(0.77, 0.555); +	bones.write[35].group = "RightHand"; + +	bones.write[36].bone_name = "RightIndexProximal"; +	bones.write[36].handle_offset = Vector2(0.587, 0.52); +	bones.write[36].group = "RightHand"; + +	bones.write[37].bone_name = "RightIndexIntermediate"; +	bones.write[37].handle_offset = Vector2(0.597, 0.36); +	bones.write[37].group = "RightHand"; + +	bones.write[38].bone_name = "RightIndexDistal"; +	bones.write[38].handle_offset = Vector2(0.597, 0.255); +	bones.write[38].group = "RightHand"; + +	bones.write[39].bone_name = "RightMiddleProximal"; +	bones.write[39].handle_offset = Vector2(0.5, 0.51); +	bones.write[39].group = "RightHand"; + +	bones.write[40].bone_name = "RightMiddleIntermediate"; +	bones.write[40].handle_offset = Vector2(0.5, 0.345); +	bones.write[40].group = "RightHand"; + +	bones.write[41].bone_name = "RightMiddleDistal"; +	bones.write[41].handle_offset = Vector2(0.5, 0.22); +	bones.write[41].group = "RightHand"; + +	bones.write[42].bone_name = "RightRingProximal"; +	bones.write[42].handle_offset = Vector2(0.414, 0.52); +	bones.write[42].group = "RightHand"; + +	bones.write[43].bone_name = "RightRingIntermediate"; +	bones.write[43].handle_offset = Vector2(0.41, 0.36); +	bones.write[43].group = "RightHand"; + +	bones.write[44].bone_name = "RightRingDistal"; +	bones.write[44].handle_offset = Vector2(0.409, 0.25); +	bones.write[44].group = "RightHand"; + +	bones.write[45].bone_name = "RightLittleProximal"; +	bones.write[45].handle_offset = Vector2(0.337, 0.543); +	bones.write[45].group = "RightHand"; + +	bones.write[46].bone_name = "RightLittleIntermediate"; +	bones.write[46].handle_offset = Vector2(0.328, 0.415); +	bones.write[46].group = "RightHand"; + +	bones.write[47].bone_name = "RightLittleDistal"; +	bones.write[47].handle_offset = Vector2(0.328, 0.32); +	bones.write[47].group = "RightHand"; + +	bones.write[48].bone_name = "LeftUpperLeg"; +	bones.write[48].handle_offset = Vector2(0.549, 0.49); +	bones.write[48].group = "Body"; + +	bones.write[49].bone_name = "LeftLowerLeg"; +	bones.write[49].handle_offset = Vector2(0.548, 0.683); +	bones.write[49].group = "Body"; + +	bones.write[50].bone_name = "LeftFoot"; +	bones.write[50].handle_offset = Vector2(0.545, 0.9); +	bones.write[50].group = "Body"; + +	bones.write[51].bone_name = "LeftToes"; +	bones.write[51].handle_offset = Vector2(0.545, 0.95); +	bones.write[51].group = "Body"; + +	bones.write[52].bone_name = "RightUpperLeg"; +	bones.write[52].handle_offset = Vector2(0.451, 0.49); +	bones.write[52].group = "Body"; + +	bones.write[53].bone_name = "RightLowerLeg"; +	bones.write[53].handle_offset = Vector2(0.452, 0.683); +	bones.write[53].group = "Body"; + +	bones.write[54].bone_name = "RightFoot"; +	bones.write[54].handle_offset = Vector2(0.455, 0.9); +	bones.write[54].group = "Body"; + +	bones.write[55].bone_name = "RightToes"; +	bones.write[55].handle_offset = Vector2(0.455, 0.95); +	bones.write[55].group = "Body"; +} + +SkeletonProfileHumanoid::~SkeletonProfileHumanoid() { +} + +////////////////////////////////////// diff --git a/scene/resources/skeleton_profile.h b/scene/resources/skeleton_profile.h new file mode 100644 index 0000000000..920aaa2b8d --- /dev/null +++ b/scene/resources/skeleton_profile.h @@ -0,0 +1,100 @@ +/*************************************************************************/ +/*  skeleton_profile.h                                                   */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the       */ +/* "Software"), to deal in the Software without restriction, including   */ +/* without limitation the rights to use, copy, modify, merge, publish,   */ +/* distribute, sublicense, and/or sell copies of the Software, and to    */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions:                                             */ +/*                                                                       */ +/* The above copyright notice and this permission notice shall be        */ +/* included in all copies or substantial portions of the Software.       */ +/*                                                                       */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ +/*************************************************************************/ + +#ifndef SKELETON_PROFILE_H +#define SKELETON_PROFILE_H + +#include "texture.h" + +class SkeletonProfile : public Resource { +	GDCLASS(SkeletonProfile, Resource); + +protected: +	// Note: SkeletonProfileHumanoid which extends SkeletonProfile exists to unify standard bone names. +	// That is what is_read_only is for, so don't make it public. +	bool is_read_only = false; + +	struct SkeletonProfileGroup { +		StringName group_name; +		Ref<Texture2D> texture; +	}; + +	struct SkeletonProfileBone { +		StringName bone_name; +		Vector2 handle_offset; +		StringName group; +	}; + +	Vector<SkeletonProfileGroup> groups; +	Vector<SkeletonProfileBone> bones; + +	bool _get(const StringName &p_path, Variant &r_ret) const; +	bool _set(const StringName &p_path, const Variant &p_value); +	virtual void _validate_property(PropertyInfo &property) const override; +	void _get_property_list(List<PropertyInfo> *p_list) const; +	static void _bind_methods(); + +public: +	int get_group_size(); +	void set_group_size(int p_size); + +	StringName get_group_name(int p_group_idx) const; +	void set_group_name(int p_group_idx, const StringName p_group_name); + +	Ref<Texture2D> get_texture(int p_group_idx) const; +	void set_texture(int p_group_idx, const Ref<Texture2D> &p_texture); + +	int get_bone_size(); +	void set_bone_size(int p_size); + +	StringName get_bone_name(int p_bone_idx) const; +	void set_bone_name(int p_bone_idx, const StringName p_bone_name); + +	Vector2 get_handle_offset(int p_bone_idx) const; +	void set_handle_offset(int p_bone_idx, const Vector2 p_handle_offset); + +	StringName get_group(int p_bone_idx) const; +	void set_group(int p_bone_idx, const StringName p_group); + +	bool has_bone(StringName p_bone_name); + +	SkeletonProfile(); +	~SkeletonProfile(); +}; + +class SkeletonProfileHumanoid : public SkeletonProfile { +	GDCLASS(SkeletonProfileHumanoid, SkeletonProfile); + +public: +	SkeletonProfileHumanoid(); +	~SkeletonProfileHumanoid(); +}; + +#endif // SKELETON_PROFILE_H  |