1/**************************************************************************/
2/* node_3d_editor_gizmos.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "node_3d_editor_gizmos.h"
32
33#include "core/math/geometry_2d.h"
34#include "core/math/geometry_3d.h"
35#include "editor/editor_node.h"
36#include "editor/editor_settings.h"
37#include "editor/editor_string_names.h"
38#include "editor/plugins/node_3d_editor_plugin.h"
39#include "scene/resources/primitive_meshes.h"
40
41#define HANDLE_HALF_SIZE 9.5
42
43bool EditorNode3DGizmo::is_editable() const {
44 ERR_FAIL_NULL_V(spatial_node, false);
45 Node *edited_root = spatial_node->get_tree()->get_edited_scene_root();
46 if (spatial_node == edited_root) {
47 return true;
48 }
49 if (spatial_node->get_owner() == edited_root) {
50 return true;
51 }
52
53 if (edited_root->is_editable_instance(spatial_node->get_owner())) {
54 return true;
55 }
56
57 return false;
58}
59
60void EditorNode3DGizmo::clear() {
61 ERR_FAIL_NULL(RenderingServer::get_singleton());
62 for (int i = 0; i < instances.size(); i++) {
63 if (instances[i].instance.is_valid()) {
64 RS::get_singleton()->free(instances[i].instance);
65 }
66 }
67
68 billboard_handle = false;
69 collision_segments.clear();
70 collision_mesh = Ref<TriangleMesh>();
71 instances.clear();
72 handles.clear();
73 handle_ids.clear();
74 secondary_handles.clear();
75 secondary_handle_ids.clear();
76}
77
78void EditorNode3DGizmo::redraw() {
79 if (!GDVIRTUAL_CALL(_redraw)) {
80 ERR_FAIL_NULL(gizmo_plugin);
81 gizmo_plugin->redraw(this);
82 }
83
84 if (Node3DEditor::get_singleton()->is_current_selected_gizmo(this)) {
85 Node3DEditor::get_singleton()->update_transform_gizmo();
86 }
87}
88
89String EditorNode3DGizmo::get_handle_name(int p_id, bool p_secondary) const {
90 String ret;
91 if (GDVIRTUAL_CALL(_get_handle_name, p_id, p_secondary, ret)) {
92 return ret;
93 }
94
95 ERR_FAIL_NULL_V(gizmo_plugin, "");
96 return gizmo_plugin->get_handle_name(this, p_id, p_secondary);
97}
98
99bool EditorNode3DGizmo::is_handle_highlighted(int p_id, bool p_secondary) const {
100 bool success;
101 if (GDVIRTUAL_CALL(_is_handle_highlighted, p_id, p_secondary, success)) {
102 return success;
103 }
104
105 ERR_FAIL_NULL_V(gizmo_plugin, false);
106 return gizmo_plugin->is_handle_highlighted(this, p_id, p_secondary);
107}
108
109Variant EditorNode3DGizmo::get_handle_value(int p_id, bool p_secondary) const {
110 Variant value;
111 if (GDVIRTUAL_CALL(_get_handle_value, p_id, p_secondary, value)) {
112 return value;
113 }
114
115 ERR_FAIL_NULL_V(gizmo_plugin, Variant());
116 return gizmo_plugin->get_handle_value(this, p_id, p_secondary);
117}
118
119void EditorNode3DGizmo::begin_handle_action(int p_id, bool p_secondary) {
120 if (GDVIRTUAL_CALL(_begin_handle_action, p_id, p_secondary)) {
121 return;
122 }
123
124 ERR_FAIL_NULL(gizmo_plugin);
125 gizmo_plugin->begin_handle_action(this, p_id, p_secondary);
126}
127
128void EditorNode3DGizmo::set_handle(int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
129 if (GDVIRTUAL_CALL(_set_handle, p_id, p_secondary, p_camera, p_point)) {
130 return;
131 }
132
133 ERR_FAIL_NULL(gizmo_plugin);
134 gizmo_plugin->set_handle(this, p_id, p_secondary, p_camera, p_point);
135}
136
137void EditorNode3DGizmo::commit_handle(int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
138 if (GDVIRTUAL_CALL(_commit_handle, p_id, p_secondary, p_restore, p_cancel)) {
139 return;
140 }
141
142 ERR_FAIL_NULL(gizmo_plugin);
143 gizmo_plugin->commit_handle(this, p_id, p_secondary, p_restore, p_cancel);
144}
145
146int EditorNode3DGizmo::subgizmos_intersect_ray(Camera3D *p_camera, const Vector2 &p_point) const {
147 int id;
148 if (GDVIRTUAL_CALL(_subgizmos_intersect_ray, p_camera, p_point, id)) {
149 return id;
150 }
151
152 ERR_FAIL_NULL_V(gizmo_plugin, -1);
153 return gizmo_plugin->subgizmos_intersect_ray(this, p_camera, p_point);
154}
155
156Vector<int> EditorNode3DGizmo::subgizmos_intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum) const {
157 TypedArray<Plane> frustum;
158 frustum.resize(p_frustum.size());
159 for (int i = 0; i < p_frustum.size(); i++) {
160 frustum[i] = p_frustum[i];
161 }
162 Vector<int> ret;
163 if (GDVIRTUAL_CALL(_subgizmos_intersect_frustum, p_camera, frustum, ret)) {
164 return ret;
165 }
166
167 ERR_FAIL_NULL_V(gizmo_plugin, Vector<int>());
168 return gizmo_plugin->subgizmos_intersect_frustum(this, p_camera, p_frustum);
169}
170
171Transform3D EditorNode3DGizmo::get_subgizmo_transform(int p_id) const {
172 Transform3D ret;
173 if (GDVIRTUAL_CALL(_get_subgizmo_transform, p_id, ret)) {
174 return ret;
175 }
176
177 ERR_FAIL_NULL_V(gizmo_plugin, Transform3D());
178 return gizmo_plugin->get_subgizmo_transform(this, p_id);
179}
180
181void EditorNode3DGizmo::set_subgizmo_transform(int p_id, Transform3D p_transform) {
182 if (GDVIRTUAL_CALL(_set_subgizmo_transform, p_id, p_transform)) {
183 return;
184 }
185
186 ERR_FAIL_NULL(gizmo_plugin);
187 gizmo_plugin->set_subgizmo_transform(this, p_id, p_transform);
188}
189
190void EditorNode3DGizmo::commit_subgizmos(const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
191 TypedArray<Transform3D> restore;
192 restore.resize(p_restore.size());
193 for (int i = 0; i < p_restore.size(); i++) {
194 restore[i] = p_restore[i];
195 }
196
197 if (GDVIRTUAL_CALL(_commit_subgizmos, p_ids, restore, p_cancel)) {
198 return;
199 }
200
201 ERR_FAIL_NULL(gizmo_plugin);
202 gizmo_plugin->commit_subgizmos(this, p_ids, p_restore, p_cancel);
203}
204
205void EditorNode3DGizmo::set_node_3d(Node3D *p_node) {
206 ERR_FAIL_NULL(p_node);
207 spatial_node = p_node;
208}
209
210void EditorNode3DGizmo::Instance::create_instance(Node3D *p_base, bool p_hidden) {
211 instance = RS::get_singleton()->instance_create2(mesh->get_rid(), p_base->get_world_3d()->get_scenario());
212 RS::get_singleton()->instance_attach_object_instance_id(instance, p_base->get_instance_id());
213 if (skin_reference.is_valid()) {
214 RS::get_singleton()->instance_attach_skeleton(instance, skin_reference->get_skeleton());
215 }
216 if (extra_margin) {
217 RS::get_singleton()->instance_set_extra_visibility_margin(instance, 1);
218 }
219 RS::get_singleton()->instance_geometry_set_cast_shadows_setting(instance, RS::SHADOW_CASTING_SETTING_OFF);
220 int layer = p_hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER;
221 RS::get_singleton()->instance_set_layer_mask(instance, layer); //gizmos are 26
222 RS::get_singleton()->instance_geometry_set_flag(instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
223 RS::get_singleton()->instance_geometry_set_flag(instance, RS::INSTANCE_FLAG_USE_BAKED_LIGHT, false);
224}
225
226void EditorNode3DGizmo::add_mesh(const Ref<Mesh> &p_mesh, const Ref<Material> &p_material, const Transform3D &p_xform, const Ref<SkinReference> &p_skin_reference) {
227 ERR_FAIL_NULL(spatial_node);
228 ERR_FAIL_COND_MSG(!p_mesh.is_valid(), "EditorNode3DGizmo.add_mesh() requires a valid Mesh resource.");
229
230 Instance ins;
231
232 ins.mesh = p_mesh;
233 ins.skin_reference = p_skin_reference;
234 ins.material = p_material;
235 ins.xform = p_xform;
236 if (valid) {
237 ins.create_instance(spatial_node, hidden);
238 RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform() * ins.xform);
239 if (ins.material.is_valid()) {
240 RS::get_singleton()->instance_geometry_set_material_override(ins.instance, p_material->get_rid());
241 }
242 }
243
244 instances.push_back(ins);
245}
246
247void EditorNode3DGizmo::add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard, const Color &p_modulate) {
248 add_vertices(p_lines, p_material, Mesh::PRIMITIVE_LINES, p_billboard, p_modulate);
249}
250
251void 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) {
252 if (p_vertices.is_empty()) {
253 return;
254 }
255
256 ERR_FAIL_NULL(spatial_node);
257 Instance ins;
258
259 Ref<ArrayMesh> mesh = memnew(ArrayMesh);
260 Array a;
261 a.resize(Mesh::ARRAY_MAX);
262
263 a[Mesh::ARRAY_VERTEX] = p_vertices;
264
265 Vector<Color> color;
266 color.resize(p_vertices.size());
267 {
268 Color *w = color.ptrw();
269 for (int i = 0; i < p_vertices.size(); i++) {
270 if (is_selected()) {
271 w[i] = Color(1, 1, 1, 0.8) * p_modulate;
272 } else {
273 w[i] = Color(1, 1, 1, 0.2) * p_modulate;
274 }
275 }
276 }
277
278 a[Mesh::ARRAY_COLOR] = color;
279
280 mesh->add_surface_from_arrays(p_primitive_type, a);
281 mesh->surface_set_material(0, p_material);
282
283 if (p_billboard) {
284 float md = 0;
285 for (int i = 0; i < p_vertices.size(); i++) {
286 md = MAX(0, p_vertices[i].length());
287 }
288 if (md) {
289 mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0));
290 }
291 }
292
293 ins.mesh = mesh;
294 if (valid) {
295 ins.create_instance(spatial_node, hidden);
296 RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform());
297 }
298
299 instances.push_back(ins);
300}
301
302void EditorNode3DGizmo::add_unscaled_billboard(const Ref<Material> &p_material, real_t p_scale, const Color &p_modulate) {
303 ERR_FAIL_NULL(spatial_node);
304 Instance ins;
305
306 Vector<Vector3> vs = {
307 Vector3(-p_scale, p_scale, 0),
308 Vector3(p_scale, p_scale, 0),
309 Vector3(p_scale, -p_scale, 0),
310 Vector3(-p_scale, -p_scale, 0)
311 };
312
313 Vector<Vector2> uv = {
314 Vector2(0, 0),
315 Vector2(1, 0),
316 Vector2(1, 1),
317 Vector2(0, 1)
318 };
319
320 Vector<Color> colors = {
321 p_modulate,
322 p_modulate,
323 p_modulate,
324 p_modulate
325 };
326
327 Vector<int> indices = { 0, 1, 2, 0, 2, 3 };
328
329 Ref<ArrayMesh> mesh = memnew(ArrayMesh);
330 Array a;
331 a.resize(Mesh::ARRAY_MAX);
332 a[Mesh::ARRAY_VERTEX] = vs;
333 a[Mesh::ARRAY_TEX_UV] = uv;
334 a[Mesh::ARRAY_INDEX] = indices;
335 a[Mesh::ARRAY_COLOR] = colors;
336 mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a);
337 mesh->surface_set_material(0, p_material);
338
339 float md = 0;
340 for (int i = 0; i < vs.size(); i++) {
341 md = MAX(0, vs[i].length());
342 }
343 if (md) {
344 mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0));
345 }
346
347 selectable_icon_size = p_scale;
348 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));
349
350 ins.mesh = mesh;
351 if (valid) {
352 ins.create_instance(spatial_node, hidden);
353 RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform());
354 }
355
356 selectable_icon_size = p_scale;
357
358 instances.push_back(ins);
359}
360
361void EditorNode3DGizmo::add_collision_triangles(const Ref<TriangleMesh> &p_tmesh) {
362 collision_mesh = p_tmesh;
363}
364
365void EditorNode3DGizmo::add_collision_segments(const Vector<Vector3> &p_lines) {
366 int from = collision_segments.size();
367 collision_segments.resize(from + p_lines.size());
368 for (int i = 0; i < p_lines.size(); i++) {
369 collision_segments.write[from + i] = p_lines[i];
370 }
371}
372
373void EditorNode3DGizmo::add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, const Vector<int> &p_ids, bool p_billboard, bool p_secondary) {
374 billboard_handle = p_billboard;
375
376 if (!is_selected() || !is_editable()) {
377 return;
378 }
379
380 ERR_FAIL_NULL(spatial_node);
381
382 Vector<Vector3> &handle_list = p_secondary ? secondary_handles : handles;
383 Vector<int> &id_list = p_secondary ? secondary_handle_ids : handle_ids;
384
385 if (p_ids.is_empty()) {
386 ERR_FAIL_COND_MSG(!id_list.is_empty(), "IDs must be provided for all handles, as handles with IDs already exist.");
387 } else {
388 ERR_FAIL_COND_MSG(p_handles.size() != p_ids.size(), "The number of IDs should be the same as the number of handles.");
389 }
390
391 bool is_current_hover_gizmo = Node3DEditor::get_singleton()->get_current_hover_gizmo() == this;
392 bool current_hover_handle_secondary;
393 int current_hover_handle = Node3DEditor::get_singleton()->get_current_hover_gizmo_handle(current_hover_handle_secondary);
394
395 Instance ins;
396 Ref<ArrayMesh> mesh = memnew(ArrayMesh);
397
398 Array a;
399 a.resize(RS::ARRAY_MAX);
400 a[RS::ARRAY_VERTEX] = p_handles;
401 Vector<Color> colors;
402 {
403 colors.resize(p_handles.size());
404 Color *w = colors.ptrw();
405 for (int i = 0; i < p_handles.size(); i++) {
406 Color col(1, 1, 1, 1);
407 if (is_handle_highlighted(i, p_secondary)) {
408 col = Color(0, 0, 1, 0.9);
409 }
410
411 int id = p_ids.is_empty() ? i : p_ids[i];
412 if (!is_current_hover_gizmo || current_hover_handle != id || p_secondary != current_hover_handle_secondary) {
413 col.a = 0.8;
414 }
415
416 w[i] = col;
417 }
418 }
419 a[RS::ARRAY_COLOR] = colors;
420 mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, a);
421 mesh->surface_set_material(0, p_material);
422
423 if (p_billboard) {
424 float md = 0;
425 for (int i = 0; i < p_handles.size(); i++) {
426 md = MAX(0, p_handles[i].length());
427 }
428 if (md) {
429 mesh->set_custom_aabb(AABB(Vector3(-md, -md, -md), Vector3(md, md, md) * 2.0));
430 }
431 }
432
433 ins.mesh = mesh;
434 ins.extra_margin = true;
435 if (valid) {
436 ins.create_instance(spatial_node, hidden);
437 RS::get_singleton()->instance_set_transform(ins.instance, spatial_node->get_global_transform());
438 }
439 instances.push_back(ins);
440
441 int current_size = handle_list.size();
442 handle_list.resize(current_size + p_handles.size());
443 for (int i = 0; i < p_handles.size(); i++) {
444 handle_list.write[current_size + i] = p_handles[i];
445 }
446
447 if (!p_ids.is_empty()) {
448 current_size = id_list.size();
449 id_list.resize(current_size + p_ids.size());
450 for (int i = 0; i < p_ids.size(); i++) {
451 id_list.write[current_size + i] = p_ids[i];
452 }
453 }
454}
455
456void EditorNode3DGizmo::add_solid_box(const Ref<Material> &p_material, Vector3 p_size, Vector3 p_position, const Transform3D &p_xform) {
457 ERR_FAIL_NULL(spatial_node);
458
459 BoxMesh box_mesh;
460 box_mesh.set_size(p_size);
461
462 Array arrays = box_mesh.surface_get_arrays(0);
463 PackedVector3Array vertex = arrays[RS::ARRAY_VERTEX];
464 Vector3 *w = vertex.ptrw();
465
466 for (int i = 0; i < vertex.size(); ++i) {
467 w[i] += p_position;
468 }
469
470 arrays[RS::ARRAY_VERTEX] = vertex;
471
472 Ref<ArrayMesh> m = memnew(ArrayMesh);
473 m->add_surface_from_arrays(box_mesh.surface_get_primitive_type(0), arrays);
474 add_mesh(m, p_material, p_xform);
475}
476
477bool EditorNode3DGizmo::intersect_frustum(const Camera3D *p_camera, const Vector<Plane> &p_frustum) {
478 ERR_FAIL_NULL_V(spatial_node, false);
479 ERR_FAIL_COND_V(!valid, false);
480
481 if (hidden && !gizmo_plugin->is_selectable_when_hidden()) {
482 return false;
483 }
484
485 if (selectable_icon_size > 0.0f) {
486 Vector3 origin = spatial_node->get_global_transform().get_origin();
487
488 const Plane *p = p_frustum.ptr();
489 int fc = p_frustum.size();
490
491 bool any_out = false;
492
493 for (int j = 0; j < fc; j++) {
494 if (p[j].is_point_over(origin)) {
495 any_out = true;
496 break;
497 }
498 }
499
500 return !any_out;
501 }
502
503 if (collision_segments.size()) {
504 const Plane *p = p_frustum.ptr();
505 int fc = p_frustum.size();
506
507 int vc = collision_segments.size();
508 const Vector3 *vptr = collision_segments.ptr();
509 Transform3D t = spatial_node->get_global_transform();
510
511 bool any_out = false;
512 for (int j = 0; j < fc; j++) {
513 for (int i = 0; i < vc; i++) {
514 Vector3 v = t.xform(vptr[i]);
515 if (p[j].is_point_over(v)) {
516 any_out = true;
517 break;
518 }
519 }
520 if (any_out) {
521 break;
522 }
523 }
524
525 if (!any_out) {
526 return true;
527 }
528 }
529
530 if (collision_mesh.is_valid()) {
531 Transform3D t = spatial_node->get_global_transform();
532
533 Vector3 mesh_scale = t.get_basis().get_scale();
534 t.orthonormalize();
535
536 Transform3D it = t.affine_inverse();
537
538 Vector<Plane> transformed_frustum;
539 int plane_count = p_frustum.size();
540 transformed_frustum.resize(plane_count);
541
542 for (int i = 0; i < plane_count; i++) {
543 transformed_frustum.write[i] = it.xform(p_frustum[i]);
544 }
545
546 Vector<Vector3> convex_points = Geometry3D::compute_convex_mesh_points(transformed_frustum.ptr(), plane_count);
547 if (collision_mesh->inside_convex_shape(transformed_frustum.ptr(), plane_count, convex_points.ptr(), convex_points.size(), mesh_scale)) {
548 return true;
549 }
550 }
551
552 return false;
553}
554
555void EditorNode3DGizmo::handles_intersect_ray(Camera3D *p_camera, const Vector2 &p_point, bool p_shift_pressed, int &r_id, bool &r_secondary) {
556 r_id = -1;
557 r_secondary = false;
558
559 ERR_FAIL_NULL(spatial_node);
560 ERR_FAIL_COND(!valid);
561
562 if (hidden) {
563 return;
564 }
565
566 Transform3D camera_xform = p_camera->get_global_transform();
567 Transform3D t = spatial_node->get_global_transform();
568 if (billboard_handle) {
569 t.set_look_at(t.origin, t.origin - camera_xform.basis.get_column(2), camera_xform.basis.get_column(1));
570 }
571
572 float min_d = 1e20;
573
574 for (int i = 0; i < secondary_handles.size(); i++) {
575 Vector3 hpos = t.xform(secondary_handles[i]);
576 Vector2 p = p_camera->unproject_position(hpos);
577
578 if (p.distance_to(p_point) < HANDLE_HALF_SIZE) {
579 real_t dp = p_camera->get_transform().origin.distance_to(hpos);
580 if (dp < min_d) {
581 min_d = dp;
582 if (secondary_handle_ids.is_empty()) {
583 r_id = i;
584 } else {
585 r_id = secondary_handle_ids[i];
586 }
587 r_secondary = true;
588 }
589 }
590 }
591
592 if (r_id != -1 && p_shift_pressed) {
593 return;
594 }
595
596 min_d = 1e20;
597
598 for (int i = 0; i < handles.size(); i++) {
599 Vector3 hpos = t.xform(handles[i]);
600 Vector2 p = p_camera->unproject_position(hpos);
601
602 if (p.distance_to(p_point) < HANDLE_HALF_SIZE) {
603 real_t dp = p_camera->get_transform().origin.distance_to(hpos);
604 if (dp < min_d) {
605 min_d = dp;
606 if (handle_ids.is_empty()) {
607 r_id = i;
608 } else {
609 r_id = handle_ids[i];
610 }
611 r_secondary = false;
612 }
613 }
614 }
615}
616
617bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal) {
618 ERR_FAIL_NULL_V(spatial_node, false);
619 ERR_FAIL_COND_V(!valid, false);
620
621 if (hidden && !gizmo_plugin->is_selectable_when_hidden()) {
622 return false;
623 }
624
625 if (selectable_icon_size > 0.0f) {
626 Transform3D t = spatial_node->get_global_transform();
627 Vector3 camera_position = p_camera->get_camera_transform().origin;
628 if (!camera_position.is_equal_approx(t.origin)) {
629 t.set_look_at(t.origin, camera_position);
630 }
631
632 float scale = t.origin.distance_to(p_camera->get_camera_transform().origin);
633
634 if (p_camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) {
635 float aspect = p_camera->get_viewport()->get_visible_rect().size.aspect();
636 float size = p_camera->get_size();
637 scale = size / aspect;
638 }
639
640 Point2 center = p_camera->unproject_position(t.origin);
641
642 Transform3D orig_camera_transform = p_camera->get_camera_transform();
643
644 if (!orig_camera_transform.origin.is_equal_approx(t.origin) &&
645 ABS(orig_camera_transform.basis.get_column(Vector3::AXIS_Z).dot(Vector3(0, 1, 0))) < 0.99) {
646 p_camera->look_at(t.origin);
647 }
648
649 Vector3 c0 = t.xform(Vector3(selectable_icon_size, selectable_icon_size, 0) * scale);
650 Vector3 c1 = t.xform(Vector3(-selectable_icon_size, -selectable_icon_size, 0) * scale);
651
652 Point2 p0 = p_camera->unproject_position(c0);
653 Point2 p1 = p_camera->unproject_position(c1);
654
655 p_camera->set_global_transform(orig_camera_transform);
656
657 Rect2 rect(p0, (p1 - p0).abs());
658
659 rect.set_position(center - rect.get_size() / 2.0);
660
661 if (rect.has_point(p_point)) {
662 r_pos = t.origin;
663 r_normal = -p_camera->project_ray_normal(p_point);
664 return true;
665 }
666 }
667
668 if (collision_segments.size()) {
669 Plane camp(-p_camera->get_transform().basis.get_column(2).normalized(), p_camera->get_transform().origin);
670
671 int vc = collision_segments.size();
672 const Vector3 *vptr = collision_segments.ptr();
673 Transform3D t = spatial_node->get_global_transform();
674 if (billboard_handle) {
675 t.set_look_at(t.origin, t.origin - p_camera->get_transform().basis.get_column(2), p_camera->get_transform().basis.get_column(1));
676 }
677
678 Vector3 cp;
679 float cpd = 1e20;
680
681 for (int i = 0; i < vc / 2; i++) {
682 Vector3 a = t.xform(vptr[i * 2 + 0]);
683 Vector3 b = t.xform(vptr[i * 2 + 1]);
684 Vector2 s[2];
685 s[0] = p_camera->unproject_position(a);
686 s[1] = p_camera->unproject_position(b);
687
688 Vector2 p = Geometry2D::get_closest_point_to_segment(p_point, s);
689
690 float pd = p.distance_to(p_point);
691
692 if (pd < cpd) {
693 float d = s[0].distance_to(s[1]);
694 Vector3 tcp;
695 if (d > 0) {
696 float d2 = s[0].distance_to(p) / d;
697 tcp = a + (b - a) * d2;
698
699 } else {
700 tcp = a;
701 }
702
703 if (camp.distance_to(tcp) < p_camera->get_near()) {
704 continue;
705 }
706 cp = tcp;
707 cpd = pd;
708 }
709 }
710
711 if (cpd < 8) {
712 r_pos = cp;
713 r_normal = -p_camera->project_ray_normal(p_point);
714 return true;
715 }
716 }
717
718 if (collision_mesh.is_valid()) {
719 Transform3D gt = spatial_node->get_global_transform();
720
721 if (billboard_handle) {
722 gt.set_look_at(gt.origin, gt.origin - p_camera->get_transform().basis.get_column(2), p_camera->get_transform().basis.get_column(1));
723 }
724
725 Transform3D ai = gt.affine_inverse();
726 Vector3 ray_from = ai.xform(p_camera->project_ray_origin(p_point));
727 Vector3 ray_dir = ai.basis.xform(p_camera->project_ray_normal(p_point)).normalized();
728 Vector3 rpos, rnorm;
729
730 if (collision_mesh->intersect_ray(ray_from, ray_dir, rpos, rnorm)) {
731 r_pos = gt.xform(rpos);
732 r_normal = gt.basis.xform(rnorm).normalized();
733 return true;
734 }
735 }
736
737 return false;
738}
739
740bool EditorNode3DGizmo::is_subgizmo_selected(int p_id) const {
741 Node3DEditor *ed = Node3DEditor::get_singleton();
742 ERR_FAIL_NULL_V(ed, false);
743 return ed->is_current_selected_gizmo(this) && ed->is_subgizmo_selected(p_id);
744}
745
746Vector<int> EditorNode3DGizmo::get_subgizmo_selection() const {
747 Vector<int> ret;
748
749 Node3DEditor *ed = Node3DEditor::get_singleton();
750 ERR_FAIL_NULL_V(ed, ret);
751
752 if (ed->is_current_selected_gizmo(this)) {
753 ret = ed->get_subgizmo_selection();
754 }
755
756 return ret;
757}
758
759void EditorNode3DGizmo::create() {
760 ERR_FAIL_NULL(spatial_node);
761 ERR_FAIL_COND(valid);
762 valid = true;
763
764 for (int i = 0; i < instances.size(); i++) {
765 instances.write[i].create_instance(spatial_node, hidden);
766 }
767
768 transform();
769}
770
771void EditorNode3DGizmo::transform() {
772 ERR_FAIL_NULL(spatial_node);
773 ERR_FAIL_COND(!valid);
774 for (int i = 0; i < instances.size(); i++) {
775 RS::get_singleton()->instance_set_transform(instances[i].instance, spatial_node->get_global_transform() * instances[i].xform);
776 }
777}
778
779void EditorNode3DGizmo::free() {
780 ERR_FAIL_NULL(RenderingServer::get_singleton());
781 ERR_FAIL_NULL(spatial_node);
782 ERR_FAIL_COND(!valid);
783
784 for (int i = 0; i < instances.size(); i++) {
785 if (instances[i].instance.is_valid()) {
786 RS::get_singleton()->free(instances[i].instance);
787 }
788 instances.write[i].instance = RID();
789 }
790
791 clear();
792
793 valid = false;
794}
795
796void EditorNode3DGizmo::set_hidden(bool p_hidden) {
797 hidden = p_hidden;
798 int layer = hidden ? 0 : 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER;
799 for (int i = 0; i < instances.size(); ++i) {
800 RS::get_singleton()->instance_set_layer_mask(instances[i].instance, layer);
801 }
802}
803
804void EditorNode3DGizmo::set_plugin(EditorNode3DGizmoPlugin *p_plugin) {
805 gizmo_plugin = p_plugin;
806}
807
808void EditorNode3DGizmo::_bind_methods() {
809 ClassDB::bind_method(D_METHOD("add_lines", "lines", "material", "billboard", "modulate"), &EditorNode3DGizmo::add_lines, DEFVAL(false), DEFVAL(Color(1, 1, 1)));
810 ClassDB::bind_method(D_METHOD("add_mesh", "mesh", "material", "transform", "skeleton"), &EditorNode3DGizmo::add_mesh, DEFVAL(Variant()), DEFVAL(Transform3D()), DEFVAL(Ref<SkinReference>()));
811 ClassDB::bind_method(D_METHOD("add_collision_segments", "segments"), &EditorNode3DGizmo::add_collision_segments);
812 ClassDB::bind_method(D_METHOD("add_collision_triangles", "triangles"), &EditorNode3DGizmo::add_collision_triangles);
813 ClassDB::bind_method(D_METHOD("add_unscaled_billboard", "material", "default_scale", "modulate"), &EditorNode3DGizmo::add_unscaled_billboard, DEFVAL(1), DEFVAL(Color(1, 1, 1)));
814 ClassDB::bind_method(D_METHOD("add_handles", "handles", "material", "ids", "billboard", "secondary"), &EditorNode3DGizmo::add_handles, DEFVAL(false), DEFVAL(false));
815 ClassDB::bind_method(D_METHOD("set_node_3d", "node"), &EditorNode3DGizmo::_set_node_3d);
816 ClassDB::bind_method(D_METHOD("get_node_3d"), &EditorNode3DGizmo::get_node_3d);
817 ClassDB::bind_method(D_METHOD("get_plugin"), &EditorNode3DGizmo::get_plugin);
818 ClassDB::bind_method(D_METHOD("clear"), &EditorNode3DGizmo::clear);
819 ClassDB::bind_method(D_METHOD("set_hidden", "hidden"), &EditorNode3DGizmo::set_hidden);
820 ClassDB::bind_method(D_METHOD("is_subgizmo_selected", "id"), &EditorNode3DGizmo::is_subgizmo_selected);
821 ClassDB::bind_method(D_METHOD("get_subgizmo_selection"), &EditorNode3DGizmo::get_subgizmo_selection);
822
823 GDVIRTUAL_BIND(_redraw);
824 GDVIRTUAL_BIND(_get_handle_name, "id", "secondary");
825 GDVIRTUAL_BIND(_is_handle_highlighted, "id", "secondary");
826
827 GDVIRTUAL_BIND(_get_handle_value, "id", "secondary");
828 GDVIRTUAL_BIND(_set_handle, "id", "secondary", "camera", "point");
829 GDVIRTUAL_BIND(_commit_handle, "id", "secondary", "restore", "cancel");
830
831 GDVIRTUAL_BIND(_subgizmos_intersect_ray, "camera", "point");
832 GDVIRTUAL_BIND(_subgizmos_intersect_frustum, "camera", "frustum");
833 GDVIRTUAL_BIND(_set_subgizmo_transform, "id", "transform");
834 GDVIRTUAL_BIND(_get_subgizmo_transform, "id");
835 GDVIRTUAL_BIND(_commit_subgizmos, "ids", "restores", "cancel");
836}
837
838EditorNode3DGizmo::EditorNode3DGizmo() {
839 valid = false;
840 billboard_handle = false;
841 hidden = false;
842 selected = false;
843 spatial_node = nullptr;
844 gizmo_plugin = nullptr;
845 selectable_icon_size = -1.0f;
846}
847
848EditorNode3DGizmo::~EditorNode3DGizmo() {
849 if (gizmo_plugin != nullptr) {
850 gizmo_plugin->unregister_gizmo(this);
851 }
852 clear();
853}
854
855/////
856
857void EditorNode3DGizmoPlugin::create_material(const String &p_name, const Color &p_color, bool p_billboard, bool p_on_top, bool p_use_vertex_color) {
858 Color instantiated_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/instantiated");
859
860 Vector<Ref<StandardMaterial3D>> mats;
861
862 for (int i = 0; i < 4; i++) {
863 bool selected = i % 2 == 1;
864 bool instantiated = i < 2;
865
866 Ref<StandardMaterial3D> material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
867
868 Color color = instantiated ? instantiated_color : p_color;
869
870 if (!selected) {
871 color.a *= 0.3;
872 }
873
874 material->set_albedo(color);
875 material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
876 material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
877 material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1);
878 material->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
879
880 if (p_use_vertex_color) {
881 material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
882 material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
883 }
884
885 if (p_billboard) {
886 material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED);
887 }
888
889 if (p_on_top && selected) {
890 material->set_on_top_of_alpha();
891 }
892
893 mats.push_back(material);
894 }
895
896 materials[p_name] = mats;
897}
898
899void EditorNode3DGizmoPlugin::create_icon_material(const String &p_name, const Ref<Texture2D> &p_texture, bool p_on_top, const Color &p_albedo) {
900 Color instantiated_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/instantiated");
901
902 Vector<Ref<StandardMaterial3D>> icons;
903
904 for (int i = 0; i < 4; i++) {
905 bool selected = i % 2 == 1;
906 bool instantiated = i < 2;
907
908 Ref<StandardMaterial3D> icon = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
909
910 Color color = instantiated ? instantiated_color : p_albedo;
911
912 if (!selected) {
913 color.a *= 0.85;
914 }
915
916 icon->set_albedo(color);
917
918 icon->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
919 icon->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
920 icon->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
921 icon->set_cull_mode(StandardMaterial3D::CULL_DISABLED);
922 icon->set_depth_draw_mode(StandardMaterial3D::DEPTH_DRAW_DISABLED);
923 icon->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
924 icon->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, p_texture);
925 icon->set_flag(StandardMaterial3D::FLAG_FIXED_SIZE, true);
926 icon->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED);
927 icon->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN);
928
929 if (p_on_top && selected) {
930 icon->set_on_top_of_alpha();
931 }
932
933 icons.push_back(icon);
934 }
935
936 materials[p_name] = icons;
937}
938
939void EditorNode3DGizmoPlugin::create_handle_material(const String &p_name, bool p_billboard, const Ref<Texture2D> &p_icon) {
940 Ref<StandardMaterial3D> handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
941
942 handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
943 handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);
944 Ref<Texture2D> handle_t = p_icon != nullptr ? p_icon : EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle"), EditorStringName(EditorIcons));
945 handle_material->set_point_size(handle_t->get_width());
946 handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle_t);
947 handle_material->set_albedo(Color(1, 1, 1));
948 handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
949 handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
950 handle_material->set_on_top_of_alpha();
951 if (p_billboard) {
952 handle_material->set_billboard_mode(StandardMaterial3D::BILLBOARD_ENABLED);
953 handle_material->set_on_top_of_alpha();
954 }
955 handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
956
957 materials[p_name] = Vector<Ref<StandardMaterial3D>>();
958 materials[p_name].push_back(handle_material);
959}
960
961void EditorNode3DGizmoPlugin::add_material(const String &p_name, Ref<StandardMaterial3D> p_material) {
962 materials[p_name] = Vector<Ref<StandardMaterial3D>>();
963 materials[p_name].push_back(p_material);
964}
965
966Ref<StandardMaterial3D> EditorNode3DGizmoPlugin::get_material(const String &p_name, const Ref<EditorNode3DGizmo> &p_gizmo) {
967 ERR_FAIL_COND_V(!materials.has(p_name), Ref<StandardMaterial3D>());
968 ERR_FAIL_COND_V(materials[p_name].size() == 0, Ref<StandardMaterial3D>());
969
970 if (p_gizmo.is_null() || materials[p_name].size() == 1) {
971 return materials[p_name][0];
972 }
973
974 int index = (p_gizmo->is_selected() ? 1 : 0) + (p_gizmo->is_editable() ? 2 : 0);
975
976 Ref<StandardMaterial3D> mat = materials[p_name][index];
977
978 if (current_state == ON_TOP && p_gizmo->is_selected()) {
979 mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
980 } else {
981 mat->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, false);
982 }
983
984 return mat;
985}
986
987String EditorNode3DGizmoPlugin::get_gizmo_name() const {
988 String ret;
989 if (GDVIRTUAL_CALL(_get_gizmo_name, ret)) {
990 return ret;
991 }
992
993 WARN_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.");
994 return TTR("Unnamed Gizmo");
995}
996
997int EditorNode3DGizmoPlugin::get_priority() const {
998 int ret;
999 if (GDVIRTUAL_CALL(_get_priority, ret)) {
1000 return ret;
1001 }
1002 return 0;
1003}
1004
1005Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::get_gizmo(Node3D *p_spatial) {
1006 if (get_script_instance() && get_script_instance()->has_method("_get_gizmo")) {
1007 return get_script_instance()->call("_get_gizmo", p_spatial);
1008 }
1009
1010 Ref<EditorNode3DGizmo> ref = create_gizmo(p_spatial);
1011
1012 if (ref.is_null()) {
1013 return ref;
1014 }
1015
1016 ref->set_plugin(this);
1017 ref->set_node_3d(p_spatial);
1018 ref->set_hidden(current_state == HIDDEN);
1019
1020 current_gizmos.push_back(ref.ptr());
1021 return ref;
1022}
1023
1024void EditorNode3DGizmoPlugin::_bind_methods() {
1025 ClassDB::bind_method(D_METHOD("create_material", "name", "color", "billboard", "on_top", "use_vertex_color"), &EditorNode3DGizmoPlugin::create_material, DEFVAL(false), DEFVAL(false), DEFVAL(false));
1026 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)));
1027 ClassDB::bind_method(D_METHOD("create_handle_material", "name", "billboard", "texture"), &EditorNode3DGizmoPlugin::create_handle_material, DEFVAL(false), DEFVAL(Variant()));
1028 ClassDB::bind_method(D_METHOD("add_material", "name", "material"), &EditorNode3DGizmoPlugin::add_material);
1029
1030 ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorNode3DGizmoPlugin::get_material, DEFVAL(Ref<EditorNode3DGizmo>()));
1031
1032 GDVIRTUAL_BIND(_has_gizmo, "for_node_3d");
1033 GDVIRTUAL_BIND(_create_gizmo, "for_node_3d");
1034
1035 GDVIRTUAL_BIND(_get_gizmo_name);
1036 GDVIRTUAL_BIND(_get_priority);
1037 GDVIRTUAL_BIND(_can_be_hidden);
1038 GDVIRTUAL_BIND(_is_selectable_when_hidden);
1039
1040 GDVIRTUAL_BIND(_redraw, "gizmo");
1041 GDVIRTUAL_BIND(_get_handle_name, "gizmo", "handle_id", "secondary");
1042 GDVIRTUAL_BIND(_is_handle_highlighted, "gizmo", "handle_id", "secondary");
1043 GDVIRTUAL_BIND(_get_handle_value, "gizmo", "handle_id", "secondary");
1044
1045 GDVIRTUAL_BIND(_set_handle, "gizmo", "handle_id", "secondary", "camera", "screen_pos");
1046 GDVIRTUAL_BIND(_commit_handle, "gizmo", "handle_id", "secondary", "restore", "cancel");
1047
1048 GDVIRTUAL_BIND(_subgizmos_intersect_ray, "gizmo", "camera", "screen_pos");
1049 GDVIRTUAL_BIND(_subgizmos_intersect_frustum, "gizmo", "camera", "frustum_planes");
1050 GDVIRTUAL_BIND(_get_subgizmo_transform, "gizmo", "subgizmo_id");
1051 GDVIRTUAL_BIND(_set_subgizmo_transform, "gizmo", "subgizmo_id", "transform");
1052 GDVIRTUAL_BIND(_commit_subgizmos, "gizmo", "ids", "restores", "cancel");
1053}
1054
1055bool EditorNode3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
1056 bool success = false;
1057 GDVIRTUAL_CALL(_has_gizmo, p_spatial, success);
1058 return success;
1059}
1060
1061Ref<EditorNode3DGizmo> EditorNode3DGizmoPlugin::create_gizmo(Node3D *p_spatial) {
1062 Ref<EditorNode3DGizmo> ret;
1063 if (GDVIRTUAL_CALL(_create_gizmo, p_spatial, ret)) {
1064 return ret;
1065 }
1066
1067 Ref<EditorNode3DGizmo> ref;
1068 if (has_gizmo(p_spatial)) {
1069 ref.instantiate();
1070 }
1071 return ref;
1072}
1073
1074bool EditorNode3DGizmoPlugin::can_be_hidden() const {
1075 bool ret = true;
1076 GDVIRTUAL_CALL(_can_be_hidden, ret);
1077 return ret;
1078}
1079
1080bool EditorNode3DGizmoPlugin::is_selectable_when_hidden() const {
1081 bool ret = false;
1082 GDVIRTUAL_CALL(_is_selectable_when_hidden, ret);
1083 return ret;
1084}
1085
1086void EditorNode3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
1087 GDVIRTUAL_CALL(_redraw, p_gizmo);
1088}
1089
1090bool EditorNode3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
1091 bool ret = false;
1092 GDVIRTUAL_CALL(_is_handle_highlighted, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, ret);
1093 return ret;
1094}
1095
1096String EditorNode3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
1097 String ret;
1098 GDVIRTUAL_CALL(_get_handle_name, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, ret);
1099 return ret;
1100}
1101
1102Variant EditorNode3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const {
1103 Variant ret;
1104 GDVIRTUAL_CALL(_get_handle_value, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, ret);
1105 return ret;
1106}
1107
1108void EditorNode3DGizmoPlugin::begin_handle_action(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) {
1109 GDVIRTUAL_CALL(_begin_handle_action, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary);
1110}
1111
1112void EditorNode3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) {
1113 GDVIRTUAL_CALL(_set_handle, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, p_camera, p_point);
1114}
1115
1116void EditorNode3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) {
1117 GDVIRTUAL_CALL(_commit_handle, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_secondary, p_restore, p_cancel);
1118}
1119
1120int EditorNode3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const {
1121 int ret = -1;
1122 GDVIRTUAL_CALL(_subgizmos_intersect_ray, Ref<EditorNode3DGizmo>(p_gizmo), p_camera, p_point, ret);
1123 return ret;
1124}
1125
1126Vector<int> EditorNode3DGizmoPlugin::subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const {
1127 TypedArray<Plane> frustum;
1128 frustum.resize(p_frustum.size());
1129 for (int i = 0; i < p_frustum.size(); i++) {
1130 frustum[i] = p_frustum[i];
1131 }
1132 Vector<int> ret;
1133 GDVIRTUAL_CALL(_subgizmos_intersect_frustum, Ref<EditorNode3DGizmo>(p_gizmo), p_camera, frustum, ret);
1134 return ret;
1135}
1136
1137Transform3D EditorNode3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const {
1138 Transform3D ret;
1139 GDVIRTUAL_CALL(_get_subgizmo_transform, Ref<EditorNode3DGizmo>(p_gizmo), p_id, ret);
1140 return ret;
1141}
1142
1143void EditorNode3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) {
1144 GDVIRTUAL_CALL(_set_subgizmo_transform, Ref<EditorNode3DGizmo>(p_gizmo), p_id, p_transform);
1145}
1146
1147void EditorNode3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
1148 TypedArray<Transform3D> restore;
1149 restore.resize(p_restore.size());
1150 for (int i = 0; i < p_restore.size(); i++) {
1151 restore[i] = p_restore[i];
1152 }
1153
1154 GDVIRTUAL_CALL(_commit_subgizmos, Ref<EditorNode3DGizmo>(p_gizmo), p_ids, restore, p_cancel);
1155}
1156
1157void EditorNode3DGizmoPlugin::set_state(int p_state) {
1158 current_state = p_state;
1159 for (int i = 0; i < current_gizmos.size(); ++i) {
1160 current_gizmos[i]->set_hidden(current_state == HIDDEN);
1161 }
1162}
1163
1164int EditorNode3DGizmoPlugin::get_state() const {
1165 return current_state;
1166}
1167
1168void EditorNode3DGizmoPlugin::unregister_gizmo(EditorNode3DGizmo *p_gizmo) {
1169 current_gizmos.erase(p_gizmo);
1170}
1171
1172EditorNode3DGizmoPlugin::EditorNode3DGizmoPlugin() {
1173 current_state = VISIBLE;
1174}
1175
1176EditorNode3DGizmoPlugin::~EditorNode3DGizmoPlugin() {
1177 for (int i = 0; i < current_gizmos.size(); ++i) {
1178 current_gizmos[i]->set_plugin(nullptr);
1179 current_gizmos[i]->get_node_3d()->remove_gizmo(current_gizmos[i]);
1180 }
1181 if (Node3DEditor::get_singleton()) {
1182 Node3DEditor::get_singleton()->update_all_gizmos();
1183 }
1184}
1185
1186//////
1187