From cfb555a08175c811ea06a43ea320b81a2c90554a Mon Sep 17 00:00:00 2001 From: jfons Date: Wed, 23 Jun 2021 16:49:50 +0200 Subject: Node3D gizmo improvements * Clean-up of node_3d_editor_plugin.{h,cpp}: removed unused code, fixed some bugs. * Moved node_3d_editor_gizmos.{h,cpp} to editor/plugins. * Added support for multiple gizmos per node. This means custom gizmos will no longer override the built-in ones and that multiple gizmos can be used in more complex nodes. * Added support for handle IDs. When adding handles to a gizmo, an ID can be specified for each one, making it easier to work with gizmos that have a variable number of handles. * Added support for subgizmos, selectable elements that can be transformed without needing a node of their own. By overriding _subgizmo_intersect_frustum() and/or _subgizmo_intersect_ray() gizmos can define which subgizmos should be selected on a region or click selection. Subgizmo transformations are applied using get/set/commit virtual methods, similar to how handles work. --- core/math/triangle_mesh.cpp | 12 +- doc/classes/EditorNode3DGizmo.xml | 127 +- doc/classes/EditorNode3DGizmoPlugin.xml | 93 +- doc/classes/Node3D.xml | 39 +- editor/node_3d_editor_gizmos.cpp | 5019 ------------------ editor/node_3d_editor_gizmos.h | 492 -- editor/plugins/node_3d_editor_gizmos.cpp | 5477 ++++++++++++++++++++ editor/plugins/node_3d_editor_gizmos.h | 635 +++ editor/plugins/node_3d_editor_plugin.cpp | 1288 +++-- editor/plugins/node_3d_editor_plugin.h | 177 +- editor/plugins/path_3d_editor_plugin.cpp | 56 +- editor/plugins/path_3d_editor_plugin.h | 10 +- modules/csg/csg_gizmos.cpp | 44 +- modules/csg/csg_gizmos.h | 22 +- modules/csg/csg_shape.cpp | 52 +- .../navigation/navigation_mesh_editor_plugin.cpp | 4 +- scene/3d/audio_stream_player_3d.cpp | 4 +- scene/3d/camera_3d.cpp | 8 +- scene/3d/collision_polygon_3d.cpp | 6 +- scene/3d/collision_shape_3d.cpp | 6 +- scene/3d/decal.cpp | 2 +- scene/3d/gpu_particles_3d.cpp | 2 +- scene/3d/gpu_particles_collision_3d.cpp | 20 +- scene/3d/light_3d.cpp | 4 +- scene/3d/lightmap_gi.cpp | 2 +- scene/3d/mesh_instance_3d.cpp | 4 +- scene/3d/navigation_region_3d.cpp | 6 +- scene/3d/node_3d.cpp | 141 +- scene/3d/node_3d.h | 20 +- scene/3d/occluder_instance_3d.cpp | 4 +- scene/3d/path_3d.cpp | 2 +- scene/3d/physics_body_3d.cpp | 12 +- scene/3d/physics_joint_3d.cpp | 20 +- scene/3d/ray_cast_3d.cpp | 6 +- scene/3d/reflection_probe.cpp | 4 +- scene/3d/skeleton_3d.cpp | 2 +- scene/3d/spring_arm_3d.cpp | 2 +- scene/3d/sprite_3d.cpp | 2 +- scene/3d/vehicle_body_3d.cpp | 4 +- scene/3d/visible_on_screen_notifier_3d.cpp | 2 +- scene/3d/voxel_gi.cpp | 4 +- scene/scene_string_names.cpp | 1 + scene/scene_string_names.h | 1 + 43 files changed, 7236 insertions(+), 6602 deletions(-) delete mode 100644 editor/node_3d_editor_gizmos.cpp delete mode 100644 editor/node_3d_editor_gizmos.h create mode 100644 editor/plugins/node_3d_editor_gizmos.cpp create mode 100644 editor/plugins/node_3d_editor_gizmos.h 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 @@ - Custom gizmo for editing Node3D objects. + Gizmo for editing Node3D objects. - 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. @@ -12,64 +12,119 @@ - + - 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. + + + + + + + + + + + + + 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. - + - 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. - + - 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]. + + + + + + + + + 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]. - + - 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. - 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. - + - 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. + + + + + + + + + + + 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. + + + + + + + + + + + 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]. + + + + + + + + + + + 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]. @@ -78,7 +133,7 @@ - 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]. @@ -87,7 +142,7 @@ - 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]. @@ -97,13 +152,15 @@ - + - + + + - 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]. @@ -118,7 +175,7 @@ - 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]. @@ -126,14 +183,14 @@ - + - + - + - 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]. @@ -146,7 +203,7 @@ - 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]. @@ -170,6 +227,22 @@ Returns the Node3D node associated with this gizmo. + + + + + Returns a list of the currently selected subgizmos. Can be used to highlight selected elements during [method _redraw]. + + + + + + + + + Returns [code]true[/code] if the given subgizmo is currently selected. Can be used to highlight selected elements during [method _redraw]. + + 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 @@ - 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. @@ -22,14 +22,31 @@ - + - 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. + + + + + + + + + + + + + + + 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. @@ -53,7 +70,7 @@ - + Override this method to provide gizmo's handle names. Called for this plugin's active gizmos. @@ -64,18 +81,29 @@ - + - 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. - 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. + + + + + + + + + + + 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. @@ -92,10 +120,10 @@ - + - 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. @@ -111,7 +139,7 @@ - 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. @@ -119,14 +147,53 @@ - + - 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. + + + + + + + + + + + + + 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. + + + + + + + + + + + + + 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. + + + + + + + + + + + + + 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. 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 @@ https://github.com/godotengine/godot-demo-projects/tree/master/3d + + + + + + + Attach a gizmo to this [code]Node3D[/code]. + + + + + + + Clear all gizmos attached to this [code]Node3D[/code]. + + + + + + + Clears subgizmo selection for this node in the editor. Useful when subgizmo IDs become invalid after a property change. + + @@ -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. + + + + + Returns all the gizmos attached to this [code]Node3D[/code]. + + @@ -276,18 +306,15 @@ Changes the node's position by the given offset [Vector3] in local space. - + - Updates the [Node3DGizmo] of this node. + Updates all the [Node3DGizmo]s attached to this node. - - The [Node3DGizmo] for this node. Used for example in [EditorNode3DGizmo] as custom visualization and editing handles in Editor. - World3D space (global) [Transform3D] of this node. @@ -324,7 +351,7 @@ 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. Node3D nodes receives this notification when they are registered to new [World3D] resource. diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/node_3d_editor_gizmos.cpp deleted file mode 100644 index 74fb38b66b..0000000000 --- a/editor/node_3d_editor_gizmos.cpp +++ /dev/null @@ -1,5019 +0,0 @@ -/*************************************************************************/ -/* node_3d_editor_gizmos.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "node_3d_editor_gizmos.h" - -#include "core/math/convex_hull.h" -#include "core/math/geometry_2d.h" -#include "core/math/geometry_3d.h" -#include "scene/3d/audio_stream_player_3d.h" -#include "scene/3d/collision_polygon_3d.h" -#include "scene/3d/collision_shape_3d.h" -#include "scene/3d/cpu_particles_3d.h" -#include "scene/3d/decal.h" -#include "scene/3d/gpu_particles_3d.h" -#include "scene/3d/gpu_particles_collision_3d.h" -#include "scene/3d/light_3d.h" -#include "scene/3d/lightmap_gi.h" -#include "scene/3d/lightmap_probe.h" -#include "scene/3d/listener_3d.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/navigation_region_3d.h" -#include "scene/3d/occluder_instance_3d.h" -#include "scene/3d/physics_joint_3d.h" -#include "scene/3d/position_3d.h" -#include "scene/3d/ray_cast_3d.h" -#include "scene/3d/reflection_probe.h" -#include "scene/3d/soft_body_3d.h" -#include "scene/3d/spring_arm_3d.h" -#include "scene/3d/sprite_3d.h" -#include "scene/3d/vehicle_body_3d.h" -#include "scene/3d/visible_on_screen_notifier_3d.h" -#include "scene/3d/voxel_gi.h" -#include "scene/resources/box_shape_3d.h" -#include "scene/resources/capsule_shape_3d.h" -#include "scene/resources/concave_polygon_shape_3d.h" -#include "scene/resources/convex_polygon_shape_3d.h" -#include "scene/resources/cylinder_shape_3d.h" -#include "scene/resources/height_map_shape_3d.h" -#include "scene/resources/primitive_meshes.h" -#include "scene/resources/ray_shape_3d.h" -#include "scene/resources/sphere_shape_3d.h" -#include "scene/resources/surface_tool.h" -#include "scene/resources/world_margin_shape_3d.h" - -#define HANDLE_HALF_SIZE 9.5 - -bool EditorNode3DGizmo::is_editable() const { - ERR_FAIL_COND_V(!spatial_node, false); - Node *edited_root = spatial_node->get_tree()->get_edited_scene_root(); - if (spatial_node == edited_root) { - return true; - } - if (spatial_node->get_owner() == edited_root) { - return true; - } - - if (edited_root->is_editable_instance(spatial_node->get_owner())) { - return true; - } - - return false; -} - -void EditorNode3DGizmo::clear() { - for (int i = 0; i < instances.size(); i++) { - if (instances[i].instance.is_valid()) { - RS::get_singleton()->free(instances[i].instance); - } - } - - billboard_handle = false; - collision_segments.clear(); - collision_mesh = Ref(); - instances.clear(); - handles.clear(); - secondary_handles.clear(); -} - -void EditorNode3DGizmo::redraw() { - if (get_script_instance() && get_script_instance()->has_method("_redraw")) { - get_script_instance()->call("_redraw"); - return; - } - - ERR_FAIL_COND(!gizmo_plugin); - gizmo_plugin->redraw(this); -} - -String EditorNode3DGizmo::get_handle_name(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_idx); - } - - ERR_FAIL_COND_V(!gizmo_plugin, ""); - return gizmo_plugin->get_handle_name(this, p_idx); -} - -bool EditorNode3DGizmo::is_handle_highlighted(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_idx); - } - - ERR_FAIL_COND_V(!gizmo_plugin, false); - return gizmo_plugin->is_handle_highlighted(this, p_idx); -} - -Variant EditorNode3DGizmo::get_handle_value(int p_idx) { - if (get_script_instance() && get_script_instance()->has_method("_get_handle_value")) { - return get_script_instance()->call("_get_handle_value", p_idx); - } - - ERR_FAIL_COND_V(!gizmo_plugin, Variant()); - return gizmo_plugin->get_handle_value(this, p_idx); -} - -void EditorNode3DGizmo::set_handle(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_idx, p_camera, p_point); - return; - } - - ERR_FAIL_COND(!gizmo_plugin); - gizmo_plugin->set_handle(this, p_idx, p_camera, p_point); -} - -void EditorNode3DGizmo::commit_handle(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_idx, p_restore, p_cancel); - return; - } - - ERR_FAIL_COND(!gizmo_plugin); - gizmo_plugin->commit_handle(this, p_idx, p_restore, p_cancel); -} - -void EditorNode3DGizmo::set_spatial_node(Node3D *p_node) { - ERR_FAIL_NULL(p_node); - spatial_node = p_node; -} - -void EditorNode3DGizmo::Instance::create_instance(Node3D *p_base, bool p_hidden) { - instance = RS::get_singleton()->instance_create2(mesh->get_rid(), p_base->get_world_3d()->get_scenario()); - RS::get_singleton()->instance_attach_object_instance_id(instance, p_base->get_instance_id()); - if (skin_reference.is_valid()) { - RS::get_singleton()->instance_attach_skeleton(instance, skin_reference->get_skeleton()); - } - if (extra_margin) { - RS::get_singleton()->instance_set_extra_visibility_margin(instance, 1); - } - RS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, RS::SHADOW_CASTING_SETTING_OFF); - int layer = p_hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER; - RS::get_singleton()->instance_set_layer_mask(instance, layer); //gizmos are 26 - RS::get_singleton()->instance_geometry_set_flag(instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); -} - -void EditorNode3DGizmo::add_mesh(const Ref &p_mesh, bool p_billboard, const Ref &p_skin_reference, const Ref &p_material) { - 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; - if (valid) { - ins.create_instance(spatial_node, hidden); - RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); - if (ins.material.is_valid()) { - RS::get_singleton()->instance_geometry_set_material_override(ins.instance, p_material->get_rid()); - } - } - - instances.push_back(ins); -} - -void EditorNode3DGizmo::add_lines(const Vector &p_lines, const Ref &p_material, bool p_billboard, const Color &p_modulate) { - add_vertices(p_lines, p_material, Mesh::PRIMITIVE_LINES, p_billboard, p_modulate); -} - -void EditorNode3DGizmo::add_vertices(const Vector &p_vertices, const Ref &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard, const Color &p_modulate) { - if (p_vertices.is_empty()) { - return; - } - - ERR_FAIL_COND(!spatial_node); - Instance ins; - - Ref mesh = memnew(ArrayMesh); - Array a; - a.resize(Mesh::ARRAY_MAX); - - a[Mesh::ARRAY_VERTEX] = p_vertices; - - Vector color; - color.resize(p_vertices.size()); - { - Color *w = color.ptrw(); - for (int i = 0; i < p_vertices.size(); i++) { - if (is_selected()) { - w[i] = Color(1, 1, 1, 0.8) * p_modulate; - } else { - w[i] = Color(1, 1, 1, 0.2) * p_modulate; - } - } - } - - a[Mesh::ARRAY_COLOR] = color; - - mesh->add_surface_from_arrays(p_primitive_type, a); - mesh->surface_set_material(0, p_material); - - if (p_billboard) { - float md = 0; - for (int i = 0; i < p_vertices.size(); i++) { - md = MAX(0, p_vertices[i].length()); - } - if (md) { - mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); - } - } - - ins.billboard = p_billboard; - ins.mesh = mesh; - 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); -} - -void EditorNode3DGizmo::add_unscaled_billboard(const Ref &p_material, float p_scale, const Color &p_modulate) { - ERR_FAIL_COND(!spatial_node); - Instance ins; - - Vector vs; - Vector uv; - Vector colors; - - vs.push_back(Vector3(-p_scale, p_scale, 0)); - vs.push_back(Vector3(p_scale, p_scale, 0)); - vs.push_back(Vector3(p_scale, -p_scale, 0)); - vs.push_back(Vector3(-p_scale, -p_scale, 0)); - - uv.push_back(Vector2(0, 0)); - uv.push_back(Vector2(1, 0)); - uv.push_back(Vector2(1, 1)); - uv.push_back(Vector2(0, 1)); - - colors.push_back(p_modulate); - colors.push_back(p_modulate); - colors.push_back(p_modulate); - colors.push_back(p_modulate); - - Ref mesh = memnew(ArrayMesh); - Array a; - a.resize(Mesh::ARRAY_MAX); - a[Mesh::ARRAY_VERTEX] = vs; - a[Mesh::ARRAY_TEX_UV] = uv; - Vector indices; - indices.push_back(0); - indices.push_back(1); - indices.push_back(2); - indices.push_back(0); - indices.push_back(2); - indices.push_back(3); - a[Mesh::ARRAY_INDEX] = indices; - a[Mesh::ARRAY_COLOR] = colors; - mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); - mesh->surface_set_material(0, p_material); - - float md = 0; - for (int i = 0; i < vs.size(); i++) { - md = MAX(0, vs[i].length()); - } - if (md) { - mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); - } - - selectable_icon_size = p_scale; - 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()); - } - - selectable_icon_size = p_scale; - - instances.push_back(ins); -} - -void EditorNode3DGizmo::add_collision_triangles(const Ref &p_tmesh) { - collision_mesh = p_tmesh; -} - -void EditorNode3DGizmo::add_collision_segments(const Vector &p_lines) { - int from = collision_segments.size(); - collision_segments.resize(from + p_lines.size()); - for (int i = 0; i < p_lines.size(); i++) { - collision_segments.write[from + i] = p_lines[i]; - } -} - -void EditorNode3DGizmo::add_handles(const Vector &p_handles, const Ref &p_material, bool p_billboard, bool p_secondary) { - billboard_handle = p_billboard; - - if (!is_selected() || !is_editable()) { - return; - } - - ERR_FAIL_COND(!spatial_node); - - Instance ins; - - Ref mesh = memnew(ArrayMesh); - - Array a; - a.resize(RS::ARRAY_MAX); - a[RS::ARRAY_VERTEX] = p_handles; - Vector colors; - { - colors.resize(p_handles.size()); - Color *w = colors.ptrw(); - for (int i = 0; i < p_handles.size(); i++) { - Color col(1, 1, 1, 1); - if (is_handle_highlighted(i)) { - col = Color(0, 0, 1, 0.9); - } - - if (Node3DEditor::get_singleton()->get_over_gizmo_handle() != i) { - col.a = 0.8; - } - - w[i] = col; - } - } - a[RS::ARRAY_COLOR] = colors; - mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a); - mesh->surface_set_material(0, p_material); - - if (p_billboard) { - float md = 0; - for (int i = 0; i < p_handles.size(); i++) { - md = MAX(0, p_handles[i].length()); - } - if (md) { - mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); - } - } - - 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]; - } - } -} - -void EditorNode3DGizmo::add_solid_box(Ref &p_material, Vector3 p_size, Vector3 p_position) { - ERR_FAIL_COND(!spatial_node); - - BoxMesh box_mesh; - box_mesh.set_size(p_size); - - Array arrays = box_mesh.surface_get_arrays(0); - PackedVector3Array vertex = arrays[RS::ARRAY_VERTEX]; - Vector3 *w = vertex.ptrw(); - - for (int i = 0; i < vertex.size(); ++i) { - w[i] += p_position; - } - - arrays[RS::ARRAY_VERTEX] = vertex; - - Ref 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); -} - -bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector &p_frustum) { - 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) { - Vector3 origin = spatial_node->get_global_transform().get_origin(); - - const Plane *p = p_frustum.ptr(); - int fc = p_frustum.size(); - - bool any_out = false; - - for (int j = 0; j < fc; j++) { - if (p[j].is_point_over(origin)) { - any_out = true; - break; - } - } - - return !any_out; - } - - if (collision_segments.size()) { - const Plane *p = p_frustum.ptr(); - int fc = p_frustum.size(); - - int vc = collision_segments.size(); - const Vector3 *vptr = collision_segments.ptr(); - Transform3D t = spatial_node->get_global_transform(); - - bool any_out = false; - for (int j = 0; j < fc; j++) { - for (int i = 0; i < vc; i++) { - Vector3 v = t.xform(vptr[i]); - if (p[j].is_point_over(v)) { - any_out = true; - break; - } - } - if (any_out) { - break; - } - } - - if (!any_out) { - return true; - } - } - - if (collision_mesh.is_valid()) { - Transform3D t = spatial_node->get_global_transform(); - - Vector3 mesh_scale = t.get_basis().get_scale(); - t.orthonormalize(); - - Transform3D it = t.affine_inverse(); - - Vector transformed_frustum; - - for (int i = 0; i < p_frustum.size(); i++) { - transformed_frustum.push_back(it.xform(p_frustum[i])); - } - - Vector 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)) { - return true; - } - } - - 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); - - if (hidden && !gizmo_plugin->is_selectable_when_hidden()) { - return false; - } - - 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)); - } - - float min_d = 1e20; - int idx = -1; - - 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_sec_first && idx != -1) { - *r_gizmo_handle = idx; - return true; - } - - min_d = 1e20; - - 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 (idx >= 0) { - *r_gizmo_handle = idx; - return true; - } - } - - if (selectable_icon_size > 0.0f) { - Transform3D t = spatial_node->get_global_transform(); - Vector3 camera_position = p_camera->get_camera_transform().origin; - if (!camera_position.is_equal_approx(t.origin)) { - t.set_look_at(t.origin, camera_position); - } - - float scale = t.origin.distance_to(p_camera->get_camera_transform().origin); - - if (p_camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) { - float aspect = p_camera->get_viewport()->get_visible_rect().size.aspect(); - float size = p_camera->get_size(); - scale = size / aspect; - } - - Point2 center = p_camera->unproject_position(t.origin); - - Transform3D orig_camera_transform = p_camera->get_camera_transform(); - - if (!orig_camera_transform.origin.is_equal_approx(t.origin) && - ABS(orig_camera_transform.basis.get_axis(Vector3::AXIS_Z).dot(Vector3(0, 1, 0))) < 0.99) { - p_camera->look_at(t.origin); - } - - Vector3 c0 = t.xform(Vector3(selectable_icon_size, selectable_icon_size, 0) * scale); - Vector3 c1 = t.xform(Vector3(-selectable_icon_size, -selectable_icon_size, 0) * scale); - - Point2 p0 = p_camera->unproject_position(c0); - Point2 p1 = p_camera->unproject_position(c1); - - p_camera->set_global_transform(orig_camera_transform); - - Rect2 rect(p0, (p1 - p0).abs()); - - rect.set_position(center - rect.get_size() / 2.0); - - if (rect.has_point(p_point)) { - r_pos = t.origin; - r_normal = -p_camera->project_ray_normal(p_point); - return true; - } - } - - if (collision_segments.size()) { - Plane camp(p_camera->get_transform().origin, (-p_camera->get_transform().basis.get_axis(2)).normalized()); - - int vc = collision_segments.size(); - const Vector3 *vptr = collision_segments.ptr(); - 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)); - } - - Vector3 cp; - float cpd = 1e20; - - for (int i = 0; i < vc / 2; i++) { - Vector3 a = t.xform(vptr[i * 2 + 0]); - Vector3 b = t.xform(vptr[i * 2 + 1]); - Vector2 s[2]; - s[0] = p_camera->unproject_position(a); - s[1] = p_camera->unproject_position(b); - - Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, s); - - float pd = p.distance_to(p_point); - - if (pd < cpd) { - float d = s[0].distance_to(s[1]); - Vector3 tcp; - if (d > 0) { - float d2 = s[0].distance_to(p) / d; - tcp = a + (b - a) * d2; - - } else { - tcp = a; - } - - if (camp.distance_to(tcp) < p_camera->get_near()) { - continue; - } - cp = tcp; - cpd = pd; - } - } - - if (cpd < 8) { - r_pos = cp; - r_normal = -p_camera->project_ray_normal(p_point); - return true; - } - } - - if (collision_mesh.is_valid()) { - Transform3D gt = spatial_node->get_global_transform(); - - if (billboard_handle) { - gt.set_look_at(gt.origin, gt.origin - p_camera->get_transform().basis.get_axis(2), p_camera->get_transform().basis.get_axis(1)); - } - - Transform3D ai = gt.affine_inverse(); - Vector3 ray_from = ai.xform(p_camera->project_ray_origin(p_point)); - Vector3 ray_dir = ai.basis.xform(p_camera->project_ray_normal(p_point)).normalized(); - Vector3 rpos, rnorm; - - if (collision_mesh->intersect_ray(ray_from, ray_dir, rpos, rnorm)) { - r_pos = gt.xform(rpos); - r_normal = gt.basis.xform(rnorm).normalized(); - return true; - } - } - - return false; -} - -void EditorNode3DGizmo::create() { - ERR_FAIL_COND(!spatial_node); - ERR_FAIL_COND(valid); - valid = true; - - for (int i = 0; i < instances.size(); i++) { - instances.write[i].create_instance(spatial_node, hidden); - } - - transform(); -} - -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()); - } -} - -void EditorNode3DGizmo::free() { - ERR_FAIL_COND(!spatial_node); - ERR_FAIL_COND(!valid); - - for (int i = 0; i < instances.size(); i++) { - if (instances[i].instance.is_valid()) { - RS::get_singleton()->free(instances[i].instance); - } - instances.write[i].instance = RID(); - } - - clear(); - - valid = false; -} - -void EditorNode3DGizmo::set_hidden(bool p_hidden) { - hidden = p_hidden; - int layer = hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER; - for (int i = 0; i < instances.size(); ++i) { - RS::get_singleton()->instance_set_layer_mask(instances[i].instance, layer); - } -} - -void EditorNode3DGizmo::set_plugin(EditorNode3DGizmoPlugin *p_plugin) { - gizmo_plugin = 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()), DEFVAL(Variant())); - 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("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); - - 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"))); - - MethodInfo hvget(Variant::NIL, "_get_handle_value", PropertyInfo(Variant::INT, "index")); - 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")); - cm.default_arguments.push_back(false); - BIND_VMETHOD(cm); -} - -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; -} - -EditorNode3DGizmo::~EditorNode3DGizmo() { - if (gizmo_plugin != nullptr) { - gizmo_plugin->unregister_gizmo(this); - } - clear(); -} - -Vector3 EditorNode3DGizmo::get_handle_pos(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, handles.size(), Vector3()); - - return handles[p_idx]; -} - -//// light gizmo - -Light3DGizmoPlugin::Light3DGizmoPlugin() { - // Enable vertex colors for the materials below as the gizmo color depends on the light color. - create_material("lines_primary", Color(1, 1, 1), false, false, true); - create_material("lines_secondary", Color(1, 1, 1, 0.35), false, false, true); - create_material("lines_billboard", Color(1, 1, 1), true, false, true); - - create_icon_material("light_directional_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoDirectionalLight"), SNAME("EditorIcons"))); - create_icon_material("light_omni_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoLight"), SNAME("EditorIcons"))); - create_icon_material("light_spot_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoSpotLight"), SNAME("EditorIcons"))); - - create_handle_material("handles"); - create_handle_material("handles_billboard", true); -} - -bool Light3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String Light3DGizmoPlugin::get_gizmo_name() const { - return "Light3D"; -} - -int Light3DGizmoPlugin::get_priority() const { - return -1; -} - -String Light3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - if (p_idx == 0) { - return "Radius"; - } else { - return "Aperture"; - } -} - -Variant Light3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); - if (p_idx == 0) { - return light->get_param(Light3D::PARAM_RANGE); - } - if (p_idx == 1) { - return light->get_param(Light3D::PARAM_SPOT_ANGLE); - } - - return Variant(); -} - -static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vector3 &p_to, float p_arc_radius, const Transform3D &p_arc_xform) { - //bleh, discrete is simpler - static const int arc_test_points = 64; - float min_d = 1e20; - Vector3 min_p; - - for (int i = 0; i < arc_test_points; i++) { - float a = i * Math_PI * 0.5 / arc_test_points; - float an = (i + 1) * Math_PI * 0.5 / arc_test_points; - Vector3 p = Vector3(Math::cos(a), 0, -Math::sin(a)) * p_arc_radius; - Vector3 n = Vector3(Math::cos(an), 0, -Math::sin(an)) * p_arc_radius; - - Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(p, n, p_from, p_to, ra, rb); - - float d = ra.distance_to(rb); - if (d < min_d) { - min_d = d; - min_p = ra; - } - } - - //min_p = p_arc_xform.affine_inverse().xform(min_p); - float a = (Math_PI * 0.5) - Vector2(min_p.x, -min_p.z).angle(); - return Math::rad2deg(a); -} - -void Light3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); - Transform3D gt = light->get_global_transform(); - Transform3D gi = gt.affine_inverse(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - 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 (Object::cast_to(light)) { - Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, -4096), s[0], s[1], ra, rb); - - float d = -ra.z; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d <= 0) { // Equal is here for negative zero. - d = 0; - } - - light->set_param(Light3D::PARAM_RANGE, d); - } else if (Object::cast_to(light)) { - Plane cp = Plane(gt.origin, p_camera->get_transform().basis.get_axis(2)); - - Vector3 inters; - if (cp.intersects_ray(ray_from, ray_dir, &inters)) { - float r = inters.distance_to(gt.origin); - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - r = Math::snapped(r, Node3DEditor::get_singleton()->get_translate_snap()); - } - - light->set_param(Light3D::PARAM_RANGE, r); - } - } - - } else if (p_idx == 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) { - Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); - if (p_cancel) { - light->set_param(p_idx == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore); - - } else if (p_idx == 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) { - 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)); - ur->add_undo_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, p_restore); - ur->commit_action(); - } -} - -void Light3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); - - Color color = light->get_color(); - // Make the gizmo color as bright as possible for better visibility - color.set_hsv(color.get_h(), color.get_s(), 1); - - p_gizmo->clear(); - - if (Object::cast_to(light)) { - Ref material = get_material("lines_primary", p_gizmo); - Ref icon = get_material("light_directional_icon", p_gizmo); - - const int arrow_points = 7; - const float arrow_length = 1.5; - - Vector3 arrow[arrow_points] = { - Vector3(0, 0, -1), - Vector3(0, 0.8, 0), - Vector3(0, 0.3, 0), - Vector3(0, 0.3, arrow_length), - Vector3(0, -0.3, arrow_length), - Vector3(0, -0.3, 0), - Vector3(0, -0.8, 0) - }; - - int arrow_sides = 2; - - Vector lines; - - for (int i = 0; i < arrow_sides; i++) { - for (int j = 0; j < arrow_points; j++) { - Basis ma(Vector3(0, 0, 1), Math_PI * i / arrow_sides); - - Vector3 v1 = arrow[j] - Vector3(0, 0, arrow_length); - Vector3 v2 = arrow[(j + 1) % arrow_points] - Vector3(0, 0, arrow_length); - - lines.push_back(ma.xform(v1)); - lines.push_back(ma.xform(v2)); - } - } - - p_gizmo->add_lines(lines, material, false, color); - p_gizmo->add_unscaled_billboard(icon, 0.05, color); - } - - if (Object::cast_to(light)) { - // Use both a billboard circle and 3 non-billboard circles for a better sphere-like representation - const Ref lines_material = get_material("lines_secondary", p_gizmo); - const Ref lines_billboard_material = get_material("lines_billboard", p_gizmo); - const Ref icon = get_material("light_omni_icon", p_gizmo); - - OmniLight3D *on = Object::cast_to(light); - const float r = on->get_param(Light3D::PARAM_RANGE); - Vector points; - Vector points_billboard; - - for (int i = 0; i < 120; i++) { - // Create a circle - const float ra = Math::deg2rad((float)(i * 3)); - const float rb = Math::deg2rad((float)((i + 1) * 3)); - const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - // Draw axis-aligned circles - points.push_back(Vector3(a.x, 0, a.y)); - points.push_back(Vector3(b.x, 0, b.y)); - points.push_back(Vector3(0, a.x, a.y)); - points.push_back(Vector3(0, b.x, b.y)); - points.push_back(Vector3(a.x, a.y, 0)); - points.push_back(Vector3(b.x, b.y, 0)); - - // Draw a billboarded circle - points_billboard.push_back(Vector3(a.x, a.y, 0)); - points_billboard.push_back(Vector3(b.x, b.y, 0)); - } - - p_gizmo->add_lines(points, lines_material, true, color); - p_gizmo->add_lines(points_billboard, lines_billboard_material, true, color); - p_gizmo->add_unscaled_billboard(icon, 0.05, color); - - Vector handles; - handles.push_back(Vector3(r, 0, 0)); - p_gizmo->add_handles(handles, get_material("handles_billboard"), true); - } - - if (Object::cast_to(light)) { - const Ref material_primary = get_material("lines_primary", p_gizmo); - const Ref material_secondary = get_material("lines_secondary", p_gizmo); - const Ref icon = get_material("light_spot_icon", p_gizmo); - - Vector points_primary; - Vector points_secondary; - SpotLight3D *sl = Object::cast_to(light); - - float r = sl->get_param(Light3D::PARAM_RANGE); - float w = r * Math::sin(Math::deg2rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); - float d = r * Math::cos(Math::deg2rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); - - for (int i = 0; i < 120; i++) { - // Draw a circle - const float ra = Math::deg2rad((float)(i * 3)); - const float rb = Math::deg2rad((float)((i + 1) * 3)); - const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; - const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w; - - points_primary.push_back(Vector3(a.x, a.y, -d)); - points_primary.push_back(Vector3(b.x, b.y, -d)); - - if (i % 15 == 0) { - // Draw 8 lines from the cone origin to the sides of the circle - points_secondary.push_back(Vector3(a.x, a.y, -d)); - points_secondary.push_back(Vector3()); - } - } - - points_primary.push_back(Vector3(0, 0, -r)); - points_primary.push_back(Vector3()); - - p_gizmo->add_lines(points_primary, material_primary, false, color); - p_gizmo->add_lines(points_secondary, material_secondary, false, color); - - Vector handles; - handles.push_back(Vector3(0, 0, -r)); - handles.push_back(Vector3(w, 0, -d)); - - p_gizmo->add_handles(handles, get_material("handles")); - p_gizmo->add_unscaled_billboard(icon, 0.05, color); - } -} - -////// - -//// player gizmo -AudioStreamPlayer3DGizmoPlugin::AudioStreamPlayer3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1)); - - create_icon_material("stream_player_3d_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("Gizmo3DSamplePlayer"), SNAME("EditorIcons"))); - create_material("stream_player_3d_material_primary", gizmo_color); - create_material("stream_player_3d_material_secondary", gizmo_color * Color(1, 1, 1, 0.35)); - create_handle_material("handles"); -} - -bool AudioStreamPlayer3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String AudioStreamPlayer3DGizmoPlugin::get_gizmo_name() const { - return "AudioStreamPlayer3D"; -} - -int AudioStreamPlayer3DGizmoPlugin::get_priority() const { - return -1; -} - -String AudioStreamPlayer3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - return "Emission Radius"; -} - -Variant AudioStreamPlayer3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - AudioStreamPlayer3D *player = Object::cast_to(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) { - AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); - - Transform3D gt = player->get_global_transform(); - Transform3D gi = gt.affine_inverse(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - Vector3 ray_to = ray_from + ray_dir * 4096; - - ray_from = gi.xform(ray_from); - ray_to = gi.xform(ray_to); - - float closest_dist = 1e20; - float closest_angle = 1e20; - - for (int i = 0; i < 180; i++) { - float a = Math::deg2rad((float)i); - float an = Math::deg2rad((float)(i + 1)); - - Vector3 from(Math::sin(a), 0, -Math::cos(a)); - Vector3 to(Math::sin(an), 0, -Math::cos(an)); - - Vector3 r1, r2; - Geometry3D::get_closest_points_between_segments(from, to, ray_from, ray_to, r1, r2); - float d = r1.distance_to(r2); - if (d < closest_dist) { - closest_dist = d; - closest_angle = i; - } - } - - if (closest_angle < 91) { - player->set_emission_angle(closest_angle); - } -} - -void AudioStreamPlayer3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); - - if (p_cancel) { - player->set_emission_angle(p_restore); - - } else { - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change AudioStreamPlayer3D Emission Angle")); - ur->add_do_method(player, "set_emission_angle", player->get_emission_angle()); - ur->add_undo_method(player, "set_emission_angle", p_restore); - ur->commit_action(); - } -} - -void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - const AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - const Ref icon = get_material("stream_player_3d_icon", p_gizmo); - - if (player->is_emission_angle_enabled()) { - const float pc = player->get_emission_angle(); - const float ofs = -Math::cos(Math::deg2rad(pc)); - const float radius = Math::sin(Math::deg2rad(pc)); - - Vector points_primary; - points_primary.resize(200); - - real_t step = Math_TAU / 100.0; - for (int i = 0; i < 100; i++) { - const float a = i * step; - const float an = (i + 1) * step; - - const Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs); - const Vector3 to(Math::sin(an) * radius, Math::cos(an) * radius, ofs); - - points_primary.write[i * 2 + 0] = from; - points_primary.write[i * 2 + 1] = to; - } - - const Ref material_primary = get_material("stream_player_3d_material_primary", p_gizmo); - p_gizmo->add_lines(points_primary, material_primary); - - Vector points_secondary; - points_secondary.resize(16); - - for (int i = 0; i < 8; i++) { - const float a = i * (Math_TAU / 8.0); - const Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs); - - points_secondary.write[i * 2 + 0] = from; - points_secondary.write[i * 2 + 1] = Vector3(); - } - - const Ref material_secondary = get_material("stream_player_3d_material_secondary", p_gizmo); - p_gizmo->add_lines(points_secondary, material_secondary); - - Vector handles; - const float ha = Math::deg2rad(player->get_emission_angle()); - handles.push_back(Vector3(Math::sin(ha), 0, -Math::cos(ha))); - p_gizmo->add_handles(handles, get_material("handles")); - } - - p_gizmo->add_unscaled_billboard(icon, 0.05); -} - -////// - -Camera3DGizmoPlugin::Camera3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8)); - - create_material("camera_material", gizmo_color); - create_handle_material("handles"); -} - -bool Camera3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String Camera3DGizmoPlugin::get_gizmo_name() const { - return "Camera3D"; -} - -int Camera3DGizmoPlugin::get_priority() const { - return -1; -} - -String Camera3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); - - if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { - return "FOV"; - } else { - return "Size"; - } -} - -Variant Camera3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); - - if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { - return camera->get_fov(); - } else { - return camera->get_size(); - } -} - -void Camera3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); - - Transform3D gt = camera->get_global_transform(); - Transform3D gi = gt.affine_inverse(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - 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 (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { - Transform3D gt2 = camera->get_global_transform(); - float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], 1.0, gt2); - camera->set("fov", CLAMP(a * 2.0, 1, 179)); - } else { - Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb); - float d = ra.x * 2.0; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - d = CLAMP(d, 0.1, 16384); - - camera->set("size", d); - } -} - -void Camera3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); - - if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { - if (p_cancel) { - camera->set("fov", p_restore); - } else { - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Camera FOV")); - ur->add_do_property(camera, "fov", camera->get_fov()); - ur->add_undo_property(camera, "fov", p_restore); - ur->commit_action(); - } - - } else { - if (p_cancel) { - camera->set("size", p_restore); - } else { - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Camera Size")); - ur->add_do_property(camera, "size", camera->get_size()); - ur->add_undo_property(camera, "size", p_restore); - ur->commit_action(); - } - } -} - -void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - Vector handles; - - Ref material = get_material("camera_material", p_gizmo); - -#define ADD_TRIANGLE(m_a, m_b, m_c) \ - { \ - lines.push_back(m_a); \ - lines.push_back(m_b); \ - lines.push_back(m_b); \ - lines.push_back(m_c); \ - lines.push_back(m_c); \ - lines.push_back(m_a); \ - } - -#define ADD_QUAD(m_a, m_b, m_c, m_d) \ - { \ - lines.push_back(m_a); \ - lines.push_back(m_b); \ - lines.push_back(m_b); \ - lines.push_back(m_c); \ - lines.push_back(m_c); \ - lines.push_back(m_d); \ - lines.push_back(m_d); \ - lines.push_back(m_a); \ - } - - switch (camera->get_projection()) { - case Camera3D::PROJECTION_PERSPECTIVE: { - // The real FOV is halved for accurate representation - float fov = camera->get_fov() / 2.0; - - Vector3 side = Vector3(Math::sin(Math::deg2rad(fov)), 0, -Math::cos(Math::deg2rad(fov))); - Vector3 nside = side; - nside.x = -nside.x; - Vector3 up = Vector3(0, side.x, 0); - - ADD_TRIANGLE(Vector3(), side + up, side - up); - ADD_TRIANGLE(Vector3(), nside + up, nside - up); - ADD_TRIANGLE(Vector3(), side + up, nside + up); - ADD_TRIANGLE(Vector3(), side - up, nside - up); - - handles.push_back(side); - side.x *= 0.25; - nside.x *= 0.25; - Vector3 tup(0, up.y * 3 / 2, side.z); - ADD_TRIANGLE(tup, side + up, nside + up); - - } break; - case Camera3D::PROJECTION_ORTHOGONAL: { - float size = camera->get_size(); - - float hsize = size * 0.5; - Vector3 right(hsize, 0, 0); - Vector3 up(0, hsize, 0); - Vector3 back(0, 0, -1.0); - Vector3 front(0, 0, 0); - - ADD_QUAD(-up - right, -up + right, up + right, up - right); - ADD_QUAD(-up - right + back, -up + right + back, up + right + back, up - right + back); - ADD_QUAD(up + right, up + right + back, up - right + back, up - right); - ADD_QUAD(-up + right, -up + right + back, -up - right + back, -up - right); - - handles.push_back(right + back); - - right.x *= 0.25; - Vector3 tup(0, up.y * 3 / 2, back.z); - ADD_TRIANGLE(tup, right + up + back, -right + up + back); - - } break; - case Camera3D::PROJECTION_FRUSTUM: { - float hsize = camera->get_size() / 2.0; - - Vector3 side = Vector3(hsize, 0, -camera->get_near()).normalized(); - Vector3 nside = side; - nside.x = -nside.x; - Vector3 up = Vector3(0, side.x, 0); - Vector3 offset = Vector3(camera->get_frustum_offset().x, camera->get_frustum_offset().y, 0.0); - - ADD_TRIANGLE(Vector3(), side + up + offset, side - up + offset); - ADD_TRIANGLE(Vector3(), nside + up + offset, nside - up + offset); - ADD_TRIANGLE(Vector3(), side + up + offset, nside + up + offset); - ADD_TRIANGLE(Vector3(), side - up + offset, nside - up + offset); - - side.x *= 0.25; - nside.x *= 0.25; - Vector3 tup(0, up.y * 3 / 2, side.z); - ADD_TRIANGLE(tup + offset, side + up + offset, nside + up + offset); - } - } - -#undef ADD_TRIANGLE -#undef ADD_QUAD - - p_gizmo->add_lines(lines, material); - p_gizmo->add_handles(handles, get_material("handles")); - - ClippedCamera3D *clipcam = Object::cast_to(camera); - if (clipcam) { - Node3D *parent = Object::cast_to(camera->get_parent()); - if (!parent) { - return; - } - Vector3 cam_normal = -camera->get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized(); - Vector3 cam_x = camera->get_global_transform().basis.get_axis(Vector3::AXIS_X).normalized(); - Vector3 cam_y = camera->get_global_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); - Vector3 cam_pos = camera->get_global_transform().origin; - Vector3 parent_pos = parent->get_global_transform().origin; - - Plane parent_plane(parent_pos, cam_normal); - Vector3 ray_from = parent_plane.project(cam_pos); - - lines.clear(); - lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); - lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); - - lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); - lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); - - lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); - lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); - - lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); - lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); - - if (parent_plane.distance_to(cam_pos) < 0) { - lines.push_back(ray_from); - lines.push_back(cam_pos); - } - - Transform3D local = camera->get_global_transform().affine_inverse(); - for (int i = 0; i < lines.size(); i++) { - lines.write[i] = local.xform(lines[i]); - } - - p_gizmo->add_lines(lines, material); - } -} - -////// - -MeshInstance3DGizmoPlugin::MeshInstance3DGizmoPlugin() { -} - -bool MeshInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr && Object::cast_to(p_spatial) == nullptr; -} - -String MeshInstance3DGizmoPlugin::get_gizmo_name() const { - return "MeshInstance3D"; -} - -int MeshInstance3DGizmoPlugin::get_priority() const { - return -1; -} - -bool MeshInstance3DGizmoPlugin::can_be_hidden() const { - return false; -} - -void MeshInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - MeshInstance3D *mesh = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Ref m = mesh->get_mesh(); - - if (!m.is_valid()) { - return; //none - } - - Ref tm = m->generate_triangle_mesh(); - if (tm.is_valid()) { - p_gizmo->add_collision_triangles(tm); - } -} - -///// - -OccluderInstance3DGizmoPlugin::OccluderInstance3DGizmoPlugin() { - create_material("line_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/occluder", Color(0.8, 0.5, 1))); -} - -bool OccluderInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String OccluderInstance3DGizmoPlugin::get_gizmo_name() const { - return "OccluderInstance3D"; -} - -int OccluderInstance3DGizmoPlugin::get_priority() const { - return -1; -} - -void OccluderInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - OccluderInstance3D *occluder_instance = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Ref o = occluder_instance->get_occluder(); - - if (!o.is_valid()) { - return; - } - - Vector lines = o->get_debug_lines(); - if (!lines.is_empty()) { - Ref material = get_material("line_material", p_gizmo); - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); - } -} - -///// - -Sprite3DGizmoPlugin::Sprite3DGizmoPlugin() { -} - -bool Sprite3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String Sprite3DGizmoPlugin::get_gizmo_name() const { - return "Sprite3D"; -} - -int Sprite3DGizmoPlugin::get_priority() const { - return -1; -} - -bool Sprite3DGizmoPlugin::can_be_hidden() const { - return false; -} - -void Sprite3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Sprite3D *sprite = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Ref tm = sprite->generate_triangle_mesh(); - if (tm.is_valid()) { - p_gizmo->add_collision_triangles(tm); - } -} - -/// - -Position3DGizmoPlugin::Position3DGizmoPlugin() { - pos3d_mesh = Ref(memnew(ArrayMesh)); - cursor_points = Vector(); - - Vector cursor_colors; - const float cs = 0.25; - // Add more points to create a "hard stop" in the color gradient. - cursor_points.push_back(Vector3(+cs, 0, 0)); - cursor_points.push_back(Vector3()); - cursor_points.push_back(Vector3()); - cursor_points.push_back(Vector3(-cs, 0, 0)); - - cursor_points.push_back(Vector3(0, +cs, 0)); - cursor_points.push_back(Vector3()); - cursor_points.push_back(Vector3()); - cursor_points.push_back(Vector3(0, -cs, 0)); - - cursor_points.push_back(Vector3(0, 0, +cs)); - cursor_points.push_back(Vector3()); - cursor_points.push_back(Vector3()); - cursor_points.push_back(Vector3(0, 0, -cs)); - - // Use the axis color which is brighter for the positive axis. - // Use a darkened axis color for the negative axis. - // This makes it possible to see in which direction the Position3D node is rotated - // (which can be important depending on how it's used). - const Color color_x = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("axis_x_color"), SNAME("Editor")); - cursor_colors.push_back(color_x); - cursor_colors.push_back(color_x); - // FIXME: Use less strong darkening factor once GH-48573 is fixed. - // The current darkening factor compensates for lines being too bright in the 3D editor. - cursor_colors.push_back(color_x.lerp(Color(0, 0, 0), 0.75)); - cursor_colors.push_back(color_x.lerp(Color(0, 0, 0), 0.75)); - - const Color color_y = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("axis_y_color"), SNAME("Editor")); - cursor_colors.push_back(color_y); - cursor_colors.push_back(color_y); - cursor_colors.push_back(color_y.lerp(Color(0, 0, 0), 0.75)); - cursor_colors.push_back(color_y.lerp(Color(0, 0, 0), 0.75)); - - const Color color_z = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("axis_z_color"), SNAME("Editor")); - cursor_colors.push_back(color_z); - cursor_colors.push_back(color_z); - cursor_colors.push_back(color_z.lerp(Color(0, 0, 0), 0.75)); - cursor_colors.push_back(color_z.lerp(Color(0, 0, 0), 0.75)); - - Ref mat = memnew(StandardMaterial3D); - mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); - mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); - - Array d; - d.resize(RS::ARRAY_MAX); - d[Mesh::ARRAY_VERTEX] = cursor_points; - d[Mesh::ARRAY_COLOR] = cursor_colors; - pos3d_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, d); - pos3d_mesh->surface_set_material(0, mat); -} - -bool Position3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String Position3DGizmoPlugin::get_gizmo_name() const { - return "Position3D"; -} - -int Position3DGizmoPlugin::get_priority() const { - return -1; -} - -void Position3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - p_gizmo->clear(); - p_gizmo->add_mesh(pos3d_mesh); - p_gizmo->add_collision_segments(cursor_points); -} - -///// - -Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4)); - create_material("skeleton_material", gizmo_color); -} - -bool Skeleton3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String Skeleton3DGizmoPlugin::get_gizmo_name() const { - return "Skeleton3D"; -} - -int Skeleton3DGizmoPlugin::get_priority() const { - return -1; -} - -void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Skeleton3D *skel = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Ref material = get_material("skeleton_material", p_gizmo); - - Ref surface_tool(memnew(SurfaceTool)); - - surface_tool->begin(Mesh::PRIMITIVE_LINES); - surface_tool->set_material(material); - Vector grests; - grests.resize(skel->get_bone_count()); - - Vector bones; - Vector weights; - bones.resize(4); - weights.resize(4); - - for (int i = 0; i < 4; i++) { - bones.write[i] = 0; - weights.write[i] = 0; - } - - weights.write[0] = 1; - - AABB aabb; - - Color bonecolor = Color(1.0, 0.4, 0.4, 0.3); - Color rootcolor = Color(0.4, 1.0, 0.4, 0.1); - - for (int i_bone = 0; i_bone < skel->get_bone_count(); i_bone++) { - int i = skel->get_process_order(i_bone); - - int parent = skel->get_bone_parent(i); - - if (parent >= 0) { - grests.write[i] = grests[parent] * skel->get_bone_rest(i); - - Vector3 v0 = grests[parent].origin; - Vector3 v1 = grests[i].origin; - Vector3 d = (v1 - v0).normalized(); - float dist = v0.distance_to(v1); - - //find closest axis - int closest = -1; - float closest_d = 0.0; - - for (int j = 0; j < 3; j++) { - float dp = Math::abs(grests[parent].basis[j].normalized().dot(d)); - if (j == 0 || dp > closest_d) { - closest = j; - } - } - - //find closest other - Vector3 first; - Vector3 points[4]; - int pointidx = 0; - for (int j = 0; j < 3; j++) { - bones.write[0] = parent; - surface_tool->set_bones(bones); - surface_tool->set_weights(weights); - surface_tool->set_color(rootcolor); - surface_tool->add_vertex(v0 - grests[parent].basis[j].normalized() * dist * 0.05); - surface_tool->set_bones(bones); - surface_tool->set_weights(weights); - surface_tool->set_color(rootcolor); - surface_tool->add_vertex(v0 + grests[parent].basis[j].normalized() * dist * 0.05); - - if (j == closest) { - continue; - } - - Vector3 axis; - if (first == Vector3()) { - axis = d.cross(d.cross(grests[parent].basis[j])).normalized(); - first = axis; - } else { - axis = d.cross(first).normalized(); - } - - for (int k = 0; k < 2; k++) { - if (k == 1) { - axis = -axis; - } - Vector3 point = v0 + d * dist * 0.2; - point += axis * dist * 0.1; - - bones.write[0] = parent; - surface_tool->set_bones(bones); - surface_tool->set_weights(weights); - surface_tool->set_color(bonecolor); - surface_tool->add_vertex(v0); - surface_tool->set_bones(bones); - surface_tool->set_weights(weights); - surface_tool->set_color(bonecolor); - surface_tool->add_vertex(point); - - bones.write[0] = parent; - surface_tool->set_bones(bones); - surface_tool->set_weights(weights); - surface_tool->set_color(bonecolor); - surface_tool->add_vertex(point); - bones.write[0] = i; - surface_tool->set_bones(bones); - surface_tool->set_weights(weights); - surface_tool->set_color(bonecolor); - surface_tool->add_vertex(v1); - points[pointidx++] = point; - } - } - - SWAP(points[1], points[2]); - for (int j = 0; j < 4; j++) { - bones.write[0] = parent; - surface_tool->set_bones(bones); - surface_tool->set_weights(weights); - surface_tool->set_color(bonecolor); - surface_tool->add_vertex(points[j]); - surface_tool->set_bones(bones); - surface_tool->set_weights(weights); - surface_tool->set_color(bonecolor); - surface_tool->add_vertex(points[(j + 1) % 4]); - } - } else { - grests.write[i] = skel->get_bone_rest(i); - bones.write[0] = i; - } - } - - Ref m = surface_tool->commit(); - p_gizmo->add_mesh(m, false, skel->register_skin(Ref())); -} - -//// - -PhysicalBone3DGizmoPlugin::PhysicalBone3DGizmoPlugin() { - create_material("joint_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1))); -} - -bool PhysicalBone3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String PhysicalBone3DGizmoPlugin::get_gizmo_name() const { - return "PhysicalBone3D"; -} - -int PhysicalBone3DGizmoPlugin::get_priority() const { - return -1; -} - -void PhysicalBone3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - p_gizmo->clear(); - - PhysicalBone3D *physical_bone = Object::cast_to(p_gizmo->get_spatial_node()); - - if (!physical_bone) { - return; - } - - Skeleton3D *sk(physical_bone->find_skeleton_parent()); - if (!sk) { - return; - } - - PhysicalBone3D *pb(sk->get_physical_bone(physical_bone->get_bone_id())); - if (!pb) { - return; - } - - PhysicalBone3D *pbp(sk->get_physical_bone_parent(physical_bone->get_bone_id())); - if (!pbp) { - return; - } - - Vector points; - - switch (physical_bone->get_joint_type()) { - case PhysicalBone3D::JOINT_TYPE_PIN: { - Joint3DGizmoPlugin::CreatePinJointGizmo(physical_bone->get_joint_offset(), points); - } break; - case PhysicalBone3D::JOINT_TYPE_CONE: { - const PhysicalBone3D::ConeJointData *cjd(static_cast(physical_bone->get_joint_data())); - Joint3DGizmoPlugin::CreateConeTwistJointGizmo( - physical_bone->get_joint_offset(), - physical_bone->get_global_transform() * physical_bone->get_joint_offset(), - pb->get_global_transform(), - pbp->get_global_transform(), - cjd->swing_span, - cjd->twist_span, - &points, - &points); - } break; - case PhysicalBone3D::JOINT_TYPE_HINGE: { - const PhysicalBone3D::HingeJointData *hjd(static_cast(physical_bone->get_joint_data())); - Joint3DGizmoPlugin::CreateHingeJointGizmo( - physical_bone->get_joint_offset(), - physical_bone->get_global_transform() * physical_bone->get_joint_offset(), - pb->get_global_transform(), - pbp->get_global_transform(), - hjd->angular_limit_lower, - hjd->angular_limit_upper, - hjd->angular_limit_enabled, - points, - &points, - &points); - } break; - case PhysicalBone3D::JOINT_TYPE_SLIDER: { - const PhysicalBone3D::SliderJointData *sjd(static_cast(physical_bone->get_joint_data())); - Joint3DGizmoPlugin::CreateSliderJointGizmo( - physical_bone->get_joint_offset(), - physical_bone->get_global_transform() * physical_bone->get_joint_offset(), - pb->get_global_transform(), - pbp->get_global_transform(), - sjd->angular_limit_lower, - sjd->angular_limit_upper, - sjd->linear_limit_lower, - sjd->linear_limit_upper, - points, - &points, - &points); - } break; - case PhysicalBone3D::JOINT_TYPE_6DOF: { - const PhysicalBone3D::SixDOFJointData *sdofjd(static_cast(physical_bone->get_joint_data())); - Joint3DGizmoPlugin::CreateGeneric6DOFJointGizmo( - physical_bone->get_joint_offset(), - - physical_bone->get_global_transform() * physical_bone->get_joint_offset(), - pb->get_global_transform(), - pbp->get_global_transform(), - - sdofjd->axis_data[0].angular_limit_lower, - sdofjd->axis_data[0].angular_limit_upper, - sdofjd->axis_data[0].linear_limit_lower, - sdofjd->axis_data[0].linear_limit_upper, - sdofjd->axis_data[0].angular_limit_enabled, - sdofjd->axis_data[0].linear_limit_enabled, - - sdofjd->axis_data[1].angular_limit_lower, - sdofjd->axis_data[1].angular_limit_upper, - sdofjd->axis_data[1].linear_limit_lower, - sdofjd->axis_data[1].linear_limit_upper, - sdofjd->axis_data[1].angular_limit_enabled, - sdofjd->axis_data[1].linear_limit_enabled, - - sdofjd->axis_data[2].angular_limit_lower, - sdofjd->axis_data[2].angular_limit_upper, - sdofjd->axis_data[2].linear_limit_lower, - sdofjd->axis_data[2].linear_limit_upper, - sdofjd->axis_data[2].angular_limit_enabled, - sdofjd->axis_data[2].linear_limit_enabled, - - points, - &points, - &points); - } break; - default: - return; - } - - Ref material = get_material("joint_material", p_gizmo); - - p_gizmo->add_collision_segments(points); - p_gizmo->add_lines(points, material); -} - -///// - -RayCast3DGizmoPlugin::RayCast3DGizmoPlugin() { - const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); - const float gizmo_value = gizmo_color.get_v(); - const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); - create_material("shape_material_disabled", gizmo_color_disabled); -} - -bool RayCast3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String RayCast3DGizmoPlugin::get_gizmo_name() const { - return "RayCast3D"; -} - -int RayCast3DGizmoPlugin::get_priority() const { - return -1; -} - -void RayCast3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - RayCast3D *raycast = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - const Ref material = raycast->is_enabled() ? raycast->get_debug_material() : get_material("shape_material_disabled"); - - p_gizmo->add_lines(raycast->get_debug_line_vertices(), material); - - if (raycast->get_debug_shape_thickness() > 1) { - p_gizmo->add_vertices(raycast->get_debug_shape_vertices(), material, Mesh::PRIMITIVE_TRIANGLE_STRIP); - } - - p_gizmo->add_collision_segments(raycast->get_debug_line_vertices()); -} - -///// - -void SpringArm3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - SpringArm3D *spring_arm = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - - lines.push_back(Vector3()); - lines.push_back(Vector3(0, 0, 1.0) * spring_arm->get_length()); - - Ref material = get_material("shape_material", p_gizmo); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); -} - -SpringArm3DGizmoPlugin::SpringArm3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); -} - -bool SpringArm3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String SpringArm3DGizmoPlugin::get_gizmo_name() const { - return "SpringArm3D"; -} - -int SpringArm3DGizmoPlugin::get_priority() const { - return -1; -} - -///// - -VehicleWheel3DGizmoPlugin::VehicleWheel3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); -} - -bool VehicleWheel3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String VehicleWheel3DGizmoPlugin::get_gizmo_name() const { - return "VehicleWheel3D"; -} - -int VehicleWheel3DGizmoPlugin::get_priority() const { - return -1; -} - -void VehicleWheel3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - VehicleWheel3D *car_wheel = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector points; - - float r = car_wheel->get_radius(); - const int skip = 10; - for (int i = 0; i <= 360; i += skip) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + skip); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - points.push_back(Vector3(0, a.x, a.y)); - points.push_back(Vector3(0, b.x, b.y)); - - const int springsec = 4; - - for (int j = 0; j < springsec; j++) { - float t = car_wheel->get_suspension_rest_length() * 5; - points.push_back(Vector3(a.x, i / 360.0 * t / springsec + j * (t / springsec), a.y) * 0.2); - points.push_back(Vector3(b.x, (i + skip) / 360.0 * t / springsec + j * (t / springsec), b.y) * 0.2); - } - } - - //travel - points.push_back(Vector3(0, 0, 0)); - points.push_back(Vector3(0, car_wheel->get_suspension_rest_length(), 0)); - - //axis - points.push_back(Vector3(r * 0.2, car_wheel->get_suspension_rest_length(), 0)); - points.push_back(Vector3(-r * 0.2, car_wheel->get_suspension_rest_length(), 0)); - //axis - points.push_back(Vector3(r * 0.2, 0, 0)); - points.push_back(Vector3(-r * 0.2, 0, 0)); - - //forward line - points.push_back(Vector3(0, -r, 0)); - points.push_back(Vector3(0, -r, r * 2)); - points.push_back(Vector3(0, -r, r * 2)); - points.push_back(Vector3(r * 2 * 0.2, -r, r * 2 * 0.8)); - points.push_back(Vector3(0, -r, r * 2)); - points.push_back(Vector3(-r * 2 * 0.2, -r, r * 2 * 0.8)); - - Ref material = get_material("shape_material", p_gizmo); - - p_gizmo->add_lines(points, material); - p_gizmo->add_collision_segments(points); -} - -/////////// - -SoftBody3DGizmoPlugin::SoftBody3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); - create_handle_material("handles"); -} - -bool SoftBody3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String SoftBody3DGizmoPlugin::get_gizmo_name() const { - return "SoftBody3D"; -} - -int SoftBody3DGizmoPlugin::get_priority() const { - return -1; -} - -bool SoftBody3DGizmoPlugin::is_selectable_when_hidden() const { - return true; -} - -void SoftBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - if (!soft_body || soft_body->get_mesh().is_null()) { - return; - } - - // find mesh - - Vector lines; - - soft_body->get_mesh()->generate_debug_mesh_lines(lines); - - if (!lines.size()) { - return; - } - - Ref tm = soft_body->get_mesh()->generate_triangle_mesh(); - - Vector points; - for (int i = 0; i < soft_body->get_mesh()->get_surface_count(); i++) { - Array arrays = soft_body->get_mesh()->surface_get_arrays(i); - ERR_CONTINUE(arrays.is_empty()); - - const Vector &vertices = arrays[Mesh::ARRAY_VERTEX]; - points.append_array(vertices); - } - - Ref material = get_material("shape_material", p_gizmo); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_handles(points, get_material("handles")); - p_gizmo->add_collision_triangles(tm); -} - -String SoftBody3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - return "SoftBody3D pin point"; -} - -Variant SoftBody3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); - return Variant(soft_body->is_point_pinned(p_idx)); -} - -void SoftBody3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); - soft_body->pin_point_toggle(p_idx); -} - -bool SoftBody3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int idx) const { - SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); - return soft_body->is_point_pinned(idx); -} - -/////////// - -VisibleOnScreenNotifier3DGizmoPlugin::VisibleOnScreenNotifier3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/visibility_notifier", Color(0.8, 0.5, 0.7)); - create_material("visibility_notifier_material", gizmo_color); - gizmo_color.a = 0.1; - create_material("visibility_notifier_solid_material", gizmo_color); - create_handle_material("handles"); -} - -bool VisibleOnScreenNotifier3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String VisibleOnScreenNotifier3DGizmoPlugin::get_gizmo_name() const { - return "VisibleOnScreenNotifier3D"; -} - -int VisibleOnScreenNotifier3DGizmoPlugin::get_priority() const { - return -1; -} - -String VisibleOnScreenNotifier3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - switch (p_idx) { - case 0: - return "Size X"; - case 1: - return "Size Y"; - case 2: - return "Size Z"; - case 3: - return "Pos X"; - case 4: - return "Pos Y"; - case 5: - return "Pos Z"; - } - - return ""; -} - -Variant VisibleOnScreenNotifier3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - VisibleOnScreenNotifier3D *notifier = Object::cast_to(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) { - VisibleOnScreenNotifier3D *notifier = Object::cast_to(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; - - AABB aabb = notifier->get_aabb(); - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; - - Vector3 ofs = aabb.position + aabb.size * 0.5; - - Vector3 axis; - axis[p_idx] = 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]; - 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; - 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]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - 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; - notifier->set_aabb(aabb); - } -} - -void VisibleOnScreenNotifier3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - VisibleOnScreenNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); - - if (p_cancel) { - notifier->set_aabb(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Notifier AABB")); - ur->add_do_method(notifier, "set_aabb", notifier->get_aabb()); - ur->add_undo_method(notifier, "set_aabb", p_restore); - ur->commit_action(); -} - -void VisibleOnScreenNotifier3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - VisibleOnScreenNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - AABB aabb = notifier->get_aabb(); - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - Vector handles; - - for (int i = 0; i < 3; i++) { - Vector3 ax; - ax[i] = aabb.position[i] + aabb.size[i]; - ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5; - ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5; - handles.push_back(ax); - } - - Vector3 center = aabb.position + aabb.size * 0.5; - for (int i = 0; i < 3; i++) { - Vector3 ax; - ax[i] = 1.0; - handles.push_back(center + ax); - lines.push_back(center); - lines.push_back(center + ax); - } - - Ref material = get_material("visibility_notifier_material", p_gizmo); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); - - if (p_gizmo->is_selected()) { - Ref solid_material = get_material("visibility_notifier_solid_material", p_gizmo); - p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); - } - - p_gizmo->add_handles(handles, get_material("handles")); -} - -//// - -CPUParticles3DGizmoPlugin::CPUParticles3DGizmoPlugin() { - create_icon_material("particles_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoCPUParticles3D"), SNAME("EditorIcons"))); -} - -bool CPUParticles3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String CPUParticles3DGizmoPlugin::get_gizmo_name() const { - return "CPUParticles3D"; -} - -int CPUParticles3DGizmoPlugin::get_priority() const { - return -1; -} - -bool CPUParticles3DGizmoPlugin::is_selectable_when_hidden() const { - return true; -} - -void CPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Ref icon = get_material("particles_icon", p_gizmo); - p_gizmo->add_unscaled_billboard(icon, 0.05); -} - -//// - -GPUParticles3DGizmoPlugin::GPUParticles3DGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4)); - create_material("particles_material", gizmo_color); - gizmo_color.a = 0.1; - create_material("particles_solid_material", gizmo_color); - create_icon_material("particles_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoGPUParticles3D"), SNAME("EditorIcons"))); - create_handle_material("handles"); -} - -bool GPUParticles3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String GPUParticles3DGizmoPlugin::get_gizmo_name() const { - return "GPUParticles3D"; -} - -int GPUParticles3DGizmoPlugin::get_priority() const { - return -1; -} - -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) { - case 0: - return "Size X"; - case 1: - return "Size Y"; - case 2: - return "Size Z"; - case 3: - return "Pos X"; - case 4: - return "Pos Y"; - case 5: - return "Pos Z"; - } - - return ""; -} - -Variant GPUParticles3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - GPUParticles3D *particles = Object::cast_to(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) { - GPUParticles3D *particles = Object::cast_to(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; - - AABB aabb = particles->get_visibility_aabb(); - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; - - Vector3 ofs = aabb.position + aabb.size * 0.5; - - Vector3 axis; - axis[p_idx] = 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]; - 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; - 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]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - 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; - particles->set_visibility_aabb(aabb); - } -} - -void GPUParticles3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); - - if (p_cancel) { - particles->set_visibility_aabb(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Particles AABB")); - ur->add_do_method(particles, "set_visibility_aabb", particles->get_visibility_aabb()); - ur->add_undo_method(particles, "set_visibility_aabb", p_restore); - ur->commit_action(); -} - -void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - AABB aabb = particles->get_visibility_aabb(); - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - Vector handles; - - for (int i = 0; i < 3; i++) { - Vector3 ax; - ax[i] = aabb.position[i] + aabb.size[i]; - ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5; - ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5; - handles.push_back(ax); - } - - Vector3 center = aabb.position + aabb.size * 0.5; - for (int i = 0; i < 3; i++) { - Vector3 ax; - ax[i] = 1.0; - handles.push_back(center + ax); - lines.push_back(center); - lines.push_back(center + ax); - } - - Ref material = get_material("particles_material", p_gizmo); - Ref icon = get_material("particles_icon", p_gizmo); - - p_gizmo->add_lines(lines, material); - - if (p_gizmo->is_selected()) { - Ref solid_material = get_material("particles_solid_material", p_gizmo); - p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); - } - - p_gizmo->add_handles(handles, get_material("handles")); - p_gizmo->add_unscaled_billboard(icon, 0.05); -} - -//// - -//// - -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); - gizmo_color.a = 0.15; - create_material("shape_material_internal", gizmo_color); - - create_handle_material("handles"); -} - -bool GPUParticlesCollision3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return (Object::cast_to(p_spatial) != nullptr) || (Object::cast_to(p_spatial) != nullptr); -} - -String GPUParticlesCollision3DGizmoPlugin::get_gizmo_name() const { - return "GPUParticlesCollision3D"; -} - -int GPUParticlesCollision3DGizmoPlugin::get_priority() const { - return -1; -} - -String GPUParticlesCollision3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - const Node3D *cs = p_gizmo->get_spatial_node(); - - if (Object::cast_to(cs) || Object::cast_to(cs)) { - return "Radius"; - } - - if (Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs)) { - return "Extents"; - } - - return ""; -} - -Variant GPUParticlesCollision3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - const Node3D *cs = p_gizmo->get_spatial_node(); - - if (Object::cast_to(cs) || Object::cast_to(cs)) { - return p_gizmo->get_spatial_node()->call("get_radius"); - } - - if (Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs)) { - return Vector3(p_gizmo->get_spatial_node()->call("get_extents")); - } - - return Variant(); -} - -void GPUParticlesCollision3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - Node3D *sn = p_gizmo->get_spatial_node(); - - Transform3D gt = sn->get_global_transform(); - Transform3D gi = gt.affine_inverse(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; - - if (Object::cast_to(sn) || Object::cast_to(sn)) { - Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); - float d = ra.x; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - sn->call("set_radius", d); - } - - if (Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn)) { - Vector3 axis; - axis[p_idx] = 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]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - Vector3 he = sn->call("get_extents"); - he[p_idx] = d; - sn->call("set_extents", he); - } -} - -void GPUParticlesCollision3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - Node3D *sn = p_gizmo->get_spatial_node(); - - if (Object::cast_to(sn) || Object::cast_to(sn)) { - if (p_cancel) { - sn->call("set_radius", p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Radius")); - ur->add_do_method(sn, "set_radius", sn->call("get_radius")); - ur->add_undo_method(sn, "set_radius", p_restore); - ur->commit_action(); - } - - if (Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn)) { - if (p_cancel) { - sn->call("set_extents", p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Box Shape Extents")); - ur->add_do_method(sn, "set_extents", sn->call("get_extents")); - ur->add_undo_method(sn, "set_extents", p_restore); - ur->commit_action(); - } -} - -void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Node3D *cs = p_gizmo->get_spatial_node(); - - print_line("redraw request " + itos(cs != nullptr)); - p_gizmo->clear(); - - const Ref material = - get_material("shape_material", p_gizmo); - const Ref material_internal = - get_material("shape_material_internal", p_gizmo); - - Ref handles_material = get_material("handles"); - - if (Object::cast_to(cs) || Object::cast_to(cs)) { - float r = cs->call("get_radius"); - - Vector points; - - for (int i = 0; i <= 360; i++) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - points.push_back(Vector3(a.x, 0, a.y)); - points.push_back(Vector3(b.x, 0, b.y)); - points.push_back(Vector3(0, a.x, a.y)); - points.push_back(Vector3(0, b.x, b.y)); - points.push_back(Vector3(a.x, a.y, 0)); - points.push_back(Vector3(b.x, b.y, 0)); - } - - Vector collision_segments; - - for (int i = 0; i < 64; i++) { - float ra = i * (Math_TAU / 64.0); - float rb = (i + 1) * (Math_TAU / 64.0); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - collision_segments.push_back(Vector3(a.x, 0, a.y)); - collision_segments.push_back(Vector3(b.x, 0, b.y)); - collision_segments.push_back(Vector3(0, a.x, a.y)); - collision_segments.push_back(Vector3(0, b.x, b.y)); - collision_segments.push_back(Vector3(a.x, a.y, 0)); - collision_segments.push_back(Vector3(b.x, b.y, 0)); - } - - p_gizmo->add_lines(points, material); - p_gizmo->add_collision_segments(collision_segments); - Vector handles; - handles.push_back(Vector3(r, 0, 0)); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs)) { - Vector lines; - AABB aabb; - aabb.position = -cs->call("get_extents").operator Vector3(); - aabb.size = aabb.position * -2; - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - Vector handles; - - for (int i = 0; i < 3; i++) { - Vector3 ax; - ax[i] = cs->call("get_extents").operator Vector3()[i]; - handles.push_back(ax); - } - - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); - p_gizmo->add_handles(handles, handles_material); - - GPUParticlesCollisionSDF *col_sdf = Object::cast_to(cs); - if (col_sdf) { - static const int subdivs[GPUParticlesCollisionSDF::RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 }; - int subdiv = subdivs[col_sdf->get_resolution()]; - float cell_size = aabb.get_longest_axis_size() / subdiv; - - lines.clear(); - - for (int i = 1; i < subdiv; i++) { - for (int j = 0; j < 3; j++) { - if (cell_size * i > aabb.size[j]) { - continue; - } - - Vector2 dir; - dir[j] = 1.0; - Vector2 ta, tb; - int j_n1 = (j + 1) % 3; - int j_n2 = (j + 2) % 3; - ta[j_n1] = 1.0; - tb[j_n2] = 1.0; - - for (int k = 0; k < 4; k++) { - Vector3 from = aabb.position, to = aabb.position; - from[j] += cell_size * i; - to[j] += cell_size * i; - - if (k & 1) { - to[j_n1] += aabb.size[j_n1]; - } else { - to[j_n2] += aabb.size[j_n2]; - } - - if (k & 2) { - from[j_n1] += aabb.size[j_n1]; - from[j_n2] += aabb.size[j_n2]; - } - - lines.push_back(from); - lines.push_back(to); - } - } - } - - p_gizmo->add_lines(lines, material_internal); - } - } -} - -///// - -//// - -ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5)); - - create_material("reflection_probe_material", gizmo_color); - - gizmo_color.a = 0.5; - create_material("reflection_internal_material", gizmo_color); - - gizmo_color.a = 0.1; - create_material("reflection_probe_solid_material", gizmo_color); - - create_icon_material("reflection_probe_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoReflectionProbe"), SNAME("EditorIcons"))); - create_handle_material("handles"); -} - -bool ReflectionProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String ReflectionProbeGizmoPlugin::get_gizmo_name() const { - return "ReflectionProbe"; -} - -int ReflectionProbeGizmoPlugin::get_priority() const { - return -1; -} - -String ReflectionProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - switch (p_idx) { - case 0: - return "Extents X"; - case 1: - return "Extents Y"; - case 2: - return "Extents Z"; - case 3: - return "Origin X"; - case 4: - return "Origin Y"; - case 5: - return "Origin Z"; - } - - return ""; -} - -Variant ReflectionProbeGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - ReflectionProbe *probe = Object::cast_to(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) { - ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); - Transform3D gt = probe->get_global_transform(); - - Transform3D gi = gt.affine_inverse(); - - if (p_idx < 3) { - Vector3 extents = probe->get_extents(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; - - Vector3 axis; - axis[p_idx] = 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]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - extents[p_idx] = d; - probe->set_extents(extents); - } else { - p_idx -= 3; - - Vector3 origin = probe->get_origin_offset(); - origin[p_idx] = 0; - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; - - Vector3 axis; - axis[p_idx] = 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; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - origin[p_idx] = d; - probe->set_origin_offset(origin); - } -} - -void ReflectionProbeGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); - - AABB restore = p_restore; - - if (p_cancel) { - probe->set_extents(restore.position); - probe->set_origin_offset(restore.size); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Probe Extents")); - ur->add_do_method(probe, "set_extents", probe->get_extents()); - ur->add_do_method(probe, "set_origin_offset", probe->get_origin_offset()); - ur->add_undo_method(probe, "set_extents", restore.position); - ur->add_undo_method(probe, "set_origin_offset", restore.size); - ur->commit_action(); -} - -void ReflectionProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - Vector internal_lines; - Vector3 extents = probe->get_extents(); - - AABB aabb; - aabb.position = -extents; - aabb.size = extents * 2; - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - for (int i = 0; i < 8; i++) { - Vector3 ep = aabb.get_endpoint(i); - internal_lines.push_back(probe->get_origin_offset()); - internal_lines.push_back(ep); - } - - Vector handles; - - for (int i = 0; i < 3; i++) { - Vector3 ax; - ax[i] = aabb.position[i] + aabb.size[i]; - handles.push_back(ax); - } - - for (int i = 0; i < 3; i++) { - Vector3 orig_handle = probe->get_origin_offset(); - orig_handle[i] -= 0.25; - lines.push_back(orig_handle); - handles.push_back(orig_handle); - - orig_handle[i] += 0.5; - lines.push_back(orig_handle); - } - - Ref material = get_material("reflection_probe_material", p_gizmo); - Ref material_internal = get_material("reflection_internal_material", p_gizmo); - Ref icon = get_material("reflection_probe_icon", p_gizmo); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_lines(internal_lines, material_internal); - - if (p_gizmo->is_selected()) { - Ref solid_material = get_material("reflection_probe_solid_material", p_gizmo); - p_gizmo->add_solid_box(solid_material, probe->get_extents() * 2.0); - } - - p_gizmo->add_unscaled_billboard(icon, 0.05); - p_gizmo->add_handles(handles, get_material("handles")); -} - -/////////////////////////////// - -//// - -DecalGizmoPlugin::DecalGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/decal", Color(0.6, 0.5, 1.0)); - - create_material("decal_material", gizmo_color); - - create_handle_material("handles"); -} - -bool DecalGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String DecalGizmoPlugin::get_gizmo_name() const { - return "Decal"; -} - -int DecalGizmoPlugin::get_priority() const { - return -1; -} - -String DecalGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - switch (p_idx) { - case 0: - return "Extents X"; - case 1: - return "Extents Y"; - case 2: - return "Extents Z"; - } - - return ""; -} - -Variant DecalGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - Decal *decal = Object::cast_to(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) { - Decal *decal = Object::cast_to(p_gizmo->get_spatial_node()); - Transform3D gt = decal->get_global_transform(); - - Transform3D gi = gt.affine_inverse(); - - Vector3 extents = decal->get_extents(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; - - Vector3 axis; - axis[p_idx] = 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]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - extents[p_idx] = d; - decal->set_extents(extents); -} - -void DecalGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - Decal *decal = Object::cast_to(p_gizmo->get_spatial_node()); - - Vector3 restore = p_restore; - - if (p_cancel) { - decal->set_extents(restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Decal Extents")); - ur->add_do_method(decal, "set_extents", decal->get_extents()); - ur->add_undo_method(decal, "set_extents", restore); - ur->commit_action(); -} - -void DecalGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Decal *decal = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector lines; - Vector3 extents = decal->get_extents(); - - AABB aabb; - aabb.position = -extents; - aabb.size = extents * 2; - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - if (a.y == b.y) { - lines.push_back(a); - lines.push_back(b); - } else { - Vector3 ah = a.lerp(b, 0.2); - lines.push_back(a); - lines.push_back(ah); - Vector3 bh = b.lerp(a, 0.2); - lines.push_back(b); - lines.push_back(bh); - } - } - - lines.push_back(Vector3(0, extents.y, 0)); - lines.push_back(Vector3(0, extents.y * 1.2, 0)); - - Vector handles; - - for (int i = 0; i < 3; i++) { - Vector3 ax; - ax[i] = aabb.position[i] + aabb.size[i]; - handles.push_back(ax); - } - - Ref material = get_material("decal_material", p_gizmo); - - p_gizmo->add_lines(lines, material); - - p_gizmo->add_handles(handles, get_material("handles")); -} - -/////////////////////////////// -VoxelGIGizmoPlugin::VoxelGIGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/voxel_gi", Color(0.5, 1, 0.6)); - - create_material("voxel_gi_material", gizmo_color); - - // This gizmo draws a lot of lines. Use a low opacity to make it not too intrusive. - gizmo_color.a = 0.1; - create_material("voxel_gi_internal_material", gizmo_color); - - gizmo_color.a = 0.05; - create_material("voxel_gi_solid_material", gizmo_color); - - create_icon_material("voxel_gi_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoVoxelGI"), SNAME("EditorIcons"))); - create_handle_material("handles"); -} - -bool VoxelGIGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String VoxelGIGizmoPlugin::get_gizmo_name() const { - return "VoxelGI"; -} - -int VoxelGIGizmoPlugin::get_priority() const { - return -1; -} - -String VoxelGIGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - switch (p_idx) { - case 0: - return "Extents X"; - case 1: - return "Extents Y"; - case 2: - return "Extents Z"; - } - - return ""; -} - -Variant VoxelGIGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - VoxelGI *probe = Object::cast_to(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) { - VoxelGI *probe = Object::cast_to(p_gizmo->get_spatial_node()); - - Transform3D gt = probe->get_global_transform(); - Transform3D gi = gt.affine_inverse(); - - Vector3 extents = probe->get_extents(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; - - Vector3 axis; - axis[p_idx] = 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]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - extents[p_idx] = d; - probe->set_extents(extents); -} - -void VoxelGIGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - VoxelGI *probe = Object::cast_to(p_gizmo->get_spatial_node()); - - Vector3 restore = p_restore; - - if (p_cancel) { - probe->set_extents(restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Probe Extents")); - ur->add_do_method(probe, "set_extents", probe->get_extents()); - ur->add_undo_method(probe, "set_extents", restore); - ur->commit_action(); -} - -void VoxelGIGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - VoxelGI *probe = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref material = get_material("voxel_gi_material", p_gizmo); - Ref icon = get_material("voxel_gi_icon", p_gizmo); - Ref material_internal = get_material("voxel_gi_internal_material", p_gizmo); - - p_gizmo->clear(); - - Vector lines; - Vector3 extents = probe->get_extents(); - - static const int subdivs[VoxelGI::SUBDIV_MAX] = { 64, 128, 256, 512 }; - - AABB aabb = AABB(-extents, extents * 2); - int subdiv = subdivs[probe->get_subdiv()]; - float cell_size = aabb.get_longest_axis_size() / subdiv; - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - p_gizmo->add_lines(lines, material); - - lines.clear(); - - for (int i = 1; i < subdiv; i++) { - for (int j = 0; j < 3; j++) { - if (cell_size * i > aabb.size[j]) { - continue; - } - - Vector2 dir; - dir[j] = 1.0; - Vector2 ta, tb; - int j_n1 = (j + 1) % 3; - int j_n2 = (j + 2) % 3; - ta[j_n1] = 1.0; - tb[j_n2] = 1.0; - - for (int k = 0; k < 4; k++) { - Vector3 from = aabb.position, to = aabb.position; - from[j] += cell_size * i; - to[j] += cell_size * i; - - if (k & 1) { - to[j_n1] += aabb.size[j_n1]; - } else { - to[j_n2] += aabb.size[j_n2]; - } - - if (k & 2) { - from[j_n1] += aabb.size[j_n1]; - from[j_n2] += aabb.size[j_n2]; - } - - lines.push_back(from); - lines.push_back(to); - } - } - } - - p_gizmo->add_lines(lines, material_internal); - - Vector handles; - - for (int i = 0; i < 3; i++) { - Vector3 ax; - ax[i] = aabb.position[i] + aabb.size[i]; - handles.push_back(ax); - } - - if (p_gizmo->is_selected()) { - Ref solid_material = get_material("voxel_gi_solid_material", p_gizmo); - p_gizmo->add_solid_box(solid_material, aabb.get_size()); - } - - p_gizmo->add_unscaled_billboard(icon, 0.05); - p_gizmo->add_handles(handles, get_material("handles")); -} - -//// - -LightmapGIGizmoPlugin::LightmapGIGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/lightmap_lines", Color(0.5, 0.6, 1)); - - gizmo_color.a = 0.1; - create_material("lightmap_lines", gizmo_color); - - Ref mat = memnew(StandardMaterial3D); - mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); - mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); - mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, false); - - add_material("lightmap_probe_material", mat); - - 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 { - return ""; -} - -Variant LightmapGIGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - return Variant(); -} - -void LightmapGIGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { -} - -void LightmapGIGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { -} - -bool LightmapGIGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String LightmapGIGizmoPlugin::get_gizmo_name() const { - return "LightmapGI"; -} - -int LightmapGIGizmoPlugin::get_priority() const { - return -1; -} - -void LightmapGIGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Ref icon = get_material("baked_indirect_light_icon", p_gizmo); - LightmapGI *baker = Object::cast_to(p_gizmo->get_spatial_node()); - Ref data = baker->get_light_data(); - - p_gizmo->add_unscaled_billboard(icon, 0.05); - - if (data.is_null()) { - return; - } - - Ref material_lines = get_material("lightmap_lines", p_gizmo); - Ref material_probes = get_material("lightmap_probe_material", p_gizmo); - - p_gizmo->clear(); - - Vector lines; - Set lines_found; - - Vector points = data->get_capture_points(); - if (points.size() == 0) { - return; - } - Vector sh = data->get_capture_sh(); - if (sh.size() != points.size() * 9) { - return; - } - - Vector tetrahedrons = data->get_capture_tetrahedra(); - - for (int i = 0; i < tetrahedrons.size(); i += 4) { - for (int j = 0; j < 4; j++) { - for (int k = j + 1; k < 4; k++) { - Vector2i pair; - pair.x = tetrahedrons[i + j]; - pair.y = tetrahedrons[i + k]; - - if (pair.y < pair.x) { - SWAP(pair.x, pair.y); - } - if (lines_found.has(pair)) { - continue; - } - lines_found.insert(pair); - lines.push_back(points[pair.x]); - lines.push_back(points[pair.y]); - } - } - } - - p_gizmo->add_lines(lines, material_lines); - - int stack_count = 8; - int sector_count = 16; - - float sector_step = (Math_PI * 2.0) / sector_count; - float stack_step = Math_PI / stack_count; - - Vector vertices; - Vector colors; - Vector indices; - float radius = 0.3; - - for (int p = 0; p < points.size(); p++) { - int vertex_base = vertices.size(); - Vector3 sh_col[9]; - for (int i = 0; i < 9; i++) { - sh_col[i].x = sh[p * 9 + i].r; - sh_col[i].y = sh[p * 9 + i].g; - sh_col[i].z = sh[p * 9 + i].b; - } - - for (int i = 0; i <= stack_count; ++i) { - float stack_angle = Math_PI / 2 - i * stack_step; // starting from pi/2 to -pi/2 - float xy = radius * Math::cos(stack_angle); // r * cos(u) - float z = radius * Math::sin(stack_angle); // r * sin(u) - - // add (sector_count+1) vertices per stack - // the first and last vertices have same position and normal, but different tex coords - for (int j = 0; j <= sector_count; ++j) { - float sector_angle = j * sector_step; // starting from 0 to 2pi - - // vertex position (x, y, z) - float x = xy * Math::cos(sector_angle); // r * cos(u) * cos(v) - float y = xy * Math::sin(sector_angle); // r * cos(u) * sin(v) - - Vector3 n = Vector3(x, z, y); - vertices.push_back(points[p] + n); - n.normalize(); - - const float c1 = 0.429043; - const float c2 = 0.511664; - const float c3 = 0.743125; - const float c4 = 0.886227; - const float c5 = 0.247708; - Vector3 light = (c1 * sh_col[8] * (n.x * n.x - n.y * n.y) + - c3 * sh_col[6] * n.z * n.z + - c4 * sh_col[0] - - c5 * sh_col[6] + - 2.0 * c1 * sh_col[4] * n.x * n.y + - 2.0 * c1 * sh_col[7] * n.x * n.z + - 2.0 * c1 * sh_col[5] * n.y * n.z + - 2.0 * c2 * sh_col[3] * n.x + - 2.0 * c2 * sh_col[1] * n.y + - 2.0 * c2 * sh_col[2] * n.z); - - colors.push_back(Color(light.x, light.y, light.z, 1)); - } - } - - for (int i = 0; i < stack_count; ++i) { - int k1 = i * (sector_count + 1); // beginning of current stack - int k2 = k1 + sector_count + 1; // beginning of next stack - - for (int j = 0; j < sector_count; ++j, ++k1, ++k2) { - // 2 triangles per sector excluding first and last stacks - // k1 => k2 => k1+1 - if (i != 0) { - indices.push_back(vertex_base + k1); - indices.push_back(vertex_base + k2); - indices.push_back(vertex_base + k1 + 1); - } - - // k1+1 => k2 => k2+1 - if (i != (stack_count - 1)) { - indices.push_back(vertex_base + k1 + 1); - indices.push_back(vertex_base + k2); - indices.push_back(vertex_base + k2 + 1); - } - } - } - } - - Array array; - array.resize(RS::ARRAY_MAX); - array[RS::ARRAY_VERTEX] = vertices; - array[RS::ARRAY_INDEX] = indices; - array[RS::ARRAY_COLOR] = colors; - - Ref mesh; - mesh.instantiate(); - mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array, Array(), Dictionary(), 0); //no compression - mesh->surface_set_material(0, material_probes); - - p_gizmo->add_mesh(mesh); -} - -///////// - -LightmapProbeGizmoPlugin::LightmapProbeGizmoPlugin() { - Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/lightprobe_lines", Color(0.5, 0.6, 1)); - - gizmo_color.a = 0.3; - create_material("lightprobe_lines", gizmo_color); -} - -String LightmapProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - return ""; -} - -Variant LightmapProbeGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - return Variant(); -} - -void LightmapProbeGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { -} - -void LightmapProbeGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { -} - -bool LightmapProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String LightmapProbeGizmoPlugin::get_gizmo_name() const { - return "LightmapProbe"; -} - -int LightmapProbeGizmoPlugin::get_priority() const { - return -1; -} - -void LightmapProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Ref material_lines = get_material("lightprobe_lines", p_gizmo); - - p_gizmo->clear(); - - Vector lines; - - int stack_count = 8; - int sector_count = 16; - - float sector_step = (Math_PI * 2.0) / sector_count; - float stack_step = Math_PI / stack_count; - - Vector vertices; - float radius = 0.2; - - for (int i = 0; i <= stack_count; ++i) { - float stack_angle = Math_PI / 2 - i * stack_step; // starting from pi/2 to -pi/2 - float xy = radius * Math::cos(stack_angle); // r * cos(u) - float z = radius * Math::sin(stack_angle); // r * sin(u) - - // add (sector_count+1) vertices per stack - // the first and last vertices have same position and normal, but different tex coords - for (int j = 0; j <= sector_count; ++j) { - float sector_angle = j * sector_step; // starting from 0 to 2pi - - // vertex position (x, y, z) - float x = xy * Math::cos(sector_angle); // r * cos(u) * cos(v) - float y = xy * Math::sin(sector_angle); // r * cos(u) * sin(v) - - Vector3 n = Vector3(x, z, y); - vertices.push_back(n); - } - } - - for (int i = 0; i < stack_count; ++i) { - int k1 = i * (sector_count + 1); // beginning of current stack - int k2 = k1 + sector_count + 1; // beginning of next stack - - for (int j = 0; j < sector_count; ++j, ++k1, ++k2) { - // 2 triangles per sector excluding first and last stacks - // k1 => k2 => k1+1 - if (i != 0) { - lines.push_back(vertices[k1]); - lines.push_back(vertices[k2]); - lines.push_back(vertices[k1]); - lines.push_back(vertices[k1 + 1]); - } - - if (i != (stack_count - 1)) { - lines.push_back(vertices[k1 + 1]); - lines.push_back(vertices[k2]); - lines.push_back(vertices[k2]); - lines.push_back(vertices[k2 + 1]); - } - } - } - - p_gizmo->add_lines(lines, material_lines); -} - -//// - -CollisionObject3DGizmoPlugin::CollisionObject3DGizmoPlugin() { - const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); - const float gizmo_value = gizmo_color.get_v(); - const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); - create_material("shape_material_disabled", gizmo_color_disabled); -} - -bool CollisionObject3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String CollisionObject3DGizmoPlugin::get_gizmo_name() const { - return "CollisionObject3D"; -} - -int CollisionObject3DGizmoPlugin::get_priority() const { - return -2; -} - -void CollisionObject3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - CollisionObject3D *co = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - List owners; - co->get_shape_owners(&owners); - for (List::Element *E = owners.front(); E; E = E->next()) { - uint32_t owner_id = E->get(); - Transform3D xform = co->shape_owner_get_transform(owner_id); - Object *owner = co->shape_owner_get_owner(owner_id); - // Exclude CollisionShape3D and CollisionPolygon3D as they have their gizmo. - if (!Object::cast_to(owner) && !Object::cast_to(owner)) { - Ref material = get_material(!co->is_shape_owner_disabled(owner_id) ? "shape_material" : "shape_material_disabled", p_gizmo); - for (int shape_id = 0; shape_id < co->shape_owner_get_shape_count(owner_id); shape_id++) { - Ref s = co->shape_owner_get_shape(owner_id, shape_id); - if (s.is_null()) { - continue; - } - SurfaceTool st; - st.append_from(s->get_debug_mesh(), 0, xform); - - p_gizmo->add_mesh(st.commit(), false, Ref(), material); - p_gizmo->add_collision_segments(s->get_debug_mesh_lines()); - } - } - } -} - -//// - -CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() { - const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); - const float gizmo_value = gizmo_color.get_v(); - const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); - create_material("shape_material_disabled", gizmo_color_disabled); - create_handle_material("handles"); -} - -bool CollisionShape3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String CollisionShape3DGizmoPlugin::get_gizmo_name() const { - return "CollisionShape3D"; -} - -int CollisionShape3DGizmoPlugin::get_priority() const { - return -1; -} - -String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_idx) const { - const CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref s = cs->get_shape(); - if (s.is_null()) { - return ""; - } - - if (Object::cast_to(*s)) { - return "Radius"; - } - - if (Object::cast_to(*s)) { - return "Size"; - } - - if (Object::cast_to(*s)) { - return p_idx == 0 ? "Radius" : "Height"; - } - - if (Object::cast_to(*s)) { - return p_idx == 0 ? "Radius" : "Height"; - } - - if (Object::cast_to(*s)) { - return "Length"; - } - - return ""; -} - -Variant CollisionShape3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int p_idx) const { - CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref s = cs->get_shape(); - if (s.is_null()) { - return Variant(); - } - - if (Object::cast_to(*s)) { - Ref ss = s; - return ss->get_radius(); - } - - if (Object::cast_to(*s)) { - Ref bs = s; - return bs->get_size(); - } - - if (Object::cast_to(*s)) { - Ref cs2 = s; - return p_idx == 0 ? cs2->get_radius() : cs2->get_height(); - } - - if (Object::cast_to(*s)) { - Ref cs2 = s; - return p_idx == 0 ? cs2->get_radius() : cs2->get_height(); - } - - if (Object::cast_to(*s)) { - Ref cs2 = s; - return cs2->get_length(); - } - - return Variant(); -} - -void CollisionShape3DGizmoPlugin::set_handle(EditorNode3DGizmo *p_gizmo, int p_idx, Camera3D *p_camera, const Point2 &p_point) { - CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref s = cs->get_shape(); - if (s.is_null()) { - return; - } - - Transform3D gt = cs->get_global_transform(); - Transform3D gi = gt.affine_inverse(); - - Vector3 ray_from = p_camera->project_ray_origin(p_point); - Vector3 ray_dir = p_camera->project_ray_normal(p_point); - - Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; - - if (Object::cast_to(*s)) { - Ref ss = s; - Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); - float d = ra.x; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - ss->set_radius(d); - } - - if (Object::cast_to(*s)) { - Ref rs = s; - Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb); - float d = ra.z; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - rs->set_length(d); - } - - if (Object::cast_to(*s)) { - Vector3 axis; - axis[p_idx] = 1.0; - Ref 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]; - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - Vector3 he = bs->get_size(); - he[p_idx] = d * 2; - bs->set_size(he); - } - - if (Object::cast_to(*s)) { - Vector3 axis; - axis[p_idx == 0 ? 0 : 2] = 1.0; - Ref 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) { - d -= cs2->get_radius(); - } - - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - if (p_idx == 0) { - cs2->set_radius(d); - } else if (p_idx == 1) { - cs2->set_height(d * 2.0); - } - } - - if (Object::cast_to(*s)) { - Vector3 axis; - axis[p_idx == 0 ? 0 : 1] = 1.0; - Ref 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 (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - - if (p_idx == 0) { - cs2->set_radius(d); - } else if (p_idx == 1) { - cs2->set_height(d * 2.0); - } - } -} - -void CollisionShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) { - CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref s = cs->get_shape(); - if (s.is_null()) { - return; - } - - if (Object::cast_to(*s)) { - Ref ss = s; - if (p_cancel) { - ss->set_radius(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Sphere Shape Radius")); - ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); - ur->add_undo_method(ss.ptr(), "set_radius", p_restore); - ur->commit_action(); - } - - if (Object::cast_to(*s)) { - Ref ss = s; - if (p_cancel) { - ss->set_size(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Box Shape Size")); - ur->add_do_method(ss.ptr(), "set_size", ss->get_size()); - ur->add_undo_method(ss.ptr(), "set_size", p_restore); - ur->commit_action(); - } - - if (Object::cast_to(*s)) { - Ref ss = s; - if (p_cancel) { - if (p_idx == 0) { - ss->set_radius(p_restore); - } else { - ss->set_height(p_restore); - } - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - if (p_idx == 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); - } else { - ur->create_action(TTR("Change Capsule Shape Height")); - ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); - ur->add_undo_method(ss.ptr(), "set_height", p_restore); - } - - ur->commit_action(); - } - - if (Object::cast_to(*s)) { - Ref ss = s; - if (p_cancel) { - if (p_idx == 0) { - ss->set_radius(p_restore); - } else { - ss->set_height(p_restore); - } - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - if (p_idx == 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); - } else { - ur->create_action( - /// - - //////// - TTR("Change Cylinder Shape Height")); - ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); - ur->add_undo_method(ss.ptr(), "set_height", p_restore); - } - - ur->commit_action(); - } - - if (Object::cast_to(*s)) { - Ref ss = s; - if (p_cancel) { - ss->set_length(p_restore); - return; - } - - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change Ray Shape Length")); - ur->add_do_method(ss.ptr(), "set_length", ss->get_length()); - ur->add_undo_method(ss.ptr(), "set_length", p_restore); - ur->commit_action(); - } -} - -void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Ref s = cs->get_shape(); - if (s.is_null()) { - return; - } - - const Ref material = - get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); - Ref handles_material = get_material("handles"); - - if (Object::cast_to(*s)) { - Ref sp = s; - float r = sp->get_radius(); - - Vector points; - - for (int i = 0; i <= 360; i++) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - points.push_back(Vector3(a.x, 0, a.y)); - points.push_back(Vector3(b.x, 0, b.y)); - points.push_back(Vector3(0, a.x, a.y)); - points.push_back(Vector3(0, b.x, b.y)); - points.push_back(Vector3(a.x, a.y, 0)); - points.push_back(Vector3(b.x, b.y, 0)); - } - - Vector collision_segments; - - for (int i = 0; i < 64; i++) { - float ra = i * (Math_TAU / 64.0); - float rb = (i + 1) * (Math_TAU / 64.0); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; - - collision_segments.push_back(Vector3(a.x, 0, a.y)); - collision_segments.push_back(Vector3(b.x, 0, b.y)); - collision_segments.push_back(Vector3(0, a.x, a.y)); - collision_segments.push_back(Vector3(0, b.x, b.y)); - collision_segments.push_back(Vector3(a.x, a.y, 0)); - collision_segments.push_back(Vector3(b.x, b.y, 0)); - } - - p_gizmo->add_lines(points, material); - p_gizmo->add_collision_segments(collision_segments); - Vector handles; - handles.push_back(Vector3(r, 0, 0)); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(*s)) { - Ref bs = s; - Vector lines; - AABB aabb; - aabb.position = -bs->get_size() / 2; - aabb.size = bs->get_size(); - - for (int i = 0; i < 12; i++) { - Vector3 a, b; - aabb.get_edge(i, a, b); - lines.push_back(a); - lines.push_back(b); - } - - Vector handles; - - for (int i = 0; i < 3; i++) { - Vector3 ax; - ax[i] = bs->get_size()[i] / 2; - handles.push_back(ax); - } - - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(*s)) { - Ref cs2 = s; - float radius = cs2->get_radius(); - float height = cs2->get_height(); - - Vector points; - - Vector3 d(0, height * 0.5, 0); - for (int i = 0; i < 360; i++) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - - points.push_back(Vector3(a.x, 0, a.y) + d); - points.push_back(Vector3(b.x, 0, b.y) + d); - - points.push_back(Vector3(a.x, 0, a.y) - d); - points.push_back(Vector3(b.x, 0, b.y) - d); - - if (i % 90 == 0) { - points.push_back(Vector3(a.x, 0, a.y) + d); - points.push_back(Vector3(a.x, 0, a.y) - d); - } - - Vector3 dud = i < 180 ? d : -d; - - points.push_back(Vector3(0, a.x, a.y) + dud); - points.push_back(Vector3(0, b.x, b.y) + dud); - points.push_back(Vector3(a.y, a.x, 0) + dud); - points.push_back(Vector3(b.y, b.x, 0) + dud); - } - - p_gizmo->add_lines(points, material); - - Vector collision_segments; - - for (int i = 0; i < 64; i++) { - float ra = i * (Math_TAU / 64.0); - float rb = (i + 1) * (Math_TAU / 64.0); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - - collision_segments.push_back(Vector3(a.x, 0, a.y) + d); - collision_segments.push_back(Vector3(b.x, 0, b.y) + d); - - collision_segments.push_back(Vector3(a.x, 0, a.y) - d); - collision_segments.push_back(Vector3(b.x, 0, b.y) - d); - - if (i % 16 == 0) { - collision_segments.push_back(Vector3(a.x, 0, a.y) + d); - collision_segments.push_back(Vector3(a.x, 0, a.y) - d); - } - - Vector3 dud = i < 32 ? d : -d; - - collision_segments.push_back(Vector3(0, a.x, a.y) + dud); - collision_segments.push_back(Vector3(0, b.x, b.y) + dud); - collision_segments.push_back(Vector3(a.y, a.x, 0) + dud); - collision_segments.push_back(Vector3(b.y, b.x, 0) + dud); - } - - p_gizmo->add_collision_segments(collision_segments); - - Vector handles; - handles.push_back(Vector3(cs2->get_radius(), 0, 0)); - handles.push_back(Vector3(0, cs2->get_height() * 0.5 + cs2->get_radius(), 0)); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(*s)) { - Ref cs2 = s; - float radius = cs2->get_radius(); - float height = cs2->get_height(); - - Vector points; - - Vector3 d(0, height * 0.5, 0); - for (int i = 0; i < 360; i++) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - - points.push_back(Vector3(a.x, 0, a.y) + d); - points.push_back(Vector3(b.x, 0, b.y) + d); - - points.push_back(Vector3(a.x, 0, a.y) - d); - points.push_back(Vector3(b.x, 0, b.y) - d); - - if (i % 90 == 0) { - points.push_back(Vector3(a.x, 0, a.y) + d); - points.push_back(Vector3(a.x, 0, a.y) - d); - } - } - - p_gizmo->add_lines(points, material); - - Vector collision_segments; - - for (int i = 0; i < 64; i++) { - float ra = i * (Math_TAU / 64.0); - float rb = (i + 1) * (Math_TAU / 64.0); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; - - collision_segments.push_back(Vector3(a.x, 0, a.y) + d); - collision_segments.push_back(Vector3(b.x, 0, b.y) + d); - - collision_segments.push_back(Vector3(a.x, 0, a.y) - d); - collision_segments.push_back(Vector3(b.x, 0, b.y) - d); - - if (i % 16 == 0) { - collision_segments.push_back(Vector3(a.x, 0, a.y) + d); - collision_segments.push_back(Vector3(a.x, 0, a.y) - d); - } - } - - p_gizmo->add_collision_segments(collision_segments); - - Vector handles; - handles.push_back(Vector3(cs2->get_radius(), 0, 0)); - handles.push_back(Vector3(0, cs2->get_height() * 0.5, 0)); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(*s)) { - Ref ps = s; - Plane p = ps->get_plane(); - Vector points; - - Vector3 n1 = p.get_any_perpendicular_normal(); - Vector3 n2 = p.normal.cross(n1).normalized(); - - Vector3 pface[4] = { - p.normal * p.d + n1 * 10.0 + n2 * 10.0, - p.normal * p.d + n1 * 10.0 + n2 * -10.0, - p.normal * p.d + n1 * -10.0 + n2 * -10.0, - p.normal * p.d + n1 * -10.0 + n2 * 10.0, - }; - - points.push_back(pface[0]); - points.push_back(pface[1]); - points.push_back(pface[1]); - points.push_back(pface[2]); - points.push_back(pface[2]); - points.push_back(pface[3]); - points.push_back(pface[3]); - points.push_back(pface[0]); - points.push_back(p.normal * p.d); - points.push_back(p.normal * p.d + p.normal * 3); - - p_gizmo->add_lines(points, material); - p_gizmo->add_collision_segments(points); - } - - if (Object::cast_to(*s)) { - Vector points = Object::cast_to(*s)->get_points(); - - if (points.size() > 3) { - Vector varr = Variant(points); - Geometry3D::MeshData md; - Error err = ConvexHullComputer::convex_hull(varr, md); - if (err == OK) { - Vector points2; - points2.resize(md.edges.size() * 2); - for (int i = 0; i < md.edges.size(); i++) { - points2.write[i * 2 + 0] = md.vertices[md.edges[i].a]; - points2.write[i * 2 + 1] = md.vertices[md.edges[i].b]; - } - - p_gizmo->add_lines(points2, material); - p_gizmo->add_collision_segments(points2); - } - } - } - - if (Object::cast_to(*s)) { - Ref cs2 = s; - Ref mesh = cs2->get_debug_mesh(); - p_gizmo->add_mesh(mesh, false, Ref(), material); - p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines()); - } - - if (Object::cast_to(*s)) { - Ref rs = s; - - Vector points; - points.push_back(Vector3()); - points.push_back(Vector3(0, 0, rs->get_length())); - p_gizmo->add_lines(points, material); - p_gizmo->add_collision_segments(points); - Vector handles; - handles.push_back(Vector3(0, 0, rs->get_length())); - p_gizmo->add_handles(handles, handles_material); - } - - if (Object::cast_to(*s)) { - Ref hms = s; - - Ref mesh = hms->get_debug_mesh(); - p_gizmo->add_mesh(mesh, false, Ref(), material); - } -} - -///// - -CollisionPolygon3DGizmoPlugin::CollisionPolygon3DGizmoPlugin() { - const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); - create_material("shape_material", gizmo_color); - const float gizmo_value = gizmo_color.get_v(); - const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); - create_material("shape_material_disabled", gizmo_color_disabled); -} - -bool CollisionPolygon3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String CollisionPolygon3DGizmoPlugin::get_gizmo_name() const { - return "CollisionPolygon3D"; -} - -int CollisionPolygon3DGizmoPlugin::get_priority() const { - return -1; -} - -void CollisionPolygon3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - CollisionPolygon3D *polygon = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Vector points = polygon->get_polygon(); - float depth = polygon->get_depth() * 0.5; - - Vector lines; - for (int i = 0; i < points.size(); i++) { - int n = (i + 1) % points.size(); - lines.push_back(Vector3(points[i].x, points[i].y, depth)); - lines.push_back(Vector3(points[n].x, points[n].y, depth)); - lines.push_back(Vector3(points[i].x, points[i].y, -depth)); - lines.push_back(Vector3(points[n].x, points[n].y, -depth)); - lines.push_back(Vector3(points[i].x, points[i].y, depth)); - lines.push_back(Vector3(points[i].x, points[i].y, -depth)); - } - - const Ref material = - get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); - - p_gizmo->add_lines(lines, material); - p_gizmo->add_collision_segments(lines); -} - -//// - -NavigationRegion3DGizmoPlugin::NavigationRegion3DGizmoPlugin() { - create_material("navigation_edge_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge", Color(0.5, 1, 1))); - create_material("navigation_edge_material_disabled", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge_disabled", Color(0.7, 0.7, 0.7))); - create_material("navigation_solid_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid", Color(0.5, 1, 1, 0.4))); - create_material("navigation_solid_material_disabled", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid_disabled", Color(0.7, 0.7, 0.7, 0.4))); -} - -bool NavigationRegion3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String NavigationRegion3DGizmoPlugin::get_gizmo_name() const { - return "NavigationRegion3D"; -} - -int NavigationRegion3DGizmoPlugin::get_priority() const { - return -1; -} - -void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - NavigationRegion3D *navmesh = Object::cast_to(p_gizmo->get_spatial_node()); - - Ref edge_material = get_material("navigation_edge_material", p_gizmo); - Ref edge_material_disabled = get_material("navigation_edge_material_disabled", p_gizmo); - Ref solid_material = get_material("navigation_solid_material", p_gizmo); - Ref solid_material_disabled = get_material("navigation_solid_material_disabled", p_gizmo); - - p_gizmo->clear(); - Ref navmeshie = navmesh->get_navigation_mesh(); - if (navmeshie.is_null()) { - return; - } - - Vector vertices = navmeshie->get_vertices(); - const Vector3 *vr = vertices.ptr(); - List faces; - for (int i = 0; i < navmeshie->get_polygon_count(); i++) { - Vector p = navmeshie->get_polygon(i); - - for (int j = 2; j < p.size(); j++) { - Face3 f; - f.vertex[0] = vr[p[0]]; - f.vertex[1] = vr[p[j - 1]]; - f.vertex[2] = vr[p[j]]; - - faces.push_back(f); - } - } - - if (faces.is_empty()) { - return; - } - - Map<_EdgeKey, bool> edge_map; - Vector tmeshfaces; - tmeshfaces.resize(faces.size() * 3); - - { - Vector3 *tw = tmeshfaces.ptrw(); - int tidx = 0; - - for (List::Element *E = faces.front(); E; E = E->next()) { - const Face3 &f = E->get(); - - for (int j = 0; j < 3; j++) { - tw[tidx++] = f.vertex[j]; - _EdgeKey ek; - ek.from = f.vertex[j].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); - ek.to = f.vertex[(j + 1) % 3].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); - if (ek.from < ek.to) { - SWAP(ek.from, ek.to); - } - - Map<_EdgeKey, bool>::Element *F = edge_map.find(ek); - - if (F) { - F->get() = false; - - } else { - edge_map[ek] = true; - } - } - } - } - Vector lines; - - for (Map<_EdgeKey, bool>::Element *E = edge_map.front(); E; E = E->next()) { - if (E->get()) { - lines.push_back(E->key().from); - lines.push_back(E->key().to); - } - } - - Ref tmesh = memnew(TriangleMesh); - tmesh->create(tmeshfaces); - - if (lines.size()) { - p_gizmo->add_lines(lines, navmesh->is_enabled() ? edge_material : edge_material_disabled); - } - p_gizmo->add_collision_triangles(tmesh); - Ref m = memnew(ArrayMesh); - Array a; - a.resize(Mesh::ARRAY_MAX); - a[0] = tmeshfaces; - m->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); - m->surface_set_material(0, navmesh->is_enabled() ? solid_material : solid_material_disabled); - p_gizmo->add_mesh(m); - p_gizmo->add_collision_segments(lines); -} - -////// - -#define BODY_A_RADIUS 0.25 -#define BODY_B_RADIUS 0.27 - -Basis JointGizmosDrawer::look_body(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) { - const Vector3 &p_eye(p_joint_transform.origin); - const Vector3 &p_target(p_body_transform.origin); - - Vector3 v_x, v_y, v_z; - - // Look the body with X - v_x = p_target - p_eye; - v_x.normalize(); - - v_z = v_x.cross(Vector3(0, 1, 0)); - v_z.normalize(); - - v_y = v_z.cross(v_x); - v_y.normalize(); - - Basis base; - base.set(v_x, v_y, v_z); - - // Absorb current joint transform - base = p_joint_transform.basis.inverse() * base; - - return base; -} - -Basis JointGizmosDrawer::look_body_toward(Vector3::Axis p_axis, const Transform3D &joint_transform, const Transform3D &body_transform) { - switch (p_axis) { - case Vector3::AXIS_X: - return look_body_toward_x(joint_transform, body_transform); - case Vector3::AXIS_Y: - return look_body_toward_y(joint_transform, body_transform); - case Vector3::AXIS_Z: - return look_body_toward_z(joint_transform, body_transform); - default: - return Basis(); - } -} - -Basis JointGizmosDrawer::look_body_toward_x(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) { - const Vector3 &p_eye(p_joint_transform.origin); - const Vector3 &p_target(p_body_transform.origin); - - const Vector3 p_front(p_joint_transform.basis.get_axis(0)); - - Vector3 v_x, v_y, v_z; - - // Look the body with X - v_x = p_target - p_eye; - v_x.normalize(); - - v_y = p_front.cross(v_x); - v_y.normalize(); - - v_z = v_y.cross(p_front); - v_z.normalize(); - - // Clamp X to FRONT axis - v_x = p_front; - v_x.normalize(); - - Basis base; - base.set(v_x, v_y, v_z); - - // Absorb current joint transform - base = p_joint_transform.basis.inverse() * base; - - return base; -} - -Basis JointGizmosDrawer::look_body_toward_y(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) { - const Vector3 &p_eye(p_joint_transform.origin); - const Vector3 &p_target(p_body_transform.origin); - - const Vector3 p_up(p_joint_transform.basis.get_axis(1)); - - Vector3 v_x, v_y, v_z; - - // Look the body with X - v_x = p_target - p_eye; - v_x.normalize(); - - v_z = v_x.cross(p_up); - v_z.normalize(); - - v_x = p_up.cross(v_z); - v_x.normalize(); - - // Clamp Y to UP axis - v_y = p_up; - v_y.normalize(); - - Basis base; - base.set(v_x, v_y, v_z); - - // Absorb current joint transform - base = p_joint_transform.basis.inverse() * base; - - return base; -} - -Basis JointGizmosDrawer::look_body_toward_z(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) { - const Vector3 &p_eye(p_joint_transform.origin); - const Vector3 &p_target(p_body_transform.origin); - - const Vector3 p_lateral(p_joint_transform.basis.get_axis(2)); - - Vector3 v_x, v_y, v_z; - - // Look the body with X - v_x = p_target - p_eye; - v_x.normalize(); - - v_z = p_lateral; - v_z.normalize(); - - v_y = v_z.cross(v_x); - v_y.normalize(); - - // Clamp X to Z axis - v_x = v_y.cross(v_z); - v_x.normalize(); - - Basis base; - base.set(v_x, v_y, v_z); - - // Absorb current joint transform - base = p_joint_transform.basis.inverse() * base; - - return base; -} - -void JointGizmosDrawer::draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform3D &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector &r_points, bool p_inverse) { - if (p_limit_lower == p_limit_upper) { - r_points.push_back(p_offset.translated(Vector3()).origin); - r_points.push_back(p_offset.translated(p_base.xform(Vector3(0.5, 0, 0))).origin); - - } else { - if (p_limit_lower > p_limit_upper) { - p_limit_lower = -Math_PI; - p_limit_upper = Math_PI; - } - - const int points = 32; - - for (int i = 0; i < points; i++) { - real_t s = p_limit_lower + i * (p_limit_upper - p_limit_lower) / points; - real_t n = p_limit_lower + (i + 1) * (p_limit_upper - p_limit_lower) / points; - - Vector3 from; - Vector3 to; - switch (p_axis) { - case Vector3::AXIS_X: - if (p_inverse) { - from = p_base.xform(Vector3(0, Math::sin(s), Math::cos(s))) * p_radius; - to = p_base.xform(Vector3(0, Math::sin(n), Math::cos(n))) * p_radius; - } else { - from = p_base.xform(Vector3(0, -Math::sin(s), Math::cos(s))) * p_radius; - to = p_base.xform(Vector3(0, -Math::sin(n), Math::cos(n))) * p_radius; - } - break; - case Vector3::AXIS_Y: - if (p_inverse) { - from = p_base.xform(Vector3(Math::cos(s), 0, -Math::sin(s))) * p_radius; - to = p_base.xform(Vector3(Math::cos(n), 0, -Math::sin(n))) * p_radius; - } else { - from = p_base.xform(Vector3(Math::cos(s), 0, Math::sin(s))) * p_radius; - to = p_base.xform(Vector3(Math::cos(n), 0, Math::sin(n))) * p_radius; - } - break; - case Vector3::AXIS_Z: - from = p_base.xform(Vector3(Math::cos(s), Math::sin(s), 0)) * p_radius; - to = p_base.xform(Vector3(Math::cos(n), Math::sin(n), 0)) * p_radius; - break; - } - - if (i == points - 1) { - r_points.push_back(p_offset.translated(to).origin); - r_points.push_back(p_offset.translated(Vector3()).origin); - } - if (i == 0) { - r_points.push_back(p_offset.translated(from).origin); - r_points.push_back(p_offset.translated(Vector3()).origin); - } - - r_points.push_back(p_offset.translated(from).origin); - r_points.push_back(p_offset.translated(to).origin); - } - - r_points.push_back(p_offset.translated(Vector3(0, p_radius * 1.5, 0)).origin); - r_points.push_back(p_offset.translated(Vector3()).origin); - } -} - -void JointGizmosDrawer::draw_cone(const Transform3D &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector &r_points) { - float r = 1.0; - float w = r * Math::sin(p_swing); - float d = r * Math::cos(p_swing); - - //swing - for (int i = 0; i < 360; i += 10) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 10); - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w; - - r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, a.x, a.y))).origin); - r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, b.x, b.y))).origin); - - if (i % 90 == 0) { - r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, a.x, a.y))).origin); - r_points.push_back(p_offset.translated(p_base.xform(Vector3())).origin); - } - } - - r_points.push_back(p_offset.translated(p_base.xform(Vector3())).origin); - r_points.push_back(p_offset.translated(p_base.xform(Vector3(1, 0, 0))).origin); - - /// Twist - float ts = Math::rad2deg(p_twist); - ts = MIN(ts, 720); - - for (int i = 0; i < int(ts); i += 5) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 5); - float c = i / 720.0; - float cn = (i + 5) / 720.0; - Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w * c; - Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w * cn; - - r_points.push_back(p_offset.translated(p_base.xform(Vector3(c, a.x, a.y))).origin); - r_points.push_back(p_offset.translated(p_base.xform(Vector3(cn, b.x, b.y))).origin); - } -} - -//// - -Joint3DGizmoPlugin::Joint3DGizmoPlugin() { - create_material("joint_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1))); - create_material("joint_body_a_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1))); - create_material("joint_body_b_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1))); - - update_timer = memnew(Timer); - update_timer->set_name("JointGizmoUpdateTimer"); - update_timer->set_wait_time(1.0 / 120.0); - update_timer->connect("timeout", callable_mp(this, &Joint3DGizmoPlugin::incremental_update_gizmos)); - update_timer->set_autostart(true); - EditorNode::get_singleton()->call_deferred(SNAME("add_child"), update_timer); -} - -void Joint3DGizmoPlugin::incremental_update_gizmos() { - if (!current_gizmos.is_empty()) { - update_idx++; - update_idx = update_idx % current_gizmos.size(); - redraw(current_gizmos[update_idx]); - } -} - -bool Joint3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to(p_spatial) != nullptr; -} - -String Joint3DGizmoPlugin::get_gizmo_name() const { - return "Joint3D"; -} - -int Joint3DGizmoPlugin::get_priority() const { - return -1; -} - -void Joint3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - Joint3D *joint = Object::cast_to(p_gizmo->get_spatial_node()); - - p_gizmo->clear(); - - Node3D *node_body_a = nullptr; - if (!joint->get_node_a().is_empty()) { - node_body_a = Object::cast_to(joint->get_node(joint->get_node_a())); - } - - Node3D *node_body_b = nullptr; - if (!joint->get_node_b().is_empty()) { - node_body_b = Object::cast_to(joint->get_node(joint->get_node_b())); - } - - if (!node_body_a && !node_body_b) { - return; - } - - Ref common_material = get_material("joint_material", p_gizmo); - Ref body_a_material = get_material("joint_body_a_material", p_gizmo); - Ref body_b_material = get_material("joint_body_b_material", p_gizmo); - - Vector points; - Vector body_a_points; - Vector body_b_points; - - if (Object::cast_to(joint)) { - CreatePinJointGizmo(Transform3D(), points); - p_gizmo->add_collision_segments(points); - p_gizmo->add_lines(points, common_material); - } - - HingeJoint3D *hinge = Object::cast_to(joint); - if (hinge) { - CreateHingeJointGizmo( - Transform3D(), - hinge->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform3D(), - node_body_b ? node_body_b->get_global_transform() : Transform3D(), - hinge->get_param(HingeJoint3D::PARAM_LIMIT_LOWER), - hinge->get_param(HingeJoint3D::PARAM_LIMIT_UPPER), - hinge->get_flag(HingeJoint3D::FLAG_USE_LIMIT), - points, - node_body_a ? &body_a_points : nullptr, - node_body_b ? &body_b_points : nullptr); - - p_gizmo->add_collision_segments(points); - p_gizmo->add_collision_segments(body_a_points); - p_gizmo->add_collision_segments(body_b_points); - - p_gizmo->add_lines(points, common_material); - p_gizmo->add_lines(body_a_points, body_a_material); - p_gizmo->add_lines(body_b_points, body_b_material); - } - - SliderJoint3D *slider = Object::cast_to(joint); - if (slider) { - CreateSliderJointGizmo( - Transform3D(), - slider->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform3D(), - node_body_b ? node_body_b->get_global_transform() : Transform3D(), - slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_LOWER), - slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_UPPER), - slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_LOWER), - slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_UPPER), - points, - node_body_a ? &body_a_points : nullptr, - node_body_b ? &body_b_points : nullptr); - - p_gizmo->add_collision_segments(points); - p_gizmo->add_collision_segments(body_a_points); - p_gizmo->add_collision_segments(body_b_points); - - p_gizmo->add_lines(points, common_material); - p_gizmo->add_lines(body_a_points, body_a_material); - p_gizmo->add_lines(body_b_points, body_b_material); - } - - ConeTwistJoint3D *cone = Object::cast_to(joint); - if (cone) { - CreateConeTwistJointGizmo( - Transform3D(), - cone->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform3D(), - node_body_b ? node_body_b->get_global_transform() : Transform3D(), - cone->get_param(ConeTwistJoint3D::PARAM_SWING_SPAN), - cone->get_param(ConeTwistJoint3D::PARAM_TWIST_SPAN), - node_body_a ? &body_a_points : nullptr, - node_body_b ? &body_b_points : nullptr); - - p_gizmo->add_collision_segments(body_a_points); - p_gizmo->add_collision_segments(body_b_points); - - p_gizmo->add_lines(body_a_points, body_a_material); - p_gizmo->add_lines(body_b_points, body_b_material); - } - - Generic6DOFJoint3D *gen = Object::cast_to(joint); - if (gen) { - CreateGeneric6DOFJointGizmo( - Transform3D(), - gen->get_global_transform(), - node_body_a ? node_body_a->get_global_transform() : Transform3D(), - node_body_b ? node_body_b->get_global_transform() : Transform3D(), - - gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), - gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), - gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), - gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), - gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), - gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), - - gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), - gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), - gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), - gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), - gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), - gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), - - gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), - gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), - gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), - gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), - gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), - gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), - - points, - node_body_a ? &body_a_points : nullptr, - node_body_a ? &body_b_points : nullptr); - - p_gizmo->add_collision_segments(points); - p_gizmo->add_collision_segments(body_a_points); - p_gizmo->add_collision_segments(body_b_points); - - p_gizmo->add_lines(points, common_material); - p_gizmo->add_lines(body_a_points, body_a_material); - p_gizmo->add_lines(body_b_points, body_b_material); - } -} - -void Joint3DGizmoPlugin::CreatePinJointGizmo(const Transform3D &p_offset, Vector &r_cursor_points) { - float cs = 0.25; - - r_cursor_points.push_back(p_offset.translated(Vector3(+cs, 0, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(-cs, 0, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, +cs, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, -cs, 0)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, +cs)).origin); - r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, -cs)).origin); -} - -void Joint3DGizmoPlugin::CreateHingeJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector &r_common_points, Vector *r_body_a_points, Vector *r_body_b_points) { - r_common_points.push_back(p_offset.translated(Vector3(0, 0, 0.5)).origin); - r_common_points.push_back(p_offset.translated(Vector3(0, 0, -0.5)).origin); - - if (!p_use_limit) { - p_limit_upper = -1; - p_limit_lower = 0; - } - - if (r_body_a_points) { - JointGizmosDrawer::draw_circle(Vector3::AXIS_Z, - BODY_A_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_a), - p_limit_lower, - p_limit_upper, - *r_body_a_points); - } - - if (r_body_b_points) { - JointGizmosDrawer::draw_circle(Vector3::AXIS_Z, - BODY_B_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_b), - p_limit_lower, - p_limit_upper, - *r_body_b_points); - } -} - -void Joint3DGizmoPlugin::CreateSliderJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector &r_points, Vector *r_body_a_points, Vector *r_body_b_points) { - p_linear_limit_lower = -p_linear_limit_lower; - p_linear_limit_upper = -p_linear_limit_upper; - - float cs = 0.25; - r_points.push_back(p_offset.translated(Vector3(0, 0, 0.5)).origin); - r_points.push_back(p_offset.translated(Vector3(0, 0, -0.5)).origin); - - if (p_linear_limit_lower >= p_linear_limit_upper) { - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, 0, 0)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, 0, 0)).origin); - - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, -cs)).origin); - - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, -cs)).origin); - r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, -cs)).origin); - - } else { - r_points.push_back(p_offset.translated(Vector3(+cs * 2, 0, 0)).origin); - r_points.push_back(p_offset.translated(Vector3(-cs * 2, 0, 0)).origin); - } - - if (r_body_a_points) { - JointGizmosDrawer::draw_circle( - Vector3::AXIS_X, - BODY_A_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_a), - p_angular_limit_lower, - p_angular_limit_upper, - *r_body_a_points); - } - - if (r_body_b_points) { - JointGizmosDrawer::draw_circle( - Vector3::AXIS_X, - BODY_B_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_b), - p_angular_limit_lower, - p_angular_limit_upper, - *r_body_b_points, - true); - } -} - -void Joint3DGizmoPlugin::CreateConeTwistJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_swing, real_t p_twist, Vector *r_body_a_points, Vector *r_body_b_points) { - if (r_body_a_points) { - JointGizmosDrawer::draw_cone( - p_offset, - JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_a), - p_swing, - p_twist, - *r_body_a_points); - } - - if (r_body_b_points) { - JointGizmosDrawer::draw_cone( - p_offset, - JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_b), - p_swing, - p_twist, - *r_body_b_points); - } -} - -void Joint3DGizmoPlugin::CreateGeneric6DOFJointGizmo( - const Transform3D &p_offset, - const Transform3D &p_trs_joint, - const Transform3D &p_trs_body_a, - const Transform3D &p_trs_body_b, - real_t p_angular_limit_lower_x, - real_t p_angular_limit_upper_x, - real_t p_linear_limit_lower_x, - real_t p_linear_limit_upper_x, - bool p_enable_angular_limit_x, - bool p_enable_linear_limit_x, - real_t p_angular_limit_lower_y, - real_t p_angular_limit_upper_y, - real_t p_linear_limit_lower_y, - real_t p_linear_limit_upper_y, - bool p_enable_angular_limit_y, - bool p_enable_linear_limit_y, - real_t p_angular_limit_lower_z, - real_t p_angular_limit_upper_z, - real_t p_linear_limit_lower_z, - real_t p_linear_limit_upper_z, - bool p_enable_angular_limit_z, - bool p_enable_linear_limit_z, - Vector &r_points, - Vector *r_body_a_points, - Vector *r_body_b_points) { - float cs = 0.25; - - for (int ax = 0; ax < 3; ax++) { - float ll = 0; - float ul = 0; - float lll = 0; - float lul = 0; - - int a1 = 0; - int a2 = 0; - int a3 = 0; - bool enable_ang = false; - bool enable_lin = false; - - switch (ax) { - case 0: - ll = p_angular_limit_lower_x; - ul = p_angular_limit_upper_x; - lll = -p_linear_limit_lower_x; - lul = -p_linear_limit_upper_x; - enable_ang = p_enable_angular_limit_x; - enable_lin = p_enable_linear_limit_x; - a1 = 0; - a2 = 1; - a3 = 2; - break; - case 1: - ll = p_angular_limit_lower_y; - ul = p_angular_limit_upper_y; - lll = -p_linear_limit_lower_y; - lul = -p_linear_limit_upper_y; - enable_ang = p_enable_angular_limit_y; - enable_lin = p_enable_linear_limit_y; - a1 = 1; - a2 = 2; - a3 = 0; - break; - case 2: - ll = p_angular_limit_lower_z; - ul = p_angular_limit_upper_z; - lll = -p_linear_limit_lower_z; - lul = -p_linear_limit_upper_z; - enable_ang = p_enable_angular_limit_z; - enable_lin = p_enable_linear_limit_z; - a1 = 2; - a2 = 0; - a3 = 1; - break; - } - -#define ADD_VTX(x, y, z) \ - { \ - Vector3 v; \ - v[a1] = (x); \ - v[a2] = (y); \ - v[a3] = (z); \ - r_points.push_back(p_offset.translated(v).origin); \ - } - - if (enable_lin && lll >= lul) { - ADD_VTX(lul, 0, 0); - ADD_VTX(lll, 0, 0); - - ADD_VTX(lul, -cs, -cs); - ADD_VTX(lul, -cs, cs); - ADD_VTX(lul, -cs, cs); - ADD_VTX(lul, cs, cs); - ADD_VTX(lul, cs, cs); - ADD_VTX(lul, cs, -cs); - ADD_VTX(lul, cs, -cs); - ADD_VTX(lul, -cs, -cs); - - ADD_VTX(lll, -cs, -cs); - ADD_VTX(lll, -cs, cs); - ADD_VTX(lll, -cs, cs); - ADD_VTX(lll, cs, cs); - ADD_VTX(lll, cs, cs); - ADD_VTX(lll, cs, -cs); - ADD_VTX(lll, cs, -cs); - ADD_VTX(lll, -cs, -cs); - - } else { - ADD_VTX(+cs * 2, 0, 0); - ADD_VTX(-cs * 2, 0, 0); - } - - if (!enable_ang) { - ll = 0; - ul = -1; - } - - if (r_body_a_points) { - JointGizmosDrawer::draw_circle( - static_cast(ax), - BODY_A_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward(static_cast(ax), p_trs_joint, p_trs_body_a), - ll, - ul, - *r_body_a_points, - true); - } - - if (r_body_b_points) { - JointGizmosDrawer::draw_circle( - static_cast(ax), - BODY_B_RADIUS, - p_offset, - JointGizmosDrawer::look_body_toward(static_cast(ax), p_trs_joint, p_trs_body_b), - ll, - ul, - *r_body_b_points); - } - } - -#undef ADD_VTX -} diff --git a/editor/node_3d_editor_gizmos.h b/editor/node_3d_editor_gizmos.h deleted file mode 100644 index 6f071859ec..0000000000 --- a/editor/node_3d_editor_gizmos.h +++ /dev/null @@ -1,492 +0,0 @@ -/*************************************************************************/ -/* node_3d_editor_gizmos.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef SPATIAL_EDITOR_GIZMOS_H -#define SPATIAL_EDITOR_GIZMOS_H - -#include "editor/plugins/node_3d_editor_plugin.h" -#include "scene/3d/camera_3d.h" - -class Camera3D; - -class Light3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(Light3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - 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; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - Light3DGizmoPlugin(); -}; - -class AudioStreamPlayer3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(AudioStreamPlayer3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - 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; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - AudioStreamPlayer3DGizmoPlugin(); -}; - -class Camera3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(Camera3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - 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; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - Camera3DGizmoPlugin(); -}; - -class MeshInstance3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(MeshInstance3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - bool can_be_hidden() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - MeshInstance3DGizmoPlugin(); -}; - -class OccluderInstance3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(OccluderInstance3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - OccluderInstance3DGizmoPlugin(); -}; - -class Sprite3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(Sprite3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - bool can_be_hidden() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - Sprite3DGizmoPlugin(); -}; - -class Position3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(Position3DGizmoPlugin, EditorNode3DGizmoPlugin); - - Ref pos3d_mesh; - Vector cursor_points; - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - Position3DGizmoPlugin(); -}; - -class Skeleton3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(Skeleton3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - Skeleton3DGizmoPlugin(); -}; - -class PhysicalBone3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(PhysicalBone3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - PhysicalBone3DGizmoPlugin(); -}; - -class RayCast3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(RayCast3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - RayCast3DGizmoPlugin(); -}; - -class SpringArm3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(SpringArm3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - SpringArm3DGizmoPlugin(); -}; - -class VehicleWheel3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(VehicleWheel3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - VehicleWheel3DGizmoPlugin(); -}; - -class SoftBody3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(SoftBody3DGizmoPlugin, 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 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; - - SoftBody3DGizmoPlugin(); -}; - -class VisibleOnScreenNotifier3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(VisibleOnScreenNotifier3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - 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; - - VisibleOnScreenNotifier3DGizmoPlugin(); -}; - -class CPUParticles3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(CPUParticles3DGizmoPlugin, 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; - CPUParticles3DGizmoPlugin(); -}; - -class GPUParticles3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(GPUParticles3DGizmoPlugin, 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 = false) override; - - GPUParticles3DGizmoPlugin(); -}; - -class GPUParticlesCollision3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(GPUParticlesCollision3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - 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; - - GPUParticlesCollision3DGizmoPlugin(); -}; - -class ReflectionProbeGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(ReflectionProbeGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - 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; - - ReflectionProbeGizmoPlugin(); -}; - -class DecalGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(DecalGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - 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; - - DecalGizmoPlugin(); -}; - -class VoxelGIGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(VoxelGIGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - 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; - - VoxelGIGizmoPlugin(); -}; - -class LightmapGIGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(LightmapGIGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - 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; - - LightmapGIGizmoPlugin(); -}; - -class LightmapProbeGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(LightmapProbeGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - 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; - - LightmapProbeGizmoPlugin(); -}; - -class CollisionObject3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(CollisionObject3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - CollisionObject3DGizmoPlugin(); -}; - -class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - 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; - - CollisionShape3DGizmoPlugin(); -}; - -class CollisionPolygon3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(CollisionPolygon3DGizmoPlugin, EditorNode3DGizmoPlugin); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - CollisionPolygon3DGizmoPlugin(); -}; - -class NavigationRegion3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(NavigationRegion3DGizmoPlugin, EditorNode3DGizmoPlugin); - - struct _EdgeKey { - Vector3 from; - Vector3 to; - - bool operator<(const _EdgeKey &p_with) const { return from == p_with.from ? to < p_with.to : from < p_with.from; } - }; - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - NavigationRegion3DGizmoPlugin(); -}; - -class JointGizmosDrawer { -public: - static Basis look_body(const Transform3D &p_joint_transform, const Transform3D &p_body_transform); - static Basis look_body_toward(Vector3::Axis p_axis, const Transform3D &joint_transform, const Transform3D &body_transform); - static Basis look_body_toward_x(const Transform3D &p_joint_transform, const Transform3D &p_body_transform); - static Basis look_body_toward_y(const Transform3D &p_joint_transform, const Transform3D &p_body_transform); - /// Special function just used for physics joints, it returns a basis constrained toward Joint Z axis - /// with axis X and Y that are looking toward the body and oriented toward up - static Basis look_body_toward_z(const Transform3D &p_joint_transform, const Transform3D &p_body_transform); - - // Draw circle around p_axis - static void draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform3D &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector &r_points, bool p_inverse = false); - static void draw_cone(const Transform3D &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector &r_points); -}; - -class Joint3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(Joint3DGizmoPlugin, EditorNode3DGizmoPlugin); - - Timer *update_timer; - uint64_t update_idx = 0; - - void incremental_update_gizmos(); - -public: - bool has_gizmo(Node3D *p_spatial) override; - String get_gizmo_name() const override; - int get_priority() const override; - void redraw(EditorNode3DGizmo *p_gizmo) override; - - static void CreatePinJointGizmo(const Transform3D &p_offset, Vector &r_cursor_points); - static void CreateHingeJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector &r_common_points, Vector *r_body_a_points, Vector *r_body_b_points); - static void CreateSliderJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector &r_points, Vector *r_body_a_points, Vector *r_body_b_points); - static void CreateConeTwistJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_swing, real_t p_twist, Vector *r_body_a_points, Vector *r_body_b_points); - static void CreateGeneric6DOFJointGizmo( - const Transform3D &p_offset, - const Transform3D &p_trs_joint, - const Transform3D &p_trs_body_a, - const Transform3D &p_trs_body_b, - real_t p_angular_limit_lower_x, - real_t p_angular_limit_upper_x, - real_t p_linear_limit_lower_x, - real_t p_linear_limit_upper_x, - bool p_enable_angular_limit_x, - bool p_enable_linear_limit_x, - real_t p_angular_limit_lower_y, - real_t p_angular_limit_upper_y, - real_t p_linear_limit_lower_y, - real_t p_linear_limit_upper_y, - bool p_enable_angular_limit_y, - bool p_enable_linear_limit_y, - real_t p_angular_limit_lower_z, - real_t p_angular_limit_upper_z, - real_t p_linear_limit_lower_z, - real_t p_linear_limit_upper_z, - bool p_enable_angular_limit_z, - bool p_enable_linear_limit_z, - Vector &r_points, - Vector *r_body_a_points, - Vector *r_body_b_points); - - Joint3DGizmoPlugin(); -}; - -#endif // SPATIAL_EDITOR_GIZMOS_H diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp new file mode 100644 index 0000000000..492ba51b8a --- /dev/null +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -0,0 +1,5477 @@ +/*************************************************************************/ +/* node_3d_editor_gizmos.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "node_3d_editor_gizmos.h" + +#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" +#include "scene/3d/decal.h" +#include "scene/3d/gpu_particles_3d.h" +#include "scene/3d/gpu_particles_collision_3d.h" +#include "scene/3d/light_3d.h" +#include "scene/3d/lightmap_gi.h" +#include "scene/3d/lightmap_probe.h" +#include "scene/3d/listener_3d.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/navigation_region_3d.h" +#include "scene/3d/occluder_instance_3d.h" +#include "scene/3d/physics_joint_3d.h" +#include "scene/3d/position_3d.h" +#include "scene/3d/ray_cast_3d.h" +#include "scene/3d/reflection_probe.h" +#include "scene/3d/soft_body_3d.h" +#include "scene/3d/spring_arm_3d.h" +#include "scene/3d/sprite_3d.h" +#include "scene/3d/vehicle_body_3d.h" +#include "scene/3d/visible_on_screen_notifier_3d.h" +#include "scene/3d/voxel_gi.h" +#include "scene/resources/box_shape_3d.h" +#include "scene/resources/capsule_shape_3d.h" +#include "scene/resources/concave_polygon_shape_3d.h" +#include "scene/resources/convex_polygon_shape_3d.h" +#include "scene/resources/cylinder_shape_3d.h" +#include "scene/resources/height_map_shape_3d.h" +#include "scene/resources/primitive_meshes.h" +#include "scene/resources/ray_shape_3d.h" +#include "scene/resources/sphere_shape_3d.h" +#include "scene/resources/surface_tool.h" +#include "scene/resources/world_margin_shape_3d.h" + +#define HANDLE_HALF_SIZE 9.5 + +bool EditorNode3DGizmo::is_editable() const { + ERR_FAIL_COND_V(!spatial_node, false); + Node *edited_root = spatial_node->get_tree()->get_edited_scene_root(); + if (spatial_node == edited_root) { + return true; + } + if (spatial_node->get_owner() == edited_root) { + return true; + } + + if (edited_root->is_editable_instance(spatial_node->get_owner())) { + return true; + } + + return false; +} + +void EditorNode3DGizmo::clear() { + for (int i = 0; i < instances.size(); i++) { + if (instances[i].instance.is_valid()) { + RS::get_singleton()->free(instances[i].instance); + } + } + + billboard_handle = false; + collision_segments.clear(); + collision_mesh = Ref(); + instances.clear(); + handles.clear(); + secondary_handles.clear(); +} + +void EditorNode3DGizmo::redraw() { + if (get_script_instance() && get_script_instance()->has_method("_redraw")) { + get_script_instance()->call("_redraw"); + } else { + 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_id) const { + if (get_script_instance() && get_script_instance()->has_method("_get_handle_name")) { + return get_script_instance()->call("_get_handle_name", p_id); + } + + ERR_FAIL_COND_V(!gizmo_plugin, ""); + return gizmo_plugin->get_handle_name(this, p_id); +} + +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_id); + } + + ERR_FAIL_COND_V(!gizmo_plugin, false); + return gizmo_plugin->is_handle_highlighted(this, p_id); +} + +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_id); + } + + ERR_FAIL_COND_V(!gizmo_plugin, Variant()); + return gizmo_plugin->get_handle_value(this, p_id); +} + +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_id, p_camera, p_point); + return; + } + + ERR_FAIL_COND(!gizmo_plugin); + gizmo_plugin->set_handle(this, p_id, p_camera, p_point); +} + +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_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 EditorNode3DGizmo::subgizmos_intersect_frustum(const Camera3D *p_camera, const Vector &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()); + 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->set_subgizmo_transform(this, p_id, p_transform); +} + +void EditorNode3DGizmo::commit_subgizmos(const Vector &p_ids, const Vector &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) { + ERR_FAIL_NULL(p_node); + spatial_node = p_node; +} + +void EditorNode3DGizmo::Instance::create_instance(Node3D *p_base, bool p_hidden) { + instance = RS::get_singleton()->instance_create2(mesh->get_rid(), p_base->get_world_3d()->get_scenario()); + RS::get_singleton()->instance_attach_object_instance_id(instance, p_base->get_instance_id()); + if (skin_reference.is_valid()) { + RS::get_singleton()->instance_attach_skeleton(instance, skin_reference->get_skeleton()); + } + if (extra_margin) { + RS::get_singleton()->instance_set_extra_visibility_margin(instance, 1); + } + RS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, RS::SHADOW_CASTING_SETTING_OFF); + int layer = p_hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER; + RS::get_singleton()->instance_set_layer_mask(instance, layer); //gizmos are 26 + RS::get_singleton()->instance_geometry_set_flag(instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true); +} + +void EditorNode3DGizmo::add_mesh(const Ref &p_mesh, const Ref &p_material, const Transform3D &p_xform, const Ref &p_skin_reference) { + ERR_FAIL_COND(!spatial_node); + Instance ins; + + 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() * ins.xform); + if (ins.material.is_valid()) { + RS::get_singleton()->instance_geometry_set_material_override(ins.instance, p_material->get_rid()); + } + } + + instances.push_back(ins); +} + +void EditorNode3DGizmo::add_lines(const Vector &p_lines, const Ref &p_material, bool p_billboard, const Color &p_modulate) { + add_vertices(p_lines, p_material, Mesh::PRIMITIVE_LINES, p_billboard, p_modulate); +} + +void EditorNode3DGizmo::add_vertices(const Vector &p_vertices, const Ref &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard, const Color &p_modulate) { + if (p_vertices.is_empty()) { + return; + } + + ERR_FAIL_COND(!spatial_node); + Instance ins; + + Ref mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + + a[Mesh::ARRAY_VERTEX] = p_vertices; + + Vector color; + color.resize(p_vertices.size()); + { + Color *w = color.ptrw(); + for (int i = 0; i < p_vertices.size(); i++) { + if (is_selected()) { + w[i] = Color(1, 1, 1, 0.8) * p_modulate; + } else { + w[i] = Color(1, 1, 1, 0.2) * p_modulate; + } + } + } + + a[Mesh::ARRAY_COLOR] = color; + + mesh->add_surface_from_arrays(p_primitive_type, a); + mesh->surface_set_material(0, p_material); + + if (p_billboard) { + float md = 0; + for (int i = 0; i < p_vertices.size(); i++) { + md = MAX(0, p_vertices[i].length()); + } + if (md) { + mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); + } + } + + ins.mesh = mesh; + 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); +} + +void EditorNode3DGizmo::add_unscaled_billboard(const Ref &p_material, float p_scale, const Color &p_modulate) { + ERR_FAIL_COND(!spatial_node); + Instance ins; + + Vector vs; + Vector uv; + Vector colors; + + vs.push_back(Vector3(-p_scale, p_scale, 0)); + vs.push_back(Vector3(p_scale, p_scale, 0)); + vs.push_back(Vector3(p_scale, -p_scale, 0)); + vs.push_back(Vector3(-p_scale, -p_scale, 0)); + + uv.push_back(Vector2(0, 0)); + uv.push_back(Vector2(1, 0)); + uv.push_back(Vector2(1, 1)); + uv.push_back(Vector2(0, 1)); + + colors.push_back(p_modulate); + colors.push_back(p_modulate); + colors.push_back(p_modulate); + colors.push_back(p_modulate); + + Ref mesh = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[Mesh::ARRAY_VERTEX] = vs; + a[Mesh::ARRAY_TEX_UV] = uv; + Vector indices; + indices.push_back(0); + indices.push_back(1); + indices.push_back(2); + indices.push_back(0); + indices.push_back(2); + indices.push_back(3); + a[Mesh::ARRAY_INDEX] = indices; + a[Mesh::ARRAY_COLOR] = colors; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + mesh->surface_set_material(0, p_material); + + float md = 0; + for (int i = 0; i < vs.size(); i++) { + md = MAX(0, vs[i].length()); + } + if (md) { + mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); + } + + selectable_icon_size = p_scale; + 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; + if (valid) { + ins.create_instance(spatial_node, hidden); + RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform()); + } + + selectable_icon_size = p_scale; + + instances.push_back(ins); +} + +void EditorNode3DGizmo::add_collision_triangles(const Ref &p_tmesh) { + collision_mesh = p_tmesh; +} + +void EditorNode3DGizmo::add_collision_segments(const Vector &p_lines) { + int from = collision_segments.size(); + collision_segments.resize(from + p_lines.size()); + for (int i = 0; i < p_lines.size(); i++) { + collision_segments.write[from + i] = p_lines[i]; + } +} + +void EditorNode3DGizmo::add_handles(const Vector &p_handles, const Ref &p_material, const Vector &p_ids, bool p_billboard, bool p_secondary) { + billboard_handle = p_billboard; + + if (!is_selected() || !is_editable()) { + return; + } + + ERR_FAIL_COND(!spatial_node); + + 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 mesh = memnew(ArrayMesh); + + Array a; + a.resize(RS::ARRAY_MAX); + a[RS::ARRAY_VERTEX] = p_handles; + Vector colors; + { + colors.resize(p_handles.size()); + Color *w = colors.ptrw(); + for (int i = 0; i < p_handles.size(); i++) { + Color col(1, 1, 1, 1); + if (is_handle_highlighted(i)) { + col = Color(0, 0, 1, 0.9); + } + + int id = p_ids.is_empty() ? i : p_ids[i]; + if (!is_current_hover_gizmo || current_hover_handle != id) { + col.a = 0.8; + } + + w[i] = col; + } + } + a[RS::ARRAY_COLOR] = colors; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a); + mesh->surface_set_material(0, p_material); + + if (p_billboard) { + float md = 0; + for (int i = 0; i < p_handles.size(); i++) { + md = MAX(0, p_handles[i].length()); + } + if (md) { + mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0)); + } + } + + ins.mesh = mesh; + 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); + + Vector &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 &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 &p_material, Vector3 p_size, Vector3 p_position, const Transform3D &p_xform) { + ERR_FAIL_COND(!spatial_node); + + BoxMesh box_mesh; + box_mesh.set_size(p_size); + + Array arrays = box_mesh.surface_get_arrays(0); + PackedVector3Array vertex = arrays[RS::ARRAY_VERTEX]; + Vector3 *w = vertex.ptrw(); + + for (int i = 0; i < vertex.size(); ++i) { + w[i] += p_position; + } + + arrays[RS::ARRAY_VERTEX] = vertex; + + Ref m = memnew(ArrayMesh); + m->add_surface_from_arrays(box_mesh.surface_get_primitive_type(0), arrays); + add_mesh(m, p_material, p_xform); +} + +bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector &p_frustum) { + 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) { + Vector3 origin = spatial_node->get_global_transform().get_origin(); + + const Plane *p = p_frustum.ptr(); + int fc = p_frustum.size(); + + bool any_out = false; + + for (int j = 0; j < fc; j++) { + if (p[j].is_point_over(origin)) { + any_out = true; + break; + } + } + + return !any_out; + } + + if (collision_segments.size()) { + const Plane *p = p_frustum.ptr(); + int fc = p_frustum.size(); + + int vc = collision_segments.size(); + const Vector3 *vptr = collision_segments.ptr(); + Transform3D t = spatial_node->get_global_transform(); + + bool any_out = false; + for (int j = 0; j < fc; j++) { + for (int i = 0; i < vc; i++) { + Vector3 v = t.xform(vptr[i]); + if (p[j].is_point_over(v)) { + any_out = true; + break; + } + } + if (any_out) { + break; + } + } + + if (!any_out) { + return true; + } + } + + if (collision_mesh.is_valid()) { + Transform3D t = spatial_node->get_global_transform(); + + Vector3 mesh_scale = t.get_basis().get_scale(); + t.orthonormalize(); + + Transform3D it = t.affine_inverse(); + + Vector transformed_frustum; + int plane_count = p_frustum.size(); + transformed_frustum.resize(plane_count); + + for (int i = 0; i < plane_count; i++) { + transformed_frustum.write[i] = it.xform(p_frustum[i]); + } + + Vector 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; + } + } + + return false; +} + +void EditorNode3DGizmo::handles_intersect_ray(Camera3D *p_camera, const Vector2 &p_point, bool p_shift_pressed, int &r_id) { + r_id = -1; + + ERR_FAIL_COND(!spatial_node); + ERR_FAIL_COND(!valid); + + if (hidden) { + return; + } + + 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; + + 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) { + min_d = dp; + if (secondary_handle_ids.is_empty()) { + r_id = i; + } else { + r_id = secondary_handle_ids[i]; + } + } + } + } + + if (r_id != -1 && p_shift_pressed) { + return; + } + + min_d = 1e20; + + 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) { + min_d = dp; + if (handle_ids.is_empty()) { + r_id = i; + } else { + r_id = handle_ids[i]; + } + } + } + } +} + +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) { + Transform3D t = spatial_node->get_global_transform(); + Vector3 camera_position = p_camera->get_camera_transform().origin; + if (!camera_position.is_equal_approx(t.origin)) { + t.set_look_at(t.origin, camera_position); + } + + float scale = t.origin.distance_to(p_camera->get_camera_transform().origin); + + if (p_camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) { + float aspect = p_camera->get_viewport()->get_visible_rect().size.aspect(); + float size = p_camera->get_size(); + scale = size / aspect; + } + + Point2 center = p_camera->unproject_position(t.origin); + + Transform3D orig_camera_transform = p_camera->get_camera_transform(); + + if (!orig_camera_transform.origin.is_equal_approx(t.origin) && + ABS(orig_camera_transform.basis.get_axis(Vector3::AXIS_Z).dot(Vector3(0, 1, 0))) < 0.99) { + p_camera->look_at(t.origin); + } + + Vector3 c0 = t.xform(Vector3(selectable_icon_size, selectable_icon_size, 0) * scale); + Vector3 c1 = t.xform(Vector3(-selectable_icon_size, -selectable_icon_size, 0) * scale); + + Point2 p0 = p_camera->unproject_position(c0); + Point2 p1 = p_camera->unproject_position(c1); + + p_camera->set_global_transform(orig_camera_transform); + + Rect2 rect(p0, (p1 - p0).abs()); + + rect.set_position(center - rect.get_size() / 2.0); + + if (rect.has_point(p_point)) { + r_pos = t.origin; + r_normal = -p_camera->project_ray_normal(p_point); + return true; + } + } + + if (collision_segments.size()) { + Plane camp(p_camera->get_transform().origin, (-p_camera->get_transform().basis.get_axis(2)).normalized()); + + int vc = collision_segments.size(); + const Vector3 *vptr = collision_segments.ptr(); + 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)); + } + + Vector3 cp; + float cpd = 1e20; + + for (int i = 0; i < vc / 2; i++) { + Vector3 a = t.xform(vptr[i * 2 + 0]); + Vector3 b = t.xform(vptr[i * 2 + 1]); + Vector2 s[2]; + s[0] = p_camera->unproject_position(a); + s[1] = p_camera->unproject_position(b); + + Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, s); + + float pd = p.distance_to(p_point); + + if (pd < cpd) { + float d = s[0].distance_to(s[1]); + Vector3 tcp; + if (d > 0) { + float d2 = s[0].distance_to(p) / d; + tcp = a + (b - a) * d2; + + } else { + tcp = a; + } + + if (camp.distance_to(tcp) < p_camera->get_near()) { + continue; + } + cp = tcp; + cpd = pd; + } + } + + if (cpd < 8) { + r_pos = cp; + r_normal = -p_camera->project_ray_normal(p_point); + return true; + } + } + + if (collision_mesh.is_valid()) { + Transform3D gt = spatial_node->get_global_transform(); + + if (billboard_handle) { + gt.set_look_at(gt.origin, gt.origin - p_camera->get_transform().basis.get_axis(2), p_camera->get_transform().basis.get_axis(1)); + } + + Transform3D ai = gt.affine_inverse(); + Vector3 ray_from = ai.xform(p_camera->project_ray_origin(p_point)); + Vector3 ray_dir = ai.basis.xform(p_camera->project_ray_normal(p_point)).normalized(); + Vector3 rpos, rnorm; + + if (collision_mesh->intersect_ray(ray_from, ray_dir, rpos, rnorm)) { + r_pos = gt.xform(rpos); + r_normal = gt.basis.xform(rnorm).normalized(); + return true; + } + } + + 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 EditorNode3DGizmo::get_subgizmo_selection() const { + Vector 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); + valid = true; + + for (int i = 0; i < instances.size(); i++) { + instances.write[i].create_instance(spatial_node, hidden); + } + + transform(); +} + +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() * instances[i].xform); + } +} + +void EditorNode3DGizmo::free() { + ERR_FAIL_COND(!spatial_node); + ERR_FAIL_COND(!valid); + + for (int i = 0; i < instances.size(); i++) { + if (instances[i].instance.is_valid()) { + RS::get_singleton()->free(instances[i].instance); + } + instances.write[i].instance = RID(); + } + + clear(); + + valid = false; +} + +void EditorNode3DGizmo::set_hidden(bool p_hidden) { + hidden = p_hidden; + int layer = hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER; + for (int i = 0; i < instances.size(); ++i) { + RS::get_singleton()->instance_set_layer_mask(instances[i].instance, layer); + } +} + +void EditorNode3DGizmo::set_plugin(EditorNode3DGizmoPlugin *p_plugin) { + gizmo_plugin = 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", "material", "transform", "skeleton"), &EditorNode3DGizmo::add_mesh, DEFVAL(Variant()), DEFVAL(Transform3D()), DEFVAL(Ref())); + 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", "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, "id"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "_is_handle_highlighted", PropertyInfo(Variant::INT, "id"))); + + 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, "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; + selected = false; + spatial_node = nullptr; + gizmo_plugin = nullptr; + selectable_icon_size = -1.0f; +} + +EditorNode3DGizmo::~EditorNode3DGizmo() { + if (gizmo_plugin != nullptr) { + gizmo_plugin->unregister_gizmo(this); + } + clear(); +} + +///// + +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> mats; + + for (int i = 0; i < 4; i++) { + bool selected = i % 2 == 1; + bool instantiated = i < 2; + + Ref material = Ref(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 &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> icons; + + for (int i = 0; i < 4; i++) { + bool selected = i % 2 == 1; + bool instantiated = i < 2; + + Ref icon = Ref(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 &p_icon) { + Ref handle_material = Ref(memnew(StandardMaterial3D)); + + handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); + Ref 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>(); + materials[p_name].push_back(handle_material); +} + +void EditorNode3DGizmoPlugin::add_material(const String &p_name, Ref p_material) { + materials[p_name] = Vector>(); + materials[p_name].push_back(p_material); +} + +Ref EditorNode3DGizmoPlugin::get_material(const String &p_name, const Ref &p_gizmo) { + ERR_FAIL_COND_V(!materials.has(p_name), Ref()); + ERR_FAIL_COND_V(materials[p_name].size() == 0, Ref()); + + 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 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 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 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())); + + 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 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 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 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 EditorNode3DGizmoPlugin::subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector &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(); +} + +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 &p_ids, const Vector &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 + +Light3DGizmoPlugin::Light3DGizmoPlugin() { + // Enable vertex colors for the materials below as the gizmo color depends on the light color. + create_material("lines_primary", Color(1, 1, 1), false, false, true); + create_material("lines_secondary", Color(1, 1, 1, 0.35), false, false, true); + create_material("lines_billboard", Color(1, 1, 1), true, false, true); + + create_icon_material("light_directional_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoDirectionalLight"), SNAME("EditorIcons"))); + create_icon_material("light_omni_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoLight"), SNAME("EditorIcons"))); + create_icon_material("light_spot_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoSpotLight"), SNAME("EditorIcons"))); + + create_handle_material("handles"); + create_handle_material("handles_billboard", true); +} + +bool Light3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String Light3DGizmoPlugin::get_gizmo_name() const { + return "Light3D"; +} + +int Light3DGizmoPlugin::get_priority() const { + return -1; +} + +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(const EditorNode3DGizmo *p_gizmo, int p_id) const { + Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); + if (p_id == 0) { + return light->get_param(Light3D::PARAM_RANGE); + } + if (p_id == 1) { + return light->get_param(Light3D::PARAM_SPOT_ANGLE); + } + + return Variant(); +} + +static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vector3 &p_to, float p_arc_radius, const Transform3D &p_arc_xform) { + //bleh, discrete is simpler + static const int arc_test_points = 64; + float min_d = 1e20; + Vector3 min_p; + + for (int i = 0; i < arc_test_points; i++) { + float a = i * Math_PI * 0.5 / arc_test_points; + float an = (i + 1) * Math_PI * 0.5 / arc_test_points; + Vector3 p = Vector3(Math::cos(a), 0, -Math::sin(a)) * p_arc_radius; + Vector3 n = Vector3(Math::cos(an), 0, -Math::sin(an)) * p_arc_radius; + + Vector3 ra, rb; + Geometry3D::get_closest_points_between_segments(p, n, p_from, p_to, ra, rb); + + float d = ra.distance_to(rb); + if (d < min_d) { + min_d = d; + min_p = ra; + } + } + + //min_p = p_arc_xform.affine_inverse().xform(min_p); + float a = (Math_PI * 0.5) - Vector2(min_p.x, -min_p.z).angle(); + return Math::rad2deg(a); +} + +void Light3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { + Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); + Transform3D gt = light->get_global_transform(); + Transform3D gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + 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_id == 0) { + if (Object::cast_to(light)) { + Vector3 ra, rb; + Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, -4096), s[0], s[1], ra, rb); + + float d = -ra.z; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d <= 0) { // Equal is here for negative zero. + d = 0; + } + + light->set_param(Light3D::PARAM_RANGE, d); + } else if (Object::cast_to(light)) { + Plane cp = Plane(gt.origin, p_camera->get_transform().basis.get_axis(2)); + + Vector3 inters; + if (cp.intersects_ray(ray_from, ray_dir, &inters)) { + float r = inters.distance_to(gt.origin); + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + r = Math::snapped(r, Node3DEditor::get_singleton()->get_translate_snap()); + } + + light->set_param(Light3D::PARAM_RANGE, r); + } + } + + } 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(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { + Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); + if (p_cancel) { + light->set_param(p_id == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore); + + } 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_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)); + ur->add_undo_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, p_restore); + ur->commit_action(); + } +} + +void Light3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Light3D *light = Object::cast_to(p_gizmo->get_spatial_node()); + + Color color = light->get_color(); + // Make the gizmo color as bright as possible for better visibility + color.set_hsv(color.get_h(), color.get_s(), 1); + + p_gizmo->clear(); + + if (Object::cast_to(light)) { + Ref material = get_material("lines_primary", p_gizmo); + Ref icon = get_material("light_directional_icon", p_gizmo); + + const int arrow_points = 7; + const float arrow_length = 1.5; + + Vector3 arrow[arrow_points] = { + Vector3(0, 0, -1), + Vector3(0, 0.8, 0), + Vector3(0, 0.3, 0), + Vector3(0, 0.3, arrow_length), + Vector3(0, -0.3, arrow_length), + Vector3(0, -0.3, 0), + Vector3(0, -0.8, 0) + }; + + int arrow_sides = 2; + + Vector lines; + + for (int i = 0; i < arrow_sides; i++) { + for (int j = 0; j < arrow_points; j++) { + Basis ma(Vector3(0, 0, 1), Math_PI * i / arrow_sides); + + Vector3 v1 = arrow[j] - Vector3(0, 0, arrow_length); + Vector3 v2 = arrow[(j + 1) % arrow_points] - Vector3(0, 0, arrow_length); + + lines.push_back(ma.xform(v1)); + lines.push_back(ma.xform(v2)); + } + } + + p_gizmo->add_lines(lines, material, false, color); + p_gizmo->add_unscaled_billboard(icon, 0.05, color); + } + + if (Object::cast_to(light)) { + // Use both a billboard circle and 3 non-billboard circles for a better sphere-like representation + const Ref lines_material = get_material("lines_secondary", p_gizmo); + const Ref lines_billboard_material = get_material("lines_billboard", p_gizmo); + const Ref icon = get_material("light_omni_icon", p_gizmo); + + OmniLight3D *on = Object::cast_to(light); + const float r = on->get_param(Light3D::PARAM_RANGE); + Vector points; + Vector points_billboard; + + for (int i = 0; i < 120; i++) { + // Create a circle + const float ra = Math::deg2rad((float)(i * 3)); + const float rb = Math::deg2rad((float)((i + 1) * 3)); + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + // Draw axis-aligned circles + points.push_back(Vector3(a.x, 0, a.y)); + points.push_back(Vector3(b.x, 0, b.y)); + points.push_back(Vector3(0, a.x, a.y)); + points.push_back(Vector3(0, b.x, b.y)); + points.push_back(Vector3(a.x, a.y, 0)); + points.push_back(Vector3(b.x, b.y, 0)); + + // Draw a billboarded circle + points_billboard.push_back(Vector3(a.x, a.y, 0)); + points_billboard.push_back(Vector3(b.x, b.y, 0)); + } + + p_gizmo->add_lines(points, lines_material, true, color); + p_gizmo->add_lines(points_billboard, lines_billboard_material, true, color); + p_gizmo->add_unscaled_billboard(icon, 0.05, color); + + Vector handles; + handles.push_back(Vector3(r, 0, 0)); + p_gizmo->add_handles(handles, get_material("handles_billboard"), Vector(), true); + } + + if (Object::cast_to(light)) { + const Ref material_primary = get_material("lines_primary", p_gizmo); + const Ref material_secondary = get_material("lines_secondary", p_gizmo); + const Ref icon = get_material("light_spot_icon", p_gizmo); + + Vector points_primary; + Vector points_secondary; + SpotLight3D *sl = Object::cast_to(light); + + float r = sl->get_param(Light3D::PARAM_RANGE); + float w = r * Math::sin(Math::deg2rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); + float d = r * Math::cos(Math::deg2rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); + + for (int i = 0; i < 120; i++) { + // Draw a circle + const float ra = Math::deg2rad((float)(i * 3)); + const float rb = Math::deg2rad((float)((i + 1) * 3)); + const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; + const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w; + + points_primary.push_back(Vector3(a.x, a.y, -d)); + points_primary.push_back(Vector3(b.x, b.y, -d)); + + if (i % 15 == 0) { + // Draw 8 lines from the cone origin to the sides of the circle + points_secondary.push_back(Vector3(a.x, a.y, -d)); + points_secondary.push_back(Vector3()); + } + } + + points_primary.push_back(Vector3(0, 0, -r)); + points_primary.push_back(Vector3()); + + p_gizmo->add_lines(points_primary, material_primary, false, color); + p_gizmo->add_lines(points_secondary, material_secondary, false, color); + + Vector handles; + handles.push_back(Vector3(0, 0, -r)); + handles.push_back(Vector3(w, 0, -d)); + + p_gizmo->add_handles(handles, get_material("handles")); + p_gizmo->add_unscaled_billboard(icon, 0.05, color); + } +} + +////// + +//// player gizmo +AudioStreamPlayer3DGizmoPlugin::AudioStreamPlayer3DGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1)); + + create_icon_material("stream_player_3d_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("Gizmo3DSamplePlayer"), SNAME("EditorIcons"))); + create_material("stream_player_3d_material_primary", gizmo_color); + create_material("stream_player_3d_material_secondary", gizmo_color * Color(1, 1, 1, 0.35)); + create_handle_material("handles"); +} + +bool AudioStreamPlayer3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String AudioStreamPlayer3DGizmoPlugin::get_gizmo_name() const { + return "AudioStreamPlayer3D"; +} + +int AudioStreamPlayer3DGizmoPlugin::get_priority() const { + return -1; +} + +String AudioStreamPlayer3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { + return "Emission Radius"; +} + +Variant AudioStreamPlayer3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { + AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); + return player->get_emission_angle(); +} + +void AudioStreamPlayer3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { + AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform3D gt = player->get_global_transform(); + Transform3D gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + Vector3 ray_to = ray_from + ray_dir * 4096; + + ray_from = gi.xform(ray_from); + ray_to = gi.xform(ray_to); + + float closest_dist = 1e20; + float closest_angle = 1e20; + + for (int i = 0; i < 180; i++) { + float a = Math::deg2rad((float)i); + float an = Math::deg2rad((float)(i + 1)); + + Vector3 from(Math::sin(a), 0, -Math::cos(a)); + Vector3 to(Math::sin(an), 0, -Math::cos(an)); + + Vector3 r1, r2; + Geometry3D::get_closest_points_between_segments(from, to, ray_from, ray_to, r1, r2); + float d = r1.distance_to(r2); + if (d < closest_dist) { + closest_dist = d; + closest_angle = i; + } + } + + if (closest_angle < 91) { + player->set_emission_angle(closest_angle); + } +} + +void AudioStreamPlayer3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { + AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); + + if (p_cancel) { + player->set_emission_angle(p_restore); + + } else { + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change AudioStreamPlayer3D Emission Angle")); + ur->add_do_method(player, "set_emission_angle", player->get_emission_angle()); + ur->add_undo_method(player, "set_emission_angle", p_restore); + ur->commit_action(); + } +} + +void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + const AudioStreamPlayer3D *player = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + const Ref icon = get_material("stream_player_3d_icon", p_gizmo); + + if (player->is_emission_angle_enabled()) { + const float pc = player->get_emission_angle(); + const float ofs = -Math::cos(Math::deg2rad(pc)); + const float radius = Math::sin(Math::deg2rad(pc)); + + Vector points_primary; + points_primary.resize(200); + + real_t step = Math_TAU / 100.0; + for (int i = 0; i < 100; i++) { + const float a = i * step; + const float an = (i + 1) * step; + + const Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs); + const Vector3 to(Math::sin(an) * radius, Math::cos(an) * radius, ofs); + + points_primary.write[i * 2 + 0] = from; + points_primary.write[i * 2 + 1] = to; + } + + const Ref material_primary = get_material("stream_player_3d_material_primary", p_gizmo); + p_gizmo->add_lines(points_primary, material_primary); + + Vector points_secondary; + points_secondary.resize(16); + + for (int i = 0; i < 8; i++) { + const float a = i * (Math_TAU / 8.0); + const Vector3 from(Math::sin(a) * radius, Math::cos(a) * radius, ofs); + + points_secondary.write[i * 2 + 0] = from; + points_secondary.write[i * 2 + 1] = Vector3(); + } + + const Ref material_secondary = get_material("stream_player_3d_material_secondary", p_gizmo); + p_gizmo->add_lines(points_secondary, material_secondary); + + Vector handles; + const float ha = Math::deg2rad(player->get_emission_angle()); + handles.push_back(Vector3(Math::sin(ha), 0, -Math::cos(ha))); + p_gizmo->add_handles(handles, get_material("handles")); + } + + p_gizmo->add_unscaled_billboard(icon, 0.05); +} + +////// + +Camera3DGizmoPlugin::Camera3DGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8)); + + create_material("camera_material", gizmo_color); + create_handle_material("handles"); +} + +bool Camera3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String Camera3DGizmoPlugin::get_gizmo_name() const { + return "Camera3D"; +} + +int Camera3DGizmoPlugin::get_priority() const { + return -1; +} + +String Camera3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { + Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); + + if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { + return "FOV"; + } else { + return "Size"; + } +} + +Variant Camera3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { + Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); + + if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { + return camera->get_fov(); + } else { + return camera->get_size(); + } +} + +void Camera3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { + Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform3D gt = camera->get_global_transform(); + Transform3D gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + 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 (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { + Transform3D gt2 = camera->get_global_transform(); + float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], 1.0, gt2); + camera->set("fov", CLAMP(a * 2.0, 1, 179)); + } else { + Vector3 ra, rb; + Geometry3D::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb); + float d = ra.x * 2.0; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + d = CLAMP(d, 0.1, 16384); + + camera->set("size", d); + } +} + +void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { + Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); + + if (camera->get_projection() == Camera3D::PROJECTION_PERSPECTIVE) { + if (p_cancel) { + camera->set("fov", p_restore); + } else { + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Camera FOV")); + ur->add_do_property(camera, "fov", camera->get_fov()); + ur->add_undo_property(camera, "fov", p_restore); + ur->commit_action(); + } + + } else { + if (p_cancel) { + camera->set("size", p_restore); + } else { + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Camera Size")); + ur->add_do_property(camera, "size", camera->get_size()); + ur->add_undo_property(camera, "size", p_restore); + ur->commit_action(); + } + } +} + +void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Camera3D *camera = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + Vector handles; + + Ref material = get_material("camera_material", p_gizmo); + +#define ADD_TRIANGLE(m_a, m_b, m_c) \ + { \ + lines.push_back(m_a); \ + lines.push_back(m_b); \ + lines.push_back(m_b); \ + lines.push_back(m_c); \ + lines.push_back(m_c); \ + lines.push_back(m_a); \ + } + +#define ADD_QUAD(m_a, m_b, m_c, m_d) \ + { \ + lines.push_back(m_a); \ + lines.push_back(m_b); \ + lines.push_back(m_b); \ + lines.push_back(m_c); \ + lines.push_back(m_c); \ + lines.push_back(m_d); \ + lines.push_back(m_d); \ + lines.push_back(m_a); \ + } + + switch (camera->get_projection()) { + case Camera3D::PROJECTION_PERSPECTIVE: { + // The real FOV is halved for accurate representation + float fov = camera->get_fov() / 2.0; + + Vector3 side = Vector3(Math::sin(Math::deg2rad(fov)), 0, -Math::cos(Math::deg2rad(fov))); + Vector3 nside = side; + nside.x = -nside.x; + Vector3 up = Vector3(0, side.x, 0); + + ADD_TRIANGLE(Vector3(), side + up, side - up); + ADD_TRIANGLE(Vector3(), nside + up, nside - up); + ADD_TRIANGLE(Vector3(), side + up, nside + up); + ADD_TRIANGLE(Vector3(), side - up, nside - up); + + handles.push_back(side); + side.x *= 0.25; + nside.x *= 0.25; + Vector3 tup(0, up.y * 3 / 2, side.z); + ADD_TRIANGLE(tup, side + up, nside + up); + + } break; + case Camera3D::PROJECTION_ORTHOGONAL: { + float size = camera->get_size(); + + float hsize = size * 0.5; + Vector3 right(hsize, 0, 0); + Vector3 up(0, hsize, 0); + Vector3 back(0, 0, -1.0); + Vector3 front(0, 0, 0); + + ADD_QUAD(-up - right, -up + right, up + right, up - right); + ADD_QUAD(-up - right + back, -up + right + back, up + right + back, up - right + back); + ADD_QUAD(up + right, up + right + back, up - right + back, up - right); + ADD_QUAD(-up + right, -up + right + back, -up - right + back, -up - right); + + handles.push_back(right + back); + + right.x *= 0.25; + Vector3 tup(0, up.y * 3 / 2, back.z); + ADD_TRIANGLE(tup, right + up + back, -right + up + back); + + } break; + case Camera3D::PROJECTION_FRUSTUM: { + float hsize = camera->get_size() / 2.0; + + Vector3 side = Vector3(hsize, 0, -camera->get_near()).normalized(); + Vector3 nside = side; + nside.x = -nside.x; + Vector3 up = Vector3(0, side.x, 0); + Vector3 offset = Vector3(camera->get_frustum_offset().x, camera->get_frustum_offset().y, 0.0); + + ADD_TRIANGLE(Vector3(), side + up + offset, side - up + offset); + ADD_TRIANGLE(Vector3(), nside + up + offset, nside - up + offset); + ADD_TRIANGLE(Vector3(), side + up + offset, nside + up + offset); + ADD_TRIANGLE(Vector3(), side - up + offset, nside - up + offset); + + side.x *= 0.25; + nside.x *= 0.25; + Vector3 tup(0, up.y * 3 / 2, side.z); + ADD_TRIANGLE(tup + offset, side + up + offset, nside + up + offset); + } + } + +#undef ADD_TRIANGLE +#undef ADD_QUAD + + p_gizmo->add_lines(lines, material); + p_gizmo->add_handles(handles, get_material("handles")); + + ClippedCamera3D *clipcam = Object::cast_to(camera); + if (clipcam) { + Node3D *parent = Object::cast_to(camera->get_parent()); + if (!parent) { + return; + } + Vector3 cam_normal = -camera->get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized(); + Vector3 cam_x = camera->get_global_transform().basis.get_axis(Vector3::AXIS_X).normalized(); + Vector3 cam_y = camera->get_global_transform().basis.get_axis(Vector3::AXIS_Y).normalized(); + Vector3 cam_pos = camera->get_global_transform().origin; + Vector3 parent_pos = parent->get_global_transform().origin; + + Plane parent_plane(parent_pos, cam_normal); + Vector3 ray_from = parent_plane.project(cam_pos); + + lines.clear(); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); + + lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5); + lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); + + lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5); + lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); + + lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5); + lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5); + + if (parent_plane.distance_to(cam_pos) < 0) { + lines.push_back(ray_from); + lines.push_back(cam_pos); + } + + Transform3D local = camera->get_global_transform().affine_inverse(); + for (int i = 0; i < lines.size(); i++) { + lines.write[i] = local.xform(lines[i]); + } + + p_gizmo->add_lines(lines, material); + } +} + +////// + +MeshInstance3DGizmoPlugin::MeshInstance3DGizmoPlugin() { +} + +bool MeshInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr && Object::cast_to(p_spatial) == nullptr; +} + +String MeshInstance3DGizmoPlugin::get_gizmo_name() const { + return "MeshInstance3D"; +} + +int MeshInstance3DGizmoPlugin::get_priority() const { + return -1; +} + +bool MeshInstance3DGizmoPlugin::can_be_hidden() const { + return false; +} + +void MeshInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + MeshInstance3D *mesh = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref m = mesh->get_mesh(); + + if (!m.is_valid()) { + return; //none + } + + Ref tm = m->generate_triangle_mesh(); + if (tm.is_valid()) { + p_gizmo->add_collision_triangles(tm); + } +} + +///// + +OccluderInstance3DGizmoPlugin::OccluderInstance3DGizmoPlugin() { + create_material("line_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/occluder", Color(0.8, 0.5, 1))); +} + +bool OccluderInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String OccluderInstance3DGizmoPlugin::get_gizmo_name() const { + return "OccluderInstance3D"; +} + +int OccluderInstance3DGizmoPlugin::get_priority() const { + return -1; +} + +void OccluderInstance3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + OccluderInstance3D *occluder_instance = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref o = occluder_instance->get_occluder(); + + if (!o.is_valid()) { + return; + } + + Vector lines = o->get_debug_lines(); + if (!lines.is_empty()) { + Ref material = get_material("line_material", p_gizmo); + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + } +} + +///// + +Sprite3DGizmoPlugin::Sprite3DGizmoPlugin() { +} + +bool Sprite3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String Sprite3DGizmoPlugin::get_gizmo_name() const { + return "Sprite3D"; +} + +int Sprite3DGizmoPlugin::get_priority() const { + return -1; +} + +bool Sprite3DGizmoPlugin::can_be_hidden() const { + return false; +} + +void Sprite3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Sprite3D *sprite = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref tm = sprite->generate_triangle_mesh(); + if (tm.is_valid()) { + p_gizmo->add_collision_triangles(tm); + } +} + +/// + +Position3DGizmoPlugin::Position3DGizmoPlugin() { + pos3d_mesh = Ref(memnew(ArrayMesh)); + cursor_points = Vector(); + + Vector cursor_colors; + const float cs = 0.25; + // Add more points to create a "hard stop" in the color gradient. + cursor_points.push_back(Vector3(+cs, 0, 0)); + cursor_points.push_back(Vector3()); + cursor_points.push_back(Vector3()); + cursor_points.push_back(Vector3(-cs, 0, 0)); + + cursor_points.push_back(Vector3(0, +cs, 0)); + cursor_points.push_back(Vector3()); + cursor_points.push_back(Vector3()); + cursor_points.push_back(Vector3(0, -cs, 0)); + + cursor_points.push_back(Vector3(0, 0, +cs)); + cursor_points.push_back(Vector3()); + cursor_points.push_back(Vector3()); + cursor_points.push_back(Vector3(0, 0, -cs)); + + // Use the axis color which is brighter for the positive axis. + // Use a darkened axis color for the negative axis. + // This makes it possible to see in which direction the Position3D node is rotated + // (which can be important depending on how it's used). + const Color color_x = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("axis_x_color"), SNAME("Editor")); + cursor_colors.push_back(color_x); + cursor_colors.push_back(color_x); + // FIXME: Use less strong darkening factor once GH-48573 is fixed. + // The current darkening factor compensates for lines being too bright in the 3D editor. + cursor_colors.push_back(color_x.lerp(Color(0, 0, 0), 0.75)); + cursor_colors.push_back(color_x.lerp(Color(0, 0, 0), 0.75)); + + const Color color_y = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("axis_y_color"), SNAME("Editor")); + cursor_colors.push_back(color_y); + cursor_colors.push_back(color_y); + cursor_colors.push_back(color_y.lerp(Color(0, 0, 0), 0.75)); + cursor_colors.push_back(color_y.lerp(Color(0, 0, 0), 0.75)); + + const Color color_z = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("axis_z_color"), SNAME("Editor")); + cursor_colors.push_back(color_z); + cursor_colors.push_back(color_z); + cursor_colors.push_back(color_z.lerp(Color(0, 0, 0), 0.75)); + cursor_colors.push_back(color_z.lerp(Color(0, 0, 0), 0.75)); + + Ref mat = memnew(StandardMaterial3D); + mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + + Array d; + d.resize(RS::ARRAY_MAX); + d[Mesh::ARRAY_VERTEX] = cursor_points; + d[Mesh::ARRAY_COLOR] = cursor_colors; + pos3d_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, d); + pos3d_mesh->surface_set_material(0, mat); +} + +bool Position3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String Position3DGizmoPlugin::get_gizmo_name() const { + return "Position3D"; +} + +int Position3DGizmoPlugin::get_priority() const { + return -1; +} + +void Position3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + p_gizmo->clear(); + p_gizmo->add_mesh(pos3d_mesh); + p_gizmo->add_collision_segments(cursor_points); +} + +///// + +Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4)); + create_material("skeleton_material", gizmo_color); +} + +bool Skeleton3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String Skeleton3DGizmoPlugin::get_gizmo_name() const { + return "Skeleton3D"; +} + +int Skeleton3DGizmoPlugin::get_priority() const { + return -1; +} + +void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Skeleton3D *skel = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref material = get_material("skeleton_material", p_gizmo); + + Ref surface_tool(memnew(SurfaceTool)); + + surface_tool->begin(Mesh::PRIMITIVE_LINES); + surface_tool->set_material(material); + Vector grests; + grests.resize(skel->get_bone_count()); + + Vector bones; + Vector weights; + bones.resize(4); + weights.resize(4); + + for (int i = 0; i < 4; i++) { + bones.write[i] = 0; + weights.write[i] = 0; + } + + weights.write[0] = 1; + + AABB aabb; + + Color bonecolor = Color(1.0, 0.4, 0.4, 0.3); + Color rootcolor = Color(0.4, 1.0, 0.4, 0.1); + + for (int i_bone = 0; i_bone < skel->get_bone_count(); i_bone++) { + int i = skel->get_process_order(i_bone); + + int parent = skel->get_bone_parent(i); + + if (parent >= 0) { + grests.write[i] = grests[parent] * skel->get_bone_rest(i); + + Vector3 v0 = grests[parent].origin; + Vector3 v1 = grests[i].origin; + Vector3 d = (v1 - v0).normalized(); + float dist = v0.distance_to(v1); + + //find closest axis + int closest = -1; + float closest_d = 0.0; + + for (int j = 0; j < 3; j++) { + float dp = Math::abs(grests[parent].basis[j].normalized().dot(d)); + if (j == 0 || dp > closest_d) { + closest = j; + } + } + + //find closest other + Vector3 first; + Vector3 points[4]; + int pointidx = 0; + for (int j = 0; j < 3; j++) { + bones.write[0] = parent; + surface_tool->set_bones(bones); + surface_tool->set_weights(weights); + surface_tool->set_color(rootcolor); + surface_tool->add_vertex(v0 - grests[parent].basis[j].normalized() * dist * 0.05); + surface_tool->set_bones(bones); + surface_tool->set_weights(weights); + surface_tool->set_color(rootcolor); + surface_tool->add_vertex(v0 + grests[parent].basis[j].normalized() * dist * 0.05); + + if (j == closest) { + continue; + } + + Vector3 axis; + if (first == Vector3()) { + axis = d.cross(d.cross(grests[parent].basis[j])).normalized(); + first = axis; + } else { + axis = d.cross(first).normalized(); + } + + for (int k = 0; k < 2; k++) { + if (k == 1) { + axis = -axis; + } + Vector3 point = v0 + d * dist * 0.2; + point += axis * dist * 0.1; + + bones.write[0] = parent; + surface_tool->set_bones(bones); + surface_tool->set_weights(weights); + surface_tool->set_color(bonecolor); + surface_tool->add_vertex(v0); + surface_tool->set_bones(bones); + surface_tool->set_weights(weights); + surface_tool->set_color(bonecolor); + surface_tool->add_vertex(point); + + bones.write[0] = parent; + surface_tool->set_bones(bones); + surface_tool->set_weights(weights); + surface_tool->set_color(bonecolor); + surface_tool->add_vertex(point); + bones.write[0] = i; + surface_tool->set_bones(bones); + surface_tool->set_weights(weights); + surface_tool->set_color(bonecolor); + surface_tool->add_vertex(v1); + points[pointidx++] = point; + } + } + + SWAP(points[1], points[2]); + for (int j = 0; j < 4; j++) { + bones.write[0] = parent; + surface_tool->set_bones(bones); + surface_tool->set_weights(weights); + surface_tool->set_color(bonecolor); + surface_tool->add_vertex(points[j]); + surface_tool->set_bones(bones); + surface_tool->set_weights(weights); + surface_tool->set_color(bonecolor); + surface_tool->add_vertex(points[(j + 1) % 4]); + } + } else { + grests.write[i] = skel->get_bone_rest(i); + bones.write[0] = i; + } + } + + Ref m = surface_tool->commit(); + p_gizmo->add_mesh(m, Ref(), Transform3D(), skel->register_skin(Ref())); +} + +//// + +PhysicalBone3DGizmoPlugin::PhysicalBone3DGizmoPlugin() { + create_material("joint_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1))); +} + +bool PhysicalBone3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String PhysicalBone3DGizmoPlugin::get_gizmo_name() const { + return "PhysicalBone3D"; +} + +int PhysicalBone3DGizmoPlugin::get_priority() const { + return -1; +} + +void PhysicalBone3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + p_gizmo->clear(); + + PhysicalBone3D *physical_bone = Object::cast_to(p_gizmo->get_spatial_node()); + + if (!physical_bone) { + return; + } + + Skeleton3D *sk(physical_bone->find_skeleton_parent()); + if (!sk) { + return; + } + + PhysicalBone3D *pb(sk->get_physical_bone(physical_bone->get_bone_id())); + if (!pb) { + return; + } + + PhysicalBone3D *pbp(sk->get_physical_bone_parent(physical_bone->get_bone_id())); + if (!pbp) { + return; + } + + Vector points; + + switch (physical_bone->get_joint_type()) { + case PhysicalBone3D::JOINT_TYPE_PIN: { + Joint3DGizmoPlugin::CreatePinJointGizmo(physical_bone->get_joint_offset(), points); + } break; + case PhysicalBone3D::JOINT_TYPE_CONE: { + const PhysicalBone3D::ConeJointData *cjd(static_cast(physical_bone->get_joint_data())); + Joint3DGizmoPlugin::CreateConeTwistJointGizmo( + physical_bone->get_joint_offset(), + physical_bone->get_global_transform() * physical_bone->get_joint_offset(), + pb->get_global_transform(), + pbp->get_global_transform(), + cjd->swing_span, + cjd->twist_span, + &points, + &points); + } break; + case PhysicalBone3D::JOINT_TYPE_HINGE: { + const PhysicalBone3D::HingeJointData *hjd(static_cast(physical_bone->get_joint_data())); + Joint3DGizmoPlugin::CreateHingeJointGizmo( + physical_bone->get_joint_offset(), + physical_bone->get_global_transform() * physical_bone->get_joint_offset(), + pb->get_global_transform(), + pbp->get_global_transform(), + hjd->angular_limit_lower, + hjd->angular_limit_upper, + hjd->angular_limit_enabled, + points, + &points, + &points); + } break; + case PhysicalBone3D::JOINT_TYPE_SLIDER: { + const PhysicalBone3D::SliderJointData *sjd(static_cast(physical_bone->get_joint_data())); + Joint3DGizmoPlugin::CreateSliderJointGizmo( + physical_bone->get_joint_offset(), + physical_bone->get_global_transform() * physical_bone->get_joint_offset(), + pb->get_global_transform(), + pbp->get_global_transform(), + sjd->angular_limit_lower, + sjd->angular_limit_upper, + sjd->linear_limit_lower, + sjd->linear_limit_upper, + points, + &points, + &points); + } break; + case PhysicalBone3D::JOINT_TYPE_6DOF: { + const PhysicalBone3D::SixDOFJointData *sdofjd(static_cast(physical_bone->get_joint_data())); + Joint3DGizmoPlugin::CreateGeneric6DOFJointGizmo( + physical_bone->get_joint_offset(), + + physical_bone->get_global_transform() * physical_bone->get_joint_offset(), + pb->get_global_transform(), + pbp->get_global_transform(), + + sdofjd->axis_data[0].angular_limit_lower, + sdofjd->axis_data[0].angular_limit_upper, + sdofjd->axis_data[0].linear_limit_lower, + sdofjd->axis_data[0].linear_limit_upper, + sdofjd->axis_data[0].angular_limit_enabled, + sdofjd->axis_data[0].linear_limit_enabled, + + sdofjd->axis_data[1].angular_limit_lower, + sdofjd->axis_data[1].angular_limit_upper, + sdofjd->axis_data[1].linear_limit_lower, + sdofjd->axis_data[1].linear_limit_upper, + sdofjd->axis_data[1].angular_limit_enabled, + sdofjd->axis_data[1].linear_limit_enabled, + + sdofjd->axis_data[2].angular_limit_lower, + sdofjd->axis_data[2].angular_limit_upper, + sdofjd->axis_data[2].linear_limit_lower, + sdofjd->axis_data[2].linear_limit_upper, + sdofjd->axis_data[2].angular_limit_enabled, + sdofjd->axis_data[2].linear_limit_enabled, + + points, + &points, + &points); + } break; + default: + return; + } + + Ref material = get_material("joint_material", p_gizmo); + + p_gizmo->add_collision_segments(points); + p_gizmo->add_lines(points, material); +} + +///// + +RayCast3DGizmoPlugin::RayCast3DGizmoPlugin() { + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + const float gizmo_value = gizmo_color.get_v(); + const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); + create_material("shape_material_disabled", gizmo_color_disabled); +} + +bool RayCast3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String RayCast3DGizmoPlugin::get_gizmo_name() const { + return "RayCast3D"; +} + +int RayCast3DGizmoPlugin::get_priority() const { + return -1; +} + +void RayCast3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + RayCast3D *raycast = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + const Ref material = raycast->is_enabled() ? raycast->get_debug_material() : get_material("shape_material_disabled"); + + p_gizmo->add_lines(raycast->get_debug_line_vertices(), material); + + if (raycast->get_debug_shape_thickness() > 1) { + p_gizmo->add_vertices(raycast->get_debug_shape_vertices(), material, Mesh::PRIMITIVE_TRIANGLE_STRIP); + } + + p_gizmo->add_collision_segments(raycast->get_debug_line_vertices()); +} + +///// + +void SpringArm3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + SpringArm3D *spring_arm = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + + lines.push_back(Vector3()); + lines.push_back(Vector3(0, 0, 1.0) * spring_arm->get_length()); + + Ref material = get_material("shape_material", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); +} + +SpringArm3DGizmoPlugin::SpringArm3DGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); +} + +bool SpringArm3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String SpringArm3DGizmoPlugin::get_gizmo_name() const { + return "SpringArm3D"; +} + +int SpringArm3DGizmoPlugin::get_priority() const { + return -1; +} + +///// + +VehicleWheel3DGizmoPlugin::VehicleWheel3DGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); +} + +bool VehicleWheel3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String VehicleWheel3DGizmoPlugin::get_gizmo_name() const { + return "VehicleWheel3D"; +} + +int VehicleWheel3DGizmoPlugin::get_priority() const { + return -1; +} + +void VehicleWheel3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + VehicleWheel3D *car_wheel = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector points; + + float r = car_wheel->get_radius(); + const int skip = 10; + for (int i = 0; i <= 360; i += skip) { + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + skip); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + points.push_back(Vector3(0, a.x, a.y)); + points.push_back(Vector3(0, b.x, b.y)); + + const int springsec = 4; + + for (int j = 0; j < springsec; j++) { + float t = car_wheel->get_suspension_rest_length() * 5; + points.push_back(Vector3(a.x, i / 360.0 * t / springsec + j * (t / springsec), a.y) * 0.2); + points.push_back(Vector3(b.x, (i + skip) / 360.0 * t / springsec + j * (t / springsec), b.y) * 0.2); + } + } + + //travel + points.push_back(Vector3(0, 0, 0)); + points.push_back(Vector3(0, car_wheel->get_suspension_rest_length(), 0)); + + //axis + points.push_back(Vector3(r * 0.2, car_wheel->get_suspension_rest_length(), 0)); + points.push_back(Vector3(-r * 0.2, car_wheel->get_suspension_rest_length(), 0)); + //axis + points.push_back(Vector3(r * 0.2, 0, 0)); + points.push_back(Vector3(-r * 0.2, 0, 0)); + + //forward line + points.push_back(Vector3(0, -r, 0)); + points.push_back(Vector3(0, -r, r * 2)); + points.push_back(Vector3(0, -r, r * 2)); + points.push_back(Vector3(r * 2 * 0.2, -r, r * 2 * 0.8)); + points.push_back(Vector3(0, -r, r * 2)); + points.push_back(Vector3(-r * 2 * 0.2, -r, r * 2 * 0.8)); + + Ref material = get_material("shape_material", p_gizmo); + + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(points); +} + +/////////// + +SoftBody3DGizmoPlugin::SoftBody3DGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + create_handle_material("handles"); +} + +bool SoftBody3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String SoftBody3DGizmoPlugin::get_gizmo_name() const { + return "SoftBody3D"; +} + +int SoftBody3DGizmoPlugin::get_priority() const { + return -1; +} + +bool SoftBody3DGizmoPlugin::is_selectable_when_hidden() const { + return true; +} + +void SoftBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + if (!soft_body || soft_body->get_mesh().is_null()) { + return; + } + + // find mesh + + Vector lines; + + soft_body->get_mesh()->generate_debug_mesh_lines(lines); + + if (!lines.size()) { + return; + } + + Ref tm = soft_body->get_mesh()->generate_triangle_mesh(); + + Vector points; + for (int i = 0; i < soft_body->get_mesh()->get_surface_count(); i++) { + Array arrays = soft_body->get_mesh()->surface_get_arrays(i); + ERR_CONTINUE(arrays.is_empty()); + + const Vector &vertices = arrays[Mesh::ARRAY_VERTEX]; + points.append_array(vertices); + } + + Ref material = get_material("shape_material", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_handles(points, get_material("handles")); + p_gizmo->add_collision_triangles(tm); +} + +String SoftBody3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { + return "SoftBody3D pin point"; +} + +Variant SoftBody3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { + SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); + return Variant(soft_body->is_point_pinned(p_id)); +} + +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(p_gizmo->get_spatial_node()); + soft_body->pin_point_toggle(p_id); +} + +bool SoftBody3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const { + SoftBody3D *soft_body = Object::cast_to(p_gizmo->get_spatial_node()); + return soft_body->is_point_pinned(p_id); +} + +/////////// + +VisibleOnScreenNotifier3DGizmoPlugin::VisibleOnScreenNotifier3DGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/visibility_notifier", Color(0.8, 0.5, 0.7)); + create_material("visibility_notifier_material", gizmo_color); + gizmo_color.a = 0.1; + create_material("visibility_notifier_solid_material", gizmo_color); + create_handle_material("handles"); +} + +bool VisibleOnScreenNotifier3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String VisibleOnScreenNotifier3DGizmoPlugin::get_gizmo_name() const { + return "VisibleOnScreenNotifier3D"; +} + +int VisibleOnScreenNotifier3DGizmoPlugin::get_priority() const { + return -1; +} + +String VisibleOnScreenNotifier3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { + switch (p_id) { + case 0: + return "Size X"; + case 1: + return "Size Y"; + case 2: + return "Size Z"; + case 3: + return "Pos X"; + case 4: + return "Pos Y"; + case 5: + return "Pos Z"; + } + + return ""; +} + +Variant VisibleOnScreenNotifier3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { + VisibleOnScreenNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); + return notifier->get_aabb(); +} + +void VisibleOnScreenNotifier3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { + VisibleOnScreenNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform3D gt = notifier->get_global_transform(); + + Transform3D gi = gt.affine_inverse(); + + 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); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + + Vector3 ofs = aabb.position + aabb.size * 0.5; + + Vector3 axis; + 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_id]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + 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_id] - ofs[p_id]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + //resize + 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(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { + VisibleOnScreenNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); + + if (p_cancel) { + notifier->set_aabb(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Notifier AABB")); + ur->add_do_method(notifier, "set_aabb", notifier->get_aabb()); + ur->add_undo_method(notifier, "set_aabb", p_restore); + ur->commit_action(); +} + +void VisibleOnScreenNotifier3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + VisibleOnScreenNotifier3D *notifier = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + AABB aabb = notifier->get_aabb(); + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + Vector handles; + + for (int i = 0; i < 3; i++) { + Vector3 ax; + ax[i] = aabb.position[i] + aabb.size[i]; + ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5; + ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5; + handles.push_back(ax); + } + + Vector3 center = aabb.position + aabb.size * 0.5; + for (int i = 0; i < 3; i++) { + Vector3 ax; + ax[i] = 1.0; + handles.push_back(center + ax); + lines.push_back(center); + lines.push_back(center + ax); + } + + Ref material = get_material("visibility_notifier_material", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + + if (p_gizmo->is_selected()) { + Ref solid_material = get_material("visibility_notifier_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); + } + + p_gizmo->add_handles(handles, get_material("handles")); +} + +//// + +CPUParticles3DGizmoPlugin::CPUParticles3DGizmoPlugin() { + create_icon_material("particles_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoCPUParticles3D"), SNAME("EditorIcons"))); +} + +bool CPUParticles3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String CPUParticles3DGizmoPlugin::get_gizmo_name() const { + return "CPUParticles3D"; +} + +int CPUParticles3DGizmoPlugin::get_priority() const { + return -1; +} + +bool CPUParticles3DGizmoPlugin::is_selectable_when_hidden() const { + return true; +} + +void CPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Ref icon = get_material("particles_icon", p_gizmo); + p_gizmo->add_unscaled_billboard(icon, 0.05); +} + +//// + +GPUParticles3DGizmoPlugin::GPUParticles3DGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4)); + create_material("particles_material", gizmo_color); + gizmo_color.a = 0.1; + create_material("particles_solid_material", gizmo_color); + create_icon_material("particles_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoGPUParticles3D"), SNAME("EditorIcons"))); + create_handle_material("handles"); +} + +bool GPUParticles3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String GPUParticles3DGizmoPlugin::get_gizmo_name() const { + return "GPUParticles3D"; +} + +int GPUParticles3DGizmoPlugin::get_priority() const { + return -1; +} + +bool GPUParticles3DGizmoPlugin::is_selectable_when_hidden() const { + return true; +} + +String GPUParticles3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { + switch (p_id) { + case 0: + return "Size X"; + case 1: + return "Size Y"; + case 2: + return "Size Z"; + case 3: + return "Pos X"; + case 4: + return "Pos Y"; + case 5: + return "Pos Z"; + } + + return ""; +} + +Variant GPUParticles3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { + GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); + return particles->get_visibility_aabb(); +} + +void GPUParticles3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { + GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform3D gt = particles->get_global_transform(); + Transform3D gi = gt.affine_inverse(); + + 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); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + + Vector3 ofs = aabb.position + aabb.size * 0.5; + + Vector3 axis; + 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_id]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + 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_id] - ofs[p_id]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + //resize + 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(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { + GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); + + if (p_cancel) { + particles->set_visibility_aabb(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Particles AABB")); + ur->add_do_method(particles, "set_visibility_aabb", particles->get_visibility_aabb()); + ur->add_undo_method(particles, "set_visibility_aabb", p_restore); + ur->commit_action(); +} + +void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + GPUParticles3D *particles = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + AABB aabb = particles->get_visibility_aabb(); + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + Vector handles; + + for (int i = 0; i < 3; i++) { + Vector3 ax; + ax[i] = aabb.position[i] + aabb.size[i]; + ax[(i + 1) % 3] = aabb.position[(i + 1) % 3] + aabb.size[(i + 1) % 3] * 0.5; + ax[(i + 2) % 3] = aabb.position[(i + 2) % 3] + aabb.size[(i + 2) % 3] * 0.5; + handles.push_back(ax); + } + + Vector3 center = aabb.position + aabb.size * 0.5; + for (int i = 0; i < 3; i++) { + Vector3 ax; + ax[i] = 1.0; + handles.push_back(center + ax); + lines.push_back(center); + lines.push_back(center + ax); + } + + Ref material = get_material("particles_material", p_gizmo); + Ref icon = get_material("particles_icon", p_gizmo); + + p_gizmo->add_lines(lines, material); + + if (p_gizmo->is_selected()) { + Ref solid_material = get_material("particles_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0); + } + + p_gizmo->add_handles(handles, get_material("handles")); + p_gizmo->add_unscaled_billboard(icon, 0.05); +} + +//// + +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); + gizmo_color.a = 0.15; + create_material("shape_material_internal", gizmo_color); + + create_handle_material("handles"); +} + +bool GPUParticlesCollision3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return (Object::cast_to(p_spatial) != nullptr) || (Object::cast_to(p_spatial) != nullptr); +} + +String GPUParticlesCollision3DGizmoPlugin::get_gizmo_name() const { + return "GPUParticlesCollision3D"; +} + +int GPUParticlesCollision3DGizmoPlugin::get_priority() const { + return -1; +} + +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(cs) || Object::cast_to(cs)) { + return "Radius"; + } + + if (Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs)) { + return "Extents"; + } + + return ""; +} + +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(cs) || Object::cast_to(cs)) { + return p_gizmo->get_spatial_node()->call("get_radius"); + } + + if (Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs)) { + return Vector3(p_gizmo->get_spatial_node()->call("get_extents")); + } + + return Variant(); +} + +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(); + Transform3D gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + + if (Object::cast_to(sn) || Object::cast_to(sn)) { + Vector3 ra, rb; + Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); + float d = ra.x; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + + sn->call("set_radius", d); + } + + if (Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn)) { + Vector3 axis; + 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_id]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + + Vector3 he = sn->call("get_extents"); + he[p_id] = d; + sn->call("set_extents", he); + } +} + +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(sn) || Object::cast_to(sn)) { + if (p_cancel) { + sn->call("set_radius", p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Radius")); + ur->add_do_method(sn, "set_radius", sn->call("get_radius")); + ur->add_undo_method(sn, "set_radius", p_restore); + ur->commit_action(); + } + + if (Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn) || Object::cast_to(sn)) { + if (p_cancel) { + sn->call("set_extents", p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Box Shape Extents")); + ur->add_do_method(sn, "set_extents", sn->call("get_extents")); + ur->add_undo_method(sn, "set_extents", p_restore); + ur->commit_action(); + } +} + +void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Node3D *cs = p_gizmo->get_spatial_node(); + + print_line("redraw request " + itos(cs != nullptr)); + p_gizmo->clear(); + + const Ref material = + get_material("shape_material", p_gizmo); + const Ref material_internal = + get_material("shape_material_internal", p_gizmo); + + Ref handles_material = get_material("handles"); + + if (Object::cast_to(cs) || Object::cast_to(cs)) { + float r = cs->call("get_radius"); + + Vector points; + + for (int i = 0; i <= 360; i++) { + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 1); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + points.push_back(Vector3(a.x, 0, a.y)); + points.push_back(Vector3(b.x, 0, b.y)); + points.push_back(Vector3(0, a.x, a.y)); + points.push_back(Vector3(0, b.x, b.y)); + points.push_back(Vector3(a.x, a.y, 0)); + points.push_back(Vector3(b.x, b.y, 0)); + } + + Vector collision_segments; + + for (int i = 0; i < 64; i++) { + float ra = i * (Math_TAU / 64.0); + float rb = (i + 1) * (Math_TAU / 64.0); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + collision_segments.push_back(Vector3(a.x, 0, a.y)); + collision_segments.push_back(Vector3(b.x, 0, b.y)); + collision_segments.push_back(Vector3(0, a.x, a.y)); + collision_segments.push_back(Vector3(0, b.x, b.y)); + collision_segments.push_back(Vector3(a.x, a.y, 0)); + collision_segments.push_back(Vector3(b.x, b.y, 0)); + } + + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(collision_segments); + Vector handles; + handles.push_back(Vector3(r, 0, 0)); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs) || Object::cast_to(cs)) { + Vector lines; + AABB aabb; + aabb.position = -cs->call("get_extents").operator Vector3(); + aabb.size = aabb.position * -2; + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + Vector handles; + + for (int i = 0; i < 3; i++) { + Vector3 ax; + ax[i] = cs->call("get_extents").operator Vector3()[i]; + handles.push_back(ax); + } + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + p_gizmo->add_handles(handles, handles_material); + + GPUParticlesCollisionSDF *col_sdf = Object::cast_to(cs); + if (col_sdf) { + static const int subdivs[GPUParticlesCollisionSDF::RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 }; + int subdiv = subdivs[col_sdf->get_resolution()]; + float cell_size = aabb.get_longest_axis_size() / subdiv; + + lines.clear(); + + for (int i = 1; i < subdiv; i++) { + for (int j = 0; j < 3; j++) { + if (cell_size * i > aabb.size[j]) { + continue; + } + + Vector2 dir; + dir[j] = 1.0; + Vector2 ta, tb; + int j_n1 = (j + 1) % 3; + int j_n2 = (j + 2) % 3; + ta[j_n1] = 1.0; + tb[j_n2] = 1.0; + + for (int k = 0; k < 4; k++) { + Vector3 from = aabb.position, to = aabb.position; + from[j] += cell_size * i; + to[j] += cell_size * i; + + if (k & 1) { + to[j_n1] += aabb.size[j_n1]; + } else { + to[j_n2] += aabb.size[j_n2]; + } + + if (k & 2) { + from[j_n1] += aabb.size[j_n1]; + from[j_n2] += aabb.size[j_n2]; + } + + lines.push_back(from); + lines.push_back(to); + } + } + } + + p_gizmo->add_lines(lines, material_internal); + } + } +} + +///// + +//// + +ReflectionProbeGizmoPlugin::ReflectionProbeGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/reflection_probe", Color(0.6, 1, 0.5)); + + create_material("reflection_probe_material", gizmo_color); + + gizmo_color.a = 0.5; + create_material("reflection_internal_material", gizmo_color); + + gizmo_color.a = 0.1; + create_material("reflection_probe_solid_material", gizmo_color); + + create_icon_material("reflection_probe_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoReflectionProbe"), SNAME("EditorIcons"))); + create_handle_material("handles"); +} + +bool ReflectionProbeGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String ReflectionProbeGizmoPlugin::get_gizmo_name() const { + return "ReflectionProbe"; +} + +int ReflectionProbeGizmoPlugin::get_priority() const { + return -1; +} + +String ReflectionProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { + switch (p_id) { + case 0: + return "Extents X"; + case 1: + return "Extents Y"; + case 2: + return "Extents Z"; + case 3: + return "Origin X"; + case 4: + return "Origin Y"; + case 5: + return "Origin Z"; + } + + return ""; +} + +Variant ReflectionProbeGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { + ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + return AABB(probe->get_extents(), probe->get_origin_offset()); +} + +void ReflectionProbeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { + ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + Transform3D gt = probe->get_global_transform(); + + Transform3D gi = gt.affine_inverse(); + + if (p_id < 3) { + Vector3 extents = probe->get_extents(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; + + Vector3 axis; + 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_id]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + + extents[p_id] = d; + probe->set_extents(extents); + } else { + p_id -= 3; + + Vector3 origin = probe->get_origin_offset(); + origin[p_id] = 0; + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; + + Vector3 axis; + 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_id] + 0.25; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + origin[p_id] = d; + probe->set_origin_offset(origin); + } +} + +void ReflectionProbeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { + ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + + AABB restore = p_restore; + + if (p_cancel) { + probe->set_extents(restore.position); + probe->set_origin_offset(restore.size); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Probe Extents")); + ur->add_do_method(probe, "set_extents", probe->get_extents()); + ur->add_do_method(probe, "set_origin_offset", probe->get_origin_offset()); + ur->add_undo_method(probe, "set_extents", restore.position); + ur->add_undo_method(probe, "set_origin_offset", restore.size); + ur->commit_action(); +} + +void ReflectionProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + ReflectionProbe *probe = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + Vector internal_lines; + Vector3 extents = probe->get_extents(); + + AABB aabb; + aabb.position = -extents; + aabb.size = extents * 2; + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + for (int i = 0; i < 8; i++) { + Vector3 ep = aabb.get_endpoint(i); + internal_lines.push_back(probe->get_origin_offset()); + internal_lines.push_back(ep); + } + + Vector handles; + + for (int i = 0; i < 3; i++) { + Vector3 ax; + ax[i] = aabb.position[i] + aabb.size[i]; + handles.push_back(ax); + } + + for (int i = 0; i < 3; i++) { + Vector3 orig_handle = probe->get_origin_offset(); + orig_handle[i] -= 0.25; + lines.push_back(orig_handle); + handles.push_back(orig_handle); + + orig_handle[i] += 0.5; + lines.push_back(orig_handle); + } + + Ref material = get_material("reflection_probe_material", p_gizmo); + Ref material_internal = get_material("reflection_internal_material", p_gizmo); + Ref icon = get_material("reflection_probe_icon", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_lines(internal_lines, material_internal); + + if (p_gizmo->is_selected()) { + Ref solid_material = get_material("reflection_probe_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, probe->get_extents() * 2.0); + } + + p_gizmo->add_unscaled_billboard(icon, 0.05); + p_gizmo->add_handles(handles, get_material("handles")); +} + +/////////////////////////////// + +//// + +DecalGizmoPlugin::DecalGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/decal", Color(0.6, 0.5, 1.0)); + + create_material("decal_material", gizmo_color); + + create_handle_material("handles"); +} + +bool DecalGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String DecalGizmoPlugin::get_gizmo_name() const { + return "Decal"; +} + +int DecalGizmoPlugin::get_priority() const { + return -1; +} + +String DecalGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { + switch (p_id) { + case 0: + return "Extents X"; + case 1: + return "Extents Y"; + case 2: + return "Extents Z"; + } + + return ""; +} + +Variant DecalGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { + Decal *decal = Object::cast_to(p_gizmo->get_spatial_node()); + return decal->get_extents(); +} + +void DecalGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { + Decal *decal = Object::cast_to(p_gizmo->get_spatial_node()); + Transform3D gt = decal->get_global_transform(); + + Transform3D gi = gt.affine_inverse(); + + Vector3 extents = decal->get_extents(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; + + Vector3 axis; + 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_id]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + + extents[p_id] = d; + decal->set_extents(extents); +} + +void DecalGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { + Decal *decal = Object::cast_to(p_gizmo->get_spatial_node()); + + Vector3 restore = p_restore; + + if (p_cancel) { + decal->set_extents(restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Decal Extents")); + ur->add_do_method(decal, "set_extents", decal->get_extents()); + ur->add_undo_method(decal, "set_extents", restore); + ur->commit_action(); +} + +void DecalGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Decal *decal = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector lines; + Vector3 extents = decal->get_extents(); + + AABB aabb; + aabb.position = -extents; + aabb.size = extents * 2; + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + if (a.y == b.y) { + lines.push_back(a); + lines.push_back(b); + } else { + Vector3 ah = a.lerp(b, 0.2); + lines.push_back(a); + lines.push_back(ah); + Vector3 bh = b.lerp(a, 0.2); + lines.push_back(b); + lines.push_back(bh); + } + } + + lines.push_back(Vector3(0, extents.y, 0)); + lines.push_back(Vector3(0, extents.y * 1.2, 0)); + + Vector handles; + + for (int i = 0; i < 3; i++) { + Vector3 ax; + ax[i] = aabb.position[i] + aabb.size[i]; + handles.push_back(ax); + } + + Ref material = get_material("decal_material", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_handles(handles, get_material("handles")); +} + +/////////////////////////////// +VoxelGIGizmoPlugin::VoxelGIGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/voxel_gi", Color(0.5, 1, 0.6)); + + create_material("voxel_gi_material", gizmo_color); + + // This gizmo draws a lot of lines. Use a low opacity to make it not too intrusive. + gizmo_color.a = 0.1; + create_material("voxel_gi_internal_material", gizmo_color); + + gizmo_color.a = 0.05; + create_material("voxel_gi_solid_material", gizmo_color); + + create_icon_material("voxel_gi_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoVoxelGI"), SNAME("EditorIcons"))); + create_handle_material("handles"); +} + +bool VoxelGIGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String VoxelGIGizmoPlugin::get_gizmo_name() const { + return "VoxelGI"; +} + +int VoxelGIGizmoPlugin::get_priority() const { + return -1; +} + +String VoxelGIGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { + switch (p_id) { + case 0: + return "Extents X"; + case 1: + return "Extents Y"; + case 2: + return "Extents Z"; + } + + return ""; +} + +Variant VoxelGIGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { + VoxelGI *probe = Object::cast_to(p_gizmo->get_spatial_node()); + return probe->get_extents(); +} + +void VoxelGIGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { + VoxelGI *probe = Object::cast_to(p_gizmo->get_spatial_node()); + + Transform3D gt = probe->get_global_transform(); + Transform3D gi = gt.affine_inverse(); + + Vector3 extents = probe->get_extents(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 16384) }; + + Vector3 axis; + 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_id]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + + extents[p_id] = d; + probe->set_extents(extents); +} + +void VoxelGIGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { + VoxelGI *probe = Object::cast_to(p_gizmo->get_spatial_node()); + + Vector3 restore = p_restore; + + if (p_cancel) { + probe->set_extents(restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Probe Extents")); + ur->add_do_method(probe, "set_extents", probe->get_extents()); + ur->add_undo_method(probe, "set_extents", restore); + ur->commit_action(); +} + +void VoxelGIGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + VoxelGI *probe = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref material = get_material("voxel_gi_material", p_gizmo); + Ref icon = get_material("voxel_gi_icon", p_gizmo); + Ref material_internal = get_material("voxel_gi_internal_material", p_gizmo); + + p_gizmo->clear(); + + Vector lines; + Vector3 extents = probe->get_extents(); + + static const int subdivs[VoxelGI::SUBDIV_MAX] = { 64, 128, 256, 512 }; + + AABB aabb = AABB(-extents, extents * 2); + int subdiv = subdivs[probe->get_subdiv()]; + float cell_size = aabb.get_longest_axis_size() / subdiv; + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + p_gizmo->add_lines(lines, material); + + lines.clear(); + + for (int i = 1; i < subdiv; i++) { + for (int j = 0; j < 3; j++) { + if (cell_size * i > aabb.size[j]) { + continue; + } + + Vector2 dir; + dir[j] = 1.0; + Vector2 ta, tb; + int j_n1 = (j + 1) % 3; + int j_n2 = (j + 2) % 3; + ta[j_n1] = 1.0; + tb[j_n2] = 1.0; + + for (int k = 0; k < 4; k++) { + Vector3 from = aabb.position, to = aabb.position; + from[j] += cell_size * i; + to[j] += cell_size * i; + + if (k & 1) { + to[j_n1] += aabb.size[j_n1]; + } else { + to[j_n2] += aabb.size[j_n2]; + } + + if (k & 2) { + from[j_n1] += aabb.size[j_n1]; + from[j_n2] += aabb.size[j_n2]; + } + + lines.push_back(from); + lines.push_back(to); + } + } + } + + p_gizmo->add_lines(lines, material_internal); + + Vector handles; + + for (int i = 0; i < 3; i++) { + Vector3 ax; + ax[i] = aabb.position[i] + aabb.size[i]; + handles.push_back(ax); + } + + if (p_gizmo->is_selected()) { + Ref solid_material = get_material("voxel_gi_solid_material", p_gizmo); + p_gizmo->add_solid_box(solid_material, aabb.get_size()); + } + + p_gizmo->add_unscaled_billboard(icon, 0.05); + p_gizmo->add_handles(handles, get_material("handles")); +} + +//// + +LightmapGIGizmoPlugin::LightmapGIGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/lightmap_lines", Color(0.5, 0.6, 1)); + + gizmo_color.a = 0.1; + create_material("lightmap_lines", gizmo_color); + + Ref mat = memnew(StandardMaterial3D); + mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + mat->set_cull_mode(StandardMaterial3D::CULL_DISABLED); + mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, false); + + add_material("lightmap_probe_material", mat); + + 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_id) const { + return ""; +} + +Variant LightmapGIGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { + return Variant(); +} + +void LightmapGIGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +} + +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) { + return Object::cast_to(p_spatial) != nullptr; +} + +String LightmapGIGizmoPlugin::get_gizmo_name() const { + return "LightmapGI"; +} + +int LightmapGIGizmoPlugin::get_priority() const { + return -1; +} + +void LightmapGIGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Ref icon = get_material("baked_indirect_light_icon", p_gizmo); + LightmapGI *baker = Object::cast_to(p_gizmo->get_spatial_node()); + Ref data = baker->get_light_data(); + + p_gizmo->add_unscaled_billboard(icon, 0.05); + + if (data.is_null()) { + return; + } + + Ref material_lines = get_material("lightmap_lines", p_gizmo); + Ref material_probes = get_material("lightmap_probe_material", p_gizmo); + + p_gizmo->clear(); + + Vector lines; + Set lines_found; + + Vector points = data->get_capture_points(); + if (points.size() == 0) { + return; + } + Vector sh = data->get_capture_sh(); + if (sh.size() != points.size() * 9) { + return; + } + + Vector tetrahedrons = data->get_capture_tetrahedra(); + + for (int i = 0; i < tetrahedrons.size(); i += 4) { + for (int j = 0; j < 4; j++) { + for (int k = j + 1; k < 4; k++) { + Vector2i pair; + pair.x = tetrahedrons[i + j]; + pair.y = tetrahedrons[i + k]; + + if (pair.y < pair.x) { + SWAP(pair.x, pair.y); + } + if (lines_found.has(pair)) { + continue; + } + lines_found.insert(pair); + lines.push_back(points[pair.x]); + lines.push_back(points[pair.y]); + } + } + } + + p_gizmo->add_lines(lines, material_lines); + + int stack_count = 8; + int sector_count = 16; + + float sector_step = (Math_PI * 2.0) / sector_count; + float stack_step = Math_PI / stack_count; + + Vector vertices; + Vector colors; + Vector indices; + float radius = 0.3; + + for (int p = 0; p < points.size(); p++) { + int vertex_base = vertices.size(); + Vector3 sh_col[9]; + for (int i = 0; i < 9; i++) { + sh_col[i].x = sh[p * 9 + i].r; + sh_col[i].y = sh[p * 9 + i].g; + sh_col[i].z = sh[p * 9 + i].b; + } + + for (int i = 0; i <= stack_count; ++i) { + float stack_angle = Math_PI / 2 - i * stack_step; // starting from pi/2 to -pi/2 + float xy = radius * Math::cos(stack_angle); // r * cos(u) + float z = radius * Math::sin(stack_angle); // r * sin(u) + + // add (sector_count+1) vertices per stack + // the first and last vertices have same position and normal, but different tex coords + for (int j = 0; j <= sector_count; ++j) { + float sector_angle = j * sector_step; // starting from 0 to 2pi + + // vertex position (x, y, z) + float x = xy * Math::cos(sector_angle); // r * cos(u) * cos(v) + float y = xy * Math::sin(sector_angle); // r * cos(u) * sin(v) + + Vector3 n = Vector3(x, z, y); + vertices.push_back(points[p] + n); + n.normalize(); + + const float c1 = 0.429043; + const float c2 = 0.511664; + const float c3 = 0.743125; + const float c4 = 0.886227; + const float c5 = 0.247708; + Vector3 light = (c1 * sh_col[8] * (n.x * n.x - n.y * n.y) + + c3 * sh_col[6] * n.z * n.z + + c4 * sh_col[0] - + c5 * sh_col[6] + + 2.0 * c1 * sh_col[4] * n.x * n.y + + 2.0 * c1 * sh_col[7] * n.x * n.z + + 2.0 * c1 * sh_col[5] * n.y * n.z + + 2.0 * c2 * sh_col[3] * n.x + + 2.0 * c2 * sh_col[1] * n.y + + 2.0 * c2 * sh_col[2] * n.z); + + colors.push_back(Color(light.x, light.y, light.z, 1)); + } + } + + for (int i = 0; i < stack_count; ++i) { + int k1 = i * (sector_count + 1); // beginning of current stack + int k2 = k1 + sector_count + 1; // beginning of next stack + + for (int j = 0; j < sector_count; ++j, ++k1, ++k2) { + // 2 triangles per sector excluding first and last stacks + // k1 => k2 => k1+1 + if (i != 0) { + indices.push_back(vertex_base + k1); + indices.push_back(vertex_base + k2); + indices.push_back(vertex_base + k1 + 1); + } + + // k1+1 => k2 => k2+1 + if (i != (stack_count - 1)) { + indices.push_back(vertex_base + k1 + 1); + indices.push_back(vertex_base + k2); + indices.push_back(vertex_base + k2 + 1); + } + } + } + } + + Array array; + array.resize(RS::ARRAY_MAX); + array[RS::ARRAY_VERTEX] = vertices; + array[RS::ARRAY_INDEX] = indices; + array[RS::ARRAY_COLOR] = colors; + + Ref mesh; + mesh.instantiate(); + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, array, Array(), Dictionary(), 0); //no compression + mesh->surface_set_material(0, material_probes); + + p_gizmo->add_mesh(mesh); +} + +///////// + +LightmapProbeGizmoPlugin::LightmapProbeGizmoPlugin() { + Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/lightprobe_lines", Color(0.5, 0.6, 1)); + + gizmo_color.a = 0.3; + create_material("lightprobe_lines", gizmo_color); +} + +String LightmapProbeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { + return ""; +} + +Variant LightmapProbeGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { + return Variant(); +} + +void LightmapProbeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { +} + +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) { + return Object::cast_to(p_spatial) != nullptr; +} + +String LightmapProbeGizmoPlugin::get_gizmo_name() const { + return "LightmapProbe"; +} + +int LightmapProbeGizmoPlugin::get_priority() const { + return -1; +} + +void LightmapProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Ref material_lines = get_material("lightprobe_lines", p_gizmo); + + p_gizmo->clear(); + + Vector lines; + + int stack_count = 8; + int sector_count = 16; + + float sector_step = (Math_PI * 2.0) / sector_count; + float stack_step = Math_PI / stack_count; + + Vector vertices; + float radius = 0.2; + + for (int i = 0; i <= stack_count; ++i) { + float stack_angle = Math_PI / 2 - i * stack_step; // starting from pi/2 to -pi/2 + float xy = radius * Math::cos(stack_angle); // r * cos(u) + float z = radius * Math::sin(stack_angle); // r * sin(u) + + // add (sector_count+1) vertices per stack + // the first and last vertices have same position and normal, but different tex coords + for (int j = 0; j <= sector_count; ++j) { + float sector_angle = j * sector_step; // starting from 0 to 2pi + + // vertex position (x, y, z) + float x = xy * Math::cos(sector_angle); // r * cos(u) * cos(v) + float y = xy * Math::sin(sector_angle); // r * cos(u) * sin(v) + + Vector3 n = Vector3(x, z, y); + vertices.push_back(n); + } + } + + for (int i = 0; i < stack_count; ++i) { + int k1 = i * (sector_count + 1); // beginning of current stack + int k2 = k1 + sector_count + 1; // beginning of next stack + + for (int j = 0; j < sector_count; ++j, ++k1, ++k2) { + // 2 triangles per sector excluding first and last stacks + // k1 => k2 => k1+1 + if (i != 0) { + lines.push_back(vertices[k1]); + lines.push_back(vertices[k2]); + lines.push_back(vertices[k1]); + lines.push_back(vertices[k1 + 1]); + } + + if (i != (stack_count - 1)) { + lines.push_back(vertices[k1 + 1]); + lines.push_back(vertices[k2]); + lines.push_back(vertices[k2]); + lines.push_back(vertices[k2 + 1]); + } + } + } + + p_gizmo->add_lines(lines, material_lines); +} + +//// + +CollisionObject3DGizmoPlugin::CollisionObject3DGizmoPlugin() { + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + const float gizmo_value = gizmo_color.get_v(); + const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); + create_material("shape_material_disabled", gizmo_color_disabled); +} + +bool CollisionObject3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String CollisionObject3DGizmoPlugin::get_gizmo_name() const { + return "CollisionObject3D"; +} + +int CollisionObject3DGizmoPlugin::get_priority() const { + return -2; +} + +void CollisionObject3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + CollisionObject3D *co = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + List owners; + co->get_shape_owners(&owners); + for (List::Element *E = owners.front(); E; E = E->next()) { + uint32_t owner_id = E->get(); + Transform3D xform = co->shape_owner_get_transform(owner_id); + Object *owner = co->shape_owner_get_owner(owner_id); + // Exclude CollisionShape3D and CollisionPolygon3D as they have their gizmo. + if (!Object::cast_to(owner) && !Object::cast_to(owner)) { + Ref material = get_material(!co->is_shape_owner_disabled(owner_id) ? "shape_material" : "shape_material_disabled", p_gizmo); + for (int shape_id = 0; shape_id < co->shape_owner_get_shape_count(owner_id); shape_id++) { + Ref s = co->shape_owner_get_shape(owner_id, shape_id); + if (s.is_null()) { + continue; + } + SurfaceTool st; + st.append_from(s->get_debug_mesh(), 0, xform); + + p_gizmo->add_mesh(st.commit(), material); + p_gizmo->add_collision_segments(s->get_debug_mesh_lines()); + } + } + } +} + +//// + +CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() { + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + const float gizmo_value = gizmo_color.get_v(); + const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); + create_material("shape_material_disabled", gizmo_color_disabled); + create_handle_material("handles"); +} + +bool CollisionShape3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String CollisionShape3DGizmoPlugin::get_gizmo_name() const { + return "CollisionShape3D"; +} + +int CollisionShape3DGizmoPlugin::get_priority() const { + return -1; +} + +String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const { + const CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref s = cs->get_shape(); + if (s.is_null()) { + return ""; + } + + if (Object::cast_to(*s)) { + return "Radius"; + } + + if (Object::cast_to(*s)) { + return "Size"; + } + + if (Object::cast_to(*s)) { + return p_id == 0 ? "Radius" : "Height"; + } + + if (Object::cast_to(*s)) { + return p_id == 0 ? "Radius" : "Height"; + } + + if (Object::cast_to(*s)) { + return "Length"; + } + + return ""; +} + +Variant CollisionShape3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const { + CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref s = cs->get_shape(); + if (s.is_null()) { + return Variant(); + } + + if (Object::cast_to(*s)) { + Ref ss = s; + return ss->get_radius(); + } + + if (Object::cast_to(*s)) { + Ref bs = s; + return bs->get_size(); + } + + if (Object::cast_to(*s)) { + Ref cs2 = s; + return p_id == 0 ? cs2->get_radius() : cs2->get_height(); + } + + if (Object::cast_to(*s)) { + Ref cs2 = s; + return p_id == 0 ? cs2->get_radius() : cs2->get_height(); + } + + if (Object::cast_to(*s)) { + Ref cs2 = s; + return cs2->get_length(); + } + + return Variant(); +} + +void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) const { + CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref s = cs->get_shape(); + if (s.is_null()) { + return; + } + + Transform3D gt = cs->get_global_transform(); + Transform3D gi = gt.affine_inverse(); + + Vector3 ray_from = p_camera->project_ray_origin(p_point); + Vector3 ray_dir = p_camera->project_ray_normal(p_point); + + Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) }; + + if (Object::cast_to(*s)) { + Ref ss = s; + Vector3 ra, rb; + Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb); + float d = ra.x; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + + ss->set_radius(d); + } + + if (Object::cast_to(*s)) { + Ref rs = s; + Vector3 ra, rb; + Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(0, 0, 4096), sg[0], sg[1], ra, rb); + float d = ra.z; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + + rs->set_length(d); + } + + if (Object::cast_to(*s)) { + Vector3 axis; + axis[p_id] = 1.0; + Ref bs = s; + Vector3 ra, rb; + Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); + float d = ra[p_id]; + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + + Vector3 he = bs->get_size(); + he[p_id] = d * 2; + bs->set_size(he); + } + + if (Object::cast_to(*s)) { + Vector3 axis; + axis[p_id == 0 ? 0 : 2] = 1.0; + Ref 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_id == 1) { + d -= cs2->get_radius(); + } + + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + + if (p_id == 0) { + cs2->set_radius(d); + } else if (p_id == 1) { + cs2->set_height(d * 2.0); + } + } + + if (Object::cast_to(*s)) { + Vector3 axis; + axis[p_id == 0 ? 0 : 1] = 1.0; + Ref 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 (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (d < 0.001) { + d = 0.001; + } + + if (p_id == 0) { + cs2->set_radius(d); + } else if (p_id == 1) { + cs2->set_height(d * 2.0); + } + } +} + +void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) const { + CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref s = cs->get_shape(); + if (s.is_null()) { + return; + } + + if (Object::cast_to(*s)) { + Ref ss = s; + if (p_cancel) { + ss->set_radius(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Sphere Shape Radius")); + ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); + ur->add_undo_method(ss.ptr(), "set_radius", p_restore); + ur->commit_action(); + } + + if (Object::cast_to(*s)) { + Ref ss = s; + if (p_cancel) { + ss->set_size(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Box Shape Size")); + ur->add_do_method(ss.ptr(), "set_size", ss->get_size()); + ur->add_undo_method(ss.ptr(), "set_size", p_restore); + ur->commit_action(); + } + + if (Object::cast_to(*s)) { + Ref ss = s; + if (p_cancel) { + if (p_id == 0) { + ss->set_radius(p_restore); + } else { + ss->set_height(p_restore); + } + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + 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); + } else { + ur->create_action(TTR("Change Capsule Shape Height")); + ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); + ur->add_undo_method(ss.ptr(), "set_height", p_restore); + } + + ur->commit_action(); + } + + if (Object::cast_to(*s)) { + Ref ss = s; + if (p_cancel) { + if (p_id == 0) { + ss->set_radius(p_restore); + } else { + ss->set_height(p_restore); + } + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + 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); + } else { + ur->create_action( + /// + + //////// + TTR("Change Cylinder Shape Height")); + ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); + ur->add_undo_method(ss.ptr(), "set_height", p_restore); + } + + ur->commit_action(); + } + + if (Object::cast_to(*s)) { + Ref ss = s; + if (p_cancel) { + ss->set_length(p_restore); + return; + } + + UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Change Ray Shape Length")); + ur->add_do_method(ss.ptr(), "set_length", ss->get_length()); + ur->add_undo_method(ss.ptr(), "set_length", p_restore); + ur->commit_action(); + } +} + +void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + CollisionShape3D *cs = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Ref s = cs->get_shape(); + if (s.is_null()) { + return; + } + + const Ref material = + get_material(!cs->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); + Ref handles_material = get_material("handles"); + + if (Object::cast_to(*s)) { + Ref sp = s; + float r = sp->get_radius(); + + Vector points; + + for (int i = 0; i <= 360; i++) { + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 1); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + points.push_back(Vector3(a.x, 0, a.y)); + points.push_back(Vector3(b.x, 0, b.y)); + points.push_back(Vector3(0, a.x, a.y)); + points.push_back(Vector3(0, b.x, b.y)); + points.push_back(Vector3(a.x, a.y, 0)); + points.push_back(Vector3(b.x, b.y, 0)); + } + + Vector collision_segments; + + for (int i = 0; i < 64; i++) { + float ra = i * (Math_TAU / 64.0); + float rb = (i + 1) * (Math_TAU / 64.0); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; + + collision_segments.push_back(Vector3(a.x, 0, a.y)); + collision_segments.push_back(Vector3(b.x, 0, b.y)); + collision_segments.push_back(Vector3(0, a.x, a.y)); + collision_segments.push_back(Vector3(0, b.x, b.y)); + collision_segments.push_back(Vector3(a.x, a.y, 0)); + collision_segments.push_back(Vector3(b.x, b.y, 0)); + } + + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(collision_segments); + Vector handles; + handles.push_back(Vector3(r, 0, 0)); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to(*s)) { + Ref bs = s; + Vector lines; + AABB aabb; + aabb.position = -bs->get_size() / 2; + aabb.size = bs->get_size(); + + for (int i = 0; i < 12; i++) { + Vector3 a, b; + aabb.get_edge(i, a, b); + lines.push_back(a); + lines.push_back(b); + } + + Vector handles; + + for (int i = 0; i < 3; i++) { + Vector3 ax; + ax[i] = bs->get_size()[i] / 2; + handles.push_back(ax); + } + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to(*s)) { + Ref cs2 = s; + float radius = cs2->get_radius(); + float height = cs2->get_height(); + + Vector points; + + Vector3 d(0, height * 0.5, 0); + for (int i = 0; i < 360; i++) { + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 1); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(b.x, 0, b.y) + d); + + points.push_back(Vector3(a.x, 0, a.y) - d); + points.push_back(Vector3(b.x, 0, b.y) - d); + + if (i % 90 == 0) { + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(a.x, 0, a.y) - d); + } + + Vector3 dud = i < 180 ? d : -d; + + points.push_back(Vector3(0, a.x, a.y) + dud); + points.push_back(Vector3(0, b.x, b.y) + dud); + points.push_back(Vector3(a.y, a.x, 0) + dud); + points.push_back(Vector3(b.y, b.x, 0) + dud); + } + + p_gizmo->add_lines(points, material); + + Vector collision_segments; + + for (int i = 0; i < 64; i++) { + float ra = i * (Math_TAU / 64.0); + float rb = (i + 1) * (Math_TAU / 64.0); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + collision_segments.push_back(Vector3(a.x, 0, a.y) + d); + collision_segments.push_back(Vector3(b.x, 0, b.y) + d); + + collision_segments.push_back(Vector3(a.x, 0, a.y) - d); + collision_segments.push_back(Vector3(b.x, 0, b.y) - d); + + if (i % 16 == 0) { + collision_segments.push_back(Vector3(a.x, 0, a.y) + d); + collision_segments.push_back(Vector3(a.x, 0, a.y) - d); + } + + Vector3 dud = i < 32 ? d : -d; + + collision_segments.push_back(Vector3(0, a.x, a.y) + dud); + collision_segments.push_back(Vector3(0, b.x, b.y) + dud); + collision_segments.push_back(Vector3(a.y, a.x, 0) + dud); + collision_segments.push_back(Vector3(b.y, b.x, 0) + dud); + } + + p_gizmo->add_collision_segments(collision_segments); + + Vector handles; + handles.push_back(Vector3(cs2->get_radius(), 0, 0)); + handles.push_back(Vector3(0, cs2->get_height() * 0.5 + cs2->get_radius(), 0)); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to(*s)) { + Ref cs2 = s; + float radius = cs2->get_radius(); + float height = cs2->get_height(); + + Vector points; + + Vector3 d(0, height * 0.5, 0); + for (int i = 0; i < 360; i++) { + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 1); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(b.x, 0, b.y) + d); + + points.push_back(Vector3(a.x, 0, a.y) - d); + points.push_back(Vector3(b.x, 0, b.y) - d); + + if (i % 90 == 0) { + points.push_back(Vector3(a.x, 0, a.y) + d); + points.push_back(Vector3(a.x, 0, a.y) - d); + } + } + + p_gizmo->add_lines(points, material); + + Vector collision_segments; + + for (int i = 0; i < 64; i++) { + float ra = i * (Math_TAU / 64.0); + float rb = (i + 1) * (Math_TAU / 64.0); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; + + collision_segments.push_back(Vector3(a.x, 0, a.y) + d); + collision_segments.push_back(Vector3(b.x, 0, b.y) + d); + + collision_segments.push_back(Vector3(a.x, 0, a.y) - d); + collision_segments.push_back(Vector3(b.x, 0, b.y) - d); + + if (i % 16 == 0) { + collision_segments.push_back(Vector3(a.x, 0, a.y) + d); + collision_segments.push_back(Vector3(a.x, 0, a.y) - d); + } + } + + p_gizmo->add_collision_segments(collision_segments); + + Vector handles; + handles.push_back(Vector3(cs2->get_radius(), 0, 0)); + handles.push_back(Vector3(0, cs2->get_height() * 0.5, 0)); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to(*s)) { + Ref ps = s; + Plane p = ps->get_plane(); + Vector points; + + Vector3 n1 = p.get_any_perpendicular_normal(); + Vector3 n2 = p.normal.cross(n1).normalized(); + + Vector3 pface[4] = { + p.normal * p.d + n1 * 10.0 + n2 * 10.0, + p.normal * p.d + n1 * 10.0 + n2 * -10.0, + p.normal * p.d + n1 * -10.0 + n2 * -10.0, + p.normal * p.d + n1 * -10.0 + n2 * 10.0, + }; + + points.push_back(pface[0]); + points.push_back(pface[1]); + points.push_back(pface[1]); + points.push_back(pface[2]); + points.push_back(pface[2]); + points.push_back(pface[3]); + points.push_back(pface[3]); + points.push_back(pface[0]); + points.push_back(p.normal * p.d); + points.push_back(p.normal * p.d + p.normal * 3); + + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(points); + } + + if (Object::cast_to(*s)) { + Vector points = Object::cast_to(*s)->get_points(); + + if (points.size() > 3) { + Vector varr = Variant(points); + Geometry3D::MeshData md; + Error err = ConvexHullComputer::convex_hull(varr, md); + if (err == OK) { + Vector points2; + points2.resize(md.edges.size() * 2); + for (int i = 0; i < md.edges.size(); i++) { + points2.write[i * 2 + 0] = md.vertices[md.edges[i].a]; + points2.write[i * 2 + 1] = md.vertices[md.edges[i].b]; + } + + p_gizmo->add_lines(points2, material); + p_gizmo->add_collision_segments(points2); + } + } + } + + if (Object::cast_to(*s)) { + Ref cs2 = s; + Ref mesh = cs2->get_debug_mesh(); + p_gizmo->add_mesh(mesh, material); + p_gizmo->add_collision_segments(cs2->get_debug_mesh_lines()); + } + + if (Object::cast_to(*s)) { + Ref rs = s; + + Vector points; + points.push_back(Vector3()); + points.push_back(Vector3(0, 0, rs->get_length())); + p_gizmo->add_lines(points, material); + p_gizmo->add_collision_segments(points); + Vector handles; + handles.push_back(Vector3(0, 0, rs->get_length())); + p_gizmo->add_handles(handles, handles_material); + } + + if (Object::cast_to(*s)) { + Ref hms = s; + + Ref mesh = hms->get_debug_mesh(); + p_gizmo->add_mesh(mesh, material); + } +} + +///// + +CollisionPolygon3DGizmoPlugin::CollisionPolygon3DGizmoPlugin() { + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + create_material("shape_material", gizmo_color); + const float gizmo_value = gizmo_color.get_v(); + const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); + create_material("shape_material_disabled", gizmo_color_disabled); +} + +bool CollisionPolygon3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String CollisionPolygon3DGizmoPlugin::get_gizmo_name() const { + return "CollisionPolygon3D"; +} + +int CollisionPolygon3DGizmoPlugin::get_priority() const { + return -1; +} + +void CollisionPolygon3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + CollisionPolygon3D *polygon = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Vector points = polygon->get_polygon(); + float depth = polygon->get_depth() * 0.5; + + Vector lines; + for (int i = 0; i < points.size(); i++) { + int n = (i + 1) % points.size(); + lines.push_back(Vector3(points[i].x, points[i].y, depth)); + lines.push_back(Vector3(points[n].x, points[n].y, depth)); + lines.push_back(Vector3(points[i].x, points[i].y, -depth)); + lines.push_back(Vector3(points[n].x, points[n].y, -depth)); + lines.push_back(Vector3(points[i].x, points[i].y, depth)); + lines.push_back(Vector3(points[i].x, points[i].y, -depth)); + } + + const Ref material = + get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); + + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); +} + +//// + +NavigationRegion3DGizmoPlugin::NavigationRegion3DGizmoPlugin() { + create_material("navigation_edge_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge", Color(0.5, 1, 1))); + create_material("navigation_edge_material_disabled", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_edge_disabled", Color(0.7, 0.7, 0.7))); + create_material("navigation_solid_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid", Color(0.5, 1, 1, 0.4))); + create_material("navigation_solid_material_disabled", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/navigation_solid_disabled", Color(0.7, 0.7, 0.7, 0.4))); +} + +bool NavigationRegion3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String NavigationRegion3DGizmoPlugin::get_gizmo_name() const { + return "NavigationRegion3D"; +} + +int NavigationRegion3DGizmoPlugin::get_priority() const { + return -1; +} + +void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + NavigationRegion3D *navmesh = Object::cast_to(p_gizmo->get_spatial_node()); + + Ref edge_material = get_material("navigation_edge_material", p_gizmo); + Ref edge_material_disabled = get_material("navigation_edge_material_disabled", p_gizmo); + Ref solid_material = get_material("navigation_solid_material", p_gizmo); + Ref solid_material_disabled = get_material("navigation_solid_material_disabled", p_gizmo); + + p_gizmo->clear(); + Ref navmeshie = navmesh->get_navigation_mesh(); + if (navmeshie.is_null()) { + return; + } + + Vector vertices = navmeshie->get_vertices(); + const Vector3 *vr = vertices.ptr(); + List faces; + for (int i = 0; i < navmeshie->get_polygon_count(); i++) { + Vector p = navmeshie->get_polygon(i); + + for (int j = 2; j < p.size(); j++) { + Face3 f; + f.vertex[0] = vr[p[0]]; + f.vertex[1] = vr[p[j - 1]]; + f.vertex[2] = vr[p[j]]; + + faces.push_back(f); + } + } + + if (faces.is_empty()) { + return; + } + + Map<_EdgeKey, bool> edge_map; + Vector tmeshfaces; + tmeshfaces.resize(faces.size() * 3); + + { + Vector3 *tw = tmeshfaces.ptrw(); + int tidx = 0; + + for (List::Element *E = faces.front(); E; E = E->next()) { + const Face3 &f = E->get(); + + for (int j = 0; j < 3; j++) { + tw[tidx++] = f.vertex[j]; + _EdgeKey ek; + ek.from = f.vertex[j].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); + ek.to = f.vertex[(j + 1) % 3].snapped(Vector3(CMP_EPSILON, CMP_EPSILON, CMP_EPSILON)); + if (ek.from < ek.to) { + SWAP(ek.from, ek.to); + } + + Map<_EdgeKey, bool>::Element *F = edge_map.find(ek); + + if (F) { + F->get() = false; + + } else { + edge_map[ek] = true; + } + } + } + } + Vector lines; + + for (Map<_EdgeKey, bool>::Element *E = edge_map.front(); E; E = E->next()) { + if (E->get()) { + lines.push_back(E->key().from); + lines.push_back(E->key().to); + } + } + + Ref tmesh = memnew(TriangleMesh); + tmesh->create(tmeshfaces); + + if (lines.size()) { + p_gizmo->add_lines(lines, navmesh->is_enabled() ? edge_material : edge_material_disabled); + } + p_gizmo->add_collision_triangles(tmesh); + Ref m = memnew(ArrayMesh); + Array a; + a.resize(Mesh::ARRAY_MAX); + a[0] = tmeshfaces; + m->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + m->surface_set_material(0, navmesh->is_enabled() ? solid_material : solid_material_disabled); + p_gizmo->add_mesh(m); + p_gizmo->add_collision_segments(lines); +} + +////// + +#define BODY_A_RADIUS 0.25 +#define BODY_B_RADIUS 0.27 + +Basis JointGizmosDrawer::look_body(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) { + const Vector3 &p_eye(p_joint_transform.origin); + const Vector3 &p_target(p_body_transform.origin); + + Vector3 v_x, v_y, v_z; + + // Look the body with X + v_x = p_target - p_eye; + v_x.normalize(); + + v_z = v_x.cross(Vector3(0, 1, 0)); + v_z.normalize(); + + v_y = v_z.cross(v_x); + v_y.normalize(); + + Basis base; + base.set(v_x, v_y, v_z); + + // Absorb current joint transform + base = p_joint_transform.basis.inverse() * base; + + return base; +} + +Basis JointGizmosDrawer::look_body_toward(Vector3::Axis p_axis, const Transform3D &joint_transform, const Transform3D &body_transform) { + switch (p_axis) { + case Vector3::AXIS_X: + return look_body_toward_x(joint_transform, body_transform); + case Vector3::AXIS_Y: + return look_body_toward_y(joint_transform, body_transform); + case Vector3::AXIS_Z: + return look_body_toward_z(joint_transform, body_transform); + default: + return Basis(); + } +} + +Basis JointGizmosDrawer::look_body_toward_x(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) { + const Vector3 &p_eye(p_joint_transform.origin); + const Vector3 &p_target(p_body_transform.origin); + + const Vector3 p_front(p_joint_transform.basis.get_axis(0)); + + Vector3 v_x, v_y, v_z; + + // Look the body with X + v_x = p_target - p_eye; + v_x.normalize(); + + v_y = p_front.cross(v_x); + v_y.normalize(); + + v_z = v_y.cross(p_front); + v_z.normalize(); + + // Clamp X to FRONT axis + v_x = p_front; + v_x.normalize(); + + Basis base; + base.set(v_x, v_y, v_z); + + // Absorb current joint transform + base = p_joint_transform.basis.inverse() * base; + + return base; +} + +Basis JointGizmosDrawer::look_body_toward_y(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) { + const Vector3 &p_eye(p_joint_transform.origin); + const Vector3 &p_target(p_body_transform.origin); + + const Vector3 p_up(p_joint_transform.basis.get_axis(1)); + + Vector3 v_x, v_y, v_z; + + // Look the body with X + v_x = p_target - p_eye; + v_x.normalize(); + + v_z = v_x.cross(p_up); + v_z.normalize(); + + v_x = p_up.cross(v_z); + v_x.normalize(); + + // Clamp Y to UP axis + v_y = p_up; + v_y.normalize(); + + Basis base; + base.set(v_x, v_y, v_z); + + // Absorb current joint transform + base = p_joint_transform.basis.inverse() * base; + + return base; +} + +Basis JointGizmosDrawer::look_body_toward_z(const Transform3D &p_joint_transform, const Transform3D &p_body_transform) { + const Vector3 &p_eye(p_joint_transform.origin); + const Vector3 &p_target(p_body_transform.origin); + + const Vector3 p_lateral(p_joint_transform.basis.get_axis(2)); + + Vector3 v_x, v_y, v_z; + + // Look the body with X + v_x = p_target - p_eye; + v_x.normalize(); + + v_z = p_lateral; + v_z.normalize(); + + v_y = v_z.cross(v_x); + v_y.normalize(); + + // Clamp X to Z axis + v_x = v_y.cross(v_z); + v_x.normalize(); + + Basis base; + base.set(v_x, v_y, v_z); + + // Absorb current joint transform + base = p_joint_transform.basis.inverse() * base; + + return base; +} + +void JointGizmosDrawer::draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform3D &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector &r_points, bool p_inverse) { + if (p_limit_lower == p_limit_upper) { + r_points.push_back(p_offset.translated(Vector3()).origin); + r_points.push_back(p_offset.translated(p_base.xform(Vector3(0.5, 0, 0))).origin); + + } else { + if (p_limit_lower > p_limit_upper) { + p_limit_lower = -Math_PI; + p_limit_upper = Math_PI; + } + + const int points = 32; + + for (int i = 0; i < points; i++) { + real_t s = p_limit_lower + i * (p_limit_upper - p_limit_lower) / points; + real_t n = p_limit_lower + (i + 1) * (p_limit_upper - p_limit_lower) / points; + + Vector3 from; + Vector3 to; + switch (p_axis) { + case Vector3::AXIS_X: + if (p_inverse) { + from = p_base.xform(Vector3(0, Math::sin(s), Math::cos(s))) * p_radius; + to = p_base.xform(Vector3(0, Math::sin(n), Math::cos(n))) * p_radius; + } else { + from = p_base.xform(Vector3(0, -Math::sin(s), Math::cos(s))) * p_radius; + to = p_base.xform(Vector3(0, -Math::sin(n), Math::cos(n))) * p_radius; + } + break; + case Vector3::AXIS_Y: + if (p_inverse) { + from = p_base.xform(Vector3(Math::cos(s), 0, -Math::sin(s))) * p_radius; + to = p_base.xform(Vector3(Math::cos(n), 0, -Math::sin(n))) * p_radius; + } else { + from = p_base.xform(Vector3(Math::cos(s), 0, Math::sin(s))) * p_radius; + to = p_base.xform(Vector3(Math::cos(n), 0, Math::sin(n))) * p_radius; + } + break; + case Vector3::AXIS_Z: + from = p_base.xform(Vector3(Math::cos(s), Math::sin(s), 0)) * p_radius; + to = p_base.xform(Vector3(Math::cos(n), Math::sin(n), 0)) * p_radius; + break; + } + + if (i == points - 1) { + r_points.push_back(p_offset.translated(to).origin); + r_points.push_back(p_offset.translated(Vector3()).origin); + } + if (i == 0) { + r_points.push_back(p_offset.translated(from).origin); + r_points.push_back(p_offset.translated(Vector3()).origin); + } + + r_points.push_back(p_offset.translated(from).origin); + r_points.push_back(p_offset.translated(to).origin); + } + + r_points.push_back(p_offset.translated(Vector3(0, p_radius * 1.5, 0)).origin); + r_points.push_back(p_offset.translated(Vector3()).origin); + } +} + +void JointGizmosDrawer::draw_cone(const Transform3D &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector &r_points) { + float r = 1.0; + float w = r * Math::sin(p_swing); + float d = r * Math::cos(p_swing); + + //swing + for (int i = 0; i < 360; i += 10) { + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 10); + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w; + + r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, a.x, a.y))).origin); + r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, b.x, b.y))).origin); + + if (i % 90 == 0) { + r_points.push_back(p_offset.translated(p_base.xform(Vector3(d, a.x, a.y))).origin); + r_points.push_back(p_offset.translated(p_base.xform(Vector3())).origin); + } + } + + r_points.push_back(p_offset.translated(p_base.xform(Vector3())).origin); + r_points.push_back(p_offset.translated(p_base.xform(Vector3(1, 0, 0))).origin); + + /// Twist + float ts = Math::rad2deg(p_twist); + ts = MIN(ts, 720); + + for (int i = 0; i < int(ts); i += 5) { + float ra = Math::deg2rad((float)i); + float rb = Math::deg2rad((float)i + 5); + float c = i / 720.0; + float cn = (i + 5) / 720.0; + Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w * c; + Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w * cn; + + r_points.push_back(p_offset.translated(p_base.xform(Vector3(c, a.x, a.y))).origin); + r_points.push_back(p_offset.translated(p_base.xform(Vector3(cn, b.x, b.y))).origin); + } +} + +//// + +Joint3DGizmoPlugin::Joint3DGizmoPlugin() { + create_material("joint_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1))); + create_material("joint_body_a_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1))); + create_material("joint_body_b_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1))); + + update_timer = memnew(Timer); + update_timer->set_name("JointGizmoUpdateTimer"); + update_timer->set_wait_time(1.0 / 120.0); + update_timer->connect("timeout", callable_mp(this, &Joint3DGizmoPlugin::incremental_update_gizmos)); + update_timer->set_autostart(true); + EditorNode::get_singleton()->call_deferred(SNAME("add_child"), update_timer); +} + +void Joint3DGizmoPlugin::incremental_update_gizmos() { + if (!current_gizmos.is_empty()) { + update_idx++; + update_idx = update_idx % current_gizmos.size(); + redraw(current_gizmos[update_idx]); + } +} + +bool Joint3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String Joint3DGizmoPlugin::get_gizmo_name() const { + return "Joint3D"; +} + +int Joint3DGizmoPlugin::get_priority() const { + return -1; +} + +void Joint3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + Joint3D *joint = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + Node3D *node_body_a = nullptr; + if (!joint->get_node_a().is_empty()) { + node_body_a = Object::cast_to(joint->get_node(joint->get_node_a())); + } + + Node3D *node_body_b = nullptr; + if (!joint->get_node_b().is_empty()) { + node_body_b = Object::cast_to(joint->get_node(joint->get_node_b())); + } + + if (!node_body_a && !node_body_b) { + return; + } + + Ref common_material = get_material("joint_material", p_gizmo); + Ref body_a_material = get_material("joint_body_a_material", p_gizmo); + Ref body_b_material = get_material("joint_body_b_material", p_gizmo); + + Vector points; + Vector body_a_points; + Vector body_b_points; + + if (Object::cast_to(joint)) { + CreatePinJointGizmo(Transform3D(), points); + p_gizmo->add_collision_segments(points); + p_gizmo->add_lines(points, common_material); + } + + HingeJoint3D *hinge = Object::cast_to(joint); + if (hinge) { + CreateHingeJointGizmo( + Transform3D(), + hinge->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform3D(), + node_body_b ? node_body_b->get_global_transform() : Transform3D(), + hinge->get_param(HingeJoint3D::PARAM_LIMIT_LOWER), + hinge->get_param(HingeJoint3D::PARAM_LIMIT_UPPER), + hinge->get_flag(HingeJoint3D::FLAG_USE_LIMIT), + points, + node_body_a ? &body_a_points : nullptr, + node_body_b ? &body_b_points : nullptr); + + p_gizmo->add_collision_segments(points); + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(points, common_material); + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } + + SliderJoint3D *slider = Object::cast_to(joint); + if (slider) { + CreateSliderJointGizmo( + Transform3D(), + slider->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform3D(), + node_body_b ? node_body_b->get_global_transform() : Transform3D(), + slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_LOWER), + slider->get_param(SliderJoint3D::PARAM_ANGULAR_LIMIT_UPPER), + slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_LOWER), + slider->get_param(SliderJoint3D::PARAM_LINEAR_LIMIT_UPPER), + points, + node_body_a ? &body_a_points : nullptr, + node_body_b ? &body_b_points : nullptr); + + p_gizmo->add_collision_segments(points); + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(points, common_material); + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } + + ConeTwistJoint3D *cone = Object::cast_to(joint); + if (cone) { + CreateConeTwistJointGizmo( + Transform3D(), + cone->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform3D(), + node_body_b ? node_body_b->get_global_transform() : Transform3D(), + cone->get_param(ConeTwistJoint3D::PARAM_SWING_SPAN), + cone->get_param(ConeTwistJoint3D::PARAM_TWIST_SPAN), + node_body_a ? &body_a_points : nullptr, + node_body_b ? &body_b_points : nullptr); + + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } + + Generic6DOFJoint3D *gen = Object::cast_to(joint); + if (gen) { + CreateGeneric6DOFJointGizmo( + Transform3D(), + gen->get_global_transform(), + node_body_a ? node_body_a->get_global_transform() : Transform3D(), + node_body_b ? node_body_b->get_global_transform() : Transform3D(), + + gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), + gen->get_param_x(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), + gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), + gen->get_param_x(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), + gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), + gen->get_flag_x(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), + + gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), + gen->get_param_y(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), + gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), + gen->get_param_y(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), + gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), + gen->get_flag_y(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), + + gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_LOWER_LIMIT), + gen->get_param_z(Generic6DOFJoint3D::PARAM_ANGULAR_UPPER_LIMIT), + gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_LOWER_LIMIT), + gen->get_param_z(Generic6DOFJoint3D::PARAM_LINEAR_UPPER_LIMIT), + gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_ANGULAR_LIMIT), + gen->get_flag_z(Generic6DOFJoint3D::FLAG_ENABLE_LINEAR_LIMIT), + + points, + node_body_a ? &body_a_points : nullptr, + node_body_a ? &body_b_points : nullptr); + + p_gizmo->add_collision_segments(points); + p_gizmo->add_collision_segments(body_a_points); + p_gizmo->add_collision_segments(body_b_points); + + p_gizmo->add_lines(points, common_material); + p_gizmo->add_lines(body_a_points, body_a_material); + p_gizmo->add_lines(body_b_points, body_b_material); + } +} + +void Joint3DGizmoPlugin::CreatePinJointGizmo(const Transform3D &p_offset, Vector &r_cursor_points) { + float cs = 0.25; + + r_cursor_points.push_back(p_offset.translated(Vector3(+cs, 0, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(-cs, 0, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, +cs, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, -cs, 0)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, +cs)).origin); + r_cursor_points.push_back(p_offset.translated(Vector3(0, 0, -cs)).origin); +} + +void Joint3DGizmoPlugin::CreateHingeJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector &r_common_points, Vector *r_body_a_points, Vector *r_body_b_points) { + r_common_points.push_back(p_offset.translated(Vector3(0, 0, 0.5)).origin); + r_common_points.push_back(p_offset.translated(Vector3(0, 0, -0.5)).origin); + + if (!p_use_limit) { + p_limit_upper = -1; + p_limit_lower = 0; + } + + if (r_body_a_points) { + JointGizmosDrawer::draw_circle(Vector3::AXIS_Z, + BODY_A_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_a), + p_limit_lower, + p_limit_upper, + *r_body_a_points); + } + + if (r_body_b_points) { + JointGizmosDrawer::draw_circle(Vector3::AXIS_Z, + BODY_B_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward_z(p_trs_joint, p_trs_body_b), + p_limit_lower, + p_limit_upper, + *r_body_b_points); + } +} + +void Joint3DGizmoPlugin::CreateSliderJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector &r_points, Vector *r_body_a_points, Vector *r_body_b_points) { + p_linear_limit_lower = -p_linear_limit_lower; + p_linear_limit_upper = -p_linear_limit_upper; + + float cs = 0.25; + r_points.push_back(p_offset.translated(Vector3(0, 0, 0.5)).origin); + r_points.push_back(p_offset.translated(Vector3(0, 0, -0.5)).origin); + + if (p_linear_limit_lower >= p_linear_limit_upper) { + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, 0, 0)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, 0, 0)).origin); + + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_upper, -cs, -cs)).origin); + + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, cs, -cs)).origin); + r_points.push_back(p_offset.translated(Vector3(p_linear_limit_lower, -cs, -cs)).origin); + + } else { + r_points.push_back(p_offset.translated(Vector3(+cs * 2, 0, 0)).origin); + r_points.push_back(p_offset.translated(Vector3(-cs * 2, 0, 0)).origin); + } + + if (r_body_a_points) { + JointGizmosDrawer::draw_circle( + Vector3::AXIS_X, + BODY_A_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_a), + p_angular_limit_lower, + p_angular_limit_upper, + *r_body_a_points); + } + + if (r_body_b_points) { + JointGizmosDrawer::draw_circle( + Vector3::AXIS_X, + BODY_B_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward(Vector3::AXIS_X, p_trs_joint, p_trs_body_b), + p_angular_limit_lower, + p_angular_limit_upper, + *r_body_b_points, + true); + } +} + +void Joint3DGizmoPlugin::CreateConeTwistJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_swing, real_t p_twist, Vector *r_body_a_points, Vector *r_body_b_points) { + if (r_body_a_points) { + JointGizmosDrawer::draw_cone( + p_offset, + JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_a), + p_swing, + p_twist, + *r_body_a_points); + } + + if (r_body_b_points) { + JointGizmosDrawer::draw_cone( + p_offset, + JointGizmosDrawer::look_body(p_trs_joint, p_trs_body_b), + p_swing, + p_twist, + *r_body_b_points); + } +} + +void Joint3DGizmoPlugin::CreateGeneric6DOFJointGizmo( + const Transform3D &p_offset, + const Transform3D &p_trs_joint, + const Transform3D &p_trs_body_a, + const Transform3D &p_trs_body_b, + real_t p_angular_limit_lower_x, + real_t p_angular_limit_upper_x, + real_t p_linear_limit_lower_x, + real_t p_linear_limit_upper_x, + bool p_enable_angular_limit_x, + bool p_enable_linear_limit_x, + real_t p_angular_limit_lower_y, + real_t p_angular_limit_upper_y, + real_t p_linear_limit_lower_y, + real_t p_linear_limit_upper_y, + bool p_enable_angular_limit_y, + bool p_enable_linear_limit_y, + real_t p_angular_limit_lower_z, + real_t p_angular_limit_upper_z, + real_t p_linear_limit_lower_z, + real_t p_linear_limit_upper_z, + bool p_enable_angular_limit_z, + bool p_enable_linear_limit_z, + Vector &r_points, + Vector *r_body_a_points, + Vector *r_body_b_points) { + float cs = 0.25; + + for (int ax = 0; ax < 3; ax++) { + float ll = 0; + float ul = 0; + float lll = 0; + float lul = 0; + + int a1 = 0; + int a2 = 0; + int a3 = 0; + bool enable_ang = false; + bool enable_lin = false; + + switch (ax) { + case 0: + ll = p_angular_limit_lower_x; + ul = p_angular_limit_upper_x; + lll = -p_linear_limit_lower_x; + lul = -p_linear_limit_upper_x; + enable_ang = p_enable_angular_limit_x; + enable_lin = p_enable_linear_limit_x; + a1 = 0; + a2 = 1; + a3 = 2; + break; + case 1: + ll = p_angular_limit_lower_y; + ul = p_angular_limit_upper_y; + lll = -p_linear_limit_lower_y; + lul = -p_linear_limit_upper_y; + enable_ang = p_enable_angular_limit_y; + enable_lin = p_enable_linear_limit_y; + a1 = 1; + a2 = 2; + a3 = 0; + break; + case 2: + ll = p_angular_limit_lower_z; + ul = p_angular_limit_upper_z; + lll = -p_linear_limit_lower_z; + lul = -p_linear_limit_upper_z; + enable_ang = p_enable_angular_limit_z; + enable_lin = p_enable_linear_limit_z; + a1 = 2; + a2 = 0; + a3 = 1; + break; + } + +#define ADD_VTX(x, y, z) \ + { \ + Vector3 v; \ + v[a1] = (x); \ + v[a2] = (y); \ + v[a3] = (z); \ + r_points.push_back(p_offset.translated(v).origin); \ + } + + if (enable_lin && lll >= lul) { + ADD_VTX(lul, 0, 0); + ADD_VTX(lll, 0, 0); + + ADD_VTX(lul, -cs, -cs); + ADD_VTX(lul, -cs, cs); + ADD_VTX(lul, -cs, cs); + ADD_VTX(lul, cs, cs); + ADD_VTX(lul, cs, cs); + ADD_VTX(lul, cs, -cs); + ADD_VTX(lul, cs, -cs); + ADD_VTX(lul, -cs, -cs); + + ADD_VTX(lll, -cs, -cs); + ADD_VTX(lll, -cs, cs); + ADD_VTX(lll, -cs, cs); + ADD_VTX(lll, cs, cs); + ADD_VTX(lll, cs, cs); + ADD_VTX(lll, cs, -cs); + ADD_VTX(lll, cs, -cs); + ADD_VTX(lll, -cs, -cs); + + } else { + ADD_VTX(+cs * 2, 0, 0); + ADD_VTX(-cs * 2, 0, 0); + } + + if (!enable_ang) { + ll = 0; + ul = -1; + } + + if (r_body_a_points) { + JointGizmosDrawer::draw_circle( + static_cast(ax), + BODY_A_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward(static_cast(ax), p_trs_joint, p_trs_body_a), + ll, + ul, + *r_body_a_points, + true); + } + + if (r_body_b_points) { + JointGizmosDrawer::draw_circle( + static_cast(ax), + BODY_B_RADIUS, + p_offset, + JointGizmosDrawer::look_body_toward(static_cast(ax), p_trs_joint, p_trs_body_b), + ll, + ul, + *r_body_b_points); + } + } + +#undef ADD_VTX +} diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h new file mode 100644 index 0000000000..61ee3a95a9 --- /dev/null +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -0,0 +1,635 @@ +/*************************************************************************/ +/* node_3d_editor_gizmos.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef NODE_3D_EDITOR_GIZMOS_H +#define NODE_3D_EDITOR_GIZMOS_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 mesh; + Ref material; + Ref skin_reference; + bool extra_margin = false; + Transform3D xform; + + void create_instance(Node3D *p_base, bool p_hidden = false); + }; + + bool selected; + + Vector collision_segments; + Ref collision_mesh; + + Vector handles; + Vector handle_ids; + Vector secondary_handles; + Vector secondary_handle_ids; + + float selectable_icon_size; + bool billboard_handle; + + bool valid; + bool hidden; + Vector instances; + Node3D *spatial_node; + + void _set_spatial_node(Node *p_node) { set_spatial_node(Object::cast_to(p_node)); } + +protected: + static void _bind_methods(); + + EditorNode3DGizmoPlugin *gizmo_plugin; + +public: + void add_lines(const Vector &p_lines, const Ref &p_material, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1)); + void add_vertices(const Vector &p_vertices, const Ref &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1)); + void add_mesh(const Ref &p_mesh, const Ref &p_material = Ref(), const Transform3D &p_xform = Transform3D(), const Ref &p_skin_reference = Ref()); + void add_collision_segments(const Vector &p_lines); + void add_collision_triangles(const Ref &p_tmesh); + void add_unscaled_billboard(const Ref &p_material, float p_scale = 1, const Color &p_modulate = Color(1, 1, 1)); + void add_handles(const Vector &p_handles, const Ref &p_material, const Vector &p_ids = Vector(), bool p_billboard = false, bool p_secondary = false); + void add_solid_box(Ref &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 subgizmos_intersect_frustum(const Camera3D *p_camera, const Vector &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 &p_ids, const Vector &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 get_plugin() const { return gizmo_plugin; } + bool intersect_frustum(const Camera3D *p_camera, const Vector &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 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 current_gizmos; + HashMap>> materials; + + static void _bind_methods(); + virtual bool has_gizmo(Node3D *p_spatial); + virtual Ref 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 &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 &p_texture = nullptr); + void add_material(const String &p_name, Ref p_material); + + Ref get_material(const String &p_name, const Ref &p_gizmo = Ref()); + + 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 subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector &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 &p_ids, const Vector &p_restore, bool p_cancel = false) const; + + Ref 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); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() 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 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(); +}; + +class AudioStreamPlayer3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(AudioStreamPlayer3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() 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 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(); +}; + +class Camera3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(Camera3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() 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 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(); +}; + +class MeshInstance3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(MeshInstance3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + bool can_be_hidden() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + MeshInstance3DGizmoPlugin(); +}; + +class OccluderInstance3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(OccluderInstance3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + OccluderInstance3DGizmoPlugin(); +}; + +class Sprite3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(Sprite3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + bool can_be_hidden() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + Sprite3DGizmoPlugin(); +}; + +class Position3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(Position3DGizmoPlugin, EditorNode3DGizmoPlugin); + + Ref pos3d_mesh; + Vector cursor_points; + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + Position3DGizmoPlugin(); +}; + +class Skeleton3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(Skeleton3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + Skeleton3DGizmoPlugin(); +}; + +class PhysicalBone3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(PhysicalBone3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + PhysicalBone3DGizmoPlugin(); +}; + +class RayCast3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(RayCast3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + RayCast3DGizmoPlugin(); +}; + +class SpringArm3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(SpringArm3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + SpringArm3DGizmoPlugin(); +}; + +class VehicleWheel3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(VehicleWheel3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + VehicleWheel3DGizmoPlugin(); +}; + +class SoftBody3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(SoftBody3DGizmoPlugin, 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_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(); +}; + +class VisibleOnScreenNotifier3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(VisibleOnScreenNotifier3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) 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(); +}; + +class CPUParticles3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(CPUParticles3DGizmoPlugin, 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; + CPUParticles3DGizmoPlugin(); +}; + +class GPUParticles3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(GPUParticles3DGizmoPlugin, 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_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(); +}; + +class GPUParticlesCollision3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(GPUParticlesCollision3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) 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(); +}; + +class ReflectionProbeGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(ReflectionProbeGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) 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(); +}; + +class DecalGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(DecalGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) 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(); +}; + +class VoxelGIGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(VoxelGIGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) 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(); +}; + +class LightmapGIGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(LightmapGIGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) 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(); +}; + +class LightmapProbeGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(LightmapProbeGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) 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(); +}; + +class CollisionObject3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(CollisionObject3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + CollisionObject3DGizmoPlugin(); +}; + +class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) 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(); +}; + +class CollisionPolygon3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(CollisionPolygon3DGizmoPlugin, EditorNode3DGizmoPlugin); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + CollisionPolygon3DGizmoPlugin(); +}; + +class NavigationRegion3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(NavigationRegion3DGizmoPlugin, EditorNode3DGizmoPlugin); + + struct _EdgeKey { + Vector3 from; + Vector3 to; + + bool operator<(const _EdgeKey &p_with) const { return from == p_with.from ? to < p_with.to : from < p_with.from; } + }; + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + NavigationRegion3DGizmoPlugin(); +}; + +class JointGizmosDrawer { +public: + static Basis look_body(const Transform3D &p_joint_transform, const Transform3D &p_body_transform); + static Basis look_body_toward(Vector3::Axis p_axis, const Transform3D &joint_transform, const Transform3D &body_transform); + static Basis look_body_toward_x(const Transform3D &p_joint_transform, const Transform3D &p_body_transform); + static Basis look_body_toward_y(const Transform3D &p_joint_transform, const Transform3D &p_body_transform); + /// Special function just used for physics joints, it returns a basis constrained toward Joint Z axis + /// with axis X and Y that are looking toward the body and oriented toward up + static Basis look_body_toward_z(const Transform3D &p_joint_transform, const Transform3D &p_body_transform); + + // Draw circle around p_axis + static void draw_circle(Vector3::Axis p_axis, real_t p_radius, const Transform3D &p_offset, const Basis &p_base, real_t p_limit_lower, real_t p_limit_upper, Vector &r_points, bool p_inverse = false); + static void draw_cone(const Transform3D &p_offset, const Basis &p_base, real_t p_swing, real_t p_twist, Vector &r_points); +}; + +class Joint3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(Joint3DGizmoPlugin, EditorNode3DGizmoPlugin); + + Timer *update_timer; + uint64_t update_idx = 0; + + void incremental_update_gizmos(); + +public: + bool has_gizmo(Node3D *p_spatial) override; + String get_gizmo_name() const override; + int get_priority() const override; + void redraw(EditorNode3DGizmo *p_gizmo) override; + + static void CreatePinJointGizmo(const Transform3D &p_offset, Vector &r_cursor_points); + static void CreateHingeJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_limit_lower, real_t p_limit_upper, bool p_use_limit, Vector &r_common_points, Vector *r_body_a_points, Vector *r_body_b_points); + static void CreateSliderJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_angular_limit_lower, real_t p_angular_limit_upper, real_t p_linear_limit_lower, real_t p_linear_limit_upper, Vector &r_points, Vector *r_body_a_points, Vector *r_body_b_points); + static void CreateConeTwistJointGizmo(const Transform3D &p_offset, const Transform3D &p_trs_joint, const Transform3D &p_trs_body_a, const Transform3D &p_trs_body_b, real_t p_swing, real_t p_twist, Vector *r_body_a_points, Vector *r_body_b_points); + static void CreateGeneric6DOFJointGizmo( + const Transform3D &p_offset, + const Transform3D &p_trs_joint, + const Transform3D &p_trs_body_a, + const Transform3D &p_trs_body_b, + real_t p_angular_limit_lower_x, + real_t p_angular_limit_upper_x, + real_t p_linear_limit_lower_x, + real_t p_linear_limit_upper_x, + bool p_enable_angular_limit_x, + bool p_enable_linear_limit_x, + real_t p_angular_limit_lower_y, + real_t p_angular_limit_upper_y, + real_t p_linear_limit_lower_y, + real_t p_linear_limit_upper_y, + bool p_enable_angular_limit_y, + bool p_enable_linear_limit_y, + real_t p_angular_limit_lower_z, + real_t p_angular_limit_upper_z, + real_t p_linear_limit_lower_z, + real_t p_linear_limit_upper_z, + bool p_enable_angular_limit_z, + bool p_enable_linear_limit_z, + Vector &r_points, + Vector *r_body_a_points, + Vector *r_body_b_points); + + Joint3DGizmoPlugin(); +}; + +#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(); + _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(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(ObjectDB::get_instance(clicked)); +void Node3DEditorViewport::_select_clicked(bool p_allow_locked) { + Node *node = Object::cast_to(ObjectDB::get_instance(clicked)); Node3D *selected = Object::cast_to(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(ObjectDB::get_instance(instances[i])); @@ -523,38 +518,40 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, b continue; } - Ref seg = spat->get_gizmo(); + Vector> gizmos = spat->get_gizmos(); - if ((!seg.is_valid()) || found_gizmos.has(seg)) { - continue; - } + for (int j = 0; j < gizmos.size(); j++) { + Ref 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(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(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 instances = RenderingServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world_3d()->get_scenario()); - Set> found_gizmos; - - r_includes_current = false; + Set found_nodes; for (int i = 0; i < instances.size(); i++) { Node3D *spat = Object::cast_to(ObjectDB::get_instance(instances[i])); @@ -587,53 +576,48 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, bool &r_inclu continue; } - Ref 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> gizmos = spat->get_gizmos(); + for (int j = 0; j < gizmos.size(); j++) { + Ref 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(single_selected); + + Ref old_gizmo; + if (!clicked_wants_append) { + se->subgizmos.clear(); + old_gizmo = se->gizmo; + se->gizmo.unref(); + } + + bool found_subgizmos = false; + Vector> gizmos = single_selected->get_gizmos(); + for (int j = 0; j < gizmos.size(); j++) { + Ref seg = gizmos[j]; + if (!seg.is_valid()) { + continue; + } + + if (se->gizmo.is_valid() && se->gizmo != seg) { + continue; + } + + Vector 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 instances = RenderingServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world_3d()->get_scenario()); + Set found_nodes; Vector 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(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 seg = sp->get_gizmo(); + Vector> gizmos = sp->get_gizmos(); + for (int j = 0; j < gizmos.size(); j++) { + Ref 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 &selection = editor_selection->get_selected_node_list(); + Node3D *selected = spatial_editor->get_single_selected_node(); + Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data(selected) : nullptr; - for (List::Element *E = selection.front(); E; E = E->next()) { - Node3D *sp = Object::cast_to(E->get()); - if (!sp) { - continue; + if (se && se->gizmo.is_valid()) { + for (Map::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 &selection = editor_selection->get_selected_node_list(); - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) { - continue; - } + for (List::Element *E = selection.front(); E; E = E->next()) { + Node3D *sp = Object::cast_to(E->get()); + if (!sp) { + continue; + } + + Node3DEditorSelectedItem *sel_item = editor_selection->get_node_editor_data(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 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 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 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 &p_event) { continue; } - sp->set_global_transform(se->original); + if (se->gizmo.is_valid()) { + Vector ids; + Vector restore; + + for (Map::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 &p_event) { can_select_gizmos = view_menu->get_popup()->is_item_checked(idx); } - if (can_select_gizmos && spatial_editor->get_selected()) { - Ref 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(spatial_editor->get_single_selected_node()); + Vector> gizmos = spatial_editor->get_single_selected_node()->get_gizmos(); + + bool intersected_handle = false; + for (int i = 0; i < gizmos.size(); i++) { + Ref 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(); + } 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 &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(ObjectDB::get_instance(clicked)); - if (spa) { - Ref 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 &p_event) { _edit.gizmo = Ref(); 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(selected) : nullptr; - List &selection = editor_selection->get_selected_node_list(); + if (se && se->gizmo.is_valid()) { + Vector ids; + Vector restore; - for (List::Element *E = selection.front(); E; E = E->next()) { - Node3D *sp = Object::cast_to(E->get()); - if (!sp) { - continue; + for (Map::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(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 &selection = editor_selection->get_selected_node_list(); + + for (List::Element *E = selection.front(); E; E = E->next()) { + Node3D *sp = Object::cast_to(E->get()); + if (!sp) { + continue; + } + + Node3DEditorSelectedItem *sel_item = editor_selection->get_node_editor_data(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 &p_event) { if (m.is_valid()) { _edit.mouse_pos = m->get_position(); - if (spatial_editor->get_selected()) { - Ref 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> gizmos = spatial_editor->get_single_selected_node()->get_gizmos(); + + Ref found_gizmo; + int found_handle = -1; + + for (int i = 0; i < gizmos.size(); i++) { + Ref 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 &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 &p_event) { motion = Vector3(scale, scale, scale); } - List &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 &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 &selection = editor_selection->get_selected_node_list(); for (List::Element *E = selection.front(); E; E = E->next()) { Node3D *sp = Object::cast_to(E->get()); if (!sp) { @@ -1597,44 +1786,22 @@ void Node3DEditorViewport::_sinput(const Ref &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::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 &p_event) { } } - List &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 &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 &selection = editor_selection->get_selected_node_list(); for (List::Element *E = selection.front(); E; E = E->next()) { Node3D *sp = Object::cast_to(E->get()); if (!sp) { @@ -1719,30 +1885,20 @@ void Node3DEditorViewport::_sinput(const Ref &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::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 &p_event) { set_message(vformat(TTR("Rotating %s degrees."), String::num(angle, snap_step_decimals))); angle = Math::deg2rad(angle); - List &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 &selection = editor_selection->get_selected_node_list(); for (List::Element *E = selection.front(); E; E = E->next()) { Node3D *sp = Object::cast_to(E->get()); if (!sp) { @@ -1815,32 +1970,24 @@ void Node3DEditorViewport::_sinput(const Ref &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::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::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 seg = mesh_instance->get_gizmo(); + Vector> gizmos = mesh_instance->get_gizmos(); - if ((!seg.is_valid()) || found_gizmos.has(seg)) { - continue; - } + for (int j = 0; j < gizmos.size(); j++) { + Ref 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 &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::Element *E = selection.front(); E; E = E->next()) { - Node3D *sp = Object::cast_to(E->get()); - if (!sp) { - continue; - } + Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data(selected) : nullptr; - Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); - if (!se) { - continue; + if (se && se->gizmo.is_valid()) { + for (Map::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 &selection = editor_selection->get_selected_node_list(); + for (List::Element *E = selection.front(); E; E = E->next()) { + Node3D *sp = Object::cast_to(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(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(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 seg = selected->get_gizmo(); - if (seg.is_valid()) { + Vector> gizmos = selected->get_gizmos(); + for (int i = 0; i < gizmos.size(); i++) { + Ref seg = gizmos[i]; + if (!seg.is_valid()) { + continue; + } seg->set_selected(false); - selected->update_gizmo(); } + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(selected); + if (se) { + se->gizmo.unref(); + se->subgizmos.clear(); + } + + selected->update_gizmos(); } selected = p_spatial; - over_gizmo_handle = -1; + current_hover_gizmo = Ref(); + current_hover_gizmo_handle = -1; if (selected) { - Ref seg = selected->get_gizmo(); - if (seg.is_valid()) { + Vector> gizmos = selected->get_gizmos(); + for (int i = 0; i < gizmos.size(); i++) { + Ref 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> gizmos = selected->get_gizmos(); + for (int i = 0; i < gizmos.size(); i++) { + Ref seg = gizmos[i]; + if (!seg.is_valid()) { + continue; + } + seg->set_selected(false); + } + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(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(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(selected) : nullptr; + if (se) { + return se->gizmo == p_gizmo; + } + return false; +} + +Vector Node3DEditor::get_subgizmo_selection() { + Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data(selected) : nullptr; + + Vector ret; + if (se) { + for (Map::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 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 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(p_obj); + } else { + sp = selected; + } + + if (!sp) { + return; + } + + Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data(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(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> mats; - - for (int i = 0; i < 4; i++) { - bool selected = i % 2 == 1; - bool instantiated = i < 2; - - Ref material = Ref(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 &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> icons; - - for (int i = 0; i < 4; i++) { - bool selected = i % 2 == 1; - bool instantiated = i < 2; - - Ref icon = Ref(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 &p_icon) { - Ref handle_material = Ref(memnew(StandardMaterial3D)); - - handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); - Ref 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>(); - materials[p_name].push_back(handle_material); -} - -void EditorNode3DGizmoPlugin::add_material(const String &p_name, Ref p_material) { - materials[p_name] = Vector>(); - materials[p_name].push_back(p_material); -} - -Ref EditorNode3DGizmoPlugin::get_material(const String &p_name, const Ref &p_gizmo) { - ERR_FAIL_COND_V(!materials.has(p_name), Ref()); - ERR_FAIL_COND_V(materials[p_name].size() == 0, Ref()); - - 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 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 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 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())); - - 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 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 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 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 mesh; - Ref material; - Ref 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 collision_segments; - Ref collision_mesh; - - struct Handle { - Vector3 pos; - bool billboard = false; - }; - - Vector handles; - Vector secondary_handles; - float selectable_icon_size; - bool billboard_handle; - - bool valid; - bool hidden; - Node3D *base; - Vector instances; - Node3D *spatial_node; - EditorNode3DGizmoPlugin *gizmo_plugin; - - void _set_spatial_node(Node *p_node) { set_spatial_node(Object::cast_to(p_node)); } - -protected: - static void _bind_methods(); - -public: - void add_lines(const Vector &p_lines, const Ref &p_material, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1)); - void add_vertices(const Vector &p_vertices, const Ref &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1)); - void add_mesh(const Ref &p_mesh, bool p_billboard = false, const Ref &p_skin_reference = Ref(), const Ref &p_material = Ref()); - void add_collision_segments(const Vector &p_lines); - void add_collision_triangles(const Ref &p_tmesh); - void add_unscaled_billboard(const Ref &p_material, float p_scale = 1, const Color &p_modulate = Color(1, 1, 1)); - void add_handles(const Vector &p_handles, const Ref &p_material, bool p_billboard = false, bool p_secondary = false); - void add_solid_box(Ref &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 get_plugin() const { return gizmo_plugin; } - Vector3 get_handle_pos(int p_idx) const; - bool intersect_frustum(const Camera3D *p_camera, const Vector &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 p_event, const Vector2 &p_relative); void _nav_zoom(Ref 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 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 gizmo; + Map subgizmos; // map ID -> initial transform Node3DEditorSelectedItem() { sp = nullptr; @@ -617,7 +531,9 @@ private: Ref plane_gizmo_color_hl[3]; Ref rotate_gizmo_color_hl[3]; - int over_gizmo_handle; + Ref 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 get_subgizmo_selection(); + + Ref get_current_hover_gizmo() const { return current_hover_gizmo; } + void set_current_hover_gizmo(Ref 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 current_gizmos; - HashMap>> materials; - - static void _bind_methods(); - virtual bool has_gizmo(Node3D *p_spatial); - virtual Ref 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 &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 &p_texture = nullptr); - void add_material(const String &p_name, Ref p_material); - - Ref get_material(const String &p_name, const Ref &p_gizmo = Ref()); - - 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 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 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 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 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 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(), 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(p_gizmo->get_spatial_node()); if (Object::cast_to(cs)) { @@ -60,17 +62,17 @@ String CSGShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, } if (Object::cast_to(cs)) { - return p_idx == 0 ? "Radius" : "Height"; + return p_id == 0 ? "Radius" : "Height"; } if (Object::cast_to(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(p_gizmo->get_spatial_node()); if (Object::cast_to(cs)) { @@ -85,18 +87,18 @@ Variant CSGShape3DGizmoPlugin::get_handle_value(EditorNode3DGizmo *p_gizmo, int if (Object::cast_to(cs)) { CSGCylinder3D *s = Object::cast_to(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(cs)) { CSGTorus3D *s = Object::cast_to(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(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(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(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(p_gizmo->get_spatial_node()); if (Object::cast_to(cs)) { @@ -227,7 +229,7 @@ void CSGShape3DGizmoPlugin::commit_handle(EditorNode3DGizmo *p_gizmo, int p_idx, if (Object::cast_to(cs)) { CSGCylinder3D *s = Object::cast_to(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(cs)) { CSGTorus3D *s = Object::cast_to(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(), solid_material); + p_gizmo->add_mesh(mesh, solid_material); } if (Object::cast_to(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 &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 &p_material) { material = p_material; _make_dirty(); - update_gizmo(); + update_gizmos(); } Ref 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 &p_polygon) { polygon = p_polygon; _make_dirty(); - update_gizmo(); + update_gizmos(); } Vector CSGPolygon3D::get_polygon() const { @@ -2258,7 +2258,7 @@ Vector 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 &p_polygon) { _build_polygon(); } update_configuration_warnings(); - update_gizmo(); + update_gizmos(); } Vector 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 CollisionShape3D::get_configuration_warnings() const { @@ -166,7 +166,7 @@ void CollisionShape3D::set_shape(const Ref &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 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 &p_data) { } } - update_gizmo(); + update_gizmos(); } Ref 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 &p_mesh) { set_base(RID()); } - update_gizmo(); + update_gizmos(); notify_property_list_changed(); } @@ -356,7 +356,7 @@ Ref 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 &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 &p_gizmo) { +void Node3D::add_gizmo(Ref 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 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 Node3D::get_gizmo() const { +Vector> Node3D::get_gizmos() const { #ifdef TOOLS_ENABLED - return data.gizmo; + return data.gizmos; #else - return Ref(); + return Vector>(); #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(); +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 gizmo; - bool gizmo_disabled = false; - bool gizmo_dirty = false; + Vector> 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 &p_gizmo); - Ref get_gizmo() const; + void set_disable_gizmos(bool p_enabled); + void update_gizmos(); + void clear_subgizmo_selection(); + Vector> get_gizmos() const; + Array get_gizmos_bind() const; + void add_gizmo(Ref p_gizmo); + void remove_gizmo(Ref 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 &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 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; -- cgit v1.2.3