Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/scene/main/viewport.cpp
9898 views
1
/**************************************************************************/
2
/* viewport.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "viewport.h"
32
33
#include "core/config/project_settings.h"
34
#include "core/debugger/engine_debugger.h"
35
#include "core/templates/pair.h"
36
#include "core/templates/sort_array.h"
37
#include "scene/2d/audio_listener_2d.h"
38
#include "scene/2d/camera_2d.h"
39
#include "scene/gui/control.h"
40
#include "scene/gui/label.h"
41
#include "scene/gui/popup.h"
42
#include "scene/gui/popup_menu.h"
43
#include "scene/gui/subviewport_container.h"
44
#include "scene/main/canvas_layer.h"
45
#include "scene/main/window.h"
46
#include "scene/resources/dpi_texture.h"
47
#include "scene/resources/mesh.h"
48
#include "scene/resources/text_line.h"
49
#include "scene/resources/world_2d.h"
50
#include "servers/audio_server.h"
51
#include "servers/rendering/rendering_server_globals.h"
52
53
#ifndef _3D_DISABLED
54
#include "scene/3d/audio_listener_3d.h"
55
#include "scene/3d/camera_3d.h"
56
#include "scene/3d/world_environment.h"
57
#endif // _3D_DISABLED
58
59
#ifndef PHYSICS_2D_DISABLED
60
#include "scene/2d/physics/collision_object_2d.h"
61
#endif // PHYSICS_2D_DISABLED
62
63
#ifndef PHYSICS_3D_DISABLED
64
#include "scene/3d/physics/collision_object_3d.h"
65
#endif // PHYSICS_3D_DISABLED
66
67
void ViewportTexture::setup_local_to_scene() {
68
// For the same target viewport, setup is only allowed once to prevent multiple free or multiple creations.
69
if (!vp_changed) {
70
return;
71
}
72
73
if (vp_pending) {
74
return;
75
}
76
77
Node *loc_scene = get_local_scene();
78
if (!loc_scene) {
79
return;
80
}
81
82
if (vp) {
83
vp->viewport_textures.erase(this);
84
vp = nullptr;
85
}
86
87
if (loc_scene->is_ready()) {
88
_setup_local_to_scene(loc_scene);
89
} else {
90
loc_scene->connect(SceneStringName(ready), callable_mp(this, &ViewportTexture::_setup_local_to_scene).bind(loc_scene), CONNECT_ONE_SHOT);
91
vp_pending = true;
92
}
93
}
94
95
void ViewportTexture::reset_local_to_scene() {
96
vp_changed = true;
97
98
if (vp) {
99
vp->viewport_textures.erase(this);
100
vp = nullptr;
101
}
102
103
if (proxy.is_valid() && proxy_ph.is_null()) {
104
proxy_ph = RS::get_singleton()->texture_2d_placeholder_create();
105
RS::get_singleton()->texture_proxy_update(proxy, proxy_ph);
106
}
107
}
108
109
void ViewportTexture::set_viewport_path_in_scene(const NodePath &p_path) {
110
if (path == p_path) {
111
return;
112
}
113
114
path = p_path;
115
116
reset_local_to_scene();
117
118
if (get_local_scene() && !path.is_empty()) {
119
setup_local_to_scene();
120
} else {
121
if (path.is_empty()) {
122
vp_changed = false;
123
}
124
emit_changed();
125
}
126
}
127
128
NodePath ViewportTexture::get_viewport_path_in_scene() const {
129
return path;
130
}
131
132
int ViewportTexture::get_width() const {
133
if (!vp) {
134
_err_print_viewport_not_set();
135
return 0;
136
}
137
if (vp->is_sub_viewport()) {
138
return vp->size.width;
139
}
140
return vp->size.width * vp->get_stretch_transform().get_scale().width;
141
}
142
143
int ViewportTexture::get_height() const {
144
if (!vp) {
145
_err_print_viewport_not_set();
146
return 0;
147
}
148
if (vp->is_sub_viewport()) {
149
return vp->size.height;
150
}
151
return vp->size.height * vp->get_stretch_transform().get_scale().height;
152
}
153
154
Size2 ViewportTexture::get_size() const {
155
if (!vp) {
156
_err_print_viewport_not_set();
157
return Size2();
158
}
159
if (vp->is_sub_viewport()) {
160
return vp->size;
161
}
162
Size2 scale = vp->get_stretch_transform().get_scale();
163
return Size2(vp->size.width * scale.width, vp->size.height * scale.height).ceil();
164
}
165
166
RID ViewportTexture::get_rid() const {
167
if (proxy.is_null()) {
168
proxy_ph = RS::get_singleton()->texture_2d_placeholder_create();
169
proxy = RS::get_singleton()->texture_proxy_create(proxy_ph);
170
}
171
return proxy;
172
}
173
174
bool ViewportTexture::has_alpha() const {
175
return false;
176
}
177
178
Ref<Image> ViewportTexture::get_image() const {
179
if (!vp) {
180
_err_print_viewport_not_set();
181
return Ref<Image>();
182
}
183
return RS::get_singleton()->texture_2d_get(vp->texture_rid);
184
}
185
186
void ViewportTexture::_err_print_viewport_not_set() const {
187
if (!vp_pending && !vp_changed) {
188
ERR_PRINT("Viewport Texture must be set to use it.");
189
}
190
}
191
192
void ViewportTexture::_setup_local_to_scene(const Node *p_loc_scene) {
193
// Always reset this, even if this call fails with an error.
194
vp_pending = false;
195
196
Node *vpn = p_loc_scene->get_node_or_null(path);
197
ERR_FAIL_NULL_MSG(vpn, "Path to node is invalid: '" + String(path) + "'.");
198
vp = Object::cast_to<Viewport>(vpn);
199
ERR_FAIL_NULL_MSG(vp, "Path to node does not point to a viewport: '" + String(path) + "'.");
200
201
vp->viewport_textures.insert(this);
202
203
ERR_FAIL_NULL(RenderingServer::get_singleton());
204
if (proxy_ph.is_valid()) {
205
RS::get_singleton()->texture_proxy_update(proxy, vp->texture_rid);
206
RS::get_singleton()->free(proxy_ph);
207
proxy_ph = RID();
208
} else {
209
ERR_FAIL_COND(proxy.is_valid()); // Should be invalid.
210
proxy = RS::get_singleton()->texture_proxy_create(vp->texture_rid);
211
}
212
vp_changed = false;
213
214
emit_changed();
215
}
216
217
void ViewportTexture::_bind_methods() {
218
ClassDB::bind_method(D_METHOD("set_viewport_path_in_scene", "path"), &ViewportTexture::set_viewport_path_in_scene);
219
ClassDB::bind_method(D_METHOD("get_viewport_path_in_scene"), &ViewportTexture::get_viewport_path_in_scene);
220
221
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewport_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Viewport", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT), "set_viewport_path_in_scene", "get_viewport_path_in_scene");
222
}
223
224
ViewportTexture::ViewportTexture() {
225
set_local_to_scene(true);
226
}
227
228
ViewportTexture::~ViewportTexture() {
229
if (vp) {
230
vp->viewport_textures.erase(this);
231
}
232
233
ERR_FAIL_NULL(RenderingServer::get_singleton());
234
235
if (proxy_ph.is_valid()) {
236
RS::get_singleton()->free(proxy_ph);
237
}
238
if (proxy.is_valid()) {
239
RS::get_singleton()->free(proxy);
240
}
241
}
242
243
void Viewport::_process_dirty_canvas_parent_orders() {
244
for (const ObjectID &id : gui.canvas_parents_with_dirty_order) {
245
Object *obj = ObjectDB::get_instance(id);
246
if (!obj) {
247
continue; // May have been deleted.
248
}
249
250
Node *n = static_cast<Node *>(obj);
251
for (int i = 0; i < n->get_child_count(); i++) {
252
Node *c = n->get_child(i);
253
CanvasItem *ci = Object::cast_to<CanvasItem>(c);
254
if (ci) {
255
ci->update_draw_order();
256
continue;
257
}
258
CanvasLayer *cl = Object::cast_to<CanvasLayer>(c);
259
if (cl) {
260
cl->update_draw_order();
261
}
262
}
263
}
264
265
gui.canvas_parents_with_dirty_order.clear();
266
}
267
268
void Viewport::_sub_window_update_order() {
269
if (gui.sub_windows.size() < 2) {
270
return;
271
}
272
273
if (!gui.sub_windows[gui.sub_windows.size() - 1].window->get_flag(Window::FLAG_ALWAYS_ON_TOP)) {
274
int index = gui.sub_windows.size() - 1;
275
276
while (index > 0 && gui.sub_windows[index - 1].window->get_flag(Window::FLAG_ALWAYS_ON_TOP)) {
277
--index;
278
}
279
280
if (index != (gui.sub_windows.size() - 1)) {
281
SubWindow sw = gui.sub_windows[gui.sub_windows.size() - 1];
282
gui.sub_windows.remove_at(gui.sub_windows.size() - 1);
283
gui.sub_windows.insert(index, sw);
284
}
285
}
286
287
for (int i = 0; i < gui.sub_windows.size(); i++) {
288
RS::get_singleton()->canvas_item_set_draw_index(gui.sub_windows[i].canvas_item, i);
289
}
290
}
291
292
void Viewport::_sub_window_register(Window *p_window) {
293
ERR_FAIL_COND(!is_inside_tree());
294
for (int i = 0; i < gui.sub_windows.size(); i++) {
295
ERR_FAIL_COND(gui.sub_windows[i].window == p_window);
296
}
297
298
if (gui.sub_windows.is_empty()) {
299
subwindow_canvas = RS::get_singleton()->canvas_create();
300
RS::get_singleton()->viewport_attach_canvas(viewport, subwindow_canvas);
301
RS::get_singleton()->viewport_set_canvas_stacking(viewport, subwindow_canvas, SUBWINDOW_CANVAS_LAYER, 0);
302
}
303
SubWindow sw;
304
sw.canvas_item = RS::get_singleton()->canvas_item_create();
305
RS::get_singleton()->canvas_item_set_parent(sw.canvas_item, subwindow_canvas);
306
sw.window = p_window;
307
gui.sub_windows.push_back(sw);
308
309
if (gui.subwindow_drag == SUB_WINDOW_DRAG_DISABLED) {
310
if (p_window->get_flag(Window::FLAG_NO_FOCUS)) {
311
_sub_window_update_order();
312
} else {
313
_sub_window_grab_focus(p_window);
314
}
315
} else {
316
int index = _sub_window_find(gui.currently_dragged_subwindow);
317
sw = gui.sub_windows[index];
318
gui.sub_windows.remove_at(index);
319
gui.sub_windows.push_back(sw);
320
_sub_window_update_order();
321
}
322
323
RenderingServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, viewport);
324
}
325
326
void Viewport::_sub_window_update(Window *p_window) {
327
int index = _sub_window_find(p_window);
328
329
// _sub_window_update is sometimes called deferred, and the window may have been closed since then.
330
// For example, when the user resizes the game window.
331
// In that case, _sub_window_find will not find it, which is expected.
332
if (index == -1) {
333
return;
334
}
335
336
SubWindow &sw = gui.sub_windows.write[index];
337
sw.pending_window_update = false;
338
339
RS::get_singleton()->canvas_item_clear(sw.canvas_item);
340
const Rect2i r = Rect2i(p_window->get_position(), p_window->get_size());
341
342
if (!p_window->get_flag(Window::FLAG_BORDERLESS)) {
343
Ref<StyleBox> panel = gui.subwindow_focused == p_window ? p_window->theme_cache.embedded_border : p_window->theme_cache.embedded_unfocused_border;
344
panel->draw(sw.canvas_item, r);
345
346
// Draw the title bar text.
347
Ref<Font> title_font = p_window->theme_cache.title_font;
348
int font_size = p_window->theme_cache.title_font_size;
349
Color title_color = p_window->theme_cache.title_color;
350
int title_height = p_window->theme_cache.title_height;
351
int close_h_ofs = p_window->theme_cache.close_h_offset;
352
int close_v_ofs = p_window->theme_cache.close_v_offset;
353
354
const real_t title_space = r.size.width - panel->get_minimum_size().x - close_h_ofs;
355
if (title_space > 0) {
356
TextLine title_text = TextLine(p_window->get_displayed_title(), title_font, font_size);
357
title_text.set_width(title_space);
358
title_text.set_direction(p_window->is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
359
int x = (r.size.width - title_text.get_size().x) / 2;
360
int y = (-title_height - title_text.get_size().y) / 2;
361
362
Color font_outline_color = p_window->theme_cache.title_outline_modulate;
363
int outline_size = p_window->theme_cache.title_outline_size;
364
if (outline_size > 0 && font_outline_color.a > 0) {
365
title_text.draw_outline(sw.canvas_item, r.position + Point2(x, y), outline_size, font_outline_color);
366
}
367
title_text.draw(sw.canvas_item, r.position + Point2(x, y), title_color);
368
}
369
370
bool pressed = gui.subwindow_focused == sw.window && gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE && gui.subwindow_drag_close_inside;
371
Ref<Texture2D> close_icon = pressed ? p_window->theme_cache.close_pressed : p_window->theme_cache.close;
372
close_icon->draw(sw.canvas_item, r.position + Vector2(r.size.width - close_h_ofs, -close_v_ofs));
373
}
374
375
const Transform2D xform = sw.window->window_transform * sw.window->stretch_transform;
376
Rect2 vr = xform.xform(sw.window->get_visible_rect());
377
vr.position += p_window->get_position();
378
if (vr != r) {
379
RS::get_singleton()->canvas_item_add_rect(sw.canvas_item, r, Color());
380
}
381
RS::get_singleton()->canvas_item_add_texture_rect(sw.canvas_item, vr, sw.window->get_texture()->get_rid());
382
}
383
384
void Viewport::_sub_window_grab_focus(Window *p_window) {
385
if (p_window == nullptr) {
386
// Release current focus.
387
if (gui.subwindow_focused) {
388
gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
389
gui.subwindow_focused = nullptr;
390
gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
391
}
392
393
Window *this_window = Object::cast_to<Window>(this);
394
if (this_window) {
395
this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN);
396
}
397
398
return;
399
}
400
401
// The index needs to be update before every usage in case an event callback changed the window list.
402
int index = _sub_window_find(p_window);
403
ERR_FAIL_COND(index == -1);
404
405
if (p_window->get_flag(Window::FLAG_NO_FOCUS)) {
406
// Release current focus.
407
if (gui.subwindow_focused) {
408
gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
409
gui.subwindow_focused = nullptr;
410
gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
411
}
412
// Can only move to foreground, but no focus granted.
413
index = _sub_window_find(p_window);
414
ERR_FAIL_COND(index == -1);
415
SubWindow sw = gui.sub_windows[index];
416
gui.sub_windows.remove_at(index);
417
gui.sub_windows.push_back(sw);
418
_sub_window_update_order();
419
return;
420
}
421
422
if (gui.subwindow_focused) {
423
if (gui.subwindow_focused == p_window) {
424
return; // Nothing to do.
425
}
426
gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
427
gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
428
} else {
429
Window *this_window = Object::cast_to<Window>(this);
430
if (this_window) {
431
this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
432
}
433
}
434
435
Window *old_focus = gui.subwindow_focused;
436
437
gui.subwindow_focused = p_window;
438
439
gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN);
440
441
{ // Move to foreground.
442
index = _sub_window_find(p_window);
443
ERR_FAIL_COND(index == -1);
444
SubWindow sw = gui.sub_windows[index];
445
gui.sub_windows.remove_at(index);
446
gui.sub_windows.push_back(sw);
447
index = gui.sub_windows.size() - 1;
448
_sub_window_update_order();
449
}
450
451
if (old_focus) {
452
_sub_window_update(old_focus);
453
}
454
455
_sub_window_update(p_window);
456
}
457
458
void Viewport::_sub_window_remove(Window *p_window) {
459
int index = _sub_window_find(p_window);
460
ERR_FAIL_COND(index == -1);
461
462
ERR_FAIL_NULL(RenderingServer::get_singleton());
463
464
SubWindow sw = gui.sub_windows[index];
465
if (gui.subwindow_over == sw.window) {
466
sw.window->_mouse_leave_viewport();
467
gui.subwindow_over = nullptr;
468
}
469
RS::get_singleton()->free(sw.canvas_item);
470
gui.sub_windows.remove_at(index);
471
472
if (gui.sub_windows.is_empty()) {
473
RS::get_singleton()->free(subwindow_canvas);
474
subwindow_canvas = RID();
475
}
476
477
if (gui.currently_dragged_subwindow == p_window) {
478
gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
479
gui.currently_dragged_subwindow = nullptr;
480
}
481
482
if (gui.subwindow_focused == p_window) {
483
Window *new_focused_window;
484
Window *parent_visible = p_window->get_parent_visible_window();
485
486
gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
487
488
if (parent_visible) {
489
new_focused_window = parent_visible;
490
} else {
491
new_focused_window = Object::cast_to<Window>(this);
492
}
493
494
if (new_focused_window) {
495
int new_focused_index = _sub_window_find(new_focused_window);
496
if (new_focused_index != -1) {
497
gui.subwindow_focused = new_focused_window;
498
} else {
499
gui.subwindow_focused = nullptr;
500
}
501
502
new_focused_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN);
503
} else {
504
gui.subwindow_focused = nullptr;
505
}
506
}
507
508
RenderingServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, p_window->parent ? p_window->parent->viewport : RID());
509
}
510
511
int Viewport::_sub_window_find(Window *p_window) const {
512
for (int i = 0; i < gui.sub_windows.size(); i++) {
513
if (gui.sub_windows[i].window == p_window) {
514
return i;
515
}
516
}
517
518
return -1;
519
}
520
521
void Viewport::_update_viewport_path() {
522
if (!is_inside_tree()) {
523
return;
524
}
525
526
for (ViewportTexture *E : viewport_textures) {
527
Node *loc_scene = E->get_local_scene();
528
if (loc_scene) {
529
E->path = loc_scene->get_path_to(this);
530
}
531
}
532
}
533
534
void Viewport::_notification(int p_what) {
535
ERR_MAIN_THREAD_GUARD;
536
537
switch (p_what) {
538
case NOTIFICATION_ENTER_TREE: {
539
_update_viewport_path();
540
541
if (get_parent()) {
542
parent = get_parent()->get_viewport();
543
RenderingServer::get_singleton()->viewport_set_parent_viewport(viewport, parent->get_viewport_rid());
544
} else {
545
parent = nullptr;
546
}
547
548
current_canvas = find_world_2d()->get_canvas();
549
RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas);
550
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, current_canvas, canvas_transform);
551
RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask);
552
_update_audio_listener_2d();
553
#ifndef _3D_DISABLED
554
RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
555
_update_audio_listener_3d();
556
#endif // _3D_DISABLED
557
558
add_to_group("_viewports");
559
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
560
if (get_tree()->is_debugging_collisions_hint()) {
561
#ifndef PHYSICS_2D_DISABLED
562
PhysicsServer2D::get_singleton()->space_set_debug_contacts(find_world_2d()->get_space(), get_tree()->get_collision_debug_contact_count());
563
contact_2d_debug = RenderingServer::get_singleton()->canvas_item_create();
564
RenderingServer::get_singleton()->canvas_item_set_parent(contact_2d_debug, current_canvas);
565
#endif // PHYSICS_2D_DISABLED
566
#ifndef PHYSICS_3D_DISABLED
567
PhysicsServer3D::get_singleton()->space_set_debug_contacts(find_world_3d()->get_space(), get_tree()->get_collision_debug_contact_count());
568
contact_3d_debug_multimesh = RenderingServer::get_singleton()->multimesh_create();
569
RenderingServer::get_singleton()->multimesh_allocate_data(contact_3d_debug_multimesh, get_tree()->get_collision_debug_contact_count(), RS::MULTIMESH_TRANSFORM_3D, false);
570
RenderingServer::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, 0);
571
RenderingServer::get_singleton()->multimesh_set_mesh(contact_3d_debug_multimesh, get_tree()->get_debug_contact_mesh()->get_rid());
572
contact_3d_debug_instance = RenderingServer::get_singleton()->instance_create();
573
RenderingServer::get_singleton()->instance_set_base(contact_3d_debug_instance, contact_3d_debug_multimesh);
574
RenderingServer::get_singleton()->instance_set_scenario(contact_3d_debug_instance, find_world_3d()->get_scenario());
575
RenderingServer::get_singleton()->instance_geometry_set_flag(contact_3d_debug_instance, RS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true);
576
#endif // PHYSICS_3D_DISABLED
577
set_physics_process_internal(true);
578
}
579
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
580
} break;
581
582
case NOTIFICATION_READY: {
583
#ifndef _3D_DISABLED
584
if (audio_listener_3d_set.size() && !audio_listener_3d) {
585
AudioListener3D *first = nullptr;
586
for (AudioListener3D *E : audio_listener_3d_set) {
587
if (first == nullptr || first->is_greater_than(E)) {
588
first = E;
589
}
590
}
591
592
if (first) {
593
first->make_current();
594
}
595
}
596
597
if (camera_3d_set.size() && !camera_3d) {
598
// There are cameras but no current camera, pick first in tree and make it current.
599
Camera3D *first = nullptr;
600
for (Camera3D *E : camera_3d_set) {
601
if (first == nullptr || first->is_greater_than(E)) {
602
first = E;
603
}
604
}
605
606
if (first) {
607
first->make_current();
608
}
609
}
610
#endif // _3D_DISABLED
611
} break;
612
613
case NOTIFICATION_EXIT_TREE: {
614
_gui_cancel_tooltip();
615
616
RenderingServer::get_singleton()->viewport_set_scenario(viewport, RID());
617
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas);
618
#ifndef PHYSICS_2D_DISABLED
619
if (contact_2d_debug.is_valid()) {
620
RenderingServer::get_singleton()->free(contact_2d_debug);
621
contact_2d_debug = RID();
622
}
623
#endif // PHYSICS_2D_DISABLED
624
625
#ifndef PHYSICS_3D_DISABLED
626
if (contact_3d_debug_multimesh.is_valid()) {
627
RenderingServer::get_singleton()->free(contact_3d_debug_multimesh);
628
RenderingServer::get_singleton()->free(contact_3d_debug_instance);
629
contact_3d_debug_instance = RID();
630
contact_3d_debug_multimesh = RID();
631
}
632
#endif // PHYSICS_3D_DISABLED
633
634
remove_from_group("_viewports");
635
set_physics_process_internal(false);
636
637
RS::get_singleton()->viewport_set_active(viewport, false);
638
RenderingServer::get_singleton()->viewport_set_parent_viewport(viewport, RID());
639
} break;
640
641
case NOTIFICATION_PATH_RENAMED: {
642
_update_viewport_path();
643
} break;
644
645
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
646
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
647
if (!get_tree()) {
648
return;
649
}
650
651
#ifndef PHYSICS_2D_DISABLED
652
if (get_tree()->is_debugging_collisions_hint() && contact_2d_debug.is_valid()) {
653
RenderingServer::get_singleton()->canvas_item_clear(contact_2d_debug);
654
RenderingServer::get_singleton()->canvas_item_set_draw_index(contact_2d_debug, 0xFFFFF); //very high index
655
656
Vector<Vector2> points = PhysicsServer2D::get_singleton()->space_get_contacts(find_world_2d()->get_space());
657
int point_count = PhysicsServer2D::get_singleton()->space_get_contact_count(find_world_2d()->get_space());
658
Color ccol = get_tree()->get_debug_collision_contact_color();
659
660
for (int i = 0; i < point_count; i++) {
661
RenderingServer::get_singleton()->canvas_item_add_rect(contact_2d_debug, Rect2(points[i] - Vector2(2, 2), Vector2(5, 5)), ccol);
662
}
663
}
664
#endif // PHYSICS_2D_DISABLED
665
#ifndef PHYSICS_3D_DISABLED
666
if (get_tree()->is_debugging_collisions_hint() && contact_3d_debug_multimesh.is_valid()) {
667
Vector<Vector3> points = PhysicsServer3D::get_singleton()->space_get_contacts(find_world_3d()->get_space());
668
int point_count = PhysicsServer3D::get_singleton()->space_get_contact_count(find_world_3d()->get_space());
669
670
RS::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, point_count);
671
672
for (int i = 0; i < point_count; i++) {
673
Transform3D point_transform;
674
point_transform.origin = points[i];
675
RS::get_singleton()->multimesh_instance_set_transform(contact_3d_debug_multimesh, i, point_transform);
676
}
677
}
678
#endif // PHYSICS_3D_DISABLED
679
} break;
680
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
681
682
case NOTIFICATION_VP_MOUSE_ENTER: {
683
gui.mouse_in_viewport = true;
684
} break;
685
686
case NOTIFICATION_VP_MOUSE_EXIT: {
687
gui.mouse_in_viewport = false;
688
_drop_physics_mouseover();
689
// When the mouse exits the viewport, we don't want to end
690
// mouse_focus, because, for example, we want to continue
691
// dragging a scrollbar even if the mouse has left the viewport.
692
} break;
693
694
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
695
_gui_cancel_tooltip();
696
_drop_physics_mouseover();
697
_drop_mouse_focus();
698
// When the window focus changes, we want to end mouse_focus, but
699
// not the mouse_over. Note: The OS will trigger a separate mouse
700
// exit event if the change in focus results in the mouse exiting
701
// the window.
702
} break;
703
704
case NOTIFICATION_PREDELETE: {
705
if (gui_parent) {
706
gui_parent->gui.tooltip_popup = nullptr;
707
gui_parent->gui.tooltip_label = nullptr;
708
}
709
} break;
710
}
711
}
712
713
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
714
void Viewport::_process_picking() {
715
if (!is_inside_tree()) {
716
return;
717
}
718
if (!physics_object_picking) {
719
return;
720
}
721
if (Object::cast_to<Window>(this) && Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED) {
722
return;
723
}
724
if (!gui.mouse_in_viewport || gui.subwindow_over) {
725
// Clear picking events if the mouse has left the viewport or is over an embedded window.
726
// These are locations, that are expected to not trigger physics picking.
727
physics_picking_events.clear();
728
return;
729
}
730
#ifndef XR_DISABLED
731
if (use_xr) {
732
if (XRServer::get_singleton() != nullptr) {
733
Ref<XRInterface> xr_interface = XRServer::get_singleton()->get_primary_interface();
734
if (xr_interface.is_valid() && xr_interface->is_initialized() && xr_interface->get_view_count() > 1) {
735
WARN_PRINT_ONCE("Object picking can't be used when stereo rendering, this will be turned off!");
736
physics_object_picking = false; // don't try again.
737
return;
738
}
739
}
740
}
741
#endif // XR_DISABLED
742
743
_drop_physics_mouseover(true);
744
745
#ifndef PHYSICS_3D_DISABLED
746
Vector2 last_pos(1e20, 1e20);
747
CollisionObject3D *last_object = nullptr;
748
ObjectID last_id;
749
PhysicsDirectSpaceState3D::RayResult result;
750
#endif // PHYSICS_3D_DISABLED
751
752
#ifndef PHYSICS_2D_DISABLED
753
PhysicsDirectSpaceState2D *ss2d = PhysicsServer2D::get_singleton()->space_get_direct_state(find_world_2d()->get_space());
754
#endif // PHYSICS_2D_DISABLED
755
756
SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
757
bool parent_ignore_mouse = (parent_svc && parent_svc->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE);
758
bool create_passive_hover_event = true;
759
if (gui.mouse_over || parent_ignore_mouse) {
760
// When the mouse is over a Control node, passive hovering would cause input events for Colliders, that are behind Control nodes.
761
// When parent SubViewportContainer ignores mouse, that setting should be respected.
762
create_passive_hover_event = false;
763
} else {
764
for (const Ref<InputEvent> &e : physics_picking_events) {
765
Ref<InputEventMouse> m = e;
766
if (m.is_valid()) {
767
// A mouse event exists, so passive hovering isn't necessary.
768
create_passive_hover_event = false;
769
break;
770
}
771
}
772
}
773
774
if (create_passive_hover_event) {
775
// Create a mouse motion event. This is necessary because objects or camera may have moved.
776
// While this extra event is sent, it is checked if both camera and last object and last ID did not move.
777
// If nothing changed, the event is discarded to avoid flooding with unnecessary motion events every frame.
778
Ref<InputEventMouseMotion> mm;
779
mm.instantiate();
780
781
mm->set_device(InputEvent::DEVICE_ID_INTERNAL);
782
mm->set_position(get_mouse_position());
783
mm->set_global_position(mm->get_position());
784
mm->set_alt_pressed(Input::get_singleton()->is_key_pressed(Key::ALT));
785
mm->set_shift_pressed(Input::get_singleton()->is_key_pressed(Key::SHIFT));
786
mm->set_ctrl_pressed(Input::get_singleton()->is_key_pressed(Key::CTRL));
787
mm->set_meta_pressed(Input::get_singleton()->is_key_pressed(Key::META));
788
mm->set_button_mask(Input::get_singleton()->get_mouse_button_mask());
789
physics_picking_events.push_back(mm);
790
}
791
792
while (physics_picking_events.size()) {
793
local_input_handled = false;
794
if (!handle_input_locally) {
795
Viewport *vp = this;
796
while (!Object::cast_to<Window>(vp) && vp->get_parent()) {
797
vp = vp->get_parent()->get_viewport();
798
}
799
vp->local_input_handled = false;
800
}
801
802
Ref<InputEvent> ev = physics_picking_events.front()->get();
803
physics_picking_events.pop_front();
804
805
Vector2 pos;
806
bool is_mouse = false;
807
808
Ref<InputEventMouseMotion> mm = ev;
809
810
if (mm.is_valid()) {
811
pos = mm->get_position();
812
is_mouse = true;
813
}
814
815
Ref<InputEventMouseButton> mb = ev;
816
817
if (mb.is_valid()) {
818
pos = mb->get_position();
819
is_mouse = true;
820
}
821
822
Ref<InputEventScreenDrag> sd = ev;
823
824
if (sd.is_valid()) {
825
pos = sd->get_position();
826
}
827
828
Ref<InputEventScreenTouch> st = ev;
829
830
if (st.is_valid()) {
831
pos = st->get_position();
832
}
833
834
#ifndef PHYSICS_2D_DISABLED
835
if (ss2d) {
836
// Send to 2D.
837
838
uint64_t frame = get_tree()->get_frame();
839
840
PhysicsDirectSpaceState2D::ShapeResult res[64];
841
for (const CanvasLayer *E : canvas_layers) {
842
Transform2D canvas_layer_transform;
843
ObjectID canvas_layer_id;
844
if (E) {
845
// A descendant CanvasLayer.
846
canvas_layer_transform = E->get_final_transform();
847
canvas_layer_id = E->get_instance_id();
848
} else {
849
// This Viewport's builtin canvas.
850
canvas_layer_transform = get_canvas_transform();
851
canvas_layer_id = ObjectID();
852
}
853
854
Vector2 point = canvas_layer_transform.affine_inverse().xform(pos);
855
856
PhysicsDirectSpaceState2D::PointParameters point_params;
857
point_params.position = point;
858
point_params.canvas_instance_id = canvas_layer_id;
859
point_params.collide_with_areas = true;
860
point_params.pick_point = true;
861
862
int rc = ss2d->intersect_point(point_params, res, 64);
863
if (physics_object_picking_sort) {
864
struct ComparatorCollisionObjects {
865
bool operator()(const PhysicsDirectSpaceState2D::ShapeResult &p_a, const PhysicsDirectSpaceState2D::ShapeResult &p_b) const {
866
CollisionObject2D *a = Object::cast_to<CollisionObject2D>(p_a.collider);
867
CollisionObject2D *b = Object::cast_to<CollisionObject2D>(p_b.collider);
868
if (!a || !b) {
869
return false;
870
}
871
int za = a->get_effective_z_index();
872
int zb = b->get_effective_z_index();
873
if (za != zb) {
874
return zb < za;
875
}
876
return a->is_greater_than(b);
877
}
878
};
879
SortArray<PhysicsDirectSpaceState2D::ShapeResult, ComparatorCollisionObjects> sorter;
880
sorter.sort(res, rc);
881
}
882
for (int i = 0; i < rc; i++) {
883
if (is_input_handled()) {
884
break;
885
}
886
if (res[i].collider_id.is_valid() && res[i].collider) {
887
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(res[i].collider);
888
if (co && co->can_process()) {
889
bool send_event = true;
890
if (is_mouse) {
891
HashMap<ObjectID, uint64_t>::Iterator F = physics_2d_mouseover.find(res[i].collider_id);
892
if (!F) {
893
physics_2d_mouseover.insert(res[i].collider_id, frame);
894
co->_mouse_enter();
895
} else {
896
F->value = frame;
897
// It was already hovered, so don't send the event if it's faked.
898
if (mm.is_valid() && mm->get_device() == InputEvent::DEVICE_ID_INTERNAL) {
899
send_event = false;
900
}
901
}
902
HashMap<Pair<ObjectID, int>, uint64_t>::Iterator SF = physics_2d_shape_mouseover.find(Pair(res[i].collider_id, res[i].shape));
903
if (!SF) {
904
physics_2d_shape_mouseover.insert(Pair(res[i].collider_id, res[i].shape), frame);
905
co->_mouse_shape_enter(res[i].shape);
906
} else {
907
SF->value = frame;
908
}
909
}
910
911
if (send_event) {
912
co->_input_event_call(this, ev, res[i].shape);
913
}
914
915
if (physics_object_picking_first_only) {
916
break;
917
}
918
}
919
}
920
}
921
}
922
923
if (is_mouse) {
924
_cleanup_mouseover_colliders(false, false, frame);
925
}
926
}
927
#endif // PHYSICS_2D_DISABLED
928
929
#ifndef PHYSICS_3D_DISABLED
930
if (physics_object_picking_first_only && is_input_handled()) {
931
continue;
932
}
933
934
CollisionObject3D *capture_object = nullptr;
935
if (physics_object_capture.is_valid()) {
936
capture_object = ObjectDB::get_instance<CollisionObject3D>(physics_object_capture);
937
if (!capture_object || !camera_3d || (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed())) {
938
physics_object_capture = ObjectID();
939
} else {
940
last_id = physics_object_capture;
941
last_object = capture_object;
942
}
943
}
944
945
if (pos == last_pos) {
946
if (last_id.is_valid()) {
947
if (ObjectDB::get_instance(last_id) && last_object) {
948
// Good, exists.
949
_collision_object_3d_input_event(last_object, camera_3d, ev, result.position, result.normal, result.shape);
950
if (last_object->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
951
physics_object_capture = last_id;
952
}
953
}
954
}
955
} else {
956
if (camera_3d) {
957
Vector3 from = camera_3d->project_ray_origin(pos);
958
Vector3 dir = camera_3d->project_ray_normal(pos);
959
real_t depth_far = camera_3d->get_far();
960
961
PhysicsDirectSpaceState3D *space = PhysicsServer3D::get_singleton()->space_get_direct_state(find_world_3d()->get_space());
962
if (space) {
963
PhysicsDirectSpaceState3D::RayParameters ray_params;
964
ray_params.from = from;
965
ray_params.to = from + dir * depth_far;
966
ray_params.collide_with_areas = true;
967
ray_params.pick_ray = true;
968
969
bool col = space->intersect_ray(ray_params, result);
970
ObjectID new_collider;
971
CollisionObject3D *co = col ? Object::cast_to<CollisionObject3D>(result.collider) : nullptr;
972
if (co && co->can_process()) {
973
new_collider = result.collider_id;
974
if (!capture_object) {
975
last_object = co;
976
last_id = result.collider_id;
977
if (co->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
978
physics_object_capture = last_id;
979
}
980
}
981
}
982
983
if (is_mouse && new_collider != physics_object_over) {
984
if (physics_object_over.is_valid()) {
985
CollisionObject3D *previous_co = ObjectDB::get_instance<CollisionObject3D>(physics_object_over);
986
if (previous_co) {
987
previous_co->_mouse_exit();
988
}
989
}
990
991
if (new_collider.is_valid()) {
992
DEV_ASSERT(co);
993
co->_mouse_enter();
994
}
995
996
physics_object_over = new_collider;
997
}
998
if (capture_object) {
999
_collision_object_3d_input_event(capture_object, camera_3d, ev, result.position, result.normal, result.shape);
1000
} else if (new_collider.is_valid()) {
1001
_collision_object_3d_input_event(co, camera_3d, ev, result.position, result.normal, result.shape);
1002
}
1003
}
1004
1005
last_pos = pos;
1006
}
1007
}
1008
#endif // PHYSICS_3D_DISABLED
1009
}
1010
}
1011
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
1012
1013
RID Viewport::get_viewport_rid() const {
1014
ERR_READ_THREAD_GUARD_V(RID());
1015
return viewport;
1016
}
1017
1018
void Viewport::update_canvas_items() {
1019
ERR_MAIN_THREAD_GUARD;
1020
if (!is_inside_tree()) {
1021
return;
1022
}
1023
1024
if (is_embedding_subwindows()) {
1025
for (Viewport::SubWindow w : gui.sub_windows) {
1026
if (w.window && !w.pending_window_update) {
1027
w.pending_window_update = true;
1028
w.window->_update_viewport_size();
1029
callable_mp(this, &Viewport::_sub_window_update).call_deferred(w.window);
1030
}
1031
}
1032
}
1033
_update_canvas_items(this);
1034
}
1035
1036
void Viewport::set_use_oversampling(bool p_oversampling) {
1037
ERR_MAIN_THREAD_GUARD;
1038
if (use_font_oversampling == p_oversampling) {
1039
return;
1040
}
1041
use_font_oversampling = p_oversampling;
1042
_set_size(_get_size(), _get_size_2d_override(), _is_size_allocated());
1043
}
1044
1045
bool Viewport::is_using_oversampling() const {
1046
ERR_READ_THREAD_GUARD_V(false);
1047
return use_font_oversampling;
1048
}
1049
1050
void Viewport::set_oversampling_override(float p_oversampling) {
1051
ERR_MAIN_THREAD_GUARD;
1052
if (font_oversampling_override == p_oversampling) {
1053
return;
1054
}
1055
font_oversampling_override = p_oversampling;
1056
_set_size(_get_size(), _get_size_2d_override(), _is_size_allocated());
1057
}
1058
1059
float Viewport::get_oversampling_override() const {
1060
ERR_READ_THREAD_GUARD_V(0.0);
1061
return font_oversampling_override;
1062
}
1063
1064
bool Viewport::_set_size(const Size2i &p_size, const Size2 &p_size_2d_override, bool p_allocated) {
1065
Transform2D stretch_transform_new = Transform2D();
1066
float new_font_oversampling = 1.0;
1067
if (is_size_2d_override_stretch_enabled() && p_size_2d_override.width > 0 && p_size_2d_override.height > 0) {
1068
Size2 scale = Size2(p_size) / p_size_2d_override;
1069
stretch_transform_new.scale(scale);
1070
1071
if (use_font_oversampling) {
1072
if (font_oversampling_override <= 0.0) {
1073
new_font_oversampling = MAX(scale.x, scale.y);
1074
} else {
1075
new_font_oversampling = font_oversampling_override;
1076
}
1077
} else {
1078
new_font_oversampling = 1.0;
1079
}
1080
} else if (use_font_oversampling && font_oversampling_override > 0.0) {
1081
new_font_oversampling = font_oversampling_override;
1082
}
1083
1084
Size2i new_size = p_size.maxi(2);
1085
if (size == new_size && size_allocated == p_allocated && stretch_transform == stretch_transform_new && p_size_2d_override == size_2d_override && new_font_oversampling == font_oversampling) {
1086
return false;
1087
}
1088
1089
if (new_font_oversampling != font_oversampling) {
1090
TS->reference_oversampling_level(new_font_oversampling);
1091
TS->unreference_oversampling_level(font_oversampling);
1092
1093
DPITexture::reference_scaling_level(new_font_oversampling);
1094
DPITexture::unreference_scaling_level(font_oversampling);
1095
}
1096
1097
size = new_size;
1098
size_allocated = p_allocated;
1099
size_2d_override = p_size_2d_override;
1100
stretch_transform = stretch_transform_new;
1101
font_oversampling = new_font_oversampling;
1102
1103
#ifndef _3D_DISABLED
1104
if (!use_xr) {
1105
#endif
1106
1107
if (p_allocated) {
1108
RS::get_singleton()->viewport_set_size(viewport, size.width, size.height);
1109
} else {
1110
RS::get_singleton()->viewport_set_size(viewport, 0, 0);
1111
}
1112
1113
#ifndef _3D_DISABLED
1114
} // if (!use_xr)
1115
#endif
1116
1117
_update_global_transform();
1118
update_configuration_warnings();
1119
1120
update_canvas_items();
1121
1122
for (ViewportTexture *E : viewport_textures) {
1123
E->emit_changed();
1124
}
1125
1126
emit_signal(SNAME("size_changed"));
1127
1128
Rect2i limit = get_visible_rect();
1129
for (int i = 0; i < gui.sub_windows.size(); ++i) {
1130
Window *sw = gui.sub_windows[i].window;
1131
Rect2i rect = Rect2i(sw->position, sw->size);
1132
Rect2i new_rect = sw->fit_rect_in_parent(rect, limit);
1133
if (new_rect != rect) {
1134
sw->set_position(new_rect.position);
1135
sw->set_size(new_rect.size);
1136
}
1137
}
1138
return true;
1139
}
1140
1141
Size2i Viewport::_get_size() const {
1142
#ifndef XR_DISABLED
1143
if (use_xr) {
1144
if (XRServer::get_singleton() != nullptr) {
1145
Ref<XRInterface> xr_interface = XRServer::get_singleton()->get_primary_interface();
1146
if (xr_interface.is_valid() && xr_interface->is_initialized()) {
1147
Size2 xr_size = xr_interface->get_render_target_size();
1148
return (Size2i)xr_size;
1149
}
1150
}
1151
return Size2i();
1152
}
1153
#endif // XR_DISABLED
1154
1155
return size;
1156
}
1157
1158
Size2 Viewport::_get_size_2d_override() const {
1159
return size_2d_override;
1160
}
1161
1162
bool Viewport::_is_size_allocated() const {
1163
return size_allocated;
1164
}
1165
1166
Rect2 Viewport::get_visible_rect() const {
1167
ERR_READ_THREAD_GUARD_V(Rect2());
1168
Rect2 r;
1169
1170
if (size == Size2()) {
1171
r = Rect2(Point2(), DisplayServer::get_singleton()->window_get_size());
1172
} else {
1173
r = Rect2(Point2(), size);
1174
}
1175
1176
if (size_2d_override != Size2()) {
1177
r.size = size_2d_override;
1178
}
1179
1180
return r;
1181
}
1182
1183
void Viewport::canvas_parent_mark_dirty(Node *p_node) {
1184
ERR_MAIN_THREAD_GUARD;
1185
bool request_update = gui.canvas_parents_with_dirty_order.is_empty();
1186
gui.canvas_parents_with_dirty_order.insert(p_node->get_instance_id());
1187
if (request_update) {
1188
callable_mp(this, &Viewport::_process_dirty_canvas_parent_orders).call_deferred();
1189
}
1190
}
1191
1192
void Viewport::enable_canvas_transform_override(bool p_enable) {
1193
ERR_MAIN_THREAD_GUARD;
1194
if (override_canvas_transform == p_enable) {
1195
return;
1196
}
1197
1198
override_canvas_transform = p_enable;
1199
if (p_enable) {
1200
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform_override);
1201
} else {
1202
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform);
1203
}
1204
}
1205
1206
bool Viewport::is_canvas_transform_override_enabled() const {
1207
ERR_READ_THREAD_GUARD_V(false);
1208
return override_canvas_transform;
1209
}
1210
1211
void Viewport::set_canvas_transform_override(const Transform2D &p_transform) {
1212
ERR_MAIN_THREAD_GUARD;
1213
if (canvas_transform_override == p_transform) {
1214
return;
1215
}
1216
1217
canvas_transform_override = p_transform;
1218
if (override_canvas_transform) {
1219
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform_override);
1220
}
1221
}
1222
1223
Transform2D Viewport::get_canvas_transform_override() const {
1224
ERR_READ_THREAD_GUARD_V(Transform2D());
1225
return canvas_transform_override;
1226
}
1227
1228
void Viewport::set_canvas_transform(const Transform2D &p_transform) {
1229
ERR_MAIN_THREAD_GUARD;
1230
canvas_transform = p_transform;
1231
1232
if (!override_canvas_transform) {
1233
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform);
1234
}
1235
}
1236
1237
Transform2D Viewport::get_canvas_transform() const {
1238
ERR_READ_THREAD_GUARD_V(Transform2D());
1239
return canvas_transform;
1240
}
1241
1242
void Viewport::_update_global_transform() {
1243
Transform2D sxform = stretch_transform * global_canvas_transform;
1244
1245
RenderingServer::get_singleton()->viewport_set_global_canvas_transform(viewport, sxform);
1246
}
1247
1248
void Viewport::set_global_canvas_transform(const Transform2D &p_transform) {
1249
ERR_MAIN_THREAD_GUARD;
1250
global_canvas_transform = p_transform;
1251
1252
_update_global_transform();
1253
}
1254
1255
Transform2D Viewport::get_global_canvas_transform() const {
1256
ERR_READ_THREAD_GUARD_V(Transform2D());
1257
return global_canvas_transform;
1258
}
1259
1260
void Viewport::_canvas_layer_add(CanvasLayer *p_canvas_layer) {
1261
canvas_layers.insert(p_canvas_layer);
1262
}
1263
1264
void Viewport::_canvas_layer_remove(CanvasLayer *p_canvas_layer) {
1265
canvas_layers.erase(p_canvas_layer);
1266
}
1267
1268
void Viewport::set_transparent_background(bool p_enable) {
1269
ERR_MAIN_THREAD_GUARD;
1270
transparent_bg = p_enable;
1271
RS::get_singleton()->viewport_set_transparent_background(viewport, p_enable);
1272
}
1273
1274
bool Viewport::has_transparent_background() const {
1275
ERR_READ_THREAD_GUARD_V(false);
1276
return transparent_bg;
1277
}
1278
1279
void Viewport::set_use_hdr_2d(bool p_enable) {
1280
ERR_MAIN_THREAD_GUARD;
1281
use_hdr_2d = p_enable;
1282
RS::get_singleton()->viewport_set_use_hdr_2d(viewport, p_enable);
1283
}
1284
1285
bool Viewport::is_using_hdr_2d() const {
1286
ERR_READ_THREAD_GUARD_V(false);
1287
return use_hdr_2d;
1288
}
1289
1290
void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
1291
ERR_MAIN_THREAD_GUARD;
1292
if (world_2d == p_world_2d) {
1293
return;
1294
}
1295
1296
if (is_inside_tree()) {
1297
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas);
1298
}
1299
1300
if (world_2d.is_valid()) {
1301
world_2d->remove_viewport(this);
1302
}
1303
1304
if (p_world_2d.is_valid()) {
1305
bool do_propagate = world_2d.is_valid() && is_inside_tree();
1306
world_2d = p_world_2d;
1307
if (do_propagate) {
1308
_propagate_world_2d_changed(this);
1309
}
1310
} else {
1311
WARN_PRINT("Invalid world_2d");
1312
world_2d.instantiate();
1313
}
1314
1315
world_2d->register_viewport(this);
1316
_update_audio_listener_2d();
1317
1318
if (is_inside_tree()) {
1319
current_canvas = find_world_2d()->get_canvas();
1320
RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas);
1321
}
1322
}
1323
1324
Ref<World2D> Viewport::find_world_2d() const {
1325
ERR_READ_THREAD_GUARD_V(Ref<World2D>());
1326
if (world_2d.is_valid()) {
1327
return world_2d;
1328
} else if (parent) {
1329
return parent->find_world_2d();
1330
} else {
1331
return Ref<World2D>();
1332
}
1333
}
1334
1335
void Viewport::_propagate_drag_notification(Node *p_node, int p_what) {
1336
// Send notification to p_node and all children and descendant nodes of p_node, except to SubViewports which are not children of a SubViewportContainer.
1337
p_node->notification(p_what);
1338
bool is_svc = Object::cast_to<SubViewportContainer>(p_node);
1339
for (int i = 0; i < p_node->get_child_count(); i++) {
1340
Node *c = p_node->get_child(i);
1341
if (!is_svc && Object::cast_to<SubViewport>(c)) {
1342
continue;
1343
}
1344
Viewport::_propagate_drag_notification(c, p_what);
1345
}
1346
}
1347
1348
Ref<World2D> Viewport::get_world_2d() const {
1349
ERR_READ_THREAD_GUARD_V(Ref<World2D>());
1350
return world_2d;
1351
}
1352
1353
Transform2D Viewport::get_stretch_transform() const {
1354
return stretch_transform;
1355
}
1356
1357
Transform2D Viewport::get_final_transform() const {
1358
ERR_READ_THREAD_GUARD_V(Transform2D());
1359
return stretch_transform * global_canvas_transform;
1360
}
1361
1362
void Viewport::_update_canvas_items(Node *p_node) {
1363
if (p_node != this) {
1364
Window *w = Object::cast_to<Window>(p_node);
1365
if (w && (!w->is_inside_tree() || !w->is_embedded())) {
1366
return;
1367
}
1368
1369
CanvasItem *ci = Object::cast_to<CanvasItem>(p_node);
1370
if (ci) {
1371
ci->queue_redraw();
1372
}
1373
}
1374
1375
int cc = p_node->get_child_count();
1376
1377
for (int i = 0; i < cc; i++) {
1378
_update_canvas_items(p_node->get_child(i));
1379
}
1380
}
1381
1382
Ref<ViewportTexture> Viewport::get_texture() const {
1383
ERR_READ_THREAD_GUARD_V(Ref<ViewportTexture>());
1384
return default_texture;
1385
}
1386
1387
void Viewport::set_positional_shadow_atlas_size(int p_size) {
1388
ERR_MAIN_THREAD_GUARD;
1389
positional_shadow_atlas_size = p_size;
1390
RS::get_singleton()->viewport_set_positional_shadow_atlas_size(viewport, p_size, positional_shadow_atlas_16_bits);
1391
}
1392
1393
int Viewport::get_positional_shadow_atlas_size() const {
1394
ERR_READ_THREAD_GUARD_V(0);
1395
return positional_shadow_atlas_size;
1396
}
1397
1398
void Viewport::set_positional_shadow_atlas_16_bits(bool p_16_bits) {
1399
ERR_MAIN_THREAD_GUARD;
1400
if (positional_shadow_atlas_16_bits == p_16_bits) {
1401
return;
1402
}
1403
1404
positional_shadow_atlas_16_bits = p_16_bits;
1405
RS::get_singleton()->viewport_set_positional_shadow_atlas_size(viewport, positional_shadow_atlas_size, positional_shadow_atlas_16_bits);
1406
}
1407
1408
bool Viewport::get_positional_shadow_atlas_16_bits() const {
1409
ERR_READ_THREAD_GUARD_V(false);
1410
return positional_shadow_atlas_16_bits;
1411
}
1412
void Viewport::set_positional_shadow_atlas_quadrant_subdiv(int p_quadrant, PositionalShadowAtlasQuadrantSubdiv p_subdiv) {
1413
ERR_MAIN_THREAD_GUARD;
1414
ERR_FAIL_INDEX(p_quadrant, 4);
1415
ERR_FAIL_INDEX(p_subdiv, SHADOW_ATLAS_QUADRANT_SUBDIV_MAX);
1416
1417
if (positional_shadow_atlas_quadrant_subdiv[p_quadrant] == p_subdiv) {
1418
return;
1419
}
1420
1421
positional_shadow_atlas_quadrant_subdiv[p_quadrant] = p_subdiv;
1422
static const int subdiv[SHADOW_ATLAS_QUADRANT_SUBDIV_MAX] = { 0, 1, 4, 16, 64, 256, 1024 };
1423
1424
RS::get_singleton()->viewport_set_positional_shadow_atlas_quadrant_subdivision(viewport, p_quadrant, subdiv[p_subdiv]);
1425
}
1426
1427
Viewport::PositionalShadowAtlasQuadrantSubdiv Viewport::get_positional_shadow_atlas_quadrant_subdiv(int p_quadrant) const {
1428
ERR_READ_THREAD_GUARD_V(SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);
1429
ERR_FAIL_INDEX_V(p_quadrant, 4, SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);
1430
return positional_shadow_atlas_quadrant_subdiv[p_quadrant];
1431
}
1432
1433
Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) {
1434
if (ev.is_null()) {
1435
return ev; // No transformation defined for null event
1436
}
1437
1438
Transform2D ai = get_final_transform().affine_inverse();
1439
Ref<InputEventMouse> me = ev;
1440
if (me.is_valid()) {
1441
me = me->xformed_by(ai);
1442
// For InputEventMouse, the global position is not adjusted by ev->xformed_by() and needs to be set separately.
1443
me->set_global_position(me->get_position());
1444
return me;
1445
}
1446
return ev->xformed_by(ai);
1447
}
1448
1449
Vector2 Viewport::get_mouse_position() const {
1450
ERR_READ_THREAD_GUARD_V(Vector2());
1451
if (get_section_root_viewport() != SceneTree::get_singleton()->get_root()) {
1452
// Rely on the most recent mouse coordinate from an InputEventMouse in push_input.
1453
// In this case get_screen_transform is not applicable, because it is ambiguous.
1454
return gui.last_mouse_pos;
1455
} else if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_MOUSE)) {
1456
Transform2D xform = get_screen_transform_internal(true);
1457
if (xform.determinant() == 0) {
1458
// Screen transform can be non-invertible when the Window is minimized.
1459
return Vector2();
1460
}
1461
return xform.affine_inverse().xform(DisplayServer::get_singleton()->mouse_get_position());
1462
} else {
1463
// Fallback to Input for getting mouse position in case of emulated mouse.
1464
return get_screen_transform_internal().affine_inverse().xform(Input::get_singleton()->get_mouse_position());
1465
}
1466
}
1467
1468
void Viewport::warp_mouse(const Vector2 &p_position) {
1469
ERR_MAIN_THREAD_GUARD;
1470
Transform2D xform = get_screen_transform_internal();
1471
Vector2 gpos = xform.xform(p_position);
1472
Input::get_singleton()->warp_mouse(gpos);
1473
}
1474
1475
Point2 Viewport::wrap_mouse_in_rect(const Vector2 &p_relative, const Rect2 &p_rect) {
1476
// Move the mouse cursor from its current position to a location bounded by `p_rect`
1477
// in accordance with a heuristic that takes the traveled distance `p_relative` of the mouse
1478
// into account.
1479
1480
// All parameters are in viewport coordinates.
1481
// p_relative denotes the distance to the previous mouse position.
1482
// p_rect denotes the area, in which the mouse should be confined in.
1483
1484
// The relative distance reported for the next event after a warp is in the boundaries of the
1485
// size of the rect on that axis, but it may be greater, in which case there's no problem as
1486
// fmod() will warp it, but if the pointer has moved in the opposite direction between the
1487
// pointer relocation and the subsequent event, the reported relative distance will be less
1488
// than the size of the rect and thus fmod() will be disabled for handling the situation.
1489
// And due to this mouse warping mechanism being stateless, we need to apply some heuristics
1490
// to detect the warp: if the relative distance is greater than the half of the size of the
1491
// relevant rect (checked per each axis), it will be considered as the consequence of a former
1492
// pointer warp.
1493
1494
const Point2 rel_sign(p_relative.x >= 0.0f ? 1 : -1, p_relative.y >= 0.0 ? 1 : -1);
1495
const Size2 warp_margin = p_rect.size * 0.5f;
1496
const Point2 rel_warped(
1497
Math::fmod(p_relative.x + rel_sign.x * warp_margin.x, p_rect.size.x) - rel_sign.x * warp_margin.x,
1498
Math::fmod(p_relative.y + rel_sign.y * warp_margin.y, p_rect.size.y) - rel_sign.y * warp_margin.y);
1499
1500
const Point2 pos_local = get_mouse_position() - p_rect.position;
1501
const Point2 pos_warped(Math::fposmod(pos_local.x, p_rect.size.x), Math::fposmod(pos_local.y, p_rect.size.y));
1502
if (pos_warped != pos_local) {
1503
warp_mouse(pos_warped + p_rect.position);
1504
}
1505
1506
return rel_warped;
1507
}
1508
1509
void Viewport::_gui_sort_roots() {
1510
if (!gui.roots_order_dirty) {
1511
return;
1512
}
1513
1514
gui.roots.sort_custom<Control::CComparator>();
1515
1516
gui.roots_order_dirty = false;
1517
}
1518
1519
void Viewport::_gui_cancel_tooltip() {
1520
gui.tooltip_control = nullptr;
1521
gui.tooltip_text = "";
1522
1523
if (gui.tooltip_timer.is_valid()) {
1524
gui.tooltip_timer->release_connections();
1525
gui.tooltip_timer = Ref<SceneTreeTimer>();
1526
}
1527
if (gui.tooltip_popup) {
1528
gui.tooltip_popup->queue_free();
1529
}
1530
}
1531
1532
String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_tooltip_owner) {
1533
Vector2 pos = p_pos;
1534
String tooltip;
1535
1536
while (p_control) {
1537
tooltip = p_control->get_tooltip(pos);
1538
1539
// Temporary solution for PopupMenus.
1540
PopupMenu *menu = Object::cast_to<PopupMenu>(this);
1541
if (menu) {
1542
Ref<StyleBox> sb = menu->get_theme_stylebox(SceneStringName(panel));
1543
if (sb.is_valid()) {
1544
pos.y += sb->get_margin(SIDE_TOP);
1545
}
1546
1547
tooltip = menu->get_tooltip(pos);
1548
}
1549
1550
if (r_tooltip_owner) {
1551
*r_tooltip_owner = p_control;
1552
}
1553
1554
// If we found a tooltip, we stop here.
1555
if (!tooltip.is_empty()) {
1556
break;
1557
}
1558
1559
// Otherwise, we check parent controls unless some conditions prevent it.
1560
1561
if (p_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
1562
break;
1563
}
1564
if (p_control->is_set_as_top_level()) {
1565
break;
1566
}
1567
1568
// Transform cursor pos for parent control.
1569
pos = p_control->get_transform().xform(pos);
1570
1571
p_control = p_control->get_parent_control();
1572
}
1573
1574
return tooltip;
1575
}
1576
1577
void Viewport::cancel_tooltip() {
1578
_gui_cancel_tooltip();
1579
}
1580
1581
void Viewport::show_tooltip(Control *p_control) {
1582
if (!p_control) {
1583
return;
1584
}
1585
1586
if (gui.tooltip_timer.is_valid()) {
1587
gui.tooltip_timer->release_connections();
1588
gui.tooltip_timer = Ref<SceneTreeTimer>();
1589
}
1590
gui.tooltip_control = p_control;
1591
_gui_show_tooltip_at(p_control->get_size() / 2);
1592
}
1593
1594
void Viewport::_gui_show_tooltip() {
1595
_gui_show_tooltip_at(gui.last_mouse_pos);
1596
}
1597
1598
void Viewport::_gui_show_tooltip_at(const Point2i &p_pos) {
1599
if (!gui.tooltip_control) {
1600
return;
1601
}
1602
1603
// Get the Control under cursor and the relevant tooltip text, if any.
1604
Control *tooltip_owner = nullptr;
1605
gui.tooltip_text = _gui_get_tooltip(
1606
gui.tooltip_control,
1607
gui.tooltip_control->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos),
1608
&tooltip_owner);
1609
gui.tooltip_text = gui.tooltip_text.strip_edges();
1610
1611
// Controls can implement `make_custom_tooltip` to provide their own tooltip.
1612
// This should be a Control node which will be added as child to a TooltipPanel.
1613
Control *base_tooltip = tooltip_owner->make_custom_tooltip(gui.tooltip_text);
1614
1615
// When the custom control is not visible, don't show any tooltip.
1616
// This way, the custom tooltip from `ConnectionsDockTree` can create
1617
// its own tooltip without conflicting with the default one, even an empty tooltip.
1618
if (base_tooltip && !base_tooltip->is_visible()) {
1619
memdelete(base_tooltip);
1620
return;
1621
}
1622
1623
if (gui.tooltip_text.is_empty() && !base_tooltip) {
1624
return; // Nothing to show.
1625
}
1626
1627
// Remove previous popup if we change something.
1628
if (gui.tooltip_popup) {
1629
memdelete(gui.tooltip_popup);
1630
gui.tooltip_popup = nullptr;
1631
}
1632
1633
if (!tooltip_owner) {
1634
return;
1635
}
1636
1637
// Popup window which houses the tooltip content.
1638
PopupPanel *panel = memnew(PopupPanel);
1639
panel->set_theme_type_variation(SNAME("TooltipPanel"));
1640
1641
// If no custom tooltip is given, use a default implementation.
1642
if (!base_tooltip) {
1643
gui.tooltip_label = memnew(Label);
1644
gui.tooltip_label->set_theme_type_variation(SNAME("TooltipLabel"));
1645
gui.tooltip_label->set_text(gui.tooltip_text);
1646
gui.tooltip_label->set_auto_translate_mode(tooltip_owner->get_tooltip_auto_translate_mode());
1647
base_tooltip = gui.tooltip_label;
1648
panel->connect(SceneStringName(mouse_entered), callable_mp(this, &Viewport::_gui_cancel_tooltip));
1649
}
1650
1651
base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
1652
1653
panel->set_flag(Window::FLAG_NO_FOCUS, true);
1654
panel->set_flag(Window::FLAG_POPUP, false);
1655
panel->set_flag(Window::FLAG_MOUSE_PASSTHROUGH, true);
1656
panel->set_wrap_controls(true);
1657
panel->set_default_canvas_item_texture_filter(get_default_canvas_item_texture_filter());
1658
panel->set_default_canvas_item_texture_repeat(get_default_canvas_item_texture_repeat());
1659
panel->add_child(base_tooltip);
1660
panel->gui_parent = this;
1661
1662
gui.tooltip_popup = panel;
1663
1664
tooltip_owner->add_child(gui.tooltip_popup);
1665
1666
Window *window = Object::cast_to<Window>(gui.tooltip_popup->get_embedder());
1667
if (!window) { // Not embedded.
1668
window = gui.tooltip_popup->get_parent_visible_window();
1669
}
1670
Size2 scale = get_popup_base_transform().get_scale();
1671
real_t popup_scale = MIN(scale.x, scale.y);
1672
Point2 tooltip_offset = GLOBAL_GET_CACHED(Point2, "display/mouse_cursor/tooltip_position_offset");
1673
tooltip_offset *= popup_scale;
1674
Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_contents_minimum_size());
1675
Rect2i vr;
1676
if (gui.tooltip_popup->is_embedded()) {
1677
vr = gui.tooltip_popup->get_embedder()->get_visible_rect();
1678
} else {
1679
vr = window->get_usable_parent_rect();
1680
}
1681
panel->content_scale_factor = popup_scale;
1682
r.size *= popup_scale;
1683
r.size = r.size.ceil();
1684
r.size = r.size.min(panel->get_max_size());
1685
1686
if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_SELF_FITTING_WINDOWS) || gui.tooltip_popup->is_embedded()) {
1687
if (r.size.x + r.position.x > vr.size.x + vr.position.x) {
1688
// Place it in the opposite direction. If it fails, just hug the border.
1689
r.position.x = gui.tooltip_pos.x - r.size.x - tooltip_offset.x;
1690
1691
if (r.position.x < vr.position.x) {
1692
r.position.x = vr.position.x + vr.size.x - r.size.x;
1693
}
1694
} else if (r.position.x < vr.position.x) {
1695
r.position.x = vr.position.x;
1696
}
1697
1698
if (r.size.y + r.position.y > vr.size.y + vr.position.y) {
1699
// Same as above.
1700
r.position.y = gui.tooltip_pos.y - r.size.y - tooltip_offset.y;
1701
1702
if (r.position.y < vr.position.y) {
1703
r.position.y = vr.position.y + vr.size.y - r.size.y;
1704
}
1705
} else if (r.position.y < vr.position.y) {
1706
r.position.y = vr.position.y;
1707
}
1708
}
1709
1710
DisplayServer::WindowID active_popup = DisplayServer::get_singleton()->window_get_active_popup();
1711
if (active_popup == DisplayServer::INVALID_WINDOW_ID || active_popup == window->get_window_id()) {
1712
gui.tooltip_popup->popup(r);
1713
}
1714
gui.tooltip_popup->child_controls_changed();
1715
}
1716
1717
void Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_input) {
1718
Ref<InputEvent> ev = p_input;
1719
1720
// Returns true if an event should be impacted by a control's mouse filter.
1721
bool is_pointer_event = Ref<InputEventMouse>(p_input).is_valid() || Ref<InputEventScreenDrag>(p_input).is_valid() || Ref<InputEventScreenTouch>(p_input).is_valid();
1722
1723
Ref<InputEventMouseButton> mb = p_input;
1724
bool is_scroll_event = mb.is_valid() &&
1725
(mb->get_button_index() == MouseButton::WHEEL_DOWN ||
1726
mb->get_button_index() == MouseButton::WHEEL_UP ||
1727
mb->get_button_index() == MouseButton::WHEEL_LEFT ||
1728
mb->get_button_index() == MouseButton::WHEEL_RIGHT);
1729
1730
CanvasItem *ci = p_control;
1731
while (ci) {
1732
Control *control = Object::cast_to<Control>(ci);
1733
if (control) {
1734
if (control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
1735
control->_call_gui_input(ev);
1736
}
1737
1738
if (!control->is_inside_tree() || control->is_set_as_top_level()) {
1739
break;
1740
}
1741
if (control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP && is_pointer_event && !(is_scroll_event && control->data.force_pass_scroll_events)) {
1742
// Mouse, ScreenDrag and ScreenTouch events are stopped by default with MOUSE_FILTER_STOP, unless we have a scroll event and force_pass_scroll_events set to true
1743
set_input_as_handled();
1744
break;
1745
}
1746
}
1747
1748
if (is_input_handled()) {
1749
// Break when the event is set to handled in a child Control node or after physics picking in SubViewport.
1750
break;
1751
}
1752
1753
if (ci->is_set_as_top_level()) {
1754
break;
1755
}
1756
1757
ev = ev->xformed_by(ci->get_transform()); // Transform event upwards.
1758
ci = ci->get_parent_item();
1759
}
1760
}
1761
1762
void Viewport::_gui_call_notification(Control *p_control, int p_what) {
1763
CanvasItem *ci = p_control;
1764
while (ci) {
1765
Control *control = Object::cast_to<Control>(ci);
1766
if (control) {
1767
if (control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
1768
control->notification(p_what);
1769
}
1770
1771
if (!control->is_inside_tree()) {
1772
break;
1773
}
1774
1775
if (!control->is_inside_tree() || control->is_set_as_top_level()) {
1776
break;
1777
}
1778
if (control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
1779
break;
1780
}
1781
}
1782
1783
if (ci->is_set_as_top_level()) {
1784
break;
1785
}
1786
1787
ci = ci->get_parent_item();
1788
}
1789
}
1790
1791
// `gui_find_control` doesn't take embedded windows into account. So the caller of this function
1792
// needs to make sure, that there is no embedded window at the specified position.
1793
Control *Viewport::gui_find_control(const Point2 &p_global) {
1794
ERR_MAIN_THREAD_GUARD_V(nullptr);
1795
1796
_gui_sort_roots();
1797
1798
for (List<Control *>::Element *E = gui.roots.back(); E; E = E->prev()) {
1799
Control *sw = E->get();
1800
if (!sw->is_visible_in_tree()) {
1801
continue;
1802
}
1803
1804
Transform2D xform;
1805
CanvasItem *pci = sw->get_parent_item();
1806
if (pci) {
1807
xform = pci->get_global_transform_with_canvas();
1808
} else {
1809
xform = sw->get_canvas_transform();
1810
}
1811
1812
Control *ret = _gui_find_control_at_pos(sw, p_global, xform);
1813
if (ret) {
1814
return ret;
1815
}
1816
}
1817
1818
return nullptr;
1819
}
1820
1821
Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform) {
1822
if (!p_node->is_visible()) {
1823
return nullptr; // Canvas item hidden, discard.
1824
}
1825
1826
Transform2D matrix = p_xform * p_node->get_transform();
1827
// matrix.determinant() == 0.0f implies that node does not exist on scene
1828
if (matrix.determinant() == 0.0f) {
1829
return nullptr;
1830
}
1831
1832
Control *c = Object::cast_to<Control>(p_node);
1833
1834
if (!c || !c->is_clipping_contents() || c->has_point(matrix.affine_inverse().xform(p_global))) {
1835
for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
1836
CanvasItem *ci = Object::cast_to<CanvasItem>(p_node->get_child(i));
1837
if (!ci || ci->is_set_as_top_level()) {
1838
continue;
1839
}
1840
1841
Control *ret = _gui_find_control_at_pos(ci, p_global, matrix);
1842
if (ret) {
1843
return ret;
1844
}
1845
}
1846
}
1847
1848
if (!c || c->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE) {
1849
return nullptr;
1850
}
1851
1852
matrix.affine_invert();
1853
if (!c->has_point(matrix.xform(p_global))) {
1854
return nullptr;
1855
}
1856
1857
Control *drag_preview = _gui_get_drag_preview();
1858
if (!drag_preview || (c != drag_preview && !drag_preview->is_ancestor_of(c))) {
1859
return c;
1860
}
1861
1862
return nullptr;
1863
}
1864
1865
bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check) {
1866
// Attempt drop, try parent controls too.
1867
CanvasItem *ci = p_at_control;
1868
Viewport *section_root = get_section_root_viewport();
1869
while (ci) {
1870
Control *control = Object::cast_to<Control>(ci);
1871
if (control) {
1872
if (control->can_drop_data(p_at_pos, section_root->gui.drag_data)) {
1873
if (!p_just_check) {
1874
control->drop_data(p_at_pos, section_root->gui.drag_data);
1875
}
1876
1877
return true;
1878
}
1879
1880
if (control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
1881
break;
1882
}
1883
}
1884
1885
p_at_pos = ci->get_transform().xform(p_at_pos);
1886
1887
if (ci->is_set_as_top_level()) {
1888
break;
1889
}
1890
1891
ci = ci->get_parent_item();
1892
}
1893
1894
return false;
1895
}
1896
1897
void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
1898
ERR_FAIL_COND(p_event.is_null());
1899
1900
Ref<InputEventMouseButton> mb = p_event;
1901
if (mb.is_valid()) {
1902
Point2 mpos = mb->get_position();
1903
if (mb->is_pressed()) {
1904
if (gui.dragging && mb->get_button_index() == MouseButton::RIGHT) {
1905
_perform_drop();
1906
set_input_as_handled();
1907
return;
1908
}
1909
MouseButtonMask button_mask = mouse_button_to_mask(mb->get_button_index());
1910
if (!gui.mouse_focus_mask.is_empty() && !gui.mouse_focus_mask.has_flag(button_mask)) {
1911
// Do not steal mouse focus and stuff while a focus mask without the current mouse button exists.
1912
gui.mouse_focus_mask.set_flag(button_mask);
1913
} else {
1914
gui.mouse_focus = gui_find_control(mpos);
1915
1916
if (!gui.mouse_focus) {
1917
return;
1918
}
1919
1920
gui.mouse_focus_mask.set_flag(mouse_button_to_mask(mb->get_button_index()));
1921
1922
if (mb->get_button_index() == MouseButton::LEFT) {
1923
gui.drag_accum = Vector2();
1924
gui.drag_attempted = false;
1925
}
1926
}
1927
DEV_ASSERT(gui.mouse_focus);
1928
1929
mb = mb->xformed_by(Transform2D()); // Make a copy of the event.
1930
1931
Point2 pos = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(mpos);
1932
mb->set_position(pos);
1933
1934
#ifdef DEBUG_ENABLED
1935
if (EngineDebugger::get_singleton()) {
1936
Array arr = { gui.mouse_focus->get_path(), gui.mouse_focus->get_class() };
1937
EngineDebugger::get_singleton()->send_message("scene:click_ctrl", arr);
1938
}
1939
#endif
1940
1941
if (mb->get_button_index() == MouseButton::LEFT) { // Assign focus.
1942
CanvasItem *ci = gui.mouse_focus;
1943
while (ci) {
1944
Control *control = Object::cast_to<Control>(ci);
1945
if (control) {
1946
if (control->_is_focusable()) {
1947
// Grabbing unhovered focus can cause issues when mouse is dragged
1948
// with another button held down.
1949
if (control != gui.key_focus && gui.mouse_over_hierarchy.has(control)) {
1950
control->grab_focus();
1951
}
1952
break;
1953
}
1954
1955
if (control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
1956
break;
1957
}
1958
}
1959
1960
if (ci->is_set_as_top_level()) {
1961
break;
1962
}
1963
1964
ci = ci->get_parent_item();
1965
}
1966
}
1967
1968
if (gui.mouse_focus && gui.mouse_focus->can_process()) {
1969
_gui_call_input(gui.mouse_focus, mb);
1970
}
1971
1972
if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) {
1973
// Alternate drop use (when using force_drag(), as proposed by #5342).
1974
_perform_drop(gui.mouse_focus);
1975
}
1976
1977
_gui_cancel_tooltip();
1978
} else {
1979
if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) {
1980
_perform_drop(gui.drag_mouse_over);
1981
}
1982
1983
gui.mouse_focus_mask.clear_flag(mouse_button_to_mask(mb->get_button_index())); // Remove from mask.
1984
1985
if (!gui.mouse_focus) {
1986
// Release event is only sent if a mouse focus (previously pressed button) exists.
1987
return;
1988
}
1989
1990
mb = mb->xformed_by(Transform2D()); // Make a copy.
1991
Point2 pos = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(mpos);
1992
mb->set_position(pos);
1993
1994
Control *mouse_focus = gui.mouse_focus;
1995
1996
// Disable mouse focus if needed before calling input,
1997
// this makes popups on mouse press event work better,
1998
// as the release will never be received otherwise.
1999
if (gui.mouse_focus_mask.is_empty()) {
2000
gui.mouse_focus = nullptr;
2001
}
2002
2003
if (mouse_focus && mouse_focus->can_process()) {
2004
_gui_call_input(mouse_focus, mb);
2005
}
2006
}
2007
}
2008
2009
Ref<InputEventMouseMotion> mm = p_event;
2010
if (mm.is_valid()) {
2011
Point2 mpos = mm->get_position();
2012
2013
// Drag & drop.
2014
Viewport *section_root = get_section_root_viewport();
2015
if (!gui.drag_attempted && gui.mouse_focus && section_root && !section_root->gui.global_dragging && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
2016
gui.drag_accum += mm->get_relative();
2017
float len = gui.drag_accum.length();
2018
if (len > 10) {
2019
{ // Attempt grab, try parent controls too.
2020
CanvasItem *ci = gui.mouse_focus;
2021
while (ci) {
2022
Control *control = Object::cast_to<Control>(ci);
2023
if (control) {
2024
section_root->gui.global_dragging = true;
2025
section_root->gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos - gui.drag_accum));
2026
if (section_root->gui.drag_data.get_type() != Variant::NIL) {
2027
gui.mouse_focus = nullptr;
2028
gui.mouse_focus_mask.clear();
2029
gui.dragging = true;
2030
break;
2031
} else {
2032
Control *drag_preview = _gui_get_drag_preview();
2033
if (drag_preview) {
2034
ERR_PRINT("Don't set a drag preview and return null data. Preview was deleted and drag request ignored.");
2035
memdelete(drag_preview);
2036
gui.drag_preview_id = ObjectID();
2037
}
2038
section_root->gui.global_dragging = false;
2039
}
2040
2041
if (control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
2042
break;
2043
}
2044
}
2045
2046
if (ci->is_set_as_top_level()) {
2047
break;
2048
}
2049
2050
ci = ci->get_parent_item();
2051
}
2052
}
2053
2054
gui.drag_attempted = true;
2055
if (gui.dragging) {
2056
Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_BEGIN);
2057
}
2058
}
2059
}
2060
2061
Control *over = nullptr;
2062
if (gui.mouse_focus) {
2063
over = gui.mouse_focus;
2064
} else if (gui.mouse_in_viewport) {
2065
over = gui_find_control(mpos);
2066
}
2067
2068
DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)Input::get_singleton()->get_default_cursor_shape();
2069
2070
if (over) {
2071
Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse();
2072
Size2 pos = localizer.xform(mpos);
2073
Vector2 velocity = localizer.basis_xform(mm->get_velocity());
2074
Vector2 rel = localizer.basis_xform(mm->get_relative());
2075
2076
mm = mm->xformed_by(Transform2D()); // Make a copy.
2077
2078
mm->set_global_position(mpos);
2079
mm->set_velocity(velocity);
2080
mm->set_relative(rel);
2081
2082
// Nothing pressed.
2083
if (mm->get_button_mask().is_empty()) {
2084
bool is_tooltip_shown = false;
2085
2086
if (gui.tooltip_popup) {
2087
if (gui.tooltip_control) {
2088
String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform_with_canvas().affine_inverse().xform(mpos));
2089
tooltip = tooltip.strip_edges();
2090
2091
if (tooltip != gui.tooltip_text) {
2092
_gui_cancel_tooltip();
2093
} else {
2094
is_tooltip_shown = true;
2095
}
2096
} else {
2097
_gui_cancel_tooltip();
2098
}
2099
}
2100
2101
// Reset the timer if the mouse has moved more than 5 pixels or has entered a new control.
2102
if (!is_tooltip_shown && over->can_process()) {
2103
Vector2 new_tooltip_pos = over->get_screen_transform().xform(pos);
2104
if (over != gui.tooltip_control || gui.tooltip_pos.distance_squared_to(new_tooltip_pos) > 25) {
2105
if (gui.tooltip_timer.is_valid()) {
2106
gui.tooltip_timer->release_connections();
2107
}
2108
gui.tooltip_control = over;
2109
gui.tooltip_pos = new_tooltip_pos;
2110
gui.tooltip_timer = get_tree()->create_timer(gui.tooltip_delay);
2111
gui.tooltip_timer->set_ignore_time_scale(true);
2112
gui.tooltip_timer->connect("timeout", callable_mp(this, &Viewport::_gui_show_tooltip));
2113
}
2114
}
2115
}
2116
2117
mm->set_position(pos);
2118
2119
Control::CursorShape cursor_shape = Control::CURSOR_ARROW;
2120
{
2121
Control *c = over;
2122
Vector2 cpos = pos;
2123
while (c) {
2124
if (!gui.mouse_focus_mask.is_empty() || c->has_point(cpos)) {
2125
cursor_shape = c->get_cursor_shape(cpos);
2126
} else {
2127
cursor_shape = Control::CURSOR_ARROW;
2128
}
2129
cpos = c->get_transform().xform(cpos);
2130
if (cursor_shape != Control::CURSOR_ARROW) {
2131
break;
2132
}
2133
if (c->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
2134
break;
2135
}
2136
if (c->is_set_as_top_level()) {
2137
break;
2138
}
2139
c = c->get_parent_control();
2140
}
2141
}
2142
2143
ds_cursor_shape = (DisplayServer::CursorShape)cursor_shape;
2144
2145
if (over->can_process()) {
2146
_gui_call_input(over, mm);
2147
}
2148
}
2149
2150
if (gui.dragging) {
2151
// Handle drag & drop. This happens in the viewport where dragging started.
2152
2153
Control *drag_preview = _gui_get_drag_preview();
2154
if (drag_preview) {
2155
Vector2 pos = drag_preview->get_canvas_transform().affine_inverse().xform(mpos);
2156
drag_preview->set_position(pos);
2157
}
2158
2159
gui.drag_mouse_over = section_root->gui.target_control;
2160
if (gui.drag_mouse_over) {
2161
if (!_gui_drop(gui.drag_mouse_over, gui.drag_mouse_over->get_local_mouse_position(), true)) {
2162
gui.drag_mouse_over = nullptr;
2163
}
2164
if (gui.drag_mouse_over) {
2165
ds_cursor_shape = DisplayServer::CURSOR_CAN_DROP;
2166
} else {
2167
ds_cursor_shape = DisplayServer::CURSOR_FORBIDDEN;
2168
}
2169
}
2170
}
2171
2172
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE) && (gui.dragging || (!section_root->gui.global_dragging && !Object::cast_to<SubViewportContainer>(over)))) {
2173
// If dragging is active, then set the cursor shape only from the Viewport where dragging started.
2174
// If dragging is inactive, then set the cursor shape only when not over a SubViewportContainer.
2175
DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape);
2176
}
2177
}
2178
2179
Ref<InputEventScreenTouch> touch_event = p_event;
2180
if (touch_event.is_valid()) {
2181
Size2 pos = touch_event->get_position();
2182
const int touch_index = touch_event->get_index();
2183
if (touch_event->is_pressed()) {
2184
Control *over = gui_find_control(pos);
2185
if (over) {
2186
gui.touch_focus[touch_index] = over->get_instance_id();
2187
if (over->can_process()) {
2188
touch_event = touch_event->xformed_by(Transform2D()); // Make a copy.
2189
pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
2190
touch_event->set_position(pos);
2191
_gui_call_input(over, touch_event);
2192
}
2193
return;
2194
}
2195
} else {
2196
ObjectID control_id = gui.touch_focus[touch_index];
2197
Control *over = control_id.is_valid() ? ObjectDB::get_instance<Control>(control_id) : nullptr;
2198
if (over && over->can_process()) {
2199
touch_event = touch_event->xformed_by(Transform2D()); // Make a copy.
2200
pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
2201
touch_event->set_position(pos);
2202
2203
_gui_call_input(over, touch_event);
2204
}
2205
gui.touch_focus.erase(touch_index);
2206
return;
2207
}
2208
}
2209
2210
Ref<InputEventGesture> gesture_event = p_event;
2211
if (gesture_event.is_valid()) {
2212
_gui_cancel_tooltip();
2213
2214
Size2 pos = gesture_event->get_position();
2215
2216
Control *over = gui_find_control(pos);
2217
if (over) {
2218
if (over->can_process()) {
2219
gesture_event = gesture_event->xformed_by(Transform2D()); // Make a copy.
2220
pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
2221
gesture_event->set_position(pos);
2222
_gui_call_input(over, gesture_event);
2223
}
2224
return;
2225
}
2226
}
2227
2228
Ref<InputEventScreenDrag> drag_event = p_event;
2229
if (drag_event.is_valid()) {
2230
const int drag_event_index = drag_event->get_index();
2231
ObjectID control_id = gui.touch_focus[drag_event_index];
2232
Control *over = control_id.is_valid() ? ObjectDB::get_instance<Control>(control_id) : nullptr;
2233
if (!over) {
2234
over = gui_find_control(drag_event->get_position());
2235
}
2236
if (over) {
2237
if (over->can_process()) {
2238
Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse();
2239
Size2 pos = localizer.xform(drag_event->get_position());
2240
Vector2 velocity = localizer.basis_xform(drag_event->get_velocity());
2241
Vector2 rel = localizer.basis_xform(drag_event->get_relative());
2242
2243
drag_event = drag_event->xformed_by(Transform2D()); // Make a copy.
2244
2245
drag_event->set_velocity(velocity);
2246
drag_event->set_relative(rel);
2247
drag_event->set_position(pos);
2248
2249
_gui_call_input(over, drag_event);
2250
}
2251
2252
return;
2253
}
2254
}
2255
2256
if (mm.is_null() && mb.is_null() && p_event->is_action_type()) {
2257
if (gui.dragging && p_event->is_action_pressed(SNAME("ui_cancel")) && Input::get_singleton()->is_action_just_pressed(SNAME("ui_cancel"))) {
2258
_perform_drop();
2259
set_input_as_handled();
2260
return;
2261
}
2262
2263
if (p_event->is_action_pressed(SNAME("ui_cancel"))) {
2264
// Cancel tooltip timer or hide tooltip when pressing Escape (this is standard behavior in most applications).
2265
_gui_cancel_tooltip();
2266
if (gui.tooltip_popup) {
2267
// If a tooltip was hidden, prevent other actions associated with `ui_cancel` from occurring.
2268
// For instance, this prevents the node from being deselected when pressing Escape
2269
// to hide a documentation tooltip in the inspector.
2270
set_input_as_handled();
2271
return;
2272
}
2273
}
2274
2275
if (gui.key_focus && !gui.key_focus->is_visible_in_tree()) {
2276
gui.key_focus->release_focus();
2277
}
2278
2279
if (gui.key_focus) {
2280
if (gui.key_focus->can_process()) {
2281
gui.key_focus->_call_gui_input(p_event);
2282
}
2283
2284
if (is_input_handled()) {
2285
return;
2286
}
2287
}
2288
2289
Control *from = gui.key_focus ? gui.key_focus : nullptr;
2290
if (!from) {
2291
for (int i = 0; i < get_child_count(true); i++) {
2292
Control *c = Object::cast_to<Control>(get_child(i, true));
2293
if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) {
2294
continue;
2295
}
2296
2297
from = c;
2298
break;
2299
}
2300
}
2301
2302
if (from && p_event->is_pressed()) {
2303
Control *next = nullptr;
2304
2305
Ref<InputEventJoypadMotion> joypadmotion_event = p_event;
2306
if (joypadmotion_event.is_valid()) {
2307
Input *input = Input::get_singleton();
2308
2309
if (p_event->is_action_pressed(SNAME("ui_focus_next")) && input->is_action_just_pressed_by_event(SNAME("ui_focus_next"), p_event)) {
2310
next = from->find_next_valid_focus();
2311
}
2312
2313
if (p_event->is_action_pressed(SNAME("ui_focus_prev")) && input->is_action_just_pressed_by_event(SNAME("ui_focus_prev"), p_event)) {
2314
next = from->find_prev_valid_focus();
2315
}
2316
2317
if (p_event->is_action_pressed(SNAME("ui_accessibility_drag_and_drop")) && input->is_action_just_pressed_by_event(SNAME("ui_accessibility_drag_and_drop"), p_event)) {
2318
if (gui_is_dragging()) {
2319
from->accessibility_drop();
2320
} else {
2321
from->accessibility_drag();
2322
}
2323
}
2324
2325
if (p_event->is_action_pressed(SNAME("ui_up")) && input->is_action_just_pressed_by_event(SNAME("ui_up"), p_event)) {
2326
next = from->_get_focus_neighbor(SIDE_TOP);
2327
}
2328
2329
if (p_event->is_action_pressed(SNAME("ui_left")) && input->is_action_just_pressed_by_event(SNAME("ui_left"), p_event)) {
2330
next = from->_get_focus_neighbor(SIDE_LEFT);
2331
}
2332
2333
if (p_event->is_action_pressed(SNAME("ui_right")) && input->is_action_just_pressed_by_event(SNAME("ui_right"), p_event)) {
2334
next = from->_get_focus_neighbor(SIDE_RIGHT);
2335
}
2336
2337
if (p_event->is_action_pressed(SNAME("ui_down")) && input->is_action_just_pressed_by_event(SNAME("ui_down"), p_event)) {
2338
next = from->_get_focus_neighbor(SIDE_BOTTOM);
2339
}
2340
} else {
2341
if (p_event->is_action_pressed(SNAME("ui_focus_next"), true, true)) {
2342
next = from->find_next_valid_focus();
2343
}
2344
2345
if (p_event->is_action_pressed(SNAME("ui_focus_prev"), true, true)) {
2346
next = from->find_prev_valid_focus();
2347
}
2348
2349
if (p_event->is_action_pressed(SNAME("ui_accessibility_drag_and_drop"), true, true)) {
2350
if (gui_is_dragging()) {
2351
from->accessibility_drop();
2352
} else {
2353
from->accessibility_drag();
2354
}
2355
}
2356
2357
if (p_event->is_action_pressed(SNAME("ui_up"), true, true)) {
2358
next = from->_get_focus_neighbor(SIDE_TOP);
2359
}
2360
2361
if (p_event->is_action_pressed(SNAME("ui_left"), true, true)) {
2362
next = from->_get_focus_neighbor(SIDE_LEFT);
2363
}
2364
2365
if (p_event->is_action_pressed(SNAME("ui_right"), true, true)) {
2366
next = from->_get_focus_neighbor(SIDE_RIGHT);
2367
}
2368
2369
if (p_event->is_action_pressed(SNAME("ui_down"), true, true)) {
2370
next = from->_get_focus_neighbor(SIDE_BOTTOM);
2371
}
2372
}
2373
if (next) {
2374
next->grab_focus();
2375
set_input_as_handled();
2376
}
2377
}
2378
}
2379
}
2380
2381
void Viewport::_perform_drop(Control *p_control) {
2382
gui_perform_drop_at(p_control ? p_control->get_local_mouse_position() : Vector2(), p_control);
2383
}
2384
2385
void Viewport::gui_perform_drop_at(const Point2 &p_pos, Control *p_control) {
2386
// Without any arguments, simply cancel Drag and Drop.
2387
if (p_control) {
2388
gui.drag_successful = _gui_drop(p_control, p_pos, false);
2389
} else {
2390
gui.drag_successful = false;
2391
}
2392
2393
Control *drag_preview = _gui_get_drag_preview();
2394
if (drag_preview) {
2395
memdelete(drag_preview);
2396
gui.drag_preview_id = ObjectID();
2397
}
2398
2399
Viewport *section_root = get_section_root_viewport();
2400
section_root->gui.drag_data = Variant();
2401
gui.dragging = false;
2402
gui.drag_description = String();
2403
section_root->gui.global_dragging = false;
2404
gui.drag_mouse_over = nullptr;
2405
Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_END);
2406
// Display the new cursor shape instantly.
2407
update_mouse_cursor_state();
2408
}
2409
2410
void Viewport::_gui_cleanup_internal_state(Ref<InputEvent> p_event) {
2411
ERR_FAIL_COND(p_event.is_null());
2412
2413
Ref<InputEventMouseButton> mb = p_event;
2414
if (mb.is_valid()) {
2415
if (!mb->is_pressed()) {
2416
gui.mouse_focus_mask.clear_flag(mouse_button_to_mask(mb->get_button_index())); // Remove from mask.
2417
}
2418
}
2419
}
2420
2421
List<Control *>::Element *Viewport::_gui_add_root_control(Control *p_control) {
2422
gui.roots_order_dirty = true;
2423
return gui.roots.push_back(p_control);
2424
}
2425
2426
void Viewport::gui_set_root_order_dirty() {
2427
ERR_MAIN_THREAD_GUARD;
2428
gui.roots_order_dirty = true;
2429
}
2430
2431
void Viewport::_gui_force_drag_start() {
2432
Viewport *section_root = get_section_root_viewport();
2433
section_root->gui.global_dragging = true;
2434
}
2435
2436
void Viewport::_gui_force_drag_cancel() {
2437
Viewport *section_root = get_section_root_viewport();
2438
section_root->gui.global_dragging = false;
2439
}
2440
2441
void Viewport::_gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control) {
2442
ERR_FAIL_COND_MSG(p_data.get_type() == Variant::NIL, "Drag data must be a value.");
2443
2444
Viewport *section_root = get_section_root_viewport();
2445
ERR_FAIL_COND(!section_root->gui.global_dragging);
2446
2447
gui.dragging = true;
2448
section_root->gui.drag_data = p_data;
2449
gui.mouse_focus = nullptr;
2450
gui.mouse_focus_mask.clear();
2451
2452
if (p_control) {
2453
_gui_set_drag_preview(p_base, p_control);
2454
}
2455
Viewport::_propagate_drag_notification(section_root, NOTIFICATION_DRAG_BEGIN);
2456
}
2457
2458
void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) {
2459
ERR_FAIL_NULL(p_control);
2460
ERR_FAIL_COND(p_control->is_inside_tree());
2461
ERR_FAIL_COND(p_control->get_parent() != nullptr);
2462
2463
Control *drag_preview = _gui_get_drag_preview();
2464
if (drag_preview) {
2465
memdelete(drag_preview);
2466
}
2467
p_control->set_as_top_level(true);
2468
p_control->set_position(gui.last_mouse_pos);
2469
p_base->get_root_parent_control()->add_child(p_control); // Add as child of viewport.
2470
p_control->move_to_front();
2471
2472
gui.drag_preview_id = p_control->get_instance_id();
2473
}
2474
2475
Control *Viewport::_gui_get_drag_preview() {
2476
if (gui.drag_preview_id.is_null()) {
2477
return nullptr;
2478
} else {
2479
Control *drag_preview = ObjectDB::get_instance<Control>(gui.drag_preview_id);
2480
if (!drag_preview) {
2481
ERR_PRINT("Don't free the control set as drag preview.");
2482
gui.drag_preview_id = ObjectID();
2483
}
2484
return drag_preview;
2485
}
2486
}
2487
2488
void Viewport::_gui_remove_root_control(List<Control *>::Element *RI) {
2489
gui.roots.erase(RI);
2490
}
2491
2492
void Viewport::_gui_unfocus_control(Control *p_control) {
2493
if (gui.key_focus == p_control) {
2494
gui.key_focus->release_focus();
2495
}
2496
}
2497
2498
void Viewport::_gui_hide_control(Control *p_control) {
2499
if (gui.mouse_focus == p_control) {
2500
_drop_mouse_focus();
2501
}
2502
2503
if (gui.key_focus == p_control) {
2504
gui_release_focus();
2505
}
2506
if (gui.mouse_over == p_control || gui.mouse_over_hierarchy.has(p_control)) {
2507
_drop_mouse_over(p_control->get_parent_control());
2508
}
2509
if (gui.drag_mouse_over == p_control) {
2510
gui.drag_mouse_over = nullptr;
2511
}
2512
if (gui.tooltip_control == p_control) {
2513
_gui_cancel_tooltip();
2514
}
2515
}
2516
2517
void Viewport::_gui_remove_control(Control *p_control) {
2518
if (gui.mouse_focus == p_control) {
2519
gui.mouse_focus = nullptr;
2520
gui.mouse_focus_mask.clear();
2521
}
2522
if (gui.key_focus == p_control) {
2523
gui.key_focus = nullptr;
2524
}
2525
if (gui.mouse_over == p_control || gui.mouse_over_hierarchy.has(p_control)) {
2526
_drop_mouse_over(p_control->get_parent_control());
2527
}
2528
if (gui.drag_mouse_over == p_control) {
2529
gui.drag_mouse_over = nullptr;
2530
}
2531
if (gui.tooltip_control == p_control) {
2532
gui.tooltip_control = nullptr;
2533
}
2534
}
2535
2536
void Viewport::canvas_item_top_level_changed() {
2537
_gui_update_mouse_over();
2538
}
2539
2540
void Viewport::_gui_update_mouse_over() {
2541
if (gui.mouse_over == nullptr || gui.mouse_over_hierarchy.is_empty()) {
2542
return;
2543
}
2544
2545
if (gui.sending_mouse_enter_exit_notifications) {
2546
// If notifications are already being sent, delay call to next frame.
2547
if (get_tree() && !get_tree()->is_connected(SNAME("process_frame"), callable_mp(this, &Viewport::_gui_update_mouse_over))) {
2548
get_tree()->connect(SNAME("process_frame"), callable_mp(this, &Viewport::_gui_update_mouse_over), CONNECT_ONE_SHOT);
2549
}
2550
return;
2551
}
2552
2553
// Rebuild the mouse over hierarchy.
2554
LocalVector<Control *> new_mouse_over_hierarchy;
2555
LocalVector<Control *> needs_enter;
2556
LocalVector<int> needs_exit;
2557
2558
CanvasItem *ancestor = gui.mouse_over;
2559
bool removing = false;
2560
bool reached_top = false;
2561
while (ancestor) {
2562
Control *ancestor_control = Object::cast_to<Control>(ancestor);
2563
if (ancestor_control) {
2564
int found = gui.mouse_over_hierarchy.find(ancestor_control);
2565
if (found >= 0) {
2566
// Remove the node if the propagation chain has been broken or it is now MOUSE_FILTER_IGNORE.
2567
if (removing || ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_IGNORE) {
2568
needs_exit.push_back(found);
2569
}
2570
}
2571
if (found == 0) {
2572
if (removing) {
2573
// Stop if the chain has been broken and the top of the hierarchy has been reached.
2574
break;
2575
}
2576
reached_top = true;
2577
}
2578
if (!removing && ancestor_control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
2579
new_mouse_over_hierarchy.push_back(ancestor_control);
2580
// Add the node if it was not found and it is now not MOUSE_FILTER_IGNORE.
2581
if (found < 0) {
2582
needs_enter.push_back(ancestor_control);
2583
}
2584
}
2585
if (ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
2586
// MOUSE_FILTER_STOP breaks the propagation chain.
2587
if (reached_top) {
2588
break;
2589
}
2590
removing = true;
2591
}
2592
}
2593
if (ancestor->is_set_as_top_level()) {
2594
// Top level breaks the propagation chain.
2595
if (reached_top) {
2596
break;
2597
} else {
2598
removing = true;
2599
ancestor = Object::cast_to<CanvasItem>(ancestor->get_parent());
2600
continue;
2601
}
2602
}
2603
ancestor = ancestor->get_parent_item();
2604
}
2605
if (needs_exit.is_empty() && needs_enter.is_empty()) {
2606
return;
2607
}
2608
2609
gui.sending_mouse_enter_exit_notifications = true;
2610
2611
// Send Mouse Exit Self notification.
2612
if (gui.mouse_over && !needs_exit.is_empty() && needs_exit[0] == (int)gui.mouse_over_hierarchy.size() - 1) {
2613
gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
2614
gui.mouse_over = nullptr;
2615
}
2616
2617
// Send Mouse Exit notifications.
2618
for (int exit_control_index : needs_exit) {
2619
gui.mouse_over_hierarchy[exit_control_index]->notification(Control::NOTIFICATION_MOUSE_EXIT);
2620
}
2621
2622
// Update the mouse over hierarchy.
2623
gui.mouse_over_hierarchy.resize(new_mouse_over_hierarchy.size());
2624
for (int i = 0; i < (int)new_mouse_over_hierarchy.size(); i++) {
2625
gui.mouse_over_hierarchy[i] = new_mouse_over_hierarchy[new_mouse_over_hierarchy.size() - 1 - i];
2626
}
2627
2628
// Send Mouse Enter notifications.
2629
for (int i = needs_enter.size() - 1; i >= 0; i--) {
2630
needs_enter[i]->notification(Control::NOTIFICATION_MOUSE_ENTER);
2631
}
2632
2633
gui.sending_mouse_enter_exit_notifications = false;
2634
}
2635
2636
Window *Viewport::get_base_window() {
2637
ERR_READ_THREAD_GUARD_V(nullptr);
2638
ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
2639
2640
Viewport *v = this;
2641
Window *w = Object::cast_to<Window>(v);
2642
while (!w) {
2643
v = v->get_parent_viewport();
2644
w = Object::cast_to<Window>(v);
2645
}
2646
2647
return w;
2648
}
2649
2650
void Viewport::_gui_remove_focus_for_window(Node *p_window) {
2651
if (get_base_window() == p_window) {
2652
gui_release_focus();
2653
}
2654
}
2655
2656
bool Viewport::_gui_control_has_focus(const Control *p_control) {
2657
return gui.key_focus == p_control;
2658
}
2659
2660
void Viewport::_gui_control_grab_focus(Control *p_control) {
2661
if (gui.key_focus && gui.key_focus == p_control) {
2662
// No need for change.
2663
return;
2664
}
2665
get_tree()->call_group("_viewports", "_gui_remove_focus_for_window", get_base_window());
2666
if (p_control->is_inside_tree() && p_control->get_viewport() == this) {
2667
gui.key_focus = p_control;
2668
emit_signal(SNAME("gui_focus_changed"), p_control);
2669
p_control->notification(Control::NOTIFICATION_FOCUS_ENTER);
2670
p_control->queue_redraw();
2671
}
2672
}
2673
2674
void Viewport::_gui_accept_event() {
2675
if (is_inside_tree()) {
2676
set_input_as_handled();
2677
}
2678
}
2679
2680
void Viewport::_drop_mouse_focus() {
2681
Control *c = gui.mouse_focus;
2682
BitField<MouseButtonMask> mask = gui.mouse_focus_mask;
2683
gui.mouse_focus = nullptr;
2684
gui.mouse_focus_mask.clear();
2685
2686
if (!c) {
2687
return;
2688
}
2689
2690
for (int i = 0; i < 3; i++) {
2691
if ((int)mask & (1 << i)) {
2692
Ref<InputEventMouseButton> mb;
2693
mb.instantiate();
2694
mb->set_position(c->get_local_mouse_position());
2695
mb->set_global_position(c->get_local_mouse_position());
2696
mb->set_button_index(MouseButton(i + 1));
2697
mb->set_pressed(false);
2698
mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
2699
c->_call_gui_input(mb);
2700
}
2701
}
2702
}
2703
2704
void Viewport::_drop_physics_mouseover(bool p_paused_only) {
2705
#ifndef PHYSICS_2D_DISABLED
2706
_cleanup_mouseover_colliders(true, p_paused_only);
2707
#endif // PHYSICS_2D_DISABLED
2708
2709
#ifndef PHYSICS_3D_DISABLED
2710
if (physics_object_over.is_valid()) {
2711
CollisionObject3D *co = ObjectDB::get_instance<CollisionObject3D>(physics_object_over);
2712
if (co) {
2713
if (!co->is_inside_tree()) {
2714
physics_object_over = ObjectID();
2715
physics_object_capture = ObjectID();
2716
} else if (!(p_paused_only && co->can_process())) {
2717
co->_mouse_exit();
2718
physics_object_over = ObjectID();
2719
physics_object_capture = ObjectID();
2720
}
2721
}
2722
}
2723
#endif // PHYSICS_3D_DISABLED
2724
}
2725
2726
void Viewport::_gui_grab_click_focus(Control *p_control) {
2727
gui.mouse_click_grabber = p_control;
2728
callable_mp(this, &Viewport::_post_gui_grab_click_focus).call_deferred();
2729
}
2730
2731
void Viewport::_post_gui_grab_click_focus() {
2732
Control *focus_grabber = gui.mouse_click_grabber;
2733
if (!focus_grabber) {
2734
// Redundant grab requests were made.
2735
return;
2736
}
2737
gui.mouse_click_grabber = nullptr;
2738
2739
if (gui.mouse_focus) {
2740
if (gui.mouse_focus == focus_grabber) {
2741
return;
2742
}
2743
2744
BitField<MouseButtonMask> mask = gui.mouse_focus_mask;
2745
Point2 click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
2746
2747
for (int i = 0; i < 3; i++) {
2748
if ((int)mask & (1 << i)) {
2749
Ref<InputEventMouseButton> mb;
2750
mb.instantiate();
2751
2752
// Send unclick.
2753
2754
mb->set_position(click);
2755
mb->set_button_index(MouseButton(i + 1));
2756
mb->set_pressed(false);
2757
mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
2758
gui.mouse_focus->_call_gui_input(mb);
2759
}
2760
}
2761
2762
gui.mouse_focus = focus_grabber;
2763
click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
2764
2765
for (int i = 0; i < 3; i++) {
2766
if ((int)mask & (1 << i)) {
2767
Ref<InputEventMouseButton> mb;
2768
mb.instantiate();
2769
2770
// Send click.
2771
2772
mb->set_position(click);
2773
mb->set_button_index(MouseButton(i + 1));
2774
mb->set_pressed(true);
2775
mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
2776
callable_mp(gui.mouse_focus, &Control::_call_gui_input).call_deferred(mb);
2777
}
2778
}
2779
}
2780
}
2781
2782
///////////////////////////////
2783
2784
void Viewport::push_text_input(const String &p_text) {
2785
ERR_MAIN_THREAD_GUARD;
2786
if (gui.subwindow_focused) {
2787
gui.subwindow_focused->push_text_input(p_text);
2788
return;
2789
}
2790
2791
if (gui.key_focus) {
2792
gui.key_focus->call("set_text", p_text);
2793
}
2794
}
2795
2796
Viewport::SubWindowResize Viewport::_sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point) {
2797
if (p_subwindow->get_flag(Window::FLAG_BORDERLESS) || p_subwindow->get_flag(Window::FLAG_RESIZE_DISABLED)) {
2798
return SUB_WINDOW_RESIZE_DISABLED;
2799
}
2800
2801
Rect2i r = Rect2i(p_subwindow->get_position(), p_subwindow->get_size());
2802
2803
int title_height = p_subwindow->theme_cache.title_height;
2804
2805
r.position.y -= title_height;
2806
r.size.y += title_height;
2807
2808
if (r.has_point(p_point)) {
2809
return SUB_WINDOW_RESIZE_DISABLED; // It's inside, so no resize.
2810
}
2811
2812
int dist_x = p_point.x < r.position.x ? (p_point.x - r.position.x) : (p_point.x > (r.position.x + r.size.x) ? (p_point.x - (r.position.x + r.size.x)) : 0);
2813
int dist_y = p_point.y < r.position.y ? (p_point.y - r.position.y) : (p_point.y > (r.position.y + r.size.y) ? (p_point.y - (r.position.y + r.size.y)) : 0);
2814
2815
int limit = p_subwindow->theme_cache.resize_margin;
2816
2817
if (Math::abs(dist_x) > limit) {
2818
return SUB_WINDOW_RESIZE_DISABLED;
2819
}
2820
2821
if (Math::abs(dist_y) > limit) {
2822
return SUB_WINDOW_RESIZE_DISABLED;
2823
}
2824
2825
if (dist_x < 0 && dist_y < 0) {
2826
return SUB_WINDOW_RESIZE_TOP_LEFT;
2827
}
2828
2829
if (dist_x == 0 && dist_y < 0) {
2830
return SUB_WINDOW_RESIZE_TOP;
2831
}
2832
2833
if (dist_x > 0 && dist_y < 0) {
2834
return SUB_WINDOW_RESIZE_TOP_RIGHT;
2835
}
2836
2837
if (dist_x < 0 && dist_y == 0) {
2838
return SUB_WINDOW_RESIZE_LEFT;
2839
}
2840
2841
if (dist_x > 0 && dist_y == 0) {
2842
return SUB_WINDOW_RESIZE_RIGHT;
2843
}
2844
2845
if (dist_x < 0 && dist_y > 0) {
2846
return SUB_WINDOW_RESIZE_BOTTOM_LEFT;
2847
}
2848
2849
if (dist_x == 0 && dist_y > 0) {
2850
return SUB_WINDOW_RESIZE_BOTTOM;
2851
}
2852
2853
if (dist_x > 0 && dist_y > 0) {
2854
return SUB_WINDOW_RESIZE_BOTTOM_RIGHT;
2855
}
2856
2857
return SUB_WINDOW_RESIZE_DISABLED;
2858
}
2859
2860
bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
2861
if (gui.subwindow_drag != SUB_WINDOW_DRAG_DISABLED) {
2862
ERR_FAIL_NULL_V(gui.currently_dragged_subwindow, false);
2863
2864
Ref<InputEventMouseButton> mb = p_event;
2865
if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
2866
if (gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE) {
2867
if (gui.subwindow_drag_close_rect.has_point(mb->get_position())) {
2868
// Close window.
2869
gui.currently_dragged_subwindow->_event_callback(DisplayServer::WINDOW_EVENT_CLOSE_REQUEST);
2870
}
2871
}
2872
gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
2873
if (gui.currently_dragged_subwindow != nullptr) { // May have been erased.
2874
_sub_window_update(gui.currently_dragged_subwindow);
2875
gui.currently_dragged_subwindow = nullptr;
2876
}
2877
}
2878
2879
Ref<InputEventMouseMotion> mm = p_event;
2880
if (mm.is_valid()) {
2881
if (gui.subwindow_drag == SUB_WINDOW_DRAG_MOVE) {
2882
Vector2 diff = mm->get_position() - gui.subwindow_drag_from;
2883
Rect2i new_rect(gui.subwindow_drag_pos + diff, gui.currently_dragged_subwindow->get_size());
2884
2885
if (gui.currently_dragged_subwindow->is_clamped_to_embedder()) {
2886
new_rect = gui.currently_dragged_subwindow->fit_rect_in_parent(new_rect, get_visible_rect());
2887
}
2888
2889
gui.currently_dragged_subwindow->_rect_changed_callback(new_rect);
2890
2891
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
2892
DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_MOVE);
2893
}
2894
}
2895
if (gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE) {
2896
gui.subwindow_drag_close_inside = gui.subwindow_drag_close_rect.has_point(mm->get_position());
2897
}
2898
if (gui.subwindow_drag == SUB_WINDOW_DRAG_RESIZE) {
2899
Vector2i diff = mm->get_position() - gui.subwindow_drag_from;
2900
Size2i min_size = gui.currently_dragged_subwindow->get_min_size();
2901
Size2i min_size_clamped = gui.currently_dragged_subwindow->get_clamped_minimum_size();
2902
2903
min_size_clamped = min_size_clamped.maxi(1);
2904
2905
Rect2i r = gui.subwindow_resize_from_rect;
2906
2907
Size2i limit = r.size - min_size_clamped;
2908
2909
switch (gui.subwindow_resize_mode) {
2910
case SUB_WINDOW_RESIZE_TOP_LEFT: {
2911
diff.x = MIN(diff.x, limit.x);
2912
diff.y = MIN(diff.y, limit.y);
2913
r.position += diff;
2914
r.size -= diff;
2915
} break;
2916
case SUB_WINDOW_RESIZE_TOP: {
2917
diff.x = 0;
2918
diff.y = MIN(diff.y, limit.y);
2919
r.position += diff;
2920
r.size -= diff;
2921
} break;
2922
case SUB_WINDOW_RESIZE_TOP_RIGHT: {
2923
diff.x = MAX(diff.x, -limit.x);
2924
diff.y = MIN(diff.y, limit.y);
2925
r.position.y += diff.y;
2926
r.size.y -= diff.y;
2927
r.size.x += diff.x;
2928
} break;
2929
case SUB_WINDOW_RESIZE_LEFT: {
2930
diff.x = MIN(diff.x, limit.x);
2931
diff.y = 0;
2932
r.position += diff;
2933
r.size -= diff;
2934
2935
} break;
2936
case SUB_WINDOW_RESIZE_RIGHT: {
2937
diff.x = MAX(diff.x, -limit.x);
2938
r.size.x += diff.x;
2939
} break;
2940
case SUB_WINDOW_RESIZE_BOTTOM_LEFT: {
2941
diff.x = MIN(diff.x, limit.x);
2942
diff.y = MAX(diff.y, -limit.y);
2943
r.position.x += diff.x;
2944
r.size.x -= diff.x;
2945
r.size.y += diff.y;
2946
2947
} break;
2948
case SUB_WINDOW_RESIZE_BOTTOM: {
2949
diff.y = MAX(diff.y, -limit.y);
2950
r.size.y += diff.y;
2951
} break;
2952
case SUB_WINDOW_RESIZE_BOTTOM_RIGHT: {
2953
diff.x = MAX(diff.x, -limit.x);
2954
diff.y = MAX(diff.y, -limit.y);
2955
r.size += diff;
2956
2957
} break;
2958
default: {
2959
}
2960
}
2961
2962
Size2i max_size = gui.currently_dragged_subwindow->get_max_size();
2963
if ((max_size.x > 0 || max_size.y > 0) && (max_size.x >= min_size.x && max_size.y >= min_size.y)) {
2964
max_size = max_size.maxi(1);
2965
2966
if (r.size.x > max_size.x) {
2967
r.size.x = max_size.x;
2968
}
2969
if (r.size.y > max_size.y) {
2970
r.size.y = max_size.y;
2971
}
2972
}
2973
2974
gui.currently_dragged_subwindow->_rect_changed_callback(r);
2975
}
2976
2977
if (gui.currently_dragged_subwindow) { // May have been erased.
2978
_sub_window_update(gui.currently_dragged_subwindow);
2979
}
2980
}
2981
2982
return true; // Handled.
2983
}
2984
Ref<InputEventMouseButton> mb = p_event;
2985
// If the event is a mouse button, we need to check whether another window was clicked.
2986
2987
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
2988
Window *click_on_window = nullptr;
2989
for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
2990
SubWindow sw = gui.sub_windows.write[i];
2991
2992
// Clicked inside window?
2993
2994
Rect2i r = Rect2i(sw.window->get_position(), sw.window->get_size());
2995
2996
if (!sw.window->get_flag(Window::FLAG_BORDERLESS)) {
2997
// Check top bar.
2998
int title_height = sw.window->theme_cache.title_height;
2999
Rect2i title_bar = r;
3000
title_bar.position.y -= title_height;
3001
title_bar.size.y = title_height;
3002
3003
if (title_bar.size.y > 0 && title_bar.has_point(mb->get_position())) {
3004
click_on_window = sw.window;
3005
3006
int close_h_ofs = sw.window->theme_cache.close_h_offset;
3007
int close_v_ofs = sw.window->theme_cache.close_v_offset;
3008
Ref<Texture2D> close_icon = sw.window->theme_cache.close;
3009
3010
Rect2 close_rect;
3011
close_rect.position = Vector2(r.position.x + r.size.x - close_h_ofs, r.position.y - close_v_ofs);
3012
close_rect.size = close_icon->get_size();
3013
3014
if (gui.subwindow_focused != sw.window) {
3015
// Refocus.
3016
_sub_window_grab_focus(sw.window);
3017
}
3018
3019
if (close_rect.has_point(mb->get_position())) {
3020
gui.subwindow_drag = SUB_WINDOW_DRAG_CLOSE;
3021
gui.subwindow_drag_close_inside = true; // Starts inside.
3022
gui.subwindow_drag_close_rect = close_rect;
3023
} else {
3024
gui.subwindow_drag = SUB_WINDOW_DRAG_MOVE;
3025
}
3026
3027
gui.subwindow_drag_from = mb->get_position();
3028
gui.subwindow_drag_pos = sw.window->get_position();
3029
3030
_sub_window_update(sw.window);
3031
} else {
3032
gui.subwindow_resize_mode = _sub_window_get_resize_margin(sw.window, mb->get_position());
3033
if (gui.subwindow_resize_mode != SUB_WINDOW_RESIZE_DISABLED) {
3034
if (gui.subwindow_focused != sw.window) {
3035
// Refocus.
3036
_sub_window_grab_focus(sw.window);
3037
}
3038
3039
gui.subwindow_resize_from_rect = r;
3040
gui.subwindow_drag_from = mb->get_position();
3041
gui.subwindow_drag = SUB_WINDOW_DRAG_RESIZE;
3042
click_on_window = sw.window;
3043
}
3044
}
3045
}
3046
if (!click_on_window && r.has_point(mb->get_position())) {
3047
// Clicked, see if it needs to fetch focus.
3048
if (gui.subwindow_focused != sw.window) {
3049
// Refocus.
3050
_sub_window_grab_focus(sw.window);
3051
}
3052
3053
click_on_window = sw.window;
3054
}
3055
3056
if (click_on_window) {
3057
break;
3058
}
3059
}
3060
3061
gui.currently_dragged_subwindow = click_on_window;
3062
3063
if (!click_on_window && gui.subwindow_focused) {
3064
// No window found and clicked, remove focus.
3065
_sub_window_grab_focus(nullptr);
3066
}
3067
}
3068
3069
if (gui.subwindow_focused) {
3070
Ref<InputEventMouseMotion> mm = p_event;
3071
if (mm.is_valid()) {
3072
SubWindowResize resize = _sub_window_get_resize_margin(gui.subwindow_focused, mm->get_position());
3073
if (resize != SUB_WINDOW_RESIZE_DISABLED) {
3074
DisplayServer::CursorShape shapes[SUB_WINDOW_RESIZE_MAX] = {
3075
DisplayServer::CURSOR_ARROW,
3076
DisplayServer::CURSOR_FDIAGSIZE,
3077
DisplayServer::CURSOR_VSIZE,
3078
DisplayServer::CURSOR_BDIAGSIZE,
3079
DisplayServer::CURSOR_HSIZE,
3080
DisplayServer::CURSOR_HSIZE,
3081
DisplayServer::CURSOR_BDIAGSIZE,
3082
DisplayServer::CURSOR_VSIZE,
3083
DisplayServer::CURSOR_FDIAGSIZE
3084
};
3085
3086
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
3087
DisplayServer::get_singleton()->cursor_set_shape(shapes[resize]);
3088
}
3089
3090
return true; // Reserved for showing the resize cursor.
3091
}
3092
}
3093
}
3094
3095
if (gui.subwindow_drag != SUB_WINDOW_DRAG_DISABLED) {
3096
return true; // Dragging, don't pass the event.
3097
}
3098
3099
if (!gui.subwindow_focused) {
3100
// No window focus, check for unfocusable windows under the cursor.
3101
Ref<InputEventMouse> me = p_event;
3102
if (me.is_valid()) {
3103
for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
3104
const SubWindow &sw = gui.sub_windows[i];
3105
if (!sw.window->get_flag(Window::FLAG_NO_FOCUS) || sw.window->get_flag(Window::FLAG_MOUSE_PASSTHROUGH)) {
3106
continue;
3107
}
3108
Rect2i r = Rect2i(sw.window->get_position(), sw.window->get_size());
3109
if (r.has_point(me->get_position())) {
3110
Transform2D window_ofs;
3111
window_ofs.set_origin(-sw.window->get_position());
3112
Ref<InputEvent> ev = p_event->xformed_by(window_ofs);
3113
sw.window->_window_input(ev);
3114
return true;
3115
}
3116
}
3117
}
3118
return false;
3119
}
3120
3121
Transform2D window_ofs;
3122
window_ofs.set_origin(-gui.subwindow_focused->get_position());
3123
3124
Ref<InputEvent> ev = p_event->xformed_by(window_ofs);
3125
3126
gui.subwindow_focused->_window_input(ev);
3127
3128
return true;
3129
}
3130
3131
void Viewport::_window_start_drag(Window *p_window) {
3132
int index = _sub_window_find(p_window);
3133
ERR_FAIL_COND(index == -1);
3134
3135
SubWindow sw = gui.sub_windows.write[index];
3136
3137
if (gui.subwindow_focused != sw.window) {
3138
// Refocus.
3139
_sub_window_grab_focus(sw.window);
3140
}
3141
3142
gui.subwindow_drag = SUB_WINDOW_DRAG_MOVE;
3143
gui.subwindow_drag_from = get_mouse_position();
3144
gui.subwindow_drag_pos = sw.window->get_position();
3145
gui.currently_dragged_subwindow = sw.window;
3146
3147
_sub_window_update(sw.window);
3148
}
3149
3150
void Viewport::_window_start_resize(SubWindowResize p_edge, Window *p_window) {
3151
int index = _sub_window_find(p_window);
3152
ERR_FAIL_COND(index == -1);
3153
3154
SubWindow sw = gui.sub_windows.write[index];
3155
Rect2i r = Rect2i(sw.window->get_position(), sw.window->get_size());
3156
3157
if (gui.subwindow_focused != sw.window) {
3158
// Refocus.
3159
_sub_window_grab_focus(sw.window);
3160
}
3161
3162
gui.subwindow_drag = SUB_WINDOW_DRAG_RESIZE;
3163
gui.subwindow_resize_mode = p_edge;
3164
gui.subwindow_resize_from_rect = r;
3165
gui.subwindow_drag_from = get_mouse_position();
3166
gui.subwindow_drag_pos = sw.window->get_position();
3167
gui.currently_dragged_subwindow = sw.window;
3168
3169
_sub_window_update(sw.window);
3170
}
3171
3172
void Viewport::_update_mouse_over() {
3173
// Update gui.mouse_over and gui.subwindow_over in all Viewports.
3174
// Send necessary mouse_enter/mouse_exit signals and the MOUSE_ENTER/MOUSE_EXIT notifications for every Viewport in the SceneTree.
3175
3176
if (is_attached_in_viewport()) {
3177
// Execute this function only, when it is processed by a native Window or a SubViewport, that has no SubViewportContainer as parent.
3178
return;
3179
}
3180
3181
if (get_tree()->get_root()->is_embedding_subwindows() || is_sub_viewport()) {
3182
// Use embedder logic for calculating mouse position.
3183
_update_mouse_over(gui.last_mouse_pos);
3184
} else {
3185
// Native Window: Use DisplayServer logic for calculating mouse position.
3186
Window *receiving_window = get_tree()->get_root()->gui.windowmanager_window_over;
3187
if (!receiving_window) {
3188
return;
3189
}
3190
3191
Vector2 pos = DisplayServer::get_singleton()->mouse_get_position() - receiving_window->get_position();
3192
pos = receiving_window->get_final_transform().affine_inverse().xform(pos);
3193
3194
receiving_window->_update_mouse_over(pos);
3195
}
3196
}
3197
3198
void Viewport::_update_mouse_over(Vector2 p_pos) {
3199
gui.last_mouse_pos = p_pos; // Necessary, because mouse cursor can be over Viewports that are not reached by the InputEvent.
3200
// Look for embedded windows at mouse position.
3201
if (is_embedding_subwindows()) {
3202
for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
3203
Window *sw = gui.sub_windows[i].window;
3204
Rect2 swrect = Rect2(sw->get_position(), sw->get_size());
3205
Rect2 swrect_border = swrect;
3206
3207
if (!sw->get_flag(Window::FLAG_BORDERLESS)) {
3208
int title_height = sw->theme_cache.title_height;
3209
int margin = sw->theme_cache.resize_margin;
3210
swrect_border.position.y -= title_height + margin;
3211
swrect_border.size.y += title_height + margin * 2;
3212
swrect_border.position.x -= margin;
3213
swrect_border.size.x += margin * 2;
3214
}
3215
3216
if (swrect_border.has_point(p_pos)) {
3217
if (gui.mouse_over) {
3218
_drop_mouse_over();
3219
} else if (!gui.subwindow_over) {
3220
_drop_physics_mouseover();
3221
}
3222
if (swrect.has_point(p_pos)) {
3223
if (sw != gui.subwindow_over) {
3224
if (gui.subwindow_over) {
3225
gui.subwindow_over->_mouse_leave_viewport();
3226
}
3227
gui.subwindow_over = sw;
3228
}
3229
if (!sw->is_input_disabled()) {
3230
sw->_update_mouse_over(sw->get_final_transform().affine_inverse().xform(p_pos - sw->get_position()));
3231
}
3232
} else {
3233
if (gui.subwindow_over) {
3234
gui.subwindow_over->_mouse_leave_viewport();
3235
gui.subwindow_over = nullptr;
3236
}
3237
}
3238
return;
3239
}
3240
}
3241
3242
if (gui.subwindow_over) {
3243
// Take care of moving mouse out of any embedded Window.
3244
gui.subwindow_over->_mouse_leave_viewport();
3245
gui.subwindow_over = nullptr;
3246
}
3247
}
3248
3249
// Look for Controls at mouse position.
3250
Control *over = gui_find_control(p_pos);
3251
get_section_root_viewport()->gui.target_control = over;
3252
bool notify_embedded_viewports = false;
3253
if (over != gui.mouse_over || (!over && !gui.mouse_over_hierarchy.is_empty())) {
3254
// Find the common ancestor of `gui.mouse_over` and `over`.
3255
Control *common_ancestor = nullptr;
3256
LocalVector<Control *> over_ancestors;
3257
3258
if (over) {
3259
// Get all ancestors that the mouse is currently over and need an enter signal.
3260
CanvasItem *ancestor = over;
3261
while (ancestor) {
3262
Control *ancestor_control = Object::cast_to<Control>(ancestor);
3263
if (ancestor_control) {
3264
if (ancestor_control->get_mouse_filter_with_override() != Control::MOUSE_FILTER_IGNORE) {
3265
int found = gui.mouse_over_hierarchy.find(ancestor_control);
3266
if (found >= 0) {
3267
common_ancestor = gui.mouse_over_hierarchy[found];
3268
break;
3269
}
3270
over_ancestors.push_back(ancestor_control);
3271
}
3272
if (ancestor_control->get_mouse_filter_with_override() == Control::MOUSE_FILTER_STOP) {
3273
// MOUSE_FILTER_STOP breaks the propagation chain.
3274
break;
3275
}
3276
}
3277
if (ancestor->is_set_as_top_level()) {
3278
// Top level breaks the propagation chain.
3279
break;
3280
}
3281
ancestor = ancestor->get_parent_item();
3282
}
3283
}
3284
3285
if (gui.mouse_over || !gui.mouse_over_hierarchy.is_empty()) {
3286
// Send Mouse Exit Self and Mouse Exit notifications.
3287
_drop_mouse_over(common_ancestor);
3288
} else {
3289
_drop_physics_mouseover();
3290
}
3291
3292
if (over) {
3293
gui.mouse_over = over;
3294
gui.mouse_over_hierarchy.reserve(gui.mouse_over_hierarchy.size() + over_ancestors.size());
3295
3296
gui.sending_mouse_enter_exit_notifications = true;
3297
3298
// Send Mouse Enter notifications to parents first.
3299
for (int i = over_ancestors.size() - 1; i >= 0; i--) {
3300
gui.mouse_over_hierarchy.push_back(over_ancestors[i]);
3301
over_ancestors[i]->notification(Control::NOTIFICATION_MOUSE_ENTER);
3302
}
3303
3304
// Send Mouse Enter Self notification.
3305
if (gui.mouse_over) {
3306
gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_ENTER_SELF);
3307
}
3308
3309
gui.sending_mouse_enter_exit_notifications = false;
3310
3311
notify_embedded_viewports = true;
3312
}
3313
}
3314
3315
if (over) {
3316
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(over);
3317
if (!c) {
3318
return;
3319
}
3320
Vector2 pos = c->get_global_transform_with_canvas().affine_inverse().xform(p_pos);
3321
if (c->is_stretch_enabled()) {
3322
pos /= c->get_stretch_shrink();
3323
}
3324
3325
for (int i = 0; i < c->get_child_count(); i++) {
3326
SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i));
3327
if (!v || v->is_input_disabled()) {
3328
continue;
3329
}
3330
if (notify_embedded_viewports) {
3331
v->notification(NOTIFICATION_VP_MOUSE_ENTER);
3332
}
3333
v->_update_mouse_over(v->get_final_transform().affine_inverse().xform(pos));
3334
}
3335
3336
Viewport *section_root = get_section_root_viewport();
3337
if (section_root && c->is_mouse_target_enabled()) {
3338
// Evaluating `mouse_target` and adjusting target_control needs to happen
3339
// after `_update_mouse_over` in the SubViewports, because otherwise physics picking
3340
// would not work inside SubViewports.
3341
section_root->gui.target_control = over;
3342
}
3343
}
3344
}
3345
3346
void Viewport::_mouse_leave_viewport() {
3347
if (!is_inside_tree() || is_input_disabled()) {
3348
return;
3349
}
3350
if (gui.subwindow_over) {
3351
gui.subwindow_over->_mouse_leave_viewport();
3352
gui.subwindow_over = nullptr;
3353
} else if (gui.mouse_over) {
3354
_drop_mouse_over();
3355
}
3356
notification(NOTIFICATION_VP_MOUSE_EXIT);
3357
}
3358
3359
void Viewport::_drop_mouse_over(Control *p_until_control) {
3360
if (gui.sending_mouse_enter_exit_notifications) {
3361
// If notifications are already being sent, defer call.
3362
callable_mp(this, &Viewport::_drop_mouse_over).call_deferred(p_until_control);
3363
return;
3364
}
3365
3366
_gui_cancel_tooltip();
3367
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(gui.mouse_over);
3368
if (c) {
3369
for (int i = 0; i < c->get_child_count(); i++) {
3370
SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i));
3371
if (!v) {
3372
continue;
3373
}
3374
v->_mouse_leave_viewport();
3375
}
3376
}
3377
3378
gui.sending_mouse_enter_exit_notifications = true;
3379
if (gui.mouse_over && gui.mouse_over->is_inside_tree()) {
3380
gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT_SELF);
3381
}
3382
Viewport *section_root = get_section_root_viewport();
3383
if (section_root && section_root->gui.target_control == gui.mouse_over) {
3384
section_root->gui.target_control = nullptr;
3385
}
3386
gui.mouse_over = nullptr;
3387
3388
// Send Mouse Exit notifications to children first. Don't send to p_until_control or above.
3389
int notification_until = p_until_control ? gui.mouse_over_hierarchy.find(p_until_control) + 1 : 0;
3390
for (int i = gui.mouse_over_hierarchy.size() - 1; i >= notification_until; i--) {
3391
if (gui.mouse_over_hierarchy[i]->is_inside_tree()) {
3392
gui.mouse_over_hierarchy[i]->notification(Control::NOTIFICATION_MOUSE_EXIT);
3393
}
3394
}
3395
gui.mouse_over_hierarchy.resize(notification_until);
3396
gui.sending_mouse_enter_exit_notifications = false;
3397
}
3398
3399
void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
3400
ERR_MAIN_THREAD_GUARD;
3401
ERR_FAIL_COND(!is_inside_tree());
3402
ERR_FAIL_COND(p_event.is_null());
3403
3404
if (disable_input || disable_input_override) {
3405
return;
3406
}
3407
3408
if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) {
3409
return;
3410
}
3411
3412
local_input_handled = false;
3413
if (!handle_input_locally) {
3414
Viewport *vp = this;
3415
while (true) {
3416
if (Object::cast_to<Window>(vp) || !vp->get_parent()) {
3417
break;
3418
}
3419
vp = vp->get_parent()->get_viewport();
3420
}
3421
vp->local_input_handled = false;
3422
}
3423
3424
Ref<InputEvent> ev;
3425
if (!p_local_coords) {
3426
ev = _make_input_local(p_event);
3427
} else {
3428
ev = p_event;
3429
}
3430
3431
Ref<InputEventMouse> me = ev;
3432
if (me.is_valid()) {
3433
gui.last_mouse_pos = me->get_position();
3434
3435
_update_mouse_over();
3436
}
3437
3438
if (is_embedding_subwindows() && _sub_windows_forward_input(ev)) {
3439
set_input_as_handled();
3440
return;
3441
}
3442
3443
if (!_can_consume_input_events()) {
3444
return;
3445
}
3446
3447
if (!is_input_handled()) {
3448
ERR_FAIL_COND(!is_inside_tree());
3449
get_tree()->_call_input_pause(input_group, SceneTree::CALL_INPUT_TYPE_INPUT, ev, this); //not a bug, must happen before GUI, order is _input -> gui input -> _unhandled input
3450
}
3451
3452
if (!is_input_handled()) {
3453
ERR_FAIL_COND(!is_inside_tree());
3454
_gui_input_event(ev);
3455
} else {
3456
// Cleanup internal GUI state after accepting event during _input().
3457
_gui_cleanup_internal_state(ev);
3458
}
3459
3460
if (!is_input_handled()) {
3461
_push_unhandled_input_internal(ev);
3462
}
3463
3464
event_count++;
3465
}
3466
3467
#ifndef DISABLE_DEPRECATED
3468
void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
3469
ERR_MAIN_THREAD_GUARD;
3470
WARN_DEPRECATED_MSG(R"*(The "push_unhandled_input()" method is deprecated, use "push_input()" instead.)*");
3471
ERR_FAIL_COND(!is_inside_tree());
3472
ERR_FAIL_COND(p_event.is_null());
3473
3474
local_input_handled = false;
3475
3476
if (disable_input || disable_input_override || !_can_consume_input_events()) {
3477
return;
3478
}
3479
3480
if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) {
3481
return;
3482
}
3483
3484
Ref<InputEvent> ev;
3485
if (!p_local_coords) {
3486
ev = _make_input_local(p_event);
3487
} else {
3488
ev = p_event;
3489
}
3490
3491
_push_unhandled_input_internal(ev);
3492
}
3493
#endif // DISABLE_DEPRECATED
3494
3495
void Viewport::_push_unhandled_input_internal(const Ref<InputEvent> &p_event) {
3496
// Shortcut Input.
3497
if (Object::cast_to<InputEventKey>(*p_event) != nullptr || Object::cast_to<InputEventShortcut>(*p_event) != nullptr || Object::cast_to<InputEventJoypadButton>(*p_event) != nullptr) {
3498
ERR_FAIL_COND(!is_inside_tree());
3499
get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, p_event, this);
3500
}
3501
3502
// Unhandled key Input - Used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, and to handle Unicode input with Alt / Ctrl modifiers after handling shortcuts.
3503
if (!is_input_handled() && (Object::cast_to<InputEventKey>(*p_event) != nullptr)) {
3504
ERR_FAIL_COND(!is_inside_tree());
3505
get_tree()->_call_input_pause(unhandled_key_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, p_event, this);
3506
}
3507
3508
// Unhandled Input.
3509
if (!is_input_handled()) {
3510
ERR_FAIL_COND(!is_inside_tree());
3511
get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, p_event, this);
3512
}
3513
3514
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
3515
if (physics_object_picking && !is_input_handled()) {
3516
if (Input::get_singleton()->get_mouse_mode() != Input::MOUSE_MODE_CAPTURED &&
3517
(Object::cast_to<InputEventMouse>(*p_event) ||
3518
Object::cast_to<InputEventScreenDrag>(*p_event) ||
3519
Object::cast_to<InputEventScreenTouch>(*p_event)
3520
3521
)) {
3522
physics_picking_events.push_back(p_event);
3523
set_input_as_handled();
3524
}
3525
}
3526
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
3527
}
3528
3529
void Viewport::notify_mouse_entered() {
3530
if (gui.mouse_in_viewport) {
3531
WARN_PRINT_ED("The Viewport was previously notified that the mouse is in its area. There is no need to notify it at this time.");
3532
return;
3533
}
3534
notification(NOTIFICATION_VP_MOUSE_ENTER);
3535
}
3536
3537
void Viewport::notify_mouse_exited() {
3538
if (!gui.mouse_in_viewport) {
3539
WARN_PRINT_ED("The Viewport was previously notified that the mouse has left its area. There is no need to notify it at this time.");
3540
return;
3541
}
3542
_mouse_leave_viewport();
3543
}
3544
3545
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
3546
void Viewport::set_physics_object_picking(bool p_enable) {
3547
ERR_MAIN_THREAD_GUARD;
3548
physics_object_picking = p_enable;
3549
if (physics_object_picking) {
3550
add_to_group("_picking_viewports");
3551
} else {
3552
physics_picking_events.clear();
3553
if (is_in_group("_picking_viewports")) {
3554
remove_from_group("_picking_viewports");
3555
}
3556
}
3557
}
3558
3559
bool Viewport::get_physics_object_picking() {
3560
ERR_READ_THREAD_GUARD_V(false);
3561
return physics_object_picking;
3562
}
3563
3564
void Viewport::set_physics_object_picking_sort(bool p_enable) {
3565
ERR_MAIN_THREAD_GUARD;
3566
physics_object_picking_sort = p_enable;
3567
}
3568
3569
bool Viewport::get_physics_object_picking_sort() {
3570
ERR_READ_THREAD_GUARD_V(false);
3571
return physics_object_picking_sort;
3572
}
3573
3574
void Viewport::set_physics_object_picking_first_only(bool p_enable) {
3575
physics_object_picking_first_only = p_enable;
3576
}
3577
3578
bool Viewport::get_physics_object_picking_first_only() {
3579
return physics_object_picking_first_only;
3580
}
3581
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
3582
3583
Vector2 Viewport::get_camera_coords(const Vector2 &p_viewport_coords) const {
3584
ERR_READ_THREAD_GUARD_V(Vector2());
3585
Transform2D xf = stretch_transform * global_canvas_transform;
3586
return xf.xform(p_viewport_coords);
3587
}
3588
3589
Vector2 Viewport::get_camera_rect_size() const {
3590
ERR_READ_THREAD_GUARD_V(Vector2());
3591
return size;
3592
}
3593
3594
void Viewport::set_disable_input(bool p_disable) {
3595
ERR_MAIN_THREAD_GUARD;
3596
if (p_disable == disable_input) {
3597
return;
3598
}
3599
if (p_disable && !disable_input_override) {
3600
_drop_mouse_focus();
3601
_mouse_leave_viewport();
3602
_gui_cancel_tooltip();
3603
}
3604
disable_input = p_disable;
3605
}
3606
3607
bool Viewport::is_input_disabled() const {
3608
ERR_READ_THREAD_GUARD_V(false);
3609
return disable_input;
3610
}
3611
3612
void Viewport::set_disable_input_override(bool p_disable) {
3613
ERR_MAIN_THREAD_GUARD;
3614
if (p_disable == disable_input_override) {
3615
return;
3616
}
3617
if (p_disable && !disable_input) {
3618
_drop_mouse_focus();
3619
_mouse_leave_viewport();
3620
_gui_cancel_tooltip();
3621
}
3622
disable_input_override = p_disable;
3623
}
3624
3625
String Viewport::gui_get_drag_description() const {
3626
ERR_READ_THREAD_GUARD_V(String());
3627
if (get_section_root_viewport()->gui.drag_description.is_empty()) {
3628
return RTR("Drag-and-drop data");
3629
} else {
3630
return get_section_root_viewport()->gui.drag_description;
3631
}
3632
}
3633
3634
void Viewport::gui_set_drag_description(const String &p_description) {
3635
gui.drag_description = p_description;
3636
}
3637
3638
Variant Viewport::gui_get_drag_data() const {
3639
ERR_READ_THREAD_GUARD_V(Variant());
3640
return get_section_root_viewport()->gui.drag_data;
3641
}
3642
3643
PackedStringArray Viewport::get_configuration_warnings() const {
3644
ERR_MAIN_THREAD_GUARD_V(PackedStringArray());
3645
PackedStringArray warnings = Node::get_configuration_warnings();
3646
3647
if (size.x <= 1 || size.y <= 1) {
3648
warnings.push_back(RTR("The Viewport size must be greater than or equal to 2 pixels on both dimensions to render anything."));
3649
}
3650
return warnings;
3651
}
3652
3653
void Viewport::gui_reset_canvas_sort_index() {
3654
ERR_MAIN_THREAD_GUARD;
3655
gui.canvas_sort_index = 0;
3656
}
3657
3658
int Viewport::gui_get_canvas_sort_index() {
3659
ERR_MAIN_THREAD_GUARD_V(0);
3660
return gui.canvas_sort_index++;
3661
}
3662
3663
void Viewport::gui_release_focus() {
3664
ERR_MAIN_THREAD_GUARD;
3665
if (gui.key_focus) {
3666
Control *f = gui.key_focus;
3667
gui.key_focus = nullptr;
3668
f->notification(Control::NOTIFICATION_FOCUS_EXIT, true);
3669
f->queue_redraw();
3670
}
3671
}
3672
3673
Control *Viewport::gui_get_focus_owner() const {
3674
ERR_READ_THREAD_GUARD_V(nullptr);
3675
return gui.key_focus;
3676
}
3677
3678
Control *Viewport::gui_get_hovered_control() const {
3679
ERR_READ_THREAD_GUARD_V(nullptr);
3680
return gui.mouse_over;
3681
}
3682
3683
void Viewport::set_msaa_2d(MSAA p_msaa) {
3684
ERR_MAIN_THREAD_GUARD;
3685
ERR_FAIL_INDEX(p_msaa, MSAA_MAX);
3686
if (msaa_2d == p_msaa) {
3687
return;
3688
}
3689
msaa_2d = p_msaa;
3690
RS::get_singleton()->viewport_set_msaa_2d(viewport, RS::ViewportMSAA(p_msaa));
3691
}
3692
3693
Viewport::MSAA Viewport::get_msaa_2d() const {
3694
ERR_READ_THREAD_GUARD_V(MSAA_DISABLED);
3695
return msaa_2d;
3696
}
3697
3698
void Viewport::set_msaa_3d(MSAA p_msaa) {
3699
ERR_MAIN_THREAD_GUARD;
3700
ERR_FAIL_INDEX(p_msaa, MSAA_MAX);
3701
if (msaa_3d == p_msaa) {
3702
return;
3703
}
3704
msaa_3d = p_msaa;
3705
RS::get_singleton()->viewport_set_msaa_3d(viewport, RS::ViewportMSAA(p_msaa));
3706
}
3707
3708
Viewport::MSAA Viewport::get_msaa_3d() const {
3709
ERR_READ_THREAD_GUARD_V(MSAA_DISABLED);
3710
return msaa_3d;
3711
}
3712
3713
void Viewport::set_screen_space_aa(ScreenSpaceAA p_screen_space_aa) {
3714
ERR_MAIN_THREAD_GUARD;
3715
ERR_FAIL_INDEX(p_screen_space_aa, SCREEN_SPACE_AA_MAX);
3716
if (screen_space_aa == p_screen_space_aa) {
3717
return;
3718
}
3719
screen_space_aa = p_screen_space_aa;
3720
RS::get_singleton()->viewport_set_screen_space_aa(viewport, RS::ViewportScreenSpaceAA(p_screen_space_aa));
3721
}
3722
3723
Viewport::ScreenSpaceAA Viewport::get_screen_space_aa() const {
3724
ERR_READ_THREAD_GUARD_V(SCREEN_SPACE_AA_DISABLED);
3725
return screen_space_aa;
3726
}
3727
3728
void Viewport::set_use_taa(bool p_use_taa) {
3729
ERR_MAIN_THREAD_GUARD;
3730
if (use_taa == p_use_taa) {
3731
return;
3732
}
3733
use_taa = p_use_taa;
3734
RS::get_singleton()->viewport_set_use_taa(viewport, p_use_taa);
3735
}
3736
3737
bool Viewport::is_using_taa() const {
3738
ERR_READ_THREAD_GUARD_V(false);
3739
return use_taa;
3740
}
3741
3742
void Viewport::set_use_debanding(bool p_use_debanding) {
3743
ERR_MAIN_THREAD_GUARD;
3744
if (use_debanding == p_use_debanding) {
3745
return;
3746
}
3747
use_debanding = p_use_debanding;
3748
RS::get_singleton()->viewport_set_use_debanding(viewport, p_use_debanding);
3749
}
3750
3751
bool Viewport::is_using_debanding() const {
3752
ERR_READ_THREAD_GUARD_V(false);
3753
return use_debanding;
3754
}
3755
3756
void Viewport::set_mesh_lod_threshold(float p_pixels) {
3757
ERR_MAIN_THREAD_GUARD;
3758
mesh_lod_threshold = p_pixels;
3759
RS::get_singleton()->viewport_set_mesh_lod_threshold(viewport, mesh_lod_threshold);
3760
}
3761
3762
float Viewport::get_mesh_lod_threshold() const {
3763
ERR_READ_THREAD_GUARD_V(0);
3764
return mesh_lod_threshold;
3765
}
3766
3767
void Viewport::set_use_occlusion_culling(bool p_use_occlusion_culling) {
3768
ERR_MAIN_THREAD_GUARD;
3769
if (use_occlusion_culling == p_use_occlusion_culling) {
3770
return;
3771
}
3772
3773
use_occlusion_culling = p_use_occlusion_culling;
3774
RS::get_singleton()->viewport_set_use_occlusion_culling(viewport, p_use_occlusion_culling);
3775
3776
notify_property_list_changed();
3777
}
3778
3779
bool Viewport::is_using_occlusion_culling() const {
3780
ERR_READ_THREAD_GUARD_V(false);
3781
return use_occlusion_culling;
3782
}
3783
3784
void Viewport::set_debug_draw(DebugDraw p_debug_draw) {
3785
ERR_MAIN_THREAD_GUARD;
3786
debug_draw = p_debug_draw;
3787
RS::get_singleton()->viewport_set_debug_draw(viewport, RS::ViewportDebugDraw(p_debug_draw));
3788
}
3789
3790
Viewport::DebugDraw Viewport::get_debug_draw() const {
3791
ERR_READ_THREAD_GUARD_V(DEBUG_DRAW_DISABLED);
3792
return debug_draw;
3793
}
3794
3795
int Viewport::get_render_info(RenderInfoType p_type, RenderInfo p_info) {
3796
ERR_READ_THREAD_GUARD_V(0);
3797
return RS::get_singleton()->viewport_get_render_info(viewport, RS::ViewportRenderInfoType(p_type), RS::ViewportRenderInfo(p_info));
3798
}
3799
3800
void Viewport::set_snap_controls_to_pixels(bool p_enable) {
3801
ERR_MAIN_THREAD_GUARD;
3802
snap_controls_to_pixels = p_enable;
3803
}
3804
3805
bool Viewport::is_snap_controls_to_pixels_enabled() const {
3806
ERR_READ_THREAD_GUARD_V(false);
3807
return snap_controls_to_pixels;
3808
}
3809
3810
void Viewport::set_snap_2d_transforms_to_pixel(bool p_enable) {
3811
ERR_MAIN_THREAD_GUARD;
3812
snap_2d_transforms_to_pixel = p_enable;
3813
RS::get_singleton()->viewport_set_snap_2d_transforms_to_pixel(viewport, snap_2d_transforms_to_pixel);
3814
}
3815
3816
bool Viewport::is_snap_2d_transforms_to_pixel_enabled() const {
3817
ERR_READ_THREAD_GUARD_V(false);
3818
return snap_2d_transforms_to_pixel;
3819
}
3820
3821
void Viewport::set_snap_2d_vertices_to_pixel(bool p_enable) {
3822
ERR_MAIN_THREAD_GUARD;
3823
snap_2d_vertices_to_pixel = p_enable;
3824
RS::get_singleton()->viewport_set_snap_2d_vertices_to_pixel(viewport, snap_2d_vertices_to_pixel);
3825
}
3826
3827
bool Viewport::is_snap_2d_vertices_to_pixel_enabled() const {
3828
ERR_READ_THREAD_GUARD_V(false);
3829
return snap_2d_vertices_to_pixel;
3830
}
3831
3832
bool Viewport::gui_is_dragging() const {
3833
ERR_READ_THREAD_GUARD_V(false);
3834
return get_section_root_viewport()->gui.global_dragging;
3835
}
3836
3837
bool Viewport::gui_is_drag_successful() const {
3838
ERR_READ_THREAD_GUARD_V(false);
3839
return gui.drag_successful;
3840
}
3841
3842
void Viewport::gui_cancel_drag() {
3843
ERR_MAIN_THREAD_GUARD;
3844
if (gui_is_dragging()) {
3845
_perform_drop();
3846
}
3847
}
3848
3849
void Viewport::set_input_as_handled() {
3850
ERR_MAIN_THREAD_GUARD;
3851
if (!handle_input_locally) {
3852
ERR_FAIL_COND(!is_inside_tree());
3853
Viewport *vp = this;
3854
while (true) {
3855
if (Object::cast_to<Window>(vp)) {
3856
break;
3857
}
3858
if (!vp->get_parent()) {
3859
break;
3860
}
3861
vp = vp->get_parent()->get_viewport();
3862
}
3863
if (vp != this) {
3864
vp->set_input_as_handled();
3865
return;
3866
}
3867
}
3868
3869
local_input_handled = true;
3870
}
3871
3872
bool Viewport::is_input_handled() const {
3873
ERR_READ_THREAD_GUARD_V(false);
3874
if (!handle_input_locally) {
3875
ERR_FAIL_COND_V(!is_inside_tree(), false);
3876
const Viewport *vp = this;
3877
while (true) {
3878
if (Object::cast_to<Window>(vp)) {
3879
break;
3880
}
3881
if (!vp->get_parent()) {
3882
break;
3883
}
3884
vp = vp->get_parent()->get_viewport();
3885
}
3886
if (vp != this) {
3887
return vp->is_input_handled();
3888
}
3889
}
3890
return local_input_handled;
3891
}
3892
3893
void Viewport::set_handle_input_locally(bool p_enable) {
3894
ERR_MAIN_THREAD_GUARD;
3895
handle_input_locally = p_enable;
3896
}
3897
3898
bool Viewport::is_handling_input_locally() const {
3899
ERR_READ_THREAD_GUARD_V(false);
3900
return handle_input_locally;
3901
}
3902
3903
void Viewport::set_default_canvas_item_texture_filter(DefaultCanvasItemTextureFilter p_filter) {
3904
ERR_MAIN_THREAD_GUARD;
3905
ERR_FAIL_INDEX(p_filter, DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX);
3906
3907
if (default_canvas_item_texture_filter == p_filter) {
3908
return;
3909
}
3910
default_canvas_item_texture_filter = p_filter;
3911
switch (default_canvas_item_texture_filter) {
3912
case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST:
3913
RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
3914
break;
3915
case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR:
3916
RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR);
3917
break;
3918
case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS:
3919
RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS);
3920
break;
3921
case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS:
3922
RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
3923
break;
3924
default: {
3925
}
3926
}
3927
}
3928
3929
Viewport::DefaultCanvasItemTextureFilter Viewport::get_default_canvas_item_texture_filter() const {
3930
ERR_READ_THREAD_GUARD_V(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
3931
return default_canvas_item_texture_filter;
3932
}
3933
3934
void Viewport::set_default_canvas_item_texture_repeat(DefaultCanvasItemTextureRepeat p_repeat) {
3935
ERR_MAIN_THREAD_GUARD;
3936
ERR_FAIL_INDEX(p_repeat, DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX);
3937
3938
if (default_canvas_item_texture_repeat == p_repeat) {
3939
return;
3940
}
3941
3942
default_canvas_item_texture_repeat = p_repeat;
3943
3944
switch (default_canvas_item_texture_repeat) {
3945
case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED:
3946
RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
3947
break;
3948
case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED:
3949
RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
3950
break;
3951
case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR:
3952
RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
3953
break;
3954
default: {
3955
}
3956
}
3957
}
3958
3959
Viewport::DefaultCanvasItemTextureRepeat Viewport::get_default_canvas_item_texture_repeat() const {
3960
ERR_READ_THREAD_GUARD_V(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
3961
return default_canvas_item_texture_repeat;
3962
}
3963
3964
void Viewport::set_vrs_mode(Viewport::VRSMode p_vrs_mode) {
3965
ERR_MAIN_THREAD_GUARD;
3966
// Note, set this even if not supported on this hardware, it will only be used if it is but we want to save the value as set by the user.
3967
vrs_mode = p_vrs_mode;
3968
3969
switch (p_vrs_mode) {
3970
case VRS_TEXTURE: {
3971
RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_TEXTURE);
3972
} break;
3973
case VRS_XR: {
3974
RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_XR);
3975
} break;
3976
default: {
3977
RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_DISABLED);
3978
} break;
3979
}
3980
3981
notify_property_list_changed();
3982
}
3983
3984
Viewport::VRSMode Viewport::get_vrs_mode() const {
3985
ERR_READ_THREAD_GUARD_V(VRS_DISABLED);
3986
return vrs_mode;
3987
}
3988
3989
void Viewport::set_vrs_update_mode(VRSUpdateMode p_vrs_update_mode) {
3990
ERR_MAIN_THREAD_GUARD;
3991
3992
vrs_update_mode = p_vrs_update_mode;
3993
switch (p_vrs_update_mode) {
3994
case VRS_UPDATE_ONCE: {
3995
RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_ONCE);
3996
} break;
3997
case VRS_UPDATE_ALWAYS: {
3998
RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_ALWAYS);
3999
} break;
4000
default: {
4001
RS::get_singleton()->viewport_set_vrs_update_mode(viewport, RS::VIEWPORT_VRS_UPDATE_DISABLED);
4002
} break;
4003
}
4004
}
4005
4006
Viewport::VRSUpdateMode Viewport::get_vrs_update_mode() const {
4007
ERR_READ_THREAD_GUARD_V(VRS_UPDATE_DISABLED);
4008
return vrs_update_mode;
4009
}
4010
4011
void Viewport::set_vrs_texture(Ref<Texture2D> p_texture) {
4012
ERR_MAIN_THREAD_GUARD;
4013
vrs_texture = p_texture;
4014
4015
// TODO need to add something here in case the RID changes
4016
RID tex = p_texture.is_valid() ? p_texture->get_rid() : RID();
4017
RS::get_singleton()->viewport_set_vrs_texture(viewport, tex);
4018
}
4019
4020
Ref<Texture2D> Viewport::get_vrs_texture() const {
4021
ERR_READ_THREAD_GUARD_V(Ref<Texture2D>());
4022
return vrs_texture;
4023
}
4024
4025
DisplayServer::WindowID Viewport::get_window_id() const {
4026
ERR_READ_THREAD_GUARD_V(DisplayServer::INVALID_WINDOW_ID);
4027
return DisplayServer::MAIN_WINDOW_ID;
4028
}
4029
4030
Viewport *Viewport::get_parent_viewport() const {
4031
ERR_READ_THREAD_GUARD_V(nullptr);
4032
ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
4033
if (!get_parent()) {
4034
return nullptr; //root viewport
4035
}
4036
4037
return get_parent()->get_viewport();
4038
}
4039
4040
void Viewport::set_embedding_subwindows(bool p_embed) {
4041
ERR_THREAD_GUARD;
4042
if (gui.embed_subwindows_hint == p_embed) {
4043
return;
4044
}
4045
4046
bool allow_change = true;
4047
4048
if (!is_inside_tree()) {
4049
// Change can happen since no child window is displayed.
4050
} else if (gui.embed_subwindows_hint) {
4051
if (!gui.sub_windows.is_empty()) {
4052
// Prevent change when this viewport has embedded windows.
4053
allow_change = false;
4054
}
4055
} else {
4056
Viewport *vp = this;
4057
while (true) {
4058
if (!vp->get_parent()) {
4059
// Root window reached.
4060
break;
4061
}
4062
vp = vp->get_parent()->get_viewport();
4063
if (vp->is_embedding_subwindows()) {
4064
for (int i = 0; i < vp->gui.sub_windows.size(); i++) {
4065
if (is_ancestor_of(vp->gui.sub_windows[i].window)) {
4066
// Prevent change when this viewport has child windows that are displayed in an ancestor viewport.
4067
allow_change = false;
4068
break;
4069
}
4070
}
4071
}
4072
}
4073
4074
if (allow_change) {
4075
Vector<DisplayServer::WindowID> wl = DisplayServer::get_singleton()->get_window_list();
4076
for (const DisplayServer::WindowID &window_id : wl) {
4077
const Window *w = Window::get_from_id(window_id);
4078
if (w && is_ancestor_of(w)) {
4079
// Prevent change when this viewport has child windows that are displayed as native windows.
4080
allow_change = false;
4081
break;
4082
}
4083
}
4084
}
4085
}
4086
4087
if (allow_change) {
4088
gui.embed_subwindows_hint = p_embed;
4089
} else {
4090
WARN_PRINT("Can't change \"gui_embed_subwindows\" while a child window is displayed. Consider hiding all child windows before changing this value.");
4091
}
4092
}
4093
4094
bool Viewport::is_embedding_subwindows() const {
4095
ERR_READ_THREAD_GUARD_V(false);
4096
return gui.embed_subwindows_hint;
4097
}
4098
4099
TypedArray<Window> Viewport::get_embedded_subwindows() const {
4100
TypedArray<Window> windows;
4101
for (int i = 0; i < gui.sub_windows.size(); i++) {
4102
windows.append(gui.sub_windows[i].window);
4103
}
4104
4105
return windows;
4106
}
4107
4108
void Viewport::subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect) {
4109
int index = _sub_window_find(p_window);
4110
ERR_FAIL_COND(index == -1);
4111
4112
gui.sub_windows.write[index].parent_safe_rect = p_rect;
4113
}
4114
4115
Rect2i Viewport::subwindow_get_popup_safe_rect(Window *p_window) const {
4116
int index = _sub_window_find(p_window);
4117
// FIXME: Re-enable ERR_FAIL_COND after rewriting embedded window popup closing.
4118
// Currently it is expected, that index == -1 can happen.
4119
if (index == -1) {
4120
return Rect2i();
4121
}
4122
// ERR_FAIL_COND_V(index == -1, Rect2i());
4123
4124
return gui.sub_windows[index].parent_safe_rect;
4125
}
4126
4127
void Viewport::set_sdf_oversize(SDFOversize p_sdf_oversize) {
4128
ERR_MAIN_THREAD_GUARD;
4129
ERR_FAIL_INDEX(p_sdf_oversize, SDF_OVERSIZE_MAX);
4130
sdf_oversize = p_sdf_oversize;
4131
RS::get_singleton()->viewport_set_sdf_oversize_and_scale(viewport, RS::ViewportSDFOversize(sdf_oversize), RS::ViewportSDFScale(sdf_scale));
4132
}
4133
4134
Viewport::SDFOversize Viewport::get_sdf_oversize() const {
4135
ERR_READ_THREAD_GUARD_V(SDF_OVERSIZE_100_PERCENT);
4136
return sdf_oversize;
4137
}
4138
4139
void Viewport::set_sdf_scale(SDFScale p_sdf_scale) {
4140
ERR_MAIN_THREAD_GUARD;
4141
ERR_FAIL_INDEX(p_sdf_scale, SDF_SCALE_MAX);
4142
sdf_scale = p_sdf_scale;
4143
RS::get_singleton()->viewport_set_sdf_oversize_and_scale(viewport, RS::ViewportSDFOversize(sdf_oversize), RS::ViewportSDFScale(sdf_scale));
4144
}
4145
4146
Viewport::SDFScale Viewport::get_sdf_scale() const {
4147
ERR_READ_THREAD_GUARD_V(SDF_SCALE_100_PERCENT);
4148
return sdf_scale;
4149
}
4150
4151
Transform2D Viewport::get_screen_transform() const {
4152
ERR_READ_THREAD_GUARD_V(Transform2D());
4153
return get_screen_transform_internal();
4154
}
4155
4156
Transform2D Viewport::get_screen_transform_internal(bool p_absolute_position) const {
4157
ERR_READ_THREAD_GUARD_V(Transform2D());
4158
return get_final_transform();
4159
}
4160
4161
void Viewport::update_mouse_cursor_state() {
4162
// Updates need to happen in Window, because SubViewportContainers might be hidden behind other Controls.
4163
Window *base_window = get_base_window();
4164
if (base_window) {
4165
base_window->update_mouse_cursor_state();
4166
}
4167
}
4168
4169
void Viewport::set_canvas_cull_mask(uint32_t p_canvas_cull_mask) {
4170
ERR_MAIN_THREAD_GUARD;
4171
canvas_cull_mask = p_canvas_cull_mask;
4172
RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask);
4173
}
4174
4175
uint32_t Viewport::get_canvas_cull_mask() const {
4176
ERR_READ_THREAD_GUARD_V(0);
4177
return canvas_cull_mask;
4178
}
4179
4180
void Viewport::set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable) {
4181
ERR_MAIN_THREAD_GUARD;
4182
ERR_FAIL_UNSIGNED_INDEX(p_layer, 32);
4183
if (p_enable) {
4184
set_canvas_cull_mask(canvas_cull_mask | (1 << p_layer));
4185
} else {
4186
set_canvas_cull_mask(canvas_cull_mask & (~(1 << p_layer)));
4187
}
4188
}
4189
4190
bool Viewport::get_canvas_cull_mask_bit(uint32_t p_layer) const {
4191
ERR_READ_THREAD_GUARD_V(false);
4192
ERR_FAIL_UNSIGNED_INDEX_V(p_layer, 32, false);
4193
return (canvas_cull_mask & (1 << p_layer));
4194
}
4195
4196
#ifdef TOOLS_ENABLED
4197
bool Viewport::is_visible_subviewport() const {
4198
if (!is_sub_viewport()) {
4199
return true;
4200
}
4201
SubViewportContainer *container = Object::cast_to<SubViewportContainer>(get_parent());
4202
return container && container->is_visible_in_tree();
4203
}
4204
#endif // TOOLS_ENABLED
4205
4206
void Viewport::_update_audio_listener_2d() {
4207
if (AudioServer::get_singleton()) {
4208
AudioServer::get_singleton()->notify_listener_changed();
4209
}
4210
}
4211
4212
void Viewport::_audio_listener_2d_set(AudioListener2D *p_audio_listener) {
4213
if (audio_listener_2d == p_audio_listener) {
4214
return;
4215
} else if (audio_listener_2d) {
4216
audio_listener_2d->clear_current();
4217
}
4218
audio_listener_2d = p_audio_listener;
4219
}
4220
4221
void Viewport::_audio_listener_2d_remove(AudioListener2D *p_audio_listener) {
4222
if (audio_listener_2d == p_audio_listener) {
4223
audio_listener_2d = nullptr;
4224
}
4225
}
4226
4227
void Viewport::_camera_2d_set(Camera2D *p_camera_2d) {
4228
camera_2d = p_camera_2d;
4229
}
4230
4231
#ifndef PHYSICS_2D_DISABLED
4232
void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference) {
4233
List<ObjectID> to_erase;
4234
List<ObjectID> to_mouse_exit;
4235
4236
for (const KeyValue<ObjectID, uint64_t> &E : physics_2d_mouseover) {
4237
if (!p_clean_all_frames && E.value == p_frame_reference) {
4238
continue;
4239
}
4240
4241
Object *o = ObjectDB::get_instance(E.key);
4242
if (o) {
4243
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
4244
if (co && co->is_inside_tree()) {
4245
if (p_clean_all_frames && p_paused_only && co->can_process()) {
4246
continue;
4247
}
4248
to_mouse_exit.push_back(E.key);
4249
}
4250
}
4251
to_erase.push_back(E.key);
4252
}
4253
4254
while (to_erase.size()) {
4255
physics_2d_mouseover.erase(to_erase.front()->get());
4256
to_erase.pop_front();
4257
}
4258
4259
// Per-shape.
4260
List<Pair<ObjectID, int>> shapes_to_erase;
4261
List<Pair<ObjectID, int>> shapes_to_mouse_exit;
4262
4263
for (KeyValue<Pair<ObjectID, int>, uint64_t> &E : physics_2d_shape_mouseover) {
4264
if (!p_clean_all_frames && E.value == p_frame_reference) {
4265
continue;
4266
}
4267
4268
Object *o = ObjectDB::get_instance(E.key.first);
4269
if (o) {
4270
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
4271
if (co && co->is_inside_tree()) {
4272
if (p_clean_all_frames && p_paused_only && co->can_process()) {
4273
continue;
4274
}
4275
shapes_to_mouse_exit.push_back(E.key);
4276
}
4277
}
4278
shapes_to_erase.push_back(E.key);
4279
}
4280
4281
while (shapes_to_erase.size()) {
4282
physics_2d_shape_mouseover.erase(shapes_to_erase.front()->get());
4283
shapes_to_erase.pop_front();
4284
}
4285
4286
while (to_mouse_exit.size()) {
4287
Object *o = ObjectDB::get_instance(to_mouse_exit.front()->get());
4288
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
4289
co->_mouse_exit();
4290
to_mouse_exit.pop_front();
4291
}
4292
4293
while (shapes_to_mouse_exit.size()) {
4294
Pair<ObjectID, int> e = shapes_to_mouse_exit.front()->get();
4295
Object *o = ObjectDB::get_instance(e.first);
4296
CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
4297
co->_mouse_shape_exit(e.second);
4298
shapes_to_mouse_exit.pop_front();
4299
}
4300
}
4301
#endif // PHYSICS_2D_DISABLED
4302
4303
AudioListener2D *Viewport::get_audio_listener_2d() const {
4304
ERR_READ_THREAD_GUARD_V(nullptr);
4305
return audio_listener_2d;
4306
}
4307
4308
void Viewport::set_as_audio_listener_2d(bool p_enable) {
4309
ERR_MAIN_THREAD_GUARD;
4310
if (p_enable == is_audio_listener_2d_enabled) {
4311
return;
4312
}
4313
4314
is_audio_listener_2d_enabled = p_enable;
4315
_update_audio_listener_2d();
4316
}
4317
4318
bool Viewport::is_audio_listener_2d() const {
4319
ERR_READ_THREAD_GUARD_V(false);
4320
return is_audio_listener_2d_enabled;
4321
}
4322
4323
Camera2D *Viewport::get_camera_2d() const {
4324
ERR_READ_THREAD_GUARD_V(nullptr);
4325
return camera_2d;
4326
}
4327
4328
void Viewport::assign_next_enabled_camera_2d(const StringName &p_camera_group) {
4329
ERR_MAIN_THREAD_GUARD;
4330
List<Node *> camera_list;
4331
get_tree()->get_nodes_in_group(p_camera_group, &camera_list);
4332
4333
Camera2D *new_camera = nullptr;
4334
for (Node *E : camera_list) {
4335
Camera2D *cam = Object::cast_to<Camera2D>(E);
4336
if (!cam) {
4337
continue; // Non-camera node (e.g. ParallaxBackground).
4338
}
4339
4340
if (cam->is_enabled()) {
4341
new_camera = cam;
4342
break;
4343
}
4344
}
4345
4346
_camera_2d_set(new_camera);
4347
if (!camera_2d) {
4348
set_canvas_transform(Transform2D());
4349
}
4350
}
4351
4352
#ifndef _3D_DISABLED
4353
AudioListener3D *Viewport::get_audio_listener_3d() const {
4354
ERR_READ_THREAD_GUARD_V(nullptr);
4355
return audio_listener_3d;
4356
}
4357
4358
void Viewport::set_as_audio_listener_3d(bool p_enable) {
4359
ERR_MAIN_THREAD_GUARD;
4360
if (p_enable == is_audio_listener_3d_enabled) {
4361
return;
4362
}
4363
4364
is_audio_listener_3d_enabled = p_enable;
4365
_update_audio_listener_3d();
4366
}
4367
4368
bool Viewport::is_audio_listener_3d() const {
4369
ERR_READ_THREAD_GUARD_V(false);
4370
return is_audio_listener_3d_enabled;
4371
}
4372
4373
void Viewport::_update_audio_listener_3d() {
4374
if (AudioServer::get_singleton()) {
4375
AudioServer::get_singleton()->notify_listener_changed();
4376
}
4377
}
4378
4379
void Viewport::_listener_transform_3d_changed_notify() {
4380
}
4381
4382
void Viewport::_audio_listener_3d_set(AudioListener3D *p_listener) {
4383
if (audio_listener_3d == p_listener) {
4384
return;
4385
}
4386
4387
audio_listener_3d = p_listener;
4388
4389
_update_audio_listener_3d();
4390
_listener_transform_3d_changed_notify();
4391
}
4392
4393
bool Viewport::_audio_listener_3d_add(AudioListener3D *p_listener) {
4394
audio_listener_3d_set.insert(p_listener);
4395
return audio_listener_3d_set.size() == 1;
4396
}
4397
4398
void Viewport::_audio_listener_3d_remove(AudioListener3D *p_listener) {
4399
audio_listener_3d_set.erase(p_listener);
4400
if (audio_listener_3d == p_listener) {
4401
audio_listener_3d = nullptr;
4402
}
4403
}
4404
4405
void Viewport::_audio_listener_3d_make_next_current(AudioListener3D *p_exclude) {
4406
if (audio_listener_3d_set.size() > 0) {
4407
for (AudioListener3D *E : audio_listener_3d_set) {
4408
if (p_exclude == E) {
4409
continue;
4410
}
4411
if (!E->is_inside_tree()) {
4412
continue;
4413
}
4414
if (audio_listener_3d != nullptr) {
4415
return;
4416
}
4417
4418
E->make_current();
4419
}
4420
} else {
4421
// Attempt to reset listener to the camera position.
4422
if (camera_3d != nullptr) {
4423
_update_audio_listener_3d();
4424
_camera_3d_transform_changed_notify();
4425
}
4426
}
4427
}
4428
4429
#ifndef PHYSICS_3D_DISABLED
4430
void Viewport::_collision_object_3d_input_event(CollisionObject3D *p_object, Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) {
4431
Transform3D object_transform = p_object->get_global_transform();
4432
Transform3D camera_transform = p_camera->get_global_transform();
4433
ObjectID id = p_object->get_instance_id();
4434
4435
// Avoid sending the fake event unnecessarily if nothing really changed in the context.
4436
if (object_transform == physics_last_object_transform && camera_transform == physics_last_camera_transform && physics_last_id == id) {
4437
Ref<InputEventMouseMotion> mm = p_input_event;
4438
if (mm.is_valid() && mm->get_device() == InputEvent::DEVICE_ID_INTERNAL) {
4439
return; // Discarded.
4440
}
4441
}
4442
p_object->_input_event_call(camera_3d, p_input_event, p_pos, p_normal, p_shape);
4443
physics_last_object_transform = object_transform;
4444
physics_last_camera_transform = camera_transform;
4445
physics_last_id = id;
4446
}
4447
#endif // PHYSICS_3D_DISABLED
4448
4449
Camera3D *Viewport::get_camera_3d() const {
4450
ERR_READ_THREAD_GUARD_V(nullptr);
4451
return camera_3d;
4452
}
4453
4454
void Viewport::_camera_3d_transform_changed_notify() {
4455
}
4456
4457
void Viewport::_camera_3d_set(Camera3D *p_camera) {
4458
if (camera_3d == p_camera) {
4459
return;
4460
}
4461
4462
if (camera_3d) {
4463
camera_3d->notification(Camera3D::NOTIFICATION_LOST_CURRENT);
4464
}
4465
4466
camera_3d = p_camera;
4467
4468
if (!camera_3d_override) {
4469
if (camera_3d) {
4470
RenderingServer::get_singleton()->viewport_attach_camera(viewport, camera_3d->get_camera());
4471
} else {
4472
RenderingServer::get_singleton()->viewport_attach_camera(viewport, RID());
4473
}
4474
}
4475
4476
if (camera_3d) {
4477
camera_3d->notification(Camera3D::NOTIFICATION_BECAME_CURRENT);
4478
}
4479
4480
_update_audio_listener_3d();
4481
_camera_3d_transform_changed_notify();
4482
}
4483
4484
bool Viewport::_camera_3d_add(Camera3D *p_camera) {
4485
camera_3d_set.insert(p_camera);
4486
return camera_3d_set.size() == 1;
4487
}
4488
4489
void Viewport::_camera_3d_remove(Camera3D *p_camera) {
4490
camera_3d_set.erase(p_camera);
4491
if (camera_3d == p_camera) {
4492
_camera_3d_set(nullptr);
4493
}
4494
}
4495
4496
void Viewport::_camera_3d_make_next_current(Camera3D *p_exclude) {
4497
for (Camera3D *E : camera_3d_set) {
4498
if (p_exclude == E) {
4499
continue;
4500
}
4501
if (!E->is_inside_tree()) {
4502
continue;
4503
}
4504
if (camera_3d != nullptr) {
4505
return;
4506
}
4507
4508
E->make_current();
4509
}
4510
}
4511
4512
void Viewport::enable_camera_3d_override(bool p_enable) {
4513
ERR_MAIN_THREAD_GUARD;
4514
if (p_enable == camera_3d_override) {
4515
return;
4516
}
4517
4518
if (p_enable) {
4519
camera_3d_override.rid = RenderingServer::get_singleton()->camera_create();
4520
} else {
4521
RenderingServer::get_singleton()->free(camera_3d_override.rid);
4522
camera_3d_override.rid = RID();
4523
}
4524
4525
if (p_enable) {
4526
RenderingServer::get_singleton()->viewport_attach_camera(viewport, camera_3d_override.rid);
4527
} else if (camera_3d) {
4528
RenderingServer::get_singleton()->viewport_attach_camera(viewport, camera_3d->get_camera());
4529
} else {
4530
RenderingServer::get_singleton()->viewport_attach_camera(viewport, RID());
4531
}
4532
}
4533
4534
void Viewport::set_camera_3d_override_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far) {
4535
ERR_MAIN_THREAD_GUARD;
4536
if (camera_3d_override) {
4537
if (camera_3d_override.fov == p_fovy_degrees && camera_3d_override.z_near == p_z_near &&
4538
camera_3d_override.z_far == p_z_far && camera_3d_override.projection == Camera3DOverrideData::PROJECTION_PERSPECTIVE) {
4539
return;
4540
}
4541
4542
camera_3d_override.fov = p_fovy_degrees;
4543
camera_3d_override.z_near = p_z_near;
4544
camera_3d_override.z_far = p_z_far;
4545
camera_3d_override.projection = Camera3DOverrideData::PROJECTION_PERSPECTIVE;
4546
4547
RenderingServer::get_singleton()->camera_set_perspective(camera_3d_override.rid, camera_3d_override.fov, camera_3d_override.z_near, camera_3d_override.z_far);
4548
}
4549
}
4550
4551
void Viewport::set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far) {
4552
ERR_MAIN_THREAD_GUARD;
4553
if (camera_3d_override) {
4554
if (camera_3d_override.size == p_size && camera_3d_override.z_near == p_z_near &&
4555
camera_3d_override.z_far == p_z_far && camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) {
4556
return;
4557
}
4558
4559
camera_3d_override.size = p_size;
4560
camera_3d_override.z_near = p_z_near;
4561
camera_3d_override.z_far = p_z_far;
4562
camera_3d_override.projection = Camera3DOverrideData::PROJECTION_ORTHOGONAL;
4563
4564
RenderingServer::get_singleton()->camera_set_orthogonal(camera_3d_override.rid, camera_3d_override.size, camera_3d_override.z_near, camera_3d_override.z_far);
4565
}
4566
}
4567
4568
HashMap<StringName, real_t> Viewport::get_camera_3d_override_properties() const {
4569
HashMap<StringName, real_t> props;
4570
4571
props["size"] = 0;
4572
props["fov"] = 0;
4573
props["z_near"] = 0;
4574
props["z_far"] = 0;
4575
ERR_READ_THREAD_GUARD_V(props);
4576
4577
props["size"] = camera_3d_override.size;
4578
props["fov"] = camera_3d_override.fov;
4579
props["z_near"] = camera_3d_override.z_near;
4580
props["z_far"] = camera_3d_override.z_far;
4581
return props;
4582
}
4583
4584
void Viewport::set_disable_3d(bool p_disable) {
4585
ERR_MAIN_THREAD_GUARD;
4586
disable_3d = p_disable;
4587
RenderingServer::get_singleton()->viewport_set_disable_3d(viewport, disable_3d);
4588
}
4589
4590
bool Viewport::is_3d_disabled() const {
4591
ERR_READ_THREAD_GUARD_V(false);
4592
return disable_3d;
4593
}
4594
4595
bool Viewport::is_camera_3d_override_enabled() const {
4596
ERR_READ_THREAD_GUARD_V(false);
4597
return camera_3d_override;
4598
}
4599
4600
void Viewport::set_camera_3d_override_transform(const Transform3D &p_transform) {
4601
ERR_MAIN_THREAD_GUARD;
4602
if (camera_3d_override) {
4603
camera_3d_override.transform = p_transform;
4604
RenderingServer::get_singleton()->camera_set_transform(camera_3d_override.rid, p_transform);
4605
}
4606
}
4607
4608
Transform3D Viewport::get_camera_3d_override_transform() const {
4609
ERR_READ_THREAD_GUARD_V(Transform3D());
4610
if (camera_3d_override) {
4611
return camera_3d_override.transform;
4612
}
4613
4614
return Transform3D();
4615
}
4616
4617
Vector3 Viewport::camera_3d_override_project_ray_normal(const Point2 &p_pos) const {
4618
ERR_READ_THREAD_GUARD_V(Vector3());
4619
Vector3 ray = camera_3d_override_project_local_ray_normal(p_pos);
4620
return camera_3d_override.transform.basis.xform(ray).normalized();
4621
}
4622
4623
Vector3 Viewport::camera_3d_override_project_local_ray_normal(const Point2 &p_pos) const {
4624
ERR_READ_THREAD_GUARD_V(Vector3());
4625
Size2 viewport_size = get_camera_rect_size();
4626
Vector2 cpos = get_camera_coords(p_pos);
4627
Vector3 ray;
4628
4629
if (camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) {
4630
ray = Vector3(0, 0, -1);
4631
} else {
4632
Projection cm;
4633
cm.set_perspective(camera_3d_override.fov, get_visible_rect().size.aspect(), camera_3d_override.z_near, camera_3d_override.z_far, false);
4634
4635
Vector2 screen_he = cm.get_viewport_half_extents();
4636
ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -camera_3d_override.z_near).normalized();
4637
}
4638
4639
return ray;
4640
}
4641
4642
Vector3 Viewport::camera_3d_override_project_ray_origin(const Point2 &p_pos) const {
4643
ERR_READ_THREAD_GUARD_V(Vector3());
4644
Size2 viewport_size = get_camera_rect_size();
4645
Vector2 cpos = get_camera_coords(p_pos);
4646
ERR_FAIL_COND_V(viewport_size.y == 0, Vector3());
4647
4648
if (camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) {
4649
Vector2 pos = cpos / viewport_size;
4650
real_t vsize, hsize;
4651
hsize = camera_3d_override.size * viewport_size.aspect();
4652
vsize = camera_3d_override.size;
4653
4654
Vector3 ray;
4655
ray.x = pos.x * (hsize)-hsize / 2;
4656
ray.y = (1.0 - pos.y) * (vsize)-vsize / 2;
4657
ray.z = -camera_3d_override.z_near;
4658
ray = camera_3d_override.transform.xform(ray);
4659
return ray;
4660
} else {
4661
return camera_3d_override.transform.origin;
4662
};
4663
}
4664
4665
Ref<World3D> Viewport::get_world_3d() const {
4666
ERR_READ_THREAD_GUARD_V(Ref<World3D>());
4667
return world_3d;
4668
}
4669
4670
Ref<World3D> Viewport::find_world_3d() const {
4671
ERR_READ_THREAD_GUARD_V(Ref<World3D>());
4672
if (own_world_3d.is_valid()) {
4673
return own_world_3d;
4674
} else if (world_3d.is_valid()) {
4675
return world_3d;
4676
} else if (parent) {
4677
return parent->find_world_3d();
4678
} else {
4679
return Ref<World3D>();
4680
}
4681
}
4682
4683
void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) {
4684
ERR_MAIN_THREAD_GUARD;
4685
if (world_3d == p_world_3d) {
4686
return;
4687
}
4688
4689
if (is_inside_tree()) {
4690
_propagate_exit_world_3d(this);
4691
}
4692
4693
if (own_world_3d.is_valid() && world_3d.is_valid()) {
4694
world_3d->disconnect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4695
}
4696
4697
world_3d = p_world_3d;
4698
4699
if (own_world_3d.is_valid()) {
4700
if (world_3d.is_valid()) {
4701
own_world_3d = world_3d->duplicate();
4702
world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4703
} else {
4704
own_world_3d.instantiate();
4705
}
4706
}
4707
4708
if (is_inside_tree()) {
4709
_propagate_enter_world_3d(this);
4710
}
4711
4712
if (is_inside_tree()) {
4713
RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
4714
}
4715
4716
_update_audio_listener_3d();
4717
}
4718
4719
void Viewport::_own_world_3d_changed() {
4720
ERR_FAIL_COND(world_3d.is_null());
4721
ERR_FAIL_COND(own_world_3d.is_null());
4722
4723
if (is_inside_tree()) {
4724
_propagate_exit_world_3d(this);
4725
}
4726
4727
own_world_3d = world_3d->duplicate();
4728
4729
if (is_inside_tree()) {
4730
_propagate_enter_world_3d(this);
4731
}
4732
4733
if (is_inside_tree()) {
4734
RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
4735
}
4736
4737
_update_audio_listener_3d();
4738
}
4739
4740
void Viewport::set_use_own_world_3d(bool p_use_own_world_3d) {
4741
ERR_MAIN_THREAD_GUARD;
4742
if (p_use_own_world_3d == own_world_3d.is_valid()) {
4743
return;
4744
}
4745
4746
if (is_inside_tree()) {
4747
_propagate_exit_world_3d(this);
4748
}
4749
4750
if (p_use_own_world_3d) {
4751
if (world_3d.is_valid()) {
4752
own_world_3d = world_3d->duplicate();
4753
world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4754
} else {
4755
own_world_3d.instantiate();
4756
}
4757
} else {
4758
own_world_3d = Ref<World3D>();
4759
if (world_3d.is_valid()) {
4760
world_3d->disconnect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4761
}
4762
}
4763
4764
if (is_inside_tree()) {
4765
_propagate_enter_world_3d(this);
4766
}
4767
4768
if (is_inside_tree()) {
4769
RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
4770
}
4771
4772
_update_audio_listener_3d();
4773
}
4774
4775
bool Viewport::is_using_own_world_3d() const {
4776
ERR_READ_THREAD_GUARD_V(false);
4777
return own_world_3d.is_valid();
4778
}
4779
4780
void Viewport::_propagate_enter_world_3d(Node *p_node) {
4781
if (p_node != this) {
4782
if (!p_node->is_inside_tree()) { //may not have entered scene yet
4783
return;
4784
}
4785
4786
if (Object::cast_to<Node3D>(p_node) || Object::cast_to<WorldEnvironment>(p_node)) {
4787
p_node->notification(Node3D::NOTIFICATION_ENTER_WORLD);
4788
} else {
4789
Viewport *v = Object::cast_to<Viewport>(p_node);
4790
if (v) {
4791
if (v->world_3d.is_valid() || v->own_world_3d.is_valid()) {
4792
return;
4793
}
4794
}
4795
}
4796
}
4797
4798
for (int i = 0; i < p_node->get_child_count(); i++) {
4799
_propagate_enter_world_3d(p_node->get_child(i));
4800
}
4801
}
4802
4803
void Viewport::_propagate_exit_world_3d(Node *p_node) {
4804
if (p_node != this) {
4805
if (!p_node->is_inside_tree()) { //may have exited scene already
4806
return;
4807
}
4808
4809
if (Object::cast_to<Node3D>(p_node) || Object::cast_to<WorldEnvironment>(p_node)) {
4810
p_node->notification(Node3D::NOTIFICATION_EXIT_WORLD);
4811
} else {
4812
Viewport *v = Object::cast_to<Viewport>(p_node);
4813
if (v) {
4814
if (v->world_3d.is_valid() || v->own_world_3d.is_valid()) {
4815
return;
4816
}
4817
}
4818
}
4819
}
4820
4821
for (int i = 0; i < p_node->get_child_count(); i++) {
4822
_propagate_exit_world_3d(p_node->get_child(i));
4823
}
4824
}
4825
4826
void Viewport::set_use_xr(bool p_use_xr) {
4827
ERR_MAIN_THREAD_GUARD;
4828
if (use_xr != p_use_xr) {
4829
use_xr = p_use_xr;
4830
4831
RS::get_singleton()->viewport_set_use_xr(viewport, use_xr);
4832
4833
if (!use_xr) {
4834
// Set viewport to previous size when exiting XR.
4835
if (size_allocated) {
4836
RS::get_singleton()->viewport_set_size(viewport, size.width, size.height);
4837
} else {
4838
RS::get_singleton()->viewport_set_size(viewport, 0, 0);
4839
}
4840
4841
// Reset render target override textures.
4842
RID rt = RS::get_singleton()->viewport_get_render_target(viewport);
4843
RSG::texture_storage->render_target_set_override(rt, RID(), RID(), RID(), RID());
4844
}
4845
}
4846
}
4847
4848
bool Viewport::is_using_xr() {
4849
ERR_READ_THREAD_GUARD_V(false);
4850
return use_xr;
4851
}
4852
4853
void Viewport::set_scaling_3d_mode(Scaling3DMode p_scaling_3d_mode) {
4854
ERR_MAIN_THREAD_GUARD;
4855
if (scaling_3d_mode == p_scaling_3d_mode) {
4856
return;
4857
}
4858
4859
scaling_3d_mode = p_scaling_3d_mode;
4860
RS::get_singleton()->viewport_set_scaling_3d_mode(viewport, (RS::ViewportScaling3DMode)(int)p_scaling_3d_mode);
4861
}
4862
4863
Viewport::Scaling3DMode Viewport::get_scaling_3d_mode() const {
4864
ERR_READ_THREAD_GUARD_V(SCALING_3D_MODE_BILINEAR);
4865
return scaling_3d_mode;
4866
}
4867
4868
void Viewport::set_scaling_3d_scale(float p_scaling_3d_scale) {
4869
ERR_MAIN_THREAD_GUARD;
4870
// Clamp to reasonable values that are actually useful.
4871
// Values above 2.0 don't serve a practical purpose since the viewport
4872
// isn't displayed with mipmaps.
4873
scaling_3d_scale = CLAMP(p_scaling_3d_scale, 0.1, 2.0);
4874
4875
RS::get_singleton()->viewport_set_scaling_3d_scale(viewport, scaling_3d_scale);
4876
}
4877
4878
float Viewport::get_scaling_3d_scale() const {
4879
ERR_READ_THREAD_GUARD_V(0);
4880
return scaling_3d_scale;
4881
}
4882
4883
void Viewport::set_fsr_sharpness(float p_fsr_sharpness) {
4884
ERR_MAIN_THREAD_GUARD;
4885
if (fsr_sharpness == p_fsr_sharpness) {
4886
return;
4887
}
4888
4889
if (p_fsr_sharpness < 0.0f) {
4890
p_fsr_sharpness = 0.0f;
4891
}
4892
4893
fsr_sharpness = p_fsr_sharpness;
4894
RS::get_singleton()->viewport_set_fsr_sharpness(viewport, p_fsr_sharpness);
4895
}
4896
4897
float Viewport::get_fsr_sharpness() const {
4898
ERR_READ_THREAD_GUARD_V(0);
4899
return fsr_sharpness;
4900
}
4901
4902
void Viewport::set_texture_mipmap_bias(float p_texture_mipmap_bias) {
4903
ERR_MAIN_THREAD_GUARD;
4904
if (texture_mipmap_bias == p_texture_mipmap_bias) {
4905
return;
4906
}
4907
4908
texture_mipmap_bias = p_texture_mipmap_bias;
4909
RS::get_singleton()->viewport_set_texture_mipmap_bias(viewport, p_texture_mipmap_bias);
4910
}
4911
4912
float Viewport::get_texture_mipmap_bias() const {
4913
ERR_READ_THREAD_GUARD_V(0);
4914
return texture_mipmap_bias;
4915
}
4916
4917
void Viewport::set_anisotropic_filtering_level(AnisotropicFiltering p_anisotropic_filtering_level) {
4918
ERR_MAIN_THREAD_GUARD;
4919
if (anisotropic_filtering_level == p_anisotropic_filtering_level) {
4920
return;
4921
}
4922
4923
anisotropic_filtering_level = p_anisotropic_filtering_level;
4924
RS::get_singleton()->viewport_set_anisotropic_filtering_level(viewport, (RS::ViewportAnisotropicFiltering)(int)p_anisotropic_filtering_level);
4925
}
4926
4927
Viewport::AnisotropicFiltering Viewport::get_anisotropic_filtering_level() const {
4928
ERR_READ_THREAD_GUARD_V(ANISOTROPY_DISABLED);
4929
return anisotropic_filtering_level;
4930
}
4931
4932
#endif // _3D_DISABLED
4933
4934
void Viewport::_propagate_world_2d_changed(Node *p_node) {
4935
if (p_node != this) {
4936
if (Object::cast_to<CanvasItem>(p_node)) {
4937
p_node->notification(CanvasItem::NOTIFICATION_WORLD_2D_CHANGED);
4938
} else {
4939
Viewport *v = Object::cast_to<Viewport>(p_node);
4940
if (v) {
4941
if (v->world_2d.is_valid()) {
4942
return;
4943
}
4944
}
4945
}
4946
}
4947
4948
for (int i = 0; i < p_node->get_child_count(); ++i) {
4949
_propagate_world_2d_changed(p_node->get_child(i));
4950
}
4951
}
4952
4953
void Viewport::_bind_methods() {
4954
ClassDB::bind_method(D_METHOD("set_world_2d", "world_2d"), &Viewport::set_world_2d);
4955
ClassDB::bind_method(D_METHOD("get_world_2d"), &Viewport::get_world_2d);
4956
ClassDB::bind_method(D_METHOD("find_world_2d"), &Viewport::find_world_2d);
4957
4958
ClassDB::bind_method(D_METHOD("set_canvas_transform", "xform"), &Viewport::set_canvas_transform);
4959
ClassDB::bind_method(D_METHOD("get_canvas_transform"), &Viewport::get_canvas_transform);
4960
4961
ClassDB::bind_method(D_METHOD("set_global_canvas_transform", "xform"), &Viewport::set_global_canvas_transform);
4962
ClassDB::bind_method(D_METHOD("get_global_canvas_transform"), &Viewport::get_global_canvas_transform);
4963
ClassDB::bind_method(D_METHOD("get_stretch_transform"), &Viewport::get_stretch_transform);
4964
ClassDB::bind_method(D_METHOD("get_final_transform"), &Viewport::get_final_transform);
4965
ClassDB::bind_method(D_METHOD("get_screen_transform"), &Viewport::get_screen_transform);
4966
4967
ClassDB::bind_method(D_METHOD("get_visible_rect"), &Viewport::get_visible_rect);
4968
ClassDB::bind_method(D_METHOD("set_transparent_background", "enable"), &Viewport::set_transparent_background);
4969
ClassDB::bind_method(D_METHOD("has_transparent_background"), &Viewport::has_transparent_background);
4970
ClassDB::bind_method(D_METHOD("set_use_hdr_2d", "enable"), &Viewport::set_use_hdr_2d);
4971
ClassDB::bind_method(D_METHOD("is_using_hdr_2d"), &Viewport::is_using_hdr_2d);
4972
4973
ClassDB::bind_method(D_METHOD("set_msaa_2d", "msaa"), &Viewport::set_msaa_2d);
4974
ClassDB::bind_method(D_METHOD("get_msaa_2d"), &Viewport::get_msaa_2d);
4975
4976
ClassDB::bind_method(D_METHOD("set_msaa_3d", "msaa"), &Viewport::set_msaa_3d);
4977
ClassDB::bind_method(D_METHOD("get_msaa_3d"), &Viewport::get_msaa_3d);
4978
4979
ClassDB::bind_method(D_METHOD("set_screen_space_aa", "screen_space_aa"), &Viewport::set_screen_space_aa);
4980
ClassDB::bind_method(D_METHOD("get_screen_space_aa"), &Viewport::get_screen_space_aa);
4981
4982
ClassDB::bind_method(D_METHOD("set_use_taa", "enable"), &Viewport::set_use_taa);
4983
ClassDB::bind_method(D_METHOD("is_using_taa"), &Viewport::is_using_taa);
4984
4985
ClassDB::bind_method(D_METHOD("set_use_debanding", "enable"), &Viewport::set_use_debanding);
4986
ClassDB::bind_method(D_METHOD("is_using_debanding"), &Viewport::is_using_debanding);
4987
4988
ClassDB::bind_method(D_METHOD("set_use_occlusion_culling", "enable"), &Viewport::set_use_occlusion_culling);
4989
ClassDB::bind_method(D_METHOD("is_using_occlusion_culling"), &Viewport::is_using_occlusion_culling);
4990
4991
ClassDB::bind_method(D_METHOD("set_debug_draw", "debug_draw"), &Viewport::set_debug_draw);
4992
ClassDB::bind_method(D_METHOD("get_debug_draw"), &Viewport::get_debug_draw);
4993
4994
ClassDB::bind_method(D_METHOD("set_use_oversampling", "enable"), &Viewport::set_use_oversampling);
4995
ClassDB::bind_method(D_METHOD("is_using_oversampling"), &Viewport::is_using_oversampling);
4996
4997
ClassDB::bind_method(D_METHOD("set_oversampling_override", "oversampling"), &Viewport::set_oversampling_override);
4998
ClassDB::bind_method(D_METHOD("get_oversampling_override"), &Viewport::get_oversampling_override);
4999
5000
ClassDB::bind_method(D_METHOD("get_oversampling"), &Viewport::get_oversampling);
5001
5002
ClassDB::bind_method(D_METHOD("get_render_info", "type", "info"), &Viewport::get_render_info);
5003
5004
ClassDB::bind_method(D_METHOD("get_texture"), &Viewport::get_texture);
5005
5006
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5007
ClassDB::bind_method(D_METHOD("set_physics_object_picking", "enable"), &Viewport::set_physics_object_picking);
5008
ClassDB::bind_method(D_METHOD("get_physics_object_picking"), &Viewport::get_physics_object_picking);
5009
ClassDB::bind_method(D_METHOD("set_physics_object_picking_sort", "enable"), &Viewport::set_physics_object_picking_sort);
5010
ClassDB::bind_method(D_METHOD("get_physics_object_picking_sort"), &Viewport::get_physics_object_picking_sort);
5011
ClassDB::bind_method(D_METHOD("set_physics_object_picking_first_only", "enable"), &Viewport::set_physics_object_picking_first_only);
5012
ClassDB::bind_method(D_METHOD("get_physics_object_picking_first_only"), &Viewport::get_physics_object_picking_first_only);
5013
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5014
5015
ClassDB::bind_method(D_METHOD("get_viewport_rid"), &Viewport::get_viewport_rid);
5016
ClassDB::bind_method(D_METHOD("push_text_input", "text"), &Viewport::push_text_input);
5017
ClassDB::bind_method(D_METHOD("push_input", "event", "in_local_coords"), &Viewport::push_input, DEFVAL(false));
5018
#ifndef DISABLE_DEPRECATED
5019
ClassDB::bind_method(D_METHOD("push_unhandled_input", "event", "in_local_coords"), &Viewport::push_unhandled_input, DEFVAL(false));
5020
#endif // DISABLE_DEPRECATED
5021
ClassDB::bind_method(D_METHOD("notify_mouse_entered"), &Viewport::notify_mouse_entered);
5022
ClassDB::bind_method(D_METHOD("notify_mouse_exited"), &Viewport::notify_mouse_exited);
5023
5024
ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position);
5025
ClassDB::bind_method(D_METHOD("warp_mouse", "position"), &Viewport::warp_mouse);
5026
ClassDB::bind_method(D_METHOD("update_mouse_cursor_state"), &Viewport::update_mouse_cursor_state);
5027
5028
ClassDB::bind_method(D_METHOD("gui_cancel_drag"), &Viewport::gui_cancel_drag);
5029
ClassDB::bind_method(D_METHOD("gui_get_drag_data"), &Viewport::gui_get_drag_data);
5030
ClassDB::bind_method(D_METHOD("gui_get_drag_description"), &Viewport::gui_get_drag_description);
5031
ClassDB::bind_method(D_METHOD("gui_set_drag_description", "description"), &Viewport::gui_set_drag_description);
5032
ClassDB::bind_method(D_METHOD("gui_is_dragging"), &Viewport::gui_is_dragging);
5033
ClassDB::bind_method(D_METHOD("gui_is_drag_successful"), &Viewport::gui_is_drag_successful);
5034
5035
ClassDB::bind_method(D_METHOD("gui_release_focus"), &Viewport::gui_release_focus);
5036
ClassDB::bind_method(D_METHOD("gui_get_focus_owner"), &Viewport::gui_get_focus_owner);
5037
ClassDB::bind_method(D_METHOD("gui_get_hovered_control"), &Viewport::gui_get_hovered_control);
5038
5039
ClassDB::bind_method(D_METHOD("set_disable_input", "disable"), &Viewport::set_disable_input);
5040
ClassDB::bind_method(D_METHOD("is_input_disabled"), &Viewport::is_input_disabled);
5041
5042
ClassDB::bind_method(D_METHOD("_gui_remove_focus_for_window"), &Viewport::_gui_remove_focus_for_window);
5043
5044
ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_size", "size"), &Viewport::set_positional_shadow_atlas_size);
5045
ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_size"), &Viewport::get_positional_shadow_atlas_size);
5046
5047
ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_16_bits", "enable"), &Viewport::set_positional_shadow_atlas_16_bits);
5048
ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_16_bits"), &Viewport::get_positional_shadow_atlas_16_bits);
5049
5050
ClassDB::bind_method(D_METHOD("set_snap_controls_to_pixels", "enabled"), &Viewport::set_snap_controls_to_pixels);
5051
ClassDB::bind_method(D_METHOD("is_snap_controls_to_pixels_enabled"), &Viewport::is_snap_controls_to_pixels_enabled);
5052
5053
ClassDB::bind_method(D_METHOD("set_snap_2d_transforms_to_pixel", "enabled"), &Viewport::set_snap_2d_transforms_to_pixel);
5054
ClassDB::bind_method(D_METHOD("is_snap_2d_transforms_to_pixel_enabled"), &Viewport::is_snap_2d_transforms_to_pixel_enabled);
5055
5056
ClassDB::bind_method(D_METHOD("set_snap_2d_vertices_to_pixel", "enabled"), &Viewport::set_snap_2d_vertices_to_pixel);
5057
ClassDB::bind_method(D_METHOD("is_snap_2d_vertices_to_pixel_enabled"), &Viewport::is_snap_2d_vertices_to_pixel_enabled);
5058
5059
ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_quadrant_subdiv", "quadrant", "subdiv"), &Viewport::set_positional_shadow_atlas_quadrant_subdiv);
5060
ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_quadrant_subdiv", "quadrant"), &Viewport::get_positional_shadow_atlas_quadrant_subdiv);
5061
5062
ClassDB::bind_method(D_METHOD("set_input_as_handled"), &Viewport::set_input_as_handled);
5063
ClassDB::bind_method(D_METHOD("is_input_handled"), &Viewport::is_input_handled);
5064
5065
ClassDB::bind_method(D_METHOD("set_handle_input_locally", "enable"), &Viewport::set_handle_input_locally);
5066
ClassDB::bind_method(D_METHOD("is_handling_input_locally"), &Viewport::is_handling_input_locally);
5067
5068
ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_filter", "mode"), &Viewport::set_default_canvas_item_texture_filter);
5069
ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_filter"), &Viewport::get_default_canvas_item_texture_filter);
5070
5071
ClassDB::bind_method(D_METHOD("set_embedding_subwindows", "enable"), &Viewport::set_embedding_subwindows);
5072
ClassDB::bind_method(D_METHOD("is_embedding_subwindows"), &Viewport::is_embedding_subwindows);
5073
ClassDB::bind_method(D_METHOD("get_embedded_subwindows"), &Viewport::get_embedded_subwindows);
5074
5075
ClassDB::bind_method(D_METHOD("set_canvas_cull_mask", "mask"), &Viewport::set_canvas_cull_mask);
5076
ClassDB::bind_method(D_METHOD("get_canvas_cull_mask"), &Viewport::get_canvas_cull_mask);
5077
5078
ClassDB::bind_method(D_METHOD("set_canvas_cull_mask_bit", "layer", "enable"), &Viewport::set_canvas_cull_mask_bit);
5079
ClassDB::bind_method(D_METHOD("get_canvas_cull_mask_bit", "layer"), &Viewport::get_canvas_cull_mask_bit);
5080
5081
ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_repeat", "mode"), &Viewport::set_default_canvas_item_texture_repeat);
5082
ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_repeat"), &Viewport::get_default_canvas_item_texture_repeat);
5083
5084
ClassDB::bind_method(D_METHOD("set_sdf_oversize", "oversize"), &Viewport::set_sdf_oversize);
5085
ClassDB::bind_method(D_METHOD("get_sdf_oversize"), &Viewport::get_sdf_oversize);
5086
5087
ClassDB::bind_method(D_METHOD("set_sdf_scale", "scale"), &Viewport::set_sdf_scale);
5088
ClassDB::bind_method(D_METHOD("get_sdf_scale"), &Viewport::get_sdf_scale);
5089
5090
ClassDB::bind_method(D_METHOD("set_mesh_lod_threshold", "pixels"), &Viewport::set_mesh_lod_threshold);
5091
ClassDB::bind_method(D_METHOD("get_mesh_lod_threshold"), &Viewport::get_mesh_lod_threshold);
5092
5093
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5094
ClassDB::bind_method(D_METHOD("_process_picking"), &Viewport::_process_picking);
5095
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5096
5097
ClassDB::bind_method(D_METHOD("set_as_audio_listener_2d", "enable"), &Viewport::set_as_audio_listener_2d);
5098
ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d);
5099
ClassDB::bind_method(D_METHOD("get_audio_listener_2d"), &Viewport::get_audio_listener_2d);
5100
ClassDB::bind_method(D_METHOD("get_camera_2d"), &Viewport::get_camera_2d);
5101
5102
#ifndef _3D_DISABLED
5103
ClassDB::bind_method(D_METHOD("set_world_3d", "world_3d"), &Viewport::set_world_3d);
5104
ClassDB::bind_method(D_METHOD("get_world_3d"), &Viewport::get_world_3d);
5105
ClassDB::bind_method(D_METHOD("find_world_3d"), &Viewport::find_world_3d);
5106
5107
ClassDB::bind_method(D_METHOD("set_use_own_world_3d", "enable"), &Viewport::set_use_own_world_3d);
5108
ClassDB::bind_method(D_METHOD("is_using_own_world_3d"), &Viewport::is_using_own_world_3d);
5109
5110
ClassDB::bind_method(D_METHOD("get_audio_listener_3d"), &Viewport::get_audio_listener_3d);
5111
ClassDB::bind_method(D_METHOD("get_camera_3d"), &Viewport::get_camera_3d);
5112
ClassDB::bind_method(D_METHOD("set_as_audio_listener_3d", "enable"), &Viewport::set_as_audio_listener_3d);
5113
ClassDB::bind_method(D_METHOD("is_audio_listener_3d"), &Viewport::is_audio_listener_3d);
5114
5115
ClassDB::bind_method(D_METHOD("set_disable_3d", "disable"), &Viewport::set_disable_3d);
5116
ClassDB::bind_method(D_METHOD("is_3d_disabled"), &Viewport::is_3d_disabled);
5117
5118
ClassDB::bind_method(D_METHOD("set_use_xr", "use"), &Viewport::set_use_xr);
5119
ClassDB::bind_method(D_METHOD("is_using_xr"), &Viewport::is_using_xr);
5120
5121
ClassDB::bind_method(D_METHOD("set_scaling_3d_mode", "scaling_3d_mode"), &Viewport::set_scaling_3d_mode);
5122
ClassDB::bind_method(D_METHOD("get_scaling_3d_mode"), &Viewport::get_scaling_3d_mode);
5123
5124
ClassDB::bind_method(D_METHOD("set_scaling_3d_scale", "scale"), &Viewport::set_scaling_3d_scale);
5125
ClassDB::bind_method(D_METHOD("get_scaling_3d_scale"), &Viewport::get_scaling_3d_scale);
5126
5127
ClassDB::bind_method(D_METHOD("set_fsr_sharpness", "fsr_sharpness"), &Viewport::set_fsr_sharpness);
5128
ClassDB::bind_method(D_METHOD("get_fsr_sharpness"), &Viewport::get_fsr_sharpness);
5129
5130
ClassDB::bind_method(D_METHOD("set_texture_mipmap_bias", "texture_mipmap_bias"), &Viewport::set_texture_mipmap_bias);
5131
ClassDB::bind_method(D_METHOD("get_texture_mipmap_bias"), &Viewport::get_texture_mipmap_bias);
5132
5133
ClassDB::bind_method(D_METHOD("set_anisotropic_filtering_level", "anisotropic_filtering_level"), &Viewport::set_anisotropic_filtering_level);
5134
ClassDB::bind_method(D_METHOD("get_anisotropic_filtering_level"), &Viewport::get_anisotropic_filtering_level);
5135
5136
ClassDB::bind_method(D_METHOD("set_vrs_mode", "mode"), &Viewport::set_vrs_mode);
5137
ClassDB::bind_method(D_METHOD("get_vrs_mode"), &Viewport::get_vrs_mode);
5138
5139
ClassDB::bind_method(D_METHOD("set_vrs_update_mode", "mode"), &Viewport::set_vrs_update_mode);
5140
ClassDB::bind_method(D_METHOD("get_vrs_update_mode"), &Viewport::get_vrs_update_mode);
5141
5142
ClassDB::bind_method(D_METHOD("set_vrs_texture", "texture"), &Viewport::set_vrs_texture);
5143
ClassDB::bind_method(D_METHOD("get_vrs_texture"), &Viewport::get_vrs_texture);
5144
5145
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
5146
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_xr"), "set_use_xr", "is_using_xr");
5147
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d");
5148
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d");
5149
#endif // _3D_DISABLED
5150
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", PROPERTY_USAGE_NONE), "set_world_2d", "get_world_2d");
5151
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transparent_bg"), "set_transparent_background", "has_transparent_background");
5152
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handle_input_locally"), "set_handle_input_locally", "is_handling_input_locally");
5153
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_transforms_to_pixel"), "set_snap_2d_transforms_to_pixel", "is_snap_2d_transforms_to_pixel_enabled");
5154
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_vertices_to_pixel"), "set_snap_2d_vertices_to_pixel", "is_snap_2d_vertices_to_pixel_enabled");
5155
ADD_GROUP("Rendering", "");
5156
ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_2d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_2d", "get_msaa_2d");
5157
ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_3d", "get_msaa_3d");
5158
ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast),SMAA (Average)"), "set_screen_space_aa", "get_screen_space_aa");
5159
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_taa"), "set_use_taa", "is_using_taa");
5160
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding");
5161
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling");
5162
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mesh_lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_mesh_lod_threshold", "get_mesh_lod_threshold");
5163
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Lighting,Overdraw,Wireframe,Normal Buffer,VoxelGI Albedo,VoxelGI Lighting,VoxelGI Emission,Shadow Atlas,Directional Shadow Map,Scene Luminance,SSAO,SSIL,Directional Shadow Splits,Decal Atlas,SDFGI Cascades,SDFGI Probes,VoxelGI/SDFGI Buffer,Disable Mesh LOD,OmniLight3D Cluster,SpotLight3D Cluster,Decal Cluster,ReflectionProbe Cluster,Occlusion Culling Buffer,Motion Vectors,Internal Buffer"), "set_debug_draw", "get_debug_draw");
5164
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr_2d"), "set_use_hdr_2d", "is_using_hdr_2d");
5165
5166
#ifndef _3D_DISABLED
5167
ADD_GROUP("Scaling 3D", "");
5168
ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast),FSR 2.2 (Slow),MetalFX (Spatial),MetalFX (Temporal)"), "set_scaling_3d_mode", "get_scaling_3d_mode");
5169
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scaling_3d_scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scaling_3d_scale", "get_scaling_3d_scale");
5170
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), "set_texture_mipmap_bias", "get_texture_mipmap_bias");
5171
ADD_PROPERTY(PropertyInfo(Variant::INT, "anisotropic_filtering_level", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Faster),4× (Fast),8× (Average),16x (Slow)")), "set_anisotropic_filtering_level", "get_anisotropic_filtering_level");
5172
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness");
5173
ADD_GROUP("Variable Rate Shading", "vrs_");
5174
ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_mode", PROPERTY_HINT_ENUM, "Disabled,Texture,Depth buffer,XR"), "set_vrs_mode", "get_vrs_mode");
5175
ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,Always"), "set_vrs_update_mode", "get_vrs_update_mode");
5176
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "vrs_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_vrs_texture", "get_vrs_texture");
5177
#endif
5178
ADD_GROUP("Canvas Items", "canvas_item_");
5179
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter");
5180
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirror"), "set_default_canvas_item_texture_repeat", "get_default_canvas_item_texture_repeat");
5181
ADD_GROUP("Audio Listener", "audio_listener_");
5182
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_2d"), "set_as_audio_listener_2d", "is_audio_listener_2d");
5183
#ifndef _3D_DISABLED
5184
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_3d"), "set_as_audio_listener_3d", "is_audio_listener_3d");
5185
#endif // _3D_DISABLED
5186
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5187
ADD_GROUP("Physics", "physics_");
5188
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking"), "set_physics_object_picking", "get_physics_object_picking");
5189
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking_sort"), "set_physics_object_picking_sort", "get_physics_object_picking_sort");
5190
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking_first_only"), "set_physics_object_picking_first_only", "get_physics_object_picking_first_only");
5191
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
5192
ADD_GROUP("GUI", "gui_");
5193
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_disable_input"), "set_disable_input", "is_input_disabled");
5194
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_snap_controls_to_pixels"), "set_snap_controls_to_pixels", "is_snap_controls_to_pixels_enabled");
5195
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_embed_subwindows"), "set_embedding_subwindows", "is_embedding_subwindows");
5196
ADD_GROUP("SDF", "sdf_");
5197
ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_oversize", PROPERTY_HINT_ENUM, "100%,120%,150%,200%"), "set_sdf_oversize", "get_sdf_oversize");
5198
ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_scale", PROPERTY_HINT_ENUM, "100%,50%,25%"), "set_sdf_scale", "get_sdf_scale");
5199
ADD_GROUP("Positional Shadow Atlas", "positional_shadow_atlas_");
5200
ADD_PROPERTY(PropertyInfo(Variant::INT, "positional_shadow_atlas_size"), "set_positional_shadow_atlas_size", "get_positional_shadow_atlas_size");
5201
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "positional_shadow_atlas_16_bits"), "set_positional_shadow_atlas_16_bits", "get_positional_shadow_atlas_16_bits");
5202
ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_0", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 0);
5203
ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_1", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 1);
5204
ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_2", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 2);
5205
ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_3", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 3);
5206
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_canvas_transform", "get_canvas_transform");
5207
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_canvas_transform", "get_global_canvas_transform");
5208
ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_cull_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_canvas_cull_mask", "get_canvas_cull_mask");
5209
5210
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "oversampling"), "set_use_oversampling", "is_using_oversampling");
5211
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling_override", PROPERTY_HINT_RANGE, "0,16,0.0001,or_greater"), "set_oversampling_override", "get_oversampling_override");
5212
5213
ADD_SIGNAL(MethodInfo("size_changed"));
5214
ADD_SIGNAL(MethodInfo("gui_focus_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
5215
5216
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);
5217
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1);
5218
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_4);
5219
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_16);
5220
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_64);
5221
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_256);
5222
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1024);
5223
BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_MAX);
5224
5225
BIND_ENUM_CONSTANT(SCALING_3D_MODE_BILINEAR);
5226
BIND_ENUM_CONSTANT(SCALING_3D_MODE_FSR);
5227
BIND_ENUM_CONSTANT(SCALING_3D_MODE_FSR2);
5228
BIND_ENUM_CONSTANT(SCALING_3D_MODE_METALFX_SPATIAL);
5229
BIND_ENUM_CONSTANT(SCALING_3D_MODE_METALFX_TEMPORAL);
5230
BIND_ENUM_CONSTANT(SCALING_3D_MODE_MAX);
5231
5232
BIND_ENUM_CONSTANT(MSAA_DISABLED);
5233
BIND_ENUM_CONSTANT(MSAA_2X);
5234
BIND_ENUM_CONSTANT(MSAA_4X);
5235
BIND_ENUM_CONSTANT(MSAA_8X);
5236
BIND_ENUM_CONSTANT(MSAA_MAX);
5237
5238
BIND_ENUM_CONSTANT(ANISOTROPY_DISABLED);
5239
BIND_ENUM_CONSTANT(ANISOTROPY_2X);
5240
BIND_ENUM_CONSTANT(ANISOTROPY_4X);
5241
BIND_ENUM_CONSTANT(ANISOTROPY_8X);
5242
BIND_ENUM_CONSTANT(ANISOTROPY_16X);
5243
BIND_ENUM_CONSTANT(ANISOTROPY_MAX);
5244
5245
BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_DISABLED);
5246
BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_FXAA);
5247
BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_SMAA);
5248
BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_MAX);
5249
5250
BIND_ENUM_CONSTANT(RENDER_INFO_OBJECTS_IN_FRAME);
5251
BIND_ENUM_CONSTANT(RENDER_INFO_PRIMITIVES_IN_FRAME);
5252
BIND_ENUM_CONSTANT(RENDER_INFO_DRAW_CALLS_IN_FRAME);
5253
BIND_ENUM_CONSTANT(RENDER_INFO_MAX);
5254
5255
BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_VISIBLE);
5256
BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_SHADOW);
5257
BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_CANVAS);
5258
BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_MAX);
5259
5260
BIND_ENUM_CONSTANT(DEBUG_DRAW_DISABLED);
5261
BIND_ENUM_CONSTANT(DEBUG_DRAW_UNSHADED);
5262
BIND_ENUM_CONSTANT(DEBUG_DRAW_LIGHTING);
5263
BIND_ENUM_CONSTANT(DEBUG_DRAW_OVERDRAW);
5264
BIND_ENUM_CONSTANT(DEBUG_DRAW_WIREFRAME);
5265
BIND_ENUM_CONSTANT(DEBUG_DRAW_NORMAL_BUFFER);
5266
BIND_ENUM_CONSTANT(DEBUG_DRAW_VOXEL_GI_ALBEDO);
5267
BIND_ENUM_CONSTANT(DEBUG_DRAW_VOXEL_GI_LIGHTING);
5268
BIND_ENUM_CONSTANT(DEBUG_DRAW_VOXEL_GI_EMISSION);
5269
BIND_ENUM_CONSTANT(DEBUG_DRAW_SHADOW_ATLAS);
5270
BIND_ENUM_CONSTANT(DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS);
5271
BIND_ENUM_CONSTANT(DEBUG_DRAW_SCENE_LUMINANCE);
5272
BIND_ENUM_CONSTANT(DEBUG_DRAW_SSAO);
5273
BIND_ENUM_CONSTANT(DEBUG_DRAW_SSIL);
5274
BIND_ENUM_CONSTANT(DEBUG_DRAW_PSSM_SPLITS);
5275
BIND_ENUM_CONSTANT(DEBUG_DRAW_DECAL_ATLAS);
5276
BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI);
5277
BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI_PROBES);
5278
BIND_ENUM_CONSTANT(DEBUG_DRAW_GI_BUFFER);
5279
BIND_ENUM_CONSTANT(DEBUG_DRAW_DISABLE_LOD);
5280
BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_OMNI_LIGHTS);
5281
BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_SPOT_LIGHTS);
5282
BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_DECALS);
5283
BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_REFLECTION_PROBES);
5284
BIND_ENUM_CONSTANT(DEBUG_DRAW_OCCLUDERS)
5285
BIND_ENUM_CONSTANT(DEBUG_DRAW_MOTION_VECTORS)
5286
BIND_ENUM_CONSTANT(DEBUG_DRAW_INTERNAL_BUFFER);
5287
5288
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
5289
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR);
5290
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS);
5291
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
5292
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX);
5293
5294
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
5295
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
5296
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
5297
BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX);
5298
5299
BIND_ENUM_CONSTANT(SDF_OVERSIZE_100_PERCENT);
5300
BIND_ENUM_CONSTANT(SDF_OVERSIZE_120_PERCENT);
5301
BIND_ENUM_CONSTANT(SDF_OVERSIZE_150_PERCENT);
5302
BIND_ENUM_CONSTANT(SDF_OVERSIZE_200_PERCENT);
5303
BIND_ENUM_CONSTANT(SDF_OVERSIZE_MAX);
5304
5305
BIND_ENUM_CONSTANT(SDF_SCALE_100_PERCENT);
5306
BIND_ENUM_CONSTANT(SDF_SCALE_50_PERCENT);
5307
BIND_ENUM_CONSTANT(SDF_SCALE_25_PERCENT);
5308
BIND_ENUM_CONSTANT(SDF_SCALE_MAX);
5309
5310
BIND_ENUM_CONSTANT(VRS_DISABLED);
5311
BIND_ENUM_CONSTANT(VRS_TEXTURE);
5312
BIND_ENUM_CONSTANT(VRS_XR);
5313
BIND_ENUM_CONSTANT(VRS_MAX);
5314
5315
BIND_ENUM_CONSTANT(VRS_UPDATE_DISABLED);
5316
BIND_ENUM_CONSTANT(VRS_UPDATE_ONCE);
5317
BIND_ENUM_CONSTANT(VRS_UPDATE_ALWAYS);
5318
BIND_ENUM_CONSTANT(VRS_UPDATE_MAX);
5319
}
5320
5321
void Viewport::_validate_property(PropertyInfo &p_property) const {
5322
if (!Engine::get_singleton()->is_editor_hint()) {
5323
return;
5324
}
5325
if (vrs_mode != VRS_TEXTURE && (p_property.name == "vrs_texture")) {
5326
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
5327
}
5328
5329
if (vrs_mode == VRS_DISABLED && (p_property.name == "vrs_update_mode")) {
5330
p_property.usage = PROPERTY_USAGE_NO_EDITOR;
5331
}
5332
}
5333
5334
Viewport::Viewport() {
5335
world_2d.instantiate();
5336
world_2d->register_viewport(this);
5337
5338
viewport = RenderingServer::get_singleton()->viewport_create();
5339
texture_rid = RenderingServer::get_singleton()->viewport_get_texture(viewport);
5340
5341
default_texture.instantiate();
5342
default_texture->vp = this;
5343
viewport_textures.insert(default_texture.ptr());
5344
default_texture->proxy = RS::get_singleton()->texture_proxy_create(texture_rid);
5345
5346
canvas_layers.insert(nullptr); // This eases picking code (interpreted as the canvas of the Viewport).
5347
5348
set_positional_shadow_atlas_size(positional_shadow_atlas_size);
5349
5350
for (int i = 0; i < 4; i++) {
5351
positional_shadow_atlas_quadrant_subdiv[i] = SHADOW_ATLAS_QUADRANT_SUBDIV_MAX;
5352
}
5353
set_positional_shadow_atlas_quadrant_subdiv(0, SHADOW_ATLAS_QUADRANT_SUBDIV_4);
5354
set_positional_shadow_atlas_quadrant_subdiv(1, SHADOW_ATLAS_QUADRANT_SUBDIV_4);
5355
set_positional_shadow_atlas_quadrant_subdiv(2, SHADOW_ATLAS_QUADRANT_SUBDIV_16);
5356
set_positional_shadow_atlas_quadrant_subdiv(3, SHADOW_ATLAS_QUADRANT_SUBDIV_64);
5357
5358
set_mesh_lod_threshold(mesh_lod_threshold);
5359
5360
String id = itos(get_instance_id());
5361
input_group = "_vp_input" + id;
5362
unhandled_input_group = "_vp_unhandled_input" + id;
5363
shortcut_input_group = "_vp_shortcut_input" + id;
5364
unhandled_key_input_group = "_vp_unhandled_key_input" + id;
5365
5366
// Window tooltip.
5367
gui.tooltip_delay = GLOBAL_GET("gui/timers/tooltip_delay_sec");
5368
5369
#ifndef _3D_DISABLED
5370
set_scaling_3d_mode((Viewport::Scaling3DMode)(int)GLOBAL_GET("rendering/scaling_3d/mode"));
5371
set_scaling_3d_scale(GLOBAL_GET("rendering/scaling_3d/scale"));
5372
set_fsr_sharpness((float)GLOBAL_GET("rendering/scaling_3d/fsr_sharpness"));
5373
set_texture_mipmap_bias((float)GLOBAL_GET("rendering/textures/default_filters/texture_mipmap_bias"));
5374
set_anisotropic_filtering_level((Viewport::AnisotropicFiltering)(int)GLOBAL_GET("rendering/textures/default_filters/anisotropic_filtering_level"));
5375
#endif // _3D_DISABLED
5376
5377
set_sdf_oversize(sdf_oversize); // Set to server.
5378
5379
// Physics interpolation mode for viewports is a special case.
5380
// Typically viewports will be housed within Controls,
5381
// and Controls default to PHYSICS_INTERPOLATION_MODE_OFF.
5382
// Viewports can thus inherit physics interpolation OFF, which is unexpected.
5383
// Setting to ON allows each viewport to have a fresh interpolation state.
5384
set_physics_interpolation_mode(Node::PHYSICS_INTERPOLATION_MODE_ON);
5385
}
5386
5387
Viewport::~Viewport() {
5388
// Erase itself from viewport textures.
5389
for (ViewportTexture *E : viewport_textures) {
5390
E->vp = nullptr;
5391
}
5392
if (world_2d.is_valid()) {
5393
world_2d->remove_viewport(this);
5394
}
5395
ERR_FAIL_NULL(RenderingServer::get_singleton());
5396
RenderingServer::get_singleton()->free(viewport);
5397
}
5398
5399
/////////////////////////////////
5400
5401
void SubViewport::set_size(const Size2i &p_size) {
5402
ERR_MAIN_THREAD_GUARD;
5403
_internal_set_size(p_size);
5404
}
5405
5406
void SubViewport::set_size_force(const Size2i &p_size) {
5407
ERR_MAIN_THREAD_GUARD;
5408
// Use only for setting the size from the parent SubViewportContainer with enabled stretch mode.
5409
// Don't expose function to scripting.
5410
_internal_set_size(p_size, true);
5411
}
5412
5413
void SubViewport::_internal_set_size(const Size2i &p_size, bool p_force) {
5414
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
5415
if (!p_force && c && c->is_stretch_enabled()) {
5416
#ifdef DEBUG_ENABLED
5417
WARN_PRINT("Can't change the size of a `SubViewport` with a `SubViewportContainer` parent that has `stretch` enabled. Set `SubViewportContainer.stretch` to `false` to allow changing the size manually.");
5418
#endif // DEBUG_ENABLED
5419
return;
5420
}
5421
5422
_set_size(p_size, _get_size_2d_override(), true);
5423
5424
if (c) {
5425
c->update_minimum_size();
5426
c->queue_redraw();
5427
}
5428
}
5429
5430
Size2i SubViewport::get_size() const {
5431
ERR_READ_THREAD_GUARD_V(Size2());
5432
return _get_size();
5433
}
5434
5435
void SubViewport::set_size_2d_override(const Size2i &p_size) {
5436
ERR_MAIN_THREAD_GUARD;
5437
_set_size(_get_size(), p_size, true);
5438
}
5439
5440
Size2i SubViewport::get_size_2d_override() const {
5441
ERR_READ_THREAD_GUARD_V(Size2i());
5442
// Rounding will cause offset issues with the
5443
// exact positioning of subwindows, but changing the
5444
// type of size_2d_override would break compatibility.
5445
return Size2i((_get_size_2d_override() + Size2(0.5, 0.5)).floor());
5446
}
5447
5448
void SubViewport::set_size_2d_override_stretch(bool p_enable) {
5449
ERR_MAIN_THREAD_GUARD;
5450
if (p_enable == size_2d_override_stretch) {
5451
return;
5452
}
5453
5454
size_2d_override_stretch = p_enable;
5455
_set_size(_get_size(), _get_size_2d_override(), true);
5456
}
5457
5458
bool SubViewport::is_size_2d_override_stretch_enabled() const {
5459
ERR_READ_THREAD_GUARD_V(false);
5460
return size_2d_override_stretch;
5461
}
5462
5463
void SubViewport::set_update_mode(UpdateMode p_mode) {
5464
ERR_MAIN_THREAD_GUARD;
5465
update_mode = p_mode;
5466
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::ViewportUpdateMode(p_mode));
5467
}
5468
5469
SubViewport::UpdateMode SubViewport::get_update_mode() const {
5470
ERR_READ_THREAD_GUARD_V(UPDATE_DISABLED);
5471
return update_mode;
5472
}
5473
5474
void SubViewport::set_clear_mode(ClearMode p_mode) {
5475
ERR_MAIN_THREAD_GUARD;
5476
clear_mode = p_mode;
5477
RS::get_singleton()->viewport_set_clear_mode(get_viewport_rid(), RS::ViewportClearMode(p_mode));
5478
}
5479
5480
SubViewport::ClearMode SubViewport::get_clear_mode() const {
5481
ERR_READ_THREAD_GUARD_V(CLEAR_MODE_ALWAYS);
5482
return clear_mode;
5483
}
5484
5485
DisplayServer::WindowID SubViewport::get_window_id() const {
5486
ERR_READ_THREAD_GUARD_V(DisplayServer::INVALID_WINDOW_ID);
5487
return DisplayServer::INVALID_WINDOW_ID;
5488
}
5489
5490
Transform2D SubViewport::get_screen_transform_internal(bool p_absolute_position) const {
5491
ERR_READ_THREAD_GUARD_V(Transform2D());
5492
Transform2D container_transform;
5493
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
5494
if (c) {
5495
if (c->is_stretch_enabled()) {
5496
container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink()));
5497
}
5498
container_transform = c->get_viewport()->get_screen_transform_internal(p_absolute_position) * c->get_global_transform_with_canvas() * container_transform;
5499
} else {
5500
WARN_PRINT_ONCE("SubViewport is not a child of a SubViewportContainer. get_screen_transform doesn't return the actual screen position.");
5501
}
5502
return container_transform * get_final_transform();
5503
}
5504
5505
Transform2D SubViewport::get_popup_base_transform() const {
5506
ERR_READ_THREAD_GUARD_V(Transform2D());
5507
if (is_embedding_subwindows()) {
5508
return Transform2D();
5509
}
5510
SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
5511
if (!c) {
5512
return get_final_transform();
5513
}
5514
Transform2D container_transform;
5515
if (c->is_stretch_enabled()) {
5516
container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink()));
5517
}
5518
return c->get_screen_transform() * container_transform * get_final_transform();
5519
}
5520
5521
Viewport *SubViewport::get_section_root_viewport() const {
5522
if (Object::cast_to<SubViewportContainer>(get_parent()) && get_parent()->get_viewport()) {
5523
return get_parent()->get_viewport()->get_section_root_viewport();
5524
}
5525
SubViewport *vp = const_cast<SubViewport *>(this);
5526
return vp;
5527
}
5528
5529
bool SubViewport::is_attached_in_viewport() const {
5530
return Object::cast_to<SubViewportContainer>(get_parent());
5531
}
5532
5533
void SubViewport::_notification(int p_what) {
5534
ERR_MAIN_THREAD_GUARD;
5535
switch (p_what) {
5536
case NOTIFICATION_ENTER_TREE: {
5537
RS::get_singleton()->viewport_set_active(get_viewport_rid(), true);
5538
5539
SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
5540
if (parent_svc) {
5541
parent_svc->recalc_force_viewport_sizes();
5542
}
5543
} break;
5544
5545
case NOTIFICATION_EXIT_TREE: {
5546
RS::get_singleton()->viewport_set_active(get_viewport_rid(), false);
5547
} break;
5548
}
5549
}
5550
5551
void SubViewport::_bind_methods() {
5552
ClassDB::bind_method(D_METHOD("set_size", "size"), &SubViewport::set_size);
5553
ClassDB::bind_method(D_METHOD("get_size"), &SubViewport::get_size);
5554
5555
ClassDB::bind_method(D_METHOD("set_size_2d_override", "size"), &SubViewport::set_size_2d_override);
5556
ClassDB::bind_method(D_METHOD("get_size_2d_override"), &SubViewport::get_size_2d_override);
5557
5558
ClassDB::bind_method(D_METHOD("set_size_2d_override_stretch", "enable"), &SubViewport::set_size_2d_override_stretch);
5559
ClassDB::bind_method(D_METHOD("is_size_2d_override_stretch_enabled"), &SubViewport::is_size_2d_override_stretch_enabled);
5560
5561
ClassDB::bind_method(D_METHOD("set_update_mode", "mode"), &SubViewport::set_update_mode);
5562
ClassDB::bind_method(D_METHOD("get_update_mode"), &SubViewport::get_update_mode);
5563
5564
ClassDB::bind_method(D_METHOD("set_clear_mode", "mode"), &SubViewport::set_clear_mode);
5565
ClassDB::bind_method(D_METHOD("get_clear_mode"), &SubViewport::get_clear_mode);
5566
5567
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
5568
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size_2d_override", PROPERTY_HINT_NONE, "suffix:px"), "set_size_2d_override", "get_size_2d_override");
5569
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "size_2d_override_stretch"), "set_size_2d_override_stretch", "is_size_2d_override_stretch_enabled");
5570
ADD_GROUP("Render Target", "render_target_");
5571
ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_clear_mode", PROPERTY_HINT_ENUM, "Always,Never,Next Frame"), "set_clear_mode", "get_clear_mode");
5572
ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,When Visible,When Parent Visible,Always"), "set_update_mode", "get_update_mode");
5573
5574
BIND_ENUM_CONSTANT(CLEAR_MODE_ALWAYS);
5575
BIND_ENUM_CONSTANT(CLEAR_MODE_NEVER);
5576
BIND_ENUM_CONSTANT(CLEAR_MODE_ONCE);
5577
5578
BIND_ENUM_CONSTANT(UPDATE_DISABLED);
5579
BIND_ENUM_CONSTANT(UPDATE_ONCE);
5580
BIND_ENUM_CONSTANT(UPDATE_WHEN_VISIBLE);
5581
BIND_ENUM_CONSTANT(UPDATE_WHEN_PARENT_VISIBLE);
5582
BIND_ENUM_CONSTANT(UPDATE_ALWAYS);
5583
}
5584
5585
void SubViewport::_validate_property(PropertyInfo &p_property) const {
5586
if (!Engine::get_singleton()->is_editor_hint()) {
5587
return;
5588
}
5589
if (p_property.name == "size") {
5590
SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
5591
if (parent_svc && parent_svc->is_stretch_enabled()) {
5592
p_property.usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY;
5593
} else {
5594
p_property.usage = PROPERTY_USAGE_DEFAULT;
5595
}
5596
}
5597
}
5598
5599
SubViewport::SubViewport() {
5600
RS::get_singleton()->viewport_set_size(get_viewport_rid(), get_size().width, get_size().height);
5601
}
5602
5603