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