Path: blob/master/editor/scene/3d/node_3d_editor_gizmos.cpp
9903 views
/**************************************************************************/1/* node_3d_editor_gizmos.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "node_3d_editor_gizmos.h"3132#include "core/math/geometry_2d.h"33#include "core/math/geometry_3d.h"34#include "editor/editor_node.h"35#include "editor/editor_string_names.h"36#include "editor/scene/3d/node_3d_editor_plugin.h"37#include "editor/settings/editor_settings.h"38#include "scene/resources/3d/primitive_meshes.h"3940#define HANDLE_HALF_SIZE 9.54142bool EditorNode3DGizmo::is_editable() const {43ERR_FAIL_NULL_V(spatial_node, false);44Node *edited_root = spatial_node->get_tree()->get_edited_scene_root();45if (spatial_node == edited_root) {46return true;47}48if (spatial_node->get_owner() == edited_root) {49return true;50}5152if (edited_root->is_editable_instance(spatial_node->get_owner())) {53return true;54}5556return false;57}5859void EditorNode3DGizmo::clear() {60ERR_FAIL_NULL(RenderingServer::get_singleton());61for (int i = 0; i < instances.size(); i++) {62if (instances[i].instance.is_valid()) {63RS::get_singleton()->free(instances[i].instance);64}65}6667billboard_handle = false;68collision_segments.clear();69collision_meshes.clear();70instances.clear();71handles.clear();72handle_ids.clear();73secondary_handles.clear();74secondary_handle_ids.clear();75}7677void EditorNode3DGizmo::redraw() {78if (!GDVIRTUAL_CALL(_redraw)) {79ERR_FAIL_NULL(gizmo_plugin);80gizmo_plugin->redraw(this);81}8283_update_bvh();8485if (Node3DEditor::get_singleton()->is_current_selected_gizmo(this)) {86Node3DEditor::get_singleton()->update_transform_gizmo();87}88}8990String EditorNode3DGizmo::get_handle_name(int p_id, bool p_secondary) const {91String ret;92if (GDVIRTUAL_CALL(_get_handle_name, p_id, p_secondary, ret)) {93return ret;94}9596ERR_FAIL_NULL_V(gizmo_plugin, "");97return gizmo_plugin->get_handle_name(this, p_id, p_secondary);98}99100bool EditorNode3DGizmo::is_handle_highlighted(int p_id, bool p_secondary) const {101bool success;102if (GDVIRTUAL_CALL(_is_handle_highlighted, p_id, p_secondary, success)) {103return success;104}105106ERR_FAIL_NULL_V(gizmo_plugin, false);107return gizmo_plugin->is_handle_highlighted(this, p_id, p_secondary);108}109110Variant EditorNode3DGizmo::get_handle_value(int p_id, bool p_secondary) const {111Variant value;112if (GDVIRTUAL_CALL(_get_handle_value, p_id, p_secondary, value)) {113return value;114}115116ERR_FAIL_NULL_V(gizmo_plugin, Variant());117return gizmo_plugin->get_handle_value(this, p_id, p_secondary);118}119120void EditorNode3DGizmo::begin_handle_action(int p_id, bool p_secondary) {121if (GDVIRTUAL_CALL(_begin_handle_action, p_id, p_secondary)) {122return;123}124125ERR_FAIL_NULL(gizmo_plugin);126gizmo_plugin->begin_handle_action(this, p_id, p_secondary);127}128129void EditorNode3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {130if (GDVIRTUAL_CALL(_set_handle, p_id, p_secondary, p_camera, p_point)) {131return;132}133134ERR_FAIL_NULL(gizmo_plugin);135gizmo_plugin->set_handle(this, p_id, p_secondary, p_camera, p_point);136}137138void EditorNode3DGizmo::commit_handle(int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {139if (GDVIRTUAL_CALL(_commit_handle, p_id, p_secondary, p_restore, p_cancel)) {140return;141}142143ERR_FAIL_NULL(gizmo_plugin);144gizmo_plugin->commit_handle(this, p_id, p_secondary, p_restore, p_cancel);145}146147int EditorNode3DGizmo::subgizmos_intersect_ray(Camera3D *p_camera, const Vector2 &p_point) const {148int id;149if (GDVIRTUAL_CALL(_subgizmos_intersect_ray, p_camera, p_point, id)) {150return id;151}152153ERR_FAIL_NULL_V(gizmo_plugin, -1);154return gizmo_plugin->subgizmos_intersect_ray(this, p_camera, p_point);155}156157Vector<int> EditorNode3DGizmo::subgizmos_intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum) const {158TypedArray<Plane> frustum;159frustum.resize(p_frustum.size());160for (int i = 0; i < p_frustum.size(); i++) {161frustum[i] = p_frustum[i];162}163Vector<int> ret;164if (GDVIRTUAL_CALL(_subgizmos_intersect_frustum, p_camera, frustum, ret)) {165return ret;166}167168ERR_FAIL_NULL_V(gizmo_plugin, Vector<int>());169return gizmo_plugin->subgizmos_intersect_frustum(this, p_camera, p_frustum);170}171172Transform3D EditorNode3DGizmo::get_subgizmo_transform(int p_id) const {173Transform3D ret;174if (GDVIRTUAL_CALL(_get_subgizmo_transform, p_id, ret)) {175return ret;176}177178ERR_FAIL_NULL_V(gizmo_plugin, Transform3D());179return gizmo_plugin->get_subgizmo_transform(this, p_id);180}181182void EditorNode3DGizmo::set_subgizmo_transform(int p_id, Transform3D p_transform) {183if (GDVIRTUAL_CALL(_set_subgizmo_transform, p_id, p_transform)) {184return;185}186187ERR_FAIL_NULL(gizmo_plugin);188gizmo_plugin->set_subgizmo_transform(this, p_id, p_transform);189}190191void EditorNode3DGizmo::commit_subgizmos(const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {192TypedArray<Transform3D> restore;193restore.resize(p_restore.size());194for (int i = 0; i < p_restore.size(); i++) {195restore[i] = p_restore[i];196}197198if (GDVIRTUAL_CALL(_commit_subgizmos, p_ids, restore, p_cancel)) {199return;200}201202ERR_FAIL_NULL(gizmo_plugin);203gizmo_plugin->commit_subgizmos(this, p_ids, p_restore, p_cancel);204}205206void EditorNode3DGizmo::set_node_3d(Node3D *p_node) {207ERR_FAIL_NULL(p_node);208spatial_node = p_node;209}210211void EditorNode3DGizmo::Instance::create_instance(Node3D *p_base, bool p_hidden) {212instance = RS::get_singleton()->instance_create2(mesh->get_rid(), p_base->get_world_3d()->get_scenario());213RS::get_singleton()->instance_attach_object_instance_id(instance, p_base->get_instance_id());214if (skin_reference.is_valid()) {215RS::get_singleton()->instance_attach_skeleton(instance, skin_reference->get_skeleton());216}217if (extra_margin) {218RS::get_singleton()->instance_set_extra_visibility_margin(instance, 1);219}220RS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, RS::SHADOW_CASTING_SETTING_OFF);221int layer = p_hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER;222RS::get_singleton()->instance_set_layer_mask(instance, layer); //gizmos are 26223RS::get_singleton()->instance_geometry_set_flag(instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);224RS::get_singleton()->instance_geometry_set_flag(instance, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false);225}226227void EditorNode3DGizmo::add_mesh(const Ref<Mesh> &p_mesh, const Ref<Material> &p_material, const Transform3D &p_xform, const Ref<SkinReference> &p_skin_reference) {228ERR_FAIL_NULL(spatial_node);229ERR_FAIL_COND_MSG(p_mesh.is_null(), "EditorNode3DGizmo.add_mesh() requires a valid Mesh resource.");230231Instance ins;232233ins.mesh = p_mesh;234ins.skin_reference = p_skin_reference;235ins.material = p_material;236ins.xform = p_xform;237if (valid) {238ins.create_instance(spatial_node, hidden);239RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform() * ins.xform);240if (ins.material.is_valid()) {241RS::get_singleton()->instance_geometry_set_material_override(ins.instance, p_material->get_rid());242}243}244245instances.push_back(ins);246}247248void EditorNode3DGizmo::_update_bvh() {249ERR_FAIL_NULL(spatial_node);250251Transform3D transform = spatial_node->get_global_transform();252253float effective_icon_size = selectable_icon_size > 0.0f ? selectable_icon_size : 0.0f;254Vector3 icon_size_vector3 = Vector3(effective_icon_size, effective_icon_size, effective_icon_size);255AABB aabb(spatial_node->get_position() - icon_size_vector3 * 100.0f, icon_size_vector3 * 200.0f);256257for (const Vector3 &segment_end : collision_segments) {258aabb.expand_to(transform.xform(segment_end));259}260261if (!collision_meshes.is_empty()) {262for (Ref<TriangleMesh> collision_mesh : collision_meshes) {263if (collision_mesh.is_valid()) {264for (const Face3 &face : collision_mesh->get_faces()) {265aabb.expand_to(transform.xform(face.vertex[0]));266aabb.expand_to(transform.xform(face.vertex[1]));267aabb.expand_to(transform.xform(face.vertex[2]));268}269}270}271}272273Node3DEditor::get_singleton()->update_gizmo_bvh_node(274bvh_node_id,275aabb);276}277278void EditorNode3DGizmo::add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard, const Color &p_modulate) {279add_vertices(p_lines, p_material, Mesh::PRIMITIVE_LINES, p_billboard, p_modulate);280}281282void EditorNode3DGizmo::add_vertices(const Vector<Vector3> &p_vertices, const Ref<Material> &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard, const Color &p_modulate) {283if (p_vertices.is_empty()) {284return;285}286287ERR_FAIL_NULL(spatial_node);288Instance ins;289290Ref<ArrayMesh> mesh = memnew(ArrayMesh);291Array a;292a.resize(Mesh::ARRAY_MAX);293294a[Mesh::ARRAY_VERTEX] = p_vertices;295296Vector<Color> color;297color.resize(p_vertices.size());298const Color vertex_color = (is_selected() ? Color(1, 1, 1, 0.8) : Color(1, 1, 1, 0.2)) * p_modulate;299{300Color *w = color.ptrw();301for (int i = 0; i < p_vertices.size(); i++) {302w[i] = vertex_color;303}304}305306a[Mesh::ARRAY_COLOR] = color;307308mesh->add_surface_from_arrays(p_primitive_type, a);309mesh->surface_set_material(0, p_material);310311if (p_billboard) {312float md = 0;313for (int i = 0; i < p_vertices.size(); i++) {314md = MAX(0, p_vertices[i].length());315}316if (md) {317mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0));318}319}320321ins.mesh = mesh;322if (valid) {323ins.create_instance(spatial_node, hidden);324RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform());325}326327instances.push_back(ins);328}329330void EditorNode3DGizmo::add_unscaled_billboard(const Ref<Material> &p_material, real_t p_scale, const Color &p_modulate) {331ERR_FAIL_NULL(spatial_node);332Instance ins;333334Vector<Vector3> vs = {335Vector3(-p_scale, p_scale, 0),336Vector3(p_scale, p_scale, 0),337Vector3(p_scale, -p_scale, 0),338Vector3(-p_scale, -p_scale, 0)339};340341Vector<Vector2> uv = {342Vector2(0, 0),343Vector2(1, 0),344Vector2(1, 1),345Vector2(0, 1)346};347348Vector<Color> colors = {349p_modulate,350p_modulate,351p_modulate,352p_modulate353};354355Vector<int> indices = { 0, 1, 2, 0, 2, 3 };356357Ref<ArrayMesh> mesh = memnew(ArrayMesh);358Array a;359a.resize(Mesh::ARRAY_MAX);360a[Mesh::ARRAY_VERTEX] = vs;361a[Mesh::ARRAY_TEX_UV] = uv;362a[Mesh::ARRAY_INDEX] = indices;363a[Mesh::ARRAY_COLOR] = colors;364mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a);365mesh->surface_set_material(0, p_material);366367float md = 0;368for (int i = 0; i < vs.size(); i++) {369md = MAX(0, vs[i].length());370}371if (md) {372mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0));373}374375selectable_icon_size = p_scale;376mesh->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));377378ins.mesh = mesh;379if (valid) {380ins.create_instance(spatial_node, hidden);381RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform());382}383384selectable_icon_size = p_scale;385386instances.push_back(ins);387}388389void EditorNode3DGizmo::add_collision_triangles(const Ref<TriangleMesh> &p_tmesh) {390collision_meshes.push_back(p_tmesh);391}392393void EditorNode3DGizmo::add_collision_segments(const Vector<Vector3> &p_lines) {394int from = collision_segments.size();395collision_segments.resize(from + p_lines.size());396for (int i = 0; i < p_lines.size(); i++) {397collision_segments.write[from + i] = p_lines[i];398}399}400401void EditorNode3DGizmo::add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, const Vector<int> &p_ids, bool p_billboard, bool p_secondary) {402billboard_handle = p_billboard;403404if (!is_selected() || !is_editable()) {405return;406}407408ERR_FAIL_NULL(spatial_node);409410Vector<Vector3> &handle_list = p_secondary ? secondary_handles : handles;411Vector<int> &id_list = p_secondary ? secondary_handle_ids : handle_ids;412413if (p_ids.is_empty()) {414ERR_FAIL_COND_MSG(!id_list.is_empty(), "IDs must be provided for all handles, as handles with IDs already exist.");415} else {416ERR_FAIL_COND_MSG(p_handles.size() != p_ids.size(), "The number of IDs should be the same as the number of handles.");417}418419bool is_current_hover_gizmo = Node3DEditor::get_singleton()->get_current_hover_gizmo() == this;420bool current_hover_handle_secondary;421int current_hover_handle = Node3DEditor::get_singleton()->get_current_hover_gizmo_handle(current_hover_handle_secondary);422423Instance ins;424Ref<ArrayMesh> mesh = memnew(ArrayMesh);425426Array a;427a.resize(RS::ARRAY_MAX);428a[RS::ARRAY_VERTEX] = p_handles;429Vector<Color> colors;430{431colors.resize(p_handles.size());432Color *w = colors.ptrw();433for (int i = 0; i < p_handles.size(); i++) {434int id = p_ids.is_empty() ? i : p_ids[i];435436Color col(1, 1, 1, 1);437if (is_handle_highlighted(id, p_secondary)) {438col = Color(0, 0, 1, 0.9);439}440441if (!is_current_hover_gizmo || current_hover_handle != id || p_secondary != current_hover_handle_secondary) {442col.a = 0.8;443}444445w[i] = col;446}447}448a[RS::ARRAY_COLOR] = colors;449mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a);450mesh->surface_set_material(0, p_material);451452if (p_billboard) {453float md = 0;454for (int i = 0; i < p_handles.size(); i++) {455md = MAX(0, p_handles[i].length());456}457if (md) {458mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0));459}460}461462ins.mesh = mesh;463ins.extra_margin = true;464if (valid) {465ins.create_instance(spatial_node, hidden);466RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform());467}468instances.push_back(ins);469470int current_size = handle_list.size();471handle_list.resize(current_size + p_handles.size());472for (int i = 0; i < p_handles.size(); i++) {473handle_list.write[current_size + i] = p_handles[i];474}475476if (!p_ids.is_empty()) {477current_size = id_list.size();478id_list.resize(current_size + p_ids.size());479for (int i = 0; i < p_ids.size(); i++) {480id_list.write[current_size + i] = p_ids[i];481}482}483}484485void EditorNode3DGizmo::add_solid_box(const Ref<Material> &p_material, Vector3 p_size, Vector3 p_position, const Transform3D &p_xform) {486ERR_FAIL_NULL(spatial_node);487488Array arrays;489arrays.resize(RS::ARRAY_MAX);490BoxMesh::create_mesh_array(arrays, p_size);491492PackedVector3Array vertex = arrays[RS::ARRAY_VERTEX];493Vector3 *w = vertex.ptrw();494495for (int i = 0; i < vertex.size(); ++i) {496w[i] += p_position;497}498499arrays[RS::ARRAY_VERTEX] = vertex;500501Ref<ArrayMesh> m;502m.instantiate();503m->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);504add_mesh(m, p_material, p_xform);505}506507bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum) {508ERR_FAIL_NULL_V(spatial_node, false);509ERR_FAIL_COND_V(!valid, false);510511if (hidden && !gizmo_plugin->is_selectable_when_hidden()) {512return false;513}514515if (selectable_icon_size > 0.0f) {516Vector3 origin = spatial_node->get_global_transform().get_origin();517518const Plane *p = p_frustum.ptr();519int fc = p_frustum.size();520521bool any_out = false;522523for (int j = 0; j < fc; j++) {524if (p[j].is_point_over(origin)) {525any_out = true;526break;527}528}529530return !any_out;531}532533if (collision_segments.size()) {534const Plane *p = p_frustum.ptr();535int fc = p_frustum.size();536537int vc = collision_segments.size();538const Vector3 *vptr = collision_segments.ptr();539Transform3D t = spatial_node->get_global_transform();540541bool any_out = false;542for (int j = 0; j < fc; j++) {543for (int i = 0; i < vc; i++) {544Vector3 v = t.xform(vptr[i]);545if (p[j].is_point_over(v)) {546any_out = true;547break;548}549}550if (any_out) {551break;552}553}554555if (!any_out) {556return true;557}558}559560if (!collision_meshes.is_empty()) {561Transform3D t = spatial_node->get_global_transform();562563Vector3 mesh_scale = t.get_basis().get_scale();564t.orthonormalize();565566Transform3D it = t.affine_inverse();567568Vector<Plane> transformed_frustum;569int plane_count = p_frustum.size();570transformed_frustum.resize(plane_count);571572for (int i = 0; i < plane_count; i++) {573transformed_frustum.write[i] = it.xform(p_frustum[i]);574}575576Vector<Vector3> convex_points = Geometry3D::compute_convex_mesh_points(transformed_frustum.ptr(), plane_count);577578for (Ref<TriangleMesh> collision_mesh : collision_meshes) {579if (collision_mesh.is_valid()) {580if (collision_mesh->inside_convex_shape(transformed_frustum.ptr(), plane_count, convex_points.ptr(), convex_points.size(), mesh_scale)) {581return true;582}583}584}585}586587return false;588}589590void EditorNode3DGizmo::handles_intersect_ray(Camera3D *p_camera, const Vector2 &p_point, bool p_shift_pressed, int &r_id, bool &r_secondary) {591r_id = -1;592r_secondary = false;593594ERR_FAIL_NULL(spatial_node);595ERR_FAIL_COND(!valid);596597if (hidden) {598return;599}600601Transform3D camera_xform = p_camera->get_global_transform();602Transform3D t = spatial_node->get_global_transform();603if (billboard_handle) {604t.set_look_at(t.origin, t.origin - camera_xform.basis.get_column(2), camera_xform.basis.get_column(1));605}606607float min_d = 1e20;608609for (int i = 0; i < secondary_handles.size(); i++) {610Vector3 hpos = t.xform(secondary_handles[i]);611Vector2 p = p_camera->unproject_position(hpos);612613if (p.distance_to(p_point) < HANDLE_HALF_SIZE) {614real_t dp = p_camera->get_transform().origin.distance_to(hpos);615if (dp < min_d) {616min_d = dp;617if (secondary_handle_ids.is_empty()) {618r_id = i;619} else {620r_id = secondary_handle_ids[i];621}622r_secondary = true;623}624}625}626627if (r_id != -1 && p_shift_pressed) {628return;629}630631min_d = 1e20;632633for (int i = 0; i < handles.size(); i++) {634Vector3 hpos = t.xform(handles[i]);635Vector2 p = p_camera->unproject_position(hpos);636637if (p.distance_to(p_point) < HANDLE_HALF_SIZE) {638real_t dp = p_camera->get_transform().origin.distance_to(hpos);639if (dp < min_d) {640min_d = dp;641if (handle_ids.is_empty()) {642r_id = i;643} else {644r_id = handle_ids[i];645}646r_secondary = false;647}648}649}650}651652bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal) {653ERR_FAIL_NULL_V(spatial_node, false);654ERR_FAIL_COND_V(!valid, false);655656if (hidden && !gizmo_plugin->is_selectable_when_hidden()) {657return false;658}659660if (selectable_icon_size > 0.0f) {661Transform3D t = spatial_node->get_global_transform();662Vector3 camera_position = p_camera->get_camera_transform().origin;663if (!camera_position.is_equal_approx(t.origin)) {664t.set_look_at(t.origin, camera_position);665}666667float scale = t.origin.distance_to(p_camera->get_camera_transform().origin);668669if (p_camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) {670float aspect = p_camera->get_viewport()->get_visible_rect().size.aspect();671float size = p_camera->get_size();672scale = size / aspect;673}674675Point2 center = p_camera->unproject_position(t.origin);676677Transform3D orig_camera_transform = p_camera->get_camera_transform();678679if (!orig_camera_transform.origin.is_equal_approx(t.origin) &&680Math::abs(orig_camera_transform.basis.get_column(Vector3::AXIS_Z).dot(Vector3(0, 1, 0))) < 0.99) {681p_camera->look_at(t.origin);682}683684Vector3 c0 = t.xform(Vector3(selectable_icon_size, selectable_icon_size, 0) * scale);685Vector3 c1 = t.xform(Vector3(-selectable_icon_size, -selectable_icon_size, 0) * scale);686687Point2 p0 = p_camera->unproject_position(c0);688Point2 p1 = p_camera->unproject_position(c1);689690p_camera->set_global_transform(orig_camera_transform);691692Rect2 rect(p0, (p1 - p0).abs());693694rect.set_position(center - rect.get_size() / 2.0);695696if (rect.has_point(p_point)) {697r_pos = t.origin;698r_normal = -p_camera->project_ray_normal(p_point);699return true;700}701}702703if (collision_segments.size()) {704Plane camp(-p_camera->get_transform().basis.get_column(2).normalized(), p_camera->get_transform().origin);705706int vc = collision_segments.size();707const Vector3 *vptr = collision_segments.ptr();708Transform3D t = spatial_node->get_global_transform();709if (billboard_handle) {710t.set_look_at(t.origin, t.origin - p_camera->get_transform().basis.get_column(2), p_camera->get_transform().basis.get_column(1));711}712713Vector3 cp;714float cpd = 1e20;715716for (int i = 0; i < vc / 2; i++) {717const Vector3 a = t.xform(vptr[i * 2 + 0]);718const Vector3 b = t.xform(vptr[i * 2 + 1]);719const Vector2 segment_a = p_camera->unproject_position(a);720const Vector2 segment_b = p_camera->unproject_position(b);721722Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, segment_a, segment_b);723724float pd = p.distance_to(p_point);725726if (pd < cpd) {727float d = segment_a.distance_to(segment_b);728Vector3 tcp;729if (d > 0) {730float d2 = segment_a.distance_to(p) / d;731tcp = a + (b - a) * d2;732733} else {734tcp = a;735}736737if (camp.distance_to(tcp) < p_camera->get_near()) {738continue;739}740cp = tcp;741cpd = pd;742}743}744745if (cpd < 8) {746r_pos = cp;747r_normal = -p_camera->project_ray_normal(p_point);748return true;749}750}751752if (!collision_meshes.is_empty()) {753Transform3D gt = spatial_node->get_global_transform();754755if (billboard_handle) {756gt.set_look_at(gt.origin, gt.origin - p_camera->get_transform().basis.get_column(2), p_camera->get_transform().basis.get_column(1));757}758759Transform3D ai = gt.affine_inverse();760Vector3 ray_from = ai.xform(p_camera->project_ray_origin(p_point));761Vector3 ray_dir = ai.basis.xform(p_camera->project_ray_normal(p_point)).normalized();762Vector3 rpos, rnorm;763764for (Ref<TriangleMesh> collision_mesh : collision_meshes) {765if (collision_mesh.is_valid()) {766if (collision_mesh->intersect_ray(ray_from, ray_dir, rpos, rnorm)) {767r_pos = gt.xform(rpos);768r_normal = gt.basis.xform(rnorm).normalized();769return true;770}771}772}773}774775return false;776}777778bool EditorNode3DGizmo::is_subgizmo_selected(int p_id) const {779Node3DEditor *ed = Node3DEditor::get_singleton();780ERR_FAIL_NULL_V(ed, false);781return ed->is_current_selected_gizmo(this) && ed->is_subgizmo_selected(p_id);782}783784Vector<int> EditorNode3DGizmo::get_subgizmo_selection() const {785Vector<int> ret;786787Node3DEditor *ed = Node3DEditor::get_singleton();788ERR_FAIL_NULL_V(ed, ret);789790if (ed->is_current_selected_gizmo(this)) {791ret = ed->get_subgizmo_selection();792}793794return ret;795}796797void EditorNode3DGizmo::create() {798ERR_FAIL_NULL(spatial_node);799ERR_FAIL_COND(valid);800valid = true;801802for (int i = 0; i < instances.size(); i++) {803instances.write[i].create_instance(spatial_node, hidden);804}805806bvh_node_id = Node3DEditor::get_singleton()->insert_gizmo_bvh_node(807spatial_node,808AABB(spatial_node->get_position(), Vector3(0, 0, 0)));809810transform();811}812813void EditorNode3DGizmo::transform() {814ERR_FAIL_NULL(spatial_node);815ERR_FAIL_COND(!valid);816for (int i = 0; i < instances.size(); i++) {817RS::get_singleton()->instance_set_transform(instances[i].instance, spatial_node->get_global_transform() * instances[i].xform);818}819820_update_bvh();821}822823void EditorNode3DGizmo::free() {824ERR_FAIL_NULL(RenderingServer::get_singleton());825ERR_FAIL_NULL(spatial_node);826ERR_FAIL_COND(!valid);827828for (int i = 0; i < instances.size(); i++) {829if (instances[i].instance.is_valid()) {830RS::get_singleton()->free(instances[i].instance);831}832instances.write[i].instance = RID();833}834835clear();836837Node3DEditor::get_singleton()->remove_gizmo_bvh_node(bvh_node_id);838bvh_node_id = DynamicBVH::ID();839840valid = false;841}842843void EditorNode3DGizmo::set_hidden(bool p_hidden) {844hidden = p_hidden;845int layer = hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER;846for (int i = 0; i < instances.size(); ++i) {847RS::get_singleton()->instance_set_layer_mask(instances[i].instance, layer);848}849}850851void EditorNode3DGizmo::set_plugin(EditorNode3DGizmoPlugin *p_plugin) {852gizmo_plugin = p_plugin;853}854855void EditorNode3DGizmo::_bind_methods() {856ClassDB::bind_method(D_METHOD("add_lines", "lines", "material", "billboard", "modulate"), &EditorNode3DGizmo::add_lines, DEFVAL(false), DEFVAL(Color(1, 1, 1)));857ClassDB::bind_method(D_METHOD("add_mesh", "mesh", "material", "transform", "skeleton"), &EditorNode3DGizmo::add_mesh, DEFVAL(Variant()), DEFVAL(Transform3D()), DEFVAL(Ref<SkinReference>()));858ClassDB::bind_method(D_METHOD("add_collision_segments", "segments"), &EditorNode3DGizmo::add_collision_segments);859ClassDB::bind_method(D_METHOD("add_collision_triangles", "triangles"), &EditorNode3DGizmo::add_collision_triangles);860ClassDB::bind_method(D_METHOD("add_unscaled_billboard", "material", "default_scale", "modulate"), &EditorNode3DGizmo::add_unscaled_billboard, DEFVAL(1), DEFVAL(Color(1, 1, 1)));861ClassDB::bind_method(D_METHOD("add_handles", "handles", "material", "ids", "billboard", "secondary"), &EditorNode3DGizmo::add_handles, DEFVAL(false), DEFVAL(false));862ClassDB::bind_method(D_METHOD("set_node_3d", "node"), &EditorNode3DGizmo::_set_node_3d);863ClassDB::bind_method(D_METHOD("get_node_3d"), &EditorNode3DGizmo::get_node_3d);864ClassDB::bind_method(D_METHOD("get_plugin"), &EditorNode3DGizmo::get_plugin);865ClassDB::bind_method(D_METHOD("clear"), &EditorNode3DGizmo::clear);866ClassDB::bind_method(D_METHOD("set_hidden", "hidden"), &EditorNode3DGizmo::set_hidden);867ClassDB::bind_method(D_METHOD("is_subgizmo_selected", "id"), &EditorNode3DGizmo::is_subgizmo_selected);868ClassDB::bind_method(D_METHOD("get_subgizmo_selection"), &EditorNode3DGizmo::get_subgizmo_selection);869870GDVIRTUAL_BIND(_redraw);871GDVIRTUAL_BIND(_get_handle_name, "id", "secondary");872GDVIRTUAL_BIND(_is_handle_highlighted, "id", "secondary");873874GDVIRTUAL_BIND(_get_handle_value, "id", "secondary");875GDVIRTUAL_BIND(_begin_handle_action, "id", "secondary");876GDVIRTUAL_BIND(_set_handle, "id", "secondary", "camera", "point");877GDVIRTUAL_BIND(_commit_handle, "id", "secondary", "restore", "cancel");878879GDVIRTUAL_BIND(_subgizmos_intersect_ray, "camera", "point");880GDVIRTUAL_BIND(_subgizmos_intersect_frustum, "camera", "frustum");881GDVIRTUAL_BIND(_set_subgizmo_transform, "id", "transform");882GDVIRTUAL_BIND(_get_subgizmo_transform, "id");883GDVIRTUAL_BIND(_commit_subgizmos, "ids", "restores", "cancel");884}885886EditorNode3DGizmo::EditorNode3DGizmo() {887valid = false;888billboard_handle = false;889hidden = false;890selected = false;891spatial_node = nullptr;892gizmo_plugin = nullptr;893selectable_icon_size = -1.0f;894}895896EditorNode3DGizmo::~EditorNode3DGizmo() {897if (gizmo_plugin != nullptr) {898gizmo_plugin->unregister_gizmo(this);899}900clear();901}902903/////904905void EditorNode3DGizmoPlugin::create_material(const String &p_name, const Color &p_color, bool p_billboard, bool p_on_top, bool p_use_vertex_color) {906Color instantiated_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/instantiated");907908Vector<Ref<StandardMaterial3D>> mats;909910for (int i = 0; i < 4; i++) {911bool selected = i % 2 == 1;912bool instantiated = i < 2;913914Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));915916Color color = instantiated ? instantiated_color : p_color;917918if (!selected) {919color.a *= 0.3;920}921922material->set_albedo(color);923material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);924material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);925material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1);926material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);927material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);928929if (p_use_vertex_color) {930material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);931material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);932}933934if (p_billboard) {935material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED);936}937938if (p_on_top && selected) {939material->set_on_top_of_alpha();940}941942mats.push_back(material);943}944945materials[p_name] = mats;946}947948void EditorNode3DGizmoPlugin::create_icon_material(const String &p_name, const Ref<Texture2D> &p_texture, bool p_on_top, const Color &p_albedo) {949Color instantiated_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/instantiated");950951Vector<Ref<StandardMaterial3D>> icons;952953for (int i = 0; i < 4; i++) {954bool selected = i % 2 == 1;955bool instantiated = i < 2;956957Ref<StandardMaterial3D> icon = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));958959Color color = instantiated ? instantiated_color : p_albedo;960961if (!selected) {962color.r *= 0.6;963color.g *= 0.6;964color.b *= 0.6;965}966967icon->set_albedo(color);968969icon->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);970icon->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);971icon->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);972icon->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);973icon->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA_SCISSOR);974icon->set_alpha_scissor_threshold(0.1);975icon->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, p_texture);976icon->set_flag(StandardMaterial3D::FLAG_FIXED_SIZE, true);977icon->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED);978icon->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN);979980if (p_on_top && selected) {981icon->set_on_top_of_alpha();982}983984icons.push_back(icon);985}986987materials[p_name] = icons;988}989990void EditorNode3DGizmoPlugin::create_handle_material(const String &p_name, bool p_billboard, const Ref<Texture2D> &p_icon) {991Ref<StandardMaterial3D> handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));992993handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);994handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);995Ref<Texture2D> handle_t = p_icon.is_valid() ? p_icon : EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle"), EditorStringName(EditorIcons));996handle_material->set_point_size(handle_t->get_width());997handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle_t);998handle_material->set_albedo(Color(1, 1, 1));999handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);1000handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);1001handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);1002handle_material->set_on_top_of_alpha();1003if (p_billboard) {1004handle_material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED);1005handle_material->set_on_top_of_alpha();1006}1007handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);10081009materials[p_name] = Vector<Ref<StandardMaterial3D>>();1010materials[p_name].push_back(handle_material);1011}10121013void EditorNode3DGizmoPlugin::add_material(const String &p_name, Ref<StandardMaterial3D> p_material) {1014materials[p_name] = Vector<Ref<StandardMaterial3D>>();1015materials[p_name].push_back(p_material);1016}10171018Ref<StandardMaterial3D> EditorNode3DGizmoPlugin::get_material(const String &p_name, const Ref<EditorNode3DGizmo> &p_gizmo) {1019ERR_FAIL_COND_V(!materials.has(p_name), Ref<StandardMaterial3D>());1020ERR_FAIL_COND_V(materials[p_name].is_empty(), Ref<StandardMaterial3D>());10211022if (p_gizmo.is_null() || materials[p_name].size() == 1) {1023return materials[p_name][0];1024}10251026int index = (p_gizmo->is_selected() ? 1 : 0) + (p_gizmo->is_editable() ? 2 : 0);10271028Ref<StandardMaterial3D> mat = materials[p_name][index];10291030bool on_top_mat = mat->get_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST);10311032if (!on_top_mat && current_state == ON_TOP && p_gizmo->is_selected()) {1033mat = mat->duplicate();1034mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);1035}10361037return mat;1038}10391040String EditorNode3DGizmoPlugin::get_gizmo_name() const {1041String ret;1042if (GDVIRTUAL_CALL(_get_gizmo_name, ret)) {1043return ret;1044}10451046WARN_PRINT_ONCE("A 3D editor gizmo has no name defined (it will appear as \"Unnamed Gizmo\" in the \"View > Gizmos\" menu). To resolve this, override the `_get_gizmo_name()` function to return a String in the script that extends EditorNode3DGizmoPlugin.");1047return TTR("Unnamed Gizmo");1048}10491050int EditorNode3DGizmoPlugin::get_priority() const {1051int ret;1052if (GDVIRTUAL_CALL(_get_priority, ret)) {1053return ret;1054}1055return 0;1056}10571058Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::get_gizmo(Node3D *p_spatial) {1059if (get_script_instance() && get_script_instance()->has_method("_get_gizmo")) {1060return get_script_instance()->call("_get_gizmo", p_spatial);1061}10621063Ref<EditorNode3DGizmo> ref = create_gizmo(p_spatial);10641065if (ref.is_null()) {1066return ref;1067}10681069ref->set_plugin(this);1070ref->set_node_3d(p_spatial);1071ref->set_hidden(current_state == HIDDEN);10721073current_gizmos.insert(ref.ptr());1074return ref;1075}10761077void EditorNode3DGizmoPlugin::_bind_methods() {1078ClassDB::bind_method(D_METHOD("create_material", "name", "color", "billboard", "on_top", "use_vertex_color"), &EditorNode3DGizmoPlugin::create_material, DEFVAL(false), DEFVAL(false), DEFVAL(false));1079ClassDB::bind_method(D_METHOD("create_icon_material", "name", "texture", "on_top", "color"), &EditorNode3DGizmoPlugin::create_icon_material, DEFVAL(false), DEFVAL(Color(1, 1, 1, 1)));1080ClassDB::bind_method(D_METHOD("create_handle_material", "name", "billboard", "texture"), &EditorNode3DGizmoPlugin::create_handle_material, DEFVAL(false), DEFVAL(Variant()));1081ClassDB::bind_method(D_METHOD("add_material", "name", "material"), &EditorNode3DGizmoPlugin::add_material);10821083ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorNode3DGizmoPlugin::get_material, DEFVAL(Ref<EditorNode3DGizmo>()));10841085GDVIRTUAL_BIND(_has_gizmo, "for_node_3d");1086GDVIRTUAL_BIND(_create_gizmo, "for_node_3d");10871088GDVIRTUAL_BIND(_get_gizmo_name);1089GDVIRTUAL_BIND(_get_priority);1090GDVIRTUAL_BIND(_can_be_hidden);1091GDVIRTUAL_BIND(_is_selectable_when_hidden);10921093GDVIRTUAL_BIND(_redraw, "gizmo");1094GDVIRTUAL_BIND(_get_handle_name, "gizmo", "handle_id", "secondary");1095GDVIRTUAL_BIND(_is_handle_highlighted, "gizmo", "handle_id", "secondary");1096GDVIRTUAL_BIND(_get_handle_value, "gizmo", "handle_id", "secondary");10971098GDVIRTUAL_BIND(_begin_handle_action, "gizmo", "handle_id", "secondary");1099GDVIRTUAL_BIND(_set_handle, "gizmo", "handle_id", "secondary", "camera", "screen_pos");1100GDVIRTUAL_BIND(_commit_handle, "gizmo", "handle_id", "secondary", "restore", "cancel");11011102GDVIRTUAL_BIND(_subgizmos_intersect_ray, "gizmo", "camera", "screen_pos");1103GDVIRTUAL_BIND(_subgizmos_intersect_frustum, "gizmo", "camera", "frustum_planes");1104GDVIRTUAL_BIND(_get_subgizmo_transform, "gizmo", "subgizmo_id");1105GDVIRTUAL_BIND(_set_subgizmo_transform, "gizmo", "subgizmo_id", "transform");1106GDVIRTUAL_BIND(_commit_subgizmos, "gizmo", "ids", "restores", "cancel");1107}11081109bool EditorNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {1110bool success = false;1111GDVIRTUAL_CALL(_has_gizmo, p_spatial, success);1112return success;1113}11141115Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial) {1116Ref<EditorNode3DGizmo> ret;1117if (GDVIRTUAL_CALL(_create_gizmo, p_spatial, ret)) {1118return ret;1119}11201121Ref<EditorNode3DGizmo> ref;1122if (has_gizmo(p_spatial)) {1123ref.instantiate();1124}1125return ref;1126}11271128bool EditorNode3DGizmoPlugin::can_be_hidden() const {1129bool ret = true;1130GDVIRTUAL_CALL(_can_be_hidden, ret);1131return ret;1132}11331134bool EditorNode3DGizmoPlugin::is_selectable_when_hidden() const {1135bool ret = false;1136GDVIRTUAL_CALL(_is_selectable_when_hidden, ret);1137return ret;1138}11391140bool EditorNode3DGizmoPlugin::can_commit_handle_on_click() const {1141return false;1142}11431144void EditorNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {1145GDVIRTUAL_CALL(_redraw, p_gizmo);1146}11471148bool EditorNode3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {1149bool ret = false;1150GDVIRTUAL_CALL(_is_handle_highlighted, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, ret);1151return ret;1152}11531154String EditorNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {1155String ret;1156GDVIRTUAL_CALL(_get_handle_name, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, ret);1157return ret;1158}11591160Variant EditorNode3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {1161Variant ret;1162GDVIRTUAL_CALL(_get_handle_value, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, ret);1163return ret;1164}11651166void EditorNode3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {1167GDVIRTUAL_CALL(_begin_handle_action, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary);1168}11691170void EditorNode3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {1171GDVIRTUAL_CALL(_set_handle, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, p_camera, p_point);1172}11731174void EditorNode3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {1175GDVIRTUAL_CALL(_commit_handle, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, p_restore, p_cancel);1176}11771178int EditorNode3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const {1179int ret = -1;1180GDVIRTUAL_CALL(_subgizmos_intersect_ray, Ref<EditorNode3DGizmo>(p_gizmo), p_camera, p_point, ret);1181return ret;1182}11831184Vector<int> EditorNode3DGizmoPlugin::subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const {1185TypedArray<Plane> frustum;1186frustum.resize(p_frustum.size());1187for (int i = 0; i < p_frustum.size(); i++) {1188frustum[i] = p_frustum[i];1189}1190Vector<int> ret;1191GDVIRTUAL_CALL(_subgizmos_intersect_frustum, Ref<EditorNode3DGizmo>(p_gizmo), p_camera, frustum, ret);1192return ret;1193}11941195Transform3D EditorNode3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const {1196Transform3D ret;1197GDVIRTUAL_CALL(_get_subgizmo_transform, Ref<EditorNode3DGizmo>(p_gizmo), p_id, ret);1198return ret;1199}12001201void EditorNode3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) {1202GDVIRTUAL_CALL(_set_subgizmo_transform, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_transform);1203}12041205void EditorNode3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {1206TypedArray<Transform3D> restore;1207restore.resize(p_restore.size());1208for (int i = 0; i < p_restore.size(); i++) {1209restore[i] = p_restore[i];1210}12111212GDVIRTUAL_CALL(_commit_subgizmos, Ref<EditorNode3DGizmo>(p_gizmo), p_ids, restore, p_cancel);1213}12141215void EditorNode3DGizmoPlugin::set_state(int p_state) {1216current_state = p_state;1217for (EditorNode3DGizmo *current : current_gizmos) {1218current->set_hidden(current_state == HIDDEN);1219}1220}12211222int EditorNode3DGizmoPlugin::get_state() const {1223return current_state;1224}12251226void EditorNode3DGizmoPlugin::unregister_gizmo(EditorNode3DGizmo *p_gizmo) {1227current_gizmos.erase(p_gizmo);1228}12291230EditorNode3DGizmoPlugin::EditorNode3DGizmoPlugin() {1231current_state = VISIBLE;1232}12331234EditorNode3DGizmoPlugin::~EditorNode3DGizmoPlugin() {1235for (EditorNode3DGizmo *current : current_gizmos) {1236current->set_plugin(nullptr);1237current->get_node_3d()->remove_gizmo(current);1238}1239if (Node3DEditor::get_singleton()) {1240Node3DEditor::get_singleton()->update_all_gizmos();1241}1242}12431244//////124512461247