initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
6
scene/main/SCsub
Normal file
6
scene/main/SCsub
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.scene_sources, "*.cpp")
|
91
scene/main/canvas_item.compat.inc
Normal file
91
scene/main/canvas_item.compat.inc
Normal file
@@ -0,0 +1,91 @@
|
||||
/**************************************************************************/
|
||||
/* canvas_item.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
void CanvasItem::_draw_string_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
|
||||
draw_string(p_font, p_pos, p_text, p_alignment, p_width, p_font_size, p_modulate, p_jst_flags, p_direction, p_orientation, 0.0);
|
||||
}
|
||||
|
||||
void CanvasItem::_draw_multiline_string_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
|
||||
draw_multiline_string(p_font, p_pos, p_text, p_alignment, p_width, p_font_size, p_max_lines, p_modulate, p_brk_flags, p_jst_flags, p_direction, p_orientation, 0.0);
|
||||
}
|
||||
|
||||
void CanvasItem::_draw_string_outline_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_size, const Color &p_modulate, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
|
||||
draw_string_outline(p_font, p_pos, p_text, p_alignment, p_width, p_font_size, p_size, p_modulate, p_jst_flags, p_direction, p_orientation, 0.0);
|
||||
}
|
||||
|
||||
void CanvasItem::_draw_multiline_string_outline_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment, float p_width, int p_font_size, int p_max_lines, int p_size, const Color &p_modulate, BitField<TextServer::LineBreakFlag> p_brk_flags, BitField<TextServer::JustificationFlag> p_jst_flags, TextServer::Direction p_direction, TextServer::Orientation p_orientation) const {
|
||||
draw_multiline_string_outline(p_font, p_pos, p_text, p_alignment, p_width, p_font_size, p_max_lines, p_size, p_modulate, p_brk_flags, p_jst_flags, p_direction, p_orientation, 0.0);
|
||||
}
|
||||
|
||||
void CanvasItem::_draw_char_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size, const Color &p_modulate) const {
|
||||
draw_char(p_font, p_pos, p_char, p_font_size, p_modulate, 0.0);
|
||||
}
|
||||
|
||||
void CanvasItem::_draw_char_outline_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size, int p_size, const Color &p_modulate) const {
|
||||
draw_char_outline(p_font, p_pos, p_char, p_font_size, p_size, p_modulate, 0.0);
|
||||
}
|
||||
|
||||
void CanvasItem::_draw_circle_bind_compat_84472(const Point2 &p_pos, real_t p_radius, const Color &p_color) {
|
||||
draw_circle(p_pos, p_radius, p_color, true, -1.0, false);
|
||||
}
|
||||
|
||||
void CanvasItem::_draw_rect_bind_compat_84523(const Rect2 &p_rect, const Color &p_color, bool p_filled, real_t p_width) {
|
||||
draw_rect(p_rect, p_color, p_filled, p_width, false);
|
||||
}
|
||||
|
||||
void CanvasItem::_draw_dashed_line_bind_compat_84523(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, real_t p_dash, bool p_aligned) {
|
||||
draw_dashed_line(p_from, p_to, p_color, p_width, p_dash, p_aligned, false);
|
||||
}
|
||||
|
||||
void CanvasItem::_draw_multiline_bind_compat_84523(const Vector<Point2> &p_points, const Color &p_color, real_t p_width) {
|
||||
draw_multiline(p_points, p_color, p_width, false);
|
||||
}
|
||||
|
||||
void CanvasItem::_draw_multiline_colors_bind_compat_84523(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width) {
|
||||
draw_multiline_colors(p_points, p_colors, p_width, false);
|
||||
}
|
||||
|
||||
void CanvasItem::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("draw_string", "font", "pos", "text", "alignment", "width", "font_size", "modulate", "justification_flags", "direction", "orientation"), &CanvasItem::_draw_string_bind_compat_104872, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("draw_multiline_string", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "modulate", "brk_flags", "justification_flags", "direction", "orientation"), &CanvasItem::_draw_multiline_string_bind_compat_104872, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("draw_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "size", "modulate", "justification_flags", "direction", "orientation"), &CanvasItem::_draw_string_outline_bind_compat_104872, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("draw_multiline_string_outline", "font", "pos", "text", "alignment", "width", "font_size", "max_lines", "size", "modulate", "brk_flags", "justification_flags", "direction", "orientation"), &CanvasItem::_draw_multiline_string_outline_bind_compat_104872, DEFVAL(HORIZONTAL_ALIGNMENT_LEFT), DEFVAL(-1), DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(1), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND), DEFVAL(TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND), DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(TextServer::ORIENTATION_HORIZONTAL));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("draw_char", "font", "pos", "char", "font_size", "modulate"), &CanvasItem::_draw_char_bind_compat_104872, DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(Color(1.0, 1.0, 1.0)));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("draw_char_outline", "font", "pos", "char", "font_size", "size", "modulate"), &CanvasItem::_draw_char_outline_bind_compat_104872, DEFVAL(Font::DEFAULT_FONT_SIZE), DEFVAL(-1), DEFVAL(Color(1.0, 1.0, 1.0)));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("draw_circle", "position", "radius", "color"), &CanvasItem::_draw_circle_bind_compat_84472);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("draw_rect", "rect", "color", "filled", "width"), &CanvasItem::_draw_rect_bind_compat_84523, DEFVAL(true), DEFVAL(-1.0));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("draw_dashed_line", "from", "to", "color", "width", "dash", "aligned"), &CanvasItem::_draw_dashed_line_bind_compat_84523, DEFVAL(-1.0), DEFVAL(2.0), DEFVAL(true));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("draw_multiline", "points", "color", "width"), &CanvasItem::_draw_multiline_bind_compat_84523, DEFVAL(-1.0));
|
||||
ClassDB::bind_compatibility_method(D_METHOD("draw_multiline_colors", "points", "colors", "width"), &CanvasItem::_draw_multiline_colors_bind_compat_84523, DEFVAL(-1.0));
|
||||
}
|
||||
|
||||
#endif
|
1919
scene/main/canvas_item.cpp
Normal file
1919
scene/main/canvas_item.cpp
Normal file
File diff suppressed because it is too large
Load Diff
469
scene/main/canvas_item.h
Normal file
469
scene/main/canvas_item.h
Normal file
@@ -0,0 +1,469 @@
|
||||
/**************************************************************************/
|
||||
/* canvas_item.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/main/node.h"
|
||||
#include "scene/resources/font.h"
|
||||
|
||||
class CanvasLayer;
|
||||
class MultiMesh;
|
||||
class StyleBox;
|
||||
class Window;
|
||||
class World2D;
|
||||
|
||||
class CanvasItem : public Node {
|
||||
GDCLASS(CanvasItem, Node);
|
||||
|
||||
friend class CanvasLayer;
|
||||
|
||||
public:
|
||||
enum TextureFilter {
|
||||
TEXTURE_FILTER_PARENT_NODE,
|
||||
TEXTURE_FILTER_NEAREST,
|
||||
TEXTURE_FILTER_LINEAR,
|
||||
TEXTURE_FILTER_NEAREST_WITH_MIPMAPS,
|
||||
TEXTURE_FILTER_LINEAR_WITH_MIPMAPS,
|
||||
TEXTURE_FILTER_NEAREST_WITH_MIPMAPS_ANISOTROPIC,
|
||||
TEXTURE_FILTER_LINEAR_WITH_MIPMAPS_ANISOTROPIC,
|
||||
TEXTURE_FILTER_MAX
|
||||
};
|
||||
|
||||
enum TextureRepeat {
|
||||
TEXTURE_REPEAT_PARENT_NODE,
|
||||
TEXTURE_REPEAT_DISABLED,
|
||||
TEXTURE_REPEAT_ENABLED,
|
||||
TEXTURE_REPEAT_MIRROR,
|
||||
TEXTURE_REPEAT_MAX,
|
||||
};
|
||||
|
||||
enum ClipChildrenMode {
|
||||
CLIP_CHILDREN_DISABLED,
|
||||
CLIP_CHILDREN_ONLY,
|
||||
CLIP_CHILDREN_AND_DRAW,
|
||||
CLIP_CHILDREN_MAX,
|
||||
};
|
||||
|
||||
private:
|
||||
mutable SelfList<Node>
|
||||
xform_change;
|
||||
|
||||
RID canvas_item;
|
||||
StringName canvas_group;
|
||||
|
||||
CanvasLayer *canvas_layer = nullptr;
|
||||
|
||||
Color modulate = Color(1, 1, 1, 1);
|
||||
Color self_modulate = Color(1, 1, 1, 1);
|
||||
|
||||
List<CanvasItem *> children_items;
|
||||
List<CanvasItem *>::Element *C = nullptr;
|
||||
|
||||
int light_mask = 1;
|
||||
uint32_t visibility_layer = 1;
|
||||
|
||||
int z_index = 0;
|
||||
bool z_relative = true;
|
||||
bool y_sort_enabled = false;
|
||||
|
||||
Window *window = nullptr;
|
||||
bool visible = true;
|
||||
bool parent_visible_in_tree = false;
|
||||
bool pending_update = false;
|
||||
bool top_level = false;
|
||||
bool drawing = false;
|
||||
bool block_transform_notify = false;
|
||||
bool behind = false;
|
||||
bool use_parent_material = false;
|
||||
bool notify_local_transform = false;
|
||||
bool notify_transform = false;
|
||||
bool hide_clip_children = false;
|
||||
|
||||
ClipChildrenMode clip_children_mode = CLIP_CHILDREN_DISABLED;
|
||||
|
||||
mutable RS::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
|
||||
mutable RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
|
||||
TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE;
|
||||
TextureRepeat texture_repeat = TEXTURE_REPEAT_PARENT_NODE;
|
||||
|
||||
Ref<Material> material;
|
||||
mutable HashMap<StringName, Variant> instance_shader_parameters;
|
||||
mutable HashMap<StringName, StringName> instance_shader_parameter_property_remap;
|
||||
|
||||
mutable Transform2D global_transform;
|
||||
mutable MTFlag global_invalid;
|
||||
|
||||
_FORCE_INLINE_ bool _is_global_invalid() const { return is_group_processing() ? global_invalid.mt.is_set() : global_invalid.st; }
|
||||
void _set_global_invalid(bool p_invalid) const;
|
||||
|
||||
void _top_level_raise_self();
|
||||
|
||||
void _propagate_visibility_changed(bool p_parent_visible_in_tree);
|
||||
void _handle_visibility_change(bool p_visible);
|
||||
|
||||
virtual void _top_level_changed();
|
||||
virtual void _top_level_changed_on_parent();
|
||||
|
||||
void _redraw_callback();
|
||||
|
||||
void _enter_canvas();
|
||||
void _exit_canvas();
|
||||
|
||||
void _window_visibility_changed();
|
||||
|
||||
void _notify_transform(CanvasItem *p_node);
|
||||
|
||||
virtual void _physics_interpolated_changed() override;
|
||||
|
||||
static CanvasItem *current_item_drawn;
|
||||
friend class Viewport;
|
||||
void _refresh_texture_repeat_cache() const;
|
||||
void _update_texture_repeat_changed(bool p_propagate);
|
||||
void _refresh_texture_filter_cache() const;
|
||||
void _update_texture_filter_changed(bool p_propagate);
|
||||
|
||||
void _notify_transform_deferred();
|
||||
const StringName *_instance_shader_parameter_get_remap(const StringName &p_name) const;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
virtual void _update_self_texture_repeat(RS::CanvasItemTextureRepeat p_texture_repeat);
|
||||
virtual void _update_self_texture_filter(RS::CanvasItemTextureFilter p_texture_filter);
|
||||
|
||||
_FORCE_INLINE_ void _notify_transform() {
|
||||
_notify_transform(this);
|
||||
if (is_inside_tree() && !block_transform_notify && notify_local_transform) {
|
||||
notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
void item_rect_changed(bool p_size_changed = true);
|
||||
|
||||
void set_canvas_item_use_identity_transform(bool p_enable);
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _draw_string_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;
|
||||
void _draw_multiline_string_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;
|
||||
void _draw_string_outline_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;
|
||||
void _draw_multiline_string_outline_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL) const;
|
||||
void _draw_char_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const;
|
||||
void _draw_char_outline_bind_compat_104872(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const;
|
||||
void _draw_circle_bind_compat_84472(const Point2 &p_pos, real_t p_radius, const Color &p_color);
|
||||
void _draw_rect_bind_compat_84523(const Rect2 &p_rect, const Color &p_color, bool p_filled, real_t p_width);
|
||||
void _draw_dashed_line_bind_compat_84523(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width, real_t p_dash, bool p_aligned);
|
||||
void _draw_multiline_bind_compat_84523(const Vector<Point2> &p_points, const Color &p_color, real_t p_width);
|
||||
void _draw_multiline_colors_bind_compat_84523(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width);
|
||||
static void _bind_compatibility_methods();
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
_FORCE_INLINE_ void set_hide_clip_children(bool p_value) { hide_clip_children = p_value; }
|
||||
|
||||
GDVIRTUAL0(_draw)
|
||||
|
||||
public:
|
||||
enum {
|
||||
NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED, //unique
|
||||
NOTIFICATION_DRAW = 30,
|
||||
NOTIFICATION_VISIBILITY_CHANGED = 31,
|
||||
NOTIFICATION_ENTER_CANVAS = 32,
|
||||
NOTIFICATION_EXIT_CANVAS = 33,
|
||||
NOTIFICATION_LOCAL_TRANSFORM_CHANGED = 35,
|
||||
NOTIFICATION_WORLD_2D_CHANGED = 36,
|
||||
};
|
||||
|
||||
/* EDITOR AND DEBUGGING */
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Save and restore a CanvasItem state
|
||||
virtual void _edit_set_state(const Dictionary &p_state) {}
|
||||
virtual Dictionary _edit_get_state() const { return Dictionary(); }
|
||||
|
||||
// Used to move the node
|
||||
virtual void _edit_set_position(const Point2 &p_position) = 0;
|
||||
virtual Point2 _edit_get_position() const = 0;
|
||||
|
||||
// Used to scale the node
|
||||
virtual void _edit_set_scale(const Size2 &p_scale) = 0;
|
||||
virtual Size2 _edit_get_scale() const = 0;
|
||||
|
||||
// Used to rotate the node
|
||||
virtual bool _edit_use_rotation() const { return false; }
|
||||
virtual void _edit_set_rotation(real_t p_rotation) {}
|
||||
virtual real_t _edit_get_rotation() const { return 0.0; }
|
||||
|
||||
// Used to resize/move the node
|
||||
virtual void _edit_set_rect(const Rect2 &p_rect) {}
|
||||
virtual Size2 _edit_get_minimum_size() const { return Size2(-1, -1); } // LOOKS WEIRD
|
||||
|
||||
// Used to set a pivot
|
||||
virtual bool _edit_use_pivot() const { return false; }
|
||||
virtual void _edit_set_pivot(const Point2 &p_pivot) {}
|
||||
virtual Point2 _edit_get_pivot() const { return Point2(); }
|
||||
|
||||
virtual Transform2D _edit_get_transform() const;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
// Those need to be available in debug runtime, to allow for node selection.
|
||||
|
||||
// Select the node.
|
||||
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
|
||||
|
||||
// Used to resize/move the node.
|
||||
virtual bool _edit_use_rect() const { return false; } // Maybe replace with _edit_get_editmode().
|
||||
virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); }
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
void update_draw_order();
|
||||
|
||||
/* VISIBILITY */
|
||||
|
||||
void set_visible(bool p_visible);
|
||||
bool is_visible() const;
|
||||
bool is_visible_in_tree() const;
|
||||
void show();
|
||||
void hide();
|
||||
|
||||
void queue_redraw();
|
||||
void move_to_front();
|
||||
|
||||
void set_clip_children_mode(ClipChildrenMode p_clip_mode);
|
||||
ClipChildrenMode get_clip_children_mode() const;
|
||||
|
||||
virtual void set_light_mask(int p_light_mask);
|
||||
int get_light_mask() const;
|
||||
|
||||
void set_modulate(const Color &p_modulate);
|
||||
Color get_modulate() const;
|
||||
Color get_modulate_in_tree() const;
|
||||
|
||||
virtual void set_self_modulate(const Color &p_self_modulate);
|
||||
Color get_self_modulate() const;
|
||||
|
||||
void set_visibility_layer(uint32_t p_visibility_layer);
|
||||
uint32_t get_visibility_layer() const;
|
||||
|
||||
void set_visibility_layer_bit(uint32_t p_visibility_layer, bool p_enable);
|
||||
bool get_visibility_layer_bit(uint32_t p_visibility_layer) const;
|
||||
|
||||
/* ORDERING */
|
||||
|
||||
virtual void set_z_index(int p_z);
|
||||
int get_z_index() const;
|
||||
int get_effective_z_index() const;
|
||||
|
||||
void set_z_as_relative(bool p_enabled);
|
||||
bool is_z_relative() const;
|
||||
|
||||
virtual void set_y_sort_enabled(bool p_enabled);
|
||||
virtual bool is_y_sort_enabled() const;
|
||||
|
||||
/* DRAWING API */
|
||||
|
||||
void draw_dashed_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = -1.0, real_t p_dash = 2.0, bool p_aligned = true, bool p_antialiased = false);
|
||||
void draw_line(const Point2 &p_from, const Point2 &p_to, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
|
||||
void draw_polyline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
|
||||
void draw_polyline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = -1.0, bool p_antialiased = false);
|
||||
void draw_arc(const Vector2 &p_center, real_t p_radius, real_t p_start_angle, real_t p_end_angle, int p_point_count, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
|
||||
void draw_multiline(const Vector<Point2> &p_points, const Color &p_color, real_t p_width = -1.0, bool p_antialiased = false);
|
||||
void draw_multiline_colors(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width = -1.0, bool p_antialiased = false);
|
||||
void draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_filled = true, real_t p_width = -1.0, bool p_antialiased = false);
|
||||
void draw_circle(const Point2 &p_pos, real_t p_radius, const Color &p_color, bool p_filled = true, real_t p_width = -1.0, bool p_antialiased = false);
|
||||
void draw_texture(const Ref<Texture2D> &p_texture, const Point2 &p_pos, const Color &p_modulate = Color(1, 1, 1, 1));
|
||||
void draw_texture_rect(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, bool p_tile = false, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false);
|
||||
void draw_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), bool p_transpose = false, bool p_clip_uv = false);
|
||||
void draw_msdf_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1), double p_outline = 0.0, double p_pixel_range = 4.0, double p_scale = 1.0);
|
||||
void draw_lcd_texture_rect_region(const Ref<Texture2D> &p_texture, const Rect2 &p_rect, const Rect2 &p_src_rect, const Color &p_modulate = Color(1, 1, 1));
|
||||
void draw_style_box(const Ref<StyleBox> &p_style_box, const Rect2 &p_rect);
|
||||
void draw_primitive(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs, Ref<Texture2D> p_texture = Ref<Texture2D>());
|
||||
void draw_polygon(const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());
|
||||
void draw_colored_polygon(const Vector<Point2> &p_points, const Color &p_color, const Vector<Point2> &p_uvs = Vector<Point2>(), Ref<Texture2D> p_texture = Ref<Texture2D>());
|
||||
|
||||
void draw_mesh(const Ref<Mesh> &p_mesh, const Ref<Texture2D> &p_texture, const Transform2D &p_transform = Transform2D(), const Color &p_modulate = Color(1, 1, 1));
|
||||
void draw_multimesh(const Ref<MultiMesh> &p_multimesh, const Ref<Texture2D> &p_texture);
|
||||
|
||||
void draw_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL, float p_oversampling = 0.0) const;
|
||||
void draw_multiline_string(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL, float p_oversampling = 0.0) const;
|
||||
|
||||
void draw_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL, float p_oversampling = 0.0) const;
|
||||
void draw_multiline_string_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_text, HorizontalAlignment p_alignment = HORIZONTAL_ALIGNMENT_LEFT, float p_width = -1, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_max_lines = -1, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), BitField<TextServer::LineBreakFlag> p_brk_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND, BitField<TextServer::JustificationFlag> p_jst_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND, TextServer::Direction p_direction = TextServer::DIRECTION_AUTO, TextServer::Orientation p_orientation = TextServer::ORIENTATION_HORIZONTAL, float p_oversampling = 0.0) const;
|
||||
|
||||
void draw_char(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, const Color &p_modulate = Color(1.0, 1.0, 1.0), float p_oversampling = 0.0) const;
|
||||
void draw_char_outline(const Ref<Font> &p_font, const Point2 &p_pos, const String &p_char, int p_font_size = Font::DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0), float p_oversampling = 0.0) const;
|
||||
|
||||
void draw_set_transform(const Point2 &p_offset, real_t p_rot = 0.0, const Size2 &p_scale = Size2(1.0, 1.0));
|
||||
void draw_set_transform_matrix(const Transform2D &p_matrix);
|
||||
void draw_animation_slice(double p_animation_length, double p_slice_begin, double p_slice_end, double p_offset = 0);
|
||||
void draw_end_animation();
|
||||
|
||||
static CanvasItem *get_current_item_drawn();
|
||||
|
||||
/* RECT / TRANSFORM */
|
||||
|
||||
void set_as_top_level(bool p_top_level);
|
||||
bool is_set_as_top_level() const;
|
||||
|
||||
void set_draw_behind_parent(bool p_enable);
|
||||
bool is_draw_behind_parent_enabled() const;
|
||||
|
||||
CanvasItem *get_parent_item() const;
|
||||
|
||||
virtual Transform2D get_transform() const = 0;
|
||||
|
||||
virtual Transform2D get_global_transform() const;
|
||||
virtual Transform2D get_global_transform_const() const;
|
||||
virtual Transform2D get_global_transform_with_canvas() const;
|
||||
virtual Transform2D get_screen_transform() const;
|
||||
|
||||
CanvasItem *get_top_level() const;
|
||||
_FORCE_INLINE_ RID get_canvas_item() const {
|
||||
return canvas_item;
|
||||
}
|
||||
|
||||
void set_block_transform_notify(bool p_enable);
|
||||
bool is_block_transform_notify_enabled() const;
|
||||
|
||||
Transform2D get_canvas_transform() const;
|
||||
Transform2D get_viewport_transform() const;
|
||||
Rect2 get_viewport_rect() const;
|
||||
RID get_viewport_rid() const;
|
||||
RID get_canvas() const;
|
||||
ObjectID get_canvas_layer_instance_id() const;
|
||||
Ref<World2D> get_world_2d() const;
|
||||
|
||||
virtual void set_material(const Ref<Material> &p_material);
|
||||
Ref<Material> get_material() const;
|
||||
|
||||
void set_instance_shader_parameter(const StringName &p_name, const Variant &p_value);
|
||||
Variant get_instance_shader_parameter(const StringName &p_name) const;
|
||||
|
||||
virtual void set_use_parent_material(bool p_use_parent_material);
|
||||
bool get_use_parent_material() const;
|
||||
|
||||
Ref<InputEvent> make_input_local(const Ref<InputEvent> &p_event) const;
|
||||
Vector2 make_canvas_position_local(const Vector2 &screen_point) const;
|
||||
|
||||
Vector2 get_global_mouse_position() const;
|
||||
Vector2 get_local_mouse_position() const;
|
||||
|
||||
void set_notify_local_transform(bool p_enable);
|
||||
bool is_local_transform_notification_enabled() const;
|
||||
|
||||
void set_notify_transform(bool p_enable);
|
||||
bool is_transform_notification_enabled() const;
|
||||
|
||||
void force_update_transform();
|
||||
|
||||
virtual void set_texture_filter(TextureFilter p_texture_filter);
|
||||
TextureFilter get_texture_filter() const;
|
||||
|
||||
virtual void set_texture_repeat(TextureRepeat p_texture_repeat);
|
||||
TextureRepeat get_texture_repeat() const;
|
||||
|
||||
TextureFilter get_texture_filter_in_tree() const;
|
||||
TextureRepeat get_texture_repeat_in_tree() const;
|
||||
|
||||
// Used by control nodes to retrieve the parent's anchorable area
|
||||
virtual Rect2 get_anchorable_rect() const { return Rect2(0, 0, 0, 0); }
|
||||
|
||||
int get_canvas_layer() const;
|
||||
CanvasLayer *get_canvas_layer_node() const;
|
||||
|
||||
virtual PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
CanvasItem();
|
||||
~CanvasItem();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(CanvasItem::TextureFilter)
|
||||
VARIANT_ENUM_CAST(CanvasItem::TextureRepeat)
|
||||
VARIANT_ENUM_CAST(CanvasItem::ClipChildrenMode)
|
||||
|
||||
class CanvasTexture : public Texture2D {
|
||||
GDCLASS(CanvasTexture, Texture2D);
|
||||
OBJ_SAVE_TYPE(Texture2D); // Saves derived classes with common type so they can be interchanged.
|
||||
|
||||
Ref<Texture2D> diffuse_texture;
|
||||
Ref<Texture2D> normal_texture;
|
||||
Ref<Texture2D> specular_texture;
|
||||
Color specular = Color(1, 1, 1, 1);
|
||||
real_t shininess = 1.0;
|
||||
|
||||
RID canvas_texture;
|
||||
|
||||
CanvasItem::TextureFilter texture_filter = CanvasItem::TEXTURE_FILTER_PARENT_NODE;
|
||||
CanvasItem::TextureRepeat texture_repeat = CanvasItem::TEXTURE_REPEAT_PARENT_NODE;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_diffuse_texture(const Ref<Texture2D> &p_diffuse);
|
||||
Ref<Texture2D> get_diffuse_texture() const;
|
||||
|
||||
void set_normal_texture(const Ref<Texture2D> &p_normal);
|
||||
Ref<Texture2D> get_normal_texture() const;
|
||||
|
||||
void set_specular_texture(const Ref<Texture2D> &p_specular);
|
||||
Ref<Texture2D> get_specular_texture() const;
|
||||
|
||||
void set_specular_color(const Color &p_color);
|
||||
Color get_specular_color() const;
|
||||
|
||||
void set_specular_shininess(real_t p_shininess);
|
||||
real_t get_specular_shininess() const;
|
||||
|
||||
void set_texture_filter(CanvasItem::TextureFilter p_filter);
|
||||
CanvasItem::TextureFilter get_texture_filter() const;
|
||||
|
||||
void set_texture_repeat(CanvasItem::TextureRepeat p_repeat);
|
||||
CanvasItem::TextureRepeat get_texture_repeat() const;
|
||||
|
||||
virtual int get_width() const override;
|
||||
virtual int get_height() const override;
|
||||
|
||||
virtual bool is_pixel_opaque(int p_x, int p_y) const override;
|
||||
virtual bool has_alpha() const override;
|
||||
|
||||
virtual Ref<Image> get_image() const override;
|
||||
|
||||
virtual RID get_rid() const override;
|
||||
|
||||
CanvasTexture();
|
||||
~CanvasTexture();
|
||||
};
|
363
scene/main/canvas_layer.cpp
Normal file
363
scene/main/canvas_layer.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
/**************************************************************************/
|
||||
/* canvas_layer.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "canvas_layer.h"
|
||||
|
||||
#include "scene/main/canvas_item.h"
|
||||
#include "scene/main/viewport.h"
|
||||
#include "scene/resources/world_2d.h"
|
||||
|
||||
void CanvasLayer::set_layer(int p_xform) {
|
||||
layer = p_xform;
|
||||
if (viewport.is_valid()) {
|
||||
RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index());
|
||||
vp->gui_set_root_order_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
int CanvasLayer::get_layer() const {
|
||||
return layer;
|
||||
}
|
||||
|
||||
void CanvasLayer::set_visible(bool p_visible) {
|
||||
if (p_visible == visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
visible = p_visible;
|
||||
emit_signal(SceneStringName(visibility_changed));
|
||||
|
||||
for (int i = 0; i < get_child_count(); i++) {
|
||||
CanvasItem *c = Object::cast_to<CanvasItem>(get_child(i));
|
||||
if (c) {
|
||||
RenderingServer::get_singleton()->canvas_item_set_visible(c->get_canvas_item(), p_visible && c->is_visible());
|
||||
|
||||
c->_propagate_visibility_changed(p_visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasLayer::show() {
|
||||
set_visible(true);
|
||||
}
|
||||
|
||||
void CanvasLayer::hide() {
|
||||
set_visible(false);
|
||||
}
|
||||
|
||||
bool CanvasLayer::is_visible() const {
|
||||
return visible;
|
||||
}
|
||||
|
||||
void CanvasLayer::set_transform(const Transform2D &p_xform) {
|
||||
transform = p_xform;
|
||||
locrotscale_dirty = true;
|
||||
if (viewport.is_valid()) {
|
||||
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
|
||||
}
|
||||
}
|
||||
|
||||
Transform2D CanvasLayer::get_transform() const {
|
||||
return transform;
|
||||
}
|
||||
|
||||
Transform2D CanvasLayer::get_final_transform() const {
|
||||
if (is_following_viewport()) {
|
||||
Transform2D follow;
|
||||
follow.scale(Vector2(get_follow_viewport_scale(), get_follow_viewport_scale()));
|
||||
if (vp) {
|
||||
follow = vp->get_canvas_transform() * follow;
|
||||
}
|
||||
return follow * transform;
|
||||
}
|
||||
return transform;
|
||||
}
|
||||
|
||||
void CanvasLayer::_update_xform() {
|
||||
transform.set_rotation_and_scale(rot, scale);
|
||||
transform.set_origin(ofs);
|
||||
if (viewport.is_valid()) {
|
||||
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasLayer::_update_locrotscale() const {
|
||||
ofs = transform.columns[2];
|
||||
rot = transform.get_rotation();
|
||||
scale = transform.get_scale();
|
||||
locrotscale_dirty = false;
|
||||
}
|
||||
|
||||
void CanvasLayer::set_offset(const Vector2 &p_offset) {
|
||||
if (locrotscale_dirty) {
|
||||
_update_locrotscale();
|
||||
}
|
||||
|
||||
ofs = p_offset;
|
||||
_update_xform();
|
||||
}
|
||||
|
||||
Vector2 CanvasLayer::get_offset() const {
|
||||
if (locrotscale_dirty) {
|
||||
_update_locrotscale();
|
||||
}
|
||||
|
||||
return ofs;
|
||||
}
|
||||
|
||||
void CanvasLayer::set_rotation(real_t p_radians) {
|
||||
if (locrotscale_dirty) {
|
||||
_update_locrotscale();
|
||||
}
|
||||
|
||||
rot = p_radians;
|
||||
_update_xform();
|
||||
}
|
||||
|
||||
real_t CanvasLayer::get_rotation() const {
|
||||
if (locrotscale_dirty) {
|
||||
_update_locrotscale();
|
||||
}
|
||||
|
||||
return rot;
|
||||
}
|
||||
|
||||
void CanvasLayer::set_scale(const Vector2 &p_scale) {
|
||||
if (locrotscale_dirty) {
|
||||
_update_locrotscale();
|
||||
}
|
||||
|
||||
scale = p_scale;
|
||||
_update_xform();
|
||||
}
|
||||
|
||||
Vector2 CanvasLayer::get_scale() const {
|
||||
if (locrotscale_dirty) {
|
||||
_update_locrotscale();
|
||||
}
|
||||
|
||||
return scale;
|
||||
}
|
||||
|
||||
void CanvasLayer::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
if (custom_viewport && ObjectDB::get_instance(custom_viewport_id)) {
|
||||
vp = custom_viewport;
|
||||
} else {
|
||||
vp = Node::get_viewport();
|
||||
}
|
||||
ERR_FAIL_NULL_MSG(vp, "Viewport is not initialized.");
|
||||
|
||||
vp->_canvas_layer_add(this);
|
||||
viewport = vp->get_viewport_rid();
|
||||
|
||||
RenderingServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
|
||||
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
|
||||
_update_follow_viewport();
|
||||
|
||||
if (vp) {
|
||||
get_parent()->connect(SNAME("child_order_changed"), callable_mp(vp, &Viewport::canvas_parent_mark_dirty).bind(get_parent()), CONNECT_REFERENCE_COUNTED);
|
||||
vp->canvas_parent_mark_dirty(get_parent());
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
ERR_FAIL_NULL_MSG(vp, "Viewport is not initialized.");
|
||||
get_parent()->disconnect(SNAME("child_order_changed"), callable_mp(vp, &Viewport::canvas_parent_mark_dirty).bind(get_parent()));
|
||||
|
||||
vp->_canvas_layer_remove(this);
|
||||
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
|
||||
viewport = RID();
|
||||
_update_follow_viewport(false);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasLayer::update_draw_order() {
|
||||
if (is_inside_tree()) {
|
||||
RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index());
|
||||
}
|
||||
}
|
||||
|
||||
Size2 CanvasLayer::get_viewport_size() const {
|
||||
if (!is_inside_tree()) {
|
||||
return Size2(1, 1);
|
||||
}
|
||||
|
||||
ERR_FAIL_NULL_V_MSG(vp, Size2(1, 1), "Viewport is not initialized.");
|
||||
|
||||
Rect2 r = vp->get_visible_rect();
|
||||
return r.size;
|
||||
}
|
||||
|
||||
RID CanvasLayer::get_viewport() const {
|
||||
return viewport;
|
||||
}
|
||||
|
||||
void CanvasLayer::set_custom_viewport(Node *p_viewport) {
|
||||
ERR_FAIL_NULL_MSG(p_viewport, "Cannot set viewport to nullptr.");
|
||||
if (is_inside_tree()) {
|
||||
vp->_canvas_layer_remove(this);
|
||||
RenderingServer::get_singleton()->viewport_remove_canvas(viewport, canvas);
|
||||
viewport = RID();
|
||||
}
|
||||
|
||||
custom_viewport = Object::cast_to<Viewport>(p_viewport);
|
||||
|
||||
if (custom_viewport) {
|
||||
custom_viewport_id = custom_viewport->get_instance_id();
|
||||
} else {
|
||||
custom_viewport_id = ObjectID();
|
||||
}
|
||||
|
||||
if (is_inside_tree()) {
|
||||
if (custom_viewport) {
|
||||
vp = custom_viewport;
|
||||
} else {
|
||||
vp = Node::get_viewport();
|
||||
}
|
||||
|
||||
vp->_canvas_layer_add(this);
|
||||
viewport = vp->get_viewport_rid();
|
||||
|
||||
RenderingServer::get_singleton()->viewport_attach_canvas(viewport, canvas);
|
||||
RenderingServer::get_singleton()->viewport_set_canvas_stacking(viewport, canvas, layer, get_index());
|
||||
RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
|
||||
}
|
||||
}
|
||||
|
||||
Node *CanvasLayer::get_custom_viewport() const {
|
||||
return custom_viewport;
|
||||
}
|
||||
|
||||
void CanvasLayer::reset_sort_index() {
|
||||
sort_index = 0;
|
||||
}
|
||||
|
||||
int CanvasLayer::get_sort_index() {
|
||||
return sort_index++;
|
||||
}
|
||||
|
||||
RID CanvasLayer::get_canvas() const {
|
||||
return canvas;
|
||||
}
|
||||
|
||||
void CanvasLayer::set_follow_viewport(bool p_enable) {
|
||||
if (follow_viewport == p_enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
follow_viewport = p_enable;
|
||||
_update_follow_viewport();
|
||||
}
|
||||
|
||||
bool CanvasLayer::is_following_viewport() const {
|
||||
return follow_viewport;
|
||||
}
|
||||
|
||||
void CanvasLayer::set_follow_viewport_scale(float p_ratio) {
|
||||
follow_viewport_scale = p_ratio;
|
||||
_update_follow_viewport();
|
||||
}
|
||||
|
||||
float CanvasLayer::get_follow_viewport_scale() const {
|
||||
return follow_viewport_scale;
|
||||
}
|
||||
|
||||
void CanvasLayer::_update_follow_viewport(bool p_force_exit) {
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
}
|
||||
if (p_force_exit || !follow_viewport) {
|
||||
RS::get_singleton()->canvas_set_parent(canvas, RID(), 1.0);
|
||||
} else {
|
||||
RS::get_singleton()->canvas_set_parent(canvas, vp->get_world_2d()->get_canvas(), follow_viewport_scale);
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasLayer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_layer", "layer"), &CanvasLayer::set_layer);
|
||||
ClassDB::bind_method(D_METHOD("get_layer"), &CanvasLayer::get_layer);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_visible", "visible"), &CanvasLayer::set_visible);
|
||||
ClassDB::bind_method(D_METHOD("is_visible"), &CanvasLayer::is_visible);
|
||||
ClassDB::bind_method(D_METHOD("show"), &CanvasLayer::show);
|
||||
ClassDB::bind_method(D_METHOD("hide"), &CanvasLayer::hide);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_transform", "transform"), &CanvasLayer::set_transform);
|
||||
ClassDB::bind_method(D_METHOD("get_transform"), &CanvasLayer::get_transform);
|
||||
ClassDB::bind_method(D_METHOD("get_final_transform"), &CanvasLayer::get_final_transform);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &CanvasLayer::set_offset);
|
||||
ClassDB::bind_method(D_METHOD("get_offset"), &CanvasLayer::get_offset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &CanvasLayer::set_rotation);
|
||||
ClassDB::bind_method(D_METHOD("get_rotation"), &CanvasLayer::get_rotation);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &CanvasLayer::set_scale);
|
||||
ClassDB::bind_method(D_METHOD("get_scale"), &CanvasLayer::get_scale);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_follow_viewport", "enable"), &CanvasLayer::set_follow_viewport);
|
||||
ClassDB::bind_method(D_METHOD("is_following_viewport"), &CanvasLayer::is_following_viewport);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_follow_viewport_scale", "scale"), &CanvasLayer::set_follow_viewport_scale);
|
||||
ClassDB::bind_method(D_METHOD("get_follow_viewport_scale"), &CanvasLayer::get_follow_viewport_scale);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_custom_viewport", "viewport"), &CanvasLayer::set_custom_viewport);
|
||||
ClassDB::bind_method(D_METHOD("get_custom_viewport"), &CanvasLayer::get_custom_viewport);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_canvas"), &CanvasLayer::get_canvas);
|
||||
|
||||
ADD_GROUP("Layer", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "layer", PROPERTY_HINT_RANGE, itos(RS::CANVAS_LAYER_MIN) + "," + itos(RS::CANVAS_LAYER_MAX) + ",1"), "set_layer", "get_layer");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
|
||||
ADD_GROUP("Transform", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset", PROPERTY_HINT_NONE, "suffix:px"), "set_offset", "get_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-1080,1080,0.1,or_less,or_greater,radians_as_degrees"), "set_rotation", "get_rotation");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale", PROPERTY_HINT_LINK), "set_scale", "get_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform", PROPERTY_HINT_NONE, "suffix:px"), "set_transform", "get_transform");
|
||||
ADD_GROUP("", "");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_viewport", PROPERTY_HINT_RESOURCE_TYPE, "Viewport", PROPERTY_USAGE_NONE), "set_custom_viewport", "get_custom_viewport");
|
||||
ADD_GROUP("Follow Viewport", "follow_viewport");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_viewport_enabled", PROPERTY_HINT_GROUP_ENABLE), "set_follow_viewport", "is_following_viewport");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_viewport_scale", PROPERTY_HINT_RANGE, "0.001,1000,0.001,or_greater,or_less"), "set_follow_viewport_scale", "get_follow_viewport_scale");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("visibility_changed"));
|
||||
}
|
||||
|
||||
CanvasLayer::CanvasLayer() {
|
||||
canvas = RS::get_singleton()->canvas_create();
|
||||
}
|
||||
|
||||
CanvasLayer::~CanvasLayer() {
|
||||
ERR_FAIL_NULL(RenderingServer::get_singleton());
|
||||
RS::get_singleton()->free(canvas);
|
||||
}
|
111
scene/main/canvas_layer.h
Normal file
111
scene/main/canvas_layer.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/**************************************************************************/
|
||||
/* canvas_layer.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class Viewport;
|
||||
class CanvasLayer : public Node {
|
||||
GDCLASS(CanvasLayer, Node);
|
||||
|
||||
mutable bool locrotscale_dirty = false;
|
||||
mutable Vector2 ofs;
|
||||
mutable Size2 scale = Vector2(1, 1);
|
||||
mutable real_t rot = 0.0;
|
||||
int layer = 1;
|
||||
Transform2D transform;
|
||||
RID canvas;
|
||||
|
||||
ObjectID custom_viewport_id; // to check validity
|
||||
Viewport *custom_viewport = nullptr;
|
||||
|
||||
RID viewport;
|
||||
Viewport *vp = nullptr;
|
||||
|
||||
int sort_index = 0;
|
||||
bool visible = true;
|
||||
|
||||
bool follow_viewport = false;
|
||||
float follow_viewport_scale = 1.0;
|
||||
|
||||
void _update_xform();
|
||||
void _update_locrotscale() const;
|
||||
void _update_follow_viewport(bool p_force_exit = false);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void update_draw_order();
|
||||
|
||||
void set_layer(int p_xform);
|
||||
int get_layer() const;
|
||||
|
||||
void set_visible(bool p_visible);
|
||||
bool is_visible() const;
|
||||
void show();
|
||||
void hide();
|
||||
|
||||
void set_transform(const Transform2D &p_xform);
|
||||
Transform2D get_transform() const;
|
||||
Transform2D get_final_transform() const;
|
||||
|
||||
void set_offset(const Vector2 &p_offset);
|
||||
Vector2 get_offset() const;
|
||||
|
||||
void set_rotation(real_t p_radians);
|
||||
real_t get_rotation() const;
|
||||
|
||||
void set_scale(const Size2 &p_scale);
|
||||
Size2 get_scale() const;
|
||||
|
||||
Size2 get_viewport_size() const;
|
||||
|
||||
RID get_viewport() const;
|
||||
|
||||
void set_custom_viewport(Node *p_viewport);
|
||||
Node *get_custom_viewport() const;
|
||||
|
||||
void reset_sort_index();
|
||||
int get_sort_index();
|
||||
|
||||
void set_follow_viewport(bool p_enable);
|
||||
bool is_following_viewport() const;
|
||||
|
||||
void set_follow_viewport_scale(float p_ratio);
|
||||
float get_follow_viewport_scale() const;
|
||||
|
||||
RID get_canvas() const;
|
||||
|
||||
CanvasLayer();
|
||||
~CanvasLayer();
|
||||
};
|
671
scene/main/http_request.cpp
Normal file
671
scene/main/http_request.cpp
Normal file
@@ -0,0 +1,671 @@
|
||||
/**************************************************************************/
|
||||
/* http_request.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "http_request.h"
|
||||
|
||||
#include "scene/main/timer.h"
|
||||
|
||||
Error HTTPRequest::_request() {
|
||||
return client->connect_to_host(url, port, use_tls ? tls_options : nullptr);
|
||||
}
|
||||
|
||||
Error HTTPRequest::_parse_url(const String &p_url) {
|
||||
use_tls = false;
|
||||
request_string = "";
|
||||
port = 80;
|
||||
request_sent = false;
|
||||
got_response = false;
|
||||
body_len = -1;
|
||||
body.clear();
|
||||
downloaded.set(0);
|
||||
final_body_size.set(0);
|
||||
redirections = 0;
|
||||
|
||||
String scheme;
|
||||
String fragment;
|
||||
Error err = p_url.parse_url(scheme, url, port, request_string, fragment);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Error parsing URL: '%s'.", p_url));
|
||||
|
||||
if (scheme == "https://") {
|
||||
use_tls = true;
|
||||
} else if (scheme != "http://") {
|
||||
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, vformat("Invalid URL scheme: '%s'.", scheme));
|
||||
}
|
||||
|
||||
if (port == 0) {
|
||||
port = use_tls ? 443 : 80;
|
||||
}
|
||||
if (request_string.is_empty()) {
|
||||
request_string = "/";
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool HTTPRequest::has_header(const PackedStringArray &p_headers, const String &p_header_name) {
|
||||
bool exists = false;
|
||||
|
||||
String lower_case_header_name = p_header_name.to_lower();
|
||||
for (int i = 0; i < p_headers.size() && !exists; i++) {
|
||||
String sanitized = p_headers[i].strip_edges().to_lower();
|
||||
if (sanitized.begins_with(lower_case_header_name)) {
|
||||
exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
|
||||
String HTTPRequest::get_header_value(const PackedStringArray &p_headers, const String &p_header_name) {
|
||||
String value = "";
|
||||
|
||||
String lowwer_case_header_name = p_header_name.to_lower();
|
||||
for (int i = 0; i < p_headers.size(); i++) {
|
||||
if (p_headers[i].find_char(':') > 0) {
|
||||
Vector<String> parts = p_headers[i].split(":", false, 1);
|
||||
if (parts.size() > 1 && parts[0].strip_edges().to_lower() == lowwer_case_header_name) {
|
||||
value = parts[1].strip_edges();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
Error HTTPRequest::request(const String &p_url, const Vector<String> &p_custom_headers, HTTPClient::Method p_method, const String &p_request_data) {
|
||||
// Copy the string into a raw buffer.
|
||||
Vector<uint8_t> raw_data;
|
||||
|
||||
CharString charstr = p_request_data.utf8();
|
||||
size_t len = charstr.length();
|
||||
if (len > 0) {
|
||||
raw_data.resize(len);
|
||||
uint8_t *w = raw_data.ptrw();
|
||||
memcpy(w, charstr.ptr(), len);
|
||||
}
|
||||
|
||||
return request_raw(p_url, p_custom_headers, p_method, raw_data);
|
||||
}
|
||||
|
||||
Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_custom_headers, HTTPClient::Method p_method, const Vector<uint8_t> &p_request_data_raw) {
|
||||
ERR_FAIL_COND_V(!is_inside_tree(), ERR_UNCONFIGURED);
|
||||
ERR_FAIL_COND_V_MSG(requesting, ERR_BUSY, "HTTPRequest is processing a request. Wait for completion or cancel it before attempting a new one.");
|
||||
|
||||
if (timeout > 0) {
|
||||
timer->stop();
|
||||
timer->start(timeout);
|
||||
}
|
||||
|
||||
method = p_method;
|
||||
|
||||
Error err = _parse_url(p_url);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
headers = p_custom_headers;
|
||||
|
||||
if (accept_gzip) {
|
||||
// If the user has specified an Accept-Encoding header, don't overwrite it.
|
||||
if (!has_header(headers, "Accept-Encoding")) {
|
||||
headers.push_back("Accept-Encoding: gzip, deflate");
|
||||
}
|
||||
}
|
||||
|
||||
request_data = p_request_data_raw;
|
||||
|
||||
requesting = true;
|
||||
|
||||
if (use_threads.is_set()) {
|
||||
thread_done.clear();
|
||||
thread_request_quit.clear();
|
||||
client->set_blocking_mode(true);
|
||||
thread.start(_thread_func, this);
|
||||
} else {
|
||||
client->set_blocking_mode(false);
|
||||
err = _request();
|
||||
if (err != OK) {
|
||||
_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
|
||||
return ERR_CANT_CONNECT;
|
||||
}
|
||||
|
||||
set_process_internal(true);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void HTTPRequest::_thread_func(void *p_userdata) {
|
||||
HTTPRequest *hr = static_cast<HTTPRequest *>(p_userdata);
|
||||
|
||||
Error err = hr->_request();
|
||||
|
||||
if (err != OK) {
|
||||
hr->_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
|
||||
} else {
|
||||
while (!hr->thread_request_quit.is_set()) {
|
||||
bool exit = hr->_update_connection();
|
||||
if (exit) {
|
||||
break;
|
||||
}
|
||||
OS::get_singleton()->delay_usec(1);
|
||||
}
|
||||
}
|
||||
|
||||
hr->thread_done.set();
|
||||
}
|
||||
|
||||
void HTTPRequest::cancel_request() {
|
||||
timer->stop();
|
||||
|
||||
if (!requesting) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!use_threads.is_set()) {
|
||||
set_process_internal(false);
|
||||
} else {
|
||||
thread_request_quit.set();
|
||||
if (thread.is_started()) {
|
||||
thread.wait_to_finish();
|
||||
}
|
||||
}
|
||||
|
||||
file.unref();
|
||||
decompressor.unref();
|
||||
client->close();
|
||||
body.clear();
|
||||
got_response = false;
|
||||
response_code = -1;
|
||||
request_sent = false;
|
||||
requesting = false;
|
||||
}
|
||||
|
||||
bool HTTPRequest::_handle_response(bool *ret_value) {
|
||||
if (!client->has_response()) {
|
||||
_defer_done(RESULT_NO_RESPONSE, 0, PackedStringArray(), PackedByteArray());
|
||||
*ret_value = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
got_response = true;
|
||||
response_code = client->get_response_code();
|
||||
List<String> rheaders;
|
||||
client->get_response_headers(&rheaders);
|
||||
response_headers.clear();
|
||||
downloaded.set(0);
|
||||
final_body_size.set(0);
|
||||
decompressor.unref();
|
||||
|
||||
for (const String &E : rheaders) {
|
||||
response_headers.push_back(E);
|
||||
}
|
||||
|
||||
if (response_code == 301 || response_code == 302) {
|
||||
// Handle redirect.
|
||||
|
||||
if (max_redirects >= 0 && redirections >= max_redirects) {
|
||||
_defer_done(RESULT_REDIRECT_LIMIT_REACHED, response_code, response_headers, PackedByteArray());
|
||||
*ret_value = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
String new_request;
|
||||
|
||||
for (const String &E : rheaders) {
|
||||
if (E.to_lower().begins_with("location: ")) {
|
||||
new_request = E.substr(9).strip_edges();
|
||||
}
|
||||
}
|
||||
|
||||
if (!new_request.is_empty()) {
|
||||
// Process redirect.
|
||||
client->close();
|
||||
int new_redirs = redirections + 1; // Because _request() will clear it.
|
||||
Error err;
|
||||
if (new_request.begins_with("http")) {
|
||||
// New url, new request.
|
||||
_parse_url(new_request);
|
||||
} else {
|
||||
request_string = new_request;
|
||||
}
|
||||
|
||||
err = _request();
|
||||
if (err == OK) {
|
||||
request_sent = false;
|
||||
got_response = false;
|
||||
body_len = -1;
|
||||
body.clear();
|
||||
downloaded.set(0);
|
||||
final_body_size.set(0);
|
||||
redirections = new_redirs;
|
||||
*ret_value = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we need to start streaming decompression.
|
||||
String content_encoding;
|
||||
if (accept_gzip) {
|
||||
content_encoding = get_header_value(response_headers, "Content-Encoding").to_lower();
|
||||
}
|
||||
if (content_encoding == "gzip") {
|
||||
decompressor.instantiate();
|
||||
decompressor->start_decompression(false, get_download_chunk_size());
|
||||
} else if (content_encoding == "deflate") {
|
||||
decompressor.instantiate();
|
||||
decompressor->start_decompression(true, get_download_chunk_size());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HTTPRequest::_update_connection() {
|
||||
switch (client->get_status()) {
|
||||
case HTTPClient::STATUS_DISCONNECTED: {
|
||||
_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
|
||||
return true; // End it, since it's disconnected.
|
||||
} break;
|
||||
case HTTPClient::STATUS_RESOLVING: {
|
||||
client->poll();
|
||||
// Must wait.
|
||||
return false;
|
||||
} break;
|
||||
case HTTPClient::STATUS_CANT_RESOLVE: {
|
||||
_defer_done(RESULT_CANT_RESOLVE, 0, PackedStringArray(), PackedByteArray());
|
||||
return true;
|
||||
|
||||
} break;
|
||||
case HTTPClient::STATUS_CONNECTING: {
|
||||
client->poll();
|
||||
// Must wait.
|
||||
return false;
|
||||
} break; // Connecting to IP.
|
||||
case HTTPClient::STATUS_CANT_CONNECT: {
|
||||
_defer_done(RESULT_CANT_CONNECT, 0, PackedStringArray(), PackedByteArray());
|
||||
return true;
|
||||
|
||||
} break;
|
||||
case HTTPClient::STATUS_CONNECTED: {
|
||||
if (request_sent) {
|
||||
if (!got_response) {
|
||||
// No body.
|
||||
|
||||
bool ret_value;
|
||||
|
||||
if (_handle_response(&ret_value)) {
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
_defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray());
|
||||
return true;
|
||||
}
|
||||
if (body_len < 0) {
|
||||
// Chunked transfer is done.
|
||||
_defer_done(RESULT_SUCCESS, response_code, response_headers, body);
|
||||
return true;
|
||||
}
|
||||
|
||||
_defer_done(RESULT_CHUNKED_BODY_SIZE_MISMATCH, response_code, response_headers, PackedByteArray());
|
||||
return true;
|
||||
// Request might have been done.
|
||||
} else {
|
||||
// Did not request yet, do request.
|
||||
|
||||
int size = request_data.size();
|
||||
Error err = client->request(method, request_string, headers, size > 0 ? request_data.ptr() : nullptr, size);
|
||||
if (err != OK) {
|
||||
_defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
|
||||
return true;
|
||||
}
|
||||
|
||||
request_sent = true;
|
||||
return false;
|
||||
}
|
||||
} break; // Connected: break requests only accepted here.
|
||||
case HTTPClient::STATUS_REQUESTING: {
|
||||
// Must wait, still requesting.
|
||||
client->poll();
|
||||
return false;
|
||||
|
||||
} break; // Request in progress.
|
||||
case HTTPClient::STATUS_BODY: {
|
||||
if (!got_response) {
|
||||
bool ret_value;
|
||||
|
||||
if (_handle_response(&ret_value)) {
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
if (!client->is_response_chunked() && client->get_response_body_length() == 0) {
|
||||
_defer_done(RESULT_SUCCESS, response_code, response_headers, PackedByteArray());
|
||||
return true;
|
||||
}
|
||||
|
||||
// No body len (-1) if chunked or no content-length header was provided.
|
||||
// Change your webserver configuration if you want body len.
|
||||
body_len = client->get_response_body_length();
|
||||
|
||||
if (body_size_limit >= 0 && body_len > body_size_limit) {
|
||||
_defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!download_to_file.is_empty()) {
|
||||
file = FileAccess::open(download_to_file, FileAccess::WRITE);
|
||||
if (file.is_null()) {
|
||||
_defer_done(RESULT_DOWNLOAD_FILE_CANT_OPEN, response_code, response_headers, PackedByteArray());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client->poll();
|
||||
if (client->get_status() != HTTPClient::STATUS_BODY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PackedByteArray chunk;
|
||||
if (decompressor.is_null()) {
|
||||
// Chunk can be read directly.
|
||||
chunk = client->read_response_body_chunk();
|
||||
downloaded.add(chunk.size());
|
||||
} else {
|
||||
// Chunk is the result of decompression.
|
||||
PackedByteArray compressed = client->read_response_body_chunk();
|
||||
downloaded.add(compressed.size());
|
||||
|
||||
int pos = 0;
|
||||
int left = compressed.size();
|
||||
while (left) {
|
||||
int w = 0;
|
||||
Error err = decompressor->put_partial_data(compressed.ptr() + pos, left, w);
|
||||
if (err == OK) {
|
||||
PackedByteArray dc;
|
||||
dc.resize(decompressor->get_available_bytes());
|
||||
err = decompressor->get_data(dc.ptrw(), dc.size());
|
||||
chunk.append_array(dc);
|
||||
}
|
||||
if (err != OK) {
|
||||
_defer_done(RESULT_BODY_DECOMPRESS_FAILED, response_code, response_headers, PackedByteArray());
|
||||
return true;
|
||||
}
|
||||
// We need this check here because a "zip bomb" could result in a chunk of few kilos decompressing into gigabytes of data.
|
||||
if (body_size_limit >= 0 && final_body_size.get() + chunk.size() > body_size_limit) {
|
||||
_defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
|
||||
return true;
|
||||
}
|
||||
pos += w;
|
||||
left -= w;
|
||||
}
|
||||
}
|
||||
final_body_size.add(chunk.size());
|
||||
|
||||
if (body_size_limit >= 0 && final_body_size.get() > body_size_limit) {
|
||||
_defer_done(RESULT_BODY_SIZE_LIMIT_EXCEEDED, response_code, response_headers, PackedByteArray());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (chunk.size()) {
|
||||
if (file.is_valid()) {
|
||||
const uint8_t *r = chunk.ptr();
|
||||
file->store_buffer(r, chunk.size());
|
||||
if (file->get_error() != OK) {
|
||||
_defer_done(RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PackedByteArray());
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
body.append_array(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
if (body_len >= 0) {
|
||||
if (downloaded.get() == body_len) {
|
||||
_defer_done(RESULT_SUCCESS, response_code, response_headers, body);
|
||||
return true;
|
||||
}
|
||||
} else if (client->get_status() == HTTPClient::STATUS_DISCONNECTED) {
|
||||
// We read till EOF, with no errors. Request is done.
|
||||
_defer_done(RESULT_SUCCESS, response_code, response_headers, body);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} break; // Request resulted in body: break which must be read.
|
||||
case HTTPClient::STATUS_CONNECTION_ERROR: {
|
||||
_defer_done(RESULT_CONNECTION_ERROR, 0, PackedStringArray(), PackedByteArray());
|
||||
return true;
|
||||
} break;
|
||||
case HTTPClient::STATUS_TLS_HANDSHAKE_ERROR: {
|
||||
_defer_done(RESULT_TLS_HANDSHAKE_ERROR, 0, PackedStringArray(), PackedByteArray());
|
||||
return true;
|
||||
} break;
|
||||
}
|
||||
|
||||
ERR_FAIL_V(false);
|
||||
}
|
||||
|
||||
void HTTPRequest::_defer_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) {
|
||||
callable_mp(this, &HTTPRequest::_request_done).call_deferred(p_status, p_code, p_headers, p_data);
|
||||
}
|
||||
|
||||
void HTTPRequest::_request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data) {
|
||||
cancel_request();
|
||||
|
||||
emit_signal(SNAME("request_completed"), p_status, p_code, p_headers, p_data);
|
||||
}
|
||||
|
||||
void HTTPRequest::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_INTERNAL_PROCESS: {
|
||||
if (use_threads.is_set()) {
|
||||
return;
|
||||
}
|
||||
bool done = _update_connection();
|
||||
if (done) {
|
||||
set_process_internal(false);
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
if (requesting) {
|
||||
cancel_request();
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPRequest::set_use_threads(bool p_use) {
|
||||
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
|
||||
#ifdef THREADS_ENABLED
|
||||
use_threads.set_to(p_use);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool HTTPRequest::is_using_threads() const {
|
||||
return use_threads.is_set();
|
||||
}
|
||||
|
||||
void HTTPRequest::set_accept_gzip(bool p_gzip) {
|
||||
accept_gzip = p_gzip;
|
||||
}
|
||||
|
||||
bool HTTPRequest::is_accepting_gzip() const {
|
||||
return accept_gzip;
|
||||
}
|
||||
|
||||
void HTTPRequest::set_body_size_limit(int p_bytes) {
|
||||
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
|
||||
|
||||
body_size_limit = p_bytes;
|
||||
}
|
||||
|
||||
int HTTPRequest::get_body_size_limit() const {
|
||||
return body_size_limit;
|
||||
}
|
||||
|
||||
void HTTPRequest::set_download_file(const String &p_file) {
|
||||
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
|
||||
|
||||
download_to_file = p_file;
|
||||
}
|
||||
|
||||
String HTTPRequest::get_download_file() const {
|
||||
return download_to_file;
|
||||
}
|
||||
|
||||
void HTTPRequest::set_download_chunk_size(int p_chunk_size) {
|
||||
ERR_FAIL_COND(get_http_client_status() != HTTPClient::STATUS_DISCONNECTED);
|
||||
|
||||
client->set_read_chunk_size(p_chunk_size);
|
||||
}
|
||||
|
||||
int HTTPRequest::get_download_chunk_size() const {
|
||||
return client->get_read_chunk_size();
|
||||
}
|
||||
|
||||
HTTPClient::Status HTTPRequest::get_http_client_status() const {
|
||||
return client->get_status();
|
||||
}
|
||||
|
||||
void HTTPRequest::set_max_redirects(int p_max) {
|
||||
max_redirects = p_max;
|
||||
}
|
||||
|
||||
int HTTPRequest::get_max_redirects() const {
|
||||
return max_redirects;
|
||||
}
|
||||
|
||||
int HTTPRequest::get_downloaded_bytes() const {
|
||||
return downloaded.get();
|
||||
}
|
||||
|
||||
int HTTPRequest::get_body_size() const {
|
||||
return body_len;
|
||||
}
|
||||
|
||||
void HTTPRequest::set_http_proxy(const String &p_host, int p_port) {
|
||||
client->set_http_proxy(p_host, p_port);
|
||||
}
|
||||
|
||||
void HTTPRequest::set_https_proxy(const String &p_host, int p_port) {
|
||||
client->set_https_proxy(p_host, p_port);
|
||||
}
|
||||
|
||||
void HTTPRequest::set_timeout(double p_timeout) {
|
||||
ERR_FAIL_COND(p_timeout < 0);
|
||||
timeout = p_timeout;
|
||||
}
|
||||
|
||||
double HTTPRequest::get_timeout() {
|
||||
return timeout;
|
||||
}
|
||||
|
||||
void HTTPRequest::_timeout() {
|
||||
cancel_request();
|
||||
_defer_done(RESULT_TIMEOUT, 0, PackedStringArray(), PackedByteArray());
|
||||
}
|
||||
|
||||
void HTTPRequest::set_tls_options(const Ref<TLSOptions> &p_options) {
|
||||
ERR_FAIL_COND(p_options.is_null() || p_options->is_server());
|
||||
tls_options = p_options;
|
||||
}
|
||||
|
||||
void HTTPRequest::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("request", "url", "custom_headers", "method", "request_data"), &HTTPRequest::request, DEFVAL(PackedStringArray()), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(String()));
|
||||
ClassDB::bind_method(D_METHOD("request_raw", "url", "custom_headers", "method", "request_data_raw"), &HTTPRequest::request_raw, DEFVAL(PackedStringArray()), DEFVAL(HTTPClient::METHOD_GET), DEFVAL(PackedByteArray()));
|
||||
ClassDB::bind_method(D_METHOD("cancel_request"), &HTTPRequest::cancel_request);
|
||||
ClassDB::bind_method(D_METHOD("set_tls_options", "client_options"), &HTTPRequest::set_tls_options);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_http_client_status"), &HTTPRequest::get_http_client_status);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_use_threads", "enable"), &HTTPRequest::set_use_threads);
|
||||
ClassDB::bind_method(D_METHOD("is_using_threads"), &HTTPRequest::is_using_threads);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_accept_gzip", "enable"), &HTTPRequest::set_accept_gzip);
|
||||
ClassDB::bind_method(D_METHOD("is_accepting_gzip"), &HTTPRequest::is_accepting_gzip);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_body_size_limit", "bytes"), &HTTPRequest::set_body_size_limit);
|
||||
ClassDB::bind_method(D_METHOD("get_body_size_limit"), &HTTPRequest::get_body_size_limit);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_max_redirects", "amount"), &HTTPRequest::set_max_redirects);
|
||||
ClassDB::bind_method(D_METHOD("get_max_redirects"), &HTTPRequest::get_max_redirects);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_download_file", "path"), &HTTPRequest::set_download_file);
|
||||
ClassDB::bind_method(D_METHOD("get_download_file"), &HTTPRequest::get_download_file);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_downloaded_bytes"), &HTTPRequest::get_downloaded_bytes);
|
||||
ClassDB::bind_method(D_METHOD("get_body_size"), &HTTPRequest::get_body_size);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_timeout", "timeout"), &HTTPRequest::set_timeout);
|
||||
ClassDB::bind_method(D_METHOD("get_timeout"), &HTTPRequest::get_timeout);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_download_chunk_size", "chunk_size"), &HTTPRequest::set_download_chunk_size);
|
||||
ClassDB::bind_method(D_METHOD("get_download_chunk_size"), &HTTPRequest::get_download_chunk_size);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_http_proxy", "host", "port"), &HTTPRequest::set_http_proxy);
|
||||
ClassDB::bind_method(D_METHOD("set_https_proxy", "host", "port"), &HTTPRequest::set_https_proxy);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "download_file", PROPERTY_HINT_FILE_PATH), "set_download_file", "get_download_file");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "download_chunk_size", PROPERTY_HINT_RANGE, "256,16777216,suffix:B"), "set_download_chunk_size", "get_download_chunk_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_threads"), "set_use_threads", "is_using_threads");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "accept_gzip"), "set_accept_gzip", "is_accepting_gzip");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "body_size_limit", PROPERTY_HINT_RANGE, "-1,2000000000,suffix:B"), "set_body_size_limit", "get_body_size_limit");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_redirects", PROPERTY_HINT_RANGE, "-1,64"), "set_max_redirects", "get_max_redirects");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "timeout", PROPERTY_HINT_RANGE, "0,3600,0.1,or_greater,suffix:s"), "set_timeout", "get_timeout");
|
||||
|
||||
ADD_SIGNAL(MethodInfo("request_completed", PropertyInfo(Variant::INT, "result"), PropertyInfo(Variant::INT, "response_code"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "headers"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "body")));
|
||||
|
||||
BIND_ENUM_CONSTANT(RESULT_SUCCESS);
|
||||
BIND_ENUM_CONSTANT(RESULT_CHUNKED_BODY_SIZE_MISMATCH);
|
||||
BIND_ENUM_CONSTANT(RESULT_CANT_CONNECT);
|
||||
BIND_ENUM_CONSTANT(RESULT_CANT_RESOLVE);
|
||||
BIND_ENUM_CONSTANT(RESULT_CONNECTION_ERROR);
|
||||
BIND_ENUM_CONSTANT(RESULT_TLS_HANDSHAKE_ERROR);
|
||||
BIND_ENUM_CONSTANT(RESULT_NO_RESPONSE);
|
||||
BIND_ENUM_CONSTANT(RESULT_BODY_SIZE_LIMIT_EXCEEDED);
|
||||
BIND_ENUM_CONSTANT(RESULT_BODY_DECOMPRESS_FAILED);
|
||||
BIND_ENUM_CONSTANT(RESULT_REQUEST_FAILED);
|
||||
BIND_ENUM_CONSTANT(RESULT_DOWNLOAD_FILE_CANT_OPEN);
|
||||
BIND_ENUM_CONSTANT(RESULT_DOWNLOAD_FILE_WRITE_ERROR);
|
||||
BIND_ENUM_CONSTANT(RESULT_REDIRECT_LIMIT_REACHED);
|
||||
BIND_ENUM_CONSTANT(RESULT_TIMEOUT);
|
||||
}
|
||||
|
||||
HTTPRequest::HTTPRequest() {
|
||||
client = Ref<HTTPClient>(HTTPClient::create());
|
||||
tls_options = TLSOptions::client();
|
||||
timer = memnew(Timer);
|
||||
timer->set_one_shot(true);
|
||||
timer->connect("timeout", callable_mp(this, &HTTPRequest::_timeout));
|
||||
add_child(timer);
|
||||
}
|
168
scene/main/http_request.h
Normal file
168
scene/main/http_request.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/**************************************************************************/
|
||||
/* http_request.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/http_client.h"
|
||||
#include "core/io/stream_peer_gzip.h"
|
||||
#include "core/os/thread.h"
|
||||
#include "core/templates/safe_refcount.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class Timer;
|
||||
|
||||
class HTTPRequest : public Node {
|
||||
GDCLASS(HTTPRequest, Node);
|
||||
|
||||
public:
|
||||
enum Result {
|
||||
RESULT_SUCCESS,
|
||||
RESULT_CHUNKED_BODY_SIZE_MISMATCH,
|
||||
RESULT_CANT_CONNECT,
|
||||
RESULT_CANT_RESOLVE,
|
||||
RESULT_CONNECTION_ERROR,
|
||||
RESULT_TLS_HANDSHAKE_ERROR,
|
||||
RESULT_NO_RESPONSE,
|
||||
RESULT_BODY_SIZE_LIMIT_EXCEEDED,
|
||||
RESULT_BODY_DECOMPRESS_FAILED,
|
||||
RESULT_REQUEST_FAILED,
|
||||
RESULT_DOWNLOAD_FILE_CANT_OPEN,
|
||||
RESULT_DOWNLOAD_FILE_WRITE_ERROR,
|
||||
RESULT_REDIRECT_LIMIT_REACHED,
|
||||
RESULT_TIMEOUT
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
bool requesting = false;
|
||||
|
||||
String request_string;
|
||||
String url;
|
||||
int port = 80;
|
||||
Vector<String> headers;
|
||||
bool use_tls = false;
|
||||
Ref<TLSOptions> tls_options;
|
||||
HTTPClient::Method method;
|
||||
Vector<uint8_t> request_data;
|
||||
|
||||
bool request_sent = false;
|
||||
Ref<HTTPClient> client;
|
||||
PackedByteArray body;
|
||||
SafeFlag use_threads;
|
||||
bool accept_gzip = true;
|
||||
|
||||
bool got_response = false;
|
||||
int response_code = 0;
|
||||
Vector<String> response_headers;
|
||||
|
||||
String download_to_file;
|
||||
|
||||
Ref<StreamPeerGZIP> decompressor;
|
||||
Ref<FileAccess> file;
|
||||
|
||||
int body_len = -1;
|
||||
SafeNumeric<int> downloaded;
|
||||
SafeNumeric<int> final_body_size;
|
||||
int body_size_limit = -1;
|
||||
|
||||
int redirections = 0;
|
||||
|
||||
bool _update_connection();
|
||||
|
||||
int max_redirects = 8;
|
||||
|
||||
double timeout = 0;
|
||||
|
||||
void _redirect_request(const String &p_new_url);
|
||||
|
||||
bool _handle_response(bool *ret_value);
|
||||
|
||||
Error _parse_url(const String &p_url);
|
||||
Error _request();
|
||||
|
||||
bool has_header(const PackedStringArray &p_headers, const String &p_header_name);
|
||||
String get_header_value(const PackedStringArray &p_headers, const String &header_name);
|
||||
|
||||
SafeFlag thread_done;
|
||||
SafeFlag thread_request_quit;
|
||||
|
||||
Thread thread;
|
||||
|
||||
void _defer_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data);
|
||||
void _request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data);
|
||||
static void _thread_func(void *p_userdata);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
Error request(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), HTTPClient::Method p_method = HTTPClient::METHOD_GET, const String &p_request_data = ""); //connects to a full url and perform request
|
||||
Error request_raw(const String &p_url, const Vector<String> &p_custom_headers = Vector<String>(), HTTPClient::Method p_method = HTTPClient::METHOD_GET, const Vector<uint8_t> &p_request_data_raw = Vector<uint8_t>()); //connects to a full url and perform request
|
||||
void cancel_request();
|
||||
HTTPClient::Status get_http_client_status() const;
|
||||
|
||||
void set_use_threads(bool p_use);
|
||||
bool is_using_threads() const;
|
||||
|
||||
void set_accept_gzip(bool p_gzip);
|
||||
bool is_accepting_gzip() const;
|
||||
|
||||
void set_download_file(const String &p_file);
|
||||
String get_download_file() const;
|
||||
|
||||
void set_download_chunk_size(int p_chunk_size);
|
||||
int get_download_chunk_size() const;
|
||||
|
||||
void set_body_size_limit(int p_bytes);
|
||||
int get_body_size_limit() const;
|
||||
|
||||
void set_max_redirects(int p_max);
|
||||
int get_max_redirects() const;
|
||||
|
||||
Timer *timer = nullptr;
|
||||
|
||||
void set_timeout(double p_timeout);
|
||||
double get_timeout();
|
||||
|
||||
void _timeout();
|
||||
|
||||
int get_downloaded_bytes() const;
|
||||
int get_body_size() const;
|
||||
|
||||
void set_http_proxy(const String &p_host, int p_port);
|
||||
void set_https_proxy(const String &p_host, int p_port);
|
||||
|
||||
void set_tls_options(const Ref<TLSOptions> &p_options);
|
||||
|
||||
HTTPRequest();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(HTTPRequest::Result);
|
257
scene/main/instance_placeholder.cpp
Normal file
257
scene/main/instance_placeholder.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
/**************************************************************************/
|
||||
/* instance_placeholder.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "instance_placeholder.h"
|
||||
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "scene/resources/packed_scene.h"
|
||||
|
||||
bool InstancePlaceholder::_set(const StringName &p_name, const Variant &p_value) {
|
||||
PropSet ps;
|
||||
ps.name = p_name;
|
||||
ps.value = p_value;
|
||||
stored_values.push_back(ps);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InstancePlaceholder::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
for (const PropSet &E : stored_values) {
|
||||
if (E.name == p_name) {
|
||||
r_ret = E.value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void InstancePlaceholder::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
for (const PropSet &E : stored_values) {
|
||||
PropertyInfo pi;
|
||||
pi.name = E.name;
|
||||
pi.type = E.value.get_type();
|
||||
pi.usage = PROPERTY_USAGE_STORAGE;
|
||||
|
||||
p_list->push_back(pi);
|
||||
}
|
||||
}
|
||||
|
||||
void InstancePlaceholder::set_instance_path(const String &p_name) {
|
||||
path = p_name;
|
||||
}
|
||||
|
||||
String InstancePlaceholder::get_instance_path() const {
|
||||
return path;
|
||||
}
|
||||
|
||||
Node *InstancePlaceholder::create_instance(bool p_replace, const Ref<PackedScene> &p_custom_scene) {
|
||||
ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
|
||||
|
||||
Node *base = get_parent();
|
||||
if (!base) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Ref<PackedScene> ps;
|
||||
if (p_custom_scene.is_valid()) {
|
||||
ps = p_custom_scene;
|
||||
} else {
|
||||
ps = ResourceLoader::load(path, "PackedScene");
|
||||
}
|
||||
|
||||
if (ps.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
Node *instance = ps->instantiate();
|
||||
if (!instance) {
|
||||
return nullptr;
|
||||
}
|
||||
instance->set_name(get_name());
|
||||
instance->set_multiplayer_authority(get_multiplayer_authority());
|
||||
int pos = get_index();
|
||||
|
||||
for (const PropSet &E : stored_values) {
|
||||
set_value_on_instance(this, instance, E);
|
||||
}
|
||||
|
||||
if (p_replace) {
|
||||
queue_free();
|
||||
base->remove_child(this);
|
||||
}
|
||||
|
||||
base->add_child(instance);
|
||||
base->move_child(instance, pos);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
// This method will attempt to set the correct values on the placeholder instance
|
||||
// for regular types this is trivial and unnecessary.
|
||||
// For nodes however this becomes a bit tricky because they might now have existed until the instantiation,
|
||||
// so this method will try to find the correct nodes and resolve them.
|
||||
void InstancePlaceholder::set_value_on_instance(InstancePlaceholder *p_placeholder, Node *p_instance, const PropSet &p_set) {
|
||||
bool is_valid;
|
||||
|
||||
// If we don't have any info, we can't do anything,
|
||||
// so try setting the value directly.
|
||||
Variant current = p_instance->get(p_set.name, &is_valid);
|
||||
if (!is_valid) {
|
||||
p_instance->set(p_set.name, p_set.value, &is_valid);
|
||||
return;
|
||||
}
|
||||
|
||||
Variant::Type current_type = current.get_type();
|
||||
Variant::Type placeholder_type = p_set.value.get_type();
|
||||
|
||||
// Arrays are a special case, because their containing type might be different.
|
||||
if (current_type != Variant::Type::ARRAY) {
|
||||
// Check if the variant types match.
|
||||
if (Variant::evaluate(Variant::OP_EQUAL, current_type, placeholder_type)) {
|
||||
p_instance->set(p_set.name, p_set.value, &is_valid);
|
||||
if (is_valid) {
|
||||
return;
|
||||
}
|
||||
// Types match but setting failed? This is strange, so let's print a warning!
|
||||
WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name()));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// We are dealing with an Array.
|
||||
// Let's check if the subtype of the array matches first.
|
||||
// This is needed because the set method of ScriptInstance checks for type,
|
||||
// but the ClassDB set method doesn't! So we cannot reliably know what actually happens.
|
||||
Array current_array = current;
|
||||
Array placeholder_array = p_set.value;
|
||||
if (current_array.is_same_typed(placeholder_array)) {
|
||||
p_instance->set(p_set.name, p_set.value, &is_valid);
|
||||
if (is_valid) {
|
||||
return;
|
||||
}
|
||||
// Internal array types match but setting failed? This is strange, so let's print a warning!
|
||||
WARN_PRINT(vformat("Array Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(Variant::Type(current_array.get_typed_builtin())), p_placeholder->get_name()));
|
||||
}
|
||||
// Arrays are not the same internal type. This should be happening because we have a NodePath Array,
|
||||
// but the instance wants a Node Array.
|
||||
}
|
||||
|
||||
switch (current_type) {
|
||||
case Variant::Type::NIL: {
|
||||
Ref<Resource> resource = p_set.value;
|
||||
if (placeholder_type != Variant::Type::NODE_PATH && resource.is_null()) {
|
||||
break;
|
||||
}
|
||||
// If it's nil but we have a NodePath or a Resource, we guess what works.
|
||||
p_instance->set(p_set.name, p_set.value, &is_valid);
|
||||
if (is_valid) {
|
||||
break;
|
||||
}
|
||||
|
||||
p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value), &is_valid);
|
||||
break;
|
||||
}
|
||||
case Variant::Type::OBJECT: {
|
||||
if (placeholder_type != Variant::Type::NODE_PATH) {
|
||||
break;
|
||||
}
|
||||
// Easiest case, we want a node, but we have a deferred NodePath.
|
||||
p_instance->set(p_set.name, try_get_node(p_placeholder, p_instance, p_set.value));
|
||||
break;
|
||||
}
|
||||
case Variant::Type::ARRAY: {
|
||||
// If we have reached here it means our array types don't match,
|
||||
// so we will convert the placeholder array into the correct type
|
||||
// and resolve nodes if necessary.
|
||||
Array current_array = current;
|
||||
Array converted_array;
|
||||
Array placeholder_array = p_set.value;
|
||||
converted_array = current_array.duplicate();
|
||||
converted_array.resize(placeholder_array.size());
|
||||
|
||||
if (Variant::evaluate(Variant::OP_EQUAL, current_array.get_typed_builtin(), Variant::Type::NODE_PATH)) {
|
||||
// We want a typed NodePath array.
|
||||
for (int i = 0; i < placeholder_array.size(); i++) {
|
||||
converted_array.set(i, placeholder_array[i]);
|
||||
}
|
||||
} else {
|
||||
// We want Nodes, convert NodePaths.
|
||||
for (int i = 0; i < placeholder_array.size(); i++) {
|
||||
converted_array.set(i, try_get_node(p_placeholder, p_instance, placeholder_array[i]));
|
||||
}
|
||||
}
|
||||
|
||||
p_instance->set(p_set.name, converted_array, &is_valid);
|
||||
if (!is_valid) {
|
||||
WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
WARN_PRINT(vformat("Property '%s' with type '%s' could not be set when creating instance of '%s'.", p_set.name, Variant::get_type_name(current_type), p_placeholder->get_name()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Node *InstancePlaceholder::try_get_node(InstancePlaceholder *p_placeholder, Node *p_instance, const NodePath &p_path) {
|
||||
// First try to resolve internally,
|
||||
// if that fails try resolving externally.
|
||||
Node *node = p_instance->get_node_or_null(p_path);
|
||||
if (node == nullptr) {
|
||||
node = p_placeholder->get_node_or_null(p_path);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
Dictionary InstancePlaceholder::get_stored_values(bool p_with_order) {
|
||||
Dictionary ret;
|
||||
PackedStringArray order;
|
||||
|
||||
for (const PropSet &E : stored_values) {
|
||||
ret[E.name] = E.value;
|
||||
if (p_with_order) {
|
||||
order.push_back(E.name);
|
||||
}
|
||||
};
|
||||
|
||||
if (p_with_order) {
|
||||
ret[".order"] = order;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void InstancePlaceholder::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_stored_values", "with_order"), &InstancePlaceholder::get_stored_values, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("create_instance", "replace", "custom_scene"), &InstancePlaceholder::create_instance, DEFVAL(false), DEFVAL(Variant()));
|
||||
ClassDB::bind_method(D_METHOD("get_instance_path"), &InstancePlaceholder::get_instance_path);
|
||||
}
|
||||
|
||||
InstancePlaceholder::InstancePlaceholder() {
|
||||
}
|
68
scene/main/instance_placeholder.h
Normal file
68
scene/main/instance_placeholder.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/**************************************************************************/
|
||||
/* instance_placeholder.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class PackedScene;
|
||||
|
||||
class InstancePlaceholder : public Node {
|
||||
GDCLASS(InstancePlaceholder, Node);
|
||||
|
||||
String path;
|
||||
struct PropSet {
|
||||
StringName name;
|
||||
Variant value;
|
||||
};
|
||||
|
||||
List<PropSet> stored_values;
|
||||
|
||||
private:
|
||||
void set_value_on_instance(InstancePlaceholder *p_placeholder, Node *p_instance, const PropSet &p_set);
|
||||
Node *try_get_node(InstancePlaceholder *p_placeholder, Node *p_instance, const NodePath &p_path);
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_instance_path(const String &p_name);
|
||||
String get_instance_path() const;
|
||||
|
||||
Dictionary get_stored_values(bool p_with_order = false);
|
||||
|
||||
Node *create_instance(bool p_replace = false, const Ref<PackedScene> &p_custom_scene = Ref<PackedScene>());
|
||||
|
||||
InstancePlaceholder();
|
||||
};
|
117
scene/main/missing_node.cpp
Normal file
117
scene/main/missing_node.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/**************************************************************************/
|
||||
/* missing_node.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "missing_node.h"
|
||||
|
||||
bool MissingNode::_set(const StringName &p_name, const Variant &p_value) {
|
||||
if (is_recording_properties()) {
|
||||
properties.insert(p_name, p_value);
|
||||
return true; //always valid to set (add)
|
||||
} else {
|
||||
if (!properties.has(p_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
properties[p_name] = p_value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool MissingNode::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
if (!properties.has(p_name)) {
|
||||
return false;
|
||||
}
|
||||
r_ret = properties[p_name];
|
||||
return true;
|
||||
}
|
||||
|
||||
void MissingNode::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
for (const KeyValue<StringName, Variant> &E : properties) {
|
||||
p_list->push_back(PropertyInfo(E.value.get_type(), E.key));
|
||||
}
|
||||
}
|
||||
|
||||
void MissingNode::set_original_class(const String &p_class) {
|
||||
original_class = p_class;
|
||||
}
|
||||
|
||||
String MissingNode::get_original_class() const {
|
||||
return original_class;
|
||||
}
|
||||
|
||||
void MissingNode::set_original_scene(const String &p_scene) {
|
||||
original_scene = p_scene;
|
||||
}
|
||||
|
||||
String MissingNode::get_original_scene() const {
|
||||
return original_scene;
|
||||
}
|
||||
|
||||
void MissingNode::set_recording_properties(bool p_enable) {
|
||||
recording_properties = p_enable;
|
||||
}
|
||||
|
||||
bool MissingNode::is_recording_properties() const {
|
||||
return recording_properties;
|
||||
}
|
||||
|
||||
PackedStringArray MissingNode::get_configuration_warnings() const {
|
||||
// The mere existence of this node is warning.
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
if (!original_scene.is_empty()) {
|
||||
warnings.push_back(vformat(RTR("This node was an instance of scene '%s', which was no longer available when this scene was loaded."), original_scene));
|
||||
warnings.push_back(vformat(RTR("Saving current scene will discard instance and all its properties, including editable children edits (if existing).")));
|
||||
} else if (!original_class.is_empty()) {
|
||||
warnings.push_back(vformat(RTR("This node was saved as class type '%s', which was no longer available when this scene was loaded."), original_class));
|
||||
warnings.push_back(RTR("Data from the original node is kept as a placeholder until this type of node is available again. It can hence be safely re-saved without risk of data loss."));
|
||||
} else {
|
||||
warnings.push_back(RTR("Unrecognized missing node. Check scene dependency errors for details."));
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void MissingNode::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_original_class", "name"), &MissingNode::set_original_class);
|
||||
ClassDB::bind_method(D_METHOD("get_original_class"), &MissingNode::get_original_class);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_original_scene", "name"), &MissingNode::set_original_scene);
|
||||
ClassDB::bind_method(D_METHOD("get_original_scene"), &MissingNode::get_original_scene);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_recording_properties", "enable"), &MissingNode::set_recording_properties);
|
||||
ClassDB::bind_method(D_METHOD("is_recording_properties"), &MissingNode::is_recording_properties);
|
||||
|
||||
// Expose, but not save.
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "original_class", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_original_class", "get_original_class");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "original_scene", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_original_scene", "get_original_scene");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "recording_properties", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_recording_properties", "is_recording_properties");
|
||||
}
|
||||
|
||||
MissingNode::MissingNode() {
|
||||
}
|
63
scene/main/missing_node.h
Normal file
63
scene/main/missing_node.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/**************************************************************************/
|
||||
/* missing_node.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class MissingNode : public Node {
|
||||
GDCLASS(MissingNode, Node)
|
||||
HashMap<StringName, Variant> properties;
|
||||
|
||||
String original_class;
|
||||
String original_scene;
|
||||
bool recording_properties = false;
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_original_class(const String &p_class);
|
||||
String get_original_class() const;
|
||||
|
||||
void set_original_scene(const String &p_scene);
|
||||
String get_original_scene() const;
|
||||
|
||||
void set_recording_properties(bool p_enable);
|
||||
bool is_recording_properties() const;
|
||||
|
||||
virtual PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
MissingNode();
|
||||
};
|
388
scene/main/multiplayer_api.cpp
Normal file
388
scene/main/multiplayer_api.cpp
Normal file
@@ -0,0 +1,388 @@
|
||||
/**************************************************************************/
|
||||
/* multiplayer_api.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "multiplayer_api.h"
|
||||
|
||||
#include "core/io/marshalls.h"
|
||||
StringName MultiplayerAPI::default_interface;
|
||||
|
||||
void MultiplayerAPI::set_default_interface(const StringName &p_interface) {
|
||||
ERR_FAIL_COND_MSG(!ClassDB::is_parent_class(p_interface, MultiplayerAPI::get_class_static()), vformat("Can't make %s the default multiplayer interface since it does not extend MultiplayerAPI.", p_interface));
|
||||
default_interface = StringName(p_interface, true);
|
||||
}
|
||||
|
||||
StringName MultiplayerAPI::get_default_interface() {
|
||||
return default_interface;
|
||||
}
|
||||
|
||||
Ref<MultiplayerAPI> MultiplayerAPI::create_default_interface() {
|
||||
if (default_interface != StringName()) {
|
||||
return Ref<MultiplayerAPI>(Object::cast_to<MultiplayerAPI>(ClassDB::instantiate(default_interface)));
|
||||
}
|
||||
return Ref<MultiplayerAPI>(memnew(MultiplayerAPIExtension));
|
||||
}
|
||||
|
||||
// The variant is compressed and encoded; The first byte contains all the meta
|
||||
// information and the format is:
|
||||
// - The first LSB 6 bits are used for the variant type.
|
||||
// - The next two bits are used to store the encoding mode.
|
||||
// - Boolean values uses the encoding mode to store the value.
|
||||
#define VARIANT_META_TYPE_MASK 0x3F
|
||||
#define VARIANT_META_EMODE_MASK 0xC0
|
||||
#define VARIANT_META_BOOL_MASK 0x80
|
||||
#define ENCODE_8 0 << 6
|
||||
#define ENCODE_16 1 << 6
|
||||
#define ENCODE_32 2 << 6
|
||||
#define ENCODE_64 3 << 6
|
||||
Error MultiplayerAPI::encode_and_compress_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_allow_object_decoding) {
|
||||
// Unreachable because `VARIANT_MAX` == 38 and `ENCODE_VARIANT_MASK` == 77
|
||||
CRASH_COND(p_variant.get_type() > VARIANT_META_TYPE_MASK);
|
||||
|
||||
uint8_t *buf = r_buffer;
|
||||
r_len = 0;
|
||||
uint8_t encode_mode = 0;
|
||||
|
||||
switch (p_variant.get_type()) {
|
||||
case Variant::BOOL: {
|
||||
if (buf) {
|
||||
// We don't use encode_mode for booleans, so we can use it to store the value.
|
||||
buf[0] = (p_variant.operator bool()) ? (1 << 7) : 0;
|
||||
buf[0] |= p_variant.get_type();
|
||||
}
|
||||
r_len += 1;
|
||||
} break;
|
||||
case Variant::INT: {
|
||||
if (buf) {
|
||||
// Reserve the first byte for the meta.
|
||||
buf += 1;
|
||||
}
|
||||
r_len += 1;
|
||||
int64_t val = p_variant;
|
||||
if (val <= (int64_t)INT8_MAX && val >= (int64_t)INT8_MIN) {
|
||||
// Use 8 bit
|
||||
encode_mode = ENCODE_8;
|
||||
if (buf) {
|
||||
buf[0] = val;
|
||||
}
|
||||
r_len += 1;
|
||||
} else if (val <= (int64_t)INT16_MAX && val >= (int64_t)INT16_MIN) {
|
||||
// Use 16 bit
|
||||
encode_mode = ENCODE_16;
|
||||
if (buf) {
|
||||
encode_uint16(val, buf);
|
||||
}
|
||||
r_len += 2;
|
||||
} else if (val <= (int64_t)INT32_MAX && val >= (int64_t)INT32_MIN) {
|
||||
// Use 32 bit
|
||||
encode_mode = ENCODE_32;
|
||||
if (buf) {
|
||||
encode_uint32(val, buf);
|
||||
}
|
||||
r_len += 4;
|
||||
} else {
|
||||
// Use 64 bit
|
||||
encode_mode = ENCODE_64;
|
||||
if (buf) {
|
||||
encode_uint64(val, buf);
|
||||
}
|
||||
r_len += 8;
|
||||
}
|
||||
// Store the meta
|
||||
if (buf) {
|
||||
buf -= 1;
|
||||
buf[0] = encode_mode | p_variant.get_type();
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
// Any other case is not yet compressed.
|
||||
Error err = encode_variant(p_variant, r_buffer, r_len, p_allow_object_decoding);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
if (r_buffer) {
|
||||
// The first byte is not used by the marshaling, so store the type
|
||||
// so we know how to decompress and decode this variant.
|
||||
r_buffer[0] = p_variant.get_type();
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error MultiplayerAPI::decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding) {
|
||||
const uint8_t *buf = p_buffer;
|
||||
int len = p_len;
|
||||
|
||||
ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
|
||||
uint8_t type = buf[0] & VARIANT_META_TYPE_MASK;
|
||||
uint8_t encode_mode = buf[0] & VARIANT_META_EMODE_MASK;
|
||||
|
||||
ERR_FAIL_COND_V(type >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
|
||||
|
||||
switch (type) {
|
||||
case Variant::BOOL: {
|
||||
bool val = (buf[0] & VARIANT_META_BOOL_MASK) > 0;
|
||||
r_variant = val;
|
||||
if (r_len) {
|
||||
*r_len = 1;
|
||||
}
|
||||
} break;
|
||||
case Variant::INT: {
|
||||
buf += 1;
|
||||
len -= 1;
|
||||
if (r_len) {
|
||||
*r_len = 1;
|
||||
}
|
||||
if (encode_mode == ENCODE_8) {
|
||||
// 8 bits.
|
||||
ERR_FAIL_COND_V(len < 1, ERR_INVALID_DATA);
|
||||
int8_t val = buf[0];
|
||||
r_variant = val;
|
||||
if (r_len) {
|
||||
(*r_len) += 1;
|
||||
}
|
||||
} else if (encode_mode == ENCODE_16) {
|
||||
// 16 bits.
|
||||
ERR_FAIL_COND_V(len < 2, ERR_INVALID_DATA);
|
||||
int16_t val = decode_uint16(buf);
|
||||
r_variant = val;
|
||||
if (r_len) {
|
||||
(*r_len) += 2;
|
||||
}
|
||||
} else if (encode_mode == ENCODE_32) {
|
||||
// 32 bits.
|
||||
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
|
||||
int32_t val = decode_uint32(buf);
|
||||
r_variant = val;
|
||||
if (r_len) {
|
||||
(*r_len) += 4;
|
||||
}
|
||||
} else {
|
||||
// 64 bits.
|
||||
ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
|
||||
int64_t val = decode_uint64(buf);
|
||||
r_variant = val;
|
||||
if (r_len) {
|
||||
(*r_len) += 8;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
Error err = decode_variant(r_variant, p_buffer, p_len, r_len, p_allow_object_decoding);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error MultiplayerAPI::encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw, bool p_allow_object_decoding) {
|
||||
r_len = 0;
|
||||
int size = 0;
|
||||
|
||||
if (p_count == 0) {
|
||||
if (r_raw) {
|
||||
*r_raw = true;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Try raw encoding optimization.
|
||||
if (r_raw && p_count == 1) {
|
||||
*r_raw = false;
|
||||
const Variant &v = *(p_variants[0]);
|
||||
if (v.get_type() == Variant::PACKED_BYTE_ARRAY) {
|
||||
*r_raw = true;
|
||||
const PackedByteArray pba = v;
|
||||
if (p_buffer) {
|
||||
memcpy(p_buffer, pba.ptr(), pba.size());
|
||||
}
|
||||
r_len += pba.size();
|
||||
} else {
|
||||
encode_and_compress_variant(v, p_buffer, size, p_allow_object_decoding);
|
||||
r_len += size;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
// Regular encoding.
|
||||
for (int i = 0; i < p_count; i++) {
|
||||
const Variant &v = *(p_variants[i]);
|
||||
encode_and_compress_variant(v, p_buffer ? p_buffer + r_len : nullptr, size, p_allow_object_decoding);
|
||||
r_len += size;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error MultiplayerAPI::decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw, bool p_allow_object_decoding) {
|
||||
r_len = 0;
|
||||
int argc = r_variants.size();
|
||||
if (argc == 0 && p_raw) {
|
||||
return OK;
|
||||
}
|
||||
ERR_FAIL_COND_V(p_raw && argc != 1, ERR_INVALID_DATA);
|
||||
if (p_raw) {
|
||||
r_len = p_len;
|
||||
PackedByteArray pba;
|
||||
pba.resize(p_len);
|
||||
memcpy(pba.ptrw(), p_buffer, p_len);
|
||||
r_variants.write[0] = pba;
|
||||
return OK;
|
||||
}
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
ERR_FAIL_COND_V_MSG(r_len >= p_len, ERR_INVALID_DATA, "Invalid packet received. Size too small.");
|
||||
|
||||
int vlen;
|
||||
Error err = MultiplayerAPI::decode_and_decompress_variant(r_variants.write[i], &p_buffer[r_len], p_len - r_len, &vlen, p_allow_object_decoding);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid packet received. Unable to decode state variable.");
|
||||
r_len += vlen;
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error MultiplayerAPI::_rpc_bind(int p_peer, Object *p_object, const StringName &p_method, Array p_args) {
|
||||
Vector<Variant> args;
|
||||
Vector<const Variant *> argsp;
|
||||
args.resize(p_args.size());
|
||||
argsp.resize(p_args.size());
|
||||
Variant *ptr = args.ptrw();
|
||||
const Variant **pptr = argsp.ptrw();
|
||||
for (int i = 0; i < p_args.size(); i++) {
|
||||
ptr[i] = p_args[i];
|
||||
pptr[i] = &ptr[i];
|
||||
}
|
||||
return rpcp(p_object, p_peer, p_method, argsp.size() ? argsp.ptrw() : nullptr, argsp.size());
|
||||
}
|
||||
|
||||
void MultiplayerAPI::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("has_multiplayer_peer"), &MultiplayerAPI::has_multiplayer_peer);
|
||||
ClassDB::bind_method(D_METHOD("get_multiplayer_peer"), &MultiplayerAPI::get_multiplayer_peer);
|
||||
ClassDB::bind_method(D_METHOD("set_multiplayer_peer", "peer"), &MultiplayerAPI::set_multiplayer_peer);
|
||||
ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerAPI::get_unique_id);
|
||||
ClassDB::bind_method(D_METHOD("is_server"), &MultiplayerAPI::is_server);
|
||||
ClassDB::bind_method(D_METHOD("get_remote_sender_id"), &MultiplayerAPI::get_remote_sender_id);
|
||||
ClassDB::bind_method(D_METHOD("poll"), &MultiplayerAPI::poll);
|
||||
ClassDB::bind_method(D_METHOD("rpc", "peer", "object", "method", "arguments"), &MultiplayerAPI::_rpc_bind, DEFVAL(Array()));
|
||||
ClassDB::bind_method(D_METHOD("object_configuration_add", "object", "configuration"), &MultiplayerAPI::object_configuration_add);
|
||||
ClassDB::bind_method(D_METHOD("object_configuration_remove", "object", "configuration"), &MultiplayerAPI::object_configuration_remove);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_peers"), &MultiplayerAPI::get_peer_ids);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "multiplayer_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_multiplayer_peer", "get_multiplayer_peer");
|
||||
|
||||
ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("set_default_interface", "interface_name"), &MultiplayerAPI::set_default_interface);
|
||||
ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("get_default_interface"), &MultiplayerAPI::get_default_interface);
|
||||
ClassDB::bind_static_method("MultiplayerAPI", D_METHOD("create_default_interface"), &MultiplayerAPI::create_default_interface);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
|
||||
ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
|
||||
ADD_SIGNAL(MethodInfo("connected_to_server"));
|
||||
ADD_SIGNAL(MethodInfo("connection_failed"));
|
||||
ADD_SIGNAL(MethodInfo("server_disconnected"));
|
||||
|
||||
BIND_ENUM_CONSTANT(RPC_MODE_DISABLED);
|
||||
BIND_ENUM_CONSTANT(RPC_MODE_ANY_PEER);
|
||||
BIND_ENUM_CONSTANT(RPC_MODE_AUTHORITY);
|
||||
}
|
||||
|
||||
/// MultiplayerAPIExtension
|
||||
|
||||
Error MultiplayerAPIExtension::poll() {
|
||||
Error err = OK;
|
||||
GDVIRTUAL_CALL(_poll, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
void MultiplayerAPIExtension::set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) {
|
||||
GDVIRTUAL_CALL(_set_multiplayer_peer, p_peer);
|
||||
}
|
||||
|
||||
Ref<MultiplayerPeer> MultiplayerAPIExtension::get_multiplayer_peer() {
|
||||
Ref<MultiplayerPeer> peer;
|
||||
GDVIRTUAL_CALL(_get_multiplayer_peer, peer);
|
||||
return peer;
|
||||
}
|
||||
|
||||
int MultiplayerAPIExtension::get_unique_id() {
|
||||
int id = 1;
|
||||
GDVIRTUAL_CALL(_get_unique_id, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
Vector<int> MultiplayerAPIExtension::get_peer_ids() {
|
||||
Vector<int> ids;
|
||||
GDVIRTUAL_CALL(_get_peer_ids, ids);
|
||||
return ids;
|
||||
}
|
||||
|
||||
Error MultiplayerAPIExtension::rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) {
|
||||
if (!GDVIRTUAL_IS_OVERRIDDEN(_rpc)) {
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
Array args;
|
||||
for (int i = 0; i < p_argcount; i++) {
|
||||
args.push_back(*p_arg[i]);
|
||||
}
|
||||
Error ret = FAILED;
|
||||
GDVIRTUAL_CALL(_rpc, p_peer_id, p_obj, p_method, args, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int MultiplayerAPIExtension::get_remote_sender_id() {
|
||||
int id = 0;
|
||||
GDVIRTUAL_CALL(_get_remote_sender_id, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
Error MultiplayerAPIExtension::object_configuration_add(Object *p_object, Variant p_config) {
|
||||
Error err = ERR_UNAVAILABLE;
|
||||
GDVIRTUAL_CALL(_object_configuration_add, p_object, p_config, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
Error MultiplayerAPIExtension::object_configuration_remove(Object *p_object, Variant p_config) {
|
||||
Error err = ERR_UNAVAILABLE;
|
||||
GDVIRTUAL_CALL(_object_configuration_remove, p_object, p_config, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
void MultiplayerAPIExtension::_bind_methods() {
|
||||
GDVIRTUAL_BIND(_poll);
|
||||
GDVIRTUAL_BIND(_set_multiplayer_peer, "multiplayer_peer");
|
||||
GDVIRTUAL_BIND(_get_multiplayer_peer);
|
||||
GDVIRTUAL_BIND(_get_unique_id);
|
||||
GDVIRTUAL_BIND(_get_peer_ids);
|
||||
GDVIRTUAL_BIND(_rpc, "peer", "object", "method", "args");
|
||||
GDVIRTUAL_BIND(_get_remote_sender_id);
|
||||
GDVIRTUAL_BIND(_object_configuration_add, "object", "configuration");
|
||||
GDVIRTUAL_BIND(_object_configuration_remove, "object", "configuration");
|
||||
}
|
111
scene/main/multiplayer_api.h
Normal file
111
scene/main/multiplayer_api.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/**************************************************************************/
|
||||
/* multiplayer_api.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "scene/main/multiplayer_peer.h"
|
||||
|
||||
class MultiplayerAPI : public RefCounted {
|
||||
GDCLASS(MultiplayerAPI, RefCounted);
|
||||
|
||||
private:
|
||||
static StringName default_interface;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
Error _rpc_bind(int p_peer, Object *p_obj, const StringName &p_method, Array args = Array());
|
||||
|
||||
public:
|
||||
enum RPCMode {
|
||||
RPC_MODE_DISABLED, // No rpc for this method, calls to this will be blocked (default)
|
||||
RPC_MODE_ANY_PEER, // Any peer can call this RPC
|
||||
RPC_MODE_AUTHORITY, // Only the node's multiplayer authority (server by default) can call this RPC
|
||||
};
|
||||
|
||||
static Ref<MultiplayerAPI> create_default_interface();
|
||||
static void set_default_interface(const StringName &p_interface);
|
||||
static StringName get_default_interface();
|
||||
|
||||
static Error encode_and_compress_variant(const Variant &p_variant, uint8_t *p_buffer, int &r_len, bool p_allow_object_decoding);
|
||||
static Error decode_and_decompress_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len, bool p_allow_object_decoding);
|
||||
static Error encode_and_compress_variants(const Variant **p_variants, int p_count, uint8_t *p_buffer, int &r_len, bool *r_raw = nullptr, bool p_allow_object_decoding = false);
|
||||
static Error decode_and_decompress_variants(Vector<Variant> &r_variants, const uint8_t *p_buffer, int p_len, int &r_len, bool p_raw = false, bool p_allow_object_decoding = false);
|
||||
|
||||
virtual Error poll() = 0;
|
||||
virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) = 0;
|
||||
virtual Ref<MultiplayerPeer> get_multiplayer_peer() = 0;
|
||||
virtual int get_unique_id() = 0;
|
||||
virtual Vector<int> get_peer_ids() = 0;
|
||||
|
||||
virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) = 0;
|
||||
virtual int get_remote_sender_id() = 0;
|
||||
|
||||
virtual Error object_configuration_add(Object *p_object, Variant p_config) = 0;
|
||||
virtual Error object_configuration_remove(Object *p_object, Variant p_config) = 0;
|
||||
|
||||
bool has_multiplayer_peer() { return get_multiplayer_peer().is_valid(); }
|
||||
bool is_server() { return get_unique_id() == MultiplayerPeer::TARGET_PEER_SERVER; }
|
||||
|
||||
virtual ~MultiplayerAPI() {}
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode);
|
||||
|
||||
class MultiplayerAPIExtension : public MultiplayerAPI {
|
||||
GDCLASS(MultiplayerAPIExtension, MultiplayerAPI);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual Error poll() override;
|
||||
virtual void set_multiplayer_peer(const Ref<MultiplayerPeer> &p_peer) override;
|
||||
virtual Ref<MultiplayerPeer> get_multiplayer_peer() override;
|
||||
virtual int get_unique_id() override;
|
||||
virtual Vector<int> get_peer_ids() override;
|
||||
|
||||
virtual Error rpcp(Object *p_obj, int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount) override;
|
||||
virtual int get_remote_sender_id() override;
|
||||
|
||||
virtual Error object_configuration_add(Object *p_object, Variant p_config) override;
|
||||
virtual Error object_configuration_remove(Object *p_object, Variant p_config) override;
|
||||
|
||||
// Extensions
|
||||
GDVIRTUAL0R(Error, _poll);
|
||||
GDVIRTUAL1(_set_multiplayer_peer, Ref<MultiplayerPeer>);
|
||||
GDVIRTUAL0R(Ref<MultiplayerPeer>, _get_multiplayer_peer);
|
||||
GDVIRTUAL0RC(int, _get_unique_id);
|
||||
GDVIRTUAL0RC(PackedInt32Array, _get_peer_ids);
|
||||
GDVIRTUAL4R(Error, _rpc, int, Object *, StringName, Array);
|
||||
GDVIRTUAL0RC(int, _get_remote_sender_id);
|
||||
GDVIRTUAL2R(Error, _object_configuration_add, Object *, Variant);
|
||||
GDVIRTUAL2R(Error, _object_configuration_remove, Object *, Variant);
|
||||
};
|
228
scene/main/multiplayer_peer.cpp
Normal file
228
scene/main/multiplayer_peer.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/**************************************************************************/
|
||||
/* multiplayer_peer.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "multiplayer_peer.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
|
||||
uint32_t MultiplayerPeer::generate_unique_id() const {
|
||||
uint32_t hash = 0;
|
||||
|
||||
while (hash == 0 || hash == 1) {
|
||||
hash = hash_murmur3_one_32(
|
||||
(uint32_t)OS::get_singleton()->get_ticks_usec());
|
||||
hash = hash_murmur3_one_32(
|
||||
(uint32_t)OS::get_singleton()->get_unix_time(), hash);
|
||||
hash = hash_murmur3_one_32(
|
||||
(uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash);
|
||||
hash = hash_murmur3_one_32(
|
||||
(uint32_t)((uint64_t)this), hash); // Rely on ASLR heap
|
||||
hash = hash_murmur3_one_32(
|
||||
(uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack
|
||||
|
||||
hash = hash_fmix32(hash);
|
||||
hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
void MultiplayerPeer::set_transfer_channel(int p_channel) {
|
||||
transfer_channel = p_channel;
|
||||
}
|
||||
|
||||
int MultiplayerPeer::get_transfer_channel() const {
|
||||
return transfer_channel;
|
||||
}
|
||||
|
||||
void MultiplayerPeer::set_transfer_mode(TransferMode p_mode) {
|
||||
transfer_mode = p_mode;
|
||||
}
|
||||
|
||||
MultiplayerPeer::TransferMode MultiplayerPeer::get_transfer_mode() const {
|
||||
return transfer_mode;
|
||||
}
|
||||
|
||||
void MultiplayerPeer::set_refuse_new_connections(bool p_enable) {
|
||||
refuse_connections = p_enable;
|
||||
}
|
||||
|
||||
bool MultiplayerPeer::is_refusing_new_connections() const {
|
||||
return refuse_connections;
|
||||
}
|
||||
|
||||
bool MultiplayerPeer::is_server_relay_supported() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void MultiplayerPeer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel);
|
||||
ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &MultiplayerPeer::set_transfer_mode);
|
||||
ClassDB::bind_method(D_METHOD("get_transfer_mode"), &MultiplayerPeer::get_transfer_mode);
|
||||
ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer);
|
||||
ClassDB::bind_method(D_METHOD("get_packet_channel"), &MultiplayerPeer::get_packet_channel);
|
||||
ClassDB::bind_method(D_METHOD("get_packet_mode"), &MultiplayerPeer::get_packet_mode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll);
|
||||
ClassDB::bind_method(D_METHOD("close"), &MultiplayerPeer::close);
|
||||
ClassDB::bind_method(D_METHOD("disconnect_peer", "peer", "force"), &MultiplayerPeer::disconnect_peer, DEFVAL(false));
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_connection_status"), &MultiplayerPeer::get_connection_status);
|
||||
ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id);
|
||||
ClassDB::bind_method(D_METHOD("generate_unique_id"), &MultiplayerPeer::generate_unique_id);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections);
|
||||
ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_server_relay_supported"), &MultiplayerPeer::is_server_relay_supported);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel");
|
||||
|
||||
BIND_ENUM_CONSTANT(CONNECTION_DISCONNECTED);
|
||||
BIND_ENUM_CONSTANT(CONNECTION_CONNECTING);
|
||||
BIND_ENUM_CONSTANT(CONNECTION_CONNECTED);
|
||||
|
||||
BIND_CONSTANT(TARGET_PEER_BROADCAST);
|
||||
BIND_CONSTANT(TARGET_PEER_SERVER);
|
||||
|
||||
BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE);
|
||||
BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED);
|
||||
BIND_ENUM_CONSTANT(TRANSFER_MODE_RELIABLE);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("peer_connected", PropertyInfo(Variant::INT, "id")));
|
||||
ADD_SIGNAL(MethodInfo("peer_disconnected", PropertyInfo(Variant::INT, "id")));
|
||||
}
|
||||
|
||||
/*************/
|
||||
|
||||
Error MultiplayerPeerExtension::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
|
||||
Error err;
|
||||
if (GDVIRTUAL_CALL(_get_packet, r_buffer, &r_buffer_size, err)) {
|
||||
return err;
|
||||
}
|
||||
if (GDVIRTUAL_IS_OVERRIDDEN(_get_packet_script)) {
|
||||
if (!GDVIRTUAL_CALL(_get_packet_script, script_buffer)) {
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
if (script_buffer.is_empty()) {
|
||||
return Error::ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
*r_buffer = script_buffer.ptr();
|
||||
r_buffer_size = script_buffer.size();
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
WARN_PRINT_ONCE("MultiplayerPeerExtension::_get_packet_native is unimplemented!");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
Error MultiplayerPeerExtension::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
|
||||
Error err;
|
||||
if (GDVIRTUAL_CALL(_put_packet, p_buffer, p_buffer_size, err)) {
|
||||
return err;
|
||||
}
|
||||
if (GDVIRTUAL_IS_OVERRIDDEN(_put_packet_script)) {
|
||||
PackedByteArray a;
|
||||
a.resize(p_buffer_size);
|
||||
memcpy(a.ptrw(), p_buffer, p_buffer_size);
|
||||
|
||||
if (!GDVIRTUAL_CALL(_put_packet_script, a, err)) {
|
||||
return FAILED;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
WARN_PRINT_ONCE("MultiplayerPeerExtension::_put_packet_native is unimplemented!");
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
void MultiplayerPeerExtension::set_refuse_new_connections(bool p_enable) {
|
||||
if (GDVIRTUAL_CALL(_set_refuse_new_connections, p_enable)) {
|
||||
return;
|
||||
}
|
||||
MultiplayerPeer::set_refuse_new_connections(p_enable);
|
||||
}
|
||||
|
||||
bool MultiplayerPeerExtension::is_refusing_new_connections() const {
|
||||
bool refusing;
|
||||
if (GDVIRTUAL_CALL(_is_refusing_new_connections, refusing)) {
|
||||
return refusing;
|
||||
}
|
||||
return MultiplayerPeer::is_refusing_new_connections();
|
||||
}
|
||||
|
||||
bool MultiplayerPeerExtension::is_server_relay_supported() const {
|
||||
bool can_relay;
|
||||
if (GDVIRTUAL_CALL(_is_server_relay_supported, can_relay)) {
|
||||
return can_relay;
|
||||
}
|
||||
return MultiplayerPeer::is_server_relay_supported();
|
||||
}
|
||||
|
||||
void MultiplayerPeerExtension::_bind_methods() {
|
||||
GDVIRTUAL_BIND(_get_packet, "r_buffer", "r_buffer_size");
|
||||
GDVIRTUAL_BIND(_put_packet, "p_buffer", "p_buffer_size");
|
||||
GDVIRTUAL_BIND(_get_available_packet_count);
|
||||
GDVIRTUAL_BIND(_get_max_packet_size);
|
||||
|
||||
GDVIRTUAL_BIND(_get_packet_script)
|
||||
GDVIRTUAL_BIND(_put_packet_script, "p_buffer");
|
||||
|
||||
GDVIRTUAL_BIND(_get_packet_channel);
|
||||
GDVIRTUAL_BIND(_get_packet_mode);
|
||||
|
||||
GDVIRTUAL_BIND(_set_transfer_channel, "p_channel");
|
||||
GDVIRTUAL_BIND(_get_transfer_channel);
|
||||
|
||||
GDVIRTUAL_BIND(_set_transfer_mode, "p_mode");
|
||||
GDVIRTUAL_BIND(_get_transfer_mode);
|
||||
|
||||
GDVIRTUAL_BIND(_set_target_peer, "p_peer");
|
||||
|
||||
GDVIRTUAL_BIND(_get_packet_peer);
|
||||
GDVIRTUAL_BIND(_is_server);
|
||||
GDVIRTUAL_BIND(_poll);
|
||||
GDVIRTUAL_BIND(_close);
|
||||
GDVIRTUAL_BIND(_disconnect_peer, "p_peer", "p_force");
|
||||
GDVIRTUAL_BIND(_get_unique_id);
|
||||
GDVIRTUAL_BIND(_set_refuse_new_connections, "p_enable");
|
||||
GDVIRTUAL_BIND(_is_refusing_new_connections);
|
||||
GDVIRTUAL_BIND(_is_server_relay_supported);
|
||||
GDVIRTUAL_BIND(_get_connection_status);
|
||||
|
||||
ADD_PROPERTY_DEFAULT("transfer_mode", TRANSFER_MODE_RELIABLE);
|
||||
ADD_PROPERTY_DEFAULT("transfer_channel", 0);
|
||||
}
|
145
scene/main/multiplayer_peer.h
Normal file
145
scene/main/multiplayer_peer.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/**************************************************************************/
|
||||
/* multiplayer_peer.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/packet_peer.h"
|
||||
|
||||
#include "core/extension/ext_wrappers.gen.inc"
|
||||
#include "core/object/gdvirtual.gen.inc"
|
||||
#include "core/variant/native_ptr.h"
|
||||
|
||||
class MultiplayerPeer : public PacketPeer {
|
||||
GDCLASS(MultiplayerPeer, PacketPeer);
|
||||
|
||||
public:
|
||||
enum TransferMode {
|
||||
TRANSFER_MODE_UNRELIABLE,
|
||||
TRANSFER_MODE_UNRELIABLE_ORDERED,
|
||||
TRANSFER_MODE_RELIABLE
|
||||
};
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
private:
|
||||
int transfer_channel = 0;
|
||||
TransferMode transfer_mode = TRANSFER_MODE_RELIABLE;
|
||||
bool refuse_connections = false;
|
||||
|
||||
public:
|
||||
enum {
|
||||
TARGET_PEER_BROADCAST = 0,
|
||||
TARGET_PEER_SERVER = 1
|
||||
};
|
||||
|
||||
enum ConnectionStatus {
|
||||
CONNECTION_DISCONNECTED,
|
||||
CONNECTION_CONNECTING,
|
||||
CONNECTION_CONNECTED,
|
||||
};
|
||||
|
||||
virtual void set_transfer_channel(int p_channel);
|
||||
virtual int get_transfer_channel() const;
|
||||
virtual void set_transfer_mode(TransferMode p_mode);
|
||||
virtual TransferMode get_transfer_mode() const;
|
||||
virtual void set_refuse_new_connections(bool p_enable);
|
||||
virtual bool is_refusing_new_connections() const;
|
||||
virtual bool is_server_relay_supported() const;
|
||||
|
||||
virtual void set_target_peer(int p_peer_id) = 0;
|
||||
|
||||
virtual int get_packet_peer() const = 0;
|
||||
virtual TransferMode get_packet_mode() const = 0;
|
||||
virtual int get_packet_channel() const = 0;
|
||||
|
||||
virtual void disconnect_peer(int p_peer, bool p_force = false) = 0;
|
||||
|
||||
virtual bool is_server() const = 0;
|
||||
|
||||
virtual void poll() = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual int get_unique_id() const = 0;
|
||||
|
||||
virtual ConnectionStatus get_connection_status() const = 0;
|
||||
|
||||
uint32_t generate_unique_id() const;
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus);
|
||||
VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode);
|
||||
|
||||
class MultiplayerPeerExtension : public MultiplayerPeer {
|
||||
GDCLASS(MultiplayerPeerExtension, MultiplayerPeer);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
PackedByteArray script_buffer;
|
||||
|
||||
public:
|
||||
/* PacketPeer extension */
|
||||
virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size) override; ///< buffer is GONE after next get_packet
|
||||
GDVIRTUAL2R(Error, _get_packet, GDExtensionConstPtr<const uint8_t *>, GDExtensionPtr<int>);
|
||||
GDVIRTUAL0R(PackedByteArray, _get_packet_script); // For GDScript.
|
||||
|
||||
virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size) override;
|
||||
GDVIRTUAL2R(Error, _put_packet, GDExtensionConstPtr<const uint8_t>, int);
|
||||
GDVIRTUAL1R(Error, _put_packet_script, PackedByteArray); // For GDScript.
|
||||
|
||||
EXBIND0RC(int, get_available_packet_count);
|
||||
EXBIND0RC(int, get_max_packet_size);
|
||||
|
||||
/* MultiplayerPeer extension */
|
||||
virtual void set_refuse_new_connections(bool p_enable) override;
|
||||
GDVIRTUAL1(_set_refuse_new_connections, bool); // Optional.
|
||||
|
||||
virtual bool is_refusing_new_connections() const override;
|
||||
GDVIRTUAL0RC(bool, _is_refusing_new_connections); // Optional.
|
||||
|
||||
virtual bool is_server_relay_supported() const override;
|
||||
GDVIRTUAL0RC(bool, _is_server_relay_supported); // Optional.
|
||||
|
||||
EXBIND1(set_transfer_channel, int);
|
||||
EXBIND0RC(int, get_transfer_channel);
|
||||
EXBIND1(set_transfer_mode, TransferMode);
|
||||
EXBIND0RC(TransferMode, get_transfer_mode);
|
||||
EXBIND1(set_target_peer, int);
|
||||
EXBIND0RC(int, get_packet_peer);
|
||||
EXBIND0RC(TransferMode, get_packet_mode);
|
||||
EXBIND0RC(int, get_packet_channel);
|
||||
EXBIND0RC(bool, is_server);
|
||||
EXBIND0(poll);
|
||||
EXBIND0(close);
|
||||
EXBIND2(disconnect_peer, int, bool);
|
||||
EXBIND0RC(int, get_unique_id);
|
||||
EXBIND0RC(ConnectionStatus, get_connection_status);
|
||||
};
|
46
scene/main/node.compat.inc
Normal file
46
scene/main/node.compat.inc
Normal file
@@ -0,0 +1,46 @@
|
||||
/**************************************************************************/
|
||||
/* node.compat.inc */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
|
||||
void Node::_set_name_bind_compat_76560(const String &p_name) {
|
||||
set_name(p_name);
|
||||
}
|
||||
|
||||
Variant Node::_get_rpc_config_bind_compat_106848() const {
|
||||
return _get_node_rpc_config_bind();
|
||||
}
|
||||
|
||||
void Node::_bind_compatibility_methods() {
|
||||
ClassDB::bind_compatibility_method(D_METHOD("set_name", "name"), &Node::_set_name_bind_compat_76560);
|
||||
ClassDB::bind_compatibility_method(D_METHOD("get_rpc_config"), &Node::_get_rpc_config_bind_compat_106848);
|
||||
}
|
||||
|
||||
#endif
|
4213
scene/main/node.cpp
Normal file
4213
scene/main/node.cpp
Normal file
File diff suppressed because it is too large
Load Diff
897
scene/main/node.h
Normal file
897
scene/main/node.h
Normal file
@@ -0,0 +1,897 @@
|
||||
/**************************************************************************/
|
||||
/* node.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/string/node_path.h"
|
||||
#include "core/variant/typed_array.h"
|
||||
#include "scene/main/scene_tree.h"
|
||||
#include "scene/scene_string_names.h"
|
||||
|
||||
class Viewport;
|
||||
class Window;
|
||||
class SceneState;
|
||||
class Tween;
|
||||
class PropertyTweener;
|
||||
|
||||
SAFE_FLAG_TYPE_PUN_GUARANTEES
|
||||
SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t)
|
||||
|
||||
class Node : public Object {
|
||||
GDCLASS(Node, Object);
|
||||
|
||||
friend class SceneTreeFTI;
|
||||
|
||||
protected:
|
||||
// During group processing, these are thread-safe.
|
||||
// Outside group processing, these avoid the cost of sync by working as plain primitive types.
|
||||
union MTFlag {
|
||||
SafeFlag mt;
|
||||
bool st;
|
||||
MTFlag() :
|
||||
mt{} {}
|
||||
};
|
||||
template <typename T>
|
||||
union MTNumeric {
|
||||
SafeNumeric<T> mt;
|
||||
T st;
|
||||
MTNumeric() :
|
||||
mt{} {}
|
||||
};
|
||||
|
||||
public:
|
||||
// N.B. Any enum stored as a bitfield should be specified as UNSIGNED to work around
|
||||
// some compilers trying to store it as signed, and requiring 1 more bit than necessary.
|
||||
enum ProcessMode : unsigned int {
|
||||
PROCESS_MODE_INHERIT, // same as parent node
|
||||
PROCESS_MODE_PAUSABLE, // process only if not paused
|
||||
PROCESS_MODE_WHEN_PAUSED, // process only if paused
|
||||
PROCESS_MODE_ALWAYS, // process always
|
||||
PROCESS_MODE_DISABLED, // never process
|
||||
};
|
||||
|
||||
enum ProcessThreadGroup {
|
||||
PROCESS_THREAD_GROUP_INHERIT,
|
||||
PROCESS_THREAD_GROUP_MAIN_THREAD,
|
||||
PROCESS_THREAD_GROUP_SUB_THREAD,
|
||||
};
|
||||
|
||||
enum ProcessThreadMessages {
|
||||
FLAG_PROCESS_THREAD_MESSAGES = 1,
|
||||
FLAG_PROCESS_THREAD_MESSAGES_PHYSICS = 2,
|
||||
FLAG_PROCESS_THREAD_MESSAGES_ALL = 3,
|
||||
};
|
||||
|
||||
enum PhysicsInterpolationMode : unsigned int {
|
||||
PHYSICS_INTERPOLATION_MODE_INHERIT,
|
||||
PHYSICS_INTERPOLATION_MODE_ON,
|
||||
PHYSICS_INTERPOLATION_MODE_OFF,
|
||||
};
|
||||
|
||||
enum DuplicateFlags {
|
||||
DUPLICATE_SIGNALS = 1,
|
||||
DUPLICATE_GROUPS = 2,
|
||||
DUPLICATE_SCRIPTS = 4,
|
||||
DUPLICATE_USE_INSTANTIATION = 8,
|
||||
#ifdef TOOLS_ENABLED
|
||||
DUPLICATE_FROM_EDITOR = 16,
|
||||
#endif
|
||||
};
|
||||
|
||||
enum NameCasing {
|
||||
NAME_CASING_PASCAL_CASE,
|
||||
NAME_CASING_CAMEL_CASE,
|
||||
NAME_CASING_SNAKE_CASE,
|
||||
NAME_CASING_KEBAB_CASE,
|
||||
};
|
||||
|
||||
enum InternalMode {
|
||||
INTERNAL_MODE_DISABLED,
|
||||
INTERNAL_MODE_FRONT,
|
||||
INTERNAL_MODE_BACK,
|
||||
};
|
||||
|
||||
enum AutoTranslateMode : unsigned int {
|
||||
AUTO_TRANSLATE_MODE_INHERIT,
|
||||
AUTO_TRANSLATE_MODE_ALWAYS,
|
||||
AUTO_TRANSLATE_MODE_DISABLED,
|
||||
};
|
||||
|
||||
struct Comparator {
|
||||
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->is_greater_than(p_a); }
|
||||
};
|
||||
|
||||
static int orphan_node_count;
|
||||
|
||||
void _update_process(bool p_enable, bool p_for_children);
|
||||
|
||||
private:
|
||||
struct GroupData {
|
||||
bool persistent = false;
|
||||
SceneTree::Group *group = nullptr;
|
||||
};
|
||||
|
||||
struct ComparatorByIndex {
|
||||
bool operator()(const Node *p_left, const Node *p_right) const {
|
||||
static const uint32_t order[3] = { 1, 0, 2 };
|
||||
uint32_t order_left = order[p_left->data.internal_mode];
|
||||
uint32_t order_right = order[p_right->data.internal_mode];
|
||||
if (order_left == order_right) {
|
||||
return p_left->data.index < p_right->data.index;
|
||||
}
|
||||
return order_left < order_right;
|
||||
}
|
||||
};
|
||||
|
||||
struct ComparatorWithPriority {
|
||||
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.process_priority == p_a->data.process_priority ? p_b->is_greater_than(p_a) : p_b->data.process_priority > p_a->data.process_priority; }
|
||||
};
|
||||
|
||||
struct ComparatorWithPhysicsPriority {
|
||||
bool operator()(const Node *p_a, const Node *p_b) const { return p_b->data.physics_process_priority == p_a->data.physics_process_priority ? p_b->is_greater_than(p_a) : p_b->data.physics_process_priority > p_a->data.physics_process_priority; }
|
||||
};
|
||||
|
||||
// This Data struct is to avoid namespace pollution in derived classes.
|
||||
struct Data {
|
||||
String scene_file_path;
|
||||
Ref<SceneState> instance_state;
|
||||
Ref<SceneState> inherited_state;
|
||||
|
||||
Node *parent = nullptr;
|
||||
Node *owner = nullptr;
|
||||
HashMap<StringName, Node *> children;
|
||||
mutable bool children_cache_dirty = true;
|
||||
mutable LocalVector<Node *> children_cache;
|
||||
HashMap<StringName, Node *> owned_unique_nodes;
|
||||
bool unique_name_in_owner = false;
|
||||
InternalMode internal_mode = INTERNAL_MODE_DISABLED;
|
||||
mutable int internal_children_front_count_cache = 0;
|
||||
mutable int internal_children_back_count_cache = 0;
|
||||
mutable int external_children_count_cache = 0;
|
||||
mutable int index = -1; // relative to front, normal or back.
|
||||
int32_t depth = -1;
|
||||
int blocked = 0; // Safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed.
|
||||
StringName name;
|
||||
SceneTree *tree = nullptr;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
NodePath import_path; // Path used when imported, used by scene editors to keep tracking.
|
||||
#endif
|
||||
String editor_description;
|
||||
|
||||
Viewport *viewport = nullptr;
|
||||
|
||||
mutable RID accessibility_element;
|
||||
|
||||
HashMap<StringName, GroupData> grouped;
|
||||
List<Node *>::Element *OW = nullptr; // Owned element.
|
||||
List<Node *> owned;
|
||||
|
||||
Node *process_owner = nullptr;
|
||||
ProcessThreadGroup process_thread_group = PROCESS_THREAD_GROUP_INHERIT;
|
||||
Node *process_thread_group_owner = nullptr;
|
||||
int process_thread_group_order = 0;
|
||||
BitField<ProcessThreadMessages> process_thread_messages = {};
|
||||
void *process_group = nullptr; // to avoid cyclic dependency
|
||||
|
||||
int multiplayer_authority = 1; // Server by default.
|
||||
Variant rpc_config;
|
||||
|
||||
// Variables used to properly sort the node when processing, ignored otherwise.
|
||||
int process_priority = 0;
|
||||
int physics_process_priority = 0;
|
||||
|
||||
// Keep bitpacked values together to get better packing.
|
||||
ProcessMode process_mode : 3;
|
||||
PhysicsInterpolationMode physics_interpolation_mode : 2;
|
||||
AutoTranslateMode auto_translate_mode : 2;
|
||||
|
||||
bool physics_process : 1;
|
||||
bool process : 1;
|
||||
|
||||
bool physics_process_internal : 1;
|
||||
bool process_internal : 1;
|
||||
|
||||
bool input : 1;
|
||||
bool shortcut_input : 1;
|
||||
bool unhandled_input : 1;
|
||||
bool unhandled_key_input : 1;
|
||||
|
||||
// Physics interpolation can be turned on and off on a per node basis.
|
||||
// This only takes effect when the SceneTree (or project setting) physics interpolation
|
||||
// is switched on.
|
||||
bool physics_interpolated : 1;
|
||||
|
||||
// We can auto-reset physics interpolation when e.g. adding a node for the first time.
|
||||
bool physics_interpolation_reset_requested : 1;
|
||||
|
||||
// Most nodes need not be interpolated in the scene tree, physics interpolation
|
||||
// is normally only needed in the RenderingServer. However if we need to read the
|
||||
// interpolated transform of a node in the SceneTree, it is necessary to duplicate
|
||||
// the interpolation logic client side, in order to prevent stalling the RenderingServer
|
||||
// by reading back.
|
||||
bool physics_interpolated_client_side : 1;
|
||||
|
||||
// For certain nodes (e.g. CPU particles in global mode)
|
||||
// it can be useful to not send the instance transform to the
|
||||
// RenderingServer, and specify the mesh in world space.
|
||||
bool use_identity_transform : 1;
|
||||
|
||||
bool use_placeholder : 1;
|
||||
|
||||
bool display_folded : 1;
|
||||
bool editable_instance : 1;
|
||||
|
||||
bool ready_notified : 1;
|
||||
bool ready_first : 1;
|
||||
|
||||
mutable bool is_auto_translating : 1;
|
||||
mutable bool is_auto_translate_dirty : 1;
|
||||
|
||||
mutable bool is_translation_domain_inherited : 1;
|
||||
mutable bool is_translation_domain_dirty : 1;
|
||||
|
||||
mutable NodePath *path_cache = nullptr;
|
||||
|
||||
} data;
|
||||
|
||||
Ref<MultiplayerAPI> multiplayer;
|
||||
|
||||
String _get_tree_string_pretty(const String &p_prefix, bool p_last);
|
||||
String _get_tree_string(const Node *p_node);
|
||||
|
||||
Node *_get_child_by_name(const StringName &p_name) const;
|
||||
|
||||
void _replace_connections_target(Node *p_new_target);
|
||||
|
||||
void _validate_child_name(Node *p_child, bool p_force_human_readable = false);
|
||||
void _generate_serial_child_name(const Node *p_child, StringName &name) const;
|
||||
|
||||
void _propagate_reverse_notification(int p_notification);
|
||||
void _propagate_deferred_notification(int p_notification, bool p_reverse);
|
||||
void _propagate_enter_tree();
|
||||
void _propagate_ready();
|
||||
void _propagate_exit_tree();
|
||||
void _propagate_after_exit_tree();
|
||||
void _propagate_physics_interpolated(bool p_interpolated);
|
||||
void _propagate_physics_interpolation_reset_requested(bool p_requested);
|
||||
void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification);
|
||||
void _propagate_groups_dirty();
|
||||
void _propagate_translation_domain_dirty();
|
||||
Array _get_node_and_resource(const NodePath &p_path);
|
||||
|
||||
void _duplicate_properties(const Node *p_root, const Node *p_original, Node *p_copy, int p_flags) const;
|
||||
void _duplicate_signals(const Node *p_original, Node *p_copy) const;
|
||||
Node *_duplicate(int p_flags, HashMap<const Node *, Node *> *r_duplimap = nullptr) const;
|
||||
|
||||
TypedArray<StringName> _get_groups() const;
|
||||
|
||||
Error _rpc_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
Error _rpc_id_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
|
||||
friend class SceneTree;
|
||||
|
||||
void _set_tree(SceneTree *p_tree);
|
||||
void _propagate_pause_notification(bool p_enable);
|
||||
void _propagate_suspend_notification(bool p_enable);
|
||||
|
||||
_FORCE_INLINE_ bool _can_process(bool p_paused) const;
|
||||
_FORCE_INLINE_ bool _is_enabled() const;
|
||||
|
||||
void _release_unique_name_in_owner();
|
||||
void _acquire_unique_name_in_owner();
|
||||
|
||||
void _clean_up_owner();
|
||||
|
||||
_FORCE_INLINE_ void _update_children_cache() const {
|
||||
if (unlikely(data.children_cache_dirty)) {
|
||||
_update_children_cache_impl();
|
||||
}
|
||||
}
|
||||
|
||||
void _update_children_cache_impl() const;
|
||||
|
||||
// Process group management
|
||||
void _add_process_group();
|
||||
void _remove_process_group();
|
||||
void _add_to_process_thread_group();
|
||||
void _remove_from_process_thread_group();
|
||||
void _remove_tree_from_process_thread_group();
|
||||
void _add_tree_to_process_thread_group(Node *p_owner);
|
||||
|
||||
static thread_local Node *current_process_thread_group;
|
||||
|
||||
Variant _call_deferred_thread_group_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
Variant _call_thread_safe_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
|
||||
// Editor only signal to keep the SceneTreeEditor in sync.
|
||||
#ifdef TOOLS_ENABLED
|
||||
void _emit_editor_state_changed();
|
||||
#else
|
||||
void _emit_editor_state_changed() {}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
void _block() { data.blocked++; }
|
||||
void _unblock() { data.blocked--; }
|
||||
|
||||
void _notification(int p_notification);
|
||||
|
||||
virtual void _physics_interpolated_changed();
|
||||
|
||||
virtual void add_child_notify(Node *p_child);
|
||||
virtual void remove_child_notify(Node *p_child);
|
||||
virtual void move_child_notify(Node *p_child);
|
||||
virtual void owner_changed_notify();
|
||||
|
||||
void _propagate_replace_owner(Node *p_owner, Node *p_by_owner);
|
||||
|
||||
static void _bind_methods();
|
||||
static String _get_name_num_separator();
|
||||
|
||||
friend class SceneState;
|
||||
|
||||
void _add_child_nocheck(Node *p_child, const StringName &p_name, InternalMode p_internal_mode = INTERNAL_MODE_DISABLED);
|
||||
void _set_owner_nocheck(Node *p_owner);
|
||||
void _set_name_nocheck(const StringName &p_name);
|
||||
|
||||
void _set_physics_interpolated_client_side(bool p_enable) { data.physics_interpolated_client_side = p_enable; }
|
||||
bool _is_physics_interpolated_client_side() const { return data.physics_interpolated_client_side; }
|
||||
|
||||
void _set_physics_interpolation_reset_requested(bool p_enable) { data.physics_interpolation_reset_requested = p_enable; }
|
||||
bool _is_physics_interpolation_reset_requested() const { return data.physics_interpolation_reset_requested; }
|
||||
|
||||
void _set_use_identity_transform(bool p_enable) { data.use_identity_transform = p_enable; }
|
||||
bool _is_using_identity_transform() const { return data.use_identity_transform; }
|
||||
int32_t _get_scene_tree_depth() const { return data.depth; }
|
||||
|
||||
//call from SceneTree
|
||||
void _call_input(const Ref<InputEvent> &p_event);
|
||||
void _call_shortcut_input(const Ref<InputEvent> &p_event);
|
||||
void _call_unhandled_input(const Ref<InputEvent> &p_event);
|
||||
void _call_unhandled_key_input(const Ref<InputEvent> &p_event);
|
||||
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
Variant _get_node_rpc_config_bind() const {
|
||||
return get_node_rpc_config().duplicate(true);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual bool _uses_signal_mutex() const override { return false; } // Node uses thread guards instead.
|
||||
|
||||
virtual void input(const Ref<InputEvent> &p_event);
|
||||
virtual void shortcut_input(const Ref<InputEvent> &p_key_event);
|
||||
virtual void unhandled_input(const Ref<InputEvent> &p_event);
|
||||
virtual void unhandled_key_input(const Ref<InputEvent> &p_key_event);
|
||||
|
||||
GDVIRTUAL1(_process, double)
|
||||
GDVIRTUAL1(_physics_process, double)
|
||||
GDVIRTUAL0(_enter_tree)
|
||||
GDVIRTUAL0(_exit_tree)
|
||||
GDVIRTUAL0(_ready)
|
||||
GDVIRTUAL0RC(Vector<String>, _get_accessibility_configuration_warnings)
|
||||
GDVIRTUAL0RC(Vector<String>, _get_configuration_warnings)
|
||||
|
||||
GDVIRTUAL1(_input, Ref<InputEvent>)
|
||||
GDVIRTUAL1(_shortcut_input, Ref<InputEvent>)
|
||||
GDVIRTUAL1(_unhandled_input, Ref<InputEvent>)
|
||||
GDVIRTUAL1(_unhandled_key_input, Ref<InputEvent>)
|
||||
|
||||
GDVIRTUAL0RC(RID, _get_focused_accessibility_element)
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void _set_name_bind_compat_76560(const String &p_name);
|
||||
Variant _get_rpc_config_bind_compat_106848() const;
|
||||
static void _bind_compatibility_methods();
|
||||
#endif
|
||||
|
||||
public:
|
||||
enum {
|
||||
// You can make your own, but don't use the same numbers as other notifications in other nodes.
|
||||
NOTIFICATION_ENTER_TREE = 10,
|
||||
NOTIFICATION_EXIT_TREE = 11,
|
||||
NOTIFICATION_MOVED_IN_PARENT = 12,
|
||||
NOTIFICATION_READY = 13,
|
||||
NOTIFICATION_PAUSED = 14,
|
||||
NOTIFICATION_UNPAUSED = 15,
|
||||
NOTIFICATION_PHYSICS_PROCESS = 16,
|
||||
NOTIFICATION_PROCESS = 17,
|
||||
NOTIFICATION_PARENTED = 18,
|
||||
NOTIFICATION_UNPARENTED = 19,
|
||||
NOTIFICATION_SCENE_INSTANTIATED = 20,
|
||||
NOTIFICATION_DRAG_BEGIN = 21,
|
||||
NOTIFICATION_DRAG_END = 22,
|
||||
NOTIFICATION_PATH_RENAMED = 23,
|
||||
NOTIFICATION_CHILD_ORDER_CHANGED = 24,
|
||||
NOTIFICATION_INTERNAL_PROCESS = 25,
|
||||
NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26,
|
||||
NOTIFICATION_POST_ENTER_TREE = 27,
|
||||
NOTIFICATION_DISABLED = 28,
|
||||
NOTIFICATION_ENABLED = 29,
|
||||
NOTIFICATION_RESET_PHYSICS_INTERPOLATION = 2001, // A GodotSpace Odyssey.
|
||||
// Keep these linked to Node.
|
||||
|
||||
NOTIFICATION_ACCESSIBILITY_UPDATE = 3000,
|
||||
NOTIFICATION_ACCESSIBILITY_INVALIDATE = 3001,
|
||||
|
||||
NOTIFICATION_WM_MOUSE_ENTER = 1002,
|
||||
NOTIFICATION_WM_MOUSE_EXIT = 1003,
|
||||
NOTIFICATION_WM_WINDOW_FOCUS_IN = 1004,
|
||||
NOTIFICATION_WM_WINDOW_FOCUS_OUT = 1005,
|
||||
NOTIFICATION_WM_CLOSE_REQUEST = 1006,
|
||||
NOTIFICATION_WM_GO_BACK_REQUEST = 1007,
|
||||
NOTIFICATION_WM_SIZE_CHANGED = 1008,
|
||||
NOTIFICATION_WM_DPI_CHANGE = 1009,
|
||||
NOTIFICATION_VP_MOUSE_ENTER = 1010,
|
||||
NOTIFICATION_VP_MOUSE_EXIT = 1011,
|
||||
NOTIFICATION_WM_POSITION_CHANGED = 1012,
|
||||
|
||||
NOTIFICATION_OS_MEMORY_WARNING = MainLoop::NOTIFICATION_OS_MEMORY_WARNING,
|
||||
NOTIFICATION_TRANSLATION_CHANGED = MainLoop::NOTIFICATION_TRANSLATION_CHANGED,
|
||||
NOTIFICATION_WM_ABOUT = MainLoop::NOTIFICATION_WM_ABOUT,
|
||||
NOTIFICATION_CRASH = MainLoop::NOTIFICATION_CRASH,
|
||||
NOTIFICATION_OS_IME_UPDATE = MainLoop::NOTIFICATION_OS_IME_UPDATE,
|
||||
NOTIFICATION_APPLICATION_RESUMED = MainLoop::NOTIFICATION_APPLICATION_RESUMED,
|
||||
NOTIFICATION_APPLICATION_PAUSED = MainLoop::NOTIFICATION_APPLICATION_PAUSED,
|
||||
NOTIFICATION_APPLICATION_FOCUS_IN = MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN,
|
||||
NOTIFICATION_APPLICATION_FOCUS_OUT = MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT,
|
||||
NOTIFICATION_TEXT_SERVER_CHANGED = MainLoop::NOTIFICATION_TEXT_SERVER_CHANGED,
|
||||
|
||||
// Editor specific node notifications
|
||||
NOTIFICATION_EDITOR_PRE_SAVE = 9001,
|
||||
NOTIFICATION_EDITOR_POST_SAVE = 9002,
|
||||
NOTIFICATION_SUSPENDED = 9003,
|
||||
NOTIFICATION_UNSUSPENDED = 9004
|
||||
};
|
||||
|
||||
/* NODE/TREE */
|
||||
|
||||
StringName get_name() const;
|
||||
String get_description() const;
|
||||
void set_name(const StringName &p_name);
|
||||
|
||||
InternalMode get_internal_mode() const;
|
||||
|
||||
void add_child(Node *p_child, bool p_force_readable_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED);
|
||||
void add_sibling(Node *p_sibling, bool p_force_readable_name = false);
|
||||
void remove_child(Node *p_child);
|
||||
|
||||
int get_child_count(bool p_include_internal = true) const;
|
||||
Node *get_child(int p_index, bool p_include_internal = true) const;
|
||||
TypedArray<Node> get_children(bool p_include_internal = true) const;
|
||||
bool has_node(const NodePath &p_path) const;
|
||||
Node *get_node(const NodePath &p_path) const;
|
||||
Node *get_node_or_null(const NodePath &p_path) const;
|
||||
Node *find_child(const String &p_pattern, bool p_recursive = true, bool p_owned = true) const;
|
||||
TypedArray<Node> find_children(const String &p_pattern, const String &p_type = "", bool p_recursive = true, bool p_owned = true) const;
|
||||
bool has_node_and_resource(const NodePath &p_path) const;
|
||||
Node *get_node_and_resource(const NodePath &p_path, Ref<Resource> &r_res, Vector<StringName> &r_leftover_subpath, bool p_last_is_property = true) const;
|
||||
|
||||
virtual void reparent(Node *p_parent, bool p_keep_global_transform = true);
|
||||
Node *get_parent() const;
|
||||
Node *find_parent(const String &p_pattern) const;
|
||||
|
||||
Window *get_window() const;
|
||||
Window *get_non_popup_window() const;
|
||||
Window *get_last_exclusive_window() const;
|
||||
|
||||
_FORCE_INLINE_ SceneTree *get_tree() const {
|
||||
ERR_FAIL_NULL_V(data.tree, nullptr);
|
||||
return data.tree;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool is_inside_tree() const { return data.tree; }
|
||||
bool is_internal() const { return data.internal_mode != INTERNAL_MODE_DISABLED; }
|
||||
|
||||
bool is_ancestor_of(const Node *p_node) const;
|
||||
bool is_greater_than(const Node *p_node) const;
|
||||
|
||||
NodePath get_path() const;
|
||||
NodePath get_path_to(const Node *p_node, bool p_use_unique_path = false) const;
|
||||
Node *find_common_parent_with(const Node *p_node) const;
|
||||
|
||||
void add_to_group(const StringName &p_identifier, bool p_persistent = false);
|
||||
void remove_from_group(const StringName &p_identifier);
|
||||
bool is_in_group(const StringName &p_identifier) const;
|
||||
|
||||
struct GroupInfo {
|
||||
StringName name;
|
||||
bool persistent = false;
|
||||
};
|
||||
|
||||
void get_groups(List<GroupInfo> *p_groups) const;
|
||||
int get_persistent_group_count() const;
|
||||
|
||||
void move_child(Node *p_child, int p_index);
|
||||
void _move_child(Node *p_child, int p_index, bool p_ignore_end = false);
|
||||
|
||||
void set_owner(Node *p_owner);
|
||||
Node *get_owner() const;
|
||||
void get_owned_by(Node *p_by, List<Node *> *p_owned);
|
||||
|
||||
void set_unique_name_in_owner(bool p_enabled);
|
||||
bool is_unique_name_in_owner() const;
|
||||
|
||||
_FORCE_INLINE_ int get_index(bool p_include_internal = true) const {
|
||||
// p_include_internal = false doesn't make sense if the node is internal.
|
||||
ERR_FAIL_COND_V_MSG(!p_include_internal && data.internal_mode != INTERNAL_MODE_DISABLED, -1, "Node is internal. Can't get index with 'include_internal' being false.");
|
||||
if (!data.parent) {
|
||||
return data.index;
|
||||
}
|
||||
data.parent->_update_children_cache();
|
||||
|
||||
if (!p_include_internal) {
|
||||
return data.index;
|
||||
} else {
|
||||
switch (data.internal_mode) {
|
||||
case INTERNAL_MODE_DISABLED: {
|
||||
return data.parent->data.internal_children_front_count_cache + data.index;
|
||||
} break;
|
||||
case INTERNAL_MODE_FRONT: {
|
||||
return data.index;
|
||||
} break;
|
||||
case INTERNAL_MODE_BACK: {
|
||||
return data.parent->data.internal_children_front_count_cache + data.parent->data.external_children_count_cache + data.index;
|
||||
} break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Tween> create_tween();
|
||||
|
||||
void print_tree();
|
||||
void print_tree_pretty();
|
||||
String get_tree_string();
|
||||
String get_tree_string_pretty();
|
||||
|
||||
void set_scene_file_path(const String &p_scene_file_path);
|
||||
String get_scene_file_path() const;
|
||||
|
||||
void set_editor_description(const String &p_editor_description);
|
||||
String get_editor_description() const;
|
||||
|
||||
void set_editable_instance(Node *p_node, bool p_editable);
|
||||
bool is_editable_instance(const Node *p_node) const;
|
||||
Node *get_deepest_editable_node(Node *p_start_node) const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void set_property_pinned(const String &p_property, bool p_pinned);
|
||||
bool is_property_pinned(const StringName &p_property) const;
|
||||
virtual StringName get_property_store_alias(const StringName &p_property) const;
|
||||
bool is_part_of_edited_scene() const;
|
||||
#else
|
||||
bool is_part_of_edited_scene() const { return false; }
|
||||
#endif
|
||||
void get_storable_properties(HashSet<StringName> &r_storable_properties) const;
|
||||
|
||||
virtual String to_string() override;
|
||||
|
||||
/* NOTIFICATIONS */
|
||||
|
||||
void propagate_notification(int p_notification);
|
||||
|
||||
void propagate_call(const StringName &p_method, const Array &p_args = Array(), const bool p_parent_first = false);
|
||||
|
||||
/* PROCESSING */
|
||||
|
||||
void set_physics_process(bool p_process);
|
||||
double get_physics_process_delta_time() const;
|
||||
bool is_physics_processing() const;
|
||||
|
||||
void set_process(bool p_process);
|
||||
double get_process_delta_time() const;
|
||||
bool is_processing() const;
|
||||
|
||||
void set_physics_process_internal(bool p_process_internal);
|
||||
bool is_physics_processing_internal() const;
|
||||
|
||||
void set_process_internal(bool p_process_internal);
|
||||
bool is_processing_internal() const;
|
||||
|
||||
void set_process_priority(int p_priority);
|
||||
int get_process_priority() const;
|
||||
|
||||
void set_process_thread_group_order(int p_order);
|
||||
int get_process_thread_group_order() const;
|
||||
|
||||
void set_physics_process_priority(int p_priority);
|
||||
int get_physics_process_priority() const;
|
||||
|
||||
void set_process_input(bool p_enable);
|
||||
bool is_processing_input() const;
|
||||
|
||||
void set_process_shortcut_input(bool p_enable);
|
||||
bool is_processing_shortcut_input() const;
|
||||
|
||||
void set_process_unhandled_input(bool p_enable);
|
||||
bool is_processing_unhandled_input() const;
|
||||
|
||||
void set_process_unhandled_key_input(bool p_enable);
|
||||
bool is_processing_unhandled_key_input() const;
|
||||
|
||||
_FORCE_INLINE_ bool _is_any_processing() const {
|
||||
return data.process || data.process_internal || data.physics_process || data.physics_process_internal;
|
||||
}
|
||||
_FORCE_INLINE_ bool is_accessible_from_caller_thread() const {
|
||||
if (current_process_thread_group == nullptr) {
|
||||
// No thread processing.
|
||||
// Only accessible if node is outside the scene tree
|
||||
// or access will happen from a node-safe thread.
|
||||
return !data.tree || is_current_thread_safe_for_nodes();
|
||||
} else {
|
||||
// Thread processing.
|
||||
return current_process_thread_group == data.process_thread_group_owner;
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool is_readable_from_caller_thread() const {
|
||||
if (current_process_thread_group == nullptr) {
|
||||
// No thread processing.
|
||||
// Only accessible if node is outside the scene tree
|
||||
// or access will happen from a node-safe thread.
|
||||
return is_current_thread_safe_for_nodes() || unlikely(!data.tree);
|
||||
} else {
|
||||
// Thread processing.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ static bool is_group_processing() { return current_process_thread_group; }
|
||||
|
||||
void set_process_thread_messages(BitField<ProcessThreadMessages> p_flags);
|
||||
BitField<ProcessThreadMessages> get_process_thread_messages() const;
|
||||
|
||||
void queue_accessibility_update();
|
||||
|
||||
virtual RID get_accessibility_element() const;
|
||||
virtual RID get_focused_accessibility_element() const;
|
||||
virtual bool accessibility_override_tree_hierarchy() const { return false; }
|
||||
|
||||
virtual PackedStringArray get_accessibility_configuration_warnings() const;
|
||||
|
||||
Node *duplicate(int p_flags = DUPLICATE_GROUPS | DUPLICATE_SIGNALS | DUPLICATE_SCRIPTS) const;
|
||||
#ifdef TOOLS_ENABLED
|
||||
Node *duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap) const;
|
||||
Node *duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const;
|
||||
void remap_node_resources(Node *p_node, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const;
|
||||
void remap_nested_resources(Ref<Resource> p_resource, const HashMap<Ref<Resource>, Ref<Resource>> &p_resource_remap) const;
|
||||
#endif
|
||||
|
||||
// used by editors, to save what has changed only
|
||||
void set_scene_instance_state(const Ref<SceneState> &p_state);
|
||||
Ref<SceneState> get_scene_instance_state() const;
|
||||
|
||||
void set_scene_inherited_state(const Ref<SceneState> &p_state);
|
||||
Ref<SceneState> get_scene_inherited_state() const;
|
||||
|
||||
void set_scene_instance_load_placeholder(bool p_enable);
|
||||
bool get_scene_instance_load_placeholder() const;
|
||||
|
||||
template <typename... VarArgs>
|
||||
Vector<Variant> make_binds(VarArgs... p_args) {
|
||||
Vector<Variant> binds = { p_args... };
|
||||
return binds;
|
||||
}
|
||||
|
||||
void replace_by(Node *p_node, bool p_keep_groups = false);
|
||||
|
||||
void set_process_mode(ProcessMode p_mode);
|
||||
ProcessMode get_process_mode() const;
|
||||
bool can_process() const;
|
||||
bool can_process_notification(int p_what) const;
|
||||
|
||||
void set_physics_interpolation_mode(PhysicsInterpolationMode p_mode);
|
||||
PhysicsInterpolationMode get_physics_interpolation_mode() const { return data.physics_interpolation_mode; }
|
||||
_FORCE_INLINE_ bool is_physics_interpolated() const { return data.physics_interpolated; }
|
||||
_FORCE_INLINE_ bool is_physics_interpolated_and_enabled() const { return SceneTree::is_fti_enabled() && is_physics_interpolated(); }
|
||||
void reset_physics_interpolation();
|
||||
|
||||
bool is_enabled() const;
|
||||
bool is_ready() const;
|
||||
|
||||
void request_ready();
|
||||
|
||||
void set_process_thread_group(ProcessThreadGroup p_mode);
|
||||
ProcessThreadGroup get_process_thread_group() const;
|
||||
|
||||
static void print_orphan_nodes();
|
||||
static TypedArray<int> get_orphan_node_ids();
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
String validate_child_name(Node *p_child);
|
||||
String prevalidate_child_name(Node *p_child, StringName p_name);
|
||||
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
static String adjust_name_casing(const String &p_name);
|
||||
|
||||
void queue_free();
|
||||
|
||||
//hacks for speed
|
||||
static void init_node_hrcr();
|
||||
|
||||
void set_import_path(const NodePath &p_import_path); //path used when imported, used by scene editors to keep tracking
|
||||
NodePath get_import_path() const;
|
||||
|
||||
bool is_owned_by_parent() const;
|
||||
|
||||
void clear_internal_tree_resource_paths();
|
||||
|
||||
_FORCE_INLINE_ Viewport *get_viewport() const { return data.viewport; }
|
||||
|
||||
virtual PackedStringArray get_configuration_warnings() const;
|
||||
|
||||
void update_configuration_warnings();
|
||||
|
||||
void set_display_folded(bool p_folded);
|
||||
bool is_displayed_folded() const;
|
||||
|
||||
/* NETWORK */
|
||||
|
||||
virtual void set_multiplayer_authority(int p_peer_id, bool p_recursive = true);
|
||||
int get_multiplayer_authority() const;
|
||||
bool is_multiplayer_authority() const;
|
||||
|
||||
void rpc_config(const StringName &p_method, const Variant &p_config); // config a local method for RPC
|
||||
const Variant get_node_rpc_config() const;
|
||||
|
||||
template <typename... VarArgs>
|
||||
Error rpc(const StringName &p_method, VarArgs... p_args);
|
||||
|
||||
template <typename... VarArgs>
|
||||
Error rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args);
|
||||
|
||||
Error rpcp(int p_peer_id, const StringName &p_method, const Variant **p_arg, int p_argcount);
|
||||
|
||||
Ref<MultiplayerAPI> get_multiplayer() const;
|
||||
|
||||
/* INTERNATIONALIZATION */
|
||||
|
||||
void set_auto_translate_mode(AutoTranslateMode p_mode);
|
||||
AutoTranslateMode get_auto_translate_mode() const;
|
||||
bool can_auto_translate() const;
|
||||
|
||||
virtual StringName get_translation_domain() const override;
|
||||
virtual void set_translation_domain(const StringName &p_domain) override;
|
||||
void set_translation_domain_inherited();
|
||||
|
||||
_FORCE_INLINE_ String atr(const String &p_message, const StringName &p_context = "") const { return can_auto_translate() ? tr(p_message, p_context) : p_message; }
|
||||
_FORCE_INLINE_ String atr_n(const String &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const {
|
||||
if (can_auto_translate()) {
|
||||
return tr_n(p_message, p_message_plural, p_n, p_context);
|
||||
}
|
||||
return p_n == 1 ? p_message : String(p_message_plural);
|
||||
}
|
||||
|
||||
/* THREADING */
|
||||
|
||||
void call_deferred_thread_groupp(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
|
||||
template <typename... VarArgs>
|
||||
void call_deferred_thread_group(const StringName &p_method, VarArgs... p_args) {
|
||||
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
|
||||
const Variant *argptrs[sizeof...(p_args) + 1];
|
||||
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
|
||||
argptrs[i] = &args[i];
|
||||
}
|
||||
call_deferred_thread_groupp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
|
||||
}
|
||||
void set_deferred_thread_group(const StringName &p_property, const Variant &p_value);
|
||||
void notify_deferred_thread_group(int p_notification);
|
||||
|
||||
void call_thread_safep(const StringName &p_method, const Variant **p_args, int p_argcount, bool p_show_error = false);
|
||||
template <typename... VarArgs>
|
||||
void call_thread_safe(const StringName &p_method, VarArgs... p_args) {
|
||||
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
|
||||
const Variant *argptrs[sizeof...(p_args) + 1];
|
||||
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
|
||||
argptrs[i] = &args[i];
|
||||
}
|
||||
call_deferred_thread_groupp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
|
||||
}
|
||||
void set_thread_safe(const StringName &p_property, const Variant &p_value);
|
||||
void notify_thread_safe(int p_notification);
|
||||
|
||||
// These inherited functions need proper multithread locking when overridden in Node.
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
virtual void set_script(const Variant &p_script) override;
|
||||
virtual Variant get_script() const override;
|
||||
|
||||
virtual bool has_meta(const StringName &p_name) const override;
|
||||
virtual void set_meta(const StringName &p_name, const Variant &p_value) override;
|
||||
virtual void remove_meta(const StringName &p_name) override;
|
||||
virtual Variant get_meta(const StringName &p_name, const Variant &p_default = Variant()) const override;
|
||||
virtual void get_meta_list(List<StringName> *p_list) const override;
|
||||
|
||||
virtual Error emit_signalp(const StringName &p_name, const Variant **p_args, int p_argcount) override;
|
||||
virtual bool has_signal(const StringName &p_name) const override;
|
||||
virtual void get_signal_list(List<MethodInfo> *p_signals) const override;
|
||||
virtual void get_signal_connection_list(const StringName &p_signal, List<Connection> *p_connections) const override;
|
||||
virtual void get_all_signal_connections(List<Connection> *p_connections) const override;
|
||||
virtual int get_persistent_signal_connection_count() const override;
|
||||
virtual void get_signals_connected_to_this(List<Connection> *p_connections) const override;
|
||||
|
||||
virtual Error connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags = 0) override;
|
||||
virtual void disconnect(const StringName &p_signal, const Callable &p_callable) override;
|
||||
virtual bool is_connected(const StringName &p_signal, const Callable &p_callable) const override;
|
||||
virtual bool has_connections(const StringName &p_signal) const override;
|
||||
#endif
|
||||
Node();
|
||||
~Node();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(Node::DuplicateFlags);
|
||||
VARIANT_ENUM_CAST(Node::ProcessMode);
|
||||
VARIANT_ENUM_CAST(Node::ProcessThreadGroup);
|
||||
VARIANT_BITFIELD_CAST(Node::ProcessThreadMessages);
|
||||
VARIANT_ENUM_CAST(Node::InternalMode);
|
||||
VARIANT_ENUM_CAST(Node::PhysicsInterpolationMode);
|
||||
VARIANT_ENUM_CAST(Node::AutoTranslateMode);
|
||||
|
||||
typedef HashSet<Node *, Node::Comparator> NodeSet;
|
||||
|
||||
// Template definitions must be in the header so they are always fully initialized before their usage.
|
||||
// See this StackOverflow question for more information: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file
|
||||
|
||||
template <typename... VarArgs>
|
||||
Error Node::rpc(const StringName &p_method, VarArgs... p_args) {
|
||||
return rpc_id(0, p_method, p_args...);
|
||||
}
|
||||
|
||||
template <typename... VarArgs>
|
||||
Error Node::rpc_id(int p_peer_id, const StringName &p_method, VarArgs... p_args) {
|
||||
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
|
||||
const Variant *argptrs[sizeof...(p_args) + 1];
|
||||
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
|
||||
argptrs[i] = &args[i];
|
||||
}
|
||||
return rpcp(p_peer_id, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
#define ERR_THREAD_GUARD ERR_FAIL_COND_MSG(!is_accessible_from_caller_thread(), vformat("Caller thread can't call this function in this node (%s). Use call_deferred() or call_thread_group() instead.", get_description()));
|
||||
#define ERR_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(!is_accessible_from_caller_thread(), (m_ret), vformat("Caller thread can't call this function in this node (%s). Use call_deferred() or call_thread_group() instead.", get_description()));
|
||||
#define ERR_MAIN_THREAD_GUARD ERR_FAIL_COND_MSG(is_inside_tree() && !is_current_thread_safe_for_nodes(), vformat("This function in this node (%s) can only be accessed from the main thread. Use call_deferred() instead.", get_description()));
|
||||
#define ERR_MAIN_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(is_inside_tree() && !is_current_thread_safe_for_nodes(), (m_ret), vformat("This function in this node (%s) can only be accessed from the main thread. Use call_deferred() instead.", get_description()));
|
||||
#define ERR_READ_THREAD_GUARD ERR_FAIL_COND_MSG(!is_readable_from_caller_thread(), vformat("This function in this node (%s) can only be accessed from either the main thread or a thread group. Use call_deferred() instead.", get_description()));
|
||||
#define ERR_READ_THREAD_GUARD_V(m_ret) ERR_FAIL_COND_V_MSG(!is_readable_from_caller_thread(), (m_ret), vformat("This function in this node (%s) can only be accessed from either the main thread or a thread group. Use call_deferred() instead.", get_description()));
|
||||
#else
|
||||
#define ERR_THREAD_GUARD
|
||||
#define ERR_THREAD_GUARD_V(m_ret)
|
||||
#define ERR_MAIN_THREAD_GUARD
|
||||
#define ERR_MAIN_THREAD_GUARD_V(m_ret)
|
||||
#define ERR_READ_THREAD_GUARD
|
||||
#define ERR_READ_THREAD_GUARD_V(m_ret)
|
||||
#endif
|
||||
|
||||
// Add these macro to your class's 'get_configuration_warnings' function to have warnings show up in the scene tree inspector.
|
||||
#define DEPRECATED_NODE_WARNING warnings.push_back(RTR("This node is marked as deprecated and will be removed in future versions.\nPlease check the Godot documentation for information about migration."));
|
||||
#define EXPERIMENTAL_NODE_WARNING warnings.push_back(RTR("This node is marked as experimental and may be subject to removal or major changes in future versions."));
|
152
scene/main/resource_preloader.cpp
Normal file
152
scene/main/resource_preloader.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/**************************************************************************/
|
||||
/* resource_preloader.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "resource_preloader.h"
|
||||
#include "core/templates/rb_set.h"
|
||||
void ResourcePreloader::_set_resources(const Array &p_data) {
|
||||
resources.clear();
|
||||
|
||||
ERR_FAIL_COND(p_data.size() != 2);
|
||||
Vector<String> names = p_data[0];
|
||||
Array resdata = p_data[1];
|
||||
|
||||
ERR_FAIL_COND(names.size() != resdata.size());
|
||||
|
||||
for (int i = 0; i < resdata.size(); i++) {
|
||||
Ref<Resource> resource = resdata[i];
|
||||
ERR_CONTINUE(resource.is_null());
|
||||
resources[names[i]] = resource;
|
||||
|
||||
//add_resource(names[i],resource);
|
||||
}
|
||||
}
|
||||
|
||||
Array ResourcePreloader::_get_resources() const {
|
||||
Vector<String> names;
|
||||
Array arr;
|
||||
arr.resize(resources.size());
|
||||
names.resize(resources.size());
|
||||
|
||||
RBSet<String> sorted_names;
|
||||
|
||||
for (const KeyValue<StringName, Ref<Resource>> &E : resources) {
|
||||
sorted_names.insert(E.key);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (const String &E : sorted_names) {
|
||||
names.set(i, E);
|
||||
arr[i] = resources[E];
|
||||
i++;
|
||||
}
|
||||
|
||||
Array res = { names, arr };
|
||||
return res;
|
||||
}
|
||||
|
||||
void ResourcePreloader::add_resource(const StringName &p_name, const Ref<Resource> &p_resource) {
|
||||
ERR_FAIL_COND(p_resource.is_null());
|
||||
if (resources.has(p_name)) {
|
||||
StringName new_name;
|
||||
int idx = 2;
|
||||
|
||||
while (true) {
|
||||
new_name = p_name.operator String() + " " + itos(idx);
|
||||
if (resources.has(new_name)) {
|
||||
idx++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
add_resource(new_name, p_resource);
|
||||
} else {
|
||||
resources[p_name] = p_resource;
|
||||
}
|
||||
}
|
||||
|
||||
void ResourcePreloader::remove_resource(const StringName &p_name) {
|
||||
ERR_FAIL_COND(!resources.has(p_name));
|
||||
resources.erase(p_name);
|
||||
}
|
||||
|
||||
void ResourcePreloader::rename_resource(const StringName &p_from_name, const StringName &p_to_name) {
|
||||
ERR_FAIL_COND(!resources.has(p_from_name));
|
||||
|
||||
Ref<Resource> res = resources[p_from_name];
|
||||
|
||||
resources.erase(p_from_name);
|
||||
add_resource(p_to_name, res);
|
||||
}
|
||||
|
||||
bool ResourcePreloader::has_resource(const StringName &p_name) const {
|
||||
return resources.has(p_name);
|
||||
}
|
||||
|
||||
Ref<Resource> ResourcePreloader::get_resource(const StringName &p_name) const {
|
||||
ERR_FAIL_COND_V(!resources.has(p_name), Ref<Resource>());
|
||||
return resources[p_name];
|
||||
}
|
||||
|
||||
Vector<String> ResourcePreloader::_get_resource_list() const {
|
||||
Vector<String> res;
|
||||
res.resize(resources.size());
|
||||
int i = 0;
|
||||
for (const KeyValue<StringName, Ref<Resource>> &E : resources) {
|
||||
res.set(i, E.key);
|
||||
i++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void ResourcePreloader::get_resource_list(List<StringName> *p_list) {
|
||||
for (const KeyValue<StringName, Ref<Resource>> &E : resources) {
|
||||
p_list->push_back(E.key);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourcePreloader::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_set_resources", "resources"), &ResourcePreloader::_set_resources);
|
||||
ClassDB::bind_method(D_METHOD("_get_resources"), &ResourcePreloader::_get_resources);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("add_resource", "name", "resource"), &ResourcePreloader::add_resource);
|
||||
ClassDB::bind_method(D_METHOD("remove_resource", "name"), &ResourcePreloader::remove_resource);
|
||||
ClassDB::bind_method(D_METHOD("rename_resource", "name", "newname"), &ResourcePreloader::rename_resource);
|
||||
ClassDB::bind_method(D_METHOD("has_resource", "name"), &ResourcePreloader::has_resource);
|
||||
ClassDB::bind_method(D_METHOD("get_resource", "name"), &ResourcePreloader::get_resource);
|
||||
ClassDB::bind_method(D_METHOD("get_resource_list"), &ResourcePreloader::_get_resource_list);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "resources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_resources", "_get_resources");
|
||||
}
|
||||
|
||||
ResourcePreloader::ResourcePreloader() {
|
||||
}
|
57
scene/main/resource_preloader.h
Normal file
57
scene/main/resource_preloader.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**************************************************************************/
|
||||
/* resource_preloader.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class ResourcePreloader : public Node {
|
||||
GDCLASS(ResourcePreloader, Node);
|
||||
|
||||
HashMap<StringName, Ref<Resource>> resources;
|
||||
|
||||
void _set_resources(const Array &p_data);
|
||||
Array _get_resources() const;
|
||||
Vector<String> _get_resource_list() const;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void add_resource(const StringName &p_name, const Ref<Resource> &p_resource);
|
||||
void remove_resource(const StringName &p_name);
|
||||
void rename_resource(const StringName &p_from_name, const StringName &p_to_name);
|
||||
bool has_resource(const StringName &p_name) const;
|
||||
Ref<Resource> get_resource(const StringName &p_name) const;
|
||||
|
||||
void get_resource_list(List<StringName> *p_list);
|
||||
|
||||
ResourcePreloader();
|
||||
};
|
2223
scene/main/scene_tree.cpp
Normal file
2223
scene/main/scene_tree.cpp
Normal file
File diff suppressed because it is too large
Load Diff
473
scene/main/scene_tree.h
Normal file
473
scene/main/scene_tree.h
Normal file
@@ -0,0 +1,473 @@
|
||||
/**************************************************************************/
|
||||
/* scene_tree.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/main_loop.h"
|
||||
#include "core/os/thread_safe.h"
|
||||
#include "core/templates/paged_allocator.h"
|
||||
#include "core/templates/self_list.h"
|
||||
#include "scene/main/scene_tree_fti.h"
|
||||
#include "scene/resources/mesh.h"
|
||||
|
||||
#undef Window
|
||||
|
||||
class PackedScene;
|
||||
class Node;
|
||||
#ifndef _3D_DISABLED
|
||||
class Node3D;
|
||||
#endif
|
||||
class Window;
|
||||
class Material;
|
||||
class Mesh;
|
||||
class MultiplayerAPI;
|
||||
class SceneDebugger;
|
||||
class Tween;
|
||||
class Viewport;
|
||||
|
||||
class SceneTreeTimer : public RefCounted {
|
||||
GDCLASS(SceneTreeTimer, RefCounted);
|
||||
|
||||
double time_left = 0.0;
|
||||
bool process_always = true;
|
||||
bool process_in_physics = false;
|
||||
bool ignore_time_scale = false;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_time_left(double p_time);
|
||||
double get_time_left() const;
|
||||
|
||||
void set_process_always(bool p_process_always);
|
||||
bool is_process_always();
|
||||
|
||||
void set_process_in_physics(bool p_process_in_physics);
|
||||
bool is_process_in_physics();
|
||||
|
||||
void set_ignore_time_scale(bool p_ignore);
|
||||
bool is_ignoring_time_scale();
|
||||
|
||||
void release_connections();
|
||||
};
|
||||
|
||||
class SceneTree : public MainLoop {
|
||||
_THREAD_SAFE_CLASS_
|
||||
|
||||
GDCLASS(SceneTree, MainLoop);
|
||||
|
||||
public:
|
||||
typedef void (*IdleCallback)();
|
||||
|
||||
private:
|
||||
CallQueue::Allocator *process_group_call_queue_allocator = nullptr;
|
||||
|
||||
struct ProcessGroup {
|
||||
CallQueue call_queue;
|
||||
Vector<Node *> nodes;
|
||||
Vector<Node *> physics_nodes;
|
||||
bool node_order_dirty = true;
|
||||
bool physics_node_order_dirty = true;
|
||||
bool removed = false;
|
||||
Node *owner = nullptr;
|
||||
uint64_t last_pass = 0;
|
||||
};
|
||||
|
||||
struct ProcessGroupSort {
|
||||
_FORCE_INLINE_ bool operator()(const ProcessGroup *p_left, const ProcessGroup *p_right) const;
|
||||
};
|
||||
|
||||
PagedAllocator<ProcessGroup, true> group_allocator; // Allocate groups on pages, to enhance cache usage.
|
||||
|
||||
LocalVector<ProcessGroup *> process_groups;
|
||||
bool process_groups_dirty = true;
|
||||
LocalVector<ProcessGroup *> local_process_group_cache; // Used when processing to group what needs to
|
||||
uint64_t process_last_pass = 1;
|
||||
|
||||
ProcessGroup default_process_group;
|
||||
|
||||
bool node_threading_disabled = false;
|
||||
|
||||
struct Group {
|
||||
Vector<Node *> nodes;
|
||||
bool changed = false;
|
||||
};
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
struct ClientPhysicsInterpolation {
|
||||
SelfList<Node3D>::List _node_3d_list;
|
||||
void physics_process();
|
||||
} _client_physics_interpolation;
|
||||
#endif
|
||||
|
||||
Window *root = nullptr;
|
||||
|
||||
double physics_process_time = 0.0;
|
||||
double process_time = 0.0;
|
||||
bool accept_quit = true;
|
||||
bool quit_on_go_back = true;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool debug_collisions_hint = false;
|
||||
bool debug_paths_hint = false;
|
||||
bool debug_navigation_hint = false;
|
||||
#endif
|
||||
bool paused = false;
|
||||
bool suspended = false;
|
||||
|
||||
HashMap<StringName, Group> group_map;
|
||||
bool _quit = false;
|
||||
|
||||
// Static so we can get directly instead of via SceneTree pointer.
|
||||
static bool _physics_interpolation_enabled;
|
||||
|
||||
// Note that physics interpolation is hard coded to OFF in the editor,
|
||||
// therefore we have a second bool to enable e.g. configuration warnings
|
||||
// to only take effect when the project is using physics interpolation.
|
||||
static bool _physics_interpolation_enabled_in_project;
|
||||
|
||||
SceneTreeFTI scene_tree_fti;
|
||||
|
||||
StringName tree_changed_name = "tree_changed";
|
||||
StringName node_added_name = "node_added";
|
||||
StringName node_removed_name = "node_removed";
|
||||
StringName node_renamed_name = "node_renamed";
|
||||
|
||||
int64_t current_frame = 0;
|
||||
int nodes_in_tree_count = 0;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
Node *edited_scene_root = nullptr;
|
||||
#endif
|
||||
struct UGCall {
|
||||
StringName group;
|
||||
StringName call;
|
||||
|
||||
static uint32_t hash(const UGCall &p_val) {
|
||||
return p_val.group.hash() ^ p_val.call.hash();
|
||||
}
|
||||
bool operator==(const UGCall &p_with) const { return group == p_with.group && call == p_with.call; }
|
||||
bool operator<(const UGCall &p_with) const { return group == p_with.group ? call < p_with.call : group < p_with.group; }
|
||||
};
|
||||
|
||||
// Safety for when a node is deleted while a group is being called.
|
||||
|
||||
int nodes_removed_on_group_call_lock = 0;
|
||||
HashSet<Node *> nodes_removed_on_group_call; // Skip erased nodes.
|
||||
|
||||
List<ObjectID> delete_queue;
|
||||
|
||||
uint64_t accessibility_upd_per_sec = 0;
|
||||
bool accessibility_force_update = true;
|
||||
HashSet<ObjectID> accessibility_change_queue;
|
||||
uint64_t accessibility_last_update = 0;
|
||||
|
||||
HashMap<UGCall, Vector<Variant>, UGCall> unique_group_calls;
|
||||
bool ugc_locked = false;
|
||||
void _flush_ugc();
|
||||
|
||||
_FORCE_INLINE_ void _update_group_order(Group &g);
|
||||
|
||||
TypedArray<Node> _get_nodes_in_group(const StringName &p_group);
|
||||
|
||||
Node *current_scene = nullptr;
|
||||
ObjectID prev_scene_id;
|
||||
ObjectID pending_new_scene_id;
|
||||
|
||||
Color debug_collisions_color;
|
||||
Color debug_collision_contact_color;
|
||||
Color debug_paths_color;
|
||||
float debug_paths_width = 1.0f;
|
||||
Ref<ArrayMesh> debug_contact_mesh;
|
||||
Ref<Material> debug_paths_material;
|
||||
Ref<Material> collision_material;
|
||||
int collision_debug_contacts;
|
||||
|
||||
void _flush_scene_change();
|
||||
|
||||
List<Ref<SceneTreeTimer>> timers;
|
||||
List<Ref<Tween>> tweens;
|
||||
|
||||
///network///
|
||||
|
||||
Ref<MultiplayerAPI> multiplayer;
|
||||
HashMap<NodePath, Ref<MultiplayerAPI>> custom_multiplayers;
|
||||
bool multiplayer_poll = true;
|
||||
|
||||
static SceneTree *singleton;
|
||||
friend class Node;
|
||||
|
||||
void tree_changed();
|
||||
void node_added(Node *p_node);
|
||||
void node_removed(Node *p_node);
|
||||
void node_renamed(Node *p_node);
|
||||
void process_timers(double p_delta, bool p_physics_frame);
|
||||
void process_tweens(double p_delta, bool p_physics_frame);
|
||||
|
||||
Group *add_to_group(const StringName &p_group, Node *p_node);
|
||||
void remove_from_group(const StringName &p_group, Node *p_node);
|
||||
|
||||
void _process_group(ProcessGroup *p_group, bool p_physics);
|
||||
void _process_groups_thread(uint32_t p_index, bool p_physics);
|
||||
void _process(bool p_physics);
|
||||
|
||||
void _remove_process_group(Node *p_node);
|
||||
void _add_process_group(Node *p_node);
|
||||
void _remove_node_from_process_group(Node *p_node, Node *p_owner);
|
||||
void _add_node_to_process_group(Node *p_node, Node *p_owner);
|
||||
|
||||
void _call_group_flags(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
void _call_group(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
|
||||
|
||||
void _flush_delete_queue();
|
||||
// Optimization.
|
||||
friend class CanvasItem;
|
||||
friend class Node3D;
|
||||
friend class Viewport;
|
||||
|
||||
SelfList<Node>::List xform_change_list;
|
||||
|
||||
#ifdef DEBUG_ENABLED // No live editor in release build.
|
||||
friend class LiveEditor;
|
||||
#endif
|
||||
|
||||
enum {
|
||||
MAX_IDLE_CALLBACKS = 256
|
||||
};
|
||||
|
||||
static IdleCallback idle_callbacks[MAX_IDLE_CALLBACKS];
|
||||
static int idle_callback_count;
|
||||
void _call_idle_callbacks();
|
||||
|
||||
void _main_window_focus_in();
|
||||
void _main_window_close();
|
||||
void _main_window_go_back();
|
||||
|
||||
enum CallInputType {
|
||||
CALL_INPUT_TYPE_INPUT,
|
||||
CALL_INPUT_TYPE_SHORTCUT_INPUT,
|
||||
CALL_INPUT_TYPE_UNHANDLED_INPUT,
|
||||
CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT,
|
||||
};
|
||||
|
||||
//used by viewport
|
||||
void _call_input_pause(const StringName &p_group, CallInputType p_call_type, const Ref<InputEvent> &p_input, Viewport *p_viewport);
|
||||
|
||||
protected:
|
||||
void _notification(int p_notification);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
enum {
|
||||
NOTIFICATION_TRANSFORM_CHANGED = 2000
|
||||
};
|
||||
|
||||
enum GroupCallFlags {
|
||||
GROUP_CALL_DEFAULT = 0,
|
||||
GROUP_CALL_REVERSE = 1,
|
||||
GROUP_CALL_DEFERRED = 2,
|
||||
GROUP_CALL_UNIQUE = 4,
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ Window *get_root() const { return root; }
|
||||
|
||||
void call_group_flagsp(uint32_t p_call_flags, const StringName &p_group, const StringName &p_function, const Variant **p_args, int p_argcount);
|
||||
void notify_group_flags(uint32_t p_call_flags, const StringName &p_group, int p_notification);
|
||||
void set_group_flags(uint32_t p_call_flags, const StringName &p_group, const String &p_name, const Variant &p_value);
|
||||
|
||||
// `notify_group()` is immediate by default since Godot 4.0.
|
||||
void notify_group(const StringName &p_group, int p_notification);
|
||||
// `set_group()` is immediate by default since Godot 4.0.
|
||||
void set_group(const StringName &p_group, const String &p_name, const Variant &p_value);
|
||||
|
||||
template <typename... VarArgs>
|
||||
// `call_group()` is immediate by default since Godot 4.0.
|
||||
void call_group(const StringName &p_group, const StringName &p_function, VarArgs... p_args) {
|
||||
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
|
||||
const Variant *argptrs[sizeof...(p_args) + 1];
|
||||
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
|
||||
argptrs[i] = &args[i];
|
||||
}
|
||||
call_group_flagsp(GROUP_CALL_DEFAULT, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
|
||||
}
|
||||
|
||||
template <typename... VarArgs>
|
||||
void call_group_flags(uint32_t p_flags, const StringName &p_group, const StringName &p_function, VarArgs... p_args) {
|
||||
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
|
||||
const Variant *argptrs[sizeof...(p_args) + 1];
|
||||
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
|
||||
argptrs[i] = &args[i];
|
||||
}
|
||||
call_group_flagsp(p_flags, p_group, p_function, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
|
||||
}
|
||||
|
||||
void flush_transform_notifications();
|
||||
|
||||
bool is_accessibility_enabled() const;
|
||||
bool is_accessibility_supported() const;
|
||||
void _accessibility_force_update();
|
||||
void _accessibility_notify_change(const Node *p_node, bool p_remove = false);
|
||||
void _flush_accessibility_changes();
|
||||
void _process_accessibility_changes(DisplayServer::WindowID p_window_id);
|
||||
|
||||
virtual void initialize() override;
|
||||
|
||||
virtual void iteration_prepare() override;
|
||||
|
||||
virtual bool physics_process(double p_time) override;
|
||||
virtual void iteration_end() override;
|
||||
virtual bool process(double p_time) override;
|
||||
|
||||
virtual void finalize() override;
|
||||
|
||||
bool is_auto_accept_quit() const;
|
||||
void set_auto_accept_quit(bool p_enable);
|
||||
|
||||
bool is_quit_on_go_back() const;
|
||||
void set_quit_on_go_back(bool p_enable);
|
||||
|
||||
void quit(int p_exit_code = EXIT_SUCCESS);
|
||||
|
||||
_FORCE_INLINE_ double get_physics_process_time() const { return physics_process_time; }
|
||||
_FORCE_INLINE_ double get_process_time() const { return process_time; }
|
||||
|
||||
void set_pause(bool p_enabled);
|
||||
bool is_paused() const;
|
||||
void set_suspend(bool p_enabled);
|
||||
bool is_suspended() const;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
void set_debug_collisions_hint(bool p_enabled);
|
||||
bool is_debugging_collisions_hint() const;
|
||||
|
||||
void set_debug_paths_hint(bool p_enabled);
|
||||
bool is_debugging_paths_hint() const;
|
||||
|
||||
void set_debug_navigation_hint(bool p_enabled);
|
||||
bool is_debugging_navigation_hint() const;
|
||||
#else
|
||||
void set_debug_collisions_hint(bool p_enabled) {}
|
||||
bool is_debugging_collisions_hint() const { return false; }
|
||||
|
||||
void set_debug_paths_hint(bool p_enabled) {}
|
||||
bool is_debugging_paths_hint() const { return false; }
|
||||
|
||||
void set_debug_navigation_hint(bool p_enabled) {}
|
||||
bool is_debugging_navigation_hint() const { return false; }
|
||||
#endif
|
||||
|
||||
void set_debug_collisions_color(const Color &p_color);
|
||||
Color get_debug_collisions_color() const;
|
||||
|
||||
void set_debug_collision_contact_color(const Color &p_color);
|
||||
Color get_debug_collision_contact_color() const;
|
||||
|
||||
void set_debug_paths_color(const Color &p_color);
|
||||
Color get_debug_paths_color() const;
|
||||
|
||||
void set_debug_paths_width(float p_width);
|
||||
float get_debug_paths_width() const;
|
||||
|
||||
Ref<Material> get_debug_paths_material();
|
||||
Ref<Material> get_debug_collision_material();
|
||||
Ref<ArrayMesh> get_debug_contact_mesh();
|
||||
|
||||
int get_collision_debug_contact_count() { return collision_debug_contacts; }
|
||||
|
||||
int64_t get_frame() const;
|
||||
|
||||
int get_node_count() const;
|
||||
|
||||
void queue_delete(Object *p_object);
|
||||
|
||||
void get_nodes_in_group(const StringName &p_group, List<Node *> *p_list);
|
||||
Node *get_first_node_in_group(const StringName &p_group);
|
||||
bool has_group(const StringName &p_identifier) const;
|
||||
int get_node_count_in_group(const StringName &p_group) const;
|
||||
|
||||
//void change_scene(const String& p_path);
|
||||
//Node *get_loaded_scene();
|
||||
|
||||
void set_edited_scene_root(Node *p_node);
|
||||
Node *get_edited_scene_root() const;
|
||||
|
||||
void set_current_scene(Node *p_scene);
|
||||
Node *get_current_scene() const;
|
||||
Error change_scene_to_file(const String &p_path);
|
||||
Error change_scene_to_packed(const Ref<PackedScene> &p_scene);
|
||||
Error reload_current_scene();
|
||||
void unload_current_scene();
|
||||
|
||||
Ref<SceneTreeTimer> create_timer(double p_delay_sec, bool p_process_always = true, bool p_process_in_physics = false, bool p_ignore_time_scale = false);
|
||||
Ref<Tween> create_tween();
|
||||
void remove_tween(const Ref<Tween> &p_tween);
|
||||
TypedArray<Tween> get_processed_tweens();
|
||||
|
||||
//used by Main::start, don't use otherwise
|
||||
void add_current_scene(Node *p_current);
|
||||
|
||||
static SceneTree *get_singleton() { return singleton; }
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
|
||||
#endif
|
||||
|
||||
//network API
|
||||
|
||||
Ref<MultiplayerAPI> get_multiplayer(const NodePath &p_for_path = NodePath()) const;
|
||||
void set_multiplayer(Ref<MultiplayerAPI> p_multiplayer, const NodePath &p_root_path = NodePath());
|
||||
void set_multiplayer_poll_enabled(bool p_enabled);
|
||||
bool is_multiplayer_poll_enabled() const;
|
||||
|
||||
static void add_idle_callback(IdleCallback p_callback);
|
||||
|
||||
void set_disable_node_threading(bool p_disable);
|
||||
//default texture settings
|
||||
|
||||
void set_physics_interpolation_enabled(bool p_enabled);
|
||||
bool is_physics_interpolation_enabled() const { return _physics_interpolation_enabled; }
|
||||
|
||||
// Different name to disambiguate fast static versions from the user bound versions.
|
||||
static bool is_fti_enabled() { return _physics_interpolation_enabled; }
|
||||
static bool is_fti_enabled_in_project() { return _physics_interpolation_enabled_in_project; }
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
void client_physics_interpolation_add_node_3d(SelfList<Node3D> *p_elem);
|
||||
void client_physics_interpolation_remove_node_3d(SelfList<Node3D> *p_elem);
|
||||
#endif
|
||||
|
||||
SceneTreeFTI &get_scene_tree_fti() { return scene_tree_fti; }
|
||||
|
||||
SceneTree();
|
||||
~SceneTree();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(SceneTree::GroupCallFlags);
|
818
scene/main/scene_tree_fti.cpp
Normal file
818
scene/main/scene_tree_fti.cpp
Normal file
@@ -0,0 +1,818 @@
|
||||
/**************************************************************************/
|
||||
/* scene_tree_fti.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
|
||||
#include "scene_tree_fti.h"
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/math/transform_interpolator.h"
|
||||
#include "core/os/os.h"
|
||||
#include "scene/3d/visual_instance_3d.h"
|
||||
|
||||
#ifdef GODOT_SCENE_TREE_FTI_VERIFY
|
||||
#include "scene_tree_fti_tests.h"
|
||||
#endif
|
||||
|
||||
#ifdef DEV_ENABLED
|
||||
|
||||
// Uncomment this to enable some slow extra DEV_ENABLED
|
||||
// checks to ensure there aren't more than one object added to the lists.
|
||||
// #define GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
|
||||
// Uncomment this to regularly print the tree that is being interpolated.
|
||||
// #define GODOT_SCENE_TREE_FTI_PRINT_TREE
|
||||
|
||||
#endif
|
||||
|
||||
void SceneTreeFTI::_reset_node3d_flags(Node3D &r_node) {
|
||||
r_node.data.fti_on_tick_xform_list = false;
|
||||
r_node.data.fti_on_tick_property_list = false;
|
||||
r_node.data.fti_on_frame_xform_list = false;
|
||||
r_node.data.fti_on_frame_property_list = false;
|
||||
r_node.data.fti_global_xform_interp_set = false;
|
||||
r_node.data.fti_frame_xform_force_update = false;
|
||||
r_node.data.fti_processed = false;
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_reset_flags(Node *p_node) {
|
||||
Node3D *s = Object::cast_to<Node3D>(p_node);
|
||||
|
||||
if (s) {
|
||||
_reset_node3d_flags(*s);
|
||||
|
||||
// In most cases the later NOTIFICATION_RESET_PHYSICS_INTERPOLATION
|
||||
// will reset this, but this should help cover hidden nodes.
|
||||
s->data.local_transform_prev = s->get_transform();
|
||||
}
|
||||
|
||||
for (int n = 0; n < p_node->get_child_count(); n++) {
|
||||
_reset_flags(p_node->get_child(n));
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTI::set_enabled(Node *p_root, bool p_enabled) {
|
||||
if (data.enabled == p_enabled) {
|
||||
return;
|
||||
}
|
||||
MutexLock(data.mutex);
|
||||
|
||||
data.tick_xform_list[0].clear();
|
||||
data.tick_xform_list[1].clear();
|
||||
|
||||
data.frame_xform_list.clear();
|
||||
data.frame_xform_list_forced.clear();
|
||||
|
||||
data.tick_property_list[0].clear();
|
||||
data.tick_property_list[1].clear();
|
||||
|
||||
data.frame_property_list.clear();
|
||||
data.request_reset_list.clear();
|
||||
|
||||
_clear_depth_lists();
|
||||
|
||||
// Node3D flags must be reset.
|
||||
if (p_root) {
|
||||
_reset_flags(p_root);
|
||||
}
|
||||
|
||||
data.enabled = p_enabled;
|
||||
}
|
||||
|
||||
void SceneTreeFTI::tick_update() {
|
||||
if (!data.enabled) {
|
||||
return;
|
||||
}
|
||||
MutexLock(data.mutex);
|
||||
|
||||
_update_request_resets();
|
||||
|
||||
uint32_t curr_mirror = data.mirror;
|
||||
uint32_t prev_mirror = curr_mirror ? 0 : 1;
|
||||
|
||||
LocalVector<Node3D *> &curr = data.tick_xform_list[curr_mirror];
|
||||
LocalVector<Node3D *> &prev = data.tick_xform_list[prev_mirror];
|
||||
|
||||
// First detect on the previous list but not on this tick list.
|
||||
for (uint32_t n = 0; n < prev.size(); n++) {
|
||||
Node3D *s = prev[n];
|
||||
if (!s->data.fti_on_tick_xform_list) {
|
||||
// Needs a reset so jittering will stop.
|
||||
s->fti_pump_xform();
|
||||
|
||||
// Optimization - detect whether we have rested at identity xform.
|
||||
s->data.fti_is_identity_xform = s->data.local_transform == Transform3D();
|
||||
|
||||
// This may not get updated so set it to the same as global xform.
|
||||
// TODO: double check this is the best value.
|
||||
s->data.global_transform_interpolated = s->get_global_transform();
|
||||
|
||||
// Remove from interpolation list.
|
||||
if (s->data.fti_on_frame_xform_list) {
|
||||
_node_remove_from_frame_list(*s, false);
|
||||
}
|
||||
|
||||
// Ensure that the node gets at least ONE further
|
||||
// update in the resting position in the next frame update.
|
||||
if (!s->data.fti_frame_xform_force_update) {
|
||||
_node_add_to_frame_list(*s, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LocalVector<Node3D *> &curr_prop = data.tick_property_list[curr_mirror];
|
||||
LocalVector<Node3D *> &prev_prop = data.tick_property_list[prev_mirror];
|
||||
|
||||
// Detect on the previous property list but not on this tick list.
|
||||
for (uint32_t n = 0; n < prev_prop.size(); n++) {
|
||||
Node3D *s = prev_prop[n];
|
||||
|
||||
if (!s->data.fti_on_tick_property_list) {
|
||||
// Needs a reset so jittering will stop.
|
||||
s->fti_pump_xform();
|
||||
|
||||
// Ensure the servers are up to date with the final resting value.
|
||||
s->fti_update_servers_property();
|
||||
|
||||
// Remove from interpolation list.
|
||||
if (s->data.fti_on_frame_property_list) {
|
||||
s->data.fti_on_frame_property_list = false;
|
||||
data.frame_property_list.erase_unordered(s);
|
||||
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
DEV_CHECK_ONCE(data.frame_property_list.find(s) == -1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pump all on the property list that are NOT on the tick list.
|
||||
for (uint32_t n = 0; n < curr_prop.size(); n++) {
|
||||
Node3D *s = curr_prop[n];
|
||||
|
||||
// Reset, needs to be marked each tick.
|
||||
s->data.fti_on_tick_property_list = false;
|
||||
s->fti_pump_property();
|
||||
}
|
||||
|
||||
// Now pump all on the current list.
|
||||
for (uint32_t n = 0; n < curr.size(); n++) {
|
||||
Node3D *s = curr[n];
|
||||
|
||||
// Reset, needs to be marked each tick.
|
||||
s->data.fti_on_tick_xform_list = false;
|
||||
|
||||
// Pump.
|
||||
s->fti_pump_xform();
|
||||
}
|
||||
|
||||
// Clear previous list and flip.
|
||||
prev.clear();
|
||||
prev_prop.clear();
|
||||
data.mirror = prev_mirror;
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_update_request_resets() {
|
||||
// For instance when first adding to the tree, when the previous transform is
|
||||
// unset, to prevent streaking from the origin.
|
||||
for (uint32_t n = 0; n < data.request_reset_list.size(); n++) {
|
||||
Node3D *s = data.request_reset_list[n];
|
||||
if (s->_is_physics_interpolation_reset_requested()) {
|
||||
if (s->_is_vi_visible() && !s->_is_using_identity_transform()) {
|
||||
s->notification(Node3D::NOTIFICATION_RESET_PHYSICS_INTERPOLATION);
|
||||
}
|
||||
|
||||
s->_set_physics_interpolation_reset_requested(false);
|
||||
}
|
||||
}
|
||||
|
||||
data.request_reset_list.clear();
|
||||
}
|
||||
|
||||
void SceneTreeFTI::node_3d_request_reset(Node3D *p_node) {
|
||||
DEV_CHECK_ONCE(data.enabled);
|
||||
DEV_ASSERT(p_node);
|
||||
|
||||
MutexLock(data.mutex);
|
||||
|
||||
if (!p_node->_is_physics_interpolation_reset_requested()) {
|
||||
p_node->_set_physics_interpolation_reset_requested(true);
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
DEV_CHECK_ONCE(data.request_reset_list.find(p_node) == -1);
|
||||
#endif
|
||||
data.request_reset_list.push_back(p_node);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_node_3d_notify_set_property(Node3D &r_node) {
|
||||
if (!r_node.is_physics_interpolated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEV_CHECK_ONCE(data.enabled);
|
||||
|
||||
// Note that a Node3D can be on BOTH the transform list and the property list.
|
||||
if (!r_node.data.fti_on_tick_property_list) {
|
||||
r_node.data.fti_on_tick_property_list = true;
|
||||
|
||||
// Should only appear once in the property list.
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
DEV_CHECK_ONCE(data.tick_property_list[data.mirror].find(&r_node) == -1);
|
||||
#endif
|
||||
data.tick_property_list[data.mirror].push_back(&r_node);
|
||||
}
|
||||
|
||||
if (!r_node.data.fti_on_frame_property_list) {
|
||||
r_node.data.fti_on_frame_property_list = true;
|
||||
|
||||
// Should only appear once in the property frame list.
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
DEV_CHECK_ONCE(data.frame_property_list.find(&r_node) == -1);
|
||||
#endif
|
||||
data.frame_property_list.push_back(&r_node);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_create_depth_lists() {
|
||||
uint32_t first_list = data.frame_start ? 0 : 1;
|
||||
|
||||
for (uint32_t l = first_list; l < 2; l++) {
|
||||
LocalVector<Node3D *> &source_list = l == 0 ? data.frame_xform_list : data.frame_xform_list_forced;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool log_nodes_moved_on_frame = (data.traversal_mode == TM_DEBUG) && !data.frame_start && data.periodic_debug_log;
|
||||
if (log_nodes_moved_on_frame) {
|
||||
if (source_list.size()) {
|
||||
print_line(String("\n") + itos(source_list.size()) + " nodes moved during frame:");
|
||||
} else {
|
||||
print_line("0 nodes moved during frame.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (uint32_t n = 0; n < source_list.size(); n++) {
|
||||
Node3D *s = source_list[n];
|
||||
s->data.fti_processed = false;
|
||||
|
||||
int32_t depth = s->_get_scene_tree_depth();
|
||||
|
||||
// This shouldn't happen, but wouldn't be terrible if it did.
|
||||
DEV_ASSERT(depth >= 0);
|
||||
depth = MIN(depth, (int32_t)data.scene_tree_depth_limit);
|
||||
|
||||
LocalVector<Node3D *> &dest_list = data.dirty_node_depth_lists[depth];
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
// Shouldn't really happen, but duplicates don't really matter that much.
|
||||
if (dest_list.find(s) != -1) {
|
||||
ERR_FAIL_COND(dest_list.find(s) != -1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (log_nodes_moved_on_frame) {
|
||||
print_line("\t" + s->get_name());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Prevent being added to the dest_list twice when on
|
||||
// the frame_xform_list AND the frame_xform_list_forced.
|
||||
if ((l == 0) && s->data.fti_frame_xform_force_update) {
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
DEV_ASSERT(data.frame_xform_list_forced.find(s) != -1);
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
dest_list.push_back(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_clear_depth_lists() {
|
||||
for (uint32_t d = 0; d < data.scene_tree_depth_limit; d++) {
|
||||
data.dirty_node_depth_lists[d].clear();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_node_add_to_frame_list(Node3D &r_node, bool p_forced) {
|
||||
if (p_forced) {
|
||||
DEV_ASSERT(!r_node.data.fti_frame_xform_force_update);
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
int64_t found = data.frame_xform_list_forced.find(&r_node);
|
||||
if (found != -1) {
|
||||
ERR_FAIL_COND(found != -1);
|
||||
}
|
||||
#endif
|
||||
data.frame_xform_list_forced.push_back(&r_node);
|
||||
r_node.data.fti_frame_xform_force_update = true;
|
||||
} else {
|
||||
DEV_ASSERT(!r_node.data.fti_on_frame_xform_list);
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
int64_t found = data.frame_xform_list.find(&r_node);
|
||||
if (found != -1) {
|
||||
ERR_FAIL_COND(found != -1);
|
||||
}
|
||||
#endif
|
||||
data.frame_xform_list.push_back(&r_node);
|
||||
r_node.data.fti_on_frame_xform_list = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_node_remove_from_frame_list(Node3D &r_node, bool p_forced) {
|
||||
if (p_forced) {
|
||||
DEV_ASSERT(r_node.data.fti_frame_xform_force_update);
|
||||
data.frame_xform_list_forced.erase_unordered(&r_node);
|
||||
r_node.data.fti_frame_xform_force_update = false;
|
||||
} else {
|
||||
DEV_ASSERT(r_node.data.fti_on_frame_xform_list);
|
||||
data.frame_xform_list.erase_unordered(&r_node);
|
||||
r_node.data.fti_on_frame_xform_list = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_node_3d_notify_set_xform(Node3D &r_node) {
|
||||
DEV_CHECK_ONCE(data.enabled);
|
||||
|
||||
if (!r_node.is_physics_interpolated()) {
|
||||
// Force an update of non-interpolated to servers
|
||||
// on the next traversal.
|
||||
if (!r_node.data.fti_frame_xform_force_update) {
|
||||
_node_add_to_frame_list(r_node, true);
|
||||
}
|
||||
|
||||
// ToDo: Double check this is a win,
|
||||
// non-interpolated nodes we always check for identity,
|
||||
// *just in case*.
|
||||
r_node.data.fti_is_identity_xform = r_node.get_transform() == Transform3D();
|
||||
return;
|
||||
}
|
||||
|
||||
r_node.data.fti_is_identity_xform = false;
|
||||
|
||||
if (!r_node.data.fti_on_tick_xform_list) {
|
||||
r_node.data.fti_on_tick_xform_list = true;
|
||||
|
||||
// Should only appear once in the xform list.
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
DEV_CHECK_ONCE(data.tick_xform_list[data.mirror].find(&r_node) == -1);
|
||||
#endif
|
||||
data.tick_xform_list[data.mirror].push_back(&r_node);
|
||||
|
||||
// The following flag could have been previously set
|
||||
// (for removal from the tick list).
|
||||
// We no longer need this guarantee,
|
||||
// however there is probably no downside to leaving it set
|
||||
// as it will be cleared on the next frame anyway.
|
||||
// This line is left for reference.
|
||||
// r_node.data.fti_frame_xform_force_update = false;
|
||||
}
|
||||
|
||||
if (!r_node.data.fti_on_frame_xform_list) {
|
||||
_node_add_to_frame_list(r_node, false);
|
||||
}
|
||||
|
||||
// If we are in the second half of a frame, always add to the force update list,
|
||||
// because we ignore the tick update list during the second update.
|
||||
if (data.in_frame) {
|
||||
if (!r_node.data.fti_frame_xform_force_update) {
|
||||
_node_add_to_frame_list(r_node, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTI::node_3d_notify_delete(Node3D *p_node) {
|
||||
if (!data.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_NULL(p_node);
|
||||
|
||||
MutexLock(data.mutex);
|
||||
|
||||
// Remove from frame lists.
|
||||
if (p_node->data.fti_on_frame_xform_list) {
|
||||
_node_remove_from_frame_list(*p_node, false);
|
||||
}
|
||||
if (p_node->data.fti_frame_xform_force_update) {
|
||||
_node_remove_from_frame_list(*p_node, true);
|
||||
}
|
||||
|
||||
// Ensure this is kept in sync with the lists, in case a node
|
||||
// is removed and re-added to the scene tree multiple times
|
||||
// on the same frame / tick.
|
||||
p_node->_set_physics_interpolation_reset_requested(false);
|
||||
|
||||
// Keep flags consistent for the same as a new node,
|
||||
// because this node may re-enter the scene tree.
|
||||
_reset_node3d_flags(*p_node);
|
||||
|
||||
// This can potentially be optimized for large scenes with large churn,
|
||||
// as it will be doing a linear search through the lists.
|
||||
data.tick_xform_list[0].erase_unordered(p_node);
|
||||
data.tick_xform_list[1].erase_unordered(p_node);
|
||||
|
||||
data.tick_property_list[0].erase_unordered(p_node);
|
||||
data.tick_property_list[1].erase_unordered(p_node);
|
||||
|
||||
data.frame_property_list.erase_unordered(p_node);
|
||||
data.request_reset_list.erase_unordered(p_node);
|
||||
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
// There should only be one occurrence on the lists.
|
||||
// Check this in DEV_ENABLED builds.
|
||||
DEV_CHECK_ONCE(data.tick_xform_list[0].find(p_node) == -1);
|
||||
DEV_CHECK_ONCE(data.tick_xform_list[1].find(p_node) == -1);
|
||||
|
||||
DEV_CHECK_ONCE(data.tick_property_list[0].find(p_node) == -1);
|
||||
DEV_CHECK_ONCE(data.tick_property_list[1].find(p_node) == -1);
|
||||
|
||||
DEV_CHECK_ONCE(data.frame_property_list.find(p_node) == -1);
|
||||
DEV_CHECK_ONCE(data.request_reset_list.find(p_node) == -1);
|
||||
|
||||
DEV_CHECK_ONCE(data.frame_xform_list.find(p_node) == -1);
|
||||
DEV_CHECK_ONCE(data.frame_xform_list_forced.find(p_node) == -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void SceneTreeFTI::_update_dirty_nodes(Node *p_node, uint32_t p_current_half_frame, float p_interpolation_fraction, bool p_active, const Transform3D *p_parent_global_xform, int p_depth) {
|
||||
Node3D *s = Object::cast_to<Node3D>(p_node);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
data.debug_node_count++;
|
||||
#endif
|
||||
|
||||
// Don't recurse into hidden branches.
|
||||
if (s && !s->data.visible) {
|
||||
// NOTE : If we change from recursing entire tree, we should do an is_visible_in_tree()
|
||||
// check for the first of the branch.
|
||||
return;
|
||||
}
|
||||
|
||||
// Temporary direct access to children cache for speed.
|
||||
// Maybe replaced later by a more generic fast access method
|
||||
// for children.
|
||||
p_node->_update_children_cache();
|
||||
Span<Node *> children = p_node->data.children_cache.span();
|
||||
uint32_t num_children = children.size();
|
||||
|
||||
// Not a Node3D.
|
||||
// Could be e.g. a viewport or something
|
||||
// so we should still recurse to children.
|
||||
if (!s) {
|
||||
for (uint32_t n = 0; n < num_children; n++) {
|
||||
_update_dirty_nodes(children.ptr()[n], p_current_half_frame, p_interpolation_fraction, p_active, nullptr, p_depth + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We are going to be using data.global_transform, so
|
||||
// we need to ensure data.global_transform is not dirty!
|
||||
if (s->_test_dirty_bits(Node3D::DIRTY_GLOBAL_TRANSFORM)) {
|
||||
_ALLOW_DISCARD_ s->get_global_transform();
|
||||
}
|
||||
|
||||
// Start the active interpolation chain from here onwards
|
||||
// as we recurse further into the SceneTree.
|
||||
// Once we hit an active (interpolated) node, we have to fully
|
||||
// process all ancestors because their xform will also change.
|
||||
// Anything not moving (inactive) higher in the tree need not be processed.
|
||||
if (!p_active) {
|
||||
if (data.frame_start) {
|
||||
// On the frame start, activate whenever we hit something that requests interpolation.
|
||||
if (s->data.fti_on_frame_xform_list || s->data.fti_frame_xform_force_update) {
|
||||
p_active = true;
|
||||
}
|
||||
} else {
|
||||
// On the frame end, we want to re-interpolate *anything* that has moved
|
||||
// since the frame start.
|
||||
if (s->_test_dirty_bits(Node3D::DIRTY_GLOBAL_INTERPOLATED_TRANSFORM)) {
|
||||
p_active = true;
|
||||
|
||||
#if 0
|
||||
if (data.periodic_debug_log) {
|
||||
print_line("activating on : " + s->get_name());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ToDo : Check global_xform_interp is up to date for nodes
|
||||
// that are not traversed by the depth lists.
|
||||
if (data.frame_start) {
|
||||
// Mark on the Node3D whether we have set global_transform_interp.
|
||||
// This can later be used when calling `get_global_transform_interpolated()`
|
||||
// to know which xform to return.
|
||||
s->data.fti_global_xform_interp_set = p_active;
|
||||
}
|
||||
|
||||
if (p_active) {
|
||||
#ifdef GODOT_SCENE_TREE_FTI_PRINT_TREE
|
||||
bool dirty = s->_test_dirty_bits(Node3D::DIRTY_GLOBAL_INTERPOLATED_TRANSFORM);
|
||||
|
||||
if (data.periodic_debug_log && !data.use_optimized_traversal_method && !data.frame_start) {
|
||||
String sz;
|
||||
for (int n = 0; n < p_depth; n++) {
|
||||
sz += "\t";
|
||||
}
|
||||
print_line(sz + p_node->get_name() + (dirty ? " DIRTY" : "") + (s->get_transform() == Transform3D() ? "\t[IDENTITY]" : ""));
|
||||
}
|
||||
#endif
|
||||
|
||||
// First calculate our local xform.
|
||||
// This will either use interpolation, or just use the current local if not interpolated.
|
||||
Transform3D local_interp;
|
||||
if (s->is_physics_interpolated()) {
|
||||
// There may be no need to interpolate if the node has not been moved recently
|
||||
// and is therefore not on the tick list...
|
||||
if (s->data.fti_on_tick_xform_list) {
|
||||
// Make sure to call `get_transform()` rather than using local_transform directly, because
|
||||
// local_transform may be dirty and need updating from rotation / scale.
|
||||
TransformInterpolator::interpolate_transform_3d(s->data.local_transform_prev, s->get_transform(), local_interp, p_interpolation_fraction);
|
||||
} else {
|
||||
local_interp = s->get_transform();
|
||||
}
|
||||
} else {
|
||||
local_interp = s->get_transform();
|
||||
}
|
||||
|
||||
// Concatenate parent xform.
|
||||
if (!s->is_set_as_top_level()) {
|
||||
if (p_parent_global_xform) {
|
||||
s->data.global_transform_interpolated = s->data.fti_is_identity_xform ? *p_parent_global_xform : ((*p_parent_global_xform) * local_interp);
|
||||
} else {
|
||||
const Node3D *parent = s->get_parent_node_3d();
|
||||
|
||||
if (parent) {
|
||||
const Transform3D &parent_glob = parent->data.fti_global_xform_interp_set ? parent->data.global_transform_interpolated : parent->get_global_transform();
|
||||
s->data.global_transform_interpolated = s->data.fti_is_identity_xform ? parent_glob : parent_glob * local_interp;
|
||||
} else {
|
||||
s->data.global_transform_interpolated = local_interp;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s->data.global_transform_interpolated = local_interp;
|
||||
}
|
||||
|
||||
// Watch for this, disable_scale can cause incredibly confusing bugs
|
||||
// and must be checked for when calculating global xforms.
|
||||
if (s->data.disable_scale) {
|
||||
s->data.global_transform_interpolated.basis.orthonormalize();
|
||||
}
|
||||
|
||||
// Upload to RenderingServer the interpolated global xform.
|
||||
s->fti_update_servers_xform();
|
||||
|
||||
// Ensure branches are only processed once on each traversal.
|
||||
s->data.fti_processed = true;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
data.debug_nodes_processed++;
|
||||
#endif
|
||||
} // if active.
|
||||
|
||||
// Remove the dirty interp flag from EVERYTHING as we go.
|
||||
s->_clear_dirty_bits(Node3D::DIRTY_GLOBAL_INTERPOLATED_TRANSFORM);
|
||||
|
||||
// Recurse to children.
|
||||
for (uint32_t n = 0; n < num_children; n++) {
|
||||
_update_dirty_nodes(children.ptr()[n], p_current_half_frame, p_interpolation_fraction, p_active, s->data.fti_global_xform_interp_set ? &s->data.global_transform_interpolated : &s->data.global_transform, p_depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTI::frame_update(Node *p_root, bool p_frame_start) {
|
||||
if (!data.enabled || !p_root) {
|
||||
return;
|
||||
}
|
||||
MutexLock(data.mutex);
|
||||
|
||||
data.frame_start = p_frame_start;
|
||||
data.in_frame = true;
|
||||
|
||||
_update_request_resets();
|
||||
|
||||
float interpolation_fraction = Engine::get_singleton()->get_physics_interpolation_fraction();
|
||||
uint32_t frame = Engine::get_singleton()->get_frames_drawn();
|
||||
|
||||
uint64_t before = 0;
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (data.traversal_mode == TM_DEBUG) {
|
||||
before = OS::get_singleton()->get_ticks_usec();
|
||||
|
||||
if (p_frame_start && ((frame % ((60 * 15) - 3)) == 0)) {
|
||||
data.periodic_debug_log = true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef GODOT_SCENE_TREE_FTI_PRINT_TREE
|
||||
if (data.periodic_debug_log) {
|
||||
print_line(String("\nScene: ") + (data.frame_start ? "start" : "end") + "\n");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
data.debug_node_count = 0;
|
||||
data.debug_nodes_processed = 0;
|
||||
|
||||
uint32_t half_frame = p_frame_start ? (frame * 2) : ((frame * 2) + 1);
|
||||
|
||||
bool print_debug_stats = false;
|
||||
switch (data.traversal_mode) {
|
||||
case TM_LEGACY: {
|
||||
data.use_optimized_traversal_method = false;
|
||||
} break;
|
||||
case TM_DEBUG: {
|
||||
// Switch on alternate frames between the two methods.
|
||||
data.use_optimized_traversal_method = (frame % 2) == 1;
|
||||
|
||||
// Odd number ensures we debug stats for both methods.
|
||||
print_debug_stats = (frame % ((60 * 8) - 1)) == 0;
|
||||
} break;
|
||||
default: {
|
||||
data.use_optimized_traversal_method = true;
|
||||
} break;
|
||||
}
|
||||
|
||||
#ifdef GODOT_SCENE_TREE_FTI_VERIFY
|
||||
_tests->frame_update(p_root, half_frame, interpolation_fraction);
|
||||
#else
|
||||
|
||||
uint32_t skipped = 0;
|
||||
|
||||
if (!data.use_optimized_traversal_method) {
|
||||
// Reference approach.
|
||||
// Traverse the entire scene tree.
|
||||
// Slow, but robust.
|
||||
_update_dirty_nodes(p_root, half_frame, interpolation_fraction, false);
|
||||
} else {
|
||||
// Optimized approach.
|
||||
// Traverse from depth lists.
|
||||
// Be sure to check against the reference
|
||||
// implementation when making changes.
|
||||
_create_depth_lists();
|
||||
|
||||
for (uint32_t d = 0; d < data.scene_tree_depth_limit; d++) {
|
||||
const LocalVector<Node3D *> &list = data.dirty_node_depth_lists[d];
|
||||
|
||||
#if 0
|
||||
if (list.size() > 0) {
|
||||
print_line("depth " + itos(d) + ", contains " + itos(list.size()));
|
||||
}
|
||||
#endif
|
||||
|
||||
for (uint32_t n = 0; n < list.size(); n++) {
|
||||
// Already processed this frame?
|
||||
Node3D *s = list[n];
|
||||
|
||||
if (s->data.fti_processed) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
skipped++;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
// The first node requires a recursive visibility check
|
||||
// up the tree, because `is_visible()` only returns the node
|
||||
// local flag.
|
||||
if (Object::cast_to<VisualInstance3D>(s)) {
|
||||
if (!s->_is_vi_visible()) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
skipped++;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
} else if (!s->is_visible_in_tree()) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
skipped++;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
_update_dirty_nodes(s, half_frame, interpolation_fraction, true);
|
||||
}
|
||||
}
|
||||
|
||||
_clear_depth_lists();
|
||||
}
|
||||
|
||||
if (print_debug_stats) {
|
||||
uint64_t after = OS::get_singleton()->get_ticks_usec();
|
||||
print_line(String(data.use_optimized_traversal_method ? "FTI optimized" : "FTI reference") + " nodes traversed : " + itos(data.debug_node_count) + (skipped == 0 ? "" : ", skipped " + itos(skipped)) + ", processed : " + itos(data.debug_nodes_processed) + ", took " + itos(after - before) + " usec " + (data.frame_start ? "(start)" : "(end)"));
|
||||
}
|
||||
|
||||
#endif // not GODOT_SCENE_TREE_FTI_VERIFY
|
||||
|
||||
// In theory we could clear the `force_update` flags from the nodes in the traversal.
|
||||
// The problem is that hidden nodes are not recursed into, therefore the flags would
|
||||
// never get cleared and could get out of sync with the forced list.
|
||||
// So instead we are clearing them here manually.
|
||||
// This is not ideal in terms of cache coherence so perhaps another method can be
|
||||
// explored in future.
|
||||
uint32_t forced_list_size = data.frame_xform_list_forced.size();
|
||||
for (uint32_t n = 0; n < forced_list_size; n++) {
|
||||
Node3D *s = data.frame_xform_list_forced[n];
|
||||
s->data.fti_frame_xform_force_update = false;
|
||||
}
|
||||
data.frame_xform_list_forced.clear();
|
||||
|
||||
if (!p_frame_start && data.periodic_debug_log) {
|
||||
data.periodic_debug_log = false;
|
||||
}
|
||||
|
||||
// Update the properties once off at the end of the frame.
|
||||
// No need for two passes for properties.
|
||||
if (!p_frame_start) {
|
||||
for (uint32_t n = 0; n < data.frame_property_list.size(); n++) {
|
||||
Node3D *s = data.frame_property_list[n];
|
||||
s->fti_update_servers_property();
|
||||
}
|
||||
}
|
||||
|
||||
// Marks the end of the frame.
|
||||
// Enables us to recognize when change notifications
|
||||
// come in _during_ a frame (they get treated differently).
|
||||
if (!data.frame_start) {
|
||||
data.in_frame = false;
|
||||
}
|
||||
}
|
||||
|
||||
SceneTreeFTI::SceneTreeFTI() {
|
||||
#ifdef GODOT_SCENE_TREE_FTI_VERIFY
|
||||
_tests = memnew(SceneTreeFTITests(*this));
|
||||
#endif
|
||||
|
||||
Variant traversal_mode_string = GLOBAL_DEF("physics/3d/physics_interpolation/scene_traversal", "DEFAULT");
|
||||
ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "physics/3d/physics_interpolation/scene_traversal", PROPERTY_HINT_ENUM, "DEFAULT,Legacy,Debug"));
|
||||
|
||||
data.traversal_mode = TM_DEFAULT;
|
||||
|
||||
if (traversal_mode_string == "Legacy") {
|
||||
data.traversal_mode = TM_LEGACY;
|
||||
} else if (traversal_mode_string == "Debug") {
|
||||
// Don't allow debug mode in final exports,
|
||||
// it will almost certainly be a mistake.
|
||||
#ifdef DEBUG_ENABLED
|
||||
data.traversal_mode = TM_DEBUG;
|
||||
#else
|
||||
data.traversal_mode = TM_DEFAULT;
|
||||
#endif
|
||||
}
|
||||
|
||||
switch (data.traversal_mode) {
|
||||
default: {
|
||||
print_verbose("SceneTreeFTI: traversal method DEFAULT");
|
||||
} break;
|
||||
case TM_LEGACY: {
|
||||
print_verbose("SceneTreeFTI: traversal method Legacy");
|
||||
} break;
|
||||
case TM_DEBUG: {
|
||||
print_verbose("SceneTreeFTI: traversal method Debug");
|
||||
} break;
|
||||
}
|
||||
|
||||
#ifdef GODOT_SCENE_TREE_FTI_EXTRA_CHECKS
|
||||
print_line("SceneTreeFTI : GODOT_SCENE_TREE_FTI_EXTRA_CHECKS defined");
|
||||
#endif
|
||||
#ifdef GODOT_SCENE_TREE_FTI_PRINT_TREE
|
||||
print_line("SceneTreeFTI : GODOT_SCENE_TREE_FTI_PRINT_TREE defined");
|
||||
#endif
|
||||
}
|
||||
|
||||
SceneTreeFTI::~SceneTreeFTI() {
|
||||
#ifdef GODOT_SCENE_TREE_FTI_VERIFY
|
||||
if (_tests) {
|
||||
memfree(_tests);
|
||||
_tests = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // ndef _3D_DISABLED
|
173
scene/main/scene_tree_fti.h
Normal file
173
scene/main/scene_tree_fti.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/**************************************************************************/
|
||||
/* scene_tree_fti.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
|
||||
class Node3D;
|
||||
class Node;
|
||||
struct Transform3D;
|
||||
class SceneTreeFTITests;
|
||||
|
||||
#ifdef DEV_ENABLED
|
||||
// Uncomment this to verify traversal method results.
|
||||
// #define GODOT_SCENE_TREE_FTI_VERIFY
|
||||
#endif
|
||||
|
||||
#ifdef _3D_DISABLED
|
||||
// Stubs
|
||||
class SceneTreeFTI {
|
||||
public:
|
||||
void frame_update(Node *p_root, bool p_frame_start) {}
|
||||
void tick_update() {}
|
||||
void set_enabled(Node *p_root, bool p_enabled) {}
|
||||
bool is_enabled() const { return false; }
|
||||
|
||||
void node_3d_notify_changed(Node3D &r_node, bool p_transform_changed) {}
|
||||
void node_3d_notify_delete(Node3D *p_node) {}
|
||||
void node_3d_request_reset(Node3D *p_node) {}
|
||||
};
|
||||
#else
|
||||
|
||||
// Important.
|
||||
// This class uses raw pointers, so it is essential that on deletion, this class is notified
|
||||
// so that any references can be cleared up to prevent dangling pointer access.
|
||||
|
||||
// This class can be used from a custom SceneTree.
|
||||
|
||||
// Note we could potentially make SceneTreeFTI static / global to avoid the lookup through scene tree,
|
||||
// but this covers the custom case of multiple scene trees.
|
||||
|
||||
class SceneTreeFTI {
|
||||
friend class SceneTreeFTITests;
|
||||
|
||||
enum TraversalMode : unsigned {
|
||||
TM_DEFAULT,
|
||||
TM_LEGACY,
|
||||
TM_DEBUG,
|
||||
};
|
||||
|
||||
struct Data {
|
||||
static const uint32_t scene_tree_depth_limit = 32;
|
||||
|
||||
// Prev / Curr lists of Node3Ds having local xforms pumped.
|
||||
LocalVector<Node3D *> tick_xform_list[2];
|
||||
|
||||
// The frame lists are changed nodes that need to start traversal,
|
||||
// either longterm (on the tick list) or single frame forced.
|
||||
LocalVector<Node3D *> frame_xform_list;
|
||||
LocalVector<Node3D *> frame_xform_list_forced;
|
||||
|
||||
// Prev / Curr lists of Node3Ds having actively interpolated properties.
|
||||
LocalVector<Node3D *> tick_property_list[2];
|
||||
|
||||
LocalVector<Node3D *> frame_property_list;
|
||||
LocalVector<Node3D *> request_reset_list;
|
||||
LocalVector<Node3D *> dirty_node_depth_lists[scene_tree_depth_limit];
|
||||
|
||||
// When we are using two alternating lists,
|
||||
// which one is current.
|
||||
uint32_t mirror = 0;
|
||||
|
||||
// Global on / off switch for SceneTreeFTI.
|
||||
bool enabled = false;
|
||||
|
||||
// Whether we are in physics ticks, or in a frame.
|
||||
bool in_frame = false;
|
||||
|
||||
// Updating at the start of the frame, or the end on second pass.
|
||||
bool frame_start = true;
|
||||
|
||||
Mutex mutex;
|
||||
|
||||
TraversalMode traversal_mode = TM_DEFAULT;
|
||||
bool use_optimized_traversal_method = true;
|
||||
|
||||
// DEBUGGING
|
||||
bool periodic_debug_log = false;
|
||||
uint32_t debug_node_count = 0;
|
||||
uint32_t debug_nodes_processed = 0;
|
||||
|
||||
} data;
|
||||
|
||||
#ifdef GODOT_SCENE_TREE_FTI_VERIFY
|
||||
SceneTreeFTITests *_tests = nullptr;
|
||||
#endif
|
||||
|
||||
void _update_dirty_nodes(Node *p_node, uint32_t p_current_half_frame, float p_interpolation_fraction, bool p_active, const Transform3D *p_parent_global_xform = nullptr, int p_depth = 0);
|
||||
void _update_request_resets();
|
||||
|
||||
void _reset_flags(Node *p_node);
|
||||
void _reset_node3d_flags(Node3D &r_node);
|
||||
void _node_3d_notify_set_xform(Node3D &r_node);
|
||||
void _node_3d_notify_set_property(Node3D &r_node);
|
||||
|
||||
void _node_add_to_frame_list(Node3D &r_node, bool p_forced);
|
||||
void _node_remove_from_frame_list(Node3D &r_node, bool p_forced);
|
||||
|
||||
void _create_depth_lists();
|
||||
void _clear_depth_lists();
|
||||
|
||||
public:
|
||||
// Hottest function, allow inlining the data.enabled check.
|
||||
void node_3d_notify_changed(Node3D &r_node, bool p_transform_changed) {
|
||||
if (!data.enabled) {
|
||||
return;
|
||||
}
|
||||
MutexLock(data.mutex);
|
||||
|
||||
if (p_transform_changed) {
|
||||
_node_3d_notify_set_xform(r_node);
|
||||
} else {
|
||||
_node_3d_notify_set_property(r_node);
|
||||
}
|
||||
}
|
||||
|
||||
void node_3d_request_reset(Node3D *p_node);
|
||||
void node_3d_notify_delete(Node3D *p_node);
|
||||
|
||||
// Calculate interpolated xforms, send to visual server.
|
||||
void frame_update(Node *p_root, bool p_frame_start);
|
||||
|
||||
// Update local xform pumps.
|
||||
void tick_update();
|
||||
|
||||
void set_enabled(Node *p_root, bool p_enabled);
|
||||
bool is_enabled() const { return data.enabled; }
|
||||
|
||||
void set_debug_next_frame() { data.periodic_debug_log = true; }
|
||||
|
||||
SceneTreeFTI();
|
||||
~SceneTreeFTI();
|
||||
};
|
||||
|
||||
#endif // ndef _3D_DISABLED
|
246
scene/main/scene_tree_fti_tests.cpp
Normal file
246
scene/main/scene_tree_fti_tests.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
/**************************************************************************/
|
||||
/* scene_tree_fti_tests.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
|
||||
#ifdef GODOT_SCENE_TREE_FTI_VERIFY
|
||||
#include "scene_tree_fti_tests.h"
|
||||
|
||||
#include "scene/3d/node_3d.h"
|
||||
#include "scene/3d/visual_instance_3d.h"
|
||||
#include "scene/main/scene_tree_fti.h"
|
||||
|
||||
void SceneTreeFTITests::debug_verify_failed(const Node3D *p_node_3d, const Transform3D &p_test) {
|
||||
print_line("VERIFY FAILED\n");
|
||||
print_line("test xform : " + String(Variant(p_test)));
|
||||
|
||||
bool first = true;
|
||||
|
||||
while (p_node_3d) {
|
||||
int32_t depth = MAX(p_node_3d->_get_scene_tree_depth(), 0);
|
||||
String tabs;
|
||||
for (int32_t n = 0; n < depth; n++) {
|
||||
tabs += "\t";
|
||||
}
|
||||
|
||||
bool interp_equal = p_node_3d->_get_cached_global_transform_interpolated() == p_test;
|
||||
bool glob_equal = p_node_3d->get_global_transform() == p_test;
|
||||
|
||||
String sz = tabs + p_node_3d->get_name() + " [ " + p_node_3d->get_class_name() + " ]\n";
|
||||
|
||||
if (first) {
|
||||
sz += tabs + "... " + String(Variant(p_test)) + "\n";
|
||||
}
|
||||
|
||||
sz += tabs + (p_node_3d->data.fti_global_xform_interp_set ? "[I] " : "[i] ") + String(Variant(p_node_3d->_get_cached_global_transform_interpolated())) + (interp_equal ? " ***" : "") + "\n";
|
||||
sz += tabs + "[g] " + String(Variant(p_node_3d->get_global_transform())) + (glob_equal ? " ***" : "");
|
||||
|
||||
print_line(sz);
|
||||
|
||||
p_node_3d = p_node_3d->get_parent_node_3d();
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTITests::update_dirty_nodes(Node *p_node, uint32_t p_current_half_frame, float p_interpolation_fraction, bool p_active, const Transform3D *p_parent_global_xform, int p_depth) {
|
||||
SceneTreeFTI::Data &data = _fti.data;
|
||||
|
||||
// There are two runs going on here.
|
||||
// FIRST the naive entire scene tree (reference), where we are
|
||||
// setting state (i.e. writing out xforms, and other state)
|
||||
// SECOND the optimized run, where we are NOT
|
||||
// writing state, but only verifying that the xforms calculated
|
||||
// match those from the reference approach.
|
||||
bool should_verify = (data.traversal_mode == SceneTreeFTI::TM_DEBUG) && data.use_optimized_traversal_method;
|
||||
bool set_state = !should_verify;
|
||||
|
||||
Node3D *s = Object::cast_to<Node3D>(p_node);
|
||||
|
||||
if (s && !s->is_visible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!s) {
|
||||
for (int n = 0; n < p_node->get_child_count(); n++) {
|
||||
update_dirty_nodes(p_node->get_child(n), p_current_half_frame, p_interpolation_fraction, p_active, nullptr, p_depth + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->_test_dirty_bits(Node3D::DIRTY_GLOBAL_TRANSFORM)) {
|
||||
_ALLOW_DISCARD_ s->get_global_transform();
|
||||
}
|
||||
|
||||
if (!p_active) {
|
||||
if (data.frame_start) {
|
||||
if (s->data.fti_on_frame_xform_list || s->data.fti_frame_xform_force_update) {
|
||||
p_active = true;
|
||||
}
|
||||
} else {
|
||||
if (s->_test_dirty_bits(Node3D::DIRTY_GLOBAL_INTERPOLATED_TRANSFORM)) {
|
||||
p_active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.frame_start) {
|
||||
s->data.fti_global_xform_interp_set = p_active;
|
||||
}
|
||||
|
||||
if (p_active) {
|
||||
Transform3D local_interp;
|
||||
if (s->is_physics_interpolated()) {
|
||||
if (s->data.fti_on_tick_xform_list) {
|
||||
TransformInterpolator::interpolate_transform_3d(s->data.local_transform_prev, s->get_transform(), local_interp, p_interpolation_fraction);
|
||||
} else {
|
||||
local_interp = s->get_transform();
|
||||
}
|
||||
} else {
|
||||
local_interp = s->get_transform();
|
||||
}
|
||||
|
||||
if (!s->is_set_as_top_level()) {
|
||||
if (p_parent_global_xform) {
|
||||
if (should_verify) {
|
||||
Transform3D test = (*p_parent_global_xform) * local_interp;
|
||||
if (s->data.disable_scale) {
|
||||
test.basis.orthonormalize();
|
||||
}
|
||||
if (s->data.global_transform_interpolated != test) {
|
||||
debug_verify_failed(s, test);
|
||||
DEV_ASSERT(s->data.global_transform_interpolated == test);
|
||||
}
|
||||
} else {
|
||||
s->data.global_transform_interpolated = s->data.fti_is_identity_xform ? (*p_parent_global_xform) : (*p_parent_global_xform) * local_interp;
|
||||
}
|
||||
} else {
|
||||
const Node3D *parent = s->get_parent_node_3d();
|
||||
|
||||
if (parent) {
|
||||
const Transform3D &parent_glob = parent->data.fti_global_xform_interp_set ? parent->data.global_transform_interpolated : parent->get_global_transform();
|
||||
|
||||
if (should_verify) {
|
||||
Transform3D test = parent_glob * local_interp;
|
||||
if (s->data.disable_scale) {
|
||||
test.basis.orthonormalize();
|
||||
}
|
||||
if (s->data.global_transform_interpolated != test) {
|
||||
debug_verify_failed(s, test);
|
||||
DEV_ASSERT(s->data.global_transform_interpolated == test);
|
||||
}
|
||||
|
||||
} else {
|
||||
s->data.global_transform_interpolated = s->data.fti_is_identity_xform ? parent_glob : parent_glob * local_interp;
|
||||
}
|
||||
} else {
|
||||
if (set_state) {
|
||||
s->data.global_transform_interpolated = local_interp;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (set_state) {
|
||||
s->data.global_transform_interpolated = local_interp;
|
||||
}
|
||||
}
|
||||
|
||||
if (set_state) {
|
||||
if (s->data.disable_scale) {
|
||||
s->data.global_transform_interpolated.basis.orthonormalize();
|
||||
}
|
||||
|
||||
s->fti_update_servers_xform();
|
||||
|
||||
s->data.fti_frame_xform_force_update = false;
|
||||
}
|
||||
|
||||
s->data.fti_processed = true;
|
||||
} // if active.
|
||||
|
||||
if (set_state) {
|
||||
s->_clear_dirty_bits(Node3D::DIRTY_GLOBAL_INTERPOLATED_TRANSFORM);
|
||||
}
|
||||
|
||||
for (int n = 0; n < p_node->get_child_count(); n++) {
|
||||
update_dirty_nodes(p_node->get_child(n), p_current_half_frame, p_interpolation_fraction, p_active, s->data.fti_global_xform_interp_set ? &s->data.global_transform_interpolated : &s->data.global_transform, p_depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneTreeFTITests::frame_update(Node *p_root, uint32_t p_half_frame, float p_interpolation_fraction) {
|
||||
SceneTreeFTI::Data &data = _fti.data;
|
||||
|
||||
// For testing, use both methods.
|
||||
// FIRST the entire tree, writing out state.
|
||||
{
|
||||
data.use_optimized_traversal_method = false;
|
||||
update_dirty_nodes(p_root, p_half_frame, p_interpolation_fraction, false);
|
||||
}
|
||||
|
||||
// SECOND the optimized depth lists only,
|
||||
// no writing of state, and verifying results.
|
||||
{
|
||||
data.use_optimized_traversal_method = true;
|
||||
|
||||
_fti._create_depth_lists();
|
||||
|
||||
for (uint32_t d = 0; d < data.scene_tree_depth_limit; d++) {
|
||||
const LocalVector<Node3D *> &list = data.dirty_node_depth_lists[d];
|
||||
|
||||
for (uint32_t n = 0; n < list.size(); n++) {
|
||||
Node3D *s = list[n];
|
||||
|
||||
if (s->data.fti_processed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Object::cast_to<VisualInstance3D>(s)) {
|
||||
if (!s->_is_vi_visible()) {
|
||||
continue;
|
||||
}
|
||||
} else if (!s->is_visible_in_tree()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
update_dirty_nodes(s, p_half_frame, p_interpolation_fraction, true);
|
||||
}
|
||||
}
|
||||
|
||||
_fti._clear_depth_lists();
|
||||
}
|
||||
}
|
||||
|
||||
SceneTreeFTITests::SceneTreeFTITests(SceneTreeFTI &p_fti) :
|
||||
_fti(p_fti) {
|
||||
print_line("SceneTreeFTI : GODOT_SCENE_TREE_FTI_VERIFY defined");
|
||||
}
|
||||
|
||||
#endif // def GODOT_SCENE_TREE_FTI_VERIFY
|
||||
|
||||
#endif // ndef _3D_DISABLED
|
50
scene/main/scene_tree_fti_tests.h
Normal file
50
scene/main/scene_tree_fti_tests.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/**************************************************************************/
|
||||
/* scene_tree_fti_tests.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class Node3D;
|
||||
class Node;
|
||||
struct Transform3D;
|
||||
class SceneTreeFTI;
|
||||
|
||||
class SceneTreeFTITests {
|
||||
SceneTreeFTI &_fti;
|
||||
|
||||
void debug_verify_failed(const Node3D *p_node, const Transform3D &p_test);
|
||||
|
||||
public:
|
||||
void update_dirty_nodes(Node *p_node, uint32_t p_current_half_frame, float p_interpolation_fraction, bool p_active, const Transform3D *p_parent_global_xform = nullptr, int p_depth = 0);
|
||||
void frame_update(Node *p_root, uint32_t p_half_frame, float p_interpolation_fraction);
|
||||
|
||||
SceneTreeFTITests(SceneTreeFTI &p_fti);
|
||||
};
|
290
scene/main/shader_globals_override.cpp
Normal file
290
scene/main/shader_globals_override.cpp
Normal file
@@ -0,0 +1,290 @@
|
||||
/**************************************************************************/
|
||||
/* shader_globals_override.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "shader_globals_override.h"
|
||||
|
||||
#include "scene/main/node.h"
|
||||
|
||||
StringName *ShaderGlobalsOverride::_remap(const StringName &p_name) const {
|
||||
StringName *r = param_remaps.getptr(p_name);
|
||||
if (!r) {
|
||||
//not cached, do caching
|
||||
String p = p_name;
|
||||
if (p.begins_with("params/")) {
|
||||
String q = p.replace_first("params/", "");
|
||||
param_remaps[p] = q;
|
||||
r = param_remaps.getptr(p);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
bool ShaderGlobalsOverride::_set(const StringName &p_name, const Variant &p_value) {
|
||||
StringName *r = _remap(p_name);
|
||||
|
||||
if (r) {
|
||||
Override *o = overrides.getptr(*r);
|
||||
if (!o) {
|
||||
Override ov;
|
||||
ov.in_use = false;
|
||||
overrides[*r] = ov;
|
||||
o = overrides.getptr(*r);
|
||||
}
|
||||
if (o) {
|
||||
o->override = p_value;
|
||||
if (active) {
|
||||
if (o->override.get_type() == Variant::OBJECT) {
|
||||
RID tex_rid = p_value;
|
||||
RS::get_singleton()->global_shader_parameter_set_override(*r, tex_rid);
|
||||
} else {
|
||||
RS::get_singleton()->global_shader_parameter_set_override(*r, p_value);
|
||||
}
|
||||
}
|
||||
o->in_use = p_value.get_type() != Variant::NIL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ShaderGlobalsOverride::_get(const StringName &p_name, Variant &r_ret) const {
|
||||
StringName *r = _remap(p_name);
|
||||
|
||||
if (r) {
|
||||
const Override *o = overrides.getptr(*r);
|
||||
if (o) {
|
||||
r_ret = o->override;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShaderGlobalsOverride::_get_property_list(List<PropertyInfo> *p_list) const {
|
||||
Vector<StringName> variables;
|
||||
variables = RS::get_singleton()->global_shader_parameter_get_list();
|
||||
for (int i = 0; i < variables.size(); i++) {
|
||||
PropertyInfo pinfo;
|
||||
pinfo.name = "params/" + variables[i];
|
||||
pinfo.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_CHECKABLE;
|
||||
|
||||
switch (RS::get_singleton()->global_shader_parameter_get_type(variables[i])) {
|
||||
case RS::GLOBAL_VAR_TYPE_BOOL: {
|
||||
pinfo.type = Variant::BOOL;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_BVEC2: {
|
||||
pinfo.type = Variant::INT;
|
||||
pinfo.hint = PROPERTY_HINT_FLAGS;
|
||||
pinfo.hint_string = "x,y";
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_BVEC3: {
|
||||
pinfo.type = Variant::INT;
|
||||
pinfo.hint = PROPERTY_HINT_FLAGS;
|
||||
pinfo.hint_string = "x,y,z";
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_BVEC4: {
|
||||
pinfo.type = Variant::INT;
|
||||
pinfo.hint = PROPERTY_HINT_FLAGS;
|
||||
pinfo.hint_string = "x,y,z,w";
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_INT: {
|
||||
pinfo.type = Variant::INT;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_IVEC2: {
|
||||
pinfo.type = Variant::VECTOR2I;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_IVEC3: {
|
||||
pinfo.type = Variant::VECTOR3I;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_IVEC4: {
|
||||
pinfo.type = Variant::VECTOR4I;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_RECT2I: {
|
||||
pinfo.type = Variant::RECT2I;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_UINT: {
|
||||
pinfo.type = Variant::INT;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_UVEC2: {
|
||||
pinfo.type = Variant::VECTOR2I;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_UVEC3: {
|
||||
pinfo.type = Variant::VECTOR3I;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_UVEC4: {
|
||||
pinfo.type = Variant::VECTOR4I;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_FLOAT: {
|
||||
pinfo.type = Variant::FLOAT;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_VEC2: {
|
||||
pinfo.type = Variant::VECTOR2;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_VEC3: {
|
||||
pinfo.type = Variant::VECTOR3;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_VEC4: {
|
||||
pinfo.type = Variant::VECTOR4;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_RECT2: {
|
||||
pinfo.type = Variant::RECT2;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_COLOR: {
|
||||
pinfo.type = Variant::COLOR;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_MAT2: {
|
||||
pinfo.type = Variant::PACKED_FLOAT32_ARRAY;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_MAT3: {
|
||||
pinfo.type = Variant::BASIS;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_MAT4: {
|
||||
pinfo.type = Variant::PROJECTION;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_TRANSFORM_2D: {
|
||||
pinfo.type = Variant::TRANSFORM2D;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_TRANSFORM: {
|
||||
pinfo.type = Variant::TRANSFORM3D;
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_SAMPLER2D: {
|
||||
pinfo.type = Variant::OBJECT;
|
||||
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
pinfo.hint_string = "Texture2D";
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_SAMPLER2DARRAY: {
|
||||
pinfo.type = Variant::OBJECT;
|
||||
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
pinfo.hint_string = "Texture2DArray";
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_SAMPLER3D: {
|
||||
pinfo.type = Variant::OBJECT;
|
||||
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
pinfo.hint_string = "Texture3D";
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_SAMPLERCUBE: {
|
||||
pinfo.type = Variant::OBJECT;
|
||||
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
pinfo.hint_string = "Cubemap";
|
||||
} break;
|
||||
case RS::GLOBAL_VAR_TYPE_SAMPLEREXT: {
|
||||
pinfo.type = Variant::OBJECT;
|
||||
pinfo.hint = PROPERTY_HINT_RESOURCE_TYPE;
|
||||
pinfo.hint_string = "ExternalTexture";
|
||||
} break;
|
||||
default: {
|
||||
} break;
|
||||
}
|
||||
|
||||
if (!overrides.has(variables[i])) {
|
||||
Override o;
|
||||
o.in_use = false;
|
||||
Callable::CallError ce;
|
||||
Variant::construct(pinfo.type, o.override, nullptr, 0, ce);
|
||||
overrides[variables[i]] = o;
|
||||
}
|
||||
|
||||
Override *o = overrides.getptr(variables[i]);
|
||||
if (o->in_use && o->override.get_type() != Variant::NIL) {
|
||||
pinfo.usage |= PROPERTY_USAGE_CHECKED;
|
||||
pinfo.usage |= PROPERTY_USAGE_STORAGE;
|
||||
}
|
||||
|
||||
p_list->push_back(pinfo);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderGlobalsOverride::_activate() {
|
||||
ERR_FAIL_NULL(get_tree());
|
||||
List<Node *> nodes;
|
||||
get_tree()->get_nodes_in_group(SceneStringName(shader_overrides_group_active), &nodes);
|
||||
if (nodes.is_empty()) {
|
||||
//good we are the only override, enable all
|
||||
active = true;
|
||||
add_to_group(SceneStringName(shader_overrides_group_active));
|
||||
|
||||
for (const KeyValue<StringName, Override> &E : overrides) {
|
||||
const Override *o = &E.value;
|
||||
if (o->in_use && o->override.get_type() != Variant::NIL) {
|
||||
if (o->override.get_type() == Variant::OBJECT) {
|
||||
RID tex_rid = o->override;
|
||||
RS::get_singleton()->global_shader_parameter_set_override(E.key, tex_rid);
|
||||
} else {
|
||||
RS::get_singleton()->global_shader_parameter_set_override(E.key, o->override);
|
||||
}
|
||||
}
|
||||
|
||||
update_configuration_warnings(); //may have activated
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderGlobalsOverride::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case Node::NOTIFICATION_ENTER_TREE: {
|
||||
add_to_group(SceneStringName(shader_overrides_group));
|
||||
_activate();
|
||||
} break;
|
||||
|
||||
case Node::NOTIFICATION_EXIT_TREE: {
|
||||
if (active) {
|
||||
//remove overrides
|
||||
for (const KeyValue<StringName, Override> &E : overrides) {
|
||||
const Override *o = &E.value;
|
||||
if (o->in_use) {
|
||||
RS::get_singleton()->global_shader_parameter_set_override(E.key, Variant());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove_from_group(SceneStringName(shader_overrides_group_active));
|
||||
remove_from_group(SceneStringName(shader_overrides_group));
|
||||
get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFERRED, SceneStringName(shader_overrides_group), "_activate"); //another may want to activate when this is removed
|
||||
active = false;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
PackedStringArray ShaderGlobalsOverride::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
|
||||
if (!active) {
|
||||
warnings.push_back(RTR("ShaderGlobalsOverride is not active because another node of the same type is in the scene."));
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void ShaderGlobalsOverride::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_activate"), &ShaderGlobalsOverride::_activate);
|
||||
}
|
61
scene/main/shader_globals_override.h
Normal file
61
scene/main/shader_globals_override.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/**************************************************************************/
|
||||
/* shader_globals_override.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class ShaderGlobalsOverride : public Node {
|
||||
GDCLASS(ShaderGlobalsOverride, Node);
|
||||
|
||||
struct Override {
|
||||
bool in_use = false;
|
||||
Variant override;
|
||||
};
|
||||
|
||||
StringName *_remap(const StringName &p_name) const;
|
||||
|
||||
bool active = false;
|
||||
mutable HashMap<StringName, Override> overrides;
|
||||
mutable HashMap<StringName, StringName> param_remaps;
|
||||
|
||||
void _activate();
|
||||
|
||||
protected:
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
};
|
192
scene/main/status_indicator.cpp
Normal file
192
scene/main/status_indicator.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
/**************************************************************************/
|
||||
/* status_indicator.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "status_indicator.h"
|
||||
|
||||
#include "scene/gui/popup_menu.h"
|
||||
|
||||
void StatusIndicator::_notification(int p_what) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (is_part_of_edited_scene()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_ENTER_TREE: {
|
||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_STATUS_INDICATOR)) {
|
||||
if (visible && iid == DisplayServer::INVALID_INDICATOR_ID) {
|
||||
iid = DisplayServer::get_singleton()->create_status_indicator(icon, tooltip, callable_mp(this, &StatusIndicator::_callback));
|
||||
PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
|
||||
if (pm) {
|
||||
RID menu_rid = pm->bind_global_menu();
|
||||
DisplayServer::get_singleton()->status_indicator_set_menu(iid, menu_rid);
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case NOTIFICATION_EXIT_TREE: {
|
||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_STATUS_INDICATOR)) {
|
||||
if (iid != DisplayServer::INVALID_INDICATOR_ID) {
|
||||
PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
|
||||
if (pm) {
|
||||
pm->unbind_global_menu();
|
||||
DisplayServer::get_singleton()->status_indicator_set_menu(iid, RID());
|
||||
}
|
||||
DisplayServer::get_singleton()->delete_status_indicator(iid);
|
||||
iid = DisplayServer::INVALID_INDICATOR_ID;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void StatusIndicator::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_tooltip", "tooltip"), &StatusIndicator::set_tooltip);
|
||||
ClassDB::bind_method(D_METHOD("get_tooltip"), &StatusIndicator::get_tooltip);
|
||||
ClassDB::bind_method(D_METHOD("set_icon", "texture"), &StatusIndicator::set_icon);
|
||||
ClassDB::bind_method(D_METHOD("get_icon"), &StatusIndicator::get_icon);
|
||||
ClassDB::bind_method(D_METHOD("set_visible", "visible"), &StatusIndicator::set_visible);
|
||||
ClassDB::bind_method(D_METHOD("is_visible"), &StatusIndicator::is_visible);
|
||||
ClassDB::bind_method(D_METHOD("set_menu", "menu"), &StatusIndicator::set_menu);
|
||||
ClassDB::bind_method(D_METHOD("get_menu"), &StatusIndicator::get_menu);
|
||||
ClassDB::bind_method(D_METHOD("get_rect"), &StatusIndicator::get_rect);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::INT, "mouse_button"), PropertyInfo(Variant::VECTOR2I, "mouse_position")));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "get_tooltip");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_icon", "get_icon");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "menu", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PopupMenu"), "set_menu", "get_menu");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
|
||||
}
|
||||
|
||||
void StatusIndicator::_callback(MouseButton p_index, const Point2i &p_pos) {
|
||||
emit_signal(SceneStringName(pressed), p_index, p_pos);
|
||||
}
|
||||
|
||||
void StatusIndicator::set_icon(const Ref<Texture2D> &p_icon) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
icon = p_icon;
|
||||
if (iid != DisplayServer::INVALID_INDICATOR_ID) {
|
||||
DisplayServer::get_singleton()->status_indicator_set_icon(iid, icon);
|
||||
}
|
||||
}
|
||||
|
||||
Ref<Texture2D> StatusIndicator::get_icon() const {
|
||||
return icon;
|
||||
}
|
||||
|
||||
void StatusIndicator::set_tooltip(const String &p_tooltip) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
tooltip = p_tooltip;
|
||||
if (iid != DisplayServer::INVALID_INDICATOR_ID) {
|
||||
DisplayServer::get_singleton()->status_indicator_set_tooltip(iid, tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
String StatusIndicator::get_tooltip() const {
|
||||
return tooltip;
|
||||
}
|
||||
|
||||
void StatusIndicator::set_menu(const NodePath &p_menu) {
|
||||
PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
|
||||
if (pm) {
|
||||
pm->unbind_global_menu();
|
||||
if (iid != DisplayServer::INVALID_INDICATOR_ID) {
|
||||
DisplayServer::get_singleton()->status_indicator_set_menu(iid, RID());
|
||||
}
|
||||
}
|
||||
|
||||
menu = p_menu;
|
||||
|
||||
pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
|
||||
if (pm) {
|
||||
if (iid != DisplayServer::INVALID_INDICATOR_ID) {
|
||||
RID menu_rid = pm->bind_global_menu();
|
||||
DisplayServer::get_singleton()->status_indicator_set_menu(iid, menu_rid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodePath StatusIndicator::get_menu() const {
|
||||
return menu;
|
||||
}
|
||||
|
||||
void StatusIndicator::set_visible(bool p_visible) {
|
||||
ERR_MAIN_THREAD_GUARD;
|
||||
if (visible == p_visible) {
|
||||
return;
|
||||
}
|
||||
visible = p_visible;
|
||||
|
||||
if (!is_inside_tree()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (is_part_of_edited_scene()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_STATUS_INDICATOR)) {
|
||||
if (visible && iid == DisplayServer::INVALID_INDICATOR_ID) {
|
||||
iid = DisplayServer::get_singleton()->create_status_indicator(icon, tooltip, callable_mp(this, &StatusIndicator::_callback));
|
||||
PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
|
||||
if (pm) {
|
||||
RID menu_rid = pm->bind_global_menu();
|
||||
DisplayServer::get_singleton()->status_indicator_set_menu(iid, menu_rid);
|
||||
}
|
||||
}
|
||||
if (!visible && iid != DisplayServer::INVALID_INDICATOR_ID) {
|
||||
PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(menu));
|
||||
if (pm) {
|
||||
pm->unbind_global_menu();
|
||||
DisplayServer::get_singleton()->status_indicator_set_menu(iid, RID());
|
||||
}
|
||||
DisplayServer::get_singleton()->delete_status_indicator(iid);
|
||||
iid = DisplayServer::INVALID_INDICATOR_ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool StatusIndicator::is_visible() const {
|
||||
return visible;
|
||||
}
|
||||
|
||||
Rect2 StatusIndicator::get_rect() const {
|
||||
if (iid == DisplayServer::INVALID_INDICATOR_ID) {
|
||||
return Rect2();
|
||||
}
|
||||
return DisplayServer::get_singleton()->status_indicator_get_rect(iid);
|
||||
}
|
65
scene/main/status_indicator.h
Normal file
65
scene/main/status_indicator.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/**************************************************************************/
|
||||
/* status_indicator.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/main/node.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
class StatusIndicator : public Node {
|
||||
GDCLASS(StatusIndicator, Node);
|
||||
|
||||
Ref<Texture2D> icon;
|
||||
String tooltip;
|
||||
bool visible = true;
|
||||
DisplayServer::IndicatorID iid = DisplayServer::INVALID_INDICATOR_ID;
|
||||
NodePath menu;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
void _callback(MouseButton p_index, const Point2i &p_pos);
|
||||
|
||||
public:
|
||||
void set_icon(const Ref<Texture2D> &p_icon);
|
||||
Ref<Texture2D> get_icon() const;
|
||||
|
||||
void set_tooltip(const String &p_tooltip);
|
||||
String get_tooltip() const;
|
||||
|
||||
void set_menu(const NodePath &p_menu);
|
||||
NodePath get_menu() const;
|
||||
|
||||
void set_visible(bool p_visible);
|
||||
bool is_visible() const;
|
||||
|
||||
Rect2 get_rect() const;
|
||||
};
|
247
scene/main/timer.cpp
Normal file
247
scene/main/timer.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
/**************************************************************************/
|
||||
/* timer.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
void Timer::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
if (autostart) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (is_part_of_edited_scene()) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
start();
|
||||
autostart = false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PROCESS: {
|
||||
if (!processing || timer_process_callback == TIMER_PROCESS_PHYSICS || !is_processing_internal()) {
|
||||
return;
|
||||
}
|
||||
if (ignore_time_scale) {
|
||||
time_left -= Engine::get_singleton()->get_process_step();
|
||||
} else {
|
||||
time_left -= get_process_delta_time();
|
||||
}
|
||||
|
||||
if (time_left < 0) {
|
||||
if (!one_shot) {
|
||||
time_left += wait_time;
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
|
||||
emit_signal(SNAME("timeout"));
|
||||
}
|
||||
} break;
|
||||
|
||||
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
|
||||
if (!processing || timer_process_callback == TIMER_PROCESS_IDLE || !is_physics_processing_internal()) {
|
||||
return;
|
||||
}
|
||||
if (ignore_time_scale) {
|
||||
time_left -= Engine::get_singleton()->get_process_step();
|
||||
} else {
|
||||
time_left -= get_physics_process_delta_time();
|
||||
}
|
||||
|
||||
if (time_left < 0) {
|
||||
if (!one_shot) {
|
||||
time_left += wait_time;
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
emit_signal(SNAME("timeout"));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::set_wait_time(double p_time) {
|
||||
ERR_FAIL_COND_MSG(p_time <= 0, "Time should be greater than zero.");
|
||||
wait_time = p_time;
|
||||
update_configuration_warnings();
|
||||
}
|
||||
|
||||
double Timer::get_wait_time() const {
|
||||
return wait_time;
|
||||
}
|
||||
|
||||
void Timer::set_one_shot(bool p_one_shot) {
|
||||
one_shot = p_one_shot;
|
||||
}
|
||||
|
||||
bool Timer::is_one_shot() const {
|
||||
return one_shot;
|
||||
}
|
||||
|
||||
void Timer::set_autostart(bool p_start) {
|
||||
autostart = p_start;
|
||||
}
|
||||
|
||||
bool Timer::has_autostart() const {
|
||||
return autostart;
|
||||
}
|
||||
|
||||
void Timer::start(double p_time) {
|
||||
ERR_FAIL_COND_MSG(!is_inside_tree(), "Unable to start the timer because it's not inside the scene tree. Either add it or set autostart to true.");
|
||||
|
||||
if (p_time > 0) {
|
||||
set_wait_time(p_time);
|
||||
}
|
||||
time_left = wait_time;
|
||||
_set_process(true);
|
||||
}
|
||||
|
||||
void Timer::stop() {
|
||||
time_left = -1;
|
||||
_set_process(false);
|
||||
autostart = false;
|
||||
}
|
||||
|
||||
void Timer::set_paused(bool p_paused) {
|
||||
if (paused == p_paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
paused = p_paused;
|
||||
_set_process(processing);
|
||||
}
|
||||
|
||||
bool Timer::is_paused() const {
|
||||
return paused;
|
||||
}
|
||||
|
||||
void Timer::set_ignore_time_scale(bool p_ignore) {
|
||||
ignore_time_scale = p_ignore;
|
||||
}
|
||||
|
||||
bool Timer::is_ignoring_time_scale() {
|
||||
return ignore_time_scale;
|
||||
}
|
||||
|
||||
bool Timer::is_stopped() const {
|
||||
return get_time_left() <= 0;
|
||||
}
|
||||
|
||||
double Timer::get_time_left() const {
|
||||
return time_left > 0 ? time_left : 0;
|
||||
}
|
||||
|
||||
void Timer::set_timer_process_callback(TimerProcessCallback p_callback) {
|
||||
if (timer_process_callback == p_callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (timer_process_callback) {
|
||||
case TIMER_PROCESS_PHYSICS:
|
||||
if (is_physics_processing_internal()) {
|
||||
set_physics_process_internal(false);
|
||||
set_process_internal(true);
|
||||
}
|
||||
break;
|
||||
case TIMER_PROCESS_IDLE:
|
||||
if (is_processing_internal()) {
|
||||
set_process_internal(false);
|
||||
set_physics_process_internal(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
timer_process_callback = p_callback;
|
||||
}
|
||||
|
||||
Timer::TimerProcessCallback Timer::get_timer_process_callback() const {
|
||||
return timer_process_callback;
|
||||
}
|
||||
|
||||
void Timer::_set_process(bool p_process, bool p_force) {
|
||||
switch (timer_process_callback) {
|
||||
case TIMER_PROCESS_PHYSICS:
|
||||
set_physics_process_internal(p_process && !paused);
|
||||
break;
|
||||
case TIMER_PROCESS_IDLE:
|
||||
set_process_internal(p_process && !paused);
|
||||
break;
|
||||
}
|
||||
processing = p_process;
|
||||
}
|
||||
|
||||
PackedStringArray Timer::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
|
||||
if (wait_time < 0.05 - CMP_EPSILON) {
|
||||
warnings.push_back(RTR("Very low timer wait times (< 0.05 seconds) may behave in significantly different ways depending on the rendered or physics frame rate.\nConsider using a script's process loop instead of relying on a Timer for very low wait times."));
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
void Timer::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_wait_time", "time_sec"), &Timer::set_wait_time);
|
||||
ClassDB::bind_method(D_METHOD("get_wait_time"), &Timer::get_wait_time);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_one_shot", "enable"), &Timer::set_one_shot);
|
||||
ClassDB::bind_method(D_METHOD("is_one_shot"), &Timer::is_one_shot);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_autostart", "enable"), &Timer::set_autostart);
|
||||
ClassDB::bind_method(D_METHOD("has_autostart"), &Timer::has_autostart);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("start", "time_sec"), &Timer::start, DEFVAL(-1));
|
||||
ClassDB::bind_method(D_METHOD("stop"), &Timer::stop);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_paused", "paused"), &Timer::set_paused);
|
||||
ClassDB::bind_method(D_METHOD("is_paused"), &Timer::is_paused);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_ignore_time_scale", "ignore"), &Timer::set_ignore_time_scale);
|
||||
ClassDB::bind_method(D_METHOD("is_ignoring_time_scale"), &Timer::is_ignoring_time_scale);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_stopped"), &Timer::is_stopped);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_time_left"), &Timer::get_time_left);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_timer_process_callback", "callback"), &Timer::set_timer_process_callback);
|
||||
ClassDB::bind_method(D_METHOD("get_timer_process_callback"), &Timer::get_timer_process_callback);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("timeout"));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_timer_process_callback", "get_timer_process_callback");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wait_time", PROPERTY_HINT_RANGE, "0.001,4096,0.001,or_greater,exp,suffix:s"), "set_wait_time", "get_wait_time");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "is_one_shot");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autostart"), "set_autostart", "has_autostart");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "paused", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_paused", "is_paused");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_time_scale"), "set_ignore_time_scale", "is_ignoring_time_scale");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_left", PROPERTY_HINT_NONE, "suffix:s", PROPERTY_USAGE_NONE), "", "get_time_left");
|
||||
|
||||
BIND_ENUM_CONSTANT(TIMER_PROCESS_PHYSICS);
|
||||
BIND_ENUM_CONSTANT(TIMER_PROCESS_IDLE);
|
||||
}
|
89
scene/main/timer.h
Normal file
89
scene/main/timer.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/**************************************************************************/
|
||||
/* timer.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/main/node.h"
|
||||
|
||||
class Timer : public Node {
|
||||
GDCLASS(Timer, Node);
|
||||
|
||||
double wait_time = 1.0;
|
||||
bool one_shot = false;
|
||||
bool autostart = false;
|
||||
bool processing = false;
|
||||
bool paused = false;
|
||||
bool ignore_time_scale = false;
|
||||
|
||||
double time_left = -1.0;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
enum TimerProcessCallback {
|
||||
TIMER_PROCESS_PHYSICS,
|
||||
TIMER_PROCESS_IDLE,
|
||||
};
|
||||
|
||||
void set_wait_time(double p_time);
|
||||
double get_wait_time() const;
|
||||
|
||||
void set_one_shot(bool p_one_shot);
|
||||
bool is_one_shot() const;
|
||||
|
||||
void set_autostart(bool p_start);
|
||||
bool has_autostart() const;
|
||||
|
||||
void start(double p_time = -1);
|
||||
void stop();
|
||||
|
||||
void set_paused(bool p_paused);
|
||||
bool is_paused() const;
|
||||
|
||||
void set_ignore_time_scale(bool p_ignore);
|
||||
bool is_ignoring_time_scale();
|
||||
|
||||
bool is_stopped() const;
|
||||
|
||||
double get_time_left() const;
|
||||
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
void set_timer_process_callback(TimerProcessCallback p_callback);
|
||||
TimerProcessCallback get_timer_process_callback() const;
|
||||
|
||||
private:
|
||||
TimerProcessCallback timer_process_callback = TIMER_PROCESS_IDLE;
|
||||
void _set_process(bool p_process, bool p_force = false);
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(Timer::TimerProcessCallback);
|
5601
scene/main/viewport.cpp
Normal file
5601
scene/main/viewport.cpp
Normal file
File diff suppressed because it is too large
Load Diff
930
scene/main/viewport.h
Normal file
930
scene/main/viewport.h
Normal file
@@ -0,0 +1,930 @@
|
||||
/**************************************************************************/
|
||||
/* viewport.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/main/node.h"
|
||||
#include "scene/resources/texture.h"
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
class Camera3D;
|
||||
class CollisionObject3D;
|
||||
class AudioListener3D;
|
||||
class World3D;
|
||||
#endif // _3D_DISABLED
|
||||
|
||||
class AudioListener2D;
|
||||
class Camera2D;
|
||||
class CanvasItem;
|
||||
class CanvasLayer;
|
||||
class Control;
|
||||
class Label;
|
||||
class SceneTreeTimer;
|
||||
class Viewport;
|
||||
class Window;
|
||||
class World2D;
|
||||
|
||||
class ViewportTexture : public Texture2D {
|
||||
GDCLASS(ViewportTexture, Texture2D);
|
||||
|
||||
NodePath path;
|
||||
|
||||
friend class Viewport;
|
||||
Viewport *vp = nullptr;
|
||||
bool vp_pending = false;
|
||||
bool vp_changed = false;
|
||||
|
||||
void _setup_local_to_scene(const Node *p_loc_scene);
|
||||
void _err_print_viewport_not_set() const;
|
||||
|
||||
mutable RID proxy_ph;
|
||||
mutable RID proxy;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
virtual void reset_local_to_scene() override;
|
||||
|
||||
public:
|
||||
void set_viewport_path_in_scene(const NodePath &p_path);
|
||||
NodePath get_viewport_path_in_scene() const;
|
||||
|
||||
virtual void setup_local_to_scene() override;
|
||||
|
||||
virtual int get_width() const override;
|
||||
virtual int get_height() const override;
|
||||
virtual Size2 get_size() const override;
|
||||
virtual RID get_rid() const override;
|
||||
|
||||
virtual bool has_alpha() const override;
|
||||
|
||||
virtual Ref<Image> get_image() const override;
|
||||
|
||||
ViewportTexture();
|
||||
~ViewportTexture();
|
||||
};
|
||||
|
||||
class Viewport : public Node {
|
||||
GDCLASS(Viewport, Node);
|
||||
|
||||
public:
|
||||
enum Scaling3DMode {
|
||||
SCALING_3D_MODE_BILINEAR,
|
||||
SCALING_3D_MODE_FSR,
|
||||
SCALING_3D_MODE_FSR2,
|
||||
SCALING_3D_MODE_METALFX_SPATIAL,
|
||||
SCALING_3D_MODE_METALFX_TEMPORAL,
|
||||
SCALING_3D_MODE_MAX
|
||||
};
|
||||
|
||||
enum PositionalShadowAtlasQuadrantSubdiv {
|
||||
SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED,
|
||||
SHADOW_ATLAS_QUADRANT_SUBDIV_1,
|
||||
SHADOW_ATLAS_QUADRANT_SUBDIV_4,
|
||||
SHADOW_ATLAS_QUADRANT_SUBDIV_16,
|
||||
SHADOW_ATLAS_QUADRANT_SUBDIV_64,
|
||||
SHADOW_ATLAS_QUADRANT_SUBDIV_256,
|
||||
SHADOW_ATLAS_QUADRANT_SUBDIV_1024,
|
||||
SHADOW_ATLAS_QUADRANT_SUBDIV_MAX,
|
||||
};
|
||||
|
||||
enum MSAA {
|
||||
MSAA_DISABLED,
|
||||
MSAA_2X,
|
||||
MSAA_4X,
|
||||
MSAA_8X,
|
||||
// 16x MSAA is not supported due to its high cost and driver bugs.
|
||||
MSAA_MAX
|
||||
};
|
||||
|
||||
enum AnisotropicFiltering {
|
||||
ANISOTROPY_DISABLED,
|
||||
ANISOTROPY_2X,
|
||||
ANISOTROPY_4X,
|
||||
ANISOTROPY_8X,
|
||||
ANISOTROPY_16X,
|
||||
ANISOTROPY_MAX
|
||||
};
|
||||
|
||||
enum ScreenSpaceAA {
|
||||
SCREEN_SPACE_AA_DISABLED,
|
||||
SCREEN_SPACE_AA_FXAA,
|
||||
SCREEN_SPACE_AA_SMAA,
|
||||
SCREEN_SPACE_AA_MAX
|
||||
};
|
||||
|
||||
enum RenderInfo {
|
||||
RENDER_INFO_OBJECTS_IN_FRAME,
|
||||
RENDER_INFO_PRIMITIVES_IN_FRAME,
|
||||
RENDER_INFO_DRAW_CALLS_IN_FRAME,
|
||||
RENDER_INFO_MAX
|
||||
};
|
||||
|
||||
enum RenderInfoType {
|
||||
RENDER_INFO_TYPE_VISIBLE,
|
||||
RENDER_INFO_TYPE_SHADOW,
|
||||
RENDER_INFO_TYPE_CANVAS,
|
||||
RENDER_INFO_TYPE_MAX
|
||||
};
|
||||
|
||||
enum DebugDraw {
|
||||
DEBUG_DRAW_DISABLED,
|
||||
DEBUG_DRAW_UNSHADED,
|
||||
DEBUG_DRAW_LIGHTING,
|
||||
DEBUG_DRAW_OVERDRAW,
|
||||
DEBUG_DRAW_WIREFRAME,
|
||||
DEBUG_DRAW_NORMAL_BUFFER,
|
||||
DEBUG_DRAW_VOXEL_GI_ALBEDO,
|
||||
DEBUG_DRAW_VOXEL_GI_LIGHTING,
|
||||
DEBUG_DRAW_VOXEL_GI_EMISSION,
|
||||
DEBUG_DRAW_SHADOW_ATLAS,
|
||||
DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS,
|
||||
DEBUG_DRAW_SCENE_LUMINANCE,
|
||||
DEBUG_DRAW_SSAO,
|
||||
DEBUG_DRAW_SSIL,
|
||||
DEBUG_DRAW_PSSM_SPLITS,
|
||||
DEBUG_DRAW_DECAL_ATLAS,
|
||||
DEBUG_DRAW_SDFGI,
|
||||
DEBUG_DRAW_SDFGI_PROBES,
|
||||
DEBUG_DRAW_GI_BUFFER,
|
||||
DEBUG_DRAW_DISABLE_LOD,
|
||||
DEBUG_DRAW_CLUSTER_OMNI_LIGHTS,
|
||||
DEBUG_DRAW_CLUSTER_SPOT_LIGHTS,
|
||||
DEBUG_DRAW_CLUSTER_DECALS,
|
||||
DEBUG_DRAW_CLUSTER_REFLECTION_PROBES,
|
||||
DEBUG_DRAW_OCCLUDERS,
|
||||
DEBUG_DRAW_MOTION_VECTORS,
|
||||
DEBUG_DRAW_INTERNAL_BUFFER,
|
||||
};
|
||||
|
||||
enum DefaultCanvasItemTextureFilter {
|
||||
DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST,
|
||||
DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR,
|
||||
DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS,
|
||||
DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS,
|
||||
DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX
|
||||
};
|
||||
|
||||
enum DefaultCanvasItemTextureRepeat {
|
||||
DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED,
|
||||
DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED,
|
||||
DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR,
|
||||
DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX,
|
||||
};
|
||||
|
||||
enum SDFOversize {
|
||||
SDF_OVERSIZE_100_PERCENT,
|
||||
SDF_OVERSIZE_120_PERCENT,
|
||||
SDF_OVERSIZE_150_PERCENT,
|
||||
SDF_OVERSIZE_200_PERCENT,
|
||||
SDF_OVERSIZE_MAX
|
||||
};
|
||||
|
||||
enum SDFScale {
|
||||
SDF_SCALE_100_PERCENT,
|
||||
SDF_SCALE_50_PERCENT,
|
||||
SDF_SCALE_25_PERCENT,
|
||||
SDF_SCALE_MAX
|
||||
};
|
||||
|
||||
enum {
|
||||
SUBWINDOW_CANVAS_LAYER = 1024
|
||||
};
|
||||
|
||||
enum VRSMode {
|
||||
VRS_DISABLED,
|
||||
VRS_TEXTURE,
|
||||
VRS_XR,
|
||||
VRS_MAX
|
||||
};
|
||||
|
||||
enum VRSUpdateMode {
|
||||
VRS_UPDATE_DISABLED,
|
||||
VRS_UPDATE_ONCE,
|
||||
VRS_UPDATE_ALWAYS,
|
||||
VRS_UPDATE_MAX
|
||||
};
|
||||
|
||||
private:
|
||||
friend class ViewportTexture;
|
||||
|
||||
Viewport *parent = nullptr;
|
||||
Viewport *gui_parent = nullptr; // Whose gui.tooltip_popup it is.
|
||||
|
||||
HashSet<CanvasLayer *> canvas_layers;
|
||||
|
||||
bool use_font_oversampling = true;
|
||||
float font_oversampling = 1.0;
|
||||
float font_oversampling_override = 0.0;
|
||||
|
||||
RID viewport;
|
||||
RID current_canvas;
|
||||
RID subwindow_canvas;
|
||||
|
||||
bool override_canvas_transform = false;
|
||||
|
||||
Transform2D canvas_transform_override;
|
||||
Transform2D canvas_transform;
|
||||
Transform2D global_canvas_transform;
|
||||
Transform2D stretch_transform;
|
||||
|
||||
Size2i size = Size2i(512, 512);
|
||||
Size2 size_2d_override;
|
||||
bool size_allocated = false;
|
||||
|
||||
RID contact_2d_debug;
|
||||
RID contact_3d_debug_multimesh;
|
||||
RID contact_3d_debug_instance;
|
||||
|
||||
Rect2 last_vp_rect;
|
||||
|
||||
bool transparent_bg = false;
|
||||
bool use_hdr_2d = false;
|
||||
bool gen_mipmaps = false;
|
||||
|
||||
bool snap_controls_to_pixels = true;
|
||||
bool snap_2d_transforms_to_pixel = false;
|
||||
bool snap_2d_vertices_to_pixel = false;
|
||||
|
||||
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
|
||||
bool physics_object_picking = false;
|
||||
bool physics_object_picking_sort = false;
|
||||
bool physics_object_picking_first_only = false;
|
||||
List<Ref<InputEvent>> physics_picking_events;
|
||||
ObjectID physics_object_capture;
|
||||
ObjectID physics_object_over;
|
||||
Transform3D physics_last_object_transform;
|
||||
Transform3D physics_last_camera_transform;
|
||||
ObjectID physics_last_id;
|
||||
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
|
||||
|
||||
bool handle_input_locally = true;
|
||||
bool local_input_handled = false;
|
||||
|
||||
Ref<World2D> world_2d;
|
||||
|
||||
StringName input_group;
|
||||
StringName shortcut_input_group;
|
||||
StringName unhandled_input_group;
|
||||
StringName unhandled_key_input_group;
|
||||
|
||||
void _update_audio_listener_2d();
|
||||
|
||||
bool disable_3d = false;
|
||||
|
||||
static void _propagate_drag_notification(Node *p_node, int p_what);
|
||||
|
||||
void _update_global_transform();
|
||||
|
||||
RID texture_rid;
|
||||
|
||||
DebugDraw debug_draw = DEBUG_DRAW_DISABLED;
|
||||
|
||||
int positional_shadow_atlas_size = 2048;
|
||||
bool positional_shadow_atlas_16_bits = true;
|
||||
PositionalShadowAtlasQuadrantSubdiv positional_shadow_atlas_quadrant_subdiv[4];
|
||||
|
||||
MSAA msaa_2d = MSAA_DISABLED;
|
||||
MSAA msaa_3d = MSAA_DISABLED;
|
||||
ScreenSpaceAA screen_space_aa = SCREEN_SPACE_AA_DISABLED;
|
||||
bool use_taa = false;
|
||||
|
||||
Scaling3DMode scaling_3d_mode = SCALING_3D_MODE_BILINEAR;
|
||||
float scaling_3d_scale = 1.0;
|
||||
float fsr_sharpness = 0.2f;
|
||||
float texture_mipmap_bias = 0.0f;
|
||||
AnisotropicFiltering anisotropic_filtering_level = ANISOTROPY_4X;
|
||||
bool use_debanding = false;
|
||||
float mesh_lod_threshold = 1.0;
|
||||
bool use_occlusion_culling = false;
|
||||
|
||||
Ref<ViewportTexture> default_texture;
|
||||
HashSet<ViewportTexture *> viewport_textures;
|
||||
|
||||
void _update_viewport_path();
|
||||
|
||||
SDFOversize sdf_oversize = SDF_OVERSIZE_120_PERCENT;
|
||||
SDFScale sdf_scale = SDF_SCALE_50_PERCENT;
|
||||
|
||||
uint32_t canvas_cull_mask = 0xffffffff; // by default show everything
|
||||
|
||||
enum SubWindowDrag {
|
||||
SUB_WINDOW_DRAG_DISABLED,
|
||||
SUB_WINDOW_DRAG_MOVE,
|
||||
SUB_WINDOW_DRAG_CLOSE,
|
||||
SUB_WINDOW_DRAG_RESIZE,
|
||||
};
|
||||
|
||||
enum SubWindowResize {
|
||||
SUB_WINDOW_RESIZE_DISABLED,
|
||||
SUB_WINDOW_RESIZE_TOP_LEFT,
|
||||
SUB_WINDOW_RESIZE_TOP,
|
||||
SUB_WINDOW_RESIZE_TOP_RIGHT,
|
||||
SUB_WINDOW_RESIZE_LEFT,
|
||||
SUB_WINDOW_RESIZE_RIGHT,
|
||||
SUB_WINDOW_RESIZE_BOTTOM_LEFT,
|
||||
SUB_WINDOW_RESIZE_BOTTOM,
|
||||
SUB_WINDOW_RESIZE_BOTTOM_RIGHT,
|
||||
SUB_WINDOW_RESIZE_MAX
|
||||
};
|
||||
|
||||
struct SubWindow {
|
||||
Window *window = nullptr;
|
||||
RID canvas_item;
|
||||
Rect2i parent_safe_rect;
|
||||
bool pending_window_update = false;
|
||||
};
|
||||
|
||||
// VRS
|
||||
VRSMode vrs_mode = VRS_DISABLED;
|
||||
VRSUpdateMode vrs_update_mode = VRS_UPDATE_ONCE;
|
||||
Ref<Texture2D> vrs_texture;
|
||||
|
||||
struct GUI {
|
||||
bool mouse_in_viewport = false;
|
||||
HashMap<int, ObjectID> touch_focus;
|
||||
Control *mouse_focus = nullptr;
|
||||
Control *mouse_click_grabber = nullptr;
|
||||
BitField<MouseButtonMask> mouse_focus_mask = MouseButtonMask::NONE;
|
||||
Control *key_focus = nullptr;
|
||||
Control *mouse_over = nullptr;
|
||||
LocalVector<Control *> mouse_over_hierarchy;
|
||||
bool sending_mouse_enter_exit_notifications = false;
|
||||
Window *subwindow_over = nullptr; // mouse_over and subwindow_over are mutually exclusive. At all times at least one of them is nullptr.
|
||||
Window *windowmanager_window_over = nullptr; // Only used in root Viewport.
|
||||
Control *drag_mouse_over = nullptr;
|
||||
Control *tooltip_control = nullptr;
|
||||
Window *tooltip_popup = nullptr;
|
||||
Label *tooltip_label = nullptr;
|
||||
String tooltip_text;
|
||||
Point2 tooltip_pos;
|
||||
Point2 last_mouse_pos;
|
||||
Point2 drag_accum;
|
||||
bool drag_attempted = false;
|
||||
Variant drag_data; // Only used in root-Viewport and SubViewports, that are not children of a SubViewportContainer.
|
||||
ObjectID drag_preview_id;
|
||||
String drag_description;
|
||||
Ref<SceneTreeTimer> tooltip_timer;
|
||||
double tooltip_delay = 0.0;
|
||||
bool roots_order_dirty = false;
|
||||
List<Control *> roots;
|
||||
HashSet<ObjectID> canvas_parents_with_dirty_order;
|
||||
int canvas_sort_index = 0; //for sorting items with canvas as root
|
||||
bool dragging = false; // Is true in the viewport in which dragging started while dragging is active.
|
||||
bool global_dragging = false; // Is true while dragging is active. Only used in root-Viewport and SubViewports that are not children of a SubViewportContainer.
|
||||
bool drag_successful = false;
|
||||
Control *target_control = nullptr; // Control that the mouse is over in the innermost nested Viewport. Only used in root-Viewport and SubViewports, that are not children of a SubViewportContainer.
|
||||
bool embed_subwindows_hint = false;
|
||||
|
||||
Window *subwindow_focused = nullptr;
|
||||
Window *currently_dragged_subwindow = nullptr;
|
||||
SubWindowDrag subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
|
||||
Vector2 subwindow_drag_from;
|
||||
Vector2 subwindow_drag_pos;
|
||||
Rect2i subwindow_drag_close_rect;
|
||||
bool subwindow_drag_close_inside = false;
|
||||
SubWindowResize subwindow_resize_mode;
|
||||
Rect2i subwindow_resize_from_rect;
|
||||
|
||||
Vector<SubWindow> sub_windows; // Don't obtain references or pointers to the elements, as their location can change.
|
||||
} gui;
|
||||
|
||||
DefaultCanvasItemTextureFilter default_canvas_item_texture_filter = DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
|
||||
DefaultCanvasItemTextureRepeat default_canvas_item_texture_repeat = DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
|
||||
|
||||
bool disable_input = false;
|
||||
bool disable_input_override = false;
|
||||
|
||||
void _gui_call_input(Control *p_control, const Ref<InputEvent> &p_input);
|
||||
void _gui_call_notification(Control *p_control, int p_what);
|
||||
|
||||
void _gui_sort_roots();
|
||||
Control *_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform);
|
||||
|
||||
void _gui_input_event(Ref<InputEvent> p_event);
|
||||
void _perform_drop(Control *p_control = nullptr);
|
||||
void _gui_cleanup_internal_state(Ref<InputEvent> p_event);
|
||||
|
||||
void _push_unhandled_input_internal(const Ref<InputEvent> &p_event);
|
||||
|
||||
Ref<InputEvent> _make_input_local(const Ref<InputEvent> &ev);
|
||||
|
||||
friend class Control;
|
||||
|
||||
List<Control *>::Element *_gui_add_root_control(Control *p_control);
|
||||
|
||||
void _gui_remove_root_control(List<Control *>::Element *RI);
|
||||
|
||||
String _gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_tooltip_owner = nullptr);
|
||||
void _gui_cancel_tooltip();
|
||||
void _gui_show_tooltip();
|
||||
void _gui_show_tooltip_at(const Point2i &p_pos);
|
||||
|
||||
void _gui_remove_control(Control *p_control);
|
||||
void _gui_hide_control(Control *p_control);
|
||||
void _gui_update_mouse_over();
|
||||
|
||||
void _gui_force_drag_start();
|
||||
void _gui_force_drag_cancel();
|
||||
void _gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control);
|
||||
void _gui_set_drag_preview(Control *p_base, Control *p_control);
|
||||
Control *_gui_get_drag_preview();
|
||||
|
||||
void _gui_remove_focus_for_window(Node *p_window);
|
||||
void _gui_unfocus_control(Control *p_control);
|
||||
bool _gui_control_has_focus(const Control *p_control);
|
||||
void _gui_control_grab_focus(Control *p_control);
|
||||
void _gui_grab_click_focus(Control *p_control);
|
||||
void _post_gui_grab_click_focus();
|
||||
void _gui_accept_event();
|
||||
|
||||
bool _gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check);
|
||||
|
||||
friend class CanvasLayer;
|
||||
void _canvas_layer_add(CanvasLayer *p_canvas_layer);
|
||||
void _canvas_layer_remove(CanvasLayer *p_canvas_layer);
|
||||
|
||||
void _drop_mouse_over(Control *p_until_control = nullptr);
|
||||
void _drop_mouse_focus();
|
||||
void _drop_physics_mouseover(bool p_paused_only = false);
|
||||
|
||||
void _update_canvas_items(Node *p_node);
|
||||
|
||||
friend class Window;
|
||||
|
||||
void _sub_window_update_order();
|
||||
void _sub_window_register(Window *p_window);
|
||||
void _sub_window_update(Window *p_window);
|
||||
void _sub_window_grab_focus(Window *p_window);
|
||||
void _sub_window_remove(Window *p_window);
|
||||
int _sub_window_find(Window *p_window) const;
|
||||
bool _sub_windows_forward_input(const Ref<InputEvent> &p_event);
|
||||
SubWindowResize _sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point);
|
||||
|
||||
void _update_mouse_over();
|
||||
virtual void _update_mouse_over(Vector2 p_pos);
|
||||
virtual void _mouse_leave_viewport();
|
||||
|
||||
virtual bool _can_consume_input_events() const { return true; }
|
||||
uint64_t event_count = 0;
|
||||
|
||||
void _process_dirty_canvas_parent_orders();
|
||||
void _propagate_world_2d_changed(Node *p_node);
|
||||
|
||||
void _window_start_drag(Window *p_window);
|
||||
void _window_start_resize(SubWindowResize p_edge, Window *p_window);
|
||||
|
||||
protected:
|
||||
bool _set_size(const Size2i &p_size, const Size2 &p_size_2d_override, bool p_allocated);
|
||||
|
||||
Size2i _get_size() const;
|
||||
Size2 _get_size_2d_override() const;
|
||||
bool _is_size_allocated() const;
|
||||
|
||||
void _notification(int p_what);
|
||||
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
|
||||
void _process_picking();
|
||||
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
|
||||
static void _bind_methods();
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
public:
|
||||
void canvas_parent_mark_dirty(Node *p_node);
|
||||
void canvas_item_top_level_changed();
|
||||
|
||||
uint64_t get_processed_events_count() const { return event_count; }
|
||||
|
||||
void cancel_tooltip();
|
||||
void show_tooltip(Control *p_control);
|
||||
|
||||
void update_canvas_items();
|
||||
|
||||
Rect2 get_visible_rect() const;
|
||||
RID get_viewport_rid() const;
|
||||
|
||||
void set_world_2d(const Ref<World2D> &p_world_2d);
|
||||
Ref<World2D> get_world_2d() const;
|
||||
Ref<World2D> find_world_2d() const;
|
||||
|
||||
void enable_canvas_transform_override(bool p_enable);
|
||||
bool is_canvas_transform_override_enabled() const;
|
||||
|
||||
void set_canvas_transform_override(const Transform2D &p_transform);
|
||||
Transform2D get_canvas_transform_override() const;
|
||||
|
||||
void set_canvas_transform(const Transform2D &p_transform);
|
||||
Transform2D get_canvas_transform() const;
|
||||
|
||||
void set_global_canvas_transform(const Transform2D &p_transform);
|
||||
Transform2D get_global_canvas_transform() const;
|
||||
|
||||
Transform2D get_stretch_transform() const;
|
||||
virtual Transform2D get_final_transform() const;
|
||||
|
||||
void gui_set_root_order_dirty();
|
||||
|
||||
void set_transparent_background(bool p_enable);
|
||||
bool has_transparent_background() const;
|
||||
|
||||
void set_use_hdr_2d(bool p_enable);
|
||||
bool is_using_hdr_2d() const;
|
||||
|
||||
Ref<ViewportTexture> get_texture() const;
|
||||
|
||||
void set_positional_shadow_atlas_size(int p_size);
|
||||
int get_positional_shadow_atlas_size() const;
|
||||
|
||||
void set_positional_shadow_atlas_16_bits(bool p_16_bits);
|
||||
bool get_positional_shadow_atlas_16_bits() const;
|
||||
|
||||
void set_positional_shadow_atlas_quadrant_subdiv(int p_quadrant, PositionalShadowAtlasQuadrantSubdiv p_subdiv);
|
||||
PositionalShadowAtlasQuadrantSubdiv get_positional_shadow_atlas_quadrant_subdiv(int p_quadrant) const;
|
||||
|
||||
void set_msaa_2d(MSAA p_msaa);
|
||||
MSAA get_msaa_2d() const;
|
||||
|
||||
void set_msaa_3d(MSAA p_msaa);
|
||||
MSAA get_msaa_3d() const;
|
||||
|
||||
void set_screen_space_aa(ScreenSpaceAA p_screen_space_aa);
|
||||
ScreenSpaceAA get_screen_space_aa() const;
|
||||
|
||||
void set_use_taa(bool p_use_taa);
|
||||
bool is_using_taa() const;
|
||||
|
||||
void set_use_oversampling(bool p_oversampling);
|
||||
bool is_using_oversampling() const;
|
||||
|
||||
void set_oversampling_override(float p_oversampling);
|
||||
float get_oversampling_override() const;
|
||||
|
||||
float get_oversampling() const { return font_oversampling; }
|
||||
|
||||
void set_scaling_3d_mode(Scaling3DMode p_scaling_3d_mode);
|
||||
Scaling3DMode get_scaling_3d_mode() const;
|
||||
|
||||
void set_scaling_3d_scale(float p_scaling_3d_scale);
|
||||
float get_scaling_3d_scale() const;
|
||||
|
||||
void set_fsr_sharpness(float p_fsr_sharpness);
|
||||
float get_fsr_sharpness() const;
|
||||
|
||||
void set_texture_mipmap_bias(float p_texture_mipmap_bias);
|
||||
float get_texture_mipmap_bias() const;
|
||||
|
||||
void set_anisotropic_filtering_level(AnisotropicFiltering p_anisotropic_filtering_level);
|
||||
AnisotropicFiltering get_anisotropic_filtering_level() const;
|
||||
|
||||
void set_use_debanding(bool p_use_debanding);
|
||||
bool is_using_debanding() const;
|
||||
|
||||
void set_mesh_lod_threshold(float p_pixels);
|
||||
float get_mesh_lod_threshold() const;
|
||||
|
||||
void set_use_occlusion_culling(bool p_us_occlusion_culling);
|
||||
bool is_using_occlusion_culling() const;
|
||||
|
||||
Vector2 get_camera_coords(const Vector2 &p_viewport_coords) const;
|
||||
Vector2 get_camera_rect_size() const;
|
||||
|
||||
void push_text_input(const String &p_text);
|
||||
void push_input(const Ref<InputEvent> &p_event, bool p_local_coords = false);
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords = false);
|
||||
#endif // DISABLE_DEPRECATED
|
||||
void notify_mouse_entered();
|
||||
void notify_mouse_exited();
|
||||
|
||||
void set_disable_input(bool p_disable);
|
||||
bool is_input_disabled() const;
|
||||
|
||||
void set_disable_input_override(bool p_disable);
|
||||
|
||||
Vector2 get_mouse_position() const;
|
||||
void warp_mouse(const Vector2 &p_position);
|
||||
Point2 wrap_mouse_in_rect(const Vector2 &p_relative, const Rect2 &p_rect);
|
||||
virtual void update_mouse_cursor_state();
|
||||
|
||||
#if !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
|
||||
void set_physics_object_picking(bool p_enable);
|
||||
bool get_physics_object_picking();
|
||||
void set_physics_object_picking_sort(bool p_enable);
|
||||
bool get_physics_object_picking_sort();
|
||||
void set_physics_object_picking_first_only(bool p_enable);
|
||||
bool get_physics_object_picking_first_only();
|
||||
#endif // !defined(PHYSICS_2D_DISABLED) || !defined(PHYSICS_3D_DISABLED)
|
||||
|
||||
Variant gui_get_drag_data() const;
|
||||
String gui_get_drag_description() const;
|
||||
void gui_set_drag_description(const String &p_description);
|
||||
|
||||
void gui_reset_canvas_sort_index();
|
||||
int gui_get_canvas_sort_index();
|
||||
|
||||
void gui_release_focus();
|
||||
Control *gui_get_focus_owner() const;
|
||||
Control *gui_get_hovered_control() const;
|
||||
Window *get_focused_subwindow() const { return gui.subwindow_focused; }
|
||||
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
void set_debug_draw(DebugDraw p_debug_draw);
|
||||
DebugDraw get_debug_draw() const;
|
||||
|
||||
int get_render_info(RenderInfoType p_type, RenderInfo p_info);
|
||||
|
||||
void set_snap_controls_to_pixels(bool p_enable);
|
||||
bool is_snap_controls_to_pixels_enabled() const;
|
||||
|
||||
void set_snap_2d_transforms_to_pixel(bool p_enable);
|
||||
bool is_snap_2d_transforms_to_pixel_enabled() const;
|
||||
|
||||
void set_snap_2d_vertices_to_pixel(bool p_enable);
|
||||
bool is_snap_2d_vertices_to_pixel_enabled() const;
|
||||
|
||||
void set_input_as_handled();
|
||||
bool is_input_handled() const;
|
||||
|
||||
void set_handle_input_locally(bool p_enable);
|
||||
bool is_handling_input_locally() const;
|
||||
|
||||
bool gui_is_dragging() const;
|
||||
bool gui_is_drag_successful() const;
|
||||
void gui_cancel_drag();
|
||||
void gui_perform_drop_at(const Point2 &p_pos, Control *p_control = nullptr);
|
||||
|
||||
Control *gui_find_control(const Point2 &p_global);
|
||||
|
||||
void set_sdf_oversize(SDFOversize p_sdf_oversize);
|
||||
SDFOversize get_sdf_oversize() const;
|
||||
|
||||
void set_sdf_scale(SDFScale p_sdf_scale);
|
||||
SDFScale get_sdf_scale() const;
|
||||
|
||||
void set_default_canvas_item_texture_filter(DefaultCanvasItemTextureFilter p_filter);
|
||||
DefaultCanvasItemTextureFilter get_default_canvas_item_texture_filter() const;
|
||||
|
||||
void set_default_canvas_item_texture_repeat(DefaultCanvasItemTextureRepeat p_repeat);
|
||||
DefaultCanvasItemTextureRepeat get_default_canvas_item_texture_repeat() const;
|
||||
|
||||
// VRS
|
||||
|
||||
void set_vrs_mode(VRSMode p_vrs_mode);
|
||||
VRSMode get_vrs_mode() const;
|
||||
|
||||
void set_vrs_update_mode(VRSUpdateMode p_vrs_update_mode);
|
||||
VRSUpdateMode get_vrs_update_mode() const;
|
||||
|
||||
void set_vrs_texture(Ref<Texture2D> p_texture);
|
||||
Ref<Texture2D> get_vrs_texture() const;
|
||||
|
||||
virtual DisplayServer::WindowID get_window_id() const = 0;
|
||||
|
||||
void set_embedding_subwindows(bool p_embed);
|
||||
bool is_embedding_subwindows() const;
|
||||
TypedArray<Window> get_embedded_subwindows() const;
|
||||
void subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect);
|
||||
Rect2i subwindow_get_popup_safe_rect(Window *p_window) const;
|
||||
|
||||
Viewport *get_parent_viewport() const;
|
||||
Window *get_base_window();
|
||||
|
||||
void set_canvas_cull_mask(uint32_t p_layers);
|
||||
uint32_t get_canvas_cull_mask() const;
|
||||
|
||||
void set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable);
|
||||
bool get_canvas_cull_mask_bit(uint32_t p_layer) const;
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
bool is_visible_subviewport() const;
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
virtual bool is_size_2d_override_stretch_enabled() const { return true; }
|
||||
|
||||
Transform2D get_screen_transform() const;
|
||||
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const;
|
||||
virtual Transform2D get_popup_base_transform_native() const { return Transform2D(); }
|
||||
virtual Transform2D get_popup_base_transform() const { return Transform2D(); }
|
||||
virtual Viewport *get_section_root_viewport() const { return nullptr; }
|
||||
virtual bool is_attached_in_viewport() const { return false; }
|
||||
virtual bool is_sub_viewport() const { return false; }
|
||||
|
||||
private:
|
||||
// 2D audio, camera, and physics. (don't put World2D here because World2D is needed for Control nodes).
|
||||
friend class AudioListener2D; // Needs _audio_listener_2d_set and _audio_listener_2d_remove
|
||||
AudioListener2D *audio_listener_2d = nullptr;
|
||||
void _audio_listener_2d_set(AudioListener2D *p_audio_listener);
|
||||
void _audio_listener_2d_remove(AudioListener2D *p_audio_listener);
|
||||
bool is_audio_listener_2d_enabled = false;
|
||||
RID internal_audio_listener_2d;
|
||||
|
||||
friend class Camera2D; // Needs _camera_2d_set
|
||||
Camera2D *camera_2d = nullptr;
|
||||
void _camera_2d_set(Camera2D *p_camera_2d);
|
||||
|
||||
#ifndef PHYSICS_2D_DISABLED
|
||||
// Collider to frame
|
||||
HashMap<ObjectID, uint64_t> physics_2d_mouseover;
|
||||
// Collider & shape to frame
|
||||
HashMap<Pair<ObjectID, int>, uint64_t> physics_2d_shape_mouseover;
|
||||
// Cleans up colliders corresponding to old frames or all of them.
|
||||
void _cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference = 0);
|
||||
#endif // PHYSICS_2D_DISABLED
|
||||
|
||||
public:
|
||||
AudioListener2D *get_audio_listener_2d() const;
|
||||
void set_as_audio_listener_2d(bool p_enable);
|
||||
bool is_audio_listener_2d() const;
|
||||
|
||||
Camera2D *get_camera_2d() const;
|
||||
void assign_next_enabled_camera_2d(const StringName &p_camera_group);
|
||||
|
||||
#ifndef _3D_DISABLED
|
||||
private:
|
||||
// 3D audio, camera, physics, and world.
|
||||
bool use_xr = false;
|
||||
friend class AudioListener3D;
|
||||
AudioListener3D *audio_listener_3d = nullptr;
|
||||
HashSet<AudioListener3D *> audio_listener_3d_set;
|
||||
bool is_audio_listener_3d_enabled = false;
|
||||
RID internal_audio_listener_3d;
|
||||
void _update_audio_listener_3d();
|
||||
void _listener_transform_3d_changed_notify();
|
||||
void _audio_listener_3d_set(AudioListener3D *p_listener);
|
||||
bool _audio_listener_3d_add(AudioListener3D *p_listener); //true if first
|
||||
void _audio_listener_3d_remove(AudioListener3D *p_listener);
|
||||
void _audio_listener_3d_make_next_current(AudioListener3D *p_exclude);
|
||||
|
||||
#ifndef PHYSICS_3D_DISABLED
|
||||
void _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);
|
||||
#endif // PHYSICS_3D_DISABLED
|
||||
|
||||
struct Camera3DOverrideData {
|
||||
Transform3D transform;
|
||||
enum Projection {
|
||||
PROJECTION_PERSPECTIVE,
|
||||
PROJECTION_ORTHOGONAL
|
||||
};
|
||||
Projection projection = Projection::PROJECTION_PERSPECTIVE;
|
||||
real_t fov = 0.0;
|
||||
real_t size = 0.0;
|
||||
real_t z_near = 0.0;
|
||||
real_t z_far = 0.0;
|
||||
RID rid;
|
||||
|
||||
operator bool() const {
|
||||
return rid != RID();
|
||||
}
|
||||
} camera_3d_override;
|
||||
|
||||
friend class Camera3D;
|
||||
Camera3D *camera_3d = nullptr;
|
||||
HashSet<Camera3D *> camera_3d_set;
|
||||
void _camera_3d_transform_changed_notify();
|
||||
void _camera_3d_set(Camera3D *p_camera);
|
||||
bool _camera_3d_add(Camera3D *p_camera); //true if first
|
||||
void _camera_3d_remove(Camera3D *p_camera);
|
||||
void _camera_3d_make_next_current(Camera3D *p_exclude);
|
||||
|
||||
Ref<World3D> world_3d;
|
||||
Ref<World3D> own_world_3d;
|
||||
void _own_world_3d_changed();
|
||||
void _propagate_enter_world_3d(Node *p_node);
|
||||
void _propagate_exit_world_3d(Node *p_node);
|
||||
|
||||
public:
|
||||
AudioListener3D *get_audio_listener_3d() const;
|
||||
void set_as_audio_listener_3d(bool p_enable);
|
||||
bool is_audio_listener_3d() const;
|
||||
|
||||
Camera3D *get_camera_3d() const;
|
||||
void enable_camera_3d_override(bool p_enable);
|
||||
bool is_camera_3d_override_enabled() const;
|
||||
|
||||
void set_camera_3d_override_transform(const Transform3D &p_transform);
|
||||
Transform3D get_camera_3d_override_transform() const;
|
||||
|
||||
void set_camera_3d_override_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far);
|
||||
void set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far);
|
||||
HashMap<StringName, real_t> get_camera_3d_override_properties() const;
|
||||
|
||||
Vector3 camera_3d_override_project_ray_normal(const Point2 &p_pos) const;
|
||||
Vector3 camera_3d_override_project_ray_origin(const Point2 &p_pos) const;
|
||||
Vector3 camera_3d_override_project_local_ray_normal(const Point2 &p_pos) const;
|
||||
|
||||
void set_disable_3d(bool p_disable);
|
||||
bool is_3d_disabled() const;
|
||||
|
||||
void set_world_3d(const Ref<World3D> &p_world_3d);
|
||||
Ref<World3D> get_world_3d() const;
|
||||
Ref<World3D> find_world_3d() const;
|
||||
void set_use_own_world_3d(bool p_use_own_world_3d);
|
||||
bool is_using_own_world_3d() const;
|
||||
|
||||
void set_use_xr(bool p_use_xr);
|
||||
bool is_using_xr();
|
||||
#endif // _3D_DISABLED
|
||||
|
||||
Viewport();
|
||||
~Viewport();
|
||||
};
|
||||
|
||||
class SubViewport : public Viewport {
|
||||
GDCLASS(SubViewport, Viewport);
|
||||
|
||||
public:
|
||||
enum ClearMode {
|
||||
CLEAR_MODE_ALWAYS,
|
||||
CLEAR_MODE_NEVER,
|
||||
CLEAR_MODE_ONCE
|
||||
};
|
||||
|
||||
enum UpdateMode {
|
||||
UPDATE_DISABLED,
|
||||
UPDATE_ONCE, //then goes to disabled
|
||||
UPDATE_WHEN_VISIBLE, // default
|
||||
UPDATE_WHEN_PARENT_VISIBLE,
|
||||
UPDATE_ALWAYS
|
||||
};
|
||||
|
||||
private:
|
||||
UpdateMode update_mode = UPDATE_WHEN_VISIBLE;
|
||||
ClearMode clear_mode = CLEAR_MODE_ALWAYS;
|
||||
bool size_2d_override_stretch = false;
|
||||
|
||||
void _internal_set_size(const Size2i &p_size, bool p_force = false);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
virtual DisplayServer::WindowID get_window_id() const override;
|
||||
void _notification(int p_what);
|
||||
|
||||
public:
|
||||
void set_size(const Size2i &p_size);
|
||||
Size2i get_size() const;
|
||||
void set_size_force(const Size2i &p_size);
|
||||
|
||||
void set_size_2d_override(const Size2i &p_size);
|
||||
Size2i get_size_2d_override() const;
|
||||
|
||||
void set_size_2d_override_stretch(bool p_enable);
|
||||
bool is_size_2d_override_stretch_enabled() const override;
|
||||
|
||||
void set_update_mode(UpdateMode p_mode);
|
||||
UpdateMode get_update_mode() const;
|
||||
|
||||
void set_clear_mode(ClearMode p_mode);
|
||||
ClearMode get_clear_mode() const;
|
||||
|
||||
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override;
|
||||
virtual Transform2D get_popup_base_transform() const override;
|
||||
virtual Viewport *get_section_root_viewport() const override;
|
||||
virtual bool is_attached_in_viewport() const override;
|
||||
virtual bool is_sub_viewport() const override { return true; }
|
||||
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
SubViewport();
|
||||
};
|
||||
VARIANT_ENUM_CAST(Viewport::Scaling3DMode);
|
||||
VARIANT_ENUM_CAST(SubViewport::UpdateMode);
|
||||
VARIANT_ENUM_CAST(Viewport::PositionalShadowAtlasQuadrantSubdiv);
|
||||
VARIANT_ENUM_CAST(Viewport::MSAA);
|
||||
VARIANT_ENUM_CAST(Viewport::AnisotropicFiltering);
|
||||
VARIANT_ENUM_CAST(Viewport::ScreenSpaceAA);
|
||||
VARIANT_ENUM_CAST(Viewport::DebugDraw);
|
||||
VARIANT_ENUM_CAST(Viewport::SDFScale);
|
||||
VARIANT_ENUM_CAST(Viewport::SDFOversize);
|
||||
VARIANT_ENUM_CAST(Viewport::VRSMode);
|
||||
VARIANT_ENUM_CAST(Viewport::VRSUpdateMode);
|
||||
VARIANT_ENUM_CAST(SubViewport::ClearMode);
|
||||
VARIANT_ENUM_CAST(Viewport::RenderInfo);
|
||||
VARIANT_ENUM_CAST(Viewport::RenderInfoType);
|
||||
VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureFilter);
|
||||
VARIANT_ENUM_CAST(Viewport::DefaultCanvasItemTextureRepeat);
|
3506
scene/main/window.cpp
Normal file
3506
scene/main/window.cpp
Normal file
File diff suppressed because it is too large
Load Diff
538
scene/main/window.h
Normal file
538
scene/main/window.h
Normal file
@@ -0,0 +1,538 @@
|
||||
/**************************************************************************/
|
||||
/* window.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scene/main/viewport.h"
|
||||
#include "scene/resources/theme.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
class Font;
|
||||
class Shortcut;
|
||||
class StyleBox;
|
||||
class ThemeOwner;
|
||||
class ThemeContext;
|
||||
|
||||
class Window : public Viewport {
|
||||
GDCLASS(Window, Viewport);
|
||||
|
||||
public:
|
||||
// Keep synced with enum hint for `mode` property.
|
||||
enum Mode {
|
||||
MODE_WINDOWED = DisplayServer::WINDOW_MODE_WINDOWED,
|
||||
MODE_MINIMIZED = DisplayServer::WINDOW_MODE_MINIMIZED,
|
||||
MODE_MAXIMIZED = DisplayServer::WINDOW_MODE_MAXIMIZED,
|
||||
MODE_FULLSCREEN = DisplayServer::WINDOW_MODE_FULLSCREEN,
|
||||
MODE_EXCLUSIVE_FULLSCREEN = DisplayServer::WINDOW_MODE_EXCLUSIVE_FULLSCREEN,
|
||||
};
|
||||
|
||||
enum Flags {
|
||||
FLAG_RESIZE_DISABLED = DisplayServer::WINDOW_FLAG_RESIZE_DISABLED,
|
||||
FLAG_BORDERLESS = DisplayServer::WINDOW_FLAG_BORDERLESS,
|
||||
FLAG_ALWAYS_ON_TOP = DisplayServer::WINDOW_FLAG_ALWAYS_ON_TOP,
|
||||
FLAG_TRANSPARENT = DisplayServer::WINDOW_FLAG_TRANSPARENT,
|
||||
FLAG_NO_FOCUS = DisplayServer::WINDOW_FLAG_NO_FOCUS,
|
||||
FLAG_POPUP = DisplayServer::WINDOW_FLAG_POPUP,
|
||||
FLAG_EXTEND_TO_TITLE = DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE,
|
||||
FLAG_MOUSE_PASSTHROUGH = DisplayServer::WINDOW_FLAG_MOUSE_PASSTHROUGH,
|
||||
FLAG_SHARP_CORNERS = DisplayServer::WINDOW_FLAG_SHARP_CORNERS,
|
||||
FLAG_EXCLUDE_FROM_CAPTURE = DisplayServer::WINDOW_FLAG_EXCLUDE_FROM_CAPTURE,
|
||||
FLAG_POPUP_WM_HINT = DisplayServer::WINDOW_FLAG_POPUP_WM_HINT,
|
||||
FLAG_MINIMIZE_DISABLED = DisplayServer::WINDOW_FLAG_MINIMIZE_DISABLED,
|
||||
FLAG_MAXIMIZE_DISABLED = DisplayServer::WINDOW_FLAG_MAXIMIZE_DISABLED,
|
||||
FLAG_MAX = DisplayServer::WINDOW_FLAG_MAX,
|
||||
};
|
||||
|
||||
enum ContentScaleMode {
|
||||
CONTENT_SCALE_MODE_DISABLED,
|
||||
CONTENT_SCALE_MODE_CANVAS_ITEMS,
|
||||
CONTENT_SCALE_MODE_VIEWPORT,
|
||||
};
|
||||
|
||||
enum ContentScaleAspect {
|
||||
CONTENT_SCALE_ASPECT_IGNORE,
|
||||
CONTENT_SCALE_ASPECT_KEEP,
|
||||
CONTENT_SCALE_ASPECT_KEEP_WIDTH,
|
||||
CONTENT_SCALE_ASPECT_KEEP_HEIGHT,
|
||||
CONTENT_SCALE_ASPECT_EXPAND,
|
||||
};
|
||||
|
||||
enum ContentScaleStretch {
|
||||
CONTENT_SCALE_STRETCH_FRACTIONAL,
|
||||
CONTENT_SCALE_STRETCH_INTEGER,
|
||||
};
|
||||
|
||||
enum LayoutDirection {
|
||||
LAYOUT_DIRECTION_INHERITED,
|
||||
LAYOUT_DIRECTION_APPLICATION_LOCALE,
|
||||
LAYOUT_DIRECTION_LTR,
|
||||
LAYOUT_DIRECTION_RTL,
|
||||
LAYOUT_DIRECTION_SYSTEM_LOCALE,
|
||||
LAYOUT_DIRECTION_MAX,
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
LAYOUT_DIRECTION_LOCALE = LAYOUT_DIRECTION_APPLICATION_LOCALE,
|
||||
#endif // DISABLE_DEPRECATED
|
||||
};
|
||||
|
||||
enum {
|
||||
DEFAULT_WINDOW_SIZE = 100,
|
||||
};
|
||||
|
||||
// Keep synced with enum hint for `initial_position` property and `display/window/size/initial_position_type` project setting.
|
||||
enum WindowInitialPosition {
|
||||
WINDOW_INITIAL_POSITION_ABSOLUTE,
|
||||
WINDOW_INITIAL_POSITION_CENTER_PRIMARY_SCREEN,
|
||||
WINDOW_INITIAL_POSITION_CENTER_MAIN_WINDOW_SCREEN,
|
||||
WINDOW_INITIAL_POSITION_CENTER_OTHER_SCREEN,
|
||||
WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_MOUSE_FOCUS,
|
||||
WINDOW_INITIAL_POSITION_CENTER_SCREEN_WITH_KEYBOARD_FOCUS,
|
||||
};
|
||||
|
||||
private:
|
||||
DisplayServer::WindowID window_id = DisplayServer::INVALID_WINDOW_ID;
|
||||
bool initialized = false;
|
||||
|
||||
String title;
|
||||
String displayed_title;
|
||||
mutable int current_screen = 0;
|
||||
mutable Point2i position;
|
||||
mutable Size2i size = Size2i(DEFAULT_WINDOW_SIZE, DEFAULT_WINDOW_SIZE);
|
||||
mutable Size2i min_size;
|
||||
mutable Size2i max_size;
|
||||
mutable Vector<Vector2> mpath;
|
||||
mutable Mode mode = MODE_WINDOWED;
|
||||
mutable bool flags[FLAG_MAX] = {};
|
||||
bool visible = true;
|
||||
bool focused = false;
|
||||
WindowInitialPosition initial_position = WINDOW_INITIAL_POSITION_ABSOLUTE;
|
||||
bool force_native = false;
|
||||
|
||||
bool transient = false;
|
||||
bool transient_to_focused = false;
|
||||
bool exclusive = false;
|
||||
bool wrap_controls = false;
|
||||
bool updating_child_controls = false;
|
||||
bool updating_embedded_window = false;
|
||||
bool clamp_to_embedder = false;
|
||||
bool unparent_when_invisible = false;
|
||||
bool keep_title_visible = false;
|
||||
|
||||
LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED;
|
||||
|
||||
void _update_child_controls();
|
||||
void _update_embedded_window();
|
||||
|
||||
Size2i content_scale_size;
|
||||
ContentScaleMode content_scale_mode = CONTENT_SCALE_MODE_DISABLED;
|
||||
ContentScaleAspect content_scale_aspect = CONTENT_SCALE_ASPECT_IGNORE;
|
||||
ContentScaleStretch content_scale_stretch = CONTENT_SCALE_STRETCH_FRACTIONAL;
|
||||
real_t content_scale_factor = 1.0;
|
||||
|
||||
RID accessibility_title_element;
|
||||
RID accessibility_announcement_element;
|
||||
String announcement;
|
||||
String accessibility_name;
|
||||
String accessibility_description;
|
||||
|
||||
void _make_window();
|
||||
void _clear_window();
|
||||
void _update_from_window();
|
||||
void _accessibility_notify_enter(Node *p_node);
|
||||
void _accessibility_notify_exit(Node *p_node);
|
||||
|
||||
bool _try_parent_dialog(Node *p_from_node);
|
||||
|
||||
Size2i max_size_used;
|
||||
|
||||
Size2i _clamp_limit_size(const Size2i &p_limit_size);
|
||||
Size2i _clamp_window_size(const Size2i &p_size);
|
||||
void _validate_limit_size();
|
||||
void _update_viewport_size();
|
||||
void _update_window_size();
|
||||
|
||||
void _propagate_window_notification(Node *p_node, int p_notification);
|
||||
|
||||
void _update_window_callbacks();
|
||||
|
||||
Window *transient_parent = nullptr;
|
||||
Window *exclusive_child = nullptr;
|
||||
HashSet<Window *> transient_children;
|
||||
|
||||
void _clear_transient();
|
||||
void _make_transient();
|
||||
void _set_transient_exclusive_child(bool p_clear_invalid = false);
|
||||
|
||||
static Window *focused_window;
|
||||
|
||||
ThemeOwner *theme_owner = nullptr;
|
||||
Ref<Theme> theme;
|
||||
StringName theme_type_variation;
|
||||
|
||||
bool bulk_theme_override = false;
|
||||
Theme::ThemeIconMap theme_icon_override;
|
||||
Theme::ThemeStyleMap theme_style_override;
|
||||
Theme::ThemeFontMap theme_font_override;
|
||||
Theme::ThemeFontSizeMap theme_font_size_override;
|
||||
Theme::ThemeColorMap theme_color_override;
|
||||
Theme::ThemeConstantMap theme_constant_override;
|
||||
|
||||
mutable HashMap<StringName, Theme::ThemeIconMap> theme_icon_cache;
|
||||
mutable HashMap<StringName, Theme::ThemeStyleMap> theme_style_cache;
|
||||
mutable HashMap<StringName, Theme::ThemeFontMap> theme_font_cache;
|
||||
mutable HashMap<StringName, Theme::ThemeFontSizeMap> theme_font_size_cache;
|
||||
mutable HashMap<StringName, Theme::ThemeColorMap> theme_color_cache;
|
||||
mutable HashMap<StringName, Theme::ThemeConstantMap> theme_constant_cache;
|
||||
|
||||
void _theme_changed();
|
||||
void _notify_theme_override_changed();
|
||||
void _invalidate_theme_cache();
|
||||
|
||||
struct ThemeCache {
|
||||
Ref<StyleBox> embedded_border;
|
||||
Ref<StyleBox> embedded_unfocused_border;
|
||||
|
||||
Ref<Font> title_font;
|
||||
int title_font_size = 0;
|
||||
Color title_color;
|
||||
int title_height = 0;
|
||||
Color title_outline_modulate;
|
||||
int title_outline_size = 0;
|
||||
|
||||
Ref<Texture2D> close;
|
||||
Ref<Texture2D> close_pressed;
|
||||
int close_h_offset = 0;
|
||||
int close_v_offset = 0;
|
||||
|
||||
int resize_margin = 0;
|
||||
} theme_cache;
|
||||
|
||||
void _settings_changed();
|
||||
|
||||
Viewport *embedder = nullptr;
|
||||
|
||||
Transform2D window_transform;
|
||||
|
||||
friend class Viewport; //friend back, can call the methods below
|
||||
|
||||
void _window_input(const Ref<InputEvent> &p_ev);
|
||||
void _window_input_text(const String &p_text);
|
||||
void _window_drop_files(const Vector<String> &p_files);
|
||||
void _rect_changed_callback(const Rect2i &p_callback);
|
||||
void _event_callback(DisplayServer::WindowEvent p_event);
|
||||
virtual bool _can_consume_input_events() const override;
|
||||
|
||||
bool mouse_in_window = false;
|
||||
void _update_mouse_over(Vector2 p_pos) override;
|
||||
void _mouse_leave_viewport() override;
|
||||
|
||||
void _update_displayed_title();
|
||||
|
||||
Ref<Shortcut> debugger_stop_shortcut;
|
||||
|
||||
static int root_layout_direction;
|
||||
|
||||
protected:
|
||||
virtual void _pre_popup() {} // Called after "about_to_popup", but before window is shown.
|
||||
virtual Rect2i _popup_adjust_rect() const { return Rect2i(); }
|
||||
virtual void _post_popup() {}
|
||||
|
||||
virtual void _update_theme_item_cache();
|
||||
virtual void _input_from_window(const Ref<InputEvent> &p_event) {}
|
||||
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
|
||||
void _accessibility_action_grab_focus(const Variant &p_data) {
|
||||
grab_focus();
|
||||
}
|
||||
|
||||
virtual void add_child_notify(Node *p_child) override;
|
||||
virtual void remove_child_notify(Node *p_child) override;
|
||||
|
||||
GDVIRTUAL0RC(Vector2, _get_contents_minimum_size)
|
||||
|
||||
public:
|
||||
enum {
|
||||
NOTIFICATION_VISIBILITY_CHANGED = 30,
|
||||
NOTIFICATION_POST_POPUP = 31,
|
||||
NOTIFICATION_THEME_CHANGED = 32
|
||||
};
|
||||
|
||||
static void set_root_layout_direction(int p_root_dir);
|
||||
static Window *get_from_id(DisplayServer::WindowID p_window_id);
|
||||
|
||||
RID get_accessibility_element() const override;
|
||||
virtual RID get_focused_accessibility_element() const override;
|
||||
|
||||
void set_title(const String &p_title);
|
||||
String get_title() const;
|
||||
String get_displayed_title() const;
|
||||
|
||||
void set_initial_position(WindowInitialPosition p_initial_position);
|
||||
WindowInitialPosition get_initial_position() const;
|
||||
|
||||
void set_force_native(bool p_force_native);
|
||||
bool get_force_native() const;
|
||||
|
||||
void set_current_screen(int p_screen);
|
||||
int get_current_screen() const;
|
||||
|
||||
void set_position(const Point2i &p_position);
|
||||
Point2i get_position() const;
|
||||
void move_to_center();
|
||||
|
||||
void set_size(const Size2i &p_size);
|
||||
Size2i get_size() const;
|
||||
void reset_size();
|
||||
|
||||
Point2i get_position_with_decorations() const;
|
||||
Size2i get_size_with_decorations() const;
|
||||
|
||||
void set_max_size(const Size2i &p_max_size);
|
||||
Size2i get_max_size() const;
|
||||
|
||||
void set_min_size(const Size2i &p_min_size);
|
||||
Size2i get_min_size() const;
|
||||
|
||||
void set_mode(Mode p_mode);
|
||||
Mode get_mode() const;
|
||||
|
||||
void set_flag(Flags p_flag, bool p_enabled);
|
||||
bool get_flag(Flags p_flag) const;
|
||||
|
||||
bool is_popup() const;
|
||||
|
||||
bool is_maximize_allowed() const;
|
||||
|
||||
void request_attention();
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void move_to_foreground();
|
||||
#endif // DISABLE_DEPRECATED
|
||||
|
||||
virtual void set_visible(bool p_visible);
|
||||
bool is_visible() const;
|
||||
|
||||
void update_mouse_cursor_state() override;
|
||||
|
||||
void show();
|
||||
void hide();
|
||||
|
||||
void set_transient(bool p_transient);
|
||||
bool is_transient() const;
|
||||
|
||||
void set_transient_to_focused(bool p_transient_to_focused);
|
||||
bool is_transient_to_focused() const;
|
||||
|
||||
void set_exclusive(bool p_exclusive);
|
||||
bool is_exclusive() const;
|
||||
|
||||
void set_clamp_to_embedder(bool p_enable);
|
||||
bool is_clamped_to_embedder() const;
|
||||
|
||||
void set_unparent_when_invisible(bool p_unparent);
|
||||
|
||||
bool is_in_edited_scene_root() const;
|
||||
|
||||
bool can_draw() const;
|
||||
|
||||
void set_ime_active(bool p_active);
|
||||
void set_ime_position(const Point2i &p_pos);
|
||||
|
||||
bool is_embedded() const;
|
||||
Viewport *get_embedder() const;
|
||||
|
||||
void set_content_scale_size(const Size2i &p_size);
|
||||
Size2i get_content_scale_size() const;
|
||||
|
||||
void set_content_scale_mode(ContentScaleMode p_mode);
|
||||
ContentScaleMode get_content_scale_mode() const;
|
||||
|
||||
void set_content_scale_aspect(ContentScaleAspect p_aspect);
|
||||
ContentScaleAspect get_content_scale_aspect() const;
|
||||
|
||||
void set_content_scale_stretch(ContentScaleStretch p_stretch);
|
||||
ContentScaleStretch get_content_scale_stretch() const;
|
||||
|
||||
void set_keep_title_visible(bool p_title_visible);
|
||||
bool get_keep_title_visible() const;
|
||||
|
||||
void set_content_scale_factor(real_t p_factor);
|
||||
real_t get_content_scale_factor() const;
|
||||
|
||||
void set_mouse_passthrough_polygon(const Vector<Vector2> &p_region);
|
||||
Vector<Vector2> get_mouse_passthrough_polygon() const;
|
||||
|
||||
void set_wrap_controls(bool p_enable);
|
||||
bool is_wrapping_controls() const;
|
||||
void child_controls_changed();
|
||||
|
||||
Window *get_exclusive_child() const { return exclusive_child; }
|
||||
HashSet<Window *> get_transient_children() const { return transient_children; }
|
||||
Window *get_parent_visible_window() const;
|
||||
Window *get_non_popup_window() const;
|
||||
Viewport *get_parent_viewport() const;
|
||||
|
||||
virtual void popup(const Rect2i &p_screen_rect = Rect2i());
|
||||
void popup_on_parent(const Rect2i &p_parent_rect);
|
||||
void popup_centered(const Size2i &p_minsize = Size2i());
|
||||
void popup_centered_ratio(float p_ratio = 0.8);
|
||||
void popup_centered_clamped(const Size2i &p_size = Size2i(), float p_fallback_ratio = 0.75);
|
||||
|
||||
void popup_exclusive(Node *p_from_node, const Rect2i &p_screen_rect = Rect2i());
|
||||
void popup_exclusive_on_parent(Node *p_from_node, const Rect2i &p_parent_rect);
|
||||
void popup_exclusive_centered(Node *p_from_node, const Size2i &p_minsize = Size2i());
|
||||
void popup_exclusive_centered_ratio(Node *p_from_node, float p_ratio = 0.8);
|
||||
void popup_exclusive_centered_clamped(Node *p_from_node, const Size2i &p_size = Size2i(), float p_fallback_ratio = 0.75);
|
||||
|
||||
Rect2i fit_rect_in_parent(Rect2i p_rect, const Rect2i &p_parent_rect) const;
|
||||
Size2 get_contents_minimum_size() const;
|
||||
Size2 get_clamped_minimum_size() const;
|
||||
|
||||
void grab_focus();
|
||||
bool has_focus() const;
|
||||
bool has_focus_or_active_popup() const;
|
||||
|
||||
void start_drag();
|
||||
void start_resize(DisplayServer::WindowResizeEdge p_edge);
|
||||
|
||||
Rect2i get_usable_parent_rect() const;
|
||||
|
||||
void set_accessibility_name(const String &p_name);
|
||||
String get_accessibility_name() const;
|
||||
|
||||
void set_accessibility_description(const String &p_description);
|
||||
String get_accessibility_description() const;
|
||||
|
||||
void accessibility_announcement(const String &p_announcement);
|
||||
|
||||
// Internationalization.
|
||||
|
||||
void set_layout_direction(LayoutDirection p_direction);
|
||||
LayoutDirection get_layout_direction() const;
|
||||
bool is_layout_rtl() const;
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void set_use_font_oversampling(bool p_oversampling);
|
||||
bool is_using_font_oversampling() const;
|
||||
|
||||
void set_auto_translate(bool p_enable);
|
||||
bool is_auto_translating() const;
|
||||
#endif
|
||||
|
||||
// Theming.
|
||||
|
||||
void set_theme_owner_node(Node *p_node);
|
||||
Node *get_theme_owner_node() const;
|
||||
bool has_theme_owner_node() const;
|
||||
|
||||
void set_theme_context(ThemeContext *p_context, bool p_propagate = true);
|
||||
|
||||
void set_theme(const Ref<Theme> &p_theme);
|
||||
Ref<Theme> get_theme() const;
|
||||
|
||||
void set_theme_type_variation(const StringName &p_theme_type);
|
||||
StringName get_theme_type_variation() const;
|
||||
|
||||
void begin_bulk_theme_override();
|
||||
void end_bulk_theme_override();
|
||||
|
||||
void add_theme_icon_override(const StringName &p_name, const Ref<Texture2D> &p_icon);
|
||||
void add_theme_style_override(const StringName &p_name, const Ref<StyleBox> &p_style);
|
||||
void add_theme_font_override(const StringName &p_name, const Ref<Font> &p_font);
|
||||
void add_theme_font_size_override(const StringName &p_name, int p_font_size);
|
||||
void add_theme_color_override(const StringName &p_name, const Color &p_color);
|
||||
void add_theme_constant_override(const StringName &p_name, int p_constant);
|
||||
|
||||
void remove_theme_icon_override(const StringName &p_name);
|
||||
void remove_theme_style_override(const StringName &p_name);
|
||||
void remove_theme_font_override(const StringName &p_name);
|
||||
void remove_theme_font_size_override(const StringName &p_name);
|
||||
void remove_theme_color_override(const StringName &p_name);
|
||||
void remove_theme_constant_override(const StringName &p_name);
|
||||
|
||||
Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
Ref<StyleBox> get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
Ref<Font> get_theme_font(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
int get_theme_font_size(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
Color get_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
int get_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
Variant get_theme_item(Theme::DataType p_data_type, const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
#ifdef TOOLS_ENABLED
|
||||
Ref<Texture2D> get_editor_theme_icon(const StringName &p_name) const;
|
||||
Ref<Texture2D> get_editor_theme_native_menu_icon(const StringName &p_name, bool p_global_menu, bool p_dark_mode) const;
|
||||
#endif
|
||||
|
||||
bool has_theme_icon_override(const StringName &p_name) const;
|
||||
bool has_theme_stylebox_override(const StringName &p_name) const;
|
||||
bool has_theme_font_override(const StringName &p_name) const;
|
||||
bool has_theme_font_size_override(const StringName &p_name) const;
|
||||
bool has_theme_color_override(const StringName &p_name) const;
|
||||
bool has_theme_constant_override(const StringName &p_name) const;
|
||||
|
||||
bool has_theme_icon(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
bool has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
bool has_theme_font(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
bool has_theme_font_size(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
bool has_theme_color(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
bool has_theme_constant(const StringName &p_name, const StringName &p_theme_type = StringName()) const;
|
||||
|
||||
float get_theme_default_base_scale() const;
|
||||
Ref<Font> get_theme_default_font() const;
|
||||
int get_theme_default_font_size() const;
|
||||
|
||||
virtual Transform2D get_final_transform() const override;
|
||||
virtual Transform2D get_screen_transform_internal(bool p_absolute_position = false) const override;
|
||||
virtual Transform2D get_popup_base_transform() const override;
|
||||
virtual Transform2D get_popup_base_transform_native() const override;
|
||||
virtual Viewport *get_section_root_viewport() const override;
|
||||
virtual bool is_attached_in_viewport() const override;
|
||||
|
||||
Rect2i get_parent_rect() const;
|
||||
virtual DisplayServer::WindowID get_window_id() const override;
|
||||
static Window *get_focused_window() { return focused_window; }
|
||||
|
||||
virtual Size2 _get_contents_minimum_size() const;
|
||||
|
||||
Window();
|
||||
~Window();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(Window::Mode);
|
||||
VARIANT_ENUM_CAST(Window::Flags);
|
||||
VARIANT_ENUM_CAST(Window::ContentScaleMode);
|
||||
VARIANT_ENUM_CAST(Window::ContentScaleAspect);
|
||||
VARIANT_ENUM_CAST(Window::ContentScaleStretch);
|
||||
VARIANT_ENUM_CAST(Window::LayoutDirection);
|
||||
VARIANT_ENUM_CAST(Window::WindowInitialPosition);
|
Reference in New Issue
Block a user