diff options
41 files changed, 2010 insertions, 1376 deletions
diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp index 903d5951a8..bf06c848c5 100644 --- a/core/math/triangle_mesh.cpp +++ b/core/math/triangle_mesh.cpp @@ -32,9 +32,9 @@  #include "core/templates/sort_array.h" -int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc) { -	if (p_depth > max_depth) { -		max_depth = p_depth; +int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &r_max_depth, int &r_max_alloc) { +	if (p_depth > r_max_depth) { +		r_max_depth = p_depth;  	}  	if (p_size == 1) { @@ -70,10 +70,10 @@ int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, in  		} break;  	} -	int left = _create_bvh(p_bvh, p_bb, p_from, p_size / 2, p_depth + 1, max_depth, max_alloc); -	int right = _create_bvh(p_bvh, p_bb, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, max_depth, max_alloc); +	int left = _create_bvh(p_bvh, p_bb, p_from, p_size / 2, p_depth + 1, r_max_depth, r_max_alloc); +	int right = _create_bvh(p_bvh, p_bb, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, r_max_depth, r_max_alloc); -	int index = max_alloc++; +	int index = r_max_alloc++;  	BVH *_new = &p_bvh[index];  	_new->aabb = aabb;  	_new->center = aabb.position + aabb.size * 0.5; diff --git a/doc/classes/EditorNode3DGizmo.xml b/doc/classes/EditorNode3DGizmo.xml index 221b5e44d6..d4d1e4a652 100644 --- a/doc/classes/EditorNode3DGizmo.xml +++ b/doc/classes/EditorNode3DGizmo.xml @@ -1,10 +1,10 @@  <?xml version="1.0" encoding="UTF-8" ?>  <class name="EditorNode3DGizmo" inherits="Node3DGizmo" version="4.0">  	<brief_description> -		Custom gizmo for editing Node3D objects. +		Gizmo for editing Node3D objects.  	</brief_description>  	<description> -		Custom gizmo that is used for providing custom visualization and editing (handles) for Node3D objects. See [EditorNode3DGizmoPlugin] for more information. +		Gizmo that is used for providing custom visualization and editing (handles and subgizmos) for Node3D objects. Can be overridden to create custom gizmos, but for simple gizmos creating a [EditorNode3DGizmoPlugin] is usually recommended.  	</description>  	<tutorials>  	</tutorials> @@ -12,64 +12,119 @@  		<method name="_commit_handle" qualifiers="virtual">  			<return type="void">  			</return> -			<argument index="0" name="index" type="int"> +			<argument index="0" name="id" type="int">  			</argument>  			<argument index="1" name="restore" type="Variant">  			</argument>  			<argument index="2" name="cancel" type="bool" default="false">  			</argument>  			<description> -				Commit a handle being edited (handles must have been previously added by [method add_handles]). -				If the [code]cancel[/code] parameter is [code]true[/code], an option to restore the edited value to the original is provided. +				Override this method to commit a handle being edited (handles must have been previously added by [method add_handles]). This usually means creating an [UndoRedo] action for the change, using the current handle value as "do" and the [code]restore[/code] argument as "undo". +				If the [code]cancel[/code] argument is [code]true[/code], the [code]restore[/code] value should be directly set, without any [UndoRedo] action. +			</description> +		</method> +		<method name="_commit_subgizmos" qualifiers="virtual"> +			<return type="void"> +			</return> +			<argument index="0" name="ids" type="PackedInt32Array"> +			</argument> +			<argument index="1" name="restore" type="Array"> +			</argument> +			<argument index="2" name="cancel" type="bool" default="false"> +			</argument> +			<description> +				Override this method to commit a group of subgizmos being edited (see [method _subgizmos_intersect_ray] and [method _subgizmos_intersect_frustum]). This usually means creating an [UndoRedo] action for the change, using the current transforms as "do" and the [code]restore[/code] transforms as "undo". +				If the [code]cancel[/code] argument is [code]true[/code], the [code]restore[/code] transforms should be directly set, without any [UndoRedo] action.  			</description>  		</method>  		<method name="_get_handle_name" qualifiers="virtual">  			<return type="String">  			</return> -			<argument index="0" name="index" type="int"> +			<argument index="0" name="id" type="int">  			</argument>  			<description> -				Gets the name of an edited handle (handles must have been previously added by [method add_handles]). +				Override this method to return the name of an edited handle (handles must have been previously added by [method add_handles]).  				Handles can be named for reference to the user when editing.  			</description>  		</method>  		<method name="_get_handle_value" qualifiers="virtual">  			<return type="Variant">  			</return> -			<argument index="0" name="index" type="int"> +			<argument index="0" name="id" type="int">  			</argument>  			<description> -				Gets actual value of a handle. This value can be anything and used for eventually undoing the motion when calling [method _commit_handle]. +				Override this method to return the current value of a handle. This value will be requested at the start of an edit and used as the [code]restore[/code] argument in [method _commit_handle]. +			</description> +		</method> +		<method name="_get_subgizmo_transform" qualifiers="virtual"> +			<return type="Transform3D"> +			</return> +			<argument index="0" name="id" type="int"> +			</argument> +			<description> +				Override this method to return the current transform of a subgizmo. This transform will be requested at the start of an edit and used as the [code]restore[/code] argument in [method _commit_subgizmos].  			</description>  		</method>  		<method name="_is_handle_highlighted" qualifiers="virtual">  			<return type="bool">  			</return> -			<argument index="0" name="index" type="int"> +			<argument index="0" name="id" type="int">  			</argument>  			<description> -				Returns [code]true[/code] if the handle at index [code]index[/code] is highlighted by being hovered with the mouse. +				Override this method to return [code]true[/code] whenever the given handle should be highlighted in the editor.  			</description>  		</method>  		<method name="_redraw" qualifiers="virtual">  			<return type="void">  			</return>  			<description> -				This function is called when the [Node3D] this gizmo refers to changes (the [method Node3D.update_gizmo] is called). +				Override this method to add all the gizmo elements whenever a gizmo update is requested. It's common to call [method clear] at the beginning of this method and then add visual elements depending on the node's properties.  			</description>  		</method>  		<method name="_set_handle" qualifiers="virtual">  			<return type="void">  			</return> -			<argument index="0" name="index" type="int"> +			<argument index="0" name="id" type="int">  			</argument>  			<argument index="1" name="camera" type="Camera3D">  			</argument>  			<argument index="2" name="point" type="Vector2">  			</argument>  			<description> -				This function is used when the user drags a gizmo handle (previously added with [method add_handles]) in screen coordinates. -				The [Camera3D] is also provided so screen coordinates can be converted to raycasts. +				Override this method to update the node properties when the user drags a gizmo handle (previously added with [method add_handles]). The provided [code]point[/code] is the mouse position in screen coordinates and the [code]camera[/code] can be used to convert it to raycasts. +			</description> +		</method> +		<method name="_set_subgizmo_transform" qualifiers="virtual"> +			<return type="void"> +			</return> +			<argument index="0" name="id" type="int"> +			</argument> +			<argument index="1" name="transform" type="Transform3D"> +			</argument> +			<description> +				Override this method to update the node properties during subgizmo editing (see [method _subgizmos_intersect_ray] and [method _subgizmos_intersect_frustum]). The [code]transform[/code] is given in the Node3D's local coordinate system. +			</description> +		</method> +		<method name="_subgizmos_intersect_frustum" qualifiers="virtual"> +			<return type="PackedInt32Array"> +			</return> +			<argument index="0" name="camera" type="Camera3D"> +			</argument> +			<argument index="1" name="frustum" type="Array"> +			</argument> +			<description> +				Override this method to allow selecting subgizmos using mouse drag box selection. Given a [code]camera[/code] and a [code]frustum[/code], this method should return which subgizmos are contained within the frustum. The [code]frustum[/code] argument consists of an [code]Array[/code] with all the [code]Plane[/code]s that make up the selection frustum. The returned value should contain a list of unique subgizmo identifiers, which can have any non-negative value and will be used in other virtual methods like [method _get_subgizmo_transform] or [method _commit_subgizmos]. +			</description> +		</method> +		<method name="_subgizmos_intersect_ray" qualifiers="virtual"> +			<return type="int"> +			</return> +			<argument index="0" name="camera" type="Camera3D"> +			</argument> +			<argument index="1" name="point" type="Vector2"> +			</argument> +			<description> +				Override this method to allow selecting subgizmos using mouse clicks. Given a [code]camera[/code] and a [code]point[/code] in screen coordinates, this method should return which subgizmo should be selected. The returned value should be a unique subgizmo identifier, which can have any non-negative value and will be used in other virtual methods like [method _get_subgizmo_transform] or [method _commit_subgizmos].  			</description>  		</method>  		<method name="add_collision_segments"> @@ -78,7 +133,7 @@  			<argument index="0" name="segments" type="PackedVector3Array">  			</argument>  			<description> -				Adds the specified [code]segments[/code] to the gizmo's collision shape for picking. Call this function during [method _redraw]. +				Adds the specified [code]segments[/code] to the gizmo's collision shape for picking. Call this method during [method _redraw].  			</description>  		</method>  		<method name="add_collision_triangles"> @@ -87,7 +142,7 @@  			<argument index="0" name="triangles" type="TriangleMesh">  			</argument>  			<description> -				Adds collision triangles to the gizmo for picking. A [TriangleMesh] can be generated from a regular [Mesh] too. Call this function during [method _redraw]. +				Adds collision triangles to the gizmo for picking. A [TriangleMesh] can be generated from a regular [Mesh] too. Call this method during [method _redraw].  			</description>  		</method>  		<method name="add_handles"> @@ -97,13 +152,15 @@  			</argument>  			<argument index="1" name="material" type="Material">  			</argument> -			<argument index="2" name="billboard" type="bool" default="false"> +			<argument index="2" name="ids" type="PackedInt32Array">  			</argument> -			<argument index="3" name="secondary" type="bool" default="false"> +			<argument index="3" name="billboard" type="bool" default="false"> +			</argument> +			<argument index="4" name="secondary" type="bool" default="false">  			</argument>  			<description> -				Adds a list of handles (points) which can be used to deform the object being edited. -				There are virtual functions which will be called upon editing of these handles. Call this function during [method _redraw]. +				Adds a list of handles (points) which can be used to edit the properties of the gizmo's Node3D. The [code]ids[/code] argument can be used to specify a custom identifier for each handle, if an empty [code]Array[/code] is passed, the ids will be assigned automatically from the [code]handles[/code] argument order. +				There are virtual methods which will be called upon editing of these handles. Call this method during [method _redraw].  			</description>  		</method>  		<method name="add_lines"> @@ -118,7 +175,7 @@  			<argument index="3" name="modulate" type="Color" default="Color(1, 1, 1, 1)">  			</argument>  			<description> -				Adds lines to the gizmo (as sets of 2 points), with a given material. The lines are used for visualizing the gizmo. Call this function during [method _redraw]. +				Adds lines to the gizmo (as sets of 2 points), with a given material. The lines are used for visualizing the gizmo. Call this method during [method _redraw].  			</description>  		</method>  		<method name="add_mesh"> @@ -126,14 +183,14 @@  			</return>  			<argument index="0" name="mesh" type="ArrayMesh">  			</argument> -			<argument index="1" name="billboard" type="bool" default="false"> +			<argument index="1" name="material" type="Material" default="null">  			</argument> -			<argument index="2" name="skeleton" type="SkinReference" default="null"> +			<argument index="2" name="transform" type="Transform3D" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)">  			</argument> -			<argument index="3" name="material" type="Material" default="null"> +			<argument index="3" name="skeleton" type="SkinReference" default="null">  			</argument>  			<description> -				Adds a mesh to the gizmo with the specified [code]billboard[/code] state, [code]skeleton[/code] and [code]material[/code]. If [code]billboard[/code] is [code]true[/code], the mesh will rotate to always face the camera. Call this function during [method _redraw]. +				Adds a mesh to the gizmo with the specified [code]material[/code], local [code]transform[/code] and [code]skeleton[/code]. Call this method during [method _redraw].  			</description>  		</method>  		<method name="add_unscaled_billboard"> @@ -146,7 +203,7 @@  			<argument index="2" name="modulate" type="Color" default="Color(1, 1, 1, 1)">  			</argument>  			<description> -				Adds an unscaled billboard for visualization. Call this function during [method _redraw]. +				Adds an unscaled billboard for visualization and selection. Call this method during [method _redraw].  			</description>  		</method>  		<method name="clear"> @@ -170,6 +227,22 @@  				Returns the Node3D node associated with this gizmo.  			</description>  		</method> +		<method name="get_subgizmo_selection" qualifiers="const"> +			<return type="PackedInt32Array"> +			</return> +			<description> +				Returns a list of the currently selected subgizmos. Can be used to highlight selected elements during [method _redraw]. +			</description> +		</method> +		<method name="is_subgizmo_selected" qualifiers="const"> +			<return type="bool"> +			</return> +			<argument index="0" name="arg0" type="int"> +			</argument> +			<description> +				Returns [code]true[/code] if the given subgizmo is currently selected. Can be used to highlight selected elements during [method _redraw]. +			</description> +		</method>  		<method name="set_hidden">  			<return type="void">  			</return> diff --git a/doc/classes/EditorNode3DGizmoPlugin.xml b/doc/classes/EditorNode3DGizmoPlugin.xml index 41c94cbbc6..10d6bd8bc8 100644 --- a/doc/classes/EditorNode3DGizmoPlugin.xml +++ b/doc/classes/EditorNode3DGizmoPlugin.xml @@ -14,7 +14,7 @@  			<return type="bool">  			</return>  			<description> -				Override this method to define whether the gizmo can be hidden or not. Returns [code]true[/code] if not overridden. +				Override this method to define whether the gizmos handled by this plugin can be hidden or not. Returns [code]true[/code] if not overridden.  			</description>  		</method>  		<method name="_commit_handle" qualifiers="virtual"> @@ -22,14 +22,31 @@  			</return>  			<argument index="0" name="gizmo" type="EditorNode3DGizmo">  			</argument> -			<argument index="1" name="index" type="int"> +			<argument index="1" name="id" type="int">  			</argument>  			<argument index="2" name="restore" type="Variant">  			</argument>  			<argument index="3" name="cancel" type="bool" default="false">  			</argument>  			<description> -				Override this method to commit gizmo handles. Called for this plugin's active gizmos. +				Override this method to commit a handle being edited (handles must have been previously added by [method EditorNode3DGizmo.add_handles] during [method _redraw]). This usually means creating an [UndoRedo] action for the change, using the current handle value as "do" and the [code]restore[/code] argument as "undo". +				If the [code]cancel[/code] argument is [code]true[/code], the [code]restore[/code] value should be directly set, without any [UndoRedo] action. Called for this plugin's active gizmos. +			</description> +		</method> +		<method name="_commit_subgizmos" qualifiers="virtual"> +			<return type="void"> +			</return> +			<argument index="0" name="gizmo" type="EditorNode3DGizmo"> +			</argument> +			<argument index="1" name="ids" type="PackedInt32Array"> +			</argument> +			<argument index="2" name="restore" type="Array"> +			</argument> +			<argument index="3" name="cancel" type="bool" default="false"> +			</argument> +			<description> +				Override this method to commit a group of subgizmos being edited (see [method _subgizmos_intersect_ray] and [method _subgizmos_intersect_frustum]). This usually means creating an [UndoRedo] action for the change, using the current transforms as "do" and the [code]restore[/code] transforms as "undo". +				If the [code]cancel[/code] argument is [code]true[/code], the [code]restore[/code] transforms should be directly set, without any [UndoRedo] action. Called for this plugin's active gizmos.  			</description>  		</method>  		<method name="_create_gizmo" qualifiers="virtual"> @@ -53,7 +70,7 @@  			</return>  			<argument index="0" name="gizmo" type="EditorNode3DGizmo">  			</argument> -			<argument index="1" name="index" type="int"> +			<argument index="1" name="id" type="int">  			</argument>  			<description>  				Override this method to provide gizmo's handle names. Called for this plugin's active gizmos. @@ -64,18 +81,29 @@  			</return>  			<argument index="0" name="gizmo" type="EditorNode3DGizmo">  			</argument> -			<argument index="1" name="index" type="int"> +			<argument index="1" name="id" type="int">  			</argument>  			<description> -				Gets actual value of a handle from gizmo. Called for this plugin's active gizmos. +				Override this method to return the current value of a handle. This value will be requested at the start of an edit and used as the [code]restore[/code] argument in [method _commit_handle]. Called for this plugin's active gizmos.  			</description>  		</method>  		<method name="_get_priority" qualifiers="virtual">  			<return type="int">  			</return>  			<description> -				Override this method to set the gizmo's priority. Higher values correspond to higher priority. If a gizmo with higher priority conflicts with another gizmo, only the gizmo with higher priority will be used. -				All built-in editor gizmos return a priority of [code]-1[/code]. If not overridden, this method will return [code]0[/code], which means custom gizmos will automatically override built-in gizmos. +				Override this method to set the gizmo's priority. Gizmos with higher priority will have precedence when processing inputs like handles or subgizmos selection. +				All built-in editor gizmos return a priority of [code]-1[/code]. If not overridden, this method will return [code]0[/code], which means custom gizmos will automatically get higher priority than built-in gizmos. +			</description> +		</method> +		<method name="_get_subgizmo_transform" qualifiers="virtual"> +			<return type="Transform3D"> +			</return> +			<argument index="0" name="gizmo" type="EditorNode3DGizmo"> +			</argument> +			<argument index="1" name="id" type="int"> +			</argument> +			<description> +				Override this method to return the current transform of a subgizmo. This transform will be requested at the start of an edit and used in the [code]restore[/code] argument in [method _commit_subgizmos]. Called for this plugin's active gizmos.  			</description>  		</method>  		<method name="_has_gizmo" qualifiers="virtual"> @@ -92,10 +120,10 @@  			</return>  			<argument index="0" name="gizmo" type="EditorNode3DGizmo">  			</argument> -			<argument index="1" name="index" type="int"> +			<argument index="1" name="id" type="int">  			</argument>  			<description> -				Gets whether a handle is highlighted or not. Called for this plugin's active gizmos. +				Override this method to return [code]true[/code] whenever to given handle should be highlighted in the editor. Called for this plugin's active gizmos.  			</description>  		</method>  		<method name="_is_selectable_when_hidden" qualifiers="virtual"> @@ -111,7 +139,7 @@  			<argument index="0" name="gizmo" type="EditorNode3DGizmo">  			</argument>  			<description> -				Callback to redraw the provided gizmo. Called for this plugin's active gizmos. +				Override this method to add all the gizmo elements whenever a gizmo update is requested. It's common to call [method EditorNode3DGizmo.clear] at the beginning of this method and then add visual elements depending on the node's properties.  			</description>  		</method>  		<method name="_set_handle" qualifiers="virtual"> @@ -119,14 +147,53 @@  			</return>  			<argument index="0" name="gizmo" type="EditorNode3DGizmo">  			</argument> -			<argument index="1" name="index" type="int"> +			<argument index="1" name="id" type="int">  			</argument>  			<argument index="2" name="camera" type="Camera3D">  			</argument>  			<argument index="3" name="point" type="Vector2">  			</argument>  			<description> -				Update the value of a handle after it has been updated. Called for this plugin's active gizmos. +				Override this method to update the node's properties when the user drags a gizmo handle (previously added with [method EditorNode3DGizmo.add_handles]). The provided [code]point[/code] is the mouse position in screen coordinates and the [code]camera[/code] can be used to convert it to raycasts. Called for this plugin's active gizmos. +			</description> +		</method> +		<method name="_set_subgizmo_transform" qualifiers="virtual"> +			<return type="void"> +			</return> +			<argument index="0" name="gizmo" type="EditorNode3DGizmo"> +			</argument> +			<argument index="1" name="id" type="int"> +			</argument> +			<argument index="2" name="transform" type="Transform3D"> +			</argument> +			<description> +				Override this method to update the node properties during subgizmo editing (see [method _subgizmos_intersect_ray] and [method _subgizmos_intersect_frustum]). The [code]transform[/code] is given in the Node3D's local coordinate system.  Called for this plugin's active gizmos. +			</description> +		</method> +		<method name="_subgizmos_intersect_frustum" qualifiers="virtual"> +			<return type="PackedInt32Array"> +			</return> +			<argument index="0" name="gizmo" type="EditorNode3DGizmo"> +			</argument> +			<argument index="1" name="camera" type="Camera3D"> +			</argument> +			<argument index="2" name="frustum" type="Array"> +			</argument> +			<description> +				Override this method to allow selecting subgizmos using mouse drag box selection. Given a [code]camera[/code] and a [code]frustum[/code], this method should return which subgizmos are contained within the frustum. The [code]frustum[/code] argument consists of an [code]Array[/code] with all the [code]Plane[/code]s that make up the selection frustum. The returned value should contain a list of unique subgizmo identifiers, these identifiers can have any non-negative value and will be used in other virtual methods like [method _get_subgizmo_transform] or [method _commit_subgizmos].  Called for this plugin's active gizmos. +			</description> +		</method> +		<method name="_subgizmos_intersect_ray" qualifiers="virtual"> +			<return type="int"> +			</return> +			<argument index="0" name="gizmo" type="EditorNode3DGizmo"> +			</argument> +			<argument index="1" name="camera" type="Camera3D"> +			</argument> +			<argument index="2" name="point" type="Vector2"> +			</argument> +			<description> +				Override this method to allow selecting subgizmos using mouse clicks. Given a [code]camera[/code] and a [code]point[/code] in screen coordinates, this method should return which subgizmo should be selected. The returned value should be a unique subgizmo identifier, which can have any non-negative value and will be used in other virtual methods like [method _get_subgizmo_transform] or [method _commit_subgizmos]. Called for this plugin's active gizmos.  			</description>  		</method>  		<method name="add_material"> diff --git a/doc/classes/Node3D.xml b/doc/classes/Node3D.xml index 14e03a2186..9c05ed1ef6 100644 --- a/doc/classes/Node3D.xml +++ b/doc/classes/Node3D.xml @@ -13,6 +13,29 @@  		<link title="All 3D Demos">https://github.com/godotengine/godot-demo-projects/tree/master/3d</link>  	</tutorials>  	<methods> +		<method name="add_gizmo"> +			<return type="void"> +			</return> +			<argument index="0" name="gizmo" type="Node3DGizmo"> +			</argument> +			<description> +				Attach a gizmo to this [code]Node3D[/code]. +			</description> +		</method> +		<method name="clear_gizmos"> +			<return type="void"> +			</return> +			<description> +				Clear all gizmos attached to this [code]Node3D[/code]. +			</description> +		</method> +		<method name="clear_subgizmo_selection"> +			<return type="void"> +			</return> +			<description> +				Clears subgizmo selection for this node in the editor. Useful when subgizmo IDs become invalid after a property change. +			</description> +		</method>  		<method name="force_update_transform">  			<return type="void">  			</return> @@ -20,6 +43,13 @@  				Forces the transform to update. Transform changes in physics are not instant for performance reasons. Transforms are accumulated and then set. Use this if you need an up-to-date transform when doing physics operations.  			</description>  		</method> +		<method name="get_gizmos" qualifiers="const"> +			<return type="Array"> +			</return> +			<description> +				Returns all the gizmos attached to this [code]Node3D[/code]. +			</description> +		</method>  		<method name="get_parent_node_3d" qualifiers="const">  			<return type="Node3D">  			</return> @@ -276,18 +306,15 @@  				Changes the node's position by the given offset [Vector3] in local space.  			</description>  		</method> -		<method name="update_gizmo"> +		<method name="update_gizmos">  			<return type="void">  			</return>  			<description> -				Updates the [Node3DGizmo] of this node. +				Updates all the [Node3DGizmo]s attached to this node.  			</description>  		</method>  	</methods>  	<members> -		<member name="gizmo" type="Node3DGizmo" setter="set_gizmo" getter="get_gizmo"> -			The [Node3DGizmo] for this node. Used for example in [EditorNode3DGizmo] as custom visualization and editing handles in Editor. -		</member>  		<member name="global_transform" type="Transform3D" setter="set_global_transform" getter="get_global_transform">  			World3D space (global) [Transform3D] of this node.  		</member> @@ -324,7 +351,7 @@  	<constants>  		<constant name="NOTIFICATION_TRANSFORM_CHANGED" value="2000">  			Node3D nodes receives this notification when their global transform changes. This means that either the current or a parent node changed its transform. -			In order for [constant NOTIFICATION_TRANSFORM_CHANGED] to work, users first need to ask for it, with [method set_notify_transform]. The notification is also sent if the node is in the editor context and it has a valid gizmo. +			In order for [constant NOTIFICATION_TRANSFORM_CHANGED] to work, users first need to ask for it, with [method set_notify_transform]. The notification is also sent if the node is in the editor context and it has at least one valid gizmo.  		</constant>  		<constant name="NOTIFICATION_ENTER_WORLD" value="41">  			Node3D nodes receives this notification when they are registered to new [World3D] resource. diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 74fb38b66b..492ba51b8a 100644 --- a/editor/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -33,7 +33,9 @@  #include "core/math/convex_hull.h"  #include "core/math/geometry_2d.h"  #include "core/math/geometry_3d.h" +#include "editor/plugins/node_3d_editor_plugin.h"  #include "scene/3d/audio_stream_player_3d.h" +#include "scene/3d/camera_3d.h"  #include "scene/3d/collision_polygon_3d.h"  #include "scene/3d/collision_shape_3d.h"  #include "scene/3d/cpu_particles_3d.h" @@ -106,58 +108,122 @@ void EditorNode3DGizmo::clear() {  void EditorNode3DGizmo::redraw() {  	if (get_script_instance() && get_script_instance()->has_method("_redraw")) {  		get_script_instance()->call("_redraw"); -		return; +	} else { +		ERR_FAIL_COND(!gizmo_plugin); +		gizmo_plugin->redraw(this);  	} -	ERR_FAIL_COND(!gizmo_plugin); -	gizmo_plugin->redraw(this); +	if (Node3DEditor::get_singleton()->is_current_selected_gizmo(this)) { +		Node3DEditor::get_singleton()->update_transform_gizmo(); +	}  } -String EditorNode3DGizmo::get_handle_name(int p_idx) const { +String EditorNode3DGizmo::get_handle_name(int p_id) const {  	if (get_script_instance() && get_script_instance()->has_method("_get_handle_name")) { -		return get_script_instance()->call("_get_handle_name", p_idx); +		return get_script_instance()->call("_get_handle_name", p_id);  	}  	ERR_FAIL_COND_V(!gizmo_plugin, ""); -	return gizmo_plugin->get_handle_name(this, p_idx); +	return gizmo_plugin->get_handle_name(this, p_id);  } -bool EditorNode3DGizmo::is_handle_highlighted(int p_idx) const { +bool EditorNode3DGizmo::is_handle_highlighted(int p_id) const {  	if (get_script_instance() && get_script_instance()->has_method("_is_handle_highlighted")) { -		return get_script_instance()->call("_is_handle_highlighted", p_idx); +		return get_script_instance()->call("_is_handle_highlighted", p_id);  	}  	ERR_FAIL_COND_V(!gizmo_plugin, false); -	return gizmo_plugin->is_handle_highlighted(this, p_idx); +	return gizmo_plugin->is_handle_highlighted(this, p_id);  } -Variant EditorNode3DGizmo::get_handle_value(int p_idx) { +Variant EditorNode3DGizmo::get_handle_value(int p_id) const {  	if (get_script_instance() && get_script_instance()->has_method("_get_handle_value")) { -		return get_script_instance()->call("_get_handle_value", p_idx); +		return get_script_instance()->call("_get_handle_value", p_id);  	}  	ERR_FAIL_COND_V(!gizmo_plugin, Variant()); -	return gizmo_plugin->get_handle_value(this, p_idx); +	return gizmo_plugin->get_handle_value(this, p_id);  } -void EditorNode3DGizmo::set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void EditorNode3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	if (get_script_instance() && get_script_instance()->has_method("_set_handle")) { -		get_script_instance()->call("_set_handle", p_idx, p_camera, p_point); +		get_script_instance()->call("_set_handle", p_id, p_camera, p_point);  		return;  	}  	ERR_FAIL_COND(!gizmo_plugin); -	gizmo_plugin->set_handle(this, p_idx, p_camera, p_point); +	gizmo_plugin->set_handle(this, p_id, p_camera, p_point);  } -void EditorNode3DGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { +void EditorNode3DGizmo::commit_handle(int p_id, const Variant &p_restore, bool p_cancel) const {  	if (get_script_instance() && get_script_instance()->has_method("_commit_handle")) { -		get_script_instance()->call("_commit_handle", p_idx, p_restore, p_cancel); +		get_script_instance()->call("_commit_handle", p_id, p_restore, p_cancel); +		return; +	} + +	ERR_FAIL_COND(!gizmo_plugin); +	gizmo_plugin->commit_handle(this, p_id, p_restore, p_cancel); +} + +int EditorNode3DGizmo::subgizmos_intersect_ray(Camera3D *p_camera, const Vector2 &p_point) const { +	if (get_script_instance() && get_script_instance()->has_method("_subgizmos_intersect_ray")) { +		return get_script_instance()->call("_subgizmos_intersect_ray", p_camera, p_point); +	} + +	ERR_FAIL_COND_V(!gizmo_plugin, -1); +	return gizmo_plugin->subgizmos_intersect_ray(this, p_camera, p_point); +} + +Vector<int> EditorNode3DGizmo::subgizmos_intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum) const { +	if (get_script_instance() && get_script_instance()->has_method("_subgizmos_intersect_frustum")) { +		Array frustum; +		for (int i = 0; i < p_frustum.size(); i++) { +			frustum[i] = p_frustum[i]; +		} +		return get_script_instance()->call("_subgizmos_intersect_frustum", p_camera, frustum); +	} + +	ERR_FAIL_COND_V(!gizmo_plugin, Vector<int>()); +	return gizmo_plugin->subgizmos_intersect_frustum(this, p_camera, p_frustum); +} + +Transform3D EditorNode3DGizmo::get_subgizmo_transform(int p_id) const { +	if (get_script_instance() && get_script_instance()->has_method("_get_subgizmo_transform")) { +		return get_script_instance()->call("_get_subgizmo_transform", p_id); +	} + +	ERR_FAIL_COND_V(!gizmo_plugin, Transform3D()); +	return gizmo_plugin->get_subgizmo_transform(this, p_id); +} + +void EditorNode3DGizmo::set_subgizmo_transform(int p_id, Transform3D p_transform) const { +	if (get_script_instance() && get_script_instance()->has_method("_set_subgizmo_transform")) { +		get_script_instance()->call("_set_subgizmo_transform", p_id, p_transform);  		return;  	}  	ERR_FAIL_COND(!gizmo_plugin); -	gizmo_plugin->commit_handle(this, p_idx, p_restore, p_cancel); +	gizmo_plugin->set_subgizmo_transform(this, p_id, p_transform); +} + +void EditorNode3DGizmo::commit_subgizmos(const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) const { +	if (get_script_instance() && get_script_instance()->has_method("_commit_subgizmos")) { +		Array ids; +		for (int i = 0; i < p_ids.size(); i++) { +			ids[i] = p_ids[i]; +		} + +		Array restore; +		for (int i = 0; i < p_restore.size(); i++) { +			restore[i] = p_restore[i]; +		} + +		get_script_instance()->call("_commit_subgizmos", ids, restore, p_cancel); +		return; +	} + +	ERR_FAIL_COND(!gizmo_plugin); +	gizmo_plugin->commit_subgizmos(this, p_ids, p_restore, p_cancel);  }  void EditorNode3DGizmo::set_spatial_node(Node3D *p_node) { @@ -180,17 +246,17 @@ void EditorNode3DGizmo::Instance::create_instance(Node3D *p_base, bool p_hidden)  	RS::get_singleton()->instance_geometry_set_flag(instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);  } -void EditorNode3DGizmo::add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard, const Ref<SkinReference> &p_skin_reference, const Ref<Material> &p_material) { +void EditorNode3DGizmo::add_mesh(const Ref<ArrayMesh> &p_mesh, const Ref<Material> &p_material, const Transform3D &p_xform, const Ref<SkinReference> &p_skin_reference) {  	ERR_FAIL_COND(!spatial_node);  	Instance ins; -	ins.billboard = p_billboard;  	ins.mesh = p_mesh;  	ins.skin_reference = p_skin_reference;  	ins.material = p_material; +	ins.xform = p_xform;  	if (valid) {  		ins.create_instance(spatial_node, hidden); -		RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); +		RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform() * ins.xform);  		if (ins.material.is_valid()) {  			RS::get_singleton()->instance_geometry_set_material_override(ins.instance, p_material->get_rid());  		} @@ -245,7 +311,6 @@ void EditorNode3DGizmo::add_vertices(const Vector<Vector3> &p_vertices, const Re  		}  	} -	ins.billboard = p_billboard;  	ins.mesh = mesh;  	if (valid) {  		ins.create_instance(spatial_node, hidden); @@ -307,8 +372,6 @@ void EditorNode3DGizmo::add_unscaled_billboard(const Ref<Material> &p_material,  	mesh->set_custom_aabb(AABB(Vector3(-selectable_icon_size, -selectable_icon_size, -selectable_icon_size) * 100.0f, Vector3(selectable_icon_size, selectable_icon_size, selectable_icon_size) * 200.0f));  	ins.mesh = mesh; -	ins.unscaled = true; -	ins.billboard = true;  	if (valid) {  		ins.create_instance(spatial_node, hidden);  		RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); @@ -331,7 +394,7 @@ void EditorNode3DGizmo::add_collision_segments(const Vector<Vector3> &p_lines) {  	}  } -void EditorNode3DGizmo::add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, bool p_billboard, bool p_secondary) { +void EditorNode3DGizmo::add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, const Vector<int> &p_ids, bool p_billboard, bool p_secondary) {  	billboard_handle = p_billboard;  	if (!is_selected() || !is_editable()) { @@ -340,8 +403,16 @@ void EditorNode3DGizmo::add_handles(const Vector<Vector3> &p_handles, const Ref<  	ERR_FAIL_COND(!spatial_node); -	Instance ins; +	if (p_ids.is_empty()) { +		ERR_FAIL_COND_MSG((!handles.is_empty() && !handle_ids.is_empty()) || (!secondary_handles.is_empty() && !secondary_handle_ids.is_empty()), "Fail"); +	} else { +		ERR_FAIL_COND_MSG(handles.size() != handle_ids.size() || secondary_handles.size() != secondary_handle_ids.size(), "Fail"); +	} +	bool is_current_hover_gizmo = Node3DEditor::get_singleton()->get_current_hover_gizmo() == this; +	int current_hover_handle = Node3DEditor::get_singleton()->get_current_hover_gizmo_handle(); + +	Instance ins;  	Ref<ArrayMesh> mesh = memnew(ArrayMesh);  	Array a; @@ -357,7 +428,8 @@ void EditorNode3DGizmo::add_handles(const Vector<Vector3> &p_handles, const Ref<  				col = Color(0, 0, 1, 0.9);  			} -			if (Node3DEditor::get_singleton()->get_over_gizmo_handle() != i) { +			int id = p_ids.is_empty() ? i : p_ids[i]; +			if (!is_current_hover_gizmo || current_hover_handle != id) {  				col.a = 0.8;  			} @@ -379,29 +451,31 @@ void EditorNode3DGizmo::add_handles(const Vector<Vector3> &p_handles, const Ref<  	}  	ins.mesh = mesh; -	ins.billboard = p_billboard;  	ins.extra_margin = true;  	if (valid) {  		ins.create_instance(spatial_node, hidden);  		RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform());  	}  	instances.push_back(ins); -	if (!p_secondary) { -		int chs = handles.size(); -		handles.resize(chs + p_handles.size()); -		for (int i = 0; i < p_handles.size(); i++) { -			handles.write[i + chs] = p_handles[i]; -		} -	} else { -		int chs = secondary_handles.size(); -		secondary_handles.resize(chs + p_handles.size()); -		for (int i = 0; i < p_handles.size(); i++) { -			secondary_handles.write[i + chs] = p_handles[i]; + +	Vector<Vector3> &h = p_secondary ? secondary_handles : handles; +	int current_size = h.size(); +	h.resize(current_size + p_handles.size()); +	for (int i = 0; i < p_handles.size(); i++) { +		h.write[current_size + i] = p_handles[i]; +	} + +	if (!p_ids.is_empty()) { +		Vector<int> &ids = p_secondary ? secondary_handle_ids : handle_ids; +		current_size = ids.size(); +		ids.resize(current_size + p_ids.size()); +		for (int i = 0; i < p_ids.size(); i++) { +			ids.write[current_size + i] = p_ids[i];  		}  	}  } -void EditorNode3DGizmo::add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position) { +void EditorNode3DGizmo::add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position, const Transform3D &p_xform) {  	ERR_FAIL_COND(!spatial_node);  	BoxMesh box_mesh; @@ -419,8 +493,7 @@ void EditorNode3DGizmo::add_solid_box(Ref<Material> &p_material, Vector3 p_size,  	Ref<ArrayMesh> m = memnew(ArrayMesh);  	m->add_surface_from_arrays(box_mesh.surface_get_primitive_type(0), arrays); -	m->surface_set_material(0, p_material); -	add_mesh(m); +	add_mesh(m, p_material, p_xform);  }  bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum) { @@ -485,13 +558,15 @@ bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector  		Transform3D it = t.affine_inverse();  		Vector<Plane> transformed_frustum; +		int plane_count = p_frustum.size(); +		transformed_frustum.resize(plane_count); -		for (int i = 0; i < p_frustum.size(); i++) { -			transformed_frustum.push_back(it.xform(p_frustum[i])); +		for (int i = 0; i < plane_count; i++) { +			transformed_frustum.write[i] = it.xform(p_frustum[i]);  		} -		Vector<Vector3> convex_points = Geometry3D::compute_convex_mesh_points(p_frustum.ptr(), p_frustum.size()); -		if (collision_mesh->inside_convex_shape(transformed_frustum.ptr(), transformed_frustum.size(), convex_points.ptr(), convex_points.size(), mesh_scale)) { +		Vector<Vector3> convex_points = Geometry3D::compute_convex_mesh_points(transformed_frustum.ptr(), plane_count); +		if (collision_mesh->inside_convex_shape(transformed_frustum.ptr(), plane_count, convex_points.ptr(), convex_points.size(), mesh_scale)) {  			return true;  		}  	} @@ -499,64 +574,71 @@ bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector  	return false;  } -bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle, bool p_sec_first) { -	ERR_FAIL_COND_V(!spatial_node, false); -	ERR_FAIL_COND_V(!valid, false); +void EditorNode3DGizmo::handles_intersect_ray(Camera3D *p_camera, const Vector2 &p_point, bool p_shift_pressed, int &r_id) { +	r_id = -1; -	if (hidden && !gizmo_plugin->is_selectable_when_hidden()) { -		return false; +	ERR_FAIL_COND(!spatial_node); +	ERR_FAIL_COND(!valid); + +	if (hidden) { +		return;  	} -	if (r_gizmo_handle && !hidden) { -		Transform3D t = spatial_node->get_global_transform(); -		if (billboard_handle) { -			t.set_look_at(t.origin, t.origin - p_camera->get_transform().basis.get_axis(2), p_camera->get_transform().basis.get_axis(1)); -		} +	Transform3D camera_xform = p_camera->get_global_transform(); +	Transform3D t = spatial_node->get_global_transform(); +	if (billboard_handle) { +		t.set_look_at(t.origin, t.origin - camera_xform.basis.get_axis(2), camera_xform.basis.get_axis(1)); +	} -		float min_d = 1e20; -		int idx = -1; +	float min_d = 1e20; -		for (int i = 0; i < secondary_handles.size(); i++) { -			Vector3 hpos = t.xform(secondary_handles[i]); -			Vector2 p = p_camera->unproject_position(hpos); +	for (int i = 0; i < secondary_handles.size(); i++) { +		Vector3 hpos = t.xform(secondary_handles[i]); +		Vector2 p = p_camera->unproject_position(hpos); -			if (p.distance_to(p_point) < HANDLE_HALF_SIZE) { -				real_t dp = p_camera->get_transform().origin.distance_to(hpos); -				if (dp < min_d) { -					r_pos = t.xform(hpos); -					r_normal = p_camera->get_transform().basis.get_axis(2); -					min_d = dp; -					idx = i + handles.size(); +		if (p.distance_to(p_point) < HANDLE_HALF_SIZE) { +			real_t dp = p_camera->get_transform().origin.distance_to(hpos); +			if (dp < min_d) { +				min_d = dp; +				if (secondary_handle_ids.is_empty()) { +					r_id = i; +				} else { +					r_id = secondary_handle_ids[i];  				}  			}  		} +	} -		if (p_sec_first && idx != -1) { -			*r_gizmo_handle = idx; -			return true; -		} +	if (r_id != -1 && p_shift_pressed) { +		return; +	} -		min_d = 1e20; +	min_d = 1e20; -		for (int i = 0; i < handles.size(); i++) { -			Vector3 hpos = t.xform(handles[i]); -			Vector2 p = p_camera->unproject_position(hpos); +	for (int i = 0; i < handles.size(); i++) { +		Vector3 hpos = t.xform(handles[i]); +		Vector2 p = p_camera->unproject_position(hpos); -			if (p.distance_to(p_point) < HANDLE_HALF_SIZE) { -				real_t dp = p_camera->get_transform().origin.distance_to(hpos); -				if (dp < min_d) { -					r_pos = t.xform(hpos); -					r_normal = p_camera->get_transform().basis.get_axis(2); -					min_d = dp; -					idx = i; +		if (p.distance_to(p_point) < HANDLE_HALF_SIZE) { +			real_t dp = p_camera->get_transform().origin.distance_to(hpos); +			if (dp < min_d) { +				min_d = dp; +				if (handle_ids.is_empty()) { +					r_id = i; +				} else { +					r_id = handle_ids[i];  				}  			}  		} +	} +} -		if (idx >= 0) { -			*r_gizmo_handle = idx; -			return true; -		} +bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal) { +	ERR_FAIL_COND_V(!spatial_node, false); +	ERR_FAIL_COND_V(!valid, false); + +	if (hidden && !gizmo_plugin->is_selectable_when_hidden()) { +		return false;  	}  	if (selectable_icon_size > 0.0f) { @@ -674,6 +756,25 @@ bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point,  	return false;  } +bool EditorNode3DGizmo::is_subgizmo_selected(int p_id) const { +	Node3DEditor *ed = Node3DEditor::get_singleton(); +	ERR_FAIL_COND_V(!ed, false); +	return ed->is_current_selected_gizmo(this) && ed->is_subgizmo_selected(p_id); +} + +Vector<int> EditorNode3DGizmo::get_subgizmo_selection() const { +	Vector<int> ret; + +	Node3DEditor *ed = Node3DEditor::get_singleton(); +	ERR_FAIL_COND_V(!ed, ret); + +	if (ed->is_current_selected_gizmo(this)) { +		ret = ed->get_subgizmo_selection(); +	} + +	return ret; +} +  void EditorNode3DGizmo::create() {  	ERR_FAIL_COND(!spatial_node);  	ERR_FAIL_COND(valid); @@ -690,7 +791,7 @@ void EditorNode3DGizmo::transform() {  	ERR_FAIL_COND(!spatial_node);  	ERR_FAIL_COND(!valid);  	for (int i = 0; i < instances.size(); i++) { -		RS::get_singleton()->instance_set_transform(instances[i].instance, spatial_node->get_global_transform()); +		RS::get_singleton()->instance_set_transform(instances[i].instance, spatial_node->get_global_transform() * instances[i].xform);  	}  } @@ -724,38 +825,46 @@ void EditorNode3DGizmo::set_plugin(EditorNode3DGizmoPlugin *p_plugin) {  void EditorNode3DGizmo::_bind_methods() {  	ClassDB::bind_method(D_METHOD("add_lines", "lines", "material", "billboard", "modulate"), &EditorNode3DGizmo::add_lines, DEFVAL(false), DEFVAL(Color(1, 1, 1))); -	ClassDB::bind_method(D_METHOD("add_mesh", "mesh", "billboard", "skeleton", "material"), &EditorNode3DGizmo::add_mesh, DEFVAL(false), DEFVAL(Ref<SkinReference>()), DEFVAL(Variant())); +	ClassDB::bind_method(D_METHOD("add_mesh", "mesh", "material", "transform", "skeleton"), &EditorNode3DGizmo::add_mesh, DEFVAL(Variant()), DEFVAL(Transform3D()), DEFVAL(Ref<SkinReference>()));  	ClassDB::bind_method(D_METHOD("add_collision_segments", "segments"), &EditorNode3DGizmo::add_collision_segments);  	ClassDB::bind_method(D_METHOD("add_collision_triangles", "triangles"), &EditorNode3DGizmo::add_collision_triangles);  	ClassDB::bind_method(D_METHOD("add_unscaled_billboard", "material", "default_scale", "modulate"), &EditorNode3DGizmo::add_unscaled_billboard, DEFVAL(1), DEFVAL(Color(1, 1, 1))); -	ClassDB::bind_method(D_METHOD("add_handles", "handles", "material", "billboard", "secondary"), &EditorNode3DGizmo::add_handles, DEFVAL(false), DEFVAL(false)); +	ClassDB::bind_method(D_METHOD("add_handles", "handles", "material", "ids", "billboard", "secondary"), &EditorNode3DGizmo::add_handles, DEFVAL(false), DEFVAL(false));  	ClassDB::bind_method(D_METHOD("set_spatial_node", "node"), &EditorNode3DGizmo::_set_spatial_node);  	ClassDB::bind_method(D_METHOD("get_spatial_node"), &EditorNode3DGizmo::get_spatial_node);  	ClassDB::bind_method(D_METHOD("get_plugin"), &EditorNode3DGizmo::get_plugin);  	ClassDB::bind_method(D_METHOD("clear"), &EditorNode3DGizmo::clear);  	ClassDB::bind_method(D_METHOD("set_hidden", "hidden"), &EditorNode3DGizmo::set_hidden); +	ClassDB::bind_method(D_METHOD("is_subgizmo_selected"), &EditorNode3DGizmo::is_subgizmo_selected); +	ClassDB::bind_method(D_METHOD("get_subgizmo_selection"), &EditorNode3DGizmo::get_subgizmo_selection);  	BIND_VMETHOD(MethodInfo("_redraw")); -	BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_handle_name", PropertyInfo(Variant::INT, "index"))); -	BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_handle_highlighted", PropertyInfo(Variant::INT, "index"))); +	BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_handle_name", PropertyInfo(Variant::INT, "id"))); +	BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_handle_highlighted", PropertyInfo(Variant::INT, "id"))); -	MethodInfo hvget(Variant::NIL, "_get_handle_value", PropertyInfo(Variant::INT, "index")); +	MethodInfo hvget(Variant::NIL, "_get_handle_value", PropertyInfo(Variant::INT, "id"));  	hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;  	BIND_VMETHOD(hvget); -	BIND_VMETHOD(MethodInfo("_set_handle", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); -	MethodInfo cm = MethodInfo("_commit_handle", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel")); +	BIND_VMETHOD(MethodInfo("_set_handle", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); +	MethodInfo cm = MethodInfo("_commit_handle", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel"));  	cm.default_arguments.push_back(false);  	BIND_VMETHOD(cm); + +	BIND_VMETHOD(MethodInfo(Variant::INT, "_subgizmos_intersect_ray", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); +	BIND_VMETHOD(MethodInfo(Variant::PACKED_INT32_ARRAY, "_subgizmos_intersect_frustum", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::ARRAY, "frustum"))); +	BIND_VMETHOD(MethodInfo(Variant::TRANSFORM3D, "_get_subgizmo_transform", PropertyInfo(Variant::INT, "id"))); +	BIND_VMETHOD(MethodInfo("_set_subgizmo_transform", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::TRANSFORM3D, "transform"))); +	MethodInfo cs = MethodInfo("_commit_subgizmos", PropertyInfo(Variant::PACKED_INT32_ARRAY, "ids"), PropertyInfo(Variant::ARRAY, "restore"), PropertyInfo(Variant::BOOL, "cancel")); +	cs.default_arguments.push_back(false); +	BIND_VMETHOD(cs);  }  EditorNode3DGizmo::EditorNode3DGizmo() {  	valid = false;  	billboard_handle = false;  	hidden = false; -	base = nullptr;  	selected = false; -	instantiated = false;  	spatial_node = nullptr;  	gizmo_plugin = nullptr;  	selectable_icon_size = -1.0f; @@ -768,10 +877,362 @@ EditorNode3DGizmo::~EditorNode3DGizmo() {  	clear();  } -Vector3 EditorNode3DGizmo::get_handle_pos(int p_idx) const { -	ERR_FAIL_INDEX_V(p_idx, handles.size(), Vector3()); +///// + +void EditorNode3DGizmoPlugin::create_material(const String &p_name, const Color &p_color, bool p_billboard, bool p_on_top, bool p_use_vertex_color) { +	Color instantiated_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instantiated", Color(0.7, 0.7, 0.7, 0.6)); + +	Vector<Ref<StandardMaterial3D>> mats; + +	for (int i = 0; i < 4; i++) { +		bool selected = i % 2 == 1; +		bool instantiated = i < 2; + +		Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + +		Color color = instantiated ? instantiated_color : p_color; + +		if (!selected) { +			color.a *= 0.3; +		} + +		material->set_albedo(color); +		material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); +		material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); +		material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1); +		material->set_cull_mode(StandardMaterial3D::CULL_DISABLED); + +		if (p_use_vertex_color) { +			material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); +			material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); +		} + +		if (p_billboard) { +			material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); +		} + +		if (p_on_top && selected) { +			material->set_on_top_of_alpha(); +		} + +		mats.push_back(material); +	} + +	materials[p_name] = mats; +} + +void EditorNode3DGizmoPlugin::create_icon_material(const String &p_name, const Ref<Texture2D> &p_texture, bool p_on_top, const Color &p_albedo) { +	Color instantiated_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instantiated", Color(0.7, 0.7, 0.7, 0.6)); + +	Vector<Ref<StandardMaterial3D>> icons; + +	for (int i = 0; i < 4; i++) { +		bool selected = i % 2 == 1; +		bool instantiated = i < 2; + +		Ref<StandardMaterial3D> icon = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + +		Color color = instantiated ? instantiated_color : p_albedo; + +		if (!selected) { +			color.a *= 0.85; +		} + +		icon->set_albedo(color); + +		icon->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); +		icon->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); +		icon->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); +		icon->set_cull_mode(StandardMaterial3D::CULL_DISABLED); +		icon->set_depth_draw_mode(StandardMaterial3D::DEPTH_DRAW_DISABLED); +		icon->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); +		icon->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, p_texture); +		icon->set_flag(StandardMaterial3D::FLAG_FIXED_SIZE, true); +		icon->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); +		icon->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN); + +		if (p_on_top && selected) { +			icon->set_on_top_of_alpha(); +		} + +		icons.push_back(icon); +	} + +	materials[p_name] = icons; +} + +void EditorNode3DGizmoPlugin::create_handle_material(const String &p_name, bool p_billboard, const Ref<Texture2D> &p_icon) { +	Ref<StandardMaterial3D> handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + +	handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); +	handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); +	Ref<Texture2D> handle_t = p_icon != nullptr ? p_icon : Node3DEditor::get_singleton()->get_theme_icon(SNAME("Editor3DHandle"), SNAME("EditorIcons")); +	handle_material->set_point_size(handle_t->get_width()); +	handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle_t); +	handle_material->set_albedo(Color(1, 1, 1)); +	handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); +	handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); +	handle_material->set_on_top_of_alpha(); +	if (p_billboard) { +		handle_material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); +		handle_material->set_on_top_of_alpha(); +	} +	handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + +	materials[p_name] = Vector<Ref<StandardMaterial3D>>(); +	materials[p_name].push_back(handle_material); +} + +void EditorNode3DGizmoPlugin::add_material(const String &p_name, Ref<StandardMaterial3D> p_material) { +	materials[p_name] = Vector<Ref<StandardMaterial3D>>(); +	materials[p_name].push_back(p_material); +} + +Ref<StandardMaterial3D> EditorNode3DGizmoPlugin::get_material(const String &p_name, const Ref<EditorNode3DGizmo> &p_gizmo) { +	ERR_FAIL_COND_V(!materials.has(p_name), Ref<StandardMaterial3D>()); +	ERR_FAIL_COND_V(materials[p_name].size() == 0, Ref<StandardMaterial3D>()); + +	if (p_gizmo.is_null() || materials[p_name].size() == 1) { +		return materials[p_name][0]; +	} + +	int index = (p_gizmo->is_selected() ? 1 : 0) + (p_gizmo->is_editable() ? 2 : 0); + +	Ref<StandardMaterial3D> mat = materials[p_name][index]; + +	if (current_state == ON_TOP && p_gizmo->is_selected()) { +		mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); +	} else { +		mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, false); +	} + +	return mat; +} + +String EditorNode3DGizmoPlugin::get_gizmo_name() const { +	if (get_script_instance() && get_script_instance()->has_method("_get_gizmo_name")) { +		return get_script_instance()->call("_get_gizmo_name"); +	} +	return TTR("Nameless gizmo"); +} + +int EditorNode3DGizmoPlugin::get_priority() const { +	if (get_script_instance() && get_script_instance()->has_method("_get_priority")) { +		return get_script_instance()->call("_get_priority"); +	} +	return 0; +} + +Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::get_gizmo(Node3D *p_spatial) { +	if (get_script_instance() && get_script_instance()->has_method("_get_gizmo")) { +		return get_script_instance()->call("_get_gizmo", p_spatial); +	} + +	Ref<EditorNode3DGizmo> ref = create_gizmo(p_spatial); + +	if (ref.is_null()) { +		return ref; +	} + +	ref->set_plugin(this); +	ref->set_spatial_node(p_spatial); +	ref->set_hidden(current_state == HIDDEN); -	return handles[p_idx]; +	current_gizmos.push_back(ref.ptr()); +	return ref; +} + +void EditorNode3DGizmoPlugin::_bind_methods() { +#define GIZMO_REF PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "EditorNode3DGizmo") + +	BIND_VMETHOD(MethodInfo(Variant::BOOL, "_has_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); +	BIND_VMETHOD(MethodInfo(GIZMO_REF, "_create_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); + +	ClassDB::bind_method(D_METHOD("create_material", "name", "color", "billboard", "on_top", "use_vertex_color"), &EditorNode3DGizmoPlugin::create_material, DEFVAL(false), DEFVAL(false), DEFVAL(false)); +	ClassDB::bind_method(D_METHOD("create_icon_material", "name", "texture", "on_top", "color"), &EditorNode3DGizmoPlugin::create_icon_material, DEFVAL(false), DEFVAL(Color(1, 1, 1, 1))); +	ClassDB::bind_method(D_METHOD("create_handle_material", "name", "billboard", "texture"), &EditorNode3DGizmoPlugin::create_handle_material, DEFVAL(false), DEFVAL(Variant())); +	ClassDB::bind_method(D_METHOD("add_material", "name", "material"), &EditorNode3DGizmoPlugin::add_material); + +	ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorNode3DGizmoPlugin::get_material, DEFVAL(Ref<EditorNode3DGizmo>())); + +	BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_gizmo_name")); +	BIND_VMETHOD(MethodInfo(Variant::INT, "_get_priority")); +	BIND_VMETHOD(MethodInfo(Variant::BOOL, "_can_be_hidden")); +	BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_selectable_when_hidden")); + +	BIND_VMETHOD(MethodInfo("_redraw", GIZMO_REF)); +	BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_handle_name", GIZMO_REF, PropertyInfo(Variant::INT, "id"))); +	BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_handle_highlighted", GIZMO_REF, PropertyInfo(Variant::INT, "id"))); + +	MethodInfo hvget(Variant::NIL, "_get_handle_value", GIZMO_REF, PropertyInfo(Variant::INT, "id")); +	hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; +	BIND_VMETHOD(hvget); + +	BIND_VMETHOD(MethodInfo("_set_handle", GIZMO_REF, PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); +	MethodInfo cm = MethodInfo("_commit_handle", GIZMO_REF, PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel")); +	cm.default_arguments.push_back(false); +	BIND_VMETHOD(cm); + +	BIND_VMETHOD(MethodInfo(Variant::INT, "_subgizmos_intersect_ray", GIZMO_REF, PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); +	BIND_VMETHOD(MethodInfo(Variant::PACKED_INT32_ARRAY, "_subgizmos_intersect_frustum", GIZMO_REF, PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::ARRAY, "frustum"))); +	BIND_VMETHOD(MethodInfo(Variant::TRANSFORM3D, "_get_subgizmo_transform", GIZMO_REF, PropertyInfo(Variant::INT, "id"))); +	BIND_VMETHOD(MethodInfo("_set_subgizmo_transform", GIZMO_REF, PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::TRANSFORM3D, "transform"))); +	MethodInfo cs = MethodInfo("_commit_subgizmos", GIZMO_REF, PropertyInfo(Variant::PACKED_INT32_ARRAY, "ids"), PropertyInfo(Variant::ARRAY, "restore"), PropertyInfo(Variant::BOOL, "cancel")); +	cs.default_arguments.push_back(false); +	BIND_VMETHOD(cs); + +#undef GIZMO_REF +} + +bool EditorNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { +	if (get_script_instance() && get_script_instance()->has_method("_has_gizmo")) { +		return get_script_instance()->call("_has_gizmo", p_spatial); +	} +	return false; +} + +Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial) { +	if (get_script_instance() && get_script_instance()->has_method("_create_gizmo")) { +		return get_script_instance()->call("_create_gizmo", p_spatial); +	} + +	Ref<EditorNode3DGizmo> ref; +	if (has_gizmo(p_spatial)) { +		ref.instantiate(); +	} +	return ref; +} + +bool EditorNode3DGizmoPlugin::can_be_hidden() const { +	if (get_script_instance() && get_script_instance()->has_method("_can_be_hidden")) { +		return get_script_instance()->call("_can_be_hidden"); +	} +	return true; +} + +bool EditorNode3DGizmoPlugin::is_selectable_when_hidden() const { +	if (get_script_instance() && get_script_instance()->has_method("_is_selectable_when_hidden")) { +		return get_script_instance()->call("_is_selectable_when_hidden"); +	} +	return false; +} + +void EditorNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { +	if (get_script_instance() && get_script_instance()->has_method("_redraw")) { +		Ref<EditorNode3DGizmo> ref(p_gizmo); +		get_script_instance()->call("_redraw", ref); +	} +} + +bool EditorNode3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const { +	if (get_script_instance() && get_script_instance()->has_method("_is_handle_highlighted")) { +		return get_script_instance()->call("_is_handle_highlighted", p_gizmo, p_id); +	} +	return false; +} + +String EditorNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { +	if (get_script_instance() && get_script_instance()->has_method("_get_handle_name")) { +		return get_script_instance()->call("_get_handle_name", p_gizmo, p_id); +	} +	return ""; +} + +Variant EditorNode3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { +	if (get_script_instance() && get_script_instance()->has_method("_get_handle_value")) { +		return get_script_instance()->call("_get_handle_value", p_gizmo, p_id); +	} +	return Variant(); +} + +void EditorNode3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +	if (get_script_instance() && get_script_instance()->has_method("_set_handle")) { +		get_script_instance()->call("_set_handle", p_gizmo, p_id, p_camera, p_point); +	} +} + +void EditorNode3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { +	if (get_script_instance() && get_script_instance()->has_method("_commit_handle")) { +		get_script_instance()->call("_commit_handle", p_gizmo, p_id, p_restore, p_cancel); +	} +} + +int EditorNode3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const { +	if (get_script_instance() && get_script_instance()->has_method("_subgizmos_intersect_ray")) { +		return get_script_instance()->call("_subgizmos_intersect_ray", p_camera, p_point); +	} +	return -1; +} + +Vector<int> EditorNode3DGizmoPlugin::subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const { +	if (get_script_instance() && get_script_instance()->has_method("_subgizmos_intersect_frustum")) { +		Array frustum; +		for (int i = 0; i < p_frustum.size(); i++) { +			frustum[i] = p_frustum[i]; +		} +		return get_script_instance()->call("_subgizmos_intersect_frustum", p_camera, frustum); +	} + +	return Vector<int>(); +} + +Transform3D EditorNode3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const { +	if (get_script_instance() && get_script_instance()->has_method("_get_subgizmo_transform")) { +		return get_script_instance()->call("_get_subgizmo_transform", p_id); +	} + +	return Transform3D(); +} + +void EditorNode3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) const { +	if (get_script_instance() && get_script_instance()->has_method("_set_subgizmo_transform")) { +		get_script_instance()->call("_set_subgizmo_transform", p_id, p_transform); +	} +} + +void EditorNode3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) const { +	if (get_script_instance() && get_script_instance()->has_method("_commit_subgizmos")) { +		Array ids; +		for (int i = 0; i < p_ids.size(); i++) { +			ids[i] = p_ids[i]; +		} + +		Array restore; +		for (int i = 0; i < p_restore.size(); i++) { +			restore[i] = p_restore[i]; +		} + +		get_script_instance()->call("_commit_subgizmos", ids, restore, p_cancel); +	} +} + +void EditorNode3DGizmoPlugin::set_state(int p_state) { +	current_state = p_state; +	for (int i = 0; i < current_gizmos.size(); ++i) { +		current_gizmos[i]->set_hidden(current_state == HIDDEN); +	} +} + +int EditorNode3DGizmoPlugin::get_state() const { +	return current_state; +} + +void EditorNode3DGizmoPlugin::unregister_gizmo(EditorNode3DGizmo *p_gizmo) { +	current_gizmos.erase(p_gizmo); +} + +EditorNode3DGizmoPlugin::EditorNode3DGizmoPlugin() { +	current_state = VISIBLE; +} + +EditorNode3DGizmoPlugin::~EditorNode3DGizmoPlugin() { +	for (int i = 0; i < current_gizmos.size(); ++i) { +		current_gizmos[i]->set_plugin(nullptr); +		current_gizmos[i]->get_spatial_node()->remove_gizmo(current_gizmos[i]); +	} +	if (Node3DEditor::get_singleton()) { +		Node3DEditor::get_singleton()->update_all_gizmos(); +	}  }  //// light gizmo @@ -802,20 +1263,20 @@ int Light3DGizmoPlugin::get_priority() const {  	return -1;  } -String Light3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { -	if (p_idx == 0) { +String Light3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { +	if (p_id == 0) {  		return "Radius";  	} else {  		return "Aperture";  	}  } -Variant Light3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant Light3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	Light3D *light = Object::cast_to<Light3D>(p_gizmo->get_spatial_node()); -	if (p_idx == 0) { +	if (p_id == 0) {  		return light->get_param(Light3D::PARAM_RANGE);  	} -	if (p_idx == 1) { +	if (p_id == 1) {  		return light->get_param(Light3D::PARAM_SPOT_ANGLE);  	} @@ -849,7 +1310,7 @@ static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vec  	return Math::rad2deg(a);  } -void Light3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void Light3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	Light3D *light = Object::cast_to<Light3D>(p_gizmo->get_spatial_node());  	Transform3D gt = light->get_global_transform();  	Transform3D gi = gt.affine_inverse(); @@ -858,7 +1319,7 @@ void Light3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camer  	Vector3 ray_dir = p_camera->project_ray_normal(p_point);  	Vector3 s[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; -	if (p_idx == 0) { +	if (p_id == 0) {  		if (Object::cast_to<SpotLight3D>(light)) {  			Vector3 ra, rb;  			Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, -4096), s[0], s[1], ra, rb); @@ -887,24 +1348,24 @@ void Light3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camer  			}  		} -	} else if (p_idx == 1) { +	} else if (p_id == 1) {  		float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], light->get_param(Light3D::PARAM_RANGE), gt);  		light->set_param(Light3D::PARAM_SPOT_ANGLE, CLAMP(a, 0.01, 89.99));  	}  } -void Light3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void Light3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	Light3D *light = Object::cast_to<Light3D>(p_gizmo->get_spatial_node());  	if (p_cancel) { -		light->set_param(p_idx == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore); +		light->set_param(p_id == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore); -	} else if (p_idx == 0) { +	} else if (p_id == 0) {  		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();  		ur->create_action(TTR("Change Light Radius"));  		ur->add_do_method(light, "set_param", Light3D::PARAM_RANGE, light->get_param(Light3D::PARAM_RANGE));  		ur->add_undo_method(light, "set_param", Light3D::PARAM_RANGE, p_restore);  		ur->commit_action(); -	} else if (p_idx == 1) { +	} else if (p_id == 1) {  		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();  		ur->create_action(TTR("Change Light Radius"));  		ur->add_do_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, light->get_param(Light3D::PARAM_SPOT_ANGLE)); @@ -996,7 +1457,7 @@ void Light3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {  		Vector<Vector3> handles;  		handles.push_back(Vector3(r, 0, 0)); -		p_gizmo->add_handles(handles, get_material("handles_billboard"), true); +		p_gizmo->add_handles(handles, get_material("handles_billboard"), Vector<int>(), true);  	}  	if (Object::cast_to<SpotLight3D>(light)) { @@ -1068,16 +1529,16 @@ int AudioStreamPlayer3DGizmoPlugin::get_priority() const {  	return -1;  } -String AudioStreamPlayer3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { +String AudioStreamPlayer3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	return "Emission Radius";  } -Variant AudioStreamPlayer3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant AudioStreamPlayer3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_spatial_node());  	return player->get_emission_angle();  } -void AudioStreamPlayer3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void AudioStreamPlayer3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_spatial_node());  	Transform3D gt = player->get_global_transform(); @@ -1114,7 +1575,7 @@ void AudioStreamPlayer3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int  	}  } -void AudioStreamPlayer3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void AudioStreamPlayer3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	AudioStreamPlayer3D *player = Object::cast_to<AudioStreamPlayer3D>(p_gizmo->get_spatial_node());  	if (p_cancel) { @@ -1203,7 +1664,7 @@ int Camera3DGizmoPlugin::get_priority() const {  	return -1;  } -String Camera3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { +String Camera3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_spatial_node());  	if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { @@ -1213,7 +1674,7 @@ String Camera3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, in  	}  } -Variant Camera3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant Camera3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_spatial_node());  	if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { @@ -1223,7 +1684,7 @@ Variant Camera3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_  	}  } -void Camera3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void Camera3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_spatial_node());  	Transform3D gt = camera->get_global_transform(); @@ -1252,7 +1713,7 @@ void Camera3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Came  	}  } -void Camera3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	Camera3D *camera = Object::cast_to<Camera3D>(p_gizmo->get_spatial_node());  	if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { @@ -1761,7 +2222,7 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {  	}  	Ref<ArrayMesh> m = surface_tool->commit(); -	p_gizmo->add_mesh(m, false, skel->register_skin(Ref<Skin>())); +	p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skel->register_skin(Ref<Skin>()));  }  //// @@ -2102,23 +2563,23 @@ void SoftBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {  	p_gizmo->add_collision_triangles(tm);  } -String SoftBody3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { +String SoftBody3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	return "SoftBody3D pin point";  } -Variant SoftBody3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant SoftBody3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node()); -	return Variant(soft_body->is_point_pinned(p_idx)); +	return Variant(soft_body->is_point_pinned(p_id));  } -void SoftBody3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void SoftBody3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node()); -	soft_body->pin_point_toggle(p_idx); +	soft_body->pin_point_toggle(p_id);  } -bool SoftBody3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int idx) const { +bool SoftBody3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node()); -	return soft_body->is_point_pinned(idx); +	return soft_body->is_point_pinned(p_id);  }  /////////// @@ -2143,8 +2604,8 @@ int VisibleOnScreenNotifier3DGizmoPlugin::get_priority() const {  	return -1;  } -String VisibleOnScreenNotifier3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { -	switch (p_idx) { +String VisibleOnScreenNotifier3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { +	switch (p_id) {  		case 0:  			return "Size X";  		case 1: @@ -2162,20 +2623,20 @@ String VisibleOnScreenNotifier3DGizmoPlugin::get_handle_name(const EditorNode3DG  	return "";  } -Variant VisibleOnScreenNotifier3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant VisibleOnScreenNotifier3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	VisibleOnScreenNotifier3D *notifier = Object::cast_to<VisibleOnScreenNotifier3D>(p_gizmo->get_spatial_node());  	return notifier->get_aabb();  } -void VisibleOnScreenNotifier3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void VisibleOnScreenNotifier3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	VisibleOnScreenNotifier3D *notifier = Object::cast_to<VisibleOnScreenNotifier3D>(p_gizmo->get_spatial_node());  	Transform3D gt = notifier->get_global_transform();  	Transform3D gi = gt.affine_inverse(); -	bool move = p_idx >= 3; -	p_idx = p_idx % 3; +	bool move = p_id >= 3; +	p_id = p_id % 3;  	AABB aabb = notifier->get_aabb();  	Vector3 ray_from = p_camera->project_ray_origin(p_point); @@ -2186,25 +2647,25 @@ void VisibleOnScreenNotifier3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo  	Vector3 ofs = aabb.position + aabb.size * 0.5;  	Vector3 axis; -	axis[p_idx] = 1.0; +	axis[p_id] = 1.0;  	if (move) {  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); -		float d = ra[p_idx]; +		float d = ra[p_id];  		if (Node3DEditor::get_singleton()->is_snap_enabled()) {  			d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());  		} -		aabb.position[p_idx] = d - 1.0 - aabb.size[p_idx] * 0.5; +		aabb.position[p_id] = d - 1.0 - aabb.size[p_id] * 0.5;  		notifier->set_aabb(aabb);  	} else {  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); -		float d = ra[p_idx] - ofs[p_idx]; +		float d = ra[p_id] - ofs[p_id];  		if (Node3DEditor::get_singleton()->is_snap_enabled()) {  			d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());  		} @@ -2213,13 +2674,13 @@ void VisibleOnScreenNotifier3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo  			d = 0.001;  		}  		//resize -		aabb.position[p_idx] = (aabb.position[p_idx] + aabb.size[p_idx] * 0.5) - d; -		aabb.size[p_idx] = d * 2; +		aabb.position[p_id] = (aabb.position[p_id] + aabb.size[p_id] * 0.5) - d; +		aabb.size[p_id] = d * 2;  		notifier->set_aabb(aabb);  	}  } -void VisibleOnScreenNotifier3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void VisibleOnScreenNotifier3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	VisibleOnScreenNotifier3D *notifier = Object::cast_to<VisibleOnScreenNotifier3D>(p_gizmo->get_spatial_node());  	if (p_cancel) { @@ -2335,8 +2796,8 @@ bool GPUParticles3DGizmoPlugin::is_selectable_when_hidden() const {  	return true;  } -String GPUParticles3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { -	switch (p_idx) { +String GPUParticles3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { +	switch (p_id) {  		case 0:  			return "Size X";  		case 1: @@ -2354,19 +2815,19 @@ String GPUParticles3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_giz  	return "";  } -Variant GPUParticles3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant GPUParticles3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	GPUParticles3D *particles = Object::cast_to<GPUParticles3D>(p_gizmo->get_spatial_node());  	return particles->get_visibility_aabb();  } -void GPUParticles3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void GPUParticles3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	GPUParticles3D *particles = Object::cast_to<GPUParticles3D>(p_gizmo->get_spatial_node());  	Transform3D gt = particles->get_global_transform();  	Transform3D gi = gt.affine_inverse(); -	bool move = p_idx >= 3; -	p_idx = p_idx % 3; +	bool move = p_id >= 3; +	p_id = p_id % 3;  	AABB aabb = particles->get_visibility_aabb();  	Vector3 ray_from = p_camera->project_ray_origin(p_point); @@ -2377,25 +2838,25 @@ void GPUParticles3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx  	Vector3 ofs = aabb.position + aabb.size * 0.5;  	Vector3 axis; -	axis[p_idx] = 1.0; +	axis[p_id] = 1.0;  	if (move) {  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(ofs - axis * 4096, ofs + axis * 4096, sg[0], sg[1], ra, rb); -		float d = ra[p_idx]; +		float d = ra[p_id];  		if (Node3DEditor::get_singleton()->is_snap_enabled()) {  			d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());  		} -		aabb.position[p_idx] = d - 1.0 - aabb.size[p_idx] * 0.5; +		aabb.position[p_id] = d - 1.0 - aabb.size[p_id] * 0.5;  		particles->set_visibility_aabb(aabb);  	} else {  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(ofs, ofs + axis * 4096, sg[0], sg[1], ra, rb); -		float d = ra[p_idx] - ofs[p_idx]; +		float d = ra[p_id] - ofs[p_id];  		if (Node3DEditor::get_singleton()->is_snap_enabled()) {  			d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());  		} @@ -2404,13 +2865,13 @@ void GPUParticles3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx  			d = 0.001;  		}  		//resize -		aabb.position[p_idx] = (aabb.position[p_idx] + aabb.size[p_idx] * 0.5) - d; -		aabb.size[p_idx] = d * 2; +		aabb.position[p_id] = (aabb.position[p_id] + aabb.size[p_id] * 0.5) - d; +		aabb.size[p_id] = d * 2;  		particles->set_visibility_aabb(aabb);  	}  } -void GPUParticles3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void GPUParticles3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	GPUParticles3D *particles = Object::cast_to<GPUParticles3D>(p_gizmo->get_spatial_node());  	if (p_cancel) { @@ -2475,8 +2936,6 @@ void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {  //// -//// -  GPUParticlesCollision3DGizmoPlugin::GPUParticlesCollision3DGizmoPlugin() {  	Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particle_collision", Color(0.5, 0.7, 1));  	create_material("shape_material", gizmo_color); @@ -2498,7 +2957,7 @@ int GPUParticlesCollision3DGizmoPlugin::get_priority() const {  	return -1;  } -String GPUParticlesCollision3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { +String GPUParticlesCollision3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	const Node3D *cs = p_gizmo->get_spatial_node();  	if (Object::cast_to<GPUParticlesCollisionSphere>(cs) || Object::cast_to<GPUParticlesAttractorSphere>(cs)) { @@ -2512,7 +2971,7 @@ String GPUParticlesCollision3DGizmoPlugin::get_handle_name(const EditorNode3DGiz  	return "";  } -Variant GPUParticlesCollision3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant GPUParticlesCollision3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	const Node3D *cs = p_gizmo->get_spatial_node();  	if (Object::cast_to<GPUParticlesCollisionSphere>(cs) || Object::cast_to<GPUParticlesAttractorSphere>(cs)) { @@ -2526,7 +2985,7 @@ Variant GPUParticlesCollision3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *  	return Variant();  } -void GPUParticlesCollision3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void GPUParticlesCollision3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	Node3D *sn = p_gizmo->get_spatial_node();  	Transform3D gt = sn->get_global_transform(); @@ -2554,10 +3013,10 @@ void GPUParticlesCollision3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo,  	if (Object::cast_to<GPUParticlesCollisionBox>(sn) || Object::cast_to<GPUParticlesAttractorBox>(sn) || Object::cast_to<GPUParticlesAttractorVectorField>(sn) || Object::cast_to<GPUParticlesCollisionSDF>(sn) || Object::cast_to<GPUParticlesCollisionHeightField>(sn)) {  		Vector3 axis; -		axis[p_idx] = 1.0; +		axis[p_id] = 1.0;  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); -		float d = ra[p_idx]; +		float d = ra[p_id];  		if (Node3DEditor::get_singleton()->is_snap_enabled()) {  			d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());  		} @@ -2567,12 +3026,12 @@ void GPUParticlesCollision3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo,  		}  		Vector3 he = sn->call("get_extents"); -		he[p_idx] = d; +		he[p_id] = d;  		sn->call("set_extents", he);  	}  } -void GPUParticlesCollision3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	Node3D *sn = p_gizmo->get_spatial_node();  	if (Object::cast_to<GPUParticlesCollisionSphere>(sn) || Object::cast_to<GPUParticlesAttractorSphere>(sn)) { @@ -2762,8 +3221,8 @@ int ReflectionProbeGizmoPlugin::get_priority() const {  	return -1;  } -String ReflectionProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { -	switch (p_idx) { +String ReflectionProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { +	switch (p_id) {  		case 0:  			return "Extents X";  		case 1: @@ -2781,18 +3240,18 @@ String ReflectionProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gi  	return "";  } -Variant ReflectionProbeGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant ReflectionProbeGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_spatial_node());  	return AABB(probe->get_extents(), probe->get_origin_offset());  } -void ReflectionProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void ReflectionProbeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_spatial_node());  	Transform3D gt = probe->get_global_transform();  	Transform3D gi = gt.affine_inverse(); -	if (p_idx < 3) { +	if (p_id < 3) {  		Vector3 extents = probe->get_extents();  		Vector3 ray_from = p_camera->project_ray_origin(p_point); @@ -2801,11 +3260,11 @@ void ReflectionProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_id  		Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) };  		Vector3 axis; -		axis[p_idx] = 1.0; +		axis[p_id] = 1.0;  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); -		float d = ra[p_idx]; +		float d = ra[p_id];  		if (Node3DEditor::get_singleton()->is_snap_enabled()) {  			d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());  		} @@ -2814,13 +3273,13 @@ void ReflectionProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_id  			d = 0.001;  		} -		extents[p_idx] = d; +		extents[p_id] = d;  		probe->set_extents(extents);  	} else { -		p_idx -= 3; +		p_id -= 3;  		Vector3 origin = probe->get_origin_offset(); -		origin[p_idx] = 0; +		origin[p_id] = 0;  		Vector3 ray_from = p_camera->project_ray_origin(p_point);  		Vector3 ray_dir = p_camera->project_ray_normal(p_point); @@ -2828,22 +3287,22 @@ void ReflectionProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_id  		Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) };  		Vector3 axis; -		axis[p_idx] = 1.0; +		axis[p_id] = 1.0;  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(origin - axis * 16384, origin + axis * 16384, sg[0], sg[1], ra, rb);  		// Adjust the actual position to account for the gizmo handle position -		float d = ra[p_idx] + 0.25; +		float d = ra[p_id] + 0.25;  		if (Node3DEditor::get_singleton()->is_snap_enabled()) {  			d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());  		} -		origin[p_idx] = d; +		origin[p_id] = d;  		probe->set_origin_offset(origin);  	}  } -void ReflectionProbeGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void ReflectionProbeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	ReflectionProbe *probe = Object::cast_to<ReflectionProbe>(p_gizmo->get_spatial_node());  	AABB restore = p_restore; @@ -2947,8 +3406,8 @@ int DecalGizmoPlugin::get_priority() const {  	return -1;  } -String DecalGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { -	switch (p_idx) { +String DecalGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { +	switch (p_id) {  		case 0:  			return "Extents X";  		case 1: @@ -2960,12 +3419,12 @@ String DecalGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p  	return "";  } -Variant DecalGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant DecalGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	Decal *decal = Object::cast_to<Decal>(p_gizmo->get_spatial_node());  	return decal->get_extents();  } -void DecalGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void DecalGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	Decal *decal = Object::cast_to<Decal>(p_gizmo->get_spatial_node());  	Transform3D gt = decal->get_global_transform(); @@ -2979,11 +3438,11 @@ void DecalGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3  	Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) };  	Vector3 axis; -	axis[p_idx] = 1.0; +	axis[p_id] = 1.0;  	Vector3 ra, rb;  	Geometry3D::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); -	float d = ra[p_idx]; +	float d = ra[p_id];  	if (Node3DEditor::get_singleton()->is_snap_enabled()) {  		d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());  	} @@ -2992,11 +3451,11 @@ void DecalGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3  		d = 0.001;  	} -	extents[p_idx] = d; +	extents[p_id] = d;  	decal->set_extents(extents);  } -void DecalGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void DecalGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	Decal *decal = Object::cast_to<Decal>(p_gizmo->get_spatial_node());  	Vector3 restore = p_restore; @@ -3055,7 +3514,6 @@ void DecalGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {  	Ref<Material> material = get_material("decal_material", p_gizmo);  	p_gizmo->add_lines(lines, material); -  	p_gizmo->add_handles(handles, get_material("handles"));  } @@ -3088,8 +3546,8 @@ int VoxelGIGizmoPlugin::get_priority() const {  	return -1;  } -String VoxelGIGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { -	switch (p_idx) { +String VoxelGIGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { +	switch (p_id) {  		case 0:  			return "Extents X";  		case 1: @@ -3101,12 +3559,12 @@ String VoxelGIGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int  	return "";  } -Variant VoxelGIGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant VoxelGIGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	VoxelGI *probe = Object::cast_to<VoxelGI>(p_gizmo->get_spatial_node());  	return probe->get_extents();  } -void VoxelGIGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void VoxelGIGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	VoxelGI *probe = Object::cast_to<VoxelGI>(p_gizmo->get_spatial_node());  	Transform3D gt = probe->get_global_transform(); @@ -3120,11 +3578,11 @@ void VoxelGIGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camer  	Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) };  	Vector3 axis; -	axis[p_idx] = 1.0; +	axis[p_id] = 1.0;  	Vector3 ra, rb;  	Geometry3D::get_closest_points_between_segments(Vector3(), axis * 16384, sg[0], sg[1], ra, rb); -	float d = ra[p_idx]; +	float d = ra[p_id];  	if (Node3DEditor::get_singleton()->is_snap_enabled()) {  		d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());  	} @@ -3133,11 +3591,11 @@ void VoxelGIGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camer  		d = 0.001;  	} -	extents[p_idx] = d; +	extents[p_id] = d;  	probe->set_extents(extents);  } -void VoxelGIGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void VoxelGIGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	VoxelGI *probe = Object::cast_to<VoxelGI>(p_gizmo->get_spatial_node());  	Vector3 restore = p_restore; @@ -3257,18 +3715,18 @@ LightmapGIGizmoPlugin::LightmapGIGizmoPlugin() {  	create_icon_material("baked_indirect_light_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoLightmapGI"), SNAME("EditorIcons")));  } -String LightmapGIGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { +String LightmapGIGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	return "";  } -Variant LightmapGIGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant LightmapGIGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	return Variant();  } -void LightmapGIGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void LightmapGIGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  } -void LightmapGIGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void LightmapGIGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  }  bool LightmapGIGizmoPlugin::has_gizmo(Node3D *p_spatial) { @@ -3439,18 +3897,18 @@ LightmapProbeGizmoPlugin::LightmapProbeGizmoPlugin() {  	create_material("lightprobe_lines", gizmo_color);  } -String LightmapProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { +String LightmapProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	return "";  } -Variant LightmapProbeGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant LightmapProbeGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	return Variant();  } -void LightmapProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void LightmapProbeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  } -void LightmapProbeGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void LightmapProbeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  }  bool LightmapProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) { @@ -3570,7 +4028,7 @@ void CollisionObject3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {  				SurfaceTool st;  				st.append_from(s->get_debug_mesh(), 0, xform); -				p_gizmo->add_mesh(st.commit(), false, Ref<SkinReference>(), material); +				p_gizmo->add_mesh(st.commit(), material);  				p_gizmo->add_collision_segments(s->get_debug_mesh_lines());  			}  		} @@ -3600,7 +4058,7 @@ int CollisionShape3DGizmoPlugin::get_priority() const {  	return -1;  } -String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { +String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	const CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_spatial_node());  	Ref<Shape3D> s = cs->get_shape(); @@ -3617,11 +4075,11 @@ String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_g  	}  	if (Object::cast_to<CapsuleShape3D>(*s)) { -		return p_idx == 0 ? "Radius" : "Height"; +		return p_id == 0 ? "Radius" : "Height";  	}  	if (Object::cast_to<CylinderShape3D>(*s)) { -		return p_idx == 0 ? "Radius" : "Height"; +		return p_id == 0 ? "Radius" : "Height";  	}  	if (Object::cast_to<RayShape3D>(*s)) { @@ -3631,7 +4089,7 @@ String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_g  	return "";  } -Variant CollisionShape3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_spatial_node());  	Ref<Shape3D> s = cs->get_shape(); @@ -3651,12 +4109,12 @@ Variant CollisionShape3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo  	if (Object::cast_to<CapsuleShape3D>(*s)) {  		Ref<CapsuleShape3D> cs2 = s; -		return p_idx == 0 ? cs2->get_radius() : cs2->get_height(); +		return p_id == 0 ? cs2->get_radius() : cs2->get_height();  	}  	if (Object::cast_to<CylinderShape3D>(*s)) {  		Ref<CylinderShape3D> cs2 = s; -		return p_idx == 0 ? cs2->get_radius() : cs2->get_height(); +		return p_id == 0 ? cs2->get_radius() : cs2->get_height();  	}  	if (Object::cast_to<RayShape3D>(*s)) { @@ -3667,7 +4125,7 @@ Variant CollisionShape3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo  	return Variant();  } -void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_spatial_node());  	Ref<Shape3D> s = cs->get_shape(); @@ -3717,11 +4175,11 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i  	if (Object::cast_to<BoxShape3D>(*s)) {  		Vector3 axis; -		axis[p_idx] = 1.0; +		axis[p_id] = 1.0;  		Ref<BoxShape3D> bs = s;  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); -		float d = ra[p_idx]; +		float d = ra[p_id];  		if (Node3DEditor::get_singleton()->is_snap_enabled()) {  			d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());  		} @@ -3731,18 +4189,18 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i  		}  		Vector3 he = bs->get_size(); -		he[p_idx] = d * 2; +		he[p_id] = d * 2;  		bs->set_size(he);  	}  	if (Object::cast_to<CapsuleShape3D>(*s)) {  		Vector3 axis; -		axis[p_idx == 0 ? 0 : 2] = 1.0; +		axis[p_id == 0 ? 0 : 2] = 1.0;  		Ref<CapsuleShape3D> cs2 = s;  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);  		float d = axis.dot(ra); -		if (p_idx == 1) { +		if (p_id == 1) {  			d -= cs2->get_radius();  		} @@ -3754,16 +4212,16 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i  			d = 0.001;  		} -		if (p_idx == 0) { +		if (p_id == 0) {  			cs2->set_radius(d); -		} else if (p_idx == 1) { +		} else if (p_id == 1) {  			cs2->set_height(d * 2.0);  		}  	}  	if (Object::cast_to<CylinderShape3D>(*s)) {  		Vector3 axis; -		axis[p_idx == 0 ? 0 : 1] = 1.0; +		axis[p_id == 0 ? 0 : 1] = 1.0;  		Ref<CylinderShape3D> cs2 = s;  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); @@ -3776,15 +4234,15 @@ void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_i  			d = 0.001;  		} -		if (p_idx == 0) { +		if (p_id == 0) {  			cs2->set_radius(d); -		} else if (p_idx == 1) { +		} else if (p_id == 1) {  			cs2->set_height(d * 2.0);  		}  	}  } -void CollisionShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(p_gizmo->get_spatial_node());  	Ref<Shape3D> s = cs->get_shape(); @@ -3823,7 +4281,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int  	if (Object::cast_to<CapsuleShape3D>(*s)) {  		Ref<CapsuleShape3D> ss = s;  		if (p_cancel) { -			if (p_idx == 0) { +			if (p_id == 0) {  				ss->set_radius(p_restore);  			} else {  				ss->set_height(p_restore); @@ -3832,7 +4290,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int  		}  		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); -		if (p_idx == 0) { +		if (p_id == 0) {  			ur->create_action(TTR("Change Capsule Shape Radius"));  			ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());  			ur->add_undo_method(ss.ptr(), "set_radius", p_restore); @@ -3848,7 +4306,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int  	if (Object::cast_to<CylinderShape3D>(*s)) {  		Ref<CylinderShape3D> ss = s;  		if (p_cancel) { -			if (p_idx == 0) { +			if (p_id == 0) {  				ss->set_radius(p_restore);  			} else {  				ss->set_height(p_restore); @@ -3857,7 +4315,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int  		}  		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); -		if (p_idx == 0) { +		if (p_id == 0) {  			ur->create_action(TTR("Change Cylinder Shape Radius"));  			ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius());  			ur->add_undo_method(ss.ptr(), "set_radius", p_restore); @@ -4153,7 +4611,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {  	if (Object::cast_to<ConcavePolygonShape3D>(*s)) {  		Ref<ConcavePolygonShape3D> cs2 = s;  		Ref<ArrayMesh> mesh = cs2->get_debug_mesh(); -		p_gizmo->add_mesh(mesh, false, Ref<SkinReference>(), material); +		p_gizmo->add_mesh(mesh, material);  		p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines());  	} @@ -4174,7 +4632,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {  		Ref<HeightMapShape3D> hms = s;  		Ref<ArrayMesh> mesh = hms->get_debug_mesh(); -		p_gizmo->add_mesh(mesh, false, Ref<SkinReference>(), material); +		p_gizmo->add_mesh(mesh, material);  	}  } diff --git a/editor/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index 6f071859ec..61ee3a95a9 100644 --- a/editor/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -28,13 +28,156 @@  /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */  /*************************************************************************/ -#ifndef SPATIAL_EDITOR_GIZMOS_H -#define SPATIAL_EDITOR_GIZMOS_H +#ifndef NODE_3D_EDITOR_GIZMOS_H +#define NODE_3D_EDITOR_GIZMOS_H -#include "editor/plugins/node_3d_editor_plugin.h" -#include "scene/3d/camera_3d.h" +#include "core/templates/ordered_hash_map.h" +#include "scene/3d/node_3d.h" +#include "scene/3d/skeleton_3d.h"  class Camera3D; +class Timer; +class EditorNode3DGizmoPlugin; + +class EditorNode3DGizmo : public Node3DGizmo { +	GDCLASS(EditorNode3DGizmo, Node3DGizmo); + +	struct Instance { +		RID instance; +		Ref<ArrayMesh> mesh; +		Ref<Material> material; +		Ref<SkinReference> skin_reference; +		bool extra_margin = false; +		Transform3D xform; + +		void create_instance(Node3D *p_base, bool p_hidden = false); +	}; + +	bool selected; + +	Vector<Vector3> collision_segments; +	Ref<TriangleMesh> collision_mesh; + +	Vector<Vector3> handles; +	Vector<int> handle_ids; +	Vector<Vector3> secondary_handles; +	Vector<int> secondary_handle_ids; + +	float selectable_icon_size; +	bool billboard_handle; + +	bool valid; +	bool hidden; +	Vector<Instance> instances; +	Node3D *spatial_node; + +	void _set_spatial_node(Node *p_node) { set_spatial_node(Object::cast_to<Node3D>(p_node)); } + +protected: +	static void _bind_methods(); + +	EditorNode3DGizmoPlugin *gizmo_plugin; + +public: +	void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1)); +	void add_vertices(const Vector<Vector3> &p_vertices, const Ref<Material> &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1)); +	void add_mesh(const Ref<ArrayMesh> &p_mesh, const Ref<Material> &p_material = Ref<Material>(), const Transform3D &p_xform = Transform3D(), const Ref<SkinReference> &p_skin_reference = Ref<SkinReference>()); +	void add_collision_segments(const Vector<Vector3> &p_lines); +	void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh); +	void add_unscaled_billboard(const Ref<Material> &p_material, float p_scale = 1, const Color &p_modulate = Color(1, 1, 1)); +	void add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, const Vector<int> &p_ids = Vector<int>(), bool p_billboard = false, bool p_secondary = false); +	void add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3(), const Transform3D &p_xform = Transform3D()); + +	virtual bool is_handle_highlighted(int p_id) const; +	virtual String get_handle_name(int p_id) const; +	virtual Variant get_handle_value(int p_id) const; +	virtual void set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) const; +	virtual void commit_handle(int p_id, const Variant &p_restore, bool p_cancel = false) const; + +	virtual int subgizmos_intersect_ray(Camera3D *p_camera, const Vector2 &p_point) const; +	virtual Vector<int> subgizmos_intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum) const; +	virtual Transform3D get_subgizmo_transform(int p_id) const; +	virtual void set_subgizmo_transform(int p_id, Transform3D p_transform) const; +	virtual void commit_subgizmos(const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel = false) const; + +	void set_selected(bool p_selected) { selected = p_selected; } +	bool is_selected() const { return selected; } + +	void set_spatial_node(Node3D *p_node); +	Node3D *get_spatial_node() const { return spatial_node; } +	Ref<EditorNode3DGizmoPlugin> get_plugin() const { return gizmo_plugin; } +	bool intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum); +	void handles_intersect_ray(Camera3D *p_camera, const Vector2 &p_point, bool p_shift_pressed, int &r_id); +	bool intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal); +	bool is_subgizmo_selected(int p_id) const; +	Vector<int> get_subgizmo_selection() const; + +	virtual void clear() override; +	virtual void create() override; +	virtual void transform() override; +	virtual void redraw() override; +	virtual void free() override; + +	virtual bool is_editable() const; + +	void set_hidden(bool p_hidden); +	void set_plugin(EditorNode3DGizmoPlugin *p_plugin); + +	EditorNode3DGizmo(); +	~EditorNode3DGizmo(); +}; + +class EditorNode3DGizmoPlugin : public Resource { +	GDCLASS(EditorNode3DGizmoPlugin, Resource); + +public: +	static const int VISIBLE = 0; +	static const int HIDDEN = 1; +	static const int ON_TOP = 2; + +protected: +	int current_state; +	List<EditorNode3DGizmo *> current_gizmos; +	HashMap<String, Vector<Ref<StandardMaterial3D>>> materials; + +	static void _bind_methods(); +	virtual bool has_gizmo(Node3D *p_spatial); +	virtual Ref<EditorNode3DGizmo> create_gizmo(Node3D *p_spatial); + +public: +	void create_material(const String &p_name, const Color &p_color, bool p_billboard = false, bool p_on_top = false, bool p_use_vertex_color = false); +	void create_icon_material(const String &p_name, const Ref<Texture2D> &p_texture, bool p_on_top = false, const Color &p_albedo = Color(1, 1, 1, 1)); +	void create_handle_material(const String &p_name, bool p_billboard = false, const Ref<Texture2D> &p_texture = nullptr); +	void add_material(const String &p_name, Ref<StandardMaterial3D> p_material); + +	Ref<StandardMaterial3D> get_material(const String &p_name, const Ref<EditorNode3DGizmo> &p_gizmo = Ref<EditorNode3DGizmo>()); + +	virtual String get_gizmo_name() const; +	virtual int get_priority() const; +	virtual bool can_be_hidden() const; +	virtual bool is_selectable_when_hidden() const; + +	virtual void redraw(EditorNode3DGizmo *p_gizmo); +	virtual bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const; +	virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const; +	virtual Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const; +	virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const; +	virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const; + +	virtual int subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const; +	virtual Vector<int> subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const; +	virtual Transform3D get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const; +	virtual void set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) const; +	virtual void commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel = false) const; + +	Ref<EditorNode3DGizmo> get_gizmo(Node3D *p_spatial); +	void set_state(int p_state); +	int get_state() const; +	void unregister_gizmo(EditorNode3DGizmo *p_gizmo); + +	EditorNode3DGizmoPlugin(); +	virtual ~EditorNode3DGizmoPlugin(); +};  class Light3DGizmoPlugin : public EditorNode3DGizmoPlugin {  	GDCLASS(Light3DGizmoPlugin, EditorNode3DGizmoPlugin); @@ -44,10 +187,10 @@ public:  	String get_gizmo_name() const override;  	int get_priority() const override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override;  	Light3DGizmoPlugin(); @@ -61,10 +204,10 @@ public:  	String get_gizmo_name() const override;  	int get_priority() const override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override;  	AudioStreamPlayer3DGizmoPlugin(); @@ -78,10 +221,10 @@ public:  	String get_gizmo_name() const override;  	int get_priority() const override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override;  	Camera3DGizmoPlugin(); @@ -210,10 +353,10 @@ public:  	bool is_selectable_when_hidden() const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) override; -	bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int idx) const override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override; +	bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const override;  	SoftBody3DGizmoPlugin();  }; @@ -227,10 +370,10 @@ public:  	int get_priority() const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	VisibleOnScreenNotifier3DGizmoPlugin();  }; @@ -257,10 +400,10 @@ public:  	bool is_selectable_when_hidden() const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	GPUParticles3DGizmoPlugin();  }; @@ -274,10 +417,10 @@ public:  	int get_priority() const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	GPUParticlesCollision3DGizmoPlugin();  }; @@ -291,10 +434,10 @@ public:  	int get_priority() const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	ReflectionProbeGizmoPlugin();  }; @@ -308,10 +451,10 @@ public:  	int get_priority() const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	DecalGizmoPlugin();  }; @@ -325,10 +468,10 @@ public:  	int get_priority() const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	VoxelGIGizmoPlugin();  }; @@ -342,10 +485,10 @@ public:  	int get_priority() const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	LightmapGIGizmoPlugin();  }; @@ -359,10 +502,10 @@ public:  	int get_priority() const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	LightmapProbeGizmoPlugin();  }; @@ -388,10 +531,10 @@ public:  	int get_priority() const override;  	void redraw(EditorNode3DGizmo *p_gizmo) override; -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	CollisionShape3DGizmoPlugin();  }; @@ -489,4 +632,4 @@ public:  	Joint3DGizmoPlugin();  }; -#endif // SPATIAL_EDITOR_GIZMOS_H +#endif // NODE_3D_EDITOR_GIZMOS_H diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 7290a39463..ebf593e556 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -35,20 +35,20 @@  #include "core/math/camera_matrix.h"  #include "core/math/math_funcs.h"  #include "core/os/keyboard.h" -#include "core/string/print_string.h"  #include "core/templates/sort_array.h"  #include "editor/debugger/editor_debugger_node.h"  #include "editor/editor_node.h" -#include "editor/editor_scale.h"  #include "editor/editor_settings.h" -#include "editor/node_3d_editor_gizmos.h"  #include "editor/plugins/animation_player_editor_plugin.h" +#include "editor/plugins/node_3d_editor_gizmos.h"  #include "editor/plugins/script_editor_plugin.h"  #include "scene/3d/camera_3d.h"  #include "scene/3d/collision_shape_3d.h" +#include "scene/3d/light_3d.h"  #include "scene/3d/mesh_instance_3d.h"  #include "scene/3d/physics_body_3d.h"  #include "scene/3d/visual_instance_3d.h" +#include "scene/3d/world_environment.h"  #include "scene/gui/center_container.h"  #include "scene/gui/subviewport_container.h"  #include "scene/resources/packed_scene.h" @@ -58,7 +58,6 @@  #define GIZMO_ARROW_SIZE 0.35  #define GIZMO_RING_HALF_WIDTH 0.1 -#define GIZMO_SCALE_DEFAULT 0.15  #define GIZMO_PLANE_SIZE 0.2  #define GIZMO_PLANE_DST 0.3  #define GIZMO_CIRCLE_SIZE 1.1 @@ -436,16 +435,29 @@ Vector3 Node3DEditorViewport::_get_ray(const Vector2 &p_pos) const {  }  void Node3DEditorViewport::_clear_selected() { -	editor_selection->clear(); -} - -void Node3DEditorViewport::_select_clicked(bool p_append, bool p_single, bool p_allow_locked) { -	if (clicked.is_null()) { -		return; +	_edit.gizmo = Ref<EditorNode3DGizmo>(); +	_edit.gizmo_handle = -1; +	_edit.gizmo_initial_value = Variant(); + +	Node3D *selected = spatial_editor->get_single_selected_node(); +	Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr; + +	if (se && se->gizmo.is_valid()) { +		se->subgizmos.clear(); +		se->gizmo->redraw(); +		se->gizmo.unref(); +		spatial_editor->update_transform_gizmo(); +	} else { +		editor_selection->clear(); +		Node3DEditor::get_singleton()->edit(nullptr);  	} +} -	Node *node = Object::cast_to<Node>(ObjectDB::get_instance(clicked)); +void Node3DEditorViewport::_select_clicked(bool p_allow_locked) { +	Node *node = Object::cast_to<Node3D>(ObjectDB::get_instance(clicked));  	Node3D *selected = Object::cast_to<Node3D>(node); +	clicked = ObjectID(); +  	if (!selected) {  		return;  	} @@ -462,43 +474,27 @@ void Node3DEditorViewport::_select_clicked(bool p_append, bool p_single, bool p_  	}  	if (p_allow_locked || !_is_node_locked(selected)) { -		_select(selected, clicked_wants_append, true); -	} -} - -void Node3DEditorViewport::_select(Node *p_node, bool p_append, bool p_single) { -	// Add or remove a single node from the selection -	if (p_append && p_single) { -		if (editor_selection->is_selected(p_node)) { -			// Already in the selection, remove it from the selected nodes -			editor_selection->remove_node(p_node); +		if (clicked_wants_append) { +			if (editor_selection->is_selected(selected)) { +				editor_selection->remove_node(selected); +			} else { +				editor_selection->add_node(selected); +			}  		} else { -			// Add the item to the selection -			editor_selection->add_node(p_node); -		} -	} else if (p_append && !p_single) { -		// Add the item to the selection -		editor_selection->add_node(p_node); -	} else { -		// No append; single select -		editor_selection->clear(); -		editor_selection->add_node(p_node); -		// Reselect -		if (Engine::get_singleton()->is_editor_hint()) { -			editor->call("edit_node", p_node); +			if (!editor_selection->is_selected(selected)) { +				editor_selection->clear(); +				editor_selection->add_node(selected); +				editor->edit_node(selected); +			}  		} -	} -	if (editor_selection->get_selected_node_list().size() == 1) { -		editor->push_item(editor_selection->get_selected_node_list()[0]); +		if (editor_selection->get_selected_node_list().size() == 1) { +			editor->edit_node(editor_selection->get_selected_node_list()[0]); +		}  	}  } -ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, bool &r_includes_current, int *r_gizmo_handle, bool p_alt_select) { -	if (r_gizmo_handle) { -		*r_gizmo_handle = -1; -	} - +ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) {  	Vector3 ray = _get_ray(p_pos);  	Vector3 pos = _get_ray_pos(p_pos);  	Vector2 shrinked_pos = p_pos / subviewport_container->get_stretch_shrink(); @@ -514,7 +510,6 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, b  	ObjectID closest;  	Node *item = nullptr;  	float closest_dist = 1e20; -	int selected_handle = -1;  	for (int i = 0; i < instances.size(); i++) {  		Node3D *spat = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i])); @@ -523,38 +518,40 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, b  			continue;  		} -		Ref<EditorNode3DGizmo> seg = spat->get_gizmo(); +		Vector<Ref<Node3DGizmo>> gizmos = spat->get_gizmos(); -		if ((!seg.is_valid()) || found_gizmos.has(seg)) { -			continue; -		} +		for (int j = 0; j < gizmos.size(); j++) { +			Ref<EditorNode3DGizmo> seg = gizmos[j]; -		found_gizmos.insert(seg); -		Vector3 point; -		Vector3 normal; +			if ((!seg.is_valid()) || found_gizmos.has(seg)) { +				continue; +			} -		int handle = -1; -		bool inters = seg->intersect_ray(camera, shrinked_pos, point, normal, &handle, p_alt_select); +			found_gizmos.insert(seg); +			Vector3 point; +			Vector3 normal; -		if (!inters) { -			continue; -		} +			bool inters = seg->intersect_ray(camera, shrinked_pos, point, normal); -		float dist = pos.distance_to(point); +			if (!inters) { +				continue; +			} -		if (dist < 0) { -			continue; -		} +			float dist = pos.distance_to(point); -		if (dist < closest_dist) { -			item = Object::cast_to<Node>(spat); -			if (item != edited_scene) { -				item = edited_scene->get_deepest_editable_node(item); +			if (dist < 0) { +				continue;  			} -			closest = item->get_instance_id(); -			closest_dist = dist; -			selected_handle = handle; +			if (dist < closest_dist) { +				item = Object::cast_to<Node>(spat); +				if (item != edited_scene) { +					item = edited_scene->get_deepest_editable_node(item); +				} + +				closest = item->get_instance_id(); +				closest_dist = dist; +			}  		}  	} @@ -562,23 +559,15 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, b  		return ObjectID();  	} -	if (!editor_selection->is_selected(item) || (r_gizmo_handle && selected_handle >= 0)) { -		if (r_gizmo_handle) { -			*r_gizmo_handle = selected_handle; -		} -	} -  	return closest;  } -void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select, bool p_include_locked_nodes) { +void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, Vector<_RayResult> &r_results, bool p_include_locked_nodes) {  	Vector3 ray = _get_ray(p_pos);  	Vector3 pos = _get_ray_pos(p_pos);  	Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world_3d()->get_scenario()); -	Set<Ref<EditorNode3DGizmo>> found_gizmos; - -	r_includes_current = false; +	Set<Node3D *> found_nodes;  	for (int i = 0; i < instances.size(); i++) {  		Node3D *spat = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i])); @@ -587,53 +576,48 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, bool &r_inclu  			continue;  		} -		Ref<EditorNode3DGizmo> seg = spat->get_gizmo(); - -		if (!seg.is_valid()) { +		if (found_nodes.has(spat)) {  			continue;  		} -		if (found_gizmos.has(seg)) { +		if (!p_include_locked_nodes && _is_node_locked(spat)) {  			continue;  		} -		found_gizmos.insert(seg); -		Vector3 point; -		Vector3 normal; +		Vector<Ref<Node3DGizmo>> gizmos = spat->get_gizmos(); +		for (int j = 0; j < gizmos.size(); j++) { +			Ref<EditorNode3DGizmo> seg = gizmos[j]; -		int handle = -1; -		bool inters = seg->intersect_ray(camera, p_pos, point, normal, nullptr, p_alt_select); +			if (!seg.is_valid()) { +				continue; +			} -		if (!inters) { -			continue; -		} +			Vector3 point; +			Vector3 normal; -		float dist = pos.distance_to(point); +			bool inters = seg->intersect_ray(camera, p_pos, point, normal); -		if (dist < 0) { -			continue; -		} +			if (!inters) { +				continue; +			} -		if (!p_include_locked_nodes && _is_node_locked(spat)) { -			continue; -		} +			float dist = pos.distance_to(point); -		if (editor_selection->is_selected(spat)) { -			r_includes_current = true; -		} +			if (dist < 0) { +				continue; +			} -		_RayResult res; -		res.item = spat; -		res.depth = dist; -		res.handle = handle; -		results.push_back(res); -	} +			found_nodes.insert(spat); -	if (results.is_empty()) { -		return; +			_RayResult res; +			res.item = spat; +			res.depth = dist; +			r_results.push_back(res); +			break; +		}  	} -	results.sort(); +	r_results.sort();  }  Vector3 Node3DEditorViewport::_get_screen_to_space(const Vector3 &p_vector3) { @@ -656,6 +640,9 @@ Vector3 Node3DEditorViewport::_get_screen_to_space(const Vector3 &p_vector3) {  void Node3DEditorViewport::_select_region() {  	if (cursor.region_begin == cursor.region_end) { +		if (!clicked_wants_append) { +			_clear_selected(); +		}  		return; //nothing really  	} @@ -702,7 +689,66 @@ void Node3DEditorViewport::_select_region() {  	far.d += get_zfar();  	frustum.push_back(far); +	if (spatial_editor->get_single_selected_node()) { +		Node3D *single_selected = spatial_editor->get_single_selected_node(); +		Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(single_selected); + +		Ref<EditorNode3DGizmo> old_gizmo; +		if (!clicked_wants_append) { +			se->subgizmos.clear(); +			old_gizmo = se->gizmo; +			se->gizmo.unref(); +		} + +		bool found_subgizmos = false; +		Vector<Ref<Node3DGizmo>> gizmos = single_selected->get_gizmos(); +		for (int j = 0; j < gizmos.size(); j++) { +			Ref<EditorNode3DGizmo> seg = gizmos[j]; +			if (!seg.is_valid()) { +				continue; +			} + +			if (se->gizmo.is_valid() && se->gizmo != seg) { +				continue; +			} + +			Vector<int> subgizmos = seg->subgizmos_intersect_frustum(camera, frustum); +			if (!subgizmos.is_empty()) { +				se->gizmo = seg; +				for (int i = 0; i < subgizmos.size(); i++) { +					int subgizmo_id = subgizmos[i]; +					if (!se->subgizmos.has(subgizmo_id)) { +						se->subgizmos.insert(subgizmo_id, se->gizmo->get_subgizmo_transform(subgizmo_id)); +					} +				} +				found_subgizmos = true; +				break; +			} +		} + +		if (!clicked_wants_append || found_subgizmos) { +			if (se->gizmo.is_valid()) { +				se->gizmo->redraw(); +			} + +			if (old_gizmo != se->gizmo && old_gizmo.is_valid()) { +				old_gizmo->redraw(); +			} + +			spatial_editor->update_transform_gizmo(); +		} + +		if (found_subgizmos) { +			return; +		} +	} + +	if (!clicked_wants_append) { +		_clear_selected(); +	} +  	Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world_3d()->get_scenario()); +	Set<Node3D *> found_nodes;  	Vector<Node *> selected;  	Node *edited_scene = get_tree()->get_edited_scene_root(); @@ -713,6 +759,12 @@ void Node3DEditorViewport::_select_region() {  			continue;  		} +		if (found_nodes.has(sp)) { +			continue; +		} + +		found_nodes.insert(sp); +  		Node *item = Object::cast_to<Node>(sp);  		if (item != edited_scene) {  			item = edited_scene->get_deepest_editable_node(item); @@ -731,28 +783,31 @@ void Node3DEditorViewport::_select_region() {  			item = sel;  		} -		if (selected.find(item) != -1) { -			continue; -		} -  		if (_is_node_locked(item)) {  			continue;  		} -		Ref<EditorNode3DGizmo> seg = sp->get_gizmo(); +		Vector<Ref<Node3DGizmo>> gizmos = sp->get_gizmos(); +		for (int j = 0; j < gizmos.size(); j++) { +			Ref<EditorNode3DGizmo> seg = gizmos[j]; +			if (!seg.is_valid()) { +				continue; +			} -		if (!seg.is_valid()) { -			continue; +			if (seg->intersect_frustum(camera, frustum)) { +				selected.push_back(item); +			}  		} +	} -		if (seg->intersect_frustum(camera, frustum)) { -			selected.push_back(item); +	for (int i = 0; i < selected.size(); i++) { +		if (!editor_selection->is_selected(selected[i])) { +			editor_selection->add_node(selected[i]);  		}  	} -	bool single = selected.size() == 1; -	for (int i = 0; i < selected.size(); i++) { -		_select(selected[i], true, single); +	if (editor_selection->get_selected_node_list().size() == 1) { +		editor->edit_node(editor_selection->get_selected_node_list()[0]);  	}  } @@ -779,21 +834,34 @@ void Node3DEditorViewport::_compute_edit(const Point2 &p_point) {  	spatial_editor->update_transform_gizmo();  	_edit.center = spatial_editor->get_gizmo_transform().origin; -	List<Node *> &selection = editor_selection->get_selected_node_list(); +	Node3D *selected = spatial_editor->get_single_selected_node(); +	Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr; -	for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { -		Node3D *sp = Object::cast_to<Node3D>(E->get()); -		if (!sp) { -			continue; +	if (se && se->gizmo.is_valid()) { +		for (Map<int, Transform3D>::Element *E = se->subgizmos.front(); E; E = E->next()) { +			int subgizmo_id = E->key(); +			se->subgizmos[subgizmo_id] = se->gizmo->get_subgizmo_transform(subgizmo_id);  		} +		se->original_local = selected->get_transform(); +		se->original = selected->get_global_transform(); +	} else { +		List<Node *> &selection = editor_selection->get_selected_node_list(); -		Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); -		if (!se) { -			continue; -		} +		for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { +			Node3D *sp = Object::cast_to<Node3D>(E->get()); +			if (!sp) { +				continue; +			} + +			Node3DEditorSelectedItem *sel_item = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); + +			if (!sel_item) { +				continue; +			} -		se->original = se->sp->get_global_gizmo_transform(); -		se->original_local = se->sp->get_local_gizmo_transform(); +			sel_item->original_local = sel_item->sp->get_local_gizmo_transform(); +			sel_item->original = sel_item->sp->get_global_gizmo_transform(); +		}  	}  } @@ -829,7 +897,7 @@ static int _get_key_modifier(Ref<InputEventWithModifiers> e) {  	return 0;  } -bool Node3DEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only) { +bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only) {  	if (!spatial_editor->is_gizmo_visible()) {  		return false;  	} @@ -1015,6 +1083,88 @@ bool Node3DEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_high  	return false;  } +void Node3DEditorViewport::_transform_gizmo_apply(Node3D *p_node, const Transform3D &p_transform, bool p_local) { +	if (p_transform.basis.determinant() == 0) { +		return; +	} + +	if (p_local) { +		p_node->set_transform(p_transform); +	} else { +		p_node->set_global_transform(p_transform); +	} +} + +Transform3D Node3DEditorViewport::_compute_transform(TransformMode p_mode, const Transform3D &p_original, const Transform3D &p_original_local, Vector3 p_motion, double p_extra, bool p_local) { +	switch (p_mode) { +		case TRANSFORM_SCALE: { +			if (p_local) { +				Basis g = p_original.basis.orthonormalized(); +				Vector3 local_motion = g.inverse().xform(p_motion); + +				if (_edit.snap || spatial_editor->is_snap_enabled()) { +					local_motion.snap(Vector3(p_extra, p_extra, p_extra)); +				} + +				Vector3 local_scale = p_original_local.basis.get_scale() * (local_motion + Vector3(1, 1, 1)); +				Transform3D local_t = p_original_local; +				local_t.basis.set_euler_scale(p_original_local.basis.get_rotation_euler(), local_scale); +				return local_t; +			} else { +				Transform3D base = Transform3D(Basis(), _edit.center); +				if (_edit.snap || spatial_editor->is_snap_enabled()) { +					p_motion.snap(Vector3(p_extra, p_extra, p_extra)); +				} + +				Transform3D r; +				r.basis.scale(p_motion + Vector3(1, 1, 1)); +				return base * (r * (base.inverse() * p_original)); +			} +		} +		case TRANSFORM_TRANSLATE: { +			if (p_local) { +				if (_edit.snap || spatial_editor->is_snap_enabled()) { +					Basis g = p_original.basis.orthonormalized(); +					Vector3 local_motion = g.inverse().xform(p_motion); +					local_motion.snap(Vector3(p_extra, p_extra, p_extra)); + +					p_motion = g.xform(local_motion); +				} + +			} else { +				if (_edit.snap || spatial_editor->is_snap_enabled()) { +					p_motion.snap(Vector3(p_extra, p_extra, p_extra)); +				} +			} + +			// Apply translation +			Transform3D t = p_original; +			t.origin += p_motion; +			return t; +		} +		case TRANSFORM_ROTATE: { +			if (p_local) { +				Basis rot = Basis(p_motion, p_extra); + +				Vector3 scale = p_original_local.basis.get_scale(); +				Vector3 euler = (p_original_local.get_basis().orthonormalized() * rot).get_euler(); +				Transform3D t; +				t.basis.set_euler_scale(euler, scale); +				t.origin = p_original_local.origin; +				return t; +			} else { +				Transform3D r; +				r.basis.rotate(p_motion, p_extra); +				Transform3D base = Transform3D(Basis(), _edit.center); +				return base * r * base.inverse() * p_original; +			} +		} +		default: { +			ERR_FAIL_V_MSG(Transform3D(), "Invalid mode in '_compute_transform'"); +		} +	} +} +  void Node3DEditorViewport::_surface_mouse_enter() {  	if (!surface->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field())) {  		surface->grab_focus(); @@ -1038,7 +1188,7 @@ bool Node3DEditorViewport ::_is_node_locked(const Node *p_node) {  }  void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) { -	_find_items_at_pos(b->get_position(), clicked_includes_current, selection_results, b->is_shift_pressed(), spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT); +	_find_items_at_pos(b->get_position(), selection_results, spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT);  	Node *scene = editor->get_edited_scene(); @@ -1058,10 +1208,8 @@ void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) {  		selection_results.clear();  		if (clicked.is_valid()) { -			_select_clicked(clicked_wants_append, true, spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT); -			clicked = ObjectID(); +			_select_clicked(spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT);  		} -  	} else if (!selection_results.is_empty()) {  		NodePath root_path = get_tree()->get_edited_scene_root()->get_path();  		StringName root_name = root_path.get_name(root_path.get_name_count() - 1); @@ -1190,7 +1338,20 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  							continue;  						} -						sp->set_global_transform(se->original); +						if (se->gizmo.is_valid()) { +							Vector<int> ids; +							Vector<Transform3D> restore; + +							for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) { +								ids.push_back(GE->key()); +								restore.push_back(GE->value()); +							} + +							se->gizmo->commit_subgizmos(ids, restore, true); +							spatial_editor->update_transform_gizmo(); +						} else { +							sp->set_global_transform(se->original); +						}  					}  					surface->update();  					set_message(TTR("Transform Aborted."), 3); @@ -1271,28 +1432,65 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  						can_select_gizmos = view_menu->get_popup()->is_item_checked(idx);  					} -					if (can_select_gizmos && spatial_editor->get_selected()) { -						Ref<EditorNode3DGizmo> seg = spatial_editor->get_selected()->get_gizmo(); -						if (seg.is_valid()) { -							int handle = -1; -							Vector3 point; -							Vector3 normal; -							bool inters = seg->intersect_ray(camera, _edit.mouse_pos, point, normal, &handle, b->is_shift_pressed()); -							if (inters && handle != -1) { +					if (can_select_gizmos && spatial_editor->get_single_selected_node()) { +						Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(spatial_editor->get_single_selected_node()); +						Vector<Ref<Node3DGizmo>> gizmos = spatial_editor->get_single_selected_node()->get_gizmos(); + +						bool intersected_handle = false; +						for (int i = 0; i < gizmos.size(); i++) { +							Ref<EditorNode3DGizmo> seg = gizmos[i]; + +							if ((!seg.is_valid())) { +								continue; +							} + +							int subgizmo_id = seg->subgizmos_intersect_ray(camera, _edit.mouse_pos); +							if (subgizmo_id != -1) { +								ERR_CONTINUE(!se); +								if (b->is_shift_pressed()) { +									if (se->subgizmos.has(subgizmo_id)) { +										se->subgizmos.erase(subgizmo_id); +									} else { +										se->subgizmos.insert(subgizmo_id, seg->get_subgizmo_transform(subgizmo_id)); +									} +								} else { +									se->subgizmos.clear(); +									se->subgizmos.insert(subgizmo_id, seg->get_subgizmo_transform(subgizmo_id)); +								} + +								if (se->subgizmos.is_empty()) { +									se->gizmo = Ref<EditorNode3DGizmo>(); +								} else { +									se->gizmo = seg; +								} + +								seg->redraw(); +								spatial_editor->update_transform_gizmo(); +								intersected_handle = true; +								break; +							} + +							int gizmo_handle = -1; +							seg->handles_intersect_ray(camera, _edit.mouse_pos, b->is_shift_pressed(), gizmo_handle); +							if (gizmo_handle != -1) {  								_edit.gizmo = seg; -								_edit.gizmo_handle = handle; -								_edit.gizmo_initial_value = seg->get_handle_value(handle); +								_edit.gizmo_handle = gizmo_handle; +								_edit.gizmo_initial_value = seg->get_handle_value(gizmo_handle); +								intersected_handle = true;  								break;  							}  						} + +						if (intersected_handle) { +							break; +						}  					} -					if (_gizmo_select(_edit.mouse_pos)) { +					if (_transform_gizmo_select(_edit.mouse_pos)) {  						break;  					}  					clicked = ObjectID(); -					clicked_includes_current = false;  					if ((spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT && b->is_command_pressed()) || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) {  						/* HANDLE ROTATION */ @@ -1325,40 +1523,19 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  						break;  					} -					// todo scale - -					int gizmo_handle = -1; - -					clicked = _select_ray(b->get_position(), b->is_shift_pressed(), clicked_includes_current, &gizmo_handle, b->is_shift_pressed()); +					clicked = _select_ray(b->get_position());  					//clicking is always deferred to either move or release  					clicked_wants_append = b->is_shift_pressed();  					if (clicked.is_null()) { -						if (!clicked_wants_append) { -							_clear_selected(); -						} -  						//default to regionselect  						cursor.region_select = true;  						cursor.region_begin = b->get_position();  						cursor.region_end = b->get_position();  					} -					if (clicked.is_valid() && gizmo_handle >= 0) { -						Node3D *spa = Object::cast_to<Node3D>(ObjectDB::get_instance(clicked)); -						if (spa) { -							Ref<EditorNode3DGizmo> seg = spa->get_gizmo(); -							if (seg.is_valid()) { -								_edit.gizmo = seg; -								_edit.gizmo_handle = gizmo_handle; -								_edit.gizmo_initial_value = seg->get_handle_value(gizmo_handle); -								break; -							} -						} -					} -  					surface->update();  				} else {  					if (_edit.gizmo.is_valid()) { @@ -1366,47 +1543,57 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  						_edit.gizmo = Ref<EditorNode3DGizmo>();  						break;  					} +  					if (clicked.is_valid()) { -						_select_clicked(clicked_wants_append, true); -						// Processing was deferred. -						clicked = ObjectID(); +						_select_clicked(false);  					}  					if (cursor.region_select) { -						if (!clicked_wants_append) { -							_clear_selected(); -						} -  						_select_region();  						cursor.region_select = false;  						surface->update();  					}  					if (_edit.mode != TRANSFORM_NONE) { -						static const char *_transform_name[4] = { "None", "Rotate", "Translate", "Scale" }; -						undo_redo->create_action(_transform_name[_edit.mode]); +						Node3D *selected = spatial_editor->get_single_selected_node(); +						Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr; -						List<Node *> &selection = editor_selection->get_selected_node_list(); +						if (se && se->gizmo.is_valid()) { +							Vector<int> ids; +							Vector<Transform3D> restore; -						for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { -							Node3D *sp = Object::cast_to<Node3D>(E->get()); -							if (!sp) { -								continue; +							for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) { +								ids.push_back(GE->key()); +								restore.push_back(GE->value());  							} -							Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); -							if (!se) { -								continue; -							} +							se->gizmo->commit_subgizmos(ids, restore, false); +							spatial_editor->update_transform_gizmo(); +						} else { +							static const char *_transform_name[4] = { "None", "Rotate", "Translate", "Scale" }; +							undo_redo->create_action(_transform_name[_edit.mode]); + +							List<Node *> &selection = editor_selection->get_selected_node_list(); + +							for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { +								Node3D *sp = Object::cast_to<Node3D>(E->get()); +								if (!sp) { +									continue; +								} + +								Node3DEditorSelectedItem *sel_item = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); +								if (!sel_item) { +									continue; +								} -							undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); -							undo_redo->add_undo_method(sp, "set_global_transform", se->original); +								undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); +								undo_redo->add_undo_method(sp, "set_global_transform", sel_item->original); +							} +							undo_redo->commit_action();  						} -						undo_redo->commit_action();  						_edit.mode = TRANSFORM_NONE;  						set_message("");  					} -  					surface->update();  				} @@ -1421,31 +1608,39 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  	if (m.is_valid()) {  		_edit.mouse_pos = m->get_position(); -		if (spatial_editor->get_selected()) { -			Ref<EditorNode3DGizmo> seg = spatial_editor->get_selected()->get_gizmo(); -			if (seg.is_valid()) { -				int selected_handle = -1; - -				int handle = -1; -				Vector3 point; -				Vector3 normal; -				bool inters = seg->intersect_ray(camera, _edit.mouse_pos, point, normal, &handle, false); -				if (inters && handle != -1) { -					selected_handle = handle; +		if (spatial_editor->get_single_selected_node()) { +			Vector<Ref<Node3DGizmo>> gizmos = spatial_editor->get_single_selected_node()->get_gizmos(); + +			Ref<EditorNode3DGizmo> found_gizmo; +			int found_handle = -1; + +			for (int i = 0; i < gizmos.size(); i++) { +				Ref<EditorNode3DGizmo> seg = gizmos[i]; +				if (!seg.is_valid()) { +					continue;  				} -				if (selected_handle != spatial_editor->get_over_gizmo_handle()) { -					spatial_editor->set_over_gizmo_handle(selected_handle); -					spatial_editor->get_selected()->update_gizmo(); -					if (selected_handle != -1) { -						spatial_editor->select_gizmo_highlight_axis(-1); -					} +				seg->handles_intersect_ray(camera, _edit.mouse_pos, false, found_handle); + +				if (found_handle != -1) { +					found_gizmo = seg; +					break;  				}  			} + +			if (found_gizmo.is_valid()) { +				spatial_editor->select_gizmo_highlight_axis(-1); +			} + +			if (found_gizmo != spatial_editor->get_current_hover_gizmo() || found_handle != spatial_editor->get_current_hover_gizmo_handle()) { +				spatial_editor->set_current_hover_gizmo(found_gizmo); +				spatial_editor->set_current_hover_gizmo_handle(found_handle); +				spatial_editor->get_single_selected_node()->update_gizmos(); +			}  		} -		if (spatial_editor->get_over_gizmo_handle() == -1 && !(m->get_button_mask() & 1) && !_edit.gizmo.is_valid()) { -			_gizmo_select(_edit.mouse_pos, true); +		if (spatial_editor->get_current_hover_gizmo().is_null() && !(m->get_button_mask() & 1) && !_edit.gizmo.is_valid()) { +			_transform_gizmo_select(_edit.mouse_pos, true);  		}  		NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int(); @@ -1469,11 +1664,6 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  			} else {  				const bool movement_threshold_passed = _edit.original_mouse_pos.distance_to(_edit.mouse_pos) > 8 * EDSCALE;  				if (clicked.is_valid() && movement_threshold_passed) { -					if (!clicked_includes_current) { -						_select_clicked(clicked_wants_append, true); -						// Processing was deferred. -					} -  					_compute_edit(_edit.mouse_pos);  					clicked = ObjectID(); @@ -1568,8 +1758,6 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  							motion = Vector3(scale, scale, scale);  						} -						List<Node *> &selection = editor_selection->get_selected_node_list(); -  						// Disable local transformation for TRANSFORM_VIEW  						bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); @@ -1582,6 +1770,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  						set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +  									String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); +						List<Node *> &selection = editor_selection->get_selected_node_list();  						for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {  							Node3D *sp = Object::cast_to<Node3D>(E->get());  							if (!sp) { @@ -1597,44 +1786,22 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  								continue;  							} -							Transform3D original = se->original; -							Transform3D original_local = se->original_local; -							Transform3D base = Transform3D(Basis(), _edit.center); -							Transform3D t; -							Vector3 local_scale; - -							if (local_coords) { -								Basis g = original.basis.orthonormalized(); -								Vector3 local_motion = g.inverse().xform(motion); - -								if (_edit.snap || spatial_editor->is_snap_enabled()) { -									local_motion.snap(Vector3(snap, snap, snap)); -								} - -								local_scale = original_local.basis.get_scale() * (local_motion + Vector3(1, 1, 1)); - -								// Prevent scaling to 0 it would break the gizmo -								Basis check = original_local.basis; -								check.scale(local_scale); -								if (check.determinant() != 0) { -									// Apply scale -									sp->set_scale(local_scale); +							if (se->gizmo.is_valid()) { +								for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) { +									Transform3D xform = GE->get(); +									Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords); +									if (!local_coords) { +										new_xform = se->original.inverse() * new_xform; +									} +									se->gizmo->set_subgizmo_transform(GE->key(), new_xform);  								} -  							} else { -								if (_edit.snap || spatial_editor->is_snap_enabled()) { -									motion.snap(Vector3(snap, snap, snap)); -								} - -								Transform3D r; -								r.basis.scale(motion + Vector3(1, 1, 1)); -								t = base * (r * (base.inverse() * original)); - -								// Apply scale -								sp->set_global_transform(t); +								Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords); +								_transform_gizmo_apply(se->sp, new_xform, local_coords);  							}  						} +						spatial_editor->update_transform_gizmo();  						surface->update();  					} break; @@ -1691,8 +1858,6 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  							}  						} -						List<Node *> &selection = editor_selection->get_selected_node_list(); -  						// Disable local transformation for TRANSFORM_VIEW  						bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); @@ -1704,6 +1869,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  						set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +  									String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")"); +						List<Node *> &selection = editor_selection->get_selected_node_list();  						for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {  							Node3D *sp = Object::cast_to<Node3D>(E->get());  							if (!sp) { @@ -1719,30 +1885,20 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  								continue;  							} -							Transform3D original = se->original; -							Transform3D t; - -							if (local_coords) { -								if (_edit.snap || spatial_editor->is_snap_enabled()) { -									Basis g = original.basis.orthonormalized(); -									Vector3 local_motion = g.inverse().xform(motion); -									local_motion.snap(Vector3(snap, snap, snap)); - -									motion = g.xform(local_motion); +							if (se->gizmo.is_valid()) { +								for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) { +									Transform3D xform = GE->get(); +									Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original * xform, xform, motion, snap, local_coords); +									new_xform = se->original.inverse() * new_xform; +									se->gizmo->set_subgizmo_transform(GE->key(), new_xform);  								} -  							} else { -								if (_edit.snap || spatial_editor->is_snap_enabled()) { -									motion.snap(Vector3(snap, snap, snap)); -								} +								Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original, se->original_local, motion, snap, local_coords); +								_transform_gizmo_apply(se->sp, new_xform, false);  							} - -							// Apply translation -							t = original; -							t.origin += motion; -							sp->set_global_transform(t);  						} +						spatial_editor->update_transform_gizmo();  						surface->update();  					} break; @@ -1796,10 +1952,9 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  						set_message(vformat(TTR("Rotating %s degrees."), String::num(angle, snap_step_decimals)));  						angle = Math::deg2rad(angle); -						List<Node *> &selection = editor_selection->get_selected_node_list(); -  						bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW +						List<Node *> &selection = editor_selection->get_selected_node_list();  						for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {  							Node3D *sp = Object::cast_to<Node3D>(E->get());  							if (!sp) { @@ -1815,32 +1970,24 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {  								continue;  							} -							Transform3D t; - -							if (local_coords) { -								Transform3D original_local = se->original_local; -								Basis rot = Basis(axis, angle); - -								t.basis = original_local.get_basis().orthonormalized() * rot; -								t.origin = original_local.origin; - -								// Apply rotation -								sp->set_transform(t); -								sp->set_scale(original_local.basis.get_scale()); // re-apply original scale +							Vector3 compute_axis = local_coords ? axis : plane.normal; +							if (se->gizmo.is_valid()) { +								for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) { +									Transform3D xform = GE->get(); +									Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original * xform, xform, compute_axis, angle, local_coords); +									if (!local_coords) { +										new_xform = se->original.inverse() * new_xform; +									} +									se->gizmo->set_subgizmo_transform(GE->key(), new_xform); +								}  							} else { -								Transform3D original = se->original; -								Transform3D r; -								Transform3D base = Transform3D(Basis(), _edit.center); - -								r.basis.rotate(plane.normal, angle); -								t = base * r * base.inverse() * original; - -								// Apply rotation -								sp->set_global_transform(t); +								Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original, se->original_local, compute_axis, angle, local_coords); +								_transform_gizmo_apply(se->sp, new_xform, local_coords);  							}  						} +						spatial_editor->update_transform_gizmo();  						surface->update();  					} break; @@ -3310,8 +3457,7 @@ void Node3DEditorViewport::_selection_result_pressed(int p_result) {  	clicked = selection_results[p_result].item->get_instance_id();  	if (clicked.is_valid()) { -		_select_clicked(clicked_wants_append, true, spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT); -		clicked = ObjectID(); +		_select_clicked(spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT);  	}  } @@ -3606,6 +3752,13 @@ void Node3DEditorViewport::focus_selection() {  			continue;  		} +		if (se->gizmo.is_valid()) { +			for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) { +				center += se->gizmo->get_subgizmo_transform(GE->key()).origin; +				count++; +			} +		} +  		center += sp->get_global_gizmo_transform().origin;  		count++;  	} @@ -3644,34 +3797,39 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const  			continue;  		} -		Ref<EditorNode3DGizmo> seg = mesh_instance->get_gizmo(); +		Vector<Ref<Node3DGizmo>> gizmos = mesh_instance->get_gizmos(); -		if ((!seg.is_valid()) || found_gizmos.has(seg)) { -			continue; -		} +		for (int j = 0; j < gizmos.size(); j++) { +			Ref<EditorNode3DGizmo> seg = gizmos[j]; -		found_gizmos.insert(seg); +			if ((!seg.is_valid()) || found_gizmos.has(seg)) { +				continue; +			} -		Vector3 hit_point; -		Vector3 hit_normal; -		bool inters = seg->intersect_ray(camera, p_pos, hit_point, hit_normal, nullptr, false); +			found_gizmos.insert(seg); -		if (!inters) { -			continue; -		} +			Vector3 hit_point; +			Vector3 hit_normal; +			bool inters = seg->intersect_ray(camera, p_pos, hit_point, hit_normal); -		float dist = world_pos.distance_to(hit_point); +			if (!inters) { +				continue; +			} -		if (dist < 0) { -			continue; -		} +			float dist = world_pos.distance_to(hit_point); -		if (dist < closest_dist) { -			closest_dist = dist; -			point = hit_point; -			normal = hit_normal; +			if (dist < 0) { +				continue; +			} + +			if (dist < closest_dist) { +				closest_dist = dist; +				point = hit_point; +				normal = hit_normal; +			}  		}  	} +  	Vector3 offset = Vector3();  	for (int i = 0; i < 3; i++) {  		if (normal[i] > 0.0) { @@ -3972,9 +4130,8 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito  	_edit.mode = TRANSFORM_NONE;  	_edit.plane = TRANSFORM_VIEW; -	_edit.edited_gizmo = 0;  	_edit.snap = true; -	_edit.gizmo_handle = 0; +	_edit.gizmo_handle = -1;  	index = p_index;  	editor = p_editor; @@ -3982,7 +4139,6 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito  	editor_selection = editor->get_editor_selection();  	undo_redo = editor->get_undo_redo(); -	clicked_includes_current = false;  	orthogonal = false;  	auto_orthogonal = false;  	lock_rotation = false; @@ -4005,7 +4161,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito  	surface->set_anchors_and_offsets_preset(Control::PRESET_WIDE);  	surface->set_clip_contents(true);  	camera = memnew(Camera3D); -	camera->set_disable_gizmo(true); +	camera->set_disable_gizmos(true);  	camera->set_cull_mask(((1 << 20) - 1) | (1 << (GIZMO_BASE_LAYER + p_index)) | (1 << GIZMO_EDIT_LAYER) | (1 << GIZMO_GRID_LAYER) | (1 << MISC_TOOL_LAYER));  	viewport->add_child(camera);  	camera->make_current(); @@ -4574,43 +4730,54 @@ void Node3DEditor::select_gizmo_highlight_axis(int p_axis) {  }  void Node3DEditor::update_transform_gizmo() { -	List<Node *> &selection = editor_selection->get_selected_node_list(); -	AABB center; -	bool first = true; +	int count = 0; +	bool local_gizmo_coords = are_local_coords_enabled(); +	Vector3 gizmo_center;  	Basis gizmo_basis; -	bool local_gizmo_coords = are_local_coords_enabled(); -	for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { -		Node3D *sp = Object::cast_to<Node3D>(E->get()); -		if (!sp) { -			continue; -		} +	Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr; -		Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); -		if (!se) { -			continue; +	if (se && se->gizmo.is_valid()) { +		for (Map<int, Transform3D>::Element *E = se->subgizmos.front(); E; E = E->next()) { +			Transform3D xf = se->sp->get_global_transform() * se->gizmo->get_subgizmo_transform(E->key()); +			gizmo_center += xf.origin; +			if (count == 0 && local_gizmo_coords) { +				gizmo_basis = xf.basis; +				gizmo_basis.orthonormalize(); +			} +			count++;  		} +	} else { +		List<Node *> &selection = editor_selection->get_selected_node_list(); +		for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { +			Node3D *sp = Object::cast_to<Node3D>(E->get()); +			if (!sp) { +				continue; +			} -		Transform3D xf = se->sp->get_global_gizmo_transform(); +			if (sp->has_meta("_edit_lock_")) { +				continue; +			} + +			Node3DEditorSelectedItem *sel_item = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); +			if (!sel_item) { +				continue; +			} -		if (first) { -			center.position = xf.origin; -			first = false; -			if (local_gizmo_coords) { +			Transform3D xf = sel_item->sp->get_global_transform(); +			gizmo_center += xf.origin; +			if (count == 0 && local_gizmo_coords) {  				gizmo_basis = xf.basis;  				gizmo_basis.orthonormalize();  			} -		} else { -			center.expand_to(xf.origin); -			gizmo_basis = Basis(); +			count++;  		}  	} -	Vector3 pcenter = center.position + center.size * 0.5; -	gizmo.visible = !first; -	gizmo.transform.origin = pcenter; -	gizmo.transform.basis = gizmo_basis; +	gizmo.visible = count > 0; +	gizmo.transform.origin = (count > 0) ? gizmo_center / count : Vector3(); +	gizmo.transform.basis = (count == 1) ? gizmo_basis : Basis();  	for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {  		viewports[i]->update_transform_gizmo_view(); @@ -4621,7 +4788,7 @@ void _update_all_gizmos(Node *p_node) {  	for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {  		Node3D *spatial_node = Object::cast_to<Node3D>(p_node->get_child(i));  		if (spatial_node) { -			spatial_node->update_gizmo(); +			spatial_node->update_gizmos();  		}  		_update_all_gizmos(p_node->get_child(i)); @@ -4940,22 +5107,38 @@ void Node3DEditor::set_state(const Dictionary &p_state) {  void Node3DEditor::edit(Node3D *p_spatial) {  	if (p_spatial != selected) {  		if (selected) { -			Ref<EditorNode3DGizmo> seg = selected->get_gizmo(); -			if (seg.is_valid()) { +			Vector<Ref<Node3DGizmo>> gizmos = selected->get_gizmos(); +			for (int i = 0; i < gizmos.size(); i++) { +				Ref<EditorNode3DGizmo> seg = gizmos[i]; +				if (!seg.is_valid()) { +					continue; +				}  				seg->set_selected(false); -				selected->update_gizmo();  			} + +			Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected); +			if (se) { +				se->gizmo.unref(); +				se->subgizmos.clear(); +			} + +			selected->update_gizmos();  		}  		selected = p_spatial; -		over_gizmo_handle = -1; +		current_hover_gizmo = Ref<EditorNode3DGizmo>(); +		current_hover_gizmo_handle = -1;  		if (selected) { -			Ref<EditorNode3DGizmo> seg = selected->get_gizmo(); -			if (seg.is_valid()) { +			Vector<Ref<Node3DGizmo>> gizmos = selected->get_gizmos(); +			for (int i = 0; i < gizmos.size(); i++) { +				Ref<EditorNode3DGizmo> seg = gizmos[i]; +				if (!seg.is_valid()) { +					continue; +				}  				seg->set_selected(true); -				selected->update_gizmo();  			} +			selected->update_gizmos();  		}  	}  } @@ -6027,13 +6210,27 @@ void Node3DEditor::update_grid() {  	_init_grid();  } -bool Node3DEditor::is_any_freelook_active() const { -	for (unsigned int i = 0; i < VIEWPORTS_COUNT; ++i) { -		if (viewports[i]->is_freelook_active()) { -			return true; +void Node3DEditor::_selection_changed() { +	_refresh_menu_icons(); +	if (selected && editor_selection->get_selected_node_list().size() != 1) { +		Vector<Ref<Node3DGizmo>> gizmos = selected->get_gizmos(); +		for (int i = 0; i < gizmos.size(); i++) { +			Ref<EditorNode3DGizmo> seg = gizmos[i]; +			if (!seg.is_valid()) { +				continue; +			} +			seg->set_selected(false); +		} + +		Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected); +		if (se) { +			se->gizmo.unref(); +			se->subgizmos.clear();  		} +		selected->update_gizmos(); +		selected = nullptr;  	} -	return false; +	update_transform_gizmo();  }  void Node3DEditor::_refresh_menu_icons() { @@ -6325,7 +6522,7 @@ void Node3DEditor::_notification(int p_what) {  		get_tree()->connect("node_removed", callable_mp(this, &Node3DEditor::_node_removed));  		get_tree()->connect("node_added", callable_mp(this, &Node3DEditor::_node_added));  		EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->connect("node_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons)); -		editor_selection->connect("selection_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons)); +		editor_selection->connect("selection_changed", callable_mp(this, &Node3DEditor::_selection_changed));  		editor->connect("stop_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button), make_binds(false));  		editor->connect("play_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button), make_binds(true)); @@ -6384,6 +6581,34 @@ void Node3DEditor::_notification(int p_what) {  	}  } +bool Node3DEditor::is_subgizmo_selected(int p_id) { +	Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr; +	if (se) { +		return se->subgizmos.has(p_id); +	} +	return false; +} + +bool Node3DEditor::is_current_selected_gizmo(const EditorNode3DGizmo *p_gizmo) { +	Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr; +	if (se) { +		return se->gizmo == p_gizmo; +	} +	return false; +} + +Vector<int> Node3DEditor::get_subgizmo_selection() { +	Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr; + +	Vector<int> ret; +	if (se) { +		for (Map<int, Transform3D>::Element *E = se->subgizmos.front(); E; E = E->next()) { +			ret.push_back(E->key()); +		} +	} +	return ret; +} +  void Node3DEditor::add_control_to_menu_panel(Control *p_control) {  	hbc_menu->add_child(p_control);  } @@ -6411,23 +6636,43 @@ void Node3DEditor::_request_gizmo(Object *p_obj) {  	if (!sp) {  		return;  	} -	if (editor->get_edited_scene() && (sp == editor->get_edited_scene() || (sp->get_owner() && editor->get_edited_scene()->is_ancestor_of(sp)))) { -		Ref<EditorNode3DGizmo> seg; +	bool is_selected = (sp == selected); + +	if (editor->get_edited_scene() && (sp == editor->get_edited_scene() || (sp->get_owner() && editor->get_edited_scene()->is_ancestor_of(sp)))) {  		for (int i = 0; i < gizmo_plugins_by_priority.size(); ++i) { -			seg = gizmo_plugins_by_priority.write[i]->get_gizmo(sp); +			Ref<EditorNode3DGizmo> seg = gizmo_plugins_by_priority.write[i]->get_gizmo(sp);  			if (seg.is_valid()) { -				sp->set_gizmo(seg); +				sp->add_gizmo(seg); -				if (sp == selected) { -					seg->set_selected(true); -					selected->update_gizmo(); +				if (is_selected != seg->is_selected()) { +					seg->set_selected(is_selected);  				} - -				break;  			}  		} +		sp->update_gizmos(); +	} +} + +void Node3DEditor::_clear_subgizmo_selection(Object *p_obj) { +	Node3D *sp = nullptr; +	if (p_obj) { +		sp = Object::cast_to<Node3D>(p_obj); +	} else { +		sp = selected; +	} + +	if (!sp) { +		return; +	} + +	Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp); +	if (se) { +		se->subgizmos.clear(); +		se->gizmo.unref(); +		sp->update_gizmos(); +		update_transform_gizmo();  	}  } @@ -6516,7 +6761,13 @@ void Node3DEditor::_node_removed(Node *p_node) {  	}  	if (p_node == selected) { +		Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected); +		if (se) { +			se->gizmo.unref(); +			se->subgizmos.clear(); +		}  		selected = nullptr; +		update_transform_gizmo();  	}  } @@ -6554,6 +6805,7 @@ void Node3DEditor::_bind_methods() {  	ClassDB::bind_method("_unhandled_key_input", &Node3DEditor::_unhandled_key_input);  	ClassDB::bind_method("_get_editor_data", &Node3DEditor::_get_editor_data);  	ClassDB::bind_method("_request_gizmo", &Node3DEditor::_request_gizmo); +	ClassDB::bind_method("_clear_subgizmo_selection", &Node3DEditor::_clear_subgizmo_selection);  	ClassDB::bind_method("_refresh_menu_icons", &Node3DEditor::_refresh_menu_icons);  	ADD_SIGNAL(MethodInfo("transform_key_request")); @@ -7130,7 +7382,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {  	EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::FLOAT, "editors/3d/manipulator_gizmo_opacity", PROPERTY_HINT_RANGE, "0,1,0.01"));  	EDITOR_DEF("editors/3d/navigation/show_viewport_rotation_gizmo", true); -	over_gizmo_handle = -1; +	current_hover_gizmo_handle = -1;  	{  		//sun popup @@ -7444,303 +7696,3 @@ Node3DEditorPlugin::Node3DEditorPlugin(EditorNode *p_node) {  Node3DEditorPlugin::~Node3DEditorPlugin() {  } - -void EditorNode3DGizmoPlugin::create_material(const String &p_name, const Color &p_color, bool p_billboard, bool p_on_top, bool p_use_vertex_color) { -	Color instantiated_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instantiated", Color(0.7, 0.7, 0.7, 0.6)); - -	Vector<Ref<StandardMaterial3D>> mats; - -	for (int i = 0; i < 4; i++) { -		bool selected = i % 2 == 1; -		bool instantiated = i < 2; - -		Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); - -		Color color = instantiated ? instantiated_color : p_color; - -		if (!selected) { -			color.a *= 0.3; -		} - -		material->set_albedo(color); -		material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); -		material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); -		material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1); -		material->set_cull_mode(StandardMaterial3D::CULL_DISABLED); - -		if (p_use_vertex_color) { -			material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); -			material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); -		} - -		if (p_billboard) { -			material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); -		} - -		if (p_on_top && selected) { -			material->set_on_top_of_alpha(); -		} - -		mats.push_back(material); -	} - -	materials[p_name] = mats; -} - -void EditorNode3DGizmoPlugin::create_icon_material(const String &p_name, const Ref<Texture2D> &p_texture, bool p_on_top, const Color &p_albedo) { -	Color instantiated_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instantiated", Color(0.7, 0.7, 0.7, 0.6)); - -	Vector<Ref<StandardMaterial3D>> icons; - -	for (int i = 0; i < 4; i++) { -		bool selected = i % 2 == 1; -		bool instantiated = i < 2; - -		Ref<StandardMaterial3D> icon = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); - -		Color color = instantiated ? instantiated_color : p_albedo; - -		if (!selected) { -			color.a *= 0.85; -		} - -		icon->set_albedo(color); - -		icon->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); -		icon->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); -		icon->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); -		icon->set_cull_mode(StandardMaterial3D::CULL_DISABLED); -		icon->set_depth_draw_mode(StandardMaterial3D::DEPTH_DRAW_DISABLED); -		icon->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); -		icon->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, p_texture); -		icon->set_flag(StandardMaterial3D::FLAG_FIXED_SIZE, true); -		icon->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); -		icon->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN); - -		if (p_on_top && selected) { -			icon->set_on_top_of_alpha(); -		} - -		icons.push_back(icon); -	} - -	materials[p_name] = icons; -} - -void EditorNode3DGizmoPlugin::create_handle_material(const String &p_name, bool p_billboard, const Ref<Texture2D> &p_icon) { -	Ref<StandardMaterial3D> handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); - -	handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); -	handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); -	Ref<Texture2D> handle_t = p_icon != nullptr ? p_icon : Node3DEditor::get_singleton()->get_theme_icon(SNAME("Editor3DHandle"), SNAME("EditorIcons")); -	handle_material->set_point_size(handle_t->get_width()); -	handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle_t); -	handle_material->set_albedo(Color(1, 1, 1)); -	handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); -	handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); -	handle_material->set_on_top_of_alpha(); -	if (p_billboard) { -		handle_material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED); -		handle_material->set_on_top_of_alpha(); -	} -	handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - -	materials[p_name] = Vector<Ref<StandardMaterial3D>>(); -	materials[p_name].push_back(handle_material); -} - -void EditorNode3DGizmoPlugin::add_material(const String &p_name, Ref<StandardMaterial3D> p_material) { -	materials[p_name] = Vector<Ref<StandardMaterial3D>>(); -	materials[p_name].push_back(p_material); -} - -Ref<StandardMaterial3D> EditorNode3DGizmoPlugin::get_material(const String &p_name, const Ref<EditorNode3DGizmo> &p_gizmo) { -	ERR_FAIL_COND_V(!materials.has(p_name), Ref<StandardMaterial3D>()); -	ERR_FAIL_COND_V(materials[p_name].size() == 0, Ref<StandardMaterial3D>()); - -	if (p_gizmo.is_null() || materials[p_name].size() == 1) { -		return materials[p_name][0]; -	} - -	int index = (p_gizmo->is_selected() ? 1 : 0) + (p_gizmo->is_editable() ? 2 : 0); - -	Ref<StandardMaterial3D> mat = materials[p_name][index]; - -	if (current_state == ON_TOP && p_gizmo->is_selected()) { -		mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); -	} else { -		mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, false); -	} - -	return mat; -} - -String EditorNode3DGizmoPlugin::get_gizmo_name() const { -	if (get_script_instance() && get_script_instance()->has_method("_get_gizmo_name")) { -		return get_script_instance()->call("_get_gizmo_name"); -	} -	return TTR("Nameless gizmo"); -} - -int EditorNode3DGizmoPlugin::get_priority() const { -	if (get_script_instance() && get_script_instance()->has_method("_get_priority")) { -		return get_script_instance()->call("_get_priority"); -	} -	return 0; -} - -Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::get_gizmo(Node3D *p_spatial) { -	if (get_script_instance() && get_script_instance()->has_method("get_gizmo")) { -		return get_script_instance()->call("get_gizmo", p_spatial); -	} - -	Ref<EditorNode3DGizmo> ref = create_gizmo(p_spatial); - -	if (ref.is_null()) { -		return ref; -	} - -	ref->set_plugin(this); -	ref->set_spatial_node(p_spatial); -	ref->set_hidden(current_state == HIDDEN); - -	current_gizmos.push_back(ref.ptr()); -	return ref; -} - -void EditorNode3DGizmoPlugin::_bind_methods() { -#define GIZMO_REF PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "EditorNode3DGizmo") - -	BIND_VMETHOD(MethodInfo(Variant::BOOL, "_has_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); -	BIND_VMETHOD(MethodInfo(GIZMO_REF, "_create_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); - -	ClassDB::bind_method(D_METHOD("create_material", "name", "color", "billboard", "on_top", "use_vertex_color"), &EditorNode3DGizmoPlugin::create_material, DEFVAL(false), DEFVAL(false), DEFVAL(false)); -	ClassDB::bind_method(D_METHOD("create_icon_material", "name", "texture", "on_top", "color"), &EditorNode3DGizmoPlugin::create_icon_material, DEFVAL(false), DEFVAL(Color(1, 1, 1, 1))); -	ClassDB::bind_method(D_METHOD("create_handle_material", "name", "billboard", "texture"), &EditorNode3DGizmoPlugin::create_handle_material, DEFVAL(false), DEFVAL(Variant())); -	ClassDB::bind_method(D_METHOD("add_material", "name", "material"), &EditorNode3DGizmoPlugin::add_material); - -	ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorNode3DGizmoPlugin::get_material, DEFVAL(Ref<EditorNode3DGizmo>())); - -	BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_gizmo_name")); -	BIND_VMETHOD(MethodInfo(Variant::INT, "_get_priority")); -	BIND_VMETHOD(MethodInfo(Variant::BOOL, "_can_be_hidden")); -	BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_selectable_when_hidden")); - -	BIND_VMETHOD(MethodInfo("_redraw", GIZMO_REF)); -	BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_handle_name", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); - -	MethodInfo hvget(Variant::NIL, "_get_handle_value", GIZMO_REF, PropertyInfo(Variant::INT, "index")); -	hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; -	BIND_VMETHOD(hvget); - -	BIND_VMETHOD(MethodInfo("_set_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"), PropertyInfo(Variant::VECTOR2, "point"))); -	MethodInfo cm = MethodInfo("_commit_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel")); -	cm.default_arguments.push_back(false); -	BIND_VMETHOD(cm); - -	BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_handle_highlighted", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); - -#undef GIZMO_REF -} - -bool EditorNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { -	if (get_script_instance() && get_script_instance()->has_method("_has_gizmo")) { -		return get_script_instance()->call("_has_gizmo", p_spatial); -	} -	return false; -} - -Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial) { -	if (get_script_instance() && get_script_instance()->has_method("_create_gizmo")) { -		return get_script_instance()->call("_create_gizmo", p_spatial); -	} - -	Ref<EditorNode3DGizmo> ref; -	if (has_gizmo(p_spatial)) { -		ref.instantiate(); -	} -	return ref; -} - -bool EditorNode3DGizmoPlugin::can_be_hidden() const { -	if (get_script_instance() && get_script_instance()->has_method("_can_be_hidden")) { -		return get_script_instance()->call("_can_be_hidden"); -	} -	return true; -} - -bool EditorNode3DGizmoPlugin::is_selectable_when_hidden() const { -	if (get_script_instance() && get_script_instance()->has_method("_is_selectable_when_hidden")) { -		return get_script_instance()->call("_is_selectable_when_hidden"); -	} -	return false; -} - -void EditorNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { -	if (get_script_instance() && get_script_instance()->has_method("_redraw")) { -		Ref<EditorNode3DGizmo> ref(p_gizmo); -		get_script_instance()->call("_redraw", ref); -	} -} - -String EditorNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { -	if (get_script_instance() && get_script_instance()->has_method("_get_handle_name")) { -		return get_script_instance()->call("_get_handle_name", p_gizmo, p_idx); -	} -	return ""; -} - -Variant EditorNode3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { -	if (get_script_instance() && get_script_instance()->has_method("_get_handle_value")) { -		return get_script_instance()->call("_get_handle_value", p_gizmo, p_idx); -	} -	return Variant(); -} - -void EditorNode3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { -	if (get_script_instance() && get_script_instance()->has_method("_set_handle")) { -		get_script_instance()->call("_set_handle", p_gizmo, p_idx, p_camera, p_point); -	} -} - -void EditorNode3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { -	if (get_script_instance() && get_script_instance()->has_method("_commit_handle")) { -		get_script_instance()->call("_commit_handle", p_gizmo, p_idx, p_restore, p_cancel); -	} -} - -bool EditorNode3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_idx) const { -	if (get_script_instance() && get_script_instance()->has_method("_is_handle_highlighted")) { -		return get_script_instance()->call("_is_handle_highlighted", p_gizmo, p_idx); -	} -	return false; -} - -void EditorNode3DGizmoPlugin::set_state(int p_state) { -	current_state = p_state; -	for (int i = 0; i < current_gizmos.size(); ++i) { -		current_gizmos[i]->set_hidden(current_state == HIDDEN); -	} -} - -int EditorNode3DGizmoPlugin::get_state() const { -	return current_state; -} - -void EditorNode3DGizmoPlugin::unregister_gizmo(EditorNode3DGizmo *p_gizmo) { -	current_gizmos.erase(p_gizmo); -} - -EditorNode3DGizmoPlugin::EditorNode3DGizmoPlugin() { -	current_state = VISIBLE; -} - -EditorNode3DGizmoPlugin::~EditorNode3DGizmoPlugin() { -	for (int i = 0; i < current_gizmos.size(); ++i) { -		current_gizmos[i]->set_plugin(nullptr); -		current_gizmos[i]->get_spatial_node()->set_gizmo(nullptr); -	} -	if (Node3DEditor::get_singleton()) { -		Node3DEditor::get_singleton()->update_all_gizmos(); -	} -} diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index cc65cb53ca..576e4c93d3 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -34,6 +34,7 @@  #include "editor/editor_node.h"  #include "editor/editor_plugin.h"  #include "editor/editor_scale.h" +#include "editor/plugins/node_3d_editor_gizmos.h"  #include "scene/3d/light_3d.h"  #include "scene/3d/visual_instance_3d.h"  #include "scene/3d/world_environment.h" @@ -43,96 +44,10 @@  class Camera3D;  class Node3DEditor; -class EditorNode3DGizmoPlugin;  class Node3DEditorViewport;  class SubViewportContainer; - -class EditorNode3DGizmo : public Node3DGizmo { -	GDCLASS(EditorNode3DGizmo, Node3DGizmo); - -	bool selected; -	bool instantiated; - -public: -	void set_selected(bool p_selected) { selected = p_selected; } -	bool is_selected() const { return selected; } - -	struct Instance { -		RID instance; -		Ref<ArrayMesh> mesh; -		Ref<Material> material; -		Ref<SkinReference> skin_reference; -		RID skeleton; -		bool billboard = false; -		bool unscaled = false; -		bool can_intersect = false; -		bool extra_margin = false; - -		void create_instance(Node3D *p_base, bool p_hidden = false); -	}; - -	Vector<Vector3> collision_segments; -	Ref<TriangleMesh> collision_mesh; - -	struct Handle { -		Vector3 pos; -		bool billboard = false; -	}; - -	Vector<Vector3> handles; -	Vector<Vector3> secondary_handles; -	float selectable_icon_size; -	bool billboard_handle; - -	bool valid; -	bool hidden; -	Node3D *base; -	Vector<Instance> instances; -	Node3D *spatial_node; -	EditorNode3DGizmoPlugin *gizmo_plugin; - -	void _set_spatial_node(Node *p_node) { set_spatial_node(Object::cast_to<Node3D>(p_node)); } - -protected: -	static void _bind_methods(); - -public: -	void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1)); -	void add_vertices(const Vector<Vector3> &p_vertices, const Ref<Material> &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1)); -	void add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard = false, const Ref<SkinReference> &p_skin_reference = Ref<SkinReference>(), const Ref<Material> &p_material = Ref<Material>()); -	void add_collision_segments(const Vector<Vector3> &p_lines); -	void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh); -	void add_unscaled_billboard(const Ref<Material> &p_material, float p_scale = 1, const Color &p_modulate = Color(1, 1, 1)); -	void add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, bool p_billboard = false, bool p_secondary = false); -	void add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3()); - -	virtual bool is_handle_highlighted(int p_idx) const; -	virtual String get_handle_name(int p_idx) const; -	virtual Variant get_handle_value(int p_idx); -	virtual void set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point); -	virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false); - -	void set_spatial_node(Node3D *p_node); -	Node3D *get_spatial_node() const { return spatial_node; } -	Ref<EditorNode3DGizmoPlugin> get_plugin() const { return gizmo_plugin; } -	Vector3 get_handle_pos(int p_idx) const; -	bool intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum); -	bool intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = nullptr, bool p_sec_first = false); - -	virtual void clear() override; -	virtual void create() override; -	virtual void transform() override; -	virtual void redraw() override; -	virtual void free() override; - -	virtual bool is_editable() const; - -	void set_hidden(bool p_hidden); -	void set_plugin(EditorNode3DGizmoPlugin *p_plugin); - -	EditorNode3DGizmo(); -	~EditorNode3DGizmo(); -}; +class DirectionalLight3D; +class WorldEnvironment;  class ViewportRotationControl : public Control {  	GDCLASS(ViewportRotationControl, Control); @@ -307,17 +222,15 @@ private:  	struct _RayResult {  		Node3D *item = nullptr;  		float depth = 0; -		int handle = 0;  		_FORCE_INLINE_ bool operator<(const _RayResult &p_rr) const { return depth < p_rr.depth; }  	};  	void _update_name();  	void _compute_edit(const Point2 &p_point);  	void _clear_selected(); -	void _select_clicked(bool p_append, bool p_single, bool p_allow_locked = false); -	void _select(Node *p_node, bool p_append, bool p_single); -	ObjectID _select_ray(const Point2 &p_pos, bool p_append, bool &r_includes_current, int *r_gizmo_handle = nullptr, bool p_alt_select = false); -	void _find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select = false, bool p_include_locked_nodes = false); +	void _select_clicked(bool p_allow_locked); +	ObjectID _select_ray(const Point2 &p_pos); +	void _find_items_at_pos(const Point2 &p_pos, Vector<_RayResult> &r_results, bool p_include_locked);  	Vector3 _get_ray_pos(const Vector2 &p_pos) const;  	Vector3 _get_ray(const Vector2 &p_pos) const;  	Point2 _point_to_screen(const Vector3 &p_point); @@ -329,7 +242,8 @@ private:  	Vector3 _get_screen_to_space(const Vector3 &p_vector3);  	void _select_region(); -	bool _gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only = false); +	bool _transform_gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only = false); +	void _transform_gizmo_apply(Node3D *p_node, const Transform3D &p_transform, bool p_local);  	void _nav_pan(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative);  	void _nav_zoom(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative); @@ -342,7 +256,6 @@ private:  	ObjectID clicked;  	Vector<_RayResult> selection_results; -	bool clicked_includes_current;  	bool clicked_wants_append;  	PopupMenu *selection_menu; @@ -383,15 +296,12 @@ private:  		Vector3 click_ray;  		Vector3 click_ray_pos;  		Vector3 center; -		Vector3 orig_gizmo_pos; -		int edited_gizmo = 0;  		Point2 mouse_pos;  		Point2 original_mouse_pos;  		bool snap = false;  		Ref<EditorNode3DGizmo> gizmo;  		int gizmo_handle = 0;  		Variant gizmo_initial_value; -		Vector3 gizmo_initial_pos;  	} _edit;  	struct Cursor { @@ -472,6 +382,8 @@ private:  	void _project_settings_changed(); +	Transform3D _compute_transform(TransformMode p_mode, const Transform3D &p_original, const Transform3D &p_original_local, Vector3 p_motion, double p_extra, bool p_local); +  protected:  	void _notification(int p_what);  	static void _bind_methods(); @@ -512,6 +424,8 @@ public:  	Node3D *sp;  	RID sbox_instance;  	RID sbox_instance_xray; +	Ref<EditorNode3DGizmo> gizmo; +	Map<int, Transform3D> subgizmos; // map ID -> initial transform  	Node3DEditorSelectedItem() {  		sp = nullptr; @@ -617,7 +531,9 @@ private:  	Ref<StandardMaterial3D> plane_gizmo_color_hl[3];  	Ref<ShaderMaterial> rotate_gizmo_color_hl[3]; -	int over_gizmo_handle; +	Ref<Node3DGizmo> current_hover_gizmo; +	int current_hover_gizmo_handle; +  	float snap_translate_value;  	float snap_rotate_value;  	float snap_scale_value; @@ -688,7 +604,6 @@ private:  	LineEdit *snap_translate;  	LineEdit *snap_rotate;  	LineEdit *snap_scale; -	PanelContainer *menu_panel;  	LineEdit *xform_translate[3];  	LineEdit *xform_rotate[3]; @@ -734,6 +649,7 @@ private:  	Node3D *selected;  	void _request_gizmo(Object *p_obj); +	void _clear_subgizmo_selection(Object *p_obj = nullptr);  	static Node3DEditor *singleton; @@ -746,8 +662,7 @@ private:  	Node3DEditor(); -	bool is_any_freelook_active() const; - +	void _selection_changed();  	void _refresh_menu_icons();  	// Preview Sun and Environment @@ -861,10 +776,16 @@ public:  	VSplitContainer *get_shader_split();  	HSplitContainer *get_palette_split(); -	Node3D *get_selected() { return selected; } +	Node3D *get_single_selected_node() { return selected; } +	bool is_current_selected_gizmo(const EditorNode3DGizmo *p_gizmo); +	bool is_subgizmo_selected(int p_id); +	Vector<int> get_subgizmo_selection(); + +	Ref<EditorNode3DGizmo> get_current_hover_gizmo() const { return current_hover_gizmo; } +	void set_current_hover_gizmo(Ref<EditorNode3DGizmo> p_gizmo) { current_hover_gizmo = p_gizmo; } -	int get_over_gizmo_handle() const { return over_gizmo_handle; } -	void set_over_gizmo_handle(int idx) { over_gizmo_handle = idx; } +	void set_current_hover_gizmo_handle(int p_id) { current_hover_gizmo_handle = p_id; } +	int get_current_hover_gizmo_handle() const { return current_hover_gizmo_handle; }  	void set_can_preview(Camera3D *p_preview); @@ -907,50 +828,4 @@ public:  	~Node3DEditorPlugin();  }; -class EditorNode3DGizmoPlugin : public Resource { -	GDCLASS(EditorNode3DGizmoPlugin, Resource); - -public: -	static const int VISIBLE = 0; -	static const int HIDDEN = 1; -	static const int ON_TOP = 2; - -protected: -	int current_state; -	List<EditorNode3DGizmo *> current_gizmos; -	HashMap<String, Vector<Ref<StandardMaterial3D>>> materials; - -	static void _bind_methods(); -	virtual bool has_gizmo(Node3D *p_spatial); -	virtual Ref<EditorNode3DGizmo> create_gizmo(Node3D *p_spatial); - -public: -	void create_material(const String &p_name, const Color &p_color, bool p_billboard = false, bool p_on_top = false, bool p_use_vertex_color = false); -	void create_icon_material(const String &p_name, const Ref<Texture2D> &p_texture, bool p_on_top = false, const Color &p_albedo = Color(1, 1, 1, 1)); -	void create_handle_material(const String &p_name, bool p_billboard = false, const Ref<Texture2D> &p_texture = nullptr); -	void add_material(const String &p_name, Ref<StandardMaterial3D> p_material); - -	Ref<StandardMaterial3D> get_material(const String &p_name, const Ref<EditorNode3DGizmo> &p_gizmo = Ref<EditorNode3DGizmo>()); - -	virtual String get_gizmo_name() const; -	virtual int get_priority() const; -	virtual bool can_be_hidden() const; -	virtual bool is_selectable_when_hidden() const; - -	virtual void redraw(EditorNode3DGizmo *p_gizmo); -	virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const; -	virtual Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const; -	virtual void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point); -	virtual void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); -	virtual bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_idx) const; - -	Ref<EditorNode3DGizmo> get_gizmo(Node3D *p_spatial); -	void set_state(int p_state); -	int get_state() const; -	void unregister_gizmo(EditorNode3DGizmo *p_gizmo); - -	EditorNode3DGizmoPlugin(); -	virtual ~EditorNode3DGizmoPlugin(); -}; -  #endif // NODE_3D_EDITOR_PLUGIN_H diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 0a95986fe2..63b89aea35 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -36,20 +36,20 @@  #include "node_3d_editor_plugin.h"  #include "scene/resources/curve.h" -String Path3DGizmo::get_handle_name(int p_idx) const { +String Path3DGizmo::get_handle_name(int p_id) const {  	Ref<Curve3D> c = path->get_curve();  	if (c.is_null()) {  		return "";  	} -	if (p_idx < c->get_point_count()) { -		return TTR("Curve Point #") + itos(p_idx); +	if (p_id < c->get_point_count()) { +		return TTR("Curve Point #") + itos(p_id);  	} -	p_idx = p_idx - c->get_point_count() + 1; +	p_id = p_id - c->get_point_count() + 1; -	int idx = p_idx / 2; -	int t = p_idx % 2; +	int idx = p_id / 2; +	int t = p_id % 2;  	String n = TTR("Curve Point #") + itos(idx);  	if (t == 0) {  		n += " In"; @@ -60,21 +60,21 @@ String Path3DGizmo::get_handle_name(int p_idx) const {  	return n;  } -Variant Path3DGizmo::get_handle_value(int p_idx) { +Variant Path3DGizmo::get_handle_value(int p_id) const {  	Ref<Curve3D> c = path->get_curve();  	if (c.is_null()) {  		return Variant();  	} -	if (p_idx < c->get_point_count()) { -		original = c->get_point_position(p_idx); +	if (p_id < c->get_point_count()) { +		original = c->get_point_position(p_id);  		return original;  	} -	p_idx = p_idx - c->get_point_count() + 1; +	p_id = p_id - c->get_point_count() + 1; -	int idx = p_idx / 2; -	int t = p_idx % 2; +	int idx = p_id / 2; +	int t = p_id % 2;  	Vector3 ofs;  	if (t == 0) { @@ -88,7 +88,7 @@ Variant Path3DGizmo::get_handle_value(int p_idx) {  	return ofs;  } -void Path3DGizmo::set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void Path3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	Ref<Curve3D> c = path->get_curve();  	if (c.is_null()) {  		return; @@ -100,7 +100,7 @@ void Path3DGizmo::set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_poin  	Vector3 ray_dir = p_camera->project_ray_normal(p_point);  	// Setting curve point positions -	if (p_idx < c->get_point_count()) { +	if (p_id < c->get_point_count()) {  		Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2));  		Vector3 inters; @@ -112,16 +112,16 @@ void Path3DGizmo::set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_poin  			}  			Vector3 local = gi.xform(inters); -			c->set_point_position(p_idx, local); +			c->set_point_position(p_id, local);  		}  		return;  	} -	p_idx = p_idx - c->get_point_count() + 1; +	p_id = p_id - c->get_point_count() + 1; -	int idx = p_idx / 2; -	int t = p_idx % 2; +	int idx = p_id / 2; +	int t = p_id % 2;  	Vector3 base = c->get_point_position(idx); @@ -157,7 +157,7 @@ void Path3DGizmo::set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_poin  	}  } -void Path3DGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_cancel) { +void Path3DGizmo::commit_handle(int p_id, const Variant &p_restore, bool p_cancel) const {  	Ref<Curve3D> c = path->get_curve();  	if (c.is_null()) {  		return; @@ -165,27 +165,27 @@ void Path3DGizmo::commit_handle(int p_idx, const Variant &p_restore, bool p_canc  	UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); -	if (p_idx < c->get_point_count()) { +	if (p_id < c->get_point_count()) {  		if (p_cancel) { -			c->set_point_position(p_idx, p_restore); +			c->set_point_position(p_id, p_restore);  			return;  		}  		ur->create_action(TTR("Set Curve Point Position")); -		ur->add_do_method(c.ptr(), "set_point_position", p_idx, c->get_point_position(p_idx)); -		ur->add_undo_method(c.ptr(), "set_point_position", p_idx, p_restore); +		ur->add_do_method(c.ptr(), "set_point_position", p_id, c->get_point_position(p_id)); +		ur->add_undo_method(c.ptr(), "set_point_position", p_id, p_restore);  		ur->commit_action();  		return;  	} -	p_idx = p_idx - c->get_point_count() + 1; +	p_id = p_id - c->get_point_count() + 1; -	int idx = p_idx / 2; -	int t = p_idx % 2; +	int idx = p_id / 2; +	int t = p_id % 2;  	if (t == 0) {  		if (p_cancel) { -			c->set_point_in(p_idx, p_restore); +			c->set_point_in(p_id, p_restore);  			return;  		} @@ -282,7 +282,7 @@ void Path3DGizmo::redraw() {  			add_handles(handles, handles_material);  		}  		if (sec_handles.size()) { -			add_handles(sec_handles, sec_handles_material, false, true); +			add_handles(sec_handles, sec_handles_material, Vector<int>(), false, true);  		}  	}  } diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h index 13870d7591..5902500526 100644 --- a/editor/plugins/path_3d_editor_plugin.h +++ b/editor/plugins/path_3d_editor_plugin.h @@ -31,7 +31,9 @@  #ifndef PATH_EDITOR_PLUGIN_H  #define PATH_EDITOR_PLUGIN_H -#include "editor/node_3d_editor_gizmos.h" +#include "editor/editor_plugin.h" +#include "editor/plugins/node_3d_editor_gizmos.h" +#include "scene/3d/camera_3d.h"  #include "scene/3d/path_3d.h"  class Path3DGizmo : public EditorNode3DGizmo { @@ -44,9 +46,9 @@ class Path3DGizmo : public EditorNode3DGizmo {  public:  	virtual String get_handle_name(int p_idx) const override; -	virtual Variant get_handle_value(int p_idx) override; -	virtual void set_handle(int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	virtual void commit_handle(int p_idx, const Variant &p_restore, bool p_cancel = false) override; +	virtual Variant get_handle_value(int p_id) const override; +	virtual void set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	virtual void commit_handle(int p_id, const Variant &p_restore, bool p_cancel = false) const override;  	virtual void redraw() override;  	Path3DGizmo(Path3D *p_path = nullptr); diff --git a/modules/csg/csg_gizmos.cpp b/modules/csg/csg_gizmos.cpp index 37a7d96de5..fc84c029ec 100644 --- a/modules/csg/csg_gizmos.cpp +++ b/modules/csg/csg_gizmos.cpp @@ -29,6 +29,8 @@  /*************************************************************************/  #include "csg_gizmos.h" +#include "editor/plugins/node_3d_editor_plugin.h" +#include "scene/3d/camera_3d.h"  /////////// @@ -48,7 +50,7 @@ CSGShape3DGizmoPlugin::CSGShape3DGizmoPlugin() {  	create_handle_material("handles");  } -String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { +String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());  	if (Object::cast_to<CSGSphere3D>(cs)) { @@ -60,17 +62,17 @@ String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo,  	}  	if (Object::cast_to<CSGCylinder3D>(cs)) { -		return p_idx == 0 ? "Radius" : "Height"; +		return p_id == 0 ? "Radius" : "Height";  	}  	if (Object::cast_to<CSGTorus3D>(cs)) { -		return p_idx == 0 ? "InnerRadius" : "OuterRadius"; +		return p_id == 0 ? "InnerRadius" : "OuterRadius";  	}  	return "";  } -Variant CSGShape3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { +Variant CSGShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {  	CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());  	if (Object::cast_to<CSGSphere3D>(cs)) { @@ -85,18 +87,18 @@ Variant CSGShape3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int  	if (Object::cast_to<CSGCylinder3D>(cs)) {  		CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs); -		return p_idx == 0 ? s->get_radius() : s->get_height(); +		return p_id == 0 ? s->get_radius() : s->get_height();  	}  	if (Object::cast_to<CSGTorus3D>(cs)) {  		CSGTorus3D *s = Object::cast_to<CSGTorus3D>(cs); -		return p_idx == 0 ? s->get_inner_radius() : s->get_outer_radius(); +		return p_id == 0 ? s->get_inner_radius() : s->get_outer_radius();  	}  	return Variant();  } -void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { +void CSGShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const {  	CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());  	Transform3D gt = cs->get_global_transform(); @@ -129,10 +131,10 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca  		CSGBox3D *s = Object::cast_to<CSGBox3D>(cs);  		Vector3 axis; -		axis[p_idx] = 1.0; +		axis[p_id] = 1.0;  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); -		float d = ra[p_idx]; +		float d = ra[p_id];  		if (Node3DEditor::get_singleton()->is_snap_enabled()) {  			d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());  		} @@ -142,7 +144,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca  		}  		Vector3 h = s->get_size(); -		h[p_idx] = d * 2; +		h[p_id] = d * 2;  		s->set_size(h);  	} @@ -150,7 +152,7 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca  		CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);  		Vector3 axis; -		axis[p_idx == 0 ? 0 : 1] = 1.0; +		axis[p_id == 0 ? 0 : 1] = 1.0;  		Vector3 ra, rb;  		Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);  		float d = axis.dot(ra); @@ -162,9 +164,9 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca  			d = 0.001;  		} -		if (p_idx == 0) { +		if (p_id == 0) {  			s->set_radius(d); -		} else if (p_idx == 1) { +		} else if (p_id == 1) {  			s->set_height(d * 2.0);  		}  	} @@ -185,15 +187,15 @@ void CSGShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Ca  			d = 0.001;  		} -		if (p_idx == 0) { +		if (p_id == 0) {  			s->set_inner_radius(d); -		} else if (p_idx == 1) { +		} else if (p_id == 1) {  			s->set_outer_radius(d);  		}  	}  } -void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { +void CSGShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const {  	CSGShape3D *cs = Object::cast_to<CSGShape3D>(p_gizmo->get_spatial_node());  	if (Object::cast_to<CSGSphere3D>(cs)) { @@ -227,7 +229,7 @@ void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx,  	if (Object::cast_to<CSGCylinder3D>(cs)) {  		CSGCylinder3D *s = Object::cast_to<CSGCylinder3D>(cs);  		if (p_cancel) { -			if (p_idx == 0) { +			if (p_id == 0) {  				s->set_radius(p_restore);  			} else {  				s->set_height(p_restore); @@ -236,7 +238,7 @@ void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx,  		}  		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); -		if (p_idx == 0) { +		if (p_id == 0) {  			ur->create_action(TTR("Change Cylinder Radius"));  			ur->add_do_method(s, "set_radius", s->get_radius());  			ur->add_undo_method(s, "set_radius", p_restore); @@ -252,7 +254,7 @@ void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx,  	if (Object::cast_to<CSGTorus3D>(cs)) {  		CSGTorus3D *s = Object::cast_to<CSGTorus3D>(cs);  		if (p_cancel) { -			if (p_idx == 0) { +			if (p_id == 0) {  				s->set_inner_radius(p_restore);  			} else {  				s->set_outer_radius(p_restore); @@ -261,7 +263,7 @@ void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx,  		}  		UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); -		if (p_idx == 0) { +		if (p_id == 0) {  			ur->create_action(TTR("Change Torus Inner Radius"));  			ur->add_do_method(s, "set_inner_radius", s->get_inner_radius());  			ur->add_undo_method(s, "set_inner_radius", p_restore); @@ -356,7 +358,7 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {  				break;  		} -		p_gizmo->add_mesh(mesh, false, Ref<SkinReference>(), solid_material); +		p_gizmo->add_mesh(mesh, solid_material);  	}  	if (Object::cast_to<CSGSphere3D>(cs)) { diff --git a/modules/csg/csg_gizmos.h b/modules/csg/csg_gizmos.h index 8f7da35de3..847313c0b4 100644 --- a/modules/csg/csg_gizmos.h +++ b/modules/csg/csg_gizmos.h @@ -33,22 +33,22 @@  #include "csg_shape.h"  #include "editor/editor_plugin.h" -#include "editor/node_3d_editor_gizmos.h" +#include "editor/plugins/node_3d_editor_gizmos.h"  class CSGShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {  	GDCLASS(CSGShape3DGizmoPlugin, EditorNode3DGizmoPlugin);  public: -	bool has_gizmo(Node3D *p_spatial) override; -	String get_gizmo_name() const override; -	int get_priority() const override; -	bool is_selectable_when_hidden() const override; -	void redraw(EditorNode3DGizmo *p_gizmo) override; - -	String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	Variant get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const override; -	void set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) override; -	void commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) override; +	virtual bool has_gizmo(Node3D *p_spatial) override; +	virtual String get_gizmo_name() const override; +	virtual int get_priority() const override; +	virtual bool is_selectable_when_hidden() const override; +	virtual void redraw(EditorNode3DGizmo *p_gizmo) override; + +	virtual String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	virtual Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override; +	virtual void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const override; +	virtual void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const override;  	CSGShape3DGizmoPlugin();  }; diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 729dc2f8fc..73c1ba554c 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -548,7 +548,7 @@ void CSGShape3D::_notification(int p_what) {  void CSGShape3D::set_operation(Operation p_operation) {  	operation = p_operation;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  CSGShape3D::Operation CSGShape3D::get_operation() const { @@ -845,7 +845,7 @@ CSGBrush *CSGMesh3D::_build_brush() {  void CSGMesh3D::_mesh_changed() {  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  void CSGMesh3D::set_material(const Ref<Material> &p_material) { @@ -1034,7 +1034,7 @@ void CSGSphere3D::set_radius(const float p_radius) {  	ERR_FAIL_COND(p_radius <= 0);  	radius = p_radius;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  float CSGSphere3D::get_radius() const { @@ -1044,7 +1044,7 @@ float CSGSphere3D::get_radius() const {  void CSGSphere3D::set_radial_segments(const int p_radial_segments) {  	radial_segments = p_radial_segments > 4 ? p_radial_segments : 4;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  int CSGSphere3D::get_radial_segments() const { @@ -1054,7 +1054,7 @@ int CSGSphere3D::get_radial_segments() const {  void CSGSphere3D::set_rings(const int p_rings) {  	rings = p_rings > 1 ? p_rings : 1;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  int CSGSphere3D::get_rings() const { @@ -1203,7 +1203,7 @@ void CSGBox3D::_bind_methods() {  void CSGBox3D::set_size(const Vector3 &p_size) {  	size = p_size;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  Vector3 CSGBox3D::get_size() const { @@ -1213,7 +1213,7 @@ Vector3 CSGBox3D::get_size() const {  void CSGBox3D::set_material(const Ref<Material> &p_material) {  	material = p_material;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  Ref<Material> CSGBox3D::get_material() const { @@ -1384,7 +1384,7 @@ void CSGCylinder3D::_bind_methods() {  void CSGCylinder3D::set_radius(const float p_radius) {  	radius = p_radius;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  float CSGCylinder3D::get_radius() const { @@ -1394,7 +1394,7 @@ float CSGCylinder3D::get_radius() const {  void CSGCylinder3D::set_height(const float p_height) {  	height = p_height;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  float CSGCylinder3D::get_height() const { @@ -1405,7 +1405,7 @@ void CSGCylinder3D::set_sides(const int p_sides) {  	ERR_FAIL_COND(p_sides < 3);  	sides = p_sides;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  int CSGCylinder3D::get_sides() const { @@ -1415,7 +1415,7 @@ int CSGCylinder3D::get_sides() const {  void CSGCylinder3D::set_cone(const bool p_cone) {  	cone = p_cone;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  bool CSGCylinder3D::is_cone() const { @@ -1603,7 +1603,7 @@ void CSGTorus3D::_bind_methods() {  void CSGTorus3D::set_inner_radius(const float p_inner_radius) {  	inner_radius = p_inner_radius;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  float CSGTorus3D::get_inner_radius() const { @@ -1613,7 +1613,7 @@ float CSGTorus3D::get_inner_radius() const {  void CSGTorus3D::set_outer_radius(const float p_outer_radius) {  	outer_radius = p_outer_radius;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  float CSGTorus3D::get_outer_radius() const { @@ -1624,7 +1624,7 @@ void CSGTorus3D::set_sides(const int p_sides) {  	ERR_FAIL_COND(p_sides < 3);  	sides = p_sides;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  int CSGTorus3D::get_sides() const { @@ -1635,7 +1635,7 @@ void CSGTorus3D::set_ring_sides(const int p_ring_sides) {  	ERR_FAIL_COND(p_ring_sides < 3);  	ring_sides = p_ring_sides;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  int CSGTorus3D::get_ring_sides() const { @@ -2172,7 +2172,7 @@ void CSGPolygon3D::_validate_property(PropertyInfo &property) const {  void CSGPolygon3D::_path_changed() {  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  void CSGPolygon3D::_path_exited() { @@ -2248,7 +2248,7 @@ void CSGPolygon3D::_bind_methods() {  void CSGPolygon3D::set_polygon(const Vector<Vector2> &p_polygon) {  	polygon = p_polygon;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  Vector<Vector2> CSGPolygon3D::get_polygon() const { @@ -2258,7 +2258,7 @@ Vector<Vector2> CSGPolygon3D::get_polygon() const {  void CSGPolygon3D::set_mode(Mode p_mode) {  	mode = p_mode;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  	notify_property_list_changed();  } @@ -2270,7 +2270,7 @@ void CSGPolygon3D::set_depth(const float p_depth) {  	ERR_FAIL_COND(p_depth < 0.001);  	depth = p_depth;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  float CSGPolygon3D::get_depth() const { @@ -2290,7 +2290,7 @@ void CSGPolygon3D::set_spin_degrees(const float p_spin_degrees) {  	ERR_FAIL_COND(p_spin_degrees < 0.01 || p_spin_degrees > 360);  	spin_degrees = p_spin_degrees;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  float CSGPolygon3D::get_spin_degrees() const { @@ -2301,7 +2301,7 @@ void CSGPolygon3D::set_spin_sides(const int p_spin_sides) {  	ERR_FAIL_COND(p_spin_sides < 3);  	spin_sides = p_spin_sides;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  int CSGPolygon3D::get_spin_sides() const { @@ -2311,7 +2311,7 @@ int CSGPolygon3D::get_spin_sides() const {  void CSGPolygon3D::set_path_node(const NodePath &p_path) {  	path_node = p_path;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  NodePath CSGPolygon3D::get_path_node() const { @@ -2322,7 +2322,7 @@ void CSGPolygon3D::set_path_interval(float p_interval) {  	ERR_FAIL_COND_MSG(p_interval < 0.001, "Path interval cannot be smaller than 0.001.");  	path_interval = p_interval;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  float CSGPolygon3D::get_path_interval() const { @@ -2332,7 +2332,7 @@ float CSGPolygon3D::get_path_interval() const {  void CSGPolygon3D::set_path_rotation(PathRotation p_rotation) {  	path_rotation = p_rotation;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  CSGPolygon3D::PathRotation CSGPolygon3D::get_path_rotation() const { @@ -2342,7 +2342,7 @@ CSGPolygon3D::PathRotation CSGPolygon3D::get_path_rotation() const {  void CSGPolygon3D::set_path_local(bool p_enable) {  	path_local = p_enable;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  bool CSGPolygon3D::is_path_local() const { @@ -2352,7 +2352,7 @@ bool CSGPolygon3D::is_path_local() const {  void CSGPolygon3D::set_path_joined(bool p_enable) {  	path_joined = p_enable;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  bool CSGPolygon3D::is_path_joined() const { diff --git a/modules/navigation/navigation_mesh_editor_plugin.cpp b/modules/navigation/navigation_mesh_editor_plugin.cpp index 8f4203e260..587e56f593 100644 --- a/modules/navigation/navigation_mesh_editor_plugin.cpp +++ b/modules/navigation/navigation_mesh_editor_plugin.cpp @@ -65,7 +65,7 @@ void NavigationMeshEditor::_bake_pressed() {  	NavigationMeshGenerator::get_singleton()->clear(node->get_navigation_mesh());  	NavigationMeshGenerator::get_singleton()->bake(node->get_navigation_mesh(), node); -	node->update_gizmo(); +	node->update_gizmos();  }  void NavigationMeshEditor::_clear_pressed() { @@ -77,7 +77,7 @@ void NavigationMeshEditor::_clear_pressed() {  	bake_info->set_text("");  	if (node) { -		node->update_gizmo(); +		node->update_gizmos();  	}  } diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index dd8cb1f20d..bb8f9f8ccb 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -794,7 +794,7 @@ uint32_t AudioStreamPlayer3D::get_area_mask() const {  void AudioStreamPlayer3D::set_emission_angle_enabled(bool p_enable) {  	emission_angle_enabled = p_enable; -	update_gizmo(); +	update_gizmos();  }  bool AudioStreamPlayer3D::is_emission_angle_enabled() const { @@ -804,7 +804,7 @@ bool AudioStreamPlayer3D::is_emission_angle_enabled() const {  void AudioStreamPlayer3D::set_emission_angle(float p_angle) {  	ERR_FAIL_COND(p_angle < 0 || p_angle > 90);  	emission_angle = p_angle; -	update_gizmo(); +	update_gizmos();  }  float AudioStreamPlayer3D::get_emission_angle() const { diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index 0c38d85a6b..2e962b96c3 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -164,7 +164,7 @@ void Camera3D::set_perspective(float p_fovy_degrees, float p_z_near, float p_z_f  	mode = PROJECTION_PERSPECTIVE;  	RenderingServer::get_singleton()->camera_set_perspective(camera, fov, near, far); -	update_gizmo(); +	update_gizmos();  	force_change = false;  } @@ -181,7 +181,7 @@ void Camera3D::set_orthogonal(float p_size, float p_z_near, float p_z_far) {  	force_change = false;  	RenderingServer::get_singleton()->camera_set_orthogonal(camera, size, near, far); -	update_gizmo(); +	update_gizmos();  }  void Camera3D::set_frustum(float p_size, Vector2 p_offset, float p_z_near, float p_z_far) { @@ -198,7 +198,7 @@ void Camera3D::set_frustum(float p_size, Vector2 p_offset, float p_z_near, float  	force_change = false;  	RenderingServer::get_singleton()->camera_set_frustum(camera, size, frustum_offset, near, far); -	update_gizmo(); +	update_gizmos();  }  void Camera3D::set_projection(Camera3D::Projection p_mode) { @@ -755,7 +755,7 @@ void ClippedCamera3D::_notification(int p_what) {  	}  	if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { -		update_gizmo(); +		update_gizmos();  	}  } diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index 8a4f8b639b..42645f47d4 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -122,7 +122,7 @@ void CollisionPolygon3D::set_polygon(const Vector<Point2> &p_polygon) {  		_build_polygon();  	}  	update_configuration_warnings(); -	update_gizmo(); +	update_gizmos();  }  Vector<Point2> CollisionPolygon3D::get_polygon() const { @@ -136,7 +136,7 @@ AABB CollisionPolygon3D::get_item_rect() const {  void CollisionPolygon3D::set_depth(real_t p_depth) {  	depth = p_depth;  	_build_polygon(); -	update_gizmo(); +	update_gizmos();  }  real_t CollisionPolygon3D::get_depth() const { @@ -145,7 +145,7 @@ real_t CollisionPolygon3D::get_depth() const {  void CollisionPolygon3D::set_disabled(bool p_disabled) {  	disabled = p_disabled; -	update_gizmo(); +	update_gizmos();  	if (parent) {  		parent->shape_owner_set_disabled(owner_id, p_disabled); diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index be3fde8013..9643d33c86 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -117,7 +117,7 @@ void CollisionShape3D::_notification(int p_what) {  }  void CollisionShape3D::resource_changed(RES res) { -	update_gizmo(); +	update_gizmos();  }  TypedArray<String> CollisionShape3D::get_configuration_warnings() const { @@ -166,7 +166,7 @@ void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) {  	if (!shape.is_null()) {  		shape->register_owner(this);  	} -	update_gizmo(); +	update_gizmos();  	if (parent) {  		parent->shape_owner_clear_shapes(owner_id);  		if (shape.is_valid()) { @@ -187,7 +187,7 @@ Ref<Shape3D> CollisionShape3D::get_shape() const {  void CollisionShape3D::set_disabled(bool p_disabled) {  	disabled = p_disabled; -	update_gizmo(); +	update_gizmos();  	if (parent) {  		parent->shape_owner_set_disabled(owner_id, p_disabled);  	} diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index 5af7b8ca07..05f023721b 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -33,7 +33,7 @@  void Decal::set_extents(const Vector3 &p_extents) {  	extents = p_extents;  	RS::get_singleton()->decal_set_extents(decal, p_extents); -	update_gizmo(); +	update_gizmos();  }  Vector3 Decal::get_extents() const { diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index f78027e6c7..44cd7c10d8 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -99,7 +99,7 @@ void GPUParticles3D::set_randomness_ratio(float p_ratio) {  void GPUParticles3D::set_visibility_aabb(const AABB &p_aabb) {  	visibility_aabb = p_aabb;  	RS::get_singleton()->particles_set_custom_aabb(particles, visibility_aabb); -	update_gizmo(); +	update_gizmos();  }  void GPUParticles3D::set_use_local_coordinates(bool p_enable) { diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp index 5b88a6ec9f..df7d7be1fd 100644 --- a/scene/3d/gpu_particles_collision_3d.cpp +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -73,7 +73,7 @@ void GPUParticlesCollisionSphere::_bind_methods() {  void GPUParticlesCollisionSphere::set_radius(float p_radius) {  	radius = p_radius;  	RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius); -	update_gizmo(); +	update_gizmos();  }  float GPUParticlesCollisionSphere::get_radius() const { @@ -103,7 +103,7 @@ void GPUParticlesCollisionBox::_bind_methods() {  void GPUParticlesCollisionBox::set_extents(const Vector3 &p_extents) {  	extents = p_extents;  	RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); -	update_gizmo(); +	update_gizmos();  }  Vector3 GPUParticlesCollisionBox::get_extents() const { @@ -545,7 +545,7 @@ float GPUParticlesCollisionSDF::get_thickness() const {  void GPUParticlesCollisionSDF::set_extents(const Vector3 &p_extents) {  	extents = p_extents;  	RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); -	update_gizmo(); +	update_gizmos();  }  Vector3 GPUParticlesCollisionSDF::get_extents() const { @@ -554,7 +554,7 @@ Vector3 GPUParticlesCollisionSDF::get_extents() const {  void GPUParticlesCollisionSDF::set_resolution(Resolution p_resolution) {  	resolution = p_resolution; -	update_gizmo(); +	update_gizmos();  }  GPUParticlesCollisionSDF::Resolution GPUParticlesCollisionSDF::get_resolution() const { @@ -680,7 +680,7 @@ float GPUParticlesCollisionHeightField::get_follow_camera_push_ratio() const {  void GPUParticlesCollisionHeightField::set_extents(const Vector3 &p_extents) {  	extents = p_extents;  	RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); -	update_gizmo(); +	update_gizmos();  	RS::get_singleton()->particles_collision_height_field_update(_get_collision());  } @@ -691,7 +691,7 @@ Vector3 GPUParticlesCollisionHeightField::get_extents() const {  void GPUParticlesCollisionHeightField::set_resolution(Resolution p_resolution) {  	resolution = p_resolution;  	RS::get_singleton()->particles_collision_set_height_field_resolution(_get_collision(), RS::ParticlesCollisionHeightfieldResolution(resolution)); -	update_gizmo(); +	update_gizmos();  	RS::get_singleton()->particles_collision_height_field_update(_get_collision());  } @@ -761,7 +761,7 @@ float GPUParticlesAttractor3D::get_attenuation() const {  void GPUParticlesAttractor3D::set_directionality(float p_directionality) {  	directionality = p_directionality;  	RS::get_singleton()->particles_collision_set_attractor_directionality(collision, p_directionality); -	update_gizmo(); +	update_gizmos();  }  float GPUParticlesAttractor3D::get_directionality() const { @@ -808,7 +808,7 @@ void GPUParticlesAttractorSphere::_bind_methods() {  void GPUParticlesAttractorSphere::set_radius(float p_radius) {  	radius = p_radius;  	RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius); -	update_gizmo(); +	update_gizmos();  }  float GPUParticlesAttractorSphere::get_radius() const { @@ -838,7 +838,7 @@ void GPUParticlesAttractorBox::_bind_methods() {  void GPUParticlesAttractorBox::set_extents(const Vector3 &p_extents) {  	extents = p_extents;  	RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); -	update_gizmo(); +	update_gizmos();  }  Vector3 GPUParticlesAttractorBox::get_extents() const { @@ -872,7 +872,7 @@ void GPUParticlesAttractorVectorField::_bind_methods() {  void GPUParticlesAttractorVectorField::set_extents(const Vector3 &p_extents) {  	extents = p_extents;  	RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); -	update_gizmo(); +	update_gizmos();  }  Vector3 GPUParticlesAttractorVectorField::get_extents() const { diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 8478821ba1..c2943a9606 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -45,7 +45,7 @@ void Light3D::set_param(Param p_param, float p_value) {  	RS::get_singleton()->light_set_param(light, RS::LightParam(p_param), p_value);  	if (p_param == PARAM_SPOT_ANGLE || p_param == PARAM_RANGE) { -		update_gizmo(); +		update_gizmos();  		if (p_param == PARAM_SPOT_ANGLE) {  			update_configuration_warnings(); @@ -95,7 +95,7 @@ void Light3D::set_color(const Color &p_color) {  	color = p_color;  	RS::get_singleton()->light_set_color(light, p_color);  	// The gizmo color depends on the light color, so update it. -	update_gizmo(); +	update_gizmos();  }  Color Light3D::get_color() const { diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 3ffb3c0393..0085c8933d 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -1250,7 +1250,7 @@ void LightmapGI::set_light_data(const Ref<LightmapGIData> &p_data) {  		}  	} -	update_gizmo(); +	update_gizmos();  }  Ref<LightmapGIData> LightmapGI::get_light_data() const { diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 28ccbd3e68..5bb598227c 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -133,7 +133,7 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) {  		set_base(RID());  	} -	update_gizmo(); +	update_gizmos();  	notify_property_list_changed();  } @@ -356,7 +356,7 @@ Ref<Material> MeshInstance3D::get_active_material(int p_surface) const {  void MeshInstance3D::_mesh_changed() {  	ERR_FAIL_COND(mesh.is_null());  	surface_override_materials.resize(mesh->get_surface_count()); -	update_gizmo(); +	update_gizmos();  }  void MeshInstance3D::create_debug_tangents() { diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 19abb3f33a..2976dad39d 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -59,7 +59,7 @@ void NavigationRegion3D::set_enabled(bool p_enabled) {  		}  	} -	update_gizmo(); +	update_gizmos();  }  bool NavigationRegion3D::is_enabled() const { @@ -134,7 +134,7 @@ void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navmes  	emit_signal(SNAME("navigation_mesh_changed")); -	update_gizmo(); +	update_gizmos();  	update_configuration_warnings();  } @@ -211,7 +211,7 @@ void NavigationRegion3D::_bind_methods() {  }  void NavigationRegion3D::_navigation_changed() { -	update_gizmo(); +	update_gizmos();  	update_configuration_warnings();  } diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index d6c6ec75b4..d1f96230e0 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -76,7 +76,7 @@ Node3DGizmo::Node3DGizmo() {  void Node3D::_notify_dirty() {  #ifdef TOOLS_ENABLED -	if ((data.gizmo.is_valid() || data.notify_transform) && !data.ignore_notification && !xform_change.in_list()) { +	if ((!data.gizmos.is_empty() || data.notify_transform) && !data.ignore_notification && !xform_change.in_list()) {  #else  	if (data.notify_transform && !data.ignore_notification && !xform_change.in_list()) { @@ -110,7 +110,7 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) {  		E->get()->_propagate_transform_changed(p_origin);  	}  #ifdef TOOLS_ENABLED -	if ((data.gizmo.is_valid() || data.notify_transform) && !data.ignore_notification && !xform_change.in_list()) { +	if ((!data.gizmos.is_empty() || data.notify_transform) && !data.ignore_notification && !xform_change.in_list()) {  #else  	if (data.notify_transform && !data.ignore_notification && !xform_change.in_list()) {  #endif @@ -181,15 +181,14 @@ void Node3D::_notification(int p_what) {  			}  #ifdef TOOLS_ENABLED  			if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { -				//get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,SceneStringNames::get_singleton()->_spatial_editor_group,SceneStringNames::get_singleton()->_request_gizmo,this);  				get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this); -				if (!data.gizmo_disabled) { -					if (data.gizmo.is_valid()) { -						data.gizmo->create(); +				if (!data.gizmos_disabled) { +					for (int i = 0; i < data.gizmos.size(); i++) { +						data.gizmos.write[i]->create();  						if (is_visible_in_tree()) { -							data.gizmo->redraw(); +							data.gizmos.write[i]->redraw();  						} -						data.gizmo->transform(); +						data.gizmos.write[i]->transform();  					}  				}  			} @@ -198,10 +197,7 @@ void Node3D::_notification(int p_what) {  		} break;  		case NOTIFICATION_EXIT_WORLD: {  #ifdef TOOLS_ENABLED -			if (data.gizmo.is_valid()) { -				data.gizmo->free(); -				data.gizmo.unref(); -			} +			clear_gizmos();  #endif  			if (get_script_instance()) { @@ -215,8 +211,8 @@ void Node3D::_notification(int p_what) {  		case NOTIFICATION_TRANSFORM_CHANGED: {  #ifdef TOOLS_ENABLED -			if (data.gizmo.is_valid()) { -				data.gizmo->transform(); +			for (int i = 0; i < data.gizmos.size(); i++) { +				data.gizmos.write[i]->transform();  			}  #endif  		} break; @@ -368,80 +364,119 @@ Vector3 Node3D::get_scale() const {  	return data.scale;  } -void Node3D::update_gizmo() { +void Node3D::update_gizmos() {  #ifdef TOOLS_ENABLED  	if (!is_inside_world()) {  		return;  	} -	if (!data.gizmo.is_valid()) { -		get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this); + +	if (data.gizmos.is_empty()) { +		return;  	} -	if (!data.gizmo.is_valid()) { +	data.gizmos_dirty = true; +	MessageQueue::get_singleton()->push_callable(callable_mp(this, &Node3D::_update_gizmos)); +#endif +} + +void Node3D::clear_subgizmo_selection() { +#ifdef TOOLS_ENABLED +	if (!is_inside_world()) {  		return;  	} -	if (data.gizmo_dirty) { + +	if (data.gizmos.is_empty()) {  		return;  	} -	data.gizmo_dirty = true; -	MessageQueue::get_singleton()->push_call(this, "_update_gizmo"); + +	if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { +		get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_clear_subgizmo_selection, this); +	}  #endif  } -void Node3D::set_gizmo(const Ref<Node3DGizmo> &p_gizmo) { +void Node3D::add_gizmo(Ref<Node3DGizmo> p_gizmo) {  #ifdef TOOLS_ENABLED -	if (data.gizmo_disabled) { +	if (data.gizmos_disabled || p_gizmo.is_null()) {  		return;  	} -	if (data.gizmo.is_valid() && is_inside_world()) { -		data.gizmo->free(); -	} -	data.gizmo = p_gizmo; -	if (data.gizmo.is_valid() && is_inside_world()) { -		data.gizmo->create(); +	data.gizmos.push_back(p_gizmo); + +	if (p_gizmo.is_valid() && is_inside_world()) { +		p_gizmo->create();  		if (is_visible_in_tree()) { -			data.gizmo->redraw(); +			p_gizmo->redraw();  		} -		data.gizmo->transform(); +		p_gizmo->transform();  	} +#endif +} + +void Node3D::remove_gizmo(Ref<Node3DGizmo> p_gizmo) { +#ifdef TOOLS_ENABLED +	int idx = data.gizmos.find(p_gizmo); +	if (idx != -1) { +		p_gizmo->free(); +		data.gizmos.remove(idx); +	} +#endif +} + +void Node3D::clear_gizmos() { +#ifdef TOOLS_ENABLED +	for (int i = 0; i < data.gizmos.size(); i++) { +		data.gizmos.write[i]->free(); +	} +	data.gizmos.clear(); +#endif +} + +Array Node3D::get_gizmos_bind() const { +	Array ret; + +#ifdef TOOLS_ENABLED +	for (int i = 0; i < data.gizmos.size(); i++) { +		ret.push_back(Variant(data.gizmos[i].ptr())); +	}  #endif + +	return ret;  } -Ref<Node3DGizmo> Node3D::get_gizmo() const { +Vector<Ref<Node3DGizmo>> Node3D::get_gizmos() const {  #ifdef TOOLS_ENABLED -	return data.gizmo; +	return data.gizmos;  #else -	return Ref<Node3DGizmo>(); +	return Vector<Ref<Node3DGizmo>>();  #endif  } -void Node3D::_update_gizmo() { +void Node3D::_update_gizmos() {  #ifdef TOOLS_ENABLED -	if (!is_inside_world()) { +	if (data.gizmos_disabled || !is_inside_world() || !data.gizmos_dirty) {  		return;  	} -	data.gizmo_dirty = false; -	if (data.gizmo.is_valid()) { +	data.gizmos_dirty = false; +	for (int i = 0; i < data.gizmos.size(); i++) {  		if (is_visible_in_tree()) { -			data.gizmo->redraw(); +			data.gizmos.write[i]->redraw();  		} else { -			data.gizmo->clear(); +			data.gizmos.write[i]->clear();  		}  	}  #endif  }  #ifdef TOOLS_ENABLED -void Node3D::set_disable_gizmo(bool p_enabled) { -	data.gizmo_disabled = p_enabled; -	if (!p_enabled && data.gizmo.is_valid()) { -		data.gizmo = Ref<Node3DGizmo>(); +void Node3D::set_disable_gizmos(bool p_enabled) { +	data.gizmos_disabled = p_enabled; +	if (!p_enabled) { +		clear_gizmos();  	}  } -  #endif  void Node3D::set_disable_scale(bool p_enabled) { @@ -486,8 +521,9 @@ void Node3D::_propagate_visibility_changed() {  	notification(NOTIFICATION_VISIBILITY_CHANGED);  	emit_signal(SceneStringNames::get_singleton()->visibility_changed);  #ifdef TOOLS_ENABLED -	if (data.gizmo.is_valid()) { -		_update_gizmo(); +	if (!data.gizmos.is_empty()) { +		data.gizmos_dirty = true; +		_update_gizmos();  	}  #endif @@ -758,11 +794,11 @@ void Node3D::_bind_methods() {  	ClassDB::bind_method(D_METHOD("set_visibility_parent", "path"), &Node3D::set_visibility_parent);  	ClassDB::bind_method(D_METHOD("get_visibility_parent"), &Node3D::get_visibility_parent); -	ClassDB::bind_method(D_METHOD("_update_gizmo"), &Node3D::_update_gizmo); - -	ClassDB::bind_method(D_METHOD("update_gizmo"), &Node3D::update_gizmo); -	ClassDB::bind_method(D_METHOD("set_gizmo", "gizmo"), &Node3D::set_gizmo); -	ClassDB::bind_method(D_METHOD("get_gizmo"), &Node3D::get_gizmo); +	ClassDB::bind_method(D_METHOD("update_gizmos"), &Node3D::update_gizmos); +	ClassDB::bind_method(D_METHOD("add_gizmo", "gizmo"), &Node3D::add_gizmo); +	ClassDB::bind_method(D_METHOD("get_gizmos"), &Node3D::get_gizmos_bind); +	ClassDB::bind_method(D_METHOD("clear_gizmos"), &Node3D::clear_gizmos); +	ClassDB::bind_method(D_METHOD("clear_subgizmo_selection"), &Node3D::clear_subgizmo_selection);  	ClassDB::bind_method(D_METHOD("set_visible", "visible"), &Node3D::set_visible);  	ClassDB::bind_method(D_METHOD("is_visible"), &Node3D::is_visible); @@ -813,7 +849,6 @@ void Node3D::_bind_methods() {  	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"); -	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "Node3DGizmo", PROPERTY_USAGE_NONE), "set_gizmo", "get_gizmo");  	ADD_SIGNAL(MethodInfo("visibility_changed"));  } diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index fe6324c796..282f4805cc 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -90,16 +90,16 @@ class Node3D : public Node {  		bool disable_scale = false;  #ifdef TOOLS_ENABLED -		Ref<Node3DGizmo> gizmo; -		bool gizmo_disabled = false; -		bool gizmo_dirty = false; +		Vector<Ref<Node3DGizmo>> gizmos; +		bool gizmos_disabled = false; +		bool gizmos_dirty = false;  #endif  	} data;  	NodePath visibility_parent_path; -	void _update_gizmo(); +	void _update_gizmos();  	void _notify_dirty();  	void _propagate_transform_changed(Node3D *p_origin); @@ -154,10 +154,14 @@ public:  	void set_disable_scale(bool p_enabled);  	bool is_scale_disabled() const; -	void set_disable_gizmo(bool p_enabled); -	void update_gizmo(); -	void set_gizmo(const Ref<Node3DGizmo> &p_gizmo); -	Ref<Node3DGizmo> get_gizmo() const; +	void set_disable_gizmos(bool p_enabled); +	void update_gizmos(); +	void clear_subgizmo_selection(); +	Vector<Ref<Node3DGizmo>> get_gizmos() const; +	Array get_gizmos_bind() const; +	void add_gizmo(Ref<Node3DGizmo> p_gizmo); +	void remove_gizmo(Ref<Node3DGizmo> p_gizmo); +	void clear_gizmos();  	_FORCE_INLINE_ bool is_inside_world() const { return data.inside_world; } diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp index 7b736e689c..3d1a27911b 100644 --- a/scene/3d/occluder_instance_3d.cpp +++ b/scene/3d/occluder_instance_3d.cpp @@ -173,12 +173,12 @@ void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) {  		set_base(RID());  	} -	update_gizmo(); +	update_gizmos();  	update_configuration_warnings();  }  void OccluderInstance3D::_occluder_changed() { -	update_gizmo(); +	update_gizmos();  	update_configuration_warnings();  } diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index 54ae2cef75..6af2e7f879 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -38,7 +38,7 @@ void Path3D::_notification(int p_what) {  void Path3D::_curve_changed() {  	if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) { -		update_gizmo(); +		update_gizmos();  	}  	if (is_inside_tree()) {  		emit_signal(SNAME("curve_changed")); diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 760dbdadca..0e3180bcdf 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -2181,9 +2181,7 @@ bool PhysicalBone3D::_set(const StringName &p_name, const Variant &p_value) {  	if (joint_data) {  		if (joint_data->_set(p_name, p_value, joint)) {  #ifdef TOOLS_ENABLED -			if (get_gizmo().is_valid()) { -				get_gizmo()->redraw(); -			} +			update_gizmos();  #endif  			return true;  		} @@ -2371,9 +2369,7 @@ void PhysicalBone3D::_update_joint_offset() {  	set_ignore_transform_notification(false);  #ifdef TOOLS_ENABLED -	if (get_gizmo().is_valid()) { -		get_gizmo()->redraw(); -	} +	update_gizmos();  #endif  } @@ -2540,9 +2536,7 @@ void PhysicalBone3D::set_joint_type(JointType p_joint_type) {  #ifdef TOOLS_ENABLED  	notify_property_list_changed(); -	if (get_gizmo().is_valid()) { -		get_gizmo()->redraw(); -	} +	update_gizmos();  #endif  } diff --git a/scene/3d/physics_joint_3d.cpp b/scene/3d/physics_joint_3d.cpp index 01f10c171f..59440bd1a8 100644 --- a/scene/3d/physics_joint_3d.cpp +++ b/scene/3d/physics_joint_3d.cpp @@ -348,7 +348,7 @@ void HingeJoint3D::set_param(Param p_param, real_t p_value) {  		PhysicsServer3D::get_singleton()->hinge_joint_set_param(get_joint(), PhysicsServer3D::HingeJointParam(p_param), p_value);  	} -	update_gizmo(); +	update_gizmos();  }  real_t HingeJoint3D::get_param(Param p_param) const { @@ -363,7 +363,7 @@ void HingeJoint3D::set_flag(Flag p_flag, bool p_value) {  		PhysicsServer3D::get_singleton()->hinge_joint_set_flag(get_joint(), PhysicsServer3D::HingeJointFlag(p_flag), p_value);  	} -	update_gizmo(); +	update_gizmos();  }  bool HingeJoint3D::get_flag(Flag p_flag) const { @@ -497,7 +497,7 @@ void SliderJoint3D::set_param(Param p_param, real_t p_value) {  	if (is_configured()) {  		PhysicsServer3D::get_singleton()->slider_joint_set_param(get_joint(), PhysicsServer3D::SliderJointParam(p_param), p_value);  	} -	update_gizmo(); +	update_gizmos();  }  real_t SliderJoint3D::get_param(Param p_param) const { @@ -602,7 +602,7 @@ void ConeTwistJoint3D::set_param(Param p_param, real_t p_value) {  		PhysicsServer3D::get_singleton()->cone_twist_joint_set_param(get_joint(), PhysicsServer3D::ConeTwistJointParam(p_param), p_value);  	} -	update_gizmo(); +	update_gizmos();  }  real_t ConeTwistJoint3D::get_param(Param p_param) const { @@ -857,7 +857,7 @@ void Generic6DOFJoint3D::set_param_x(Param p_param, real_t p_value) {  		PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_joint(), Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);  	} -	update_gizmo(); +	update_gizmos();  }  real_t Generic6DOFJoint3D::get_param_x(Param p_param) const { @@ -871,7 +871,7 @@ void Generic6DOFJoint3D::set_param_y(Param p_param, real_t p_value) {  	if (is_configured()) {  		PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_joint(), Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);  	} -	update_gizmo(); +	update_gizmos();  }  real_t Generic6DOFJoint3D::get_param_y(Param p_param) const { @@ -885,7 +885,7 @@ void Generic6DOFJoint3D::set_param_z(Param p_param, real_t p_value) {  	if (is_configured()) {  		PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_joint(), Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value);  	} -	update_gizmo(); +	update_gizmos();  }  real_t Generic6DOFJoint3D::get_param_z(Param p_param) const { @@ -899,7 +899,7 @@ void Generic6DOFJoint3D::set_flag_x(Flag p_flag, bool p_enabled) {  	if (is_configured()) {  		PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_joint(), Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled);  	} -	update_gizmo(); +	update_gizmos();  }  bool Generic6DOFJoint3D::get_flag_x(Flag p_flag) const { @@ -913,7 +913,7 @@ void Generic6DOFJoint3D::set_flag_y(Flag p_flag, bool p_enabled) {  	if (is_configured()) {  		PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_joint(), Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled);  	} -	update_gizmo(); +	update_gizmos();  }  bool Generic6DOFJoint3D::get_flag_y(Flag p_flag) const { @@ -927,7 +927,7 @@ void Generic6DOFJoint3D::set_flag_z(Flag p_flag, bool p_enabled) {  	if (is_configured()) {  		PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_joint(), Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled);  	} -	update_gizmo(); +	update_gizmos();  }  bool Generic6DOFJoint3D::get_flag_z(Flag p_flag) const { diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index dfab3d4a17..7356ce478b 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -37,7 +37,7 @@  void RayCast3D::set_target_position(const Vector3 &p_point) {  	target_position = p_point; -	update_gizmo(); +	update_gizmos();  	if (Engine::get_singleton()->is_editor_hint()) {  		if (is_inside_tree()) { @@ -102,7 +102,7 @@ Vector3 RayCast3D::get_collision_normal() const {  void RayCast3D::set_enabled(bool p_enabled) {  	enabled = p_enabled; -	update_gizmo(); +	update_gizmos();  	if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) {  		set_physics_process_internal(p_enabled); @@ -366,7 +366,7 @@ void RayCast3D::_update_debug_shape_vertices() {  void RayCast3D::set_debug_shape_thickness(const float p_debug_shape_thickness) {  	debug_shape_thickness = p_debug_shape_thickness; -	update_gizmo(); +	update_gizmos();  	if (Engine::get_singleton()->is_editor_hint()) {  		if (is_inside_tree()) { diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index c289fc4fd7..719dbedd94 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -101,7 +101,7 @@ void ReflectionProbe::set_extents(const Vector3 &p_extents) {  	RS::get_singleton()->reflection_probe_set_extents(probe, extents);  	RS::get_singleton()->reflection_probe_set_origin_offset(probe, origin_offset); -	update_gizmo(); +	update_gizmos();  }  Vector3 ReflectionProbe::get_extents() const { @@ -119,7 +119,7 @@ void ReflectionProbe::set_origin_offset(const Vector3 &p_extents) {  	RS::get_singleton()->reflection_probe_set_extents(probe, extents);  	RS::get_singleton()->reflection_probe_set_origin_offset(probe, origin_offset); -	update_gizmo(); +	update_gizmos();  }  Vector3 ReflectionProbe::get_origin_offset() const { diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index fa3b16935c..ba70a6eb41 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -404,7 +404,7 @@ void Skeleton3D::add_bone(const String &p_name) {  	process_order_dirty = true;  	version++;  	_make_dirty(); -	update_gizmo(); +	update_gizmos();  }  int Skeleton3D::find_bone(const String &p_name) const { diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp index 1911e14d54..5e9265b4c3 100644 --- a/scene/3d/spring_arm_3d.cpp +++ b/scene/3d/spring_arm_3d.cpp @@ -84,7 +84,7 @@ real_t SpringArm3D::get_length() const {  void SpringArm3D::set_length(real_t p_length) {  	if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint())) { -		update_gizmo(); +		update_gizmos();  	}  	spring_length = p_length; diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 2a53dd6aca..fd3bf99e0a 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -174,7 +174,7 @@ void SpriteBase3D::_queue_update() {  	}  	triangle_mesh.unref(); -	update_gizmo(); +	update_gizmos();  	pending_update = true;  	call_deferred(SceneStringNames::get_singleton()->_im_update); diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index b0e37b81a5..92c0e09947 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -149,7 +149,7 @@ void VehicleWheel3D::_update(PhysicsDirectBodyState3D *s) {  void VehicleWheel3D::set_radius(real_t p_radius) {  	m_wheelRadius = p_radius; -	update_gizmo(); +	update_gizmos();  }  real_t VehicleWheel3D::get_radius() const { @@ -158,7 +158,7 @@ real_t VehicleWheel3D::get_radius() const {  void VehicleWheel3D::set_suspension_rest_length(real_t p_length) {  	m_suspensionRestLength = p_length; -	update_gizmo(); +	update_gizmos();  }  real_t VehicleWheel3D::get_suspension_rest_length() const { diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp index 682bcec449..6a80aa3f45 100644 --- a/scene/3d/visible_on_screen_notifier_3d.cpp +++ b/scene/3d/visible_on_screen_notifier_3d.cpp @@ -63,7 +63,7 @@ void VisibleOnScreenNotifier3D::set_aabb(const AABB &p_aabb) {  	RS::get_singleton()->visibility_notifier_set_aabb(get_base(), aabb); -	update_gizmo(); +	update_gizmos();  }  AABB VisibleOnScreenNotifier3D::get_aabb() const { diff --git a/scene/3d/voxel_gi.cpp b/scene/3d/voxel_gi.cpp index 3da59ac4c0..bc52db5387 100644 --- a/scene/3d/voxel_gi.cpp +++ b/scene/3d/voxel_gi.cpp @@ -265,7 +265,7 @@ Ref<VoxelGIData> VoxelGI::get_probe_data() const {  void VoxelGI::set_subdiv(Subdiv p_subdiv) {  	ERR_FAIL_INDEX(p_subdiv, SUBDIV_MAX);  	subdiv = p_subdiv; -	update_gizmo(); +	update_gizmos();  }  VoxelGI::Subdiv VoxelGI::get_subdiv() const { @@ -274,7 +274,7 @@ VoxelGI::Subdiv VoxelGI::get_subdiv() const {  void VoxelGI::set_extents(const Vector3 &p_extents) {  	extents = p_extents; -	update_gizmo(); +	update_gizmos();  }  Vector3 VoxelGI::get_extents() const { diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index d53d155cce..b8173c9623 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -136,6 +136,7 @@ SceneStringNames::SceneStringNames() {  	_spatial_editor_group = StaticCString::create("_spatial_editor_group");  	_request_gizmo = StaticCString::create("_request_gizmo"); +	_clear_subgizmo_selection = StaticCString::create("_clear_subgizmo_selection");  	offset = StaticCString::create("offset");  	unit_offset = StaticCString::create("unit_offset"); diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index a141c2b3aa..eddb0c33eb 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -156,6 +156,7 @@ public:  	StringName _spatial_editor_group;  	StringName _request_gizmo; +	StringName _clear_subgizmo_selection;  	StringName offset;  	StringName unit_offset;  |