diff options
82 files changed, 2742 insertions, 433 deletions
diff --git a/.github/workflows/javascript_builds.yml b/.github/workflows/javascript_builds.yml index b4e7d0f681..ced2c36a91 100644 --- a/.github/workflows/javascript_builds.yml +++ b/.github/workflows/javascript_builds.yml @@ -4,7 +4,7 @@ on: [push, pull_request] # Global Settings env: GODOT_BASE_BRANCH: master - SCONSFLAGS: platform=javascript verbose=yes warnings=extra debug_symbols=no module_webxr_enabled=no --jobs=2 + SCONSFLAGS: platform=javascript verbose=yes warnings=extra debug_symbols=no --jobs=2 SCONS_CACHE_LIMIT: 4096 EM_VERSION: 2.0.25 EM_CACHE_FOLDER: 'emsdk-cache' diff --git a/core/math/dynamic_bvh.cpp b/core/math/dynamic_bvh.cpp index 8e596f0f9d..f3fb473981 100644 --- a/core/math/dynamic_bvh.cpp +++ b/core/math/dynamic_bvh.cpp @@ -181,7 +181,7 @@ DynamicBVH::Volume DynamicBVH::_bounds(Node **leaves, int p_count) { void DynamicBVH::_bottom_up(Node **leaves, int p_count) { while (p_count > 1) { - real_t minsize = Math_INF; + real_t minsize = INFINITY; int minidx[2] = { -1, -1 }; for (int i = 0; i < p_count; ++i) { for (int j = i + 1; j < p_count; ++j) { diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 0146c345f0..05f2c8dac9 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -397,10 +397,10 @@ Error Expression::_get_token(Token &r_token) { r_token.value = Math_TAU; } else if (id == "INF") { r_token.type = TK_CONSTANT; - r_token.value = Math_INF; + r_token.value = INFINITY; } else if (id == "NAN") { r_token.type = TK_CONSTANT; - r_token.value = Math_NAN; + r_token.value = NAN; } else if (id == "not") { r_token.type = TK_OP_NOT; } else if (id == "or") { diff --git a/core/math/math_defs.h b/core/math/math_defs.h index df2223fb78..7692e1be47 100644 --- a/core/math/math_defs.h +++ b/core/math/math_defs.h @@ -43,8 +43,6 @@ #define Math_TAU 6.2831853071795864769252867666 #define Math_PI 3.1415926535897932384626433833 #define Math_E 2.7182818284590452353602874714 -#define Math_INF INFINITY -#define Math_NAN NAN #ifdef DEBUG_ENABLED #define MATH_CHECKS diff --git a/core/math/vector2.h b/core/math/vector2.h index 78deb473b4..4d9f3126e9 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -300,6 +300,14 @@ struct Vector2i { return p_idx ? y : x; } + _FORCE_INLINE_ int min_axis() const { + return x < y ? 0 : 1; + } + + _FORCE_INLINE_ int max_axis() const { + return x < y ? 1 : 0; + } + Vector2i min(const Vector2i &p_vector2i) const { return Vector2(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y)); } diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h index 4572b269cf..2e932f9f26 100644 --- a/core/templates/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -95,7 +95,7 @@ static inline uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev = 5381) if (p_in == 0.0f) { u.d = 0.0; } else if (Math::is_nan(p_in)) { - u.d = Math_NAN; + u.d = NAN; } else { u.d = p_in; } @@ -124,7 +124,7 @@ static inline uint64_t hash_djb2_one_float_64(double p_in, uint64_t p_prev = 538 if (p_in == 0.0f) { u.d = 0.0; } else if (Math::is_nan(p_in)) { - u.d = Math_NAN; + u.d = NAN; } else { u.d = p_in; } diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 733361fe58..65ad27d0b4 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -2002,7 +2002,7 @@ static void _register_variant_builtin_methods() { _VariantCall::add_variant_constant(Variant::VECTOR3, "ZERO", Vector3(0, 0, 0)); _VariantCall::add_variant_constant(Variant::VECTOR3, "ONE", Vector3(1, 1, 1)); - _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(Math_INF, Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(INFINITY, INFINITY, INFINITY)); _VariantCall::add_variant_constant(Variant::VECTOR3, "LEFT", Vector3(-1, 0, 0)); _VariantCall::add_variant_constant(Variant::VECTOR3, "RIGHT", Vector3(1, 0, 0)); _VariantCall::add_variant_constant(Variant::VECTOR3, "UP", Vector3(0, 1, 0)); @@ -2031,7 +2031,7 @@ static void _register_variant_builtin_methods() { _VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0)); _VariantCall::add_variant_constant(Variant::VECTOR2, "ONE", Vector2(1, 1)); - _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(INFINITY, INFINITY)); _VariantCall::add_variant_constant(Variant::VECTOR2, "LEFT", Vector2(-1, 0)); _VariantCall::add_variant_constant(Variant::VECTOR2, "RIGHT", Vector2(1, 0)); _VariantCall::add_variant_constant(Variant::VECTOR2, "UP", Vector2(0, -1)); diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 86d5ae7f38..f9c604fe3e 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -506,9 +506,9 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } else if (id == "null" || id == "nil") { value = Variant(); } else if (id == "inf") { - value = Math_INF; + value = INFINITY; } else if (id == "nan") { - value = Math_NAN; + value = NAN; } else if (id == "Vector2") { Vector<real_t> args; Error err = _parse_construct<real_t>(p_stream, args, line, r_err_str); diff --git a/doc/classes/CPUParticles3D.xml b/doc/classes/CPUParticles3D.xml index d7a89eef11..29e04dda7e 100644 --- a/doc/classes/CPUParticles3D.xml +++ b/doc/classes/CPUParticles3D.xml @@ -177,9 +177,21 @@ <member name="emission_normals" type="PackedVector3Array" setter="set_emission_normals" getter="get_emission_normals"> Sets the direction the particles will be emitted in when using [constant EMISSION_SHAPE_DIRECTED_POINTS]. </member> - <member name="emission_points" type="PackedVector3Array" setter="set_emission_points" getter="get_emission_points" default="PackedVector3Array()"> + <member name="emission_points" type="PackedVector3Array" setter="set_emission_points" getter="get_emission_points"> Sets the initial positions to spawn particles when using [constant EMISSION_SHAPE_POINTS] or [constant EMISSION_SHAPE_DIRECTED_POINTS]. </member> + <member name="emission_ring_axis" type="Vector3" setter="set_emission_ring_axis" getter="get_emission_ring_axis"> + The axis of the ring when using the emitter [constant EMISSION_SHAPE_RING]. + </member> + <member name="emission_ring_height" type="float" setter="set_emission_ring_height" getter="get_emission_ring_height"> + The height of the ring when using the emitter [constant EMISSION_SHAPE_RING]. + </member> + <member name="emission_ring_inner_radius" type="float" setter="set_emission_ring_inner_radius" getter="get_emission_ring_inner_radius"> + The inner radius of the ring when using the emitter [constant EMISSION_SHAPE_RING]. + </member> + <member name="emission_ring_radius" type="float" setter="set_emission_ring_radius" getter="get_emission_ring_radius"> + The radius of the ring when using the emitter [constant EMISSION_SHAPE_RING]. + </member> <member name="emission_shape" type="int" setter="set_emission_shape" getter="get_emission_shape" enum="CPUParticles3D.EmissionShape" default="0"> Particles will be emitted inside this region. See [enum EmissionShape] for possible values. </member> @@ -378,7 +390,10 @@ <constant name="EMISSION_SHAPE_DIRECTED_POINTS" value="4" enum="EmissionShape"> Particles will be emitted at a position chosen randomly among [member emission_points]. Particle velocity and rotation will be set based on [member emission_normals]. Particle color will be modulated by [member emission_colors]. </constant> - <constant name="EMISSION_SHAPE_MAX" value="5" enum="EmissionShape"> + <constant name="EMISSION_SHAPE_RING" value="5" enum="EmissionShape"> + Particles will be emitted in a ring or cylinder. + </constant> + <constant name="EMISSION_SHAPE_MAX" value="6" enum="EmissionShape"> Represents the size of the [enum EmissionShape] enum. </constant> </constants> diff --git a/doc/classes/ParticlesMaterial.xml b/doc/classes/ParticlesMaterial.xml index 52430b3f45..640a26b8cb 100644 --- a/doc/classes/ParticlesMaterial.xml +++ b/doc/classes/ParticlesMaterial.xml @@ -174,6 +174,18 @@ <member name="emission_point_texture" type="Texture2D" setter="set_emission_point_texture" getter="get_emission_point_texture"> Particles will be emitted at positions determined by sampling this texture at a random position. Used with [constant EMISSION_SHAPE_POINTS] and [constant EMISSION_SHAPE_DIRECTED_POINTS]. Can be created automatically from mesh or node by selecting "Create Emission Points from Mesh/Node" under the "Particles" tool in the toolbar. </member> + <member name="emission_ring_axis" type="Vector3" setter="set_emission_ring_axis" getter="get_emission_ring_axis"> + The axis of the ring when using the emitter [constant EMISSION_SHAPE_RING]. + </member> + <member name="emission_ring_height" type="float" setter="set_emission_ring_height" getter="get_emission_ring_height"> + The height of the ring when using the emitter [constant EMISSION_SHAPE_RING]. + </member> + <member name="emission_ring_inner_radius" type="float" setter="set_emission_ring_inner_radius" getter="get_emission_ring_inner_radius"> + The inner radius of the ring when using the emitter [constant EMISSION_SHAPE_RING]. + </member> + <member name="emission_ring_radius" type="float" setter="set_emission_ring_radius" getter="get_emission_ring_radius"> + The radius of the ring when using the emitter [constant EMISSION_SHAPE_RING]. + </member> <member name="emission_shape" type="int" setter="set_emission_shape" getter="get_emission_shape" enum="ParticlesMaterial.EmissionShape" default="0"> Particles will be emitted inside this region. Use [enum EmissionShape] constants for values. </member> @@ -338,7 +350,10 @@ <constant name="EMISSION_SHAPE_DIRECTED_POINTS" value="4" enum="EmissionShape"> Particles will be emitted at a position determined by sampling a random point on the [member emission_point_texture]. Particle velocity and rotation will be set based on [member emission_normal_texture]. Particle color will be modulated by [member emission_color_texture]. </constant> - <constant name="EMISSION_SHAPE_MAX" value="5" enum="EmissionShape"> + <constant name="EMISSION_SHAPE_RING" value="5" enum="EmissionShape"> + Particles will be emitted in a ring or cylinder. + </constant> + <constant name="EMISSION_SHAPE_MAX" value="6" enum="EmissionShape"> Represents the size of the [enum EmissionShape] enum. </constant> <constant name="SUB_EMITTER_DISABLED" value="0" enum="SubEmitterMode"> diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml index 5a4068ec86..e6f696d27c 100644 --- a/doc/classes/TileMap.xml +++ b/doc/classes/TileMap.xml @@ -36,6 +36,8 @@ </return> <argument index="0" name="coords" type="Vector2i"> </argument> + <argument index="1" name="use_proxies" type="bool"> + </argument> <description> </description> </method> @@ -44,6 +46,8 @@ </return> <argument index="0" name="coords" type="Vector2i"> </argument> + <argument index="1" name="use_proxies" type="bool"> + </argument> <description> </description> </method> @@ -52,6 +56,8 @@ </return> <argument index="0" name="coords" type="Vector2i"> </argument> + <argument index="1" name="use_proxies" type="bool"> + </argument> <description> </description> </method> diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 436e15387d..8185b09a30 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -27,6 +27,40 @@ <description> </description> </method> + <method name="cleanup_invalid_tile_proxies"> + <return type="void"> + </return> + <description> + </description> + </method> + <method name="clear_tile_proxies"> + <return type="void"> + </return> + <description> + </description> + </method> + <method name="get_alternative_level_tile_proxy"> + <return type="Array"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <argument index="1" name="coords_from" type="Vector2i"> + </argument> + <argument index="2" name="alternative_from" type="int"> + </argument> + <description> + </description> + </method> + <method name="get_coords_level_tile_proxy"> + <return type="Array"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <argument index="1" name="coords_from" type="Vector2i"> + </argument> + <description> + </description> + </method> <method name="get_navigation_layer_layers" qualifiers="const"> <return type="int"> </return> @@ -103,6 +137,14 @@ <description> </description> </method> + <method name="get_source_level_tile_proxy"> + <return type="int"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <description> + </description> + </method> <method name="get_terrain_color" qualifiers="const"> <return type="Color"> </return> @@ -139,6 +181,28 @@ <description> </description> </method> + <method name="has_alternative_level_tile_proxy"> + <return type="bool"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <argument index="1" name="coords_from" type="Vector2i"> + </argument> + <argument index="2" name="alternative_from" type="int"> + </argument> + <description> + </description> + </method> + <method name="has_coords_level_tile_proxy"> + <return type="bool"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <argument index="1" name="coords_from" type="Vector2i"> + </argument> + <description> + </description> + </method> <method name="has_source" qualifiers="const"> <return type="bool"> </return> @@ -147,6 +211,48 @@ <description> </description> </method> + <method name="has_source_level_tile_proxy"> + <return type="bool"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <description> + </description> + </method> + <method name="map_tile_proxy" qualifiers="const"> + <return type="Array"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <argument index="1" name="coords_from" type="Vector2i"> + </argument> + <argument index="2" name="alternative_from" type="int"> + </argument> + <description> + </description> + </method> + <method name="remove_alternative_level_tile_proxy"> + <return type="void"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <argument index="1" name="coords_from" type="Vector2i"> + </argument> + <argument index="2" name="alternative_from" type="int"> + </argument> + <description> + </description> + </method> + <method name="remove_coords_level_tile_proxy"> + <return type="void"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <argument index="1" name="coords_from" type="Vector2i"> + </argument> + <description> + </description> + </method> <method name="remove_source"> <return type="void"> </return> @@ -155,6 +261,46 @@ <description> </description> </method> + <method name="remove_source_level_tile_proxy"> + <return type="void"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <description> + </description> + </method> + <method name="set_alternative_level_tile_proxy"> + <return type="void"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <argument index="1" name="coords_from" type="Vector2i"> + </argument> + <argument index="2" name="alternative_from" type="int"> + </argument> + <argument index="3" name="source_to" type="int"> + </argument> + <argument index="4" name="coords_to" type="Vector2i"> + </argument> + <argument index="5" name="alternative_to" type="int"> + </argument> + <description> + </description> + </method> + <method name="set_coords_level_tile_proxy"> + <return type="void"> + </return> + <argument index="0" name="p_source_from" type="int"> + </argument> + <argument index="1" name="coords_from" type="Vector2i"> + </argument> + <argument index="2" name="source_to" type="int"> + </argument> + <argument index="3" name="coords_to" type="Vector2i"> + </argument> + <description> + </description> + </method> <method name="set_navigation_layer_layers"> <return type="void"> </return> @@ -225,6 +371,16 @@ <description> </description> </method> + <method name="set_source_level_tile_proxy"> + <return type="void"> + </return> + <argument index="0" name="source_from" type="int"> + </argument> + <argument index="1" name="source_to" type="int"> + </argument> + <description> + </description> + </method> <method name="set_terrain_color"> <return type="void"> </return> diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 6de7f6c425..2cb73664f5 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -57,35 +57,43 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f } } +void EditorLog::_update_theme() { + Ref<Font> normal_font = get_theme_font(SNAME("output_source"), SNAME("EditorFonts")); + if (normal_font.is_valid()) { + log->add_theme_font_override("normal_font", normal_font); + } + + log->add_theme_font_size_override("normal_font_size", get_theme_font_size(SNAME("output_source_size"), SNAME("EditorFonts"))); + log->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4)); + + Ref<Font> bold_font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); + if (bold_font.is_valid()) { + log->add_theme_font_override("bold_font", bold_font); + } + + type_filter_map[MSG_TYPE_STD]->toggle_button->set_icon(get_theme_icon(SNAME("Popup"), SNAME("EditorIcons"))); + type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_icon(get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons"))); + type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_icon(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons"))); + type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); + + clear_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); + copy_button->set_icon(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons"))); + collapse_button->set_icon(get_theme_icon(SNAME("CombineLines"), SNAME("EditorIcons"))); + show_search_button->set_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); +} + void EditorLog::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - //button->set_icon(get_icon("Console","EditorIcons")); - log->add_theme_font_override("normal_font", get_theme_font(SNAME("output_source"), SNAME("EditorFonts"))); - log->add_theme_font_size_override("normal_font_size", get_theme_font_size(SNAME("output_source_size"), SNAME("EditorFonts"))); - log->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4)); - log->add_theme_font_override("bold_font", get_theme_font(SNAME("bold"), SNAME("EditorFonts"))); - - type_filter_map[MSG_TYPE_STD]->toggle_button->set_icon(get_theme_icon(SNAME("Popup"), SNAME("EditorIcons"))); - type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_icon(get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons"))); - type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_icon(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons"))); - type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); - - clear_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); - copy_button->set_icon(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons"))); - collapse_button->set_icon(get_theme_icon(SNAME("CombineLines"), SNAME("EditorIcons"))); - show_search_button->set_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); - - _load_state(); - - } else if (p_what == NOTIFICATION_THEME_CHANGED) { - Ref<Font> df_output_code = get_theme_font(SNAME("output_source"), SNAME("EditorFonts")); - if (df_output_code.is_valid()) { - if (log != nullptr) { - log->add_theme_font_override("normal_font", get_theme_font(SNAME("output_source"), SNAME("EditorFonts"))); - log->add_theme_font_size_override("normal_font_size", get_theme_font_size(SNAME("output_source_size"), SNAME("EditorFonts"))); - log->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4)); - } - } + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + _update_theme(); + _load_state(); + } break; + case NOTIFICATION_THEME_CHANGED: { + _update_theme(); + _rebuild_log(); + } break; + default: + break; } } diff --git a/editor/editor_log.h b/editor/editor_log.h index 3b6476634a..6cbf4bedee 100644 --- a/editor/editor_log.h +++ b/editor/editor_log.h @@ -163,6 +163,8 @@ private: void _save_state(); void _load_state(); + void _update_theme(); + protected: static void _bind_methods(); void _notification(int p_what); diff --git a/editor/node_3d_editor_gizmos.cpp b/editor/node_3d_editor_gizmos.cpp index 2e85e19732..74fb38b66b 100644 --- a/editor/node_3d_editor_gizmos.cpp +++ b/editor/node_3d_editor_gizmos.cpp @@ -562,7 +562,7 @@ bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, if (selectable_icon_size > 0.0f) { Transform3D t = spatial_node->get_global_transform(); Vector3 camera_position = p_camera->get_camera_transform().origin; - if (camera_position.distance_squared_to(t.origin) > 0.01) { + if (!camera_position.is_equal_approx(t.origin)) { t.set_look_at(t.origin, camera_position); } @@ -578,7 +578,7 @@ bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point, Transform3D orig_camera_transform = p_camera->get_camera_transform(); - if (orig_camera_transform.origin.distance_squared_to(t.origin) > 0.01 && + if (!orig_camera_transform.origin.is_equal_approx(t.origin) && ABS(orig_camera_transform.basis.get_axis(Vector3::AXIS_Z).dot(Vector3(0, 1, 0))) < 0.99) { p_camera->look_at(t.origin); } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index a0884a81cd..7290a39463 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -3338,7 +3338,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() { Transform3D camera_xform = camera->get_transform(); - if (xform.origin.distance_squared_to(camera_xform.origin) < 0.01) { + if (xform.origin.is_equal_approx(camera_xform.origin)) { for (int i = 0; i < 3; i++) { RenderingServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], false); RenderingServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], false); diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp index 8861fee7a6..8866e8c53e 100644 --- a/editor/plugins/path_2d_editor_plugin.cpp +++ b/editor/plugins/path_2d_editor_plugin.cpp @@ -481,7 +481,7 @@ void Path2DEditor::_mode_selected(int p_mode) { Vector2 begin = node->get_curve()->get_point_position(0); Vector2 end = node->get_curve()->get_point_position(node->get_curve()->get_point_count() - 1); - if (begin.distance_to(end) < CMP_EPSILON) { + if (begin.is_equal_approx(end)) { return; } diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index cd06fdc757..9bb8fbacbb 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -65,13 +65,11 @@ void SpriteFramesEditor::_sheet_preview_draw() { int x = i * width; split_sheet_preview->draw_line(Point2(x, 0), Point2(x, size.height), Color(1, 1, 1, a)); split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, size.height), Color(0, 0, 0, a)); - - for (int j = 1; j < v; j++) { - int y = j * height; - - split_sheet_preview->draw_line(Point2(0, y), Point2(size.width, y), Color(1, 1, 1, a)); - split_sheet_preview->draw_line(Point2(0, y + 1), Point2(size.width, y + 1), Color(0, 0, 0, a)); - } + } + for (int i = 1; i < v; i++) { + int y = i * height; + split_sheet_preview->draw_line(Point2(0, y), Point2(size.width, y), Color(1, 1, 1, a)); + split_sheet_preview->draw_line(Point2(0, y + 1), Point2(size.width, y + 1), Color(0, 0, 0, a)); } if (frames_selected.size() == 0) { diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp new file mode 100644 index 0000000000..bbafc7802b --- /dev/null +++ b/editor/plugins/tiles/atlas_merging_dialog.cpp @@ -0,0 +1,320 @@ +/*************************************************************************/ +/* atlas_merging_dialog.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "atlas_merging_dialog.h" + +#include "editor/editor_scale.h" + +#include "scene/gui/control.h" +#include "scene/gui/split_container.h" + +void AtlasMergingDialog::_property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing) { + _set(p_property, p_value); +} + +void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atlas_sources, int p_max_columns) { + merged.instantiate(); + merged_mapping.clear(); + + if (p_atlas_sources.size() >= 2) { + Ref<Image> output_image; + output_image.instantiate(); + output_image->create(1, 1, false, Image::FORMAT_RGBA8); + + // Compute the new texture region size. + Vector2i new_texture_region_size; + for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) { + Ref<TileSetAtlasSource> atlas_source = p_atlas_sources[source_index]; + new_texture_region_size = new_texture_region_size.max(atlas_source->get_texture_region_size()); + } + + // Generate the merged TileSetAtlasSource. + Vector2i atlas_offset; + int line_height = 0; + for (int source_index = 0; source_index < p_atlas_sources.size(); source_index++) { + Ref<TileSetAtlasSource> atlas_source = p_atlas_sources[source_index]; + merged_mapping.push_back(Map<Vector2i, Vector2i>()); + + // Layout the tiles. + Vector2i atlas_size; + + for (int tile_index = 0; tile_index < atlas_source->get_tiles_count(); tile_index++) { + Vector2i tile_id = atlas_source->get_tile_id(tile_index); + atlas_size = atlas_size.max(tile_id + atlas_source->get_tile_size_in_atlas(tile_id)); + + Rect2i new_tile_rect_in_altas = Rect2i(atlas_offset + tile_id, atlas_source->get_tile_size_in_atlas(tile_id)); + + // Create tiles and alternatives, then copy their properties. + for (int alternative_index = 0; alternative_index < atlas_source->get_alternative_tiles_count(tile_id); alternative_index++) { + int alternative_id = atlas_source->get_alternative_tile_id(tile_id, alternative_index); + if (alternative_id == 0) { + merged->create_tile(new_tile_rect_in_altas.position, new_tile_rect_in_altas.size); + } else { + merged->create_alternative_tile(new_tile_rect_in_altas.position, alternative_index); + } + + // Copy the properties. + TileData *original_tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id)); + List<PropertyInfo> properties; + original_tile_data->get_property_list(&properties); + for (List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { + const StringName &property_name = E->get().name; + merged->set(property_name, original_tile_data->get(property_name)); + } + + // Add to the mapping. + merged_mapping[source_index][tile_id] = new_tile_rect_in_altas.position; + } + + // Copy the texture. + Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id); + Rect2 dst_rect_wide = Rect2i(new_tile_rect_in_altas.position * new_texture_region_size, new_tile_rect_in_altas.size * new_texture_region_size); + if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) { + output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height())); + } + output_image->blit_rect(atlas_source->get_texture()->get_image(), src_rect, (dst_rect_wide.get_position() + dst_rect_wide.get_end()) / 2 - src_rect.size / 2); + } + + // Compute the atlas offset. + line_height = MAX(atlas_size.y, line_height); + atlas_offset.x += atlas_size.x; + if (atlas_offset.x >= p_max_columns) { + atlas_offset.x = 0; + atlas_offset.y += line_height; + line_height = 0; + } + } + + Ref<ImageTexture> output_image_texture; + output_image_texture.instantiate(); + output_image_texture->create_from_image(output_image); + + merged->set_texture(output_image_texture); + merged->set_texture_region_size(new_texture_region_size); + } +} + +void AtlasMergingDialog::_update_texture() { + Vector<int> selected = atlas_merging_atlases_list->get_selected_items(); + if (selected.size() >= 2) { + Vector<Ref<TileSetAtlasSource>> to_merge; + for (int i = 0; i < selected.size(); i++) { + int source_id = atlas_merging_atlases_list->get_item_metadata(selected[i]); + to_merge.push_back(tile_set->get_source(source_id)); + } + _generate_merged(to_merge, next_line_after_column); + preview->set_texture(merged->get_texture()); + preview->show(); + select_2_atlases_label->hide(); + get_ok_button()->set_disabled(false); + merge_button->set_disabled(false); + } else { + _generate_merged(Vector<Ref<TileSetAtlasSource>>(), next_line_after_column); + preview->set_texture(Ref<Texture2D>()); + preview->hide(); + select_2_atlases_label->show(); + get_ok_button()->set_disabled(true); + merge_button->set_disabled(true); + } +} + +void AtlasMergingDialog::_merge_confirmed(String p_path) { + ERR_FAIL_COND(!merged.is_valid()); + + Ref<ImageTexture> output_image_texture = merged->get_texture(); + output_image_texture->get_image()->save_png(p_path); + + Ref<Texture2D> new_texture_resource = ResourceLoader::load(p_path, "Texture2D"); + merged->set_texture(new_texture_resource); + + undo_redo->create_action("Merge TileSetAtlasSource"); + int next_id = tile_set->get_next_source_id(); + undo_redo->add_do_method(*tile_set, "add_source", merged, next_id); + undo_redo->add_undo_method(*tile_set, "remove_source", next_id); + + if (delete_original_atlases) { + // Delete originals if needed. + Vector<int> selected = atlas_merging_atlases_list->get_selected_items(); + for (int i = 0; i < selected.size(); i++) { + int source_id = atlas_merging_atlases_list->get_item_metadata(selected[i]); + Ref<TileSetAtlasSource> tas = tile_set->get_source(source_id); + undo_redo->add_do_method(*tile_set, "remove_source", source_id); + undo_redo->add_undo_method(*tile_set, "add_source", tas, source_id); + + // Add the tile proxies. + for (int tile_index = 0; tile_index < tas->get_tiles_count(); tile_index++) { + Vector2i tile_id = tas->get_tile_id(tile_index); + undo_redo->add_do_method(*tile_set, "set_coords_level_tile_proxy", source_id, tile_id, next_id, merged_mapping[i][tile_id]); + if (tile_set->has_coords_level_tile_proxy(source_id, tile_id)) { + Array a = tile_set->get_coords_level_tile_proxy(source_id, tile_id); + undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", a[0], a[1]); + } else { + undo_redo->add_undo_method(*tile_set, "remove_coords_level_tile_proxy", source_id, tile_id); + } + } + } + } + undo_redo->commit_action(); + commited_actions_count++; + + hide(); +} + +void AtlasMergingDialog::ok_pressed() { + delete_original_atlases = false; + editor_file_dialog->popup_file_dialog(); +} + +void AtlasMergingDialog::cancel_pressed() { + for (int i = 0; i < commited_actions_count; i++) { + undo_redo->undo(); + } + commited_actions_count = 0; +} + +void AtlasMergingDialog::custom_action(const String &p_action) { + if (p_action == "merge") { + delete_original_atlases = true; + editor_file_dialog->popup_file_dialog(); + } +} + +bool AtlasMergingDialog::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "next_line_after_column" && p_value.get_type() == Variant::INT) { + next_line_after_column = p_value; + _update_texture(); + return true; + } + return false; +} + +bool AtlasMergingDialog::_get(const StringName &p_name, Variant &r_ret) const { + if (p_name == "next_line_after_column") { + r_ret = next_line_after_column; + return true; + } + return false; +} + +void AtlasMergingDialog::update_tile_set(Ref<TileSet> p_tile_set) { + ERR_FAIL_COND(!p_tile_set.is_valid()); + tile_set = p_tile_set; + + atlas_merging_atlases_list->clear(); + for (int i = 0; i < p_tile_set->get_source_count(); i++) { + int source_id = p_tile_set->get_source_id(i); + Ref<TileSetAtlasSource> atlas_source = p_tile_set->get_source(source_id); + if (atlas_source.is_valid()) { + Ref<Texture2D> texture = atlas_source->get_texture(); + if (texture.is_valid()) { + String item_text = vformat("%s (id:%d)", texture->get_path().get_file(), source_id); + atlas_merging_atlases_list->add_item(item_text, texture); + atlas_merging_atlases_list->set_item_metadata(atlas_merging_atlases_list->get_item_count() - 1, source_id); + } + } + } + + get_ok_button()->set_disabled(true); + merge_button->set_disabled(true); + + commited_actions_count = 0; +} + +AtlasMergingDialog::AtlasMergingDialog() { + // Atlas merging window. + set_title(TTR("Atlas Merging")); + set_hide_on_ok(false); + + // Ok buttons + get_ok_button()->set_text(TTR("Merge (Keep original Atlases)")); + get_ok_button()->set_disabled(true); + merge_button = add_button(TTR("Merge"), true, "merge"); + merge_button->set_disabled(true); + + HSplitContainer *atlas_merging_h_split_container = memnew(HSplitContainer); + atlas_merging_h_split_container->set_h_size_flags(Control::SIZE_EXPAND_FILL); + atlas_merging_h_split_container->set_v_size_flags(Control::SIZE_EXPAND_FILL); + add_child(atlas_merging_h_split_container); + + // Atlas sources item list. + atlas_merging_atlases_list = memnew(ItemList); + atlas_merging_atlases_list->set_fixed_icon_size(Size2i(60, 60) * EDSCALE); + atlas_merging_atlases_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); + atlas_merging_atlases_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + atlas_merging_atlases_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); + atlas_merging_atlases_list->set_custom_minimum_size(Size2(100, 200)); + atlas_merging_atlases_list->set_select_mode(ItemList::SELECT_MULTI); + atlas_merging_atlases_list->connect("multi_selected", callable_mp(this, &AtlasMergingDialog::_update_texture).unbind(2)); + atlas_merging_h_split_container->add_child(atlas_merging_atlases_list); + + VBoxContainer *atlas_merging_right_panel = memnew(VBoxContainer); + atlas_merging_right_panel->set_h_size_flags(Control::SIZE_EXPAND_FILL); + atlas_merging_h_split_container->add_child(atlas_merging_right_panel); + + // Settings. + Label *settings_label = memnew(Label); + settings_label->set_text(TTR("Settings:")); + atlas_merging_right_panel->add_child(settings_label); + + columns_editor_property = memnew(EditorPropertyInteger); + columns_editor_property->set_label(TTR("Next Line After Column")); + columns_editor_property->set_object_and_property(this, "next_line_after_column"); + columns_editor_property->update_property(); + columns_editor_property->connect("property_changed", callable_mp(this, &AtlasMergingDialog::_property_changed)); + atlas_merging_right_panel->add_child(columns_editor_property); + + // Preview. + Label *preview_label = memnew(Label); + preview_label->set_text(TTR("Preview:")); + atlas_merging_right_panel->add_child(preview_label); + + preview = memnew(TextureRect); + preview->set_h_size_flags(Control::SIZE_EXPAND_FILL); + preview->set_v_size_flags(Control::SIZE_EXPAND_FILL); + preview->set_expand(true); + preview->hide(); + preview->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); + atlas_merging_right_panel->add_child(preview); + + select_2_atlases_label = memnew(Label); + select_2_atlases_label->set_h_size_flags(Control::SIZE_EXPAND_FILL); + select_2_atlases_label->set_v_size_flags(Control::SIZE_EXPAND_FILL); + select_2_atlases_label->set_align(Label::ALIGN_CENTER); + select_2_atlases_label->set_valign(Label::VALIGN_CENTER); + select_2_atlases_label->set_text(TTR("Please select two atlases or more.")); + atlas_merging_right_panel->add_child(select_2_atlases_label); + + // The file dialog to choose the texture path. + editor_file_dialog = memnew(EditorFileDialog); + editor_file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); + editor_file_dialog->add_filter("*.png"); + editor_file_dialog->connect("file_selected", callable_mp(this, &AtlasMergingDialog::_merge_confirmed)); + add_child(editor_file_dialog); +} diff --git a/editor/plugins/tiles/atlas_merging_dialog.h b/editor/plugins/tiles/atlas_merging_dialog.h new file mode 100644 index 0000000000..7cb54bc17e --- /dev/null +++ b/editor/plugins/tiles/atlas_merging_dialog.h @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* atlas_merging_dialog.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 ATLAS_MERGING_DIALOG_H +#define ATLAS_MERGING_DIALOG_H + +#include "editor/editor_node.h" +#include "editor/editor_properties.h" + +#include "scene/gui/dialogs.h" +#include "scene/gui/item_list.h" +#include "scene/gui/texture_rect.h" +#include "scene/resources/tile_set.h" + +class AtlasMergingDialog : public ConfirmationDialog { + GDCLASS(AtlasMergingDialog, ConfirmationDialog); + +private: + int commited_actions_count = 0; + bool delete_original_atlases = true; + Ref<TileSetAtlasSource> merged; + LocalVector<Map<Vector2i, Vector2i>> merged_mapping; + Ref<TileSet> tile_set; + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + // Settings. + int next_line_after_column = 30; + + // GUI. + ItemList *atlas_merging_atlases_list; + EditorPropertyVector2i *texture_region_size_editor_property; + EditorPropertyInteger *columns_editor_property; + TextureRect *preview; + Label *select_2_atlases_label; + EditorFileDialog *editor_file_dialog; + Button *merge_button; + + void _property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing); + + void _generate_merged(Vector<Ref<TileSetAtlasSource>> p_atlas_sources, int p_max_columns); + void _update_texture(); + void _merge_confirmed(String p_path); + +protected: + virtual void ok_pressed() override; + virtual void cancel_pressed() override; + virtual void custom_action(const String &) override; + + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + +public: + void update_tile_set(Ref<TileSet> p_tile_set); + + AtlasMergingDialog(); +}; + +#endif // ATLAS_MERGING_DIALOG_H diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index bafc2b3985..b2046f4322 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -47,7 +47,7 @@ class TileAtlasView : public Control { private: TileSet *tile_set; TileSetAtlasSource *tile_set_atlas_source; - int source_id = -1; + int source_id = TileSet::INVALID_SOURCE; enum DragType { DRAG_TYPE_NONE, diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 6e3c55d801..44822bec3a 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -56,18 +56,11 @@ void TileMapEditorTilesPlugin::_notification(int p_what) { picker_button->set_icon(get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons"))); erase_button->set_icon(get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons"))); - toggle_grid_button->set_icon(get_theme_icon(SNAME("Grid"), SNAME("EditorIcons"))); - missing_atlas_texture_icon = get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons")); - - toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid")); break; case NOTIFICATION_VISIBILITY_CHANGED: _stop_dragging(); break; - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: - toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid")); - break; } } @@ -85,10 +78,6 @@ void TileMapEditorTilesPlugin::_on_scattering_spinbox_changed(double p_value) { scattering = p_value; } -void TileMapEditorTilesPlugin::_on_grid_toggled(bool p_pressed) { - EditorSettings::get_singleton()->set("editors/tiles_editor/display_grid", p_pressed); -} - void TileMapEditorTilesPlugin::_update_toolbar() { // Stop draggig if needed. _stop_dragging(); @@ -204,7 +193,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() { } // Synchronize - TilesEditor::get_singleton()->set_atlas_sources_lists_current(sources_list->get_current()); + TilesEditor::get_singleton()->set_sources_lists_current(sources_list->get_current()); } void TileMapEditorTilesPlugin::_update_bottom_panel() { @@ -410,7 +399,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p if (!tile_map_selection.is_empty()) { undo_redo->create_action(TTR("Delete tiles")); for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + undo_redo->add_do_method(tile_map, "set_cell", E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get())); } undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection()); @@ -441,7 +430,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p if (!tile_map_selection.is_empty()) { undo_redo->create_action(TTR("Delete tiles")); for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { - undo_redo->add_do_method(tile_map, "set_cell", E->get(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + undo_redo->add_do_method(tile_map, "set_cell", E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); undo_redo->add_undo_method(tile_map, "set_cell", E->get(), tile_map->get_cell_source_id(E->get()), tile_map->get_cell_atlas_coords(E->get()), tile_map->get_cell_alternative_tile(E->get())); } undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection()); @@ -462,12 +451,12 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p case DRAG_TYPE_PAINT: { Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_last_mouse_pos, mpos); for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) { - if (!erase_button->is_pressed() && E->get().source_id == -1) { + if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) { continue; } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords))); + drag_modified.insert(coords, tile_map->get_cell(coords)); } tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } @@ -478,12 +467,12 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p if (!drag_modified.has(line[i])) { Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed()); for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) { - if (!erase_button->is_pressed() && E->get().source_id == -1) { + if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) { continue; } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords))); + drag_modified.insert(coords, tile_map->get_cell(coords)); } tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } @@ -516,8 +505,8 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p drag_modified.clear(); for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) { Vector2i coords = E->get(); - drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords))); - tile_map->set_cell(coords, -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + drag_modified.insert(coords, tile_map->get_cell(coords)); + tile_map->set_cell(coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); } } else { // Select tiles @@ -536,12 +525,12 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p drag_modified.clear(); Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, mpos, mpos); for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) { - if (!erase_button->is_pressed() && E->get().source_id == -1) { + if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) { continue; } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords))); + drag_modified.insert(coords, tile_map->get_cell(coords)); } tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } @@ -562,12 +551,12 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p if (!drag_modified.has(line[i])) { Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed()); for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) { - if (!erase_button->is_pressed() && E->get().source_id == -1) { + if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) { continue; } Vector2i coords = E->key(); if (!drag_modified.has(coords)) { - drag_modified.insert(coords, TileMapCell(tile_map->get_cell_source_id(coords), tile_map->get_cell_atlas_coords(coords), tile_map->get_cell_alternative_tile(coords))); + drag_modified.insert(coords, tile_map->get_cell(coords)); } tile_map->set_cell(coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); } @@ -634,7 +623,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over for (int x = rect.position.x; x < rect.get_end().x; x++) { for (int y = rect.position.y; y < rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); - if (tile_map->get_cell_source_id(coords) != -1) { + if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) { Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - tile_shape_size / 2, tile_shape_size)); tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0), false); } @@ -648,7 +637,7 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over for (int x = rect.position.x; x < rect.get_end().x; x++) { for (int y = rect.position.y; y < rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); - if (tile_map->get_cell_source_id(coords) != -1) { + if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) { to_draw.insert(coords); } } @@ -871,7 +860,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_ // Get or create the pattern. TileMapPattern erase_pattern; - erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern; Map<Vector2i, TileMapCell> output; @@ -923,7 +912,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start // Get or create the pattern. TileMapPattern erase_pattern; - erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern; // Compute the offset to align things to the bottom or right. @@ -974,7 +963,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i // Get or create the pattern. TileMapPattern erase_pattern; - erase_pattern.set_cell(Vector2i(0, 0), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern; Map<Vector2i, TileMapCell> output; @@ -983,7 +972,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i // If we are filling empty tiles, compute the tilemap boundaries. Rect2i boundaries; - if (source.source_id == -1) { + if (source.source_id == TileSet::INVALID_SOURCE) { boundaries = tile_map->get_used_rect(); } @@ -999,7 +988,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i if (source.source_id == tile_map->get_cell_source_id(coords) && source.get_atlas_coords() == tile_map->get_cell_atlas_coords(coords) && source.alternative_tile == tile_map->get_cell_alternative_tile(coords) && - (source.source_id != -1 || boundaries.has_point(coords))) { + (source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) { if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) { // Paint a random tile. output.insert(coords, _pick_random_tile(pattern)); @@ -1027,7 +1016,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i } else { // Replace all tiles like the source. TypedArray<Vector2i> to_check; - if (source.source_id == -1) { + if (source.source_id == TileSet::INVALID_SOURCE) { Rect2i rect = tile_map->get_used_rect(); if (rect.size.x <= 0 || rect.size.y <= 0) { rect = Rect2i(p_coords, Vector2i(1, 1)); @@ -1045,7 +1034,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i if (source.source_id == tile_map->get_cell_source_id(coords) && source.get_atlas_coords() == tile_map->get_cell_atlas_coords(coords) && source.alternative_tile == tile_map->get_cell_alternative_tile(coords) && - (source.source_id != -1 || boundaries.has_point(coords))) { + (source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) { if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) { // Paint a random tile. output.insert(coords, _pick_random_tile(pattern)); @@ -1102,7 +1091,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { tile_map_selection.erase(coords); } } else { - if (tile_map->get_cell_source_id(coords) != -1) { + if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) { tile_map_selection.insert(coords); } } @@ -1173,7 +1162,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { for (int x = rect.position.x; x < rect.get_end().x; x++) { for (int y = rect.position.y; y < rect.get_end().y; y++) { Vector2i coords = Vector2i(x, y); - if (tile_map->get_cell_source_id(coords) != -1) { + if (tile_map->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) { coords_array.push_back(coords); } } @@ -1198,7 +1187,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, mpos); undo_redo->create_action(TTR("Paint tiles")); for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) { - if (!erase_button->is_pressed() && E->get().source_id == -1) { + if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) { continue; } undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); @@ -1210,7 +1199,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { Map<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos)); undo_redo->create_action(TTR("Paint tiles")); for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) { - if (!erase_button->is_pressed() && E->get().source_id == -1) { + if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) { continue; } undo_redo->add_do_method(tile_map, "set_cell", E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); @@ -1221,7 +1210,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { case DRAG_TYPE_BUCKET: { undo_redo->create_action(TTR("Paint tiles")); for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) { - if (!erase_button->is_pressed() && E->get().source_id == -1) { + if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) { continue; } undo_redo->add_do_method(tile_map, "set_cell", E->key(), tile_map->get_cell_source_id(E->key()), tile_map->get_cell_atlas_coords(E->key()), tile_map->get_cell_alternative_tile(E->key())); @@ -1249,7 +1238,7 @@ void TileMapEditorTilesPlugin::_stop_dragging() { void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() { TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); if (!tile_map) { - hovered_tile.source_id = -1; + hovered_tile.source_id = TileSet::INVALID_SOURCE; hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; tile_set_selection.clear(); @@ -1260,7 +1249,7 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() { Ref<TileSet> tile_set = tile_map->get_tileset(); if (!tile_set.is_valid()) { - hovered_tile.source_id = -1; + hovered_tile.source_id = TileSet::INVALID_SOURCE; hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; tile_set_selection.clear(); @@ -1271,7 +1260,7 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() { int source_index = sources_list->get_current(); if (source_index < 0 || source_index >= sources_list->get_item_count()) { - hovered_tile.source_id = -1; + hovered_tile.source_id = TileSet::INVALID_SOURCE; hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; tile_set_selection.clear(); @@ -1287,7 +1276,7 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() { !tile_set->has_source(hovered_tile.source_id) || !tile_set->get_source(hovered_tile.source_id)->has_tile(hovered_tile.get_atlas_coords()) || !tile_set->get_source(hovered_tile.source_id)->has_alternative_tile(hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile)) { - hovered_tile.source_id = -1; + hovered_tile.source_id = TileSet::INVALID_SOURCE; hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; } @@ -1403,7 +1392,7 @@ void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern( TypedArray<Vector2i> used_cells = selection_pattern->get_used_cells(); for (int i = 0; i < used_cells.size(); i++) { Vector2i coords = used_cells[i]; - if (selection_pattern->get_cell_source_id(coords) != -1) { + if (selection_pattern->get_cell_source_id(coords) != TileSet::INVALID_SOURCE) { tile_set_selection.insert(TileMapCell(selection_pattern->get_cell_source_id(coords), selection_pattern->get_cell_atlas_coords(coords), selection_pattern->get_cell_alternative_tile(coords))); } } @@ -1475,7 +1464,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() { } void TileMapEditorTilesPlugin::_tile_atlas_control_mouse_exited() { - hovered_tile.source_id = -1; + hovered_tile.source_id = TileSet::INVALID_SOURCE; hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; tile_set_dragging_selection = false; @@ -1634,7 +1623,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_draw() { } void TileMapEditorTilesPlugin::_tile_alternatives_control_mouse_exited() { - hovered_tile.source_id = -1; + hovered_tile.source_id = TileSet::INVALID_SOURCE; hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; tile_set_dragging_selection = false; @@ -1856,19 +1845,6 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { _on_random_tile_checkbox_toggled(false); - // Wide empty separation control. - Control *h_empty_space = memnew(Control); - h_empty_space->set_h_size_flags(SIZE_EXPAND_FILL); - toolbar->add_child(h_empty_space); - - // Grid toggle. - toggle_grid_button = memnew(Button); - toggle_grid_button->set_flat(true); - toggle_grid_button->set_toggle_mode(true); - toggle_grid_button->set_tooltip(TTR("Toggle grid visibility.")); - toggle_grid_button->connect("toggled", callable_mp(this, &TileMapEditorTilesPlugin::_on_grid_toggled)); - toolbar->add_child(toggle_grid_button); - // Default tool. paint_tool_button->set_pressed(true); _update_toolbar(); @@ -1898,8 +1874,8 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1)); sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_bottom_panel).unbind(1)); - sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_sources_lists_current)); - sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_sources_list), varray(sources_list)); + sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_sources_lists_current)); + sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_sources_list), varray(sources_list)); atlas_sources_split_container->add_child(sources_list); // Tile atlas source. @@ -2968,7 +2944,7 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() { } TileMapCell empty_cell; - empty_cell.source_id = -1; + empty_cell.source_id = TileSet::INVALID_SOURCE; empty_cell.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); empty_cell.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; per_terrain_terrains_tile_patterns_tiles[i][empty_pattern].insert(empty_cell); @@ -3190,6 +3166,9 @@ void TileMapEditor::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: missing_tile_texture = get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")); warning_pattern_texture = get_theme_icon(SNAME("WarningPattern"), SNAME("EditorIcons")); + advanced_menu_button->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons"))); + toggle_grid_button->set_icon(get_theme_icon(SNAME("Grid"), SNAME("EditorIcons"))); + toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid")); break; case NOTIFICATION_INTERNAL_PROCESS: if (is_visible_in_tree() && tileset_changed_needs_update) { @@ -3199,6 +3178,44 @@ void TileMapEditor::_notification(int p_what) { tileset_changed_needs_update = false; } break; + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: + toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid")); + break; + } +} + +void TileMapEditor::_on_grid_toggled(bool p_pressed) { + EditorSettings::get_singleton()->set("editors/tiles_editor/display_grid", p_pressed); +} + +void TileMapEditor::_advanced_menu_button_id_pressed(int p_id) { + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (!tile_map) { + return; + } + + Ref<TileSet> tile_set = tile_map->get_tileset(); + if (!tile_set.is_valid()) { + return; + } + + if (p_id == 0) { // Replace Tile Proxies + undo_redo->create_action(TTR("Replace Tiles with Proxies")); + TypedArray<Vector2i> used_cells = tile_map->get_used_cells(); + for (int i = 0; i < used_cells.size(); i++) { + Vector2i cell_coords = used_cells[i]; + TileMapCell from = tile_map->get_cell(cell_coords); + Array to_array = tile_set->map_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile); + TileMapCell to; + to.source_id = to_array[0]; + to.set_atlas_coords(to_array[1]); + to.alternative_tile = to_array[2]; + if (from != to) { + undo_redo->add_do_method(tile_map, "set_cell", cell_coords, to.source_id, to.get_atlas_coords(), to.alternative_tile); + undo_redo->add_undo_method(tile_map, "set_cell", cell_coords, from.source_id, from.get_atlas_coords(), from.alternative_tile); + } + } + undo_redo->commit_action(); } } @@ -3349,7 +3366,6 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { Vector2i tile_shape_size = tile_set->get_tile_size(); // Draw tiles with invalid IDs in the grid. - float icon_ratio = MIN(missing_tile_texture->get_size().x / tile_set->get_tile_size().x, missing_tile_texture->get_size().y / tile_set->get_tile_size().y) / 3; TypedArray<Vector2i> used_cells = tile_map->get_used_cells(); for (int i = 0; i < used_cells.size(); i++) { Vector2i coords = used_cells[i]; @@ -3365,25 +3381,33 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { if (!source || !source->has_tile(tile_atlas_coords) || !source->has_alternative_tile(tile_atlas_coords, tile_alternative_tile)) { // Generate a random color from the hashed values of the tiles. - Array to_hash; - to_hash.push_back(tile_source_id); - to_hash.push_back(tile_atlas_coords); - to_hash.push_back(tile_alternative_tile); - uint32_t hash = RandomPCG(to_hash.hash()).rand(); - - Color color; - color = color.from_hsv( - (float)((hash >> 24) & 0xFF) / 256.0, - Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), - Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), - 0.8); - - // Draw the scaled tile. - Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - Vector2(tile_shape_size) / 2, Vector2(tile_shape_size))); - tile_set->draw_tile_shape(p_overlay, cell_region, color, true, warning_pattern_texture); + Array a = tile_set->map_tile_proxy(tile_source_id, tile_atlas_coords, tile_alternative_tile); + if (int(a[0]) == tile_source_id && Vector2i(a[1]) == tile_atlas_coords && int(a[2]) == tile_alternative_tile) { + // Only display the pattern if we have no proxy tile. + Array to_hash; + to_hash.push_back(tile_source_id); + to_hash.push_back(tile_atlas_coords); + to_hash.push_back(tile_alternative_tile); + uint32_t hash = RandomPCG(to_hash.hash()).rand(); + + Color color; + color = color.from_hsv( + (float)((hash >> 24) & 0xFF) / 256.0, + Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), + Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), + 0.8); + + // Draw the scaled tile. + Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - Vector2(tile_shape_size) / 2, Vector2(tile_shape_size))); + tile_set->draw_tile_shape(p_overlay, cell_region, color, true, warning_pattern_texture); + } // Draw the warning icon. - Rect2 rect = Rect2(xform.xform(tile_map->map_to_world(coords)) - (icon_ratio * missing_tile_texture->get_size() * xform.get_scale() / 2), icon_ratio * missing_tile_texture->get_size() * xform.get_scale()); + int min_axis = missing_tile_texture->get_size().min_axis(); + Vector2 icon_size; + icon_size[min_axis] = tile_set->get_tile_size()[min_axis] / 3; + icon_size[(min_axis + 1) % 2] = (icon_size[min_axis] * missing_tile_texture->get_size()[(min_axis + 1) % 2] / missing_tile_texture->get_size()[min_axis]); + Rect2 rect = Rect2(xform.xform(tile_map->map_to_world(coords)) - (icon_size * xform.get_scale() / 2), icon_size * xform.get_scale()); p_overlay->draw_texture_rect(missing_tile_texture, rect); } } @@ -3503,6 +3527,26 @@ TileMapEditor::TileMapEditor() { tilemap_toolbar->add_child(tile_map_editor_plugins[i]->get_toolbar()); } + // Wide empty separation control. + Control *h_empty_space = memnew(Control); + h_empty_space->set_h_size_flags(SIZE_EXPAND_FILL); + tilemap_toolbar->add_child(h_empty_space); + + // Grid toggle. + toggle_grid_button = memnew(Button); + toggle_grid_button->set_flat(true); + toggle_grid_button->set_toggle_mode(true); + toggle_grid_button->set_tooltip(TTR("Toggle grid visibility.")); + toggle_grid_button->connect("toggled", callable_mp(this, &TileMapEditor::_on_grid_toggled)); + tilemap_toolbar->add_child(toggle_grid_button); + + // Advanced settings menu button. + advanced_menu_button = memnew(MenuButton); + advanced_menu_button->set_flat(true); + advanced_menu_button->get_popup()->add_item(TTR("Automatically Replace Tiles with Proxies")); + advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileMapEditor::_advanced_menu_button_id_pressed)); + tilemap_toolbar->add_child(advanced_menu_button); + missing_tileset_label = memnew(Label); missing_tileset_label->set_text(TTR("The edited TileMap node has no TileSet resource.")); missing_tileset_label->set_h_size_flags(SIZE_EXPAND_FILL); diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h index a6f4ec3021..236774a06b 100644 --- a/editor/plugins/tiles/tile_map_editor.h +++ b/editor/plugins/tiles/tile_map_editor.h @@ -82,9 +82,6 @@ private: void _on_random_tile_checkbox_toggled(bool p_pressed); void _on_scattering_spinbox_changed(double p_value); - Button *toggle_grid_button; - void _on_grid_toggled(bool p_pressed); - void _update_toolbar(); ///// Tilemap editing. ///// @@ -300,6 +297,7 @@ class TileMapEditor : public VBoxContainer { GDCLASS(TileMapEditor, VBoxContainer); private: + UndoRedo *undo_redo = EditorNode::get_undo_redo(); bool tileset_changed_needs_update = false; ObjectID tile_map_id; @@ -309,6 +307,12 @@ private: // Toolbar. HBoxContainer *tilemap_toolbar; + Button *toggle_grid_button; + void _on_grid_toggled(bool p_pressed); + + MenuButton *advanced_menu_button; + void _advanced_menu_button_id_pressed(int p_id); + // Bottom panel Label *missing_tileset_label; Tabs *tabs; diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp new file mode 100644 index 0000000000..e5e7efa531 --- /dev/null +++ b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp @@ -0,0 +1,476 @@ +/*************************************************************************/ +/* tile_proxies_manager_dialog.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "tile_proxies_manager_dialog.h" + +#include "editor/editor_scale.h" + +void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list) { + ItemList *item_list = Object::cast_to<ItemList>(p_item_list); + popup_menu->set_size(Vector2(1, 1)); + popup_menu->set_position(get_position() + item_list->get_global_mouse_position()); + popup_menu->popup(); +} + +void TileProxiesManagerDialog::_menu_id_pressed(int p_id) { + if (p_id == 0) { + // Delete. + _delete_selected_bindings(); + } +} + +void TileProxiesManagerDialog::_delete_selected_bindings() { + undo_redo->create_action("Remove Tile Proxies"); + + Vector<int> source_level_selected = source_level_list->get_selected_items(); + for (int i = 0; i < source_level_selected.size(); i++) { + int key = source_level_list->get_item_metadata(source_level_selected[i]); + int val = tile_set->get_source_level_tile_proxy(key); + undo_redo->add_do_method(*tile_set, "remove_source_level_tile_proxy", key); + undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", key, val); + } + + Vector<int> coords_level_selected = coords_level_list->get_selected_items(); + for (int i = 0; i < coords_level_selected.size(); i++) { + Array key = coords_level_list->get_item_metadata(coords_level_selected[i]); + Array val = tile_set->get_coords_level_tile_proxy(key[0], key[1]); + undo_redo->add_do_method(*tile_set, "remove_coords_level_tile_proxy", key[0], key[1]); + undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", key[0], key[1], val[0], val[1]); + } + + Vector<int> alternative_level_selected = alternative_level_list->get_selected_items(); + for (int i = 0; i < alternative_level_selected.size(); i++) { + Array key = alternative_level_list->get_item_metadata(alternative_level_selected[i]); + Array val = tile_set->get_coords_level_tile_proxy(key[0], key[1]); + undo_redo->add_do_method(*tile_set, "remove_alternative_level_tile_proxy", key[0], key[1], key[2]); + undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", key[0], key[1], key[2], val[0], val[1], val[2]); + } + undo_redo->add_do_method(this, "_update_lists"); + undo_redo->add_undo_method(this, "_update_lists"); + undo_redo->commit_action(); + + commited_actions_count += 1; +} + +void TileProxiesManagerDialog::_update_lists() { + source_level_list->clear(); + coords_level_list->clear(); + alternative_level_list->clear(); + + Array proxies = tile_set->get_source_level_tile_proxies(); + for (int i = 0; i < proxies.size(); i++) { + Array proxy = proxies[i]; + String text = vformat("%s", proxy[0]).rpad(5) + "-> " + vformat("%s", proxy[1]); + int id = source_level_list->add_item(text); + source_level_list->set_item_metadata(id, proxy[0]); + } + + proxies = tile_set->get_coords_level_tile_proxies(); + for (int i = 0; i < proxies.size(); i++) { + Array proxy = proxies[i]; + String text = vformat("%s, %s", proxy[0], proxy[1]).rpad(17) + "-> " + vformat("%s, %s", proxy[2], proxy[3]); + int id = coords_level_list->add_item(text); + coords_level_list->set_item_metadata(id, proxy.slice(0, 2)); + } + + proxies = tile_set->get_alternative_level_tile_proxies(); + for (int i = 0; i < proxies.size(); i++) { + Array proxy = proxies[i]; + String text = vformat("%s, %s, %s", proxy[0], proxy[1], proxy[2]).rpad(24) + "-> " + vformat("%s, %s, %s", proxy[3], proxy[4], proxy[5]); + int id = alternative_level_list->add_item(text); + alternative_level_list->set_item_metadata(id, proxy.slice(0, 3)); + } +} + +void TileProxiesManagerDialog::_update_enabled_property_editors() { + if (from.source_id == TileSet::INVALID_SOURCE) { + from.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + to.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS); + from.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; + to.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; + coords_from_property_editor->hide(); + coords_to_property_editor->hide(); + alternative_from_property_editor->hide(); + alternative_to_property_editor->hide(); + } else if (from.get_atlas_coords().x == -1 || from.get_atlas_coords().y == -1) { + from.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; + to.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; + coords_from_property_editor->show(); + coords_to_property_editor->show(); + alternative_from_property_editor->hide(); + alternative_to_property_editor->hide(); + } else { + coords_from_property_editor->show(); + coords_to_property_editor->show(); + alternative_from_property_editor->show(); + alternative_to_property_editor->show(); + } + + source_from_property_editor->update_property(); + source_to_property_editor->update_property(); + coords_from_property_editor->update_property(); + coords_to_property_editor->update_property(); + alternative_from_property_editor->update_property(); + alternative_to_property_editor->update_property(); +} + +void TileProxiesManagerDialog::_property_changed(const String &p_path, const Variant &p_value, const String &p_name, bool p_changing) { + _set(p_path, p_value); +} + +void TileProxiesManagerDialog::_add_button_pressed() { + if (from.source_id != TileSet::INVALID_SOURCE && to.source_id != TileSet::INVALID_SOURCE) { + Vector2i from_coords = from.get_atlas_coords(); + Vector2i to_coords = to.get_atlas_coords(); + if (from_coords.x >= 0 && from_coords.y >= 0 && to_coords.x >= 0 && to_coords.y >= 0) { + if (from.alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE && to.alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE) { + undo_redo->create_action("Create Alternative-level Tile Proxy"); + undo_redo->add_do_method(*tile_set, "set_alternative_level_tile_proxy", from.source_id, from.get_atlas_coords(), from.alternative_tile, to.source_id, to.get_atlas_coords(), to.alternative_tile); + if (tile_set->has_alternative_level_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile)) { + Array a = tile_set->get_alternative_level_tile_proxy(from.source_id, from.get_atlas_coords(), from.alternative_tile); + undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", to.source_id, to.get_atlas_coords(), to.alternative_tile, a[0], a[1], a[2]); + } else { + undo_redo->add_undo_method(*tile_set, "remove_alternative_level_tile_proxy", from.source_id, from.get_atlas_coords(), from.alternative_tile); + } + } else { + undo_redo->create_action("Create Coords-level Tile Proxy"); + undo_redo->add_do_method(*tile_set, "set_coords_level_tile_proxy", from.source_id, from.get_atlas_coords(), to.source_id, to.get_atlas_coords()); + if (tile_set->has_coords_level_tile_proxy(from.source_id, from.get_atlas_coords())) { + Array a = tile_set->get_coords_level_tile_proxy(from.source_id, from.get_atlas_coords()); + undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", to.source_id, to.get_atlas_coords(), a[0], a[1]); + } else { + undo_redo->add_undo_method(*tile_set, "remove_coords_level_tile_proxy", from.source_id, from.get_atlas_coords()); + } + } + } else { + undo_redo->create_action("Create source-level Tile Proxy"); + undo_redo->add_do_method(*tile_set, "set_source_level_tile_proxy", from.source_id, to.source_id); + if (tile_set->has_source_level_tile_proxy(from.source_id)) { + undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", to.source_id, tile_set->get_source_level_tile_proxy(from.source_id)); + } else { + undo_redo->add_undo_method(*tile_set, "remove_source_level_tile_proxy", from.source_id); + } + } + undo_redo->add_do_method(this, "_update_lists"); + undo_redo->add_undo_method(this, "_update_lists"); + undo_redo->commit_action(); + commited_actions_count++; + } +} + +void TileProxiesManagerDialog::_clear_invalid_button_pressed() { + undo_redo->create_action("Delete All Invalid Tile Proxies"); + + undo_redo->add_do_method(*tile_set, "cleanup_invalid_tile_proxies"); + + Array proxies = tile_set->get_source_level_tile_proxies(); + for (int i = 0; i < proxies.size(); i++) { + Array proxy = proxies[i]; + undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", proxy[0], proxy[1]); + } + + proxies = tile_set->get_coords_level_tile_proxies(); + for (int i = 0; i < proxies.size(); i++) { + Array proxy = proxies[i]; + undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3]); + } + + proxies = tile_set->get_alternative_level_tile_proxies(); + for (int i = 0; i < proxies.size(); i++) { + Array proxy = proxies[i]; + undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3], proxy[4], proxy[5]); + } + undo_redo->add_do_method(this, "_update_lists"); + undo_redo->add_undo_method(this, "_update_lists"); + undo_redo->commit_action(); +} + +void TileProxiesManagerDialog::_clear_all_button_pressed() { + undo_redo->create_action("Delete All Tile Proxies"); + + undo_redo->add_do_method(*tile_set, "clear_tile_proxies"); + + Array proxies = tile_set->get_source_level_tile_proxies(); + for (int i = 0; i < proxies.size(); i++) { + Array proxy = proxies[i]; + undo_redo->add_undo_method(*tile_set, "set_source_level_tile_proxy", proxy[0], proxy[1]); + } + + proxies = tile_set->get_coords_level_tile_proxies(); + for (int i = 0; i < proxies.size(); i++) { + Array proxy = proxies[i]; + undo_redo->add_undo_method(*tile_set, "set_coords_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3]); + } + + proxies = tile_set->get_alternative_level_tile_proxies(); + for (int i = 0; i < proxies.size(); i++) { + Array proxy = proxies[i]; + undo_redo->add_undo_method(*tile_set, "set_alternative_level_tile_proxy", proxy[0], proxy[1], proxy[2], proxy[3], proxy[4], proxy[5]); + } + undo_redo->add_do_method(this, "_update_lists"); + undo_redo->add_undo_method(this, "_update_lists"); + undo_redo->commit_action(); +} + +bool TileProxiesManagerDialog::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "from_source") { + from.source_id = MAX(int(p_value), -1); + } else if (p_name == "from_coords") { + from.set_atlas_coords(Vector2i(p_value).max(Vector2i(-1, -1))); + } else if (p_name == "from_alternative") { + from.alternative_tile = MAX(int(p_value), -1); + } else if (p_name == "to_source") { + to.source_id = MAX(int(p_value), 0); + } else if (p_name == "to_coords") { + to.set_atlas_coords(Vector2i(p_value).max(Vector2i(0, 0))); + } else if (p_name == "to_alternative") { + to.alternative_tile = MAX(int(p_value), 0); + } else { + return false; + } + _update_enabled_property_editors(); + return true; +} + +bool TileProxiesManagerDialog::_get(const StringName &p_name, Variant &r_ret) const { + if (p_name == "from_source") { + r_ret = from.source_id; + } else if (p_name == "from_coords") { + r_ret = from.get_atlas_coords(); + } else if (p_name == "from_alternative") { + r_ret = from.alternative_tile; + } else if (p_name == "to_source") { + r_ret = to.source_id; + } else if (p_name == "to_coords") { + r_ret = to.get_atlas_coords(); + } else if (p_name == "to_alternative") { + r_ret = to.alternative_tile; + } else { + return false; + } + return true; +} + +void TileProxiesManagerDialog::_unhandled_key_input(Ref<InputEvent> p_event) { + ERR_FAIL_COND(p_event.is_null()); + + if (p_event->is_pressed() && !p_event->is_echo() && (Object::cast_to<InputEventKey>(p_event.ptr()) || Object::cast_to<InputEventJoypadButton>(p_event.ptr()) || Object::cast_to<InputEventAction>(*p_event))) { + if (!is_inside_tree() || !is_visible()) { + return; + } + + if (popup_menu->activate_item_by_event(p_event, false)) { + set_input_as_handled(); + } + } +} + +void TileProxiesManagerDialog::cancel_pressed() { + for (int i = 0; i < commited_actions_count; i++) { + undo_redo->undo(); + } + commited_actions_count = 0; +} + +void TileProxiesManagerDialog::_bind_methods() { + ClassDB::bind_method(D_METHOD("_update_lists"), &TileProxiesManagerDialog::_update_lists); + ClassDB::bind_method(D_METHOD("_unhandled_key_input"), &TileProxiesManagerDialog::_unhandled_key_input); +} + +void TileProxiesManagerDialog::update_tile_set(Ref<TileSet> p_tile_set) { + ERR_FAIL_COND(!p_tile_set.is_valid()); + tile_set = p_tile_set; + commited_actions_count = 0; + _update_lists(); +} + +TileProxiesManagerDialog::TileProxiesManagerDialog() { + // Tile proxy management window. + set_title(TTR("Tile Proxies Management")); + set_process_unhandled_key_input(true); + + to.source_id = 0; + to.set_atlas_coords(Vector2i()); + to.alternative_tile = 0; + + VBoxContainer *vbox_container = memnew(VBoxContainer); + vbox_container->set_h_size_flags(Control::SIZE_EXPAND_FILL); + vbox_container->set_v_size_flags(Control::SIZE_EXPAND_FILL); + add_child(vbox_container); + + Label *source_level_label = memnew(Label); + source_level_label->set_text(TTR("Source-level proxies")); + vbox_container->add_child(source_level_label); + + source_level_list = memnew(ItemList); + source_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + source_level_list->set_select_mode(ItemList::SELECT_MULTI); + source_level_list->set_allow_rmb_select(true); + source_level_list->connect("item_rmb_selected", callable_mp(this, &TileProxiesManagerDialog::_right_clicked), varray(source_level_list)); + vbox_container->add_child(source_level_list); + + Label *coords_level_label = memnew(Label); + coords_level_label->set_text(TTR("Coords-level proxies")); + vbox_container->add_child(coords_level_label); + + coords_level_list = memnew(ItemList); + coords_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + coords_level_list->set_select_mode(ItemList::SELECT_MULTI); + coords_level_list->set_allow_rmb_select(true); + coords_level_list->connect("item_rmb_selected", callable_mp(this, &TileProxiesManagerDialog::_right_clicked), varray(coords_level_list)); + vbox_container->add_child(coords_level_list); + + Label *alternative_level_label = memnew(Label); + alternative_level_label->set_text(TTR("Alternative-level proxies")); + vbox_container->add_child(alternative_level_label); + + alternative_level_list = memnew(ItemList); + alternative_level_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + alternative_level_list->set_select_mode(ItemList::SELECT_MULTI); + alternative_level_list->set_allow_rmb_select(true); + alternative_level_list->connect("item_rmb_selected", callable_mp(this, &TileProxiesManagerDialog::_right_clicked), varray(alternative_level_list)); + vbox_container->add_child(alternative_level_list); + + popup_menu = memnew(PopupMenu); + popup_menu->add_shortcut(ED_GET_SHORTCUT("ui_text_delete")); + popup_menu->connect("id_pressed", callable_mp(this, &TileProxiesManagerDialog::_menu_id_pressed)); + add_child(popup_menu); + + // Add proxy panel. + HSeparator *h_separator = memnew(HSeparator); + vbox_container->add_child(h_separator); + + Label *add_label = memnew(Label); + add_label->set_text(TTR("Add a new tile proxy:")); + vbox_container->add_child(add_label); + + HBoxContainer *hboxcontainer = memnew(HBoxContainer); + vbox_container->add_child(hboxcontainer); + + // From + VBoxContainer *vboxcontainer_from = memnew(VBoxContainer); + vboxcontainer_from->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hboxcontainer->add_child(vboxcontainer_from); + + source_from_property_editor = memnew(EditorPropertyInteger); + source_from_property_editor->set_label(TTR("From Source")); + source_from_property_editor->set_object_and_property(this, "from_source"); + source_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed)); + source_from_property_editor->set_selectable(false); + source_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); + source_from_property_editor->setup(-1, 99999, 1, true, false); + vboxcontainer_from->add_child(source_from_property_editor); + + coords_from_property_editor = memnew(EditorPropertyVector2i); + coords_from_property_editor->set_label(TTR("From Coords")); + coords_from_property_editor->set_object_and_property(this, "from_coords"); + coords_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed)); + coords_from_property_editor->set_selectable(false); + coords_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); + coords_from_property_editor->setup(-1, 99999, true); + coords_from_property_editor->hide(); + vboxcontainer_from->add_child(coords_from_property_editor); + + alternative_from_property_editor = memnew(EditorPropertyInteger); + alternative_from_property_editor->set_label(TTR("From Alternative")); + alternative_from_property_editor->set_object_and_property(this, "from_alternative"); + alternative_from_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed)); + alternative_from_property_editor->set_selectable(false); + alternative_from_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); + alternative_from_property_editor->setup(-1, 99999, 1, true, false); + alternative_from_property_editor->hide(); + vboxcontainer_from->add_child(alternative_from_property_editor); + + // To + VBoxContainer *vboxcontainer_to = memnew(VBoxContainer); + vboxcontainer_to->set_h_size_flags(Control::SIZE_EXPAND_FILL); + hboxcontainer->add_child(vboxcontainer_to); + + source_to_property_editor = memnew(EditorPropertyInteger); + source_to_property_editor->set_label(TTR("To Source")); + source_to_property_editor->set_object_and_property(this, "to_source"); + source_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed)); + source_to_property_editor->set_selectable(false); + source_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); + source_to_property_editor->setup(-1, 99999, 1, true, false); + vboxcontainer_to->add_child(source_to_property_editor); + + coords_to_property_editor = memnew(EditorPropertyVector2i); + coords_to_property_editor->set_label(TTR("To Coords")); + coords_to_property_editor->set_object_and_property(this, "to_coords"); + coords_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed)); + coords_to_property_editor->set_selectable(false); + coords_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); + coords_to_property_editor->setup(-1, 99999, true); + coords_to_property_editor->hide(); + vboxcontainer_to->add_child(coords_to_property_editor); + + alternative_to_property_editor = memnew(EditorPropertyInteger); + alternative_to_property_editor->set_label(TTR("To Alternative")); + alternative_to_property_editor->set_object_and_property(this, "to_alternative"); + alternative_to_property_editor->connect("property_changed", callable_mp(this, &TileProxiesManagerDialog::_property_changed)); + alternative_to_property_editor->set_selectable(false); + alternative_to_property_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL); + alternative_to_property_editor->setup(-1, 99999, 1, true, false); + alternative_to_property_editor->hide(); + vboxcontainer_to->add_child(alternative_to_property_editor); + + Button *add_button = memnew(Button); + add_button->set_text(TTR("Add")); + add_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER); + add_button->connect("pressed", callable_mp(this, &TileProxiesManagerDialog::_add_button_pressed)); + vbox_container->add_child(add_button); + + h_separator = memnew(HSeparator); + vbox_container->add_child(h_separator); + + // Generic actions. + Label *generic_actions_label = memnew(Label); + generic_actions_label->set_text(TTR("Global actions:")); + vbox_container->add_child(generic_actions_label); + + hboxcontainer = memnew(HBoxContainer); + vbox_container->add_child(hboxcontainer); + + Button *clear_invalid_button = memnew(Button); + clear_invalid_button->set_text(TTR("Clear Invalid")); + clear_invalid_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER); + clear_invalid_button->connect("pressed", callable_mp(this, &TileProxiesManagerDialog::_clear_invalid_button_pressed)); + hboxcontainer->add_child(clear_invalid_button); + + Button *clear_all_button = memnew(Button); + clear_all_button->set_text(TTR("Clear All")); + clear_all_button->set_h_size_flags(Control::SIZE_SHRINK_CENTER); + clear_all_button->connect("pressed", callable_mp(this, &TileProxiesManagerDialog::_clear_all_button_pressed)); + hboxcontainer->add_child(clear_all_button); + + h_separator = memnew(HSeparator); + vbox_container->add_child(h_separator); +} diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.h b/editor/plugins/tiles/tile_proxies_manager_dialog.h new file mode 100644 index 0000000000..f6898e960b --- /dev/null +++ b/editor/plugins/tiles/tile_proxies_manager_dialog.h @@ -0,0 +1,90 @@ +/*************************************************************************/ +/* tile_proxies_manager_dialog.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 TILE_PROXIES_MANAGER_DIALOG_H +#define TILE_PROXIES_MANAGER_DIALOG_H + +#include "editor/editor_node.h" +#include "editor/editor_properties.h" + +#include "scene/gui/dialogs.h" +#include "scene/gui/item_list.h" +#include "scene/resources/tile_set.h" + +class TileProxiesManagerDialog : public ConfirmationDialog { + GDCLASS(TileProxiesManagerDialog, ConfirmationDialog); + +private: + int commited_actions_count = 0; + Ref<TileSet> tile_set; + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + TileMapCell from; + TileMapCell to; + + // GUI + ItemList *source_level_list; + ItemList *coords_level_list; + ItemList *alternative_level_list; + + EditorPropertyInteger *source_from_property_editor; + EditorPropertyVector2i *coords_from_property_editor; + EditorPropertyInteger *alternative_from_property_editor; + EditorPropertyInteger *source_to_property_editor; + EditorPropertyVector2i *coords_to_property_editor; + EditorPropertyInteger *alternative_to_property_editor; + + PopupMenu *popup_menu; + void _right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list); + void _menu_id_pressed(int p_id); + void _delete_selected_bindings(); + void _update_lists(); + void _update_enabled_property_editors(); + void _property_changed(const String &p_path, const Variant &p_value, const String &p_name, bool p_changing); + void _add_button_pressed(); + + void _clear_invalid_button_pressed(); + void _clear_all_button_pressed(); + +protected: + bool _set(const StringName &p_name, const Variant &p_value); + bool _get(const StringName &p_name, Variant &r_ret) const; + void _unhandled_key_input(Ref<InputEvent> p_event); + virtual void cancel_pressed() override; + static void _bind_methods(); + +public: + void update_tile_set(Ref<TileSet> p_tile_set); + + TileProxiesManagerDialog(); +}; + +#endif // TILE_PROXIES_MANAGER_DIALOG_H diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 6b617cf4d8..de910dfd32 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -745,7 +745,7 @@ void TileSetAtlasSourceEditor::_update_atlas_view() { button->add_theme_style_override("hover", memnew(StyleBoxEmpty)); button->add_theme_style_override("focus", memnew(StyleBoxEmpty)); button->add_theme_style_override("pressed", memnew(StyleBoxEmpty)); - button->connect("pressed", callable_mp(tile_set_atlas_source, &TileSetAtlasSource::create_alternative_tile), varray(tile_id, -1)); + button->connect("pressed", callable_mp(tile_set_atlas_source, &TileSetAtlasSource::create_alternative_tile), varray(tile_id, TileSetSource::INVALID_TILE_ALTERNATIVE)); button->set_rect(Rect2(Vector2(pos.x, pos.y + (y_increment - texture_region_base_size.y) / 2.0), Vector2(texture_region_base_size_min, texture_region_base_size_min))); button->set_expand_icon(true); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index dbb0756a16..501416c340 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -65,7 +65,7 @@ private: private: Ref<TileSet> tile_set; TileSetAtlasSource *tile_set_atlas_source = nullptr; - int source_id = -1; + int source_id = TileSet::INVALID_SOURCE; protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -108,7 +108,7 @@ private: Ref<TileSet> tile_set; TileSetAtlasSource *tile_set_atlas_source = nullptr; - int tile_set_atlas_source_id = -1; + int tile_set_atlas_source_id = TileSet::INVALID_SOURCE; UndoRedo *undo_redo = EditorNode::get_undo_redo(); diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 76ce6f0065..ba98a7d6b3 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -50,7 +50,7 @@ void TileSetEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, C if (p_from == sources_list) { // Handle dropping a texture in the list of atlas resources. - int source_id = -1; + int source_id = TileSet::INVALID_SOURCE; int added = 0; Dictionary d = p_data; Vector<String> files = d["files"]; @@ -77,7 +77,7 @@ void TileSetEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, C } // Update the selected source (thus triggering an update). - _update_atlas_sources_list(source_id); + _update_sources_list(source_id); } } @@ -114,11 +114,11 @@ bool TileSetEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_dat return false; } -void TileSetEditor::_update_atlas_sources_list(int force_selected_id) { +void TileSetEditor::_update_sources_list(int force_selected_id) { ERR_FAIL_COND(!tile_set.is_valid()); // Get the previously selected id. - int old_selected = -1; + int old_selected = TileSet::INVALID_SOURCE; if (sources_list->get_current() >= 0) { int source_id = sources_list->get_item_metadata(sources_list->get_current()); if (tile_set->has_source(source_id)) { @@ -126,7 +126,7 @@ void TileSetEditor::_update_atlas_sources_list(int force_selected_id) { } } - int to_select = -1; + int to_select = TileSet::INVALID_SOURCE; if (force_selected_id >= 0) { to_select = force_selected_id; } else if (old_selected >= 0) { @@ -200,7 +200,7 @@ void TileSetEditor::_update_atlas_sources_list(int force_selected_id) { _source_selected(sources_list->get_current()); // Synchronize the lists. - TilesEditor::get_singleton()->set_atlas_sources_lists_current(sources_list->get_current()); + TilesEditor::get_singleton()->set_sources_lists_current(sources_list->get_current()); } void TileSetEditor::_source_selected(int p_source_index) { @@ -235,6 +235,23 @@ void TileSetEditor::_source_selected(int p_source_index) { } } +void TileSetEditor::_source_delete_pressed() { + ERR_FAIL_COND(!tile_set.is_valid()); + + // Update the selected source. + int to_delete = sources_list->get_item_metadata(sources_list->get_current()); + + Ref<TileSetSource> source = tile_set->get_source(to_delete); + + // Remove the source. + undo_redo->create_action(TTR("Remove source")); + undo_redo->add_do_method(*tile_set, "remove_source", to_delete); + undo_redo->add_undo_method(*tile_set, "add_source", source, to_delete); + undo_redo->commit_action(); + + _update_sources_list(); +} + void TileSetEditor::_source_add_id_pressed(int p_id_pressed) { ERR_FAIL_COND(!tile_set.is_valid()); @@ -251,7 +268,7 @@ void TileSetEditor::_source_add_id_pressed(int p_id_pressed) { undo_redo->add_undo_method(*tile_set, "remove_source", source_id); undo_redo->commit_action(); - _update_atlas_sources_list(source_id); + _update_sources_list(source_id); } break; case 1: { int source_id = tile_set->get_next_source_id(); @@ -264,28 +281,26 @@ void TileSetEditor::_source_add_id_pressed(int p_id_pressed) { undo_redo->add_undo_method(*tile_set, "remove_source", source_id); undo_redo->commit_action(); - _update_atlas_sources_list(source_id); + _update_sources_list(source_id); } break; default: ERR_FAIL(); } } -void TileSetEditor::_source_delete_pressed() { +void TileSetEditor::_sources_advanced_menu_id_pressed(int p_id_pressed) { ERR_FAIL_COND(!tile_set.is_valid()); - // Update the selected source. - int to_delete = sources_list->get_item_metadata(sources_list->get_current()); - - Ref<TileSetSource> source = tile_set->get_source(to_delete); - - // Remove the source. - undo_redo->create_action(TTR("Remove source")); - undo_redo->add_do_method(*tile_set, "remove_source", to_delete); - undo_redo->add_undo_method(*tile_set, "add_source", source, to_delete); - undo_redo->commit_action(); - - _update_atlas_sources_list(); + switch (p_id_pressed) { + case 0: { + atlas_merging_dialog->update_tile_set(tile_set); + atlas_merging_dialog->popup_centered_ratio(0.5); + } break; + case 1: { + tile_proxies_manager_dialog->update_tile_set(tile_set); + tile_proxies_manager_dialog->popup_centered_ratio(0.5); + } break; + } } void TileSetEditor::_notification(int p_what) { @@ -294,6 +309,7 @@ void TileSetEditor::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: sources_delete_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); sources_add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + sources_advanced_menu_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); missing_texture_texture = get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons")); break; case NOTIFICATION_INTERNAL_PROCESS: @@ -301,7 +317,7 @@ void TileSetEditor::_notification(int p_what) { if (tile_set.is_valid()) { tile_set->set_edited(true); } - _update_atlas_sources_list(); + _update_sources_list(); tile_set_changed_needs_update = false; } break; @@ -414,7 +430,7 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) { // Add the listener again. if (tile_set.is_valid()) { tile_set->connect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed)); - _update_atlas_sources_list(); + _update_sources_list(); } tile_set_atlas_source_editor->hide(); @@ -447,8 +463,8 @@ TileSetEditor::TileSetEditor() { sources_list->set_h_size_flags(SIZE_EXPAND_FILL); sources_list->set_v_size_flags(SIZE_EXPAND_FILL); sources_list->connect("item_selected", callable_mp(this, &TileSetEditor::_source_selected)); - sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_sources_lists_current)); - sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_atlas_sources_list), varray(sources_list)); + sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_sources_lists_current)); + sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_sources_list), varray(sources_list)); sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST); sources_list->set_drag_forwarding(this); split_container_left_side->add_child(sources_list); @@ -470,6 +486,19 @@ TileSetEditor::TileSetEditor() { sources_add_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetEditor::_source_add_id_pressed)); sources_bottom_actions->add_child(sources_add_button); + sources_advanced_menu_button = memnew(MenuButton); + sources_advanced_menu_button->set_flat(true); + sources_advanced_menu_button->get_popup()->add_item(TTR("Open Atlas Merging Tool")); + sources_advanced_menu_button->get_popup()->add_item(TTR("Manage Tile Proxies")); + sources_advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetEditor::_sources_advanced_menu_id_pressed)); + sources_bottom_actions->add_child(sources_advanced_menu_button); + + atlas_merging_dialog = memnew(AtlasMergingDialog); + add_child(atlas_merging_dialog); + + tile_proxies_manager_dialog = memnew(TileProxiesManagerDialog); + add_child(tile_proxies_manager_dialog); + // Right side container. VBoxContainer *split_container_right_side = memnew(VBoxContainer); split_container_right_side->set_h_size_flags(SIZE_EXPAND_FILL); @@ -489,7 +518,7 @@ TileSetEditor::TileSetEditor() { tile_set_atlas_source_editor = memnew(TileSetAtlasSourceEditor); tile_set_atlas_source_editor->set_h_size_flags(SIZE_EXPAND_FILL); tile_set_atlas_source_editor->set_v_size_flags(SIZE_EXPAND_FILL); - tile_set_atlas_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_atlas_sources_list)); + tile_set_atlas_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_sources_list)); split_container_right_side->add_child(tile_set_atlas_source_editor); tile_set_atlas_source_editor->hide(); @@ -497,7 +526,7 @@ TileSetEditor::TileSetEditor() { tile_set_scenes_collection_source_editor = memnew(TileSetScenesCollectionSourceEditor); tile_set_scenes_collection_source_editor->set_h_size_flags(SIZE_EXPAND_FILL); tile_set_scenes_collection_source_editor->set_v_size_flags(SIZE_EXPAND_FILL); - tile_set_scenes_collection_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_atlas_sources_list)); + tile_set_scenes_collection_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_sources_list)); split_container_right_side->add_child(tile_set_scenes_collection_source_editor); tile_set_scenes_collection_source_editor->hide(); diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h index 9e50aca62f..970e3fabb6 100644 --- a/editor/plugins/tiles/tile_set_editor.h +++ b/editor/plugins/tiles/tile_set_editor.h @@ -31,8 +31,10 @@ #ifndef TILE_SET_EDITOR_H #define TILE_SET_EDITOR_H +#include "atlas_merging_dialog.h" #include "scene/gui/box_container.h" #include "scene/resources/tile_set.h" +#include "tile_proxies_manager_dialog.h" #include "tile_set_atlas_source_editor.h" #include "tile_set_scenes_collection_source_editor.h" @@ -51,16 +53,21 @@ private: UndoRedo *undo_redo = EditorNode::get_undo_redo(); - void _update_atlas_sources_list(int force_selected_id = -1); + void _update_sources_list(int force_selected_id = -1); - // -- Sources management -- + // Sources management. Button *sources_delete_button; MenuButton *sources_add_button; + MenuButton *sources_advanced_menu_button; ItemList *sources_list; Ref<Texture2D> missing_texture_texture; void _source_selected(int p_source_index); - void _source_add_id_pressed(int p_id_pressed); void _source_delete_pressed(); + void _source_add_id_pressed(int p_id_pressed); + void _sources_advanced_menu_id_pressed(int p_id_pressed); + + AtlasMergingDialog *atlas_merging_dialog; + TileProxiesManagerDialog *tile_proxies_manager_dialog; void _tile_set_changed(); diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 339efc7b99..79b869b511 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -118,11 +118,11 @@ void TilesEditor::_update_editors() { CanvasItemEditor::get_singleton()->update_viewport(); } -void TilesEditor::set_atlas_sources_lists_current(int p_current) { +void TilesEditor::set_sources_lists_current(int p_current) { atlas_sources_lists_current = p_current; } -void TilesEditor::synchronize_atlas_sources_list(Object *p_current) { +void TilesEditor::synchronize_sources_list(Object *p_current) { ItemList *item_list = Object::cast_to<ItemList>(p_current); ERR_FAIL_COND(!item_list); diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h index 6cc6f51598..f976d68938 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.h +++ b/editor/plugins/tiles/tiles_editor_plugin.h @@ -76,8 +76,8 @@ public: void forward_canvas_draw_over_viewport(Control *p_overlay) { tilemap_editor->forward_canvas_draw_over_viewport(p_overlay); } // To synchronize the atlas sources lists. - void set_atlas_sources_lists_current(int p_current); - void synchronize_atlas_sources_list(Object *p_current); + void set_sources_lists_current(int p_current); + void synchronize_sources_list(Object *p_current); void set_atlas_view_transform(float p_zoom, Vector2 p_scroll); void synchronize_atlas_view(Object *p_current); diff --git a/modules/csg/csg.cpp b/modules/csg/csg.cpp index 5a37486568..57206ae18b 100644 --- a/modules/csg/csg.cpp +++ b/modules/csg/csg.cpp @@ -42,7 +42,7 @@ inline static bool is_snapable(const Vector3 &p_point1, const Vector3 &p_point2, inline static Vector2 interpolate_segment_uv(const Vector2 p_segement_points[2], const Vector2 p_uvs[2], const Vector2 &p_interpolation_point) { float segment_length = (p_segement_points[1] - p_segement_points[0]).length(); - if (segment_length < CMP_EPSILON) { + if (p_segement_points[0].is_equal_approx(p_segement_points[1])) { return p_uvs[0]; } @@ -53,13 +53,13 @@ inline static Vector2 interpolate_segment_uv(const Vector2 p_segement_points[2], } inline static Vector2 interpolate_triangle_uv(const Vector2 p_vertices[3], const Vector2 p_uvs[3], const Vector2 &p_interpolation_point) { - if (p_interpolation_point.distance_squared_to(p_vertices[0]) < CMP_EPSILON2) { + if (p_interpolation_point.is_equal_approx(p_vertices[0])) { return p_uvs[0]; } - if (p_interpolation_point.distance_squared_to(p_vertices[1]) < CMP_EPSILON2) { + if (p_interpolation_point.is_equal_approx(p_vertices[1])) { return p_uvs[1]; } - if (p_interpolation_point.distance_squared_to(p_vertices[2]) < CMP_EPSILON2) { + if (p_interpolation_point.is_equal_approx(p_vertices[2])) { return p_uvs[2]; } @@ -589,7 +589,7 @@ bool CSGBrushOperation::MeshMerge::_bvh_inside(FaceBVH *facebvhptr, int p_max_de Vector3 intersection_point; // Check if faces are co-planar. - if ((current_normal - face_normal).length_squared() < CMP_EPSILON2 && + if (current_normal.is_equal_approx(face_normal) && is_point_in_triangle(face_center, current_points)) { // Only add an intersection if not a B face. if (!face.from_b) { diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp index 53424f2cfd..729dc2f8fc 100644 --- a/modules/csg/csg_shape.cpp +++ b/modules/csg/csg_shape.cpp @@ -778,7 +778,7 @@ CSGBrush *CSGMesh3D::_build_brush() { } } - bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON; + bool flat = normal[0].is_equal_approx(normal[1]) && normal[0].is_equal_approx(normal[2]); vw[as + j + 0] = vertex[0]; vw[as + j + 1] = vertex[1]; @@ -820,7 +820,7 @@ CSGBrush *CSGMesh3D::_build_brush() { } } - bool flat = normal[0].distance_to(normal[1]) < CMP_EPSILON && normal[0].distance_to(normal[2]) < CMP_EPSILON; + bool flat = normal[0].is_equal_approx(normal[1]) && normal[0].is_equal_approx(normal[2]); vw[as + j + 0] = vertex[0]; vw[as + j + 1] = vertex[1]; diff --git a/modules/fbx/data/fbx_mesh_data.cpp b/modules/fbx/data/fbx_mesh_data.cpp index 0d33b8e7c6..8b4b1e08b3 100644 --- a/modules/fbx/data/fbx_mesh_data.cpp +++ b/modules/fbx/data/fbx_mesh_data.cpp @@ -525,7 +525,7 @@ void FBXMeshData::reorganize_vertices( } const Vector3 vert_poly_normal = *nrml_arr->getptr(*pid); - if ((this_vert_poly_normal - vert_poly_normal).length_squared() > CMP_EPSILON) { + if (!vert_poly_normal.is_equal_approx(this_vert_poly_normal)) { // Yes this polygon need duplication. need_duplication = true; break; @@ -586,7 +586,7 @@ void FBXMeshData::reorganize_vertices( continue; } const Vector2 vert_poly_uv = *uvs->getptr(*pid); - if (((*this_vert_poly_uv) - vert_poly_uv).length_squared() > CMP_EPSILON) { + if (!vert_poly_uv.is_equal_approx(*this_vert_poly_uv)) { // Yes this polygon need duplication. need_duplication = true; break; diff --git a/modules/gdnative/include/net/godot_webrtc.h b/modules/gdnative/include/net/godot_webrtc.h index 25aa72dae1..62b7e3c2c5 100644 --- a/modules/gdnative/include/net/godot_webrtc.h +++ b/modules/gdnative/include/net/godot_webrtc.h @@ -101,6 +101,7 @@ typedef struct { int (*get_max_retransmits)(const void *); const char *(*get_protocol)(const void *); bool (*is_negotiated)(const void *); + int (*get_buffered_amount)(const void *); godot_error (*poll)(void *); void (*close)(void *); diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index d0b7722847..f7113674ec 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1637,8 +1637,8 @@ void GDScriptLanguage::init() { _add_global(StaticCString::create("PI"), Math_PI); _add_global(StaticCString::create("TAU"), Math_TAU); - _add_global(StaticCString::create("INF"), Math_INF); - _add_global(StaticCString::create("NAN"), Math_NAN); + _add_global(StaticCString::create("INF"), INFINITY); + _add_global(StaticCString::create("NAN"), NAN); //populate native classes diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index c1a7bedbc8..79023df284 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -457,12 +457,12 @@ void GDScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_const Pair<String, Variant> infinity; infinity.first = "INF"; - infinity.second = Math_INF; + infinity.second = INFINITY; p_constants->push_back(infinity); Pair<String, Variant> nan; nan.first = "NAN"; - nan.second = Math_NAN; + nan.second = NAN; p_constants->push_back(nan); } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index df36afc0f0..e103d86b15 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -2123,10 +2123,10 @@ GDScriptParser::ExpressionNode *GDScriptParser::parse_builtin_constant(Expressio constant->value = Math_TAU; break; case GDScriptTokenizer::Token::CONST_INF: - constant->value = Math_INF; + constant->value = INFINITY; break; case GDScriptTokenizer::Token::CONST_NAN: - constant->value = Math_NAN; + constant->value = NAN; break; default: return nullptr; // Unreachable. diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 71a9b3667f..09bae884b9 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -2796,7 +2796,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) { mat = mat3d; } - import_mesh->add_surface(primitive, array, morphs, Dictionary(), mat); + import_mesh->add_surface(primitive, array, morphs, Dictionary(), mat, mat->get_name()); } Vector<float> blend_weights; diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp index 41306f0687..3150ca0bc8 100644 --- a/modules/navigation/nav_map.cpp +++ b/modules/navigation/nav_map.cpp @@ -734,7 +734,7 @@ void NavMap::dispatch_callbacks() { void NavMap::clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys, Vector<Vector3> &path, const gd::NavigationPoly *from_poly, const Vector3 &p_to_point, const gd::NavigationPoly *p_to_poly) const { Vector3 from = path[path.size() - 1]; - if (from.distance_to(p_to_point) < CMP_EPSILON) { + if (from.is_equal_approx(p_to_point)) { return; } Plane cut_plane; @@ -752,10 +752,10 @@ void NavMap::clip_path(const std::vector<gd::NavigationPoly> &p_navigation_polys ERR_FAIL_COND(from_poly->back_navigation_poly_id == -1); from_poly = &p_navigation_polys[from_poly->back_navigation_poly_id]; - if (pathway_start.distance_to(pathway_end) > CMP_EPSILON) { + if (!pathway_start.is_equal_approx(pathway_end)) { Vector3 inters; if (cut_plane.intersects_segment(pathway_start, pathway_end, &inters)) { - if (inters.distance_to(p_to_point) > CMP_EPSILON && inters.distance_to(path[path.size() - 1]) > CMP_EPSILON) { + if (!inters.is_equal_approx(p_to_point) && !inters.is_equal_approx(path[path.size() - 1])) { path.push_back(inters); } } diff --git a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml index 1c23b58507..ba4eba26fd 100644 --- a/modules/visual_script/doc_classes/VisualScriptCustomNode.xml +++ b/modules/visual_script/doc_classes/VisualScriptCustomNode.xml @@ -30,6 +30,24 @@ Return the count of input value ports. </description> </method> + <method name="_get_input_value_port_hint" qualifiers="virtual"> + <return type="int"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + Return the specified input port's hint. See the [enum @GlobalScope.PropertyHint] hints. + </description> + </method> + <method name="_get_input_value_port_hint_string" qualifiers="virtual"> + <return type="String"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + Return the specified input port's hint string. + </description> + </method> <method name="_get_input_value_port_name" qualifiers="virtual"> <return type="String"> </return> @@ -71,13 +89,31 @@ Return the amount of output value ports. </description> </method> + <method name="_get_output_value_port_hint" qualifiers="virtual"> + <return type="int"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + Return the specified output port's hint. See the [enum @GlobalScope.PropertyHint] hints. + </description> + </method> + <method name="_get_output_value_port_hint_string" qualifiers="virtual"> + <return type="String"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + Return the specified output port's hint string. + </description> + </method> <method name="_get_output_value_port_name" qualifiers="virtual"> <return type="String"> </return> <argument index="0" name="idx" type="int"> </argument> <description> - Return the specified output's name. + Return the specified output port's name. </description> </method> <method name="_get_output_value_port_type" qualifiers="virtual"> @@ -86,7 +122,7 @@ <argument index="0" name="idx" type="int"> </argument> <description> - Return the specified output's type. See the [enum Variant.Type] values. + Return the specified output port's type. See the [enum Variant.Type] values. </description> </method> <method name="_get_text" qualifiers="virtual"> diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 2e0eb4757a..ed0583a133 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -2251,41 +2251,35 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da undo_redo->create_action(TTR("Add Node(s) From Tree")); int base_id = script->get_available_id(); - if (nodes.size() > 1) { - use_node = true; - } - - for (int i = 0; i < nodes.size(); i++) { - NodePath np = nodes[i]; - Node *node = get_node(np); - if (!node) { - continue; - } + if (use_node || nodes.size() > 1) { + for (int i = 0; i < nodes.size(); i++) { + NodePath np = nodes[i]; + Node *node = get_node(np); + if (!node) { + continue; + } - Ref<VisualScriptNode> n; + Ref<VisualScriptNode> n; - if (use_node) { Ref<VisualScriptSceneNode> scene_node; scene_node.instantiate(); scene_node->set_node_path(sn->get_path_to(node)); n = scene_node; - } else { - // ! Doesn't work properly. - Ref<VisualScriptFunctionCall> call; - call.instantiate(); - call->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH); - call->set_base_path(sn->get_path_to(node)); - call->set_base_type(node->get_class()); - n = call; - method_select->select_from_instance(node, "", true, node->get_class()); - selecting_method_id = base_id; - } - undo_redo->add_do_method(script.ptr(), "add_node", base_id, n, pos); - undo_redo->add_undo_method(script.ptr(), "remove_node", base_id); + undo_redo->add_do_method(script.ptr(), "add_node", base_id, n, pos); + undo_redo->add_undo_method(script.ptr(), "remove_node", base_id); + + base_id++; + pos += Vector2(25, 25); + } - base_id++; - pos += Vector2(25, 25); + } else { + NodePath np = nodes[0]; + Node *node = get_node(np); + drop_position = pos; + drop_node = node; + drop_path = sn->get_path_to(node); + new_connect_node_select->select_from_instance(node, "", false, node->get_class()); } undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -2404,14 +2398,6 @@ void VisualScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da } } -void VisualScriptEditor::_selected_method(const String &p_method, const String &p_type, const bool p_connecting) { - Ref<VisualScriptFunctionCall> vsfc = script->get_node(selecting_method_id); - if (!vsfc.is_valid()) { - return; - } - vsfc->set_function(p_method); -} - void VisualScriptEditor::_draw_color_over_button(Object *obj, Color p_color) { Button *button = Object::cast_to<Button>(obj); if (!button) { @@ -3153,6 +3139,11 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri Set<int> vn; + if (drop_position != Vector2()) { + pos = drop_position; + } + drop_position = Vector2(); + bool port_node_exists = true; // if (func == StringName()) { @@ -3206,18 +3197,62 @@ void VisualScriptEditor::_selected_connect_node(const String &p_text, const Stri if (p_category == String("method")) { Ref<VisualScriptFunctionCall> n; n.instantiate(); + if (!drop_path.is_empty()) { + if (drop_path == ".") { + n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_SELF); + } else { + n->set_call_mode(VisualScriptFunctionCall::CALL_MODE_NODE_PATH); + n->set_base_path(drop_path); + } + } + if (drop_node) { + n->set_base_type(drop_node->get_class()); + if (drop_node->get_script_instance()) { + n->set_base_script(drop_node->get_script_instance()->get_script()->get_path()); + } + } vnode = n; } else if (p_category == String("set")) { Ref<VisualScriptPropertySet> n; n.instantiate(); + if (!drop_path.is_empty()) { + if (drop_path == ".") { + n->set_call_mode(VisualScriptPropertySet::CALL_MODE_SELF); + } else { + n->set_call_mode(VisualScriptPropertySet::CALL_MODE_NODE_PATH); + n->set_base_path(drop_path); + } + } + if (drop_node) { + n->set_base_type(drop_node->get_class()); + if (drop_node->get_script_instance()) { + n->set_base_script(drop_node->get_script_instance()->get_script()->get_path()); + } + } vnode = n; script_prop_set = n; } else if (p_category == String("get")) { Ref<VisualScriptPropertyGet> n; n.instantiate(); n->set_property(p_text); + if (!drop_path.is_empty()) { + if (drop_path == ".") { + n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_SELF); + } else { + n->set_call_mode(VisualScriptPropertyGet::CALL_MODE_NODE_PATH); + n->set_base_path(drop_path); + } + } + if (drop_node) { + n->set_base_type(drop_node->get_class()); + if (drop_node->get_script_instance()) { + n->set_base_script(drop_node->get_script_instance()->get_script()->get_path()); + } + } vnode = n; } + drop_path = String(); + drop_node = nullptr; if (p_category == String("action")) { if (p_text == "VisualScriptCondition") { @@ -4446,11 +4481,6 @@ VisualScriptEditor::VisualScriptEditor() { add_child(default_value_edit); default_value_edit->connect("variant_changed", callable_mp(this, &VisualScriptEditor::_default_value_changed)); - method_select = memnew(VisualScriptPropertySelector); - add_child(method_select); - method_select->connect("selected", callable_mp(this, &VisualScriptEditor::_selected_method)); - error_line = -1; - new_connect_node_select = memnew(VisualScriptPropertySelector); add_child(new_connect_node_select); new_connect_node_select->connect("selected", callable_mp(this, &VisualScriptEditor::_selected_connect_node)); diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 1f0f087be7..3b7ed3dba6 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -179,6 +179,9 @@ class VisualScriptEditor : public ScriptEditorBase { void connect_data(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode, int new_id); + NodePath drop_path; + Node *drop_node = nullptr; + Vector2 drop_position; void _selected_connect_node(const String &p_text, const String &p_category, const bool p_connecting = true); void connect_seq(Ref<VisualScriptNode> vnode_old, Ref<VisualScriptNode> vnode_new, int new_id); @@ -270,9 +273,6 @@ class VisualScriptEditor : public ScriptEditorBase { void _graph_ofs_changed(const Vector2 &p_ofs); void _comment_node_resized(const Vector2 &p_new_size, int p_node); - int selecting_method_id; - void _selected_method(const String &p_method, const String &p_type, const bool p_connecting); - void _draw_color_over_button(Object *obj, Color p_color); void _button_resource_previewed(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, Variant p_ud); diff --git a/modules/visual_script/visual_script_expression.cpp b/modules/visual_script/visual_script_expression.cpp index d63fbeb726..99b7275008 100644 --- a/modules/visual_script/visual_script_expression.cpp +++ b/modules/visual_script/visual_script_expression.cpp @@ -526,10 +526,10 @@ Error VisualScriptExpression::_get_token(Token &r_token) { r_token.value = Math_TAU; } else if (id == "INF") { r_token.type = TK_CONSTANT; - r_token.value = Math_INF; + r_token.value = INFINITY; } else if (id == "NAN") { r_token.type = TK_CONSTANT; - r_token.value = Math_NAN; + r_token.value = NAN; } else if (id == "not") { r_token.type = TK_OP_NOT; } else if (id == "or") { diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index 8f7b514881..7b5ca56dcc 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -254,25 +254,32 @@ PropertyInfo VisualScriptFunctionCall::get_output_value_port_info(int p_idx) con } String VisualScriptFunctionCall::get_caption() const { - if (call_mode == CALL_MODE_SELF) { - return " " + String(function) + "()"; - } - if (call_mode == CALL_MODE_SINGLETON) { - return String(singleton) + ":" + String(function) + "()"; - } else if (call_mode == CALL_MODE_BASIC_TYPE) { - return Variant::get_type_name(basic_type) + "." + String(function) + "()"; - } else if (call_mode == CALL_MODE_NODE_PATH) { - return " [" + String(base_path.simplified()) + "]." + String(function) + "()"; - } else { - return " " + base_type + "." + String(function) + "()"; - } + return " " + String(function) + "()"; } String VisualScriptFunctionCall::get_text() const { + String text; + + if (call_mode == CALL_MODE_BASIC_TYPE) { + text = String("On ") + Variant::get_type_name(basic_type); + } else if (call_mode == CALL_MODE_INSTANCE) { + text = String("On ") + base_type; + } else if (call_mode == CALL_MODE_NODE_PATH) { + text = "[" + String(base_path.simplified()) + "]"; + } else if (call_mode == CALL_MODE_SELF) { + text = "On Self"; + } else if (call_mode == CALL_MODE_SINGLETON) { + text = String(singleton) + ":" + String(function) + "()"; + } + if (rpc_call_mode) { - return "RPC"; + text += " RPC"; + if (rpc_call_mode == RPC_UNRELIABLE || rpc_call_mode == RPC_UNRELIABLE_TO_ID) { + text += " UNREL"; + } } - return ""; + + return text; } void VisualScriptFunctionCall::set_basic_type(Variant::Type p_type) { @@ -901,11 +908,11 @@ static Ref<VisualScriptNode> create_function_call_node(const String &p_name) { ////////////////////////////////////////// int VisualScriptPropertySet::get_output_sequence_port_count() const { - return call_mode != CALL_MODE_BASIC_TYPE ? 1 : 0; + return 1; } bool VisualScriptPropertySet::has_input_sequence_port() const { - return call_mode != CALL_MODE_BASIC_TYPE; + return 1; } Node *VisualScriptPropertySet::_get_base_node() const { @@ -990,8 +997,7 @@ PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const if (p_idx == 0) { PropertyInfo pi; pi.type = (call_mode == CALL_MODE_INSTANCE ? Variant::OBJECT : basic_type); - pi.name = (call_mode == CALL_MODE_INSTANCE ? String("instance") : Variant::get_type_name(basic_type).to_lower()); - _adjust_input_index(pi); + pi.name = "instance"; return pi; } } @@ -1000,21 +1006,24 @@ PropertyInfo VisualScriptPropertySet::get_input_value_port_info(int p_idx) const ClassDB::get_property_list(_get_base_type(), &props, false); for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { if (E->get().name == property) { - PropertyInfo pinfo = PropertyInfo(E->get().type, "value", PROPERTY_HINT_TYPE_STRING, E->get().hint_string); + String detail_prop_name = property; + if (index != StringName()) { + detail_prop_name += "." + String(index); + } + PropertyInfo pinfo = PropertyInfo(E->get().type, detail_prop_name, PROPERTY_HINT_TYPE_STRING, E->get().hint_string); _adjust_input_index(pinfo); return pinfo; } } PropertyInfo pinfo = type_cache; - pinfo.name = "value"; _adjust_input_index(pinfo); return pinfo; } PropertyInfo VisualScriptPropertySet::get_output_value_port_info(int p_idx) const { if (call_mode == CALL_MODE_BASIC_TYPE) { - return PropertyInfo(basic_type, "out"); + return PropertyInfo(basic_type, "pass"); } else if (call_mode == CALL_MODE_INSTANCE) { return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type()); } else { @@ -1036,17 +1045,18 @@ String VisualScriptPropertySet::get_caption() const { } String VisualScriptPropertySet::get_text() const { + if (!has_input_sequence_port()) { + return ""; + } if (call_mode == CALL_MODE_BASIC_TYPE) { return String("On ") + Variant::get_type_name(basic_type); + } else if (call_mode == CALL_MODE_INSTANCE) { + return String("On ") + base_type; + } else if (call_mode == CALL_MODE_NODE_PATH) { + return " [" + String(base_path.simplified()) + "]"; + } else { + return "On Self"; } - - static const char *cname[3] = { - "Self", - "Scene Node", - "Instance" - }; - - return String("On ") + cname[call_mode]; } void VisualScriptPropertySet::_update_base_type() { @@ -1707,7 +1717,9 @@ int VisualScriptPropertyGet::get_input_value_port_count() const { } int VisualScriptPropertyGet::get_output_value_port_count() const { - return 1; + int pc = (call_mode == CALL_MODE_BASIC_TYPE || call_mode == CALL_MODE_INSTANCE) ? 2 : 1; + + return pc; } String VisualScriptPropertyGet::get_output_sequence_port_text(int p_port) const { @@ -1727,33 +1739,46 @@ PropertyInfo VisualScriptPropertyGet::get_input_value_port_info(int p_idx) const } PropertyInfo VisualScriptPropertyGet::get_output_value_port_info(int p_idx) const { - List<PropertyInfo> props; - ClassDB::get_property_list(_get_base_type(), &props, false); - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - if (E->get().name == property) { - return PropertyInfo(E->get().type, "value." + String(index)); + if (call_mode == CALL_MODE_BASIC_TYPE && p_idx == 0) { + return PropertyInfo(basic_type, "pass"); + } else if (call_mode == CALL_MODE_INSTANCE && p_idx == 0) { + return PropertyInfo(Variant::OBJECT, "pass", PROPERTY_HINT_TYPE_STRING, get_base_type()); + } else { + List<PropertyInfo> props; + ClassDB::get_property_list(_get_base_type(), &props, false); + for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { + if (E->get().name == property) { + PropertyInfo pinfo = PropertyInfo(E->get().type, String(property) + "." + String(index), E->get().hint, E->get().hint_string); + _adjust_input_index(pinfo); + return pinfo; + } } } - return PropertyInfo(type_cache, "value"); + PropertyInfo pinfo = PropertyInfo(type_cache, "value"); + _adjust_input_index(pinfo); + return pinfo; } String VisualScriptPropertyGet::get_caption() const { - return String("Get ") + property; + String prop = String("Get ") + property; + if (index != StringName()) { + prop += "." + String(index); + } + + return prop; } String VisualScriptPropertyGet::get_text() const { if (call_mode == CALL_MODE_BASIC_TYPE) { return String("On ") + Variant::get_type_name(basic_type); + } else if (call_mode == CALL_MODE_INSTANCE) { + return String("On ") + base_type; + } else if (call_mode == CALL_MODE_NODE_PATH) { + return " [" + String(base_path.simplified()) + "]"; + } else { + return "On Self"; } - - static const char *cname[3] = { - "Self", - "Scene Node", - "Instance" - }; - - return String("On ") + cname[call_mode]; } void VisualScriptPropertyGet::set_base_type(const StringName &p_type) { @@ -1933,6 +1958,19 @@ Variant::Type VisualScriptPropertyGet::_get_type_cache() const { return type_cache; } +void VisualScriptPropertyGet::_adjust_input_index(PropertyInfo &pinfo) const { + if (index != StringName()) { + Variant v; + Callable::CallError ce; + Variant::construct(pinfo.type, v, nullptr, 0, ce); + Variant i = v.get(index); + pinfo.type = i.get_type(); + pinfo.name = String(property) + "." + index; + } else { + pinfo.name = String(property); + } +} + void VisualScriptPropertyGet::set_index(const StringName &p_type) { if (index == p_type) { return; @@ -2159,15 +2197,17 @@ public: bool valid; Variant v = *p_inputs[0]; - *p_outputs[0] = v.get(property, &valid); + *p_outputs[1] = v.get(property, &valid); if (index != StringName()) { - *p_outputs[0] = p_outputs[0]->get_named(index, valid); + *p_outputs[1] = p_outputs[1]->get_named(index, valid); } if (!valid) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; r_error_str = RTR("Invalid index property name."); } + + *p_outputs[0] = v; }; } @@ -2228,7 +2268,7 @@ int VisualScriptEmitSignal::get_input_value_port_count() const { } int VisualScriptEmitSignal::get_output_value_port_count() const { - return 0; + return 1; } String VisualScriptEmitSignal::get_output_sequence_port_text(int p_port) const { @@ -2271,6 +2311,16 @@ StringName VisualScriptEmitSignal::get_signal() const { return name; } +void VisualScriptEmitSignal::_adjust_input_index(PropertyInfo &pinfo) const { + if (index != StringName()) { + Variant v; + Callable::CallError ce; + Variant::construct(pinfo.type, v, nullptr, 0, ce); + Variant i = v.get(index); + pinfo.type = i.get_type(); + } +} + void VisualScriptEmitSignal::_validate_property(PropertyInfo &property) const { if (property.name == "signal") { property.hint = PROPERTY_HINT_ENUM; diff --git a/modules/visual_script/visual_script_func_nodes.h b/modules/visual_script/visual_script_func_nodes.h index eb17be1fbe..37a707d108 100644 --- a/modules/visual_script/visual_script_func_nodes.h +++ b/modules/visual_script/visual_script_func_nodes.h @@ -272,6 +272,8 @@ private: void _set_type_cache(Variant::Type p_type); Variant::Type _get_type_cache() const; + void _adjust_input_index(PropertyInfo &pinfo) const; + protected: virtual void _validate_property(PropertyInfo &property) const override; @@ -326,6 +328,9 @@ class VisualScriptEmitSignal : public VisualScriptNode { private: StringName name; + StringName index; + + void _adjust_input_index(PropertyInfo &pinfo) const; protected: virtual void _validate_property(PropertyInfo &property) const override; diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 2a46051ccf..86d8050acb 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -2182,8 +2182,8 @@ double VisualScriptMathConstant::const_value[MATH_CONSTANT_MAX] = { Math_TAU, 2.71828182845904523536, Math::sqrt(2.0), - Math_INF, - Math_NAN + INFINITY, + NAN }; int VisualScriptMathConstant::get_output_sequence_port_count() const { @@ -2868,6 +2868,12 @@ PropertyInfo VisualScriptCustomNode::get_input_value_port_info(int p_idx) const if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_name")) { info.name = get_script_instance()->call("_get_input_value_port_name", p_idx); } + if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_hint")) { + info.hint = PropertyHint(int(get_script_instance()->call("_get_input_value_port_hint", p_idx))); + } + if (get_script_instance() && get_script_instance()->has_method("_get_input_value_port_hint_string")) { + info.hint_string = get_script_instance()->call("_get_input_value_port_hint_string", p_idx); + } return info; } @@ -2879,9 +2885,31 @@ PropertyInfo VisualScriptCustomNode::get_output_value_port_info(int p_idx) const if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_name")) { info.name = get_script_instance()->call("_get_output_value_port_name", p_idx); } + if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_hint")) { + info.hint = PropertyHint(int(get_script_instance()->call("_get_output_value_port_hint", p_idx))); + } + if (get_script_instance() && get_script_instance()->has_method("_get_output_value_port_hint_string")) { + info.hint_string = get_script_instance()->call("_get_output_value_port_hint_string", p_idx); + } return info; } +VisualScriptCustomNode::TypeGuess VisualScriptCustomNode::guess_output_type(TypeGuess *p_inputs, int p_output) const { + TypeGuess tg; + PropertyInfo pi = VisualScriptCustomNode::get_output_value_port_info(p_output); + tg.type = pi.type; + if (pi.type == Variant::OBJECT) { + if (pi.hint == PROPERTY_HINT_RESOURCE_TYPE) { + if (pi.hint_string.is_resource_file()) { + tg.script = ResourceLoader::load(pi.hint_string); + } else if (ClassDB::class_exists(pi.hint_string)) { + tg.gdclass = pi.hint_string; + } + } + } + return tg; +} + String VisualScriptCustomNode::get_caption() const { if (get_script_instance() && get_script_instance()->has_method("_get_caption")) { return get_script_instance()->call("_get_caption"); @@ -3003,9 +3031,13 @@ void VisualScriptCustomNode::_bind_methods() { BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_value_port_type", PropertyInfo(Variant::INT, "idx"))); BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_input_value_port_name", PropertyInfo(Variant::INT, "idx"))); + BIND_VMETHOD(MethodInfo(Variant::INT, "_get_input_value_port_hint", PropertyInfo(Variant::INT, "idx"))); + BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_input_value_port_hint_string", PropertyInfo(Variant::INT, "idx"))); BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_value_port_type", PropertyInfo(Variant::INT, "idx"))); BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_output_value_port_name", PropertyInfo(Variant::INT, "idx"))); + BIND_VMETHOD(MethodInfo(Variant::INT, "_get_output_value_port_hint", PropertyInfo(Variant::INT, "idx"))); + BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_output_value_port_hint_string", PropertyInfo(Variant::INT, "idx"))); BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_caption")); BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_text")); diff --git a/modules/visual_script/visual_script_nodes.h b/modules/visual_script/visual_script_nodes.h index b599b92b3a..2390e5c7bc 100644 --- a/modules/visual_script/visual_script_nodes.h +++ b/modules/visual_script/visual_script_nodes.h @@ -792,6 +792,8 @@ public: virtual VisualScriptNodeInstance *instantiate(VisualScriptInstance *p_instance) override; + virtual TypeGuess guess_output_type(TypeGuess *p_inputs, int p_output) const override; + void _script_changed(); VisualScriptCustomNode(); diff --git a/modules/webrtc/doc_classes/WebRTCDataChannel.xml b/modules/webrtc/doc_classes/WebRTCDataChannel.xml index 5c90038b9a..3435dda982 100644 --- a/modules/webrtc/doc_classes/WebRTCDataChannel.xml +++ b/modules/webrtc/doc_classes/WebRTCDataChannel.xml @@ -14,6 +14,13 @@ Closes this data channel, notifying the other peer. </description> </method> + <method name="get_buffered_amount" qualifiers="const"> + <return type="int"> + </return> + <description> + Returns the number of bytes currently queued to be sent over this channel. + </description> + </method> <method name="get_id" qualifiers="const"> <return type="int"> </return> diff --git a/modules/webrtc/library_godot_webrtc.js b/modules/webrtc/library_godot_webrtc.js index 5269117686..a0a6c21be3 100644 --- a/modules/webrtc/library_godot_webrtc.js +++ b/modules/webrtc/library_godot_webrtc.js @@ -166,6 +166,11 @@ const GodotRTCDataChannel = { return GodotRTCDataChannel.get_prop(p_id, 'negotiated', 65535); }, + godot_js_rtc_datachannel_get_buffered_amount__sig: 'ii', + godot_js_rtc_datachannel_get_buffered_amount: function (p_id) { + return GodotRTCDataChannel.get_prop(p_id, 'bufferedAmount', 0); + }, + godot_js_rtc_datachannel_label_get__sig: 'ii', godot_js_rtc_datachannel_label_get: function (p_id) { const ref = IDHandler.get(p_id); diff --git a/modules/webrtc/webrtc_data_channel.cpp b/modules/webrtc/webrtc_data_channel.cpp index 004112f992..ca520a733d 100644 --- a/modules/webrtc/webrtc_data_channel.cpp +++ b/modules/webrtc/webrtc_data_channel.cpp @@ -46,6 +46,7 @@ void WebRTCDataChannel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_max_retransmits"), &WebRTCDataChannel::get_max_retransmits); ClassDB::bind_method(D_METHOD("get_protocol"), &WebRTCDataChannel::get_protocol); ClassDB::bind_method(D_METHOD("is_negotiated"), &WebRTCDataChannel::is_negotiated); + ClassDB::bind_method(D_METHOD("get_buffered_amount"), &WebRTCDataChannel::get_buffered_amount); ADD_PROPERTY(PropertyInfo(Variant::INT, "write_mode", PROPERTY_HINT_ENUM), "set_write_mode", "get_write_mode"); diff --git a/modules/webrtc/webrtc_data_channel.h b/modules/webrtc/webrtc_data_channel.h index 20affc513f..809d35c6e3 100644 --- a/modules/webrtc/webrtc_data_channel.h +++ b/modules/webrtc/webrtc_data_channel.h @@ -70,6 +70,8 @@ public: virtual String get_protocol() const = 0; virtual bool is_negotiated() const = 0; + virtual int get_buffered_amount() const = 0; + virtual Error poll() = 0; virtual void close() = 0; diff --git a/modules/webrtc/webrtc_data_channel_gdnative.cpp b/modules/webrtc/webrtc_data_channel_gdnative.cpp index f3009dde2f..10a3367557 100644 --- a/modules/webrtc/webrtc_data_channel_gdnative.cpp +++ b/modules/webrtc/webrtc_data_channel_gdnative.cpp @@ -111,6 +111,11 @@ bool WebRTCDataChannelGDNative::is_negotiated() const { return interface->is_negotiated(interface->data); } +int WebRTCDataChannelGDNative::get_buffered_amount() const { + ERR_FAIL_COND_V(interface == NULL, 0); + return interface->get_buffered_amount(interface->data); +} + Error WebRTCDataChannelGDNative::get_packet(const uint8_t **r_buffer, int &r_buffer_size) { ERR_FAIL_COND_V(interface == nullptr, ERR_UNCONFIGURED); return (Error)interface->get_packet(interface->data, r_buffer, &r_buffer_size); diff --git a/modules/webrtc/webrtc_data_channel_gdnative.h b/modules/webrtc/webrtc_data_channel_gdnative.h index 7e02a32046..5c80edd48c 100644 --- a/modules/webrtc/webrtc_data_channel_gdnative.h +++ b/modules/webrtc/webrtc_data_channel_gdnative.h @@ -60,6 +60,7 @@ public: virtual int get_max_retransmits() const override; virtual String get_protocol() const override; virtual bool is_negotiated() const override; + virtual int get_buffered_amount() const override; virtual Error poll() override; virtual void close() override; diff --git a/modules/webrtc/webrtc_data_channel_js.cpp b/modules/webrtc/webrtc_data_channel_js.cpp index dfbec80c86..31d6a0568c 100644 --- a/modules/webrtc/webrtc_data_channel_js.cpp +++ b/modules/webrtc/webrtc_data_channel_js.cpp @@ -46,6 +46,7 @@ extern int godot_js_rtc_datachannel_id_get(int p_id); extern int godot_js_rtc_datachannel_max_packet_lifetime_get(int p_id); extern int godot_js_rtc_datachannel_max_retransmits_get(int p_id); extern int godot_js_rtc_datachannel_is_negotiated(int p_id); +extern int godot_js_rtc_datachannel_get_buffered_amount(int p_id); extern char *godot_js_rtc_datachannel_label_get(int p_id); // Must free the returned string. extern char *godot_js_rtc_datachannel_protocol_get(int p_id); // Must free the returned string. extern void godot_js_rtc_datachannel_destroy(int p_id); @@ -181,6 +182,10 @@ bool WebRTCDataChannelJS::is_negotiated() const { return godot_js_rtc_datachannel_is_negotiated(_js_id); } +int WebRTCDataChannelJS::get_buffered_amount() const { + return godot_js_rtc_datachannel_get_buffered_amount(_js_id); +} + WebRTCDataChannelJS::WebRTCDataChannelJS() { } diff --git a/modules/webrtc/webrtc_data_channel_js.h b/modules/webrtc/webrtc_data_channel_js.h index db58ebccff..5cd6a32ed9 100644 --- a/modules/webrtc/webrtc_data_channel_js.h +++ b/modules/webrtc/webrtc_data_channel_js.h @@ -72,6 +72,7 @@ public: virtual int get_max_retransmits() const override; virtual String get_protocol() const override; virtual bool is_negotiated() const override; + virtual int get_buffered_amount() const override; virtual Error poll() override; virtual void close() override; diff --git a/modules/webxr/webxr_interface_js.cpp b/modules/webxr/webxr_interface_js.cpp index 6957dea8c4..099e769303 100644 --- a/modules/webxr/webxr_interface_js.cpp +++ b/modules/webxr/webxr_interface_js.cpp @@ -35,6 +35,7 @@ #include "core/os/os.h" #include "emscripten.h" #include "godot_webxr.h" +#include "servers/rendering/renderer_compositor.h" #include <stdlib.h> void _emwebxr_on_session_supported(char *p_session_mode, int p_supported) { @@ -102,7 +103,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_input_event(char *p_signal_name ERR_FAIL_COND(interface.is_null()); StringName signal_name = StringName(p_signal_name); - interface->emit_signal(SNAME(signal_name), p_input_source + 1); + interface->emit_signal(signal_name, p_input_source + 1); } extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_simple_event(char *p_signal_name) { @@ -113,7 +114,7 @@ extern "C" EMSCRIPTEN_KEEPALIVE void _emwebxr_on_simple_event(char *p_signal_nam ERR_FAIL_COND(interface.is_null()); StringName signal_name = StringName(p_signal_name); - interface->emit_signal(SNAME(signal_name)); + interface->emit_signal(signal_name); } void WebXRInterfaceJS::is_session_supported(const String &p_session_mode) { @@ -376,6 +377,22 @@ void WebXRInterfaceJS::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_targ return; } godot_webxr_commit_for_eye(p_eye); +} + +Vector<BlitToScreen> WebXRInterfaceJS::commit_views(RID p_render_target, const Rect2 &p_screen_rect) { + Vector<BlitToScreen> blit_to_screen; + + if (!initialized) { + return blit_to_screen; + } + + // @todo Refactor this to be based on "views" rather than "eyes". + godot_webxr_commit_for_eye(XRInterface::EYE_LEFT); + if (godot_webxr_get_view_count() > 1) { + godot_webxr_commit_for_eye(XRInterface::EYE_RIGHT); + } + + return blit_to_screen; }; void WebXRInterfaceJS::process() { diff --git a/modules/webxr/webxr_interface_js.h b/modules/webxr/webxr_interface_js.h index 3b5509adeb..f9368582b7 100644 --- a/modules/webxr/webxr_interface_js.h +++ b/modules/webxr/webxr_interface_js.h @@ -89,6 +89,7 @@ public: virtual CameraMatrix get_projection_for_view(uint32_t p_view, real_t p_aspect, real_t p_z_near, real_t p_z_far) override; virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye) override; virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) override; + virtual Vector<BlitToScreen> commit_views(RID p_render_target, const Rect2 &p_screen_rect) override; virtual void process() override; virtual void notification(int p_what) override; diff --git a/platform/android/java/app/gradle.properties b/platform/android/java/app/gradle.properties new file mode 100644 index 0000000000..19587bd81f --- /dev/null +++ b/platform/android/java/app/gradle.properties @@ -0,0 +1,25 @@ +# Godot custom build Gradle settings. +# These properties apply when running custom build from the Godot editor. +# NOTE: This should be kept in sync with 'godot/platform/android/java/gradle.properties' except +# where otherwise specified. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +android.enableJetifier=true +android.useAndroidX=true + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx4536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +org.gradle.warning.mode=all + +# Enable resource optimizations for release build. +# NOTE: This is turned off for template release build in order to support the build legacy process. +android.enableResourceOptimizations=true diff --git a/platform/android/java/build.gradle b/platform/android/java/build.gradle index ee24a46d9f..87bb2ea218 100644 --- a/platform/android/java/build.gradle +++ b/platform/android/java/build.gradle @@ -115,7 +115,7 @@ task zipCustomBuild(type: Zip) { doFirst { logger.lifecycle("Generating Godot custom build template") } - from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradle.properties', 'gradlew', 'gradlew.bat', 'gradle/**'])) + from(fileTree(dir: 'app', excludes: ['**/build/**', '**/.gradle/**', '**/*.iml']), fileTree(dir: '.', includes: ['gradlew', 'gradlew.bat', 'gradle/**'])) include '**/*' archiveFileName = 'android_source.zip' destinationDirectory = file(binDir) diff --git a/platform/android/java/gradle.properties b/platform/android/java/gradle.properties index 6b3b62a9da..b51a19a005 100644 --- a/platform/android/java/gradle.properties +++ b/platform/android/java/gradle.properties @@ -1,4 +1,6 @@ # Project-wide Gradle settings. +# NOTE: This should be kept in sync with 'godot/platform/android/java/app/gradle.properties' except +# where otherwise specified. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* @@ -20,3 +22,7 @@ org.gradle.jvmargs=-Xmx4536m # org.gradle.parallel=true org.gradle.warning.mode=all + +# Disable resource optimizations for template release build. +# NOTE: This is turned on for custom build in order to improve the release build. +android.enableResourceOptimizations=false diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index 6d1e27bf1b..bf2f8f8947 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -58,7 +58,7 @@ void TileMapPattern::remove_cell(const Vector2i &p_coords, bool p_update_size) { } int TileMapPattern::get_cell_source_id(const Vector2i &p_coords) const { - ERR_FAIL_COND_V(!pattern.has(p_coords), -1); + ERR_FAIL_COND_V(!pattern.has(p_coords), TileSet::INVALID_SOURCE); return pattern[p_coords].source_id; } @@ -113,7 +113,7 @@ void TileMapPattern::clear() { }; void TileMapPattern::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(-1), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); + ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMapPattern::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); ClassDB::bind_method(D_METHOD("has_cell", "coords"), &TileMapPattern::has_cell); ClassDB::bind_method(D_METHOD("remove_cell", "coords"), &TileMapPattern::remove_cell); ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMapPattern::get_cell_source_id); @@ -513,15 +513,15 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i Vector2i atlas_coords = p_atlas_coords; int alternative_tile = p_alternative_tile; - if ((source_id == -1 || atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE) && - (source_id != -1 || atlas_coords != TileSetSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE)) { + if ((source_id == TileSet::INVALID_SOURCE || atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE) && + (source_id != TileSet::INVALID_SOURCE || atlas_coords != TileSetSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE)) { WARN_PRINT("Setting a cell a cell as empty requires both source_id, atlas_coord and alternative_tile to be set to their respective \"invalid\" values. Values were thus changes accordingly."); - source_id = -1; + source_id = TileSet::INVALID_SOURCE; atlas_coords = TileSetSource::INVALID_ATLAS_COORDS; alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; } - if (!E && source_id == -1) { + if (!E && source_id == TileSet::INVALID_SOURCE) { return; // Nothing to do, the tile is already empty. } @@ -530,7 +530,7 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i Map<Vector2i, TileMapQuadrant>::Element *Q = quadrant_map.find(qk); - if (source_id == -1) { + if (source_id == TileSet::INVALID_SOURCE) { // Erase existing cell in the tile map. tile_map.erase(pk); @@ -579,18 +579,23 @@ void TileMap::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i } } -int TileMap::get_cell_source_id(const Vector2i &p_coords) const { +int TileMap::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) const { // Get a cell source id from position const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); if (!E) { - return -1; + return TileSet::INVALID_SOURCE; + } + + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + return proxyed[0]; } return E->get().source_id; } -Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords) const { +Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies) const { // Get a cell source id from position const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); @@ -598,10 +603,15 @@ Vector2i TileMap::get_cell_atlas_coords(const Vector2i &p_coords) const { return TileSetSource::INVALID_ATLAS_COORDS; } + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + return proxyed[1]; + } + return E->get().get_atlas_coords(); } -int TileMap::get_cell_alternative_tile(const Vector2i &p_coords) const { +int TileMap::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies) const { // Get a cell source id from position const Map<Vector2i, TileMapCell>::Element *E = tile_map.find(p_coords); @@ -609,6 +619,11 @@ int TileMap::get_cell_alternative_tile(const Vector2i &p_coords) const { return TileSetSource::INVALID_TILE_ALTERNATIVE; } + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile); + return proxyed[2]; + } + return E->get().alternative_tile; } @@ -697,11 +712,18 @@ void TileMap::set_pattern(Vector2i p_position, const TileMapPattern *p_pattern) } } -TileMapCell TileMap::get_cell(const Vector2i &p_coords) const { +TileMapCell TileMap::get_cell(const Vector2i &p_coords, bool p_use_proxies) const { if (!tile_map.has(p_coords)) { return TileMapCell(); } else { - return tile_map.find(p_coords)->get(); + TileMapCell c = tile_map.find(p_coords)->get(); + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(c.source_id, c.get_atlas_coords(), c.alternative_tile); + c.source_id = proxyed[0]; + c.set_atlas_coords(proxyed[1]); + c.alternative_tile = proxyed[2]; + } + return c; } } @@ -720,7 +742,7 @@ void TileMap::fix_invalid_tiles() { } } for (Set<Vector2i>::Element *E = coords.front(); E; E = E->next()) { - set_cell(E->get(), -1, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + set_cell(E->get(), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); } } @@ -799,25 +821,28 @@ void TileMap::_set_tile_data(const Vector<int> &p_data) { SWAP(local[9], local[10]); } #endif + // Extracts position in TileMap. int16_t x = decode_uint16(&local[0]); int16_t y = decode_uint16(&local[2]); if (format == FORMAT_3) { uint16_t source_id = decode_uint16(&local[4]); uint16_t atlas_coords_x = decode_uint16(&local[6]); - uint16_t atlas_coords_y = decode_uint32(&local[8]); + uint16_t atlas_coords_y = decode_uint16(&local[8]); uint16_t alternative_tile = decode_uint16(&local[10]); set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile); } else { #ifndef DISABLE_DEPRECATED - uint32_t v = decode_uint32(&local[4]); - v &= (1 << 29) - 1; + // Previous decated format. - // We generate an alternative tile number out of the the flags - // An option should create the alternative in the tileset for compatibility + uint32_t v = decode_uint32(&local[4]); + // Extract the transform flags that used to be in the tilemap. bool flip_h = v & (1 << 29); bool flip_v = v & (1 << 30); bool transpose = v & (1 << 31); + v &= (1 << 29) - 1; + + // Extract autotile/atlas coords. int16_t coord_x = 0; int16_t coord_y = 0; if (format == FORMAT_2) { @@ -825,13 +850,17 @@ void TileMap::_set_tile_data(const Vector<int> &p_data) { coord_y = decode_uint16(&local[10]); } - int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2); - if (tile_set.is_valid()) { - v = tile_set->compatibility_get_source_for_tile_id(v); + Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose); + if (a.size() == 3) { + set_cell(Vector2i(x, y), a[0], a[1], a[2]); + } else { + ERR_PRINT(vformat("No valid tile in Tileset for: tile:%s coords:%s flip_h:%s flip_v:%s transpose:%s", v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose)); + } + } else { + int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2); + set_cell(Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile); } - - set_cell(Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile); #endif } } @@ -1727,10 +1756,10 @@ void TileMap::_bind_methods() { ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "show_navigation"), &TileMap::set_navigation_visibility_mode); ClassDB::bind_method(D_METHOD("get_navigation_visibility_mode"), &TileMap::get_navigation_visibility_mode); - ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(-1), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); - ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMap::get_cell_source_id); - ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMap::get_cell_atlas_coords); - ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMap::get_cell_alternative_tile); + ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE)); + ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords", "use_proxies"), &TileMap::get_cell_source_id); + ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords", "use_proxies"), &TileMap::get_cell_atlas_coords); + ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile); ClassDB::bind_method(D_METHOD("fix_invalid_tiles"), &TileMap::fix_invalid_tiles); ClassDB::bind_method(D_METHOD("get_surrounding_tiles", "coords"), &TileMap::get_surrounding_tiles); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 3001e6b471..9e35e73ad8 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -265,16 +265,16 @@ public: VisibilityMode get_navigation_visibility_mode(); void set_cell(const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE); - int get_cell_source_id(const Vector2i &p_coords) const; - Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const; - int get_cell_alternative_tile(const Vector2i &p_coords) const; + int get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies = false) const; + Vector2i get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies = false) const; + int get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies = false) const; TileMapPattern *get_pattern(TypedArray<Vector2i> p_coords_array); Vector2i map_pattern(Vector2i p_position_in_tilemap, Vector2i p_coords_in_pattern, const TileMapPattern *p_pattern); void set_pattern(Vector2i p_position, const TileMapPattern *p_pattern); // Not exposed to users - TileMapCell get_cell(const Vector2i &p_coords) const; + TileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const; Map<Vector2i, TileMapQuadrant> &get_quadrant_map(); int get_effective_quadrant_size() const; diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index 9e0bc9f3a1..60f8ad8f36 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -401,6 +401,22 @@ void CPUParticles3D::set_emission_colors(const Vector<Color> &p_colors) { emission_colors = p_colors; } +void CPUParticles3D::set_emission_ring_axis(Vector3 p_axis) { + emission_ring_axis = p_axis; +} + +void CPUParticles3D::set_emission_ring_height(float p_height) { + emission_ring_height = p_height; +} + +void CPUParticles3D::set_emission_ring_radius(float p_radius) { + emission_ring_radius = p_radius; +} + +void CPUParticles3D::set_emission_ring_inner_radius(float p_radius) { + emission_ring_inner_radius = p_radius; +} + float CPUParticles3D::get_emission_sphere_radius() const { return emission_sphere_radius; } @@ -421,6 +437,22 @@ Vector<Color> CPUParticles3D::get_emission_colors() const { return emission_colors; } +Vector3 CPUParticles3D::get_emission_ring_axis() const { + return emission_ring_axis; +} + +float CPUParticles3D::get_emission_ring_height() const { + return emission_ring_height; +} + +float CPUParticles3D::get_emission_ring_radius() const { + return emission_ring_radius; +} + +float CPUParticles3D::get_emission_ring_inner_radius() const { + return emission_ring_inner_radius; +} + CPUParticles3D::EmissionShape CPUParticles3D::get_emission_shape() const { return emission_shape; } @@ -442,7 +474,7 @@ void CPUParticles3D::_validate_property(PropertyInfo &property) const { property.usage = PROPERTY_USAGE_NONE; } - if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { + if ((property.name == "emission_point_texture" || property.name == "emission_color_texture" || property.name == "emission_points") && (emission_shape != EMISSION_SHAPE_POINTS && (emission_shape != EMISSION_SHAPE_DIRECTED_POINTS))) { property.usage = PROPERTY_USAGE_NONE; } @@ -450,6 +482,10 @@ void CPUParticles3D::_validate_property(PropertyInfo &property) const { property.usage = PROPERTY_USAGE_NONE; } + if (property.name.begins_with("emission_ring_") && emission_shape != EMISSION_SHAPE_RING) { + property.usage = PROPERTY_USAGE_NONE; + } + if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { property.usage = PROPERTY_USAGE_NONE; } @@ -746,6 +782,21 @@ void CPUParticles3D::_particles_process(float p_delta) { p.base_color = emission_colors.get(random_idx); } } break; + case EMISSION_SHAPE_RING: { + float ring_random_angle = Math::randf() * 2.0 * Math_PI; + float ring_random_radius = Math::randf() * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius; + Vector3 axis = emission_ring_axis.normalized(); + Vector3 ortho_axis = Vector3(); + if (axis == Vector3(1.0, 0.0, 0.0)) { + ortho_axis = Vector3(0.0, 1.0, 0.0).cross(axis); + } else { + ortho_axis = Vector3(1.0, 0.0, 0.0).cross(axis); + } + ortho_axis = ortho_axis.normalized(); + ortho_axis.rotate(axis, ring_random_angle); + ortho_axis = ortho_axis.normalized(); + p.transform.origin = ortho_axis * ring_random_radius + (Math::randf() * emission_ring_height - emission_ring_height / 2.0) * axis; + } break; case EMISSION_SHAPE_MAX: { // Max value for validity check. break; } @@ -1336,18 +1387,34 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emission_colors", "array"), &CPUParticles3D::set_emission_colors); ClassDB::bind_method(D_METHOD("get_emission_colors"), &CPUParticles3D::get_emission_colors); + ClassDB::bind_method(D_METHOD("set_emission_ring_axis", "axis"), &CPUParticles3D::set_emission_ring_axis); + ClassDB::bind_method(D_METHOD("get_emission_ring_axis"), &CPUParticles3D::get_emission_ring_axis); + + ClassDB::bind_method(D_METHOD("set_emission_ring_height", "height"), &CPUParticles3D::set_emission_ring_height); + ClassDB::bind_method(D_METHOD("get_emission_ring_height"), &CPUParticles3D::get_emission_ring_height); + + ClassDB::bind_method(D_METHOD("set_emission_ring_radius", "radius"), &CPUParticles3D::set_emission_ring_radius); + ClassDB::bind_method(D_METHOD("get_emission_ring_radius"), &CPUParticles3D::get_emission_ring_radius); + + ClassDB::bind_method(D_METHOD("set_emission_ring_inner_radius", "inner_radius"), &CPUParticles3D::set_emission_ring_inner_radius); + ClassDB::bind_method(D_METHOD("get_emission_ring_inner_radius"), &CPUParticles3D::get_emission_ring_inner_radius); + ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles3D::get_gravity); ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles3D::set_gravity); ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles3D::convert_from_particles); ADD_GROUP("Emission Shape", "emission_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points,Ring", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "emission_points"), "set_emission_points", "get_emission_points"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_ring_axis"), "set_emission_ring_axis", "get_emission_ring_axis"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height"), "set_emission_ring_height", "get_emission_ring_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius"), "set_emission_ring_radius", "get_emission_ring_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius"); ADD_GROUP("Particle Flags", "particle_flag_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_rotate_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ROTATE_Y); @@ -1433,6 +1500,7 @@ void CPUParticles3D::_bind_methods() { BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX); BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS); BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_RING); BIND_ENUM_CONSTANT(EMISSION_SHAPE_MAX); } @@ -1461,6 +1529,10 @@ CPUParticles3D::CPUParticles3D() { set_emission_shape(EMISSION_SHAPE_POINT); set_emission_sphere_radius(1); set_emission_box_extents(Vector3(1, 1, 1)); + set_emission_ring_axis(Vector3(0, 0, 1.0)); + set_emission_ring_height(1); + set_emission_ring_radius(1); + set_emission_ring_inner_radius(0); set_gravity(Vector3(0, -9.8, 0)); diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index b35e659757..07d345ba2c 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -76,6 +76,7 @@ public: EMISSION_SHAPE_BOX, EMISSION_SHAPE_POINTS, EMISSION_SHAPE_DIRECTED_POINTS, + EMISSION_SHAPE_RING, EMISSION_SHAPE_MAX }; @@ -171,6 +172,10 @@ private: Vector<Vector3> emission_normals; Vector<Color> emission_colors; int emission_point_count = 0; + Vector3 emission_ring_axis; + float emission_ring_height; + float emission_ring_radius; + float emission_ring_inner_radius; Vector3 gravity = Vector3(0, -9.8, 0); @@ -268,6 +273,10 @@ public: void set_emission_normals(const Vector<Vector3> &p_normals); void set_emission_colors(const Vector<Color> &p_colors); void set_emission_point_count(int p_count); + void set_emission_ring_axis(Vector3 p_axis); + void set_emission_ring_height(float p_height); + void set_emission_ring_radius(float p_radius); + void set_emission_ring_inner_radius(float p_radius); EmissionShape get_emission_shape() const; float get_emission_sphere_radius() const; @@ -276,6 +285,10 @@ public: Vector<Vector3> get_emission_normals() const; Vector<Color> get_emission_colors() const; int get_emission_point_count() const; + Vector3 get_emission_ring_axis() const; + float get_emission_ring_height() const; + float get_emission_ring_radius() const; + float get_emission_ring_inner_radius() const; void set_gravity(const Vector3 &p_gravity); Vector3 get_gravity() const; diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 66e3535fc4..3ffb3c0393 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -599,7 +599,7 @@ void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, f const Vector3 *pp = probe_positions.ptr(); bool exists = false; for (int j = 0; j < ppcount; j++) { - if (pp[j].distance_to(real_pos) < CMP_EPSILON) { + if (pp[j].is_equal_approx(real_pos)) { exists = true; break; } diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index 12f055c01d..f1b9708f91 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -36,17 +36,17 @@ #include <stdlib.h> static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv, const Vector3 *p_normal, Vector2 &r_uv, Vector3 &r_normal) { - if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2) { + if (p_pos.is_equal_approx(p_vtx[0])) { r_uv = p_uv[0]; r_normal = p_normal[0]; return; } - if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2) { + if (p_pos.is_equal_approx(p_vtx[1])) { r_uv = p_uv[1]; r_normal = p_normal[1]; return; } - if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2) { + if (p_pos.is_equal_approx(p_vtx[2])) { r_uv = p_uv[2]; r_normal = p_normal[2]; return; diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 6878cbaa12..d88a9badf4 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -387,19 +387,19 @@ Vector2 AnimationNodeBlendSpace2D::get_closest_point(const Vector2 &p_point) { } void AnimationNodeBlendSpace2D::_blend_triangle(const Vector2 &p_pos, const Vector2 *p_points, float *r_weights) { - if (p_pos.distance_squared_to(p_points[0]) < CMP_EPSILON2) { + if (p_pos.is_equal_approx(p_points[0])) { r_weights[0] = 1; r_weights[1] = 0; r_weights[2] = 0; return; } - if (p_pos.distance_squared_to(p_points[1]) < CMP_EPSILON2) { + if (p_pos.is_equal_approx(p_points[1])) { r_weights[0] = 0; r_weights[1] = 1; r_weights[2] = 0; return; } - if (p_pos.distance_squared_to(p_points[2]) < CMP_EPSILON2) { + if (p_pos.is_equal_approx(p_points[2])) { r_weights[0] = 0; r_weights[1] = 0; r_weights[2] = 1; diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 7b7f1c3818..32b6ba84b5 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -263,7 +263,7 @@ private: Transform3D physics_last_camera_transform; ObjectID physics_last_id; bool physics_has_last_mousepos = false; - Vector2 physics_last_mousepos = Vector2(Math_INF, Math_INF); + Vector2 physics_last_mousepos = Vector2(INFINITY, INFINITY); struct { bool alt = false; bool control = false; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 2d83f59bb5..439fe649a1 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -1085,6 +1085,10 @@ void unregister_scene_types() { BaseMaterial3D::finish_shaders(); #endif // _3D_DISABLED + PhysicalSkyMaterial::cleanup_shader(); + PanoramaSkyMaterial::cleanup_shader(); + ProceduralSkyMaterial::cleanup_shader(); + ParticlesMaterial::finish_shaders(); CanvasItemMaterial::finish_shaders(); ColorPicker::finish_shaders(); diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp index 00bea312b3..7da9cb96ee 100644 --- a/scene/resources/particles_material.cpp +++ b/scene/resources/particles_material.cpp @@ -90,6 +90,10 @@ void ParticlesMaterial::init_shaders() { shader_names->emission_texture_points = "emission_texture_points"; shader_names->emission_texture_normal = "emission_texture_normal"; shader_names->emission_texture_color = "emission_texture_color"; + shader_names->emission_ring_axis = "emission_ring_axis"; + shader_names->emission_ring_height = "emission_ring_height"; + shader_names->emission_ring_radius = "emission_ring_radius"; + shader_names->emission_ring_inner_radius = "emission_ring_inner_radius"; shader_names->gravity = "gravity"; @@ -194,6 +198,12 @@ void ParticlesMaterial::_update_shader() { code += "uniform sampler2D emission_texture_color : hint_white;\n"; } } break; + case EMISSION_SHAPE_RING: { + code += "uniform vec3 " + shader_names->emission_ring_axis + ";\n"; + code += "uniform float " + shader_names->emission_ring_height + ";\n"; + code += "uniform float " + shader_names->emission_ring_radius + ";\n"; + code += "uniform float " + shader_names->emission_ring_inner_radius + ";\n"; + } break; case EMISSION_SHAPE_MAX: { // Max value for validity check. break; } @@ -396,6 +406,28 @@ void ParticlesMaterial::_update_shader() { } } } break; + case EMISSION_SHAPE_RING: { + code += " float ring_spawn_angle = rand_from_seed(alt_seed) * 2.0 * pi;\n"; + code += " float ring_random_radius = rand_from_seed(alt_seed) * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius;\n"; + code += " vec3 axis = normalize(emission_ring_axis);\n"; + code += " vec3 ortho_axis = vec3(0.0);\n"; + code += " if (axis == vec3(1.0, 0.0, 0.0)) {\n"; + code += " ortho_axis = cross(axis, vec3(0.0, 1.0, 0.0));\n"; + code += " } else {\n"; + code += " ortho_axis = cross(axis, vec3(1.0, 0.0, 0.0));\n"; + code += " }\n"; + code += " ortho_axis = normalize(ortho_axis);\n"; + code += " float s = sin(ring_spawn_angle);\n"; + code += " float c = cos(ring_spawn_angle);\n"; + code += " float oc = 1.0 - c;\n"; + code += " ortho_axis = mat3(\n"; + code += " vec3(c + axis.x * axis.x * oc, axis.x * axis.y * oc - axis.z * s, axis.x * axis.z *oc + axis.y * s),\n"; + code += " vec3(axis.x * axis.y * oc + s * axis.z, c + axis.y * axis.y * oc, axis.y * axis.z * oc - axis.x * s),\n"; + code += " vec3(axis.z * axis.x * oc - axis.y * s, axis.z * axis.y * oc + axis.x * s, c + axis.z * axis.z * oc)\n"; + code += " ) * ortho_axis;\n"; + code += " ortho_axis = normalize(ortho_axis);\n"; + code += " TRANSFORM[3].xyz = ortho_axis * ring_random_radius + (rand_from_seed(alt_seed) * emission_ring_height - emission_ring_height / 2.0) * axis;\n"; + } break; case EMISSION_SHAPE_MAX: { // Max value for validity check. break; } @@ -990,6 +1022,26 @@ void ParticlesMaterial::set_emission_point_count(int p_count) { RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_texture_point_count, p_count); } +void ParticlesMaterial::set_emission_ring_axis(Vector3 p_axis) { + emission_ring_axis = p_axis; + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_axis, p_axis); +} + +void ParticlesMaterial::set_emission_ring_height(float p_height) { + emission_ring_height = p_height; + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_height, p_height); +} + +void ParticlesMaterial::set_emission_ring_radius(float p_radius) { + emission_ring_radius = p_radius; + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_radius, p_radius); +} + +void ParticlesMaterial::set_emission_ring_inner_radius(float p_radius) { + emission_ring_inner_radius = p_radius; + RenderingServer::get_singleton()->material_set_param(_get_material(), shader_names->emission_ring_inner_radius, p_radius); +} + ParticlesMaterial::EmissionShape ParticlesMaterial::get_emission_shape() const { return emission_shape; } @@ -1018,6 +1070,22 @@ int ParticlesMaterial::get_emission_point_count() const { return emission_point_count; } +Vector3 ParticlesMaterial::get_emission_ring_axis() const { + return emission_ring_axis; +} + +float ParticlesMaterial::get_emission_ring_height() const { + return emission_ring_height; +} + +float ParticlesMaterial::get_emission_ring_radius() const { + return emission_ring_radius; +} + +float ParticlesMaterial::get_emission_ring_inner_radius() const { + return emission_ring_inner_radius; +} + void ParticlesMaterial::set_gravity(const Vector3 &p_gravity) { gravity = p_gravity; Vector3 gset = gravity; @@ -1054,7 +1122,7 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const { property.usage = PROPERTY_USAGE_NONE; } - if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { + if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape != EMISSION_SHAPE_POINTS && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS)) { property.usage = PROPERTY_USAGE_NONE; } @@ -1066,6 +1134,10 @@ void ParticlesMaterial::_validate_property(PropertyInfo &property) const { property.usage = PROPERTY_USAGE_NONE; } + if (property.name.begins_with("emission_ring_") && emission_shape != EMISSION_SHAPE_RING) { + property.usage = PROPERTY_USAGE_NONE; + } + if (property.name == "sub_emitter_frequency" && sub_emitter_mode != SUB_EMITTER_CONSTANT) { property.usage = PROPERTY_USAGE_NONE; } @@ -1212,6 +1284,18 @@ void ParticlesMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emission_point_count", "point_count"), &ParticlesMaterial::set_emission_point_count); ClassDB::bind_method(D_METHOD("get_emission_point_count"), &ParticlesMaterial::get_emission_point_count); + ClassDB::bind_method(D_METHOD("set_emission_ring_axis", "axis"), &ParticlesMaterial::set_emission_ring_axis); + ClassDB::bind_method(D_METHOD("get_emission_ring_axis"), &ParticlesMaterial::get_emission_ring_axis); + + ClassDB::bind_method(D_METHOD("set_emission_ring_height", "height"), &ParticlesMaterial::set_emission_ring_height); + ClassDB::bind_method(D_METHOD("get_emission_ring_height"), &ParticlesMaterial::get_emission_ring_height); + + ClassDB::bind_method(D_METHOD("set_emission_ring_radius", "radius"), &ParticlesMaterial::set_emission_ring_radius); + ClassDB::bind_method(D_METHOD("get_emission_ring_radius"), &ParticlesMaterial::get_emission_ring_radius); + + ClassDB::bind_method(D_METHOD("set_emission_ring_inner_radius", "inner_radius"), &ParticlesMaterial::set_emission_ring_inner_radius); + ClassDB::bind_method(D_METHOD("get_emission_ring_inner_radius"), &ParticlesMaterial::get_emission_ring_inner_radius); + ClassDB::bind_method(D_METHOD("get_gravity"), &ParticlesMaterial::get_gravity); ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &ParticlesMaterial::set_gravity); @@ -1249,13 +1333,17 @@ void ParticlesMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime_randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_lifetime_randomness", "get_lifetime_randomness"); ADD_GROUP("Emission Shape", "emission_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points"), "set_emission_shape", "get_emission_shape"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points,Ring"), "set_emission_shape", "get_emission_shape"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01,or_greater"), "set_emission_sphere_radius", "get_emission_sphere_radius"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_point_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_emission_point_texture", "get_emission_point_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_normal_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_emission_normal_texture", "get_emission_normal_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "emission_color_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_emission_color_texture", "get_emission_color_texture"); ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_point_count", PROPERTY_HINT_RANGE, "0,1000000,1"), "set_emission_point_count", "get_emission_point_count"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_ring_axis"), "set_emission_ring_axis", "get_emission_ring_axis"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height"), "set_emission_ring_height", "get_emission_ring_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius"), "set_emission_ring_radius", "get_emission_ring_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius"); ADD_GROUP("ParticleFlags", "particle_flag_"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_rotate_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ROTATE_Y); @@ -1355,6 +1443,7 @@ void ParticlesMaterial::_bind_methods() { BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX); BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS); BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_RING); BIND_ENUM_CONSTANT(EMISSION_SHAPE_MAX); BIND_ENUM_CONSTANT(SUB_EMITTER_DISABLED); @@ -1384,6 +1473,10 @@ ParticlesMaterial::ParticlesMaterial() : set_emission_shape(EMISSION_SHAPE_POINT); set_emission_sphere_radius(1); set_emission_box_extents(Vector3(1, 1, 1)); + set_emission_ring_axis(Vector3(0, 0, 1.0)); + set_emission_ring_height(1); + set_emission_ring_radius(1); + set_emission_ring_inner_radius(0); set_gravity(Vector3(0, -9.8, 0)); set_lifetime_randomness(0); diff --git a/scene/resources/particles_material.h b/scene/resources/particles_material.h index 3f874bd68c..8b0b26a3d1 100644 --- a/scene/resources/particles_material.h +++ b/scene/resources/particles_material.h @@ -74,6 +74,7 @@ public: EMISSION_SHAPE_BOX, EMISSION_SHAPE_POINTS, EMISSION_SHAPE_DIRECTED_POINTS, + EMISSION_SHAPE_RING, EMISSION_SHAPE_MAX }; @@ -195,6 +196,10 @@ private: StringName emission_texture_points; StringName emission_texture_normal; StringName emission_texture_color; + StringName emission_ring_axis; + StringName emission_ring_height; + StringName emission_ring_radius; + StringName emission_ring_inner_radius; StringName gravity; @@ -235,6 +240,10 @@ private: Ref<Texture2D> emission_point_texture; Ref<Texture2D> emission_normal_texture; Ref<Texture2D> emission_color_texture; + Vector3 emission_ring_axis; + float emission_ring_height; + float emission_ring_radius; + float emission_ring_inner_radius; int emission_point_count = 1; bool anim_loop; @@ -293,6 +302,10 @@ public: void set_emission_point_texture(const Ref<Texture2D> &p_points); void set_emission_normal_texture(const Ref<Texture2D> &p_normals); void set_emission_color_texture(const Ref<Texture2D> &p_colors); + void set_emission_ring_axis(Vector3 p_axis); + void set_emission_ring_height(float p_height); + void set_emission_ring_radius(float p_radius); + void set_emission_ring_inner_radius(float p_radius); void set_emission_point_count(int p_count); EmissionShape get_emission_shape() const; @@ -301,6 +314,10 @@ public: Ref<Texture2D> get_emission_point_texture() const; Ref<Texture2D> get_emission_normal_texture() const; Ref<Texture2D> get_emission_color_texture() const; + Vector3 get_emission_ring_axis() const; + float get_emission_ring_height() const; + float get_emission_ring_radius() const; + float get_emission_ring_inner_radius() const; int get_emission_point_count() const; void set_gravity(const Vector3 &p_gravity); diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp index b34d3feb47..ec00f9d7b7 100644 --- a/scene/resources/sky_material.cpp +++ b/scene/resources/sky_material.cpp @@ -30,6 +30,9 @@ #include "sky_material.h" +Mutex ProceduralSkyMaterial::shader_mutex; +RID ProceduralSkyMaterial::shader; + void ProceduralSkyMaterial::set_sky_top_color(const Color &p_sky_top) { sky_top_color = p_sky_top; RS::get_singleton()->material_set_param(_get_material(), "sky_top_color", sky_top_color.to_linear()); @@ -128,7 +131,17 @@ Shader::Mode ProceduralSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } +RID ProceduralSkyMaterial::get_rid() const { + _update_shader(); + if (!shader_set) { + RS::get_singleton()->material_set_shader(_get_material(), shader); + shader_set = true; + } + return _get_material(); +} + RID ProceduralSkyMaterial::get_shader_rid() const { + _update_shader(); return shader; } @@ -180,10 +193,18 @@ void ProceduralSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sun_curve", PROPERTY_HINT_EXP_EASING), "set_sun_curve", "get_sun_curve"); } -ProceduralSkyMaterial::ProceduralSkyMaterial() { - shader = RS::get_singleton()->shader_create(); +void ProceduralSkyMaterial::cleanup_shader() { + if (shader.is_valid()) { + RS::get_singleton()->free(shader); + } +} - RS::get_singleton()->shader_set_code(shader, R"( +void ProceduralSkyMaterial::_update_shader() { + shader_mutex.lock(); + if (shader.is_null()) { + shader = RS::get_singleton()->shader_create(); + + RS::get_singleton()->shader_set_code(shader, R"( shader_type sky; uniform vec4 sky_top_color : hint_color = vec4(0.35, 0.46, 0.71, 1.0); @@ -250,9 +271,11 @@ void sky() { COLOR = mix(ground, sky, step(0.0, EYEDIR.y)); } )"); + } + shader_mutex.unlock(); +} - RS::get_singleton()->material_set_shader(_get_material(), shader); - +ProceduralSkyMaterial::ProceduralSkyMaterial() { set_sky_top_color(Color(0.35, 0.46, 0.71)); set_sky_horizon_color(Color(0.55, 0.69, 0.81)); set_sky_curve(0.09); @@ -268,7 +291,6 @@ void sky() { } ProceduralSkyMaterial::~ProceduralSkyMaterial() { - RS::get_singleton()->free(shader); RS::get_singleton()->material_set_shader(_get_material(), RID()); } @@ -293,7 +315,17 @@ Shader::Mode PanoramaSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } +RID PanoramaSkyMaterial::get_rid() const { + _update_shader(); + if (!shader_set) { + RS::get_singleton()->material_set_shader(_get_material(), shader); + shader_set = true; + } + return _get_material(); +} + RID PanoramaSkyMaterial::get_shader_rid() const { + _update_shader(); return shader; } @@ -304,10 +336,21 @@ void PanoramaSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "panorama", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_panorama", "get_panorama"); } -PanoramaSkyMaterial::PanoramaSkyMaterial() { - shader = RS::get_singleton()->shader_create(); +Mutex PanoramaSkyMaterial::shader_mutex; +RID PanoramaSkyMaterial::shader; - RS::get_singleton()->shader_set_code(shader, R"( +void PanoramaSkyMaterial::cleanup_shader() { + if (shader.is_valid()) { + RS::get_singleton()->free(shader); + } +} + +void PanoramaSkyMaterial::_update_shader() { + shader_mutex.lock(); + if (shader.is_null()) { + shader = RS::get_singleton()->shader_create(); + + RS::get_singleton()->shader_set_code(shader, R"( shader_type sky; uniform sampler2D source_panorama : filter_linear; @@ -316,12 +359,15 @@ void sky() { COLOR = texture(source_panorama, SKY_COORDS).rgb; } )"); + } - RS::get_singleton()->material_set_shader(_get_material(), shader); + shader_mutex.unlock(); +} + +PanoramaSkyMaterial::PanoramaSkyMaterial() { } PanoramaSkyMaterial::~PanoramaSkyMaterial() { - RS::get_singleton()->free(shader); RS::get_singleton()->material_set_shader(_get_material(), RID()); } @@ -436,10 +482,23 @@ Shader::Mode PhysicalSkyMaterial::get_shader_mode() const { return Shader::MODE_SKY; } +RID PhysicalSkyMaterial::get_rid() const { + _update_shader(); + if (!shader_set) { + RS::get_singleton()->material_set_shader(_get_material(), shader); + shader_set = true; + } + return _get_material(); +} + RID PhysicalSkyMaterial::get_shader_rid() const { + _update_shader(); return shader; } +Mutex PhysicalSkyMaterial::shader_mutex; +RID PhysicalSkyMaterial::shader; + void PhysicalSkyMaterial::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rayleigh_coefficient", "rayleigh"), &PhysicalSkyMaterial::set_rayleigh_coefficient); ClassDB::bind_method(D_METHOD("get_rayleigh_coefficient"), &PhysicalSkyMaterial::get_rayleigh_coefficient); @@ -491,10 +550,18 @@ void PhysicalSkyMaterial::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "night_sky", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_night_sky", "get_night_sky"); } -PhysicalSkyMaterial::PhysicalSkyMaterial() { - shader = RS::get_singleton()->shader_create(); +void PhysicalSkyMaterial::cleanup_shader() { + if (shader.is_valid()) { + RS::get_singleton()->free(shader); + } +} - RS::get_singleton()->shader_set_code(shader, R"( +void PhysicalSkyMaterial::_update_shader() { + shader_mutex.lock(); + if (shader.is_null()) { + shader = RS::get_singleton()->shader_create(); + + RS::get_singleton()->shader_set_code(shader, R"( shader_type sky; uniform float rayleigh : hint_range(0, 64) = 2.0; @@ -588,9 +655,12 @@ void sky() { } } )"); + } - RS::get_singleton()->material_set_shader(_get_material(), shader); + shader_mutex.unlock(); +} +PhysicalSkyMaterial::PhysicalSkyMaterial() { set_rayleigh_coefficient(2.0); set_rayleigh_color(Color(0.056, 0.14, 0.3)); set_mie_coefficient(0.005); @@ -604,5 +674,4 @@ void sky() { } PhysicalSkyMaterial::~PhysicalSkyMaterial() { - RS::get_singleton()->free(shader); } diff --git a/scene/resources/sky_material.h b/scene/resources/sky_material.h index 8fe015519d..63e730617b 100644 --- a/scene/resources/sky_material.h +++ b/scene/resources/sky_material.h @@ -51,7 +51,10 @@ private: float sun_angle_max; float sun_curve; - RID shader; + static Mutex shader_mutex; + static RID shader; + static void _update_shader(); + mutable bool shader_set = false; protected: static void _bind_methods(); @@ -90,6 +93,9 @@ public: virtual Shader::Mode get_shader_mode() const override; virtual RID get_shader_rid() const override; + virtual RID get_rid() const override; + + static void cleanup_shader(); ProceduralSkyMaterial(); ~ProceduralSkyMaterial(); @@ -103,7 +109,11 @@ class PanoramaSkyMaterial : public Material { private: Ref<Texture2D> panorama; - RID shader; + + static Mutex shader_mutex; + static RID shader; + static void _update_shader(); + mutable bool shader_set = false; protected: static void _bind_methods(); @@ -115,6 +125,9 @@ public: virtual Shader::Mode get_shader_mode() const override; virtual RID get_shader_rid() const override; + virtual RID get_rid() const override; + + static void cleanup_shader(); PanoramaSkyMaterial(); ~PanoramaSkyMaterial(); @@ -127,7 +140,8 @@ class PhysicalSkyMaterial : public Material { GDCLASS(PhysicalSkyMaterial, Material); private: - RID shader; + static Mutex shader_mutex; + static RID shader; float rayleigh; Color rayleigh_color; @@ -140,6 +154,8 @@ private: float exposure; float dither_strength; Ref<Texture2D> night_sky; + static void _update_shader(); + mutable bool shader_set = false; protected: static void _bind_methods(); @@ -182,6 +198,9 @@ public: virtual Shader::Mode get_shader_mode() const override; virtual RID get_shader_rid() const override; + static void cleanup_shader(); + virtual RID get_rid() const override; + PhysicalSkyMaterial(); ~PhysicalSkyMaterial(); }; diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index deb5a50eb2..f9b5a4cfec 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -41,6 +41,8 @@ /////////////////////////////// TileSet ////////////////////////////////////// +const int TileSet::INVALID_SOURCE = -1; + const char *TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[] = { "right_side", "right_corner", @@ -127,8 +129,8 @@ void TileSet::_compute_next_source_id() { // Sources management int TileSet::add_source(Ref<TileSetSource> p_tile_set_source, int p_atlas_source_id_override) { - ERR_FAIL_COND_V(!p_tile_set_source.is_valid(), -1); - ERR_FAIL_COND_V_MSG(p_atlas_source_id_override >= 0 && (sources.has(p_atlas_source_id_override)), -1, vformat("Cannot create TileSet atlas source. Another atlas source exists with id %d.", p_atlas_source_id_override)); + ERR_FAIL_COND_V(!p_tile_set_source.is_valid(), TileSet::INVALID_SOURCE); + ERR_FAIL_COND_V_MSG(p_atlas_source_id_override >= 0 && (sources.has(p_atlas_source_id_override)), TileSet::INVALID_SOURCE, vformat("Cannot create TileSet atlas source. Another atlas source exists with id %d.", p_atlas_source_id_override)); int new_source_id = p_atlas_source_id_override >= 0 ? p_atlas_source_id_override : next_source_id; sources[new_source_id] = p_tile_set_source; @@ -191,7 +193,7 @@ int TileSet::get_source_count() const { } int TileSet::get_source_id(int p_index) const { - ERR_FAIL_INDEX_V(p_index, source_ids.size(), -1); + ERR_FAIL_INDEX_V(p_index, source_ids.size(), TileSet::INVALID_SOURCE); return source_ids[p_index]; } @@ -607,6 +609,254 @@ Variant::Type TileSet::get_custom_data_type(int p_layer_id) const { return custom_data_layers[p_layer_id].type; } +void TileSet::set_source_level_tile_proxy(int p_source_from, int p_source_to) { + ERR_FAIL_COND(p_source_from == TileSet::INVALID_SOURCE || p_source_to == TileSet::INVALID_SOURCE); + + source_level_proxies[p_source_from] = p_source_to; + + emit_changed(); +} + +int TileSet::get_source_level_tile_proxy(int p_source_from) { + ERR_FAIL_COND_V(!source_level_proxies.has(p_source_from), TileSet::INVALID_SOURCE); + + return source_level_proxies[p_source_from]; +} + +bool TileSet::has_source_level_tile_proxy(int p_source_from) { + return source_level_proxies.has(p_source_from); +} + +void TileSet::remove_source_level_tile_proxy(int p_source_from) { + ERR_FAIL_COND(!source_level_proxies.has(p_source_from)); + + source_level_proxies.erase(p_source_from); + + emit_changed(); +} + +void TileSet::set_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_source_to, Vector2i p_coords_to) { + ERR_FAIL_COND(p_source_from == TileSet::INVALID_SOURCE || p_source_to == TileSet::INVALID_SOURCE); + ERR_FAIL_COND(p_coords_from == TileSetSource::INVALID_ATLAS_COORDS || p_coords_to == TileSetSource::INVALID_ATLAS_COORDS); + + Array from; + from.push_back(p_source_from); + from.push_back(p_coords_from); + + Array to; + to.push_back(p_source_to); + to.push_back(p_coords_to); + + coords_level_proxies[from] = to; + + emit_changed(); +} + +Array TileSet::get_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from) { + Array from; + from.push_back(p_source_from); + from.push_back(p_coords_from); + + ERR_FAIL_COND_V(!coords_level_proxies.has(from), Array()); + + return coords_level_proxies[from]; +} + +bool TileSet::has_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from) { + Array from; + from.push_back(p_source_from); + from.push_back(p_coords_from); + + return coords_level_proxies.has(from); +} + +void TileSet::remove_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from) { + Array from; + from.push_back(p_source_from); + from.push_back(p_coords_from); + + ERR_FAIL_COND(!coords_level_proxies.has(from)); + + coords_level_proxies.erase(from); + + emit_changed(); +} + +void TileSet::set_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from, int p_source_to, Vector2i p_coords_to, int p_alternative_to) { + ERR_FAIL_COND(p_source_from == TileSet::INVALID_SOURCE || p_source_to == TileSet::INVALID_SOURCE); + ERR_FAIL_COND(p_coords_from == TileSetSource::INVALID_ATLAS_COORDS || p_coords_to == TileSetSource::INVALID_ATLAS_COORDS); + + Array from; + from.push_back(p_source_from); + from.push_back(p_coords_from); + from.push_back(p_alternative_from); + + Array to; + to.push_back(p_source_to); + to.push_back(p_coords_to); + to.push_back(p_alternative_to); + + alternative_level_proxies[from] = to; + + emit_changed(); +} + +Array TileSet::get_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) { + Array from; + from.push_back(p_source_from); + from.push_back(p_coords_from); + from.push_back(p_alternative_from); + + ERR_FAIL_COND_V(!alternative_level_proxies.has(from), Array()); + + return alternative_level_proxies[from]; +} + +bool TileSet::has_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) { + Array from; + from.push_back(p_source_from); + from.push_back(p_coords_from); + from.push_back(p_alternative_from); + + return alternative_level_proxies.has(from); +} + +void TileSet::remove_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) { + Array from; + from.push_back(p_source_from); + from.push_back(p_coords_from); + from.push_back(p_alternative_from); + + ERR_FAIL_COND(!alternative_level_proxies.has(from)); + + alternative_level_proxies.erase(from); + + emit_changed(); +} + +Array TileSet::get_source_level_tile_proxies() const { + Array output; + for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { + Array proxy; + proxy.push_back(E->key()); + proxy.push_back(E->get()); + output.push_back(proxy); + } + return output; +} + +Array TileSet::get_coords_level_tile_proxies() const { + Array output; + for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { + Array proxy; + proxy.append_array(E->key()); + proxy.append_array(E->get()); + output.push_back(proxy); + } + return output; +} + +Array TileSet::get_alternative_level_tile_proxies() const { + Array output; + for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { + Array proxy; + proxy.append_array(E->key()); + proxy.append_array(E->get()); + output.push_back(proxy); + } + return output; +} + +Array TileSet::map_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) const { + Array from; + from.push_back(p_source_from); + from.push_back(p_coords_from); + from.push_back(p_alternative_from); + + // Check if the tile is valid, and if so, don't map the tile and return the input. + if (has_source(p_source_from)) { + Ref<TileSetSource> source = get_source(p_source_from); + if (source->has_tile(p_coords_from) && source->has_alternative_tile(p_coords_from, p_alternative_from)) { + return from; + } + } + + // Source, coords and alternative match. + if (alternative_level_proxies.has(from)) { + return alternative_level_proxies[from].duplicate(); + } + + // Source and coords match. + from.pop_back(); + if (coords_level_proxies.has(from)) { + Array output = coords_level_proxies[from].duplicate(); + output.push_back(p_alternative_from); + return output; + } + + // Source matches. + if (source_level_proxies.has(p_source_from)) { + Array output; + output.push_back(source_level_proxies[p_source_from]); + output.push_back(p_coords_from); + output.push_back(p_alternative_from); + return output; + } + + Array output; + output.push_back(p_source_from); + output.push_back(p_coords_from); + output.push_back(p_alternative_from); + return output; +} + +void TileSet::cleanup_invalid_tile_proxies() { + // Source level. + Vector<int> source_to_remove; + for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { + if (has_source(E->key())) { + source_to_remove.append(E->key()); + } + } + for (int i = 0; i < source_to_remove.size(); i++) { + remove_source_level_tile_proxy(source_to_remove[i]); + } + + // Coords level. + Vector<Array> coords_to_remove; + for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { + Array a = E->key(); + if (has_source(a[0]) && get_source(a[0])->has_tile(a[1])) { + coords_to_remove.append(a); + } + } + for (int i = 0; i < coords_to_remove.size(); i++) { + Array a = coords_to_remove[i]; + remove_coords_level_tile_proxy(a[0], a[1]); + } + + // Alternative level. + Vector<Array> alternative_to_remove; + for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { + Array a = E->key(); + if (has_source(a[0]) && get_source(a[0])->has_tile(a[1]) && get_source(a[0])->has_alternative_tile(a[1], a[2])) { + alternative_to_remove.append(a); + } + } + for (int i = 0; i < alternative_to_remove.size(); i++) { + Array a = alternative_to_remove[i]; + remove_alternative_level_tile_proxy(a[0], a[1], a[2]); + } +} + +void TileSet::clear_tile_proxies() { + source_level_proxies.clear(); + coords_level_proxies.clear(); + alternative_level_proxies.clear(); + + emit_changed(); +} + Vector<Vector2> TileSet::get_tile_shape_polygon() { Vector<Vector2> points; if (tile_shape == TileSet::TILE_SHAPE_SQUARE) { @@ -1539,7 +1789,7 @@ const Vector2i TileSetSource::INVALID_ATLAS_COORDS = Vector2i(-1, -1); const int TileSetSource::INVALID_TILE_ALTERNATIVE = -1; #ifndef DISABLE_DEPRECATED -void TileSet::compatibility_conversion() { +void TileSet::_compatibility_conversion() { for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) { CompatibilityTileData *ctd = E->value(); @@ -1551,13 +1801,93 @@ void TileSet::compatibility_conversion() { // Handle each tile as a new source. Not optimal but at least it should stay compatible. switch (ctd->tile_mode) { - case 0: // SINGLE_TILE - // TODO - break; - case 1: // AUTO_TILE - // TODO - break; - case 2: // ATLAS_TILE + case COMPATIBILITY_TILE_MODE_SINGLE_TILE: { + atlas_source->set_margins(ctd->region.get_position()); + atlas_source->set_texture_region_size(ctd->region.get_size()); + + Vector2i coords; + for (int flags = 0; flags < 8; flags++) { + bool flip_h = flags & 1; + bool flip_v = flags & 2; + bool transpose = flags & 4; + + int alternative_tile = 0; + if (!atlas_source->has_tile(coords)) { + atlas_source->create_tile(coords); + } else { + alternative_tile = atlas_source->create_alternative_tile(coords); + } + + // Add to the mapping. + Array key_array; + key_array.push_back(flip_h); + key_array.push_back(flip_v); + key_array.push_back(transpose); + + Array value_array; + value_array.push_back(source_id); + value_array.push_back(coords); + value_array.push_back(alternative_tile); + + if (!compatibility_tilemap_mapping.has(E->key())) { + compatibility_tilemap_mapping[E->key()] = Map<Array, Array>(); + } + compatibility_tilemap_mapping[E->key()][key_array] = value_array; + compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_SINGLE_TILE; + + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile)); + + tile_data->set_flip_h(flip_h); + tile_data->set_flip_v(flip_v); + tile_data->set_transpose(transpose); + tile_data->tile_set_material(ctd->material); + tile_data->set_modulate(ctd->modulate); + tile_data->set_z_index(ctd->z_index); + + if (ctd->occluder.is_valid()) { + if (get_occlusion_layers_count() < 1) { + set_occlusion_layers_count(1); + } + tile_data->set_occluder(0, ctd->occluder); + } + if (ctd->navigation.is_valid()) { + if (get_navigation_layers_count() < 1) { + set_navigation_layers_count(1); + } + tile_data->set_navigation_polygon(0, ctd->autotile_navpoly_map[coords]); + } + + tile_data->set_z_index(ctd->z_index); + + // Add the shapes. + if (ctd->shapes.size() > 0) { + if (get_physics_layers_count() < 1) { + set_physics_layers_count(1); + } + } + for (int k = 0; k < ctd->shapes.size(); k++) { + CompatibilityShapeData csd = ctd->shapes[k]; + if (csd.autotile_coords == coords) { + Ref<ConvexPolygonShape2D> convex_shape = csd.shape; // Only ConvexPolygonShape2D are supported, which is the default type used by the 3.x editor + if (convex_shape.is_valid()) { + Vector<Vector2> polygon = convex_shape->get_points(); + for (int point_index = 0; point_index < polygon.size(); point_index++) { + polygon.write[point_index] = csd.transform.xform(polygon[point_index]); + } + tile_data->set_collision_polygons_count(0, tile_data->get_collision_polygons_count(0) + 1); + int index = tile_data->get_collision_polygons_count(0) - 1; + tile_data->set_collision_polygon_one_way(0, index, csd.one_way); + tile_data->set_collision_polygon_one_way_margin(0, index, csd.one_way_margin); + tile_data->set_collision_polygon_points(0, index, polygon); + } + } + } + } + } break; + case COMPATIBILITY_TILE_MODE_AUTO_TILE: { + // Not supported. It would need manual conversion. + } break; + case COMPATIBILITY_TILE_MODE_ATLAS_TILE: { atlas_source->set_margins(ctd->region.get_position()); atlas_source->set_separation(Vector2i(ctd->autotile_spacing, ctd->autotile_spacing)); atlas_source->set_texture_region_size(ctd->autotile_tile_size); @@ -1578,6 +1908,25 @@ void TileSet::compatibility_conversion() { } else { alternative_tile = atlas_source->create_alternative_tile(coords); } + + // Add to the mapping. + Array key_array; + key_array.push_back(coords); + key_array.push_back(flip_h); + key_array.push_back(flip_v); + key_array.push_back(transpose); + + Array value_array; + value_array.push_back(source_id); + value_array.push_back(coords); + value_array.push_back(alternative_tile); + + if (!compatibility_tilemap_mapping.has(E->key())) { + compatibility_tilemap_mapping[E->key()] = Map<Array, Array>(); + } + compatibility_tilemap_mapping[E->key()][key_array] = value_array; + compatibility_tilemap_mapping_tile_modes[E->key()] = COMPATIBILITY_TILE_MODE_ATLAS_TILE; + TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(coords, alternative_tile)); tile_data->set_flip_h(flip_h); @@ -1641,7 +1990,7 @@ void TileSet::compatibility_conversion() { } } } - break; + } break; } // Offset all shapes @@ -1655,9 +2004,6 @@ void TileSet::compatibility_conversion() { convex->set_points(points); } } - - // Add the mapping to the map - compatibility_source_mapping.insert(E->key(), source_id); } // Reset compatibility data @@ -1666,14 +2012,50 @@ void TileSet::compatibility_conversion() { } compatibility_data = Map<int, CompatibilityTileData *>(); } + +Array TileSet::compatibility_tilemap_map(int p_tile_id, Vector2i p_coords, bool p_flip_h, bool p_flip_v, bool p_transpose) { + Array cannot_convert_array; + cannot_convert_array.push_back(TileSet::INVALID_SOURCE); + cannot_convert_array.push_back(TileSetAtlasSource::INVALID_ATLAS_COORDS); + cannot_convert_array.push_back(TileSetAtlasSource::INVALID_TILE_ALTERNATIVE); + + if (!compatibility_tilemap_mapping.has(p_tile_id)) { + return cannot_convert_array; + } + + int tile_mode = compatibility_tilemap_mapping_tile_modes[p_tile_id]; + switch (tile_mode) { + case COMPATIBILITY_TILE_MODE_SINGLE_TILE: { + Array a; + a.push_back(p_flip_h); + a.push_back(p_flip_v); + a.push_back(p_transpose); + return compatibility_tilemap_mapping[p_tile_id][a]; + } + case COMPATIBILITY_TILE_MODE_AUTO_TILE: + return cannot_convert_array; + break; + case COMPATIBILITY_TILE_MODE_ATLAS_TILE: { + Array a; + a.push_back(p_coords); + a.push_back(p_flip_h); + a.push_back(p_flip_v); + a.push_back(p_transpose); + return compatibility_tilemap_mapping[p_tile_id][a]; + } + default: + return cannot_convert_array; + break; + } +}; + #endif // DISABLE_DEPRECATED bool TileSet::_set(const StringName &p_name, const Variant &p_value) { Vector<String> components = String(p_name).split("/", true, 2); #ifndef DISABLE_DEPRECATED - // TODO: THIS IS HOW WE CHECK IF WE HAVE A DEPRECATED RESOURCE - // This should be moved to a dedicated conversion system + // TODO: This should be moved to a dedicated conversion system (see #50691) if (components.size() >= 1 && components[0].is_valid_int()) { int id = components[0].to_int(); @@ -1809,29 +2191,23 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { /* // IGNORED FOR NOW, they seem duplicated data compared to the shapes array } else if (what == "shape") { - // TODO } else if (what == "shape_offset") { - // TODO } else if (what == "shape_transform") { - // TODO } else if (what == "shape_one_way") { - // TODO } else if (what == "shape_one_way_margin") { - // TODO } // IGNORED FOR NOW, maybe useless ? else if (what == "occluder_offset") { // Not } else if (what == "navigation_offset") { - // TODO } */ } else if (what == "z_index") { ctd->z_index = p_value; - // TODO: remove the conversion from here, it's not where it should be done - compatibility_conversion(); + // TODO: remove the conversion from here, it's not where it should be done (see #50691) + _compatibility_conversion(); } else { return false; } @@ -1966,6 +2342,31 @@ bool TileSet::_set(const StringName &p_name, const Variant &p_value) { add_source(p_value, source_id); } return true; + } else if (components.size() == 2 && components[0] == "tile_proxies") { + ERR_FAIL_COND_V(p_value.get_type() != Variant::ARRAY, false); + Array a = p_value; + ERR_FAIL_COND_V(a.size() % 2 != 0, false); + if (components[1] == "source_level") { + for (int i = 0; i < a.size(); i += 2) { + set_source_level_tile_proxy(a[i], a[i + 1]); + } + return true; + } else if (components[1] == "coords_level") { + for (int i = 0; i < a.size(); i += 2) { + Array key = a[i]; + Array value = a[i + 1]; + set_coords_level_tile_proxy(key[0], key[1], value[0], value[1]); + } + return true; + } else if (components[1] == "alternative_level") { + for (int i = 0; i < a.size(); i += 2) { + Array key = a[i]; + Array value = a[i + 1]; + set_alternative_level_tile_proxy(key[0], key[1], key[2], value[0], value[1], value[2]); + } + return true; + } + return false; } #ifndef DISABLE_DEPRECATED @@ -2065,6 +2466,33 @@ bool TileSet::_get(const StringName &p_name, Variant &r_ret) const { } else { return false; } + } else if (components.size() == 2 && components[0] == "tile_proxies") { + if (components[1] == "source_level") { + Array a; + for (Map<int, int>::Element *E = source_level_proxies.front(); E; E = E->next()) { + a.push_back(E->key()); + a.push_back(E->get()); + } + r_ret = a; + return true; + } else if (components[1] == "coords_level") { + Array a; + for (Map<Array, Array>::Element *E = coords_level_proxies.front(); E; E = E->next()) { + a.push_back(E->key()); + a.push_back(E->get()); + } + r_ret = a; + return true; + } else if (components[1] == "alternative_level") { + Array a; + for (Map<Array, Array>::Element *E = alternative_level_proxies.front(); E; E = E->next()) { + a.push_back(E->key()); + a.push_back(E->get()); + } + r_ret = a; + return true; + } + return false; } return false; @@ -2138,12 +2566,19 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const { for (Map<int, Ref<TileSetSource>>::Element *E_source = sources.front(); E_source; E_source = E_source->next()) { p_list->push_back(PropertyInfo(Variant::INT, vformat("sources/%d", E_source->key()), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } + + // Tile Proxies. + // Note: proxies need to be set after sources are set. + p_list->push_back(PropertyInfo(Variant::NIL, "Tile Proxies", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } void TileSet::_bind_methods() { // Sources management. ClassDB::bind_method(D_METHOD("get_next_source_id"), &TileSet::get_next_source_id); - ClassDB::bind_method(D_METHOD("add_source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_source", "atlas_source_id_override"), &TileSet::add_source, DEFVAL(TileSet::INVALID_SOURCE)); ClassDB::bind_method(D_METHOD("remove_source", "source_id"), &TileSet::remove_source); ClassDB::bind_method(D_METHOD("set_source_id", "source_id"), &TileSet::set_source_id); ClassDB::bind_method(D_METHOD("get_source_count"), &TileSet::get_source_count); @@ -2212,6 +2647,27 @@ void TileSet::_bind_methods() { ClassDB::bind_method(D_METHOD("set_custom_data_layers_count", "custom_data_layers_count"), &TileSet::set_custom_data_layers_count); ClassDB::bind_method(D_METHOD("get_custom_data_layers_count"), &TileSet::get_custom_data_layers_count); + // Tile proxies + ClassDB::bind_method(D_METHOD("set_source_level_tile_proxy", "source_from", "source_to"), &TileSet::set_source_level_tile_proxy); + ClassDB::bind_method(D_METHOD("get_source_level_tile_proxy", "source_from"), &TileSet::get_source_level_tile_proxy); + ClassDB::bind_method(D_METHOD("has_source_level_tile_proxy", "source_from"), &TileSet::has_source_level_tile_proxy); + ClassDB::bind_method(D_METHOD("remove_source_level_tile_proxy", "source_from"), &TileSet::remove_source_level_tile_proxy); + + ClassDB::bind_method(D_METHOD("set_coords_level_tile_proxy", "p_source_from", "coords_from", "source_to", "coords_to"), &TileSet::set_coords_level_tile_proxy); + ClassDB::bind_method(D_METHOD("get_coords_level_tile_proxy", "source_from", "coords_from"), &TileSet::get_coords_level_tile_proxy); + ClassDB::bind_method(D_METHOD("has_coords_level_tile_proxy", "source_from", "coords_from"), &TileSet::has_coords_level_tile_proxy); + ClassDB::bind_method(D_METHOD("remove_coords_level_tile_proxy", "source_from", "coords_from"), &TileSet::remove_coords_level_tile_proxy); + + ClassDB::bind_method(D_METHOD("set_alternative_level_tile_proxy", "source_from", "coords_from", "alternative_from", "source_to", "coords_to", "alternative_to"), &TileSet::set_alternative_level_tile_proxy); + ClassDB::bind_method(D_METHOD("get_alternative_level_tile_proxy", "source_from", "coords_from", "alternative_from"), &TileSet::get_alternative_level_tile_proxy); + ClassDB::bind_method(D_METHOD("has_alternative_level_tile_proxy", "source_from", "coords_from", "alternative_from"), &TileSet::has_alternative_level_tile_proxy); + ClassDB::bind_method(D_METHOD("remove_alternative_level_tile_proxy", "source_from", "coords_from", "alternative_from"), &TileSet::remove_alternative_level_tile_proxy); + + ClassDB::bind_method(D_METHOD("map_tile_proxy", "source_from", "coords_from", "alternative_from"), &TileSet::map_tile_proxy); + + ClassDB::bind_method(D_METHOD("cleanup_invalid_tile_proxies"), &TileSet::cleanup_invalid_tile_proxies); + ClassDB::bind_method(D_METHOD("clear_tile_proxies"), &TileSet::clear_tile_proxies); + ADD_GROUP("Rendering", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uv_clipping"), "set_uv_clipping", "is_uv_clipping"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "y_sorting"), "set_y_sorting", "is_y_sorting"); @@ -2755,8 +3211,8 @@ void TileSetAtlasSource::clear_tiles_outside_texture() { } int TileSetAtlasSource::create_alternative_tile(const Vector2i p_atlas_coords, int p_alternative_id_override) { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); - ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), -1, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override)); + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("TileSetAtlasSource has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(p_alternative_id_override >= 0 && tiles[p_atlas_coords].alternatives.has(p_alternative_id_override), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("Cannot create alternative tile. Another alternative exists with id %d.", p_alternative_id_override)); int new_alternative_id = p_alternative_id_override >= 0 ? p_alternative_id_override : tiles[p_atlas_coords].next_alternative_id; @@ -2809,7 +3265,7 @@ bool TileSetAtlasSource::has_alternative_tile(const Vector2i p_atlas_coords, int } int TileSetAtlasSource::get_next_alternative_tile_id(const Vector2i p_atlas_coords) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); return tiles[p_atlas_coords].next_alternative_id; } @@ -2819,8 +3275,8 @@ int TileSetAtlasSource::get_alternative_tiles_count(const Vector2i p_atlas_coord } int TileSetAtlasSource::get_alternative_tile_id(const Vector2i p_atlas_coords, int p_index) const { - ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), -1, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); - ERR_FAIL_INDEX_V(p_index, tiles[p_atlas_coords].alternatives_ids.size(), -1); + ERR_FAIL_COND_V_MSG(!tiles.has(p_atlas_coords), TileSetSource::INVALID_TILE_ALTERNATIVE, vformat("The TileSetAtlasSource atlas has no tile at %s.", String(p_atlas_coords))); + ERR_FAIL_INDEX_V(p_index, tiles[p_atlas_coords].alternatives_ids.size(), TileSetSource::INVALID_TILE_ALTERNATIVE); return tiles[p_atlas_coords].alternatives_ids[p_index]; } @@ -2861,7 +3317,7 @@ void TileSetAtlasSource::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tile_at_coords", "atlas_coords"), &TileSetAtlasSource::get_tile_at_coords); // Alternative tiles - ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("create_alternative_tile", "atlas_coords", "alternative_id_override"), &TileSetAtlasSource::create_alternative_tile, DEFVAL(INVALID_TILE_ALTERNATIVE)); ClassDB::bind_method(D_METHOD("remove_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::remove_alternative_tile); ClassDB::bind_method(D_METHOD("set_alternative_tile_id", "atlas_coords", "alternative_tile", "new_id"), &TileSetAtlasSource::set_alternative_tile_id); ClassDB::bind_method(D_METHOD("has_alternative_tile", "atlas_coords", "alternative_tile"), &TileSetAtlasSource::has_alternative_tile); @@ -2948,7 +3404,7 @@ bool TileSetScenesCollectionSource::has_alternative_tile(const Vector2i p_atlas_ } int TileSetScenesCollectionSource::create_scene_tile(Ref<PackedScene> p_packed_scene, int p_id_override) { - ERR_FAIL_COND_V_MSG(p_id_override >= 0 && scenes.has(p_id_override), -1, vformat("Cannot create scene tile. Another scene tile exists with id %d.", p_id_override)); + ERR_FAIL_COND_V_MSG(p_id_override >= 0 && scenes.has(p_id_override), INVALID_TILE_ALTERNATIVE, vformat("Cannot create scene tile. Another scene tile exists with id %d.", p_id_override)); int new_scene_id = p_id_override >= 0 ? p_id_override : next_scene_id; @@ -3096,7 +3552,7 @@ void TileSetScenesCollectionSource::_bind_methods() { ClassDB::bind_method(D_METHOD("get_scene_tiles_count"), &TileSetScenesCollectionSource::get_scene_tiles_count); ClassDB::bind_method(D_METHOD("get_scene_tile_id", "index"), &TileSetScenesCollectionSource::get_scene_tile_id); ClassDB::bind_method(D_METHOD("has_scene_tile_id", "id"), &TileSetScenesCollectionSource::has_scene_tile_id); - ClassDB::bind_method(D_METHOD("create_scene_tile", "packed_scene", "id_override"), &TileSetScenesCollectionSource::create_scene_tile, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("create_scene_tile", "packed_scene", "id_override"), &TileSetScenesCollectionSource::create_scene_tile, DEFVAL(INVALID_TILE_ALTERNATIVE)); ClassDB::bind_method(D_METHOD("set_scene_tile_id", "id", "new_id"), &TileSetScenesCollectionSource::set_scene_tile_id); ClassDB::bind_method(D_METHOD("set_scene_tile_scene", "id", "packed_scene"), &TileSetScenesCollectionSource::set_scene_tile_scene); ClassDB::bind_method(D_METHOD("get_scene_tile_scene", "id"), &TileSetScenesCollectionSource::get_scene_tile_scene); @@ -3919,7 +4375,7 @@ void TileSetPluginAtlasRendering::update_dirty_quadrants(TileMap *p_tile_map, Se // Iterate over the cells of the quadrant. for (Map<Vector2i, Vector2i, TileMapQuadrant::CoordsWorldComparator>::Element *E_cell = q.world_to_map.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = p_tile_map->get_cell(E_cell->value()); + TileMapCell c = p_tile_map->get_cell(E_cell->value(), true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -4059,7 +4515,7 @@ void TileSetPluginAtlasRendering::draw_quadrant_debug(TileMap *p_tile_map, TileM RenderingServer *rs = RenderingServer::get_singleton(); Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()); for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { - const TileMapCell &c = p_tile_map->get_cell(E_cell->get()); + const TileMapCell &c = p_tile_map->get_cell(E_cell->get(), true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -4151,7 +4607,7 @@ void TileSetPluginAtlasPhysics::update_dirty_quadrants(TileMap *p_tile_map, Self } for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = p_tile_map->get_cell(E_cell->get()); + TileMapCell c = p_tile_map->get_cell(E_cell->get(), true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -4276,7 +4732,7 @@ void TileSetPluginAtlasPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMap Color debug_collision_color = p_tile_map->get_tree()->get_debug_collisions_color(); for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = p_tile_map->get_cell(E_cell->get()); + TileMapCell c = p_tile_map->get_cell(E_cell->get(), true); Transform2D xform; xform.set_origin(p_tile_map->map_to_world(E_cell->get()) - quadrant_pos); @@ -4370,7 +4826,7 @@ void TileSetPluginAtlasNavigation::update_dirty_quadrants(TileMap *p_tile_map, S // Get the navigation polygons and create regions. for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = p_tile_map->get_cell(E_cell->get()); + TileMapCell c = p_tile_map->get_cell(E_cell->get(), true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -4455,7 +4911,7 @@ void TileSetPluginAtlasNavigation::draw_quadrant_debug(TileMap *p_tile_map, Tile Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()); for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { - TileMapCell c = p_tile_map->get_cell(E_cell->get()); + TileMapCell c = p_tile_map->get_cell(E_cell->get(), true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -4526,7 +4982,7 @@ void TileSetPluginScenesCollections::update_dirty_quadrants(TileMap *p_tile_map, // Recreate the scenes. for (Set<Vector2i>::Element *E_cell = q.cells.front(); E_cell; E_cell = E_cell->next()) { - const TileMapCell &c = p_tile_map->get_cell(E_cell->get()); + const TileMapCell &c = p_tile_map->get_cell(E_cell->get(), true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { @@ -4585,7 +5041,7 @@ void TileSetPluginScenesCollections::draw_quadrant_debug(TileMap *p_tile_map, Ti RenderingServer *rs = RenderingServer::get_singleton(); Vector2 quadrant_pos = p_tile_map->map_to_world(p_quadrant->coords * p_tile_map->get_effective_quadrant_size()); for (Set<Vector2i>::Element *E_cell = p_quadrant->cells.front(); E_cell; E_cell = E_cell->next()) { - const TileMapCell &c = p_tile_map->get_cell(E_cell->get()); + const TileMapCell &c = p_tile_map->get_cell(E_cell->get(), true); TileSetSource *source; if (tile_set->has_source(c.source_id)) { diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index dbf6dbabe6..0a07981171 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -79,15 +79,15 @@ private: Vector2 tex_offset; Ref<ShaderMaterial> material; Rect2 region; - int tile_mode; - Color modulate; + int tile_mode = 0; + Color modulate = Color(1, 1, 1); // Atlas or autotiles data - int autotile_bitmask_mode; + int autotile_bitmask_mode = 0; Vector2 autotile_icon_coordinate; Size2i autotile_tile_size = Size2i(16, 16); - int autotile_spacing; + int autotile_spacing = 0; Map<Vector2i, int> autotile_bitmask_flags; Map<Vector2i, Ref<OccluderPolygon2D>> autotile_occluder_map; Map<Vector2i, Ref<NavigationPolygon>> autotile_navpoly_map; @@ -99,23 +99,29 @@ private: Vector2 occluder_offset; Ref<NavigationPolygon> navigation; Vector2 navigation_offset; - int z_index; + int z_index = 0; }; - Map<int, CompatibilityTileData *> compatibility_data = Map<int, CompatibilityTileData *>(); - Map<int, int> compatibility_source_mapping = Map<int, int>(); + enum CompatibilityTileMode { + COMPATIBILITY_TILE_MODE_SINGLE_TILE = 0, + COMPATIBILITY_TILE_MODE_AUTO_TILE, + COMPATIBILITY_TILE_MODE_ATLAS_TILE, + }; -private: - void compatibility_conversion(); + Map<int, CompatibilityTileData *> compatibility_data; + Map<int, int> compatibility_tilemap_mapping_tile_modes; + Map<int, Map<Array, Array>> compatibility_tilemap_mapping; -public: - int compatibility_get_source_for_tile_id(int p_old_source) { - return compatibility_source_mapping[p_old_source]; - }; + void _compatibility_conversion(); +public: + // Format of output array [source_id, atlas_coords, alternative] + Array compatibility_tilemap_map(int p_tile_id, Vector2i p_coords, bool p_flip_h, bool p_flip_v, bool p_transpose); #endif // DISABLE_DEPRECATED public: + static const int INVALID_SOURCE; // -1; + enum CellNeighbor { CELL_NEIGHBOR_RIGHT_SIDE = 0, CELL_NEIGHBOR_RIGHT_CORNER, @@ -165,7 +171,6 @@ public: TILE_OFFSET_AXIS_VERTICAL, }; -public: struct PackedSceneSource { Ref<PackedScene> scene; Vector2 offset; @@ -246,6 +251,11 @@ private: void _compute_next_source_id(); void _source_changed(); + // Tile proxies + Map<int, int> source_level_proxies; + Map<Array, Array> coords_level_proxies; + Map<Array, Array> alternative_level_proxies; + // Helpers Vector<Point2> _get_square_corner_or_side_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); Vector<Point2> _get_square_corner_terrain_bit_polygon(Vector2i p_size, TileSet::CellNeighbor p_bit); @@ -341,6 +351,31 @@ public: void set_custom_data_type(int p_layer_id, Variant::Type p_value); Variant::Type get_custom_data_type(int p_layer_id) const; + // Tiles proxies. + void set_source_level_tile_proxy(int p_source_from, int p_source_to); + int get_source_level_tile_proxy(int p_source_from); + bool has_source_level_tile_proxy(int p_source_from); + void remove_source_level_tile_proxy(int p_source_from); + + void set_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_source_to, Vector2i p_coords_to); + Array get_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from); + bool has_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from); + void remove_coords_level_tile_proxy(int p_source_from, Vector2i p_coords_from); + + void set_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from, int p_source_to, Vector2i p_coords_to, int p_alternative_to); + Array get_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from); + bool has_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from); + void remove_alternative_level_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from); + + Array get_source_level_tile_proxies() const; + Array get_coords_level_tile_proxies() const; + Array get_alternative_level_tile_proxies() const; + + Array map_tile_proxy(int p_source_from, Vector2i p_coords_from, int p_alternative_from) const; + + void cleanup_invalid_tile_proxies(); + void clear_tile_proxies(); + // Helpers Vector<Vector2> get_tile_shape_polygon(); void draw_tile_shape(CanvasItem *p_canvas_item, Rect2 p_region, Color p_color, bool p_filled = false, Ref<Texture2D> p_texture = Ref<Texture2D>()); diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp index 0f6412e6e9..ef380ab233 100644 --- a/scene/resources/visual_shader.cpp +++ b/scene/resources/visual_shader.cpp @@ -1740,6 +1740,7 @@ void VisualShader::_update_shader() const { } Map<int, String> code_map; + Set<int> empty_funcs; for (int i = 0; i < TYPE_MAX; i++) { if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) { @@ -1776,6 +1777,7 @@ void VisualShader::_update_shader() const { } if (is_empty_func) { + empty_funcs.insert(i); continue; } @@ -1934,7 +1936,11 @@ void VisualShader::_update_shader() const { if (!has_func_name(RenderingServer::ShaderMode(shader_mode), func_name[i])) { continue; } - tcode = tcode.insert(insertion_pos[i], global_code_per_func[Type(i)]); + String func_code = global_code_per_func[Type(i)].as_string(); + if (empty_funcs.has(Type(i)) && !func_code.is_empty()) { + func_code = vformat("%s%s%s", String("\nvoid " + String(func_name[i]) + "() {\n"), func_code, "}\n"); + } + tcode = tcode.insert(insertion_pos[i], func_code); } final_code += tcode; diff --git a/servers/physics_3d/collision_solver_3d_sat.cpp b/servers/physics_3d/collision_solver_3d_sat.cpp index 1cfb9ba3ad..6a7f2b73c5 100644 --- a/servers/physics_3d/collision_solver_3d_sat.cpp +++ b/servers/physics_3d/collision_solver_3d_sat.cpp @@ -629,9 +629,7 @@ public: _FORCE_INLINE_ bool test_axis(const Vector3 &p_axis, bool p_directional = false) { Vector3 axis = p_axis; - if (Math::abs(axis.x) < CMP_EPSILON && - Math::abs(axis.y) < CMP_EPSILON && - Math::abs(axis.z) < CMP_EPSILON) { + if (axis.is_equal_approx(Vector3())) { // strange case, try an upwards separator axis = Vector3(0.0, 1.0, 0.0); } diff --git a/servers/physics_3d/shape_3d_sw.cpp b/servers/physics_3d/shape_3d_sw.cpp index 2ffab0c923..04a174f9c8 100644 --- a/servers/physics_3d/shape_3d_sw.cpp +++ b/servers/physics_3d/shape_3d_sw.cpp @@ -1783,7 +1783,7 @@ bool HeightMapShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 int z = floor(local_begin.z); // Workaround cases where the ray starts at an integer position. - if (Math::abs(cross_x) < CMP_EPSILON) { + if (Math::is_zero_approx(cross_x)) { cross_x += delta_x; // If going backwards, we should ignore the position we would get by the above flooring, // because the ray is not heading in that direction. @@ -1792,7 +1792,7 @@ bool HeightMapShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 } } - if (Math::abs(cross_z) < CMP_EPSILON) { + if (Math::is_zero_approx(cross_z)) { cross_z += delta_z; if (z_step == -1) { z -= 1; diff --git a/servers/physics_3d/shape_3d_sw.h b/servers/physics_3d/shape_3d_sw.h index bc8bd3e695..0d1b7cc3d7 100644 --- a/servers/physics_3d/shape_3d_sw.h +++ b/servers/physics_3d/shape_3d_sw.h @@ -128,7 +128,7 @@ class PlaneShape3DSW : public Shape3DSW { public: Plane get_plane() const; - virtual real_t get_area() const { return Math_INF; } + virtual real_t get_area() const { return INFINITY; } virtual PhysicsServer3D::ShapeType get_type() const { return PhysicsServer3D::SHAPE_PLANE; } virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const; virtual Vector3 get_support(const Vector3 &p_normal) const; diff --git a/servers/physics_3d/soft_body_3d_sw.cpp b/servers/physics_3d/soft_body_3d_sw.cpp index 63a0fe11ba..724125bea8 100644 --- a/servers/physics_3d/soft_body_3d_sw.cpp +++ b/servers/physics_3d/soft_body_3d_sw.cpp @@ -1172,7 +1172,7 @@ struct _SoftBodyIntersectSegmentInfo { Vector3 dir; Vector3 hit_position; uint32_t hit_face_index = -1; - real_t hit_dist_sq = Math_INF; + real_t hit_dist_sq = INFINITY; static bool process_hit(uint32_t p_face_index, void *p_userdata) { _SoftBodyIntersectSegmentInfo &query_info = *(_SoftBodyIntersectSegmentInfo *)(p_userdata); @@ -1203,7 +1203,7 @@ bool SoftBodyShape3DSW::intersect_segment(const Vector3 &p_begin, const Vector3 soft_body->query_ray(p_begin, p_end, _SoftBodyIntersectSegmentInfo::process_hit, &query_info); - if (query_info.hit_dist_sq != Math_INF) { + if (query_info.hit_dist_sq != INFINITY) { r_result = query_info.hit_position; r_normal = soft_body->get_face_normal(query_info.hit_face_index); return true; diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index f771d6d0ef..3b9aca200b 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -946,7 +946,9 @@ void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, Iden break; case IdentifierType::IDENTIFIER_VARYING: if (HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG) && used_varyings.has(p_identifier)) { - used_varyings[p_identifier].used = true; + if (shader->varyings[p_identifier].stage != ShaderNode::Varying::STAGE_VERTEX && shader->varyings[p_identifier].stage != ShaderNode::Varying::STAGE_FRAGMENT) { + used_varyings[p_identifier].used = true; + } } break; case IdentifierType::IDENTIFIER_UNIFORM: @@ -2434,6 +2436,10 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI if (shader->uniforms.has(varname)) { fail = true; } else { + if (shader->varyings.has(varname)) { + _set_error(vformat("Varyings cannot be passed for '%s' parameter!", "out")); + return false; + } if (p_function_info.built_ins.has(varname)) { BuiltInInfo info = p_function_info.built_ins[varname]; if (info.constant) { @@ -3306,8 +3312,8 @@ bool ShaderLanguage::_is_operator_assign(Operator p_op) const { } bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, String *r_message) { - if (current_function == String("light")) { - *r_message = RTR("Varying may not be assigned in the 'light' function."); + if (current_function != String("vertex") && current_function != String("fragment")) { + *r_message = vformat(RTR("Varying may not be assigned in the '%s' function."), current_function); return false; } switch (p_varying.stage) { @@ -3318,12 +3324,15 @@ bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, St p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT; } break; + case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT: + case ShaderNode::Varying::STAGE_VERTEX_TO_LIGHT: case ShaderNode::Varying::STAGE_VERTEX: if (current_function == varying_function_names.fragment) { *r_message = RTR("Varyings which assigned in 'vertex' function may not be reassigned in 'fragment' or 'light'."); return false; } break; + case ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT: case ShaderNode::Varying::STAGE_FRAGMENT: if (current_function == varying_function_names.vertex) { *r_message = RTR("Varyings which assigned in 'fragment' function may not be reassigned in 'vertex' or 'light'."); @@ -4114,6 +4123,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } else if (shader->uniforms.has(varname)) { error = true; } else { + if (shader->varyings.has(varname)) { + _set_error(vformat("Varyings cannot be passed for '%s' parameter!", _get_qualifier_str(call_function->arguments[i].qualifier))); + return nullptr; + } if (p_function_info.built_ins.has(varname)) { BuiltInInfo info = p_function_info.built_ins[varname]; if (info.constant) { @@ -7862,15 +7875,6 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct tk = _get_token(); } - - for (Map<StringName, ShaderNode::Varying>::Element *E = shader->varyings.front(); E; E = E->next()) { - if (E->get().stage == ShaderNode::Varying::STAGE_VERTEX || E->get().stage == ShaderNode::Varying::STAGE_FRAGMENT) { - _set_tkpos(E->get().tkpos); - _set_error(RTR("Varying must only be used in two different stages, which can be 'vertex' 'fragment' and 'light'")); - return ERR_PARSE_ERROR; - } - } - return OK; } |