1 /*************************************************************************/
2 /* spatial_editor_plugin.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
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 "spatial_editor_plugin.h"
32
33 #include "core/math/camera_matrix.h"
34 #include "core/os/input.h"
35 #include "core/os/keyboard.h"
36 #include "core/print_string.h"
37 #include "core/project_settings.h"
38 #include "core/sort_array.h"
39 #include "editor/editor_node.h"
40 #include "editor/editor_scale.h"
41 #include "editor/editor_settings.h"
42 #include "editor/plugins/animation_player_editor_plugin.h"
43 #include "editor/plugins/script_editor_plugin.h"
44 #include "editor/script_editor_debugger.h"
45 #include "editor/spatial_editor_gizmos.h"
46 #include "scene/3d/camera.h"
47 #include "scene/3d/collision_shape.h"
48 #include "scene/3d/mesh_instance.h"
49 #include "scene/3d/physics_body.h"
50 #include "scene/3d/visual_instance.h"
51 #include "scene/gui/viewport_container.h"
52 #include "scene/resources/packed_scene.h"
53 #include "scene/resources/surface_tool.h"
54
55 #define DISTANCE_DEFAULT 4
56
57 #define GIZMO_ARROW_SIZE 0.35
58 #define GIZMO_RING_HALF_WIDTH 0.1
59 #define GIZMO_SCALE_DEFAULT 0.15
60 #define GIZMO_PLANE_SIZE 0.2
61 #define GIZMO_PLANE_DST 0.3
62 #define GIZMO_CIRCLE_SIZE 1.1
63 #define GIZMO_SCALE_OFFSET (GIZMO_CIRCLE_SIZE + 0.3)
64 #define GIZMO_ARROW_OFFSET (GIZMO_CIRCLE_SIZE + 0.3)
65
66 #define ZOOM_MIN_DISTANCE 0.001
67 #define ZOOM_MULTIPLIER 1.08
68 #define ZOOM_INDICATOR_DELAY_S 1.5
69
70 #define FREELOOK_MIN_SPEED 0.01
71 #define FREELOOK_SPEED_MULTIPLIER 1.08
72
73 #define MIN_Z 0.01
74 #define MAX_Z 1000000.0
75
76 #define MIN_FOV 0.01
77 #define MAX_FOV 179
78
_notification(int p_what)79 void ViewportRotationControl::_notification(int p_what) {
80
81 if (p_what == NOTIFICATION_ENTER_TREE) {
82 axis_menu_options.clear();
83 axis_menu_options.push_back(SpatialEditorViewport::VIEW_RIGHT);
84 axis_menu_options.push_back(SpatialEditorViewport::VIEW_TOP);
85 axis_menu_options.push_back(SpatialEditorViewport::VIEW_FRONT);
86 axis_menu_options.push_back(SpatialEditorViewport::VIEW_LEFT);
87 axis_menu_options.push_back(SpatialEditorViewport::VIEW_BOTTOM);
88 axis_menu_options.push_back(SpatialEditorViewport::VIEW_REAR);
89
90 axis_colors.clear();
91 axis_colors.push_back(get_color("axis_x_color", "Editor"));
92 axis_colors.push_back(get_color("axis_y_color", "Editor"));
93 axis_colors.push_back(get_color("axis_z_color", "Editor"));
94 update();
95
96 if (!is_connected("mouse_exited", this, "_on_mouse_exited")) {
97 connect("mouse_exited", this, "_on_mouse_exited");
98 }
99 }
100
101 if (p_what == NOTIFICATION_DRAW && viewport != nullptr) {
102 _draw();
103 }
104 }
105
_draw()106 void ViewportRotationControl::_draw() {
107 Vector2i center = get_size() / 2.0;
108 float radius = get_size().x / 2.0;
109
110 if (focused_axis > -2 || orbiting) {
111 draw_circle(center, radius, Color(0.5, 0.5, 0.5, 0.25));
112 }
113
114 Vector<Axis2D> axis_to_draw;
115 _get_sorted_axis(axis_to_draw);
116 for (int i = 0; i < axis_to_draw.size(); ++i) {
117 _draw_axis(axis_to_draw[i]);
118 }
119 }
120
_draw_axis(const Axis2D & p_axis)121 void ViewportRotationControl::_draw_axis(const Axis2D &p_axis) {
122 bool focused = focused_axis == p_axis.axis;
123 bool positive = p_axis.axis < 3;
124 bool front = (Math::abs(p_axis.z_axis) <= 0.001 && positive) || p_axis.z_axis > 0.001;
125 int direction = p_axis.axis % 3;
126
127 Color axis_color = axis_colors[direction];
128
129 if (!front) {
130 axis_color = axis_color.darkened(0.4);
131 }
132 Color c = focused ? Color(0.9, 0.9, 0.9) : axis_color;
133
134 if (positive) {
135 Vector2i center = get_size() / 2.0;
136 draw_line(center, p_axis.screen_point, c, 1.5 * EDSCALE, true);
137 }
138
139 if (front) {
140 String axis_name = direction == 0 ? "X" : (direction == 1 ? "Y" : "Z");
141 draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS, c);
142 draw_char(get_font("rotation_control", "EditorFonts"), p_axis.screen_point + Vector2(-4.0, 5.0) * EDSCALE, axis_name, "", Color(0.3, 0.3, 0.3));
143 } else {
144 draw_circle(p_axis.screen_point, AXIS_CIRCLE_RADIUS * (0.55 + (0.2 * (1.0 + p_axis.z_axis))), c);
145 }
146 }
147
_get_sorted_axis(Vector<Axis2D> & r_axis)148 void ViewportRotationControl::_get_sorted_axis(Vector<Axis2D> &r_axis) {
149 Vector2i center = get_size() / 2.0;
150 float radius = get_size().x / 2.0;
151
152 float axis_radius = radius - AXIS_CIRCLE_RADIUS - 2.0 * EDSCALE;
153 Basis camera_basis = viewport->to_camera_transform(viewport->cursor).get_basis().inverse();
154
155 for (int i = 0; i < 3; ++i) {
156 Vector3 axis_3d = camera_basis.get_axis(i);
157 Vector2i axis_vector = Vector2(axis_3d.x, -axis_3d.y) * axis_radius;
158
159 if (Math::abs(axis_3d.z) < 1.0) {
160 Axis2D pos_axis;
161 pos_axis.axis = i;
162 pos_axis.screen_point = center + axis_vector;
163 pos_axis.z_axis = axis_3d.z;
164 r_axis.push_back(pos_axis);
165
166 Axis2D neg_axis;
167 neg_axis.axis = i + 3;
168 neg_axis.screen_point = center - axis_vector;
169 neg_axis.z_axis = -axis_3d.z;
170 r_axis.push_back(neg_axis);
171 } else {
172 // Special case when the camera is aligned with one axis
173 Axis2D axis;
174 axis.axis = i + (axis_3d.z < 0 ? 0 : 3);
175 axis.screen_point = center;
176 axis.z_axis = 1.0;
177 r_axis.push_back(axis);
178 }
179 }
180
181 r_axis.sort_custom<Axis2DCompare>();
182 }
183
_gui_input(Ref<InputEvent> p_event)184 void ViewportRotationControl::_gui_input(Ref<InputEvent> p_event) {
185 const Ref<InputEventMouseButton> mb = p_event;
186 if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) {
187 Vector2 pos = mb->get_position();
188 if (mb->is_pressed()) {
189 if (pos.distance_to(get_size() / 2.0) < get_size().x / 2.0) {
190 orbiting = true;
191 }
192 } else {
193 if (focused_axis > -1) {
194 viewport->_menu_option(axis_menu_options[focused_axis]);
195 _update_focus();
196 }
197 orbiting = false;
198 if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED) {
199 Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
200 Input::get_singleton()->warp_mouse_position(orbiting_mouse_start);
201 }
202 }
203 }
204
205 const Ref<InputEventMouseMotion> mm = p_event;
206 if (mm.is_valid()) {
207 if (orbiting) {
208 if (Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_VISIBLE) {
209 Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
210 orbiting_mouse_start = mm->get_global_position();
211 }
212 viewport->_nav_orbit(mm, viewport->_get_warped_mouse_motion(mm));
213 focused_axis = -1;
214 } else {
215 _update_focus();
216 }
217 }
218 }
219
_update_focus()220 void ViewportRotationControl::_update_focus() {
221 int original_focus = focused_axis;
222 focused_axis = -2;
223 Vector2 mouse_pos = get_local_mouse_position();
224
225 if (mouse_pos.distance_to(get_size() / 2.0) < get_size().x / 2.0) {
226 focused_axis = -1;
227 }
228
229 Vector<Axis2D> axes;
230 _get_sorted_axis(axes);
231
232 for (int i = 0; i < axes.size(); i++) {
233 const Axis2D &axis = axes[i];
234 if (mouse_pos.distance_to(axis.screen_point) < AXIS_CIRCLE_RADIUS) {
235 focused_axis = axis.axis;
236 }
237 }
238
239 if (focused_axis != original_focus) {
240 update();
241 }
242 }
243
_on_mouse_exited()244 void ViewportRotationControl::_on_mouse_exited() {
245 focused_axis = -2;
246 update();
247 }
248
set_viewport(SpatialEditorViewport * p_viewport)249 void ViewportRotationControl::set_viewport(SpatialEditorViewport *p_viewport) {
250 viewport = p_viewport;
251 }
252
_bind_methods()253 void ViewportRotationControl::_bind_methods() {
254 ClassDB::bind_method(D_METHOD("_gui_input"), &ViewportRotationControl::_gui_input);
255 ClassDB::bind_method(D_METHOD("_on_mouse_exited"), &ViewportRotationControl::_on_mouse_exited);
256 }
257
_update_camera(float p_interp_delta)258 void SpatialEditorViewport::_update_camera(float p_interp_delta) {
259
260 bool is_orthogonal = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL;
261
262 Cursor old_camera_cursor = camera_cursor;
263 camera_cursor = cursor;
264
265 if (p_interp_delta > 0) {
266
267 //-------
268 // Perform smoothing
269
270 if (is_freelook_active()) {
271
272 // Higher inertia should increase "lag" (lerp with factor between 0 and 1)
273 // Inertia of zero should produce instant movement (lerp with factor of 1) in this case it returns a really high value and gets clamped to 1.
274 real_t inertia = EDITOR_GET("editors/3d/freelook/freelook_inertia");
275 inertia = MAX(0.001, inertia);
276 real_t factor = (1.0 / inertia) * p_interp_delta;
277
278 // We interpolate a different point here, because in freelook mode the focus point (cursor.pos) orbits around eye_pos
279 camera_cursor.eye_pos = old_camera_cursor.eye_pos.linear_interpolate(cursor.eye_pos, CLAMP(factor, 0, 1));
280
281 float orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
282 orbit_inertia = MAX(0.0001, orbit_inertia);
283 camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
284 camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
285
286 if (Math::abs(camera_cursor.x_rot - cursor.x_rot) < 0.1) {
287 camera_cursor.x_rot = cursor.x_rot;
288 }
289 if (Math::abs(camera_cursor.y_rot - cursor.y_rot) < 0.1) {
290 camera_cursor.y_rot = cursor.y_rot;
291 }
292
293 Vector3 forward = to_camera_transform(camera_cursor).basis.xform(Vector3(0, 0, -1));
294 camera_cursor.pos = camera_cursor.eye_pos + forward * camera_cursor.distance;
295
296 } else {
297
298 //when not being manipulated, move softly
299 float free_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
300 float free_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia");
301 //when being manipulated, move more quickly
302 float manip_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_orbit_inertia");
303 float manip_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_translation_inertia");
304
305 float zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia");
306
307 //determine if being manipulated
308 bool manipulated = Input::get_singleton()->get_mouse_button_mask() & (2 | 4);
309 manipulated |= Input::get_singleton()->is_key_pressed(KEY_SHIFT);
310 manipulated |= Input::get_singleton()->is_key_pressed(KEY_ALT);
311 manipulated |= Input::get_singleton()->is_key_pressed(KEY_CONTROL);
312
313 float orbit_inertia = MAX(0.00001, manipulated ? manip_orbit_inertia : free_orbit_inertia);
314 float translation_inertia = MAX(0.0001, manipulated ? manip_translation_inertia : free_translation_inertia);
315 zoom_inertia = MAX(0.0001, zoom_inertia);
316
317 camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
318 camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
319
320 if (Math::abs(camera_cursor.x_rot - cursor.x_rot) < 0.1) {
321 camera_cursor.x_rot = cursor.x_rot;
322 }
323 if (Math::abs(camera_cursor.y_rot - cursor.y_rot) < 0.1) {
324 camera_cursor.y_rot = cursor.y_rot;
325 }
326
327 camera_cursor.pos = old_camera_cursor.pos.linear_interpolate(cursor.pos, MIN(1.f, p_interp_delta * (1 / translation_inertia)));
328 camera_cursor.distance = Math::lerp(old_camera_cursor.distance, cursor.distance, MIN(1.f, p_interp_delta * (1 / zoom_inertia)));
329 }
330 }
331
332 //-------
333 // Apply camera transform
334
335 float tolerance = 0.001;
336 bool equal = true;
337 if (Math::abs(old_camera_cursor.x_rot - camera_cursor.x_rot) > tolerance || Math::abs(old_camera_cursor.y_rot - camera_cursor.y_rot) > tolerance) {
338 equal = false;
339 }
340
341 if (equal && old_camera_cursor.pos.distance_squared_to(camera_cursor.pos) > tolerance * tolerance) {
342 equal = false;
343 }
344
345 if (equal && Math::abs(old_camera_cursor.distance - camera_cursor.distance) > tolerance) {
346 equal = false;
347 }
348
349 if (!equal || p_interp_delta == 0 || is_freelook_active() || is_orthogonal != orthogonal) {
350
351 camera->set_global_transform(to_camera_transform(camera_cursor));
352
353 if (orthogonal) {
354 float half_fov = Math::deg2rad(get_fov()) / 2.0;
355 float height = 2.0 * cursor.distance * Math::tan(half_fov);
356 camera->set_orthogonal(height, get_znear(), get_zfar());
357 } else {
358 camera->set_perspective(get_fov(), get_znear(), get_zfar());
359 }
360
361 update_transform_gizmo_view();
362 rotation_control->update();
363 }
364 }
365
to_camera_transform(const Cursor & p_cursor) const366 Transform SpatialEditorViewport::to_camera_transform(const Cursor &p_cursor) const {
367 Transform camera_transform;
368 camera_transform.translate(p_cursor.pos);
369 camera_transform.basis.rotate(Vector3(1, 0, 0), -p_cursor.x_rot);
370 camera_transform.basis.rotate(Vector3(0, 1, 0), -p_cursor.y_rot);
371
372 if (orthogonal)
373 camera_transform.translate(0, 0, (get_zfar() - get_znear()) / 2.0);
374 else
375 camera_transform.translate(0, 0, p_cursor.distance);
376
377 return camera_transform;
378 }
379
get_selected_count() const380 int SpatialEditorViewport::get_selected_count() const {
381
382 Map<Node *, Object *> &selection = editor_selection->get_selection();
383
384 int count = 0;
385
386 for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
387
388 Spatial *sp = Object::cast_to<Spatial>(E->key());
389 if (!sp)
390 continue;
391
392 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
393 if (!se)
394 continue;
395
396 count++;
397 }
398
399 return count;
400 }
401
get_znear() const402 float SpatialEditorViewport::get_znear() const {
403
404 return CLAMP(spatial_editor->get_znear(), MIN_Z, MAX_Z);
405 }
get_zfar() const406 float SpatialEditorViewport::get_zfar() const {
407
408 return CLAMP(spatial_editor->get_zfar(), MIN_Z, MAX_Z);
409 }
get_fov() const410 float SpatialEditorViewport::get_fov() const {
411
412 return CLAMP(spatial_editor->get_fov(), MIN_FOV, MAX_FOV);
413 }
414
_get_camera_transform() const415 Transform SpatialEditorViewport::_get_camera_transform() const {
416
417 return camera->get_global_transform();
418 }
419
_get_camera_position() const420 Vector3 SpatialEditorViewport::_get_camera_position() const {
421
422 return _get_camera_transform().origin;
423 }
424
_point_to_screen(const Vector3 & p_point)425 Point2 SpatialEditorViewport::_point_to_screen(const Vector3 &p_point) {
426
427 return camera->unproject_position(p_point) * viewport_container->get_stretch_shrink();
428 }
429
_get_ray_pos(const Vector2 & p_pos) const430 Vector3 SpatialEditorViewport::_get_ray_pos(const Vector2 &p_pos) const {
431
432 return camera->project_ray_origin(p_pos / viewport_container->get_stretch_shrink());
433 }
434
_get_camera_normal() const435 Vector3 SpatialEditorViewport::_get_camera_normal() const {
436
437 return -_get_camera_transform().basis.get_axis(2);
438 }
439
_get_ray(const Vector2 & p_pos) const440 Vector3 SpatialEditorViewport::_get_ray(const Vector2 &p_pos) const {
441
442 return camera->project_ray_normal(p_pos / viewport_container->get_stretch_shrink());
443 }
444
_clear_selected()445 void SpatialEditorViewport::_clear_selected() {
446
447 editor_selection->clear();
448 }
449
_select_clicked(bool p_append,bool p_single)450 void SpatialEditorViewport::_select_clicked(bool p_append, bool p_single) {
451
452 if (!clicked)
453 return;
454
455 Node *node = Object::cast_to<Node>(ObjectDB::get_instance(clicked));
456 Spatial *selected = Object::cast_to<Spatial>(node);
457 if (!selected)
458 return;
459
460 // Replace the node by the group if grouped
461 while (node && node != editor->get_edited_scene()->get_parent()) {
462 Spatial *selected_tmp = Object::cast_to<Spatial>(node);
463 if (selected_tmp && node->has_meta("_edit_group_")) {
464 selected = selected_tmp;
465 }
466 node = node->get_parent();
467 }
468
469 if (!_is_node_locked(selected))
470 _select(selected, clicked_wants_append, true);
471 }
472
_select(Node * p_node,bool p_append,bool p_single)473 void SpatialEditorViewport::_select(Node *p_node, bool p_append, bool p_single) {
474
475 if (!p_append) {
476 editor_selection->clear();
477 }
478
479 if (editor_selection->is_selected(p_node)) {
480 //erase
481 editor_selection->remove_node(p_node);
482 } else {
483
484 editor_selection->add_node(p_node);
485 }
486
487 if (p_single) {
488 if (Engine::get_singleton()->is_editor_hint())
489 editor->call("edit_node", p_node);
490 }
491 }
492
_select_ray(const Point2 & p_pos,bool p_append,bool & r_includes_current,int * r_gizmo_handle,bool p_alt_select)493 ObjectID SpatialEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, bool &r_includes_current, int *r_gizmo_handle, bool p_alt_select) {
494
495 if (r_gizmo_handle)
496 *r_gizmo_handle = -1;
497
498 Vector3 ray = _get_ray(p_pos);
499 Vector3 pos = _get_ray_pos(p_pos);
500 Vector2 shrinked_pos = p_pos / viewport_container->get_stretch_shrink();
501
502 Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world()->get_scenario());
503 Set<Ref<EditorSpatialGizmo> > found_gizmos;
504
505 Node *edited_scene = get_tree()->get_edited_scene_root();
506 ObjectID closest = 0;
507 Node *item = NULL;
508 float closest_dist = 1e20;
509 int selected_handle = -1;
510
511 for (int i = 0; i < instances.size(); i++) {
512
513 Spatial *spat = Object::cast_to<Spatial>(ObjectDB::get_instance(instances[i]));
514
515 if (!spat)
516 continue;
517
518 Ref<EditorSpatialGizmo> seg = spat->get_gizmo();
519
520 if ((!seg.is_valid()) || found_gizmos.has(seg)) {
521 continue;
522 }
523
524 found_gizmos.insert(seg);
525 Vector3 point;
526 Vector3 normal;
527
528 int handle = -1;
529 bool inters = seg->intersect_ray(camera, shrinked_pos, point, normal, &handle, p_alt_select);
530
531 if (!inters)
532 continue;
533
534 float dist = pos.distance_to(point);
535
536 if (dist < 0)
537 continue;
538
539 if (dist < closest_dist) {
540
541 item = Object::cast_to<Node>(spat);
542 while (item->get_owner() && item->get_owner() != edited_scene && !edited_scene->is_editable_instance(item->get_owner())) {
543 item = item->get_owner();
544 }
545
546 closest = item->get_instance_id();
547 closest_dist = dist;
548 selected_handle = handle;
549 }
550 }
551
552 if (!item)
553 return 0;
554
555 if (!editor_selection->is_selected(item) || (r_gizmo_handle && selected_handle >= 0)) {
556
557 if (r_gizmo_handle)
558 *r_gizmo_handle = selected_handle;
559 }
560
561 return closest;
562 }
563
_find_items_at_pos(const Point2 & p_pos,bool & r_includes_current,Vector<_RayResult> & results,bool p_alt_select)564 void SpatialEditorViewport::_find_items_at_pos(const Point2 &p_pos, bool &r_includes_current, Vector<_RayResult> &results, bool p_alt_select) {
565
566 Vector3 ray = _get_ray(p_pos);
567 Vector3 pos = _get_ray_pos(p_pos);
568
569 Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world()->get_scenario());
570 Set<Ref<EditorSpatialGizmo> > found_gizmos;
571
572 r_includes_current = false;
573
574 for (int i = 0; i < instances.size(); i++) {
575
576 Spatial *spat = Object::cast_to<Spatial>(ObjectDB::get_instance(instances[i]));
577
578 if (!spat)
579 continue;
580
581 Ref<EditorSpatialGizmo> seg = spat->get_gizmo();
582
583 if (!seg.is_valid())
584 continue;
585
586 if (found_gizmos.has(seg))
587 continue;
588
589 found_gizmos.insert(seg);
590 Vector3 point;
591 Vector3 normal;
592
593 int handle = -1;
594 bool inters = seg->intersect_ray(camera, p_pos, point, normal, NULL, p_alt_select);
595
596 if (!inters)
597 continue;
598
599 float dist = pos.distance_to(point);
600
601 if (dist < 0)
602 continue;
603
604 if (editor_selection->is_selected(spat))
605 r_includes_current = true;
606
607 _RayResult res;
608 res.item = spat;
609 res.depth = dist;
610 res.handle = handle;
611 results.push_back(res);
612 }
613
614 if (results.empty())
615 return;
616
617 results.sort();
618 }
619
_get_screen_to_space(const Vector3 & p_vector3)620 Vector3 SpatialEditorViewport::_get_screen_to_space(const Vector3 &p_vector3) {
621
622 CameraMatrix cm;
623 if (orthogonal) {
624 cm.set_orthogonal(camera->get_size(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar());
625 } else {
626 cm.set_perspective(get_fov(), get_size().aspect(), get_znear() + p_vector3.z, get_zfar());
627 }
628 Vector2 screen_he = cm.get_viewport_half_extents();
629
630 Transform camera_transform;
631 camera_transform.translate(cursor.pos);
632 camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot);
633 camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot);
634 camera_transform.translate(0, 0, cursor.distance);
635
636 return camera_transform.xform(Vector3(((p_vector3.x / get_size().width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (p_vector3.y / get_size().height)) * 2.0 - 1.0) * screen_he.y, -(get_znear() + p_vector3.z)));
637 }
638
_select_region()639 void SpatialEditorViewport::_select_region() {
640
641 if (cursor.region_begin == cursor.region_end)
642 return; //nothing really
643
644 float z_offset = MAX(0.0, 5.0 - get_znear());
645
646 Vector3 box[4] = {
647 Vector3(
648 MIN(cursor.region_begin.x, cursor.region_end.x),
649 MIN(cursor.region_begin.y, cursor.region_end.y),
650 z_offset),
651 Vector3(
652 MAX(cursor.region_begin.x, cursor.region_end.x),
653 MIN(cursor.region_begin.y, cursor.region_end.y),
654 z_offset),
655 Vector3(
656 MAX(cursor.region_begin.x, cursor.region_end.x),
657 MAX(cursor.region_begin.y, cursor.region_end.y),
658 z_offset),
659 Vector3(
660 MIN(cursor.region_begin.x, cursor.region_end.x),
661 MAX(cursor.region_begin.y, cursor.region_end.y),
662 z_offset)
663 };
664
665 Vector<Plane> frustum;
666
667 Vector3 cam_pos = _get_camera_position();
668
669 for (int i = 0; i < 4; i++) {
670
671 Vector3 a = _get_screen_to_space(box[i]);
672 Vector3 b = _get_screen_to_space(box[(i + 1) % 4]);
673 if (orthogonal) {
674 frustum.push_back(Plane(a, (a - b).normalized()));
675 } else {
676 frustum.push_back(Plane(a, b, cam_pos));
677 }
678 }
679
680 Plane near(cam_pos, -_get_camera_normal());
681 near.d -= get_znear();
682 frustum.push_back(near);
683
684 Plane far = -near;
685 far.d += get_zfar();
686 frustum.push_back(far);
687
688 Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world()->get_scenario());
689 Vector<Node *> selected;
690
691 Node *edited_scene = get_tree()->get_edited_scene_root();
692
693 for (int i = 0; i < instances.size(); i++) {
694
695 Spatial *sp = Object::cast_to<Spatial>(ObjectDB::get_instance(instances[i]));
696 if (!sp || _is_node_locked(sp))
697 continue;
698
699 Node *item = Object::cast_to<Node>(sp);
700 while (item->get_owner() && item->get_owner() != edited_scene && !edited_scene->is_editable_instance(item->get_owner())) {
701 item = item->get_owner();
702 }
703
704 // Replace the node by the group if grouped
705 if (item->is_class("Spatial")) {
706 Spatial *sel = Object::cast_to<Spatial>(item);
707 while (item && item != editor->get_edited_scene()->get_parent()) {
708 Spatial *selected_tmp = Object::cast_to<Spatial>(item);
709 if (selected_tmp && item->has_meta("_edit_group_")) {
710 sel = selected_tmp;
711 }
712 item = item->get_parent();
713 }
714 item = sel;
715 }
716
717 if (selected.find(item) != -1) continue;
718
719 if (_is_node_locked(item)) continue;
720
721 Ref<EditorSpatialGizmo> seg = sp->get_gizmo();
722
723 if (!seg.is_valid())
724 continue;
725
726 if (seg->intersect_frustum(camera, frustum)) {
727 selected.push_back(item);
728 }
729 }
730
731 bool single = selected.size() == 1;
732 for (int i = 0; i < selected.size(); i++) {
733 _select(selected[i], true, single);
734 }
735 }
736
_update_name()737 void SpatialEditorViewport::_update_name() {
738
739 String view_mode = orthogonal ? TTR("Orthogonal") : TTR("Perspective");
740
741 if (auto_orthogonal) {
742 view_mode += " [auto]";
743 }
744
745 if (name != "")
746 view_menu->set_text(name + " " + view_mode);
747 else
748 view_menu->set_text(view_mode);
749
750 view_menu->set_size(Vector2(0, 0)); // resets the button size
751 }
752
_compute_edit(const Point2 & p_point)753 void SpatialEditorViewport::_compute_edit(const Point2 &p_point) {
754
755 _edit.click_ray = _get_ray(Vector2(p_point.x, p_point.y));
756 _edit.click_ray_pos = _get_ray_pos(Vector2(p_point.x, p_point.y));
757 _edit.plane = TRANSFORM_VIEW;
758 spatial_editor->update_transform_gizmo();
759 _edit.center = spatial_editor->get_gizmo_transform().origin;
760
761 List<Node *> &selection = editor_selection->get_selected_node_list();
762
763 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
764
765 Spatial *sp = Object::cast_to<Spatial>(E->get());
766 if (!sp)
767 continue;
768
769 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
770 if (!se)
771 continue;
772
773 se->original = se->sp->get_global_gizmo_transform();
774 se->original_local = se->sp->get_local_gizmo_transform();
775 }
776 }
777
_get_key_modifier_setting(const String & p_property)778 static int _get_key_modifier_setting(const String &p_property) {
779
780 switch (EditorSettings::get_singleton()->get(p_property).operator int()) {
781
782 case 0: return 0;
783 case 1: return KEY_SHIFT;
784 case 2: return KEY_ALT;
785 case 3: return KEY_META;
786 case 4: return KEY_CONTROL;
787 }
788 return 0;
789 }
790
_get_key_modifier(Ref<InputEventWithModifiers> e)791 static int _get_key_modifier(Ref<InputEventWithModifiers> e) {
792 if (e->get_shift())
793 return KEY_SHIFT;
794 if (e->get_alt())
795 return KEY_ALT;
796 if (e->get_control())
797 return KEY_CONTROL;
798 if (e->get_metakey())
799 return KEY_META;
800 return 0;
801 }
802
_gizmo_select(const Vector2 & p_screenpos,bool p_highlight_only)803 bool SpatialEditorViewport::_gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only) {
804
805 if (!spatial_editor->is_gizmo_visible())
806 return false;
807 if (get_selected_count() == 0) {
808 if (p_highlight_only)
809 spatial_editor->select_gizmo_highlight_axis(-1);
810 return false;
811 }
812
813 Vector3 ray_pos = _get_ray_pos(Vector2(p_screenpos.x, p_screenpos.y));
814 Vector3 ray = _get_ray(Vector2(p_screenpos.x, p_screenpos.y));
815
816 Transform gt = spatial_editor->get_gizmo_transform();
817 float gs = gizmo_scale;
818
819 if (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_MOVE) {
820
821 int col_axis = -1;
822 float col_d = 1e20;
823
824 for (int i = 0; i < 3; i++) {
825
826 Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gs * (GIZMO_ARROW_OFFSET + (GIZMO_ARROW_SIZE * 0.5));
827 float grabber_radius = gs * GIZMO_ARROW_SIZE;
828
829 Vector3 r;
830
831 if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) {
832 float d = r.distance_to(ray_pos);
833 if (d < col_d) {
834 col_d = d;
835 col_axis = i;
836 }
837 }
838 }
839
840 bool is_plane_translate = false;
841 // plane select
842 if (col_axis == -1) {
843 col_d = 1e20;
844
845 for (int i = 0; i < 3; i++) {
846
847 Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized();
848 Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized();
849
850 Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gs * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST);
851
852 Vector3 r;
853 Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
854
855 if (plane.intersects_ray(ray_pos, ray, &r)) {
856
857 float dist = r.distance_to(grabber_pos);
858 if (dist < (gs * GIZMO_PLANE_SIZE)) {
859
860 float d = ray_pos.distance_to(r);
861 if (d < col_d) {
862 col_d = d;
863 col_axis = i;
864
865 is_plane_translate = true;
866 }
867 }
868 }
869 }
870 }
871
872 if (col_axis != -1) {
873
874 if (p_highlight_only) {
875
876 spatial_editor->select_gizmo_highlight_axis(col_axis + (is_plane_translate ? 6 : 0));
877
878 } else {
879 //handle plane translate
880 _edit.mode = TRANSFORM_TRANSLATE;
881 _compute_edit(Point2(p_screenpos.x, p_screenpos.y));
882 _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_translate ? 3 : 0));
883 }
884 return true;
885 }
886 }
887
888 if (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_ROTATE) {
889
890 int col_axis = -1;
891 float col_d = 1e20;
892
893 for (int i = 0; i < 3; i++) {
894
895 Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
896 Vector3 r;
897 if (!plane.intersects_ray(ray_pos, ray, &r))
898 continue;
899
900 float dist = r.distance_to(gt.origin);
901
902 if (dist > gs * (GIZMO_CIRCLE_SIZE - GIZMO_RING_HALF_WIDTH) && dist < gs * (GIZMO_CIRCLE_SIZE + GIZMO_RING_HALF_WIDTH)) {
903
904 float d = ray_pos.distance_to(r);
905 if (d < col_d) {
906 col_d = d;
907 col_axis = i;
908 }
909 }
910 }
911
912 if (col_axis != -1) {
913
914 if (p_highlight_only) {
915
916 spatial_editor->select_gizmo_highlight_axis(col_axis + 3);
917 } else {
918 //handle rotate
919 _edit.mode = TRANSFORM_ROTATE;
920 _compute_edit(Point2(p_screenpos.x, p_screenpos.y));
921 _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis);
922 }
923 return true;
924 }
925 }
926
927 if (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SCALE) {
928
929 int col_axis = -1;
930 float col_d = 1e20;
931
932 for (int i = 0; i < 3; i++) {
933
934 Vector3 grabber_pos = gt.origin + gt.basis.get_axis(i) * gs * GIZMO_SCALE_OFFSET;
935 float grabber_radius = gs * GIZMO_ARROW_SIZE;
936
937 Vector3 r;
938
939 if (Geometry::segment_intersects_sphere(ray_pos, ray_pos + ray * MAX_Z, grabber_pos, grabber_radius, &r)) {
940 float d = r.distance_to(ray_pos);
941 if (d < col_d) {
942 col_d = d;
943 col_axis = i;
944 }
945 }
946 }
947
948 bool is_plane_scale = false;
949 // plane select
950 if (col_axis == -1) {
951 col_d = 1e20;
952
953 for (int i = 0; i < 3; i++) {
954
955 Vector3 ivec2 = gt.basis.get_axis((i + 1) % 3).normalized();
956 Vector3 ivec3 = gt.basis.get_axis((i + 2) % 3).normalized();
957
958 Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gs * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST);
959
960 Vector3 r;
961 Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
962
963 if (plane.intersects_ray(ray_pos, ray, &r)) {
964
965 float dist = r.distance_to(grabber_pos);
966 if (dist < (gs * GIZMO_PLANE_SIZE)) {
967
968 float d = ray_pos.distance_to(r);
969 if (d < col_d) {
970 col_d = d;
971 col_axis = i;
972
973 is_plane_scale = true;
974 }
975 }
976 }
977 }
978 }
979
980 if (col_axis != -1) {
981
982 if (p_highlight_only) {
983
984 spatial_editor->select_gizmo_highlight_axis(col_axis + (is_plane_scale ? 12 : 9));
985
986 } else {
987 //handle scale
988 _edit.mode = TRANSFORM_SCALE;
989 _compute_edit(Point2(p_screenpos.x, p_screenpos.y));
990 _edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_scale ? 3 : 0));
991 }
992 return true;
993 }
994 }
995
996 if (p_highlight_only)
997 spatial_editor->select_gizmo_highlight_axis(-1);
998
999 return false;
1000 }
1001
_surface_mouse_enter()1002 void SpatialEditorViewport::_surface_mouse_enter() {
1003
1004 if (!surface->has_focus() && (!get_focus_owner() || !get_focus_owner()->is_text_field()))
1005 surface->grab_focus();
1006 }
1007
_surface_mouse_exit()1008 void SpatialEditorViewport::_surface_mouse_exit() {
1009
1010 _remove_preview();
1011 }
1012
_surface_focus_enter()1013 void SpatialEditorViewport::_surface_focus_enter() {
1014
1015 view_menu->set_disable_shortcuts(false);
1016 }
1017
_surface_focus_exit()1018 void SpatialEditorViewport::_surface_focus_exit() {
1019
1020 view_menu->set_disable_shortcuts(true);
1021 }
_is_node_locked(const Node * p_node)1022 bool SpatialEditorViewport ::_is_node_locked(const Node *p_node) {
1023 return p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_");
1024 }
_list_select(Ref<InputEventMouseButton> b)1025 void SpatialEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
1026
1027 _find_items_at_pos(b->get_position(), clicked_includes_current, selection_results, b->get_shift());
1028
1029 Node *scene = editor->get_edited_scene();
1030
1031 for (int i = 0; i < selection_results.size(); i++) {
1032 Spatial *item = selection_results[i].item;
1033 if (item != scene && item->get_owner() != scene && !scene->is_editable_instance(item->get_owner())) {
1034 //invalid result
1035 selection_results.remove(i);
1036 i--;
1037 }
1038 }
1039
1040 clicked_wants_append = b->get_shift();
1041
1042 if (selection_results.size() == 1) {
1043
1044 clicked = selection_results[0].item->get_instance_id();
1045 selection_results.clear();
1046
1047 if (clicked) {
1048 _select_clicked(clicked_wants_append, true);
1049 clicked = 0;
1050 }
1051
1052 } else if (!selection_results.empty()) {
1053
1054 NodePath root_path = get_tree()->get_edited_scene_root()->get_path();
1055 StringName root_name = root_path.get_name(root_path.get_name_count() - 1);
1056
1057 for (int i = 0; i < selection_results.size(); i++) {
1058
1059 Spatial *spat = selection_results[i].item;
1060
1061 Ref<Texture> icon = EditorNode::get_singleton()->get_object_icon(spat, "Node");
1062
1063 String node_path = "/" + root_name + "/" + root_path.rel_path_to(spat->get_path());
1064
1065 selection_menu->add_item(spat->get_name());
1066 selection_menu->set_item_icon(i, icon);
1067 selection_menu->set_item_metadata(i, node_path);
1068 selection_menu->set_item_tooltip(i, String(spat->get_name()) + "\nType: " + spat->get_class() + "\nPath: " + node_path);
1069 }
1070
1071 selection_menu->set_global_position(b->get_global_position());
1072 selection_menu->popup();
1073 }
1074 }
1075
_sinput(const Ref<InputEvent> & p_event)1076 void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
1077
1078 if (previewing)
1079 return; //do NONE
1080
1081 {
1082 EditorNode *en = editor;
1083 EditorPluginList *force_input_forwarding_list = en->get_editor_plugins_force_input_forwarding();
1084 if (!force_input_forwarding_list->empty()) {
1085 bool discard = force_input_forwarding_list->forward_spatial_gui_input(camera, p_event, true);
1086 if (discard)
1087 return;
1088 }
1089 }
1090 {
1091 EditorNode *en = editor;
1092 EditorPluginList *over_plugin_list = en->get_editor_plugins_over();
1093 if (!over_plugin_list->empty()) {
1094 bool discard = over_plugin_list->forward_spatial_gui_input(camera, p_event, false);
1095 if (discard)
1096 return;
1097 }
1098 }
1099
1100 Ref<InputEventMouseButton> b = p_event;
1101
1102 if (b.is_valid()) {
1103 emit_signal("clicked", this);
1104
1105 float zoom_factor = 1 + (ZOOM_MULTIPLIER - 1) * b->get_factor();
1106 switch (b->get_button_index()) {
1107
1108 case BUTTON_WHEEL_UP: {
1109 if (is_freelook_active())
1110 scale_freelook_speed(zoom_factor);
1111 else
1112 scale_cursor_distance(1.0 / zoom_factor);
1113 } break;
1114
1115 case BUTTON_WHEEL_DOWN: {
1116 if (is_freelook_active())
1117 scale_freelook_speed(1.0 / zoom_factor);
1118 else
1119 scale_cursor_distance(zoom_factor);
1120 } break;
1121
1122 case BUTTON_RIGHT: {
1123
1124 NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
1125
1126 if (b->is_pressed() && _edit.gizmo.is_valid()) {
1127 //restore
1128 _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_initial_value, true);
1129 _edit.gizmo = Ref<EditorSpatialGizmo>();
1130 }
1131
1132 if (_edit.mode == TRANSFORM_NONE && b->is_pressed()) {
1133
1134 if (b->get_alt()) {
1135
1136 if (nav_scheme == NAVIGATION_MAYA)
1137 break;
1138
1139 _list_select(b);
1140 return;
1141 }
1142 }
1143
1144 if (_edit.mode != TRANSFORM_NONE && b->is_pressed()) {
1145 //cancel motion
1146 _edit.mode = TRANSFORM_NONE;
1147
1148 List<Node *> &selection = editor_selection->get_selected_node_list();
1149
1150 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
1151
1152 Spatial *sp = Object::cast_to<Spatial>(E->get());
1153 if (!sp)
1154 continue;
1155
1156 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
1157 if (!se)
1158 continue;
1159
1160 sp->set_global_transform(se->original);
1161 }
1162 surface->update();
1163 set_message(TTR("Transform Aborted."), 3);
1164 }
1165
1166 if (b->is_pressed()) {
1167 const int mod = _get_key_modifier(b);
1168 if (!orthogonal) {
1169 if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) {
1170 set_freelook_active(true);
1171 }
1172 }
1173 } else {
1174 set_freelook_active(false);
1175 }
1176
1177 if (freelook_active && !surface->has_focus()) {
1178 // Focus usually doesn't trigger on right-click, but in case of freelook it should,
1179 // otherwise using keyboard navigation would misbehave
1180 surface->grab_focus();
1181 }
1182
1183 } break;
1184 case BUTTON_MIDDLE: {
1185
1186 if (b->is_pressed() && _edit.mode != TRANSFORM_NONE) {
1187
1188 switch (_edit.plane) {
1189
1190 case TRANSFORM_VIEW: {
1191
1192 _edit.plane = TRANSFORM_X_AXIS;
1193 set_message(TTR("X-Axis Transform."), 2);
1194 name = "";
1195 _update_name();
1196 } break;
1197 case TRANSFORM_X_AXIS: {
1198
1199 _edit.plane = TRANSFORM_Y_AXIS;
1200 set_message(TTR("Y-Axis Transform."), 2);
1201
1202 } break;
1203 case TRANSFORM_Y_AXIS: {
1204
1205 _edit.plane = TRANSFORM_Z_AXIS;
1206 set_message(TTR("Z-Axis Transform."), 2);
1207
1208 } break;
1209 case TRANSFORM_Z_AXIS: {
1210
1211 _edit.plane = TRANSFORM_VIEW;
1212 set_message(TTR("View Plane Transform."), 2);
1213
1214 } break;
1215 case TRANSFORM_YZ:
1216 case TRANSFORM_XZ:
1217 case TRANSFORM_XY: {
1218 } break;
1219 }
1220 }
1221 } break;
1222 case BUTTON_LEFT: {
1223
1224 if (b->is_pressed()) {
1225
1226 NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
1227 if ((nav_scheme == NAVIGATION_MAYA || nav_scheme == NAVIGATION_MODO) && b->get_alt()) {
1228 break;
1229 }
1230
1231 if (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_LIST_SELECT) {
1232 _list_select(b);
1233 break;
1234 }
1235
1236 _edit.mouse_pos = b->get_position();
1237 _edit.snap = spatial_editor->is_snap_enabled();
1238 _edit.mode = TRANSFORM_NONE;
1239
1240 //gizmo has priority over everything
1241
1242 bool can_select_gizmos = true;
1243
1244 {
1245 int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS);
1246 can_select_gizmos = view_menu->get_popup()->is_item_checked(idx);
1247 }
1248
1249 if (can_select_gizmos && spatial_editor->get_selected()) {
1250
1251 Ref<EditorSpatialGizmo> seg = spatial_editor->get_selected()->get_gizmo();
1252 if (seg.is_valid()) {
1253 int handle = -1;
1254 Vector3 point;
1255 Vector3 normal;
1256 bool inters = seg->intersect_ray(camera, _edit.mouse_pos, point, normal, &handle, b->get_shift());
1257 if (inters && handle != -1) {
1258
1259 _edit.gizmo = seg;
1260 _edit.gizmo_handle = handle;
1261 _edit.gizmo_initial_value = seg->get_handle_value(handle);
1262 break;
1263 }
1264 }
1265 }
1266
1267 if (_gizmo_select(_edit.mouse_pos))
1268 break;
1269
1270 clicked = 0;
1271 clicked_includes_current = false;
1272
1273 if ((spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT && b->get_control()) || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_ROTATE) {
1274
1275 /* HANDLE ROTATION */
1276 if (get_selected_count() == 0)
1277 break; //bye
1278 //handle rotate
1279 _edit.mode = TRANSFORM_ROTATE;
1280 _compute_edit(b->get_position());
1281 break;
1282 }
1283
1284 if (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_MOVE) {
1285
1286 if (get_selected_count() == 0)
1287 break; //bye
1288 //handle translate
1289 _edit.mode = TRANSFORM_TRANSLATE;
1290 _compute_edit(b->get_position());
1291 break;
1292 }
1293
1294 if (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SCALE) {
1295
1296 if (get_selected_count() == 0)
1297 break; //bye
1298 //handle scale
1299 _edit.mode = TRANSFORM_SCALE;
1300 _compute_edit(b->get_position());
1301 break;
1302 }
1303
1304 // todo scale
1305
1306 int gizmo_handle = -1;
1307
1308 clicked = _select_ray(b->get_position(), b->get_shift(), clicked_includes_current, &gizmo_handle, b->get_shift());
1309
1310 //clicking is always deferred to either move or release
1311
1312 clicked_wants_append = b->get_shift();
1313
1314 if (!clicked) {
1315
1316 if (!clicked_wants_append)
1317 _clear_selected();
1318
1319 //default to regionselect
1320 cursor.region_select = true;
1321 cursor.region_begin = b->get_position();
1322 cursor.region_end = b->get_position();
1323 }
1324
1325 if (clicked && gizmo_handle >= 0) {
1326
1327 Spatial *spa = Object::cast_to<Spatial>(ObjectDB::get_instance(clicked));
1328 if (spa) {
1329
1330 Ref<EditorSpatialGizmo> seg = spa->get_gizmo();
1331 if (seg.is_valid()) {
1332
1333 _edit.gizmo = seg;
1334 _edit.gizmo_handle = gizmo_handle;
1335 _edit.gizmo_initial_value = seg->get_handle_value(gizmo_handle);
1336 break;
1337 }
1338 }
1339 }
1340
1341 surface->update();
1342 } else {
1343
1344 if (_edit.gizmo.is_valid()) {
1345
1346 _edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_initial_value, false);
1347 _edit.gizmo = Ref<EditorSpatialGizmo>();
1348 break;
1349 }
1350 if (clicked) {
1351 _select_clicked(clicked_wants_append, true);
1352 // Processing was deferred.
1353 clicked = 0;
1354 }
1355
1356 if (cursor.region_select) {
1357
1358 if (!clicked_wants_append) _clear_selected();
1359
1360 _select_region();
1361 cursor.region_select = false;
1362 surface->update();
1363 }
1364
1365 if (_edit.mode != TRANSFORM_NONE) {
1366
1367 static const char *_transform_name[4] = { "None", "Rotate", "Translate", "Scale" };
1368 undo_redo->create_action(_transform_name[_edit.mode]);
1369
1370 List<Node *> &selection = editor_selection->get_selected_node_list();
1371
1372 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
1373
1374 Spatial *sp = Object::cast_to<Spatial>(E->get());
1375 if (!sp)
1376 continue;
1377
1378 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
1379 if (!se)
1380 continue;
1381
1382 undo_redo->add_do_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
1383 undo_redo->add_undo_method(sp, "set_global_transform", se->original);
1384 }
1385 undo_redo->commit_action();
1386 _edit.mode = TRANSFORM_NONE;
1387 set_message("");
1388 }
1389
1390 surface->update();
1391 }
1392
1393 } break;
1394 }
1395 }
1396
1397 Ref<InputEventMouseMotion> m = p_event;
1398
1399 if (m.is_valid()) {
1400
1401 _edit.mouse_pos = m->get_position();
1402
1403 if (spatial_editor->get_selected()) {
1404
1405 Ref<EditorSpatialGizmo> seg = spatial_editor->get_selected()->get_gizmo();
1406 if (seg.is_valid()) {
1407
1408 int selected_handle = -1;
1409
1410 int handle = -1;
1411 Vector3 point;
1412 Vector3 normal;
1413 bool inters = seg->intersect_ray(camera, _edit.mouse_pos, point, normal, &handle, false);
1414 if (inters && handle != -1) {
1415
1416 selected_handle = handle;
1417 }
1418
1419 if (selected_handle != spatial_editor->get_over_gizmo_handle()) {
1420 spatial_editor->set_over_gizmo_handle(selected_handle);
1421 spatial_editor->get_selected()->update_gizmo();
1422 if (selected_handle != -1)
1423 spatial_editor->select_gizmo_highlight_axis(-1);
1424 }
1425 }
1426 }
1427
1428 if (spatial_editor->get_over_gizmo_handle() == -1 && !(m->get_button_mask() & 1) && !_edit.gizmo.is_valid()) {
1429
1430 _gizmo_select(_edit.mouse_pos, true);
1431 }
1432
1433 NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
1434 NavigationMode nav_mode = NAVIGATION_NONE;
1435
1436 if (_edit.gizmo.is_valid()) {
1437
1438 _edit.gizmo->set_handle(_edit.gizmo_handle, camera, m->get_position());
1439 Variant v = _edit.gizmo->get_handle_value(_edit.gizmo_handle);
1440 String n = _edit.gizmo->get_handle_name(_edit.gizmo_handle);
1441 set_message(n + ": " + String(v));
1442
1443 } else if (m->get_button_mask() & BUTTON_MASK_LEFT) {
1444
1445 if (nav_scheme == NAVIGATION_MAYA && m->get_alt()) {
1446 nav_mode = NAVIGATION_ORBIT;
1447 } else if (nav_scheme == NAVIGATION_MODO && m->get_alt() && m->get_shift()) {
1448 nav_mode = NAVIGATION_PAN;
1449 } else if (nav_scheme == NAVIGATION_MODO && m->get_alt() && m->get_control()) {
1450 nav_mode = NAVIGATION_ZOOM;
1451 } else if (nav_scheme == NAVIGATION_MODO && m->get_alt()) {
1452 nav_mode = NAVIGATION_ORBIT;
1453 } else {
1454 if (clicked) {
1455
1456 if (!clicked_includes_current) {
1457
1458 _select_clicked(clicked_wants_append, true);
1459 // Processing was deferred.
1460 }
1461
1462 _compute_edit(_edit.mouse_pos);
1463 clicked = 0;
1464
1465 _edit.mode = TRANSFORM_TRANSLATE;
1466 }
1467
1468 if (cursor.region_select) {
1469 cursor.region_end = m->get_position();
1470 surface->update();
1471 return;
1472 }
1473
1474 if (_edit.mode == TRANSFORM_NONE)
1475 return;
1476
1477 Vector3 ray_pos = _get_ray_pos(m->get_position());
1478 Vector3 ray = _get_ray(m->get_position());
1479 float snap = EDITOR_GET("interface/inspector/default_float_step");
1480 int snap_step_decimals = Math::range_step_decimals(snap);
1481
1482 switch (_edit.mode) {
1483
1484 case TRANSFORM_SCALE: {
1485
1486 Vector3 motion_mask;
1487 Plane plane;
1488 bool plane_mv = false;
1489
1490 switch (_edit.plane) {
1491 case TRANSFORM_VIEW:
1492 motion_mask = Vector3(0, 0, 0);
1493 plane = Plane(_edit.center, _get_camera_normal());
1494 break;
1495 case TRANSFORM_X_AXIS:
1496 motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0);
1497 plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
1498 break;
1499 case TRANSFORM_Y_AXIS:
1500 motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1);
1501 plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
1502 break;
1503 case TRANSFORM_Z_AXIS:
1504 motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2);
1505 plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
1506 break;
1507 case TRANSFORM_YZ:
1508 motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(1);
1509 plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
1510 plane_mv = true;
1511 break;
1512 case TRANSFORM_XZ:
1513 motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(0);
1514 plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1));
1515 plane_mv = true;
1516 break;
1517 case TRANSFORM_XY:
1518 motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0) + spatial_editor->get_gizmo_transform().basis.get_axis(1);
1519 plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2));
1520 plane_mv = true;
1521 break;
1522 }
1523
1524 Vector3 intersection;
1525 if (!plane.intersects_ray(ray_pos, ray, &intersection))
1526 break;
1527
1528 Vector3 click;
1529 if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click))
1530 break;
1531
1532 Vector3 motion = intersection - click;
1533 if (_edit.plane != TRANSFORM_VIEW) {
1534
1535 if (!plane_mv) {
1536
1537 motion = motion_mask.dot(motion) * motion_mask;
1538
1539 } else {
1540
1541 // Alternative planar scaling mode
1542 if (_get_key_modifier(m) != KEY_SHIFT) {
1543 motion = motion_mask.dot(motion) * motion_mask;
1544 }
1545 }
1546
1547 } else {
1548 float center_click_dist = click.distance_to(_edit.center);
1549 float center_inters_dist = intersection.distance_to(_edit.center);
1550 if (center_click_dist == 0)
1551 break;
1552
1553 float scale = center_inters_dist - center_click_dist;
1554 motion = Vector3(scale, scale, scale);
1555 }
1556
1557 List<Node *> &selection = editor_selection->get_selected_node_list();
1558
1559 // Disable local transformation for TRANSFORM_VIEW
1560 bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
1561
1562 if (_edit.snap || spatial_editor->is_snap_enabled()) {
1563 snap = spatial_editor->get_scale_snap() / 100;
1564 }
1565 Vector3 motion_snapped = motion;
1566 motion_snapped.snap(Vector3(snap, snap, snap));
1567 // This might not be necessary anymore after issue #288 is solved (in 4.0?).
1568 set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
1569 String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
1570
1571 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
1572
1573 Spatial *sp = Object::cast_to<Spatial>(E->get());
1574 if (!sp) {
1575 continue;
1576 }
1577
1578 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
1579 if (!se) {
1580 continue;
1581 }
1582
1583 if (sp->has_meta("_edit_lock_")) {
1584 continue;
1585 }
1586
1587 Transform original = se->original;
1588 Transform original_local = se->original_local;
1589 Transform base = Transform(Basis(), _edit.center);
1590 Transform t;
1591 Vector3 local_scale;
1592
1593 if (local_coords) {
1594
1595 Basis g = original.basis.orthonormalized();
1596 Vector3 local_motion = g.inverse().xform(motion);
1597
1598 if (_edit.snap || spatial_editor->is_snap_enabled()) {
1599 local_motion.snap(Vector3(snap, snap, snap));
1600 }
1601
1602 local_scale = original_local.basis.get_scale() * (local_motion + Vector3(1, 1, 1));
1603
1604 // Prevent scaling to 0 it would break the gizmo
1605 Basis check = original_local.basis;
1606 check.scale(local_scale);
1607 if (check.determinant() != 0) {
1608
1609 // Apply scale
1610 sp->set_scale(local_scale);
1611 }
1612
1613 } else {
1614
1615 if (_edit.snap || spatial_editor->is_snap_enabled()) {
1616 motion.snap(Vector3(snap, snap, snap));
1617 }
1618
1619 Transform r;
1620 r.basis.scale(motion + Vector3(1, 1, 1));
1621 t = base * (r * (base.inverse() * original));
1622
1623 // Apply scale
1624 sp->set_global_transform(t);
1625 }
1626 }
1627
1628 surface->update();
1629
1630 } break;
1631
1632 case TRANSFORM_TRANSLATE: {
1633
1634 Vector3 motion_mask;
1635 Plane plane;
1636 bool plane_mv = false;
1637
1638 switch (_edit.plane) {
1639 case TRANSFORM_VIEW:
1640 plane = Plane(_edit.center, _get_camera_normal());
1641 break;
1642 case TRANSFORM_X_AXIS:
1643 motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0);
1644 plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
1645 break;
1646 case TRANSFORM_Y_AXIS:
1647 motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1);
1648 plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
1649 break;
1650 case TRANSFORM_Z_AXIS:
1651 motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2);
1652 plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
1653 break;
1654 case TRANSFORM_YZ:
1655 plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
1656 plane_mv = true;
1657 break;
1658 case TRANSFORM_XZ:
1659 plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1));
1660 plane_mv = true;
1661 break;
1662 case TRANSFORM_XY:
1663 plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2));
1664 plane_mv = true;
1665 break;
1666 }
1667
1668 Vector3 intersection;
1669 if (!plane.intersects_ray(ray_pos, ray, &intersection))
1670 break;
1671
1672 Vector3 click;
1673 if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click))
1674 break;
1675
1676 Vector3 motion = intersection - click;
1677 if (_edit.plane != TRANSFORM_VIEW) {
1678 if (!plane_mv) {
1679 motion = motion_mask.dot(motion) * motion_mask;
1680 }
1681 }
1682
1683 List<Node *> &selection = editor_selection->get_selected_node_list();
1684
1685 // Disable local transformation for TRANSFORM_VIEW
1686 bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
1687
1688 if (_edit.snap || spatial_editor->is_snap_enabled()) {
1689 snap = spatial_editor->get_translate_snap();
1690 }
1691 Vector3 motion_snapped = motion;
1692 motion_snapped.snap(Vector3(snap, snap, snap));
1693 set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
1694 String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
1695
1696 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
1697
1698 Spatial *sp = Object::cast_to<Spatial>(E->get());
1699 if (!sp) {
1700 continue;
1701 }
1702
1703 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
1704 if (!se) {
1705 continue;
1706 }
1707
1708 if (sp->has_meta("_edit_lock_")) {
1709 continue;
1710 }
1711
1712 Transform original = se->original;
1713 Transform t;
1714
1715 if (local_coords) {
1716
1717 if (_edit.snap || spatial_editor->is_snap_enabled()) {
1718 Basis g = original.basis.orthonormalized();
1719 Vector3 local_motion = g.inverse().xform(motion);
1720 local_motion.snap(Vector3(snap, snap, snap));
1721
1722 motion = g.xform(local_motion);
1723 }
1724
1725 } else {
1726
1727 if (_edit.snap || spatial_editor->is_snap_enabled()) {
1728 motion.snap(Vector3(snap, snap, snap));
1729 }
1730 }
1731
1732 // Apply translation
1733 t = original;
1734 t.origin += motion;
1735 sp->set_global_transform(t);
1736 }
1737
1738 surface->update();
1739
1740 } break;
1741
1742 case TRANSFORM_ROTATE: {
1743
1744 Plane plane;
1745 Vector3 axis;
1746
1747 switch (_edit.plane) {
1748 case TRANSFORM_VIEW:
1749 plane = Plane(_edit.center, _get_camera_normal());
1750 break;
1751 case TRANSFORM_X_AXIS:
1752 plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
1753 axis = Vector3(1, 0, 0);
1754 break;
1755 case TRANSFORM_Y_AXIS:
1756 plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1));
1757 axis = Vector3(0, 1, 0);
1758 break;
1759 case TRANSFORM_Z_AXIS:
1760 plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2));
1761 axis = Vector3(0, 0, 1);
1762 break;
1763 case TRANSFORM_YZ:
1764 case TRANSFORM_XZ:
1765 case TRANSFORM_XY:
1766 break;
1767 }
1768
1769 Vector3 intersection;
1770 if (!plane.intersects_ray(ray_pos, ray, &intersection))
1771 break;
1772
1773 Vector3 click;
1774 if (!plane.intersects_ray(_edit.click_ray_pos, _edit.click_ray, &click))
1775 break;
1776
1777 Vector3 y_axis = (click - _edit.center).normalized();
1778 Vector3 x_axis = plane.normal.cross(y_axis).normalized();
1779
1780 float angle = Math::atan2(x_axis.dot(intersection - _edit.center), y_axis.dot(intersection - _edit.center));
1781
1782 if (_edit.snap || spatial_editor->is_snap_enabled()) {
1783 snap = spatial_editor->get_rotate_snap();
1784 }
1785 angle = Math::rad2deg(angle) + snap * 0.5; //else it won't reach +180
1786 angle -= Math::fmod(angle, snap);
1787 set_message(vformat(TTR("Rotating %s degrees."), String::num(angle, snap_step_decimals)));
1788 angle = Math::deg2rad(angle);
1789
1790 List<Node *> &selection = editor_selection->get_selected_node_list();
1791
1792 bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW
1793
1794 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
1795
1796 Spatial *sp = Object::cast_to<Spatial>(E->get());
1797 if (!sp)
1798 continue;
1799
1800 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
1801 if (!se)
1802 continue;
1803
1804 if (sp->has_meta("_edit_lock_")) {
1805 continue;
1806 }
1807
1808 Transform t;
1809
1810 if (local_coords) {
1811
1812 Transform original_local = se->original_local;
1813 Basis rot = Basis(axis, angle);
1814
1815 t.basis = original_local.get_basis().orthonormalized() * rot;
1816 t.origin = original_local.origin;
1817
1818 // Apply rotation
1819 sp->set_transform(t);
1820 sp->set_scale(original_local.basis.get_scale()); // re-apply original scale
1821
1822 } else {
1823
1824 Transform original = se->original;
1825 Transform r;
1826 Transform base = Transform(Basis(), _edit.center);
1827
1828 r.basis.rotate(plane.normal, angle);
1829 t = base * r * base.inverse() * original;
1830
1831 // Apply rotation
1832 sp->set_global_transform(t);
1833 }
1834 }
1835
1836 surface->update();
1837
1838 } break;
1839 default: {
1840 }
1841 }
1842 }
1843
1844 } else if ((m->get_button_mask() & BUTTON_MASK_RIGHT) || freelook_active) {
1845
1846 if (nav_scheme == NAVIGATION_MAYA && m->get_alt()) {
1847 nav_mode = NAVIGATION_ZOOM;
1848 } else if (freelook_active) {
1849 nav_mode = NAVIGATION_LOOK;
1850 } else if (orthogonal) {
1851 nav_mode = NAVIGATION_PAN;
1852 }
1853
1854 } else if (m->get_button_mask() & BUTTON_MASK_MIDDLE) {
1855
1856 if (nav_scheme == NAVIGATION_GODOT) {
1857
1858 const int mod = _get_key_modifier(m);
1859
1860 if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) {
1861 nav_mode = NAVIGATION_PAN;
1862 } else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) {
1863 nav_mode = NAVIGATION_ZOOM;
1864 } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
1865 // Always allow Alt as a modifier to better support graphic tablets.
1866 nav_mode = NAVIGATION_ORBIT;
1867 }
1868
1869 } else if (nav_scheme == NAVIGATION_MAYA) {
1870 if (m->get_alt())
1871 nav_mode = NAVIGATION_PAN;
1872 }
1873
1874 } else if (EditorSettings::get_singleton()->get("editors/3d/navigation/emulate_3_button_mouse")) {
1875 // Handle trackpad (no external mouse) use case
1876 const int mod = _get_key_modifier(m);
1877
1878 if (mod) {
1879 if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) {
1880 nav_mode = NAVIGATION_PAN;
1881 } else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) {
1882 nav_mode = NAVIGATION_ZOOM;
1883 } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
1884 // Always allow Alt as a modifier to better support graphic tablets.
1885 nav_mode = NAVIGATION_ORBIT;
1886 }
1887 }
1888 }
1889
1890 switch (nav_mode) {
1891 case NAVIGATION_PAN: {
1892 _nav_pan(m, _get_warped_mouse_motion(m));
1893
1894 } break;
1895
1896 case NAVIGATION_ZOOM: {
1897 _nav_zoom(m, m->get_relative());
1898
1899 } break;
1900
1901 case NAVIGATION_ORBIT: {
1902 _nav_orbit(m, _get_warped_mouse_motion(m));
1903
1904 } break;
1905
1906 case NAVIGATION_LOOK: {
1907 _nav_look(m, _get_warped_mouse_motion(m));
1908
1909 } break;
1910
1911 default: {
1912 }
1913 }
1914 }
1915
1916 Ref<InputEventMagnifyGesture> magnify_gesture = p_event;
1917 if (magnify_gesture.is_valid()) {
1918
1919 if (is_freelook_active())
1920 scale_freelook_speed(magnify_gesture->get_factor());
1921 else
1922 scale_cursor_distance(1.0 / magnify_gesture->get_factor());
1923 }
1924
1925 Ref<InputEventPanGesture> pan_gesture = p_event;
1926 if (pan_gesture.is_valid()) {
1927
1928 NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
1929 NavigationMode nav_mode = NAVIGATION_NONE;
1930
1931 if (nav_scheme == NAVIGATION_GODOT) {
1932
1933 const int mod = _get_key_modifier(pan_gesture);
1934
1935 if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) {
1936 nav_mode = NAVIGATION_PAN;
1937 } else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) {
1938 nav_mode = NAVIGATION_ZOOM;
1939 } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
1940 // Always allow Alt as a modifier to better support graphic tablets.
1941 nav_mode = NAVIGATION_ORBIT;
1942 }
1943
1944 } else if (nav_scheme == NAVIGATION_MAYA) {
1945 if (pan_gesture->get_alt())
1946 nav_mode = NAVIGATION_PAN;
1947 }
1948
1949 switch (nav_mode) {
1950 case NAVIGATION_PAN: {
1951 _nav_pan(pan_gesture, pan_gesture->get_delta());
1952
1953 } break;
1954
1955 case NAVIGATION_ZOOM: {
1956 _nav_zoom(pan_gesture, pan_gesture->get_delta());
1957
1958 } break;
1959
1960 case NAVIGATION_ORBIT: {
1961 _nav_orbit(pan_gesture, pan_gesture->get_delta());
1962
1963 } break;
1964
1965 case NAVIGATION_LOOK: {
1966 _nav_look(pan_gesture, pan_gesture->get_delta());
1967
1968 } break;
1969
1970 default: {
1971 }
1972 }
1973 }
1974
1975 Ref<InputEventKey> k = p_event;
1976
1977 if (k.is_valid()) {
1978 if (!k->is_pressed())
1979 return;
1980
1981 if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) {
1982 if (_edit.mode != TRANSFORM_NONE) {
1983 _edit.snap = !_edit.snap;
1984 }
1985 }
1986 if (ED_IS_SHORTCUT("spatial_editor/bottom_view", p_event)) {
1987 _menu_option(VIEW_BOTTOM);
1988 }
1989 if (ED_IS_SHORTCUT("spatial_editor/top_view", p_event)) {
1990 _menu_option(VIEW_TOP);
1991 }
1992 if (ED_IS_SHORTCUT("spatial_editor/rear_view", p_event)) {
1993 _menu_option(VIEW_REAR);
1994 }
1995 if (ED_IS_SHORTCUT("spatial_editor/front_view", p_event)) {
1996 _menu_option(VIEW_FRONT);
1997 }
1998 if (ED_IS_SHORTCUT("spatial_editor/left_view", p_event)) {
1999 _menu_option(VIEW_LEFT);
2000 }
2001 if (ED_IS_SHORTCUT("spatial_editor/right_view", p_event)) {
2002 _menu_option(VIEW_RIGHT);
2003 }
2004 if (ED_IS_SHORTCUT("spatial_editor/focus_origin", p_event)) {
2005 _menu_option(VIEW_CENTER_TO_ORIGIN);
2006 }
2007 if (ED_IS_SHORTCUT("spatial_editor/focus_selection", p_event)) {
2008 _menu_option(VIEW_CENTER_TO_SELECTION);
2009 }
2010 // Orthgonal mode doesn't work in freelook.
2011 if (!freelook_active && ED_IS_SHORTCUT("spatial_editor/switch_perspective_orthogonal", p_event)) {
2012 _menu_option(orthogonal ? VIEW_PERSPECTIVE : VIEW_ORTHOGONAL);
2013 }
2014 if (ED_IS_SHORTCUT("spatial_editor/align_transform_with_view", p_event)) {
2015 _menu_option(VIEW_ALIGN_TRANSFORM_WITH_VIEW);
2016 }
2017 if (ED_IS_SHORTCUT("spatial_editor/align_rotation_with_view", p_event)) {
2018 _menu_option(VIEW_ALIGN_ROTATION_WITH_VIEW);
2019 }
2020 if (ED_IS_SHORTCUT("spatial_editor/insert_anim_key", p_event)) {
2021 if (!get_selected_count() || _edit.mode != TRANSFORM_NONE)
2022 return;
2023
2024 if (!AnimationPlayerEditor::singleton->get_track_editor()->has_keying()) {
2025 set_message(TTR("Keying is disabled (no key inserted)."));
2026 return;
2027 }
2028
2029 List<Node *> &selection = editor_selection->get_selected_node_list();
2030
2031 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
2032
2033 Spatial *sp = Object::cast_to<Spatial>(E->get());
2034 if (!sp)
2035 continue;
2036
2037 spatial_editor->emit_signal("transform_key_request", sp, "", sp->get_transform());
2038 }
2039
2040 set_message(TTR("Animation Key Inserted."));
2041 }
2042
2043 // Freelook doesn't work in orthogonal mode.
2044 if (!orthogonal && ED_IS_SHORTCUT("spatial_editor/freelook_toggle", p_event)) {
2045 set_freelook_active(!is_freelook_active());
2046
2047 } else if (k->get_scancode() == KEY_ESCAPE) {
2048 set_freelook_active(false);
2049 }
2050
2051 if (k->get_scancode() == KEY_SPACE) {
2052 if (!k->is_pressed()) emit_signal("toggle_maximize_view", this);
2053 }
2054 }
2055
2056 // freelook uses most of the useful shortcuts, like save, so its ok
2057 // to consider freelook active as end of the line for future events.
2058 if (freelook_active)
2059 accept_event();
2060 }
2061
_nav_pan(Ref<InputEventWithModifiers> p_event,const Vector2 & p_relative)2062 void SpatialEditorViewport::_nav_pan(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
2063
2064 const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
2065
2066 real_t pan_speed = 1 / 150.0;
2067 int pan_speed_modifier = 10;
2068 if (nav_scheme == NAVIGATION_MAYA && p_event->get_shift())
2069 pan_speed *= pan_speed_modifier;
2070
2071 Transform camera_transform;
2072
2073 camera_transform.translate(cursor.pos);
2074 camera_transform.basis.rotate(Vector3(1, 0, 0), -cursor.x_rot);
2075 camera_transform.basis.rotate(Vector3(0, 1, 0), -cursor.y_rot);
2076 const bool invert_x_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_x_axis");
2077 const bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis");
2078 Vector3 translation(
2079 (invert_x_axis ? -1 : 1) * -p_relative.x * pan_speed,
2080 (invert_y_axis ? -1 : 1) * p_relative.y * pan_speed,
2081 0);
2082 translation *= cursor.distance / DISTANCE_DEFAULT;
2083 camera_transform.translate(translation);
2084 cursor.pos = camera_transform.origin;
2085 }
2086
_nav_zoom(Ref<InputEventWithModifiers> p_event,const Vector2 & p_relative)2087 void SpatialEditorViewport::_nav_zoom(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
2088
2089 const NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
2090
2091 real_t zoom_speed = 1 / 80.0;
2092 int zoom_speed_modifier = 10;
2093 if (nav_scheme == NAVIGATION_MAYA && p_event->get_shift())
2094 zoom_speed *= zoom_speed_modifier;
2095
2096 NavigationZoomStyle zoom_style = (NavigationZoomStyle)EditorSettings::get_singleton()->get("editors/3d/navigation/zoom_style").operator int();
2097 if (zoom_style == NAVIGATION_ZOOM_HORIZONTAL) {
2098 if (p_relative.x > 0)
2099 scale_cursor_distance(1 - p_relative.x * zoom_speed);
2100 else if (p_relative.x < 0)
2101 scale_cursor_distance(1.0 / (1 + p_relative.x * zoom_speed));
2102 } else {
2103 if (p_relative.y > 0)
2104 scale_cursor_distance(1 + p_relative.y * zoom_speed);
2105 else if (p_relative.y < 0)
2106 scale_cursor_distance(1.0 / (1 - p_relative.y * zoom_speed));
2107 }
2108 }
2109
_nav_orbit(Ref<InputEventWithModifiers> p_event,const Vector2 & p_relative)2110 void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
2111
2112 if (lock_rotation) {
2113 _nav_pan(p_event, p_relative);
2114 return;
2115 }
2116
2117 if (orthogonal && auto_orthogonal) {
2118 _menu_option(VIEW_PERSPECTIVE);
2119 }
2120
2121 const real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
2122 const real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
2123 const bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis");
2124 const bool invert_x_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_x_axis");
2125
2126 if (invert_y_axis) {
2127 cursor.x_rot -= p_relative.y * radians_per_pixel;
2128 } else {
2129 cursor.x_rot += p_relative.y * radians_per_pixel;
2130 }
2131 // Clamp the Y rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
2132 cursor.x_rot = CLAMP(cursor.x_rot, -1.57, 1.57);
2133
2134 if (invert_x_axis) {
2135 cursor.y_rot -= p_relative.x * radians_per_pixel;
2136 } else {
2137 cursor.y_rot += p_relative.x * radians_per_pixel;
2138 }
2139 name = "";
2140 _update_name();
2141 }
2142
_nav_look(Ref<InputEventWithModifiers> p_event,const Vector2 & p_relative)2143 void SpatialEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
2144
2145 if (orthogonal) {
2146 _nav_pan(p_event, p_relative);
2147 return;
2148 }
2149
2150 if (orthogonal && auto_orthogonal) {
2151 _menu_option(VIEW_PERSPECTIVE);
2152 }
2153
2154 const real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity");
2155 const real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel);
2156 const bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis");
2157
2158 // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag".
2159 const Transform prev_camera_transform = to_camera_transform(cursor);
2160
2161 if (invert_y_axis) {
2162 cursor.x_rot -= p_relative.y * radians_per_pixel;
2163 } else {
2164 cursor.x_rot += p_relative.y * radians_per_pixel;
2165 }
2166 // Clamp the Y rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
2167 cursor.x_rot = CLAMP(cursor.x_rot, -1.57, 1.57);
2168
2169 cursor.y_rot += p_relative.x * radians_per_pixel;
2170
2171 // Look is like the opposite of Orbit: the focus point rotates around the camera
2172 Transform camera_transform = to_camera_transform(cursor);
2173 Vector3 pos = camera_transform.xform(Vector3(0, 0, 0));
2174 Vector3 prev_pos = prev_camera_transform.xform(Vector3(0, 0, 0));
2175 Vector3 diff = prev_pos - pos;
2176 cursor.pos += diff;
2177
2178 name = "";
2179 _update_name();
2180 }
2181
set_freelook_active(bool active_now)2182 void SpatialEditorViewport::set_freelook_active(bool active_now) {
2183
2184 if (!freelook_active && active_now) {
2185 // Sync camera cursor to cursor to "cut" interpolation jumps due to changing referential
2186 cursor = camera_cursor;
2187
2188 // Make sure eye_pos is synced, because freelook referential is eye pos rather than orbit pos
2189 Vector3 forward = to_camera_transform(cursor).basis.xform(Vector3(0, 0, -1));
2190 cursor.eye_pos = cursor.pos - cursor.distance * forward;
2191 // Also sync the camera cursor, otherwise switching to freelook will be trippy if inertia is active
2192 camera_cursor.eye_pos = cursor.eye_pos;
2193
2194 if (EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_speed_zoom_link")) {
2195 // Re-adjust freelook speed from the current zoom level
2196 real_t base_speed = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_base_speed");
2197 freelook_speed = base_speed * cursor.distance;
2198 }
2199
2200 previous_mouse_position = get_local_mouse_position();
2201
2202 // Hide mouse like in an FPS (warping doesn't work)
2203 Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
2204
2205 } else if (freelook_active && !active_now) {
2206 // Sync camera cursor to cursor to "cut" interpolation jumps due to changing referential
2207 cursor = camera_cursor;
2208
2209 // Restore mouse
2210 Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
2211
2212 // Restore the previous mouse position when leaving freelook mode.
2213 // This is done because leaving `Input.MOUSE_MODE_CAPTURED` will center the cursor
2214 // due to OS limitations.
2215 warp_mouse(previous_mouse_position);
2216 }
2217
2218 freelook_active = active_now;
2219 }
2220
scale_cursor_distance(real_t scale)2221 void SpatialEditorViewport::scale_cursor_distance(real_t scale) {
2222
2223 // Prevents zero distance which would short-circuit any scaling
2224 if (cursor.distance < ZOOM_MIN_DISTANCE)
2225 cursor.distance = ZOOM_MIN_DISTANCE;
2226
2227 cursor.distance *= scale;
2228
2229 if (cursor.distance < ZOOM_MIN_DISTANCE)
2230 cursor.distance = ZOOM_MIN_DISTANCE;
2231
2232 zoom_indicator_delay = ZOOM_INDICATOR_DELAY_S;
2233 surface->update();
2234 }
2235
scale_freelook_speed(real_t scale)2236 void SpatialEditorViewport::scale_freelook_speed(real_t scale) {
2237
2238 // Prevents zero distance which would short-circuit any scaling
2239 if (freelook_speed < FREELOOK_MIN_SPEED)
2240 freelook_speed = FREELOOK_MIN_SPEED;
2241
2242 freelook_speed *= scale;
2243
2244 if (freelook_speed < FREELOOK_MIN_SPEED)
2245 freelook_speed = FREELOOK_MIN_SPEED;
2246
2247 zoom_indicator_delay = ZOOM_INDICATOR_DELAY_S;
2248 surface->update();
2249 }
2250
_get_warped_mouse_motion(const Ref<InputEventMouseMotion> & p_ev_mouse_motion) const2251 Point2i SpatialEditorViewport::_get_warped_mouse_motion(const Ref<InputEventMouseMotion> &p_ev_mouse_motion) const {
2252 Point2i relative;
2253 if (bool(EDITOR_DEF("editors/3d/navigation/warped_mouse_panning", false))) {
2254 relative = Input::get_singleton()->warp_mouse_motion(p_ev_mouse_motion, surface->get_global_rect());
2255 } else {
2256 relative = p_ev_mouse_motion->get_relative();
2257 }
2258 return relative;
2259 }
2260
is_shortcut_pressed(const String & p_path)2261 static bool is_shortcut_pressed(const String &p_path) {
2262 Ref<ShortCut> shortcut = ED_GET_SHORTCUT(p_path);
2263 if (shortcut.is_null()) {
2264 return false;
2265 }
2266 InputEventKey *k = Object::cast_to<InputEventKey>(shortcut->get_shortcut().ptr());
2267 if (k == NULL) {
2268 return false;
2269 }
2270 const Input &input = *Input::get_singleton();
2271 int scancode = k->get_scancode();
2272 return input.is_key_pressed(scancode);
2273 }
2274
_update_freelook(real_t delta)2275 void SpatialEditorViewport::_update_freelook(real_t delta) {
2276
2277 if (!is_freelook_active()) {
2278 return;
2279 }
2280
2281 const FreelookNavigationScheme navigation_scheme = (FreelookNavigationScheme)EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_navigation_scheme").operator int();
2282
2283 Vector3 forward;
2284 if (navigation_scheme == FREELOOK_FULLY_AXIS_LOCKED) {
2285 // Forward/backward keys will always go straight forward/backward, never moving on the Y axis.
2286 forward = Vector3(0, 0, -1).rotated(Vector3(0, 1, 0), camera->get_rotation().y);
2287 } else {
2288 // Forward/backward keys will be relative to the camera pitch.
2289 forward = camera->get_transform().basis.xform(Vector3(0, 0, -1));
2290 }
2291
2292 const Vector3 right = camera->get_transform().basis.xform(Vector3(1, 0, 0));
2293
2294 Vector3 up;
2295 if (navigation_scheme == FREELOOK_PARTIALLY_AXIS_LOCKED || navigation_scheme == FREELOOK_FULLY_AXIS_LOCKED) {
2296 // Up/down keys will always go up/down regardless of camera pitch.
2297 up = Vector3(0, 1, 0);
2298 } else {
2299 // Up/down keys will be relative to the camera pitch.
2300 up = camera->get_transform().basis.xform(Vector3(0, 1, 0));
2301 }
2302
2303 Vector3 direction;
2304
2305 if (is_shortcut_pressed("spatial_editor/freelook_left")) {
2306 direction -= right;
2307 }
2308 if (is_shortcut_pressed("spatial_editor/freelook_right")) {
2309 direction += right;
2310 }
2311 if (is_shortcut_pressed("spatial_editor/freelook_forward")) {
2312 direction += forward;
2313 }
2314 if (is_shortcut_pressed("spatial_editor/freelook_backwards")) {
2315 direction -= forward;
2316 }
2317 if (is_shortcut_pressed("spatial_editor/freelook_up")) {
2318 direction += up;
2319 }
2320 if (is_shortcut_pressed("spatial_editor/freelook_down")) {
2321 direction -= up;
2322 }
2323
2324 real_t speed = freelook_speed;
2325
2326 if (is_shortcut_pressed("spatial_editor/freelook_speed_modifier")) {
2327 speed *= 3.0;
2328 }
2329 if (is_shortcut_pressed("spatial_editor/freelook_slow_modifier")) {
2330 speed *= 0.333333;
2331 }
2332
2333 const Vector3 motion = direction * speed * delta;
2334 cursor.pos += motion;
2335 cursor.eye_pos += motion;
2336 }
2337
set_message(String p_message,float p_time)2338 void SpatialEditorViewport::set_message(String p_message, float p_time) {
2339
2340 message = p_message;
2341 message_time = p_time;
2342 }
2343
edited_scene_changed()2344 void SpatialEditorPlugin::edited_scene_changed() {
2345 for (uint32_t i = 0; i < SpatialEditor::VIEWPORTS_COUNT; i++) {
2346 SpatialEditorViewport *viewport = SpatialEditor::get_singleton()->get_editor_viewport(i);
2347 if (viewport->is_visible()) {
2348 viewport->notification(Control::NOTIFICATION_VISIBILITY_CHANGED);
2349 }
2350 }
2351 }
2352
_notification(int p_what)2353 void SpatialEditorViewport::_notification(int p_what) {
2354
2355 if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
2356
2357 bool visible = is_visible_in_tree();
2358
2359 set_process(visible);
2360
2361 if (visible) {
2362 orthogonal = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL));
2363 _update_name();
2364 _update_camera(0);
2365 } else {
2366 set_freelook_active(false);
2367 }
2368 call_deferred("update_transform_gizmo_view");
2369 rotation_control->set_visible(EditorSettings::get_singleton()->get("editors/3d/navigation/show_viewport_rotation_gizmo"));
2370 }
2371
2372 if (p_what == NOTIFICATION_RESIZED) {
2373
2374 call_deferred("update_transform_gizmo_view");
2375 }
2376
2377 if (p_what == NOTIFICATION_PROCESS) {
2378
2379 real_t delta = get_process_delta_time();
2380
2381 if (zoom_indicator_delay > 0) {
2382 zoom_indicator_delay -= delta;
2383 if (zoom_indicator_delay <= 0) {
2384 surface->update();
2385 }
2386 }
2387
2388 _update_freelook(delta);
2389
2390 Node *scene_root = editor->get_scene_tree_dock()->get_editor_data()->get_edited_scene_root();
2391 if (previewing_cinema && scene_root != NULL) {
2392 Camera *cam = scene_root->get_viewport()->get_camera();
2393 if (cam != NULL && cam != previewing) {
2394 //then switch the viewport's camera to the scene's viewport camera
2395 if (previewing != NULL) {
2396 previewing->disconnect("tree_exited", this, "_preview_exited_scene");
2397 }
2398 previewing = cam;
2399 previewing->connect("tree_exited", this, "_preview_exited_scene");
2400 VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), cam->get_camera());
2401 surface->update();
2402 }
2403 }
2404
2405 _update_camera(delta);
2406
2407 Map<Node *, Object *> &selection = editor_selection->get_selection();
2408
2409 bool changed = false;
2410 bool exist = false;
2411
2412 for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
2413
2414 Spatial *sp = Object::cast_to<Spatial>(E->key());
2415 if (!sp)
2416 continue;
2417
2418 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
2419 if (!se)
2420 continue;
2421
2422 Transform t = sp->get_global_gizmo_transform();
2423
2424 exist = true;
2425 if (se->last_xform == t && !se->last_xform_dirty)
2426 continue;
2427 changed = true;
2428 se->last_xform_dirty = false;
2429 se->last_xform = t;
2430
2431 VisualInstance *vi = Object::cast_to<VisualInstance>(sp);
2432
2433 se->aabb = vi ? vi->get_aabb() : _calculate_spatial_bounds(sp);
2434
2435 t.translate(se->aabb.position);
2436
2437 // apply AABB scaling before item's global transform
2438 Basis aabb_s;
2439 aabb_s.scale(se->aabb.size);
2440 t.basis = t.basis * aabb_s;
2441
2442 VisualServer::get_singleton()->instance_set_transform(se->sbox_instance, t);
2443 }
2444
2445 if (changed || (spatial_editor->is_gizmo_visible() && !exist)) {
2446 spatial_editor->update_transform_gizmo();
2447 }
2448
2449 if (message_time > 0) {
2450
2451 if (message != last_message) {
2452 surface->update();
2453 last_message = message;
2454 }
2455
2456 message_time -= get_physics_process_delta_time();
2457 if (message_time < 0)
2458 surface->update();
2459 }
2460
2461 //update shadow atlas if changed
2462
2463 int shadowmap_size = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/size");
2464 int atlas_q0 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_0_subdiv");
2465 int atlas_q1 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_1_subdiv");
2466 int atlas_q2 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_2_subdiv");
2467 int atlas_q3 = ProjectSettings::get_singleton()->get("rendering/quality/shadow_atlas/quadrant_3_subdiv");
2468
2469 viewport->set_shadow_atlas_size(shadowmap_size);
2470 viewport->set_shadow_atlas_quadrant_subdiv(0, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q0));
2471 viewport->set_shadow_atlas_quadrant_subdiv(1, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q1));
2472 viewport->set_shadow_atlas_quadrant_subdiv(2, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q2));
2473 viewport->set_shadow_atlas_quadrant_subdiv(3, Viewport::ShadowAtlasQuadrantSubdiv(atlas_q3));
2474
2475 bool shrink = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION));
2476
2477 if (shrink != (viewport_container->get_stretch_shrink() > 1)) {
2478 viewport_container->set_stretch_shrink(shrink ? 2 : 1);
2479 }
2480
2481 //update msaa if changed
2482
2483 int msaa_mode = ProjectSettings::get_singleton()->get("rendering/quality/filters/msaa");
2484 viewport->set_msaa(Viewport::MSAA(msaa_mode));
2485
2486 bool hdr = ProjectSettings::get_singleton()->get("rendering/quality/depth/hdr");
2487 viewport->set_hdr(hdr);
2488
2489 bool show_info = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION));
2490 info_label->set_visible(show_info);
2491
2492 Camera *current_camera;
2493
2494 if (previewing) {
2495 current_camera = previewing;
2496 } else {
2497 current_camera = camera;
2498 }
2499
2500 if (show_info) {
2501 String text;
2502 text += "X: " + rtos(current_camera->get_translation().x).pad_decimals(1) + "\n";
2503 text += "Y: " + rtos(current_camera->get_translation().y).pad_decimals(1) + "\n";
2504 text += "Z: " + rtos(current_camera->get_translation().z).pad_decimals(1) + "\n";
2505 text += TTR("Pitch") + ": " + itos(Math::round(current_camera->get_rotation_degrees().x)) + "\n";
2506 text += TTR("Yaw") + ": " + itos(Math::round(current_camera->get_rotation_degrees().y)) + "\n\n";
2507 text += TTR("Objects Drawn") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_OBJECTS_IN_FRAME)) + "\n";
2508 text += TTR("Material Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_MATERIAL_CHANGES_IN_FRAME)) + "\n";
2509 text += TTR("Shader Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_SHADER_CHANGES_IN_FRAME)) + "\n";
2510 text += TTR("Surface Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_SURFACE_CHANGES_IN_FRAME)) + "\n";
2511 text += TTR("Draw Calls") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_DRAW_CALLS_IN_FRAME)) + "\n";
2512 text += TTR("Vertices") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_VERTICES_IN_FRAME));
2513 info_label->set_text(text);
2514 }
2515
2516 // FPS Counter.
2517 bool show_fps = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FPS));
2518 fps_label->set_visible(show_fps);
2519
2520 if (show_fps) {
2521 String text;
2522 const float temp_fps = Engine::get_singleton()->get_frames_per_second();
2523 text += TTR(vformat("FPS: %d (%s ms)", temp_fps, String::num(1000.0f / temp_fps, 2)));
2524 fps_label->set_text(text);
2525 }
2526
2527 bool show_cinema = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW));
2528 cinema_label->set_visible(show_cinema);
2529 if (show_cinema) {
2530 float cinema_half_width = cinema_label->get_size().width / 2.0f;
2531 cinema_label->set_anchor_and_margin(MARGIN_LEFT, 0.5f, -cinema_half_width);
2532 }
2533
2534 if (lock_rotation) {
2535 float locked_half_width = locked_label->get_size().width / 2.0f;
2536 locked_label->set_anchor_and_margin(MARGIN_LEFT, 0.5f, -locked_half_width);
2537 }
2538 }
2539
2540 if (p_what == NOTIFICATION_ENTER_TREE) {
2541
2542 surface->connect("draw", this, "_draw");
2543 surface->connect("gui_input", this, "_sinput");
2544 surface->connect("mouse_entered", this, "_surface_mouse_enter");
2545 surface->connect("mouse_exited", this, "_surface_mouse_exit");
2546 surface->connect("focus_entered", this, "_surface_focus_enter");
2547 surface->connect("focus_exited", this, "_surface_focus_exit");
2548
2549 _init_gizmo_instance(index);
2550 }
2551
2552 if (p_what == NOTIFICATION_EXIT_TREE) {
2553
2554 _finish_gizmo_instances();
2555 }
2556
2557 if (p_what == NOTIFICATION_THEME_CHANGED) {
2558
2559 view_menu->set_icon(get_icon("GuiTabMenuHl", "EditorIcons"));
2560 preview_camera->set_icon(get_icon("Camera", "EditorIcons"));
2561
2562 view_menu->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2563 view_menu->add_style_override("hover", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2564 view_menu->add_style_override("pressed", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2565 view_menu->add_style_override("focus", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2566 view_menu->add_style_override("disabled", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2567
2568 preview_camera->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2569 preview_camera->add_style_override("hover", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2570 preview_camera->add_style_override("pressed", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2571 preview_camera->add_style_override("focus", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2572 preview_camera->add_style_override("disabled", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2573
2574 info_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2575 fps_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2576 cinema_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2577 locked_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles"));
2578 }
2579 }
2580
draw_indicator_bar(Control & surface,real_t fill,const Ref<Texture> icon,const Ref<Font> font,const String & text)2581 static void draw_indicator_bar(Control &surface, real_t fill, const Ref<Texture> icon, const Ref<Font> font, const String &text) {
2582 // Adjust bar size from control height
2583 const Vector2 surface_size = surface.get_size();
2584 const real_t h = surface_size.y / 2.0;
2585 const real_t y = (surface_size.y - h) / 2.0;
2586
2587 const Rect2 r(10 * EDSCALE, y, 6 * EDSCALE, h);
2588 const real_t sy = r.size.y * fill;
2589
2590 // Note: because this bar appears over the viewport, it has to stay readable for any background color
2591 // Draw both neutral dark and bright colors to account this
2592 surface.draw_rect(r, Color(1, 1, 1, 0.2));
2593 surface.draw_rect(Rect2(r.position.x, r.position.y + r.size.y - sy, r.size.x, sy), Color(1, 1, 1, 0.6));
2594 surface.draw_rect(r.grow(1), Color(0, 0, 0, 0.7), false, Math::round(EDSCALE));
2595
2596 const Vector2 icon_size = icon->get_size();
2597 const Vector2 icon_pos = Vector2(r.position.x - (icon_size.x - r.size.x) / 2, r.position.y + r.size.y + 2 * EDSCALE);
2598 surface.draw_texture(icon, icon_pos);
2599
2600 // Draw text below the bar (for speed/zoom information).
2601 surface.draw_string(font, Vector2(icon_pos.x, icon_pos.y + icon_size.y + 16 * EDSCALE), text);
2602 }
2603
_draw()2604 void SpatialEditorViewport::_draw() {
2605
2606 EditorPluginList *over_plugin_list = EditorNode::get_singleton()->get_editor_plugins_over();
2607 if (!over_plugin_list->empty()) {
2608 over_plugin_list->forward_spatial_draw_over_viewport(surface);
2609 }
2610
2611 EditorPluginList *force_over_plugin_list = editor->get_editor_plugins_force_over();
2612 if (!force_over_plugin_list->empty()) {
2613 force_over_plugin_list->forward_spatial_force_draw_over_viewport(surface);
2614 }
2615
2616 if (surface->has_focus()) {
2617 Size2 size = surface->get_size();
2618 Rect2 r = Rect2(Point2(), size);
2619 get_stylebox("Focus", "EditorStyles")->draw(surface->get_canvas_item(), r);
2620 }
2621
2622 if (cursor.region_select) {
2623 const Rect2 selection_rect = Rect2(cursor.region_begin, cursor.region_end - cursor.region_begin);
2624
2625 surface->draw_rect(
2626 selection_rect,
2627 get_color("box_selection_fill_color", "Editor"));
2628
2629 surface->draw_rect(
2630 selection_rect,
2631 get_color("box_selection_stroke_color", "Editor"),
2632 false,
2633 Math::round(EDSCALE));
2634 }
2635
2636 RID ci = surface->get_canvas_item();
2637
2638 if (message_time > 0) {
2639 Ref<Font> font = get_font("font", "Label");
2640 Point2 msgpos = Point2(5, get_size().y - 20);
2641 font->draw(ci, msgpos + Point2(1, 1), message, Color(0, 0, 0, 0.8));
2642 font->draw(ci, msgpos + Point2(-1, -1), message, Color(0, 0, 0, 0.8));
2643 font->draw(ci, msgpos, message, Color(1, 1, 1, 1));
2644 }
2645
2646 if (_edit.mode == TRANSFORM_ROTATE) {
2647
2648 Point2 center = _point_to_screen(_edit.center);
2649 VisualServer::get_singleton()->canvas_item_add_line(
2650 ci,
2651 _edit.mouse_pos,
2652 center,
2653 get_color("accent_color", "Editor") * Color(1, 1, 1, 0.6),
2654 Math::round(2 * EDSCALE),
2655 true);
2656 }
2657 if (previewing) {
2658
2659 Size2 ss = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
2660 float aspect = ss.aspect();
2661 Size2 s = get_size();
2662
2663 Rect2 draw_rect;
2664
2665 switch (previewing->get_keep_aspect_mode()) {
2666 case Camera::KEEP_WIDTH: {
2667
2668 draw_rect.size = Size2(s.width, s.width / aspect);
2669 draw_rect.position.x = 0;
2670 draw_rect.position.y = (s.height - draw_rect.size.y) * 0.5;
2671
2672 } break;
2673 case Camera::KEEP_HEIGHT: {
2674
2675 draw_rect.size = Size2(s.height * aspect, s.height);
2676 draw_rect.position.y = 0;
2677 draw_rect.position.x = (s.width - draw_rect.size.x) * 0.5;
2678
2679 } break;
2680 }
2681
2682 draw_rect = Rect2(Vector2(), s).clip(draw_rect);
2683
2684 surface->draw_rect(draw_rect, Color(0.6, 0.6, 0.1, 0.5), false, Math::round(2 * EDSCALE));
2685
2686 } else {
2687
2688 if (zoom_indicator_delay > 0.0) {
2689
2690 if (is_freelook_active()) {
2691 // Show speed
2692
2693 real_t min_speed = FREELOOK_MIN_SPEED;
2694 real_t max_speed = camera->get_zfar();
2695 real_t scale_length = (max_speed - min_speed);
2696
2697 if (!Math::is_zero_approx(scale_length)) {
2698 real_t logscale_t = 1.0 - Math::log(1 + freelook_speed - min_speed) / Math::log(1 + scale_length);
2699
2700 // There is no real maximum speed so that factor can become negative,
2701 // Let's make it look asymptotic instead (will decrease slower and slower).
2702 if (logscale_t < 0.25)
2703 logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0);
2704
2705 // Display the freelook speed to help the user get a better sense of scale.
2706 const int precision = freelook_speed < 1.0 ? 2 : 1;
2707 draw_indicator_bar(
2708 *surface,
2709 1.0 - logscale_t,
2710 get_icon("ViewportSpeed", "EditorIcons"),
2711 get_font("font", "Label"),
2712 vformat("%s u/s", String::num(freelook_speed).pad_decimals(precision)));
2713 }
2714
2715 } else {
2716 // Show zoom
2717
2718 real_t min_distance = ZOOM_MIN_DISTANCE; // TODO Why not pick znear to limit zoom?
2719 real_t max_distance = camera->get_zfar();
2720 real_t scale_length = (max_distance - min_distance);
2721
2722 if (!Math::is_zero_approx(scale_length)) {
2723 real_t logscale_t = 1.0 - Math::log(1 + cursor.distance - min_distance) / Math::log(1 + scale_length);
2724
2725 // There is no real maximum distance so that factor can become negative,
2726 // Let's make it look asymptotic instead (will decrease slower and slower).
2727 if (logscale_t < 0.25)
2728 logscale_t = 0.25 * Math::exp(4.0 * logscale_t - 1.0);
2729
2730 // Display the zoom center distance to help the user get a better sense of scale.
2731 const int precision = cursor.distance < 1.0 ? 2 : 1;
2732 draw_indicator_bar(
2733 *surface,
2734 logscale_t,
2735 get_icon("ViewportZoom", "EditorIcons"),
2736 get_font("font", "Label"),
2737 vformat("%s u", String::num(cursor.distance).pad_decimals(precision)));
2738 }
2739 }
2740 }
2741 }
2742 }
2743
_menu_option(int p_option)2744 void SpatialEditorViewport::_menu_option(int p_option) {
2745
2746 switch (p_option) {
2747
2748 case VIEW_TOP: {
2749
2750 cursor.y_rot = 0;
2751 cursor.x_rot = Math_PI / 2.0;
2752 set_message(TTR("Top View."), 2);
2753 name = TTR("Top");
2754 _set_auto_orthogonal();
2755 _update_name();
2756
2757 } break;
2758 case VIEW_BOTTOM: {
2759
2760 cursor.y_rot = 0;
2761 cursor.x_rot = -Math_PI / 2.0;
2762 set_message(TTR("Bottom View."), 2);
2763 name = TTR("Bottom");
2764 _set_auto_orthogonal();
2765 _update_name();
2766
2767 } break;
2768 case VIEW_LEFT: {
2769
2770 cursor.x_rot = 0;
2771 cursor.y_rot = Math_PI / 2.0;
2772 set_message(TTR("Left View."), 2);
2773 name = TTR("Left");
2774 _set_auto_orthogonal();
2775 _update_name();
2776
2777 } break;
2778 case VIEW_RIGHT: {
2779
2780 cursor.x_rot = 0;
2781 cursor.y_rot = -Math_PI / 2.0;
2782 set_message(TTR("Right View."), 2);
2783 name = TTR("Right");
2784 _set_auto_orthogonal();
2785 _update_name();
2786
2787 } break;
2788 case VIEW_FRONT: {
2789
2790 cursor.x_rot = 0;
2791 cursor.y_rot = 0;
2792 set_message(TTR("Front View."), 2);
2793 name = TTR("Front");
2794 _set_auto_orthogonal();
2795 _update_name();
2796
2797 } break;
2798 case VIEW_REAR: {
2799
2800 cursor.x_rot = 0;
2801 cursor.y_rot = Math_PI;
2802 set_message(TTR("Rear View."), 2);
2803 name = TTR("Rear");
2804 _set_auto_orthogonal();
2805 _update_name();
2806
2807 } break;
2808 case VIEW_CENTER_TO_ORIGIN: {
2809
2810 cursor.pos = Vector3(0, 0, 0);
2811
2812 } break;
2813 case VIEW_CENTER_TO_SELECTION: {
2814
2815 focus_selection();
2816
2817 } break;
2818 case VIEW_ALIGN_TRANSFORM_WITH_VIEW: {
2819
2820 if (!get_selected_count())
2821 break;
2822
2823 Transform camera_transform = camera->get_global_transform();
2824
2825 List<Node *> &selection = editor_selection->get_selected_node_list();
2826
2827 undo_redo->create_action(TTR("Align Transform with View"));
2828
2829 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
2830
2831 Spatial *sp = Object::cast_to<Spatial>(E->get());
2832 if (!sp)
2833 continue;
2834
2835 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
2836 if (!se)
2837 continue;
2838
2839 Transform xform;
2840 if (orthogonal) {
2841 xform = sp->get_global_transform();
2842 xform.basis.set_euler(camera_transform.basis.get_euler());
2843 } else {
2844 xform = camera_transform;
2845 xform.scale_basis(sp->get_scale());
2846 }
2847
2848 undo_redo->add_do_method(sp, "set_global_transform", xform);
2849 undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
2850 }
2851 undo_redo->commit_action();
2852
2853 } break;
2854 case VIEW_ALIGN_ROTATION_WITH_VIEW: {
2855
2856 if (!get_selected_count())
2857 break;
2858
2859 Transform camera_transform = camera->get_global_transform();
2860
2861 List<Node *> &selection = editor_selection->get_selected_node_list();
2862
2863 undo_redo->create_action(TTR("Align Rotation with View"));
2864 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
2865
2866 Spatial *sp = Object::cast_to<Spatial>(E->get());
2867 if (!sp)
2868 continue;
2869
2870 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
2871 if (!se)
2872 continue;
2873
2874 undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_rotation());
2875 undo_redo->add_undo_method(sp, "set_rotation", sp->get_rotation());
2876 }
2877 undo_redo->commit_action();
2878
2879 } break;
2880 case VIEW_ENVIRONMENT: {
2881
2882 int idx = view_menu->get_popup()->get_item_index(VIEW_ENVIRONMENT);
2883 bool current = view_menu->get_popup()->is_item_checked(idx);
2884 current = !current;
2885 if (current) {
2886
2887 camera->set_environment(RES());
2888 } else {
2889
2890 camera->set_environment(SpatialEditor::get_singleton()->get_viewport_environment());
2891 }
2892
2893 view_menu->get_popup()->set_item_checked(idx, current);
2894
2895 } break;
2896 case VIEW_PERSPECTIVE: {
2897
2898 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true);
2899 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), false);
2900 orthogonal = false;
2901 auto_orthogonal = false;
2902 call_deferred("update_transform_gizmo_view");
2903 _update_name();
2904
2905 } break;
2906 case VIEW_ORTHOGONAL: {
2907
2908 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), false);
2909 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ORTHOGONAL), true);
2910 orthogonal = true;
2911 auto_orthogonal = false;
2912 call_deferred("update_transform_gizmo_view");
2913 _update_name();
2914
2915 } break;
2916 case VIEW_AUTO_ORTHOGONAL: {
2917
2918 int idx = view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL);
2919 bool current = view_menu->get_popup()->is_item_checked(idx);
2920 current = !current;
2921 view_menu->get_popup()->set_item_checked(idx, current);
2922 if (auto_orthogonal) {
2923 auto_orthogonal = false;
2924 _update_name();
2925 }
2926 } break;
2927 case VIEW_LOCK_ROTATION: {
2928
2929 int idx = view_menu->get_popup()->get_item_index(VIEW_LOCK_ROTATION);
2930 bool current = view_menu->get_popup()->is_item_checked(idx);
2931 lock_rotation = !current;
2932 view_menu->get_popup()->set_item_checked(idx, !current);
2933 if (lock_rotation) {
2934 locked_label->show();
2935 } else {
2936 locked_label->hide();
2937 }
2938
2939 } break;
2940 case VIEW_AUDIO_LISTENER: {
2941
2942 int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER);
2943 bool current = view_menu->get_popup()->is_item_checked(idx);
2944 current = !current;
2945 viewport->set_as_audio_listener(current);
2946 view_menu->get_popup()->set_item_checked(idx, current);
2947
2948 } break;
2949 case VIEW_AUDIO_DOPPLER: {
2950
2951 int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER);
2952 bool current = view_menu->get_popup()->is_item_checked(idx);
2953 current = !current;
2954 camera->set_doppler_tracking(current ? Camera::DOPPLER_TRACKING_IDLE_STEP : Camera::DOPPLER_TRACKING_DISABLED);
2955 view_menu->get_popup()->set_item_checked(idx, current);
2956
2957 } break;
2958 case VIEW_CINEMATIC_PREVIEW: {
2959
2960 int idx = view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW);
2961 bool current = view_menu->get_popup()->is_item_checked(idx);
2962 current = !current;
2963 view_menu->get_popup()->set_item_checked(idx, current);
2964 previewing_cinema = true;
2965 _toggle_cinema_preview(current);
2966
2967 if (current) {
2968 preview_camera->hide();
2969 } else {
2970 if (previewing != NULL)
2971 preview_camera->show();
2972 }
2973 } break;
2974 case VIEW_GIZMOS: {
2975
2976 int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS);
2977 bool current = view_menu->get_popup()->is_item_checked(idx);
2978 current = !current;
2979 if (current)
2980 camera->set_cull_mask(((1 << 20) - 1) | (1 << (GIZMO_BASE_LAYER + index)) | (1 << GIZMO_EDIT_LAYER) | (1 << GIZMO_GRID_LAYER));
2981 else
2982 camera->set_cull_mask(((1 << 20) - 1) | (1 << (GIZMO_BASE_LAYER + index)) | (1 << GIZMO_GRID_LAYER));
2983 view_menu->get_popup()->set_item_checked(idx, current);
2984
2985 } break;
2986 case VIEW_HALF_RESOLUTION: {
2987
2988 int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION);
2989 bool current = view_menu->get_popup()->is_item_checked(idx);
2990 current = !current;
2991 view_menu->get_popup()->set_item_checked(idx, current);
2992 } break;
2993 case VIEW_INFORMATION: {
2994
2995 int idx = view_menu->get_popup()->get_item_index(VIEW_INFORMATION);
2996 bool current = view_menu->get_popup()->is_item_checked(idx);
2997 view_menu->get_popup()->set_item_checked(idx, !current);
2998
2999 } break;
3000 case VIEW_FPS: {
3001
3002 int idx = view_menu->get_popup()->get_item_index(VIEW_FPS);
3003 bool current = view_menu->get_popup()->is_item_checked(idx);
3004 view_menu->get_popup()->set_item_checked(idx, !current);
3005
3006 } break;
3007 case VIEW_DISPLAY_NORMAL: {
3008
3009 viewport->set_debug_draw(Viewport::DEBUG_DRAW_DISABLED);
3010
3011 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true);
3012 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME), false);
3013 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW), false);
3014 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS), false);
3015 } break;
3016 case VIEW_DISPLAY_WIREFRAME: {
3017
3018 viewport->set_debug_draw(Viewport::DEBUG_DRAW_WIREFRAME);
3019 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), false);
3020 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME), true);
3021 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW), false);
3022 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS), false);
3023
3024 } break;
3025 case VIEW_DISPLAY_OVERDRAW: {
3026
3027 viewport->set_debug_draw(Viewport::DEBUG_DRAW_OVERDRAW);
3028 VisualServer::get_singleton()->scenario_set_debug(get_tree()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_OVERDRAW);
3029 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), false);
3030 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME), false);
3031 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW), true);
3032 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS), false);
3033
3034 } break;
3035 case VIEW_DISPLAY_SHADELESS: {
3036
3037 viewport->set_debug_draw(Viewport::DEBUG_DRAW_UNSHADED);
3038 VisualServer::get_singleton()->scenario_set_debug(get_tree()->get_root()->get_world()->get_scenario(), VisualServer::SCENARIO_DEBUG_SHADELESS);
3039 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), false);
3040 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME), false);
3041 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW), false);
3042 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS), true);
3043
3044 } break;
3045 }
3046 }
3047
_set_auto_orthogonal()3048 void SpatialEditorViewport::_set_auto_orthogonal() {
3049 if (!orthogonal && view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL))) {
3050 _menu_option(VIEW_ORTHOGONAL);
3051 auto_orthogonal = true;
3052 }
3053 }
3054
_preview_exited_scene()3055 void SpatialEditorViewport::_preview_exited_scene() {
3056
3057 preview_camera->disconnect("toggled", this, "_toggle_camera_preview");
3058 preview_camera->set_pressed(false);
3059 _toggle_camera_preview(false);
3060 preview_camera->connect("toggled", this, "_toggle_camera_preview");
3061 view_menu->show();
3062 }
3063
_init_gizmo_instance(int p_idx)3064 void SpatialEditorViewport::_init_gizmo_instance(int p_idx) {
3065
3066 uint32_t layer = 1 << (GIZMO_BASE_LAYER + p_idx);
3067
3068 for (int i = 0; i < 3; i++) {
3069 move_gizmo_instance[i] = VS::get_singleton()->instance_create();
3070 VS::get_singleton()->instance_set_base(move_gizmo_instance[i], spatial_editor->get_move_gizmo(i)->get_rid());
3071 VS::get_singleton()->instance_set_scenario(move_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario());
3072 VS::get_singleton()->instance_set_visible(move_gizmo_instance[i], false);
3073 VS::get_singleton()->instance_geometry_set_cast_shadows_setting(move_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
3074 VS::get_singleton()->instance_set_layer_mask(move_gizmo_instance[i], layer);
3075
3076 move_plane_gizmo_instance[i] = VS::get_singleton()->instance_create();
3077 VS::get_singleton()->instance_set_base(move_plane_gizmo_instance[i], spatial_editor->get_move_plane_gizmo(i)->get_rid());
3078 VS::get_singleton()->instance_set_scenario(move_plane_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario());
3079 VS::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], false);
3080 VS::get_singleton()->instance_geometry_set_cast_shadows_setting(move_plane_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
3081 VS::get_singleton()->instance_set_layer_mask(move_plane_gizmo_instance[i], layer);
3082
3083 rotate_gizmo_instance[i] = VS::get_singleton()->instance_create();
3084 VS::get_singleton()->instance_set_base(rotate_gizmo_instance[i], spatial_editor->get_rotate_gizmo(i)->get_rid());
3085 VS::get_singleton()->instance_set_scenario(rotate_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario());
3086 VS::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], false);
3087 VS::get_singleton()->instance_geometry_set_cast_shadows_setting(rotate_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
3088 VS::get_singleton()->instance_set_layer_mask(rotate_gizmo_instance[i], layer);
3089
3090 scale_gizmo_instance[i] = VS::get_singleton()->instance_create();
3091 VS::get_singleton()->instance_set_base(scale_gizmo_instance[i], spatial_editor->get_scale_gizmo(i)->get_rid());
3092 VS::get_singleton()->instance_set_scenario(scale_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario());
3093 VS::get_singleton()->instance_set_visible(scale_gizmo_instance[i], false);
3094 VS::get_singleton()->instance_geometry_set_cast_shadows_setting(scale_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
3095 VS::get_singleton()->instance_set_layer_mask(scale_gizmo_instance[i], layer);
3096
3097 scale_plane_gizmo_instance[i] = VS::get_singleton()->instance_create();
3098 VS::get_singleton()->instance_set_base(scale_plane_gizmo_instance[i], spatial_editor->get_scale_plane_gizmo(i)->get_rid());
3099 VS::get_singleton()->instance_set_scenario(scale_plane_gizmo_instance[i], get_tree()->get_root()->get_world()->get_scenario());
3100 VS::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false);
3101 VS::get_singleton()->instance_geometry_set_cast_shadows_setting(scale_plane_gizmo_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
3102 VS::get_singleton()->instance_set_layer_mask(scale_plane_gizmo_instance[i], layer);
3103 }
3104 }
3105
_finish_gizmo_instances()3106 void SpatialEditorViewport::_finish_gizmo_instances() {
3107
3108 for (int i = 0; i < 3; i++) {
3109 VS::get_singleton()->free(move_gizmo_instance[i]);
3110 VS::get_singleton()->free(move_plane_gizmo_instance[i]);
3111 VS::get_singleton()->free(rotate_gizmo_instance[i]);
3112 VS::get_singleton()->free(scale_gizmo_instance[i]);
3113 VS::get_singleton()->free(scale_plane_gizmo_instance[i]);
3114 }
3115 }
_toggle_camera_preview(bool p_activate)3116 void SpatialEditorViewport::_toggle_camera_preview(bool p_activate) {
3117
3118 ERR_FAIL_COND(p_activate && !preview);
3119 ERR_FAIL_COND(!p_activate && !previewing);
3120
3121 rotation_control->set_visible(!p_activate);
3122
3123 if (!p_activate) {
3124
3125 previewing->disconnect("tree_exiting", this, "_preview_exited_scene");
3126 previewing = NULL;
3127 VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), camera->get_camera()); //restore
3128 if (!preview)
3129 preview_camera->hide();
3130 view_menu->set_disabled(false);
3131 surface->update();
3132
3133 } else {
3134
3135 previewing = preview;
3136 previewing->connect("tree_exiting", this, "_preview_exited_scene");
3137 VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), preview->get_camera()); //replace
3138 view_menu->set_disabled(true);
3139 surface->update();
3140 }
3141 }
3142
_toggle_cinema_preview(bool p_activate)3143 void SpatialEditorViewport::_toggle_cinema_preview(bool p_activate) {
3144 previewing_cinema = p_activate;
3145 if (!previewing_cinema) {
3146 if (previewing != NULL)
3147 previewing->disconnect("tree_exited", this, "_preview_exited_scene");
3148
3149 previewing = NULL;
3150 VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), camera->get_camera()); //restore
3151 preview_camera->set_pressed(false);
3152 if (!preview) {
3153 preview_camera->hide();
3154 } else {
3155 preview_camera->show();
3156 }
3157 view_menu->show();
3158 surface->update();
3159 }
3160 }
3161
_selection_result_pressed(int p_result)3162 void SpatialEditorViewport::_selection_result_pressed(int p_result) {
3163
3164 if (selection_results.size() <= p_result)
3165 return;
3166
3167 clicked = selection_results[p_result].item->get_instance_id();
3168
3169 if (clicked) {
3170 _select_clicked(clicked_wants_append, true);
3171 clicked = 0;
3172 }
3173 }
3174
_selection_menu_hide()3175 void SpatialEditorViewport::_selection_menu_hide() {
3176
3177 selection_results.clear();
3178 selection_menu->clear();
3179 selection_menu->set_size(Vector2(0, 0));
3180 }
3181
set_can_preview(Camera * p_preview)3182 void SpatialEditorViewport::set_can_preview(Camera *p_preview) {
3183
3184 preview = p_preview;
3185
3186 if (!preview_camera->is_pressed() && !previewing_cinema)
3187 preview_camera->set_visible(p_preview);
3188 }
3189
update_transform_gizmo_view()3190 void SpatialEditorViewport::update_transform_gizmo_view() {
3191
3192 if (!is_visible_in_tree())
3193 return;
3194
3195 Transform xform = spatial_editor->get_gizmo_transform();
3196
3197 Transform camera_xform = camera->get_transform();
3198
3199 if (xform.origin.distance_squared_to(camera_xform.origin) < 0.01) {
3200 for (int i = 0; i < 3; i++) {
3201 VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], false);
3202 VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], false);
3203 VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], false);
3204 VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], false);
3205 VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false);
3206 }
3207 return;
3208 }
3209
3210 Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized();
3211 Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized();
3212 Plane p(camera_xform.origin, camz);
3213 float gizmo_d = MAX(Math::abs(p.distance_to(xform.origin)), CMP_EPSILON);
3214 float d0 = camera->unproject_position(camera_xform.origin + camz * gizmo_d).y;
3215 float d1 = camera->unproject_position(camera_xform.origin + camz * gizmo_d + camy).y;
3216 float dd = Math::abs(d0 - d1);
3217 if (dd == 0)
3218 dd = 0.0001;
3219
3220 float gizmo_size = EditorSettings::get_singleton()->get("editors/3d/manipulator_gizmo_size");
3221 // At low viewport heights, multiply the gizmo scale based on the viewport height.
3222 // This prevents the gizmo from growing very large and going outside the viewport.
3223 const int viewport_base_height = 400 * MAX(1, EDSCALE);
3224 gizmo_scale =
3225 (gizmo_size / Math::abs(dd)) * MAX(1, EDSCALE) *
3226 MIN(viewport_base_height, viewport_container->get_size().height) / viewport_base_height /
3227 viewport_container->get_stretch_shrink();
3228 Vector3 scale = Vector3(1, 1, 1) * gizmo_scale;
3229
3230 xform.basis.scale(scale);
3231
3232 for (int i = 0; i < 3; i++) {
3233 VisualServer::get_singleton()->instance_set_transform(move_gizmo_instance[i], xform);
3234 VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_MOVE));
3235 VisualServer::get_singleton()->instance_set_transform(move_plane_gizmo_instance[i], xform);
3236 VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_MOVE));
3237 VisualServer::get_singleton()->instance_set_transform(rotate_gizmo_instance[i], xform);
3238 VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SELECT || spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_ROTATE));
3239 VisualServer::get_singleton()->instance_set_transform(scale_gizmo_instance[i], xform);
3240 VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SCALE));
3241 VisualServer::get_singleton()->instance_set_transform(scale_plane_gizmo_instance[i], xform);
3242 VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], spatial_editor->is_gizmo_visible() && (spatial_editor->get_tool_mode() == SpatialEditor::TOOL_MODE_SCALE));
3243 }
3244 }
3245
set_state(const Dictionary & p_state)3246 void SpatialEditorViewport::set_state(const Dictionary &p_state) {
3247
3248 if (p_state.has("position"))
3249 cursor.pos = p_state["position"];
3250 if (p_state.has("x_rotation"))
3251 cursor.x_rot = p_state["x_rotation"];
3252 if (p_state.has("y_rotation"))
3253 cursor.y_rot = p_state["y_rotation"];
3254 if (p_state.has("distance"))
3255 cursor.distance = p_state["distance"];
3256
3257 if (p_state.has("use_orthogonal")) {
3258 bool orth = p_state["use_orthogonal"];
3259
3260 if (orth)
3261 _menu_option(VIEW_ORTHOGONAL);
3262 else
3263 _menu_option(VIEW_PERSPECTIVE);
3264 }
3265 if (p_state.has("view_name")) {
3266 name = p_state["view_name"];
3267 _update_name();
3268 }
3269 if (p_state.has("auto_orthogonal")) {
3270 auto_orthogonal = p_state["auto_orthogonal"];
3271 _update_name();
3272 }
3273 if (p_state.has("auto_orthogonal_enabled")) {
3274 bool enabled = p_state["auto_orthogonal_enabled"];
3275 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL), enabled);
3276 }
3277 if (p_state.has("display_mode")) {
3278 int display = p_state["display_mode"];
3279
3280 int idx = view_menu->get_popup()->get_item_index(display);
3281 if (!view_menu->get_popup()->is_item_checked(idx))
3282 _menu_option(display);
3283 }
3284 if (p_state.has("lock_rotation")) {
3285 lock_rotation = p_state["lock_rotation"];
3286
3287 int idx = view_menu->get_popup()->get_item_index(VIEW_LOCK_ROTATION);
3288 view_menu->get_popup()->set_item_checked(idx, lock_rotation);
3289 }
3290 if (p_state.has("use_environment")) {
3291 bool env = p_state["use_environment"];
3292
3293 if (env != camera->get_environment().is_valid())
3294 _menu_option(VIEW_ENVIRONMENT);
3295 }
3296 if (p_state.has("listener")) {
3297 bool listener = p_state["listener"];
3298
3299 int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER);
3300 viewport->set_as_audio_listener(listener);
3301 view_menu->get_popup()->set_item_checked(idx, listener);
3302 }
3303 if (p_state.has("doppler")) {
3304 bool doppler = p_state["doppler"];
3305
3306 int idx = view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER);
3307 camera->set_doppler_tracking(doppler ? Camera::DOPPLER_TRACKING_IDLE_STEP : Camera::DOPPLER_TRACKING_DISABLED);
3308 view_menu->get_popup()->set_item_checked(idx, doppler);
3309 }
3310 if (p_state.has("gizmos")) {
3311 bool gizmos = p_state["gizmos"];
3312
3313 int idx = view_menu->get_popup()->get_item_index(VIEW_GIZMOS);
3314 if (view_menu->get_popup()->is_item_checked(idx) != gizmos)
3315 _menu_option(VIEW_GIZMOS);
3316 }
3317 if (p_state.has("information")) {
3318 bool information = p_state["information"];
3319
3320 int idx = view_menu->get_popup()->get_item_index(VIEW_INFORMATION);
3321 if (view_menu->get_popup()->is_item_checked(idx) != information)
3322 _menu_option(VIEW_INFORMATION);
3323 }
3324 if (p_state.has("fps")) {
3325 bool fps = p_state["fps"];
3326
3327 int idx = view_menu->get_popup()->get_item_index(VIEW_FPS);
3328 if (view_menu->get_popup()->is_item_checked(idx) != fps)
3329 _menu_option(VIEW_FPS);
3330 }
3331 if (p_state.has("half_res")) {
3332 bool half_res = p_state["half_res"];
3333
3334 int idx = view_menu->get_popup()->get_item_index(VIEW_HALF_RESOLUTION);
3335 view_menu->get_popup()->set_item_checked(idx, half_res);
3336 }
3337 if (p_state.has("cinematic_preview")) {
3338 previewing_cinema = p_state["cinematic_preview"];
3339
3340 int idx = view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW);
3341 view_menu->get_popup()->set_item_checked(idx, previewing_cinema);
3342 }
3343
3344 if (preview_camera->is_connected("toggled", this, "_toggle_camera_preview")) {
3345 preview_camera->disconnect("toggled", this, "_toggle_camera_preview");
3346 }
3347 if (p_state.has("previewing")) {
3348 Node *pv = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["previewing"]);
3349 if (Object::cast_to<Camera>(pv)) {
3350 previewing = Object::cast_to<Camera>(pv);
3351 previewing->connect("tree_exiting", this, "_preview_exited_scene");
3352 VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), previewing->get_camera()); //replace
3353 view_menu->set_disabled(true);
3354 surface->update();
3355 preview_camera->set_pressed(true);
3356 preview_camera->show();
3357 }
3358 }
3359 preview_camera->connect("toggled", this, "_toggle_camera_preview");
3360 }
3361
get_state() const3362 Dictionary SpatialEditorViewport::get_state() const {
3363
3364 Dictionary d;
3365 d["position"] = cursor.pos;
3366 d["x_rotation"] = cursor.x_rot;
3367 d["y_rotation"] = cursor.y_rot;
3368 d["distance"] = cursor.distance;
3369 d["use_environment"] = camera->get_environment().is_valid();
3370 d["use_orthogonal"] = camera->get_projection() == Camera::PROJECTION_ORTHOGONAL;
3371 d["view_name"] = name;
3372 d["auto_orthogonal"] = auto_orthogonal;
3373 d["auto_orthogonal_enabled"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL));
3374 if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL)))
3375 d["display_mode"] = VIEW_DISPLAY_NORMAL;
3376 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME)))
3377 d["display_mode"] = VIEW_DISPLAY_WIREFRAME;
3378 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW)))
3379 d["display_mode"] = VIEW_DISPLAY_OVERDRAW;
3380 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS)))
3381 d["display_mode"] = VIEW_DISPLAY_SHADELESS;
3382 d["listener"] = viewport->is_audio_listener();
3383 d["doppler"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER));
3384 d["gizmos"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS));
3385 d["information"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION));
3386 d["fps"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FPS));
3387 d["half_res"] = viewport_container->get_stretch_shrink() > 1;
3388 d["cinematic_preview"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW));
3389 if (previewing)
3390 d["previewing"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(previewing);
3391 if (lock_rotation)
3392 d["lock_rotation"] = lock_rotation;
3393
3394 return d;
3395 }
3396
_bind_methods()3397 void SpatialEditorViewport::_bind_methods() {
3398
3399 ClassDB::bind_method(D_METHOD("_draw"), &SpatialEditorViewport::_draw);
3400
3401 ClassDB::bind_method(D_METHOD("_surface_mouse_enter"), &SpatialEditorViewport::_surface_mouse_enter);
3402 ClassDB::bind_method(D_METHOD("_surface_mouse_exit"), &SpatialEditorViewport::_surface_mouse_exit);
3403 ClassDB::bind_method(D_METHOD("_surface_focus_enter"), &SpatialEditorViewport::_surface_focus_enter);
3404 ClassDB::bind_method(D_METHOD("_surface_focus_exit"), &SpatialEditorViewport::_surface_focus_exit);
3405 ClassDB::bind_method(D_METHOD("_sinput"), &SpatialEditorViewport::_sinput);
3406 ClassDB::bind_method(D_METHOD("_menu_option"), &SpatialEditorViewport::_menu_option);
3407 ClassDB::bind_method(D_METHOD("_toggle_camera_preview"), &SpatialEditorViewport::_toggle_camera_preview);
3408 ClassDB::bind_method(D_METHOD("_preview_exited_scene"), &SpatialEditorViewport::_preview_exited_scene);
3409 ClassDB::bind_method(D_METHOD("_update_camera"), &SpatialEditorViewport::_update_camera);
3410 ClassDB::bind_method(D_METHOD("update_transform_gizmo_view"), &SpatialEditorViewport::update_transform_gizmo_view);
3411 ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &SpatialEditorViewport::_selection_result_pressed);
3412 ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &SpatialEditorViewport::_selection_menu_hide);
3413 ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &SpatialEditorViewport::can_drop_data_fw);
3414 ClassDB::bind_method(D_METHOD("drop_data_fw"), &SpatialEditorViewport::drop_data_fw);
3415
3416 ADD_SIGNAL(MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport")));
3417 ADD_SIGNAL(MethodInfo("clicked", PropertyInfo(Variant::OBJECT, "viewport")));
3418 }
3419
reset()3420 void SpatialEditorViewport::reset() {
3421
3422 orthogonal = false;
3423 auto_orthogonal = false;
3424 lock_rotation = false;
3425 message_time = 0;
3426 message = "";
3427 last_message = "";
3428 name = "";
3429
3430 cursor = Cursor();
3431 _update_name();
3432 }
3433
focus_selection()3434 void SpatialEditorViewport::focus_selection() {
3435 if (!get_selected_count())
3436 return;
3437
3438 Vector3 center;
3439 int count = 0;
3440
3441 List<Node *> &selection = editor_selection->get_selected_node_list();
3442
3443 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
3444
3445 Spatial *sp = Object::cast_to<Spatial>(E->get());
3446 if (!sp)
3447 continue;
3448
3449 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
3450 if (!se)
3451 continue;
3452
3453 center += sp->get_global_gizmo_transform().origin;
3454 count++;
3455 }
3456
3457 if (count != 0) {
3458 center /= float(count);
3459 }
3460
3461 cursor.pos = center;
3462 }
3463
assign_pending_data_pointers(Spatial * p_preview_node,AABB * p_preview_bounds,AcceptDialog * p_accept)3464 void SpatialEditorViewport::assign_pending_data_pointers(Spatial *p_preview_node, AABB *p_preview_bounds, AcceptDialog *p_accept) {
3465 preview_node = p_preview_node;
3466 preview_bounds = p_preview_bounds;
3467 accept = p_accept;
3468 }
3469
_get_instance_position(const Point2 & p_pos) const3470 Vector3 SpatialEditorViewport::_get_instance_position(const Point2 &p_pos) const {
3471 const float MAX_DISTANCE = 10;
3472
3473 Vector3 world_ray = _get_ray(p_pos);
3474 Vector3 world_pos = _get_ray_pos(p_pos);
3475
3476 Vector<ObjectID> instances = VisualServer::get_singleton()->instances_cull_ray(world_pos, world_ray, get_tree()->get_root()->get_world()->get_scenario());
3477 Set<Ref<EditorSpatialGizmo> > found_gizmos;
3478
3479 float closest_dist = MAX_DISTANCE;
3480
3481 Vector3 point = world_pos + world_ray * MAX_DISTANCE;
3482 Vector3 normal = Vector3(0.0, 0.0, 0.0);
3483
3484 for (int i = 0; i < instances.size(); i++) {
3485
3486 MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(ObjectDB::get_instance(instances[i]));
3487
3488 if (!mesh_instance)
3489 continue;
3490
3491 Ref<EditorSpatialGizmo> seg = mesh_instance->get_gizmo();
3492
3493 if ((!seg.is_valid()) || found_gizmos.has(seg)) {
3494 continue;
3495 }
3496
3497 found_gizmos.insert(seg);
3498
3499 Vector3 hit_point;
3500 Vector3 hit_normal;
3501 bool inters = seg->intersect_ray(camera, p_pos, hit_point, hit_normal, NULL, false);
3502
3503 if (!inters)
3504 continue;
3505
3506 float dist = world_pos.distance_to(hit_point);
3507
3508 if (dist < 0)
3509 continue;
3510
3511 if (dist < closest_dist) {
3512 closest_dist = dist;
3513 point = hit_point;
3514 normal = hit_normal;
3515 }
3516 }
3517 Vector3 offset = Vector3();
3518 for (int i = 0; i < 3; i++) {
3519 if (normal[i] > 0.0)
3520 offset[i] = (preview_bounds->get_size()[i] - (preview_bounds->get_size()[i] + preview_bounds->get_position()[i]));
3521 else if (normal[i] < 0.0)
3522 offset[i] = -(preview_bounds->get_size()[i] + preview_bounds->get_position()[i]);
3523 }
3524 return point + offset;
3525 }
3526
_calculate_spatial_bounds(const Spatial * p_parent,bool p_exclude_toplevel_transform)3527 AABB SpatialEditorViewport::_calculate_spatial_bounds(const Spatial *p_parent, bool p_exclude_toplevel_transform) {
3528 AABB bounds;
3529
3530 const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(p_parent);
3531 if (mesh_instance) {
3532 bounds = mesh_instance->get_aabb();
3533 }
3534
3535 for (int i = 0; i < p_parent->get_child_count(); i++) {
3536 Spatial *child = Object::cast_to<Spatial>(p_parent->get_child(i));
3537 if (child) {
3538 AABB child_bounds = _calculate_spatial_bounds(child, false);
3539
3540 if (bounds.size == Vector3() && p_parent->get_class_name() == StringName("Spatial")) {
3541 bounds = child_bounds;
3542 } else {
3543 bounds.merge_with(child_bounds);
3544 }
3545 }
3546 }
3547
3548 if (bounds.size == Vector3() && p_parent->get_class_name() != StringName("Spatial")) {
3549 bounds = AABB(Vector3(-0.2, -0.2, -0.2), Vector3(0.4, 0.4, 0.4));
3550 }
3551
3552 if (!p_exclude_toplevel_transform) {
3553 bounds = p_parent->get_transform().xform(bounds);
3554 }
3555
3556 return bounds;
3557 }
3558
_create_preview(const Vector<String> & files) const3559 void SpatialEditorViewport::_create_preview(const Vector<String> &files) const {
3560 for (int i = 0; i < files.size(); i++) {
3561 String path = files[i];
3562 RES res = ResourceLoader::load(path);
3563 ERR_CONTINUE(res.is_null());
3564 Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
3565 Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res));
3566 if (mesh != NULL || scene != NULL) {
3567 if (mesh != NULL) {
3568 MeshInstance *mesh_instance = memnew(MeshInstance);
3569 mesh_instance->set_mesh(mesh);
3570 preview_node->add_child(mesh_instance);
3571 } else {
3572 if (scene.is_valid()) {
3573 Node *instance = scene->instance();
3574 if (instance) {
3575 preview_node->add_child(instance);
3576 }
3577 }
3578 }
3579 editor->get_scene_root()->add_child(preview_node);
3580 }
3581 }
3582 *preview_bounds = _calculate_spatial_bounds(preview_node);
3583 }
3584
_remove_preview()3585 void SpatialEditorViewport::_remove_preview() {
3586 if (preview_node->get_parent()) {
3587 for (int i = preview_node->get_child_count() - 1; i >= 0; i--) {
3588 Node *node = preview_node->get_child(i);
3589 node->queue_delete();
3590 preview_node->remove_child(node);
3591 }
3592 editor->get_scene_root()->remove_child(preview_node);
3593 }
3594 }
3595
_cyclical_dependency_exists(const String & p_target_scene_path,Node * p_desired_node)3596 bool SpatialEditorViewport::_cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) {
3597 if (p_desired_node->get_filename() == p_target_scene_path) {
3598 return true;
3599 }
3600
3601 int childCount = p_desired_node->get_child_count();
3602 for (int i = 0; i < childCount; i++) {
3603 Node *child = p_desired_node->get_child(i);
3604 if (_cyclical_dependency_exists(p_target_scene_path, child)) {
3605 return true;
3606 }
3607 }
3608 return false;
3609 }
3610
_create_instance(Node * parent,String & path,const Point2 & p_point)3611 bool SpatialEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) {
3612 RES res = ResourceLoader::load(path);
3613 ERR_FAIL_COND_V(res.is_null(), false);
3614
3615 Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
3616 Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res));
3617
3618 Node *instanced_scene = NULL;
3619
3620 if (mesh != NULL || scene != NULL) {
3621 if (mesh != NULL) {
3622 MeshInstance *mesh_instance = memnew(MeshInstance);
3623 mesh_instance->set_mesh(mesh);
3624 mesh_instance->set_name(path.get_file().get_basename());
3625 instanced_scene = mesh_instance;
3626 } else {
3627 if (!scene.is_valid()) { // invalid scene
3628 return false;
3629 } else {
3630 instanced_scene = scene->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
3631 }
3632 }
3633 }
3634
3635 if (instanced_scene == NULL) {
3636 return false;
3637 }
3638
3639 if (editor->get_edited_scene()->get_filename() != "") { // cyclical instancing
3640 if (_cyclical_dependency_exists(editor->get_edited_scene()->get_filename(), instanced_scene)) {
3641 memdelete(instanced_scene);
3642 return false;
3643 }
3644 }
3645
3646 if (scene != NULL) {
3647 instanced_scene->set_filename(ProjectSettings::get_singleton()->localize_path(path));
3648 }
3649
3650 editor_data->get_undo_redo().add_do_method(parent, "add_child", instanced_scene);
3651 editor_data->get_undo_redo().add_do_method(instanced_scene, "set_owner", editor->get_edited_scene());
3652 editor_data->get_undo_redo().add_do_reference(instanced_scene);
3653 editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instanced_scene);
3654
3655 String new_name = parent->validate_child_name(instanced_scene);
3656 ScriptEditorDebugger *sed = ScriptEditor::get_singleton()->get_debugger();
3657 editor_data->get_undo_redo().add_do_method(sed, "live_debug_instance_node", editor->get_edited_scene()->get_path_to(parent), path, new_name);
3658 editor_data->get_undo_redo().add_undo_method(sed, "live_debug_remove_node", NodePath(String(editor->get_edited_scene()->get_path_to(parent)) + "/" + new_name));
3659
3660 Spatial *spatial = Object::cast_to<Spatial>(instanced_scene);
3661 if (spatial) {
3662 Transform global_transform;
3663 Spatial *parent_spatial = Object::cast_to<Spatial>(parent);
3664 if (parent_spatial) {
3665 global_transform = parent_spatial->get_global_gizmo_transform();
3666 }
3667
3668 global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point));
3669 global_transform.basis *= spatial->get_transform().basis;
3670
3671 editor_data->get_undo_redo().add_do_method(instanced_scene, "set_global_transform", global_transform);
3672 }
3673
3674 return true;
3675 }
3676
_perform_drop_data()3677 void SpatialEditorViewport::_perform_drop_data() {
3678 _remove_preview();
3679
3680 Vector<String> error_files;
3681
3682 editor_data->get_undo_redo().create_action(TTR("Create Node"));
3683
3684 for (int i = 0; i < selected_files.size(); i++) {
3685 String path = selected_files[i];
3686 RES res = ResourceLoader::load(path);
3687 if (res.is_null()) {
3688 continue;
3689 }
3690 Ref<PackedScene> scene = Ref<PackedScene>(Object::cast_to<PackedScene>(*res));
3691 Ref<Mesh> mesh = Ref<Mesh>(Object::cast_to<Mesh>(*res));
3692 if (mesh != NULL || scene != NULL) {
3693 bool success = _create_instance(target_node, path, drop_pos);
3694 if (!success) {
3695 error_files.push_back(path);
3696 }
3697 }
3698 }
3699
3700 editor_data->get_undo_redo().commit_action();
3701
3702 if (error_files.size() > 0) {
3703 String files_str;
3704 for (int i = 0; i < error_files.size(); i++) {
3705 files_str += error_files[i].get_file().get_basename() + ",";
3706 }
3707 files_str = files_str.substr(0, files_str.length() - 1);
3708 accept->set_text(vformat(TTR("Error instancing scene from %s"), files_str.c_str()));
3709 accept->popup_centered_minsize();
3710 }
3711 }
3712
can_drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from) const3713 bool SpatialEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
3714
3715 bool can_instance = false;
3716
3717 if (!preview_node->is_inside_tree()) {
3718 Dictionary d = p_data;
3719 if (d.has("type") && (String(d["type"]) == "files")) {
3720 Vector<String> files = d["files"];
3721
3722 List<String> scene_extensions;
3723 ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions);
3724 List<String> mesh_extensions;
3725 ResourceLoader::get_recognized_extensions_for_type("Mesh", &mesh_extensions);
3726
3727 for (int i = 0; i < files.size(); i++) {
3728 if (mesh_extensions.find(files[i].get_extension()) || scene_extensions.find(files[i].get_extension())) {
3729 RES res = ResourceLoader::load(files[i]);
3730 if (res.is_null()) {
3731 continue;
3732 }
3733
3734 String type = res->get_class();
3735 if (type == "PackedScene") {
3736 Ref<PackedScene> sdata = ResourceLoader::load(files[i]);
3737 Node *instanced_scene = sdata->instance(PackedScene::GEN_EDIT_STATE_INSTANCE);
3738 if (!instanced_scene) {
3739 continue;
3740 }
3741 memdelete(instanced_scene);
3742 } else if (type == "Mesh" || type == "ArrayMesh" || type == "PrimitiveMesh") {
3743 Ref<Mesh> mesh = ResourceLoader::load(files[i]);
3744 if (!mesh.is_valid()) {
3745 continue;
3746 }
3747 } else {
3748 continue;
3749 }
3750 can_instance = true;
3751 break;
3752 }
3753 }
3754 if (can_instance) {
3755 _create_preview(files);
3756 }
3757 }
3758 } else {
3759 can_instance = true;
3760 }
3761
3762 if (can_instance) {
3763 Transform global_transform = Transform(Basis(), _get_instance_position(p_point));
3764 preview_node->set_global_transform(global_transform);
3765 }
3766
3767 return can_instance;
3768 }
3769
drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from)3770 void SpatialEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
3771 if (!can_drop_data_fw(p_point, p_data, p_from))
3772 return;
3773
3774 bool is_shift = Input::get_singleton()->is_key_pressed(KEY_SHIFT);
3775
3776 selected_files.clear();
3777 Dictionary d = p_data;
3778 if (d.has("type") && String(d["type"]) == "files") {
3779 selected_files = d["files"];
3780 }
3781
3782 List<Node *> list = editor->get_editor_selection()->get_selected_node_list();
3783 if (list.size() == 0) {
3784 Node *root_node = editor->get_edited_scene();
3785 if (root_node) {
3786 list.push_back(root_node);
3787 } else {
3788 accept->set_text(TTR("No parent to instance a child at."));
3789 accept->popup_centered_minsize();
3790 _remove_preview();
3791 return;
3792 }
3793 }
3794 if (list.size() != 1) {
3795 accept->set_text(TTR("This operation requires a single selected node."));
3796 accept->popup_centered_minsize();
3797 _remove_preview();
3798 return;
3799 }
3800
3801 target_node = list[0];
3802 if (is_shift && target_node != editor->get_edited_scene()) {
3803 target_node = target_node->get_parent();
3804 }
3805 drop_pos = p_point;
3806
3807 _perform_drop_data();
3808 }
3809
SpatialEditorViewport(SpatialEditor * p_spatial_editor,EditorNode * p_editor,int p_index)3810 SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, EditorNode *p_editor, int p_index) {
3811
3812 _edit.mode = TRANSFORM_NONE;
3813 _edit.plane = TRANSFORM_VIEW;
3814 _edit.edited_gizmo = 0;
3815 _edit.snap = 1;
3816 _edit.gizmo_handle = 0;
3817
3818 index = p_index;
3819 editor = p_editor;
3820 editor_data = editor->get_scene_tree_dock()->get_editor_data();
3821 editor_selection = editor->get_editor_selection();
3822 undo_redo = editor->get_undo_redo();
3823 clicked = 0;
3824 clicked_includes_current = false;
3825 orthogonal = false;
3826 auto_orthogonal = false;
3827 lock_rotation = false;
3828 message_time = 0;
3829 zoom_indicator_delay = 0.0;
3830
3831 spatial_editor = p_spatial_editor;
3832 ViewportContainer *c = memnew(ViewportContainer);
3833 viewport_container = c;
3834 c->set_stretch(true);
3835 add_child(c);
3836 c->set_anchors_and_margins_preset(Control::PRESET_WIDE);
3837 viewport = memnew(Viewport);
3838 viewport->set_disable_input(true);
3839
3840 c->add_child(viewport);
3841 surface = memnew(Control);
3842 surface->set_drag_forwarding(this);
3843 add_child(surface);
3844 surface->set_anchors_and_margins_preset(Control::PRESET_WIDE);
3845 surface->set_clip_contents(true);
3846 camera = memnew(Camera);
3847 camera->set_disable_gizmo(true);
3848 camera->set_cull_mask(((1 << 20) - 1) | (1 << (GIZMO_BASE_LAYER + p_index)) | (1 << GIZMO_EDIT_LAYER) | (1 << GIZMO_GRID_LAYER));
3849 viewport->add_child(camera);
3850 camera->make_current();
3851 surface->set_focus_mode(FOCUS_ALL);
3852
3853 VBoxContainer *vbox = memnew(VBoxContainer);
3854 surface->add_child(vbox);
3855 vbox->set_position(Point2(10, 10) * EDSCALE);
3856
3857 view_menu = memnew(MenuButton);
3858 view_menu->set_flat(false);
3859 vbox->add_child(view_menu);
3860 view_menu->set_h_size_flags(0);
3861
3862 view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/top_view"), VIEW_TOP);
3863 view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/bottom_view"), VIEW_BOTTOM);
3864 view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/left_view"), VIEW_LEFT);
3865 view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/right_view"), VIEW_RIGHT);
3866 view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/front_view"), VIEW_FRONT);
3867 view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/rear_view"), VIEW_REAR);
3868 view_menu->get_popup()->add_separator();
3869 view_menu->get_popup()->add_radio_check_item(TTR("Perspective") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_PERSPECTIVE);
3870 view_menu->get_popup()->add_radio_check_item(TTR("Orthogonal") + " (" + ED_GET_SHORTCUT("spatial_editor/switch_perspective_orthogonal")->get_as_text() + ")", VIEW_ORTHOGONAL);
3871 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_PERSPECTIVE), true);
3872 view_menu->get_popup()->add_check_item(TTR("Auto Orthogonal Enabled"), VIEW_AUTO_ORTHOGONAL);
3873 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUTO_ORTHOGONAL), true);
3874 view_menu->get_popup()->add_separator();
3875 view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_lock_rotation", TTR("Lock View Rotation")), VIEW_LOCK_ROTATION);
3876 view_menu->get_popup()->add_separator();
3877 view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_normal", TTR("Display Normal")), VIEW_DISPLAY_NORMAL);
3878 view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_wireframe", TTR("Display Wireframe")), VIEW_DISPLAY_WIREFRAME);
3879 view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_overdraw", TTR("Display Overdraw")), VIEW_DISPLAY_OVERDRAW);
3880 view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS);
3881 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true);
3882 view_menu->get_popup()->add_separator();
3883 view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_environment", TTR("View Environment")), VIEW_ENVIRONMENT);
3884 view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_gizmos", TTR("View Gizmos")), VIEW_GIZMOS);
3885 view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_information", TTR("View Information")), VIEW_INFORMATION);
3886 view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_fps", TTR("View FPS")), VIEW_FPS);
3887 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_ENVIRONMENT), true);
3888 view_menu->get_popup()->add_separator();
3889 view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_half_resolution", TTR("Half Resolution")), VIEW_HALF_RESOLUTION);
3890 view_menu->get_popup()->add_separator();
3891 view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_audio_listener", TTR("Audio Listener")), VIEW_AUDIO_LISTENER);
3892 view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_audio_doppler", TTR("Enable Doppler")), VIEW_AUDIO_DOPPLER);
3893 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS), true);
3894
3895 view_menu->get_popup()->add_separator();
3896 view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_cinematic_preview", TTR("Cinematic Preview")), VIEW_CINEMATIC_PREVIEW);
3897
3898 view_menu->get_popup()->add_separator();
3899 view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_origin"), VIEW_CENTER_TO_ORIGIN);
3900 view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/focus_selection"), VIEW_CENTER_TO_SELECTION);
3901 view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_transform_with_view"), VIEW_ALIGN_TRANSFORM_WITH_VIEW);
3902 view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/align_rotation_with_view"), VIEW_ALIGN_ROTATION_WITH_VIEW);
3903 view_menu->get_popup()->connect("id_pressed", this, "_menu_option");
3904
3905 view_menu->set_disable_shortcuts(true);
3906
3907 if (OS::get_singleton()->get_current_video_driver() == OS::VIDEO_DRIVER_GLES2) {
3908 // Alternate display modes only work when using the GLES3 renderer; make this explicit.
3909 const int normal_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL);
3910 const int wireframe_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME);
3911 const int overdraw_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW);
3912 const int shadeless_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS);
3913 const String unsupported_tooltip = TTR("Not available when using the GLES2 renderer.");
3914
3915 view_menu->get_popup()->set_item_disabled(normal_idx, true);
3916 view_menu->get_popup()->set_item_tooltip(normal_idx, unsupported_tooltip);
3917 view_menu->get_popup()->set_item_disabled(wireframe_idx, true);
3918 view_menu->get_popup()->set_item_tooltip(wireframe_idx, unsupported_tooltip);
3919 view_menu->get_popup()->set_item_disabled(overdraw_idx, true);
3920 view_menu->get_popup()->set_item_tooltip(overdraw_idx, unsupported_tooltip);
3921 view_menu->get_popup()->set_item_disabled(shadeless_idx, true);
3922 view_menu->get_popup()->set_item_tooltip(shadeless_idx, unsupported_tooltip);
3923 }
3924
3925 ED_SHORTCUT("spatial_editor/freelook_left", TTR("Freelook Left"), KEY_A);
3926 ED_SHORTCUT("spatial_editor/freelook_right", TTR("Freelook Right"), KEY_D);
3927 ED_SHORTCUT("spatial_editor/freelook_forward", TTR("Freelook Forward"), KEY_W);
3928 ED_SHORTCUT("spatial_editor/freelook_backwards", TTR("Freelook Backwards"), KEY_S);
3929 ED_SHORTCUT("spatial_editor/freelook_up", TTR("Freelook Up"), KEY_E);
3930 ED_SHORTCUT("spatial_editor/freelook_down", TTR("Freelook Down"), KEY_Q);
3931 ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT);
3932 ED_SHORTCUT("spatial_editor/freelook_slow_modifier", TTR("Freelook Slow Modifier"), KEY_ALT);
3933
3934 preview_camera = memnew(CheckBox);
3935 preview_camera->set_text(TTR("Preview"));
3936 vbox->add_child(preview_camera);
3937 preview_camera->set_h_size_flags(0);
3938 preview_camera->hide();
3939 preview_camera->connect("toggled", this, "_toggle_camera_preview");
3940 previewing = NULL;
3941 gizmo_scale = 1.0;
3942
3943 preview_node = NULL;
3944
3945 info_label = memnew(Label);
3946 info_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE);
3947 info_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -90 * EDSCALE);
3948 info_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE);
3949 info_label->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -10 * EDSCALE);
3950 info_label->set_h_grow_direction(GROW_DIRECTION_BEGIN);
3951 info_label->set_v_grow_direction(GROW_DIRECTION_BEGIN);
3952 surface->add_child(info_label);
3953 info_label->hide();
3954
3955 cinema_label = memnew(Label);
3956 cinema_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE);
3957 cinema_label->set_h_grow_direction(GROW_DIRECTION_END);
3958 cinema_label->set_align(Label::ALIGN_CENTER);
3959 surface->add_child(cinema_label);
3960 cinema_label->set_text(TTR("Cinematic Preview"));
3961 cinema_label->hide();
3962 previewing_cinema = false;
3963
3964 locked_label = memnew(Label);
3965 locked_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -20 * EDSCALE);
3966 locked_label->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -10 * EDSCALE);
3967 locked_label->set_h_grow_direction(GROW_DIRECTION_END);
3968 locked_label->set_v_grow_direction(GROW_DIRECTION_BEGIN);
3969 locked_label->set_align(Label::ALIGN_CENTER);
3970 surface->add_child(locked_label);
3971 locked_label->set_text(TTR("View Rotation Locked"));
3972 locked_label->hide();
3973
3974 top_right_vbox = memnew(VBoxContainer);
3975 top_right_vbox->set_anchors_and_margins_preset(PRESET_TOP_RIGHT, PRESET_MODE_MINSIZE, 2.0 * EDSCALE);
3976 top_right_vbox->set_h_grow_direction(GROW_DIRECTION_BEGIN);
3977
3978 rotation_control = memnew(ViewportRotationControl);
3979 rotation_control->set_custom_minimum_size(Size2(80, 80) * EDSCALE);
3980 rotation_control->set_h_size_flags(SIZE_SHRINK_END);
3981 rotation_control->set_viewport(this);
3982 top_right_vbox->add_child(rotation_control);
3983
3984 fps_label = memnew(Label);
3985 fps_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -90 * EDSCALE);
3986 fps_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE);
3987 fps_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE);
3988 fps_label->set_h_grow_direction(GROW_DIRECTION_BEGIN);
3989 fps_label->set_tooltip(TTR("Note: The FPS value displayed is the editor's framerate.\nIt cannot be used as a reliable indication of in-game performance."));
3990 fps_label->set_mouse_filter(MOUSE_FILTER_PASS); // Otherwise tooltip doesn't show.
3991 top_right_vbox->add_child(fps_label);
3992 fps_label->hide();
3993
3994 surface->add_child(top_right_vbox);
3995
3996 accept = NULL;
3997
3998 freelook_active = false;
3999 freelook_speed = EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_base_speed");
4000
4001 selection_menu = memnew(PopupMenu);
4002 add_child(selection_menu);
4003 selection_menu->set_custom_minimum_size(Size2(100, 0) * EDSCALE);
4004 selection_menu->connect("id_pressed", this, "_selection_result_pressed");
4005 selection_menu->connect("popup_hide", this, "_selection_menu_hide");
4006
4007 if (p_index == 0) {
4008 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_LISTENER), true);
4009 viewport->set_as_audio_listener(true);
4010 }
4011
4012 name = "";
4013 _update_name();
4014
4015 EditorSettings::get_singleton()->connect("settings_changed", this, "update_transform_gizmo_view");
4016 }
4017
4018 //////////////////////////////////////////////////////////////
4019
_gui_input(const Ref<InputEvent> & p_event)4020 void SpatialEditorViewportContainer::_gui_input(const Ref<InputEvent> &p_event) {
4021
4022 Ref<InputEventMouseButton> mb = p_event;
4023
4024 if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) {
4025
4026 if (mb->is_pressed()) {
4027 Vector2 size = get_size();
4028
4029 int h_sep = get_constant("separation", "HSplitContainer");
4030 int v_sep = get_constant("separation", "VSplitContainer");
4031
4032 int mid_w = size.width * ratio_h;
4033 int mid_h = size.height * ratio_v;
4034
4035 dragging_h = mb->get_position().x > (mid_w - h_sep / 2) && mb->get_position().x < (mid_w + h_sep / 2);
4036 dragging_v = mb->get_position().y > (mid_h - v_sep / 2) && mb->get_position().y < (mid_h + v_sep / 2);
4037
4038 drag_begin_pos = mb->get_position();
4039 drag_begin_ratio.x = ratio_h;
4040 drag_begin_ratio.y = ratio_v;
4041
4042 switch (view) {
4043 case VIEW_USE_1_VIEWPORT: {
4044
4045 dragging_h = false;
4046 dragging_v = false;
4047
4048 } break;
4049 case VIEW_USE_2_VIEWPORTS: {
4050
4051 dragging_h = false;
4052
4053 } break;
4054 case VIEW_USE_2_VIEWPORTS_ALT: {
4055
4056 dragging_v = false;
4057
4058 } break;
4059 case VIEW_USE_3_VIEWPORTS:
4060 case VIEW_USE_3_VIEWPORTS_ALT:
4061 case VIEW_USE_4_VIEWPORTS: {
4062
4063 // Do nothing.
4064
4065 } break;
4066 }
4067 } else {
4068 dragging_h = false;
4069 dragging_v = false;
4070 }
4071 }
4072
4073 Ref<InputEventMouseMotion> mm = p_event;
4074
4075 if (mm.is_valid()) {
4076
4077 if (view == VIEW_USE_3_VIEWPORTS || view == VIEW_USE_3_VIEWPORTS_ALT || view == VIEW_USE_4_VIEWPORTS) {
4078 Vector2 size = get_size();
4079
4080 int h_sep = get_constant("separation", "HSplitContainer");
4081 int v_sep = get_constant("separation", "VSplitContainer");
4082
4083 int mid_w = size.width * ratio_h;
4084 int mid_h = size.height * ratio_v;
4085
4086 bool was_hovering_h = hovering_h;
4087 bool was_hovering_v = hovering_v;
4088 hovering_h = mm->get_position().x > (mid_w - h_sep / 2) && mm->get_position().x < (mid_w + h_sep / 2);
4089 hovering_v = mm->get_position().y > (mid_h - v_sep / 2) && mm->get_position().y < (mid_h + v_sep / 2);
4090
4091 if (was_hovering_h != hovering_h || was_hovering_v != hovering_v) {
4092 update();
4093 }
4094 }
4095
4096 if (dragging_h) {
4097 float new_ratio = drag_begin_ratio.x + (mm->get_position().x - drag_begin_pos.x) / get_size().width;
4098 new_ratio = CLAMP(new_ratio, 40 / get_size().width, (get_size().width - 40) / get_size().width);
4099 ratio_h = new_ratio;
4100 queue_sort();
4101 update();
4102 }
4103 if (dragging_v) {
4104 float new_ratio = drag_begin_ratio.y + (mm->get_position().y - drag_begin_pos.y) / get_size().height;
4105 new_ratio = CLAMP(new_ratio, 40 / get_size().height, (get_size().height - 40) / get_size().height);
4106 ratio_v = new_ratio;
4107 queue_sort();
4108 update();
4109 }
4110 }
4111 }
4112
_notification(int p_what)4113 void SpatialEditorViewportContainer::_notification(int p_what) {
4114
4115 if (p_what == NOTIFICATION_MOUSE_ENTER || p_what == NOTIFICATION_MOUSE_EXIT) {
4116
4117 mouseover = (p_what == NOTIFICATION_MOUSE_ENTER);
4118 update();
4119 }
4120
4121 if (p_what == NOTIFICATION_DRAW && mouseover) {
4122
4123 Ref<Texture> h_grabber = get_icon("grabber", "HSplitContainer");
4124 Ref<Texture> v_grabber = get_icon("grabber", "VSplitContainer");
4125
4126 Ref<Texture> hdiag_grabber = get_icon("GuiViewportHdiagsplitter", "EditorIcons");
4127 Ref<Texture> vdiag_grabber = get_icon("GuiViewportVdiagsplitter", "EditorIcons");
4128 Ref<Texture> vh_grabber = get_icon("GuiViewportVhsplitter", "EditorIcons");
4129
4130 Vector2 size = get_size();
4131
4132 int h_sep = get_constant("separation", "HSplitContainer");
4133
4134 int v_sep = get_constant("separation", "VSplitContainer");
4135
4136 int mid_w = size.width * ratio_h;
4137 int mid_h = size.height * ratio_v;
4138
4139 int size_left = mid_w - h_sep / 2;
4140 int size_bottom = size.height - mid_h - v_sep / 2;
4141
4142 switch (view) {
4143
4144 case VIEW_USE_1_VIEWPORT: {
4145
4146 // Nothing to show.
4147
4148 } break;
4149 case VIEW_USE_2_VIEWPORTS: {
4150
4151 draw_texture(v_grabber, Vector2((size.width - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2));
4152 set_default_cursor_shape(CURSOR_VSPLIT);
4153
4154 } break;
4155 case VIEW_USE_2_VIEWPORTS_ALT: {
4156
4157 draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, (size.height - h_grabber->get_height()) / 2));
4158 set_default_cursor_shape(CURSOR_HSPLIT);
4159
4160 } break;
4161 case VIEW_USE_3_VIEWPORTS: {
4162
4163 if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) {
4164 draw_texture(hdiag_grabber, Vector2(mid_w - hdiag_grabber->get_width() / 2, mid_h - v_grabber->get_height() / 4));
4165 set_default_cursor_shape(CURSOR_DRAG);
4166 } else if ((hovering_v && !dragging_h) || dragging_v) {
4167 draw_texture(v_grabber, Vector2((size.width - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2));
4168 set_default_cursor_shape(CURSOR_VSPLIT);
4169 } else if (hovering_h || dragging_h) {
4170 draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, mid_h + v_grabber->get_height() / 2 + (size_bottom - h_grabber->get_height()) / 2));
4171 set_default_cursor_shape(CURSOR_HSPLIT);
4172 }
4173
4174 } break;
4175 case VIEW_USE_3_VIEWPORTS_ALT: {
4176
4177 if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) {
4178 draw_texture(vdiag_grabber, Vector2(mid_w - vdiag_grabber->get_width() + v_grabber->get_height() / 4, mid_h - vdiag_grabber->get_height() / 2));
4179 set_default_cursor_shape(CURSOR_DRAG);
4180 } else if ((hovering_v && !dragging_h) || dragging_v) {
4181 draw_texture(v_grabber, Vector2((size_left - v_grabber->get_width()) / 2, mid_h - v_grabber->get_height() / 2));
4182 set_default_cursor_shape(CURSOR_VSPLIT);
4183 } else if (hovering_h || dragging_h) {
4184 draw_texture(h_grabber, Vector2(mid_w - h_grabber->get_width() / 2, (size.height - h_grabber->get_height()) / 2));
4185 set_default_cursor_shape(CURSOR_HSPLIT);
4186 }
4187
4188 } break;
4189 case VIEW_USE_4_VIEWPORTS: {
4190
4191 Vector2 half(mid_w, mid_h);
4192 if ((hovering_v && hovering_h && !dragging_v && !dragging_h) || (dragging_v && dragging_h)) {
4193 draw_texture(vh_grabber, half - vh_grabber->get_size() / 2.0);
4194 set_default_cursor_shape(CURSOR_DRAG);
4195 } else if ((hovering_v && !dragging_h) || dragging_v) {
4196 draw_texture(v_grabber, half - v_grabber->get_size() / 2.0);
4197 set_default_cursor_shape(CURSOR_VSPLIT);
4198 } else if (hovering_h || dragging_h) {
4199 draw_texture(h_grabber, half - h_grabber->get_size() / 2.0);
4200 set_default_cursor_shape(CURSOR_HSPLIT);
4201 }
4202
4203 } break;
4204 }
4205 }
4206
4207 if (p_what == NOTIFICATION_SORT_CHILDREN) {
4208
4209 SpatialEditorViewport *viewports[4];
4210 int vc = 0;
4211 for (int i = 0; i < get_child_count(); i++) {
4212 viewports[vc] = Object::cast_to<SpatialEditorViewport>(get_child(i));
4213 if (viewports[vc]) {
4214 vc++;
4215 }
4216 }
4217
4218 ERR_FAIL_COND(vc != 4);
4219
4220 Size2 size = get_size();
4221
4222 if (size.x < 10 || size.y < 10) {
4223 for (int i = 0; i < 4; i++) {
4224 viewports[i]->hide();
4225 }
4226 return;
4227 }
4228 int h_sep = get_constant("separation", "HSplitContainer");
4229
4230 int v_sep = get_constant("separation", "VSplitContainer");
4231
4232 int mid_w = size.width * ratio_h;
4233 int mid_h = size.height * ratio_v;
4234
4235 int size_left = mid_w - h_sep / 2;
4236 int size_right = size.width - mid_w - h_sep / 2;
4237
4238 int size_top = mid_h - v_sep / 2;
4239 int size_bottom = size.height - mid_h - v_sep / 2;
4240
4241 switch (view) {
4242
4243 case VIEW_USE_1_VIEWPORT: {
4244
4245 viewports[0]->show();
4246 for (int i = 1; i < 4; i++) {
4247
4248 viewports[i]->hide();
4249 }
4250
4251 fit_child_in_rect(viewports[0], Rect2(Vector2(), size));
4252
4253 } break;
4254 case VIEW_USE_2_VIEWPORTS: {
4255
4256 for (int i = 0; i < 4; i++) {
4257
4258 if (i == 1 || i == 3)
4259 viewports[i]->hide();
4260 else
4261 viewports[i]->show();
4262 }
4263
4264 fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size.width, size_top)));
4265 fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size.width, size_bottom)));
4266
4267 } break;
4268 case VIEW_USE_2_VIEWPORTS_ALT: {
4269
4270 for (int i = 0; i < 4; i++) {
4271
4272 if (i == 1 || i == 3)
4273 viewports[i]->hide();
4274 else
4275 viewports[i]->show();
4276 }
4277 fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size.height)));
4278 fit_child_in_rect(viewports[2], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size.height)));
4279
4280 } break;
4281 case VIEW_USE_3_VIEWPORTS: {
4282
4283 for (int i = 0; i < 4; i++) {
4284
4285 if (i == 1)
4286 viewports[i]->hide();
4287 else
4288 viewports[i]->show();
4289 }
4290
4291 fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size.width, size_top)));
4292 fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom)));
4293 fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, mid_h + v_sep / 2), Vector2(size_right, size_bottom)));
4294
4295 } break;
4296 case VIEW_USE_3_VIEWPORTS_ALT: {
4297
4298 for (int i = 0; i < 4; i++) {
4299
4300 if (i == 1)
4301 viewports[i]->hide();
4302 else
4303 viewports[i]->show();
4304 }
4305
4306 fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size_top)));
4307 fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom)));
4308 fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size.height)));
4309
4310 } break;
4311 case VIEW_USE_4_VIEWPORTS: {
4312
4313 for (int i = 0; i < 4; i++) {
4314
4315 viewports[i]->show();
4316 }
4317
4318 fit_child_in_rect(viewports[0], Rect2(Vector2(), Vector2(size_left, size_top)));
4319 fit_child_in_rect(viewports[1], Rect2(Vector2(mid_w + h_sep / 2, 0), Vector2(size_right, size_top)));
4320 fit_child_in_rect(viewports[2], Rect2(Vector2(0, mid_h + v_sep / 2), Vector2(size_left, size_bottom)));
4321 fit_child_in_rect(viewports[3], Rect2(Vector2(mid_w + h_sep / 2, mid_h + v_sep / 2), Vector2(size_right, size_bottom)));
4322
4323 } break;
4324 }
4325 }
4326 }
4327
set_view(View p_view)4328 void SpatialEditorViewportContainer::set_view(View p_view) {
4329
4330 view = p_view;
4331 queue_sort();
4332 }
4333
get_view()4334 SpatialEditorViewportContainer::View SpatialEditorViewportContainer::get_view() {
4335
4336 return view;
4337 }
4338
_bind_methods()4339 void SpatialEditorViewportContainer::_bind_methods() {
4340
4341 ClassDB::bind_method("_gui_input", &SpatialEditorViewportContainer::_gui_input);
4342 }
4343
SpatialEditorViewportContainer()4344 SpatialEditorViewportContainer::SpatialEditorViewportContainer() {
4345
4346 set_clip_contents(true);
4347 view = VIEW_USE_1_VIEWPORT;
4348 mouseover = false;
4349 ratio_h = 0.5;
4350 ratio_v = 0.5;
4351 hovering_v = false;
4352 hovering_h = false;
4353 dragging_v = false;
4354 dragging_h = false;
4355 }
4356
4357 ///////////////////////////////////////////////////////////////////
4358
4359 SpatialEditor *SpatialEditor::singleton = NULL;
4360
~SpatialEditorSelectedItem()4361 SpatialEditorSelectedItem::~SpatialEditorSelectedItem() {
4362
4363 if (sbox_instance.is_valid())
4364 VisualServer::get_singleton()->free(sbox_instance);
4365 }
4366
select_gizmo_highlight_axis(int p_axis)4367 void SpatialEditor::select_gizmo_highlight_axis(int p_axis) {
4368
4369 for (int i = 0; i < 3; i++) {
4370
4371 move_gizmo[i]->surface_set_material(0, i == p_axis ? gizmo_color_hl[i] : gizmo_color[i]);
4372 move_plane_gizmo[i]->surface_set_material(0, (i + 6) == p_axis ? plane_gizmo_color_hl[i] : plane_gizmo_color[i]);
4373 rotate_gizmo[i]->surface_set_material(0, (i + 3) == p_axis ? gizmo_color_hl[i] : gizmo_color[i]);
4374 scale_gizmo[i]->surface_set_material(0, (i + 9) == p_axis ? gizmo_color_hl[i] : gizmo_color[i]);
4375 scale_plane_gizmo[i]->surface_set_material(0, (i + 12) == p_axis ? plane_gizmo_color_hl[i] : plane_gizmo_color[i]);
4376 }
4377 }
4378
update_transform_gizmo()4379 void SpatialEditor::update_transform_gizmo() {
4380
4381 List<Node *> &selection = editor_selection->get_selected_node_list();
4382 AABB center;
4383 bool first = true;
4384
4385 Basis gizmo_basis;
4386 bool local_gizmo_coords = are_local_coords_enabled();
4387
4388 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
4389
4390 Spatial *sp = Object::cast_to<Spatial>(E->get());
4391 if (!sp)
4392 continue;
4393
4394 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
4395 if (!se)
4396 continue;
4397
4398 Transform xf = se->sp->get_global_gizmo_transform();
4399
4400 if (first) {
4401 center.position = xf.origin;
4402 first = false;
4403 if (local_gizmo_coords) {
4404 gizmo_basis = xf.basis;
4405 gizmo_basis.orthonormalize();
4406 }
4407 } else {
4408 center.expand_to(xf.origin);
4409 gizmo_basis = Basis();
4410 }
4411 }
4412
4413 Vector3 pcenter = center.position + center.size * 0.5;
4414 gizmo.visible = !first;
4415 gizmo.transform.origin = pcenter;
4416 gizmo.transform.basis = gizmo_basis;
4417
4418 for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {
4419 viewports[i]->update_transform_gizmo_view();
4420 }
4421 }
4422
_update_all_gizmos(Node * p_node)4423 void _update_all_gizmos(Node *p_node) {
4424 for (int i = p_node->get_child_count() - 1; 0 <= i; --i) {
4425 Spatial *spatial_node = Object::cast_to<Spatial>(p_node->get_child(i));
4426 if (spatial_node) {
4427 spatial_node->update_gizmo();
4428 }
4429
4430 _update_all_gizmos(p_node->get_child(i));
4431 }
4432 }
4433
update_all_gizmos(Node * p_node)4434 void SpatialEditor::update_all_gizmos(Node *p_node) {
4435 if (!p_node) {
4436 p_node = SceneTree::get_singleton()->get_root();
4437 }
4438 _update_all_gizmos(p_node);
4439 }
4440
_get_editor_data(Object * p_what)4441 Object *SpatialEditor::_get_editor_data(Object *p_what) {
4442
4443 Spatial *sp = Object::cast_to<Spatial>(p_what);
4444 if (!sp)
4445 return NULL;
4446
4447 SpatialEditorSelectedItem *si = memnew(SpatialEditorSelectedItem);
4448
4449 si->sp = sp;
4450 si->sbox_instance = VisualServer::get_singleton()->instance_create2(selection_box->get_rid(), sp->get_world()->get_scenario());
4451 VS::get_singleton()->instance_geometry_set_cast_shadows_setting(si->sbox_instance, VS::SHADOW_CASTING_SETTING_OFF);
4452
4453 return si;
4454 }
4455
_generate_selection_box()4456 void SpatialEditor::_generate_selection_box() {
4457
4458 AABB aabb(Vector3(), Vector3(1, 1, 1));
4459 aabb.grow_by(aabb.get_longest_axis_size() / 20.0);
4460
4461 Ref<SurfaceTool> st = memnew(SurfaceTool);
4462
4463 st->begin(Mesh::PRIMITIVE_LINES);
4464 for (int i = 0; i < 12; i++) {
4465
4466 Vector3 a, b;
4467 aabb.get_edge(i, a, b);
4468
4469 st->add_color(Color(1.0, 1.0, 0.8, 0.8));
4470 st->add_vertex(a);
4471 st->add_color(Color(1.0, 1.0, 0.8, 0.4));
4472 st->add_vertex(a.linear_interpolate(b, 0.2));
4473
4474 st->add_color(Color(1.0, 1.0, 0.8, 0.4));
4475 st->add_vertex(a.linear_interpolate(b, 0.8));
4476 st->add_color(Color(1.0, 1.0, 0.8, 0.8));
4477 st->add_vertex(b);
4478 }
4479
4480 Ref<SpatialMaterial> mat = memnew(SpatialMaterial);
4481 mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
4482 mat->set_albedo(Color(1, 1, 1));
4483 mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
4484 mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
4485 mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
4486 st->set_material(mat);
4487 selection_box = st->commit();
4488 }
4489
get_state() const4490 Dictionary SpatialEditor::get_state() const {
4491
4492 Dictionary d;
4493
4494 d["snap_enabled"] = snap_enabled;
4495 d["translate_snap"] = get_translate_snap();
4496 d["rotate_snap"] = get_rotate_snap();
4497 d["scale_snap"] = get_scale_snap();
4498
4499 d["local_coords"] = tool_option_button[TOOL_OPT_LOCAL_COORDS]->is_pressed();
4500
4501 int vc = 0;
4502 if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT)))
4503 vc = 1;
4504 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS)))
4505 vc = 2;
4506 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS)))
4507 vc = 3;
4508 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS)))
4509 vc = 4;
4510 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT)))
4511 vc = 5;
4512 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT)))
4513 vc = 6;
4514
4515 d["viewport_mode"] = vc;
4516 Array vpdata;
4517 for (int i = 0; i < 4; i++) {
4518 vpdata.push_back(viewports[i]->get_state());
4519 }
4520
4521 d["viewports"] = vpdata;
4522
4523 d["show_grid"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID));
4524 d["show_origin"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN));
4525 d["fov"] = get_fov();
4526 d["znear"] = get_znear();
4527 d["zfar"] = get_zfar();
4528
4529 Dictionary gizmos_status;
4530 for (int i = 0; i < gizmo_plugins_by_name.size(); i++) {
4531 if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue;
4532 int state = gizmos_menu->get_item_state(gizmos_menu->get_item_index(i));
4533 String name = gizmo_plugins_by_name[i]->get_name();
4534 gizmos_status[name] = state;
4535 }
4536
4537 d["gizmos_status"] = gizmos_status;
4538
4539 return d;
4540 }
set_state(const Dictionary & p_state)4541 void SpatialEditor::set_state(const Dictionary &p_state) {
4542
4543 Dictionary d = p_state;
4544
4545 if (d.has("snap_enabled")) {
4546 snap_enabled = d["snap_enabled"];
4547 tool_option_button[TOOL_OPT_USE_SNAP]->set_pressed(d["snap_enabled"]);
4548 }
4549
4550 if (d.has("translate_snap"))
4551 snap_translate_value = d["translate_snap"];
4552
4553 if (d.has("rotate_snap"))
4554 snap_rotate_value = d["rotate_snap"];
4555
4556 if (d.has("scale_snap"))
4557 snap_scale_value = d["scale_snap"];
4558
4559 _snap_update();
4560
4561 if (d.has("local_coords")) {
4562 tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_pressed(d["local_coords"]);
4563 update_transform_gizmo();
4564 }
4565
4566 if (d.has("viewport_mode")) {
4567 int vc = d["viewport_mode"];
4568
4569 if (vc == 1)
4570 _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT);
4571 else if (vc == 2)
4572 _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS);
4573 else if (vc == 3)
4574 _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS);
4575 else if (vc == 4)
4576 _menu_item_pressed(MENU_VIEW_USE_4_VIEWPORTS);
4577 else if (vc == 5)
4578 _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS_ALT);
4579 else if (vc == 6)
4580 _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS_ALT);
4581 }
4582
4583 if (d.has("viewports")) {
4584 Array vp = d["viewports"];
4585 uint32_t vp_size = static_cast<uint32_t>(vp.size());
4586 if (vp_size > VIEWPORTS_COUNT) {
4587 WARN_PRINT("Ignoring superfluous viewport settings from spatial editor state.");
4588 vp_size = VIEWPORTS_COUNT;
4589 }
4590
4591 for (uint32_t i = 0; i < vp_size; i++) {
4592 viewports[i]->set_state(vp[i]);
4593 }
4594 }
4595
4596 if (d.has("zfar"))
4597 settings_zfar->set_value(float(d["zfar"]));
4598 if (d.has("znear"))
4599 settings_znear->set_value(float(d["znear"]));
4600 if (d.has("fov"))
4601 settings_fov->set_value(float(d["fov"]));
4602 if (d.has("show_grid")) {
4603 bool use = d["show_grid"];
4604
4605 if (use != view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID))) {
4606 _menu_item_pressed(MENU_VIEW_GRID);
4607 }
4608 }
4609
4610 if (d.has("show_origin")) {
4611 bool use = d["show_origin"];
4612
4613 if (use != view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN))) {
4614 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN), use);
4615 VisualServer::get_singleton()->instance_set_visible(origin_instance, use);
4616 }
4617 }
4618
4619 if (d.has("gizmos_status")) {
4620 Dictionary gizmos_status = d["gizmos_status"];
4621 List<Variant> keys;
4622 gizmos_status.get_key_list(&keys);
4623
4624 for (int j = 0; j < gizmo_plugins_by_name.size(); ++j) {
4625 if (!gizmo_plugins_by_name[j]->can_be_hidden()) continue;
4626 int state = EditorSpatialGizmoPlugin::VISIBLE;
4627 for (int i = 0; i < keys.size(); i++) {
4628 if (gizmo_plugins_by_name.write[j]->get_name() == keys[i]) {
4629 state = gizmos_status[keys[i]];
4630 break;
4631 }
4632 }
4633
4634 gizmo_plugins_by_name.write[j]->set_state(state);
4635 }
4636 _update_gizmos_menu();
4637 }
4638 }
4639
edit(Spatial * p_spatial)4640 void SpatialEditor::edit(Spatial *p_spatial) {
4641
4642 if (p_spatial != selected) {
4643 if (selected) {
4644
4645 Ref<EditorSpatialGizmo> seg = selected->get_gizmo();
4646 if (seg.is_valid()) {
4647 seg->set_selected(false);
4648 selected->update_gizmo();
4649 }
4650 }
4651
4652 selected = p_spatial;
4653 over_gizmo_handle = -1;
4654
4655 if (selected) {
4656
4657 Ref<EditorSpatialGizmo> seg = selected->get_gizmo();
4658 if (seg.is_valid()) {
4659 seg->set_selected(true);
4660 selected->update_gizmo();
4661 }
4662 }
4663 }
4664 }
4665
_snap_changed()4666 void SpatialEditor::_snap_changed() {
4667
4668 snap_translate_value = snap_translate->get_text().to_double();
4669 snap_rotate_value = snap_rotate->get_text().to_double();
4670 snap_scale_value = snap_scale->get_text().to_double();
4671 }
4672
_snap_update()4673 void SpatialEditor::_snap_update() {
4674
4675 snap_translate->set_text(String::num(snap_translate_value));
4676 snap_rotate->set_text(String::num(snap_rotate_value));
4677 snap_scale->set_text(String::num(snap_scale_value));
4678 }
4679
_xform_dialog_action()4680 void SpatialEditor::_xform_dialog_action() {
4681
4682 Transform t;
4683 //translation
4684 Vector3 scale;
4685 Vector3 rotate;
4686 Vector3 translate;
4687
4688 for (int i = 0; i < 3; i++) {
4689 translate[i] = xform_translate[i]->get_text().to_double();
4690 rotate[i] = Math::deg2rad(xform_rotate[i]->get_text().to_double());
4691 scale[i] = xform_scale[i]->get_text().to_double();
4692 }
4693
4694 t.basis.scale(scale);
4695 t.basis.rotate(rotate);
4696 t.origin = translate;
4697
4698 undo_redo->create_action(TTR("XForm Dialog"));
4699
4700 List<Node *> &selection = editor_selection->get_selected_node_list();
4701
4702 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
4703
4704 Spatial *sp = Object::cast_to<Spatial>(E->get());
4705 if (!sp)
4706 continue;
4707
4708 SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data<SpatialEditorSelectedItem>(sp);
4709 if (!se)
4710 continue;
4711
4712 bool post = xform_type->get_selected() > 0;
4713
4714 Transform tr = sp->get_global_gizmo_transform();
4715 if (post)
4716 tr = tr * t;
4717 else {
4718
4719 tr.basis = t.basis * tr.basis;
4720 tr.origin += t.origin;
4721 }
4722
4723 undo_redo->add_do_method(sp, "set_global_transform", tr);
4724 undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform());
4725 }
4726 undo_redo->commit_action();
4727 }
4728
_menu_item_toggled(bool pressed,int p_option)4729 void SpatialEditor::_menu_item_toggled(bool pressed, int p_option) {
4730
4731 switch (p_option) {
4732 case MENU_TOOL_LOCAL_COORDS: {
4733
4734 tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_pressed(pressed);
4735 update_transform_gizmo();
4736 } break;
4737
4738 case MENU_TOOL_USE_SNAP: {
4739 tool_option_button[TOOL_OPT_USE_SNAP]->set_pressed(pressed);
4740 snap_enabled = pressed;
4741 } break;
4742
4743 case MENU_TOOL_OVERRIDE_CAMERA: {
4744 ScriptEditorDebugger *const debugger = ScriptEditor::get_singleton()->get_debugger();
4745
4746 if (pressed) {
4747 using Override = ScriptEditorDebugger::CameraOverride;
4748
4749 debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id));
4750 } else {
4751 debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
4752 }
4753
4754 } break;
4755 }
4756 }
4757
_menu_gizmo_toggled(int p_option)4758 void SpatialEditor::_menu_gizmo_toggled(int p_option) {
4759
4760 const int idx = gizmos_menu->get_item_index(p_option);
4761 gizmos_menu->toggle_item_multistate(idx);
4762
4763 // Change icon
4764 const int state = gizmos_menu->get_item_state(idx);
4765 switch (state) {
4766 case EditorSpatialGizmoPlugin::VISIBLE:
4767 gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_visible"));
4768 break;
4769 case EditorSpatialGizmoPlugin::ON_TOP:
4770 gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_xray"));
4771 break;
4772 case EditorSpatialGizmoPlugin::HIDDEN:
4773 gizmos_menu->set_item_icon(idx, view_menu->get_popup()->get_icon("visibility_hidden"));
4774 break;
4775 }
4776
4777 gizmo_plugins_by_name.write[p_option]->set_state(state);
4778
4779 update_all_gizmos();
4780 }
4781
_update_camera_override_button(bool p_game_running)4782 void SpatialEditor::_update_camera_override_button(bool p_game_running) {
4783 Button *const button = tool_option_button[TOOL_OPT_OVERRIDE_CAMERA];
4784
4785 if (p_game_running) {
4786 button->set_disabled(false);
4787 button->set_tooltip(TTR("Game Camera Override\nNo game instance running."));
4788 } else {
4789 button->set_disabled(true);
4790 button->set_pressed(false);
4791 button->set_tooltip(TTR("Game Camera Override\nOverrides game camera with editor viewport camera."));
4792 }
4793 }
4794
_update_camera_override_viewport(Object * p_viewport)4795 void SpatialEditor::_update_camera_override_viewport(Object *p_viewport) {
4796 SpatialEditorViewport *current_viewport = Object::cast_to<SpatialEditorViewport>(p_viewport);
4797
4798 if (!current_viewport)
4799 return;
4800
4801 ScriptEditorDebugger *const debugger = ScriptEditor::get_singleton()->get_debugger();
4802
4803 camera_override_viewport_id = current_viewport->index;
4804 if (debugger->get_camera_override() >= ScriptEditorDebugger::OVERRIDE_3D_1) {
4805 using Override = ScriptEditorDebugger::CameraOverride;
4806
4807 debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id));
4808 }
4809 }
4810
_menu_item_pressed(int p_option)4811 void SpatialEditor::_menu_item_pressed(int p_option) {
4812
4813 switch (p_option) {
4814
4815 case MENU_TOOL_SELECT:
4816 case MENU_TOOL_MOVE:
4817 case MENU_TOOL_ROTATE:
4818 case MENU_TOOL_SCALE:
4819 case MENU_TOOL_LIST_SELECT: {
4820
4821 for (int i = 0; i < TOOL_MAX; i++)
4822 tool_button[i]->set_pressed(i == p_option);
4823 tool_mode = (ToolMode)p_option;
4824 update_transform_gizmo();
4825
4826 } break;
4827 case MENU_TRANSFORM_CONFIGURE_SNAP: {
4828
4829 snap_dialog->popup_centered(Size2(200, 180));
4830 } break;
4831 case MENU_TRANSFORM_DIALOG: {
4832
4833 for (int i = 0; i < 3; i++) {
4834
4835 xform_translate[i]->set_text("0");
4836 xform_rotate[i]->set_text("0");
4837 xform_scale[i]->set_text("1");
4838 }
4839
4840 xform_dialog->popup_centered(Size2(320, 240) * EDSCALE);
4841
4842 } break;
4843 case MENU_VIEW_USE_1_VIEWPORT: {
4844
4845 viewport_base->set_view(SpatialEditorViewportContainer::VIEW_USE_1_VIEWPORT);
4846
4847 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), true);
4848 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false);
4849 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false);
4850 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false);
4851 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false);
4852 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false);
4853
4854 } break;
4855 case MENU_VIEW_USE_2_VIEWPORTS: {
4856
4857 viewport_base->set_view(SpatialEditorViewportContainer::VIEW_USE_2_VIEWPORTS);
4858
4859 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false);
4860 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), true);
4861 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false);
4862 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false);
4863 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false);
4864 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false);
4865
4866 } break;
4867 case MENU_VIEW_USE_2_VIEWPORTS_ALT: {
4868
4869 viewport_base->set_view(SpatialEditorViewportContainer::VIEW_USE_2_VIEWPORTS_ALT);
4870
4871 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false);
4872 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false);
4873 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false);
4874 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false);
4875 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), true);
4876 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false);
4877
4878 } break;
4879 case MENU_VIEW_USE_3_VIEWPORTS: {
4880
4881 viewport_base->set_view(SpatialEditorViewportContainer::VIEW_USE_3_VIEWPORTS);
4882
4883 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false);
4884 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false);
4885 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), true);
4886 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false);
4887 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false);
4888 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false);
4889
4890 } break;
4891 case MENU_VIEW_USE_3_VIEWPORTS_ALT: {
4892
4893 viewport_base->set_view(SpatialEditorViewportContainer::VIEW_USE_3_VIEWPORTS_ALT);
4894
4895 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false);
4896 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false);
4897 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false);
4898 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), false);
4899 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false);
4900 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), true);
4901
4902 } break;
4903 case MENU_VIEW_USE_4_VIEWPORTS: {
4904
4905 viewport_base->set_view(SpatialEditorViewportContainer::VIEW_USE_4_VIEWPORTS);
4906
4907 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false);
4908 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false);
4909 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), false);
4910 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), true);
4911 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), false);
4912 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), false);
4913
4914 } break;
4915 case MENU_VIEW_ORIGIN: {
4916
4917 bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option));
4918
4919 origin_enabled = !is_checked;
4920 VisualServer::get_singleton()->instance_set_visible(origin_instance, origin_enabled);
4921 // Update the grid since its appearance depends on whether the origin is enabled
4922 _finish_grid();
4923 _init_grid();
4924
4925 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), origin_enabled);
4926 } break;
4927 case MENU_VIEW_GRID: {
4928
4929 bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option));
4930
4931 grid_enabled = !is_checked;
4932
4933 for (int i = 0; i < 3; ++i) {
4934 if (grid_enable[i]) {
4935 VisualServer::get_singleton()->instance_set_visible(grid_instance[i], grid_enabled);
4936 grid_visible[i] = grid_enabled;
4937 }
4938 }
4939
4940 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), grid_enabled);
4941
4942 } break;
4943 case MENU_VIEW_CAMERA_SETTINGS: {
4944
4945 settings_dialog->popup_centered(settings_vbc->get_combined_minimum_size() + Size2(50, 50));
4946 } break;
4947 case MENU_SNAP_TO_FLOOR: {
4948 snap_selected_nodes_to_floor();
4949 } break;
4950 case MENU_LOCK_SELECTED: {
4951 undo_redo->create_action(TTR("Lock Selected"));
4952
4953 List<Node *> &selection = editor_selection->get_selected_node_list();
4954
4955 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
4956
4957 Spatial *spatial = Object::cast_to<Spatial>(E->get());
4958 if (!spatial || !spatial->is_visible_in_tree())
4959 continue;
4960
4961 if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root())
4962 continue;
4963
4964 undo_redo->add_do_method(spatial, "set_meta", "_edit_lock_", true);
4965 undo_redo->add_undo_method(spatial, "remove_meta", "_edit_lock_");
4966 undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed");
4967 undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed");
4968 }
4969
4970 undo_redo->add_do_method(this, "_refresh_menu_icons", Variant());
4971 undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant());
4972 undo_redo->commit_action();
4973 } break;
4974 case MENU_UNLOCK_SELECTED: {
4975 undo_redo->create_action(TTR("Unlock Selected"));
4976
4977 List<Node *> &selection = editor_selection->get_selected_node_list();
4978
4979 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
4980
4981 Spatial *spatial = Object::cast_to<Spatial>(E->get());
4982 if (!spatial || !spatial->is_visible_in_tree())
4983 continue;
4984
4985 if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root())
4986 continue;
4987
4988 undo_redo->add_do_method(spatial, "remove_meta", "_edit_lock_");
4989 undo_redo->add_undo_method(spatial, "set_meta", "_edit_lock_", true);
4990 undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed");
4991 undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed");
4992 }
4993
4994 undo_redo->add_do_method(this, "_refresh_menu_icons", Variant());
4995 undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant());
4996 undo_redo->commit_action();
4997 } break;
4998 case MENU_GROUP_SELECTED: {
4999 undo_redo->create_action(TTR("Group Selected"));
5000
5001 List<Node *> &selection = editor_selection->get_selected_node_list();
5002
5003 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
5004
5005 Spatial *spatial = Object::cast_to<Spatial>(E->get());
5006 if (!spatial || !spatial->is_visible_in_tree())
5007 continue;
5008
5009 if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root())
5010 continue;
5011
5012 undo_redo->add_do_method(spatial, "set_meta", "_edit_group_", true);
5013 undo_redo->add_undo_method(spatial, "remove_meta", "_edit_group_");
5014 undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed");
5015 undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed");
5016 }
5017
5018 undo_redo->add_do_method(this, "_refresh_menu_icons", Variant());
5019 undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant());
5020 undo_redo->commit_action();
5021 } break;
5022 case MENU_UNGROUP_SELECTED: {
5023 undo_redo->create_action(TTR("Ungroup Selected"));
5024 List<Node *> &selection = editor_selection->get_selected_node_list();
5025
5026 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
5027
5028 Spatial *spatial = Object::cast_to<Spatial>(E->get());
5029 if (!spatial || !spatial->is_visible_in_tree())
5030 continue;
5031
5032 if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root())
5033 continue;
5034
5035 undo_redo->add_do_method(spatial, "remove_meta", "_edit_group_");
5036 undo_redo->add_undo_method(spatial, "set_meta", "_edit_group_", true);
5037 undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed");
5038 undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed");
5039 }
5040
5041 undo_redo->add_do_method(this, "_refresh_menu_icons", Variant());
5042 undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant());
5043 undo_redo->commit_action();
5044 } break;
5045 }
5046 }
5047
_init_indicators()5048 void SpatialEditor::_init_indicators() {
5049
5050 {
5051 origin_enabled = true;
5052 grid_enabled = true;
5053
5054 indicator_mat.instance();
5055 indicator_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
5056 indicator_mat->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
5057 indicator_mat->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
5058
5059 Vector<Color> origin_colors;
5060 Vector<Vector3> origin_points;
5061
5062 for (int i = 0; i < 3; i++) {
5063 Vector3 axis;
5064 axis[i] = 1;
5065 Color origin_color;
5066 switch (i) {
5067 case 0:
5068 origin_color = get_color("axis_x_color", "Editor");
5069 break;
5070 case 1:
5071 origin_color = get_color("axis_y_color", "Editor");
5072 break;
5073 case 2:
5074 origin_color = get_color("axis_z_color", "Editor");
5075 break;
5076 default:
5077 origin_color = Color();
5078 break;
5079 }
5080
5081 grid_enable[i] = false;
5082 grid_visible[i] = false;
5083
5084 origin_colors.push_back(origin_color);
5085 origin_colors.push_back(origin_color);
5086 origin_points.push_back(axis * 4096);
5087 origin_points.push_back(axis * -4096);
5088 }
5089
5090 grid_enable[1] = true;
5091 grid_visible[1] = true;
5092
5093 _init_grid();
5094
5095 origin = VisualServer::get_singleton()->mesh_create();
5096 Array d;
5097 d.resize(VS::ARRAY_MAX);
5098 d[VisualServer::ARRAY_VERTEX] = origin_points;
5099 d[VisualServer::ARRAY_COLOR] = origin_colors;
5100
5101 VisualServer::get_singleton()->mesh_add_surface_from_arrays(origin, VisualServer::PRIMITIVE_LINES, d);
5102 VisualServer::get_singleton()->mesh_surface_set_material(origin, 0, indicator_mat->get_rid());
5103
5104 origin_instance = VisualServer::get_singleton()->instance_create2(origin, get_tree()->get_root()->get_world()->get_scenario());
5105 VS::get_singleton()->instance_set_layer_mask(origin_instance, 1 << SpatialEditorViewport::GIZMO_GRID_LAYER);
5106
5107 VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(origin_instance, VS::SHADOW_CASTING_SETTING_OFF);
5108 }
5109
5110 {
5111
5112 //move gizmo
5113
5114 for (int i = 0; i < 3; i++) {
5115
5116 Color col;
5117 switch (i) {
5118 case 0:
5119 col = get_color("axis_x_color", "Editor");
5120 break;
5121 case 1:
5122 col = get_color("axis_y_color", "Editor");
5123 break;
5124 case 2:
5125 col = get_color("axis_z_color", "Editor");
5126 break;
5127 default:
5128 col = Color();
5129 break;
5130 }
5131
5132 col.a = EditorSettings::get_singleton()->get("editors/3d/manipulator_gizmo_opacity");
5133
5134 move_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
5135 move_plane_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
5136 rotate_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
5137 scale_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
5138 scale_plane_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
5139
5140 Ref<SpatialMaterial> mat = memnew(SpatialMaterial);
5141 mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
5142 mat->set_on_top_of_alpha();
5143 mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
5144 mat->set_albedo(col);
5145 gizmo_color[i] = mat;
5146
5147 Ref<SpatialMaterial> mat_hl = mat->duplicate();
5148 mat_hl->set_albedo(Color(col.r, col.g, col.b, 1.0));
5149 gizmo_color_hl[i] = mat_hl;
5150
5151 Vector3 ivec;
5152 ivec[i] = 1;
5153 Vector3 nivec;
5154 nivec[(i + 1) % 3] = 1;
5155 nivec[(i + 2) % 3] = 1;
5156 Vector3 ivec2;
5157 ivec2[(i + 1) % 3] = 1;
5158 Vector3 ivec3;
5159 ivec3[(i + 2) % 3] = 1;
5160
5161 //translate
5162 {
5163
5164 Ref<SurfaceTool> surftool = memnew(SurfaceTool);
5165 surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
5166
5167 // Arrow profile
5168 const int arrow_points = 5;
5169 Vector3 arrow[5] = {
5170 nivec * 0.0 + ivec * 0.0,
5171 nivec * 0.01 + ivec * 0.0,
5172 nivec * 0.01 + ivec * GIZMO_ARROW_OFFSET,
5173 nivec * 0.065 + ivec * GIZMO_ARROW_OFFSET,
5174 nivec * 0.0 + ivec * (GIZMO_ARROW_OFFSET + GIZMO_ARROW_SIZE),
5175 };
5176
5177 int arrow_sides = 16;
5178
5179 for (int k = 0; k < arrow_sides; k++) {
5180
5181 Basis ma(ivec, Math_PI * 2 * float(k) / arrow_sides);
5182 Basis mb(ivec, Math_PI * 2 * float(k + 1) / arrow_sides);
5183
5184 for (int j = 0; j < arrow_points - 1; j++) {
5185
5186 Vector3 points[4] = {
5187 ma.xform(arrow[j]),
5188 mb.xform(arrow[j]),
5189 mb.xform(arrow[j + 1]),
5190 ma.xform(arrow[j + 1]),
5191 };
5192 surftool->add_vertex(points[0]);
5193 surftool->add_vertex(points[1]);
5194 surftool->add_vertex(points[2]);
5195
5196 surftool->add_vertex(points[0]);
5197 surftool->add_vertex(points[2]);
5198 surftool->add_vertex(points[3]);
5199 }
5200 }
5201
5202 surftool->set_material(mat);
5203 surftool->commit(move_gizmo[i]);
5204 }
5205
5206 // Plane Translation
5207 {
5208 Ref<SurfaceTool> surftool = memnew(SurfaceTool);
5209 surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
5210
5211 Vector3 vec = ivec2 - ivec3;
5212 Vector3 plane[4] = {
5213 vec * GIZMO_PLANE_DST,
5214 vec * GIZMO_PLANE_DST + ivec2 * GIZMO_PLANE_SIZE,
5215 vec * (GIZMO_PLANE_DST + GIZMO_PLANE_SIZE),
5216 vec * GIZMO_PLANE_DST - ivec3 * GIZMO_PLANE_SIZE
5217 };
5218
5219 Basis ma(ivec, Math_PI / 2);
5220
5221 Vector3 points[4] = {
5222 ma.xform(plane[0]),
5223 ma.xform(plane[1]),
5224 ma.xform(plane[2]),
5225 ma.xform(plane[3]),
5226 };
5227 surftool->add_vertex(points[0]);
5228 surftool->add_vertex(points[1]);
5229 surftool->add_vertex(points[2]);
5230
5231 surftool->add_vertex(points[0]);
5232 surftool->add_vertex(points[2]);
5233 surftool->add_vertex(points[3]);
5234
5235 Ref<SpatialMaterial> plane_mat = memnew(SpatialMaterial);
5236 plane_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
5237 plane_mat->set_on_top_of_alpha();
5238 plane_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
5239 plane_mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
5240 plane_mat->set_albedo(col);
5241 plane_gizmo_color[i] = plane_mat; // needed, so we can draw planes from both sides
5242 surftool->set_material(plane_mat);
5243 surftool->commit(move_plane_gizmo[i]);
5244
5245 Ref<SpatialMaterial> plane_mat_hl = plane_mat->duplicate();
5246 plane_mat_hl->set_albedo(Color(col.r, col.g, col.b, 1.0));
5247 plane_gizmo_color_hl[i] = plane_mat_hl; // needed, so we can draw planes from both sides
5248 }
5249
5250 // Rotate
5251 {
5252
5253 Ref<SurfaceTool> surftool = memnew(SurfaceTool);
5254 surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
5255
5256 Vector3 circle[5] = {
5257 ivec * 0.02 + ivec2 * 0.02 + ivec2 * GIZMO_CIRCLE_SIZE,
5258 ivec * -0.02 + ivec2 * 0.02 + ivec2 * GIZMO_CIRCLE_SIZE,
5259 ivec * -0.02 + ivec2 * -0.02 + ivec2 * GIZMO_CIRCLE_SIZE,
5260 ivec * 0.02 + ivec2 * -0.02 + ivec2 * GIZMO_CIRCLE_SIZE,
5261 ivec * 0.02 + ivec2 * 0.02 + ivec2 * GIZMO_CIRCLE_SIZE,
5262 };
5263
5264 for (int k = 0; k < 64; k++) {
5265
5266 Basis ma(ivec, Math_PI * 2 * float(k) / 64);
5267 Basis mb(ivec, Math_PI * 2 * float(k + 1) / 64);
5268
5269 for (int j = 0; j < 4; j++) {
5270
5271 Vector3 points[4] = {
5272 ma.xform(circle[j]),
5273 mb.xform(circle[j]),
5274 mb.xform(circle[j + 1]),
5275 ma.xform(circle[j + 1]),
5276 };
5277 surftool->add_vertex(points[0]);
5278 surftool->add_vertex(points[1]);
5279 surftool->add_vertex(points[2]);
5280
5281 surftool->add_vertex(points[0]);
5282 surftool->add_vertex(points[2]);
5283 surftool->add_vertex(points[3]);
5284 }
5285 }
5286
5287 surftool->set_material(mat);
5288 surftool->commit(rotate_gizmo[i]);
5289 }
5290
5291 // Scale
5292 {
5293 Ref<SurfaceTool> surftool = memnew(SurfaceTool);
5294 surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
5295
5296 // Cube arrow profile
5297 const int arrow_points = 6;
5298 Vector3 arrow[6] = {
5299 nivec * 0.0 + ivec * 0.0,
5300 nivec * 0.01 + ivec * 0.0,
5301 nivec * 0.01 + ivec * 1.0 * GIZMO_SCALE_OFFSET,
5302 nivec * 0.07 + ivec * 1.0 * GIZMO_SCALE_OFFSET,
5303 nivec * 0.07 + ivec * 1.11 * GIZMO_SCALE_OFFSET,
5304 nivec * 0.0 + ivec * 1.11 * GIZMO_SCALE_OFFSET,
5305 };
5306
5307 int arrow_sides = 4;
5308
5309 for (int k = 0; k < 4; k++) {
5310
5311 Basis ma(ivec, Math_PI * 2 * float(k) / arrow_sides);
5312 Basis mb(ivec, Math_PI * 2 * float(k + 1) / arrow_sides);
5313
5314 for (int j = 0; j < arrow_points - 1; j++) {
5315
5316 Vector3 points[4] = {
5317 ma.xform(arrow[j]),
5318 mb.xform(arrow[j]),
5319 mb.xform(arrow[j + 1]),
5320 ma.xform(arrow[j + 1]),
5321 };
5322 surftool->add_vertex(points[0]);
5323 surftool->add_vertex(points[1]);
5324 surftool->add_vertex(points[2]);
5325
5326 surftool->add_vertex(points[0]);
5327 surftool->add_vertex(points[2]);
5328 surftool->add_vertex(points[3]);
5329 }
5330 }
5331
5332 surftool->set_material(mat);
5333 surftool->commit(scale_gizmo[i]);
5334 }
5335
5336 // Plane Scale
5337 {
5338 Ref<SurfaceTool> surftool = memnew(SurfaceTool);
5339 surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
5340
5341 Vector3 vec = ivec2 - ivec3;
5342 Vector3 plane[4] = {
5343 vec * GIZMO_PLANE_DST,
5344 vec * GIZMO_PLANE_DST + ivec2 * GIZMO_PLANE_SIZE,
5345 vec * (GIZMO_PLANE_DST + GIZMO_PLANE_SIZE),
5346 vec * GIZMO_PLANE_DST - ivec3 * GIZMO_PLANE_SIZE
5347 };
5348
5349 Basis ma(ivec, Math_PI / 2);
5350
5351 Vector3 points[4] = {
5352 ma.xform(plane[0]),
5353 ma.xform(plane[1]),
5354 ma.xform(plane[2]),
5355 ma.xform(plane[3]),
5356 };
5357 surftool->add_vertex(points[0]);
5358 surftool->add_vertex(points[1]);
5359 surftool->add_vertex(points[2]);
5360
5361 surftool->add_vertex(points[0]);
5362 surftool->add_vertex(points[2]);
5363 surftool->add_vertex(points[3]);
5364
5365 Ref<SpatialMaterial> plane_mat = memnew(SpatialMaterial);
5366 plane_mat->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
5367 plane_mat->set_on_top_of_alpha();
5368 plane_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
5369 plane_mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
5370 plane_mat->set_albedo(col);
5371 plane_gizmo_color[i] = plane_mat; // needed, so we can draw planes from both sides
5372 surftool->set_material(plane_mat);
5373 surftool->commit(scale_plane_gizmo[i]);
5374
5375 Ref<SpatialMaterial> plane_mat_hl = plane_mat->duplicate();
5376 plane_mat_hl->set_albedo(Color(col.r, col.g, col.b, 1.0));
5377 plane_gizmo_color_hl[i] = plane_mat_hl; // needed, so we can draw planes from both sides
5378 }
5379 }
5380 }
5381
5382 _generate_selection_box();
5383 }
5384
_update_gizmos_menu()5385 void SpatialEditor::_update_gizmos_menu() {
5386
5387 gizmos_menu->clear();
5388
5389 for (int i = 0; i < gizmo_plugins_by_name.size(); ++i) {
5390 if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue;
5391 String plugin_name = gizmo_plugins_by_name[i]->get_name();
5392 const int plugin_state = gizmo_plugins_by_name[i]->get_state();
5393 gizmos_menu->add_multistate_item(TTR(plugin_name), 3, plugin_state, i);
5394 const int idx = gizmos_menu->get_item_index(i);
5395 gizmos_menu->set_item_tooltip(
5396 idx,
5397 TTR("Click to toggle between visibility states.\n\nOpen eye: Gizmo is visible.\nClosed eye: Gizmo is hidden.\nHalf-open eye: Gizmo is also visible through opaque surfaces (\"x-ray\")."));
5398 switch (plugin_state) {
5399 case EditorSpatialGizmoPlugin::VISIBLE:
5400 gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_visible"));
5401 break;
5402 case EditorSpatialGizmoPlugin::ON_TOP:
5403 gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_xray"));
5404 break;
5405 case EditorSpatialGizmoPlugin::HIDDEN:
5406 gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_hidden"));
5407 break;
5408 }
5409 }
5410 }
5411
_update_gizmos_menu_theme()5412 void SpatialEditor::_update_gizmos_menu_theme() {
5413 for (int i = 0; i < gizmo_plugins_by_name.size(); ++i) {
5414 if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue;
5415 const int plugin_state = gizmo_plugins_by_name[i]->get_state();
5416 const int idx = gizmos_menu->get_item_index(i);
5417 switch (plugin_state) {
5418 case EditorSpatialGizmoPlugin::VISIBLE:
5419 gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_visible"));
5420 break;
5421 case EditorSpatialGizmoPlugin::ON_TOP:
5422 gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_xray"));
5423 break;
5424 case EditorSpatialGizmoPlugin::HIDDEN:
5425 gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_hidden"));
5426 break;
5427 }
5428 }
5429 }
5430
_init_grid()5431 void SpatialEditor::_init_grid() {
5432
5433 PoolVector<Color> grid_colors[3];
5434 PoolVector<Vector3> grid_points[3];
5435
5436 Color primary_grid_color = EditorSettings::get_singleton()->get("editors/3d/primary_grid_color");
5437 Color secondary_grid_color = EditorSettings::get_singleton()->get("editors/3d/secondary_grid_color");
5438 int grid_size = EditorSettings::get_singleton()->get("editors/3d/grid_size");
5439 int primary_grid_steps = EditorSettings::get_singleton()->get("editors/3d/primary_grid_steps");
5440
5441 for (int i = 0; i < 3; i++) {
5442 Vector3 axis;
5443 axis[i] = 1;
5444 Vector3 axis_n1;
5445 axis_n1[(i + 1) % 3] = 1;
5446 Vector3 axis_n2;
5447 axis_n2[(i + 2) % 3] = 1;
5448
5449 for (int j = -grid_size; j <= grid_size; j++) {
5450 Vector3 p1 = axis_n1 * j + axis_n2 * -grid_size;
5451 Vector3 p1_dest = p1 * (-axis_n2 + axis_n1);
5452 Vector3 p2 = axis_n2 * j + axis_n1 * -grid_size;
5453 Vector3 p2_dest = p2 * (-axis_n1 + axis_n2);
5454
5455 Color line_color = secondary_grid_color;
5456 if (origin_enabled && j == 0) {
5457 // Don't draw the center lines of the grid if the origin is enabled
5458 // The origin would overlap the grid lines in this case, causing flickering
5459 continue;
5460 } else if (j % primary_grid_steps == 0) {
5461 line_color = primary_grid_color;
5462 }
5463
5464 grid_points[i].push_back(p1);
5465 grid_points[i].push_back(p1_dest);
5466 grid_colors[i].push_back(line_color);
5467 grid_colors[i].push_back(line_color);
5468
5469 grid_points[i].push_back(p2);
5470 grid_points[i].push_back(p2_dest);
5471 grid_colors[i].push_back(line_color);
5472 grid_colors[i].push_back(line_color);
5473 }
5474
5475 grid[i] = VisualServer::get_singleton()->mesh_create();
5476 Array d;
5477 d.resize(VS::ARRAY_MAX);
5478 d[VisualServer::ARRAY_VERTEX] = grid_points[i];
5479 d[VisualServer::ARRAY_COLOR] = grid_colors[i];
5480 VisualServer::get_singleton()->mesh_add_surface_from_arrays(grid[i], VisualServer::PRIMITIVE_LINES, d);
5481 VisualServer::get_singleton()->mesh_surface_set_material(grid[i], 0, indicator_mat->get_rid());
5482 grid_instance[i] = VisualServer::get_singleton()->instance_create2(grid[i], get_tree()->get_root()->get_world()->get_scenario());
5483
5484 VisualServer::get_singleton()->instance_set_visible(grid_instance[i], grid_visible[i]);
5485 VisualServer::get_singleton()->instance_geometry_set_cast_shadows_setting(grid_instance[i], VS::SHADOW_CASTING_SETTING_OFF);
5486 VS::get_singleton()->instance_set_layer_mask(grid_instance[i], 1 << SpatialEditorViewport::GIZMO_GRID_LAYER);
5487 }
5488 }
5489
_finish_indicators()5490 void SpatialEditor::_finish_indicators() {
5491
5492 VisualServer::get_singleton()->free(origin_instance);
5493 VisualServer::get_singleton()->free(origin);
5494
5495 _finish_grid();
5496 }
5497
_finish_grid()5498 void SpatialEditor::_finish_grid() {
5499 for (int i = 0; i < 3; i++) {
5500 VisualServer::get_singleton()->free(grid_instance[i]);
5501 VisualServer::get_singleton()->free(grid[i]);
5502 }
5503 }
5504
is_any_freelook_active() const5505 bool SpatialEditor::is_any_freelook_active() const {
5506 for (unsigned int i = 0; i < VIEWPORTS_COUNT; ++i) {
5507 if (viewports[i]->is_freelook_active())
5508 return true;
5509 }
5510 return false;
5511 }
5512
_refresh_menu_icons()5513 void SpatialEditor::_refresh_menu_icons() {
5514
5515 bool all_locked = true;
5516 bool all_grouped = true;
5517
5518 List<Node *> &selection = editor_selection->get_selected_node_list();
5519
5520 if (selection.empty()) {
5521 all_locked = false;
5522 all_grouped = false;
5523 } else {
5524 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
5525 if (Object::cast_to<Spatial>(E->get()) && !Object::cast_to<Spatial>(E->get())->has_meta("_edit_lock_")) {
5526 all_locked = false;
5527 break;
5528 }
5529 }
5530 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
5531 if (Object::cast_to<Spatial>(E->get()) && !Object::cast_to<Spatial>(E->get())->has_meta("_edit_group_")) {
5532 all_grouped = false;
5533 break;
5534 }
5535 }
5536 }
5537
5538 tool_button[TOOL_LOCK_SELECTED]->set_visible(!all_locked);
5539 tool_button[TOOL_LOCK_SELECTED]->set_disabled(selection.empty());
5540 tool_button[TOOL_UNLOCK_SELECTED]->set_visible(all_locked);
5541
5542 tool_button[TOOL_GROUP_SELECTED]->set_visible(!all_grouped);
5543 tool_button[TOOL_GROUP_SELECTED]->set_disabled(selection.empty());
5544 tool_button[TOOL_UNGROUP_SELECTED]->set_visible(all_grouped);
5545 }
5546
5547 template <typename T>
_get_child_nodes(Node * parent_node)5548 Set<T *> _get_child_nodes(Node *parent_node) {
5549 Set<T *> nodes = Set<T *>();
5550 T *node = Node::cast_to<T>(parent_node);
5551 if (node) {
5552 nodes.insert(node);
5553 }
5554
5555 for (int i = 0; i < parent_node->get_child_count(); i++) {
5556 Node *child_node = parent_node->get_child(i);
5557 Set<T *> child_nodes = _get_child_nodes<T>(child_node);
5558 for (typename Set<T *>::Element *I = child_nodes.front(); I; I = I->next()) {
5559 nodes.insert(I->get());
5560 }
5561 }
5562
5563 return nodes;
5564 }
5565
_get_physics_bodies_rid(Node * node)5566 Set<RID> _get_physics_bodies_rid(Node *node) {
5567 Set<RID> rids = Set<RID>();
5568 PhysicsBody *pb = Node::cast_to<PhysicsBody>(node);
5569 if (pb) {
5570 rids.insert(pb->get_rid());
5571 }
5572 Set<PhysicsBody *> child_nodes = _get_child_nodes<PhysicsBody>(node);
5573 for (Set<PhysicsBody *>::Element *I = child_nodes.front(); I; I = I->next()) {
5574 rids.insert(I->get()->get_rid());
5575 }
5576
5577 return rids;
5578 }
5579
snap_selected_nodes_to_floor()5580 void SpatialEditor::snap_selected_nodes_to_floor() {
5581 List<Node *> &selection = editor_selection->get_selected_node_list();
5582 Dictionary snap_data;
5583
5584 for (List<Node *>::Element *E = selection.front(); E; E = E->next()) {
5585 Spatial *sp = Object::cast_to<Spatial>(E->get());
5586 if (sp) {
5587 Vector3 from = Vector3();
5588 Vector3 position_offset = Vector3();
5589
5590 // Priorities for snapping to floor are CollisionShapes, VisualInstances and then origin
5591 Set<VisualInstance *> vi = _get_child_nodes<VisualInstance>(sp);
5592 Set<CollisionShape *> cs = _get_child_nodes<CollisionShape>(sp);
5593
5594 if (cs.size()) {
5595 AABB aabb = sp->get_global_transform().xform(cs.front()->get()->get_shape()->get_debug_mesh()->get_aabb());
5596 for (Set<CollisionShape *>::Element *I = cs.front(); I; I = I->next()) {
5597 aabb.merge_with(sp->get_global_transform().xform(I->get()->get_shape()->get_debug_mesh()->get_aabb()));
5598 }
5599 Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5);
5600 from = aabb.position + size;
5601 position_offset.y = from.y - sp->get_global_transform().origin.y;
5602 } else if (vi.size()) {
5603 AABB aabb = vi.front()->get()->get_transformed_aabb();
5604 for (Set<VisualInstance *>::Element *I = vi.front(); I; I = I->next()) {
5605 aabb.merge_with(I->get()->get_transformed_aabb());
5606 }
5607 Vector3 size = aabb.size * Vector3(0.5, 0.0, 0.5);
5608 from = aabb.position + size;
5609 position_offset.y = from.y - sp->get_global_transform().origin.y;
5610 } else {
5611 from = sp->get_global_transform().origin;
5612 }
5613
5614 // We add a bit of margin to the from position to avoid it from snapping
5615 // when the spatial is already on a floor and there's another floor under
5616 // it
5617 from = from + Vector3(0.0, 0.2, 0.0);
5618
5619 Dictionary d;
5620
5621 d["from"] = from;
5622 d["position_offset"] = position_offset;
5623 snap_data[sp] = d;
5624 }
5625 }
5626
5627 PhysicsDirectSpaceState *ss = get_tree()->get_root()->get_world()->get_direct_space_state();
5628 PhysicsDirectSpaceState::RayResult result;
5629
5630 Array keys = snap_data.keys();
5631
5632 // The maximum height an object can travel to be snapped
5633 const float max_snap_height = 20.0;
5634
5635 // Will be set to `true` if at least one node from the selection was successfully snapped
5636 bool snapped_to_floor = false;
5637
5638 if (keys.size()) {
5639 // For snapping to be performed, there must be solid geometry under at least one of the selected nodes.
5640 // We need to check this before snapping to register the undo/redo action only if needed.
5641 for (int i = 0; i < keys.size(); i++) {
5642 Node *node = keys[i];
5643 Spatial *sp = Object::cast_to<Spatial>(node);
5644 Dictionary d = snap_data[node];
5645 Vector3 from = d["from"];
5646 Vector3 to = from - Vector3(0.0, max_snap_height, 0.0);
5647 Set<RID> excluded = _get_physics_bodies_rid(sp);
5648
5649 if (ss->intersect_ray(from, to, result, excluded)) {
5650 snapped_to_floor = true;
5651 }
5652 }
5653
5654 if (snapped_to_floor) {
5655 undo_redo->create_action(TTR("Snap Nodes To Floor"));
5656
5657 // Perform snapping if at least one node can be snapped
5658 for (int i = 0; i < keys.size(); i++) {
5659 Node *node = keys[i];
5660 Spatial *sp = Object::cast_to<Spatial>(node);
5661 Dictionary d = snap_data[node];
5662 Vector3 from = d["from"];
5663 Vector3 to = from - Vector3(0.0, max_snap_height, 0.0);
5664 Set<RID> excluded = _get_physics_bodies_rid(sp);
5665
5666 if (ss->intersect_ray(from, to, result, excluded)) {
5667 Vector3 position_offset = d["position_offset"];
5668 Transform new_transform = sp->get_global_transform();
5669
5670 new_transform.origin.y = result.position.y;
5671 new_transform.origin = new_transform.origin - position_offset;
5672
5673 undo_redo->add_do_method(sp, "set_global_transform", new_transform);
5674 undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_transform());
5675 }
5676 }
5677
5678 undo_redo->commit_action();
5679 } else {
5680 EditorNode::get_singleton()->show_warning(TTR("Couldn't find a solid floor to snap the selection to."));
5681 }
5682 }
5683 }
5684
_unhandled_key_input(Ref<InputEvent> p_event)5685 void SpatialEditor::_unhandled_key_input(Ref<InputEvent> p_event) {
5686
5687 if (!is_visible_in_tree() || get_viewport()->gui_has_modal_stack())
5688 return;
5689
5690 snap_key_enabled = Input::get_singleton()->is_key_pressed(KEY_CONTROL);
5691 }
_notification(int p_what)5692 void SpatialEditor::_notification(int p_what) {
5693
5694 if (p_what == NOTIFICATION_READY) {
5695
5696 tool_button[SpatialEditor::TOOL_MODE_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons"));
5697 tool_button[SpatialEditor::TOOL_MODE_MOVE]->set_icon(get_icon("ToolMove", "EditorIcons"));
5698 tool_button[SpatialEditor::TOOL_MODE_ROTATE]->set_icon(get_icon("ToolRotate", "EditorIcons"));
5699 tool_button[SpatialEditor::TOOL_MODE_SCALE]->set_icon(get_icon("ToolScale", "EditorIcons"));
5700 tool_button[SpatialEditor::TOOL_MODE_LIST_SELECT]->set_icon(get_icon("ListSelect", "EditorIcons"));
5701 tool_button[SpatialEditor::TOOL_LOCK_SELECTED]->set_icon(get_icon("Lock", "EditorIcons"));
5702 tool_button[SpatialEditor::TOOL_UNLOCK_SELECTED]->set_icon(get_icon("Unlock", "EditorIcons"));
5703 tool_button[SpatialEditor::TOOL_GROUP_SELECTED]->set_icon(get_icon("Group", "EditorIcons"));
5704 tool_button[SpatialEditor::TOOL_UNGROUP_SELECTED]->set_icon(get_icon("Ungroup", "EditorIcons"));
5705
5706 tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_icon("Object", "EditorIcons"));
5707 tool_option_button[SpatialEditor::TOOL_OPT_USE_SNAP]->set_icon(get_icon("Snap", "EditorIcons"));
5708 tool_option_button[SpatialEditor::TOOL_OPT_OVERRIDE_CAMERA]->set_icon(get_icon("Camera", "EditorIcons"));
5709
5710 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_icon("Panels1", "EditorIcons"));
5711 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_icon("Panels2", "EditorIcons"));
5712 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), get_icon("Panels2Alt", "EditorIcons"));
5713 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_icon("Panels3", "EditorIcons"));
5714 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_icon("Panels3Alt", "EditorIcons"));
5715 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_icon("Panels4", "EditorIcons"));
5716
5717 _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT);
5718
5719 _refresh_menu_icons();
5720
5721 get_tree()->connect("node_removed", this, "_node_removed");
5722 EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->connect("node_changed", this, "_refresh_menu_icons");
5723 editor_selection->connect("selection_changed", this, "_refresh_menu_icons");
5724
5725 editor->connect("stop_pressed", this, "_update_camera_override_button", make_binds(false));
5726 editor->connect("play_pressed", this, "_update_camera_override_button", make_binds(true));
5727 } else if (p_what == NOTIFICATION_ENTER_TREE) {
5728
5729 _register_all_gizmos();
5730 _update_gizmos_menu();
5731 _init_indicators();
5732 } else if (p_what == NOTIFICATION_THEME_CHANGED) {
5733 _update_gizmos_menu_theme();
5734 } else if (p_what == NOTIFICATION_EXIT_TREE) {
5735
5736 _finish_indicators();
5737 } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
5738 tool_button[SpatialEditor::TOOL_MODE_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons"));
5739 tool_button[SpatialEditor::TOOL_MODE_MOVE]->set_icon(get_icon("ToolMove", "EditorIcons"));
5740 tool_button[SpatialEditor::TOOL_MODE_ROTATE]->set_icon(get_icon("ToolRotate", "EditorIcons"));
5741 tool_button[SpatialEditor::TOOL_MODE_SCALE]->set_icon(get_icon("ToolScale", "EditorIcons"));
5742 tool_button[SpatialEditor::TOOL_MODE_LIST_SELECT]->set_icon(get_icon("ListSelect", "EditorIcons"));
5743 tool_button[SpatialEditor::TOOL_LOCK_SELECTED]->set_icon(get_icon("Lock", "EditorIcons"));
5744 tool_button[SpatialEditor::TOOL_UNLOCK_SELECTED]->set_icon(get_icon("Unlock", "EditorIcons"));
5745 tool_button[SpatialEditor::TOOL_GROUP_SELECTED]->set_icon(get_icon("Group", "EditorIcons"));
5746 tool_button[SpatialEditor::TOOL_UNGROUP_SELECTED]->set_icon(get_icon("Ungroup", "EditorIcons"));
5747
5748 tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_icon("Object", "EditorIcons"));
5749 tool_option_button[SpatialEditor::TOOL_OPT_USE_SNAP]->set_icon(get_icon("Snap", "EditorIcons"));
5750
5751 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_icon("Panels1", "EditorIcons"));
5752 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_icon("Panels2", "EditorIcons"));
5753 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), get_icon("Panels2Alt", "EditorIcons"));
5754 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_icon("Panels3", "EditorIcons"));
5755 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_icon("Panels3Alt", "EditorIcons"));
5756 view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_icon("Panels4", "EditorIcons"));
5757
5758 // Update grid color by rebuilding grid.
5759 _finish_grid();
5760 _init_grid();
5761 } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
5762 if (!is_visible() && tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->is_pressed()) {
5763 ScriptEditorDebugger *debugger = ScriptEditor::get_singleton()->get_debugger();
5764
5765 debugger->set_camera_override(ScriptEditorDebugger::OVERRIDE_NONE);
5766 tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_pressed(false);
5767 }
5768 }
5769 }
5770
add_control_to_menu_panel(Control * p_control)5771 void SpatialEditor::add_control_to_menu_panel(Control *p_control) {
5772
5773 hbc_menu->add_child(p_control);
5774 }
5775
remove_control_from_menu_panel(Control * p_control)5776 void SpatialEditor::remove_control_from_menu_panel(Control *p_control) {
5777
5778 hbc_menu->remove_child(p_control);
5779 }
5780
set_can_preview(Camera * p_preview)5781 void SpatialEditor::set_can_preview(Camera *p_preview) {
5782
5783 for (int i = 0; i < 4; i++) {
5784 viewports[i]->set_can_preview(p_preview);
5785 }
5786 }
5787
get_shader_split()5788 VSplitContainer *SpatialEditor::get_shader_split() {
5789
5790 return shader_split;
5791 }
5792
get_palette_split()5793 HSplitContainer *SpatialEditor::get_palette_split() {
5794
5795 return palette_split;
5796 }
5797
_request_gizmo(Object * p_obj)5798 void SpatialEditor::_request_gizmo(Object *p_obj) {
5799
5800 Spatial *sp = Object::cast_to<Spatial>(p_obj);
5801 if (!sp)
5802 return;
5803 if (editor->get_edited_scene() && (sp == editor->get_edited_scene() || (sp->get_owner() && editor->get_edited_scene()->is_a_parent_of(sp)))) {
5804
5805 Ref<EditorSpatialGizmo> seg;
5806
5807 for (int i = 0; i < gizmo_plugins_by_priority.size(); ++i) {
5808 seg = gizmo_plugins_by_priority.write[i]->get_gizmo(sp);
5809
5810 if (seg.is_valid()) {
5811 sp->set_gizmo(seg);
5812
5813 if (sp == selected) {
5814 seg->set_selected(true);
5815 selected->update_gizmo();
5816 }
5817
5818 break;
5819 }
5820 }
5821 }
5822 }
5823
_toggle_maximize_view(Object * p_viewport)5824 void SpatialEditor::_toggle_maximize_view(Object *p_viewport) {
5825 if (!p_viewport) return;
5826 SpatialEditorViewport *current_viewport = Object::cast_to<SpatialEditorViewport>(p_viewport);
5827 if (!current_viewport) return;
5828
5829 int index = -1;
5830 bool maximized = false;
5831 for (int i = 0; i < 4; i++) {
5832 if (viewports[i] == current_viewport) {
5833 index = i;
5834 if (current_viewport->get_global_rect() == viewport_base->get_global_rect())
5835 maximized = true;
5836 break;
5837 }
5838 }
5839 if (index == -1) return;
5840
5841 if (!maximized) {
5842
5843 for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {
5844 if (i == (uint32_t)index)
5845 viewports[i]->set_anchors_and_margins_preset(Control::PRESET_WIDE);
5846 else
5847 viewports[i]->hide();
5848 }
5849 } else {
5850
5851 for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++)
5852 viewports[i]->show();
5853
5854 if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT)))
5855 _menu_item_pressed(MENU_VIEW_USE_1_VIEWPORT);
5856 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS)))
5857 _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS);
5858 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT)))
5859 _menu_item_pressed(MENU_VIEW_USE_2_VIEWPORTS_ALT);
5860 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS)))
5861 _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS);
5862 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT)))
5863 _menu_item_pressed(MENU_VIEW_USE_3_VIEWPORTS_ALT);
5864 else if (view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS)))
5865 _menu_item_pressed(MENU_VIEW_USE_4_VIEWPORTS);
5866 }
5867 }
5868
_node_removed(Node * p_node)5869 void SpatialEditor::_node_removed(Node *p_node) {
5870
5871 if (p_node == selected)
5872 selected = NULL;
5873 }
5874
_register_all_gizmos()5875 void SpatialEditor::_register_all_gizmos() {
5876 add_gizmo_plugin(Ref<CameraSpatialGizmoPlugin>(memnew(CameraSpatialGizmoPlugin)));
5877 add_gizmo_plugin(Ref<LightSpatialGizmoPlugin>(memnew(LightSpatialGizmoPlugin)));
5878 add_gizmo_plugin(Ref<AudioStreamPlayer3DSpatialGizmoPlugin>(memnew(AudioStreamPlayer3DSpatialGizmoPlugin)));
5879 add_gizmo_plugin(Ref<MeshInstanceSpatialGizmoPlugin>(memnew(MeshInstanceSpatialGizmoPlugin)));
5880 add_gizmo_plugin(Ref<SoftBodySpatialGizmoPlugin>(memnew(SoftBodySpatialGizmoPlugin)));
5881 add_gizmo_plugin(Ref<Sprite3DSpatialGizmoPlugin>(memnew(Sprite3DSpatialGizmoPlugin)));
5882 add_gizmo_plugin(Ref<SkeletonSpatialGizmoPlugin>(memnew(SkeletonSpatialGizmoPlugin)));
5883 add_gizmo_plugin(Ref<Position3DSpatialGizmoPlugin>(memnew(Position3DSpatialGizmoPlugin)));
5884 add_gizmo_plugin(Ref<RayCastSpatialGizmoPlugin>(memnew(RayCastSpatialGizmoPlugin)));
5885 add_gizmo_plugin(Ref<SpringArmSpatialGizmoPlugin>(memnew(SpringArmSpatialGizmoPlugin)));
5886 add_gizmo_plugin(Ref<VehicleWheelSpatialGizmoPlugin>(memnew(VehicleWheelSpatialGizmoPlugin)));
5887 add_gizmo_plugin(Ref<VisibilityNotifierGizmoPlugin>(memnew(VisibilityNotifierGizmoPlugin)));
5888 add_gizmo_plugin(Ref<ParticlesGizmoPlugin>(memnew(ParticlesGizmoPlugin)));
5889 add_gizmo_plugin(Ref<CPUParticlesGizmoPlugin>(memnew(CPUParticlesGizmoPlugin)));
5890 add_gizmo_plugin(Ref<ReflectionProbeGizmoPlugin>(memnew(ReflectionProbeGizmoPlugin)));
5891 add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin)));
5892 add_gizmo_plugin(Ref<BakedIndirectLightGizmoPlugin>(memnew(BakedIndirectLightGizmoPlugin)));
5893 add_gizmo_plugin(Ref<CollisionShapeSpatialGizmoPlugin>(memnew(CollisionShapeSpatialGizmoPlugin)));
5894 add_gizmo_plugin(Ref<CollisionPolygonSpatialGizmoPlugin>(memnew(CollisionPolygonSpatialGizmoPlugin)));
5895 add_gizmo_plugin(Ref<NavigationMeshSpatialGizmoPlugin>(memnew(NavigationMeshSpatialGizmoPlugin)));
5896 add_gizmo_plugin(Ref<JointSpatialGizmoPlugin>(memnew(JointSpatialGizmoPlugin)));
5897 add_gizmo_plugin(Ref<PhysicalBoneSpatialGizmoPlugin>(memnew(PhysicalBoneSpatialGizmoPlugin)));
5898 }
5899
_bind_methods()5900 void SpatialEditor::_bind_methods() {
5901
5902 ClassDB::bind_method("_unhandled_key_input", &SpatialEditor::_unhandled_key_input);
5903 ClassDB::bind_method("_node_removed", &SpatialEditor::_node_removed);
5904 ClassDB::bind_method("_menu_item_pressed", &SpatialEditor::_menu_item_pressed);
5905 ClassDB::bind_method("_menu_gizmo_toggled", &SpatialEditor::_menu_gizmo_toggled);
5906 ClassDB::bind_method("_menu_item_toggled", &SpatialEditor::_menu_item_toggled);
5907 ClassDB::bind_method("_xform_dialog_action", &SpatialEditor::_xform_dialog_action);
5908 ClassDB::bind_method("_get_editor_data", &SpatialEditor::_get_editor_data);
5909 ClassDB::bind_method("_request_gizmo", &SpatialEditor::_request_gizmo);
5910 ClassDB::bind_method("_toggle_maximize_view", &SpatialEditor::_toggle_maximize_view);
5911 ClassDB::bind_method("_refresh_menu_icons", &SpatialEditor::_refresh_menu_icons);
5912 ClassDB::bind_method("_update_camera_override_button", &SpatialEditor::_update_camera_override_button);
5913 ClassDB::bind_method("_update_camera_override_viewport", &SpatialEditor::_update_camera_override_viewport);
5914 ClassDB::bind_method("_snap_changed", &SpatialEditor::_snap_changed);
5915 ClassDB::bind_method("_snap_update", &SpatialEditor::_snap_update);
5916
5917 ADD_SIGNAL(MethodInfo("transform_key_request"));
5918 ADD_SIGNAL(MethodInfo("item_lock_status_changed"));
5919 ADD_SIGNAL(MethodInfo("item_group_status_changed"));
5920 }
5921
clear()5922 void SpatialEditor::clear() {
5923
5924 settings_fov->set_value(EDITOR_DEF("editors/3d/default_fov", 70.0));
5925 settings_znear->set_value(EDITOR_DEF("editors/3d/default_z_near", 0.05));
5926 settings_zfar->set_value(EDITOR_DEF("editors/3d/default_z_far", 1500.0));
5927
5928 for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {
5929 viewports[i]->reset();
5930 }
5931
5932 VisualServer::get_singleton()->instance_set_visible(origin_instance, true);
5933 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_ORIGIN), true);
5934 for (int i = 0; i < 3; ++i) {
5935 if (grid_enable[i]) {
5936 VisualServer::get_singleton()->instance_set_visible(grid_instance[i], true);
5937 grid_visible[i] = true;
5938 }
5939 }
5940
5941 for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {
5942
5943 viewports[i]->view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(SpatialEditorViewport::VIEW_AUDIO_LISTENER), i == 0);
5944 viewports[i]->viewport->set_as_audio_listener(i == 0);
5945 }
5946
5947 view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_GRID), true);
5948 }
5949
SpatialEditor(EditorNode * p_editor)5950 SpatialEditor::SpatialEditor(EditorNode *p_editor) {
5951
5952 gizmo.visible = true;
5953 gizmo.scale = 1.0;
5954
5955 viewport_environment = Ref<Environment>(memnew(Environment));
5956 undo_redo = p_editor->get_undo_redo();
5957 VBoxContainer *vbc = this;
5958
5959 custom_camera = NULL;
5960 singleton = this;
5961 editor = p_editor;
5962 editor_selection = editor->get_editor_selection();
5963 editor_selection->add_editor_plugin(this);
5964
5965 snap_enabled = false;
5966 snap_key_enabled = false;
5967 tool_mode = TOOL_MODE_SELECT;
5968
5969 camera_override_viewport_id = 0;
5970
5971 hbc_menu = memnew(HBoxContainer);
5972 vbc->add_child(hbc_menu);
5973
5974 Vector<Variant> button_binds;
5975 button_binds.resize(1);
5976 String sct;
5977
5978 tool_button[TOOL_MODE_SELECT] = memnew(ToolButton);
5979 hbc_menu->add_child(tool_button[TOOL_MODE_SELECT]);
5980 tool_button[TOOL_MODE_SELECT]->set_toggle_mode(true);
5981 tool_button[TOOL_MODE_SELECT]->set_flat(true);
5982 tool_button[TOOL_MODE_SELECT]->set_pressed(true);
5983 button_binds.write[0] = MENU_TOOL_SELECT;
5984 tool_button[TOOL_MODE_SELECT]->connect("pressed", this, "_menu_item_pressed", button_binds);
5985 tool_button[TOOL_MODE_SELECT]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTR("Select Mode"), KEY_Q));
5986 tool_button[TOOL_MODE_SELECT]->set_tooltip(keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate\nAlt+Drag: Move\nAlt+RMB: Depth list selection"));
5987
5988 hbc_menu->add_child(memnew(VSeparator));
5989
5990 tool_button[TOOL_MODE_MOVE] = memnew(ToolButton);
5991 hbc_menu->add_child(tool_button[TOOL_MODE_MOVE]);
5992 tool_button[TOOL_MODE_MOVE]->set_toggle_mode(true);
5993 tool_button[TOOL_MODE_MOVE]->set_flat(true);
5994 button_binds.write[0] = MENU_TOOL_MOVE;
5995 tool_button[TOOL_MODE_MOVE]->connect("pressed", this, "_menu_item_pressed", button_binds);
5996 tool_button[TOOL_MODE_MOVE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_move", TTR("Move Mode"), KEY_W));
5997
5998 tool_button[TOOL_MODE_ROTATE] = memnew(ToolButton);
5999 hbc_menu->add_child(tool_button[TOOL_MODE_ROTATE]);
6000 tool_button[TOOL_MODE_ROTATE]->set_toggle_mode(true);
6001 tool_button[TOOL_MODE_ROTATE]->set_flat(true);
6002 button_binds.write[0] = MENU_TOOL_ROTATE;
6003 tool_button[TOOL_MODE_ROTATE]->connect("pressed", this, "_menu_item_pressed", button_binds);
6004 tool_button[TOOL_MODE_ROTATE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_rotate", TTR("Rotate Mode"), KEY_E));
6005
6006 tool_button[TOOL_MODE_SCALE] = memnew(ToolButton);
6007 hbc_menu->add_child(tool_button[TOOL_MODE_SCALE]);
6008 tool_button[TOOL_MODE_SCALE]->set_toggle_mode(true);
6009 tool_button[TOOL_MODE_SCALE]->set_flat(true);
6010 button_binds.write[0] = MENU_TOOL_SCALE;
6011 tool_button[TOOL_MODE_SCALE]->connect("pressed", this, "_menu_item_pressed", button_binds);
6012 tool_button[TOOL_MODE_SCALE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_scale", TTR("Scale Mode"), KEY_R));
6013
6014 hbc_menu->add_child(memnew(VSeparator));
6015
6016 tool_button[TOOL_MODE_LIST_SELECT] = memnew(ToolButton);
6017 hbc_menu->add_child(tool_button[TOOL_MODE_LIST_SELECT]);
6018 tool_button[TOOL_MODE_LIST_SELECT]->set_toggle_mode(true);
6019 tool_button[TOOL_MODE_LIST_SELECT]->set_flat(true);
6020 button_binds.write[0] = MENU_TOOL_LIST_SELECT;
6021 tool_button[TOOL_MODE_LIST_SELECT]->connect("pressed", this, "_menu_item_pressed", button_binds);
6022 tool_button[TOOL_MODE_LIST_SELECT]->set_tooltip(TTR("Show a list of all objects at the position clicked\n(same as Alt+RMB in select mode)."));
6023
6024 tool_button[TOOL_LOCK_SELECTED] = memnew(ToolButton);
6025 hbc_menu->add_child(tool_button[TOOL_LOCK_SELECTED]);
6026 button_binds.write[0] = MENU_LOCK_SELECTED;
6027 tool_button[TOOL_LOCK_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds);
6028 tool_button[TOOL_LOCK_SELECTED]->set_tooltip(TTR("Lock the selected object in place (can't be moved)."));
6029
6030 tool_button[TOOL_UNLOCK_SELECTED] = memnew(ToolButton);
6031 hbc_menu->add_child(tool_button[TOOL_UNLOCK_SELECTED]);
6032 button_binds.write[0] = MENU_UNLOCK_SELECTED;
6033 tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds);
6034 tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip(TTR("Unlock the selected object (can be moved)."));
6035
6036 tool_button[TOOL_GROUP_SELECTED] = memnew(ToolButton);
6037 hbc_menu->add_child(tool_button[TOOL_GROUP_SELECTED]);
6038 button_binds.write[0] = MENU_GROUP_SELECTED;
6039 tool_button[TOOL_GROUP_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds);
6040 tool_button[TOOL_GROUP_SELECTED]->set_tooltip(TTR("Makes sure the object's children are not selectable."));
6041
6042 tool_button[TOOL_UNGROUP_SELECTED] = memnew(ToolButton);
6043 hbc_menu->add_child(tool_button[TOOL_UNGROUP_SELECTED]);
6044 button_binds.write[0] = MENU_UNGROUP_SELECTED;
6045 tool_button[TOOL_UNGROUP_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds);
6046 tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip(TTR("Restores the object's children's ability to be selected."));
6047
6048 hbc_menu->add_child(memnew(VSeparator));
6049
6050 tool_option_button[TOOL_OPT_LOCAL_COORDS] = memnew(ToolButton);
6051 hbc_menu->add_child(tool_option_button[TOOL_OPT_LOCAL_COORDS]);
6052 tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_toggle_mode(true);
6053 tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_flat(true);
6054 button_binds.write[0] = MENU_TOOL_LOCAL_COORDS;
6055 tool_option_button[TOOL_OPT_LOCAL_COORDS]->connect("toggled", this, "_menu_item_toggled", button_binds);
6056 tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_shortcut(ED_SHORTCUT("spatial_editor/local_coords", TTR("Use Local Space"), KEY_T));
6057
6058 tool_option_button[TOOL_OPT_USE_SNAP] = memnew(ToolButton);
6059 hbc_menu->add_child(tool_option_button[TOOL_OPT_USE_SNAP]);
6060 tool_option_button[TOOL_OPT_USE_SNAP]->set_toggle_mode(true);
6061 tool_option_button[TOOL_OPT_USE_SNAP]->set_flat(true);
6062 button_binds.write[0] = MENU_TOOL_USE_SNAP;
6063 tool_option_button[TOOL_OPT_USE_SNAP]->connect("toggled", this, "_menu_item_toggled", button_binds);
6064 tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut(ED_SHORTCUT("spatial_editor/snap", TTR("Use Snap"), KEY_Y));
6065
6066 hbc_menu->add_child(memnew(VSeparator));
6067
6068 tool_option_button[TOOL_OPT_OVERRIDE_CAMERA] = memnew(ToolButton);
6069 hbc_menu->add_child(tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]);
6070 tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_toggle_mode(true);
6071 tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_flat(true);
6072 tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_disabled(true);
6073 button_binds.write[0] = MENU_TOOL_OVERRIDE_CAMERA;
6074 tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->connect("toggled", this, "_menu_item_toggled", button_binds);
6075 _update_camera_override_button(false);
6076
6077 hbc_menu->add_child(memnew(VSeparator));
6078
6079 // Drag and drop support;
6080 preview_node = memnew(Spatial);
6081 preview_bounds = AABB();
6082
6083 ED_SHORTCUT("spatial_editor/bottom_view", TTR("Bottom View"), KEY_MASK_ALT + KEY_KP_7);
6084 ED_SHORTCUT("spatial_editor/top_view", TTR("Top View"), KEY_KP_7);
6085 ED_SHORTCUT("spatial_editor/rear_view", TTR("Rear View"), KEY_MASK_ALT + KEY_KP_1);
6086 ED_SHORTCUT("spatial_editor/front_view", TTR("Front View"), KEY_KP_1);
6087 ED_SHORTCUT("spatial_editor/left_view", TTR("Left View"), KEY_MASK_ALT + KEY_KP_3);
6088 ED_SHORTCUT("spatial_editor/right_view", TTR("Right View"), KEY_KP_3);
6089 ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal View"), KEY_KP_5);
6090 ED_SHORTCUT("spatial_editor/insert_anim_key", TTR("Insert Animation Key"), KEY_K);
6091 ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), KEY_O);
6092 ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), KEY_F);
6093 ED_SHORTCUT("spatial_editor/align_transform_with_view", TTR("Align Transform with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_M);
6094 ED_SHORTCUT("spatial_editor/align_rotation_with_view", TTR("Align Rotation with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_F);
6095 ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KEY_MASK_SHIFT + KEY_F);
6096
6097 PopupMenu *p;
6098
6099 transform_menu = memnew(MenuButton);
6100 transform_menu->set_text(TTR("Transform"));
6101 transform_menu->set_switch_on_hover(true);
6102 hbc_menu->add_child(transform_menu);
6103
6104 p = transform_menu->get_popup();
6105 p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap Object to Floor"), KEY_PAGEDOWN), MENU_SNAP_TO_FLOOR);
6106 p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG);
6107
6108 p->add_separator();
6109 p->add_shortcut(ED_SHORTCUT("spatial_editor/configure_snap", TTR("Configure Snap...")), MENU_TRANSFORM_CONFIGURE_SNAP);
6110
6111 p->connect("id_pressed", this, "_menu_item_pressed");
6112
6113 view_menu = memnew(MenuButton);
6114 view_menu->set_text(TTR("View"));
6115 view_menu->set_switch_on_hover(true);
6116 hbc_menu->add_child(view_menu);
6117
6118 p = view_menu->get_popup();
6119
6120 accept = memnew(AcceptDialog);
6121 editor->get_gui_base()->add_child(accept);
6122
6123 p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT);
6124 p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
6125 p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
6126 p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS);
6127 p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT);
6128 p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
6129 p->add_separator();
6130
6131 p->add_submenu_item(TTR("Gizmos"), "GizmosMenu");
6132
6133 p->add_separator();
6134 p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
6135 p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid")), MENU_VIEW_GRID);
6136
6137 p->add_separator();
6138 p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings...")), MENU_VIEW_CAMERA_SETTINGS);
6139
6140 p->set_item_checked(p->get_item_index(MENU_VIEW_ORIGIN), true);
6141 p->set_item_checked(p->get_item_index(MENU_VIEW_GRID), true);
6142
6143 p->connect("id_pressed", this, "_menu_item_pressed");
6144
6145 gizmos_menu = memnew(PopupMenu);
6146 p->add_child(gizmos_menu);
6147 gizmos_menu->set_name("GizmosMenu");
6148 gizmos_menu->set_hide_on_checkable_item_selection(false);
6149 gizmos_menu->connect("id_pressed", this, "_menu_gizmo_toggled");
6150
6151 /* REST OF MENU */
6152
6153 palette_split = memnew(HSplitContainer);
6154 palette_split->set_v_size_flags(SIZE_EXPAND_FILL);
6155 vbc->add_child(palette_split);
6156
6157 shader_split = memnew(VSplitContainer);
6158 shader_split->set_h_size_flags(SIZE_EXPAND_FILL);
6159 palette_split->add_child(shader_split);
6160 viewport_base = memnew(SpatialEditorViewportContainer);
6161 shader_split->add_child(viewport_base);
6162 viewport_base->set_v_size_flags(SIZE_EXPAND_FILL);
6163 for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) {
6164
6165 viewports[i] = memnew(SpatialEditorViewport(this, editor, i));
6166 viewports[i]->connect("toggle_maximize_view", this, "_toggle_maximize_view");
6167 viewports[i]->connect("clicked", this, "_update_camera_override_viewport");
6168 viewports[i]->assign_pending_data_pointers(preview_node, &preview_bounds, accept);
6169 viewport_base->add_child(viewports[i]);
6170 }
6171
6172 /* SNAP DIALOG */
6173
6174 snap_translate_value = 1;
6175 snap_rotate_value = 15;
6176 snap_scale_value = 10;
6177
6178 snap_dialog = memnew(ConfirmationDialog);
6179 snap_dialog->set_title(TTR("Snap Settings"));
6180 add_child(snap_dialog);
6181 snap_dialog->connect("confirmed", this, "_snap_changed");
6182 snap_dialog->get_cancel()->connect("pressed", this, "_snap_update");
6183
6184 VBoxContainer *snap_dialog_vbc = memnew(VBoxContainer);
6185 snap_dialog->add_child(snap_dialog_vbc);
6186
6187 snap_translate = memnew(LineEdit);
6188 snap_dialog_vbc->add_margin_child(TTR("Translate Snap:"), snap_translate);
6189
6190 snap_rotate = memnew(LineEdit);
6191 snap_dialog_vbc->add_margin_child(TTR("Rotate Snap (deg.):"), snap_rotate);
6192
6193 snap_scale = memnew(LineEdit);
6194 snap_dialog_vbc->add_margin_child(TTR("Scale Snap (%):"), snap_scale);
6195
6196 _snap_update();
6197
6198 /* SETTINGS DIALOG */
6199
6200 settings_dialog = memnew(ConfirmationDialog);
6201 settings_dialog->set_title(TTR("Viewport Settings"));
6202 add_child(settings_dialog);
6203 settings_vbc = memnew(VBoxContainer);
6204 settings_vbc->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
6205 settings_dialog->add_child(settings_vbc);
6206
6207 settings_fov = memnew(SpinBox);
6208 settings_fov->set_max(MAX_FOV);
6209 settings_fov->set_min(MIN_FOV);
6210 settings_fov->set_step(0.01);
6211 settings_fov->set_value(EDITOR_DEF("editors/3d/default_fov", 70.0));
6212 settings_vbc->add_margin_child(TTR("Perspective FOV (deg.):"), settings_fov);
6213
6214 settings_znear = memnew(SpinBox);
6215 settings_znear->set_max(MAX_Z);
6216 settings_znear->set_min(MIN_Z);
6217 settings_znear->set_step(0.01);
6218 settings_znear->set_value(EDITOR_DEF("editors/3d/default_z_near", 0.05));
6219 settings_vbc->add_margin_child(TTR("View Z-Near:"), settings_znear);
6220
6221 settings_zfar = memnew(SpinBox);
6222 settings_zfar->set_max(MAX_Z);
6223 settings_zfar->set_min(MIN_Z);
6224 settings_zfar->set_step(0.01);
6225 settings_zfar->set_value(EDITOR_DEF("editors/3d/default_z_far", 1500));
6226 settings_vbc->add_margin_child(TTR("View Z-Far:"), settings_zfar);
6227
6228 for (uint32_t i = 0; i < VIEWPORTS_COUNT; ++i) {
6229 settings_dialog->connect("confirmed", viewports[i], "_update_camera", varray(0.0));
6230 }
6231
6232 /* XFORM DIALOG */
6233
6234 xform_dialog = memnew(ConfirmationDialog);
6235 xform_dialog->set_title(TTR("Transform Change"));
6236 add_child(xform_dialog);
6237
6238 VBoxContainer *xform_vbc = memnew(VBoxContainer);
6239 xform_dialog->add_child(xform_vbc);
6240
6241 Label *l = memnew(Label);
6242 l->set_text(TTR("Translate:"));
6243 xform_vbc->add_child(l);
6244
6245 HBoxContainer *xform_hbc = memnew(HBoxContainer);
6246 xform_vbc->add_child(xform_hbc);
6247
6248 for (int i = 0; i < 3; i++) {
6249
6250 xform_translate[i] = memnew(LineEdit);
6251 xform_translate[i]->set_h_size_flags(SIZE_EXPAND_FILL);
6252 xform_hbc->add_child(xform_translate[i]);
6253 }
6254
6255 l = memnew(Label);
6256 l->set_text(TTR("Rotate (deg.):"));
6257 xform_vbc->add_child(l);
6258
6259 xform_hbc = memnew(HBoxContainer);
6260 xform_vbc->add_child(xform_hbc);
6261
6262 for (int i = 0; i < 3; i++) {
6263 xform_rotate[i] = memnew(LineEdit);
6264 xform_rotate[i]->set_h_size_flags(SIZE_EXPAND_FILL);
6265 xform_hbc->add_child(xform_rotate[i]);
6266 }
6267
6268 l = memnew(Label);
6269 l->set_text(TTR("Scale (ratio):"));
6270 xform_vbc->add_child(l);
6271
6272 xform_hbc = memnew(HBoxContainer);
6273 xform_vbc->add_child(xform_hbc);
6274
6275 for (int i = 0; i < 3; i++) {
6276 xform_scale[i] = memnew(LineEdit);
6277 xform_scale[i]->set_h_size_flags(SIZE_EXPAND_FILL);
6278 xform_hbc->add_child(xform_scale[i]);
6279 }
6280
6281 l = memnew(Label);
6282 l->set_text(TTR("Transform Type"));
6283 xform_vbc->add_child(l);
6284
6285 xform_type = memnew(OptionButton);
6286 xform_type->set_h_size_flags(SIZE_EXPAND_FILL);
6287 xform_type->add_item(TTR("Pre"));
6288 xform_type->add_item(TTR("Post"));
6289 xform_vbc->add_child(xform_type);
6290
6291 xform_dialog->connect("confirmed", this, "_xform_dialog_action");
6292
6293 scenario_debug = VisualServer::SCENARIO_DEBUG_DISABLED;
6294
6295 selected = NULL;
6296
6297 set_process_unhandled_key_input(true);
6298 add_to_group("_spatial_editor_group");
6299
6300 EDITOR_DEF("editors/3d/manipulator_gizmo_size", 80);
6301 EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d/manipulator_gizmo_size", PROPERTY_HINT_RANGE, "16,1024,1"));
6302 EDITOR_DEF("editors/3d/manipulator_gizmo_opacity", 0.4);
6303 EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::REAL, "editors/3d/manipulator_gizmo_opacity", PROPERTY_HINT_RANGE, "0,1,0.01"));
6304 EDITOR_DEF("editors/3d/navigation/show_viewport_rotation_gizmo", true);
6305
6306 over_gizmo_handle = -1;
6307 }
6308
~SpatialEditor()6309 SpatialEditor::~SpatialEditor() {
6310 memdelete(preview_node);
6311 }
6312
make_visible(bool p_visible)6313 void SpatialEditorPlugin::make_visible(bool p_visible) {
6314
6315 if (p_visible) {
6316
6317 spatial_editor->show();
6318 spatial_editor->set_process(true);
6319
6320 } else {
6321
6322 spatial_editor->hide();
6323 spatial_editor->set_process(false);
6324 }
6325 }
edit(Object * p_object)6326 void SpatialEditorPlugin::edit(Object *p_object) {
6327
6328 spatial_editor->edit(Object::cast_to<Spatial>(p_object));
6329 }
6330
handles(Object * p_object) const6331 bool SpatialEditorPlugin::handles(Object *p_object) const {
6332
6333 return p_object->is_class("Spatial");
6334 }
6335
get_state() const6336 Dictionary SpatialEditorPlugin::get_state() const {
6337 return spatial_editor->get_state();
6338 }
6339
set_state(const Dictionary & p_state)6340 void SpatialEditorPlugin::set_state(const Dictionary &p_state) {
6341
6342 spatial_editor->set_state(p_state);
6343 }
6344
snap_cursor_to_plane(const Plane & p_plane)6345 void SpatialEditor::snap_cursor_to_plane(const Plane &p_plane) {
6346
6347 //cursor.pos=p_plane.project(cursor.pos);
6348 }
6349
snap_point(Vector3 p_target,Vector3 p_start) const6350 Vector3 SpatialEditor::snap_point(Vector3 p_target, Vector3 p_start) const {
6351 if (is_snap_enabled()) {
6352 p_target.x = Math::snap_scalar(0.0, get_translate_snap(), p_target.x);
6353 p_target.y = Math::snap_scalar(0.0, get_translate_snap(), p_target.y);
6354 p_target.z = Math::snap_scalar(0.0, get_translate_snap(), p_target.z);
6355 }
6356 return p_target;
6357 }
6358
get_translate_snap() const6359 float SpatialEditor::get_translate_snap() const {
6360 float snap_value;
6361 if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
6362 snap_value = snap_translate->get_text().to_double() / 10.0;
6363 } else {
6364 snap_value = snap_translate->get_text().to_double();
6365 }
6366
6367 return snap_value;
6368 }
6369
get_rotate_snap() const6370 float SpatialEditor::get_rotate_snap() const {
6371 float snap_value;
6372 if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
6373 snap_value = snap_rotate->get_text().to_double() / 3.0;
6374 } else {
6375 snap_value = snap_rotate->get_text().to_double();
6376 }
6377
6378 return snap_value;
6379 }
6380
get_scale_snap() const6381 float SpatialEditor::get_scale_snap() const {
6382 float snap_value;
6383 if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
6384 snap_value = snap_scale->get_text().to_double() / 2.0;
6385 } else {
6386 snap_value = snap_scale->get_text().to_double();
6387 }
6388
6389 return snap_value;
6390 }
6391
_bind_methods()6392 void SpatialEditorPlugin::_bind_methods() {
6393
6394 ClassDB::bind_method("snap_cursor_to_plane", &SpatialEditorPlugin::snap_cursor_to_plane);
6395 }
6396
snap_cursor_to_plane(const Plane & p_plane)6397 void SpatialEditorPlugin::snap_cursor_to_plane(const Plane &p_plane) {
6398
6399 spatial_editor->snap_cursor_to_plane(p_plane);
6400 }
6401
6402 struct _GizmoPluginPriorityComparator {
6403
operator ()_GizmoPluginPriorityComparator6404 bool operator()(const Ref<EditorSpatialGizmoPlugin> &p_a, const Ref<EditorSpatialGizmoPlugin> &p_b) const {
6405 if (p_a->get_priority() == p_b->get_priority()) {
6406 return p_a->get_name() < p_b->get_name();
6407 }
6408 return p_a->get_priority() > p_b->get_priority();
6409 }
6410 };
6411
6412 struct _GizmoPluginNameComparator {
6413
operator ()_GizmoPluginNameComparator6414 bool operator()(const Ref<EditorSpatialGizmoPlugin> &p_a, const Ref<EditorSpatialGizmoPlugin> &p_b) const {
6415 return p_a->get_name() < p_b->get_name();
6416 }
6417 };
6418
add_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> p_plugin)6419 void SpatialEditor::add_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> p_plugin) {
6420 ERR_FAIL_NULL(p_plugin.ptr());
6421
6422 gizmo_plugins_by_priority.push_back(p_plugin);
6423 gizmo_plugins_by_priority.sort_custom<_GizmoPluginPriorityComparator>();
6424
6425 gizmo_plugins_by_name.push_back(p_plugin);
6426 gizmo_plugins_by_name.sort_custom<_GizmoPluginNameComparator>();
6427
6428 _update_gizmos_menu();
6429 SpatialEditor::get_singleton()->update_all_gizmos();
6430 }
6431
remove_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> p_plugin)6432 void SpatialEditor::remove_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> p_plugin) {
6433 gizmo_plugins_by_priority.erase(p_plugin);
6434 gizmo_plugins_by_name.erase(p_plugin);
6435 _update_gizmos_menu();
6436 }
6437
SpatialEditorPlugin(EditorNode * p_node)6438 SpatialEditorPlugin::SpatialEditorPlugin(EditorNode *p_node) {
6439
6440 editor = p_node;
6441 spatial_editor = memnew(SpatialEditor(p_node));
6442 spatial_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
6443 editor->get_viewport()->add_child(spatial_editor);
6444
6445 spatial_editor->hide();
6446 spatial_editor->connect("transform_key_request", editor->get_inspector_dock(), "_transform_keyed");
6447 }
6448
~SpatialEditorPlugin()6449 SpatialEditorPlugin::~SpatialEditorPlugin() {
6450 }
6451
create_material(const String & p_name,const Color & p_color,bool p_billboard,bool p_on_top,bool p_use_vertex_color)6452 void EditorSpatialGizmoPlugin::create_material(const String &p_name, const Color &p_color, bool p_billboard, bool p_on_top, bool p_use_vertex_color) {
6453
6454 Color instanced_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instanced", Color(0.7, 0.7, 0.7, 0.6));
6455
6456 Vector<Ref<SpatialMaterial> > mats;
6457
6458 for (int i = 0; i < 4; i++) {
6459 bool selected = i % 2 == 1;
6460 bool instanced = i < 2;
6461
6462 Ref<SpatialMaterial> material = Ref<SpatialMaterial>(memnew(SpatialMaterial));
6463
6464 Color color = instanced ? instanced_color : p_color;
6465
6466 if (!selected) {
6467 color.a *= 0.3;
6468 }
6469
6470 material->set_albedo(color);
6471 material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
6472 material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
6473 material->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN + 1);
6474
6475 if (p_use_vertex_color) {
6476 material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
6477 material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
6478 }
6479
6480 if (p_billboard) {
6481 material->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED);
6482 }
6483
6484 if (p_on_top && selected) {
6485 material->set_on_top_of_alpha();
6486 }
6487
6488 mats.push_back(material);
6489 }
6490
6491 materials[p_name] = mats;
6492 }
6493
create_icon_material(const String & p_name,const Ref<Texture> & p_texture,bool p_on_top,const Color & p_albedo)6494 void EditorSpatialGizmoPlugin::create_icon_material(const String &p_name, const Ref<Texture> &p_texture, bool p_on_top, const Color &p_albedo) {
6495
6496 Color instanced_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/instanced", Color(0.7, 0.7, 0.7, 0.6));
6497
6498 Vector<Ref<SpatialMaterial> > icons;
6499
6500 for (int i = 0; i < 4; i++) {
6501 bool selected = i % 2 == 1;
6502 bool instanced = i < 2;
6503
6504 Ref<SpatialMaterial> icon = Ref<SpatialMaterial>(memnew(SpatialMaterial));
6505
6506 Color color = instanced ? instanced_color : p_albedo;
6507
6508 if (!selected) {
6509 color.a *= 0.85;
6510 }
6511
6512 icon->set_albedo(color);
6513
6514 icon->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
6515 icon->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
6516 icon->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
6517 icon->set_cull_mode(SpatialMaterial::CULL_DISABLED);
6518 icon->set_depth_draw_mode(SpatialMaterial::DEPTH_DRAW_DISABLED);
6519 icon->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
6520 icon->set_texture(SpatialMaterial::TEXTURE_ALBEDO, p_texture);
6521 icon->set_flag(SpatialMaterial::FLAG_FIXED_SIZE, true);
6522 icon->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED);
6523 icon->set_render_priority(SpatialMaterial::RENDER_PRIORITY_MIN);
6524
6525 if (p_on_top && selected) {
6526 icon->set_on_top_of_alpha();
6527 }
6528
6529 icons.push_back(icon);
6530 }
6531
6532 materials[p_name] = icons;
6533 }
6534
create_handle_material(const String & p_name,bool p_billboard)6535 void EditorSpatialGizmoPlugin::create_handle_material(const String &p_name, bool p_billboard) {
6536 Ref<SpatialMaterial> handle_material = Ref<SpatialMaterial>(memnew(SpatialMaterial));
6537
6538 handle_material->set_flag(SpatialMaterial::FLAG_UNSHADED, true);
6539 handle_material->set_flag(SpatialMaterial::FLAG_USE_POINT_SIZE, true);
6540 Ref<Texture> handle_t = SpatialEditor::get_singleton()->get_icon("Editor3DHandle", "EditorIcons");
6541 handle_material->set_point_size(handle_t->get_width());
6542 handle_material->set_texture(SpatialMaterial::TEXTURE_ALBEDO, handle_t);
6543 handle_material->set_albedo(Color(1, 1, 1));
6544 handle_material->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
6545 handle_material->set_flag(SpatialMaterial::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
6546 handle_material->set_flag(SpatialMaterial::FLAG_SRGB_VERTEX_COLOR, true);
6547 handle_material->set_on_top_of_alpha();
6548 if (p_billboard) {
6549 handle_material->set_billboard_mode(SpatialMaterial::BILLBOARD_ENABLED);
6550 handle_material->set_on_top_of_alpha();
6551 }
6552
6553 materials[p_name] = Vector<Ref<SpatialMaterial> >();
6554 materials[p_name].push_back(handle_material);
6555 }
6556
add_material(const String & p_name,Ref<SpatialMaterial> p_material)6557 void EditorSpatialGizmoPlugin::add_material(const String &p_name, Ref<SpatialMaterial> p_material) {
6558 materials[p_name] = Vector<Ref<SpatialMaterial> >();
6559 materials[p_name].push_back(p_material);
6560 }
6561
get_material(const String & p_name,const Ref<EditorSpatialGizmo> & p_gizmo)6562 Ref<SpatialMaterial> EditorSpatialGizmoPlugin::get_material(const String &p_name, const Ref<EditorSpatialGizmo> &p_gizmo) {
6563 ERR_FAIL_COND_V(!materials.has(p_name), Ref<SpatialMaterial>());
6564 ERR_FAIL_COND_V(materials[p_name].size() == 0, Ref<SpatialMaterial>());
6565
6566 if (p_gizmo.is_null() || materials[p_name].size() == 1) return materials[p_name][0];
6567
6568 int index = (p_gizmo->is_selected() ? 1 : 0) + (p_gizmo->is_editable() ? 2 : 0);
6569
6570 Ref<SpatialMaterial> mat = materials[p_name][index];
6571
6572 if (current_state == ON_TOP && p_gizmo->is_selected()) {
6573 mat->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, true);
6574 } else {
6575 mat->set_flag(SpatialMaterial::FLAG_DISABLE_DEPTH_TEST, false);
6576 }
6577
6578 return mat;
6579 }
6580
get_name() const6581 String EditorSpatialGizmoPlugin::get_name() const {
6582 if (get_script_instance() && get_script_instance()->has_method("get_name")) {
6583 return get_script_instance()->call("get_name");
6584 }
6585 return TTR("Nameless gizmo");
6586 }
6587
get_priority() const6588 int EditorSpatialGizmoPlugin::get_priority() const {
6589 if (get_script_instance() && get_script_instance()->has_method("get_priority")) {
6590 return get_script_instance()->call("get_priority");
6591 }
6592 return 0;
6593 }
6594
get_gizmo(Spatial * p_spatial)6595 Ref<EditorSpatialGizmo> EditorSpatialGizmoPlugin::get_gizmo(Spatial *p_spatial) {
6596
6597 if (get_script_instance() && get_script_instance()->has_method("get_gizmo")) {
6598 return get_script_instance()->call("get_gizmo", p_spatial);
6599 }
6600
6601 Ref<EditorSpatialGizmo> ref = create_gizmo(p_spatial);
6602
6603 if (ref.is_null()) return ref;
6604
6605 ref->set_plugin(this);
6606 ref->set_spatial_node(p_spatial);
6607 ref->set_hidden(current_state == HIDDEN);
6608
6609 current_gizmos.push_back(ref.ptr());
6610 return ref;
6611 }
6612
_bind_methods()6613 void EditorSpatialGizmoPlugin::_bind_methods() {
6614 #define GIZMO_REF PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "EditorSpatialGizmo")
6615
6616 BIND_VMETHOD(MethodInfo(Variant::BOOL, "has_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Spatial")));
6617 BIND_VMETHOD(MethodInfo(GIZMO_REF, "create_gizmo", PropertyInfo(Variant::OBJECT, "spatial", PROPERTY_HINT_RESOURCE_TYPE, "Spatial")));
6618
6619 ClassDB::bind_method(D_METHOD("create_material", "name", "color", "billboard", "on_top", "use_vertex_color"), &EditorSpatialGizmoPlugin::create_material, DEFVAL(false), DEFVAL(false), DEFVAL(false));
6620 ClassDB::bind_method(D_METHOD("create_icon_material", "name", "texture", "on_top", "color"), &EditorSpatialGizmoPlugin::create_icon_material, DEFVAL(false), DEFVAL(Color(1, 1, 1, 1)));
6621 ClassDB::bind_method(D_METHOD("create_handle_material", "name", "billboard"), &EditorSpatialGizmoPlugin::create_handle_material, DEFVAL(false));
6622 ClassDB::bind_method(D_METHOD("add_material", "name", "material"), &EditorSpatialGizmoPlugin::add_material);
6623
6624 ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorSpatialGizmoPlugin::get_material); //, DEFVAL(Ref<EditorSpatialGizmo>()));
6625
6626 BIND_VMETHOD(MethodInfo(Variant::STRING, "get_name"));
6627 BIND_VMETHOD(MethodInfo(Variant::STRING, "get_priority"));
6628 BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_be_hidden"));
6629 BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_selectable_when_hidden"));
6630
6631 BIND_VMETHOD(MethodInfo("redraw", GIZMO_REF));
6632 BIND_VMETHOD(MethodInfo(Variant::STRING, "get_handle_name", GIZMO_REF, PropertyInfo(Variant::INT, "index")));
6633
6634 MethodInfo hvget(Variant::NIL, "get_handle_value", GIZMO_REF, PropertyInfo(Variant::INT, "index"));
6635 hvget.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
6636 BIND_VMETHOD(hvget);
6637
6638 BIND_VMETHOD(MethodInfo("set_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera"), PropertyInfo(Variant::VECTOR2, "point")));
6639 MethodInfo cm = MethodInfo("commit_handle", GIZMO_REF, PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::NIL, "restore"), PropertyInfo(Variant::BOOL, "cancel"));
6640 cm.default_arguments.push_back(false);
6641 BIND_VMETHOD(cm);
6642
6643 BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_handle_highlighted", GIZMO_REF, PropertyInfo(Variant::INT, "index")));
6644
6645 #undef GIZMO_REF
6646 }
6647
has_gizmo(Spatial * p_spatial)6648 bool EditorSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) {
6649 if (get_script_instance() && get_script_instance()->has_method("has_gizmo")) {
6650 return get_script_instance()->call("has_gizmo", p_spatial);
6651 }
6652 return false;
6653 }
6654
create_gizmo(Spatial * p_spatial)6655 Ref<EditorSpatialGizmo> EditorSpatialGizmoPlugin::create_gizmo(Spatial *p_spatial) {
6656
6657 if (get_script_instance() && get_script_instance()->has_method("create_gizmo")) {
6658 return get_script_instance()->call("create_gizmo", p_spatial);
6659 }
6660
6661 Ref<EditorSpatialGizmo> ref;
6662 if (has_gizmo(p_spatial)) ref.instance();
6663 return ref;
6664 }
6665
can_be_hidden() const6666 bool EditorSpatialGizmoPlugin::can_be_hidden() const {
6667 if (get_script_instance() && get_script_instance()->has_method("can_be_hidden")) {
6668 return get_script_instance()->call("can_be_hidden");
6669 }
6670 return true;
6671 }
6672
is_selectable_when_hidden() const6673 bool EditorSpatialGizmoPlugin::is_selectable_when_hidden() const {
6674 if (get_script_instance() && get_script_instance()->has_method("is_selectable_when_hidden")) {
6675 return get_script_instance()->call("is_selectable_when_hidden");
6676 }
6677 return false;
6678 }
6679
redraw(EditorSpatialGizmo * p_gizmo)6680 void EditorSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
6681 if (get_script_instance() && get_script_instance()->has_method("redraw")) {
6682 Ref<EditorSpatialGizmo> ref(p_gizmo);
6683 get_script_instance()->call("redraw", ref);
6684 }
6685 }
6686
get_handle_name(const EditorSpatialGizmo * p_gizmo,int p_idx) const6687 String EditorSpatialGizmoPlugin::get_handle_name(const EditorSpatialGizmo *p_gizmo, int p_idx) const {
6688 if (get_script_instance() && get_script_instance()->has_method("get_handle_name")) {
6689 return get_script_instance()->call("get_handle_name", p_gizmo, p_idx);
6690 }
6691 return "";
6692 }
6693
get_handle_value(EditorSpatialGizmo * p_gizmo,int p_idx) const6694 Variant EditorSpatialGizmoPlugin::get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const {
6695 if (get_script_instance() && get_script_instance()->has_method("get_handle_value")) {
6696 return get_script_instance()->call("get_handle_value", p_gizmo, p_idx);
6697 }
6698 return Variant();
6699 }
6700
set_handle(EditorSpatialGizmo * p_gizmo,int p_idx,Camera * p_camera,const Point2 & p_point)6701 void EditorSpatialGizmoPlugin::set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point) {
6702 if (get_script_instance() && get_script_instance()->has_method("set_handle")) {
6703 get_script_instance()->call("set_handle", p_gizmo, p_idx, p_camera, p_point);
6704 }
6705 }
6706
commit_handle(EditorSpatialGizmo * p_gizmo,int p_idx,const Variant & p_restore,bool p_cancel)6707 void EditorSpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel) {
6708 if (get_script_instance() && get_script_instance()->has_method("commit_handle")) {
6709 get_script_instance()->call("commit_handle", p_gizmo, p_idx, p_restore, p_cancel);
6710 }
6711 }
6712
is_handle_highlighted(const EditorSpatialGizmo * p_gizmo,int p_idx) const6713 bool EditorSpatialGizmoPlugin::is_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int p_idx) const {
6714 if (get_script_instance() && get_script_instance()->has_method("is_handle_highlighted")) {
6715 return get_script_instance()->call("is_handle_highlighted", p_gizmo, p_idx);
6716 }
6717 return false;
6718 }
6719
set_state(int p_state)6720 void EditorSpatialGizmoPlugin::set_state(int p_state) {
6721 current_state = p_state;
6722 for (int i = 0; i < current_gizmos.size(); ++i) {
6723 current_gizmos[i]->set_hidden(current_state == HIDDEN);
6724 }
6725 }
6726
get_state() const6727 int EditorSpatialGizmoPlugin::get_state() const {
6728 return current_state;
6729 }
6730
unregister_gizmo(EditorSpatialGizmo * p_gizmo)6731 void EditorSpatialGizmoPlugin::unregister_gizmo(EditorSpatialGizmo *p_gizmo) {
6732 current_gizmos.erase(p_gizmo);
6733 }
6734
EditorSpatialGizmoPlugin()6735 EditorSpatialGizmoPlugin::EditorSpatialGizmoPlugin() {
6736 current_state = VISIBLE;
6737 }
6738
~EditorSpatialGizmoPlugin()6739 EditorSpatialGizmoPlugin::~EditorSpatialGizmoPlugin() {
6740 for (int i = 0; i < current_gizmos.size(); ++i) {
6741 current_gizmos[i]->set_plugin(NULL);
6742 current_gizmos[i]->get_spatial_node()->set_gizmo(NULL);
6743 }
6744 if (SpatialEditor::get_singleton()) {
6745 SpatialEditor::get_singleton()->update_all_gizmos();
6746 }
6747 }
6748